summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes19
-rw-r--r--.gitignore32
-rw-r--r--INSTALL81
-rw-r--r--INSTALL-GIT79
-rw-r--r--LICENSE35
-rw-r--r--Makefile.am8
-rw-r--r--NEWS14
-rw-r--r--README.md105
-rwxr-xr-xbootstrap16
-rw-r--r--build/bootstrap.make96
-rw-r--r--build/export/odb/stub.make11
-rw-r--r--build/hxx/hxx-cxx.make58
-rw-r--r--build/import/cli/cli-cxx.make47
-rw-r--r--build/import/cli/configuration-rules.make13
-rwxr-xr-xbuild/import/cli/configure53
-rw-r--r--build/import/cli/stub.make28
-rw-r--r--build/import/libcutl/configuration-rules.make13
-rwxr-xr-xbuild/import/libcutl/configure53
-rw-r--r--build/import/libcutl/stub.make28
-rw-r--r--build/import/odb/configuration-rules.make13
-rwxr-xr-xbuild/import/odb/configure53
-rw-r--r--build/import/odb/hxx-cxx.make127
-rw-r--r--build/import/odb/stub.make28
-rw-r--r--build/root.build52
-rw-r--r--buildfile13
-rw-r--r--configure.ac150
-rw-r--r--doc/.gitignore4
-rw-r--r--doc/Makefile.am7
-rw-r--r--doc/buildfile20
-rwxr-xr-xdoc/doc.sh78
-rw-r--r--doc/makefile80
-rw-r--r--doc/manual.html2ps69
-rw-r--r--doc/manual.xhtml27015
-rw-r--r--libodb-boost/.gitignore25
-rw-r--r--libodb-boost/GPLv2340
-rw-r--r--libodb-boost/INSTALL6
-rw-r--r--libodb-boost/LICENSE20
l---------libodb-boost/NEWS1
-rw-r--r--libodb-boost/README20
-rw-r--r--libodb-boost/build/.gitignore (renamed from build/.gitignore)0
-rw-r--r--libodb-boost/build/bootstrap.build10
-rw-r--r--libodb-boost/build/export.build9
-rw-r--r--libodb-boost/build/root.build17
-rw-r--r--libodb-boost/buildfile9
-rw-r--r--libodb-boost/manifest25
-rw-r--r--libodb-boost/odb/boost.options10
-rw-r--r--libodb-boost/odb/boost/buildfile65
-rw-r--r--libodb-boost/odb/boost/date-time.options5
-rw-r--r--libodb-boost/odb/boost/date-time/exceptions.cxx37
-rw-r--r--libodb-boost/odb/boost/date-time/exceptions.hxx43
-rw-r--r--libodb-boost/odb/boost/date-time/gregorian-common.options4
-rw-r--r--libodb-boost/odb/boost/date-time/gregorian-mssql.options11
-rw-r--r--libodb-boost/odb/boost/date-time/gregorian-mysql.options11
-rw-r--r--libodb-boost/odb/boost/date-time/gregorian-oracle.options11
-rw-r--r--libodb-boost/odb/boost/date-time/gregorian-pgsql.options11
-rw-r--r--libodb-boost/odb/boost/date-time/gregorian-sqlite.options11
-rw-r--r--libodb-boost/odb/boost/date-time/mssql/gregorian-mapping.hxx15
-rw-r--r--libodb-boost/odb/boost/date-time/mssql/gregorian-traits.hxx68
-rw-r--r--libodb-boost/odb/boost/date-time/mssql/posix-time-mapping.hxx21
-rw-r--r--libodb-boost/odb/boost/date-time/mssql/posix-time-traits.hxx194
-rw-r--r--libodb-boost/odb/boost/date-time/mysql/gregorian-mapping.hxx14
-rw-r--r--libodb-boost/odb/boost/date-time/mysql/gregorian-traits.hxx72
-rw-r--r--libodb-boost/odb/boost/date-time/mysql/posix-time-mapping.hxx19
-rw-r--r--libodb-boost/odb/boost/date-time/mysql/posix-time-traits.hxx238
-rw-r--r--libodb-boost/odb/boost/date-time/oracle/gregorian-mapping.hxx14
-rw-r--r--libodb-boost/odb/boost/date-time/oracle/gregorian-traits.hxx83
-rw-r--r--libodb-boost/odb/boost/date-time/oracle/posix-time-mapping.hxx20
-rw-r--r--libodb-boost/odb/boost/date-time/oracle/posix-time-traits.hxx237
-rw-r--r--libodb-boost/odb/boost/date-time/pgsql/gregorian-mapping.hxx14
-rw-r--r--libodb-boost/odb/boost/date-time/pgsql/gregorian-traits.hxx69
-rw-r--r--libodb-boost/odb/boost/date-time/pgsql/posix-time-mapping.hxx19
-rw-r--r--libodb-boost/odb/boost/date-time/pgsql/posix-time-traits.hxx169
-rw-r--r--libodb-boost/odb/boost/date-time/posix-time-common.options4
-rw-r--r--libodb-boost/odb/boost/date-time/posix-time-mssql.options11
-rw-r--r--libodb-boost/odb/boost/date-time/posix-time-mysql.options11
-rw-r--r--libodb-boost/odb/boost/date-time/posix-time-oracle.options11
-rw-r--r--libodb-boost/odb/boost/date-time/posix-time-pgsql.options11
-rw-r--r--libodb-boost/odb/boost/date-time/posix-time-sqlite.options11
-rw-r--r--libodb-boost/odb/boost/date-time/sqlite/gregorian-mapping.hxx14
-rw-r--r--libodb-boost/odb/boost/date-time/sqlite/gregorian-traits.hxx133
-rw-r--r--libodb-boost/odb/boost/date-time/sqlite/posix-time-mapping.hxx19
-rw-r--r--libodb-boost/odb/boost/date-time/sqlite/posix-time-traits.hxx238
-rw-r--r--libodb-boost/odb/boost/details/config.hxx15
-rw-r--r--libodb-boost/odb/boost/details/export.hxx46
-rw-r--r--libodb-boost/odb/boost/exception.hxx28
-rw-r--r--libodb-boost/odb/boost/lazy-ptr.hxx9
-rw-r--r--libodb-boost/odb/boost/multi-index.options7
-rw-r--r--libodb-boost/odb/boost/multi-index/container-traits.hxx215
-rw-r--r--libodb-boost/odb/boost/optional.options7
-rw-r--r--libodb-boost/odb/boost/optional/wrapper-traits.hxx63
-rw-r--r--libodb-boost/odb/boost/smart-ptr.options19
-rw-r--r--libodb-boost/odb/boost/smart-ptr/lazy-pointer-traits.hxx61
-rw-r--r--libodb-boost/odb/boost/smart-ptr/lazy-ptr.hxx269
-rw-r--r--libodb-boost/odb/boost/smart-ptr/lazy-ptr.ixx675
-rw-r--r--libodb-boost/odb/boost/smart-ptr/lazy-ptr.txx43
-rw-r--r--libodb-boost/odb/boost/smart-ptr/pointer-traits.hxx110
-rw-r--r--libodb-boost/odb/boost/smart-ptr/wrapper-traits.hxx64
-rw-r--r--libodb-boost/odb/boost/unordered.options7
-rw-r--r--libodb-boost/odb/boost/unordered/container-traits.hxx275
-rw-r--r--libodb-boost/odb/boost/uuid.options4
-rw-r--r--libodb-boost/odb/boost/uuid/mssql/uuid-mapping.hxx22
-rw-r--r--libodb-boost/odb/boost/uuid/mssql/uuid-traits.hxx65
-rw-r--r--libodb-boost/odb/boost/uuid/mysql/uuid-mapping.hxx22
-rw-r--r--libodb-boost/odb/boost/uuid/mysql/uuid-traits.hxx81
-rw-r--r--libodb-boost/odb/boost/uuid/oracle/uuid-mapping.hxx22
-rw-r--r--libodb-boost/odb/boost/uuid/oracle/uuid-traits.hxx77
-rw-r--r--libodb-boost/odb/boost/uuid/pgsql/uuid-mapping.hxx22
-rw-r--r--libodb-boost/odb/boost/uuid/pgsql/uuid-traits.hxx70
-rw-r--r--libodb-boost/odb/boost/uuid/sqlite/uuid-mapping.hxx22
-rw-r--r--libodb-boost/odb/boost/uuid/sqlite/uuid-traits.hxx81
-rw-r--r--libodb-boost/odb/boost/uuid/uuid-common.options4
-rw-r--r--libodb-boost/odb/boost/uuid/uuid-mssql.options11
-rw-r--r--libodb-boost/odb/boost/uuid/uuid-mysql.options11
-rw-r--r--libodb-boost/odb/boost/uuid/uuid-oracle.options11
-rw-r--r--libodb-boost/odb/boost/uuid/uuid-pgsql.options11
-rw-r--r--libodb-boost/odb/boost/uuid/uuid-sqlite.options11
-rw-r--r--libodb-boost/odb/boost/version.hxx0
-rw-r--r--libodb-boost/odb/boost/version.hxx.in74
-rw-r--r--libodb-boost/odb/boost/version.options12
-rw-r--r--libodb-boost/tests/.gitignore1
-rw-r--r--libodb-boost/tests/basics/buildfile6
-rw-r--r--libodb-boost/tests/basics/driver.cxx22
-rw-r--r--libodb-boost/tests/build/.gitignore (renamed from tests/build/.gitignore)0
-rw-r--r--libodb-boost/tests/build/bootstrap.build8
-rw-r--r--libodb-boost/tests/build/root.build23
-rw-r--r--libodb-boost/tests/buildfile4
-rw-r--r--libodb-mssql/.gitignore25
-rw-r--r--libodb-mssql/INSTALL6
-rw-r--r--libodb-mssql/LICENSE21
-rw-r--r--libodb-mssql/NCUEL294
l---------libodb-mssql/NEWS1
-rw-r--r--libodb-mssql/README20
-rw-r--r--libodb-mssql/build/.gitignore3
-rw-r--r--libodb-mssql/build/bootstrap.build10
-rw-r--r--libodb-mssql/build/export.build9
-rw-r--r--libodb-mssql/build/root.build19
-rw-r--r--libodb-mssql/buildfile9
-rw-r--r--libodb-mssql/manifest47
-rw-r--r--libodb-mssql/odb/mssql/auto-handle.cxx17
-rw-r--r--libodb-mssql/odb/mssql/auto-handle.hxx88
-rw-r--r--libodb-mssql/odb/mssql/binding.hxx65
-rw-r--r--libodb-mssql/odb/mssql/buildfile142
-rw-r--r--libodb-mssql/odb/mssql/connection-factory.cxx159
-rw-r--r--libodb-mssql/odb/mssql/connection-factory.hxx134
-rw-r--r--libodb-mssql/odb/mssql/connection.cxx287
-rw-r--r--libodb-mssql/odb/mssql/connection.hxx187
-rw-r--r--libodb-mssql/odb/mssql/connection.ixx44
-rw-r--r--libodb-mssql/odb/mssql/container-statements.hxx353
-rw-r--r--libodb-mssql/odb/mssql/container-statements.txx96
-rw-r--r--libodb-mssql/odb/mssql/database.cxx580
-rw-r--r--libodb-mssql/odb/mssql/database.hxx629
-rw-r--r--libodb-mssql/odb/mssql/database.ixx644
-rw-r--r--libodb-mssql/odb/mssql/details/.gitignore1
-rw-r--r--libodb-mssql/odb/mssql/details/config.hxx15
-rw-r--r--libodb-mssql/odb/mssql/details/conversion.hxx58
-rw-r--r--libodb-mssql/odb/mssql/details/export.hxx50
-rw-r--r--libodb-mssql/odb/mssql/details/options.cli63
-rw-r--r--libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.cxx1125
-rw-r--r--libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.hxx562
-rw-r--r--libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.ixx372
-rw-r--r--libodb-mssql/odb/mssql/error.cxx273
-rw-r--r--libodb-mssql/odb/mssql/error.hxx40
-rw-r--r--libodb-mssql/odb/mssql/exceptions.cxx109
-rw-r--r--libodb-mssql/odb/mssql/exceptions.hxx138
-rw-r--r--libodb-mssql/odb/mssql/forward.hxx91
-rw-r--r--libodb-mssql/odb/mssql/mssql-fwd.hxx216
-rw-r--r--libodb-mssql/odb/mssql/mssql-types.hxx161
-rw-r--r--libodb-mssql/odb/mssql/mssql.hxx69
-rw-r--r--libodb-mssql/odb/mssql/no-id-object-result.hxx85
-rw-r--r--libodb-mssql/odb/mssql/no-id-object-result.txx154
-rw-r--r--libodb-mssql/odb/mssql/no-id-object-statements.hxx137
-rw-r--r--libodb-mssql/odb/mssql/no-id-object-statements.txx39
-rw-r--r--libodb-mssql/odb/mssql/polymorphic-object-result.hxx99
-rw-r--r--libodb-mssql/odb/mssql/polymorphic-object-result.txx325
-rw-r--r--libodb-mssql/odb/mssql/polymorphic-object-statements.hxx469
-rw-r--r--libodb-mssql/odb/mssql/polymorphic-object-statements.txx140
-rw-r--r--libodb-mssql/odb/mssql/prepared-query.cxx15
-rw-r--r--libodb-mssql/odb/mssql/prepared-query.hxx34
-rw-r--r--libodb-mssql/odb/mssql/query-const-expr.cxx14
-rw-r--r--libodb-mssql/odb/mssql/query-dynamic.cxx157
-rw-r--r--libodb-mssql/odb/mssql/query-dynamic.hxx32
-rw-r--r--libodb-mssql/odb/mssql/query-dynamic.ixx30
-rw-r--r--libodb-mssql/odb/mssql/query-dynamic.txx25
-rw-r--r--libodb-mssql/odb/mssql/query.cxx406
-rw-r--r--libodb-mssql/odb/mssql/query.hxx2711
-rw-r--r--libodb-mssql/odb/mssql/query.ixx34
-rw-r--r--libodb-mssql/odb/mssql/query.txx169
-rw-r--r--libodb-mssql/odb/mssql/section-statements.hxx201
-rw-r--r--libodb-mssql/odb/mssql/section-statements.txx50
-rw-r--r--libodb-mssql/odb/mssql/simple-object-result.hxx89
-rw-r--r--libodb-mssql/odb/mssql/simple-object-result.txx186
-rw-r--r--libodb-mssql/odb/mssql/simple-object-statements.cxx15
-rw-r--r--libodb-mssql/odb/mssql/simple-object-statements.hxx606
-rw-r--r--libodb-mssql/odb/mssql/simple-object-statements.ixx68
-rw-r--r--libodb-mssql/odb/mssql/simple-object-statements.txx173
-rw-r--r--libodb-mssql/odb/mssql/statement-cache.hxx59
-rw-r--r--libodb-mssql/odb/mssql/statement-cache.txx60
-rw-r--r--libodb-mssql/odb/mssql/statement-processing.cxx356
-rw-r--r--libodb-mssql/odb/mssql/statement.cxx1740
-rw-r--r--libodb-mssql/odb/mssql/statement.hxx558
-rw-r--r--libodb-mssql/odb/mssql/statement.ixx41
-rw-r--r--libodb-mssql/odb/mssql/statements-base.cxx15
-rw-r--r--libodb-mssql/odb/mssql/statements-base.hxx63
-rw-r--r--libodb-mssql/odb/mssql/tracer.cxx60
-rw-r--r--libodb-mssql/odb/mssql/tracer.hxx61
-rw-r--r--libodb-mssql/odb/mssql/traits-calls.hxx190
-rw-r--r--libodb-mssql/odb/mssql/traits.cxx616
-rw-r--r--libodb-mssql/odb/mssql/traits.hxx2176
-rw-r--r--libodb-mssql/odb/mssql/traits.txx399
-rw-r--r--libodb-mssql/odb/mssql/transaction-impl.cxx103
-rw-r--r--libodb-mssql/odb/mssql/transaction-impl.hxx49
-rw-r--r--libodb-mssql/odb/mssql/transaction.cxx26
-rw-r--r--libodb-mssql/odb/mssql/transaction.hxx88
-rw-r--r--libodb-mssql/odb/mssql/transaction.ixx57
-rw-r--r--libodb-mssql/odb/mssql/version.hxx0
-rw-r--r--libodb-mssql/odb/mssql/version.hxx.in60
-rw-r--r--libodb-mssql/odb/mssql/view-result.hxx85
-rw-r--r--libodb-mssql/odb/mssql/view-result.txx153
-rw-r--r--libodb-mssql/odb/mssql/view-statements.hxx83
-rw-r--r--libodb-mssql/odb/mssql/view-statements.txx30
-rw-r--r--libodb-mssql/tests/.gitignore1
-rw-r--r--libodb-mssql/tests/basics/buildfile6
-rw-r--r--libodb-mssql/tests/basics/driver.cxx37
-rw-r--r--libodb-mssql/tests/build/.gitignore3
-rw-r--r--libodb-mssql/tests/build/bootstrap.build8
-rw-r--r--libodb-mssql/tests/build/root.build23
-rw-r--r--libodb-mssql/tests/buildfile4
-rw-r--r--libodb-mysql/.gitignore25
-rw-r--r--libodb-mysql/GPLv2340
-rw-r--r--libodb-mysql/INSTALL6
-rw-r--r--libodb-mysql/LICENSE20
l---------libodb-mysql/NEWS1
-rw-r--r--libodb-mysql/README20
-rw-r--r--libodb-mysql/build/.gitignore3
-rw-r--r--libodb-mysql/build/bootstrap.build10
-rw-r--r--libodb-mysql/build/export.build9
-rw-r--r--libodb-mysql/build/root.build45
-rw-r--r--libodb-mysql/buildfile9
-rw-r--r--libodb-mysql/manifest173
-rw-r--r--libodb-mysql/odb/mysql/auto-handle.hxx94
-rw-r--r--libodb-mysql/odb/mysql/binding.hxx45
-rw-r--r--libodb-mysql/odb/mysql/buildfile142
-rw-r--r--libodb-mysql/odb/mysql/connection-factory.cxx301
-rw-r--r--libodb-mysql/odb/mysql/connection-factory.hxx140
-rw-r--r--libodb-mysql/odb/mysql/connection.cxx218
-rw-r--r--libodb-mysql/odb/mysql/connection.hxx231
-rw-r--r--libodb-mysql/odb/mysql/connection.ixx44
-rw-r--r--libodb-mysql/odb/mysql/container-statements.hxx360
-rw-r--r--libodb-mysql/odb/mysql/container-statements.txx107
-rw-r--r--libodb-mysql/odb/mysql/database.cxx357
-rw-r--r--libodb-mysql/odb/mysql/database.hxx540
-rw-r--r--libodb-mysql/odb/mysql/database.ixx617
-rw-r--r--libodb-mysql/odb/mysql/details/.gitignore1
-rw-r--r--libodb-mysql/odb/mysql/details/config-vc.h12
-rw-r--r--libodb-mysql/odb/mysql/details/config.h.in17
-rw-r--r--libodb-mysql/odb/mysql/details/config.hxx23
-rw-r--r--libodb-mysql/odb/mysql/details/conversion.hxx58
-rw-r--r--libodb-mysql/odb/mysql/details/export.hxx50
-rw-r--r--libodb-mysql/odb/mysql/details/options.cli61
-rw-r--r--libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.cxx1125
-rw-r--r--libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.hxx570
-rw-r--r--libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.ixx384
-rw-r--r--libodb-mysql/odb/mysql/enum.cxx28
-rw-r--r--libodb-mysql/odb/mysql/enum.hxx129
-rw-r--r--libodb-mysql/odb/mysql/error.cxx76
-rw-r--r--libodb-mysql/odb/mysql/error.hxx34
-rw-r--r--libodb-mysql/odb/mysql/exceptions.cxx71
-rw-r--r--libodb-mysql/odb/mysql/exceptions.hxx87
-rw-r--r--libodb-mysql/odb/mysql/forward.hxx91
-rw-r--r--libodb-mysql/odb/mysql/mysql-types.hxx42
-rw-r--r--libodb-mysql/odb/mysql/mysql.hxx31
-rw-r--r--libodb-mysql/odb/mysql/no-id-object-result.hxx81
-rw-r--r--libodb-mysql/odb/mysql/no-id-object-result.txx178
-rw-r--r--libodb-mysql/odb/mysql/no-id-object-statements.hxx135
-rw-r--r--libodb-mysql/odb/mysql/no-id-object-statements.txx36
-rw-r--r--libodb-mysql/odb/mysql/polymorphic-object-result.hxx94
-rw-r--r--libodb-mysql/odb/mysql/polymorphic-object-result.txx370
-rw-r--r--libodb-mysql/odb/mysql/polymorphic-object-statements.hxx474
-rw-r--r--libodb-mysql/odb/mysql/polymorphic-object-statements.txx145
-rw-r--r--libodb-mysql/odb/mysql/prepared-query.cxx15
-rw-r--r--libodb-mysql/odb/mysql/prepared-query.hxx34
-rw-r--r--libodb-mysql/odb/mysql/query-const-expr.cxx14
-rw-r--r--libodb-mysql/odb/mysql/query-dynamic.cxx157
-rw-r--r--libodb-mysql/odb/mysql/query-dynamic.hxx32
-rw-r--r--libodb-mysql/odb/mysql/query-dynamic.ixx26
-rw-r--r--libodb-mysql/odb/mysql/query-dynamic.txx20
-rw-r--r--libodb-mysql/odb/mysql/query.cxx346
-rw-r--r--libodb-mysql/odb/mysql/query.hxx2277
-rw-r--r--libodb-mysql/odb/mysql/query.ixx34
-rw-r--r--libodb-mysql/odb/mysql/query.txx168
-rw-r--r--libodb-mysql/odb/mysql/section-statements.hxx200
-rw-r--r--libodb-mysql/odb/mysql/section-statements.txx40
-rw-r--r--libodb-mysql/odb/mysql/simple-object-result.hxx85
-rw-r--r--libodb-mysql/odb/mysql/simple-object-result.txx235
-rw-r--r--libodb-mysql/odb/mysql/simple-object-statements.cxx15
-rw-r--r--libodb-mysql/odb/mysql/simple-object-statements.hxx587
-rw-r--r--libodb-mysql/odb/mysql/simple-object-statements.ixx68
-rw-r--r--libodb-mysql/odb/mysql/simple-object-statements.txx146
-rw-r--r--libodb-mysql/odb/mysql/statement-cache.hxx60
-rw-r--r--libodb-mysql/odb/mysql/statement-cache.txx60
-rw-r--r--libodb-mysql/odb/mysql/statement.cxx814
-rw-r--r--libodb-mysql/odb/mysql/statement.hxx327
-rw-r--r--libodb-mysql/odb/mysql/statements-base.cxx15
-rw-r--r--libodb-mysql/odb/mysql/statements-base.hxx63
-rw-r--r--libodb-mysql/odb/mysql/tracer.cxx60
-rw-r--r--libodb-mysql/odb/mysql/tracer.hxx61
-rw-r--r--libodb-mysql/odb/mysql/traits-calls.hxx210
-rw-r--r--libodb-mysql/odb/mysql/traits.cxx145
-rw-r--r--libodb-mysql/odb/mysql/traits.hxx988
-rw-r--r--libodb-mysql/odb/mysql/transaction-impl.cxx108
-rw-r--r--libodb-mysql/odb/mysql/transaction-impl.hxx49
-rw-r--r--libodb-mysql/odb/mysql/transaction.cxx26
-rw-r--r--libodb-mysql/odb/mysql/transaction.hxx88
-rw-r--r--libodb-mysql/odb/mysql/transaction.ixx57
-rw-r--r--libodb-mysql/odb/mysql/version.hxx0
-rw-r--r--libodb-mysql/odb/mysql/version.hxx.in81
-rw-r--r--libodb-mysql/odb/mysql/view-result.hxx81
-rw-r--r--libodb-mysql/odb/mysql/view-result.txx178
-rw-r--r--libodb-mysql/odb/mysql/view-statements.hxx88
-rw-r--r--libodb-mysql/odb/mysql/view-statements.txx33
-rw-r--r--libodb-mysql/tests/.gitignore1
-rw-r--r--libodb-mysql/tests/basics/buildfile6
-rw-r--r--libodb-mysql/tests/basics/driver.cxx37
-rw-r--r--libodb-mysql/tests/build/.gitignore3
-rw-r--r--libodb-mysql/tests/build/bootstrap.build8
-rw-r--r--libodb-mysql/tests/build/root.build23
-rw-r--r--libodb-mysql/tests/buildfile4
-rw-r--r--libodb-oracle/.gitignore25
-rw-r--r--libodb-oracle/INSTALL6
-rw-r--r--libodb-oracle/LICENSE21
-rw-r--r--libodb-oracle/NCUEL294
l---------libodb-oracle/NEWS1
-rw-r--r--libodb-oracle/README20
-rw-r--r--libodb-oracle/build/.gitignore3
-rw-r--r--libodb-oracle/build/bootstrap.build10
-rw-r--r--libodb-oracle/build/export.build9
-rw-r--r--libodb-oracle/build/root.build19
-rw-r--r--libodb-oracle/buildfile9
-rw-r--r--libodb-oracle/manifest42
-rw-r--r--libodb-oracle/odb/oracle/auto-descriptor.cxx27
-rw-r--r--libodb-oracle/odb/oracle/auto-descriptor.hxx127
-rw-r--r--libodb-oracle/odb/oracle/auto-handle.cxx37
-rw-r--r--libodb-oracle/odb/oracle/auto-handle.hxx290
-rw-r--r--libodb-oracle/odb/oracle/binding.hxx66
-rw-r--r--libodb-oracle/odb/oracle/buildfile138
-rw-r--r--libodb-oracle/odb/oracle/connection-factory.cxx159
-rw-r--r--libodb-oracle/odb/oracle/connection-factory.hxx134
-rw-r--r--libodb-oracle/odb/oracle/connection.cxx175
-rw-r--r--libodb-oracle/odb/oracle/connection.hxx189
-rw-r--r--libodb-oracle/odb/oracle/connection.ixx44
-rw-r--r--libodb-oracle/odb/oracle/container-statements.hxx345
-rw-r--r--libodb-oracle/odb/oracle/container-statements.txx96
-rw-r--r--libodb-oracle/odb/oracle/database.cxx352
-rw-r--r--libodb-oracle/odb/oracle/database.hxx542
-rw-r--r--libodb-oracle/odb/oracle/database.ixx640
-rw-r--r--libodb-oracle/odb/oracle/details/.gitignore1
-rw-r--r--libodb-oracle/odb/oracle/details/config.hxx15
-rw-r--r--libodb-oracle/odb/oracle/details/conversion.hxx58
-rw-r--r--libodb-oracle/odb/oracle/details/date.hxx59
-rw-r--r--libodb-oracle/odb/oracle/details/export.hxx50
-rw-r--r--libodb-oracle/odb/oracle/details/number.cxx269
-rw-r--r--libodb-oracle/odb/oracle/details/number.hxx44
-rw-r--r--libodb-oracle/odb/oracle/details/options.cli61
-rw-r--r--libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.cxx1125
-rw-r--r--libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.hxx570
-rw-r--r--libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.ixx384
-rw-r--r--libodb-oracle/odb/oracle/error.cxx225
-rw-r--r--libodb-oracle/odb/oracle/error.hxx41
-rw-r--r--libodb-oracle/odb/oracle/exceptions.cxx124
-rw-r--r--libodb-oracle/odb/oracle/exceptions.hxx135
-rw-r--r--libodb-oracle/odb/oracle/forward.hxx92
-rw-r--r--libodb-oracle/odb/oracle/no-id-object-result.hxx84
-rw-r--r--libodb-oracle/odb/oracle/no-id-object-result.txx149
-rw-r--r--libodb-oracle/odb/oracle/no-id-object-statements.hxx134
-rw-r--r--libodb-oracle/odb/oracle/no-id-object-statements.txx39
-rw-r--r--libodb-oracle/odb/oracle/oracle-fwd.hxx35
-rw-r--r--libodb-oracle/odb/oracle/oracle-types.cxx374
-rw-r--r--libodb-oracle/odb/oracle/oracle-types.hxx300
-rw-r--r--libodb-oracle/odb/oracle/polymorphic-object-result.hxx98
-rw-r--r--libodb-oracle/odb/oracle/polymorphic-object-result.txx320
-rw-r--r--libodb-oracle/odb/oracle/polymorphic-object-statements.hxx462
-rw-r--r--libodb-oracle/odb/oracle/polymorphic-object-statements.txx137
-rw-r--r--libodb-oracle/odb/oracle/prepared-query.cxx15
-rw-r--r--libodb-oracle/odb/oracle/prepared-query.hxx34
-rw-r--r--libodb-oracle/odb/oracle/query-const-expr.cxx14
-rw-r--r--libodb-oracle/odb/oracle/query-dynamic.cxx163
-rw-r--r--libodb-oracle/odb/oracle/query-dynamic.hxx32
-rw-r--r--libodb-oracle/odb/oracle/query-dynamic.ixx72
-rw-r--r--libodb-oracle/odb/oracle/query-dynamic.txx25
-rw-r--r--libodb-oracle/odb/oracle/query.cxx356
-rw-r--r--libodb-oracle/odb/oracle/query.hxx2261
-rw-r--r--libodb-oracle/odb/oracle/query.ixx34
-rw-r--r--libodb-oracle/odb/oracle/query.txx169
-rw-r--r--libodb-oracle/odb/oracle/section-statements.hxx195
-rw-r--r--libodb-oracle/odb/oracle/section-statements.txx49
-rw-r--r--libodb-oracle/odb/oracle/simple-object-result.hxx88
-rw-r--r--libodb-oracle/odb/oracle/simple-object-result.txx181
-rw-r--r--libodb-oracle/odb/oracle/simple-object-statements.cxx15
-rw-r--r--libodb-oracle/odb/oracle/simple-object-statements.hxx594
-rw-r--r--libodb-oracle/odb/oracle/simple-object-statements.ixx68
-rw-r--r--libodb-oracle/odb/oracle/simple-object-statements.txx164
-rw-r--r--libodb-oracle/odb/oracle/statement-cache.hxx59
-rw-r--r--libodb-oracle/odb/oracle/statement-cache.txx60
-rw-r--r--libodb-oracle/odb/oracle/statement.cxx2055
-rw-r--r--libodb-oracle/odb/oracle/statement.hxx538
-rw-r--r--libodb-oracle/odb/oracle/statement.ixx62
-rw-r--r--libodb-oracle/odb/oracle/statements-base.cxx15
-rw-r--r--libodb-oracle/odb/oracle/statements-base.hxx63
-rw-r--r--libodb-oracle/odb/oracle/tracer.cxx60
-rw-r--r--libodb-oracle/odb/oracle/tracer.hxx61
-rw-r--r--libodb-oracle/odb/oracle/traits-calls.hxx190
-rw-r--r--libodb-oracle/odb/oracle/traits.cxx201
-rw-r--r--libodb-oracle/odb/oracle/traits.hxx1491
-rw-r--r--libodb-oracle/odb/oracle/traits.txx130
-rw-r--r--libodb-oracle/odb/oracle/transaction-impl.cxx160
-rw-r--r--libodb-oracle/odb/oracle/transaction-impl.hxx50
-rw-r--r--libodb-oracle/odb/oracle/transaction.cxx26
-rw-r--r--libodb-oracle/odb/oracle/transaction.hxx88
-rw-r--r--libodb-oracle/odb/oracle/transaction.ixx57
-rw-r--r--libodb-oracle/odb/oracle/version.hxx0
-rw-r--r--libodb-oracle/odb/oracle/version.hxx.in61
-rw-r--r--libodb-oracle/odb/oracle/view-result.hxx84
-rw-r--r--libodb-oracle/odb/oracle/view-result.txx148
-rw-r--r--libodb-oracle/odb/oracle/view-statements.hxx81
-rw-r--r--libodb-oracle/odb/oracle/view-statements.txx30
-rw-r--r--libodb-oracle/tests/.gitignore1
-rw-r--r--libodb-oracle/tests/basics/buildfile6
-rw-r--r--libodb-oracle/tests/basics/driver.cxx37
-rw-r--r--libodb-oracle/tests/build/.gitignore3
-rw-r--r--libodb-oracle/tests/build/bootstrap.build8
-rw-r--r--libodb-oracle/tests/build/root.build23
-rw-r--r--libodb-oracle/tests/buildfile4
-rw-r--r--libodb-pgsql/.gitignore25
-rw-r--r--libodb-pgsql/GPLv2340
-rw-r--r--libodb-pgsql/INSTALL6
-rw-r--r--libodb-pgsql/LICENSE20
l---------libodb-pgsql/NEWS1
-rw-r--r--libodb-pgsql/README20
-rw-r--r--libodb-pgsql/build/.gitignore3
-rw-r--r--libodb-pgsql/build/bootstrap.build10
-rw-r--r--libodb-pgsql/build/export.build9
-rw-r--r--libodb-pgsql/build/root.build19
-rw-r--r--libodb-pgsql/buildfile9
-rw-r--r--libodb-pgsql/manifest172
-rw-r--r--libodb-pgsql/odb/pgsql/auto-handle.cxx24
-rw-r--r--libodb-pgsql/odb/pgsql/auto-handle.hxx90
-rw-r--r--libodb-pgsql/odb/pgsql/binding.hxx74
-rw-r--r--libodb-pgsql/odb/pgsql/buildfile132
-rw-r--r--libodb-pgsql/odb/pgsql/connection-factory.cxx159
-rw-r--r--libodb-pgsql/odb/pgsql/connection-factory.hxx134
-rw-r--r--libodb-pgsql/odb/pgsql/connection.cxx141
-rw-r--r--libodb-pgsql/odb/pgsql/connection.hxx183
-rw-r--r--libodb-pgsql/odb/pgsql/connection.ixx44
-rw-r--r--libodb-pgsql/odb/pgsql/container-statements.hxx425
-rw-r--r--libodb-pgsql/odb/pgsql/container-statements.txx154
-rw-r--r--libodb-pgsql/odb/pgsql/database.cxx424
-rw-r--r--libodb-pgsql/odb/pgsql/database.hxx527
-rw-r--r--libodb-pgsql/odb/pgsql/database.ixx638
-rw-r--r--libodb-pgsql/odb/pgsql/details/.gitignore1
-rw-r--r--libodb-pgsql/odb/pgsql/details/config.hxx15
-rw-r--r--libodb-pgsql/odb/pgsql/details/conversion.hxx58
-rw-r--r--libodb-pgsql/odb/pgsql/details/endian-traits.cxx30
-rw-r--r--libodb-pgsql/odb/pgsql/details/endian-traits.hxx155
-rw-r--r--libodb-pgsql/odb/pgsql/details/export.hxx50
-rw-r--r--libodb-pgsql/odb/pgsql/details/options.cli56
-rw-r--r--libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.cxx1114
-rw-r--r--libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.hxx562
-rw-r--r--libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.ixx372
-rw-r--r--libodb-pgsql/odb/pgsql/error.cxx86
-rw-r--r--libodb-pgsql/odb/pgsql/error.hxx41
-rw-r--r--libodb-pgsql/odb/pgsql/error.ixx31
-rw-r--r--libodb-pgsql/odb/pgsql/exceptions.cxx79
-rw-r--r--libodb-pgsql/odb/pgsql/exceptions.hxx80
-rw-r--r--libodb-pgsql/odb/pgsql/forward.hxx91
-rw-r--r--libodb-pgsql/odb/pgsql/no-id-object-result.hxx77
-rw-r--r--libodb-pgsql/odb/pgsql/no-id-object-result.txx114
-rw-r--r--libodb-pgsql/odb/pgsql/no-id-object-statements.hxx147
-rw-r--r--libodb-pgsql/odb/pgsql/no-id-object-statements.txx50
-rw-r--r--libodb-pgsql/odb/pgsql/pgsql-fwd.hxx19
-rw-r--r--libodb-pgsql/odb/pgsql/pgsql-oid.hxx47
-rw-r--r--libodb-pgsql/odb/pgsql/pgsql-types.hxx59
-rw-r--r--libodb-pgsql/odb/pgsql/polymorphic-object-result.hxx94
-rw-r--r--libodb-pgsql/odb/pgsql/polymorphic-object-result.txx287
-rw-r--r--libodb-pgsql/odb/pgsql/polymorphic-object-statements.hxx513
-rw-r--r--libodb-pgsql/odb/pgsql/polymorphic-object-statements.txx158
-rw-r--r--libodb-pgsql/odb/pgsql/prepared-query.cxx15
-rw-r--r--libodb-pgsql/odb/pgsql/prepared-query.hxx34
-rw-r--r--libodb-pgsql/odb/pgsql/query-const-expr.cxx14
-rw-r--r--libodb-pgsql/odb/pgsql/query-dynamic.cxx157
-rw-r--r--libodb-pgsql/odb/pgsql/query-dynamic.hxx32
-rw-r--r--libodb-pgsql/odb/pgsql/query-dynamic.ixx26
-rw-r--r--libodb-pgsql/odb/pgsql/query-dynamic.txx20
-rw-r--r--libodb-pgsql/odb/pgsql/query.cxx444
-rw-r--r--libodb-pgsql/odb/pgsql/query.hxx2181
-rw-r--r--libodb-pgsql/odb/pgsql/query.ixx34
-rw-r--r--libodb-pgsql/odb/pgsql/query.txx168
-rw-r--r--libodb-pgsql/odb/pgsql/section-statements.hxx220
-rw-r--r--libodb-pgsql/odb/pgsql/section-statements.txx51
-rw-r--r--libodb-pgsql/odb/pgsql/simple-object-result.hxx85
-rw-r--r--libodb-pgsql/odb/pgsql/simple-object-result.txx158
-rw-r--r--libodb-pgsql/odb/pgsql/simple-object-statements.cxx15
-rw-r--r--libodb-pgsql/odb/pgsql/simple-object-statements.hxx657
-rw-r--r--libodb-pgsql/odb/pgsql/simple-object-statements.ixx68
-rw-r--r--libodb-pgsql/odb/pgsql/simple-object-statements.txx190
-rw-r--r--libodb-pgsql/odb/pgsql/statement-cache.hxx57
-rw-r--r--libodb-pgsql/odb/pgsql/statement-cache.txx60
-rw-r--r--libodb-pgsql/odb/pgsql/statement.cxx1548
-rw-r--r--libodb-pgsql/odb/pgsql/statement.hxx450
-rw-r--r--libodb-pgsql/odb/pgsql/statements-base.cxx15
-rw-r--r--libodb-pgsql/odb/pgsql/statements-base.hxx63
-rw-r--r--libodb-pgsql/odb/pgsql/tracer.cxx60
-rw-r--r--libodb-pgsql/odb/pgsql/tracer.hxx61
-rw-r--r--libodb-pgsql/odb/pgsql/traits-calls.hxx214
-rw-r--r--libodb-pgsql/odb/pgsql/traits.cxx143
-rw-r--r--libodb-pgsql/odb/pgsql/traits.hxx944
-rw-r--r--libodb-pgsql/odb/pgsql/transaction-impl.cxx107
-rw-r--r--libodb-pgsql/odb/pgsql/transaction-impl.hxx49
-rw-r--r--libodb-pgsql/odb/pgsql/transaction.cxx26
-rw-r--r--libodb-pgsql/odb/pgsql/transaction.hxx88
-rw-r--r--libodb-pgsql/odb/pgsql/transaction.ixx57
-rw-r--r--libodb-pgsql/odb/pgsql/version.hxx0
-rw-r--r--libodb-pgsql/odb/pgsql/version.hxx.in61
-rw-r--r--libodb-pgsql/odb/pgsql/view-result.hxx77
-rw-r--r--libodb-pgsql/odb/pgsql/view-result.txx114
-rw-r--r--libodb-pgsql/odb/pgsql/view-statements.hxx88
-rw-r--r--libodb-pgsql/odb/pgsql/view-statements.txx33
-rw-r--r--libodb-pgsql/tests/.gitignore1
-rw-r--r--libodb-pgsql/tests/basics/buildfile6
-rw-r--r--libodb-pgsql/tests/basics/driver.cxx37
-rw-r--r--libodb-pgsql/tests/build/.gitignore3
-rw-r--r--libodb-pgsql/tests/build/bootstrap.build8
-rw-r--r--libodb-pgsql/tests/build/root.build23
-rw-r--r--libodb-pgsql/tests/buildfile4
-rw-r--r--libodb-qt/.gitignore25
-rw-r--r--libodb-qt/GPLv2340
-rw-r--r--libodb-qt/INSTALL6
-rw-r--r--libodb-qt/LICENSE20
l---------libodb-qt/NEWS1
-rw-r--r--libodb-qt/README20
-rw-r--r--libodb-qt/build/.gitignore3
-rw-r--r--libodb-qt/build/bootstrap.build10
-rw-r--r--libodb-qt/build/export.build9
-rw-r--r--libodb-qt/build/root.build17
-rw-r--r--libodb-qt/buildfile9
-rw-r--r--libodb-qt/manifest25
-rw-r--r--libodb-qt/odb/qt.options7
-rw-r--r--libodb-qt/odb/qt/basic.options4
-rw-r--r--libodb-qt/odb/qt/basic/basic-common.options4
-rw-r--r--libodb-qt/odb/qt/basic/basic-mssql.options13
-rw-r--r--libodb-qt/odb/qt/basic/basic-mysql.options13
-rw-r--r--libodb-qt/odb/qt/basic/basic-oracle.options13
-rw-r--r--libodb-qt/odb/qt/basic/basic-pgsql.options13
-rw-r--r--libodb-qt/odb/qt/basic/basic-sqlite.options13
-rw-r--r--libodb-qt/odb/qt/basic/mssql/default-mapping.hxx29
-rw-r--r--libodb-qt/odb/qt/basic/mssql/qbyte-array-traits.hxx177
-rw-r--r--libodb-qt/odb/qt/basic/mssql/qstring-traits.hxx372
-rw-r--r--libodb-qt/odb/qt/basic/mssql/quuid-traits.hxx58
-rw-r--r--libodb-qt/odb/qt/basic/mysql/default-mapping.hxx28
-rw-r--r--libodb-qt/odb/qt/basic/mysql/qbyte-array-traits.hxx78
-rw-r--r--libodb-qt/odb/qt/basic/mysql/qstring-traits.hxx93
-rw-r--r--libodb-qt/odb/qt/basic/mysql/quuid-traits.hxx74
-rw-r--r--libodb-qt/odb/qt/basic/oracle/default-mapping.hxx27
-rw-r--r--libodb-qt/odb/qt/basic/oracle/qbyte-array-traits.hxx167
-rw-r--r--libodb-qt/odb/qt/basic/oracle/qstring-traits.hxx206
-rw-r--r--libodb-qt/odb/qt/basic/oracle/quuid-traits.hxx70
-rw-r--r--libodb-qt/odb/qt/basic/pgsql/default-mapping.hxx27
-rw-r--r--libodb-qt/odb/qt/basic/pgsql/qbyte-array-traits.hxx77
-rw-r--r--libodb-qt/odb/qt/basic/pgsql/qstring-traits.hxx74
-rw-r--r--libodb-qt/odb/qt/basic/pgsql/quuid-traits.hxx65
-rw-r--r--libodb-qt/odb/qt/basic/sqlite/default-mapping.hxx27
-rw-r--r--libodb-qt/odb/qt/basic/sqlite/qbyte-array-traits.hxx77
-rw-r--r--libodb-qt/odb/qt/basic/sqlite/qstring-traits.hxx83
-rw-r--r--libodb-qt/odb/qt/basic/sqlite/quuid-traits.hxx74
-rw-r--r--libodb-qt/odb/qt/buildfile65
-rw-r--r--libodb-qt/odb/qt/containers.options18
-rw-r--r--libodb-qt/odb/qt/containers/list-iterator.hxx30
-rw-r--r--libodb-qt/odb/qt/containers/list-traits.hxx107
-rw-r--r--libodb-qt/odb/qt/containers/list-traits.txx101
-rw-r--r--libodb-qt/odb/qt/containers/list.hxx433
-rw-r--r--libodb-qt/odb/qt/containers/list.ixx318
-rw-r--r--libodb-qt/odb/qt/containers/mutable-list-iterator.hxx110
-rw-r--r--libodb-qt/odb/qt/containers/qhash-traits.hxx129
-rw-r--r--libodb-qt/odb/qt/containers/qlinked-list-traits.hxx82
-rw-r--r--libodb-qt/odb/qt/containers/qlist-traits.hxx72
-rw-r--r--libodb-qt/odb/qt/containers/qmap-traits.hxx130
-rw-r--r--libodb-qt/odb/qt/containers/qset-traits.hxx69
-rw-r--r--libodb-qt/odb/qt/containers/qvector-traits.hxx82
-rw-r--r--libodb-qt/odb/qt/date-time.options4
-rw-r--r--libodb-qt/odb/qt/date-time/date-time-common.options4
-rw-r--r--libodb-qt/odb/qt/date-time/date-time-mssql.options13
-rw-r--r--libodb-qt/odb/qt/date-time/date-time-mysql.options13
-rw-r--r--libodb-qt/odb/qt/date-time/date-time-oracle.options13
-rw-r--r--libodb-qt/odb/qt/date-time/date-time-pgsql.options13
-rw-r--r--libodb-qt/odb/qt/date-time/date-time-sqlite.options13
-rw-r--r--libodb-qt/odb/qt/date-time/exceptions.cxx25
-rw-r--r--libodb-qt/odb/qt/date-time/exceptions.hxx34
-rw-r--r--libodb-qt/odb/qt/date-time/mssql/default-mapping.hxx31
-rw-r--r--libodb-qt/odb/qt/date-time/mssql/qdate-time-traits.hxx104
-rw-r--r--libodb-qt/odb/qt/date-time/mssql/qdate-traits.hxx63
-rw-r--r--libodb-qt/odb/qt/date-time/mssql/qtime-traits.hxx80
-rw-r--r--libodb-qt/odb/qt/date-time/mysql/default-mapping.hxx26
-rw-r--r--libodb-qt/odb/qt/date-time/mysql/qdate-time-traits.hxx135
-rw-r--r--libodb-qt/odb/qt/date-time/mysql/qdate-traits.hxx73
-rw-r--r--libodb-qt/odb/qt/date-time/mysql/qtime-traits.hxx74
-rw-r--r--libodb-qt/odb/qt/date-time/oracle/default-mapping.hxx29
-rw-r--r--libodb-qt/odb/qt/date-time/oracle/qdate-time-traits.hxx136
-rw-r--r--libodb-qt/odb/qt/date-time/oracle/qdate-traits.hxx76
-rw-r--r--libodb-qt/odb/qt/date-time/oracle/qtime-traits.hxx66
-rw-r--r--libodb-qt/odb/qt/date-time/pgsql/default-mapping.hxx26
-rw-r--r--libodb-qt/odb/qt/date-time/pgsql/qdate-time-traits.hxx70
-rw-r--r--libodb-qt/odb/qt/date-time/pgsql/qdate-traits.hxx70
-rw-r--r--libodb-qt/odb/qt/date-time/pgsql/qtime-traits.hxx73
-rw-r--r--libodb-qt/odb/qt/date-time/sqlite/default-mapping.hxx26
-rw-r--r--libodb-qt/odb/qt/date-time/sqlite/qdate-time-traits.hxx138
-rw-r--r--libodb-qt/odb/qt/date-time/sqlite/qdate-traits.hxx141
-rw-r--r--libodb-qt/odb/qt/date-time/sqlite/qtime-traits.hxx120
-rw-r--r--libodb-qt/odb/qt/details/config.hxx32
-rw-r--r--libodb-qt/odb/qt/details/export.hxx46
-rw-r--r--libodb-qt/odb/qt/exception.hxx28
-rw-r--r--libodb-qt/odb/qt/lazy-ptr.hxx9
-rw-r--r--libodb-qt/odb/qt/list-iterator.hxx9
-rw-r--r--libodb-qt/odb/qt/list.hxx9
-rw-r--r--libodb-qt/odb/qt/mutable-list-iterator.hxx9
-rw-r--r--libodb-qt/odb/qt/smart-ptr.options19
-rw-r--r--libodb-qt/odb/qt/smart-ptr/lazy-pointer-traits.hxx61
-rw-r--r--libodb-qt/odb/qt/smart-ptr/lazy-ptr.hxx442
-rw-r--r--libodb-qt/odb/qt/smart-ptr/lazy-ptr.ixx626
-rw-r--r--libodb-qt/odb/qt/smart-ptr/lazy-ptr.txx74
-rw-r--r--libodb-qt/odb/qt/smart-ptr/pointer-traits.hxx110
-rw-r--r--libodb-qt/odb/qt/smart-ptr/wrapper-traits.hxx64
-rw-r--r--libodb-qt/odb/qt/version.hxx0
-rw-r--r--libodb-qt/odb/qt/version.hxx.in75
-rw-r--r--libodb-qt/odb/qt/version.options16
-rw-r--r--libodb-qt/tests/.gitignore1
-rw-r--r--libodb-qt/tests/basics/buildfile6
-rw-r--r--libodb-qt/tests/basics/driver.cxx22
-rw-r--r--libodb-qt/tests/build/.gitignore3
-rw-r--r--libodb-qt/tests/build/bootstrap.build8
-rw-r--r--libodb-qt/tests/build/root.build23
-rw-r--r--libodb-qt/tests/buildfile4
-rw-r--r--libodb-sqlite/.gitignore25
-rw-r--r--libodb-sqlite/GPLv2340
-rw-r--r--libodb-sqlite/INSTALL6
-rw-r--r--libodb-sqlite/LICENSE20
l---------libodb-sqlite/NEWS1
-rw-r--r--libodb-sqlite/README20
-rw-r--r--libodb-sqlite/build/.gitignore3
-rw-r--r--libodb-sqlite/build/bootstrap.build10
-rw-r--r--libodb-sqlite/build/export.build9
-rw-r--r--libodb-sqlite/build/root.build19
-rw-r--r--libodb-sqlite/buildfile9
-rw-r--r--libodb-sqlite/manifest125
-rw-r--r--libodb-sqlite/odb/sqlite/auto-handle.hxx101
-rw-r--r--libodb-sqlite/odb/sqlite/binding.hxx44
-rw-r--r--libodb-sqlite/odb/sqlite/blob-stream.hxx31
-rw-r--r--libodb-sqlite/odb/sqlite/blob.hxx68
-rw-r--r--libodb-sqlite/odb/sqlite/buildfile132
-rw-r--r--libodb-sqlite/odb/sqlite/connection-factory.cxx417
-rw-r--r--libodb-sqlite/odb/sqlite/connection-factory.hxx273
-rw-r--r--libodb-sqlite/odb/sqlite/connection.cxx310
-rw-r--r--libodb-sqlite/odb/sqlite/connection.hxx356
-rw-r--r--libodb-sqlite/odb/sqlite/connection.ixx108
-rw-r--r--libodb-sqlite/odb/sqlite/container-statements.hxx355
-rw-r--r--libodb-sqlite/odb/sqlite/container-statements.txx107
-rw-r--r--libodb-sqlite/odb/sqlite/database.cxx306
-rw-r--r--libodb-sqlite/odb/sqlite/database.hxx567
-rw-r--r--libodb-sqlite/odb/sqlite/database.ixx622
-rw-r--r--libodb-sqlite/odb/sqlite/details/.gitignore1
-rw-r--r--libodb-sqlite/odb/sqlite/details/config.hxx23
-rw-r--r--libodb-sqlite/odb/sqlite/details/conversion.hxx58
-rw-r--r--libodb-sqlite/odb/sqlite/details/export.hxx50
-rw-r--r--libodb-sqlite/odb/sqlite/details/options.cli47
-rw-r--r--libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.cxx1027
-rw-r--r--libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.hxx530
-rw-r--r--libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.ixx324
-rw-r--r--libodb-sqlite/odb/sqlite/error.cxx94
-rw-r--r--libodb-sqlite/odb/sqlite/error.hxx27
-rw-r--r--libodb-sqlite/odb/sqlite/exceptions.cxx92
-rw-r--r--libodb-sqlite/odb/sqlite/exceptions.hxx101
-rw-r--r--libodb-sqlite/odb/sqlite/forward.hxx112
-rw-r--r--libodb-sqlite/odb/sqlite/no-id-object-result.hxx80
-rw-r--r--libodb-sqlite/odb/sqlite/no-id-object-result.txx114
-rw-r--r--libodb-sqlite/odb/sqlite/no-id-object-statements.hxx136
-rw-r--r--libodb-sqlite/odb/sqlite/no-id-object-statements.txx36
-rw-r--r--libodb-sqlite/odb/sqlite/polymorphic-object-result.hxx98
-rw-r--r--libodb-sqlite/odb/sqlite/polymorphic-object-result.txx287
-rw-r--r--libodb-sqlite/odb/sqlite/polymorphic-object-statements.hxx479
-rw-r--r--libodb-sqlite/odb/sqlite/polymorphic-object-statements.txx145
-rw-r--r--libodb-sqlite/odb/sqlite/prepared-query.cxx27
-rw-r--r--libodb-sqlite/odb/sqlite/prepared-query.hxx37
-rw-r--r--libodb-sqlite/odb/sqlite/query-const-expr.cxx14
-rw-r--r--libodb-sqlite/odb/sqlite/query-dynamic.cxx157
-rw-r--r--libodb-sqlite/odb/sqlite/query-dynamic.hxx32
-rw-r--r--libodb-sqlite/odb/sqlite/query-dynamic.ixx26
-rw-r--r--libodb-sqlite/odb/sqlite/query-dynamic.txx20
-rw-r--r--libodb-sqlite/odb/sqlite/query.cxx378
-rw-r--r--libodb-sqlite/odb/sqlite/query.hxx1728
-rw-r--r--libodb-sqlite/odb/sqlite/query.ixx46
-rw-r--r--libodb-sqlite/odb/sqlite/query.txx168
-rw-r--r--libodb-sqlite/odb/sqlite/section-statements.hxx198
-rw-r--r--libodb-sqlite/odb/sqlite/section-statements.txx40
-rw-r--r--libodb-sqlite/odb/sqlite/simple-object-result.hxx88
-rw-r--r--libodb-sqlite/odb/sqlite/simple-object-result.txx158
-rw-r--r--libodb-sqlite/odb/sqlite/simple-object-statements.cxx15
-rw-r--r--libodb-sqlite/odb/sqlite/simple-object-statements.hxx591
-rw-r--r--libodb-sqlite/odb/sqlite/simple-object-statements.ixx68
-rw-r--r--libodb-sqlite/odb/sqlite/simple-object-statements.txx146
-rw-r--r--libodb-sqlite/odb/sqlite/sqlite-types.hxx57
-rw-r--r--libodb-sqlite/odb/sqlite/statement-cache.hxx60
-rw-r--r--libodb-sqlite/odb/sqlite/statement-cache.txx60
-rw-r--r--libodb-sqlite/odb/sqlite/statement.cxx988
-rw-r--r--libodb-sqlite/odb/sqlite/statement.hxx394
-rw-r--r--libodb-sqlite/odb/sqlite/statements-base.cxx15
-rw-r--r--libodb-sqlite/odb/sqlite/statements-base.hxx63
-rw-r--r--libodb-sqlite/odb/sqlite/stream.cxx120
-rw-r--r--libodb-sqlite/odb/sqlite/stream.hxx85
-rw-r--r--libodb-sqlite/odb/sqlite/text-stream.hxx31
-rw-r--r--libodb-sqlite/odb/sqlite/text.hxx69
-rw-r--r--libodb-sqlite/odb/sqlite/tracer.cxx60
-rw-r--r--libodb-sqlite/odb/sqlite/tracer.hxx61
-rw-r--r--libodb-sqlite/odb/sqlite/traits-calls.hxx214
-rw-r--r--libodb-sqlite/odb/sqlite/traits.cxx219
-rw-r--r--libodb-sqlite/odb/sqlite/traits.hxx1099
-rw-r--r--libodb-sqlite/odb/sqlite/transaction-impl.cxx172
-rw-r--r--libodb-sqlite/odb/sqlite/transaction-impl.hxx66
-rw-r--r--libodb-sqlite/odb/sqlite/transaction.cxx26
-rw-r--r--libodb-sqlite/odb/sqlite/transaction.hxx87
-rw-r--r--libodb-sqlite/odb/sqlite/transaction.ixx57
-rw-r--r--libodb-sqlite/odb/sqlite/version.hxx0
-rw-r--r--libodb-sqlite/odb/sqlite/version.hxx.in61
-rw-r--r--libodb-sqlite/odb/sqlite/view-result.hxx80
-rw-r--r--libodb-sqlite/odb/sqlite/view-result.txx114
-rw-r--r--libodb-sqlite/odb/sqlite/view-statements.hxx88
-rw-r--r--libodb-sqlite/odb/sqlite/view-statements.txx33
-rw-r--r--libodb-sqlite/tests/.gitignore1
-rw-r--r--libodb-sqlite/tests/basics/buildfile6
-rw-r--r--libodb-sqlite/tests/basics/driver.cxx52
-rw-r--r--libodb-sqlite/tests/build/.gitignore3
-rw-r--r--libodb-sqlite/tests/build/bootstrap.build8
-rw-r--r--libodb-sqlite/tests/build/root.build23
-rw-r--r--libodb-sqlite/tests/buildfile4
-rw-r--r--libodb/.gitignore25
-rw-r--r--libodb/GPLv2340
-rw-r--r--libodb/INSTALL6
-rw-r--r--libodb/LICENSE20
l---------libodb/NEWS1
-rw-r--r--libodb/README20
-rw-r--r--libodb/build/.gitignore3
-rw-r--r--libodb/build/bootstrap.build10
-rw-r--r--libodb/build/export.build9
-rw-r--r--libodb/build/root.build17
-rw-r--r--libodb/buildfile9
-rw-r--r--libodb/manifest173
-rw-r--r--libodb/odb/buildfile67
-rw-r--r--libodb/odb/c-array-traits.hxx103
-rw-r--r--libodb/odb/cache-traits.hxx182
-rw-r--r--libodb/odb/callback.hxx42
-rw-r--r--libodb/odb/connection.cxx125
-rw-r--r--libodb/odb/connection.hxx228
-rw-r--r--libodb/odb/connection.ixx131
-rw-r--r--libodb/odb/connection.txx44
-rw-r--r--libodb/odb/container-traits.hxx219
-rw-r--r--libodb/odb/core.hxx20
-rw-r--r--libodb/odb/database.cxx83
-rw-r--r--libodb/odb/database.hxx657
-rw-r--r--libodb/odb/database.ixx879
-rw-r--r--libodb/odb/database.txx491
-rw-r--r--libodb/odb/details/buffer.cxx35
-rw-r--r--libodb/odb/details/buffer.hxx92
-rw-r--r--libodb/odb/details/c-string.hxx28
-rw-r--r--libodb/odb/details/condition.cxx13
-rw-r--r--libodb/odb/details/condition.hxx68
-rw-r--r--libodb/odb/details/config-vc.h23
-rw-r--r--libodb/odb/details/config.h18
-rw-r--r--libodb/odb/details/config.hxx74
-rw-r--r--libodb/odb/details/exception.hxx21
-rw-r--r--libodb/odb/details/export.hxx46
-rw-r--r--libodb/odb/details/function-wrapper.hxx90
-rw-r--r--libodb/odb/details/function-wrapper.ixx49
-rw-r--r--libodb/odb/details/function-wrapper.txx89
-rw-r--r--libodb/odb/details/lock.cxx13
-rw-r--r--libodb/odb/details/lock.hxx59
-rw-r--r--libodb/odb/details/meta/answer.hxx30
-rw-r--r--libodb/odb/details/meta/class-p.hxx34
-rw-r--r--libodb/odb/details/meta/polymorphic-p.hxx57
-rw-r--r--libodb/odb/details/meta/remove-const-volatile.hxx31
-rw-r--r--libodb/odb/details/meta/remove-const.hxx32
-rw-r--r--libodb/odb/details/meta/remove-pointer.hxx32
-rw-r--r--libodb/odb/details/meta/remove-volatile.hxx32
-rw-r--r--libodb/odb/details/meta/static-assert.hxx32
-rw-r--r--libodb/odb/details/mutex.cxx13
-rw-r--r--libodb/odb/details/mutex.hxx53
-rw-r--r--libodb/odb/details/posix/condition.hxx47
-rw-r--r--libodb/odb/details/posix/condition.ixx38
-rw-r--r--libodb/odb/details/posix/exceptions.cxx22
-rw-r--r--libodb/odb/details/posix/exceptions.hxx38
-rw-r--r--libodb/odb/details/posix/mutex.hxx44
-rw-r--r--libodb/odb/details/posix/mutex.ixx37
-rw-r--r--libodb/odb/details/posix/thread.cxx44
-rw-r--r--libodb/odb/details/posix/thread.hxx41
-rw-r--r--libodb/odb/details/posix/thread.ixx29
-rw-r--r--libodb/odb/details/posix/tls.hxx106
-rw-r--r--libodb/odb/details/posix/tls.ixx20
-rw-r--r--libodb/odb/details/posix/tls.txx121
-rw-r--r--libodb/odb/details/shared-ptr-fwd.hxx24
-rw-r--r--libodb/odb/details/shared-ptr.hxx167
-rw-r--r--libodb/odb/details/shared-ptr/base.cxx83
-rw-r--r--libodb/odb/details/shared-ptr/base.hxx131
-rw-r--r--libodb/odb/details/shared-ptr/base.ixx119
-rw-r--r--libodb/odb/details/shared-ptr/base.txx198
-rw-r--r--libodb/odb/details/shared-ptr/counter-type.hxx23
-rw-r--r--libodb/odb/details/shared-ptr/exception.hxx31
-rw-r--r--libodb/odb/details/thread.cxx22
-rw-r--r--libodb/odb/details/thread.hxx65
-rw-r--r--libodb/odb/details/tls.hxx168
-rw-r--r--libodb/odb/details/transfer-ptr.hxx73
-rw-r--r--libodb/odb/details/type-info.hxx36
-rw-r--r--libodb/odb/details/unique-ptr.hxx95
-rw-r--r--libodb/odb/details/unused.hxx21
-rw-r--r--libodb/odb/details/win32/condition.cxx54
-rw-r--r--libodb/odb/details/win32/condition.hxx52
-rw-r--r--libodb/odb/details/win32/condition.ixx30
-rw-r--r--libodb/odb/details/win32/dll.cxx48
-rw-r--r--libodb/odb/details/win32/exceptions.cxx22
-rw-r--r--libodb/odb/details/win32/exceptions.hxx40
-rw-r--r--libodb/odb/details/win32/init.cxx41
-rw-r--r--libodb/odb/details/win32/init.hxx36
-rw-r--r--libodb/odb/details/win32/lock.hxx49
-rw-r--r--libodb/odb/details/win32/mutex.hxx43
-rw-r--r--libodb/odb/details/win32/mutex.ixx32
-rw-r--r--libodb/odb/details/win32/once-init.hxx23
-rw-r--r--libodb/odb/details/win32/once.cxx26
-rw-r--r--libodb/odb/details/win32/once.hxx50
-rw-r--r--libodb/odb/details/win32/once.ixx42
-rw-r--r--libodb/odb/details/win32/thread.cxx88
-rw-r--r--libodb/odb/details/win32/thread.hxx59
-rw-r--r--libodb/odb/details/win32/tls-init.hxx26
-rw-r--r--libodb/odb/details/win32/tls.cxx245
-rw-r--r--libodb/odb/details/win32/tls.hxx120
-rw-r--r--libodb/odb/details/win32/tls.ixx20
-rw-r--r--libodb/odb/details/win32/tls.txx94
-rw-r--r--libodb/odb/details/win32/windows.hxx33
-rw-r--r--libodb/odb/details/wrapper-p.hxx38
-rw-r--r--libodb/odb/exception.hxx36
-rw-r--r--libodb/odb/exceptions.cxx430
-rw-r--r--libodb/odb/exceptions.hxx523
-rw-r--r--libodb/odb/forward.hxx178
-rw-r--r--libodb/odb/function-table.hxx50
-rw-r--r--libodb/odb/lazy-pointer-traits.hxx141
-rw-r--r--libodb/odb/lazy-ptr-impl.hxx188
-rw-r--r--libodb/odb/lazy-ptr-impl.ixx397
-rw-r--r--libodb/odb/lazy-ptr-impl.txx60
-rw-r--r--libodb/odb/lazy-ptr.hxx681
-rw-r--r--libodb/odb/lazy-ptr.ixx1681
-rw-r--r--libodb/odb/lazy-ptr.txx114
-rw-r--r--libodb/odb/nested-container.hxx218
-rw-r--r--libodb/odb/no-id-object-result.hxx182
-rw-r--r--libodb/odb/no-id-object-result.txx21
-rw-r--r--libodb/odb/no-op-cache-traits.hxx236
-rw-r--r--libodb/odb/nullable.hxx492
-rw-r--r--libodb/odb/object-result.hxx164
-rw-r--r--libodb/odb/pointer-traits.hxx405
-rw-r--r--libodb/odb/polymorphic-info.hxx188
-rw-r--r--libodb/odb/polymorphic-map.hxx276
-rw-r--r--libodb/odb/polymorphic-map.ixx19
-rw-r--r--libodb/odb/polymorphic-map.txx75
-rw-r--r--libodb/odb/polymorphic-object-result.hxx224
-rw-r--r--libodb/odb/polymorphic-object-result.txx70
-rw-r--r--libodb/odb/post.hxx6
-rw-r--r--libodb/odb/pre.hxx23
-rw-r--r--libodb/odb/prepared-query.cxx47
-rw-r--r--libodb/odb/prepared-query.hxx201
-rw-r--r--libodb/odb/query-dynamic.cxx200
-rw-r--r--libodb/odb/query-dynamic.hxx1069
-rw-r--r--libodb/odb/query-dynamic.ixx18
-rw-r--r--libodb/odb/query-dynamic.txx139
-rw-r--r--libodb/odb/query.hxx119
-rw-r--r--libodb/odb/result.cxx40
-rw-r--r--libodb/odb/result.hxx247
-rw-r--r--libodb/odb/result.txx49
-rw-r--r--libodb/odb/schema-catalog-impl.hxx54
-rw-r--r--libodb/odb/schema-catalog.cxx387
-rw-r--r--libodb/odb/schema-catalog.hxx392
-rw-r--r--libodb/odb/schema-version.hxx71
-rw-r--r--libodb/odb/section.cxx27
-rw-r--r--libodb/odb/section.hxx122
-rw-r--r--libodb/odb/session.cxx66
-rw-r--r--libodb/odb/session.hxx217
-rw-r--r--libodb/odb/session.ixx60
-rw-r--r--libodb/odb/session.txx90
-rw-r--r--libodb/odb/simple-object-result.hxx201
-rw-r--r--libodb/odb/simple-object-result.txx58
-rw-r--r--libodb/odb/statement-processing-common.hxx214
-rw-r--r--libodb/odb/statement-processing.cxx685
-rw-r--r--libodb/odb/statement.cxx12
-rw-r--r--libodb/odb/statement.hxx108
-rw-r--r--libodb/odb/std-array-traits.hxx72
-rw-r--r--libodb/odb/std-deque-traits.hxx69
-rw-r--r--libodb/odb/std-forward-list-traits.hxx73
-rw-r--r--libodb/odb/std-list-traits.hxx73
-rw-r--r--libodb/odb/std-map-traits.hxx142
-rw-r--r--libodb/odb/std-set-traits.hxx134
-rw-r--r--libodb/odb/std-unordered-map-traits.hxx133
-rw-r--r--libodb/odb/std-unordered-set-traits.hxx125
-rw-r--r--libodb/odb/std-vector-traits.hxx123
-rw-r--r--libodb/odb/tr1/lazy-pointer-traits.hxx61
-rw-r--r--libodb/odb/tr1/lazy-ptr.hxx267
-rw-r--r--libodb/odb/tr1/lazy-ptr.ixx638
-rw-r--r--libodb/odb/tr1/lazy-ptr.txx43
-rw-r--r--libodb/odb/tr1/memory.hxx37
-rw-r--r--libodb/odb/tr1/pointer-traits.hxx121
-rw-r--r--libodb/odb/tr1/wrapper-traits.hxx76
-rw-r--r--libodb/odb/tracer.cxx95
-rw-r--r--libodb/odb/tracer.hxx36
-rw-r--r--libodb/odb/traits.hxx317
-rw-r--r--libodb/odb/transaction.cxx358
-rw-r--r--libodb/odb/transaction.hxx278
-rw-r--r--libodb/odb/transaction.ixx68
-rw-r--r--libodb/odb/vector-impl.cxx208
-rw-r--r--libodb/odb/vector-impl.hxx221
-rw-r--r--libodb/odb/vector-impl.ixx210
-rw-r--r--libodb/odb/vector-traits.hxx106
-rw-r--r--libodb/odb/vector-traits.txx100
-rw-r--r--libodb/odb/vector.hxx635
-rw-r--r--libodb/odb/vector.ixx359
-rw-r--r--libodb/odb/version.hxx0
-rw-r--r--libodb/odb/version.hxx.in65
-rw-r--r--libodb/odb/view-image.hxx36
-rw-r--r--libodb/odb/view-result.hxx231
-rw-r--r--libodb/odb/view-result.txx39
-rw-r--r--libodb/odb/wrapper-traits.hxx276
-rw-r--r--libodb/tests/.gitignore1
-rw-r--r--libodb/tests/basics/buildfile6
-rw-r--r--libodb/tests/basics/driver.cxx29
-rw-r--r--libodb/tests/build/.gitignore3
-rw-r--r--libodb/tests/build/bootstrap.build8
-rw-r--r--libodb/tests/build/root.build23
-rw-r--r--libodb/tests/buildfile4
-rw-r--r--m4/disable-rpath.m424
-rw-r--r--m4/gcc-plugin.m4133
-rw-r--r--m4/libcutl.m481
-rw-r--r--m4/libtool-link.m445
-rw-r--r--makefile34
-rw-r--r--manifest24
-rw-r--r--odb-tests/.gitignore41
-rw-r--r--odb-tests/GPLv2340
-rw-r--r--odb-tests/LICENSE20
l---------odb-tests/NEWS1
-rw-r--r--odb-tests/README.md82
-rw-r--r--odb-tests/boost/common/multi-index/driver.cxx189
-rw-r--r--odb-tests/boost/common/multi-index/test.hxx113
-rw-r--r--odb-tests/boost/common/optional/driver.cxx74
-rw-r--r--odb-tests/boost/common/optional/test.hxx31
-rw-r--r--odb-tests/boost/common/smart-ptr/driver.cxx202
-rw-r--r--odb-tests/boost/common/smart-ptr/test.hxx82
-rw-r--r--odb-tests/boost/common/template/driver.cxx39
-rw-r--r--odb-tests/boost/common/template/template-vc10.vcxproj177
-rw-r--r--odb-tests/boost/common/template/template-vc10.vcxproj.filters24
-rw-r--r--odb-tests/boost/common/template/template-vc11.vcxproj181
-rw-r--r--odb-tests/boost/common/template/template-vc11.vcxproj.filters24
-rw-r--r--odb-tests/boost/common/template/template-vc12.vcxproj185
-rw-r--r--odb-tests/boost/common/template/template-vc12.vcxproj.filters24
-rw-r--r--odb-tests/boost/common/template/template-vc8.vcproj353
-rw-r--r--odb-tests/boost/common/template/template-vc9.vcproj360
-rw-r--r--odb-tests/boost/common/template/test.hxx25
-rw-r--r--odb-tests/boost/common/unordered/driver.cxx212
-rw-r--r--odb-tests/boost/common/unordered/test.hxx113
-rw-r--r--odb-tests/boost/common/uuid/driver.cxx69
-rw-r--r--odb-tests/boost/common/uuid/test.hxx37
-rw-r--r--odb-tests/boost/mssql/date-time/driver.cxx158
-rw-r--r--odb-tests/boost/mssql/date-time/test.hxx58
-rw-r--r--odb-tests/boost/mssql/template/driver.cxx39
-rw-r--r--odb-tests/boost/mssql/template/template-vc10.vcxproj180
-rw-r--r--odb-tests/boost/mssql/template/template-vc10.vcxproj.filters25
-rw-r--r--odb-tests/boost/mssql/template/template-vc11.vcxproj184
-rw-r--r--odb-tests/boost/mssql/template/template-vc11.vcxproj.filters25
-rw-r--r--odb-tests/boost/mssql/template/template-vc12.vcxproj188
-rw-r--r--odb-tests/boost/mssql/template/template-vc12.vcxproj.filters25
-rw-r--r--odb-tests/boost/mssql/template/template-vc8.vcproj354
-rw-r--r--odb-tests/boost/mssql/template/template-vc9.vcproj361
-rw-r--r--odb-tests/boost/mssql/template/test.hxx25
-rw-r--r--odb-tests/boost/mysql/date-time/driver.cxx218
-rw-r--r--odb-tests/boost/mysql/date-time/test.hxx65
-rw-r--r--odb-tests/boost/mysql/template/driver.cxx39
-rw-r--r--odb-tests/boost/mysql/template/template-vc10.vcxproj180
-rw-r--r--odb-tests/boost/mysql/template/template-vc10.vcxproj.filters25
-rw-r--r--odb-tests/boost/mysql/template/template-vc11.vcxproj184
-rw-r--r--odb-tests/boost/mysql/template/template-vc11.vcxproj.filters25
-rw-r--r--odb-tests/boost/mysql/template/template-vc12.vcxproj188
-rw-r--r--odb-tests/boost/mysql/template/template-vc12.vcxproj.filters25
-rw-r--r--odb-tests/boost/mysql/template/template-vc8.vcproj354
-rw-r--r--odb-tests/boost/mysql/template/template-vc9.vcproj361
-rw-r--r--odb-tests/boost/mysql/template/test.hxx25
-rw-r--r--odb-tests/boost/oracle/date-time/driver.cxx136
-rw-r--r--odb-tests/boost/oracle/date-time/test.hxx42
-rw-r--r--odb-tests/boost/oracle/template/driver.cxx39
-rw-r--r--odb-tests/boost/oracle/template/template-vc10.vcxproj180
-rw-r--r--odb-tests/boost/oracle/template/template-vc10.vcxproj.filters25
-rw-r--r--odb-tests/boost/oracle/template/template-vc11.vcxproj184
-rw-r--r--odb-tests/boost/oracle/template/template-vc11.vcxproj.filters25
-rw-r--r--odb-tests/boost/oracle/template/template-vc12.vcxproj188
-rw-r--r--odb-tests/boost/oracle/template/template-vc12.vcxproj.filters25
-rw-r--r--odb-tests/boost/oracle/template/template-vc8.vcproj354
-rw-r--r--odb-tests/boost/oracle/template/template-vc9.vcproj361
-rw-r--r--odb-tests/boost/oracle/template/test.hxx25
-rw-r--r--odb-tests/boost/pgsql/date-time/driver.cxx157
-rw-r--r--odb-tests/boost/pgsql/date-time/test.hxx39
-rw-r--r--odb-tests/boost/pgsql/template/driver.cxx39
-rw-r--r--odb-tests/boost/pgsql/template/template-vc10.vcxproj180
-rw-r--r--odb-tests/boost/pgsql/template/template-vc10.vcxproj.filters25
-rw-r--r--odb-tests/boost/pgsql/template/template-vc11.vcxproj184
-rw-r--r--odb-tests/boost/pgsql/template/template-vc11.vcxproj.filters25
-rw-r--r--odb-tests/boost/pgsql/template/template-vc12.vcxproj188
-rw-r--r--odb-tests/boost/pgsql/template/template-vc12.vcxproj.filters25
-rw-r--r--odb-tests/boost/pgsql/template/template-vc8.vcproj354
-rw-r--r--odb-tests/boost/pgsql/template/template-vc9.vcproj361
-rw-r--r--odb-tests/boost/pgsql/template/test.hxx25
-rw-r--r--odb-tests/boost/sqlite/date-time/driver.cxx208
-rw-r--r--odb-tests/boost/sqlite/date-time/test.hxx57
-rw-r--r--odb-tests/boost/sqlite/template/driver.cxx39
-rw-r--r--odb-tests/boost/sqlite/template/template-vc10.vcxproj180
-rw-r--r--odb-tests/boost/sqlite/template/template-vc10.vcxproj.filters25
-rw-r--r--odb-tests/boost/sqlite/template/template-vc11.vcxproj184
-rw-r--r--odb-tests/boost/sqlite/template/template-vc11.vcxproj.filters25
-rw-r--r--odb-tests/boost/sqlite/template/template-vc12.vcxproj188
-rw-r--r--odb-tests/boost/sqlite/template/template-vc12.vcxproj.filters25
-rw-r--r--odb-tests/boost/sqlite/template/template-vc8.vcproj354
-rw-r--r--odb-tests/boost/sqlite/template/template-vc9.vcproj361
-rw-r--r--odb-tests/boost/sqlite/template/test.hxx25
-rw-r--r--odb-tests/build/.gitignore3
-rw-r--r--odb-tests/build/bootstrap.build9
-rw-r--r--odb-tests/build/export.build9
-rw-r--r--odb-tests/build/root.build336
-rw-r--r--odb-tests/buildfile12
-rw-r--r--odb-tests/common/access/buildfile45
-rw-r--r--odb-tests/common/access/driver.cxx262
-rw-r--r--odb-tests/common/access/test.hxx592
-rw-r--r--odb-tests/common/access/testscript33
-rw-r--r--odb-tests/common/as/buildfile49
-rw-r--r--odb-tests/common/as/driver.cxx348
-rw-r--r--odb-tests/common/as/test.hxx270
-rw-r--r--odb-tests/common/as/testscript33
-rw-r--r--odb-tests/common/blob/buildfile40
-rw-r--r--odb-tests/common/blob/driver.cxx76
-rw-r--r--odb-tests/common/blob/test.hxx71
-rw-r--r--odb-tests/common/blob/testscript33
-rw-r--r--odb-tests/common/buildfile6
-rw-r--r--odb-tests/common/bulk/buildfile52
-rw-r--r--odb-tests/common/bulk/driver.cxx1203
-rw-r--r--odb-tests/common/bulk/test.hxx211
-rw-r--r--odb-tests/common/bulk/testscript503
-rw-r--r--odb-tests/common/callback/buildfile41
-rw-r--r--odb-tests/common/callback/driver.cxx184
-rw-r--r--odb-tests/common/callback/test.hxx43
-rw-r--r--odb-tests/common/callback/testscript100
-rw-r--r--odb-tests/common/changelog/.gitignore3
-rw-r--r--odb-tests/common/changelog/add-column-mssql-diff.xml16
-rw-r--r--odb-tests/common/changelog/add-column-mssql-patch.xml13
-rw-r--r--odb-tests/common/changelog/add-column-mysql-diff.xml16
-rw-r--r--odb-tests/common/changelog/add-column-mysql-patch.xml13
-rw-r--r--odb-tests/common/changelog/add-column-oracle-diff.xml16
-rw-r--r--odb-tests/common/changelog/add-column-oracle-patch.xml13
-rw-r--r--odb-tests/common/changelog/add-column-pgsql-diff.xml16
-rw-r--r--odb-tests/common/changelog/add-column-pgsql-patch.xml13
-rw-r--r--odb-tests/common/changelog/add-column-sqlite-diff.xml16
-rw-r--r--odb-tests/common/changelog/add-column-sqlite-patch.xml13
-rw-r--r--odb-tests/common/changelog/add-column.hxx20
-rw-r--r--odb-tests/common/changelog/add-foreign-key-diff.xml28
-rw-r--r--odb-tests/common/changelog/add-foreign-key-mssql-diff.xml28
-rw-r--r--odb-tests/common/changelog/add-foreign-key-mssql-patch.xml25
-rw-r--r--odb-tests/common/changelog/add-foreign-key-mysql-diff.xml28
-rw-r--r--odb-tests/common/changelog/add-foreign-key-mysql-patch.xml25
-rw-r--r--odb-tests/common/changelog/add-foreign-key-oracle-diff.xml28
-rw-r--r--odb-tests/common/changelog/add-foreign-key-oracle-patch.xml25
-rw-r--r--odb-tests/common/changelog/add-foreign-key-pgsql-diff.xml28
-rw-r--r--odb-tests/common/changelog/add-foreign-key-pgsql-patch.xml25
-rw-r--r--odb-tests/common/changelog/add-foreign-key-sqlite-diff.xml28
-rw-r--r--odb-tests/common/changelog/add-foreign-key-sqlite-patch.xml25
-rw-r--r--odb-tests/common/changelog/add-foreign-key.hxx29
-rw-r--r--odb-tests/common/changelog/add-index-mssql-diff.xml21
-rw-r--r--odb-tests/common/changelog/add-index-mssql-patch.xml18
-rw-r--r--odb-tests/common/changelog/add-index-mysql-diff.xml21
-rw-r--r--odb-tests/common/changelog/add-index-mysql-patch.xml18
-rw-r--r--odb-tests/common/changelog/add-index-oracle-diff.xml21
-rw-r--r--odb-tests/common/changelog/add-index-oracle-patch.xml18
-rw-r--r--odb-tests/common/changelog/add-index-pgsql-diff.xml21
-rw-r--r--odb-tests/common/changelog/add-index-pgsql-patch.xml18
-rw-r--r--odb-tests/common/changelog/add-index-sqlite-diff.xml21
-rw-r--r--odb-tests/common/changelog/add-index-sqlite-patch.xml18
-rw-r--r--odb-tests/common/changelog/add-index.hxx24
-rw-r--r--odb-tests/common/changelog/add-table-mssql-diff.xml45
-rw-r--r--odb-tests/common/changelog/add-table-mssql-patch.xml44
-rw-r--r--odb-tests/common/changelog/add-table-mysql-diff.xml45
-rw-r--r--odb-tests/common/changelog/add-table-mysql-patch.xml44
-rw-r--r--odb-tests/common/changelog/add-table-oracle-diff.xml45
-rw-r--r--odb-tests/common/changelog/add-table-oracle-patch.xml44
-rw-r--r--odb-tests/common/changelog/add-table-pgsql-diff.xml45
-rw-r--r--odb-tests/common/changelog/add-table-pgsql-patch.xml44
-rw-r--r--odb-tests/common/changelog/add-table-sqlite-diff.xml45
-rw-r--r--odb-tests/common/changelog/add-table-sqlite-patch.xml44
-rw-r--r--odb-tests/common/changelog/add-table.hxx34
-rw-r--r--odb-tests/common/changelog/alter-column-mssql-diff.xml17
-rw-r--r--odb-tests/common/changelog/alter-column-mssql-patch.xml13
-rw-r--r--odb-tests/common/changelog/alter-column-mysql-diff.xml17
-rw-r--r--odb-tests/common/changelog/alter-column-mysql-patch.xml13
-rw-r--r--odb-tests/common/changelog/alter-column-oracle-diff.xml17
-rw-r--r--odb-tests/common/changelog/alter-column-oracle-patch.xml13
-rw-r--r--odb-tests/common/changelog/alter-column-pgsql-diff.xml17
-rw-r--r--odb-tests/common/changelog/alter-column-pgsql-patch.xml13
-rw-r--r--odb-tests/common/changelog/alter-column-sqlite-diff.xml17
-rw-r--r--odb-tests/common/changelog/alter-column-sqlite-patch.xml13
-rw-r--r--odb-tests/common/changelog/alter-column.hxx21
-rw-r--r--odb-tests/common/changelog/buildfile30
-rw-r--r--odb-tests/common/changelog/drop-column-mssql-diff.xml17
-rw-r--r--odb-tests/common/changelog/drop-column-mssql-patch.xml12
-rw-r--r--odb-tests/common/changelog/drop-column-mysql-diff.xml17
-rw-r--r--odb-tests/common/changelog/drop-column-mysql-patch.xml12
-rw-r--r--odb-tests/common/changelog/drop-column-oracle-diff.xml17
-rw-r--r--odb-tests/common/changelog/drop-column-oracle-patch.xml12
-rw-r--r--odb-tests/common/changelog/drop-column-pgsql-diff.xml17
-rw-r--r--odb-tests/common/changelog/drop-column-pgsql-patch.xml12
-rw-r--r--odb-tests/common/changelog/drop-column-sqlite-diff.xml17
-rw-r--r--odb-tests/common/changelog/drop-column-sqlite-patch.xml12
-rw-r--r--odb-tests/common/changelog/drop-column.hxx20
-rw-r--r--odb-tests/common/changelog/drop-foreign-key-mssql-diff.xml30
-rw-r--r--odb-tests/common/changelog/drop-foreign-key-mssql-patch.xml18
-rw-r--r--odb-tests/common/changelog/drop-foreign-key-mysql-diff.xml30
-rw-r--r--odb-tests/common/changelog/drop-foreign-key-mysql-patch.xml18
-rw-r--r--odb-tests/common/changelog/drop-foreign-key-oracle-diff.xml30
-rw-r--r--odb-tests/common/changelog/drop-foreign-key-oracle-patch.xml18
-rw-r--r--odb-tests/common/changelog/drop-foreign-key-pgsql-diff.xml30
-rw-r--r--odb-tests/common/changelog/drop-foreign-key-pgsql-patch.xml18
-rw-r--r--odb-tests/common/changelog/drop-foreign-key-sqlite-diff.xml30
-rw-r--r--odb-tests/common/changelog/drop-foreign-key-sqlite-patch.xml18
-rw-r--r--odb-tests/common/changelog/drop-foreign-key.hxx29
-rw-r--r--odb-tests/common/changelog/drop-index-mssql-diff.xml20
-rw-r--r--odb-tests/common/changelog/drop-index-mssql-patch.xml13
-rw-r--r--odb-tests/common/changelog/drop-index-mysql-diff.xml20
-rw-r--r--odb-tests/common/changelog/drop-index-mysql-patch.xml13
-rw-r--r--odb-tests/common/changelog/drop-index-oracle-diff.xml20
-rw-r--r--odb-tests/common/changelog/drop-index-oracle-patch.xml13
-rw-r--r--odb-tests/common/changelog/drop-index-pgsql-diff.xml20
-rw-r--r--odb-tests/common/changelog/drop-index-pgsql-patch.xml13
-rw-r--r--odb-tests/common/changelog/drop-index-sqlite-diff.xml20
-rw-r--r--odb-tests/common/changelog/drop-index-sqlite-patch.xml13
-rw-r--r--odb-tests/common/changelog/drop-index.hxx21
-rw-r--r--odb-tests/common/changelog/drop-table-mssql-diff.xml47
-rw-r--r--odb-tests/common/changelog/drop-table-mssql-patch.xml13
-rw-r--r--odb-tests/common/changelog/drop-table-mysql-diff.xml47
-rw-r--r--odb-tests/common/changelog/drop-table-mysql-patch.xml13
-rw-r--r--odb-tests/common/changelog/drop-table-oracle-diff.xml47
-rw-r--r--odb-tests/common/changelog/drop-table-oracle-patch.xml13
-rw-r--r--odb-tests/common/changelog/drop-table-pgsql-diff.xml47
-rw-r--r--odb-tests/common/changelog/drop-table-pgsql-patch.xml13
-rw-r--r--odb-tests/common/changelog/drop-table-sqlite-diff.xml47
-rw-r--r--odb-tests/common/changelog/drop-table-sqlite-patch.xml13
-rw-r--r--odb-tests/common/changelog/drop-table.hxx34
l---------odb-tests/common/changelog/model-mssql-diff.xml1
l---------odb-tests/common/changelog/model-mssql-patch.xml1
-rw-r--r--odb-tests/common/changelog/model-mssql.xml56
l---------odb-tests/common/changelog/model-mysql-diff.xml1
l---------odb-tests/common/changelog/model-mysql-patch.xml1
-rw-r--r--odb-tests/common/changelog/model-mysql.xml56
l---------odb-tests/common/changelog/model-oracle-diff.xml1
l---------odb-tests/common/changelog/model-oracle-patch.xml1
-rw-r--r--odb-tests/common/changelog/model-oracle.xml56
l---------odb-tests/common/changelog/model-pgsql-diff.xml1
l---------odb-tests/common/changelog/model-pgsql-patch.xml1
-rw-r--r--odb-tests/common/changelog/model-pgsql.xml56
l---------odb-tests/common/changelog/model-sqlite-diff.xml1
l---------odb-tests/common/changelog/model-sqlite-patch.xml1
-rw-r--r--odb-tests/common/changelog/model-sqlite.xml56
-rw-r--r--odb-tests/common/changelog/model.hxx46
-rw-r--r--odb-tests/common/changelog/testscript66
-rw-r--r--odb-tests/common/circular/buildfile9
-rw-r--r--odb-tests/common/circular/multiple/.gitignore6
-rw-r--r--odb-tests/common/circular/multiple/buildfile49
-rw-r--r--odb-tests/common/circular/multiple/driver.cxx69
-rw-r--r--odb-tests/common/circular/multiple/test1.hxx27
-rw-r--r--odb-tests/common/circular/multiple/test2.hxx17
-rw-r--r--odb-tests/common/circular/multiple/testscript31
-rw-r--r--odb-tests/common/circular/single/buildfile41
-rw-r--r--odb-tests/common/circular/single/driver.cxx40
-rw-r--r--odb-tests/common/circular/single/test.hxx28
-rw-r--r--odb-tests/common/circular/single/testscript33
-rw-r--r--odb-tests/common/composite/buildfile41
-rw-r--r--odb-tests/common/composite/driver.cxx229
-rw-r--r--odb-tests/common/composite/test.hxx250
-rw-r--r--odb-tests/common/composite/testscript33
-rw-r--r--odb-tests/common/const-member/buildfile40
-rw-r--r--odb-tests/common/const-member/driver.cxx119
-rw-r--r--odb-tests/common/const-member/test.hxx109
-rw-r--r--odb-tests/common/const-member/testscript33
-rw-r--r--odb-tests/common/const-object/buildfile41
-rw-r--r--odb-tests/common/const-object/driver.cxx216
-rw-r--r--odb-tests/common/const-object/test.hxx51
-rw-r--r--odb-tests/common/const-object/testscript33
-rw-r--r--odb-tests/common/container/basics/buildfile40
-rw-r--r--odb-tests/common/container/basics/driver.cxx523
-rw-r--r--odb-tests/common/container/basics/test.hxx245
-rw-r--r--odb-tests/common/container/basics/testscript33
-rw-r--r--odb-tests/common/container/change-tracking/buildfile40
-rw-r--r--odb-tests/common/container/change-tracking/driver.cxx729
-rw-r--r--odb-tests/common/container/change-tracking/test.hxx106
-rw-r--r--odb-tests/common/container/change-tracking/testscript33
-rw-r--r--odb-tests/common/ctor/buildfile41
-rw-r--r--odb-tests/common/ctor/driver.cxx79
-rw-r--r--odb-tests/common/ctor/test.hxx29
-rw-r--r--odb-tests/common/ctor/testscript33
-rw-r--r--odb-tests/common/default/buildfile41
-rw-r--r--odb-tests/common/default/driver.cxx81
-rw-r--r--odb-tests/common/default/test.hxx67
-rw-r--r--odb-tests/common/default/testscript33
-rw-r--r--odb-tests/common/definition/.gitignore6
-rw-r--r--odb-tests/common/definition/buildfile52
-rw-r--r--odb-tests/common/definition/driver.cxx56
-rw-r--r--odb-tests/common/definition/test.hxx26
-rw-r--r--odb-tests/common/definition/testscript33
-rw-r--r--odb-tests/common/definition/time-mapping.hxx17
-rw-r--r--odb-tests/common/enum/buildfile41
-rw-r--r--odb-tests/common/enum/driver.cxx84
-rw-r--r--odb-tests/common/enum/test.hxx47
-rw-r--r--odb-tests/common/enum/testscript33
-rw-r--r--odb-tests/common/erase-query/buildfile41
-rw-r--r--odb-tests/common/erase-query/driver.cxx181
-rw-r--r--odb-tests/common/erase-query/test.hxx46
-rw-r--r--odb-tests/common/erase-query/testscript33
-rw-r--r--odb-tests/common/id/auto/buildfile40
-rw-r--r--odb-tests/common/id/auto/driver.cxx96
-rw-r--r--odb-tests/common/id/auto/test.hxx40
-rw-r--r--odb-tests/common/id/auto/testscript33
-rw-r--r--odb-tests/common/id/composite/buildfile42
-rw-r--r--odb-tests/common/id/composite/driver.cxx731
-rw-r--r--odb-tests/common/id/composite/test.hxx519
-rw-r--r--odb-tests/common/id/composite/testscript33
-rw-r--r--odb-tests/common/id/nested/buildfile41
-rw-r--r--odb-tests/common/id/nested/driver.cxx266
-rw-r--r--odb-tests/common/id/nested/test.hxx217
-rw-r--r--odb-tests/common/id/nested/testscript33
-rw-r--r--odb-tests/common/include/.gitignore17
-rw-r--r--odb-tests/common/include/buildfile51
-rw-r--r--odb-tests/common/include/driver.cxx42
-rw-r--r--odb-tests/common/include/obj1.hxx25
-rw-r--r--odb-tests/common/include/obj2.hxx25
-rw-r--r--odb-tests/common/include/obj3.hxx25
-rw-r--r--odb-tests/common/include/objs1.hxx13
-rw-r--r--odb-tests/common/include/objs2.hxx13
-rw-r--r--odb-tests/common/include/objs3.hxx11
-rw-r--r--odb-tests/common/include/objs4.hxx11
-rw-r--r--odb-tests/common/include/test1.hxx16
-rw-r--r--odb-tests/common/include/test2.hxx15
-rw-r--r--odb-tests/common/include/test3.hxx12
-rw-r--r--odb-tests/common/include/test4.hxx12
-rw-r--r--odb-tests/common/include/testscript31
-rw-r--r--odb-tests/common/index/buildfile40
-rw-r--r--odb-tests/common/index/driver.cxx44
-rw-r--r--odb-tests/common/index/test.hxx142
-rw-r--r--odb-tests/common/index/testscript33
-rw-r--r--odb-tests/common/inheritance/polymorphism/.gitignore76
-rw-r--r--odb-tests/common/inheritance/polymorphism/buildfile52
-rw-r--r--odb-tests/common/inheritance/polymorphism/driver.cxx2093
-rw-r--r--odb-tests/common/inheritance/polymorphism/test1.hxx115
-rw-r--r--odb-tests/common/inheritance/polymorphism/test10.hxx78
-rw-r--r--odb-tests/common/inheritance/polymorphism/test11.hxx78
-rw-r--r--odb-tests/common/inheritance/polymorphism/test12.hxx79
-rw-r--r--odb-tests/common/inheritance/polymorphism/test13.hxx46
-rw-r--r--odb-tests/common/inheritance/polymorphism/test14.hxx99
-rw-r--r--odb-tests/common/inheritance/polymorphism/test15.hxx44
-rw-r--r--odb-tests/common/inheritance/polymorphism/test2.hxx105
-rw-r--r--odb-tests/common/inheritance/polymorphism/test3.hxx146
-rw-r--r--odb-tests/common/inheritance/polymorphism/test4.hxx84
-rw-r--r--odb-tests/common/inheritance/polymorphism/test5.hxx92
-rw-r--r--odb-tests/common/inheritance/polymorphism/test6.hxx64
-rw-r--r--odb-tests/common/inheritance/polymorphism/test7.hxx54
-rw-r--r--odb-tests/common/inheritance/polymorphism/test8.hxx129
-rw-r--r--odb-tests/common/inheritance/polymorphism/test9.hxx161
-rw-r--r--odb-tests/common/inheritance/polymorphism/testscript80
-rw-r--r--odb-tests/common/inheritance/reuse/buildfile41
-rw-r--r--odb-tests/common/inheritance/reuse/driver.cxx237
-rw-r--r--odb-tests/common/inheritance/reuse/test.hxx163
-rw-r--r--odb-tests/common/inheritance/reuse/testscript33
-rw-r--r--odb-tests/common/inheritance/transient/buildfile41
-rw-r--r--odb-tests/common/inheritance/transient/driver.cxx80
-rw-r--r--odb-tests/common/inheritance/transient/test.hxx60
-rw-r--r--odb-tests/common/inheritance/transient/testscript33
-rw-r--r--odb-tests/common/inverse/buildfile42
-rw-r--r--odb-tests/common/inverse/driver.cxx502
-rw-r--r--odb-tests/common/inverse/test.hxx391
-rw-r--r--odb-tests/common/inverse/testscript33
-rw-r--r--odb-tests/common/lazy-ptr/buildfile41
-rw-r--r--odb-tests/common/lazy-ptr/driver.cxx360
-rw-r--r--odb-tests/common/lazy-ptr/test.hxx147
-rw-r--r--odb-tests/common/lazy-ptr/testscript33
-rw-r--r--odb-tests/common/lifecycle/buildfile40
-rw-r--r--odb-tests/common/lifecycle/driver.cxx248
-rw-r--r--odb-tests/common/lifecycle/test.hxx27
-rw-r--r--odb-tests/common/lifecycle/testscript33
-rw-r--r--odb-tests/common/no-id/buildfile41
-rw-r--r--odb-tests/common/no-id/driver.cxx102
-rw-r--r--odb-tests/common/no-id/test.hxx21
-rw-r--r--odb-tests/common/no-id/testscript33
-rw-r--r--odb-tests/common/object/buildfile41
-rw-r--r--odb-tests/common/object/driver.cxx84
-rw-r--r--odb-tests/common/object/test.hxx49
-rw-r--r--odb-tests/common/object/testscript33
-rw-r--r--odb-tests/common/optimistic/buildfile41
-rw-r--r--odb-tests/common/optimistic/driver.cxx300
-rw-r--r--odb-tests/common/optimistic/test.hxx76
-rw-r--r--odb-tests/common/optimistic/testscript33
-rw-r--r--odb-tests/common/pragma/buildfile39
-rw-r--r--odb-tests/common/pragma/driver.cxx27
-rw-r--r--odb-tests/common/pragma/test.hxx40
-rw-r--r--odb-tests/common/pragma/testscript31
-rw-r--r--odb-tests/common/prepared/buildfile43
-rw-r--r--odb-tests/common/prepared/driver.cxx444
-rw-r--r--odb-tests/common/prepared/test.hxx32
-rw-r--r--odb-tests/common/prepared/testscript33
-rw-r--r--odb-tests/common/query/array/buildfile43
-rw-r--r--odb-tests/common/query/array/driver.cxx200
-rw-r--r--odb-tests/common/query/array/test.hxx70
-rw-r--r--odb-tests/common/query/array/testscript33
-rw-r--r--odb-tests/common/query/basics/buildfile42
-rw-r--r--odb-tests/common/query/basics/driver.cxx668
-rw-r--r--odb-tests/common/query/basics/test.hxx117
-rw-r--r--odb-tests/common/query/basics/testscript150
-rw-r--r--odb-tests/common/query/one/buildfile42
-rw-r--r--odb-tests/common/query/one/driver.cxx205
-rw-r--r--odb-tests/common/query/one/test.hxx26
-rw-r--r--odb-tests/common/query/one/testscript33
-rw-r--r--odb-tests/common/readonly/buildfile40
-rw-r--r--odb-tests/common/readonly/driver.cxx324
-rw-r--r--odb-tests/common/readonly/test.hxx225
-rw-r--r--odb-tests/common/readonly/testscript33
-rw-r--r--odb-tests/common/relationship/basics/buildfile41
-rw-r--r--odb-tests/common/relationship/basics/driver.cxx150
-rw-r--r--odb-tests/common/relationship/basics/test.hxx260
-rw-r--r--odb-tests/common/relationship/basics/testscript33
-rw-r--r--odb-tests/common/relationship/on-delete/buildfile43
-rw-r--r--odb-tests/common/relationship/on-delete/driver.cxx82
-rw-r--r--odb-tests/common/relationship/on-delete/test.hxx58
-rw-r--r--odb-tests/common/relationship/on-delete/testscript33
-rw-r--r--odb-tests/common/relationship/query/buildfile42
-rw-r--r--odb-tests/common/relationship/query/driver.cxx168
-rw-r--r--odb-tests/common/relationship/query/test.hxx140
-rw-r--r--odb-tests/common/relationship/query/testscript33
-rw-r--r--odb-tests/common/schema/embedded/basics/buildfile42
-rw-r--r--odb-tests/common/schema/embedded/basics/driver.cxx59
-rw-r--r--odb-tests/common/schema/embedded/basics/test.hxx23
-rw-r--r--odb-tests/common/schema/embedded/basics/testscript31
-rw-r--r--odb-tests/common/schema/embedded/order/.gitignore6
-rw-r--r--odb-tests/common/schema/embedded/order/buildfile48
-rw-r--r--odb-tests/common/schema/embedded/order/driver.cxx65
-rw-r--r--odb-tests/common/schema/embedded/order/test1.hxx23
-rw-r--r--odb-tests/common/schema/embedded/order/test2.hxx17
-rw-r--r--odb-tests/common/schema/embedded/order/testscript31
-rw-r--r--odb-tests/common/schema/namespace/buildfile41
-rw-r--r--odb-tests/common/schema/namespace/driver.cxx113
-rw-r--r--odb-tests/common/schema/namespace/test.hxx158
-rw-r--r--odb-tests/common/schema/namespace/testscript33
-rw-r--r--odb-tests/common/section/basics/buildfile41
-rw-r--r--odb-tests/common/section/basics/driver.cxx1735
-rw-r--r--odb-tests/common/section/basics/test.hxx628
-rw-r--r--odb-tests/common/section/basics/testscript33
-rw-r--r--odb-tests/common/section/polymorphism/buildfile41
-rw-r--r--odb-tests/common/section/polymorphism/driver.cxx1807
-rw-r--r--odb-tests/common/section/polymorphism/test.hxx542
-rw-r--r--odb-tests/common/section/polymorphism/testscript33
-rw-r--r--odb-tests/common/session/cache/buildfile41
-rw-r--r--odb-tests/common/session/cache/driver.cxx83
-rw-r--r--odb-tests/common/session/cache/test.hxx50
-rw-r--r--odb-tests/common/session/cache/testscript33
-rw-r--r--odb-tests/common/session/custom/buildfile43
-rw-r--r--odb-tests/common/session/custom/driver.cxx231
-rw-r--r--odb-tests/common/session/custom/session.cxx57
-rw-r--r--odb-tests/common/session/custom/session.hxx191
-rw-r--r--odb-tests/common/session/custom/session.txx159
-rw-r--r--odb-tests/common/session/custom/test.hxx118
-rw-r--r--odb-tests/common/session/custom/testscript33
-rw-r--r--odb-tests/common/statement/processing/buildfile8
-rw-r--r--odb-tests/common/statement/processing/driver.cxx619
-rw-r--r--odb-tests/common/statement/processing/testscript6
-rw-r--r--odb-tests/common/threads/buildfile49
-rw-r--r--odb-tests/common/threads/driver.cxx236
-rw-r--r--odb-tests/common/threads/test.hxx29
-rw-r--r--odb-tests/common/threads/testscript50
-rw-r--r--odb-tests/common/transaction/basics/buildfile13
-rw-r--r--odb-tests/common/transaction/basics/driver.cxx151
-rw-r--r--odb-tests/common/transaction/basics/testscript62
-rw-r--r--odb-tests/common/transaction/callback/buildfile13
-rw-r--r--odb-tests/common/transaction/callback/driver.cxx231
-rw-r--r--odb-tests/common/transaction/callback/testscript72
-rw-r--r--odb-tests/common/types/buildfile39
-rw-r--r--odb-tests/common/types/driver.cxx37
-rw-r--r--odb-tests/common/types/test.hxx55
-rw-r--r--odb-tests/common/types/testscript6
-rw-r--r--odb-tests/common/view/basics/buildfile45
-rw-r--r--odb-tests/common/view/basics/driver.cxx838
-rw-r--r--odb-tests/common/view/basics/test.hxx616
-rw-r--r--odb-tests/common/view/basics/testscript33
-rw-r--r--odb-tests/common/view/olv/.gitignore46
-rw-r--r--odb-tests/common/view/olv/buildfile50
-rw-r--r--odb-tests/common/view/olv/driver.cxx646
-rw-r--r--odb-tests/common/view/olv/test1.hxx116
-rw-r--r--odb-tests/common/view/olv/test2.hxx122
-rw-r--r--odb-tests/common/view/olv/test3.hxx106
-rw-r--r--odb-tests/common/view/olv/test4.hxx174
-rw-r--r--odb-tests/common/view/olv/test5.hxx86
-rw-r--r--odb-tests/common/view/olv/test6.hxx57
-rw-r--r--odb-tests/common/view/olv/test7.hxx57
-rw-r--r--odb-tests/common/view/olv/test8.hxx54
-rw-r--r--odb-tests/common/view/olv/test9.hxx78
-rw-r--r--odb-tests/common/view/olv/testscript39
-rw-r--r--odb-tests/common/virtual/buildfile42
-rw-r--r--odb-tests/common/virtual/driver.cxx154
-rw-r--r--odb-tests/common/virtual/test.hxx171
-rw-r--r--odb-tests/common/virtual/testscript33
-rw-r--r--odb-tests/common/wrapper/buildfile40
-rw-r--r--odb-tests/common/wrapper/driver.cxx216
-rw-r--r--odb-tests/common/wrapper/test.hxx214
-rw-r--r--odb-tests/common/wrapper/testscript33
-rw-r--r--odb-tests/database-options.testscript75
-rw-r--r--odb-tests/evolution/.gitignore15
-rw-r--r--odb-tests/evolution/add-column/buildfile63
-rw-r--r--odb-tests/evolution/add-column/driver.cxx130
-rw-r--r--odb-tests/evolution/add-column/model.hxx38
-rw-r--r--odb-tests/evolution/add-column/test1.hxx9
-rw-r--r--odb-tests/evolution/add-column/test2.hxx11
-rw-r--r--odb-tests/evolution/add-column/test3.hxx11
-rw-r--r--odb-tests/evolution/add-column/testscript54
-rw-r--r--odb-tests/evolution/add-foreign-key/buildfile64
-rw-r--r--odb-tests/evolution/add-foreign-key/driver.cxx178
-rw-r--r--odb-tests/evolution/add-foreign-key/model.hxx66
-rw-r--r--odb-tests/evolution/add-foreign-key/test1.hxx9
-rw-r--r--odb-tests/evolution/add-foreign-key/test2.hxx11
-rw-r--r--odb-tests/evolution/add-foreign-key/test3.hxx11
-rw-r--r--odb-tests/evolution/add-foreign-key/testscript54
-rw-r--r--odb-tests/evolution/add-index/buildfile63
-rw-r--r--odb-tests/evolution/add-index/driver.cxx170
-rw-r--r--odb-tests/evolution/add-index/model.hxx33
-rw-r--r--odb-tests/evolution/add-index/test1.hxx9
-rw-r--r--odb-tests/evolution/add-index/test2.hxx11
-rw-r--r--odb-tests/evolution/add-index/test3.hxx11
-rw-r--r--odb-tests/evolution/add-index/testscript54
-rw-r--r--odb-tests/evolution/add-table/buildfile63
-rw-r--r--odb-tests/evolution/add-table/driver.cxx145
-rw-r--r--odb-tests/evolution/add-table/model.hxx48
-rw-r--r--odb-tests/evolution/add-table/test1.hxx9
-rw-r--r--odb-tests/evolution/add-table/test2.hxx11
-rw-r--r--odb-tests/evolution/add-table/test3.hxx11
-rw-r--r--odb-tests/evolution/add-table/testscript54
-rw-r--r--odb-tests/evolution/alter-column/buildfile63
-rw-r--r--odb-tests/evolution/alter-column/driver.cxx164
-rw-r--r--odb-tests/evolution/alter-column/model.hxx56
-rw-r--r--odb-tests/evolution/alter-column/test1.hxx9
-rw-r--r--odb-tests/evolution/alter-column/test2.hxx11
-rw-r--r--odb-tests/evolution/alter-column/test3.hxx11
-rw-r--r--odb-tests/evolution/alter-column/testscript55
-rw-r--r--odb-tests/evolution/combined/buildfile64
-rw-r--r--odb-tests/evolution/combined/driver.cxx162
-rw-r--r--odb-tests/evolution/combined/model.hxx174
-rw-r--r--odb-tests/evolution/combined/test1.hxx9
-rw-r--r--odb-tests/evolution/combined/test2.hxx11
-rw-r--r--odb-tests/evolution/combined/test3.hxx11
-rw-r--r--odb-tests/evolution/combined/testscript54
-rw-r--r--odb-tests/evolution/data/buildfile63
-rw-r--r--odb-tests/evolution/data/driver.cxx179
-rw-r--r--odb-tests/evolution/data/model.hxx45
-rw-r--r--odb-tests/evolution/data/test1.hxx9
-rw-r--r--odb-tests/evolution/data/test2.hxx11
-rw-r--r--odb-tests/evolution/data/test3.hxx11
-rw-r--r--odb-tests/evolution/data/testscript54
-rw-r--r--odb-tests/evolution/drop-column/buildfile64
-rw-r--r--odb-tests/evolution/drop-column/driver.cxx131
-rw-r--r--odb-tests/evolution/drop-column/model.hxx59
-rw-r--r--odb-tests/evolution/drop-column/test1.hxx9
-rw-r--r--odb-tests/evolution/drop-column/test2.hxx11
-rw-r--r--odb-tests/evolution/drop-column/test3.hxx11
-rw-r--r--odb-tests/evolution/drop-column/testscript54
-rw-r--r--odb-tests/evolution/drop-foreign-key/buildfile64
-rw-r--r--odb-tests/evolution/drop-foreign-key/driver.cxx149
-rw-r--r--odb-tests/evolution/drop-foreign-key/model.hxx50
-rw-r--r--odb-tests/evolution/drop-foreign-key/test1.hxx9
-rw-r--r--odb-tests/evolution/drop-foreign-key/test2.hxx11
-rw-r--r--odb-tests/evolution/drop-foreign-key/test3.hxx11
-rw-r--r--odb-tests/evolution/drop-foreign-key/testscript55
-rw-r--r--odb-tests/evolution/drop-index/buildfile63
-rw-r--r--odb-tests/evolution/drop-index/driver.cxx159
-rw-r--r--odb-tests/evolution/drop-index/model.hxx33
-rw-r--r--odb-tests/evolution/drop-index/test1.hxx9
-rw-r--r--odb-tests/evolution/drop-index/test2.hxx11
-rw-r--r--odb-tests/evolution/drop-index/test3.hxx11
-rw-r--r--odb-tests/evolution/drop-index/testscript54
-rw-r--r--odb-tests/evolution/drop-table/buildfile63
-rw-r--r--odb-tests/evolution/drop-table/driver.cxx173
-rw-r--r--odb-tests/evolution/drop-table/model.hxx94
-rw-r--r--odb-tests/evolution/drop-table/test1.hxx9
-rw-r--r--odb-tests/evolution/drop-table/test2.hxx11
-rw-r--r--odb-tests/evolution/drop-table/test3.hxx11
-rw-r--r--odb-tests/evolution/drop-table/testscript54
-rw-r--r--odb-tests/evolution/embedded/buildfile59
-rw-r--r--odb-tests/evolution/embedded/driver.cxx185
-rw-r--r--odb-tests/evolution/embedded/model.hxx45
-rw-r--r--odb-tests/evolution/embedded/test1.hxx9
-rw-r--r--odb-tests/evolution/embedded/test2.hxx11
-rw-r--r--odb-tests/evolution/embedded/test3.hxx11
-rw-r--r--odb-tests/evolution/embedded/testscript21
-rw-r--r--odb-tests/evolution/soft-add/buildfile64
-rw-r--r--odb-tests/evolution/soft-add/driver.cxx2224
-rw-r--r--odb-tests/evolution/soft-add/model.hxx504
-rw-r--r--odb-tests/evolution/soft-add/test1.hxx9
-rw-r--r--odb-tests/evolution/soft-add/test2.hxx11
-rw-r--r--odb-tests/evolution/soft-add/test3.hxx11
-rw-r--r--odb-tests/evolution/soft-add/testscript54
-rw-r--r--odb-tests/evolution/soft-delete/buildfile65
-rw-r--r--odb-tests/evolution/soft-delete/driver.cxx2207
-rw-r--r--odb-tests/evolution/soft-delete/model.hxx512
-rw-r--r--odb-tests/evolution/soft-delete/test1.hxx9
-rw-r--r--odb-tests/evolution/soft-delete/test2.hxx11
-rw-r--r--odb-tests/evolution/soft-delete/test3.hxx11
-rw-r--r--odb-tests/evolution/soft-delete/testscript54
-rw-r--r--odb-tests/evolution/version/buildfile63
-rw-r--r--odb-tests/evolution/version/driver.cxx161
-rw-r--r--odb-tests/evolution/version/model.hxx45
-rw-r--r--odb-tests/evolution/version/test1.hxx9
-rw-r--r--odb-tests/evolution/version/test2.hxx11
-rw-r--r--odb-tests/evolution/version/test3.hxx11
-rw-r--r--odb-tests/evolution/version/testscript54
-rw-r--r--odb-tests/libcommon/.gitignore3
-rw-r--r--odb-tests/libcommon/buffer.hxx104
-rw-r--r--odb-tests/libcommon/buildfile50
-rw-r--r--odb-tests/libcommon/common.cxx364
-rw-r--r--odb-tests/libcommon/common.hxx47
-rw-r--r--odb-tests/libcommon/common.txx24
-rw-r--r--odb-tests/libcommon/concrete.hxx57
-rw-r--r--odb-tests/libcommon/config.hxx.in14
-rw-r--r--odb-tests/libcommon/export.hxx39
-rw-r--r--odb-tests/manifest146
-rw-r--r--odb-tests/mssql/custom/custom.sql42
-rw-r--r--odb-tests/mssql/custom/driver.cxx135
-rw-r--r--odb-tests/mssql/custom/query.hxx183
-rw-r--r--odb-tests/mssql/custom/test.hxx121
-rw-r--r--odb-tests/mssql/custom/traits.cxx128
-rw-r--r--odb-tests/mssql/custom/traits.hxx148
-rw-r--r--odb-tests/mssql/database/driver.cxx105
-rw-r--r--odb-tests/mssql/native/driver.cxx73
-rw-r--r--odb-tests/mssql/query/driver.cxx188
-rw-r--r--odb-tests/mssql/query/test.hxx38
-rw-r--r--odb-tests/mssql/stored-proc/driver.cxx231
-rw-r--r--odb-tests/mssql/stored-proc/test.hxx63
-rw-r--r--odb-tests/mssql/template/driver.cxx40
-rw-r--r--odb-tests/mssql/template/template-vc10.vcxproj180
-rw-r--r--odb-tests/mssql/template/template-vc10.vcxproj.filters25
-rw-r--r--odb-tests/mssql/template/template-vc11.vcxproj184
-rw-r--r--odb-tests/mssql/template/template-vc11.vcxproj.filters25
-rw-r--r--odb-tests/mssql/template/template-vc12.vcxproj188
-rw-r--r--odb-tests/mssql/template/template-vc12.vcxproj.filters25
-rw-r--r--odb-tests/mssql/template/template-vc8.vcproj354
-rw-r--r--odb-tests/mssql/template/template-vc9.vcproj361
-rw-r--r--odb-tests/mssql/template/test.hxx25
-rw-r--r--odb-tests/mssql/types/driver.cxx381
-rw-r--r--odb-tests/mssql/types/test.hxx517
-rw-r--r--odb-tests/mssql/types/traits.hxx223
-rw-r--r--odb-tests/mysql-schema.testscript9
-rw-r--r--odb-tests/mysql.testscript12
-rw-r--r--odb-tests/mysql/custom/buildfile40
-rw-r--r--odb-tests/mysql/custom/driver.cxx117
-rw-r--r--odb-tests/mysql/custom/query.hxx160
-rw-r--r--odb-tests/mysql/custom/test.hxx54
-rw-r--r--odb-tests/mysql/custom/testscript11
-rw-r--r--odb-tests/mysql/custom/traits.hxx88
-rw-r--r--odb-tests/mysql/database/buildfile14
-rw-r--r--odb-tests/mysql/database/driver.cxx72
-rw-r--r--odb-tests/mysql/database/testscript6
-rw-r--r--odb-tests/mysql/index/buildfile37
-rw-r--r--odb-tests/mysql/index/driver.cxx44
-rw-r--r--odb-tests/mysql/index/test.hxx20
-rw-r--r--odb-tests/mysql/index/testscript11
-rw-r--r--odb-tests/mysql/native/buildfile19
-rw-r--r--odb-tests/mysql/native/driver.cxx75
-rw-r--r--odb-tests/mysql/native/testscript9
-rw-r--r--odb-tests/mysql/truncation/buildfile38
-rw-r--r--odb-tests/mysql/truncation/driver.cxx192
-rw-r--r--odb-tests/mysql/truncation/test.hxx48
-rw-r--r--odb-tests/mysql/truncation/testscript11
-rw-r--r--odb-tests/mysql/types/buildfile39
-rw-r--r--odb-tests/mysql/types/driver.cxx168
-rw-r--r--odb-tests/mysql/types/test.hxx336
-rw-r--r--odb-tests/mysql/types/testscript11
-rw-r--r--odb-tests/mysql/types/traits.hxx198
-rw-r--r--odb-tests/oracle/custom/custom.sql53
-rw-r--r--odb-tests/oracle/custom/driver.cxx77
-rw-r--r--odb-tests/oracle/custom/test.hxx40
-rw-r--r--odb-tests/oracle/custom/traits.hxx76
-rw-r--r--odb-tests/oracle/database/driver.cxx33
-rw-r--r--odb-tests/oracle/native/driver.cxx77
-rw-r--r--odb-tests/oracle/template/driver.cxx40
-rw-r--r--odb-tests/oracle/template/template-vc10.vcxproj180
-rw-r--r--odb-tests/oracle/template/template-vc10.vcxproj.filters25
-rw-r--r--odb-tests/oracle/template/template-vc11.vcxproj184
-rw-r--r--odb-tests/oracle/template/template-vc11.vcxproj.filters25
-rw-r--r--odb-tests/oracle/template/template-vc12.vcxproj188
-rw-r--r--odb-tests/oracle/template/template-vc12.vcxproj.filters25
-rw-r--r--odb-tests/oracle/template/template-vc8.vcproj354
-rw-r--r--odb-tests/oracle/template/template-vc9.vcproj361
-rw-r--r--odb-tests/oracle/template/test.hxx25
-rw-r--r--odb-tests/oracle/types/driver.cxx366
-rw-r--r--odb-tests/oracle/types/test.hxx353
-rw-r--r--odb-tests/oracle/types/traits.hxx192
-rw-r--r--odb-tests/pgsql-schema.testscript6
-rw-r--r--odb-tests/pgsql.testscript12
-rw-r--r--odb-tests/pgsql/buildfile6
-rw-r--r--odb-tests/pgsql/bulk/buildfile39
-rw-r--r--odb-tests/pgsql/bulk/driver.cxx363
-rw-r--r--odb-tests/pgsql/bulk/test.hxx34
-rw-r--r--odb-tests/pgsql/bulk/testscript18
-rw-r--r--odb-tests/pgsql/custom/buildfile40
-rw-r--r--odb-tests/pgsql/custom/driver.cxx125
-rw-r--r--odb-tests/pgsql/custom/query.hxx158
-rw-r--r--odb-tests/pgsql/custom/test.hxx86
-rw-r--r--odb-tests/pgsql/custom/testscript11
-rw-r--r--odb-tests/pgsql/custom/traits.hxx166
-rw-r--r--odb-tests/pgsql/database/buildfile14
-rw-r--r--odb-tests/pgsql/database/driver.cxx44
-rw-r--r--odb-tests/pgsql/database/testscript6
-rw-r--r--odb-tests/pgsql/index/buildfile37
-rw-r--r--odb-tests/pgsql/index/driver.cxx44
-rw-r--r--odb-tests/pgsql/index/test.hxx19
-rw-r--r--odb-tests/pgsql/index/testscript11
-rw-r--r--odb-tests/pgsql/native/buildfile19
-rw-r--r--odb-tests/pgsql/native/driver.cxx75
-rw-r--r--odb-tests/pgsql/native/testscript9
-rw-r--r--odb-tests/pgsql/truncation/buildfile38
-rw-r--r--odb-tests/pgsql/truncation/driver.cxx163
-rw-r--r--odb-tests/pgsql/truncation/test.hxx46
-rw-r--r--odb-tests/pgsql/truncation/testscript11
-rw-r--r--odb-tests/pgsql/types/buildfile39
-rw-r--r--odb-tests/pgsql/types/driver.cxx165
-rw-r--r--odb-tests/pgsql/types/test.hxx220
-rw-r--r--odb-tests/pgsql/types/testscript11
-rw-r--r--odb-tests/pgsql/types/traits.hxx171
-rw-r--r--odb-tests/qt/common/basic/driver.cxx66
-rw-r--r--odb-tests/qt/common/basic/test.hxx35
-rw-r--r--odb-tests/qt/common/containers/basics/driver.cxx569
-rw-r--r--odb-tests/qt/common/containers/basics/test.hxx224
-rw-r--r--odb-tests/qt/common/containers/change-tracking/driver.cxx643
-rw-r--r--odb-tests/qt/common/containers/change-tracking/test.hxx46
-rw-r--r--odb-tests/qt/common/smart-ptr/driver.cxx248
-rw-r--r--odb-tests/qt/common/smart-ptr/test.hxx77
-rw-r--r--odb-tests/qt/common/template/driver.cxx43
-rw-r--r--odb-tests/qt/common/template/template-qt4-vc10.vcxproj177
-rw-r--r--odb-tests/qt/common/template/template-qt4-vc10.vcxproj.filters24
-rw-r--r--odb-tests/qt/common/template/template-qt4-vc11.vcxproj181
-rw-r--r--odb-tests/qt/common/template/template-qt4-vc11.vcxproj.filters24
-rw-r--r--odb-tests/qt/common/template/template-qt4-vc12.vcxproj185
-rw-r--r--odb-tests/qt/common/template/template-qt4-vc12.vcxproj.filters24
-rw-r--r--odb-tests/qt/common/template/template-qt4-vc8.vcproj353
-rw-r--r--odb-tests/qt/common/template/template-qt4-vc9.vcproj360
-rw-r--r--odb-tests/qt/common/template/template-qt5-vc10.vcxproj177
-rw-r--r--odb-tests/qt/common/template/template-qt5-vc10.vcxproj.filters24
-rw-r--r--odb-tests/qt/common/template/template-qt5-vc11.vcxproj181
-rw-r--r--odb-tests/qt/common/template/template-qt5-vc11.vcxproj.filters24
-rw-r--r--odb-tests/qt/common/template/template-qt5-vc12.vcxproj185
-rw-r--r--odb-tests/qt/common/template/template-qt5-vc12.vcxproj.filters24
-rw-r--r--odb-tests/qt/common/template/template-qt5-vc9.vcproj360
-rw-r--r--odb-tests/qt/common/template/test.hxx25
-rw-r--r--odb-tests/qt/mssql/basic/driver.cxx65
-rw-r--r--odb-tests/qt/mssql/basic/test.hxx46
-rw-r--r--odb-tests/qt/mssql/date-time/driver.cxx94
-rw-r--r--odb-tests/qt/mssql/date-time/test.hxx68
-rw-r--r--odb-tests/qt/mssql/template/driver.cxx43
-rw-r--r--odb-tests/qt/mssql/template/template-qt4-vc10.vcxproj180
-rw-r--r--odb-tests/qt/mssql/template/template-qt4-vc10.vcxproj.filters25
-rw-r--r--odb-tests/qt/mssql/template/template-qt4-vc11.vcxproj184
-rw-r--r--odb-tests/qt/mssql/template/template-qt4-vc11.vcxproj.filters25
-rw-r--r--odb-tests/qt/mssql/template/template-qt4-vc12.vcxproj188
-rw-r--r--odb-tests/qt/mssql/template/template-qt4-vc12.vcxproj.filters25
-rw-r--r--odb-tests/qt/mssql/template/template-qt4-vc8.vcproj354
-rw-r--r--odb-tests/qt/mssql/template/template-qt4-vc9.vcproj361
-rw-r--r--odb-tests/qt/mssql/template/template-qt5-vc10.vcxproj180
-rw-r--r--odb-tests/qt/mssql/template/template-qt5-vc10.vcxproj.filters25
-rw-r--r--odb-tests/qt/mssql/template/template-qt5-vc11.vcxproj184
-rw-r--r--odb-tests/qt/mssql/template/template-qt5-vc11.vcxproj.filters25
-rw-r--r--odb-tests/qt/mssql/template/template-qt5-vc12.vcxproj188
-rw-r--r--odb-tests/qt/mssql/template/template-qt5-vc12.vcxproj.filters25
-rw-r--r--odb-tests/qt/mssql/template/template-qt5-vc9.vcproj361
-rw-r--r--odb-tests/qt/mssql/template/test.hxx25
-rw-r--r--odb-tests/qt/mysql/basic/driver.cxx60
-rw-r--r--odb-tests/qt/mysql/basic/test.hxx27
-rw-r--r--odb-tests/qt/mysql/date-time/driver.cxx178
-rw-r--r--odb-tests/qt/mysql/date-time/test.hxx70
-rw-r--r--odb-tests/qt/mysql/template/driver.cxx43
-rw-r--r--odb-tests/qt/mysql/template/template-qt4-vc10.vcxproj180
-rw-r--r--odb-tests/qt/mysql/template/template-qt4-vc10.vcxproj.filters25
-rw-r--r--odb-tests/qt/mysql/template/template-qt4-vc11.vcxproj184
-rw-r--r--odb-tests/qt/mysql/template/template-qt4-vc11.vcxproj.filters25
-rw-r--r--odb-tests/qt/mysql/template/template-qt4-vc12.vcxproj188
-rw-r--r--odb-tests/qt/mysql/template/template-qt4-vc12.vcxproj.filters25
-rw-r--r--odb-tests/qt/mysql/template/template-qt4-vc8.vcproj354
-rw-r--r--odb-tests/qt/mysql/template/template-qt4-vc9.vcproj361
-rw-r--r--odb-tests/qt/mysql/template/template-qt5-vc10.vcxproj180
-rw-r--r--odb-tests/qt/mysql/template/template-qt5-vc10.vcxproj.filters25
-rw-r--r--odb-tests/qt/mysql/template/template-qt5-vc11.vcxproj184
-rw-r--r--odb-tests/qt/mysql/template/template-qt5-vc11.vcxproj.filters25
-rw-r--r--odb-tests/qt/mysql/template/template-qt5-vc12.vcxproj188
-rw-r--r--odb-tests/qt/mysql/template/template-qt5-vc12.vcxproj.filters25
-rw-r--r--odb-tests/qt/mysql/template/template-qt5-vc9.vcproj361
-rw-r--r--odb-tests/qt/mysql/template/test.hxx25
-rw-r--r--odb-tests/qt/oracle/basic/driver.cxx81
-rw-r--r--odb-tests/qt/oracle/basic/test.hxx51
-rw-r--r--odb-tests/qt/oracle/date-time/driver.cxx80
-rw-r--r--odb-tests/qt/oracle/date-time/test.hxx45
-rw-r--r--odb-tests/qt/oracle/template/driver.cxx43
-rw-r--r--odb-tests/qt/oracle/template/template-qt4-vc10.vcxproj180
-rw-r--r--odb-tests/qt/oracle/template/template-qt4-vc10.vcxproj.filters25
-rw-r--r--odb-tests/qt/oracle/template/template-qt4-vc11.vcxproj184
-rw-r--r--odb-tests/qt/oracle/template/template-qt4-vc11.vcxproj.filters25
-rw-r--r--odb-tests/qt/oracle/template/template-qt4-vc12.vcxproj188
-rw-r--r--odb-tests/qt/oracle/template/template-qt4-vc12.vcxproj.filters25
-rw-r--r--odb-tests/qt/oracle/template/template-qt4-vc8.vcproj354
-rw-r--r--odb-tests/qt/oracle/template/template-qt4-vc9.vcproj361
-rw-r--r--odb-tests/qt/oracle/template/template-qt5-vc10.vcxproj180
-rw-r--r--odb-tests/qt/oracle/template/template-qt5-vc10.vcxproj.filters25
-rw-r--r--odb-tests/qt/oracle/template/template-qt5-vc11.vcxproj184
-rw-r--r--odb-tests/qt/oracle/template/template-qt5-vc11.vcxproj.filters25
-rw-r--r--odb-tests/qt/oracle/template/template-qt5-vc12.vcxproj188
-rw-r--r--odb-tests/qt/oracle/template/template-qt5-vc12.vcxproj.filters25
-rw-r--r--odb-tests/qt/oracle/template/template-qt5-vc9.vcproj361
-rw-r--r--odb-tests/qt/oracle/template/test.hxx25
-rw-r--r--odb-tests/qt/pgsql/basic/driver.cxx60
-rw-r--r--odb-tests/qt/pgsql/basic/test.hxx27
-rw-r--r--odb-tests/qt/pgsql/date-time/driver.cxx108
-rw-r--r--odb-tests/qt/pgsql/date-time/test.hxx43
-rw-r--r--odb-tests/qt/pgsql/template/driver.cxx43
-rw-r--r--odb-tests/qt/pgsql/template/template-qt4-vc10.vcxproj180
-rw-r--r--odb-tests/qt/pgsql/template/template-qt4-vc10.vcxproj.filters25
-rw-r--r--odb-tests/qt/pgsql/template/template-qt4-vc11.vcxproj184
-rw-r--r--odb-tests/qt/pgsql/template/template-qt4-vc11.vcxproj.filters25
-rw-r--r--odb-tests/qt/pgsql/template/template-qt4-vc12.vcxproj188
-rw-r--r--odb-tests/qt/pgsql/template/template-qt4-vc12.vcxproj.filters25
-rw-r--r--odb-tests/qt/pgsql/template/template-qt4-vc8.vcproj354
-rw-r--r--odb-tests/qt/pgsql/template/template-qt4-vc9.vcproj361
-rw-r--r--odb-tests/qt/pgsql/template/template-qt5-vc10.vcxproj180
-rw-r--r--odb-tests/qt/pgsql/template/template-qt5-vc10.vcxproj.filters25
-rw-r--r--odb-tests/qt/pgsql/template/template-qt5-vc11.vcxproj184
-rw-r--r--odb-tests/qt/pgsql/template/template-qt5-vc11.vcxproj.filters25
-rw-r--r--odb-tests/qt/pgsql/template/template-qt5-vc12.vcxproj188
-rw-r--r--odb-tests/qt/pgsql/template/template-qt5-vc12.vcxproj.filters25
-rw-r--r--odb-tests/qt/pgsql/template/template-qt5-vc9.vcproj361
-rw-r--r--odb-tests/qt/pgsql/template/test.hxx25
-rw-r--r--odb-tests/qt/sqlite/basic/driver.cxx60
-rw-r--r--odb-tests/qt/sqlite/basic/test.hxx27
-rw-r--r--odb-tests/qt/sqlite/date-time/driver.cxx140
-rw-r--r--odb-tests/qt/sqlite/date-time/test.hxx60
-rw-r--r--odb-tests/qt/sqlite/template/driver.cxx43
-rw-r--r--odb-tests/qt/sqlite/template/template-qt4-vc10.vcxproj180
-rw-r--r--odb-tests/qt/sqlite/template/template-qt4-vc10.vcxproj.filters25
-rw-r--r--odb-tests/qt/sqlite/template/template-qt4-vc11.vcxproj184
-rw-r--r--odb-tests/qt/sqlite/template/template-qt4-vc11.vcxproj.filters25
-rw-r--r--odb-tests/qt/sqlite/template/template-qt4-vc12.vcxproj188
-rw-r--r--odb-tests/qt/sqlite/template/template-qt4-vc12.vcxproj.filters25
-rw-r--r--odb-tests/qt/sqlite/template/template-qt4-vc8.vcproj354
-rw-r--r--odb-tests/qt/sqlite/template/template-qt4-vc9.vcproj361
-rw-r--r--odb-tests/qt/sqlite/template/template-qt5-vc10.vcxproj180
-rw-r--r--odb-tests/qt/sqlite/template/template-qt5-vc10.vcxproj.filters25
-rw-r--r--odb-tests/qt/sqlite/template/template-qt5-vc11.vcxproj184
-rw-r--r--odb-tests/qt/sqlite/template/template-qt5-vc11.vcxproj.filters25
-rw-r--r--odb-tests/qt/sqlite/template/template-qt5-vc12.vcxproj188
-rw-r--r--odb-tests/qt/sqlite/template/template-qt5-vc12.vcxproj.filters25
-rw-r--r--odb-tests/qt/sqlite/template/template-qt5-vc9.vcproj361
-rw-r--r--odb-tests/qt/sqlite/template/test.hxx25
-rw-r--r--odb-tests/sqlite-schema.testscript5
-rw-r--r--odb-tests/sqlite.testscript10
-rw-r--r--odb-tests/sqlite/attach/buildfile35
-rw-r--r--odb-tests/sqlite/attach/driver.cxx109
-rw-r--r--odb-tests/sqlite/attach/test.hxx32
-rw-r--r--odb-tests/sqlite/attach/testscript9
-rw-r--r--odb-tests/sqlite/auto/buildfile35
-rw-r--r--odb-tests/sqlite/auto/driver.cxx76
-rw-r--r--odb-tests/sqlite/auto/test.hxx32
-rw-r--r--odb-tests/sqlite/auto/testscript9
-rw-r--r--odb-tests/sqlite/custom/buildfile34
-rw-r--r--odb-tests/sqlite/custom/driver.cxx78
-rw-r--r--odb-tests/sqlite/custom/test.hxx39
-rw-r--r--odb-tests/sqlite/custom/testscript9
-rw-r--r--odb-tests/sqlite/database/buildfile14
-rw-r--r--odb-tests/sqlite/database/driver.cxx40
-rw-r--r--odb-tests/sqlite/database/testscript6
-rw-r--r--odb-tests/sqlite/native/buildfile15
-rw-r--r--odb-tests/sqlite/native/driver.cxx74
-rw-r--r--odb-tests/sqlite/native/testscript9
-rw-r--r--odb-tests/sqlite/stream/buildfile34
-rw-r--r--odb-tests/sqlite/stream/driver.cxx281
-rw-r--r--odb-tests/sqlite/stream/test.hxx37
-rw-r--r--odb-tests/sqlite/stream/testscript9
-rw-r--r--odb-tests/sqlite/transaction/buildfile33
-rw-r--r--odb-tests/sqlite/transaction/driver.cxx61
-rw-r--r--odb-tests/sqlite/transaction/test.hxx26
-rw-r--r--odb-tests/sqlite/transaction/testscript9
-rw-r--r--odb-tests/sqlite/truncation/buildfile34
-rw-r--r--odb-tests/sqlite/truncation/driver.cxx163
-rw-r--r--odb-tests/sqlite/truncation/test.hxx46
-rw-r--r--odb-tests/sqlite/truncation/testscript9
-rw-r--r--odb-tests/sqlite/types/buildfile35
-rw-r--r--odb-tests/sqlite/types/driver.cxx113
-rw-r--r--odb-tests/sqlite/types/test.hxx125
-rw-r--r--odb-tests/sqlite/types/testscript9
-rw-r--r--odb-tests/sqlite/types/traits.hxx57
-rw-r--r--odb/.gitignore28
-rw-r--r--odb/GPLv3 (renamed from GPLv3)0
-rw-r--r--odb/INSTALL36
-rw-r--r--odb/LICENSE21
-rw-r--r--odb/Makefile.am34
l---------odb/NEWS1
-rw-r--r--odb/README (renamed from README)0
-rw-r--r--odb/build/.gitignore3
-rw-r--r--odb/build/bootstrap.build (renamed from build/bootstrap.build)0
-rw-r--r--odb/build/export.build (renamed from build/export.build)0
-rw-r--r--odb/build/root.build110
-rw-r--r--odb/buildfile140
-rw-r--r--odb/common-query.cxx1413
-rw-r--r--odb/context.cxx3289
-rw-r--r--odb/context.hxx1933
-rw-r--r--odb/cxx-lexer.cxx364
-rw-r--r--odb/diagnostics.hxx96
-rw-r--r--odb/doc/.gitignore5
-rw-r--r--odb/doc/buildfile191
-rw-r--r--odb/doc/default.css (renamed from doc/default.css)0
-rw-r--r--odb/doc/manual.html2ps69
-rw-r--r--odb/doc/manual.xhtml27073
-rw-r--r--odb/doc/odb-arch.png (renamed from doc/odb-arch.png)bin16883 -> 16883 bytes
-rw-r--r--odb/doc/odb-arch.svg (renamed from doc/odb-arch.svg)0
-rw-r--r--odb/doc/odb-epilogue.1 (renamed from doc/odb-epilogue.1)0
-rw-r--r--odb/doc/odb-epilogue.xhtml (renamed from doc/odb-epilogue.xhtml)0
-rw-r--r--odb/doc/odb-flow.png (renamed from doc/odb-flow.png)bin39092 -> 39092 bytes
-rw-r--r--odb/doc/odb-flow.svg (renamed from doc/odb-flow.svg)0
-rw-r--r--odb/doc/odb-prologue.1 (renamed from doc/odb-prologue.1)0
-rw-r--r--odb/doc/odb-prologue.xhtml (renamed from doc/odb-prologue.xhtml)0
-rw-r--r--odb/doc/pregenerated/odb.1799
-rw-r--r--odb/doc/pregenerated/odb.xhtml978
-rw-r--r--odb/gcc.hxx195
-rw-r--r--odb/generator.cxx1044
-rw-r--r--odb/header.cxx900
-rw-r--r--odb/location.hxx25
-rw-r--r--odb/makefile317
-rw-r--r--odb/manifest34
-rw-r--r--odb/odb.cxx2032
-rw-r--r--odb/odb/.gitignore2
-rw-r--r--odb/odb/buildfile172
-rw-r--r--odb/odb/common-query.cxx1440
-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.cxx3389
-rw-r--r--odb/odb/context.hxx1941
-rw-r--r--odb/odb/context.ixx (renamed from odb/context.ixx)0
-rw-r--r--odb/odb/cxx-lexer.cxx366
-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.hxx96
-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.hxx215
-rw-r--r--odb/odb/generate.hxx (renamed from odb/generate.hxx)0
-rw-r--r--odb/odb/generator.cxx1044
-rw-r--r--odb/odb/generator.hxx (renamed from odb/generator.hxx)0
-rw-r--r--odb/odb/header.cxx902
-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.hxx25
-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.cxx2188
-rw-r--r--odb/odb/option-functions.cxx132
-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.cxx352
-rw-r--r--odb/odb/option-types.hxx391
-rw-r--r--odb/odb/options.cli1086
-rw-r--r--odb/odb/parser.cxx2403
-rw-r--r--odb/odb/parser.hxx (renamed from odb/parser.hxx)0
-rw-r--r--odb/odb/plugin.cxx458
-rw-r--r--odb/odb/pragma.cxx4442
-rw-r--r--odb/odb/pragma.hxx287
-rw-r--r--odb/odb/pregenerated/odb/options.cxx4034
-rw-r--r--odb/odb/pregenerated/odb/options.hxx2340
-rw-r--r--odb/odb/pregenerated/odb/options.ixx3471
-rw-r--r--odb/odb/processor.cxx3267
-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.hxx39
-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.hxx81
-rw-r--r--odb/odb/relational/header.cxx (renamed from odb/relational/header.cxx)0
-rw-r--r--odb/odb/relational/header.hxx1466
-rw-r--r--odb/odb/relational/inline.cxx (renamed from odb/relational/inline.cxx)0
-rw-r--r--odb/odb/relational/inline.hxx693
-rw-r--r--odb/odb/relational/model.cxx (renamed from odb/relational/model.cxx)0
-rw-r--r--odb/odb/relational/model.hxx868
-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.cxx161
-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.cxx786
-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.cxx285
-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.cxx1140
-rw-r--r--odb/odb/relational/processor.cxx1564
-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.hxx1606
-rw-r--r--odb/odb/relational/source.cxx6345
-rw-r--r--odb/odb/relational/source.hxx7158
-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.cxx471
-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.cxx54
-rw-r--r--odb/odb/semantics/class-template.hxx (renamed from odb/semantics/class-template.hxx)0
-rw-r--r--odb/odb/semantics/class.cxx175
-rw-r--r--odb/odb/semantics/class.hxx (renamed from odb/semantics/class.hxx)0
-rw-r--r--odb/odb/semantics/derived.cxx254
-rw-r--r--odb/odb/semantics/derived.hxx440
-rw-r--r--odb/odb/semantics/elements.cxx619
-rw-r--r--odb/odb/semantics/elements.hxx841
-rw-r--r--odb/odb/semantics/elements.ixx (renamed from odb/semantics/elements.ixx)0
-rw-r--r--odb/odb/semantics/enum.cxx85
-rw-r--r--odb/odb/semantics/enum.hxx (renamed from odb/semantics/enum.hxx)0
-rw-r--r--odb/odb/semantics/fundamental.cxx222
-rw-r--r--odb/odb/semantics/fundamental.hxx (renamed from odb/semantics/fundamental.hxx)0
-rw-r--r--odb/odb/semantics/namespace.cxx107
-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.cxx187
-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.cxx56
-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.cxx201
-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.cxx179
-rw-r--r--odb/odb/semantics/relational/elements.hxx462
-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.cxx218
-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.cxx145
-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.cxx97
-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.cxx54
-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.cxx80
-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.cxx183
-rw-r--r--odb/odb/semantics/relational/table.hxx (renamed from odb/semantics/relational/table.hxx)0
-rw-r--r--odb/odb/semantics/template.cxx88
-rw-r--r--odb/odb/semantics/template.hxx (renamed from odb/semantics/template.hxx)0
-rw-r--r--odb/odb/semantics/union-template.cxx54
-rw-r--r--odb/odb/semantics/union-template.hxx (renamed from odb/semantics/union-template.hxx)0
-rw-r--r--odb/odb/semantics/union.cxx36
-rw-r--r--odb/odb/semantics/union.hxx (renamed from odb/semantics/union.hxx)0
-rw-r--r--odb/odb/semantics/unit.cxx42
-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.hxx238
-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.hxx162
-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.cxx1917
-rw-r--r--odb/odb/validator.hxx (renamed from odb/validator.hxx)0
-rw-r--r--odb/odb/version.hxx0
-rw-r--r--odb/odb/version.hxx.in54
-rw-r--r--odb/option-functions.cxx132
-rw-r--r--odb/option-types.cxx349
-rw-r--r--odb/option-types.hxx390
-rw-r--r--odb/options.cli1082
-rw-r--r--odb/options.cxx3907
-rw-r--r--odb/options.hxx2308
-rw-r--r--odb/options.ixx3450
-rw-r--r--odb/parser.cxx2323
-rw-r--r--odb/plugin.cxx458
-rw-r--r--odb/pragma.cxx4444
-rw-r--r--odb/pragma.hxx287
-rw-r--r--odb/processor.cxx3237
-rw-r--r--odb/profile.hxx39
-rw-r--r--odb/relational/generate.hxx81
-rw-r--r--odb/relational/header.hxx1464
-rw-r--r--odb/relational/inline.hxx689
-rw-r--r--odb/relational/model.hxx868
-rw-r--r--odb/relational/mysql/model.cxx135
-rw-r--r--odb/relational/pgsql/context.cxx786
-rw-r--r--odb/relational/pgsql/header.cxx271
-rw-r--r--odb/relational/pgsql/source.cxx1140
-rw-r--r--odb/relational/processor.cxx1562
-rw-r--r--odb/relational/schema.hxx1603
-rw-r--r--odb/relational/source.cxx6324
-rw-r--r--odb/relational/source.hxx7142
-rw-r--r--odb/relational/sqlite/source.cxx470
-rw-r--r--odb/semantics/class-template.cxx54
-rw-r--r--odb/semantics/class.cxx175
-rw-r--r--odb/semantics/derived.cxx254
-rw-r--r--odb/semantics/derived.hxx438
-rw-r--r--odb/semantics/elements.cxx619
-rw-r--r--odb/semantics/elements.hxx841
-rw-r--r--odb/semantics/enum.cxx85
-rw-r--r--odb/semantics/fundamental.cxx222
-rw-r--r--odb/semantics/namespace.cxx107
-rw-r--r--odb/semantics/relational/changelog.cxx187
-rw-r--r--odb/semantics/relational/changeset.cxx56
-rw-r--r--odb/semantics/relational/column.cxx201
-rw-r--r--odb/semantics/relational/elements.cxx179
-rw-r--r--odb/semantics/relational/elements.hxx468
-rw-r--r--odb/semantics/relational/foreign-key.cxx218
-rw-r--r--odb/semantics/relational/index.cxx145
-rw-r--r--odb/semantics/relational/key.cxx97
-rw-r--r--odb/semantics/relational/model.cxx54
-rw-r--r--odb/semantics/relational/primary-key.cxx80
-rw-r--r--odb/semantics/relational/table.cxx183
-rw-r--r--odb/semantics/template.cxx88
-rw-r--r--odb/semantics/union-template.cxx54
-rw-r--r--odb/semantics/union.cxx36
-rw-r--r--odb/semantics/unit.cxx42
-rw-r--r--odb/tests/.gitignore (renamed from tests/.gitignore)0
-rw-r--r--odb/tests/build/.gitignore3
-rw-r--r--odb/tests/build/bootstrap.build (renamed from tests/build/bootstrap.build)0
-rw-r--r--odb/tests/build/root.build16
-rw-r--r--odb/tests/buildfile (renamed from tests/buildfile)0
-rw-r--r--odb/tests/testscript (renamed from tests/testscript)0
-rw-r--r--odb/traversal/elements.hxx238
-rw-r--r--odb/traversal/relational/elements.hxx162
-rw-r--r--odb/validator.cxx1912
-rw-r--r--odb/version.hxx37
-rw-r--r--packages.manifest20
-rw-r--r--repositories.manifest21
-rw-r--r--tests/build/root.build16
-rw-r--r--version1
2124 files changed, 313825 insertions, 92645 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..1631641
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,19 @@
+# This is a good default: files that are auto-detected by git to be text are
+# converted to the platform-native line ending (LF on Unix, CRLF on Windows)
+# in the working tree and to LF in the repository.
+#
+* text=auto
+
+# Use `eol=crlf` for files that should have the CRLF line ending both in the
+# working tree (even on Unix) and in the repository.
+#
+#*.bat text eol=crlf
+
+# Use `eol=lf` for files that should have the LF line ending both in the
+# working tree (even on Windows) and in the repository.
+#
+#*.sh text eol=lf
+
+# Use `binary` to make sure certain files are never auto-detected as text.
+#
+#*.png binary
diff --git a/.gitignore b/.gitignore
index 6c5bf86..dd04439 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,33 +1,5 @@
.bdep/
-# Compiler/linker output.
+# Local default options files.
#
-*.d
-*.t
-*.i
-*.ii
-*.o
-*.obj
-*.so
-*.dll
-*.a
-*.lib
-*.exp
-*.pdb
-*.ilk
-*.exe
-*.exe.dlls/
-*.exe.manifest
-*.pc
-
-*.l
-*.l.cpp-options
-
-# Generated documentation.
-#
-*.pdf
-*.ps
-
-# Generated build system files.
-#
-*-dynamic.make
+.build2/local/
diff --git a/INSTALL b/INSTALL
deleted file mode 100644
index e2e1b0e..0000000
--- a/INSTALL
+++ /dev/null
@@ -1,81 +0,0 @@
-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 on UNIX
-================
-
-The following build instructions are for the Linux/UNIX/Mac OS X
-operating systems.
-
-The standard autotools-based build system is used on these platforms.
-After unpacking the source code archive, change to the odb package
-directory (referred to as odb/ from now on) and run the configure
-script:
-
-./configure
-
-To see the available configuration options run configure with --help:
-
-./configure --help
-
-The configure script expects the libcutl headers and libraries to be
-installed in a directory where the C++ compiler and linker will search
-for them by default (normally /usr and /usr/local). If libcutl is
-installed in another directory, you can use the CPPFLAGS and LDFLAGS
-configure variables to specify its location, for example:
-
-./configure CPPFLAGS=-I/opt/libcutl/include LDFLAGS=-L/opt/libcutl/lib
-
-If libcutl is not installed and you would like to use its build
-directory instead, you can use the --with-libcutl configure option
-to specify its location, for example:
-
-./configure --with-libcutl=/tmp/libcutl
-
-As another example, the following configure command uses the specified
-GNU g++ compiler and compiles with optimization and without debug
-information:
-
-./configure CXX=g++-4.5 CXXFLAGS=-O3
-
-Once configuration is complete, run make to build odb:
-
-make
-
-Once the build is completed successfully, you can install odb using the
-install target (you may need to do this step as root depending on the
-installation directory):
-
-make install
-
-
-Building on Windows
-===================
-
-Building odb on Windows involves a custom build procedure. Consider
-using the pre-compiled binary distribution of odb for Windows or write
-to odb-users@codesynthesis.com for more information.
diff --git a/INSTALL-GIT b/INSTALL-GIT
deleted file mode 100644
index b04cf68..0000000
--- a/INSTALL-GIT
+++ /dev/null
@@ -1,79 +0,0 @@
-The following instructions describe how to work with the source code that was
-checked out from the git repository.
-
-The major difference between using a released source code package and source
-code from the repository is that the former does not contain autotools-based
-makefiles or Visual Studio project files. Instead, it contains templates for
-these files as well as its own, custom build system. This build system is
-used for development as well as to automatically generate the autotools and
-Visual Studio files.
-
-This file describes how to use this build system to build the package as well
-as to create a release-ready source distribution which contains the autotools
-build system and Visual Studio project files.
-
-
-Prerequisites
-=============
-
-Besides the prerequisites listed in the INSTALL file, you will need the
-following additional packages:
-
- - GNU bash >= 2.0.0 http://www.gnu.org/software/bash/
- - GNU make >= 3.81 http://www.gnu.org/software/make/
- - build >= latest http://www.codesynthesis.com/projects/build/
- - cli >= latest http://www.codesynthesis.com/projects/cli/
-
-If you are planning to create the source code distributions, then you will
-also need the following packages:
-
- - GNU m4 >= 1.4.0 http://www.gnu.org/software/m4/
- - GNU sed >= 4.0.0 http://www.gnu.org/software/sed/
- - tofrodos >= 1.7.0 http://www.thefreecountry.com/tofrodos/
-
-As we as the GNU autotools:
-
- - GNU libtool >= 2.2.6b http://www.gnu.org/software/libtool/
- - GNU autoconf >= 2.67 http://www.gnu.org/software/autoconf/
- - GNU automake >= 1.11.1 http://www.gnu.org/software/automake/
-
-Any reasonably up to date GNU/Linux installation would normally have all of
-the above packages already present, except for build, cli, and maybe tofrodos.
-
-
-Configuring and Building
-========================
-
-To build the source code simply run make in the root directory of the package.
-The first time you run make, the build process will also configure the
-package by asking you several questions. On the subsequent runs, make will
-only rebuild what has changed.
-
-To run the automated test suite (if any), run 'make test'. To clean the object
-files, executables, etc., run 'make clean'. To de-configure the package (that
-is, to remove configuration files in addition to objects, executables, etc.),
-run 'make disfigure'.
-
-
-Creating Distribution
-=====================
-
-To create the source code distribution, use the dist make target as well as
-the dist_prefix variable to specify the directory where the distribution files
-should be placed. For example:
-
-make dist dist_prefix=/tmp/package-1.1.0
-
-Once the distribution files are ready, change to the distribution directory
-and run the bootstrap script to bootstrap the autotools build system, for
-example:
-
-cd /tmp/package-1.1.0
-./bootsrap
-
-To create the source code archives, use the autotools build system. First
-configuring the package (see the INSTALL file for more information on this
-step) and then use the dist target to make the archives, for example:
-
-./configure
-make dist
diff --git a/LICENSE b/LICENSE
index db2e236..7629ad9 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,21 +1,24 @@
-Copyright (c) 2009-2020 Code Synthesis Tools CC.
+Copyright (c) 2005-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.
+The individual ODB components are licensed as follows (see the LICENSE file
+in each package for the exact terms and conditions):
-For more information on ODB licensing as well as for answers to
-some of the common licensing questions, visit the ODB License
-page:
+odb (ODB compiler)
+ GPL-3.0-only
-http://www.codesynthesis.com/products/odb/license.xhtml
+libodb
+libodb-sqlite
+libodb-pgsql
+libodb-mysql
+libodb-qt
+libodb-boost
+ GPL-2.0-only or Commercial Proprietary License (CPL)
-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.
+libodb-oracle
+libodb-mssql
+ Non-Commercial Use and Evaluation License (NCUEL) or Commercial Proprietary
+ License (CPL)
-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
+odb-tests
+odb-example
+ GPL-2.0-only
diff --git a/Makefile.am b/Makefile.am
deleted file mode 100644
index 11b661c..0000000
--- a/Makefile.am
+++ /dev/null
@@ -1,8 +0,0 @@
-# file : Makefile.am
-# license : GNU GPL v3; see accompanying LICENSE file
-
-SUBDIRS = __path__(dirs)
-dist_doc_DATA = __file__(docs)
-EXTRA_DIST = __file__(extra_dist)
-ACLOCAL_AMFLAGS = -I m4
-
diff --git a/NEWS b/NEWS
index 0f5fac2..1bd577e 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,10 @@ Version 2.5.0
@@ Ref to the manual.
+ * Support for custom table definition options in addition to column
+ definition options. For details, refer to Section 14.1.16, "options" in
+ the ODB manual.
+
* Support for nested object ids. Now the 'id' pragma specifier can optionally
include the data member path to the id inside a composite value. For
example:
@@ -67,6 +71,16 @@ Version 2.5.0
* Database classes are now move-constructible. This means they can be
returned by value from a function in C++11.
+ * Support for bulk operations in PostgreSQL 14 using the new pipeline mode.
+ For details on bulk operations see Section 15.3, "Bulk Database Operations"
+ in the ODB manual. Note that while this functionality requires libpq
+ version 14 or later, it should be usable with PostgreSQL servers version
+ 7.4 or later. The development of this support was sponsored by Qube
+ Research & Technologies Limited.
+
+ * Support for SQLite ATTACH DATABASE. Attached databases are represented as
+ special odb::sqlite::database instances. @@ TODO: doc ref.
+
* Support for SQLite incremental BLOB/TEXT I/O (the sqlite3_blob_open()
functionality). For details, refer to Section 18.1.3, "Incremental
BLOB/TEXT I/O" in the ODB manual.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..49a5a49
--- /dev/null
+++ b/README.md
@@ -0,0 +1,105 @@
+# ODB - ORM for C++
+
+ODB is an open-source, cross-platform, and cross-database object-relational
+mapping (ORM) system for C++. It allows you to persist C++ objects to a
+relational database without having to deal with tables, columns, or SQL and
+without manually writing any mapping code. ODB supports MySQL, SQLite,
+PostgreSQL, Oracle, and Microsoft SQL Server relational databases. It also
+comes with optional profiles for Boost and Qt which allow you to seamlessly
+use value types, containers, and smart pointers from these libraries in your
+persistent C++ classes.
+
+For further information, including licensing conditions, documentation, and
+binary packages, refer to the [ODB project
+page](https://codesynthesis.com/products/odb/).
+
+NOTE: the steps described below are more appropriate for the development of
+ODB as opposed to consumption. In case you just want to use ODB:
+
+* If you want to use a binary package, see the [ODB download
+ page](https://codesynthesis.com/products/odb/download.xhtml).
+
+* If you want to build ODB from source and use the result from a project that
+ uses a build system other than `build2`, then see [Installing ODB with
+ `build2`](https://codesynthesis.com/products/odb/doc/install-build2.xhtml)
+ for the step-by-step instructions.
+
+* If you want to use ODB from a project that uses `build2` as the build
+ system, then see the accompanying `PACKAGE-README.md` file.
+
+The development setup for ODB uses multiple build configurations: a single
+host configuration for the ODB compiler and a target configuration per
+database. For example:
+
+```
+git clone .../odb.git
+cd odb
+
+bdep init --empty
+
+bdep config create @host ../odb-host --type host cc config.cxx=g++
+bdep config create @sqlite ../odb-sqlite cc config.cxx=g++
+bdep config create @pgsql ../odb-pgsql cc config.cxx=g++
+bdep config create @mysql ../odb-mysql cc config.cxx=g++
+
+bdep init @host -d odb
+bdep init @sqlite -d libodb -d libodb-sqlite -d odb-tests
+bdep init @pgsql -d libodb -d libodb-pgsql -d odb-tests
+bdep init @mysql -d libodb -d libodb-mysql -d odb-tests
+```
+
+Note that while the target configurations can use any compiler (and you can
+create multiple such configurations for different compilers), the host
+configuration must use GCC because the ODB compiler is implemented as a GCC
+plugin.
+
+You can use system-installed versions of the client libraries and tools
+instead of building them from source. For example:
+
+```
+bdep config link @pgsql @host
+bdep config link @mysql @host
+
+bdep init @sqlite -d libodb -d libodb-sqlite -d odb-tests ?sys:libsqlite3
+bdep init @pgsql -d libodb -d libodb-pgsql -d odb-tests ?sys:libpq { @host }+ ?sys:psql/*
+bdep init @mysql -d libodb -d libodb-mysql -d odb-tests ?sys:libmysqlclient { @host }+ ?sys:mysql-client/*
+```
+
+You can also create a target configuration for testing multi-database
+support. For example:
+
+```
+bdep config create @multi ../odb-multi cc config.cxx=g++
+
+bdep init @multi -d libodb -d libodb-sqlite -d libodb-pgsql -d libodb-mysql
+bdep init @multi -d odb-tests config.odb_tests.database='sqlite pgsql mysql'
+```
+
+See `odb-tests/README.md` for instructions on setting up various databases to
+run tests.
+
+To generate the documentation in the `.ps` and `.pdf` formats, the `html2ps`
+and `ps2pdf14` programs are required (the latter is from `ghostscript`). A
+warning is issued in the development mode if these programs are not available.
+
+To test installation of the ODB compiler, create a separate target
+configuration (it will automatically resolve any build-time dependencies from
+`host`):
+
+```
+bdep config create @install ../odb-install --type target cc config.cxx=g++ \
+ config.install.root=/tmp/install
+
+bdep init @install -d odb
+
+b install: ../odb-install/odb/
+```
+
+Note that if building manually (that is, without `bdep`) and in-source, then
+the checked out `odb/odb/version.hxx`, `libodb/odb/version.hxx`, and
+`libodb-*/odb/*/version.hxx` files will be overwritten during the build but
+these changes should be ignored. To do this automatically, run:
+
+```
+git update-index --assume-unchanged odb/odb/version.hxx libodb/odb/version.hxx libodb-*/odb/*/version.hxx
+```
diff --git a/bootstrap b/bootstrap
deleted file mode 100755
index 4f6ee07..0000000
--- a/bootstrap
+++ /dev/null
@@ -1,16 +0,0 @@
-#! /bin/sh
-
-# file : bootstrap
-# license : GNU GPL v3; see accompanying LICENSE file
-
-#
-# Bootstrap the automake build system.
-#
-
-rm -f config.cache
-
-if test ! -d m4; then
- mkdir m4
-fi
-
-autoreconf --install
diff --git a/build/bootstrap.make b/build/bootstrap.make
deleted file mode 100644
index f0c56e8..0000000
--- a/build/bootstrap.make
+++ /dev/null
@@ -1,96 +0,0 @@
-# file : build/bootstrap.make
-# license : GNU GPL v3; see accompanying LICENSE file
-
-project_name := odb
-
-# First try to include the bundled bootstrap.make if it exist. If that
-# fails, let make search for the external bootstrap.make.
-#
-build := build-0.3
-
--include $(dir $(lastword $(MAKEFILE_LIST)))../../$(build)/bootstrap.make
-
-ifeq ($(patsubst %build/bootstrap.make,,$(lastword $(MAKEFILE_LIST))),)
-include $(build)/bootstrap.make
-endif
-
-def_goal := $(.DEFAULT_GOAL)
-
-# Include C++ configuration. We need to know if we are using the generic
-# C++ compiler in which case we need to compensate for missing dependency
-# auto-generation (see below).
-#
-$(call include,$(bld_root)/cxx/configuration.make)
-
-# Aliases
-#
-.PHONY: $(out_base)/ \
- $(out_base)/.dist \
- $(out_base)/.clean
-
-ifdef %interactive%
-
-.PHONY: dist clean
-
-dist: $(out_base)/.dist
-clean: $(out_base)/.clean
-
-endif
-
-# Make sure the distribution prefix is set if the goal is dist.
-#
-ifneq ($(filter $(MAKECMDGOALS),dist),)
-ifeq ($(dist_prefix),)
-$(error dist_prefix is not set)
-endif
-endif
-
-ifdef cxx_id
-
-# It would be better to do these checks in the script once instead
-# of for every makefile.
-#
-ifneq ($(MAKECMDGOALS),disfigure)
-
-ifneq ($(cxx_id),gnu)
-$(error only GNU g++ can be used to build the ODB compiler)
-endif
-
-$(call include,$(bld_root)/cxx/gnu/configuration.make)
-
-ifdef cxx_gnu
-ifeq ($(shell $(cxx_gnu) -print-file-name=plugin),plugin)
-$(error $(cxx_gnu) does not support plugins)
-endif
-endif
-endif # disfigure
-
-endif # cxx_id
-
-# If we don't have dependency auto-generation then we need to manually
-# make sure that generated files are generated before C++ file are
-# compiler. To do this we make the object files ($2) depend in order-
-# only on generated files ($3).
-#
-ifeq ($(cxx_id),generic)
-
-define include-dep
-$(if $2,$(eval $2: | $3))
-endef
-
-else
-
-define include-dep
-$(call -include,$1)
-endef
-
-endif
-
-# Don't include dependency info for certain targets.
-#
-ifneq ($(filter $(MAKECMDGOALS),clean disfigure dist),)
-include-dep =
-endif
-
-
-.DEFAULT_GOAL := $(def_goal)
diff --git a/build/export/odb/stub.make b/build/export/odb/stub.make
deleted file mode 100644
index 890ae22..0000000
--- a/build/export/odb/stub.make
+++ /dev/null
@@ -1,11 +0,0 @@
-# file : build/export/odb/stub.make
-# license : GNU GPL v3; see accompanying LICENSE file
-
-$(call include-once,$(src_root)/odb/makefile,$(out_root))
-
-# Use the rules file from odb's import directory instead of the
-# importing project's one.
-#
-$(call export,\
- odb: $(out_root)/odb/odb,\
- odb-rules: $(scf_root)/import/odb/hxx-cxx.make)
diff --git a/build/hxx/hxx-cxx.make b/build/hxx/hxx-cxx.make
deleted file mode 100644
index cdd0f01..0000000
--- a/build/hxx/hxx-cxx.make
+++ /dev/null
@@ -1,58 +0,0 @@
-# file : build/hxx/hxx-cxx.make
-# license : GNU GPL v3; see accompanying LICENSE file
-
-# Get the C++ configuration (file extensions, and extra CPP options).
-#
-$(call include,$(bld_root)/cxx/configuration.make)
-
-odb_pattern := \
-$(out_base)/%-odb.$(cxx_s_suffix) \
-$(out_base)/%-odb.$(cxx_h_suffix) \
-$(out_base)/%-odb.$(cxx_i_suffix) \
-$(out_base)/%.sql
-
-$(odb_pattern): odb := odb
-$(odb_pattern): odb_options := \
---hxx-suffix .$(cxx_h_suffix) \
---ixx-suffix .$(cxx_i_suffix) \
---cxx-suffix .$(cxx_s_suffix)
-
-$(odb_pattern): odb-expand-cpp-options-impl = \
-$(if $1,$(shell sed -e 's%include: \(.*\)%\1%' -e t -e d $1))
-
-$(odb_pattern): odb-expand-cpp-options = \
-$(call odb-expand-cpp-options-impl,$(filter %.cpp-options,$1))
-
-.PRECIOUS: $(odb_pattern)
-
-ifeq ($(out_base),$(src_base))
-
-$(odb_pattern): $(src_base)/%.$(cxx_h_suffix)
- $(call message,odb $<,$(odb) $(cpp_options) \
-$(call expand-cpp-options,$^) $(cxx_pp_extra_options) $(odb_options) \
---output-dir $(dir $@) $<)
-
-else
-
-$(odb_pattern): $(src_base)/%.$(cxx_h_suffix) | $$(dir $$@).
- $(call message,odb $<,$(odb) $(cpp_options) \
-$(call expand-cpp-options,$^) $(cxx_pp_extra_options) $(odb_options) \
---output-dir $(dir $@) $<)
-
-$(odb_pattern): $(out_base)/%.$(cxx_h_suffix) | $$(dir $$@).
- $(call message,odb $<,$(odb) $(cpp_options) \
-$(call expand-cpp-options,$^) $(cxx_pp_extra_options) $(odb_options) \
---output-dir $(dir $@) $<)
-endif
-
-.PHONY: $(out_base)/%-odb.cxx.hxx.clean
-
-$(out_base)/%-odb.cxx.hxx.clean: cxx_s_suffix := $(cxx_s_suffix)
-$(out_base)/%-odb.cxx.hxx.clean: cxx_h_suffix := $(cxx_h_suffix)
-$(out_base)/%-odb.cxx.hxx.clean: cxx_i_suffix := $(cxx_i_suffix)
-
-$(out_base)/%-odb.cxx.hxx.clean:
- $(call message,rm $$1,rm -f $$1,$(@:.cxx.hxx.clean=.$(cxx_s_suffix)))
- $(call message,rm $$1,rm -f $$1,$(@:.cxx.hxx.clean=.$(cxx_h_suffix)))
- $(call message,rm $$1,rm -f $$1,$(@:.cxx.hxx.clean=.$(cxx_i_suffix)))
- $(call message,rm $$1,rm -f $$1,$(@:-odb.cxx.hxx.clean=.sql))
diff --git a/build/import/cli/cli-cxx.make b/build/import/cli/cli-cxx.make
deleted file mode 100644
index 9bdf238..0000000
--- a/build/import/cli/cli-cxx.make
+++ /dev/null
@@ -1,47 +0,0 @@
-# file : build/import/cli/cli-cxx.make
-# license : MIT; see accompanying LICENSE file
-
-# Here we are operating in the importing project's space, not in
-# cli's.
-#
-
-# Get the C++ file extensions.
-#
-$(call include,$(bld_root)/cxx/configuration-static.make)
-
-cli_pattern := \
-$(out_base)/%.$(cxx_s_suffix) \
-$(out_base)/%.$(cxx_h_suffix) \
-$(out_base)/%.$(cxx_i_suffix)
-
-$(cli_pattern): cli_options := \
---hxx-suffix .$(cxx_h_suffix) \
---ixx-suffix .$(cxx_i_suffix) \
---cxx-suffix .$(cxx_s_suffix)
-
-.PRECIOUS: $(cli_pattern)
-
-ifeq ($(out_base),$(src_base))
-
-$(cli_pattern): $(src_base)/%.cli
- $(call message,cli $<,$(cli) $(cli_options) --output-dir $(dir $@) $<)
-
-else
-
-$(cli_pattern): $(src_base)/%.cli | $$(dir $$@).
- $(call message,cli $<,$(cli) $(cli_options) --output-dir $(dir $@) $<)
-
-$(cli_pattern): $(out_base)/%.cli | $$(dir $$@).
- $(call message,cli $<,$(cli) $(cli_options) --output-dir $(dir $@) $<)
-endif
-
-.PHONY: $(out_base)/%.cxx.cli.clean
-
-$(out_base)/%.cxx.cli.clean: cxx_s_suffix := $(cxx_s_suffix)
-$(out_base)/%.cxx.cli.clean: cxx_h_suffix := $(cxx_h_suffix)
-$(out_base)/%.cxx.cli.clean: cxx_i_suffix := $(cxx_i_suffix)
-
-$(out_base)/%.cxx.cli.clean:
- $(call message,rm $$1,rm -f $$1,$(@:.cxx.cli.clean=.$(cxx_s_suffix)))
- $(call message,rm $$1,rm -f $$1,$(@:.cxx.cli.clean=.$(cxx_h_suffix)))
- $(call message,rm $$1,rm -f $$1,$(@:.cxx.cli.clean=.$(cxx_i_suffix)))
diff --git a/build/import/cli/configuration-rules.make b/build/import/cli/configuration-rules.make
deleted file mode 100644
index 6355000..0000000
--- a/build/import/cli/configuration-rules.make
+++ /dev/null
@@ -1,13 +0,0 @@
-# file : build/import/cli/configuration-rules.make
-# license : MIT; see accompanying LICENSE file
-
-$(dcf_root)/import/cli/configuration-dynamic.make: | $(dcf_root)/import/cli/.
- $(call message,,$(scf_root)/import/cli/configure $@)
-
-ifndef %foreign%
-
-$(dcf_root)/.disfigure::
- $(call message,rm $(dcf_root)/import/cli/configuration-dynamic.make,\
-rm -f $(dcf_root)/import/cli/configuration-dynamic.make)
-
-endif
diff --git a/build/import/cli/configure b/build/import/cli/configure
deleted file mode 100755
index 2a1fde4..0000000
--- a/build/import/cli/configure
+++ /dev/null
@@ -1,53 +0,0 @@
-#! /usr/bin/env bash
-
-# file : build/import/cli/configure
-# license : MIT; see accompanying LICENSE file
-
-
-# $1 - out file
-#
-# bld_root - build root
-# project_name - project name
-#
-
-source $bld_root/dialog.bash
-
-
-$echo
-$echo "Configuring external dependency on 'cli' for '$project_name'."
-$echo
-
-$echo
-$echo "Would you like to configure dependency on the installed "
-$echo "version of 'cli' as opposed to the development build?"
-$echo
-
-installed=`read_y_n y`
-
-path=
-
-if [ "$installed" = "n" ]; then
-
-$echo
-$echo "Please enter the src_root for 'cli'."
-$echo
-
-src_root=`read_path --directory --exist`
-
-$echo
-$echo "Please enter the out_root for 'cli'."
-$eche
-
-out_root=`read_path --directory $src_root`
-
-fi
-
-echo cli_installed := $installed >$1
-
-if [ "$installed" = "n" ]; then
-
-echo src_root := $src_root >>$1
-echo scf_root := \$\(src_root\)/build >>$1
-echo out_root := $out_root >>$1
-
-fi
diff --git a/build/import/cli/stub.make b/build/import/cli/stub.make
deleted file mode 100644
index 741b371..0000000
--- a/build/import/cli/stub.make
+++ /dev/null
@@ -1,28 +0,0 @@
-# file : build/import/cli/stub.make
-# license : MIT; see accompanying LICENSE file
-
-$(call include-once,$(scf_root)/import/cli/configuration-rules.make,$(dcf_root))
-
-cli_installed :=
-
-$(call -include,$(dcf_root)/import/cli/configuration-dynamic.make)
-
-ifdef cli_installed
-
-ifeq ($(cli_installed),y)
-
-$(call export,cli: cli,cli-rules: $(scf_root)/import/cli/cli-cxx.make)
-
-else
-
-# Include export stub.
-#
-$(call include,$(scf_root)/export/cli/stub.make)
-
-endif
-
-else
-
-.NOTPARALLEL:
-
-endif
diff --git a/build/import/libcutl/configuration-rules.make b/build/import/libcutl/configuration-rules.make
deleted file mode 100644
index 4559510..0000000
--- a/build/import/libcutl/configuration-rules.make
+++ /dev/null
@@ -1,13 +0,0 @@
-# file : build/import/libcutl/configuration-rules.make
-# license : MIT; see accompanying LICENSE file
-
-$(dcf_root)/import/libcutl/configuration-dynamic.make: | $(dcf_root)/import/libcutl/.
- $(call message,,$(scf_root)/import/libcutl/configure $@)
-
-ifndef %foreign%
-
-$(dcf_root)/.disfigure::
- $(call message,rm $(dcf_root)/import/libcutl/configuration-dynamic.make,\
-rm -f $(dcf_root)/import/libcutl/configuration-dynamic.make)
-
-endif
diff --git a/build/import/libcutl/configure b/build/import/libcutl/configure
deleted file mode 100755
index 5c182e2..0000000
--- a/build/import/libcutl/configure
+++ /dev/null
@@ -1,53 +0,0 @@
-#! /usr/bin/env bash
-
-# file : build/import/libcutl/configure
-# license : MIT; see accompanying LICENSE file
-
-
-# $1 - out file
-#
-# bld_root - build root
-# project_name - project name
-#
-
-source $bld_root/dialog.bash
-
-
-$echo
-$echo "Configuring external dependency on 'libcutl' for '$project_name'."
-$echo
-
-$echo
-$echo "Would you like to configure dependency on the installed "
-$echo "version of 'libcutl' as opposed to the development build?"
-$echo
-
-installed=`read_y_n y`
-
-path=
-
-if [ "$installed" = "n" ]; then
-
-$echo
-$echo "Please enter the src_root for 'libcutl'."
-$echo
-
-src_root=`read_path --directory --exist`
-
-$echo
-$echo "Please enter the out_root for 'libcutl'."
-$echo
-
-out_root=`read_path --directory $src_root`
-
-fi
-
-echo libcutl_installed := $installed >$1
-
-if [ "$installed" = "n" ]; then
-
-echo src_root := $src_root >>$1
-echo scf_root := \$\(src_root\)/build >>$1
-echo out_root := $out_root >>$1
-
-fi
diff --git a/build/import/libcutl/stub.make b/build/import/libcutl/stub.make
deleted file mode 100644
index 11876f2..0000000
--- a/build/import/libcutl/stub.make
+++ /dev/null
@@ -1,28 +0,0 @@
-# file : build/import/libcutl/stub.make
-# license : MIT; see accompanying LICENSE file
-
-$(call include-once,$(scf_root)/import/libcutl/configuration-rules.make,$(dcf_root))
-
-libcutl_installed :=
-
-$(call -include,$(dcf_root)/import/libcutl/configuration-dynamic.make)
-
-ifdef libcutl_installed
-
-ifeq ($(libcutl_installed),y)
-
-$(call export,l: -lcutl,cpp-options: )
-
-else
-
-# Include export stub.
-#
-$(call include,$(scf_root)/export/libcutl/stub.make)
-
-endif
-
-else
-
-.NOTPARALLEL:
-
-endif
diff --git a/build/import/odb/configuration-rules.make b/build/import/odb/configuration-rules.make
deleted file mode 100644
index 81d21e1..0000000
--- a/build/import/odb/configuration-rules.make
+++ /dev/null
@@ -1,13 +0,0 @@
-# file : build/import/odb/configuration-rules.make
-# license : GNU GPL v3; see accompanying LICENSE file
-
-$(dcf_root)/import/odb/configuration-dynamic.make: | $(dcf_root)/import/odb/.
- $(call message,,$(scf_root)/import/odb/configure $@)
-
-ifndef %foreign%
-
-$(dcf_root)/.disfigure::
- $(call message,rm $(dcf_root)/import/odb/configuration-dynamic.make,\
-rm -f $(dcf_root)/import/odb/configuration-dynamic.make)
-
-endif
diff --git a/build/import/odb/configure b/build/import/odb/configure
deleted file mode 100755
index 6cb5f9b..0000000
--- a/build/import/odb/configure
+++ /dev/null
@@ -1,53 +0,0 @@
-#! /usr/bin/env bash
-
-# file : build/import/odb/configure
-# license : GNU GPL v3; see accompanying LICENSE file
-
-
-# $1 - out file
-#
-# bld_root - build root
-# project_name - project name
-#
-
-source $bld_root/dialog.bash
-
-
-$echo
-$echo "Configuring external dependency on 'odb' for '$project_name'."
-$echo
-
-$echo
-$echo "Would you like to configure dependency on the installed "
-$echo "version of 'odb' as opposed to the development build?"
-$echo
-
-installed=`read_y_n y`
-
-path=
-
-if [ "$installed" = "n" ]; then
-
-$echo
-$echo "Please enter the src_root for 'odb'."
-$echo
-
-src_root=`read_path --directory --exist`
-
-$echo
-$echo "Please enter the out_root for 'odb'."
-$eche
-
-out_root=`read_path --directory $src_root`
-
-fi
-
-echo odb_installed := $installed >$1
-
-if [ "$installed" = "n" ]; then
-
-echo src_root := $src_root >>$1
-echo scf_root := \$\(src_root\)/build >>$1
-echo out_root := $out_root >>$1
-
-fi
diff --git a/build/import/odb/hxx-cxx.make b/build/import/odb/hxx-cxx.make
deleted file mode 100644
index 987acc0..0000000
--- a/build/import/odb/hxx-cxx.make
+++ /dev/null
@@ -1,127 +0,0 @@
-# file : build/import/odb/hxx-cxx.make
-# license : GNU GPL v3; see accompanying LICENSE file
-
-# Here we are operating in the importing project's space, not in odb's.
-#
-
-# Get the C++ configuration (file extensions, and extra CPP options).
-#
-$(call include,$(bld_root)/cxx/configuration.make)
-
-odb_databases := mysql sqlite pgsql oracle mssql
-
-odb_pattern := \
-$(out_base)/%-odb.$(cxx_s_suffix) \
-$(out_base)/%-odb.$(cxx_h_suffix) \
-$(out_base)/%-odb.$(cxx_i_suffix) \
-$(out_base)/%.sql
-
-odb_patterns := $(odb_pattern)
-
-define odb-db-pattern
-odb_$1_pattern := \
-$$(out_base)/%-odb-$1.$$(cxx_s_suffix) \
-$$(out_base)/%-odb-$1.$$(cxx_h_suffix) \
-$$(out_base)/%-odb-$1.$$(cxx_i_suffix) \
-$$(out_base)/%-$1.sql
-
-odb_patterns += $$(odb_$1_pattern)
-
-endef # Trailing newline is important.
-
-$(foreach d,$(odb_databases),$(eval $(call odb-db-pattern,$d)))
-
-$(odb_patterns): odb_options := \
---hxx-suffix .$(cxx_h_suffix) \
---ixx-suffix .$(cxx_i_suffix) \
---cxx-suffix .$(cxx_s_suffix)
-
-$(odb_patterns): odb-expand-cpp-options-impl = \
-$(if $1,$(shell sed -e 's%include: \(.*\)%\1%' -e t -e d $1))
-
-$(odb_patterns): odb-expand-cpp-options = \
-$(call odb-expand-cpp-options-impl,$(filter %.cpp-options,$1))
-
-# We only check for the long option name to avoid false positives.
-#
-$(odb_pattern): odb-default-database = \
-$(if $(filter --multi-database ,$(odb_options)),--database common )
-
-$(odb_pattern): odb-default-database-message = \
-$(if $(filter --multi-database ,$(odb_options)),[common] )
-
-.PRECIOUS: $(odb_patterns)
-
-ifeq ($(out_base),$(src_base))
-
-$(odb_pattern): $(src_base)/%.$(cxx_h_suffix)
- $(call message,odb $(call odb-default-database-message)$<,$(odb) \
-$(cpp_options) $(call odb-expand-cpp-options,$^) $(cxx_pp_extra_options) \
-$(odb_options) $(call odb-default-database)--output-dir $(dir $@) $<)
-
-define odb-db-rule
-$$(odb_$1_pattern): $$(src_base)/%.$$(cxx_h_suffix)
- $$(call message,odb [$1] $$<,$$(odb) $$(cpp_options) \
-$$(call odb-expand-cpp-options,$$^) $$(cxx_pp_extra_options) $$(odb_options) \
---database $1 --output-dir $$(dir $$@) $$<)
-
-endef # Trailing newline is important.
-
-else
-
-$(odb_pattern): $(src_base)/%.$(cxx_h_suffix) | $$(dir $$@).
- $(call message,odb $(call odb-default-database-message)$<,$(odb) \
-$(cpp_options) $(call odb-expand-cpp-options,$^) $(cxx_pp_extra_options) \
-$(odb_options) $(call odb-default-database)--output-dir $(dir $@) $<)
-
-$(odb_pattern): $(out_base)/%.$(cxx_h_suffix) | $$(dir $$@).
- $(call message,odb $(call odb-default-database-message)$<,$(odb) \
-$(cpp_options) $(call odb-expand-cpp-options,$^) $(cxx_pp_extra_options) \
-$(odb_options) $(call odb-default-database) --output-dir $(dir $@) $<)
-
-define odb-db-rule
-$$(odb_$1_pattern): $$(src_base)/%.$$(cxx_h_suffix) | $$$$(dir $$$$@).
- $$(call message,odb [$1] $$<,$$(odb) $$(cpp_options) \
-$$(call odb-expand-cpp-options,$$^) $$(cxx_pp_extra_options) $$(odb_options) \
---database $1 --output-dir $$(dir $$@) $$<)
-
-$$(odb_$1_pattern): $$(out_base)/%.$$(cxx_h_suffix) | $$$$(dir $$$$@).
- $$(call message,odb [$1] $$<,$$(odb) $$(cpp_options) \
-$$(call odb-expand-cpp-options,$$^) $$(cxx_pp_extra_options) $$(odb_options) \
---database $1 --output-dir $$(dir $$@) $$<)
-
-endef # Trailing newline is important.
-endif
-
-$(foreach d,$(odb_databases),$(eval $(call odb-db-rule,$d)))
-
-# Clean.
-#
-.PHONY: $(out_base)/%-odb.cxx.hxx.clean
-
-$(out_base)/%-odb.cxx.hxx.clean: cxx_s_suffix := $(cxx_s_suffix)
-$(out_base)/%-odb.cxx.hxx.clean: cxx_h_suffix := $(cxx_h_suffix)
-$(out_base)/%-odb.cxx.hxx.clean: cxx_i_suffix := $(cxx_i_suffix)
-
-$(out_base)/%-odb.cxx.hxx.clean:
- $(call message,rm $$1,rm -f $$1,$(@:.cxx.hxx.clean=.$(cxx_s_suffix)))
- $(call message,rm $$1,rm -f $$1,$(@:.cxx.hxx.clean=.$(cxx_h_suffix)))
- $(call message,rm $$1,rm -f $$1,$(@:.cxx.hxx.clean=.$(cxx_i_suffix)))
- $(call message,rm $$1,rm -f $$1,$(@:-odb.cxx.hxx.clean=.sql))
-
-define odb-db-clean-rule
-.PHONY: $$(out_base)/%-odb-$1.cxx.hxx.clean
-
-$$(out_base)/%-odb-$1.cxx.hxx.clean: cxx_s_suffix := $$(cxx_s_suffix)
-$$(out_base)/%-odb-$1.cxx.hxx.clean: cxx_h_suffix := $$(cxx_h_suffix)
-$$(out_base)/%-odb-$1.cxx.hxx.clean: cxx_i_suffix := $$(cxx_i_suffix)
-
-$$(out_base)/%-odb-$1.cxx.hxx.clean:
- $$(call message,rm $$$$1,rm -f $$$$1,$$(@:.cxx.hxx.clean=.$$(cxx_s_suffix)))
- $$(call message,rm $$$$1,rm -f $$$$1,$$(@:.cxx.hxx.clean=.$$(cxx_h_suffix)))
- $$(call message,rm $$$$1,rm -f $$$$1,$$(@:.cxx.hxx.clean=.$$(cxx_i_suffix)))
- $$(call message,rm $$$$1,rm -f $$$$1,$$(@:-odb-$1.cxx.hxx.clean=-$1.sql))
-
-endef # Trailing newline is important.
-
-$(foreach d,$(odb_databases),$(eval $(call odb-db-clean-rule,$d)))
diff --git a/build/import/odb/stub.make b/build/import/odb/stub.make
deleted file mode 100644
index b2fcebe..0000000
--- a/build/import/odb/stub.make
+++ /dev/null
@@ -1,28 +0,0 @@
-# file : build/import/odb/stub.make
-# license : GNU GPL v3; see accompanying LICENSE file
-
-$(call include-once,$(scf_root)/import/odb/configuration-rules.make,$(dcf_root))
-
-odb_installed :=
-
-$(call -include,$(dcf_root)/import/odb/configuration-dynamic.make)
-
-ifdef odb_installed
-
-ifeq ($(odb_installed),y)
-
-$(call export,odb: odb,odb-rules: $(scf_root)/import/odb/hxx-cxx.make)
-
-else
-
-# Include export stub.
-#
-$(call include,$(scf_root)/export/odb/stub.make)
-
-endif
-
-else
-
-.NOTPARALLEL:
-
-endif
diff --git a/build/root.build b/build/root.build
deleted file mode 100644
index edff38f..0000000
--- a/build/root.build
+++ /dev/null
@@ -1,52 +0,0 @@
-# file : build/root.build
-# license : GNU GPL v3; see accompanying LICENSE file
-
-cxx.std = latest
-
-using cxx
-
-if ($cxx.id != 'gcc')
- fail 'ODB compiler can only be built with GCC'
-
-# Determine the GCC plugin directory.
-#
-# If plugin support is disabled, then -print-file-name will print the name we
-# have passed (the real plugin directory will always be absolute).
-#
-# It can also include '..' components (e.g., on Windows) so normalize it for
-# good measure.
-#
-plugin_dir = [dir_path] $process.run($cxx.path -print-file-name=plugin)
-
-if ("$plugin_dir" == plugin)
- fail "$recall($cxx.path) does not support plugins"
-
-plugin_dir = $normalize($plugin_dir)
-
-if ($build.version.number > 12000000000)
- config [config.report] plugin_dir
-
-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"
-
-# Load the cli module but only if it's available. This way a distribution
-# that includes pre-generated files can be built without installing cli.
-# This is also the reason why we need to explicitly spell out individual
-# source file prerequisites instead of using the cli.cxx{} group (it won't
-# be there unless the module is configured).
-#
-using? cli
-
-# Specify the test target for cross-testing.
-#
-test.target = $cxx.target
diff --git a/buildfile b/buildfile
index f656924..c3c8909 100644
--- a/buildfile
+++ b/buildfile
@@ -1,9 +1,6 @@
-# file : buildfile
-# license : GNU GPL v3; see accompanying LICENSE file
-
-./: {*/ -build/ -m4/} doc{GPLv3 INSTALL LICENSE NEWS README} manifest
-
-# Don't install tests or the INSTALL file.
+# Glue buildfile that "pulls" all the packages in the project.
#
-tests/: install = false
-doc{INSTALL}@./: install = false
+import pkgs = [dir_paths] $process.run_regex(\
+ cat $src_root/packages.manifest, '\s*location\s*:\s*(\S+)\s*', '\1')
+
+./: $pkgs
diff --git a/configure.ac b/configure.ac
deleted file mode 100644
index 1d92980..0000000
--- a/configure.ac
+++ /dev/null
@@ -1,150 +0,0 @@
-# file : configure.ac
-# license : GNU GPL v3; see accompanying LICENSE file
-
-AC_PREREQ(2.60)
-AC_INIT([odb], [__value__(version)], [odb-users@codesynthesis.com])
-AC_CONFIG_AUX_DIR([config])
-AC_CONFIG_MACRO_DIR([m4])
-AC_CONFIG_SRCDIR([odb/version.hxx])
-
-AM_INIT_AUTOMAKE([-Wall -Werror foreign nostdinc subdir-objects dist-bzip2 dist-zip tar-ustar])
-m4_equote()[m4_ifdef]m4_dquote()([AM_PROG_AR], [AM_PROG_AR]) # Required by automake 1.12.
-
-LT_INIT([disable-static])
-
-AC_CANONICAL_HOST
-
-# Check for C++ compiler and use it to compile the tests.
-#
-AC_PROG_CXX
-AC_LANG(C++)
-
-# Create the libtool executable so that we can use it in further tests.
-#
-LT_OUTPUT
-
-# Test for plugin support in GCC.
-#
-GCC_PLUGIN
-
-# See if we are building static plugin. Static build should only be
-# used if you are building a custom version of GCC with the ODB
-# plugin linked-in statically. This is primarily useful on Windows
-# where GCC plugins are not (yet) supported due to dynamic loading
-# limitations. In this case the headers normally come from the GCC
-# build directly via CPPFLAGS.
-#
-if test x$static_plugin = xyes; then
- AC_WARN([Building static plugin for direct linking into GCC executable!])
- AC_DEFINE([ODB_STATIC_PLUGIN], [1], [Building static plugin.])
- plugindir='$(pkglibexecdir)'
-
-# Otherwise, see if we should install the plugin into the GCC plugin dir.
-#
-elif test x$gcc_plugin_dir != xno; then
- AC_DEFINE([ODB_GCC_PLUGIN_DIR], [1], [Plugin is in GCC plugin directory.])
- plugindir=$gcc_plugin_dir
-
-# Otherwise, try to figure out a relative path from the driver (bindir) to
-# the plugin (libexecdir).
-#
-elif test x$static_plugin = xno; then
- # Get the expanded values for bindir and libexecdir.
- #
- if test x$exec_prefix = xNONE; then
- if test x$prefix = xNONE; then
- e_exec_prefix=$ac_default_prefix
- else
- e_exec_prefix=$prefix
- fi
- else
- e_exec_prefix=$exec_prefix
- fi
-
- e_pkglibexecdir=`echo "$libexecdir/$PACKAGE_NAME" | sed "s?^\\\${exec_prefix}?$e_exec_prefix?"`
- e_bindir=`echo "$bindir" | sed "s?^\\\${exec_prefix}?$e_exec_prefix?"`
-
- # Try to find a common prefix.
- #
- common=$e_bindir
- rel_plugindir=$e_pkglibexecdir
-
- while test x$common != x/; do
- suffix=`echo "$e_pkglibexecdir" | sed "s?^$common/*??"`
- if test x$suffix != x$e_pkglibexecdir; then
- # Replace all the remaining directories in bindir with ".."
- # and append the suffix.
- rel_plugindir=`echo "$e_bindir" | sed "s?^$common/*??"`
- rel_plugindir=`echo "$rel_plugindir" | sed ['s?[^/][^/]*?..?g']`
- if test x$rel_plugindir != x -a x$suffix != x; then
- rel_plugindir="$rel_plugindir/$suffix"
- else
- rel_plugindir="$rel_plugindir$suffix"
- fi
- break
- fi
- common=`AS_DIRNAME(["$common"])`
- done
- AC_DEFINE_UNQUOTED([ODB_PLUGIN_PATH], ["$rel_plugindir"], [Plugin path.])
- plugindir='$(pkglibexecdir)'
-fi
-
-AC_SUBST([plugindir])
-
-# G++ name.
-#
-AC_ARG_WITH(
- [gxx-name],
- [AC_HELP_STRING([--with-gxx-name=NAME], [g++ executable name to embed in the ODB compiler driver])],
- [case $withval in
- no)
- gxx_name=
- ;;
- yes)
- gxx_name=$CXX
- ;;
- *)
- gxx_name="$withval"
- ;;
- esac],
- [gxx_name=$CXX])
-
-AS_IF(
- [test "x$gxx_name" != x],
- [AC_DEFINE_UNQUOTED([ODB_GXX_NAME], ["$gxx_name"], [g++ binary.])])
-
-# Default options file.
-#
-AC_ARG_WITH(
- [options-file],
- [AC_HELP_STRING([--with-options-file=PATH], [default options file path to embed in the driver])],
- [case $withval in
- no)
- options_file=
- ;;
- yes)
- options_file=../etc/odb/default.options
- ;;
- *)
- options_file="$withval"
- ;;
- esac],
- [options_file=])
-
-AS_IF(
- [test "x$options_file" != x],
- [AC_DEFINE_UNQUOTED([ODB_DEFAULT_OPTIONS_FILE], ["$options_file"], [default options file path.])])
-
-# Check for libcutl.
-#
-LIBCUTL([],[AC_MSG_ERROR([libcutl is not found; consider using --with-libcutl=DIR])])
-
-# Check if we should disable rpath.
-#
-DISABLE_RPATH
-
-# Output.
-#
-AC_CONFIG_HEADERS([odb/config.h])
-AC_CONFIG_FILES([__path__(config_files)])
-AC_OUTPUT
diff --git a/doc/.gitignore b/doc/.gitignore
deleted file mode 100644
index 5accfef..0000000
--- a/doc/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-odb.xhtml
-odb.1
-*.ps
-*.pdf
diff --git a/doc/Makefile.am b/doc/Makefile.am
deleted file mode 100644
index f09e34b..0000000
--- a/doc/Makefile.am
+++ /dev/null
@@ -1,7 +0,0 @@
-# file : doc/Makefile.am
-# license : GNU GPL v3; see accompanying LICENSE file
-
-dist_ps_DATA = __file__(ps_docs)
-dist_pdf_DATA = __file__(pdf_docs)
-dist_html_DATA = __file__(html_docs)
-dist_man_MANS = __file__(man_docs)
diff --git a/doc/buildfile b/doc/buildfile
deleted file mode 100644
index 45b7ac2..0000000
--- a/doc/buildfile
+++ /dev/null
@@ -1,20 +0,0 @@
-# file : doc/buildfile
-# license : GNU GPL v3; see accompanying LICENSE file
-
-define css: doc
-css{*}: extension = css
-
-define xhtml: doc
-xhtml{*}: extension = xhtml
-
-./: css{default}
-
-# @@ BUILD2 TMP: auto-generated and not in git (also odb-manual.* below)
-#
-./: file{odb-*.1} file{odb-*.xhtml}
- # {man1 xhtml}{odb}
-
-./: doc{manual.xhtml} doc{*.png} file{*.svg +*.html2ps}
- #doc{odb-manual.ps odb-manual.pdf}
-
-./: file{doc.sh}
diff --git a/doc/doc.sh b/doc/doc.sh
deleted file mode 100755
index 4e96aed..0000000
--- a/doc/doc.sh
+++ /dev/null
@@ -1,78 +0,0 @@
-#! /usr/bin/env bash
-
-version=2.5.0-b.6
-
-trap 'exit 1' ERR
-set -o errtrace # Trap in functions.
-
-function info () { echo "$*" 1>&2; }
-function error () { info "$*"; exit 1; }
-
-date="$(date +"%B %Y")"
-copyright="$(sed -n -re 's%^Copyright \(c\) (.+)\.$%\1%p' ../LICENSE)"
-
-while [ $# -gt 0 ]; do
- case $1 in
- --clean)
- rm -f odb.xhtml odb.1
- rm -f odb-manual.ps odb-manual.pdf
- exit 0
- ;;
- *)
- error "unexpected $1"
- ;;
- esac
-done
-
-function compile () # <input-name> <output-name>
-{
- local i=$1; shift
- local o=$1; shift
-
- # Use a bash array to handle empty arguments.
- #
- local ops=()
- while [ $# -gt 0 ]; do
- ops=("${ops[@]}" "$1")
- shift
- done
-
- # --html-suffix .xhtml
- cli -I .. \
--v project="odb" \
--v version="$version" \
--v date="$date" \
--v copyright="$copyright" \
-"${ops[@]}" --generate-html --stdout \
---html-prologue-file odb-prologue.xhtml \
---html-epilogue-file odb-epilogue.xhtml \
-"../odb/$i.cli" >"$o.xhtml"
-
- # --man-suffix .1
- cli -I .. \
--v project="odb" \
--v version="$version" \
--v date="$date" \
--v copyright="$copyright" \
-"${ops[@]}" --generate-man --stdout \
---man-prologue-file odb-prologue.1 \
---man-epilogue-file odb-epilogue.1 \
-"../odb/$i.cli" >"$o.1"
-}
-
-compile options odb --suppress-undocumented
-
-# Manual.
-#
-
-#function compile_doc ()
-#{
-# html2ps -f doc.html2ps:a4.html2ps -o "$n-a4.ps" "$n.xhtml"
-# ps2pdf14 -sPAPERSIZE=a4 -dOptimize=true -dEmbedAllFonts=true "$n-a4.ps" "$n-a4.pdf"
-#
-# html2ps -f doc.html2ps:letter.html2ps -o "$n-letter.ps" "$n.xhtml"
-# ps2pdf14 -sPAPERSIZE=letter -dOptimize=true -dEmbedAllFonts=true "$n-letter.ps" "$n-letter.pdf"
-#}
-
-html2ps -f manual.html2ps -o odb-manual.ps manual.xhtml
-ps2pdf14 -dOptimize=true -dEmbedAllFonts=true odb-manual.ps odb-manual.pdf
diff --git a/doc/makefile b/doc/makefile
deleted file mode 100644
index 481f22d..0000000
--- a/doc/makefile
+++ /dev/null
@@ -1,80 +0,0 @@
-# file : doc/makefile
-# license : GNU GPL v3; see accompanying LICENSE file
-
-include $(dir $(lastword $(MAKEFILE_LIST)))../build/bootstrap.make
-
-default := $(out_base)/
-dist := $(out_base)/.dist
-clean := $(out_base)/.clean
-
-# Import.
-#
-$(call import,\
- $(scf_root)/import/cli/stub.make,\
- cli: cli,cli-rules: cli_rules)
-
-# Build.
-#
-$(default): \
-$(out_base)/odb.1 \
-$(out_base)/odb.xhtml \
-$(out_base)/odb-manual.ps \
-$(out_base)/odb-manual.pdf
-
-# Man/html pages.
-#
-$(out_base)/odb.xhtml $(out_base)/odb.1: cli := $(cli)
-
-$(out_base)/odb.xhtml: $(src_root)/odb/options.cli \
- $(src_base)/odb-prologue.xhtml \
- $(src_base)/odb-epilogue.xhtml | $(out_base)/.
- $(call message,cli-html $<,$(cli) --generate-html --stdout \
---suppress-undocumented \
---html-prologue-file $(src_base)/odb-prologue.xhtml \
---html-epilogue-file $(src_base)/odb-epilogue.xhtml $< >$@)
-
-$(out_base)/odb.1: $(src_root)/odb/options.cli \
- $(src_base)/odb-prologue.1 \
- $(src_base)/odb-epilogue.1 | $(out_base)/.
- $(call message,cli-man $<,$(cli) --generate-man --stdout \
---suppress-undocumented \
---man-prologue-file $(src_base)/odb-prologue.1 \
---man-epilogue-file $(src_base)/odb-epilogue.1 $< >$@)
-
-# Manual.
-#
-$(out_base)/odb-manual.ps: $(src_base)/manual.xhtml \
- $(src_base)/manual.html2ps \
- $(src_base)/odb-arch.png \
- $(src_base)/odb-flow.png | $(out_base)/.
- $(call message,html2ps $<,html2ps -f $(src_base)/manual.html2ps -o $@ $<)
-
-$(out_base)/odb-manual.pdf: $(out_base)/odb-manual.ps
- $(call message,ps2pdf $<,ps2pdf14 $< $@)
-
-# Dist.
-#
-$(dist): data_dist := default.css manual.xhtml odb-arch.png odb-flow.png
-$(dist): export ps_docs := odb-manual.ps
-$(dist): export pdf_docs := odb-manual.pdf
-$(dist): export html_docs := $(data_dist) odb.xhtml
-$(dist): export man_docs := odb.1
-$(dist): \
-$(out_base)/odb.1 \
-$(out_base)/odb.xhtml \
-$(out_base)/odb-manual.ps \
-$(out_base)/odb-manual.pdf
- $(call dist-data,$^)
- $(call dist-data,$(data_dist))
- $(call meta-automake)
-
-# Clean.
-#
-$(clean):
- $(call message,rm $$1,rm -f $$1,$(out_base)/odb.1)
- $(call message,rm $$1,rm -f $$1,$(out_base)/odb.xhtml)
- $(call message,rm $$1,rm -f $$1,$(out_base)/odb-manual.ps)
- $(call message,rm $$1,rm -f $$1,$(out_base)/odb-manual.pdf)
-
-$(call include,$(bld_root)/dist.make)
-$(call include,$(bld_root)/meta/automake.make)
diff --git a/doc/manual.html2ps b/doc/manual.html2ps
deleted file mode 100644
index 6acd29d..0000000
--- a/doc/manual.html2ps
+++ /dev/null
@@ -1,69 +0,0 @@
-@html2ps {
- option {
- toc: hb;
- colour: 1;
- hyphenate: 1;
- titlepage: 1;
- }
-
- datefmt: "%B %Y";
-
- titlepage {
- content: "
-<div align=center>
- <h1><big>C++ Object Persistence with ODB</big></h1>
- <h1>&nbsp;</h1>
- <h1>&nbsp;</h1>
- <h1>&nbsp;</h1>
- <h1>&nbsp;</h1>
- <h1>&nbsp;</h1>
- <h1>&nbsp;</h1>
- <h1>&nbsp;</h1>
- <h1>&nbsp;</h1>
-</div>
- <p>Copyright &#169; 2009-2020 Code Synthesis Tools CC.</p>
-
- <p>Permission is granted to copy, distribute and/or modify this
- document under the terms of the
- <a href='http://www.codesynthesis.com/licenses/fdl-1.3.txt'>GNU Free
- Documentation License, version 1.3</a>; with no Invariant Sections,
- no Front-Cover Texts and no Back-Cover Texts.
- </p>
-
- <p>Revision $[revision], $D</p>
-
- <p>This revision of the manual describes ODB $[version] and is available
- in the following formats:
- <a href='http://www.codesynthesis.com/products/odb/doc/manual.xhtml'>XHTML</a>,
- <a href='http://www.codesynthesis.com/products/odb/doc/odb-manual.pdf'>PDF</a>, and
- <a href='http://www.codesynthesis.com/products/odb/doc/odb-manual.ps'>PostScript</a>.</p>";
- }
-
- toc {
- indent: 2em;
- }
-
- header {
- odd-right: $H;
- even-left: $H;
- }
-
- footer {
- odd-left: Revision $[revision], $D;
- odd-center: $T;
- odd-right: $N;
-
- even-left: $N;
- even-center: $T;
- even-right: Revision $[revision], $D;
- }
-}
-
-body {
- font-size: 12pt;
- text-align: justify;
-}
-
-pre {
- font-size: 10pt;
-}
diff --git a/doc/manual.xhtml b/doc/manual.xhtml
deleted file mode 100644
index 5bd49bd..0000000
--- a/doc/manual.xhtml
+++ /dev/null
@@ -1,27015 +0,0 @@
-<?xml version="1.0" encoding="iso-8859-1"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
-
-<head>
- <title>C++ Object Persistence with ODB</title>
-
- <meta name="copyright" content="&#169; 2009-2020 Code Synthesis Tools CC"/>
- <meta name="keywords" content="odb,c++,object,persistence,ORM,relational,database,RDBMS,ODBMS,OODBMS"/>
- <meta name="description" content="C++ Object Persistence with ODB"/>
- <meta name="revision" content="2.4"/>
- <meta name="version" content="2.4.0"/>
-
-<!--
-
-If you make changes to this document, follow these stylistic rules
-for consistency.
-
- - Don't use 'object' for instances of non-persistent classes. Use
- 'instance' instead.
-
- - Each overloaded function should still be referred to as function.
- When saying that a function is overloaded, use term 'version',
- for example The persist() function has two overloaded versions.
- Don't use version to refer to individual functions, use function
- instead. The same holds for constructors.
-
- - Use 'object id' and 'object's identifier'. But not other combinations
- of the two.
-
-@@ Check that parts TOCs are up to date.
-
--->
-
- <link rel="stylesheet" type="text/css" href="default.css" />
-
-<style type="text/css">
- pre {
- padding : 0 0 0 0em;
- margin : 0em 0em 0em 0;
-
- font-size : 102%
- }
-
- body {
- min-width: 48em;
- }
-
- h1 {
- font-weight: bold;
- font-size: 200%;
- line-height: 1.2em;
- }
-
- h2 {
- font-weight : bold;
- font-size : 150%;
-
- padding-top : 0.8em;
- }
-
- h3 {
- font-size : 140%;
- padding-top : 0.8em;
- }
-
- /* Force page break for both PDF and HTML (when printing). */
- hr.page-break {
- height: 0;
- width: 0;
- border: 0;
- visibility: hidden;
-
- page-break-after: always;
- }
-
- /* Adjust indentation for three levels. */
- #container {
- max-width: 48em;
- }
-
- #content {
- padding: 0 0.1em 0 4em;
- /*background-color: red;*/
- }
-
- #content h1 {
- margin-left: -2.06em;
- }
-
- #content h2 {
- margin-left: -1.33em;
- }
-
- /* Title page */
-
- #titlepage {
- padding: 2em 0 1em 0;
- border-bottom: 1px solid black;
- }
-
- #titlepage .title {
- font-weight: bold;
- font-size: 200%;
- text-align: center;
- padding: 1em 0 2em 0;
- }
-
- #titlepage p {
- padding-bottom: 1em;
- }
-
- #titlepage #revision {
- padding-bottom: 0em;
- }
-
- /* Lists */
- ul.list li, ol.list li {
- padding-top : 0.3em;
- padding-bottom : 0.3em;
- }
-
- div.img {
- text-align: center;
- padding: 2em 0 2em 0;
- }
-
- /* */
- dl dt {
- padding : 0.8em 0 0 0;
- }
-
- /* TOC */
- table.toc {
- border-style : none;
- border-collapse : separate;
- border-spacing : 0;
-
- margin : 0.2em 0 0.2em 0;
- padding : 0 0 0 0;
- }
-
- table.toc tr {
- padding : 0 0 0 0;
- margin : 0 0 0 0;
- }
-
- table.toc * td, table.toc * th {
- border-style : none;
- margin : 0 0 0 0;
- vertical-align : top;
- }
-
- table.toc * th {
- font-weight : normal;
- padding : 0em 0.1em 0em 0;
- text-align : left;
- white-space : nowrap;
- }
-
- table.toc * table.toc th {
- padding-left : 1em;
- }
-
- table.toc * td {
- padding : 0em 0 0em 0.7em;
- text-align : left;
- }
-
- /* operators table */
- #operators {
- margin: 2em 0 2em 0;
-
- border-collapse : collapse;
- border : 1px solid;
- border-color : #000000;
-
- font-size : 11px;
- line-height : 14px;
- }
-
- #operators th, #operators td {
- border: 1px solid;
- padding : 0.9em 0.9em 0.7em 0.9em;
- }
-
- #operators th {
- background : #cde8f6;
- }
-
- #operators td {
- text-align: left;
- }
-
- /* scenarios table */
- .scenarios {
- margin: 2em 0 2em 0;
-
- border-collapse : collapse;
- border : 1px solid;
- border-color : #000000;
-
- font-size : 11px;
- line-height : 14px;
- }
-
- .scenarios th, .scenarios td {
- border: 1px solid;
- padding : 0.9em 0.9em 0.7em 0.9em;
- }
-
- .scenarios th {
- background : #cde8f6;
- }
-
- .scenarios td {
- text-align: left;
- }
-
- /* specifiers table */
- .specifiers {
- margin: 2em 0 2em 0;
-
- border-collapse : collapse;
- border : 1px solid;
- border-color : #000000;
-
- font-size : 11px;
- line-height : 14px;
- }
-
- .specifiers th, .specifiers td {
- border: 1px solid;
- padding : 0.9em 0.9em 0.7em 0.9em;
- }
-
- .specifiers th {
- background : #cde8f6;
- }
-
- .specifiers td {
- text-align: left;
- }
-
- /* mapping table */
- #mapping {
- margin: 2em 0 2em 0;
-
- border-collapse : collapse;
- border : 1px solid;
- border-color : #000000;
-
- font-size : 11px;
- line-height : 14px;
- }
-
- #mapping th, #mapping td {
- border: 1px solid;
- padding : 0.9em 0.9em 0.7em 0.9em;
- }
-
- #mapping th {
- background : #cde8f6;
- }
-
- #mapping td {
- text-align: left;
- }
-
-</style>
-
-
-</head>
-
-<body>
-<div id="container">
- <div id="content">
-
- <div class="noprint">
-
- <div id="titlepage">
- <div class="title">C++ Object Persistence with ODB</div>
-
- <p>Copyright &#169; 2009-2020 Code Synthesis Tools CC.</p>
-
- <p>Permission is granted to copy, distribute and/or modify this
- document under the terms of the
- <a href="http://www.codesynthesis.com/licenses/fdl-1.3.txt">GNU Free
- Documentation License, version 1.3</a>; with no Invariant Sections,
- no Front-Cover Texts and no Back-Cover Texts.</p>
-
- <!-- REMEMBER TO CHANGE VERSIONS IN THE META TAGS ABOVE! -->
- <p id="revision">Revision 2.4, February 2015</p>
- <p>This revision of the manual describes ODB 2.4.0 and is available
- in the following formats:
- <a href="http://www.codesynthesis.com/products/odb/doc/manual.xhtml">XHTML</a>,
- <a href="http://www.codesynthesis.com/products/odb/doc/odb-manual.pdf">PDF</a>, and
- <a href="http://www.codesynthesis.com/products/odb/doc/odb-manual.ps">PostScript</a>.</p>
- </div>
-
- <hr class="page-break"/>
- <h1>Table of Contents</h1>
-
- <table class="toc">
- <tr>
- <th></th><td><a href="#0">Preface</a>
- <table class="toc">
- <tr><th></th><td><a href="#0.1">About This Document</a></td></tr>
- <tr><th></th><td><a href="#0.2">More Information</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th colspan="2"><a href="#I">PART I OBJECT-RELATIONAL MAPPING</a></th>
- </tr>
-
- <tr>
- <th>1</th><td><a href="#1">Introduction</a>
- <table class="toc">
- <tr><th>1.1</th><td><a href="#1.1">Architecture and Workflow</a></td></tr>
- <tr><th>1.2</th><td><a href="#1.2">Benefits</a></td></tr>
- <tr><th>1.3</th><td><a href="#1.3">Supported C++ Standards</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>2</th><td><a href="#2">Hello World Example</a>
- <table class="toc">
- <tr><th>2.1</th><td><a href="#2.1">Declaring Persistent Classes</a></td></tr>
- <tr><th>2.2</th><td><a href="#2.2">Generating Database Support Code</a></td></tr>
- <tr><th>2.3</th><td><a href="#2.3">Compiling and Running</a></td></tr>
- <tr><th>2.4</th><td><a href="#2.4">Making Objects Persistent</a></td></tr>
- <tr><th>2.5</th><td><a href="#2.5">Querying the Database for Objects</a></td></tr>
- <tr><th>2.6</th><td><a href="#2.6">Updating Persistent Objects</a></td></tr>
- <tr><th>2.7</th><td><a href="#2.7">Defining and Using Views</a></td></tr>
- <tr><th>2.8</th><td><a href="#2.8">Deleting Persistent Objects</a></td></tr>
- <tr><th>2.9</th><td><a href="#2.9">Changing Persistent Classes</a></td></tr>
- <tr><th>2.10</th><td><a href="#2.10">Accessing Multiple Databases</a></td></tr>
- <tr><th>2.11</th><td><a href="#2.11">Summary</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>3</th><td><a href="#3">Working with Persistent Objects</a>
- <table class="toc">
- <tr><th>3.1</th><td><a href="#3.1">Concepts and Terminology</a></td></tr>
- <tr><th>3.2</th><td><a href="#3.2">Declaring Persistent Objects and Values</a></td></tr>
- <tr><th>3.3</th><td><a href="#3.3">Object and View Pointers</a></td></tr>
- <tr><th>3.4</th><td><a href="#3.4">Database</a></td></tr>
- <tr><th>3.5</th><td><a href="#3.5">Transactions</a></td></tr>
- <tr><th>3.6</th><td><a href="#3.6">Connections</a></td></tr>
- <tr><th>3.7</th><td><a href="#3.7">Error Handling and Recovery</a></td></tr>
- <tr><th>3.8</th><td><a href="#3.8">Making Objects Persistent</a></td></tr>
- <tr><th>3.9</th><td><a href="#3.9">Loading Persistent Objects</a></td></tr>
- <tr><th>3.10</th><td><a href="#3.10">Updating Persistent Objects</a></td></tr>
- <tr><th>3.11</th><td><a href="#3.11">Deleting Persistent Objects</a></td></tr>
- <tr><th>3.12</th><td><a href="#3.12">Executing Native SQL Statements</a></td></tr>
- <tr><th>3.13</th><td><a href="#3.13">Tracing SQL Statement Execution</a></td></tr>
- <tr><th>3.14</th><td><a href="#3.14">ODB Exceptions</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>4</th><td><a href="#4">Querying the Database</a>
- <table class="toc">
- <tr><th>4.1</th><td><a href="#4.1">ODB Query Language</a></td></tr>
- <tr><th>4.2</th><td><a href="#4.2">Parameter Binding</a></td></tr>
- <tr><th>4.3</th><td><a href="#4.3">Executing a Query</a></td></tr>
- <tr><th>4.4</th><td><a href="#4.4">Query Result</a></td></tr>
- <tr><th>4.5</th><td><a href="#4.5">Prepared Queries</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>5</th><td><a href="#5">Containers</a>
- <table class="toc">
- <tr><th>5.1</th><td><a href="#5.1">Ordered Containers</a></td></tr>
- <tr><th>5.2</th><td><a href="#5.2">Set and Multiset Containers</a></td></tr>
- <tr><th>5.3</th><td><a href="#5.3">Map and Multimap Containers</a></td></tr>
- <tr>
- <th>5.4</th><td><a href="#5.4">Change-Tracking Containers</a>
- <table class="toc">
- <tr><th>5.4.1</th><td><a href="#5.4.1">Change-Tracking <code>vector</code></a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>5.5</th><td><a href="#5.5">Using Custom Containers</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>6</th><td><a href="#6">Relationships</a>
- <table class="toc">
- <tr>
- <th>6.1</th><td><a href="#6.1">Unidirectional Relationships</a>
- <table class="toc">
- <tr><th>6.1.1</th><td><a href="#6.1.1">To-One Relationships</a></td></tr>
- <tr><th>6.1.2</th><td><a href="#6.1.2">To-Many Relationships</a></td></tr>
- </table>
- </td>
- </tr>
- <tr>
- <th>6.2</th><td><a href="#6.2">Bidirectional Relationships</a>
- <table class="toc">
- <tr><th>6.2.1</th><td><a href="#6.2.1">One-to-One Relationships</a></td></tr>
- <tr><th>6.2.2</th><td><a href="#6.2.2">One-to-Many Relationships</a></td></tr>
- <tr><th>6.2.3</th><td><a href="#6.2.3">Many-to-Many Relationships</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>6.3</th><td><a href="#6.3">Circular Relationships</a></td></tr>
- <tr><th>6.4</th><td><a href="#6.4">Lazy Pointers</a></td></tr>
- <tr><th>6.5</th><td><a href="#6.5">Using Custom Smart Pointers</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>7</th><td><a href="#7">Value Types</a>
- <table class="toc">
- <tr><th>7.1</th><td><a href="#7.1">Simple Value Types</a></td></tr>
- <tr>
- <th>7.2</th><td><a href="#7.2">Composite Value Types</a>
- <table class="toc">
- <tr><th>7.2.1</th><td><a href="#7.2.1">Composite Object Ids</a></td></tr>
- <tr><th>7.2.2</th><td><a href="#7.2.2">Composite Value Column and Table Names</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>7.3</th><td><a href="#7.3">Pointers and <code>NULL</code> Value Semantics</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>8</th><td><a href="#8">Inheritance</a>
- <table class="toc">
- <tr><th>8.1</th><td><a href="#8.1">Reuse Inheritance</a></td></tr>
- <tr>
- <th>8.2</th><td><a href="#8.2">Polymorphism Inheritance</a>
- <table class="toc">
- <tr><th>8.2.1</th><td><a href="#8.2.1">Performance and Limitations</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>8.3</th><td><a href="#8.3">Mixed Inheritance</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>9</th><td><a href="#9">Sections</a>
- <table class="toc">
- <tr><th>9.1</th><td><a href="#9.1">Sections and Inheritance</a></td></tr>
- <tr><th>9.2</th><td><a href="#9.2">Sections and Optimistic Concurrency</a></td></tr>
- <tr><th>9.3</th><td><a href="#9.3">Sections and Lazy Pointers</a></td></tr>
- <tr><th>9.4</th><td><a href="#9.4">Sections and Change-Tracking Containers</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>10</th><td><a href="#10">Views</a>
- <table class="toc">
- <tr><th>10.1</th><td><a href="#10.1">Object Views</a></td></tr>
- <tr><th>10.2</th><td><a href="#10.2">Object Loading Views</a></td></tr>
- <tr><th>10.3</th><td><a href="#10.3">Table Views</a></td></tr>
- <tr><th>10.4</th><td><a href="#10.4">Mixed Views</a></td></tr>
- <tr><th>10.5</th><td><a href="#10.5">View Query Conditions</a></td></tr>
- <tr><th>10.6</th><td><a href="#10.6">Native Views</a></td></tr>
- <tr><th>10.7</th><td><a href="#10.7">Other View Features and Limitations</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>11</th><td><a href="#11">Session</a>
- <table class="toc">
- <tr><th>11.1</th><td><a href="#11.1">Object Cache</a></td></tr>
- <tr><th>11.2</th><td><a href="#11.2">Custom Sessions</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>12</th><td><a href="#12">Optimistic Concurrency</a></td>
- </tr>
-
- <tr>
- <th>13</th><td><a href="#13">Database Schema Evolution</a>
- <table class="toc">
- <tr><th>13.1</th><td><a href="#13.1">Object Model Version and Changelog</a></td></tr>
- <tr><th>13.2</th><td><a href="#13.2">Schema Migration</a></td></tr>
- <tr>
- <th>13.3</th><td><a href="#13.3">Data Migration</a>
- <table class="toc">
- <tr><th>13.3.1</th><td><a href="#13.3.1">Immediate Data Migration</a></td></tr>
- <tr><th>13.3.2</th><td><a href="#13.3.2">Gradual Data Migration</a></td></tr>
- </table>
- </td>
- </tr>
- <tr>
- <th>13.4</th><td><a href="#13.4">Soft Object Model Changes</a>
- <table class="toc">
- <tr><th>13.4.1</th><td><a href="#13.4.1">Reuse Inheritance Changes</a></td></tr>
- <tr><th>13.4.2</th><td><a href="#13.4.2">Polymorphism Inheritance Changes</a></td></tr>
- </table>
- </td>
- </tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>14</th><td><a href="#14">ODB Pragma Language</a>
- <table class="toc">
- <tr>
- <th>14.1</th><td><a href="#14.1">Object Type Pragmas</a>
- <table class="toc">
- <tr><th>14.1.1</th><td><a href="#14.1.1"><code>table</code></a></td></tr>
- <tr><th>14.1.2</th><td><a href="#14.1.2"><code>pointer</code></a></td></tr>
- <tr><th>14.1.3</th><td><a href="#14.1.3"><code>abstract</code></a></td></tr>
- <tr><th>14.1.4</th><td><a href="#14.1.4"><code>readonly</code></a></td></tr>
- <tr><th>14.1.5</th><td><a href="#14.1.5"><code>optimistic</code></a></td></tr>
- <tr><th>14.1.6</th><td><a href="#14.1.6"><code>no_id</code></a></td></tr>
- <tr><th>14.1.7</th><td><a href="#14.1.7"><code>callback</code></a></td></tr>
- <tr><th>14.1.8</th><td><a href="#14.1.8"><code>schema</code></a></td></tr>
- <tr><th>14.1.9</th><td><a href="#14.1.9"><code>polymorphic</code></a></td></tr>
- <tr><th>14.1.10</th><td><a href="#14.1.10"><code>session</code></a></td></tr>
- <tr><th>14.1.11</th><td><a href="#14.1.11"><code>definition</code></a></td></tr>
- <tr><th>14.1.12</th><td><a href="#14.1.12"><code>transient</code></a></td></tr>
- <tr><th>14.1.13</th><td><a href="#14.1.13"><code>sectionable</code></a></td></tr>
- <tr><th>14.1.14</th><td><a href="#14.1.14"><code>deleted</code></a></td></tr>
- <tr><th>14.1.15</th><td><a href="#14.1.15"><code>bulk</code></a></td></tr>
- </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>
- </table>
- </td>
- </tr>
- <tr><th>19.6</th><td><a href="#19.6">PostgreSQL Index Definition</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>20</th><td><a href="#20">Oracle Database</a>
- <table class="toc">
- <tr>
- <th>20.1</th><td><a href="#20.1">Oracle Type Mapping</a>
- <table class="toc">
- <tr><th>20.1.1</th><td><a href="#20.1.1">String Type Mapping</a></td></tr>
- <tr><th>20.1.2</th><td><a href="#20.1.2">Binary Type Mapping</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>20.2</th><td><a href="#20.2">Oracle Database Class</a></td></tr>
- <tr><th>20.3</th><td><a href="#20.3">Oracle Connection and Connection Factory</a></td></tr>
- <tr><th>20.4</th><td><a href="#20.4">Oracle Exceptions</a></td></tr>
- <tr>
- <th>20.5</th><td><a href="#20.5">Oracle Limitations</a>
- <table class="toc">
- <tr><th>20.5.1</th><td><a href="#20.5.1">Identifier Truncation</a></td></tr>
- <tr><th>20.5.2</th><td><a href="#20.5.2">Query Result Caching</a></td></tr>
- <tr><th>20.5.3</th><td><a href="#20.5.3">Foreign Key Constraints</a></td></tr>
- <tr><th>20.5.4</th><td><a href="#20.5.4">Unique Constraint Violations</a></td></tr>
- <tr><th>20.5.5</th><td><a href="#20.5.5">Large <code>FLOAT</code> and <code>NUMBER</code> Types</a></td></tr>
- <tr><th>20.5.6</th><td><a href="#20.5.6">Timezones</a></td></tr>
- <tr><th>20.5.7</th><td><a href="#20.5.7"><code>LONG</code> Types</a></td></tr>
- <tr><th>20.5.8</th><td><a href="#20.5.8">LOB Types and By-Value Accessors/Modifiers</a></td></tr>
- <tr><th>20.5.9</th><td><a href="#20.5.9">Database Schema Evolution</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>20.6</th><td><a href="#20.6">Oracle Index Definition</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>21</th><td><a href="#21">Microsoft SQL Server Database</a>
- <table class="toc">
- <tr>
- <th>21.1</th><td><a href="#21.1">SQL Server Type Mapping</a>
- <table class="toc">
- <tr><th>21.1.1</th><td><a href="#21.1.1">String Type Mapping</a></td></tr>
- <tr><th>21.1.2</th><td><a href="#21.1.2">Binary Type and <code>UNIQUEIDENTIFIER</code> Mapping</a></td></tr>
- <tr><th>21.1.3</th><td><a href="#21.1.3"><code>ROWVERSION</code> Mapping</a></td></tr>
- <tr><th>21.1.4</th><td><a href="#21.1.4">Long String and Binary Types</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>21.2</th><td><a href="#21.2">SQL Server Database Class</a></td></tr>
- <tr><th>21.3</th><td><a href="#21.3">SQL Server Connection and Connection Factory</a></td></tr>
- <tr><th>21.4</th><td><a href="#21.4">SQL Server Exceptions</a></td></tr>
- <tr>
- <th>21.5</th><td><a href="#21.5">SQL Server Limitations</a>
- <table class="toc">
- <tr><th>21.5.1</th><td><a href="#21.5.1">Query Result Caching</a></td></tr>
- <tr><th>21.5.2</th><td><a href="#21.5.2">Foreign Key Constraints</a></td></tr>
- <tr><th>21.5.3</th><td><a href="#21.5.3">Unique Constraint Violations</a></td></tr>
- <tr><th>21.5.4</th><td><a href="#21.5.4">Multi-threaded Windows Applications</a></td></tr>
- <tr><th>21.5.5</th><td><a href="#21.5.5">Affected Row Count and DDL Statements</a></td></tr>
- <tr><th>21.5.6</th><td><a href="#21.5.6">Long Data and Auto Object Ids, <code>ROWVERSION</code></a></td></tr>
- <tr><th>21.5.7</th><td><a href="#21.5.7">Long Data and By-Value Accessors/Modifiers</a></td></tr>
- <tr><th>21.5.8</th><td><a href="#21.5.8">Bulk Update and <code>ROWVERSION</code></a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>21.6</th><td><a href="#21.6">SQL Server Index Definition</a></td></tr>
- <tr><th>21.7</th><td><a href="#21.7">SQL Server Stored Procedures</a></td></tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th colspan="2"><a href="#III">PART III PROFILES</a></th>
- </tr>
-
- <tr>
- <th>22</th><td><a href="#22">Profiles Introduction</a></td>
- </tr>
-
- <tr>
- <th>23</th><td><a href="#23">Boost Profile</a>
- <table class="toc">
- <tr><th>23.1</th><td><a href="#23.1">Smart Pointers Library</a></td></tr>
- <tr><th>23.2</th><td><a href="#23.2">Unordered Containers Library</a></td></tr>
- <tr><th>23.3</th><td><a href="#23.3">Multi-Index Container Library</a></td></tr>
- <tr><th>23.4</th><td><a href="#23.4">Optional Library</a></td></tr>
- <tr>
- <th>23.5</th><td><a href="#23.5">Date Time Library</a>
- <table class="toc">
- <tr><th>23.5.1</th><td><a href="#23.5.1">MySQL Database Type Mapping</a></td></tr>
- <tr><th>23.5.2</th><td><a href="#23.5.2">SQLite Database Type Mapping</a></td></tr>
- <tr><th>23.5.3</th><td><a href="#23.5.3">PostgreSQL Database Type Mapping</a></td></tr>
- <tr><th>23.5.4</th><td><a href="#23.5.4">Oracle Database Type Mapping</a></td></tr>
- <tr><th>23.5.5</th><td><a href="#23.5.5">SQL Server Database Type Mapping</a></td></tr>
- </table>
- </td>
- </tr>
- <tr>
- <th>23.6</th><td><a href="#23.6">Uuid Library</a>
- <table class="toc">
- <tr><th>23.6.1</th><td><a href="#23.6.1">MySQL Database Type Mapping</a></td></tr>
- <tr><th>23.6.2</th><td><a href="#23.6.2">SQLite Database Type Mapping</a></td></tr>
- <tr><th>23.6.3</th><td><a href="#23.6.3">PostgreSQL Database Type Mapping</a></td></tr>
- <tr><th>23.6.4</th><td><a href="#23.6.4">Oracle Database Type Mapping</a></td></tr>
- <tr><th>23.6.5</th><td><a href="#23.6.5">SQL Server Database Type Mapping</a></td></tr>
- </table>
- </td>
- </tr>
- </table>
- </td>
- </tr>
-
- <tr>
- <th>24</th><td><a href="#24">Qt Profile</a>
- <table class="toc">
- <tr>
- <th>24.1</th><td><a href="#24.1">Basic Types Library</a>
- <table class="toc">
- <tr><th>24.1.1</th><td><a href="#24.1.1">MySQL Database Type Mapping</a></td></tr>
- <tr><th>24.1.2</th><td><a href="#24.1.2">SQLite Database Type Mapping</a></td></tr>
- <tr><th>24.1.3</th><td><a href="#24.1.3">PostgreSQL Database Type Mapping</a></td></tr>
- <tr><th>24.1.4</th><td><a href="#24.1.4">Oracle Database Type Mapping</a></td></tr>
- <tr><th>24.1.5</th><td><a href="#24.1.5">SQL Server Database Type Mapping</a></td></tr>
- </table>
- </td>
- </tr>
- <tr><th>24.2</th><td><a href="#24.2">Smart Pointers Library</a></td></tr>
- <tr>
- <th>24.3</th><td><a href="#24.3">Containers Library</a>
- <table class="toc">
- <tr><th>24.3.1</th><td><a href="#24.3.1">Change-Tracking <code>QList</code></a></td></tr>
- </table>
- </td>
- </tr>
- <tr>
- <th>24.4</th><td><a href="#24.4">Date Time Library</a>
- <table class="toc">
- <tr><th>24.4.1</th><td><a href="#24.4.1">MySQL Database Type Mapping</a></td></tr>
- <tr><th>24.4.2</th><td><a href="#24.4.2">SQLite Database Type Mapping</a></td></tr>
- <tr><th>24.4.3</th><td><a href="#24.4.3">PostgreSQL Database Type Mapping</a></td></tr>
- <tr><th>24.4.4</th><td><a href="#24.4.4">Oracle Database Type Mapping</a></td></tr>
- <tr><th>24.4.5</th><td><a href="#24.4.5">SQL Server Database Type Mapping</a></td></tr>
- </table>
- </td>
- </tr>
- </table>
- </td>
- </tr>
-
- </table>
- </div>
-
- <hr class="page-break"/>
- <h1><a name="0">Preface</a></h1>
-
- <p>As more critical aspects of our lives become dependant on software
- systems, more and more applications are required to save the data
- they work on in persistent and reliable storage. Database management
- systems and, in particular, relational database management systems
- (RDBMS) are commonly used for such storage. However, while the
- application development techniques and programming languages have
- evolved significantly over the past decades, the relational database
- technology in this area stayed relatively unchanged. In particular,
- this led to the now infamous mismatch between the object-oriented
- model used by many modern applications and the relational model still
- used by RDBMS.</p>
-
- <p>While relational databases may be inconvenient to use from modern
- programming languages, they are still the main choice for many
- applications due to their maturity, reliability, as well as the
- availability of tools and alternative implementations.</p>
-
- <p>To allow application developers to utilize relational databases
- from their object-oriented applications, a technique called
- object-relational mapping (ORM) is often used. It involves a
- conversion layer that maps between objects in the application's
- memory and their relational representation in the database. While
- the object-relational mapping code can be written manually,
- automated ORM systems are available for most object-oriented
- programming languages in use today.</p>
-
- <p>ODB is an ORM system for the C++ programming language. It was
- designed and implemented with the following main goals:</p>
-
- <ul class="list">
- <li>Provide a fully-automatic ORM system. In particular, the
- application developer should not have to manually write any
- mapping code, neither for persistent classes nor for their
- data member. </li>
-
- <li>Provide clean and easy to use object-oriented persistence
- model and database APIs that support the development of realistic
- applications for a wide variety of domains.</li>
-
- <li>Provide a portable and thread-safe implementation. ODB should be
- written in standard C++ and capable of persisting any standard
- C++ classes.</li>
-
- <li>Provide profiles that integrate ODB with type systems of
- widely-used frameworks and libraries such as Qt and Boost.</li>
-
- <li>Provide a high-performance and low overhead implementation. ODB
- should make efficient use of database and application resources.</li>
-
- </ul>
-
-
- <h2><a name="0.1">About This Document</a></h2>
-
- <p>The goal of this manual is to provide you with an understanding
- of the object persistence model and APIs which are implemented by ODB.
- As such, this document is intended for C++ application developers and
- software architects who are looking for a C++ object persistence
- solution. Prior experience with C++ is required to understand
- this document. A basic understanding of relational database systems
- is advantageous but not expected or required.</p>
-
-
- <h2><a name="0.2">More Information</a></h2>
-
- <p>Beyond this manual, you may also find the following sources of
- information useful:</p>
-
- <ul class="list">
- <li><a href="http://www.codesynthesis.com/products/odb/doc/odb.xhtml">ODB
- Compiler Command Line Manual.</a></li>
-
- <li>The <code>INSTALL</code> files in the ODB source packages provide
- build instructions for various platforms.</li>
-
- <li>The <code>odb-examples</code> package contains a collection of
- examples and a README file with an overview of each example.</li>
-
- <li>The <a href="http://www.codesynthesis.com/mailman/listinfo/odb-users">odb-users</a>
- mailing list is the place to ask technical questions about ODB.
- Furthermore, the searchable
- <a href="http://www.codesynthesis.com/pipermail/odb-users/">archives</a>
- may already have answers to some of your questions.</li>
-
- </ul>
-
-
- <!-- PART -->
-
-
- <hr class="page-break"/>
- <h1><a name="I">PART I&nbsp;&nbsp;
- <span style="font-weight: normal;">OBJECT-RELATIONAL MAPPING</span></a></h1>
-
- <p>Part I describes the essential database concepts, APIs, and tools that
- together comprise the object-relational mapping for C++ as implemented
- by ODB. It consists of the following chapters.</p>
-
- <table class="toc">
- <tr><th>1</th><td><a href="#1">Introduction</a></td></tr>
- <tr><th>2</th><td><a href="#2">Hello World Example</a></td></tr>
- <tr><th>3</th><td><a href="#3">Working with Persistent Objects</a></td></tr>
- <tr><th>4</th><td><a href="#4">Querying the Database</a></td></tr>
- <tr><th>5</th><td><a href="#5">Containers</a></td></tr>
- <tr><th>6</th><td><a href="#6">Relationships</a></td></tr>
- <tr><th>7</th><td><a href="#7">Value Types</a></td></tr>
- <tr><th>8</th><td><a href="#8">Inheritance</a></td></tr>
- <tr><th>10</th><td><a href="#10">Views</a></td></tr>
- <tr><th>11</th><td><a href="#11">Session</a></td></tr>
- <tr><th>12</th><td><a href="#12">Optimistic Concurrency</a></td></tr>
- <tr><th>13</th><td><a href="#13">Database Schema Evolution</a></td></tr>
- <tr><th>14</th><td><a href="#14">ODB Pragma Language</a></td></tr>
- </table>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="1">1 Introduction</a></h1>
-
- <p>ODB is an object-relational mapping (ORM) system for C++. It provides
- tools, APIs, and library support that allow you to persist C++ objects
- to a relational database (RDBMS) without having to deal with tables,
- columns, or SQL and without manually writing any of the mapping code.</p>
-
- <p>ODB is highly flexible and customizable. It can either completely
- hide the relational nature of the underlying database or expose
- some of the details as required. For example, you can automatically
- map basic C++ types to suitable SQL types, generate the relational
- database schema for your persistent classes, and use simple, safe,
- and yet powerful object query language instead of SQL. Or you can
- assign SQL types to individual data members, use the existing
- database schema, run native SQL <code>SELECT</code> queries, and
- call stored procedures. In fact, at an extreme, ODB can be used
- as <em>just</em> a convenient way to handle results of native SQL
- queries.</p>
-
- <p>ODB is not a framework. It does not dictate how you should write
- your application. Rather, it is designed to fit into your
- style and architecture by only handling object persistence
- and not interfering with any other functionality. There is
- no common base type that all persistent classes should derive
- from nor are there any restrictions on the data member types
- in persistent classes. Existing classes can be made persistent
- with a few or no modifications.</p>
-
- <p>ODB has been designed for high performance and low memory
- overhead. Prepared statements are used to send and receive
- object state in binary format instead of text which reduces
- the load on the application and the database server. Extensive
- caching of connections, prepared statements, and buffers saves
- time and resources on connection establishment, statement parsing,
- and memory allocations. For each supported database system the
- native C API is used instead of ODBC or higher-level wrapper
- APIs to reduce overhead and provide the most efficient implementation
- for each database operation. Finally, persistent classes have
- zero memory overhead. There are no hidden "database" members
- that each class must have nor are there per-object data structures
- allocated by ODB.</p>
-
- <p>In this chapter we present a high-level overview of ODB.
- We will start with the ODB architecture and then outline the
- workflow of building an application that uses ODB. We will
- then continue by contrasting the drawbacks of the traditional
- way of saving C++ objects to relational databases with the
- benefits of using ODB for object persistence. We conclude the
- chapter by discussing the C++ standards supported by ODB. The
- next chapter takes a more hands-on approach and shows the
- concrete steps necessary to implement object persistence in
- a simple "Hello World" application.</p>
-
- <h2><a name="1.1">1.1 Architecture and Workflow</a></h2>
-
- <p>From the application developer's perspective, ODB
- consists of three main components: the ODB compiler, the common
- runtime library, called <code>libodb</code>, and the
- database-specific runtime libraries, called
- <code>libodb-&lt;database></code>, where &lt;database> is
- the name of the database system this runtime
- is for, for example, <code>libodb-mysql</code>. For instance,
- if the application is going to use the MySQL database for
- object persistence, then the three ODB components that this
- application will use are the ODB compiler, <code>libodb</code>
- and <code>libodb-mysql</code>.</p>
-
- <p>The ODB compiler generates the database support code for
- persistent classes in your application. The input to the ODB
- compiler is one or more C++ header files defining C++ classes
- that you want to make persistent. For each input header file
- the ODB compiler generates a set of C++ source files implementing
- conversion between persistent C++ classes defined in this
- header and their database representation. The ODB compiler
- can also generate a database schema file that creates tables
- necessary to store the persistent classes.</p>
-
- <p>The ODB compiler is a real C++ compiler except that it produces
- C++ instead of assembly or machine code. In particular, it is not
- an ad-hoc header pre-processor that is only capable of recognizing
- a subset of C++. ODB is capable of parsing any standard C++ code.</p>
-
- <p>The common runtime library defines database system-independent
- interfaces that your application can use to manipulate persistent
- objects. The database-specific runtime library provides implementations
- of these interfaces for a concrete database as well as other
- database-specific utilities that are used by the generated code.
- Normally, the application does not use the database-specific
- runtime library directly but rather works with it via the common
- interfaces from <code>libodb</code>. The following diagram shows
- the object persistence architecture of an application that uses
- MySQL as the underlying database system:</p>
-
- <!-- align=center is needed for html2ps -->
- <div class="img" align="center"><img src="odb-arch.png"/></div>
-
- <p>The ODB system also defines two special-purpose languages:
- the ODB Pragma Language and ODB Query Language. The ODB Pragma
- Language is used to communicate various properties of persistent
- classes to the ODB compiler by means of special <code>#pragma</code>
- directives embedded in the C++ header files. It controls aspects
- of the object-relational mapping such as names of tables and columns
- that are used for persistent classes and their members or mapping between
- C++ types and database types.</p>
-
- <p>The ODB Query Language is an object-oriented database query
- language that can be used to search for objects matching
- certain criteria. It is modeled after and is integrated into
- C++ allowing you to write expressive and safe queries that look
- and feel like ordinary C++.</p>
-
- <p>The use of the ODB compiler to generate database support code
- adds an additional step to your application build sequence. The
- following diagram outlines the typical build workflow of an
- application that uses ODB:</p>
-
- <!-- align=center is needed for html2ps -->
- <div class="img" align="center"><img src="odb-flow.png"/></div>
-
- <h2><a name="1.2">1.2 Benefits</a></h2>
-
- <p>The traditional way of saving C++ objects to relational databases
- requires that you manually write code which converts between the database
- and C++ representations of each persistent class. The actions that
- such code usually performs include conversion between C++ values and
- strings or database types, preparation and execution of SQL queries,
- as well as handling the result sets. Writing this code manually has
- the following drawbacks:</p>
-
- <ul class="list">
- <li><b>Difficult and time consuming.</b> Writing database conversion
- code for any non-trivial application requires extensive
- knowledge of the specific database system and its APIs.
- It can also take a considerable amount of time to write
- and maintain. Supporting multi-threaded applications can
- complicate this task even further.</li>
-
- <li><b>Suboptimal performance.</b> Optimal conversion often
- requires writing large amounts of extra code, such as
- parameter binding for prepared statements and caching
- of connections, statements, and buffers. Writing code
- like this in an ad-hoc manner is often too difficult
- and time consuming.</li>
-
- <li><b>Database vendor lock-in.</b> The conversion code is written for
- a specific database which makes it hard to switch to another
- database vendor.</li>
-
- <li><b>Lack of type safety.</b> It is easy to misspell column names or
- pass incompatible values in SQL queries. Such errors will
- only be detected at runtime.</li>
-
- <li><b>Complicates the application.</b> The database conversion code
- often ends up interspersed throughout the application making it
- hard to debug, change, and maintain.</li>
- </ul>
-
- <p>In contrast, using ODB for C++ object persistence has the
- following benefits:</p>
-
- <ul class="list">
- <li><b>Ease of use.</b> ODB automatically generates database conversion
- code from your C++ class declarations and allows you to manipulate
- persistent objects using simple and thread-safe object-oriented
- database APIs.</li>
-
- <li><b>Concise code.</b> With ODB hiding the details of the underlying
- database, the application logic is written using the natural object
- vocabulary instead of tables, columns and SQL. The resulting code
- is simpler and thus easier to read and understand.</li>
-
- <li><b>Optimal performance.</b> ODB has been designed for high performance
- and low memory overhead. All the available optimization techniques,
- such as prepared statements and extensive connection, statement,
- and buffer caching, are used to provide the most efficient
- implementation for each database operation.</li>
-
- <li><b>Database portability.</b> Because the database conversion code
- is automatically generated, it is easy to switch from one database
- vendor to another. In fact, it is possible to test your application
- on several database systems before making a choice.</li>
-
- <li><b>Safety.</b> The ODB object persistence and query APIs are
- statically typed. You use C++ identifiers instead of strings
- to refer to object members and the generated code makes sure
- database and C++ types are compatible. All this helps catch
- programming errors at compile-time rather than at runtime.</li>
-
- <li><b>Maintainability.</b> Automatic code generation minimizes the
- effort needed to adapt the application to changes in persistent
- classes. The database support code is kept separately from the
- class declarations and application logic. This makes the
- application easier to debug and maintain.</li>
- </ul>
-
- <p>Overall, ODB provides an easy to use yet flexible and powerful
- object-relational mapping (ORM) system for C++. Unlike other
- ORM implementations for C++ that still require you to write
- database conversion or member registration code for each
- persistent class, ODB keeps persistent classes purely
- declarative. The functional part, the database conversion
- code, is automatically generated by the ODB compiler from
- these declarations.</p>
-
- <h2><a name="1.3">1.3 Supported C++ Standards</a></h2>
-
- <p>ODB provides support for ISO/IEC C++ 1998/2003 (C++98/03),
- ISO/IEC TR 19768 C++ Library Extensions (C++ TR1), and
- ISO/IEC C++ 2011 (C++11). While the majority of the examples in
- this manual use C++98/03, support for the new functionality and
- library components introduced in TR1 and C++11 are discussed
- throughout the document. The <code>c++11</code> example in the
- <code>odb-examples</code> package also shows ODB support for
- various C++11 features.</p>
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="2">2 Hello World Example</a></h1>
-
- <p>In this chapter we will show how to create a simple C++
- application that relies on ODB for object persistence using
- the traditional "Hello World" example. In particular, we will
- discuss how to declare persistent classes, generate database
- support code, as well as compile and run our application. We
- will also learn how to make objects persistent, load, update
- and delete persistent objects, as well as query the database
- for persistent objects that match certain criteria. The example
- also shows how to define and use views, a mechanism that allows
- us to create projections of persistent objects, database tables,
- or to handle results of native SQL queries or stored procedure
- calls.</p>
-
- <p>The code presented in this chapter is based on the
- <code>hello</code> example which can be found in the
- <code>odb-examples</code> package of the ODB distribution.</p>
-
- <h2><a name="2.1">2.1 Declaring Persistent Classes</a></h2>
-
- <p>In our "Hello World" example we will depart slightly from
- the norm and say hello to people instead of the world. People
- in our application will be represented as objects of C++ class
- <code>person</code> which is saved in <code>person.hxx</code>:</p>
-
- <pre class="cxx">
-// person.hxx
-//
-
-#include &lt;string>
-
-class person
-{
-public:
- person (const std::string&amp; first,
- const std::string&amp; last,
- unsigned short age);
-
- const std::string&amp; first () const;
- const std::string&amp; last () const;
-
- unsigned short age () const;
- void age (unsigned short);
-
-private:
- std::string first_;
- std::string last_;
- unsigned short age_;
-};
- </pre>
-
- <p>In order not to miss anyone whom we need to greet, we would like
- to save the <code>person</code> objects in a database. To achieve this
- we declare the <code>person</code> class as persistent:</p>
-
- <pre class="cxx">
-// person.hxx
-//
-
-#include &lt;string>
-
-#include &lt;odb/core.hxx> // (1)
-
-#pragma db object // (2)
-class person
-{
- ...
-
-private:
- person () {} // (3)
-
- friend class odb::access; // (4)
-
- #pragma db id auto // (5)
- unsigned long id_; // (5)
-
- std::string first_;
- std::string last_;
- unsigned short age_;
-};
- </pre>
-
- <p>To be able to save the <code>person</code> objects in the database
- we had to make five changes, marked with (1) to (5), to the original
- class definition. The first change is the inclusion of the ODB
- header <code>&lt;odb/core.hxx></code>. This header provides a number
- of core ODB declarations, such as <code>odb::access</code>, that
- are used to define persistent classes.</p>
-
- <p>The second change is the addition of <code>db&nbsp;object</code>
- pragma just before the class definition. This pragma tells the
- ODB compiler that the class that follows is persistent. Note
- that making a class persistent does not mean that all objects
- of this class will automatically be stored in the database.
- You would still create ordinary or <em>transient</em> instances
- of this class just as you would before. The difference is that
- now you can make such transient instances persistent, as we will
- see shortly.</p>
-
- <p>The third change is the addition of the default constructor.
- The ODB-generated database support code will use this constructor
- when instantiating an object from the persistent state. Just as we have
- done for the <code>person</code> class, you can make the default
- constructor private or protected if you don't want to make it
- available to the users of your class. Note also that with some
- limitations it is possible to have a persistent class without
- the default constructor.</p>
-
- <p>With the fourth change we make the <code>odb::access</code> class a
- friend of our <code>person</code> class. This is necessary to make
- the default constructor and the data members accessible to the
- database support code. If your class has a public default constructor and
- either public data members or public accessors and modifiers for the
- data members, then the <code>friend</code> declaration is unnecessary.</p>
-
- <p>The final change adds a data member called <code>id_</code> which
- is preceded by another pragma. In ODB every persistent object normally
- has a unique, within its class, identifier. Or, in other words, no two
- persistent instances of the same type have equal identifiers. While it
- is possible to define a persistent class without an object id, the number
- of database operations that can be performed on such a class is limited.
- For our class we use an integer id. The <code>db&nbsp;id auto</code>
- pragma that precedes the <code>id_</code> member tells the ODB compiler
- that the following member is the object's identifier. The
- <code>auto</code> specifier indicates that it is a database-assigned
- id. A unique id will be automatically generated by the database and
- assigned to the object when it is made persistent.</p>
-
- <p>In this example we chose to add an identifier because none of
- the existing members could serve the same purpose. However, if
- a class already has a member with suitable properties, then it
- is natural to use that member as an identifier. For example,
- if our <code>person</code> class contained some form of personal
- identification (SSN in the United States or ID/passport number
- in other countries), then we could use that as an id. Or, if
- we stored an email associated with each person, then we could
- have used that if each person is presumed to have a unique
- email address.</p>
-
- <p>As another example, consider the following alternative version
- of the <code>person</code> class. Here we use one of
- the existing data members as id. Also the data members are kept
- private and are instead accessed via public accessor and modifier
- functions. Finally, the ODB pragmas are grouped together and are
- placed after the class definition. They could have also been moved
- into a separate header leaving the original class completely
- unchanged (for more information on such a non-intrusive conversion
- refer to <a href="#14">Chapter 14, "ODB Pragma Language"</a>).</p>
-
- <pre class="cxx">
-class person
-{
-public:
- person ();
-
- const std::string&amp; email () const;
- void email (const std::string&amp;);
-
- const std::string&amp; get_name () const;
- std::string&amp; set_name ();
-
- unsigned short getAge () const;
- void setAge (unsigned short);
-
-private:
- std::string email_;
- std::string name_;
- unsigned short age_;
-};
-
-#pragma db object(person)
-#pragma db member(person::email_) id
- </pre>
-
- <p>Now that we have the header file with the persistent class, let's
- see how we can generate that database support code.</p>
-
- <h2><a name="2.2">2.2 Generating Database Support Code</a></h2>
-
- <p>The persistent class definition that we created in the previous
- section was particularly light on any code that could actually
- do the job and store the person's data to a database. There
- was no serialization or deserialization code, not even data member
- registration, that you would normally have to write by hand in
- other ORM libraries for C++. This is because in ODB code
- that translates between the database and C++ representations
- of an object is automatically generated by the ODB compiler.</p>
-
- <p>To compile the <code>person.hxx</code> header we created in the
- previous section and generate the support code for the MySQL
- database, we invoke the ODB compiler from a terminal (UNIX) or
- a command prompt (Windows):</p>
-
- <pre class="terminal">
-odb -d mysql --generate-query person.hxx
- </pre>
-
- <p>We will use MySQL as the database of choice in the remainder of
- this chapter, though other supported database systems can be used
- instead.</p>
-
- <p>If you haven't installed the common ODB runtime library
- (<code>libodb</code>) or installed it into a directory where
- C++ compilers don't search for headers by default,
- then you may get the following error:</p>
-
- <pre class="terminal">
-person.hxx:10:24: fatal error: odb/core.hxx: No such file or directory
- </pre>
-
- <p>To resolve this you will need to specify the <code>libodb</code> headers
- location with the <code>-I</code> preprocessor option, for example:</p>
-
- <pre class="terminal">
-odb -I.../libodb -d mysql --generate-query person.hxx
- </pre>
-
- <p>Here <code>.../libodb</code> represents the path to the
- <code>libodb</code> directory.</p>
-
- <p>The above invocation of the ODB compiler produces three C++ files:
- <code>person-odb.hxx</code>, <code>person-odb.ixx</code>,
- <code>person-odb.cxx</code>. You normally don't use types
- or functions contained in these files directly. Rather, all
- you have to do is include <code>person-odb.hxx</code> in
- C++ files where you are performing database operations
- with classes from <code>person.hxx</code> as well as compile
- <code>person-odb.cxx</code> and link the resulting object
- file to your application.</p>
-
- <p>You may be wondering what the <code>--generate-query</code>
- option is for. It instructs the ODB compiler to generate
- optional query support code that we will use later in our
- "Hello World" example. Another option that we will find
- useful is <code>--generate-schema</code>. This option
- makes the ODB compiler generate a fourth file,
- <code>person.sql</code>, which is the database schema
- for the persistent classes defined in <code>person.hxx</code>:</p>
-
- <pre class="terminal">
-odb -d mysql --generate-query --generate-schema person.hxx
- </pre>
-
- <p>The database schema file contains SQL statements that creates
- tables necessary to store the persistent classes. We will learn
- how to use it in the next section.</p>
-
- <p>If you would like to see a list of all the available ODB compiler
- options, refer to the
- <a href="http://www.codesynthesis.com/products/odb/doc/odb.xhtml">ODB
- Compiler Command Line Manual</a>.</p>
-
- <p>Now that we have the persistent class and the database support
- code, the only part that is left is the application code that
- does something useful with all of this. But before we move on to
- the fun part, let's first learn how to build and run an application
- that uses ODB. This way when we have some application code
- to try, there are no more delays before we can run it.</p>
-
- <h2><a name="2.3">2.3 Compiling and Running</a></h2>
-
- <p>Assuming that the <code>main()</code> function with the application
- code is saved in <code>driver.cxx</code> and the database support
- code and schema are generated as described in the previous section,
- to build our application we will first need to compile all the C++
- source files and then link them with two ODB runtime libraries.</p>
-
- <p>On UNIX, the compilation part can be done with the following commands
- (substitute <code>c++</code> with your C++ compiler name; for Microsoft
- Visual Studio setup, see the <code>odb-examples</code> package):</p>
-
- <pre class="terminal">
-c++ -c driver.cxx
-c++ -c person-odb.cxx
- </pre>
-
- <p>Similar to the ODB compilation, if you get an error stating that
- a header in <code>odb/</code> or <code>odb/mysql</code> directory
- is not found, you will need to use the <code>-I</code>
- preprocessor option to specify the location of the common ODB runtime
- library (<code>libodb</code>) and MySQL ODB runtime library
- (<code>libodb-mysql</code>).</p>
-
- <p>Once the compilation is done, we can link the application with
- the following command:</p>
-
- <pre class="terminal">
-c++ -o driver driver.o person-odb.o -lodb-mysql -lodb
- </pre>
-
- <p>Notice that we link our application with two ODB libraries:
- <code>libodb</code> which is a common runtime library and
- <code>libodb-mysql</code> which is a MySQL runtime library
- (if you use another database, then the name of this library
- will change accordingly). If you get an error saying that
- one of these libraries could not be found, then you will need
- to use the <code>-L</code> linker option to specify their locations.</p>
-
- <p>Before we can run our application we need to create a database
- schema using the generated <code>person.sql</code> file. For MySQL
- we can use the <code>mysql</code> client program, for example:</p>
-
- <pre class="terminal">
-mysql --user=odb_test --database=odb_test &lt; person.sql
- </pre>
-
- <p>The above command will log in to a local MySQL server as user
- <code>odb_test</code> without a password and use the database
- named <code>odb_test</code>. Beware that after executing this
- command, all the data stored in the <code>odb_test</code> database
- will be deleted.</p>
-
- <p>Note also that using a standalone generated SQL file is not the
- only way to create a database schema in ODB. We can also embed
- the schema directly into our application or use custom schemas
- that were not generated by the ODB compiler. Refer to
- <a href="#3.4">Section 3.4, "Database"</a> for details.</p>
-
- <p>Once the database schema is ready, we run our application
- using the same login and database name:</p>
-
- <pre class="terminal">
-./driver --user odb_test --database odb_test
- </pre>
-
-
- <h2><a name="2.4">2.4 Making Objects Persistent</a></h2>
-
- <p>Now that we have the infrastructure work out of the way, it
- is time to see our first code fragment that interacts with the
- database. In this section we will learn how to make <code>person</code>
- objects persistent:</p>
-
- <pre class="cxx">
-// driver.cxx
-//
-
-#include &lt;memory> // std::auto_ptr
-#include &lt;iostream>
-
-#include &lt;odb/database.hxx>
-#include &lt;odb/transaction.hxx>
-
-#include &lt;odb/mysql/database.hxx>
-
-#include "person.hxx"
-#include "person-odb.hxx"
-
-using namespace std;
-using namespace odb::core;
-
-int
-main (int argc, char* argv[])
-{
- try
- {
- auto_ptr&lt;database> db (new odb::mysql::database (argc, argv));
-
- unsigned long john_id, jane_id, joe_id;
-
- // Create a few persistent person objects.
- //
- {
- person john ("John", "Doe", 33);
- person jane ("Jane", "Doe", 32);
- person joe ("Joe", "Dirt", 30);
-
- transaction t (db->begin ());
-
- // Make objects persistent and save their ids for later use.
- //
- john_id = db->persist (john);
- jane_id = db->persist (jane);
- joe_id = db->persist (joe);
-
- t.commit ();
- }
- }
- catch (const odb::exception&amp; e)
- {
- cerr &lt;&lt; e.what () &lt;&lt; endl;
- return 1;
- }
-}
- </pre>
-
- <p>Let's examine this code piece by piece. At the beginning we include
- a bunch of headers. After the standard C++ headers we include
- <code>&lt;odb/database.hxx></code>
- and <code>&lt;odb/transaction.hxx></code> which define database
- system-independent <code>odb::database</code> and
- <code>odb::transaction</code> interfaces. Then we include
- <code>&lt;odb/mysql/database.hxx></code> which defines the
- MySQL implementation of the <code>database</code> interface. Finally,
- we include <code>person.hxx</code> and <code>person-odb.hxx</code>
- which define our persistent <code>person</code> class.</p>
-
- <p>Then we have two <code>using namespace</code> directives. The first
- one brings in the names from the standard namespace and the second
- brings in the ODB declarations which we will use later in the file.
- Notice that in the second directive we use the <code>odb::core</code>
- namespace instead of just <code>odb</code>. The former only brings
- into the current namespace the essential ODB names, such as the
- <code>database</code> and <code>transaction</code> classes, without
- any of the auxiliary objects. This minimizes the likelihood of name
- conflicts with other libraries. Note also that you should continue
- using the <code>odb</code> namespace when qualifying individual names.
- For example, you should write <code>odb::database</code>, not
- <code>odb::core::database</code>.</p>
-
- <p>Once we are in <code>main()</code>, the first thing we do is create
- the MySQL database object. Notice that this is the last line in
- <code>driver.cxx</code> that mentions MySQL explicitly; the rest
- of the code works through the common interfaces and is database
- system-independent. We use the <code>argc</code>/<code>argv</code>
- <code>mysql::database</code> constructor which automatically
- extract the database parameters, such as login name, password,
- database name, etc., from the command line. In your own applications
- you may prefer to use other <code>mysql::database</code>
- constructors which allow you to pass this information directly
- (<a href="#17.2">Section 17.2, "MySQL Database Class"</a>).</p>
-
- <p>Next, we create three <code>person</code> objects. Right now they are
- transient objects, which means that if we terminate the application
- at this point, they will be gone without any evidence of them ever
- existing. The next line starts a database transaction. We discuss
- transactions in detail later in this manual. For now, all we need
- to know is that all ODB database operations must be performed within
- a transaction and that a transaction is an atomic unit of work; all
- database operations performed within a transaction either succeed
- (committed) together or are automatically undone (rolled back).</p>
-
- <p>Once we are in a transaction, we call the <code>persist()</code>
- database function on each of our <code>person</code> objects.
- At this point the state of each object is saved in the database.
- However, note that this state is not permanent until and unless
- the transaction is committed. If, for example, our application
- crashes at this point, there will still be no evidence of our
- objects ever existing.</p>
-
- <p>In our case, one more thing happens when we call <code>persist()</code>.
- Remember that we decided to use database-assigned identifiers for our
- <code>person</code> objects. The call to <code>persist()</code> is
- where this assignment happens. Once this function returns, the
- <code>id_</code> member contains this object's unique identifier.
- As a convenience, the <code>persist()</code> function also returns
- a copy of the object's identifier that it made persistent. We
- save the returned identifier for each object in a local variable.
- We will use these identifiers later in the chapter to perform other
- database operations on our persistent objects.</p>
-
- <p>After we have persisted our objects, it is time to commit the
- transaction and make the changes permanent. Only after the
- <code>commit()</code> function returns successfully, are we
- guaranteed that the objects are made persistent. Continuing
- with the crash example, if our application terminates after
- the commit for whatever reason, the objects' state in the
- database will remain intact. In fact, as we will discover
- shortly, our application can be restarted and load the
- original objects from the database. Note also that a
- transaction must be committed explicitly with the
- <code>commit()</code> call. If the <code>transaction</code>
- object leaves scope without the transaction being
- explicitly committed or rolled back, it will automatically be
- rolled back. This behavior allows you not to worry about
- exceptions being thrown within a transaction; if they
- cross the transaction boundary, the transaction will
- automatically be rolled back and all the changes made
- to the database undone.</p>
-
- <p>The final bit of code in our example is the <code>catch</code>
- block that handles the database exceptions. We do this by catching
- the base ODB exception (<a href="#3.14">Section 3.14, "ODB
- Exceptions"</a>) and printing the diagnostics.</p>
-
- <p>Let's now compile (<a href="#2.3">Section 2.3, "Compiling and
- Running"</a>) and then run our first ODB application:</p>
-
- <pre class="terminal">
-mysql --user=odb_test --database=odb_test &lt; person.sql
-./driver --user odb_test --database odb_test
- </pre>
-
- <p>Our first application doesn't print anything except for error
- messages so we can't really tell whether it actually stored the
- objects' state in the database. While we will make our application
- more entertaining shortly, for now we can use the <code>mysql</code>
- client to examine the database content. It will also give us a feel
- for how the objects are stored:</p>
-
- <pre class="terminal">
-mysql --user=odb_test --database=odb_test
-
-Welcome to the MySQL monitor.
-
-mysql> select * from person;
-
-+----+-------+------+-----+
-| id | first | last | age |
-+----+-------+------+-----+
-| 1 | John | Doe | 33 |
-| 2 | Jane | Doe | 32 |
-| 3 | Joe | Dirt | 30 |
-+----+-------+------+-----+
-3 rows in set (0.00 sec)
-
-mysql> quit
- </pre>
-
- <p>Another way to get more insight into what's going on under the hood,
- is to trace the SQL statements executed by ODB as a result of
- each database operation. Here is how we can enable tracing just for
- the duration of our transaction:</p>
-
- <pre class="cxx">
- // Create a few persistent person objects.
- //
- {
- ...
-
- transaction t (db->begin ());
-
- t.tracer (stderr_tracer);
-
- // Make objects persistent and save their ids for later use.
- //
- john_id = db->persist (john);
- jane_id = db->persist (jane);
- joe_id = db->persist (joe);
-
- t.commit ();
- }
- </pre>
-
- <p>With this modification our application now produces the following
- output:</p>
-
- <pre class="terminal">
-INSERT INTO `person` (`id`,`first`,`last`,`age`) VALUES (?,?,?,?)
-INSERT INTO `person` (`id`,`first`,`last`,`age`) VALUES (?,?,?,?)
-INSERT INTO `person` (`id`,`first`,`last`,`age`) VALUES (?,?,?,?)
- </pre>
-
- <p>Note that we see question marks instead of the actual values
- because ODB uses prepared statements and sends the data to the
- database in binary form. For more information on tracing, refer
- to <a href="#3.13">Section 3.13, "Tracing SQL Statement Execution"</a>.
- In the next section we will see how to access persistent objects
- from our application.</p>
-
- <h2><a name="2.5">2.5 Querying the Database for Objects</a></h2>
-
- <p>So far our application doesn't resemble a typical "Hello World"
- example. It doesn't print anything except for error messages.
- Let's change that and teach our application to say hello to
- people from our database. To make it a bit more interesting,
- let's say hello only to people over 30:</p>
-
- <pre class="cxx">
-// driver.cxx
-//
-
-...
-
-int
-main (int argc, char* argv[])
-{
- try
- {
- ...
-
- // Create a few persistent person objects.
- //
- {
- ...
- }
-
- typedef odb::query&lt;person> query;
- typedef odb::result&lt;person> result;
-
- // Say hello to those over 30.
- //
- {
- transaction t (db->begin ());
-
- result r (db->query&lt;person> (query::age > 30));
-
- for (result::iterator i (r.begin ()); i != r.end (); ++i)
- {
- cout &lt;&lt; "Hello, " &lt;&lt; i->first () &lt;&lt; "!" &lt;&lt; endl;
- }
-
- t.commit ();
- }
- }
- catch (const odb::exception&amp; e)
- {
- cerr &lt;&lt; e.what () &lt;&lt; endl;
- return 1;
- }
-}
- </pre>
-
- <p>The first half of our application is the same as before and is
- replaced with "..." in the above listing for brevity. Again, let's
- examine the rest of it piece by piece.</p>
-
- <p>The two <code>typedef</code>s create convenient aliases for two
- template instantiations that will be used a lot in our application.
- The first is the query type for the <code>person</code> objects
- and the second is the result type for that query.</p>
-
- <p>Then we begin a new transaction and call the <code>query()</code>
- database function. We pass a query expression
- (<code>query::age > 30</code>) which limits the returned objects
- only to those with the age greater than 30. We also save the result
- of the query in a local variable.</p>
-
- <p>The next few lines perform a standard for-loop iteration
- over the result sequence printing hello for every returned person.
- Then we commit the transaction and that's it. Let's see what
- this application will print:</p>
-
- <pre class="terminal">
-mysql --user=odb_test --database=odb_test &lt; person.sql
-./driver --user odb_test --database odb_test
-
-Hello, John!
-Hello, Jane!
- </pre>
-
-
- <p>That looks about right, but how do we know that the query actually
- used the database instead of just using some in-memory artifacts of
- the earlier <code>persist()</code> calls? One way to test this
- would be to comment out the first transaction in our application
- and re-run it without re-creating the database schema. This way the
- objects that were persisted during the previous run will be returned.
- Alternatively, we can just re-run the same application without
- re-creating the schema and notice that we now show duplicate
- objects:</p>
-
- <pre class="terminal">
-./driver --user odb_test --database odb_test
-
-Hello, John!
-Hello, Jane!
-Hello, John!
-Hello, Jane!
- </pre>
-
- <p>What happens here is that the previous run of our application
- persisted a set of <code>person</code> objects and when we re-run
- the application, we persist another set with the same names but
- with different ids. When we later run the query, matches from
- both sets are returned. We can change the line where we print
- the "Hello" string as follows to illustrate this point:</p>
-
- <pre class="cxx">
-cout &lt;&lt; "Hello, " &lt;&lt; i->first () &lt;&lt; " (" &lt;&lt; i->id () &lt;&lt; ")!" &lt;&lt; endl;
- </pre>
-
- <p>If we now re-run this modified program, again without re-creating
- the database schema, we will get the following output:</p>
-
- <pre class="terminal">
-./driver --user odb_test --database odb_test
-
-Hello, John (1)!
-Hello, Jane (2)!
-Hello, John (4)!
-Hello, Jane (5)!
-Hello, John (7)!
-Hello, Jane (8)!
- </pre>
-
- <p>The identifiers 3, 6, and 9 that are missing from the above list belong
- to the "Joe Dirt" objects which are not selected by this query.</p>
-
- <h2><a name="2.6">2.6 Updating Persistent Objects</a></h2>
-
- <p>While making objects persistent and then selecting some of them using
- queries are two useful operations, most applications will also need
- to change the object's state and then make these changes persistent.
- Let's illustrate this by updating Joe's age who just had a birthday:</p>
-
- <pre class="cxx">
-// driver.cxx
-//
-
-...
-
-int
-main (int argc, char* argv[])
-{
- try
- {
- ...
-
- unsigned long john_id, jane_id, joe_id;
-
- // Create a few persistent person objects.
- //
- {
- ...
-
- // Save object ids for later use.
- //
- john_id = john.id ();
- jane_id = jane.id ();
- joe_id = joe.id ();
- }
-
- // Joe Dirt just had a birthday, so update his age.
- //
- {
- transaction t (db->begin ());
-
- auto_ptr&lt;person> joe (db->load&lt;person> (joe_id));
- joe->age (joe->age () + 1);
- db->update (*joe);
-
- t.commit ();
- }
-
- // Say hello to those over 30.
- //
- {
- ...
- }
- }
- catch (const odb::exception&amp; e)
- {
- cerr &lt;&lt; e.what () &lt;&lt; endl;
- return 1;
- }
-}
- </pre>
-
- <p>The beginning and the end of the new transaction are the same as
- the previous two. Once within a transaction, we call the
- <code>load()</code> database function to instantiate a
- <code>person</code> object with Joe's persistent state. We
- pass Joe's object identifier that we stored earlier when we
- made this object persistent. While here we use
- <code>std::auto_ptr</code> to manage the returned object, we
- could have also used another smart pointer, for example
- <code>std::unique_ptr</code> from C++11 or <code>shared_ptr</code>
- from TR1, C++11, or Boost. For more information
- on the object lifetime management and the smart pointers that we
- can use for that, see <a href="#3.3">Section 3.3, "Object
- and View Pointers"</a>.</p>
-
- <p>With the instantiated object in hand we increment the age
- and call the <code>update()</code> function to update
- the object's state in the database. Once the transaction is
- committed, the changes are made permanent.</p>
-
- <p>If we now run this application, we will see Joe in the output
- since he is now over 30:</p>
-
- <pre class="terminal">
-mysql --user=odb_test --database=odb_test &lt; person.sql
-./driver --user odb_test --database odb_test
-
-Hello, John!
-Hello, Jane!
-Hello, Joe!
- </pre>
-
- <p>What if we didn't have an identifier for Joe? Maybe this object
- was made persistent in another run of our application or by another
- application altogether. Provided that we only have one Joe Dirt
- in the database, we can use the query facility to come up with
- an alternative implementation of the above transaction:</p>
-
- <pre class="cxx">
- // Joe Dirt just had a birthday, so update his age. An
- // alternative implementation without using the object id.
- //
- {
- transaction t (db->begin ());
-
- // Here we know that there can be only one Joe Dirt in our
- // database so we use the query_one() shortcut instead of
- // manually iterating over the result returned by query().
- //
- auto_ptr&lt;person> joe (
- db->query_one&lt;person> (query::first == "Joe" &amp;&amp;
- query::last == "Dirt"));
-
- if (joe.get () != 0)
- {
- joe->age (joe->age () + 1);
- db->update (*joe);
- }
-
- t.commit ();
- }
- </pre>
-
- <h2><a name="2.7">2.7 Defining and Using Views</a></h2>
-
- <p>Suppose that we need to gather some basic statistics about the people
- stored in our database. Things like the total head count, as well as
- the minimum and maximum ages. One way to do it would be to query
- the database for all the <code>person</code> objects and then
- calculate this information as we iterate over the query result.
- While this approach may work fine for our database with just three
- people in it, it would be very inefficient if we had a large
- number of objects.</p>
-
- <p>While it may not be conceptually pure from the object-oriented
- programming point of view, a relational database can perform
- some computations much faster and much more economically than
- if we performed the same operations ourselves in the application's
- process.</p>
-
- <p>To support such cases ODB provides the notion of views. An ODB view
- is a C++ <code>class</code> that embodies a light-weight, read-only
- projection of one or more persistent objects or database tables or
- the result of a native SQL query execution or stored procedure
- call.</p>
-
- <p>Some of the common applications of views include loading a subset of
- data members from objects or columns database tables, executing and
- handling results of arbitrary SQL queries, including aggregate
- queries, as well as joining multiple objects and/or database
- tables using object relationships or custom join conditions.</p>
-
- <p>While you can find a much more detailed description of views in
- <a href="#10">Chapter 10, "Views"</a>, here is how we can define
- the <code>person_stat</code> view that returns the basic statistics
- about the <code>person</code> objects:</p>
-
- <pre class="cxx">
-#pragma db view object(person)
-struct person_stat
-{
- #pragma db column("count(" + person::id_ + ")")
- std::size_t count;
-
- #pragma db column("min(" + person::age_ + ")")
- unsigned short min_age;
-
- #pragma db column("max(" + person::age_ + ")")
- unsigned short max_age;
-};
- </pre>
-
- <p>Normally, to get the result of a view we use the same
- <code>query()</code> function as when querying the database for
- an object. Here, however, we are executing an aggregate query
- which always returns exactly one element. Therefore, instead
- of getting the result instance and then iterating over it, we
- can use the shortcut <code>query_value()</code> function. Here is
- how we can load and print our statistics using the view we have
- just created:</p>
-
- <pre class="cxx">
- // Print some statistics about all the people in our database.
- //
- {
- transaction t (db->begin ());
-
- // The result of this query always has exactly one element.
- //
- person_stat ps (db->query_value&lt;person_stat> ());
-
- cout &lt;&lt; "count : " &lt;&lt; ps.count &lt;&lt; endl
- &lt;&lt; "min age: " &lt;&lt; ps.min_age &lt;&lt; endl
- &lt;&lt; "max age: " &lt;&lt; ps.max_age &lt;&lt; endl;
-
- t.commit ();
- }
- </pre>
-
- <p>If we now add the <code>person_stat</code> view to the
- <code>person.hxx</code> header, the above transaction
- to <code>driver.cxx</code>, as well as re-compile and
- re-run our example, then we will see the following
- additional lines in the output:</p>
-
- <pre class="term">
-count : 3
-min age: 31
-max age: 33
- </pre>
-
- <h2><a name="2.8">2.8 Deleting Persistent Objects</a></h2>
-
- <p>The last operation that we will discuss in this chapter is deleting
- the persistent object from the database. The following code
- fragment shows how we can delete an object given its identifier:</p>
-
- <pre class="cxx">
- // John Doe is no longer in our database.
- //
- {
- transaction t (db->begin ());
- db->erase&lt;person> (john_id);
- t.commit ();
- }
- </pre>
-
- <p>To delete John from the database we start a transaction, call
- the <code>erase()</code> database function with John's object
- id, and commit the transaction. After the transaction is committed,
- the erased object is no longer persistent.</p>
-
- <p>If we don't have an object id handy, we can use queries to find
- and delete the object:</p>
-
- <pre class="cxx">
- // John Doe is no longer in our database. An alternative
- // implementation without using the object id.
- //
- {
- transaction t (db->begin ());
-
- // Here we know that there can be only one John Doe in our
- // database so we use the query_one() shortcut again.
- //
- auto_ptr&lt;person> john (
- db->query_one&lt;person> (query::first == "John" &amp;&amp;
- query::last == "Doe"));
-
- if (john.get () != 0)
- db->erase (*john);
-
- t.commit ();
- }
- </pre>
-
- <h2><a name="2.9">2.9 Changing Persistent Classes</a></h2>
-
- <p>When the definition of a transient C++ class is changed, for
- example by adding or deleting a data member, we don't have to
- worry about any existing instances of this class not matching
- the new definition. After all, to make the class changes
- effective we have to restart the application and none of the
- transient instances will survive this.</p>
-
- <p>Things are not as simple for persistent classes. Because they
- are stored in the database and therefore survive application
- restarts, we have a new problem: what happens to the state of
- existing objects (which correspond to the old definition) once
- we change our persistent class?</p>
-
- <p>The problem of working with old objects, called <em>database
- schema evolution</em>, is a complex issue and ODB provides
- comprehensive support for handling it. While this support
- is covered in detail in <a href="#13">Chapter 13,
- "Database Schema Evolution"</a>, let us consider a simple
- example that should give us a sense of the functionality
- provided by ODB in this area.</p>
-
- <p>Suppose that after using our <code>person</code> persistent
- class for some time and creating a number of databases
- containing its instances, we realized that for some people
- we also need to store their middle name. If we go ahead and
- just add the new data member, everything will work fine
- with new databases. Existing databases, however, have a
- table that does not correspond to the new class definition.
- Specifically, the generated database support code now
- expects there to be a column to store the middle name.
- But such a column was never created in the old databases.</p>
-
- <p>ODB can automatically generate SQL statements that will
- migrate old databases to match the new class definitions.
- But first, we need to enable schema evolution support by
- defining a version for our object model:</p>
-
- <pre class="cxx">
-// person.hxx
-//
-
-#pragma db model version(1, 1)
-
-class person
-{
- ...
-
- std::string first_;
- std::string last_;
- unsigned short age_;
-};
- </pre>
-
- <p>The first number in the <code>version</code> pragma is the
- base model version. This is the lowest version we will be
- able to migrate from. The second number is the current model
- version. Since we haven't made any changes yet to our
- persistent class, both of these values are <code>1</code>.</p>
-
- <p>Next we need to re-compile our <code>person.hxx</code> header
- file with the ODB compiler, just as we did before:</p>
-
- <pre class="terminal">
-odb -d mysql --generate-query --generate-schema person.hxx
- </pre>
-
- <p>If we now look at the list of files produced by the ODB compiler,
- we will notice a new file: <code>person.xml</code>. This
- is a changelog file where the ODB compiler keeps track of the
- database changes corresponding to our class changes. Note that
- this file is automatically maintained by the ODB compiler and
- all we have to do is keep it around between re-compilations.</p>
-
- <p>Now we are ready to add the middle name to our <code>person</code>
- class. We also give it a default value (empty string) which
- is what will be assigned to existing objects in old databases.
- Notice that we have also incremented the current version:</p>
-
- <pre class="cxx">
-// person.hxx
-//
-
-#pragma db model version(1, 2)
-
-class person
-{
- ...
-
- std::string first_;
-
- #pragma db default("")
- std::string middle_;
-
- std::string last_;
- unsigned short age_;
-};
- </pre>
-
- <p>If we now recompile the <code>person.hxx</code> header again, we will
- see two extra generated files: <code>person-002-pre.sql</code>
- and <code>person-002-post.sql</code>. These two files contain
- schema migration statements from version <code>1</code> to
- version <code>2</code>. Similar to schema creation, schema
- migration statements can also be embedded into the generated
- C++ code.</p>
-
- <p><code>person-002-pre.sql</code> and <code>person-002-post.sql</code>
- are the pre and post schema migration files. To migrate
- one of our old databases, we first execute the pre migration
- file:</p>
-
- <pre class="terminal">
-mysql --user=odb_test --database=odb_test &lt; person-002-pre.sql
- </pre>
-
- <p>Between the pre and post schema migrations we can run data
- migration code, if required. At this stage, we can both
- access the old and store the new data. In our case we don't
- need any data migration code since we assigned the default
- value to the middle name for all the existing objects.</p>
-
- <p>To finish the migration process we execute the post migration
- statements:</p>
-
- <pre class="terminal">
-mysql --user=odb_test --database=odb_test &lt; person-002-post.sql
- </pre>
-
- <h2><a name="2.10">2.10 Working with Multiple Databases</a></h2>
-
- <p>Accessing multiple databases (that is, data stores) is simply a
- matter of creating multiple <code>odb::&lt;db>::database</code>
- instances representing each database. For example:</p>
-
- <pre class="cxx">
-odb::mysql::database db1 ("john", "secret", "test_db1");
-odb::mysql::database db2 ("john", "secret", "test_db2");
- </pre>
-
- <p>Some database systems also allow attaching multiple databases to
- the same instance. A more interesting question is how we access
- multiple database systems (that is, database implementations) from
- the same application. For example, our application may need to store
- some objects in a remote MySQL database and others in a local SQLite
- file. Or, our application may need to be able to store its objects
- in a database system that is selected by the user at runtime.</p>
-
- <p>ODB provides comprehensive multi-database support that ranges from
- tight integration with specific database systems to being able to
- write database-agnostic code and loading individual database systems
- support dynamically. While all these aspects are covered in detail
- in <a href="#16">Chapter 16, "Multi-Database Support"</a>, in this
- section we will get a taste of this functionality by extending our
- "Hello World" example to be able to store its data either in MySQL
- or PostgreSQL (other database systems supported by ODB can be added
- in a similar manner).</p>
-
- <p>The first step in adding multi-database support is to re-compile
- our <code>person.hxx</code> header to generate database support
- code for additional database systems:</p>
-
- <pre class="terminal">
-odb --multi-database dynamic -d common -d mysql -d pgsql \
---generate-query --generate-schema person.hxx
- </pre>
-
- <p>The <code>--multi-database</code> ODB compiler option turns on
- multi-database support. For now it is not important what the
- <code>dynamic</code> value that we passed to this option means, but
- if you are curious, see <a href="#16">Chapter 16</a>. The result of this
- command are three sets of generated files: <code>person-odb.?xx</code>
- (common interface; corresponds to the <code>common</code> database),
- <code>person-odb-mysql.?xx</code> (MySQL support code), and
- <code>person-odb-pgsql.?xx</code> (PostgreSQL support code). There
- are also two schema files: <code>person-mysql.sql</code> and
- <code>person-pgsql.sql</code>.</p>
-
- <p>The only part that we need to change in <code>driver.cxx</code>
- is how we create the database instance. Specifically, this line:</p>
-
- <pre class="cxx">
-auto_ptr&lt;database> db (new odb::mysql::database (argc, argv));
- </pre>
-
- <p>Now our example is capable of storing its data either in MySQL or
- PostgreSQL so we need to somehow allow the caller to specify which
- database we must use. To keep things simple, we will make the first
- command line argument specify the database system we must use while
- the rest will contain the database-specific options which we will
- pass to the <code>odb::&lt;db>::database</code> constructor as
- before. Let's put all this logic into a separate function which we
- will call <code>create_database()</code>. Here is what the beginning
- of our modified <code>driver.cxx</code> will look like (the remainder
- is unchanged):</p>
-
- <pre class="cxx">
-// driver.cxx
-//
-
-#include &lt;string>
-#include &lt;memory> // std::auto_ptr
-#include &lt;iostream>
-
-#include &lt;odb/database.hxx>
-#include &lt;odb/transaction.hxx>
-
-#include &lt;odb/mysql/database.hxx>
-#include &lt;odb/pgsql/database.hxx>
-
-#include "person.hxx"
-#include "person-odb.hxx"
-
-using namespace std;
-using namespace odb::core;
-
-auto_ptr&lt;database>
-create_database (int argc, char* argv[])
-{
- auto_ptr&lt;database> r;
-
- if (argc &lt; 2)
- {
- cerr &lt;&lt; "error: database system name expected" &lt;&lt; endl;
- return r;
- }
-
- string db (argv[1]);
-
- if (db == "mysql")
- r.reset (new odb::mysql::database (argc, argv));
- else if (db == "pgsql")
- r.reset (new odb::pgsql::database (argc, argv));
- else
- cerr &lt;&lt; "error: unknown database system " &lt;&lt; db &lt;&lt; endl;
-
- return r;
-}
-
-int
-main (int argc, char* argv[])
-{
- try
- {
- auto_ptr&lt;database> db (create_database (argc, argv));
-
- if (db.get () == 0)
- return 1; // Diagnostics has already been issued.
-
- ...
- </pre>
-
- <p>And that's it. The only thing left is to build and run our
- example:</p>
-
- <pre class="terminal">
-c++ -c driver.cxx
-c++ -c person-odb.cxx
-c++ -c person-odb-mysql.cxx
-c++ -c person-odb-pgsql.cxx
-c++ -o driver driver.o person-odb.o person-odb-mysql.o \
-person-odb-pgsql.o -lodb-mysql -lodb-pgsql -lodb
- </pre>
-
- <p>Here is how we can access a MySQL database:</p>
-
- <pre class="terminal">
-mysql --user=odb_test --database=odb_test &lt; person-mysql.sql
-./driver mysql --user odb_test --database odb_test
- </pre>
-
- <p>Or a PostgreSQL database:</p>
-
- <pre class="terminal">
-psql --user=odb_test --dbname=odb_test -f person-pgsql.sql
-./driver pgsql --user odb_test --database odb_test
- </pre>
-
- <h2><a name="2.11">2.11 Summary</a></h2>
-
- <p>This chapter presented a very simple application which, nevertheless,
- exercised all of the core database functions: <code>persist()</code>,
- <code>query()</code>, <code>load()</code>, <code>update()</code>,
- and <code>erase()</code>. We also saw that writing an application
- that uses ODB involves the following steps:</p>
-
- <ol>
- <li>Declare persistent classes in header files.</li>
- <li>Compile these headers to generate database support code.</li>
- <li>Link the application with the generated code and two ODB runtime
- libraries.</li>
- </ol>
-
- <p>Do not be concerned if, at this point, much appears unclear. The intent
- of this chapter is to give you only a general idea of how to persist C++
- objects with ODB. We will cover all the details throughout the remainder
- of this manual.</p>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="3">3 Working with Persistent Objects</a></h1>
-
- <p>The previous chapters gave us a high-level overview of ODB and
- showed how to use it to store C++ objects in a database. In this
- chapter we will examine the ODB object persistence model as
- well as the core database APIs in greater detail. We will
- start with basic concepts and terminology in <a href="#3.1">Section
- 3.1</a> and <a href="#3.3">Section 3.3</a> and continue with the
- discussion of the <code>odb::database</code> class in
- <a href="#3.4">Section 3.4</a>, transactions in
- <a href="#3.5">Section 3.5</a>, and connections in
- <a href="#3.6">Section 3.6</a>. The remainder of this chapter
- deals with the core database operations and concludes with
- the discussion of ODB exceptions.</p>
-
- <p>In this chapter we will continue to use and expand the
- <code>person</code> persistent class that we have developed in the
- previous chapter.</p>
-
- <h2><a name="3.1">3.1 Concepts and Terminology</a></h2>
-
- <p>The term <em>database</em> can refer to three distinct things:
- a general notion of a place where an application stores its data,
- a software implementation for managing this data (for example
- MySQL), and, finally, some database software implementations
- may manage several data stores which are usually distinguished
- by name. This name is also commonly referred to as a database.</p>
-
- <p>In this manual, when we use the word <em>database</em>, we
- refer to the first meaning above, for example,
- "The <code>update()</code> function saves the object's state to
- the database." The term Database Management System (DBMS) is
- often used to refer to the second meaning of the word database.
- In this manual we will use the term <em>database system</em>
- for short, for example, "Database system-independent
- application code." Finally, to distinguish the third meaning
- from the other two, we will use the term <em>database name</em>,
- for example, "The second option specifies the database name
- that the application should use to store its data."</p>
-
- <p>In C++ there is only one notion of a type and an instance
- of a type. For example, a fundamental type, such as <code>int</code>,
- is, for the most part, treated the same as a user defined class
- type. However, when it comes to persistence, we have to place
- certain restrictions and requirements on certain C++ types that
- can be stored in the database. As a result, we divide persistent
- C++ types into two groups: <em>object types</em> and <em>value
- types</em>. An instance of an object type is called an <em>object</em>
- and an instance of a value type &mdash; a <em>value</em>.</p>
-
- <p>An object is an independent entity. It can be stored, updated,
- and deleted in the database independent of other objects.
- Normally, an object has an identifier, called <em>object id</em>,
- that is unique among all instances of an object type within a
- database. In contrast, a value can only be stored in the database
- as part of an object and doesn't have its own unique identifier.</p>
-
- <p>An object consists of data members which are either values
- (<a href="#7">Chapter 7, "Value Types"</a>), pointers
- to other objects (<a href="#6">Chapter 6, "Relationships"</a>), or
- containers of values or pointers to other objects (<a href="#5">Chapter
- 5, "Containers")</a>. Pointers to other objects and containers can
- be viewed as special kinds of values since they also can only
- be stored in the database as part of an object. Static data members
- are not stored in the database.</p>
-
- <p>An object type is a C++ class. Because of this one-to-one
- relationship, we will use terms <em>object type</em>
- and <em>object class</em> interchangeably. In contrast,
- a value type can be a fundamental C++ type, such as
- <code>int</code> or a class type, such as <code>std::string</code>.
- If a value consists of other values, then it is called a
- <em>composite value</em> and its type &mdash; a
- <em>composite value type</em> (<a href="#7.2">Section 7.2,
- "Composite Value Types"</a>). Otherwise, the value is
- called <em>simple value</em> and its type &mdash; a
- <em>simple value type</em> (<a href="#7.1">Section 7.1,
- "Simple Value Types"</a>). Note that the distinction between
- simple and composite values is conceptual rather than
- representational. For example, <code>std::string</code>
- is a simple value type because conceptually string is a
- single value even though the representation of the string
- class may contain several data members each of which could be
- considered a value. In fact, the same value type can be
- viewed (and mapped) as both simple and composite by different
- applications.</p>
-
- <p>While not strictly necessary in a purely object-oriented application,
- practical considerations often require us to only load a
- subset of an object's data members or a combination of members
- from several objects. We may also need to factor out some
- computations to the relational database instead of performing
- them in the application's process. To support such requirements
- ODB distinguishes a third kind of C++ types, called <em>views</em>
- (<a href="#10">Chapter 10, "Views"</a>). An ODB view is a C++
- <code>class</code> that embodies a light-weight, read-only
- projection of one or more persistent objects or database
- tables or the result of a native SQL query execution.</p>
-
- <p>Understanding how all these concepts map to the relational model
- will hopefully make these distinctions clearer. In a relational
- database an object type is mapped to a table and a value type is
- mapped to one or more columns. A simple value type is mapped
- to a single column while a composite value type is mapped to
- several columns. An object is stored as a row in this
- table and a value is stored as one or more cells in this row.
- A simple value is stored in a single cell while a composite
- value occupies several cells. A view is not a persistent
- entity and it is not stored in the database. Rather, it is a
- data structure that is used to capture a single row of an SQL
- query result.</p>
-
- <p>Going back to the distinction between simple and composite
- values, consider a date type which has three integer
- members: year, month, and day. In one application it can be
- considered a composite value and each member will get its
- own column in a relational database. In another application
- it can be considered a simple value and stored in a single
- column as a number of days from some predefined date.</p>
-
- <p>Until now, we have been using the term <em>persistent class</em>
- to refer to object classes. We will continue to do so even though
- a value type can also be a class. The reason for this asymmetry
- is the subordinate nature of value types when it comes to
- database operations. Remember that values are never stored
- directly but rather as part of an object that contains them.
- As a result, when we say that we want to make a C++ class
- persistent or persist an instance of a class in the database,
- we invariably refer to an object class rather than a value
- class.</p>
-
- <p>Normally, you would use object types to model real-world entities,
- things that have their own identity. For example, in the
- previous chapter we created a <code>person</code> class to model
- a person, which is a real-world entity. Name and age, which we
- used as data members in our <code>person</code> class are clearly
- values. It is hard to think of age 31 or name "Joe" as having their
- own identities.</p>
-
- <p>A good test to determine whether something is an object or
- a value, is to consider if other objects might reference
- it. A person is clearly an object because it can be referred
- to by other objects such as a spouse, an employer, or a
- bank. On the other hand, a person's age or name is not
- something that other objects would normally refer to.</p>
-
- <p>Also, when an object represents a real entity, it is easy to
- choose a suitable object id. For example, for a
- person there is an established notion of an identifier
- (SSN, student id, passport number, etc). Another alternative
- is to use a person's email address as an identifier.</p>
-
- <p>Note, however, that these are only guidelines. There could
- be good reasons to make something that would normally be
- a value an object. Consider, for example, a database that
- stores a vast number of people. Many of the <code>person</code>
- objects in this database have the same names and surnames and
- the overhead of storing them in every object may negatively
- affect the performance. In this case, we could make the first name
- and last name each an object and only store pointers to
- these objects in the <code>person</code> class.</p>
-
- <p>An instance of a persistent class can be in one of two states:
- <em>transient</em> and <em>persistent</em>. A transient
- instance only has a representation in the application's
- memory and will cease to exist when the application terminates,
- unless it is explicitly made persistent. In other words, a
- transient instance of a persistent class behaves just like an
- instance of any ordinary C++ class. A persistent instance
- has a representation in both the application's memory and the
- database. A persistent instance will remain even after the
- application terminates unless and until it is explicitly
- deleted from the database.</p>
-
- <h2><a name="3.2">3.2 Declaring Persistent Objects and Values</a></h2>
-
- <p>To make a C++ class a persistent object class we declare
- it as such using the <code>db&nbsp;object</code> pragma, for
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-};
- </pre>
-
- <p>The other pragma that we often use is <code>db&nbsp;id</code>
- which designates one of the data members as an object id, for
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db id
- unsigned long id_;
-};
- </pre>
-
- <p>The object id can be of a simple or composite (<a href="#7.2.1">Section
- 7.2.1, "Composite Object Ids"</a>) value type. This type should be
- default-constructible, copy-constructible, and copy-assignable. It
- is also possible to declare a persistent class without an object id,
- however, such a class will have limited functionality
- (<a href="#14.1.6">Section 14.1.6, "<code>no_id</code>"</a>).</p>
-
- <p>The above two pragmas are the minimum required to declare a
- persistent class with an object id. Other pragmas can be used to
- fine-tune the database-related properties of a class and its
- members (<a href="#14">Chapter 14, "ODB Pragma Language"</a>).</p>
-
- <p>Normally, a persistent class should define the default constructor. The
- generated database support code uses this constructor when
- instantiating an object from the persistent state. If we add the
- default constructor only for the database support code, then we
- can make it private provided we also make the <code>odb::access</code>
- class, defined in the <code>&lt;odb/core.hxx></code> header, a
- friend of this object class. For example:</p>
-
- <pre class="cxx">
-#include &lt;odb/core.hxx>
-
-#pragma db object
-class person
-{
- ...
-
-private:
- friend class odb::access;
- person () {}
-};
- </pre>
-
- <p>It is also possible to have an object class without the default
- constructor. However, in this case, the database operations will
- only be able to load the persistent state into an existing instance
- (<a href="#3.9">Section 3.9, "Loading Persistent Objects"</a>,
- <a href="#4.4">Section 4.4, "Query Result"</a>).</p>
-
- <p>The ODB compiler also needs access to the non-transient
- (<a href="#14.4.11">Section 14.4.11, "<code>transient</code>"</a>)
- data members of a persistent class. The ODB compiler can access
- such data members directly if they are public. It can also do
- so if they are private or protected and the <code>odb::access</code>
- class is declared a friend of the object type. For example:</p>
-
- <pre class="cxx">
-#include &lt;odb/core.hxx>
-
-#pragma db object
-class person
-{
- ...
-
-private:
- friend class odb::access;
- person () {}
-
- #pragma db id
- unsigned long id_;
-
- std::string name_;
-};
- </pre>
-
- <p>If data members are not accessible directly, then the ODB
- compiler will try to automatically find suitable accessor and
- modifier functions. To accomplish this, the ODB compiler will
- try to lookup common accessor and modifier names derived from
- the data member name. Specifically, for the <code>name_</code>
- data member in the above example, the ODB compiler will look
- for accessor functions with names: <code>get_name()</code>,
- <code>getName()</code>, <code>getname()</code>, and just
- <code>name()</code> as well as for modifier functions with
- names: <code>set_name()</code>, <code>setName()</code>,
- <code>setname()</code>, and just <code>name()</code>. You can
- also add support for custom name derivations with the
- <code>--accessor-regex</code> and <code>--modifier-regex</code>
- ODB compiler options. Refer to the
- <a href="http://www.codesynthesis.com/products/odb/doc/odb.xhtml">ODB
- Compiler Command Line Manual</a> for details on these options.
- The following example illustrates automatic accessor and modifier
- discovery:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
-public:
- person () {}
-
- ...
-
- unsigned long id () const;
- void id (unsigned long);
-
- const std::string&amp; get_name () const;
- std::string&amp; set_name ();
-
-private:
- #pragma db id
- unsigned long id_; // Uses id() for access.
-
- std::string name_; // Uses get_name()/set_name() for access.
-};
- </pre>
-
- <p>Finally, if a data member is not directly accessible and the
- ODB compiler was unable to discover suitable accessor and
- modifier functions, then we can provide custom accessor
- and modifier expressions using the <code>db&nbsp;get</code>
- and <code>db&nbsp;set</code> pragmas. For more information
- on custom accessor and modifier expressions refer to
- <a href="#14.4.5">Section 14.4.5,
- "<code>get</code>/<code>set</code>/<code>access</code>"</a>.</p>
-
- <p>Data members of a persistent class can also be split into
- separately-loaded and/or separately-updated sections.
- For more information on this functionality, refer to
- <a href="#9">Chapter 9, "Sections"</a>.</p>
-
- <p>You may be wondering whether we also have to declare value types
- as persistent. We don't need to do anything special for simple value
- types such as <code>int</code> or <code>std::string</code> since the
- ODB compiler knows how to map them to suitable database types and
- how to convert between the two. On the other hand, if a simple value
- is unknown to the ODB compiler then we will need to provide the
- mapping to the database type and, possibly, the code to
- convert between the two. For more information on how to achieve
- this refer to the <code>db&nbsp;type</code> pragma description
- in <a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>.</p>
-
- <p>Similar to object classes, composite value types have to be
- explicitly declared as persistent using the <code>db&nbsp;value</code>
- pragma, for example:</p>
-
- <pre class="cxx">
-#pragma db value
-class name
-{
- ...
-
- std::string first_;
- std::string last_;
-};
- </pre>
-
- <p>Note that a composite value cannot have a data member designated
- as an object id since, as we have discussed earlier, values do
- not have a notion of identity. A composite value type also doesn't
- have to define the default constructor, unless it is used as an
- element of a container. The ODB compiler uses the same mechanisms
- to access data members in composite value types as in object types.
- Composite value types are discussed in more detail in
- <a href="#7.2">Section 7.2, "Composite Value Types"</a>.</p>
-
- <h2><a name="3.3">3.3 Object and View Pointers</a></h2>
-
- <p>As we have seen in the previous chapter, some database operations
- create dynamically allocated instances of persistent classes and
- return pointers to these instances. As we will see in later chapters,
- pointers are also used to establish relationships between objects
- (<a href="#6">Chapter 6, "Relationships"</a>) as well as to cache
- persistent objects in a session (<a href="#11">Chapter 11,
- "Session"</a>). While in most cases you won't need to deal with
- pointers to views, it is possible to a obtain a dynamically allocated
- instance of a view using the <code>result_iterator::load()</code>
- function (<a href="#4.4">Section 4.4, "Query Results"</a>).</p>
-
- <p>By default, all these mechanisms use raw pointers to return
- objects and views as well as to pass and cache objects. This
- is normally sufficient for applications
- that have simple object lifetime requirements and do not use sessions
- or object relationships. In particular, a dynamically allocated object
- or view that is returned as a raw pointer from a database operation
- can be assigned to a smart pointer of our choice, for example
- <code>std::auto_ptr</code>, <code>std::unique_ptr</code> from C++11, or
- <code>shared_ptr</code> from TR1, C++11, or Boost.</p>
-
- <p>However, to avoid any possibility of a mistake, such as forgetting
- to use a smart pointer for a returned object or view, as well as to
- simplify the use of more advanced ODB functionality, such as sessions
- and bidirectional object relationships, it is recommended that you use
- smart pointers with the sharing semantics as object pointers.
- The <code>shared_ptr</code> smart pointer from TR1, C++11, or Boost
- is a good default choice. However, if sharing is not required and
- sessions are not used, then <code>std::unique_ptr</code> or
- <code>std::auto_ptr</code> can be used just as well.</p>
-
- <p>ODB provides several mechanisms for changing the object or view pointer
- type. To specify the pointer type on the per object or per view basis
- we can use the <code>db&nbsp;pointer</code> pragma, for example:</p>
-
- <pre class="cxx">
-#pragma db object pointer(std::tr1::shared_ptr)
-class person
-{
- ...
-};
- </pre>
-
- <p>We can also specify the default pointer for a group of objects or
- views at the namespace level:</p>
-
- <pre class="cxx">
-#pragma db namespace pointer(std::tr1::shared_ptr)
-namespace accounting
-{
- #pragma db object
- class employee
- {
- ...
- };
-
- #pragma db object
- class employer
- {
- ...
- };
-}
- </pre>
-
- <p>Finally, we can use the <code>--default-pointer</code> option to specify
- the default pointer for the whole file. Refer to the
- <a href="http://www.codesynthesis.com/products/odb/doc/odb.xhtml">ODB
- Compiler Command Line Manual</a> for details on this option's argument.
- The typical usage is shown below:</p>
-
- <pre class="terminal">
---default-pointer std::tr1::shared_ptr
- </pre>
-
- <p>An alternative to this method with the same effect is to specify the
- default pointer for the global namespace:</p>
-
- <pre class="terminal">
-#pragma db namespace() pointer(std::tr1::shared_ptr)
- </pre>
-
- <p>Note that we can always override the default pointer specified
- at the namespace level or with the command line option using
- the <code>db&nbsp;pointer</code> object or view pragma. For
- example:</p>
-
- <pre class="cxx">
-#pragma db object pointer(std::shared_ptr)
-namespace accounting
-{
- #pragma db object
- class employee
- {
- ...
- };
-
- #pragma db object pointer(std::unique_ptr)
- class employer
- {
- ...
- };
-}
- </pre>
-
- <p>Refer to <a href="#14.1.2">Section 14.1.2, "<code>pointer</code>
- (object)"</a>, <a href="#14.2.4">Section 14.2.4, "<code>pointer</code>
- (view)"</a>, and <a href="#14.5.1">Section 14.5.1, "<code>pointer</code>
- (namespace)"</a> for more information on these mechanisms.</p>
-
- <p>Built-in support that is provided by the ODB runtime library allows us
- to use <code>shared_ptr</code> (TR1 or C++11),
- <code>std::unique_ptr</code> (C++11), or <code>std::auto_ptr</code> as
- pointer types. Plus, ODB profile libraries, that are available for
- commonly used frameworks and libraries (such as Boost and Qt),
- provide support for smart pointers found in these frameworks and
- libraries (<a href="#III">Part III, "Profiles"</a>). It is also
- easy to add support for our own smart pointers, as described in
- <a href="#6.5"> Section 6.5, "Using Custom Smart Pointers"</a>.</p>
-
- <h2><a name="3.4">3.4 Database</a></h2>
-
- <p>Before an application can make use of persistence services
- offered by ODB, it has to create a database class instance. A
- database instance is the representation of the place where
- the application stores its persistent objects. We create
- a database instance by instantiating one of the database
- system-specific classes. For example, <code>odb::mysql::database</code>
- would be such a class for the MySQL database system. We will
- also normally pass a database name as an argument to the
- class' constructor. The following code fragment
- shows how we can create a database instance for the MySQL
- database system:</p>
-
- <pre class="cxx">
-#include &lt;odb/database.hxx>
-#include &lt;odb/mysql/database.hxx>
-
-auto_ptr&lt;odb::database> db (
- new odb::mysql::database (
- "test_user" // database login name
- "test_password" // database password
- "test_database" // database name
- ));
- </pre>
-
- <p>The <code>odb::database</code> class is a common interface for
- all the database system-specific classes provided by ODB. You
- would normally work with the database
- instance via this interface unless there is a specific
- functionality that your application depends on and which is
- only exposed by a particular system's <code>database</code>
- class. You will need to include the <code>&lt;odb/database.hxx></code>
- header file to make this class available in your application.</p>
-
- <p>The <code>odb::database</code> interface defines functions for
- starting transactions and manipulating persistent objects.
- These are discussed in detail in the remainder of this chapter
- as well as the next chapter which is dedicated to the topic of
- querying the database for persistent objects. For details on the
- system-specific <code>database</code> classes, refer to
- <a href="#II">Part II, "Database Systems"</a>.</p>
-
- <p>Before we can persist our objects, the corresponding database schema has
- to be created in the database. The schema contains table definitions and
- other relational database artifacts that are used to store the state of
- persistent objects in the database.</p>
-
- <p>There are several ways to create the database schema. The easiest is to
- instruct the ODB compiler to generate the corresponding schema from the
- persistent classes (<code>--generate-schema</code> option). The ODB
- compiler can generate the schema as a standalone SQL file,
- embedded into the generated C++ code, or as a separate C++ source file
- (<code>--schema-format</code> option). If we are using the SQL file
- to create the database schema, then this file should be executed,
- normally only once, before the application is started.</p>
-
- <p>Alternatively, if the schema is embedded directly into the generated
- code or produced as a separate C++ source file, then we can use the
- <code>odb::schema_catalog</code> class to create it in the database
- from within our application, for example:</p>
-
- <pre class="cxx">
-#include &lt;odb/schema-catalog.hxx>
-
-odb::transaction t (db->begin ());
-odb::schema_catalog::create_schema (*db);
-t.commit ();
- </pre>
-
- <p>Refer to the next section for information on the
- <code>odb::transaction</code> class. The complete version of the above
- code fragment is available in the <code>schema/embedded</code> example in
- the <code>odb-examples</code> package.</p>
-
- <p>The <code>odb::schema_catalog</code> class has the following interface.
- You will need to include the <code>&lt;odb/schema-catalog.hxx></code>
- header file to make this class available in your application.</p>
-
- <pre class="cxx">
-namespace odb
-{
- class schema_catalog
- {
- public:
- static void
- create_schema (database&amp;,
- const std::string&amp; name = "",
- bool drop = true);
-
- static void
- drop_schema (database&amp;, const std::string&amp; name = "");
-
- static bool
- exists (database_id, const std::string&amp; name = "");
-
- static bool
- exists (const database&amp;, const std::string&amp; name = "")
- };
-}
- </pre>
-
- <p>The first argument to the <code>create_schema()</code> function
- is the database instance that we would like to create the schema in.
- The second argument is the schema name. By default, the ODB
- compiler generates all embedded schemas with the default schema
- name (empty string). However, if your application needs to
- have several separate schemas, you can use the
- <code>--schema-name</code> ODB compiler option to assign
- custom schema names and then use these names as a second argument
- to <code>create_schema()</code>. By default, <code>create_schema()</code>
- will also delete all the database objects (tables, indexes, etc.) if
- they exist prior to creating the new ones. You can change this
- behavior by passing <code>false</code> as the third argument. The
- <code>drop_schema()</code> function allows you to delete all the
- database objects without creating the new ones.</p>
-
- <p>If the schema is not found, the <code>create_schema()</code> and
- <code>drop_schema()</code> functions throw the
- <code>odb::unknown_schema</code> exception. You can use the
- <code>exists()</code> function to check whether a schema for the
- specified database and with the specified name exists in the
- catalog. Note also that the <code>create_schema()</code> and
- <code>drop_schema()</code> functions should be called within a
- transaction.</p>
-
- <p>ODB also provides support for database schema evolution. Similar
- to schema creation, schema migration statements can be generated
- either as standalone SQL files or embedded into the generated C++
- code. For more information on schema evolution support, refer to
- <a href="#13">Chapter 13, "Database Schema Evolution"</a>.</p>
-
- <p>Finally, we can also use a custom database schema with ODB. This approach
- can work similarly to the standalone SQL file described above except that
- the database schema is hand-written or produced by another program. Or we
- could execute custom SQL statements that create the schema directly from
- our application. To map persistent classes to custom database schemas, ODB
- provides a wide range of mapping customization pragmas, such
- as <code>db&nbsp;table</code>, <code>db&nbsp;column</code>,
- and <code>db&nbsp;type</code> (<a href="#14">Chapter 14, "ODB Pragma
- Language"</a>). For sample code that shows how to perform such mapping
- for various C++ constructs, refer to the <code>schema/custom</code>
- example in the <code>odb-examples</code> package.</p>
-
- <h2><a name="3.5">3.5 Transactions</a></h2>
-
- <p>A transaction is an atomic, consistent, isolated and durable
- (ACID) unit of work. Database operations can only be
- performed within a transaction and each thread of execution
- in an application can have only one active transaction at a
- time.</p>
-
- <p>By atomicity we mean that when it comes to making changes to
- the database state within a transaction,
- either all the changes are applied or none at all. Consider,
- for example, a transaction that transfers funds between two
- objects representing bank accounts. If the debit function
- on the first object succeeds but the credit function on
- the second fails, the transaction is rolled back and the
- database state of the first object remains unchanged.</p>
-
- <p>By consistency we mean that a transaction must take all the
- objects stored in the database from one consistent state
- to another. For example, if a bank account object must
- reference a person object as its owner and we forget to
- set this reference before making the object persistent,
- the transaction will be rolled back and the database
- will remain unchanged.</p>
-
- <p>By isolation we mean that the changes made to the database
- state during a transaction are only visible inside this
- transaction until and unless it is committed. Using the
- above example with the bank transfer, the results of the
- debit operation performed on the first object is not
- visible to other transactions until the credit operation
- is successfully completed and the transaction is committed.</p>
-
- <p>By durability we mean that once the transaction is committed,
- the changes that it made to the database state are permanent
- and will survive failures such as an application crash. From
- now on the only way to alter this state is to execute and commit
- another transaction.</p>
-
- <p>A transaction is started by calling either the
- <code>database::begin()</code> or <code>connection::begin()</code>
- function. The returned transaction handle is stored in
- an instance of the <code>odb::transaction</code> class.
- You will need to include the <code>&lt;odb/transaction.hxx></code>
- header file to make this class available in your application.
- For example:</p>
-
- <pre class="cxx">
-#include &lt;odb/transaction.hxx>
-
-transaction t (db.begin ())
-
-// Perform database operations.
-
-t.commit ();
- </pre>
-
- <p>The <code>odb::transaction</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- class transaction
- {
- public:
- typedef odb::database database_type;
- typedef odb::connection connection_type;
-
- explicit
- transaction (transaction_impl*, bool make_current = true);
-
- transaction ();
-
- void
- reset (transaction_impl*, bool make_current = true);
-
- void
- commit ();
-
- void
- rollback ();
-
- database_type&amp;
- database ();
-
- connection_type&amp;
- connection ();
-
- bool
- finilized () const;
-
- public:
- static bool
- has_current ();
-
- static transaction&amp;
- current ();
-
- static void
- current (transaction&amp;);
-
- static bool
- reset_current ();
-
- // Callback API.
- //
- public:
- ...
- };
-}
- </pre>
-
- <p>The <code>commit()</code> function commits a transaction and
- <code>rollback()</code> rolls it back. Unless the transaction
- has been <em>finalized</em>, that is, explicitly committed or rolled
- back, the destructor of the <code>transaction</code> class will
- automatically roll it back when the transaction instance goes
- out of scope. If we try to commit or roll back a finalized
- transaction, the <code>odb::transaction_already_finalized</code>
- exception is thrown.</p>
-
- <p>The <code>database()</code> accessor returns the database this
- transaction is working on. Similarly, the <code>connection()</code>
- accessor returns the database connection this transaction is on
- (<a href="#3.6">Section 3.6, "Connections"</a>).</p>
-
- <p>The static <code>current()</code> accessor returns the
- currently active transaction for this thread. If there is no active
- transaction, this function throws the <code>odb::not_in_transaction</code>
- exception. We can check whether there is a transaction in effect in
- this thread using the <code>has_current()</code> static function.</p>
-
- <p>The <code>make_current</code> argument in the <code>transaction</code>
- constructor as well as the static <code>current()</code> modifier and
- <code>reset_current()</code> function give us additional
- control over the nomination of the currently active transaction.
- If we pass <code>false</code> as the <code>make_current</code>
- argument, then the newly created transaction will not
- automatically be made the active transaction for this
- thread. Later, we can use the static <code>current()</code> modifier
- to set this transaction as the active transaction.
- The <code>reset_current()</code> static function clears the
- currently active transaction. Together, these mechanisms
- allow for more advanced use cases, such as multiplexing
- two or more transactions on the same thread. For example:</p>
-
- <pre class="cxx">
-transaction t1 (db1.begin ()); // Active transaction.
-transaction t2 (db2.begin (), false); // Not active.
-
-// Perform database operations on db1.
-
-transaction::current (t2); // Deactivate t1, activate t2.
-
-// Perform database operations on db2.
-
-transaction::current (t1); // Switch back to t1.
-
-// Perform some more database operations on db1.
-
-t1.commit ();
-
-transaction::current (t2); // Switch to t2.
-
-// Perform some more database operations on db2.
-
-t2.commit ();
- </pre>
-
- <p>The <code>reset()</code> modifier allows us to reuse the same
- <code>transaction</code> instance to complete several database
- transactions. Similar to the destructor, <code>reset()</code>
- will roll the current transaction back if it hasn't been finalized.
- The default <code>transaction</code> constructor creates a finalized
- transaction which can later be initialized using <code>reset()</code>.
- The <code>finilized()</code> accessor can be used to check whether the
- transaction has been finalized. Here is how we can use this functionality
- to commit the current transaction and start a new one every time a
- certain number of database operations has been performed:</p>
-
- <pre class="cxx">
-transaction t (db.begin ());
-
-for (size_t i (0); i &lt; n; ++i)
-{
- // Perform a database operation, such as persist an object.
-
- // Commit the current transaction and start a new one after
- // every 100 operations.
- //
- if (i % 100 == 0)
- {
- t.commit ();
- t.reset (db.begin ());
- }
-}
-
-t.commit ();
- </pre>
-
- <p>For more information on the transaction callback support, refer
- to <a href="#15.1">Section 15.1, "Transaction Callbacks"</a>.</p>
-
- <p>Note that in the above discussion of atomicity, consistency,
- isolation, and durability, all of those guarantees only apply
- to the object's state in the database as opposed to the object's
- state in the application's memory. It is possible to roll
- a transaction back but still have changes from this
- transaction in the application's memory. An easy way to
- avoid this potential inconsistency is to instantiate
- persistent objects only within the transaction scope. Consider,
- for example, these two implementations of the same transaction:</p>
-
- <pre class="cxx">
-void
-update_age (database&amp; db, person&amp; p)
-{
- transaction t (db.begin ());
-
- p.age (p.age () + 1);
- db.update (p);
-
- t.commit ();
-}
- </pre>
-
- <p>In the above implementation, if the <code>update()</code> call fails
- and the transaction is rolled back, the state of the <code>person</code>
- object in the database and the state of the same object in the
- application's memory will differ. Now consider an
- alternative implementation which only instantiates the
- <code>person</code> object for the duration of the transaction:</p>
-
- <pre class="cxx">
-void
-update_age (database&amp; db, unsigned long id)
-{
- transaction t (db.begin ());
-
- auto_ptr&lt;person> p (db.load&lt;person> (id));
- p.age (p.age () + 1);
- db.update (p);
-
- t.commit ();
-}
- </pre>
-
- <p>Of course, it may not always be possible to write the
- application in this style. Oftentimes we need to access and
- modify the application's state of persistent objects out of
- transactions. In this case it may make sense to try to
- roll back the changes made to the application state if
- the transaction was rolled back and the database state
- remains unchanged. One way to do this is to re-load
- the object's state from the database, for example:</p>
-
- <pre class="cxx">
-void
-update_age (database&amp; db, person&amp; p)
-{
- try
- {
- transaction t (db.begin ());
-
- p.age (p.age () + 1);
- db.update (p);
-
- t.commit ();
- }
- catch (...)
- {
- transaction t (db.begin ());
- db.load (p.id (), p);
- t.commit ();
-
- throw;
- }
-}
- </pre>
-
- <p>See also <a href="#15.1">Section 15.1, "Transaction Callbacks"</a>
- for an alternative approach.</p>
-
- <h2><a name="3.6">3.6 Connections</a></h2>
-
- <p>The <code>odb::connection</code> class represents a connection
- to the database. Normally, you wouldn't work with connections
- directly but rather let the ODB runtime obtain and release
- connections as needed. However, certain use cases may require
- obtaining a connection manually. For completeness, this section
- describes the <code>connection</code> class and discusses some
- of its use cases. You may want to skip this section if you are
- reading through the manual for the first time.</p>
-
- <p>Similar to <code>odb::database</code>, the <code>odb::connection</code>
- class is a common interface for all the database system-specific
- classes provided by ODB. For details on the system-specific
- <code>connection</code> classes, refer to <a href="#II">Part II,
- "Database Systems"</a>.</p>
-
- <p>To make the <code>odb::connection</code> class available in your
- application you will need to include the <code>&lt;odb/connection.hxx></code>
- header file. The <code>odb::connection</code> class has the
- following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- class connection
- {
- public:
- typedef odb::database database_type;
-
- transaction
- begin () = 0;
-
- unsigned long long
- execute (const char* statement);
-
- unsigned long long
- execute (const std::string&amp; statement);
-
- unsigned long long
- execute (const char* statement, std::size_t length);
-
- database_type&amp;
- database ();
- };
-
- typedef details::shared_ptr&lt;connection> connection_ptr;
-}
- </pre>
-
- <p>The <code>begin()</code> function is used to start a transaction
- on the connection. The <code>execute()</code> functions allow
- us to execute native database statements on the connection.
- Their semantics are equivalent to the <code>database::execute()</code>
- functions (<a href="#3.12">Section 3.12, "Executing Native SQL
- Statements"</a>) except that they can be legally called outside
- a transaction. Finally, the <code>database()</code> accessor
- returns a reference to the <code>odb::database</code> instance
- to which this connection corresponds.</p>
-
- <p>To obtain a connection we call the <code>database::connection()</code>
- function. The connection is returned as <code>odb::connection_ptr</code>,
- which is an implementation-specific smart pointer with the shared
- pointer semantics. This, in particular, means that the connection
- pointer can be copied and returned from functions. Once the last
- instance of <code>connection_ptr</code> pointing to the same
- connection is destroyed, the connection is returned to the
- <code>database</code> instance. The following code fragment
- shows how we can obtain, use, and release a connection:</p>
-
- <pre class="cxx">
-using namespace odb::core;
-
-database&amp; db = ...
-connection_ptr c (db.connection ());
-
-// Temporarily disable foreign key constraints.
-//
-c->execute ("SET FOREIGN_KEY_CHECKS = 0");
-
-// Start a transaction on this connection.
-//
-transaction t (c->begin ());
-...
-t.commit ();
-
-// Restore foreign key constraints.
-//
-c->execute ("SET FOREIGN_KEY_CHECKS = 1");
-
-// When 'c' goes out of scope, the connection is returned to 'db'.
- </pre>
-
- <p>Some of the use cases which may require direct manipulation of
- connections include out-of-transaction statement execution,
- such as the execution of connection configuration statements,
- the implementation of a connection-per-thread policy, and making
- sure that a set of transactions is executed on the same
- connection.</p>
-
- <h2><a name="3.7">3.7 Error Handling and Recovery</a></h2>
-
- <p>ODB uses C++ exceptions to report database operation errors. Most
- ODB exceptions signify <em>hard</em> errors or errors that cannot
- be corrected without some intervention from the application.
- For example, if we try to load an object with an unknown object
- id, the <code>odb::object_not_persistent</code> exception is
- thrown. Our application may be able to correct this error, for
- instance, by obtaining a valid object id and trying again.
- The hard errors and corresponding ODB exceptions that can be
- thrown by each database function are described in the remainder
- of this chapter with <a href="#3.14">Section 3.14, "ODB Exceptions"</a>
- providing a quick reference for all the ODB exceptions.</p>
-
- <p>The second group of ODB exceptions signify <em>soft</em> or
- <em>recoverable</em> errors. Such errors are temporary
- failures which normally can be corrected by simply re-executing
- the transaction. ODB defines three such exceptions:
- <code>odb::connection_lost</code>, <code>odb::timeout</code>,
- and <code>odb::deadlock</code>. All recoverable ODB exceptions
- are derived from the common <code>odb::recoverable</code> base
- exception which can be used to handle all the recoverable
- conditions with a single <code>catch</code> block.</p>
-
- <p>The <code>odb::connection_lost</code> exception is thrown if
- a connection to the database is lost in the middle of
- a transaction. In this situation the transaction is aborted but
- it can be re-tried without any changes. Similarly, the
- <code>odb::timeout</code> exception is thrown if one of the
- database operations or the whole transaction has timed out.
- Again, in this case the transaction is aborted but can be
- re-tried as is.</p>
-
- <p>If two or more transactions access or modify more than one object
- and are executed concurrently by different applications or by
- different threads within the same application, then it is possible
- that these transactions will try to access objects in an incompatible
- order and deadlock. The canonical example of a deadlock are
- two transactions in which the first has modified <code>object1</code>
- and is waiting for the second transaction to commit its changes to
- <code>object2</code> so that it can also update <code>object2</code>.
- At the same time the second transaction has modified <code>object2</code>
- and is waiting for the first transaction to commit its changes to
- <code>object1</code> because it also needs to modify <code>object1</code>.
- As a result, none of the two transactions can be completed.</p>
-
- <p>The database system detects such situations and automatically
- aborts the waiting operation in one of the deadlocked transactions.
- In ODB this translates to the <code>odb::deadlock</code>
- recoverable exception being thrown from one of the database functions.</p>
-
- <p>The following code fragment shows how to handle the recoverable
- exceptions by restarting the affected transaction:</p>
-
- <pre class="cxx">
-const unsigned short max_retries = 5;
-
-for (unsigned short retry_count (0); ; retry_count++)
-{
- try
- {
- transaction t (db.begin ());
-
- ...
-
- t.commit ();
- break;
- }
- catch (const odb::recoverable&amp; e)
- {
- if (retry_count > max_retries)
- throw retry_limit_exceeded (e.what ());
- else
- continue;
- }
-}
- </pre>
-
- <h2><a name="3.8">3.8 Making Objects Persistent</a></h2>
-
- <p>A newly created instance of a persistent class is transient.
- We use the <code>database::persist()</code> function template
- to make a transient instance persistent. This function has four
- overloaded versions with the following signatures:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- typename object_traits&lt;T>::id_type
- persist (const T&amp; object);
-
- template &lt;typename T>
- typename object_traits&lt;T>::id_type
- persist (const object_traits&lt;T>::const_pointer_type&amp; object);
-
- template &lt;typename T>
- typename object_traits&lt;T>::id_type
- persist (T&amp; object);
-
- template &lt;typename T>
- typename object_traits&lt;T>::id_type
- persist (const object_traits&lt;T>::pointer_type&amp; object);
- </pre>
-
- <p>Here and in the rest of the manual,
- <code>object_traits&lt;T>::pointer_type</code> and
- <code>object_traits&lt;T>::const_pointer_type</code> denote the
- unrestricted and constant object pointer types (<a href="#3.3">Section
- 3.3, "Object and View Pointers"</a>), respectively.
- Similarly, <code>object_traits&lt;T>::id_type</code> denotes the object
- id type. The <code>odb::object_traits</code> template is part of the
- database support code generated by the ODB compiler.</p>
-
- <p>The first <code>persist()</code> function expects a constant reference
- to an instance being persisted. The second function expects a constant
- object pointer. Both of these functions can only be used on objects with
- application-assigned object ids (<a href="#14.4.2">Section 14.4.2,
- "<code>auto</code>"</a>).</p>
-
- <p>The second and third <code>persist()</code> functions are similar to the
- first two except that they operate on unrestricted references and object
- pointers. If the identifier of the object being persisted is assigned
- by the database, these functions update the id member of the passed
- instance with the assigned value. All four functions return the object
- id of the newly persisted object.</p>
-
- <p>If the database already contains an object of this type with this
- identifier, the <code>persist()</code> functions throw the
- <code>odb::object_already_persistent</code> exception. This should
- never happen for database-assigned object ids as long as the
- number of objects persisted does not exceed the value space of
- the id type.</p>
-
- <p>When calling the <code>persist()</code> functions, we don't need to
- explicitly specify the template type since it will be automatically
- deduced from the argument being passed. The following example shows
- how we can call these functions:</p>
-
- <pre class="cxx">
-person john ("John", "Doe", 33);
-shared_ptr&lt;person> jane (new person ("Jane", "Doe", 32));
-
-transaction t (db.begin ());
-
-db.persist (john);
-unsigned long jane_id (db.persist (jane));
-
-t.commit ();
-
-cerr &lt;&lt; "Jane's id: " &lt;&lt; jane_id &lt;&lt; endl;
- </pre>
-
- <p>Notice that in the above code fragment we have created instances
- that we were planning to make persistent before starting the
- transaction. Likewise, we printed Jane's id after we have committed
- the transaction. As a general rule, you should avoid performing
- operations within the transaction scope that can be performed
- before the transaction starts or after it terminates. An active
- transaction consumes both your application's resources, such as
- a database connection, as well as the database server's
- resources, such as object locks. By following the above rule you
- make sure these resources are released and made available to other
- threads in your application and to other applications as soon as
- possible.</p>
-
- <p>Some database systems support persisting multiple objects with a
- single underlying statement execution which can result in significantly
- improved performance. For such database systems ODB provides
- bulk <code>persist()</code> functions. For details, refer to
- <a href="#15.3">Section 15.3, "Bulk Database Operations"</a>.</p>
-
- <h2><a name="3.9">3.9 Loading Persistent Objects</a></h2>
-
- <p>Once an object is made persistent, and you know its object id, it
- can be loaded by the application using the <code>database::load()</code>
- function template. This function has two overloaded versions with
- the following signatures:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- typename object_traits&lt;T>::pointer_type
- load (const typename object_traits&lt;T>::id_type&amp; id);
-
- template &lt;typename T>
- void
- load (const typename object_traits&lt;T>::id_type&amp; id, T&amp; object);
- </pre>
-
- <p>Given an object id, the first function allocates a new instance
- of the object class in the dynamic memory, loads its state from
- the database, and returns the pointer to the new instance. The
- second function loads the object's state into an existing instance.
- Both functions throw <code>odb::object_not_persistent</code> if
- there is no object of this type with this id in the database.</p>
-
- <p>When we call the first <code>load()</code> function, we need to
- explicitly specify the object type. We don't need to do this for
- the second function because the object type will be automatically
- deduced from the second argument, for example:</p>
-
- <pre class="cxx">
-transaction t (db.begin ());
-
-auto_ptr&lt;person> jane (db.load&lt;person> (jane_id));
-
-db.load (jane_id, *jane);
-
-t.commit ();
- </pre>
-
- <p>In certain situations it may be necessary to reload the state
- of an object from the database. While this is easy to achieve
- using the second <code>load()</code> function, ODB provides
- the <code>database::reload()</code> function template that
- has a number of special properties. This function has two
- overloaded versions with the following signatures:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- void
- reload (T&amp; object);
-
- template &lt;typename T>
- void
- reload (const object_traits&lt;T>::pointer_type&amp; object);
- </pre>
-
- <p>The first <code>reload()</code> function expects an object
- reference, while the second expects an object pointer. Both
- functions expect the id member in the passed object to contain
- a valid object identifier and, similar to <code>load()</code>,
- both will throw <code>odb::object_not_persistent</code> if
- there is no object of this type with this id in the database.</p>
-
- <p>The first special property of <code>reload()</code>
- compared to the <code>load()</code> function is that it
- does not interact with the session's object cache
- (<a href="#11.1">Section 11.1, "Object Cache"</a>). That is, if
- the object being reloaded is already in the cache, then it will
- remain there after <code>reload()</code> returns. Similarly, if the
- object is not in the cache, then <code>reload()</code> won't
- put it there either.</p>
-
- <p>The second special property of the <code>reload()</code> function
- only manifests itself when operating on an object with the optimistic
- concurrency model. In this case, if the states of the object
- in the application memory and in the database are the same, then
- no reloading will occur. For more information on optimistic
- concurrency, refer to <a href="#12">Chapter 12, "Optimistic
- Concurrency"</a>.</p>
-
- <p>If we don't know for sure whether an object with a given id
- is persistent, we can use the <code>find()</code> function
- instead of <code>load()</code>, for example:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- typename object_traits&lt;T>::pointer_type
- find (const typename object_traits&lt;T>::id_type&amp; id);
-
- template &lt;typename T>
- bool
- find (const typename object_traits&lt;T>::id_type&amp; id, T&amp; object);
- </pre>
-
- <p>If an object with this id is not found in the database, the first
- <code>find()</code> function returns a <code>NULL</code> pointer
- while the second function leaves the passed instance unmodified and
- returns <code>false</code>.</p>
-
- <p>If we don't know the object id, then we can use queries to
- find the object (or objects) matching some criteria
- (<a href="#4">Chapter 4, "Querying the Database"</a>). Note,
- however, that loading an object's state using its
- identifier can be significantly faster than executing a query.</p>
-
-
- <h2><a name="3.10">3.10 Updating Persistent Objects</a></h2>
-
- <p>If a persistent object has been modified, we can store the updated
- state in the database using the <code>database::update()</code>
- function template. This function has three overloaded versions with
- the following signatures:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- void
- update (const T&amp; object);
-
- template &lt;typename T>
- void
- update (const object_traits&lt;T>::const_pointer_type&amp; object);
-
- template &lt;typename T>
- void
- update (const object_traits&lt;T>::pointer_type&amp; object);
- </pre>
-
- <p>The first <code>update()</code> function expects an object reference,
- while the other two expect object pointers. If the object passed to
- one of these functions does not exist in the database,
- <code>update()</code> throws the <code>odb::object_not_persistent</code>
- exception (but see a note on optimistic concurrency below).</p>
-
- <p>Below is an example of the funds transfer that we talked about
- in the earlier section on transactions. It uses the hypothetical
- <code>bank_account</code> persistent class:</p>
-
- <pre class="cxx">
-void
-transfer (database&amp; db,
- unsigned long from_acc,
- unsigned long to_acc,
- unsigned int amount)
-{
- bank_account from, to;
-
- transaction t (db.begin ());
-
- db.load (from_acc, from);
-
- if (from.balance () &lt; amount)
- throw insufficient_funds ();
-
- db.load (to_acc, to);
-
- to.balance (to.balance () + amount);
- from.balance (from.balance () - amount);
-
- db.update (to);
- db.update (from);
-
- t.commit ();
-}
- </pre>
-
- <p>The same can be accomplished using dynamically allocated objects
- and the <code>update()</code> function with object pointer argument,
- for example:</p>
-
- <pre class="cxx">
-transaction t (db.begin ());
-
-shared_ptr&lt;bank_account> from (db.load&lt;bank_account> (from_acc));
-
-if (from->balance () &lt; amount)
- throw insufficient_funds ();
-
-shared_ptr&lt;bank_account> to (db.load&lt;bank_account> (to_acc));
-
-to->balance (to->balance () + amount);
-from->balance (from->balance () - amount);
-
-db.update (to);
-db.update (from);
-
-t.commit ();
- </pre>
-
- <p>If any of the <code>update()</code> functions are operating on a
- persistent class with the optimistic concurrency model, then they will
- throw the <code>odb::object_changed</code> exception if the state of the
- object in the database has changed since it was last loaded into the
- application memory. Furthermore, for such classes, <code>update()</code>
- no longer throws the <code>object_not_persistent</code> exception if
- there is no such object in the database. Instead, this condition is
- treated as a change of object state and <code>object_changed</code>
- is thrown instead. For a more detailed discussion of optimistic
- concurrency, refer to <a href="#12">Chapter 12, "Optimistic
- Concurrency"</a>.</p>
-
- <p>In ODB, persistent classes, composite value types, as well as individual
- data members can be declared read-only (see <a href="#14.1.4">Section
- 14.1.4, "<code>readonly</code> (object)"</a>, <a href="#14.3.6">Section
- 14.3.6, "<code>readonly</code> (composite value)"</a>, and
- <a href="#14.4.12">Section 14.4.12, "<code>readonly</code>
- (data member)"</a>).</p>
-
- <p>If an individual data member is declared read-only, then
- any changes to this member will be ignored when updating the database
- state of an object using any of the above <code>update()</code>
- functions. A <code>const</code> data member is automatically treated
- as read-only. If a composite value is declared read-only then all its
- data members are treated as read-only.</p>
-
- <p>If the whole object is declared read-only then the database state of
- this object cannot be changed. Calling any of the above
- <code>update()</code> functions for such an object will result in a
- compile-time error.</p>
-
- <p>Similar to <code>persist()</code>, for database systems that support
- this functionality, ODB provides bulk <code>update()</code> functions.
- For details, refer to <a href="#15.3">Section 15.3, "Bulk Database
- Operations"</a>.</p>
-
- <h2><a name="3.11">3.11 Deleting Persistent Objects</a></h2>
-
- <p>To delete a persistent object's state from the database we use the
- <code>database::erase()</code> or <code>database::erase_query()</code>
- function templates. If the application still has an instance of the
- erased object, this instance becomes transient. The <code>erase()</code>
- function has the following overloaded versions:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- void
- erase (const T&amp; object);
-
- template &lt;typename T>
- void
- erase (const object_traits&lt;T>::const_pointer_type&amp; object);
-
- template &lt;typename T>
- void
- erase (const object_traits&lt;T>::pointer_type&amp; object);
-
- template &lt;typename T>
- void
- erase (const typename object_traits&lt;T>::id_type&amp; id);
- </pre>
-
- <p>The first <code>erase()</code> function uses an object itself, in
- the form of an object reference, to delete its state from the
- database. The next two functions accomplish the same result but using
- object pointers. Note that all three functions leave the passed
- object unchanged. It simply becomes transient. The last function
- uses the object id to identify the object to be deleted. If the
- object does not exist in the database, then all four functions
- throw the <code>odb::object_not_persistent</code> exception
- (but see a note on optimistic concurrency below).</p>
-
- <p>We have to specify the object type when calling the last
- <code>erase()</code> function. The same is unnecessary for the
- first three functions because the object type will be automatically
- deduced from their arguments. The following example shows how we
- can call these functions:</p>
-
- <pre class="cxx">
-person&amp; john = ...
-shared_ptr&lt;jane> jane = ...
-unsigned long joe_id = ...
-
-transaction t (db.begin ());
-
-db.erase (john);
-db.erase (jane);
-db.erase&lt;person> (joe_id);
-
-t.commit ();
- </pre>
-
- <p>If any of the <code>erase()</code> functions except the last one are
- operating on a persistent class with the optimistic concurrency
- model, then they will throw the <code>odb::object_changed</code> exception
- if the state of the object in the database has changed since it was
- last loaded into the application memory. Furthermore, for such
- classes, <code>erase()</code> no longer throws the
- <code>object_not_persistent</code> exception if there is no such
- object in the database. Instead, this condition is treated as a
- change of object state and <code>object_changed</code> is thrown
- instead. For a more detailed discussion of optimistic concurrency,
- refer to <a href="#12">Chapter 12, "Optimistic Concurrency"</a>.</p>
-
- <p>Similar to <code>persist()</code> and <code>update()</code>, for
- database systems that support this functionality, ODB provides
- bulk <code>erase()</code> functions. For details, refer to
- <a href="#15.3">Section 15.3, "Bulk Database Operations"</a>.</p>
-
- <p>The <code>erase_query()</code> function allows us to delete
- the state of multiple objects matching certain criteria. It uses
- the query expression of the <code>database::query()</code> function
- (<a href="#4">Chapter 4, "Querying the Database"</a>) and,
- because the ODB query facility is optional, it is only available
- if the <code>--generate-query</code> ODB compiler option was
- specified. The <code>erase_query()</code> function has the
- following overloaded versions:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- unsigned long long
- erase_query ();
-
- template &lt;typename T>
- unsigned long long
- erase_query (const odb::query&lt;T>&amp;);
- </pre>
-
- <p>The first <code>erase_query()</code> function is used to delete
- the state of all the persistent objects of a given type stored
- in the database. The second function uses the passed query instance
- to only delete the state of objects matching the query criteria.
- Both functions return the number of objects erased. When calling
- the <code>erase_query()</code> function, we have to explicitly
- specify the object type we are erasing. For example:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;person> query;
-
-transaction t (db.begin ());
-
-db.erase_query&lt;person> (query::last == "Doe" &amp;&amp; query::age &lt; 30);
-
-t.commit ();
- </pre>
-
- <p>Unlike the <code>query()</code> function, when calling
- <code>erase_query()</code> we cannot use members from pointed-to
- objects in the query expression. However, we can still use
- a member corresponding to a pointer as an ordinary object
- member that has the id type of the pointed-to object
- (<a href="#6">Chapter 6, "Relationships"</a>). This allows us
- to compare object ids as well as test the pointer for
- <code>NULL</code>. As an example, the following transaction
- makes sure that all the <code>employee</code> objects that
- reference an <code>employer</code> object that is about to
- be deleted are deleted as well. Here we assume that the
- <code>employee</code> class contains a pointer to the
- <code>employer</code> class. Refer to <a href="#6">Chapter 6,
- "Relationships"</a> for complete definitions of these
- classes.</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee> query;
-
-transaction t (db.begin ());
-
-employer&amp; e = ... // Employer object to be deleted.
-
-db.erase_query&lt;employee> (query::employer == e.id ());
-db.erase (e);
-
-t.commit ();
- </pre>
-
-
- <h2><a name="3.12">3.12 Executing Native SQL Statements</a></h2>
-
- <p>In some situations we may need to execute native SQL statements
- instead of using the object-oriented database API described above.
- For example, we may want to tune the database schema generated
- by the ODB compiler or take advantage of a feature that is
- specific to the database system we are using. The
- <code>database::execute()</code> function, which has three
- overloaded versions, provides this functionality:</p>
-
- <pre class="cxx">
- unsigned long long
- execute (const char* statement);
-
- unsigned long long
- execute (const std::string&amp; statement);
-
- unsigned long long
- execute (const char* statement, std::size_t length)
- </pre>
-
- <p>The first <code>execute()</code> function expects the SQL statement
- as a zero-terminated C-string. The last version expects the explicit
- statement length as the second argument and the statement itself
- may contain <code>'\0'</code> characters, for example, to represent
- binary data, if the database system supports it. All three functions
- return the number of rows that were affected by the statement. For
- example:</p>
-
- <pre class="cxx">
-transaction t (db.begin ());
-
-db.execute ("DROP TABLE test");
-db.execute ("CREATE TABLE test (n INT PRIMARY KEY)");
-
-t.commit ();
- </pre>
-
- <p>While these functions must always be called within a transaction,
- it may be necessary to execute a native statement outside a
- transaction. This can be done using the
- <code>connection::execute()</code> functions as described in
- <a href="#3.6">Section 3.6, "Connections"</a>.</p>
-
- <h2><a name="3.13">3.13 Tracing SQL Statement Execution</a></h2>
-
- <p>Oftentimes it is useful to understand what SQL statements are
- executed as a result of high-level database operations. For
- example, we can use this information to figure out why certain
- transactions don't produce desired results or why they take
- longer than expected.</p>
-
- <p>While this information can usually be obtained from the database
- logs, ODB provides an application-side SQL statement tracing
- support that is both more convenient and finer-grained.
- For example, in a typical situation that calls for tracing
- we would like to see the SQL statements executed as a result
- of a specific transaction. While it may be difficult to
- extract such a subset of statements from the database logs,
- it is easy to achieve with ODB tracing support:</p>
-
- <pre class="cxx">
-transaction t (db.begin ());
-t.tracer (stderr_tracer);
-
-...
-
-t.commit ();
- </pre>
-
- <p>ODB allows us to specify a tracer on the database, connection,
- and transaction levels. If specified for the database, then
- all the statements executed on this database will be traced.
- On the other hand, if a tracer is specified for the
- connection, then only the SQL statements executed on this
- connection will be traced. Similarly, a tracer specified
- for a transaction will only show statements that are
- executed as part of this transaction. All three classes
- (<code>odb::database</code>, <code>odb::connection</code>,
- and <code>odb::transaction</code>) provide the identical
- tracing API:</p>
-
- <pre class="cxx">
- void
- tracer (odb::tracer&amp;);
-
- void
- tracer (odb::tracer*);
-
- odb::tracer*
- tracer () const;
- </pre>
-
- <p>The first two <code>tracer()</code> functions allow us to set
- the tracer object with the second one allowing us to clear the
- current tracer by passing a <code>NULL</code> pointer. The
- last <code>tracer()</code> function allows us to get the
- current tracer object. It returns a <code>NULL</code> pointer
- if there is no tracer in effect. Note that the tracing API
- does not manage the lifetime of the tracer object. The tracer
- should be valid for as long as it is being used. Furthermore,
- the tracing API is not thread-safe. Trying to set a tracer
- from multiple threads simultaneously will result in
- undefined behavior.</p>
-
- <p>The <code>odb::tracer</code> class defines a callback interface
- that can be used to create custom tracer implementations. The
- <code>odb::stderr_tracer</code> and <code>odb::stderr_full_tracer</code>
- are built-in tracer implementations provided by the ODB runtime.
- They both print SQL statements being executed to the standard error
- stream. The full tracer, in addition to tracing statement executions,
- also traces their preparations and deallocations. One situation where
- the full tracer can be particularly useful is if a statement (for
- example a custom query) contains a syntax error. In this case the
- error will be detected during preparation and, as a result, the
- statement will never be executed. The only way to see such a statement
- is by using the full tracing.</p>
-
- <p>The <code>odb::tracer</code> class is defined in the
- <code>&lt;odb/tracer.hxx></code> header file which you will need to
- include in order to make this class available in your application.
- The <code>odb::tracer</code> interface provided the following
- callback functions:</p>
-
- <pre class="cxx">
-namespace odb
-{
- class tracer
- {
- public:
- virtual void
- prepare (connection&amp;, const statement&amp;);
-
- virtual void
- execute (connection&amp;, const statement&amp;);
-
- virtual void
- execute (connection&amp;, const char* statement) = 0;
-
- virtual void
- deallocate (connection&amp;, const statement&amp;);
- };
-}
- </pre>
-
- <p>The <code>prepare()</code> and <code>deallocate()</code> functions
- are called when a prepared statement is created and destroyed,
- respectively. The first <code>execute()</code> function is called
- when a prepared statement is executed while the second one is called
- when a normal statement is executed. The default implementations
- for the <code>prepare()</code> and <code>deallocate()</code>
- functions do nothing while the first <code>execute()</code> function
- calls the second one passing the statement text as the second
- argument. As a result, if all you are interested in are the
- SQL statements being executed, then you only need to override the
- second <code>execute()</code> function.</p>
-
- <p>In addition to the common <code>odb::tracer</code> interface,
- each database runtime provides a database-specific version
- as <code>odb::&lt;database>::tracer</code>. It has exactly
- the same interface as the common version except that the
- <code>connection</code> and <code>statement</code> types
- are database-specific, which gives us access to additional,
- database-specific information.</p>
-
- <p>As an example, consider a more elaborate, PostgreSQL-specific
- tracer implementation. Here we rely on the fact that the PostgreSQL
- ODB runtime uses names to identify prepared statements and this
- information can be obtained from the <code>odb::pgsql::statement</code>
- object:</p>
-
- <pre class="cxx">
-#include &lt;odb/pgsql/tracer.hxx>
-#include &lt;odb/pgsql/database.hxx>
-#include &lt;odb/pgsql/connection.hxx>
-#include &lt;odb/pgsql/statement.hxx>
-
-class pgsql_tracer: public odb::pgsql::tracer
-{
- virtual void
- prepare (odb::pgsql::connection&amp; c, const odb::pgsql::statement&amp; s)
- {
- cerr &lt;&lt; c.database ().db () &lt;&lt; ": PREPARE " &lt;&lt; s.name ()
- &lt;&lt; " AS " &lt;&lt; s.text () &lt;&lt; endl;
- }
-
- virtual void
- execute (odb::pgsql::connection&amp; c, const odb::pgsql::statement&amp; s)
- {
- cerr &lt;&lt; c.database ().db () &lt;&lt; ": EXECUTE " &lt;&lt; s.name () &lt;&lt; endl;
- }
-
- virtual void
- execute (odb::pgsql::connection&amp; c, const char* statement)
- {
- cerr &lt;&lt; c.database ().db () &lt;&lt; ": " &lt;&lt; statement &lt;&lt; endl;
- }
-
- virtual void
- deallocate (odb::pgsql::connection&amp; c, const odb::pgsql::statement&amp; s)
- {
- cerr &lt;&lt; c.database ().db () &lt;&lt; ": DEALLOCATE " &lt;&lt; s.name () &lt;&lt; endl;
- }
-};
- </pre>
-
- <p>Note also that you can only set a database-specific tracer object
- using a database-specific database instance, for example:</p>
-
- <pre class="cxx">
-pgsql_tracer tracer;
-
-odb::database&amp; db = ...;
-db.tracer (tracer); // Compile error.
-
-odb::pgsql::database&amp; db = ...;
-db.tracer (tracer); // Ok.
- </pre>
-
- <h2><a name="3.14">3.14 ODB Exceptions</a></h2>
-
- <p>In the previous sections we have already mentioned some of the
- exceptions that can be thrown by the database functions. In this
- section we will discuss the ODB exception hierarchy and document
- all the exceptions that can be thrown by the common ODB
- runtime.</p>
-
- <p>The root of the ODB exception hierarchy is the abstract
- <code>odb::exception</code> class. This class derives
- from <code>std::exception</code> and has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- struct exception: std::exception
- {
- virtual const char*
- what () const throw () = 0;
- };
-}
- </pre>
-
- <p>Catching this exception guarantees that we will catch all the
- exceptions thrown by ODB. The <code>what()</code> function
- returns a human-readable description of the condition that
- triggered the exception.</p>
-
- <p>The concrete exceptions that can be thrown by ODB are presented
- in the following listing:</p>
-
- <pre class="cxx">
-namespace odb
-{
- struct null_pointer: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- // Transaction exceptions.
- //
- struct already_in_transaction: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct not_in_transaction: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct transaction_already_finalized: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- // Session exceptions.
- //
- struct already_in_session: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct not_in_session: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct session_required: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- // Database operations exceptions.
- //
- struct recoverable: exception
- {
- };
-
- struct connection_lost: recoverable
- {
- virtual const char*
- what () const throw ();
- };
-
- struct timeout: recoverable
- {
- virtual const char*
- what () const throw ();
- };
-
- struct deadlock: recoverable
- {
- virtual const char*
- what () const throw ();
- };
-
- struct object_not_persistent: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct object_already_persistent: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct object_changed: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct result_not_cached: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct database_exception: exception
- {
- };
-
- // Polymorphism support exceptions.
- //
- struct abstract_class: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct no_type_info: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- // Prepared query support exceptions.
- //
- struct prepared_already_cached: exception
- {
- const char*
- name () const;
-
- virtual const char*
- what () const throw ();
- };
-
- struct prepared_type_mismatch: exception
- {
- const char*
- name () const;
-
- virtual const char*
- what () const throw ();
- };
-
- // Schema catalog exceptions.
- //
- struct unknown_schema: exception
- {
- const std::string&amp;
- name () const;
-
- virtual const char*
- what () const throw ();
- };
-
- struct unknown_schema_version: exception
- {
- schema_version
- version () const;
-
- virtual const char*
- what () const throw ();
- };
-
- // Section exceptions.
- //
- struct section_not_loaded: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct section_not_in_object: exception
- {
- virtual const char*
- what () const throw ();
- };
-
- // Bulk operation exceptions.
- //
- struct multiple_exceptions: exception
- {
- ...
-
- virtual const char*
- what () const throw ();
- };
-}
- </pre>
-
- <p>The <code>null_pointer</code> exception is thrown when a
- pointer to a persistent object declared non-<code>NULL</code>
- with the <code>db&nbsp;not_null</code> or
- <code>db&nbsp;value_not_null</code> pragma has the <code>NULL</code>
- value. See <a href="#6">Chapter 6, "Relationships"</a> for details.</p>
-
- <p>The next three exceptions (<code>already_in_transaction</code>,
- <code>not_in_transaction</code>,
- <code>transaction_already_finalized</code>) are thrown by the
- <code>odb::transaction</code> class and are discussed
- in <a href="#3.5">Section 3.5, "Transactions"</a>.</p>
-
- <p>The next two exceptions (<code>already_in_session</code>, and
- <code>not_in_session</code>) are thrown by the <code>odb::session</code>
- class and are discussed in <a href="#11">Chapter 11, "Session"</a>.</p>
-
- <p>The <code>session_required</code> exception is thrown when ODB detects
- that correctly loading a bidirectional object relationship requires a
- session but one is not used. See <a href="#6.2">Section 6.2,
- "Bidirectional Relationships"</a> for more information on this
- exception.</p>
-
- <p>The <code>recoverable</code> exception serves as a common base
- for all the recoverable exceptions, which are: <code>connection_lost</code>,
- <code>timeout</code>, and <code>deadlock</code>. The
- <code>connection_lost</code> exception is thrown when a connection
- to the database is lost. Similarly, the <code>timeout</code> exception
- is thrown if one of the database operations or the whole transaction
- has timed out. The <code>deadlock</code> exception is thrown when a
- transaction deadlock is detected by the database system. These
- exceptions can be thrown by any database function. See
- <a href="#3.7">Section 3.7, "Error Handling and Recovery"</a>
- for details.</p>
-
- <p>The <code>object_already_persistent</code> exception is thrown
- by the <code>persist()</code> database function. See
- <a href="#3.8">Section 3.8, "Making Objects Persistent"</a>
- for details.</p>
-
- <p>The <code>object_not_persistent</code> exception is thrown
- by the <code>load()</code>, <code>update()</code>, and
- <code>erase()</code> database functions. Refer to
- <a href="#3.9">Section 3.9, "Loading Persistent Objects"</a>,
- <a href="#3.10">Section 3.10, "Updating Persistent Objects"</a>, and
- <a href="#3.11">Section 3.11, "Deleting Persistent Objects"</a> for
- more information.</p>
-
- <p>The <code>object_changed</code> exception is thrown
- by the <code>update()</code> database function and certain
- <code>erase()</code> database functions when
- operating on objects with the optimistic concurrency model. See
- <a href="#12">Chapter 12, "Optimistic Concurrency"</a> for details.</p>
-
- <p>The <code>result_not_cached</code> exception is thrown by
- the query result class. Refer to <a href="#4.4">Section 4.4,
- "Query Result"</a> for details.</p>
-
- <p>The <code>database_exception</code> exception is a base class for all
- database system-specific exceptions that are thrown by the
- database system-specific runtime library. Refer to <a href="#II">Part
- II, "Database Systems"</a> for more information.</p>
-
- <p>The <code>abstract_class</code> exception is thrown by the database
- functions when we attempt to persist, update, load, or erase an
- instance of a polymorphic abstract class. For more information
- on abstract classes, refer to <a href="#14.1.3">Section 14.1.3,
- "<code>abstract</code>"</a>.</p>
-
- <p>The <code>no_type_info</code> exception is thrown by the database
- functions when we attempt to persist, update, load, or erase an
- instance of a polymorphic class for which no type information
- is present in the application. This normally means that the
- generated database support code for this class has not been
- linked (or dynamically loaded) into the application or the
- discriminator value has not been mapped to a persistent
- class. For more information on polymorphism support, refer to
- <a href="#8.2">Section 8.2, "Polymorphism Inheritance"</a>.</p>
-
- <p>The <code>prepared_already_cached</code> exception is thrown by the
- <code>cache_query()</code> function if a prepared query with the
- specified name is already cached. The <code>prepared_type_mismatch</code>
- exception is thrown by the <code>lookup_query()</code> function if
- the specified prepared query object type or parameters type
- does not match the one in the cache. Refer to <a href="#4.5">Section
- 4.5, "Prepared Queries"</a> for details.</p>
-
- <p>The <code>unknown_schema</code> exception is thrown by the
- <code>odb::schema_catalog</code> class if a schema with the specified
- name is not found. Refer to <a href="#3.4">Section 3.4, "Database"</a>
- for details. The <code>unknown_schema_version</code> exception is
- thrown by the <code>schema_catalog</code> functions that deal with
- database schema evolution if the passed version is unknow. Refer
- to <a href="#13">Chapter 13, "Database Schema Evolution"</a> for
- details.</p>
-
- <p>The <code>section_not_loaded</code> exception is thrown if we
- attempt to update an object section that hasn't been loaded.
- The <code>section_not_in_object</code> exception is thrown if
- the section instance being loaded or updated does not belong
- to the corresponding object. See <a href="#9">Chapter 9,
- "Sections"</a> for more information on these exceptions.</p>
-
- <p>The <code>multiple_exceptions</code> exception is thrown by the
- bulk API functions. Refer to <a href="#15.3">Section 15.3, "Bulk
- Database Operations"</a> for details.</p>
-
- <p>The <code>odb::exception</code> class is defined in the
- <code>&lt;odb/exception.hxx></code> header file. All the
- concrete ODB exceptions are defined in
- <code>&lt;odb/exceptions.hxx></code> which also includes
- <code>&lt;odb/exception.hxx></code>. Normally you don't
- need to include either of these two headers because they are
- automatically included by <code>&lt;odb/database.hxx></code>.
- However, if the source file that handles ODB exceptions
- does not include <code>&lt;odb/database.hxx></code>, then
- you will need to explicitly include one of these headers.</p>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="4">4 Querying the Database</a></h1>
-
- <p>If we don't know the identifiers of the objects that we are looking
- for, we can use queries to search the database for objects matching
- certain criteria. The ODB query facility is optional and we need to
- explicitly request the generation of the necessary database support
- code with the <code>--generate-query</code> ODB compiler option.</p>
-
- <p>ODB provides a flexible query API that offers two distinct levels of
- abstraction from the database system query language such as SQL.
- At the high level we are presented with an easy to use yet powerful
- object-oriented query language, called ODB Query Language. This
- query language is modeled after and is integrated into C++ allowing
- us to write expressive and safe queries that look and feel like
- ordinary C++. We have already seen examples of these queries in the
- introductory chapters. Below is another, more interesting, example:</p>
-
- <pre class="cxx">
- typedef odb::query&lt;person> query;
- typedef odb::result&lt;person> result;
-
- unsigned short age;
- query q (query::first == "John" &amp;&amp; query::age &lt; query::_ref (age));
-
- for (age = 10; age &lt; 100; age += 10)
- {
- result r (db.query&lt;person> (q));
- ...
- }
- </pre>
-
- <p>At the low level, queries can be written as predicates using
- the database system-native query language such as the
- <code>WHERE</code> predicate from the SQL <code>SELECT</code>
- statement. This language will be referred to as native query
- language. At this level ODB still takes care of converting
- query parameters from C++ to the database system format. Below
- is the re-implementation of the above example using SQL as
- the native query language:</p>
-
- <pre class="cxx">
- query q ("first = 'John' AND age = " + query::_ref (age));
- </pre>
-
- <p>Note that at this level we lose the static typing of
- query expressions. For example, if we wrote something like this:</p>
-
- <pre class="cxx">
- query q (query::first == 123 &amp;&amp; query::agee &lt; query::_ref (age));
- </pre>
-
- <p>We would get two errors during the C++ compilation. The first would
- indicate that we cannot compare <code>query::first</code> to an
- integer and the second would pick the misspelling in
- <code>query::agee</code>. On the other hand, if we wrote something
- like this:</p>
-
- <pre class="cxx">
- query q ("first = 123 AND agee = " + query::_ref (age));
- </pre>
-
- <p>It would compile fine and would trigger an error only when executed
- by the database system.</p>
-
- <p>We can also combine the two query languages in a single query, for
- example:</p>
-
- <pre class="cxx">
- query q ("first = 'John' AND" + (query::age &lt; query::_ref (age)));
- </pre>
-
- <h2><a name="4.1">4.1 ODB Query Language</a></h2>
-
- <p>An ODB query is an expression that tells the database system whether
- any given object matches the desired criteria. As such, a query expression
- always evaluates as <code>true</code> or <code>false</code>. At
- the higher level, an expression consists of other expressions
- combined with logical operators such as <code>&amp;&amp;</code> (AND),
- <code>||</code> (OR), and <code>!</code> (NOT). For example:</p>
-
- <pre class="cxx">
- typedef odb::query&lt;person> query;
-
- query q (query::first == "John" || query::age == 31);
- </pre>
-
- <p>At the core of every query expression lie simple expressions which
- involve one or more object members, values, or parameters. To
- refer to an object member we use an expression such as
- <code>query::first</code> above. The names of members in the
- <code>query</code> class are derived from the names of data members
- in the object class by removing the common member name decorations,
- such as leading and trailing underscores, the <code>m_</code> prefix,
- etc.</p>
-
- <p>In a simple expression an object member can be compared to a value,
- parameter, or another member using a number of predefined operators
- and functions. The following table gives an overview of the available
- expressions:</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="operators" border="1">
- <tr>
- <th>Operator</th>
- <th>Description</th>
- <th>Example</th>
- </tr>
-
- <tr>
- <td><code>==</code></td>
- <td>equal</td>
- <td><code>query::age == 31</code></td>
- </tr>
-
- <tr>
- <td><code>!=</code></td>
- <td>unequal</td>
- <td><code>query::age != 31</code></td>
- </tr>
-
- <tr>
- <td><code>&lt;</code></td>
- <td>less than</td>
- <td><code>query::age &lt; 31</code></td>
- </tr>
-
- <tr>
- <td><code>></code></td>
- <td>greater than</td>
- <td><code>query::age > 31</code></td>
- </tr>
-
- <tr>
- <td><code>&lt;=</code></td>
- <td>less than or equal</td>
- <td><code>query::age &lt;= 31</code></td>
- </tr>
-
- <tr>
- <td><code>>=</code></td>
- <td>greater than or equal</td>
- <td><code>query::age >= 31</code></td>
- </tr>
-
- <tr>
- <td><code>in()</code></td>
- <td>one of the values</td>
- <td><code>query::age.in (30, 32, 34)</code></td>
- </tr>
-
- <tr>
- <td><code>in_range()</code></td>
- <td>one of the values in range</td>
- <td><code>query::age.in_range (begin, end)</code></td>
- </tr>
-
- <tr>
- <td><code>like()</code></td>
- <td>matches a pattern</td>
- <td><code>query::first.like ("J%")</code></td>
- </tr>
-
- <tr>
- <td><code>is_null()</code></td>
- <td>value is <code>NULL</code></td>
- <td><code>query::age.is_null ()</code></td>
- </tr>
-
- <tr>
- <td><code>is_not_null()</code></td>
- <td>value is <code>NOT NULL</code></td>
- <td><code>query::age.is_not_null ()</code></td>
- </tr>
- </table>
-
- <p>The <code>in()</code> function accepts a maximum of five arguments.
- Use the <code>in_range()</code> function if you need to compare
- to more than five values. This function accepts a pair of
- standard C++ iterators and compares to all the values from
- the <code>begin</code> position inclusive and until and
- excluding the <code>end</code> position. The following
- code fragment shows how we can use these functions:</p>
-
- <pre class="cxx">
- std::vector&lt;string> names;
-
- names.push_back ("John");
- names.push_back ("Jack");
- names.push_back ("Jane");
-
- query q1 (query::first.in ("John", "Jack", "Jane"));
- query q2 (query::first.in_range (names.begin (), names.end ()));
- </pre>
-
- <p>Note that the <code>like()</code> function does not perform any
- translation of the database system-specific extensions of the
- SQL <code>LIKE</code> operator. As a result, if you would like
- your application to be portable among various database systems,
- then limit the special characters used in the pattern to
- <code>%</code> (matches zero or more characters) and <code>_</code>
- (matches exactly one character). It is also possible to specify
- the escape character as a second argument to the <code>like()</code>
- function. This character can then be used to escape the special
- characters (<code>%</code> and <code>_</code>) in the pattern.
- For example, the following query will match any two characters
- separated by an underscore:</p>
-
- <pre class="cxx">
- query q (query::name.like ("_!__", "!"));
- </pre>
-
- <p>The operator precedence in the query expressions are the same
- as for equivalent C++ operators. We can use parentheses to
- make sure the expression is evaluated in the desired order.
- For example:</p>
-
- <pre class="cxx">
- query q ((query::first == "John" || query::first == "Jane") &amp;&amp;
- query::age &lt; 31);
- </pre>
-
-
- <h2><a name="4.2">4.2 Parameter Binding</a></h2>
-
- <p>An instance of the <code>odb::query</code> class encapsulates two
- parts of information about the query: the query expression and
- the query parameters. Parameters can be bound to C++ variables
- either by value or by reference.</p>
-
- <p>If a parameter is bound by value, then the value for this parameter
- is copied from the C++ variable to the query instance at the query
- construction time. On the other hand, if a parameter is bound by
- reference, then the query instance stores a reference to the
- bound variable. The actual value of the parameter is only extracted
- at the query execution time. Consider, for example, the following
- two queries:</p>
-
- <pre class="cxx">
- string name ("John");
-
- query q1 (query::first == query::_val (name));
- query q2 (query::first == query::_ref (name));
-
- name = "Jane";
-
- db.query&lt;person> (q1); // Find John.
- db.query&lt;person> (q2); // Find Jane.
- </pre>
-
- <p>The <code>odb::query</code> class provides two special functions,
- <code>_val()</code> and <code>_ref()</code>, that allow us to
- bind the parameter either by value or by reference, respectively.
- In the ODB query language, if the binding is not specified
- explicitly, the value semantic is used by default. In the
- native query language, binding must always be specified
- explicitly. For example:</p>
-
- <pre class="cxx">
- query q1 (query::age &lt; age); // By value.
- query q2 (query::age &lt; query::_val (age)); // By value.
- query q3 (query::age &lt; query::_ref (age)); // By reference.
-
- query q4 ("age &lt; " + age); // Error.
- query q5 ("age &lt; " + query::_val (age)); // By value.
- query q6 ("age &lt; " + query::_ref (age)); // By reference.
- </pre>
-
- <p>A query that only has by-value parameters does not depend on any
- other variables and is self-sufficient once constructed. A query
- that has one or more by-reference parameters depends on the
- bound variables until the query is executed. If one such variable
- goes out of scope and we execute the query, the behavior is
- undefined.</p>
-
- <h2><a name="4.3">4.3 Executing a Query</a></h2>
-
- <p>Once we have the query instance ready and by-reference parameters
- initialized, we can execute the query using the
- <code>database::query()</code> function template. It has two
- overloaded versions:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- result&lt;T>
- query (bool cache = true);
-
- template &lt;typename T>
- result&lt;T>
- query (const odb::query&lt;T>&amp;, bool cache = true);
- </pre>
-
- <p>The first <code>query()</code> function is used to return all the
- persistent objects of a given type stored in the database.
- The second function uses the passed query instance to only return
- objects matching the query criteria. The <code>cache</code> argument
- determines whether the objects' states should be cached in the
- application's memory or if they should be returned by the database
- system one by one as the iteration over the result progresses. The
- result caching is discussed in detail in the next section.</p>
-
- <p>When calling the <code>query()</code> function, we have to
- explicitly specify the object type we are querying. For example:</p>
-
- <pre class="cxx">
- typedef odb::query&lt;person> query;
- typedef odb::result&lt;person> result;
-
- result all (db.query&lt;person> ());
- result johns (db.query&lt;person> (query::first == "John"));
- </pre>
-
- <p>Note that it is not required to explicitly create a named
- query variable before executing it. For example, the following
- two queries are equivalent:</p>
-
- <pre class="cxx">
- query q (query::first == "John");
-
- result r1 (db.query&lt;person> (q));
- result r1 (db.query&lt;person> (query::first == "John"));
- </pre>
-
- <p>Normally, we would create a named query instance if we are
- planning to run the same query multiple times and would use the
- in-line version for those that are executed only once (see also
- <a href="#4.5">Section 4.5, "Prepared Queries"</a> for a more
- optimal way to re-execute the same query multiple times). A named
- query instance that does not have any by-reference parameters is
- immutable and can be shared between multiple threads without
- synchronization. On the other hand, a query instance with
- by-reference parameters is modified every time it is executed.
- If such a query is shared among multiple threads, then access
- to this query instance must be synchronized from the execution
- point and until the completion of the iteration over the result.</p>
-
- <p>It is also possible to create queries from other queries by
- combining them using logical operators. For example:</p>
-
- <pre class="cxx">
-result
-find_minors (database&amp; db, const query&amp; name_query)
-{
- return db.query&lt;person> (name_query &amp;&amp; query::age &lt; 18);
-}
-
-result r (find_minors (db, query::first == "John"));
- </pre>
-
- <p>The result of executing a query is zero, one, or more objects
- matching the query criteria. The <code>query()</code> function
- returns this result as an instance of the <code>odb::result</code>
- class template, which provides a stream-like interface and is
- discussed in detail in the next section.</p>
-
- <p>In situations where we know that a query produces at most one
- element, we can instead use the <code>database::query_one()</code> and
- <code>database::query_value()</code> shortcut functions, for example:</p>
-
- <pre class="cxx">
- typedef odb::query&lt;person> query;
-
- auto_ptr&lt;person> p (
- db.query_one&lt;person> (
- query::email == "jon@example.com"));
- </pre>
-
- <p>The shortcut query functions have the following signatures:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- typename object_traits&lt;T>::pointer_type
- query_one ();
-
- template &lt;typename T>
- bool
- query_one (T&amp;);
-
- template &lt;typename T>
- T
- query_value ();
-
- template &lt;typename T>
- typename object_traits&lt;T>::pointer_type
- query_one (const odb::query&lt;T>&amp;);
-
- template &lt;typename T>
- bool
- query_one (const odb::query&lt;T>&amp;, T&amp;);
-
- template &lt;typename T>
- T
- query_value (const odb::query&lt;T>&amp;);
- </pre>
-
- <p>Similar to <code>query()</code>, the first three functions are used
- to return the only persistent object of a given type stored in the
- database. The second three versions use the passed query instance
- to only return the object matching the query criteria.</p>
-
- <p>Similar to the <code>database::find()</code> functions
- (<a href="#3.9">Section 3.9, "Loading Persistent Objects"</a>),
- <code>query_one()</code> can either allocate a new instance of the
- object class in the dynamic memory or it can load the object's state
- into an existing instance. The <code>query_value()</code> function
- allocates and returns the object by value.</p>
-
- <p>The <code>query_one()</code> function allows us to determine
- if the query result contains zero or one element. If no objects
- matching the query criteria were found in the database, the
- first version of <code>query_one()</code> returns the <code>NULL</code>
- pointer while the second &mdash; <code>false</code>. If the second
- version returns <code>false</code>, then the passed object
- remains unchanged. For example:</p>
-
- <pre class="cxx">
- if (unique_ptr&lt;person> p = db.query_one&lt;person> (
- query::email == "jon@example.com"))
- {
- ...
- }
-
- person p;
- if (db.query_one&lt;person> (query::email == "jon@example.com", p))
- {
- ...
- }
- </pre>
-
- <p>If the query executed using <code>query_one()</code> or
- <code>query_value()</code> returns more than one element,
- then these functions fail with an assertion. Additionally,
- <code>query_value()</code> also fails with an assertion if
- the query returned no elements.</p>
-
- <p>Common situations where we can use the shortcut functions are a
- query condition that uses a data member with the
- <code>unique</code> constraint (at most one element returned;
- see <a href="#14.7">Section 14.7, "Index Definition Pragmas"</a>)
- as well as aggregate queries (exactly one element returned; see
- <a href="#10">Chapter 10, "Views"</a>).</p>
-
- <h2><a name="4.4">4.4 Query Result</a></h2>
-
- <p>The <code>database::query()</code> function returns the result of
- executing a query as an instance of the <code>odb::result</code>
- class template, for example:</p>
-
- <pre class="cxx">
- typedef odb::query&lt;person> query;
- typedef odb::result&lt;person> result;
-
- result johns (db.query&lt;person> (query::first == "John"));
- </pre>
-
- <p>It is best to view an instance of <code>odb::result</code>
- as a handle to a stream, such as a socket stream. While we can
- make a copy of a result or assign one result to another, the
- two instances will refer to the same result stream. Advancing
- the current position in one instance will also advance it in
- another. The result instance is only usable within the transaction
- it was created in. Trying to manipulate the result after the
- transaction has terminated leads to undefined behavior.</p>
-
- <p>The <code>odb::result</code> class template conforms to the
- standard C++ sequence requirements and has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- template &lt;typename T>
- class result
- {
- public:
- typedef odb::result_iterator&lt;T> iterator;
-
- public:
- result ();
-
- result (const result&amp;);
-
- result&amp;
- operator= (const result&amp;);
-
- void
- swap (result&amp;)
-
- public:
- iterator
- begin ();
-
- iterator
- end ();
-
- public:
- void
- cache ();
-
- bool
- empty () const;
-
- std::size_t
- size () const;
- };
-}
- </pre>
-
- <p>The default constructor creates an empty result set. The
- <code>cache()</code> function caches the returned objects'
- state in the application's memory. We have already mentioned
- result caching when we talked about query execution. As you
- may remember the <code>database::query()</code> function
- caches the result unless instructed not to by the caller.
- The <code>cache()</code> function allows us to
- cache the result at a later stage if it wasn't already
- cached during query execution.</p>
-
- <p>If the result is cached, the database state of all the returned
- objects is stored in the application's memory. Note that
- the actual objects are still only instantiated on demand
- during result iteration. It is the raw database state that
- is cached in memory. In contrast, for uncached results
- the object's state is sent by the database system one object
- at a time as the iteration progresses.</p>
-
- <p>Uncached results can improve the performance of both the application
- and the database system in situations where we have a large
- number of objects in the result or if we will only examine
- a small portion of the returned objects. However, uncached
- results have a number of limitations. There can only be one
- uncached result in a transaction. Creating another result
- (cached or uncached) by calling <code>database::query()</code>
- will invalidate the existing uncached result. Furthermore,
- calling any other database functions, such as <code>update()</code>
- or <code>erase()</code> will also invalidate the uncached result.
- It also follows that uncached results cannot be used on objects
- with containers (<a href="#5">Chapter 5, "Containers"</a>) since
- loading a container would invalidate the uncached result.</p>
-
- <p>The <code>empty()</code> function returns <code>true</code> if
- there are no objects in the result and <code>false</code> otherwise.
- The <code>size()</code> function can only be called for cached results.
- It returns the number of objects in the result. If we call this
- function on an uncached result, the <code>odb::result_not_cached</code>
- exception is thrown.</p>
-
- <p>To iterate over the objects in a result we use the
- <code>begin()</code> and <code>end()</code> functions
- together with the <code>odb::result&lt;T>::iterator</code>
- type, for example:</p>
-
- <pre class="cxx">
- result r (db.query&lt;person> (query::first == "John"));
-
- for (result::iterator i (r.begin ()); i != r.end (); ++i)
- {
- ...
- }
- </pre>
-
- <p>In C++11 we can use the <code>auto</code>-typed variabe instead
- of spelling the iterator type explicitly, for example:</p>
-
- <pre class="cxx">
- for (auto i (r.begin ()); i != r.end (); ++i)
- {
- ...
- }
- </pre>
-
- <p>The C++11 range-based <code>for</code>-loop can be used to further
- simplify the iteration:</p>
-
- <pre class="cxx">
- for (person&amp; p: r)
- {
- ...
- }
- </pre>
-
- <p>The result iterator is an input iterator which means that the
- only two position operations that it supports are to move to the
- next object and to determine whether the end of the result stream
- has been reached. In fact, the result iterator can only be in two
- states: the current position and the end position. If we have
- two iterators pointing to the current position and then we
- advance one of them, the other will advance as well. This,
- for example, means that it doesn't make sense to store an
- iterator that points to some object of interest in the result
- stream with the intent of dereferencing it after the iteration
- is over. Instead, we would need to store the object itself. We
- also cannot iterate over the same result multiple times without
- re-executing the query.</p>
-
- <p>The result iterator has the following dereference functions
- that can be used to access the pointed-to object:</p>
-
- <pre class="cxx">
-namespace odb
-{
- template &lt;typename T>
- class result_iterator
- {
- public:
- T*
- operator-> () const;
-
- T&amp;
- operator* () const;
-
- typename object_traits&lt;T>::pointer_type
- load ();
-
- void
- load (T&amp; x);
-
- typename object_traits&lt;T>::id_type
- id ();
- };
-}
- </pre>
-
- <p>When we call the <code>*</code> or <code>-></code> operator,
- the iterator will allocate a new instance of the object class
- in the dynamic memory, load its state from the database
- state, and return a reference or pointer to the new instance. The
- iterator maintains the ownership of the returned object and will
- return the same pointer for subsequent calls to either of these
- operators until it is advanced to the next object or we call
- the first <code>load()</code> function (see below). For example:</p>
-
- <pre class="cxx">
- result r (db.query&lt;person> (query::first == "John"));
-
- for (result::iterator i (r.begin ()); i != r.end ();)
- {
- cout &lt;&lt; i->last () &lt;&lt; endl; // Create an object.
- person&amp; p (*i); // Reference to the same object.
- cout &lt;&lt; p.age () &lt;&lt; endl;
- ++i; // Free the object.
- }
- </pre>
-
- <p>The overloaded <code>result_iterator::load()</code> functions are
- similar to <code>database::load()</code>. The first function
- returns a dynamically allocated instance of the current
- object. As an optimization, if the iterator already owns an object
- as a result of an earlier
- call to the <code>*</code> or <code>-></code> operator, then it
- relinquishes the ownership of this object and returns it instead.
- This allows us to write code like this without worrying about
- a double allocation:</p>
-
- <pre class="cxx">
- result r (db.query&lt;person> (query::first == "John"));
-
- for (result::iterator i (r.begin ()); i != r.end (); ++i)
- {
- if (i->last == "Doe")
- {
- auto_ptr p (i.load ());
- ...
- }
- }
- </pre>
-
- <p>Note, however, that because of this optimization, a subsequent
- to <code>load()</code> call to the <code>*</code> or <code>-></code>
- operator results in the allocation of a new object.</p>
-
- <p>The second <code>load()</code> function allows
- us to load the current object's state into an existing instance.
- For example:</p>
-
- <pre class="cxx">
- result r (db.query&lt;person> (query::first == "John"));
-
- person p;
- for (result::iterator i (r.begin ()); i != r.end (); ++i)
- {
- i.load (p);
- cout &lt;&lt; p.last () &lt;&lt; endl;
- cout &lt;&lt; i.age () &lt;&lt; endl;
- }
- </pre>
-
- <p>The <code>id()</code> function return the object id of the current
- object. While we can achieve the same by loading the object and getting
- its id, this function is more efficient since it doesn't actually
- create the object. This can be useful when all we need is the object's
- identifier. For example:</p>
-
- <pre class="cxx">
- std::set&lt;unsigned long> set = ...; // Persons of interest.
-
- result r (db.query&lt;person> (query::first == "John"));
-
- for (result::iterator i (r.begin ()); i != r.end (); ++i)
- {
- if (set.find (i.id ()) != set.end ()) // No object loaded.
- {
- cout &lt;&lt; i->first () &lt;&lt; endl; // Object loaded.
- }
- }
- </pre>
-
- <h2><a name="4.5">4.5 Prepared Queries</a></h2>
-
- <p>Most modern relational database systems have the notion of a prepared
- statement. Prepared statements allow us to perform the potentially
- expensive tasks of parsing SQL, preparing the query execution
- plan, etc., once and then executing the same query multiple
- times, potentially using different values for parameters in
- each execution.</p>
-
- <p>In ODB all the non-query database operations such as
- <code>persist()</code>, <code>load()</code>, <code>update()</code>,
- etc., are implemented in terms of prepared statements that are cached
- and reused. While the <code>query()</code>, <code>query_one()</code>,
- and <code>query_value()</code> database operations also use prepared
- statements, these statements are not cached or reused by default since
- ODB has no knowledge of whether a query will be executed multiple times
- or only once. Instead, ODB provides a mechanism, called prepared queries,
- that allows us to prepare a query once and execute it multiple
- times. In other words, ODB prepared queries are a thin wrapper
- around the underlying database's prepared statement functionality.</p>
-
- <p>In most cases ODB shields the application developer from database
- connection management and multi-threading issues. However, when it
- comes to prepared queries, a basic understanding of how ODB manages
- these aspects is required. Conceptually, the <code>odb::database</code>
- class represents a specific database, that is, a data store. However,
- underneath, it maintains one or more connections to this database.
- A connection can be used only by a single thread at a time. When
- we start a transaction (by calling <code>database::begin()</code>),
- the transaction instance obtains a connection and holds on to it
- until the transaction is committed or rolled back. During this time
- no other thread can use this connection. When the transaction
- releases the connection, it may be closed or reused by another
- transaction in this or another thread. What exactly happens to
- a connection after it has been released depends on the connection
- factory that is used by the <code>odb::database</code> instance.
- For more information on connection factories, refer to
- <a href="#II">Part II, "Database Systems"</a>.</p>
-
- <p>A query prepared on one connection cannot be executed on another.
- In other words, a prepared query is associated with the connection.
- One important implication of this restriction is that we cannot
- prepare a query in one transaction and then try to execute it
- in another without making sure that both transactions use the
- same connection.</p>
-
- <p>To enable the prepared query functionality we need to specify
- the <code>--generate-prepared</code> ODB compiler option. If
- we are planning to always prepare our queries, then we can
- disable the once-off query execution support by also specifying
- the <code>--omit-unprepared</code> option.</p>
-
- <p>To prepare a query we use the <code>prepare_query()</code> function
- template. This function can be called on both the <code>odb::database</code>
- and <code>odb::connection</code> instances. The <code>odb::database</code>
- version simply obtains the connection used by the currently active
- transaction and calls the corresponding <code>odb::connection</code>
- version. If no transaction is currently active, then this function
- throws the <code>odb::not_in_transaction</code> exception
- (<a href="#3.5">Section 3.5, "Transactions"</a>). The
- <code>prepare_query()</code> function has the following signature:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- prepared_query&lt;T>
- prepare_query (const char* name, const odb::query&lt;T>&amp;);
- </pre>
-
- <p>The first argument to the <code>prepare_query()</code> function is
- the prepared query name. This name is used as a key for prepared
- query caching (discussed later) and must be unique. For some databases,
- notably PostgreSQL, it is also used as a name of the underlying prepared
- statement. The name <code>"<i>object</i>_query"</code> (for example,
- <code>"person_query"</code>) is reserved for the once-off queries
- executed by the <code>database::query()</code> function. Note that
- the <code>prepare_query()</code> function makes only a shallow copy
- of this argument, which means that the name must be valid for the
- lifetime of the returned <code>prepared_query</code> instance.</p>
-
- <p>The second argument to the <code>prepare_query()</code> function
- is the query criteria. It has the same semantics as in the
- <code>query()</code> function discussed in <a href="#4.3">Section
- 4.3, "Executing a Query"</a>. Similar to <code>query()</code>, we
- also have to explicitly specify the object type that we will be
- querying. For example:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;person> query;
-typedef odb::prepared_query&lt;person> prep_query;
-
-prep_query pq (
- db.prepare_query&lt;person> ("person-age-query", query::age > 50));
- </pre>
-
- <p>The result of executing the <code>prepare_query()</code> function is
- the <code>prepared_query</code> instance that represent the prepared
- query. It is best to view <code>prepared_query</code> as a handle to
- the underlying prepared statement. While we can make a copy of it or
- assign one <code>prepared_query</code> to another, the two instances
- will refer to the same prepared statement. Once the last instance of
- <code>prepared_query</code> referencing a specific prepared statement
- is destroyed, this statement is released. The <code>prepared_query</code>
- class template has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- template &lt;typename T>
- struct prepared_query
- {
- prepared_query ();
-
- prepared_query (const prepared_query&amp;)
- prepared_query&amp; operator= (const prepared_query&amp;)
-
- result&lt;T>
- execute (bool cache = true);
-
- typename object_traits&lt;T>::pointer_type
- execute_one ();
-
- bool
- execute_one (T&amp; object);
-
- T
- execute_value ();
-
- const char*
- name () const;
-
- statement&amp;
- statement () const;
-
- operator unspecified_bool_type () const;
- };
-}
- </pre>
-
- <p>The default constructor creates an empty <code>prepared_query</code>
- instance, that is, an instance that does not reference a prepared
- statement and therefore cannot be executed. The only way to create
- a non-empty prepared query is by calling the <code>prepare_query()</code>
- function discussed above. To test whether the prepared query is empty,
- we can use the implicit conversion operator to a boolean type. For
- example:</p>
-
- <pre class="cxx">
- prepared_query&lt;person> pq;
-
- if (pq)
- {
- // Not empty.
- ...
- }
- </pre>
-
- <p>The <code>execute()</code> function executes the query and returns
- the result instance. The <code>cache</code> argument indicates
- whether the result should be cached and has the same semantics
- as in the <code>query()</code> function. In fact, conceptually,
- <code>prepare_query()</code> and <code>execute()</code> are just
- the <code>query()</code> function split into two:
- <code>prepare_query()</code> takes the first
- <code>query()</code> argument (the query condition) while
- <code>execute()</code> takes the second (the cache flag). Note
- also that re-executing a prepared query invalidates the
- previous execution result, whether cached or uncached. </p>
-
- <p>The <code>execute_one()</code> and <code>execute_value()</code>
- functions can be used as shortcuts to execute a query that is
- known to return at most one or exactly one object, respectively.
- The arguments and return values in these functions have the same
- semantics as in <code>query_one()</code> and <code>query_value()</code>.
- And similar to <code>execute()</code> above, <code>prepare_query()</code>
- and <code>execute_one/value()</code> can be seen as the
- <code>query_one/value()</code> function split into two:
- <code>prepare_query()</code> takes the first
- <code>query_one/value()</code> argument (the query condition) while
- <code>execute_one/value()</code> takes the second argument (if any)
- and returns the result. Note also that <code>execute_one/value()</code>
- never caches its result but invalidates the result of any previous
- <code>execute()</code> call on the same prepared query.</p>
-
- <p>The <code>name()</code> function returns the prepared query name.
- This is the same name as was passed as the first argument in the
- <code>prepare_query()</code> call. The <code>statement()</code>
- function returns a reference to the underlying prepared statement.
- Note also that calling any of these functions on an empty
- <code>prepared_query</code> instance results in undefined behavior.</p>
-
- <p>The simplest use-case for a prepared query is the need to
- execute the same query multiple times within a single transaction.
- Consider the following example that queries for people that are older
- than a number of different ages. This and subsequent code fragments
- are taken from the <code>prepared</code> example in the
- <code>odb-examples</code> package.</p>
-
- <pre class="cxx">
-typedef odb::query&lt;person> query;
-typedef odb::prepared_query&lt;person> prep_query;
-typedef odb::result&lt;person> result;
-
-transaction t (db.begin ());
-
-unsigned short age;
-query q (query::age > query::_ref (age));
-prep_query pq (db.prepare_query&lt;person> ("person-age-query", q));
-
-for (age = 90; age > 40; age -= 10)
-{
- result r (pq.execute ());
- ...
-}
-
-t.commit ();
- </pre>
-
- <p>Another scenario is the need to reuse the same query in multiple
- transactions that are executed at once. As was mentioned above,
- in this case we need to make sure that the prepared query and
- all the transactions use the same connection. Consider an
- alternative version of the above example that executes each
- query in a separate transaction:</p>
-
- <pre class="cxx">
-connection_ptr conn (db.connection ());
-
-unsigned short age;
-query q (query::age > query::_ref (age));
-prep_query pq (conn->prepare_query&lt;person> ("person-age-query", q));
-
-for (age = 90; age > 40; age -= 10)
-{
- transaction t (conn->begin ());
-
- result r (pq.execute ());
- ...
-
- t.commit ();
-}
- </pre>
-
-
- <p>Note that with this approach we hold on to the database connection
- until all the transactions involving the prepared query are
- executed. In particular, this means that while we are busy, the
- connection cannot be reused by another thread. Therefore, this
- approach is only recommended if all the transactions are executed
- close to each other. Also note that an uncached (see below)
- prepared query is invalidated once we release the connection
- on which it was prepared.</p>
-
- <p>If we need to reuse a prepared query in transactions that are
- executed at various times, potentially in different threads, then
- the recommended approach is to cache the prepared query on the
- connection. To support this functionality the <code>odb::database</code>
- and <code>odb::connection</code> classes provide the following
- function templates. Similar to <code>prepare_query()</code>,
- the <code>odb::database</code> versions of the below
- functions call the corresponding <code>odb::connection</code>
- versions using the currently active transaction to resolve
- the connection.</p>
-
- <pre class="cxx">
- template &lt;typename T>
- void
- cache_query (const prepared_query&lt;T>&amp;);
-
- template &lt;typename T, typename P>
- void
- cache_query (const prepared_query&lt;T>&amp;,
- std::[auto|unique]_ptr&lt;P> params);
-
- template &lt;typename T>
- prepared_query&lt;T>
- lookup_query (const char* name) const;
-
- template &lt;typename T, typename P>
- prepared_query&lt;T>
- lookup_query (const char* name, P*&amp; params) const;
- </pre>
-
- <p>The <code>cache_query()</code> function caches the passed prepared
- query on the connection. The second overloaded version of
- <code>cache_query()</code> also takes a pointer to the
- by-reference query parameters. In C++98/03 it should be
- <code>std::auto_ptr</code> while in C++11 <code>std::auto_ptr</code>
- or <code>std::unique_ptr</code> can be used. The
- <code>cache_query()</code> function assumes ownership of the
- passed <code>params</code> argument. If a prepared query
- with the same name is already cached on this connection,
- then the <code>odb::prepared_already_cached</code> exception
- is thrown.</p>
-
- <p>The <code>lookup_query()</code> function looks up a previously
- cached prepared query given its name. The second overloaded
- version of <code>lookup_query()</code> also returns a pointer
- to the by-reference query parameters. If a prepared query
- with this name has not been cached, then an empty
- <code>prepared_query</code> instance is returned. If a
- prepared query with this name has been cached but either
- the object type or the parameters type does not match
- that which was cached, then the <code>odb::prepared_type_mismatch</code>
- exception is thrown.</p>
-
- <p>As a first example of the prepared query cache functionality,
- consider the case that does not use any by-reference parameters:</p>
-
- <pre class="cxx">
-for (unsigned short i (0); i &lt; 5; ++i)
-{
- transaction t (db.begin ());
-
- prep_query pq (db.lookup_query&lt;person> ("person-age-query"));
-
- if (!pq)
- {
- pq = db.prepare_query&lt;person> (
- "person-val-age-query", query::age > 50);
- db.cache_query (pq);
- }
-
- result r (pq.execute ());
- ...
-
- t.commit ();
-
- // Do some other work.
- //
- ...
-}
- </pre>
-
- <p>The following example shows how to do the same but for a query that
- includes by-reference parameters. In this case the parameters are
- cached together with the prepared query.</p>
-
- <pre class="cxx">
-for (unsigned short age (90); age > 40; age -= 10)
-{
- transaction t (db.begin ());
-
- unsigned short* age_param;
- prep_query pq (
- db.lookup_query&lt;person> ("person-age-query", age_param));
-
- if (!pq)
- {
- auto_ptr&lt;unsigned short> p (new unsigned short);
- age_param = p.get ();
- query q (query::age > query::_ref (*age_param));
- pq = db.prepare_query&lt;person> ("person-age-query", q);
- db.cache_query (pq, p); // Assumes ownership of p.
- }
-
- *age_param = age; // Initialize the parameter.
- result r (pq.execute ());
- ...
-
- t.commit ();
-
- // Do some other work.
- //
- ...
-}
- </pre>
-
- <p>As is evident from the above examples, when we use a prepared
- query cache, each transaction that executes a query must also
- include code that prepares and caches this query if it hasn't already
- been done. If a prepared query is used in a single place in the
- application, then this is normally not an issue since all the
- relevant code is kept in one place. However, if the same query
- is used in several different places in the application, then
- we may end up duplicating the same preparation and caching
- code, which makes it hard to maintain.</p>
-
- <p>To resolve this issue ODB allows us to register a prepared
- query factory that will be called to prepare and cache a
- query during the call to <code>lookup_query()</code>. To
- register a factory we use the <code>database::query_factory()</code>
- function. In C++98/03 it has the following signature:</p>
-
- <pre class="cxx">
- void
- query_factory (const char* name,
- void (*factory) (const char* name, connection&amp;));
- </pre>
-
- <p>While in C++11 it uses the <code>std::function</code> class
- template:</p>
-
- <pre class="cxx">
- void
- query_factory (const char* name,
- std::function&lt;void (const char* name, connection&amp;)>);
- </pre>
-
- <p>The first argument to the <code>query_factory()</code> function is
- the prepared query name that this factory will be called to prepare
- and cache. An empty name is treated as a fallback wildcard factory
- that is capable of preparing any query. The second argument is the
- factory function or, in C++11, function object or lambda.</p>
-
- <p>The example fragment shows how we can use the prepared query
- factory:</p>
-
- <pre class="cxx">
-struct params
-{
- unsigned short age;
- string first;
-};
-
-static void
-query_factory (const char* name, connection&amp; c)
-{
- auto_ptr&lt;params> p (new params);
- query q (query::age > query::_ref (p->age) &amp;&amp;
- query::first == query::_ref (p->first));
- prep_query pq (c.prepare_query&lt;person> (name, q));
- c.cache_query (pq, p);
-}
-
-db.query_factory ("person-age-name-query", &amp;query_factory);
-
-for (unsigned short age (90); age > 40; age -= 10)
-{
- transaction t (db.begin ());
-
- params* p;
- prep_query pq (db.lookup_query&lt;person> ("person-age-name-query", p));
- assert (pq);
-
- p->age = age;
- p->first = "John";
- result r (pq.execute ());
- ...
-
- t.commit ();
-}
- </pre>
-
- <p>In C++11 we could have instead used a lambda function as well as
- <code>unique_ptr</code> rather than <code>auto_ptr</code>:</p>
-
- <pre>
-db.query_factory (
- "person-age-name-query",
- [] (const char* name, connection&amp; c)
- {
- unique_ptr&lt;params> p (new params);
- query q (query::age > query::_ref (p->age) &amp;&amp;
- query::first == query::_ref (p->first));
- prep_query pq (c.prepare_query&lt;person> (name, q));
- c.cache_query (pq, std::move (p));
- });
- </pre>
-
- <!-- CHAPTER -->
-
- <hr class="page-break"/>
- <h1><a name="5">5 Containers</a></h1>
-
- <p>The ODB runtime library provides built-in persistence support for all the
- commonly used standard C++98/03 containers, namely,
- <code>std::vector</code>, <code>std::list</code>, <code>std::deque</code>,
- <code>std::set</code>, <code>std::multiset</code>, <code>std::map</code>, and
- <code>std::multimap</code> as well as C++11 <code>std::array</code>,
- <code>std::forward_list</code>, <code>std::unordered_set</code>,
- <code>std::unordered_multiset</code>, <code>std::unordered_map</code>,
- and <code>std::unordered_multimap</code>.
- Plus, ODB profile libraries, that are
- available for commonly used frameworks and libraries (such as Boost and
- Qt), provide persistence support for containers found in these frameworks
- and libraries (<a href="#III">Part III, "Profiles"</a>). Both the
- ODB runtime library and profile libraries also provide a number of
- change-tracking container equivalents which can be used to minimize
- the number of database operations necessary to synchronize the container
- state with the database (<a href="#5.4">Section 5.4, "Change-Tracking
- Containers"</a>). It is also easy to persist custom container types
- as discussed later in <a href="#5.5">Section 5.5, "Using Custom
- Containers"</a>.</p>
-
- <p>We don't need to do anything special to declare a member of a
- container type in a persistent class. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-private:
- std::vector&lt;std::string> nicknames_;
- ...
-};
- </pre>
-
- <p>The complete version of the above code fragment and the other code
- samples presented in this chapter can be found in the <code>container</code>
- example in the <code>odb-examples</code> package.</p>
-
- <p>A data member in a persistent class that is of a container type
- behaves like a value type. That is, when an object is made persistent,
- the elements of the container are stored in the database. Similarly,
- when a persistent object is loaded from the database, the contents
- of the container are automatically loaded as well. A data member
- of a container type can also use a smart pointer, as discussed
- in <a href="#7.3">Section 7.3, "Pointers and <code>NULL</code>
- Value Semantics"</a>.</p>
-
- <p>While an ordinary member is mapped to one or more columns in the
- object's table, a member of a container type is mapped to a separate
- table. The exact schema of such a table depends on the kind of
- container. ODB defines the following container kinds: ordered,
- set, multiset, map, and multimap. The container kinds and the
- contents of the tables to which they are mapped are discussed
- in detail in the following sections.</p>
-
- <p>Containers in ODB can contain simple value types (<a href="#7.1">Section
- 7.1, "Simple Value Types"</a>), composite value types
- (<a href="#7.2">Section 7.2, "Composite Value Types"</a>), and pointers
- to objects (<a href="#6">Chapter 6, "Relationships"</a>). Containers of
- containers, either directly or indirectly via a composite value
- type, are not allowed. A key in a map or multimap container can
- be a simple or composite value type but not a pointer to an object.
- An index in the ordered container should be a simple integer value
- type.</p>
-
- <p>The value type in the ordered, set, and map containers as well as
- the key type in the map containers should be default-constructible.
- The default constructor in these types can be made private in which
- case the <code>odb::access</code> class should be made a friend of
- the value or key type. For example:</p>
-
- <pre class="cxx">
-#pragma db value
-class name
-{
-public:
- name (const std::string&amp;, const std::string&amp;);
- ...
-private:
- friend class odb::access;
- name ();
- ...
-};
-
-#pragma db object
-class person
-{
- ...
-private:
- std::vector&lt;name> aliases_;
- ...
-};
- </pre>
-
-
- <h2><a name="5.1">5.1 Ordered Containers</a></h2>
-
- <p>In ODB an ordered container is any container that maintains (explicitly
- or implicitly) an order of its elements in the form of an integer index.
- Standard C++ containers that are ordered include <code>std::vector</code>
- <code>std::list</code>, and <code>std::deque</code> as well as C++11 <code>std::array</code> and
- <code>std::forward_list</code>. While elements in <code>std::set</code>
- are also kept in a specific order, this order is not based on an
- integer index but rather on the relationship between elements. As
- a result, <code>std::set</code> is not considered an ordered
- container for the purpose of persistence.</p>
-
- <p>The database table for an ordered container consists of at least
- three columns. The first column contains the object id of a
- persistent class instance of which the container is a member.
- The second column contains the element index within a container.
- And the last column contains the element value. If the object
- id or element value are composite, then, instead of a single
- column, they can occupy multiple columns. For an ordered
- container table the ODB compiler also defines two indexes:
- one for the object id column(s) and the other for the index
- column. Refer to <a href="#14.7">Section 14.7, "Index Definition
- Pragmas"</a> for more information on how to customize these
- indexes.</p>
-
- <p>Consider the following persistent object as an example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-private:
- #pragma db id auto
- unsigned long id_;
-
- std::vector&lt;std::string> nicknames_;
- ...
-};
- </pre>
-
- <p>The resulting database table (called <code>person_nicknames</code>) will
- contain the object id column of type <code>unsigned&nbsp;long</code>
- (called <code>object_id</code>), the index column of an integer type
- (called <code>index</code>), and the value column of type
- <code>std::string</code> (called <code>value</code>).</p>
-
- <p>A number of ODB pragmas allow us to customize the table name, column
- names, and native database types of an ordered container both, on
- the per-container and per-member basis. For more information on
- these pragmas, refer to <a href="#14">Chapter 14, "ODB Pragma
- Language"</a>. The following example shows some of the possible
- customizations:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-private:
- #pragma db table("nicknames") \
- id_column("person_id") \
- index_type("SMALLINT UNSIGNED") \
- index_column("nickname_number") \
- value_type("VARCHAR(255)") \
- value_column("nickname")
- std::vector&lt;std::string> nicknames_;
- ...
-};
- </pre>
-
- <p>While the C++ container used in a persistent class may be ordered,
- sometimes we may wish to store such a container in the database without
- the order information. In the example above, for instance, the order
- of person's nicknames is probably not important. To instruct the ODB
- compiler to ignore the order in ordered containers we can use the
- <code>db&nbsp;unordered</code> pragma (<a href="#14.3.9">Section 14.3.9,
- "<code>unordered</code>"</a>, <a href="#14.4.19">Section 14.4.19,
- "<code>unordered</code>"</a>). For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-private:
- #pragma db unordered
- std::vector&lt;std::string> nicknames_;
- ...
-};
- </pre>
-
- <p>The table for an ordered container that is marked unordered won't
- have the index column and the order in which elements are retrieved
- from the database may not be the same as the order in which they
- were stored.</p>
-
- <h2><a name="5.2">5.2 Set and Multiset Containers</a></h2>
-
- <p>In ODB set and multiset containers (referred to as just set
- containers) are associative containers that contain elements
- based on some relationship between them. A set container may
- or may not guarantee a particular order of the elements that
- it stores. Standard C++ containers that are considered set
- containers for the purpose of persistence include
- <code>std::set</code> and <code>std::multiset</code> as well
- as C++11 <code>std::unordered_set</code> and
- <code>std::unordered_multiset</code>.</p>
-
- <p>The database table for a set container consists of at least
- two columns. The first column contains the object id of a
- persistent class instance of which the container is a member.
- And the second column contains the element value. If the object
- id or element value are composite, then, instead of a single
- column, they can occupy multiple columns. ODB compiler also
- defines an index on a set container table for the object id
- column(s). Refer to <a href="#14.7">Section 14.7, "Index Definition
- Pragmas"</a> for more information on how to customize this
- index.</p>
-
- <p>Consider the following persistent object as an example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-private:
- #pragma db id auto
- unsigned long id_;
-
- std::set&lt;std::string> emails_;
- ...
-};
- </pre>
-
- <p>The resulting database table (called <code>person_emails</code>) will
- contain the object id column of type <code>unsigned&nbsp;long</code>
- (called <code>object_id</code>) and the value column of type
- <code>std::string</code> (called <code>value</code>).</p>
-
- <p>A number of ODB pragmas allow us to customize the table name,
- column names, and native database types of a set container, both on
- the per-container and per-member basis. For more information on
- these pragmas, refer to <a href="#14">Chapter 14, "ODB Pragma
- Language"</a>. The following example shows some of the possible
- customizations:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-private:
- #pragma db table("emails") \
- id_column("person_id") \
- value_type("VARCHAR(255)") \
- value_column("email")
- std::set&lt;std::string> emails_;
- ...
-};
- </pre>
-
- <h2><a name="5.3">5.3 Map and Multimap Containers</a></h2>
-
- <p>In ODB map and multimap containers (referred to as just map
- containers) are associative containers that contain key-value
- elements based on some relationship between keys. A map container
- may or may not guarantee a particular order of the elements that
- it stores. Standard C++ containers that are considered map
- containers for the purpose of persistence include
- <code>std::map</code> and <code>std::multimap</code> as well
- as C++11 <code>std::unordered_map</code> and
- <code>std::unordered_multimap</code>.</p>
-
- <p>The database table for a map container consists of at least
- three columns. The first column contains the object id of a
- persistent class instance of which the container is a member.
- The second column contains the element key. And the last column
- contains the element value. If the object id, element key, or
- element value are composite, then instead of a single column
- they can occupy multiple columns. ODB compiler also
- defines an index on a map container table for the object id
- column(s). Refer to <a href="#14.7">Section 14.7, "Index Definition
- Pragmas"</a> for more information on how to customize this
- index.</p>
-
- <p>Consider the following persistent object as an example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-private:
- #pragma db id auto
- unsigned long id_;
-
- std::map&lt;unsigned short, float> age_weight_map_;
- ...
-};
- </pre>
-
- <p>The resulting database table (called <code>person_age_weight_map</code>)
- will contain the object id column of type <code>unsigned&nbsp;long</code>
- (called <code>object_id</code>), the key column of type
- <code>unsigned short</code> (called <code>key</code>), and the value
- column of type <code>float</code> (called <code>value</code>).</p>
-
- <p>A number of ODB pragmas allow us to customize the table name,
- column names, and native database types of a map container, both on
- the per-container and per-member basis. For more information on
- these pragmas, refer to <a href="#14">Chapter 14, "ODB Pragma
- Language"</a>. The following example shows some of the possible
- customizations:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-private:
- #pragma db table("weight_map") \
- id_column("person_id") \
- key_type("INT UNSIGNED") \
- key_column("age") \
- value_type("DOUBLE") \
- value_column("weight")
- std::map&lt;unsigned short, float> age_weight_map_;
- ...
-};
- </pre>
-
- <h2><a name="5.4">5.4 Change-Tracking Containers</a></h2>
-
- <p>When a persistent object containing one of the standard containers
- is updated in the database, ODB has no knowledge of which elements
- were inserted, erased, or modified. As a result, ODB has no choice
- but to assume the whole container has changed and update the state
- of every single element. This can result in a significant overhead
- if a container contains a large number of elements and we only
- changed a small subset of them.</p>
-
- <p>To eliminate this overhead, ODB provides a notion of <em>change-tracking
- containers</em>. A change-tracking container, besides containing
- its elements, just like an ordinary container, also includes the
- change state for each element. When it is time to update such a
- container in the database, ODB can use this change information to
- perform a minimum number of database operations necessary to
- synchronize the container state with the database.</p>
-
- <p>The current version of the ODB runtime library provides a change-tracking
- equivalent of <code>std::vector</code> (<a href="#5.4.1">Section 5.4.1,
- "Change-Tracking <code>vector</code>"</a>) with support for other
- standard container equivalents planned for future releases. ODB
- profile libraries also provide change-tracking equivalents for some
- containers found in the corresponding frameworks and libraries
- (<a href="#III">Part III, "Profiles"</a>).</p>
-
- <p>A change-tracking container equivalent can normally be used as a drop-in
- replacement for an ordinary container except for a few minor
- interface differences (discussed in the corresponding sub-sections).
- In particular, we don't need to do anything extra to effect
- change tracking. ODB will automatically start, stop, and reset
- change tracking when necessary. The following example illustrates
- this point using <code>odb::vector</code> as a replacement for
- <code>std::vector</code>.</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- odb::vector&lt;std::string> names;
-};
-
-person p; // No change tracking (not persistent).
-p.names.push_back ("John Doe");
-
-{
- transaction t (db.begin ());
- db.persist (p); // Start change tracking (persistent).
- t.commit ();
-}
-
-p.names.push_back ("Johnny Doo");
-
-{
- transaction t (db.begin ());
- db.update (p); // One INSERT; reset change state.
- t.commit ();
-}
-
-p.names.modify (0) = "Doe, John"; // Instead of operator[].
-p.names.pop_back ();
-
-{
- transaction t (db.begin ());
- db.update (p); // One UPDATE, one DELETE; reset change state.
- t.commit ();
-}
-
-{
- transaction t (db.begin ());
- auto_ptr&lt;person> p1 (db.load&lt;person> (...)); // Start change tracking.
- p1->names.insert (p1->names.begin (), "Joe Do");
- db.update (*p1); // One UPDATE, one INSERT; reset change state.
- t.commit ();
-}
-
-{
- transaction t (db.begin ());
- db.erase (p); // One DELETE; stop change tracking (not persistent).
- t.commit ();
-}
- </pre>
-
- <p>One interesting aspect of change tracking is what happens when a
- transaction that contains an update is later rolled back. In this
- case, while the change-tracking container has reset the change
- state (after update), actual changes were not committed to the
- database. Change-tracking containers handle this case by
- automatically registering a rollback callback and then, if it is
- called, marking the container as "completely changed". In this
- state, the container no longer tracks individual element changes
- and, when updated, falls back to the complete state update, just
- like an ordinary container. The following example illustrates
- this point:</p>
-
- <pre class="cxx">
-person p;
-p.names.push_back ("John Doe");
-
-{
- transaction t (db.begin ());
- db.persist (p); // Start change tracking (persistent).
- t.commit ();
-}
-
-p.names.push_back ("Johnny Doo");
-
-for (;;)
-{
- try
- {
- transaction t (db.begin ());
-
- // First try: one INSERT.
- // Next try: one DELETE, two INSERTs.
- //
- db.update (p); // Reset change state.
-
- t.commit (); // If throws (rollback), mark as completely changed.
- break;
- }
- catch (const odb::recoverable&amp;)
- {
- continue;
- }
-}
- </pre>
-
- <p>For the interaction of change-tracking containers with change-updated
- object sections, refer to <a href="#9.4">Section 9.4, "Sections and
- Change-Tracking Containers"</a>. Note also that change-tracking
- containers cannot be accessed with by-value accessors
- (<a href="#14.4.5">Section 14.4.5,
- "<code>get</code>/<code>set</code>/<code>access</code>"</a>)
- since in certain situations such access may involve a
- modification of the container (for example, clearing the change
- flag after update).</p>
-
- <h3><a name="5.4.1">5.4.1 Change-Tracking <code>vector</code></a></h3>
-
- <p>Class template <code>odb::vector</code>, defined in
- <code>&lt;odb/vector.hxx></code>, is a change-tracking
- equivalent for <code>std::vector</code>. It
- is implemented in terms of <code>std::vector</code> and is
- implicit-convertible to and implicit-constructible from
- <code>const std::vector&amp;</code>. In particular, this
- means that we can use <code>odb::vector</code> instance
- anywhere <code>const std::vector&amp;</code> is
- expected. In addition, <code>odb::vector</code> constant
- iterator (<code>const_iterator</code>) is the same type as
- that of <code>std::vector</code>.</p>
-
- <p><code>odb::vector</code> incurs 2-bit per element overhead
- in order to store the change state. It cannot
- be stored unordered in the database (<a href="#14.4.19">Section
- 14.4.19 "<code>unordered</code>"</a>) but can be used as an inverse
- side of a relationship (<a href="#6.2">6.2 "Bidirectional
- Relationships"</a>). In this case, no change tracking is performed
- since no state for such a container is stored in the database.</p>
-
- <p>The number of database operations required to update the state
- of <code>odb::vector</code> corresponds well to the complexity
- of <code>std::vector</code> functions. In particular, adding or
- removing an element from the back of the vector (for example,
- with <code>push_back()</code> and <code>pop_back()</code>),
- requires only a single database statement execution. In contrast,
- inserting or erasing an element somewhere in the middle of the
- vector will require a database statement for every element that
- follows it.</p>
-
- <p><code>odb::vector</code> replicates most of the <code>std::vector</code>
- interface as defined in both C++98/03 and C++11 standards. However,
- functions and operators that provide direct write access to
- the elements had to be altered or disabled in order to support
- change tracking. Additional functions used to interface with
- <code>std::vector</code> and to control the change tracking state
- were also added. The following listing summarizes the differences
- between the <code>odb::vector</code> and <code>std::vector</code>
- interfaces. Any <code>std::vector</code> function or operator
- not mentioned in this listing has exactly the same signature
- and semantics in <code>odb::vector</code>. Functions and
- operators that were disabled are shown as commented out and
- are followed by functions/operators that replace them.</p>
-
- <pre class="cxx">
-namespace odb
-{
- template &lt;class T, class A = std::allocator&lt;T> >
- class vector
- {
- ...
-
- // Element access.
- //
-
- //reference operator[] (size_type);
- reference modify (size_type);
-
- //reference at (size_type);
- reference modify_at (size_type);
-
- //reference front ();
- reference modify_front ();
-
- //reference back ();
- reference modify_back ();
-
- //T* data () noexcept;
- T* modify_data () noexcept; // C++11 only.
-
- // Iterators.
- //
- typedef typename std::vector&lt;T, A>::const_iterator const_iterator;
-
- class iterator
- {
- ...
-
- // Element Access.
- //
-
- //reference operator* () const;
- const_reference operator* () const;
- reference modify () const;
-
- //pointer operator-> () const;
- const_pointer operator-> () const;
-
- //reference operator[] (difference_type);
- const_reference operator[] (difference_type);
- reference modify (difference_type) const;
-
- // Interfacing with std::vector::iterator.
- //
- typename std::vector&lt;T, A>::iterator base () const;
- };
-
- // Return std::vector iterators. The begin() functions mark
- // all the elements as modified.
- //
- typename std::vector&lt;T, A>::iterator mbegin ();
- typename std::vector&lt;T, A>::iterator mend ();
- typename std::vector&lt;T, A>::reverse_iterator mrbegin ();
- typename std::vector&lt;T, A>::reverse_iterator mrend ();
-
- // Interfacing with std::vector.
- //
- vector (const std::vector&lt;T, A>&amp;);
- vector (std::vector&lt;T, A>&amp;&amp;); // C++11 only.
-
- vector&amp; operator= (const std::vector&lt;T, A>&amp;);
- vector&amp; operator= (std::vector&lt;T, A>&amp;&amp;); // C++11 only.
-
- operator const std::vector&lt;T, A>&amp; () const;
- std::vector&lt;T, A>&amp; base ();
- const std::vector&lt;T, A>&amp; base ();
-
- // Change tracking.
- //
- bool _tracking () const;
- void _start () const;
- void _stop () const;
- void _arm (transaction&amp;) const;
- };
-}
- </pre>
-
- <p>The following example highlights some of the differences between
- the two interfaces. <code>std::vector</code> versions are commented
- out.</p>
-
- <pre class="cxx">
-#include &lt;vector>
-#include &lt;odb/vector.hxx>
-
-void f (const std::vector&lt;int>&amp;);
-
-odb::vector&lt;int> v ({1, 2, 3});
-
-f (v); // Ok, implicit conversion.
-
-if (v[1] == 2) // Ok, const access.
- //v[1]++;
- v.modify (1)++;
-
-//v.back () = 4;
-v.modify_back () = 4;
-
-for (auto i (v.begin ()); i != v.end (); ++i)
-{
- if (*i != 0) // Ok, const access.
- //*i += 10;
- i.modify () += 10;
-}
-
-std::sort (v.mbegin (), v.mend ());
- </pre>
-
- <p>Note also the subtle difference between copy/move construction
- and copy/move assignment of <code>odb::vector</code> instances.
- While copy/move constructor will copy/move both the elements as
- well as their change state, in contrast, assignment is tracked
- as any other change to the vector content.</p>
-
- <h2><a name="5.5">5.5 Using Custom Containers</a></h2>
-
- <p>While the ODB runtime and profile libraries provide support for
- a wide range of containers, it is also easy to persist custom
- container types or make a change-tracking version out of one.</p>
-
- <p>To achieve this you will need to implement the
- <code>container_traits</code> class template specialization for
- your container. First, determine the container kind (ordered, set,
- multiset, map, or multimap) for your container type. Then use a
- specialization for one of the standard C++ containers found in
- the common ODB runtime library (<code>libodb</code>) as a base
- for your own implementation.</p>
-
- <p>Once the container traits specialization is ready for your container,
- you will need to include it into the ODB compilation process using
- the <code>--odb-epilogue</code> option and into the generated header
- files with the <code>--hxx-prologue</code> option. As an example,
- suppose we have a hash table container for which we have the traits
- specialization implemented in the <code>hashtable-traits.hxx</code>
- file. Then, we can create an ODB compiler options file for this
- container and save it to <code>hashtable.options</code>:</p>
-
- <pre>
-# Options file for the hash table container.
-#
---odb-epilogue '#include "hashtable-traits.hxx"'
---hxx-prologue '#include "hashtable-traits.hxx"'
- </pre>
-
- <p>Now, whenever we compile a header file that uses the hashtable
- container, we can specify the following command line option to
- make sure it is recognized by the ODB compiler as a container
- and the traits file is included in the generated code:</p>
-
- <pre>
---options-file hashtable.options
- </pre>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="6">6 Relationships</a></h1>
-
- <p>Relationships between persistent objects are expressed with pointers or
- containers of pointers. The ODB runtime library provides built-in support
- for <code>shared_ptr</code>/<code>weak_ptr</code> (TR1 or C++11),
- <code>std::unique_ptr</code> (C++11),
- <code>std::auto_ptr</code>, and raw pointers. Plus, ODB profile
- libraries, that are available for commonly used frameworks and libraries
- (such as Boost and Qt), provide support for smart pointers found in these
- frameworks and libraries (<a href="#III">Part III, "Profiles"</a>). It is
- also easy to add support for a custom smart pointer as discussed later
- in <a href="#6.5"> Section 6.5, "Using Custom Smart Pointers"</a>. Any
- supported smart pointer can be used in a data member as long as it can be
- explicitly constructed from the canonical object pointer
- (<a href="#3.3">Section 3.3, "Object and View Pointers"</a>). For
- example, we can use <code>weak_ptr</code> if the object pointer
- is <code>shared_ptr</code>.</p>
-
- <p>When an object containing a pointer to another object is loaded,
- the pointed-to object is loaded as well. In some situations this
- eager loading of the relationships is undesirable since it
- can lead to a large number of otherwise unused objects being
- instantiated from the database. To support finer control
- over relationships loading, the ODB runtime and profile
- libraries provide the so-called <em>lazy</em> versions of
- the supported pointers. An object pointed-to by a lazy pointer
- is not loaded automatically when the containing object is loaded.
- Instead, we have to explicitly request the instantiation of the
- pointed-to object. Lazy pointers are discussed in
- detail in <a href="#6.4">Section 6.4, "Lazy Pointers"</a>.</p>
-
- <p>As a simple example, consider the following employee-employer
- relationship. Code examples presented in this chapter
- will use the <code>shared_ptr</code> and <code>weak_ptr</code>
- smart pointers from the TR1 (<code>std::tr1</code>) namespace.</p>
-
- <pre class="cxx">
-#pragma db object
-class employer
-{
- ...
-
- #pragma db id
- std::string name_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- std::string first_name_;
- std::string last_name_;
-
- shared_ptr&lt;employer> employer_;
-};
- </pre>
-
- <p>By default, an object pointer can be <code>NULL</code>. To
- specify that a pointer always points to a valid object we can
- use the <code>not_null</code> pragma (<a href="#14.4.6">Section
- 14.4.6, "<code>null</code>/<code>not_null</code>"</a>) for
- single object pointers and the <code>value_not_null</code> pragma
- (<a href="#14.4.28">Section
- 14.4.28, "<code>value_null</code>/<code>value_not_null</code>"</a>)
- for containers of object pointers. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class employee
-{
- ...
-
- #pragma db not_null
- shared_ptr&lt;employer> current_employer_;
-
- #pragma db value_not_null
- std::vector&lt;shared_ptr&lt;employer> > previous_employers_;
-};
- </pre>
-
- <p>In this case, if we call either <code>persist()</code> or
- <code>update()</code> database function on the
- <code>employee</code> object and the <code>current_employer_</code>
- pointer or one of the pointers stored in the
- <code>previous_employers_</code> container is <code>NULL</code>,
- then the <code>odb::null_pointer</code> exception will be thrown.</p>
-
- <p>We don't need to do anything special to establish or navigate a
- relationship between two persistent objects, as shown in the
- following code fragment:</p>
-
- <pre class="cxx">
-// Create an employer and a few employees.
-//
-unsigned long john_id, jane_id;
-{
- shared_ptr&lt;employer> er (new employer ("Example Inc"));
- shared_ptr&lt;employee> john (new employee ("John", "Doe"));
- shared_ptr&lt;employee> jane (new employee ("Jane", "Doe"));
-
- john->employer_ = er;
- jane->employer_ = er;
-
- transaction t (db.begin ());
-
- db.persist (er);
- john_id = db.persist (john);
- jane_id = db.persist (jane);
-
- t.commit ();
-}
-
-// Load a few employee objects and print their employer.
-//
-{
- session s;
- transaction t (db.begin ());
-
- shared_ptr&lt;employee> john (db.load&lt;employee> (john_id));
- shared_ptr&lt;employee> jane (db.load&lt;employee> (jane_id));
-
- cout &lt;&lt; john->employer_->name_ &lt;&lt; endl;
- cout &lt;&lt; jane->employer_->name_ &lt;&lt; endl;
-
- t.commit ();
-}
- </pre>
-
- <p>The only notable line in the above code is the creation of a
- session before the second transaction starts. As discussed in
- <a href="#11">Chapter 11, "Session"</a>, a session acts as a cache
- of persistent objects.
- By creating a session before loading the <code>employee</code>
- objects we make sure that their <code>employer_</code> pointers
- point to the same <code>employer</code> object. Without a
- session, each <code>employee</code> would have ended up pointing
- to its own, private instance of the Example Inc employer.</p>
-
- <p>As a general guideline, you should use a session when loading
- objects that have pointers to other persistent objects. A
- session makes sure that for a given object id, a single instance
- is shared among all other objects that relate to it.</p>
-
- <p>We can also use data members from pointed-to
- objects in database queries (<a href="#4">Chapter 4, "Querying the
- Database"</a>). For each pointer in a persistent class, the query
- class defines a smart pointer-like member that contains members
- corresponding to the data members in the pointed-to object. We
- can then use the access via a pointer syntax (<code>-></code>)
- to refer to data members in pointed-to objects.
- For example, the query class for the <code>employee</code> object
- contains the <code>employer</code> member (its name is derived from the
- <code>employer_</code> pointer) which in turn contains the
- <code>name</code> member (its name is derived from the
- <code>employer::name_</code> data member of the pointed-to object).
- As a result, we can use the <code>query::employer->name</code>
- expression while querying the database for the <code>employee</code>
- objects. For example, the following transaction finds all the
- employees of Example Inc that have the Doe last name:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee> query;
-typedef odb::result&lt;employee> result;
-
-session s;
-transaction t (db.begin ());
-
-result r (db.query&lt;employee> (
- query::employer->name == "Example Inc" &amp;&amp; query::last == "Doe"));
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
- cout &lt;&lt; i->first_ &lt;&lt; " " &lt;&lt; i->last_ &lt;&lt; endl;
-
-t.commit ();
- </pre>
-
- <p>A query class member corresponding to a non-inverse
- (<a href="#6.2">Section 6.2, "Bidirectional Relationships"</a>) object
- pointer can also be used as a normal member that has the id type
- of the pointed-to object. For example, the following query locates
- all the <code>employee</code> objects that don't have an associated
- <code>employer</code> object:</p>
-
- <pre class="cxx">
-result r (db.query&lt;employee> (query::employer.is_null ()));
- </pre>
-
- <p>An important concept to keep in mind when working with object
- relationships is the independence of persistent objects. In particular,
- when an object containing a pointer to another object is made persistent
- or is updated, the pointed-to object is not automatically persisted
- or updated. Rather, only a reference to the object (in the form of the
- object id) is stored for the pointed-to object in the database.
- The pointed-to object itself is a separate entity and should
- be made persistent or updated independently. By default, the
- same principle also applies to erasing pointed-to objects. That
- is, we have to make sure all the pointing objects are updated
- accordingly. However, in the case of erase, we can specify an
- alternative <code>on-delete</code> semantic as discussed in
- <a href="#14.4.15">Section 14.4.15, "<code>on_delete</code>"</a>.</p>
-
- <p>When persisting or updating an object containing a pointer to another
- object, the pointed-to object must have a valid object id. This,
- however, may not always be easy to achieve in complex relationships that
- involve objects with automatically assigned identifiers. In such
- cases it may be necessary to first persist an object with a pointer
- set to <code>NULL</code> and then, once the pointed-to object is
- made persistent and its identifier assigned, set the pointer
- to the correct value and update the object in the database.</p>
-
- <p>Persistent object relationships can be divided into two groups:
- unidirectional and bidirectional. Each group in turn contains
- several configurations that vary depending on the cardinality
- of the sides of the relationship. All possible unidirectional
- and bidirectional configurations are discussed in the following
- sections.</p>
-
- <h2><a name="6.1">6.1 Unidirectional Relationships</a></h2>
-
- <p>In unidirectional relationships we are only interested in navigating
- from object to object in one direction. Because there is no interest
- in navigating in the opposite direction, the cardinality of the other
- end of the relationship is unimportant. As a result, there are only
- two possible unidirectional relationships: to-one and to-many. Each
- of these relationships is described in the following sections. For
- sample code that shows how to work with these relationships, refer
- to the <code>relationship</code> example in the <code>odb-examples</code>
- package.</p>
-
- <h3><a name="6.1.1">6.1.1 To-One Relationships</a></h3>
-
- <p>An example of a unidirectional to-one relationship is the
- employee-employer relationship (an employee has one employer).
- The following persistent C++ classes model this relationship:</p>
-
- <pre class="cxx">
-#pragma db object
-class employer
-{
- ...
-
- #pragma db id
- std::string name_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db not_null
- shared_ptr&lt;employer> employer_;
-};
- </pre>
-
- <p>The corresponding database tables look like this:</p>
-
- <pre class="sql">
-CREATE TABLE employer (
- name VARCHAR (128) NOT NULL PRIMARY KEY);
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- employer VARCHAR (128) NOT NULL REFERENCES employer (name));
- </pre>
-
- <h3><a name="6.1.2">6.1.2 To-Many Relationships</a></h3>
-
- <p>An example of a unidirectional to-many relationship is the
- employee-project relationship (an employee can be involved
- in multiple projects). The following persistent C++ classes
- model this relationship:</p>
-
- <pre class="cxx">
-#pragma db object
-class project
-{
- ...
-
- #pragma db id
- std::string name_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db value_not_null unordered
- std::vector&lt;shared_ptr&lt;project> > projects_;
-};
- </pre>
-
- <p>The corresponding database tables look like this:</p>
-
- <pre class="sql">
-CREATE TABLE project (
- name VARCHAR (128) NOT NULL PRIMARY KEY);
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
-
-CREATE TABLE employee_projects (
- object_id BIGINT UNSIGNED NOT NULL,
- value VARCHAR (128) NOT NULL REFERENCES project (name));
- </pre>
-
- <p>To obtain a more canonical database schema, the names of tables
- and columns above can be customized using ODB pragmas
- (<a href="#14">Chapter 14, "ODB Pragma Language"</a>). For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class employee
-{
- ...
-
- #pragma db value_not_null unordered \
- id_column("employee_id") value_column("project_name")
- std::vector&lt;shared_ptr&lt;project> > projects_;
-};
- </pre>
-
- <p>The resulting <code>employee_projects</code> table would then
- look like this:</p>
-
- <pre class="sql">
-CREATE TABLE employee_projects (
- employee_id BIGINT UNSIGNED NOT NULL,
- project_name VARCHAR (128) NOT NULL REFERENCES project (name));
- </pre>
-
-
- <h2><a name="6.2">6.2 Bidirectional Relationships</a></h2>
-
- <p>In bidirectional relationships we are interested in navigating
- from object to object in both directions. As a result, each
- object class in a relationship contains a pointer to the other
- object. If smart pointers are used, then a weak pointer should
- be used as one of the pointers to avoid ownership cycles. For
- example:</p>
-
- <pre class="cxx">
-class employee;
-
-#pragma db object
-class position
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- weak_ptr&lt;employee> employee_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db not_null
- shared_ptr&lt;position> position_;
-};
- </pre>
-
- <p>Note that when we establish a bidirectional relationship, we
- have to set both pointers consistently. One way to make sure
- that a relationship is always in a consistent state is to
- provide a single function that updates both pointers at the
- same time. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class position: public enable_shared_from_this&lt;position>
-{
- ...
-
- void
- fill (shared_ptr&lt;employee> e)
- {
- employee_ = e;
- e->positions_ = shared_from_this ();
- }
-
-private:
- weak_ptr&lt;employee> employee_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
-private:
- friend class position;
-
- #pragma db not_null
- shared_ptr&lt;position> position_;
-};
- </pre>
-
- <p>At the beginning of this chapter we examined how to use a session
- to make sure a single object is shared among all other objects pointing
- to it. With bidirectional relationships involving weak pointers the
- use of a session becomes even more crucial. Consider the following
- transaction that tries to load the <code>position</code> object
- from the above example without using a session:</p>
-
- <pre class="cxx">
-transaction t (db.begin ())
-shared_ptr&lt;position> p (db.load&lt;position> (1));
-...
-t.commit ();
- </pre>
-
- <p>When we load the <code>position</code> object, the <code>employee</code>
- object, which it points to, is also loaded. While <code>employee</code>
- is initially stored as <code>shared_ptr</code>, it is then assigned to
- the <code>employee_</code> member which is <code>weak_ptr</code>. Once
- the assignment is complete, the shared pointer goes out of scope
- and the only pointer that points to the newly loaded
- <code>employee</code> object is the <code>employee_</code> weak
- pointer. And that means the <code>employee</code> object is deleted
- immediately after being loaded. To help avoid such pathological
- situations ODB detects cases where a newly loaded object will
- immediately be deleted and throws the <code>odb::session_required</code>
- exception.</p>
-
- <p>As the exception name suggests, the easiest way to resolve this
- problem is to use a session:</p>
-
- <pre class="cxx">
-session s;
-transaction t (db.begin ())
-shared_ptr&lt;position> p (db.load&lt;position> (1));
-...
-t.commit ();
- </pre>
-
- <p>In our example, the session will maintain a shared pointer to the
- loaded <code>employee</code> object preventing its immediate
- deletion. Another way to resolve this problem is to avoid
- immediate loading of the pointed-to objects using lazy weak
- pointers. Lazy pointers are discussed in <a href="#6.4">Section 6.4,
- "Lazy Pointers"</a> later in this chapter.</p>
-
- <p>Above, to model a bidirectional relationship in persistent classes,
- we used two pointers, one in each object. While this is a natural
- representation in C++, it does not translate to a canonical
- relational model. Consider the database schema generated for
- the above two classes:</p>
-
- <pre class="sql">
-CREATE TABLE position (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- employee BIGINT UNSIGNED REFERENCES employee (id));
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- position BIGINT UNSIGNED NOT NULL REFERENCES position (id));
- </pre>
-
- <p>While this database schema is valid, it is unconventional. We have
- a reference from a row in the <code>position</code> table to a row
- in the <code>employee</code> table. We also have a reference
- from this same row in the <code>employee</code> table back to
- the row in the <code>position</code> table. From the relational
- point of view, one of these references is redundant since
- in SQL we can easily navigate in both directions using just one
- of these references.</p>
-
- <p>To eliminate redundant database schema references we can use the
- <code>inverse</code> pragma (<a href="#14.4.14">Section 14.4.14,
- "<code>inverse</code>"</a>) which tells the ODB compiler that
- a pointer is the inverse side of a bidirectional relationship.
- Either side of a relationship can be made inverse. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class position
-{
- ...
-
- #pragma db inverse(position_)
- weak_ptr&lt;employee> employee_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db not_null
- shared_ptr&lt;position> position_;
-};
- </pre>
-
- <p>The resulting database schema looks like this:</p>
-
- <pre class="sql">
-CREATE TABLE position (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- position BIGINT UNSIGNED NOT NULL REFERENCES position (id));
- </pre>
-
- <p>As you can see, an inverse member does not have a corresponding
- column (or table, in case of an inverse container of pointers)
- and, from the point of view of database operations, is effectively
- read-only. The only way to change a bidirectional relationship
- with an inverse side is to set its direct (non-inverse)
- pointer. Also note that an ordered container (<a href="#5.1">Section
- 5.1, "Ordered Containers"</a>) of pointers that is an inverse side
- of a bidirectional relationship is always treated as unordered
- (<a href="#14.4.19">Section 14.4.19, "<code>unordered</code>"</a>)
- because the contents of such a container are implicitly built from
- the direct side of the relationship which does not contain the
- element order (index).</p>
-
- <p>There are three distinct bidirectional relationships that we
- will cover in the following sections: one-to-one, one-to-many,
- and many-to-many. We will only talk about bidirectional
- relationships with inverse sides since they result in canonical
- database schemas. For sample code that shows how to work with
- these relationships, refer to the <code>inverse</code> example
- in the <code>odb-examples</code> package.</p>
-
- <h3><a name="6.2.1">6.2.1 One-to-One Relationships</a></h3>
-
- <p>An example of a bidirectional one-to-one relationship is the
- presented above employee-position relationship (an employee
- fills one position and a position is filled by one employee).
- The following persistent C++ classes model this relationship:</p>
-
- <pre class="cxx">
-class employee;
-
-#pragma db object
-class position
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db inverse(position_)
- weak_ptr&lt;employee> employee_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db not_null
- shared_ptr&lt;position> position_;
-};
- </pre>
-
- <p>The corresponding database tables look like this:</p>
-
- <pre class="sql">
-CREATE TABLE position (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- position BIGINT UNSIGNED NOT NULL REFERENCES position (id));
- </pre>
-
- <p>If instead the other side of this relationship is made inverse,
- then the database tables will change as follows:</p>
-
- <pre class="sql">
-CREATE TABLE position (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- employee BIGINT UNSIGNED REFERENCES employee (id));
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
- </pre>
-
- <h3><a name="6.2.2">6.2.2 One-to-Many Relationships</a></h3>
-
- <p>An example of a bidirectional one-to-many relationship is the
- employer-employee relationship (an employer has multiple
- employees and an employee is employed by one employer).
- The following persistent C++ classes model this relationship:</p>
-
- <pre class="cxx">
-class employee;
-
-#pragma db object
-class employer
-{
- ...
-
- #pragma db id
- std::string name_;
-
- #pragma db value_not_null inverse(employer_)
- std::vector&lt;weak_ptr&lt;employee> > employees_
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db not_null
- shared_ptr&lt;employer> employer_;
-};
- </pre>
-
- <p>The corresponding database tables differ significantly depending
- on which side of the relationship is made inverse. If the <em>one</em>
- side (<code>employer</code>) is inverse as in the code
- above, then the resulting database schema looks like this:</p>
-
- <pre class="sql">
-CREATE TABLE employer (
- name VARCHAR (128) NOT NULL PRIMARY KEY);
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- employer VARCHAR (128) NOT NULL REFERENCES employer (name));
- </pre>
-
- <p>If instead the <em>many</em> side (<code>employee</code>) of this
- relationship is made inverse, then the database tables will change
- as follows:</p>
-
- <pre class="sql">
-CREATE TABLE employer (
- name VARCHAR (128) NOT NULL PRIMARY KEY);
-
-CREATE TABLE employer_employees (
- object_id VARCHAR (128) NOT NULL REFERENCES employer (name),
- value BIGINT UNSIGNED NOT NULL REFERENCES employee (id));
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
- </pre>
-
- <h3><a name="6.2.3">6.2.3 Many-to-Many Relationships</a></h3>
-
- <p>An example of a bidirectional many-to-many relationship is the
- employee-project relationship (an employee can work on multiple
- projects and a project can have multiple participating employees).
- The following persistent C++ classes model this relationship:</p>
-
- <pre class="cxx">
-class employee;
-
-#pragma db object
-class project
-{
- ...
-
- #pragma db id
- std::string name_;
-
- #pragma db value_not_null inverse(projects_)
- std::vector&lt;weak_ptr&lt;employee> > employees_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db value_not_null unordered
- std::vector&lt;shared_ptr&lt;project> > projects_;
-};
- </pre>
-
- <p>The corresponding database tables look like this:</p>
-
- <pre class="sql">
-CREATE TABLE project (
- name VARCHAR (128) NOT NULL PRIMARY KEY);
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
-
-CREATE TABLE employee_projects (
- object_id BIGINT UNSIGNED NOT NULL REFERENCES employee (id),
- value VARCHAR (128) NOT NULL REFERENCES project (name));
- </pre>
-
- <p>If instead the other side of this relationship is made inverse,
- then the database tables will change as follows:</p>
-
- <pre class="sql">
-CREATE TABLE project (
- name VARCHAR (128) NOT NULL PRIMARY KEY);
-
-CREATE TABLE project_employees (
- object_id VARCHAR (128) NOT NULL REFERENCES project (name),
- value BIGINT UNSIGNED NOT NULL REFERENCES employee (id));
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
- </pre>
-
- <h2><a name="6.3">6.3 Circular Relationships</a></h2>
-
- <p>A relationship between two persistent classes is circular if each
- of them references the other. Bidirectional relationships are
- always circular. A unidirectional relationship combined with
- inheritance (<a href="#8">Chapter 8, "Inheritance"</a>) can also
- be circular. For example, the <code>employee</code> class could
- derive from <code>person</code> which, in turn, could contain a
- pointer to <code>employee</code>.</p>
-
- <p>We don't need to do anything extra if persistent classes with
- circular dependencies are defined in the same header
- file. Specifically, ODB will make sure that the database tables
- and foreign key constraints are created in the correct order. As a
- result, unless you have good reasons not to, it is recommended that
- you keep persistent classes with circular dependencies in the same
- header file.</p>
-
- <p>If you have to keep such classes in separate header files, then
- there are two extra steps that you may need to take in order to
- use these classes with ODB. Consider again the example from
- <a href="#6.2.1">Section 6.2.1, "One-to-One Relationships"</a>
- but this time with the classes defined in separate headers:</p>
-
- <pre class="cxx">
-// position.hxx
-//
-class employee;
-
-#pragma db object
-class position
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db inverse(position_)
- weak_ptr&lt;employee> employee_;
-};
- </pre>
-
- <pre class="cxx">
-// employee.hxx
-//
-#include "position.hxx"
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db not_null
- shared_ptr&lt;position> position_;
-};
- </pre>
-
- <p>Note that the <code>position.hxx</code> header contains only the forward
- declaration for <code>employee</code>. While this is sufficient to
- define a valid, from the C++ point of view, <code>position</code> class,
- the ODB compiler needs to "see" the definitions of the pointed-to
- persistent classes. There are several ways we can fulfil this
- requirement. The easiest is to simply include <code>employee.hxx</code>
- at the end of <code>position.hxx</code>:</p>
-
- <pre class="cxx">
-// position.hxx
-//
-class employee;
-
-#pragma db object
-class position
-{
- ...
-};
-
-#include "employee.hxx"
- </pre>
-
- <p>We can also limit this inclusion only to the time when
- <code>position.hxx</code> is compiled with the ODB compiler:</p>
-
- <pre class="cxx">
-// position.hxx
-//
-
-...
-
-#ifdef ODB_COMPILER
-# include "employee.hxx"
-#endif
- </pre>
-
- <p>Finally, if we don't want to modify <code>position.hxx</code>,
- then we can add <code>employee.hxx</code> to the ODB compilation
- process with the <code>--odb-epilogue</code> option. For example:</p>
-
- <pre class="terminal">
-odb ... --odb-epilogue "#include \"employee.hxx\"" position.hxx
- </pre>
-
- <p>Note also that in this example we didn't have to do anything extra
- for <code>employee.hxx</code> because it already includes
- <code>position.hxx</code>. However, if instead it relied only
- on the forward declaration of the <code>position</code> class,
- then we would have to handle it in the same way as
- <code>position.hxx</code>.</p>
-
- <p>The other difficulty with separately defined classes involving
- circular relationships has to do with the correct order of foreign
- key constraint creation in the generated database schema. In
- the above example, if we generate the database schema as
- standalone SQL files, then we will end up with two such files:
- <code>position.sql</code> and <code>employee.sql</code>.
- If we try to execute <code>employee.sql</code> first, then
- we will get an error indicating that the table corresponding to
- the <code>position</code> class and referenced by the foreign
- key constraint corresponding to the <code>position_</code>
- pointer does not yet exist.</p>
-
- <p>Note that there is no such problem if the database schema
- is embedded in the generated C++ code instead of being produced
- as standalone SQL files. In this case, the ODB compiler is
- able to ensure the correct creation order even if the classes
- are defined in separate header files.</p>
-
- <p>In certain cases, for example, a bidirectional relationship
- with an inverse side, this problem can be resolved by executing
- the database schema creation files in the correct order. In our
- example, this would be <code>position.sql</code> first
- and <code>employee.sql</code> second. However, this approach
- doesn't scale beyond simple object models.</p>
-
- <p>A more robust solution to this problem is to generate the database
- schema for all the persistent classes into a single SQL file. This
- way, the ODB compiler can again ensure the correct creation order
- of tables and foreign keys. To instruct the ODB compiler to produce
- a combined schema file for several headers we can use the
- <code>--generate-schema-only</code> and <code>--at-once</code>
- options. For example:</p>
-
- <pre class="terminal">
-odb ... --generate-schema-only --at-once --input-name company \
-position.hxx employee.hxx
- </pre>
-
- <p>The result of the above command is a single <code>company.sql</code>
- file (the name is derived from the <code>--input-name</code> value)
- that contains the database creation code for both <code>position</code>
- and <code>employee</code> classes.</p>
-
- <h2><a name="6.4">6.4 Lazy Pointers</a></h2>
-
- <p>Consider again the bidirectional, one-to-many employer-employee
- relationship that was presented earlier in this chapter:</p>
-
- <pre class="cxx">
-class employee;
-
-#pragma db object
-class employer
-{
- ...
-
- #pragma db id
- std::string name_;
-
- #pragma db value_not_null inverse(employer_)
- std::vector&lt;weak_ptr&lt;employee> > employees_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- #pragma db not_null
- shared_ptr&lt;employer> employer_;
-};
- </pre>
-
- <p>Consider also the following transaction which obtains the employer
- name given the employee id:</p>
-
- <pre class="cxx">
-unsigned long id = ...
-string name;
-
-session s;
-transaction t (db.begin ());
-
-shared_ptr&lt;employee> e (db.load&lt;employee> (id));
-name = e->employer_->name_;
-
-t.commit ();
- </pre>
-
- <p>While this transaction looks very simple, it actually does a lot more
- than what meets the eye and is necessary. Consider what happens when
- we load the <code>employee</code> object: the <code>employer_</code>
- pointer is also automatically loaded which means the <code>employer</code>
- object corresponding to this employee is also loaded. But the
- <code>employer</code> object in turn contains the list of pointers
- to all the employees, which are also loaded. As a result, when object
- relationships are involved, a simple transaction like the above can
- load many more objects than is necessary.</p>
-
- <p>To overcome this problem ODB offers finer grained control over
- the relationship loading in the form of lazy pointers. A lazy
- pointer does not automatically load the pointed-to object
- when the containing object is loaded. Instead, we have to
- explicitly load the pointed-to object if and when we need to
- access it.</p>
-
- <p>The ODB runtime library provides lazy counterparts for all the
- supported pointers, namely:
- <code>odb::lazy_shared_ptr</code>/<code>lazy_weak_ptr</code>
- for C++11 <code>std::shared_ptr</code>/<code>weak_ptr</code>,
- <code>odb::tr1::lazy_shared_ptr</code>/<code>lazy_weak_ptr</code>
- for TR1 <code>std::tr1::shared_ptr</code>/<code>weak_ptr</code>,
- <code>odb::lazy_unique_ptr</code> for C++11 <code>std::unique_ptr</code>,
- <code>odb::lazy_auto_ptr</code> for <code>std::auto_ptr</code>,
- and <code>odb::lazy_ptr</code> for raw pointers. The TR1 lazy
- pointers are defined in the <code>&lt;odb/tr1/lazy-ptr.hxx></code>
- header while all the others &mdash; in
- <code>&lt;odb/lazy-ptr.hxx></code>. The ODB profile
- libraries also provide lazy pointer implementations for smart pointers
- from popular frameworks and libraries (<a href="#III">Part III,
- "Profiles"</a>).</p>
-
- <p>While we will discuss the interface of lazy pointers in more detail
- shortly, the most commonly used extra function provided by these
- pointers is <code>load()</code>. This function loads the
- pointed-to object if it hasn't already been loaded. After
- the call to this function, the lazy pointer can be used
- in the the same way as its eager counterpart. The <code>load()</code>
- function also returns the eager pointer, in case you need to pass
- it around. For a lazy weak pointer, the
- <code>load()</code> function also locks the pointer.</p>
-
- <p>The following example shows how we can change our employer-employee
- relationship to use lazy pointers. Here we choose to use lazy pointers
- for both sides of the relationship.</p>
-
- <pre class="cxx">
-class employee;
-
-#pragma db object
-class employer
-{
- ...
-
- #pragma db value_not_null inverse(employer_)
- std::vector&lt;lazy_weak_ptr&lt;employee> > employees_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db not_null
- lazy_shared_ptr&lt;employer> employer_;
-};
- </pre>
-
- <p>And the transaction is changed like this:</p>
-
- <pre class="cxx">
-unsigned long id = ...
-string name;
-
-session s;
-transaction t (db.begin ());
-
-shared_ptr&lt;employee> e (db.load&lt;employee> (id));
-e->employer_.load ();
-name = e->employer_->name_;
-
-t.commit ();
- </pre>
-
-
- <p>As a general guideline we recommend that you make at least one side
- of a bidirectional relationship lazy, especially for relationships
- with a <em>many</em> side.</p>
-
- <p>A lazy pointer implementation mimics the interface of its eager
- counterpart which can be used once the pointer is loaded. It also
- adds a number of additional functions that are specific to the
- lazy loading functionality. Overall, the interface of a lazy
- pointer follows this general outline:</p>
-
- <pre class="cxx">
-template &lt;class T>
-class lazy_ptr
-{
-public:
- //
- // The eager pointer interface.
- //
-
- // Initialization/assignment from an eager pointer to a
- // transient object.
- //
-public:
- template &lt;class Y> lazy_ptr (const eager_ptr&lt;Y>&amp;);
- template &lt;class Y> lazy_ptr&amp; operator= (const eager_ptr&lt;Y>&amp;);
-
- // Lazy loading interface.
- //
-public:
- // NULL loaded()
- //
- // true true NULL pointer to transient object
- // false true valid pointer to persistent object
- // true false unloaded pointer to persistent object
- // false false valid pointer to transient object
- //
- bool loaded () const;
-
- eager_ptr&lt;T> load () const;
-
- // Unload the pointer. For transient objects this function is
- // equivalent to reset().
- //
- void unload () const;
-
- // Get the underlying eager pointer. If this is an unloaded pointer
- // to a persistent object, then the returned pointer will be NULL.
- //
- eager_ptr&lt;T> get_eager () const;
-
- // Initialization with a persistent loaded object.
- //
- template &lt;class Y> lazy_ptr (database&amp;, Y*);
- template &lt;class Y> lazy_ptr (database&amp;, const eager_ptr&lt;Y>&amp;);
-
- template &lt;class Y> void reset (database&amp;, Y*);
- template &lt;class Y> void reset (database&amp;, const eager_ptr&lt;Y>&amp;);
-
- // Initialization with a persistent unloaded object.
- //
- template &lt;class ID> lazy_ptr (database&amp;, const ID&amp;);
-
- template &lt;class ID> void reset (database&amp;, const ID&amp;);
-
- // Query object id and database of a persistent object.
- //
- template &lt;class O /* = T */>
- // C++11: template &lt;class O = T>
- object_traits&lt;O>::id_type object_id () const;
-
- odb::database&amp; database () const;
-};
- </pre>
-
- <p>Note that to initialize a lazy pointer to a persistent object from
- its eager pointer one must use the constructor or <code>reset()</code>
- function with the database as its first argument. The database is
- required to support comparison of unloaded lazy pointers to persistent
- objects.</p>
-
- <p>In a lazy weak pointer interface, the <code>load()</code> function
- returns the <em>strong</em> (shared) eager pointer. The following
- transaction demonstrates the use of a lazy weak pointer based on
- the <code>employer</code> and <code>employee</code> classes
- presented earlier.</p>
-
- <pre class="cxx">
-typedef std::vector&lt;lazy_weak_ptr&lt;employee> > employees;
-
-session s;
-transaction t (db.begin ());
-
-shared_ptr&lt;employer> er (db.load&lt;employer> ("Example Inc"));
-employees&amp; es (er->employees ());
-
-for (employees::iterator i (es.begin ()); i != es.end (); ++i)
-{
- // We are only interested in employees with object id less than
- // 100.
- //
- lazy_weak_ptr&lt;employee>&amp; lwp (*i);
-
- if (lwp.object_id&lt;employee> () &lt; 100)
- // C++11: if (lwp.object_id () &lt; 100)
- {
- shared_ptr&lt;employee> e (lwp.load ()); // Load and lock.
- cout &lt;&lt; e->first_ &lt;&lt; " " &lt;&lt; e->last_ &lt;&lt; endl;
- }
-}
-
-t.commit ();
- </pre>
-
- <p>Notice that inside the for-loop we use a reference to the lazy
- weak pointer instead of making a copy. This is not merely to
- avoid a copy. When a lazy pointer is loaded, all other lazy
- pointers that point to the same object do not automatically
- become loaded (though an attempt to load such copies will
- result in them pointing to the same object, provided the
- same session is still in effect). By using a reference
- in the above transaction we make sure that we load the
- pointer that is contained in the <code>employer</code>
- object. This way, if we later need to re-examine this
- <code>employee</code> object, the pointer will already
- be loaded.</p>
-
- <p>As another example, suppose we want to add an employee
- to Example Inc. The straightforward implementation of this
- transaction is presented below:</p>
-
- <pre class="cxx">
-session s;
-transaction t (db.begin ());
-
-shared_ptr&lt;employer> er (db.load&lt;employer> ("Example Inc"));
-shared_ptr&lt;employee> e (new employee ("John", "Doe"));
-
-e->employer_ = er;
-er->employees ().push_back (e);
-
-db.persist (e);
-t.commit ();
- </pre>
-
- <p>Notice here that we didn't have to update the employer object
- in the database since the <code>employees_</code> list of
- pointers is an inverse side of a bidirectional relationship
- and is effectively read-only, from the persistence point of
- view.</p>
-
- <p>A faster implementation of this transaction, that avoids loading
- the employer object, relies on the ability to initialize an
- <em>unloaded</em> lazy pointer with the database where the object
- is stored as well as its identifier:</p>
-
- <pre class="cxx">
-lazy_shared_ptr&lt;employer> er (db, std::string ("Example Inc"));
-shared_ptr&lt;employee> e (new employee ("John", "Doe"));
-
-e->employer_ = er;
-
-session s;
-transaction t (db.begin ());
-
-db.persist (e);
-
-t.commit ();
- </pre>
-
- <p>For the interaction of lazy pointers with lazy-loaded object
- sections, refer to <a href="#9.3">Section 9.3, "Sections and
- Lazy Pointers"</a>.</p>
-
- <h2><a name="6.5">6.5 Using Custom Smart Pointers</a></h2>
-
- <p>While the ODB runtime and profile libraries provide support for
- the majority of widely-used pointers, it is also easy to add
- support for a custom smart pointer.</p>
-
- <p>To achieve this you will need to implement the
- <code>pointer_traits</code> class template specialization for
- your pointer. The first step is to determine the pointer kind
- since the interface of the <code>pointer_traits</code> specialization
- varies depending on the pointer kind. The supported pointer kinds
- are: <em>raw</em> (raw pointer or equivalent, that is, unmanaged),
- <em>unique</em> (smart pointer that doesn't support sharing),
- <em>shared</em> (smart pointer that supports sharing), and
- <em>weak</em> (weak counterpart of the shared pointer). Any of
- these pointers can be lazy, which also affects the
- interface of the <code>pointer_traits</code> specialization.</p>
-
- <p>Once you have determined the pointer kind for your smart pointer,
- use a specialization for one of the standard pointers found in
- the common ODB runtime library (<code>libodb</code>) as a base
- for your own implementation.</p>
-
- <p>Once the pointer traits specialization is ready, you will need to
- include it into the ODB compilation process using the
- <code>--odb-epilogue</code> option and into the generated header
- files with the <code>--hxx-prologue</code> option. As an example,
- suppose we have the <code>smart_ptr</code> smart pointer for which
- we have the traits specialization implemented in the
- <code>smart-ptr-traits.hxx</code> file. Then, we can create an ODB
- compiler options file for this pointer and save it to
- <code>smart-ptr.options</code>:</p>
-
- <pre>
-# Options file for smart_ptr.
-#
---odb-epilogue '#include "smart-ptr-traits.hxx"'
---hxx-prologue '#include "smart-ptr-traits.hxx"'
- </pre>
-
- <p>Now, whenever we compile a header file that uses <code>smart_ptr</code>,
- we can specify the following command line option to make sure it is
- recognized by the ODB compiler as a smart pointer and the traits file
- is included in the generated code:</p>
-
- <pre>
---options-file smart-ptr.options
- </pre>
-
- <p>It is also possible to implement a lazy counterpart for your
- smart pointer. The ODB runtime library provides a class template
- that encapsulates the object id management and loading
- functionality that is needed to implement a lazy pointer. All
- you need to do is wrap it with an interface that mimics
- your smart pointer. Using one of the existing lazy pointer
- implementations (either from the ODB runtime library or one
- of the profile libraries) as a base for your implementation
- is the easiest way to get started.</p>
-
-
- <!-- CHAPTER -->
-
- <hr class="page-break"/>
- <h1><a name="7">7 Value Types</a></h1>
-
- <p>In <a href="#3.1">Section 3.1, "Concepts and Terminology"</a> we have
- already discussed the notion of values and value types as well as the
- distinction between simple and composite values. This chapter covers
- simple and composite value types in more detail.</p>
-
- <h2><a name="7.1">7.1 Simple Value Types</a></h2>
-
- <p>A simple value type is a fundamental C++ type or a class type that
- is mapped to a single database column. For each supported database
- system the ODB compiler provides a default mapping to suitable
- database types for most fundamental C++ types, such as <code>int</code>
- or <code>float</code> as well as some class types, such as
- <code>std::string</code>. For more information about the default
- mapping for each database system refer to <a href="#II">Part II,
- Database Systems</a>. We can also provide a custom mapping for
- these or our own value types using the <code>db&nbsp;type</code>
- pragma (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>).</p>
-
- <h2><a name="7.2">7.2 Composite Value Types</a></h2>
-
- <p>A composite value type is a <code>class</code> or <code>struct</code>
- type that is mapped to more than one database column. To declare
- a composite value type we use the <code>db&nbsp;value</code> pragma,
- for example:</p>
-
- <pre class="cxx">
-#pragma db value
-class basic_name
-{
- ...
-
- std::string first_;
- std::string last_;
-};
- </pre>
-
- <p>The complete version of the above code fragment and the other code
- samples presented in this section can be found in the <code>composite</code>
- example in the <code>odb-examples</code> package.</p>
-
- <p>A composite value type does not have to define a default constructor,
- unless it is used as an element of a container. In this case the
- default constructor can be made private provided we also make the
- <code>odb::access</code> class, defined in the
- <code>&lt;odb/core.hxx></code> header, a friend of this value type.
- For example:</p>
-
- <pre class="cxx">
-#include &lt;odb/core.hxx>
-
-#pragma db value
-class basic_name
-{
-public:
- basic_name (const std::string&amp; first, const std::string&amp; last);
-
- ...
-
-private:
- friend class odb::access;
-
- basic_name () {} // Needed for storing basic_name in containers.
-
- ...
-};
- </pre>
-
- <p>The ODB compiler also needs access to the non-transient
- (<a href="#14.4.11">Section 14.4.11, "<code>transient</code>"</a>)
- data members of a composite value type. It uses the same mechanisms
- as for persistent classes which are discussed in
- <a href="#3.2">Section 3.2, "Declaring Persistent Objects and
- Values"</a>.</p>
-
- <p>The members of a composite value can be other value types (either
- simple or composite), containers (<a href="#5">Chapter 5,
- "Containers"</a>), and pointers to objects (<a href="#6">Chapter 6,
- "Relationships"</a>).
- Similarly, a composite value type can be used in object members,
- as an element of a container, and as a base for another composite
- value type. In particular, composite value types can be used as
- element types in set containers (<a href="#5.2">Section 5.2, "Set
- and Multiset Containers"</a>) and as key types in map containers
- (<a href="#5.3">Section 5.3, "Map and Multimap Containers"</a>).
- A composite value type that is used as an element of a container
- cannot contain other containers since containers of containers
- are not allowed. The following example illustrates some of the
- possible use cases:</p>
-
- <pre class="cxx">
-#pragma db value
-class basic_name
-{
- ...
-
- std::string first_;
- std::string last_;
-};
-
-typedef std::vector&lt;basic_name> basic_names;
-
-#pragma db value
-class name_extras
-{
- ...
-
- std::string nickname_;
- basic_names aliases_;
-};
-
-#pragma db value
-class name: public basic_name
-{
- ...
-
- std::string title_;
- name_extras extras_;
-};
-
-#pragma db object
-class person
-{
- ...
-
- name name_;
-};
- </pre>
-
- <p>A composite value type can be defined inside a persistent class,
- view, or another composite value and even made private, provided
- we make <code>odb::access</code> a friend of the containing class,
- for example:</p>
-
-<pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db value
- class name
- {
- ...
-
- std::string first_;
- std::string last_;
- };
-
- name name_;
-};
- </pre>
-
- <p>A composite value type can also be defined as an instantiation
- of a C++ class template, for example:</p>
-
- <pre class="cxx">
-template &lt;typename T>
-struct point
-{
- T x;
- T y;
- T z;
-};
-
-typedef point&lt;int> int_point;
-#pragma db value(int_point)
-
-#pragma db object
-class object
-{
- ...
-
- int_point center_;
-};
- </pre>
-
- <p>Note that the database support code for such a composite value type
- is generated when compiling the header containing the
- <code>db&nbsp;value</code> pragma and not the header containing
- the template definition or the <code>typedef</code> name. This
- allows us to use templates defined in other files, such as
- <code>std::pair</code> defined in the <code>utility</code>
- standard header file:</p>
-
- <pre class="cxx">
-#include &lt;utility> // std::pair
-
-typedef std::pair&lt;std::string, std::string> phone_numbers;
-#pragma db value(phone_numbers)
-
-#pragma db object
-class person
-{
- ...
-
- phone_numbers phone_;
-};
- </pre>
-
- <p>We can also use data members from composite value types
- in database queries (<a href="#4">Chapter 4, "Querying the
- Database"</a>). For each composite value in a persistent class, the
- query class defines a nested member that contains members corresponding
- to the data members in the value type. We can then use the member access
- syntax (.) to refer to data members in value types. For example, the
- query class for the <code>person</code> object presented above
- contains the <code>name</code> member (its name is derived from
- the <code>name_</code> data member) which in turn contains the
- <code>extras</code> member (its name is derived from the
- <code>name::extras_</code> data member of the composite value type).
- This process continues recursively for nested composite value types
- and, as a result, we can use the <code>query::name.extras.nickname</code>
- expression while querying the database for the <code>person</code>
- objects. For example:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;person> query;
-typedef odb::result&lt;person> result;
-
-transaction t (db.begin ());
-
-result r (db.query&lt;person> (
- query::name.extras.nickname == "Squeaky"));
-
-...
-
-t.commit ();
- </pre>
-
- <h3><a name="7.2.1">7.2.1 Composite Object Ids</a></h3>
-
- <p>An object id can be of a composite value type, for example:</p>
-
- <pre class="cxx">
-#pragma db value
-class name
-{
- ...
-
- std::string first_;
- std::string last_;
-};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id
- name name_;
-};
- </pre>
-
- <p>However, a value type that can be used as an object id has a number
- of restrictions. Such a value type cannot have container, object
- pointer, or read-only data members. It also must be
- default-constructible, copy-constructible, and copy-assignable.
- Furthermore, if the persistent class in which
- this composite value type is used as object id has session support
- enabled (<a href="#11">Chapter 11, "Session"</a>), then it must also
- implement the less-than comparison operator (<code>operator&lt;</code>).</p>
-
- <h3><a name="7.2.2">7.2.2 Composite Value Column and Table Names</a></h3>
-
- <p>Customizing a column name for a data member of a simple value
- type is straightforward: we simply specify the desired name with
- the <code>db&nbsp;column</code> pragma (<a href="#14.4.9">Section
- 14.4.9, "<code>column</code>"</a>). For composite value
- types things are slightly more complex since they are mapped to
- multiple columns. Consider the following example:</p>
-
- <pre class="cxx">
-#pragma db value
-class name
-{
- ...
-
- std::string first_;
- std::string last_;
-};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id auto
- unsigned long id_;
-
- name name_;
-};
- </pre>
-
- <p>The column names for the <code>first_</code> and <code>last_</code>
- members are constructed by using the sanitized name of the
- <code>person::name_</code> member as a prefix and the names of the
- members in the value type (<code>first_</code> and <code>last_</code>)
- as suffixes. As a result, the database schema for the above classes
- will look like this:</p>
-
- <pre class="sql">
-CREATE TABLE person (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- name_first TEXT NOT NULL,
- name_last TEXT NOT NULL);
- </pre>
-
- <p>We can customize both the prefix and the suffix using the
- <code>db&nbsp;column</code> pragma as shown in the following
- example:</p>
-
- <pre class="cxx">
-#pragma db value
-class name
-{
- ...
-
- #pragma db column("first_name")
- std::string first_;
-
- #pragma db column("last_name")
- std::string last_;
-};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db column("person_")
- name name_;
-};
- </pre>
-
- <p>The database schema changes as follows:</p>
-
- <pre class="sql">
-CREATE TABLE person (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- person_first_name TEXT NOT NULL,
- person_last_name TEXT NOT NULL);
- </pre>
-
- <p>We can also make the column prefix empty, for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db column("")
- name name_;
-};
- </pre>
-
- <p>This will result in the following schema:</p>
-
- <pre class="sql">
-CREATE TABLE person (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- first_name TEXT NOT NULL,
- last_name TEXT NOT NULL);
- </pre>
-
- <p>The same principle applies when a composite value type is used
- as an element of a container, except that instead of
- <code>db&nbsp;column</code>, either the <code>db&nbsp;value_column</code>
- (<a href="#14.4.36">Section 14.4.36, "<code>value_column</code>"</a>) or
- <code>db&nbsp;key_column</code>
- (<a href="#14.4.35">Section 14.4.35, "<code>key_column</code>"</a>)
- pragmas are used to specify the column prefix.</p>
-
- <p>When a composite value type contains a container, an extra table
- is used to store its elements (<a href="#5">Chapter 5, "Containers"</a>).
- The names of such tables are constructed in a way similar to the
- column names, except that by default both the object name and the
- member name are used as a prefix. For example:</p>
-
- <pre class="cxx">
-#pragma db value
-class name
-{
- ...
-
- std::string first_;
- std::string last_;
- std::vector&lt;std::string> nicknames_;
-};
-
-#pragma db object
-class person
-{
- ...
-
- name name_;
-};
- </pre>
-
- <p>The corresponding database schema will look like this:</p>
-
- <pre class="sql">
-CREATE TABLE person_name_nicknames (
- object_id BIGINT UNSIGNED NOT NULL,
- index BIGINT UNSIGNED NOT NULL,
- value TEXT NOT NULL)
-
-CREATE TABLE person (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- name_first TEXT NOT NULL,
- name_last TEXT NOT NULL);
- </pre>
-
- <p>To customize the container table name we can use the
- <code>db&nbsp;table</code> pragma (<a href="#14.4.20">Section
- 14.4.20, "<code>table</code>"</a>), for example:</p>
-
- <pre class="cxx">
-#pragma db value
-class name
-{
- ...
-
- #pragma db table("nickname")
- std::vector&lt;std::string> nicknames_;
-};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db table("person_")
- name name_;
-};
- </pre>
-
- <p>This will result in the following schema changes:</p>
-
- <pre class="sql">
-CREATE TABLE person_nickname (
- object_id BIGINT UNSIGNED NOT NULL,
- index BIGINT UNSIGNED NOT NULL,
- value TEXT NOT NULL)
- </pre>
-
- <p>Similar to columns, we can make the table prefix empty.</p>
-
-
- <h2><a name="7.3">7.3 Pointers and <code>NULL</code> Value Semantics</a></h2>
-
- <p>Relational database systems have a notion of the special
- <code>NULL</code> value that is used to indicate the absence
- of a valid value in a column. While by default ODB maps
- values to columns that do not allow <code>NULL</code> values,
- it is possible to change that with the <code>db&nbsp;null</code>
- pragma (<a href="#14.4.6">Section 14.4.6,
- "<code>null</code>/<code>not_null</code>"</a>).</p>
-
- <p>To properly support the <code>NULL</code> semantics, the
- C++ value type must have a notion of a <code>NULL</code>
- value or a similar special state concept. Most basic
- C++ types, such as <code>int</code> or <code>std::string</code>,
- do not have this notion and therefore cannot be used directly
- for <code>NULL</code>-enabled data members (in the case of a
- <code>NULL</code> value being loaded from the database,
- such data members will be default-initialized).</p>
-
- <p>To allow the easy conversion of value types that do not support
- the <code>NULL</code> semantics into the ones that do, ODB
- provides the <code>odb::nullable</code> class template. It
- allows us to wrap an existing C++ type into a container-like
- class that can either be <code>NULL</code> or contain a
- value of the wrapped type. ODB also automatically enables
- the <code>NULL</code> values for data members of the
- <code>odb::nullable</code> type. For example:</p>
-
- <pre class="cxx">
-#include &lt;odb/nullable.hxx>
-
-#pragma db object
-class person
-{
- ...
-
- std::string first_; // TEXT NOT NULL
- odb::nullable&lt;std::string> middle_; // TEXT NULL
- std::string last_; // TEXT NOT NULL
-};
- </pre>
-
- <p>The <code>odb::nullable</code> class template is defined
- in the <code>&lt;odb/nullable.hxx></code> header file and
- has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- template &lt;typename T>
- class nullable
- {
- public:
- typedef T value_type;
-
- nullable ();
- nullable (const T&amp;);
- nullable (const nullable&amp;);
- template &lt;typename Y> explicit nullable (const nullable&lt;Y>&amp;);
-
- nullable&amp; operator= (const T&amp;);
- nullable&amp; operator= (const nullable&amp;);
- template &lt;typename Y> nullable&amp; operator= (const nullable&lt;Y>&amp;);
-
- void swap (nullable&amp;);
-
- // Accessor interface.
- //
- bool null () const;
-
- T&amp; get ();
- const T&amp; get () const;
-
- // Pointer interface.
- //
- operator bool_convertible () const;
-
- T* operator-> ();
- const T* operator-> () const;
-
- T&amp; operator* ();
- const T&amp; operator* () const;
-
- // Reset to the NULL state.
- //
- void reset ();
- };
-}
- </pre>
-
- <p>The following example shows how we can use this interface:</p>
-
- <pre class="cxx">
- nullable&lt;string> ns;
-
- // Using the accessor interface.
- //
- if (ns.null ())
- {
- s = "abc";
- }
- else
- {
- string s (ns.get ());
- ns.reset ();
- }
-
- // The same using the pointer interface.
- //
- if (!ns)
- {
- s = "abc";
- }
- else
- {
- string s (*ns);
- ns.reset ();
- }
- </pre>
-
-
- <p>The <code>odb::nullable</code> class template requires the wrapped
- type to have public default and copy constructors as well as the
- copy assignment operator. Note also that the <code>odb::nullable</code>
- implementation is not the most efficient in that it always contains
- a fully constructed value of the wrapped type. This is normally
- not a concern for simple types such as the C++ fundamental
- types or <code>std::string</code>. However, it may become
- an issue for more complex types. In such cases you may want to
- consider using a more efficient implementation of the
- <em>optional value</em> concept such as the
- <code>optional</code> class template from Boost
- (<a href="#23.4">Section 23.4, "Optional Library"</a>).</p>
-
- <p>Another common C++ representation of a value that can be
- <code>NULL</code> is a pointer. ODB will automatically
- handle data members that are pointers to values, however,
- it will not automatically enable <code>NULL</code> values
- for such data members, as is the case for <code>odb::nullable</code>.
- Instead, if the <code>NULL</code> value is desired, we will
- need to enable it explicitly using the <code>db&nbsp;null</code>
- pragma. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- std::string first_;
-
- #pragma db null
- std::auto_ptr&lt;std::string> middle_;
-
- std::string last_;
-};
- </pre>
-
- <p>The ODB compiler includes built-in support for using
- <code>std::auto_ptr</code>, <code>std::unique_ptr</code> (C++11),
- and <code>shared_ptr</code> (TR1 or C++11) as pointers to values.
- Plus, ODB profile libraries, that are
- available for commonly used frameworks and libraries (such as Boost and
- Qt), provide support for smart pointers found in these frameworks
- and libraries (<a href="#III">Part III, "Profiles"</a>).</p>
-
- <p>ODB also supports the <code>NULL</code> semantics for composite
- values. In the relational database the <code>NULL</code> composite
- value is translated to <code>NULL</code> values for all the simple
- data members of this composite value. For example:</p>
-
- <pre class="cxx">
-#pragma db value
-struct name
-{
- std::string first_;
- odb::nullable&lt;std::string> middle_;
- std::string last_;
-};
-
-#pragma db object
-class person
-{
- ...
- odb::nullable&lt;name> name_;
-};
- </pre>
-
- <p>ODB does not support the <code>NULL</code> semantics for containers.
- This also means that a composite value that contains a container
- cannot be <code>NULL</code>. With this limitation in mind, we can
- still use smart pointers in data members of container types. The
- only restriction is that these pointers must not be <code>NULL</code>.
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- std::auto_ptr&lt;std::vector&lt;std::string> > aliases_;
-};
- </pre>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="8">8 Inheritance</a></h1>
-
- <p>In C++ inheritance can be used to achieve two different goals.
- We can employ inheritance to reuse common data and functionality
- in multiple classes. For example:</p>
-
- <pre class="cxx">
-class person
-{
-public:
- const std::string&amp; first () const;
- const std::string&amp; last () const;
-
-private:
- std::string first_;
- std::string last_;
-};
-
-class employee: public person
-{
- ...
-};
-
-class contractor: public person
-{
- ...
-};
- </pre>
-
- <p>In the above example both the <code>employee</code> and
- <code>contractor</code> classes inherit the <code>first_</code>
- and <code>last_</code> data members as well as the <code>first()</code>
- and <code>last()</code> accessors from the <code>person</code> base
- class.</p>
-
- <p>A common trait of this inheritance style, referred to as <em>reuse
- inheritance</em> from now on, is the lack of virtual functions and
- a virtual destructor in the base class. Also with this style the
- application code is normally written in terms of the derived classes
- instead of the base.</p>
-
- <p>The second way to utilize inheritance in C++ is to provide polymorphic
- behavior through a common interface. In this case the base class
- defines a number of virtual functions and, normally, a virtual
- destructor while the derived classes provide specific
- implementations of these virtual functions. For example:</p>
-
- <pre class="cxx">
-class person
-{
-public:
- enum employment_status
- {
- unemployed,
- temporary,
- permanent,
- self_employed
- };
-
- virtual employment_status
- employment () const = 0;
-
- virtual
- ~person ();
-};
-
-class employee: public person
-{
-public:
- virtual employment_status
- employment () const
- {
- return temporary_ ? temporary : permanent;
- }
-
-private:
- bool temporary_;
-};
-
-class contractor: public person
-{
-public:
- virtual employment_status
- employment () const
- {
- return self_employed;
- }
-};
- </pre>
-
- <p>With this inheritance style, which we will call <em>polymorphism
- inheritance</em>, the application code normally works with derived
- classes via the base class interface. Note also that it is very common
- to mix both styles in the same hierarchy. For example, the above two
- code fragments can be combined so that the <code>person</code> base
- class provides the common data members and functions as well as
- defines the polymorphic interface.</p>
-
- <p>The following sections describe the available strategies for
- mapping reuse and polymorphism inheritance styles to a relational
- data model. Note also that the distinction between the two styles is
- conceptual rather than formal. For example, it is possible to treat
- a class hierarchy that defines virtual functions as a case of reuse
- inheritance if this results in the desired database mapping and
- semantics.</p>
-
- <p>Generally, classes that employ reuse inheritance are mapped to
- completely independent entities in the database. They use different
- object id spaces and should always be passed to and returned from
- the database operations as pointers or references to derived types.
- In other words, from the persistence point of view, such classes
- behave as if the data members from the base classes were copied
- verbatim into the derived ones.</p>
-
- <p>In contrast, classes that employ polymorphism inheritance share
- the object id space and can be passed to and returned from the
- database operations <em>polymorphically</em> as pointers or
- references to the base class.</p>
-
- <p>For both inheritance styles it is sometimes desirable to prevent
- instances of a base class from being stored in the database.
- To achieve this a persistent
- class can be declared abstract using the <code>db&nbsp;abstract</code>
- pragma (<a href="#14.1.3">Section 14.1.3, "<code>abstract</code>"</a>).
- Note that a <em>C++-abstract</em> class, or a class that
- has one or more pure virtual functions and therefore cannot be
- instantiated, is also <em>database-abstract</em>. However, a
- database-abstract class is not necessarily C++-abstract. The
- ODB compiler automatically treats C++-abstract classes as
- database-abstract.</p>
-
- <h2><a name="8.1">8.1 Reuse Inheritance</a></h2>
-
- <p>Each non-abstract class from the reuse inheritance hierarchy is
- mapped to a separate database table that contains all its data
- members, including those inherited from base classes. An abstract
- persistent class does not have to define an object id, nor a default
- constructor, and it does not have a corresponding database table.
- An abstract class cannot be a pointed-to object in a relationship.
- Multiple inheritance is supported as long as each base
- class is only inherited once. The following example shows a
- persistent class hierarchy employing reuse inheritance:</p>
-
- <pre class="cxx">
-// Abstract person class. Note that it does not declare the
-// object id.
-//
-#pragma db object abstract
-class person
-{
- ...
-
- std::string first_;
- std::string last_;
-};
-
-// Abstract employee class. It derives from the person class and
-// declares the object id for all the concrete employee types.
-//
-#pragma db object abstract
-class employee: public person
-{
- ...
-
- #pragma db id auto
- unsigned long id_;
-};
-
-// Concrete permanent_employee class. Note that it doesn't define
-// any data members of its own.
-//
-#pragma db object
-class permanent_employee: public employee
-{
- ...
-};
-
-// Concrete temporary_employee class. It adds the employment
-// duration in months.
-//
-#pragma db object
-class temporary_employee: public employee
-{
- ...
-
- unsigned long duration_;
-};
-
-// Concrete contractor class. It derives from the person class
-// (and not employee; an independent contractor is not considered
-// an employee). We use the contractor's external email address
-// as the object id.
-//
-#pragma db object
-class contractor: public person
-{
- ...
-
- #pragma db id
- std::string email_;
-};
- </pre>
-
- <p>The sample database schema for this hierarchy is shown below.</p>
-
- <pre class="sql">
-CREATE TABLE permanent_employee (
- first TEXT NOT NULL,
- last TEXT NOT NULL,
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT);
-
-CREATE TABLE temporary_employee (
- first TEXT NOT NULL,
- last TEXT NOT NULL,
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
- duration BIGINT UNSIGNED NOT NULL);
-
-CREATE TABLE contractor (
- first TEXT NOT NULL,
- last TEXT NOT NULL,
- email VARCHAR (128) NOT NULL PRIMARY KEY);
- </pre>
-
- <p>The complete version of the code presented in this section is
- available in the <code>inheritance/reuse</code> example in the
- <code>odb-examples</code> package.</p>
-
- <h2><a name="8.2">8.2 Polymorphism Inheritance</a></h2>
-
- <p>There are three general approaches to mapping a polymorphic
- class hierarchy to a relational database. These are
- <em>table-per-hierarchy</em>, <em>table-per-difference</em>,
- and <em>table-per-class</em>. With the table-per-hierarchy
- mapping, all the classes in a hierarchy are stored in a single,
- "wide" table. <code>NULL</code> values are stored in columns
- corresponding to data members of derived classes that are
- not present in any particular instance.</p>
-
- <p>In the table-per-difference mapping, each class is mapped
- to a separate table. For a derived class, this table contains
- only columns corresponding to the data members added by this
- derived class.</p>
-
- <p>Finally, in the table-per-class mapping, each class is mapped
- to a separate table. For a derived class, this table contains
- columns corresponding to all the data members, from this derived
- class all the way down to the root of the hierarchy.</p>
-
- <p>The table-per-difference mapping is generally considered as
- having the best balance of flexibility, performance, and space
- efficiency. It also results in a more canonical relational
- database model compared to the other two approaches. As a
- result, this is the mapping currently implemented in ODB.
- Other mappings may be supported in the future. Note that
- multiple polymorphism inheritance or mixing polymorphism and
- reuse inheritance is not supported.</p>
-
- <p>A pointer or reference to an ordinary, non-polymorphic object
- has just one type &mdash; the class type of that object. When we
- start working with polymorphic objects, there are two types
- to consider: the <em>static type</em>, or the declaration type
- of a reference or pointer, and the object's actual or <em>dynamic
- type</em>. An example will help illustrate the difference:</p>
-
- <pre class="cxx">
-class person {...};
-class employee: public person {...};
-
-person p;
-employee e;
-
-person&amp; r1 (p);
-person&amp; r2 (e);
-
-auto_ptr&lt;person> p1 (new employee);
- </pre>
-
- <p>In the above example, the <code>r1</code> reference's both static
- and dynamic types are <code>person</code>.
- In contrast, the <code>r2</code> reference's static type is
- <code>person</code> while its dynamic type (the actual object
- that it refers to) is <code>employee</code>. Similarly,
- <code>p1</code> points to the object of the <code>person</code>
- static type but <code>employee</code> dynamic type.</p>
-
- <p>In C++, the primary mechanisms for working with polymorphic objects
- are virtual functions. We call a virtual function only knowing the
- object's static type, but the version corresponding to the object's
- dynamic type is automatically executed. This is the essence of
- runtime polymorphism support in C++: we can operate in terms of a base
- class interface but get the derived class' behavior. Similarly, the
- essence of the runtime polymorphism support in ODB is to allow us to
- persist, load, update, and query in terms of the base class interface
- but have the derived class actually stored in the database.</p>
-
- <p>To declare a persistent class as polymorphic we use the
- <code>db&nbsp;polymorphic</code> pragma. We only need to
- declare the root class of a hierarchy as polymorphic; ODB will
- treat all the derived classes as polymorphic automatically. For
- example:</p>
-
- <pre class="cxx">
-#pragma db object polymorphic
-class person
-{
- ...
-
- virtual
- ~person () = 0; // Automatically abstract.
-
- #pragma db id auto
- unsigned long id_;
-
- std::string first_;
- std::string last_;
-};
-
-#pragma db object
-class employee: public person
-{
- ...
-
- bool temporary_;
-};
-
-#pragma db object
-class contractor: public person
-{
-
- std::string email_;
-};
- </pre>
-
- <p>A persistent class hierarchy declared polymorphic must also be
- polymorphic in the C++ sense, that is, the root class must
- declare or inherit at least one virtual function. It is
- recommended that the root class also declares a virtual destructor.
- The root class of the polymorphic hierarchy must contain
- the data member designated as object id (a persistent class
- without an object id cannot be polymorphic). Note also that,
- unlike reuse inheritance, abstract polymorphic classes have
- a table in the database, just like non-abstract classes.</p>
-
- <p>Persistent classes in the same polymorphic hierarchy must use the
- same kind of object pointer (<a href="#3.3">Section 3.3,
- "Object and View Pointers"</a>). If the object pointer
- for the root class is specified as a template or using the
- special raw pointer syntax (<code>*</code>), then the ODB
- compiler will automatically use the same object pointer
- for all the derived classes. For example:</p>
-
- <pre class="cxx">
-#pragma db object polymorphic pointer(std::shared_ptr)
-class person
-{
- ...
-};
-
-#pragma db object // Object pointer is std::shared_ptr&lt;employee>.
-class employee: public person
-{
- ...
-};
-
-#pragma db object // Object pointer is std::shared_ptr&lt;contractor>.
-class contractor: public person
-{
- ...
-};
- </pre>
-
- <p>Similarly, if we enable or disable session support
- (<a href="#11">Chapter 11, "Session"</a>) for the root class, then
- the ODB compiler will automatically enable or disable it for all
- the derived classes.</p>
-
- <p>For polymorphic persistent classes, all the database operations can
- be performed on objects with different static and dynamic types.
- Similarly, operations that load persistent objects from the
- database (<code>load()</code>, <code>query()</code>, etc.), can
- return objects with different static and dynamic types. For
- example:</p>
-
- <pre class="cxx">
-unsigned long id1, id2;
-
-// Persist.
-//
-{
- shared_ptr&lt;person> p1 (new employee (...));
- shared_ptr&lt;person> p2 (new contractor (...));
-
- transaction t (db.begin ());
- id1 = db.persist (p1); // Stores employee.
- id2 = db.persist (p2); // Stores contractor.
- t.commit ();
-}
-
-// Load.
-//
-{
- shared_ptr&lt;person> p;
-
- transaction t (db.begin ());
- p = db.load&lt;person> (id1); // Loads employee.
- p = db.load&lt;person> (id2); // Loads contractor.
- t.commit ();
-}
-
-// Query.
-//
-{
- typedef odb::query&lt;person> query;
- typedef odb::result&lt;person> result;
-
- transaction t (db.begin ());
-
- result r (db.query&lt;person> (query::last == "Doe"));
-
- for (result::iterator i (r.begin ()); i != r.end (); ++i)
- {
- person&amp; p (*i); // Can be employee or contractor.
- }
-
- t.commit ();
-}
-
-// Update.
-//
-{
- shared_ptr&lt;person> p;
- shared_ptr&lt;employee> e;
-
- transaction t (db.begin ());
-
- e = db.load&lt;employee> (id1);
- e->temporary (false);
- p = e;
- db.update (p); // Updates employee.
-
- t.commit ();
-}
-
-// Erase.
-//
-{
- shared_ptr&lt;person> p;
-
- transaction t (db.begin ());
- p = db.load&lt;person> (id1); // Loads employee.
- db.erase (p); // Erases employee.
- db.erase&lt;person> (id2); // Erases contractor.
- t.commit ();
-}
- </pre>
-
-
- <p>The table-per-difference mapping, as supported by ODB, requires
- two extra columns, in addition to those corresponding to the
- data members. The first, called <em>discriminator</em>, is added
- to the table corresponding to the root class of the hierarchy.
- This column is used to determine the dynamic type of each
- object. The second column is added to tables corresponding
- to the derived classes and contains the object id. This
- column is used to form a foreign key constraint referencing
- the root class table.</p>
-
- <p>When querying the database for polymorphic objects, it is
- possible to obtain the discriminator value without
- instantiating the object. For example:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;person> query;
-typedef odb::result&lt;person> result;
-
-transaction t (db.begin ());
-
-result r (db.query&lt;person> (query::last == "Doe"));
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
-{
- std::string d (i.discriminator ());
- ...
-}
-
-t.commit ();
- </pre>
-
- <p>In the current implementation, ODB has limited support for
- customizing names, types, and values of the extra columns.
- Currently, the discriminator column is always called
- <code>typeid</code> and contains a namespace-qualified class
- name (for example, <code>"employee"</code> or
- <code>"hr::employee"</code>). The id column in the derived
- class table has the same name as the object id column in
- the root class table. Future versions of ODB will add support
- for customizing these extra columns.</p>
-
- <p>The sample database schema for the above polymorphic hierarchy
- is shown below.</p>
-
- <pre class="sql">
-CREATE TABLE person (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
- typeid VARCHAR(128) NOT NULL,
- first TEXT NOT NULL,
- last TEXT NOT NULL);
-
-CREATE TABLE employee (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- temporary TINYINT(1) NOT NULL,
-
- CONSTRAINT employee_id_fk
- FOREIGN KEY (id)
- REFERENCES person (id)
- ON DELETE CASCADE);
-
-CREATE TABLE contractor (
- id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
- email TEXT NOT NULL,
-
- CONSTRAINT contractor_id_fk
- FOREIGN KEY (id)
- REFERENCES person (id)
- ON DELETE CASCADE);
- </pre>
-
- <p>The complete version of the code presented in this section is
- available in the <code>inheritance/polymorphism</code> example
- in the <code>odb-examples</code> package.</p>
-
- <h3><a name="8.2.1">8.2.1 Performance and Limitations</a></h3>
-
- <p>A database operation on a non-polymorphic object normally translates
- to a single database statement execution (objects with containers
- and eager object pointers can be the exception). Because polymorphic
- objects have their data members
- stored in multiple tables, some database operations on such objects
- may result in multiple database statements being executed while others
- may require more complex statements. There is also some functionality
- that is not available to polymorphic objects.</p>
-
- <p>The first part of this section discusses the performance implications
- to keep in mind when designing and working with polymorphic hierarchies.
- The second part talks about limitations of polymorphic objects.</p>
-
- <p>The most important aspect of a polymorphic hierarchy that
- affects database performance is its depth. The distance between
- the root of the hierarchy and the derived class translates
- directly to the number of database statements that will have to
- be executed in order to persist, update, or erase this derived class.
- It also translates directly to the number of SQL <code>JOIN</code>
- clauses that will be needed to load or query the database for this
- derived class. As a result, to achieve best performance, we should
- try to keep our polymorphic hierarchies as flat as possible.</p>
-
- <p>When loading an object or querying the database for objects,
- ODB will need to execute two statements if this object's static
- and dynamic types are different but only one statement if
- they are the same. This example will help illustrate the
- difference:</p>
-
- <pre class="cxx">
-unsigned long id;
-
-{
- employee e (...);
-
- transaction t (db.begin ());
- id = db.persist (e);
- t.commit ();
-}
-
-{
- shared_ptr&lt;person> p;
-
- transaction t (db.begin ());
- p = db.load&lt;person> (id); // Requires two statement.
- p = db.load&lt;employee> (id); // Requires only one statement.
- t.commit ();
-}
- </pre>
-
- <p>As a result, we should try to load and query using the most
- derived class possible.</p>
-
- <p>Finally, for polymorphic objects, erasing via the object instance
- is faster than erasing via its object id. In the former case the
- object's dynamic type can be determined locally in the application
- while in the latter case an extra statement has to be executed to
- achieve the same result. For example:</p>
-
- <pre class="cxx">
-shared_ptr&lt;person> p = ...;
-
-transaction t (db.begin ());
-db.erase&lt;person> (p.id ()); // Slower (executes extra statement).
-db.erase (p); // Faster.
-t.commit ();
- </pre>
-
- <p>Polymorphic objects can use all the mechanisms that are available
- to ordinary objects. These include containers (<a href="#5">Chapter 5,
- "Containers"</a>), object relationships, including to polymorphic
- objects (<a href="#6">Chapter 6, "Relationships"</a>), views
- (<a href="#10">Chapter 10, "Views"</a>), session (<a href="#11">Chapter
- 11, "Session"</a>), and optimistic concurrency (<a href="#12">Chapter
- 12, "Optimistic Concurrency"</a>). There are, however, a few
- limitations, mainly due to the underlying use of SQL to access the
- data.</p>
-
- <p>When a polymorphic object is "joined" in a view, and the join
- condition (either in the form of an object pointer or a custom
- condition) comes from the object itself (as opposed to one of
- the objects joined previously), then this condition must only
- use data members from the derived class. For example, consider
- the following polymorphic object hierarchy and a view:</p>
-
-
- <pre class="cxx">
-#pragma db object polymorphic
-class employee
-{
- ...
-};
-
-#pragma db object
-class permanent_employee: public employee
-{
- ...
-};
-
-#pragma db object
-class temporary_employee: public employee
-{
- ...
-
- shared_ptr&lt;permanent_employee> manager_;
-};
-
-#pragma db object
-class contractor: public temporary_employee
-{
- shared_ptr&lt;permanent_employee> manager_;
-};
-
-#pragma db view object(permanent_employee) \
- object(contractor: contractor::manager_)
-struct contractor_manager
-{
- ...
-};
- </pre>
-
- <p>This view will not function correctly because the join condition
- (<code>manager_</code>) comes from the base class
- (<code>temporary_employee</code>) instead of the derived
- (<code>contractor</code>). The reason for this limitation is the
- <code>JOIN</code> clause order in the underlying SQL <code>SELECT</code>
- statement. In the view presented above, the table corresponding
- to the base class (<code>temporary_employee</code>) will have to
- be joined first which will result in this view matching both
- the <code>temporary_employee</code> and <code>contractor</code>
- objects instead of just <code>contractor</code>. It is usually
- possible to resolve this issue by reordering the objects in the
- view. Our example, for instance, can be fixed by swapping the
- two objects:</p>
-
- <pre class="cxx">
-#pragma db view object(contractor) \
- object(permanent_employee: contractor::manager_)
-struct contractor_manager
-{
- ...
-};
- </pre>
-
- <p>The <code>erase_query()</code> database function (<a href="#3.11">Section
- 3.11, "Deleting Persistent Objects"</a>) also has limited functionality
- when used on polymorphic objects. Because many database implementations
- do not support <code>JOIN</code> clauses in the SQL <code>DELETE</code>
- statement, only data members from the derived class being erased can
- be used in the query condition. For example:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee> query;
-
-transaction t (db.begin ());
-db.erase_query&lt;employee> (query::permanent); // Ok.
-db.erase_query&lt;employee> (query::last == "Doe"); // Error.
-t.commit ();
- </pre>
-
- <h2><a name="8.3">8.3 Mixed Inheritance</a></h2>
-
- <p>It is possible to mix the reuse and polymorphism inheritance
- styles in the same hierarchy. In this case, the reuse inheritance
- must be used for the "bottom" (base) part of the hierarchy while
- the polymorphism inheritance &mdash; for the "top" (derived) part.
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-};
-
-#pragma db object polymorphic
-class employee: public person // Reuse inheritance.
-{
- ...
-};
-
-#pragma db object
-class temporary_employee: public employee // Polymorphism inheritance.
-{
- ...
-};
-
-#pragma db object
-class permanent_employee: public employee // Polymorphism inheritance.
-{
- ...
-};
- </pre>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="9">9 Sections</a></h1>
-
- <p>ODB sections are an optimization mechanism that allows us to
- partition data members of a persistent class into groups that
- can be separately loaded and/or updated. This can be useful,
- for example, if an object contains expensive to load or update
- data members (such as <code>BLOB</code>s or containers) and
- that are accessed or modified infrequently. For example:</p>
-
- <pre class="cxx">
-#include &lt;odb/section.hxx>
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db load(lazy) update(manual)
- odb::section keys_;
-
- #pragma db section(keys_) type("BLOB")
- char public_key_[1024];
-
- #pragma db section(keys_) type("BLOB")
- char private_key_[1024];
-};
-
-transaction t (db.begin ());
-
-auto_ptr&lt;person> p (db.load&lt;person> (...)); // Keys are not loaded.
-
-if (need_keys)
-{
- db.load (*p, p->keys_); // Load keys.
- ...
-}
-
-db.update (*p); // Keys are not updated.
-
-if (update_keys)
-{
- ...
- db.update (*p, p->keys_); // Update keys.
-}
-
-t.commit ();
- </pre>
-
- <p>A complete example that shows how to use sections is available in
- the <code>section</code> directory in the <code>odb-examples</code>
- package.</p>
-
- <p>Why do we need to group data members into sections? Why can't
- each data member be loaded and updated independently if and
- when necessary? The reason for this requirement is that loading
- or updating a group of data members with a single database
- statement is significantly more efficient than loading or updating
- each data member with a separate statement. Because ODB
- prepares and caches statements used to load and update
- persistent objects, generating a custom statement for
- a specific set of data members that need to be loaded or
- updated together is not a viable approach either. To resolve
- this, ODB allows us to group data members that are
- often updated and/or loaded together into sections. To
- achieve the best performance, we should aim to find a balance
- between having too many sections with too few data
- members and too few sections with too many data
- members. We can use the access and modification patterns
- of our application as a base for this decision.</p>
-
- <p>To add a new section to a persistent class we declare a new
- data member of the <code>odb::section</code> type. At this
- point we also need to specify the loading and updating behavior
- of this section with the <code>db&nbsp;load</code> and
- <code>db&nbsp;update</code> pragmas, respectively.</p>
-
- <p>The loading behavior of a section can be either <code>eager</code>
- or <code>lazy</code>. An eager-loaded section is always loaded as
- part of the object load. A lazy-loaded section is not loaded
- as part of the object load and has to be explicitly loaded with
- the <code>database::load()</code> function (discussed below) if
- and when necessary.</p>
-
- <p>The updating behavior of a section can be <code>always</code>,
- <code>change</code>, or <code>manual</code>. An always-updated
- section is always updated as part of the object update,
- provided it has been loaded. A change-updated section
- is only updated as part of the object update if it has been loaded
- and marked as changed. A manually-updated section is never updated
- as part of the object update and has to be explicitly updated with
- the <code>database::update()</code> function (discussed below) if
- and when necessary.</p>
-
- <p>If no loading behavior is specified explicitly, then an eager-loaded
- section is assumed. Similarly, if no updating behavior is specified,
- then an always-updated section is assumed. An eager-loaded, always-updated
- section is pointless and therefore illegal. Only persistent classes
- with an object id can have sections.</p>
-
- <p>To specify that a data member belongs to a section we use the
- <code>db&nbsp;section</code> pragma with the section's member
- name as its single argument. Except for special data members
- such as the object id and optimistic concurrency version, any
- direct, non-transient member of a persistent class can belong
- to a section, including composite values, containers, and
- pointers to objects. For example:</p>
-
- <pre class="cxx">
-#pragma db value
-class text
-{
- std::string data;
- std::string lang;
-};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db load(lazy)
- odb::section extras_;
-
- #pragma db section(extras_)
- text bio_;
-
- #pragma db section(extras_)
- std::vector&lt;std::string> nicknames_;
-
- #pragma db section(extras_)
- std::shared_ptr&lt;person> emergency_contact_;
-};
- </pre>
-
- <p>An empty section is pointless and therefore illegal, except
- in abstract or polymorphic classes where data members can be
- added to a section by derived classes (see <a href="#9.1">Section
- 9.1, "Sections and Inheritance"</a>).</p>
-
- <p>The <code>odb::section</code> class is defined in the
- <code>&lt;odb/section.hxx></code> header file and has the
- following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- class section
- {
- public:
- // Load state.
- //
- bool
- loaded () const;
-
- void
- unload ();
-
- void
- load ();
-
- // Change state.
- //
- bool
- changed () const;
-
- void
- change ();
-
- // User data.
- //
- unsigned char
- user_data () const;
-
- void
- user_data (unsigned char);
- };
-}
- </pre>
-
- <p>The <code>loaded()</code> accessor can be used to determine whether a
- section is already loaded. The <code>unload()</code> modifier marks a
- loaded section as not loaded. This, for example, can be useful if you
- don't want the section to be reloaded during the object reload. The
- <code>load()</code> modifier marks an unloaded section as loaded
- without actually loading any of its data members. This, for example,
- can be useful if you don't want to load the old state before overwriting
- it with <code>update()</code>.</p>
-
- <p>The <code>changed()</code> accessor can be used to query the
- section's change state. The <code>change()</code> modifier
- marks the section as changed. It is valid to call this modifier
- for an unloaded (or transient) section, however, the state will
- be reset back to unchanged once the section (or object) is loaded.
- The change state is only relevant to sections with change-updated
- behavior and is ignored for all other sections.</p>
-
- <p>The size of the section class is one byte with four bits available
- to store a custom state via the <code>user_data()</code> accessor
- and modifier.</p>
-
- <p>The <code>odb::database</code> class provides special
- versions of the <code>load()</code> and <code>update()</code>
- functions that allow us to load and update sections of a
- persistent class. Their signatures are as follows:</p>
-
- <pre class="cxx">
- template &lt;typename T>
- void
- load (T&amp; object, section&amp;);
-
- template &lt;typename T>
- void
- update (const T&amp; object, const section&amp;);
- </pre>
-
- <p>Before calling the section <code>load()</code> function, the
- object itself must already be loaded. If the section is already
- loaded, then the call to <code>load()</code> will reload its
- data members. It is illegal to explicitly load an eager-loaded
- section.</p>
-
- <p>Before calling the section <code>update()</code> function, the
- section (and therefore the object) must be in the loaded state.
- If the section is not loaded, the <code>odb::section_not_loaded</code>
- exception is thrown. The section <code>update()</code> function
- does not check but does clear the section's change state. In
- other words, section <code>update()</code> will always update
- section data members in the database and clear the change flag.
- Note also that any section, that is, always-, change-, or
- manually-updated, can be explicitly updated with this function.</p>
-
- <p>Both section <code>load()</code> and <code>update()</code>, just
- like the rest of the database operations, must be performed within
- a transaction. Notice also that both <code>load()</code> and
- <code>update()</code> expect a reference to the section as
- their second argument. This reference must refer to the data
- member in the object passed as the first argument. If instead
- it refers to some other instance of the <code>section</code>
- class, for example, a local copy or a temporary, then the
- <code>odb::section_not_in_object</code> exception is thrown.
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
-public:
- ...
-
- odb::section
- keys () const {return keys_;}
-
-private:
- odb::section keys_;
-
- ...
-};
-
-auto_ptr&lt;person> p (db.load&lt;person> (...));
-
-section s (p->keys ());
-db.load (*p, s); // Throw section_not_in_object, copy.
-
-db.update (*p, p->keys ()); // Throw section_not_in_object, copy.
- </pre>
-
- <p>At first glance it may seem more appropriate to make the
- <code>section</code> class non-copyable in order to prevent
- such errors from happening. However, it is perfectly reasonable
- to expect to be able to copy (or assign) sections as part of
- the object copying (or assignment). As a result, sections are
- left copyable and copy-assignable, however, this functionality
- should not be used in accessors or modifiers. Instead, section
- accessors and modifiers should always be by-reference. Here is
- how we can fix our previous example:</p>
-
-<pre class="cxx">
-#pragma db object
-class person
-{
-public:
- ...
-
- const odb::section&amp;
- keys () const {return keys_;}
-
- odb::section&amp;
- keys () {return keys_;}
-
-private:
- odb::section keys_;
-
- ...
-};
-
-auto_ptr&lt;person> p (db.load&lt;person> (...));
-
-section&amp; s (p->keys ());
-db.load (*p, s); // Ok, reference.
-
-db.update (*p, p->keys ()); // Ok, reference.
- </pre>
-
- <p>Several other database operations affect sections. The state of
- a section in a transient object is undefined. That is, before
- the call to object <code>persist()</code> or <code>load()</code>
- functions, or after the call to object <code>erase()</code>
- function, the values returned by the <code>section::loaded()</code> and
- <code>section::changed()</code> accessors are undefined.</p>
-
- <p>After the call to <code>persist()</code>, all sections, including
- eager-loaded ones, are marked as loaded and unchanged. If instead we
- are loading an object with the <code>load()</code> call or as
- a result of a query, then eager-loaded sections are loaded
- and marked as loaded and unchanged while lazy-loaded ones are marked
- as unloaded. If a lazy-loaded section is later loaded with the
- section <code>load()</code> call, then it is marked as loaded and
- unchanged.</p>
-
- <p>When we update an object with the <code>update()</code> call,
- manually-updated sections are ignored while always-updated
- sections are updated if they are loaded. Change-updated
- sections are only updated if they are both loaded and marked
- as changed. After the update, such sections are reset to the
- unchanged state. When we reload an object with the
- <code>reload()</code> call, sections that were loaded are
- automatically reloaded and reset to the unchanged state.</p>
-
- <p>To further illustrate the state transitions of a section,
- consider this example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db load(lazy) update(change)
- odb::section keys_;
-
- ...
-};
-
-transaction t (db.begin ());
-
-person p ("John", "Doe"); // Section state is undefined (transient).
-
-db.persist (p); // Section state: loaded, unchanged.
-
-auto_ptr&lt;person> l (
- db.load&lt;person> (...)); // Section state: unloaded, unchanged.
-
-db.update (*l); // Section not updated since not loaded.
-db.update (p); // Section not updated since not changed.
-
-p.keys_.change (); // Section state: loaded, changed.
-db.update (p); // Section updated, state: loaded, unchanged.
-
-db.update (*l, l->keys_); // Throw section_not_loaded.
-db.update (p, p.keys_); // Section updated even though not changed.
-
-db.reload (*l); // Section not reloaded since not loaded.
-db.reload (p); // Section reloaded, state: loaded, unchanged.
-
-db.load (*l, l->keys_); // Section loaded, state: loaded, unchanged.
-db.load (p, p.keys_); // Section reloaded, state: loaded, unchanged.
-
-db.erase (p); // Section state is undefined (transient).
-
-t.commit ();
- </pre>
-
- <p>When using change-updated behavior, it is our responsibility to
- mark the section as changed when any of the data members belonging
- to this section is modified. A natural place to mark the section
- as changed is the modifiers for section data members, for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- typedef std::array&lt;char, 1024> key_type;
-
- const key_type&amp;
- public_key () const {return public_key_;}
-
- void
- public_key (const key_type&amp; k)
- {
- public_key_ = k;
- keys_.change ();
- }
-
- const key_type&amp;
- private_key () const {return private_key_;}
-
- void
- private_key (const key_type&amp; k)
- {
- private_key_ = k;
- keys_.change ();
- }
-
-private:
- #pragma db load(lazy) update(change)
- odb::section keys_;
-
- #pragma db section(keys_) type("BLOB")
- key_type public_key_;
-
- #pragma db section(keys_) type("BLOB")
- key_type private_key_;
-
- ...
-};
- </pre>
-
- <p>One interesting aspect of change-updated sections is what happens
- when a transaction that performed an object or section update is
- later rolled back. In this case, while the change state of a
- section has been reset (after update), actual changes were not
- committed to the database. Change-updated sections handle this
- case by automatically registering a rollback callback and then,
- if it is called, restoring the original change state. The
- following code illustrates this semantics (continuing with
- the previous example):</p>
-
- <pre class="cxx">
-auto_ptr&lt;person> p;
-
-try
-{
- transaction t (db.begin ());
- p = db.load&lt;person> (...);
- db.load (*p, p->keys_);
-
- p->private_key (new_key); // The section is marked changed.
- db.update (*p); // The section is reset to unchanged.
-
- throw failed (); // Triggers rollback.
- t.commit ();
-}
-catch (const failed&amp;)
-{
- // The section is restored back to changed.
-}
- </pre>
-
-
- <h2><a name="9.1">9.1 Sections and Inheritance</a></h2>
-
- <p>With both reuse and polymorphism inheritance (<a href="#8">Chapter 8,
- "Inheritance"</a>) it is possible to add new sections to derived
- classes. It is also possible to add data members from derived
- classes to sections declared in the base. For example:</p>
-
- <pre class="cxx">
-#pragma db object polymorphic
-class person
-{
- ...
-
- virtual void
- print ();
-
- #pragma db load(lazy)
- odb::section print_;
-
- #pragma db section(print_)
- std::string bio_;
-};
-
-#pragma db object
-class employee: public person
-{
- ...
-
- virtual void
- print ();
-
- #pragma db section(print_)
- std::vector&lt;std::string> employment_history_;
-};
-
-transaction t (db.begin ());
-
-auto_ptr&lt;person> p (db.load&lt;person> (...)); // Person or employee.
-db.load (*p, p->print_); // Load data members needed for print.
-p->print ();
-
-t.commit ();
- </pre>
-
- <p>When data members of a section are spread over several classes in a
- reuse inheritance hierarchy, both section load and update are
- performed with a single database statement. In contrast, with
- polymorphism inheritance, section load is performed with a
- single statement while update requires a separate statement
- for each class that adds to the section.</p>
-
- <p>Note also that in polymorphism inheritance the section-to-object
- association is static. Or, in other words, you can load a section
- via an object only if its static type actually contains this
- section. The following example will help illustrate this
- point further:</p>
-
- <pre class="cxx">
-#pragma db object polymorphic
-class person
-{
- ...
-};
-
-#pragma db object
-class employee: public person
-{
- ...
-
- #pragma db load(lazy)
- odb::section extras_;
-
- ...
-};
-
-#pragma db object
-class manager: public employee
-{
- ...
-};
-
-auto_ptr&lt;manager> m (db.load&lt;manager> (...));
-
-person&amp; p (*m);
-employee&amp; e (*m);
-section&amp; s (m->extras_);
-
-db.load (p, s); // Error: extras_ is not in person.
-db.load (e, s); // Ok: extras_ is in employee.
- </pre>
-
- <h2><a name="9.2">9.2 Sections and Optimistic Concurrency</a></h2>
-
- <p>When sections are used in a class with the optimistic concurrency
- model (<a href="#12">Chapter 12, "Optimistic Concurrency"</a>),
- both section update and load operations compare the object version
- to that in the database and throw the <code>odb::object_changed</code>
- exception if they do not match. In addition, the section update
- operation increments the version to indicate that the object state
- has changed. For example:</p>
-
- <pre class="cxx">
-#pragma db object optimistic
-class person
-{
- ...
-
- #pragma db version
- unsigned long long version_;
-
- #pragma db load(lazy)
- odb::section extras_;
-
- #pragma db section(extras_)
- std::string bio_;
-};
-
-auto_ptr&lt;person> p;
-
-{
- transaction t (db.begin ());
- p = db.load&lt;person> (...);
- t.commit ();
-}
-
-{
- transaction t (db.begin ());
-
- try
- {
- db.load (*p, p->extras_); // Throws if object state has changed.
- }
- catch (const object_changed&amp;)
- {
- db.reload (*p);
- db.load (*p, p->extras_); // Cannot fail.
- }
-
- t.commit ();
-}
- </pre>
-
- <p>Note also that if an object update triggers one or more
- section updates, then each such update will increment the
- object version. As a result, an update of an object that
- contains sections may result in a version increment by
- more than one.</p>
-
- <p>When sections are used together with optimistic concurrency and
- inheritance, an extra step may be required to enable this
- functionality. If you plan to add new sections to derived
- classes, then the root class of the hierarchy
- (the one that declares the version data member) must be
- declared as sectionable with the <code>db&nbsp;sectionable</code>
- pragma. For example:</p>
-
- <pre class="cxx">
-#pragma db object polymorphic sectionable
-class person
-{
- ...
-
- #pragma db version
- unsigned long long version_;
-};
-
-#pragma db object
-class employee: public person
-{
- ...
-
- #pragma db load(lazy)
- odb::section extras_;
-
- #pragma db section(extras_)
- std::vector&lt;std::string> employment_history_;
-};
- </pre>
-
- <p>This requirement has to do with the need to generate extra
- version increment code in the root class that will be used
- by sections added in the derived classes. If you forget to
- declare the root class as sectionable and later add a
- section to one of the derived classes, the ODB compiler
- will issue diagnostics.</p>
-
- <h2><a name="9.3">9.3 Sections and Lazy Pointers</a></h2>
-
- <p>If a lazy pointer (<a href="#6.4">Section 6.4, "Lazy Pointers"</a>)
- belongs to a lazy-loaded section, then we end up with two levels of
- lazy loading. Specifically, when the section is loaded, the lazy
- pointer is initialized with the object id but the object itself
- is not loaded. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class employee
-{
- ...
-
- #pragma db load(lazy)
- odb::section extras_;
-
- #pragma db section(extras_)
- odb::lazy_shared_ptr&lt;employer> employer_;
-};
-
-transaction t (db.begin ());
-
-auto_ptr&lt;employee> e (db.load&lt;employee> (...)); // employer_ is NULL.
-
-db.load (*e, e->extras_); // employer_ contains valid employer id.
-
-e->employer_.load (); // employer_ points to employer object.
-
-t.commit ();
- </pre>
-
- <h2><a name="9.4">9.4 Sections and Change-Tracking Containers</a></h2>
-
- <p>If a change-tracking container (<a href="#5.4">Section 5.4,
- "Change-Tracking Containers"</a>) belongs to a change-updated
- section, then prior to an object update ODB will check if the
- container has been changed and if so, automatically mark the
- section as changed. For example:</p>
-
-<pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db load(lazy) update(change)
- odb::section extras_;
-
- #pragma db section(extras_)
- odb::vector&lt;std::string> nicknames_;
-};
-
-transaction t (db.begin ());
-
-auto_ptr&lt;person> p (db.load&lt;person> (...));
-db.load (*p, p->extras_);
-
-p->nicknames_.push_back ("JD");
-
-db.update (*p); // Section is automatically updated even
- // though it was not marked as changed.
-t.commit ();
- </pre>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="10">10 Views</a></h1>
-
- <p>An ODB view is a C++ <code>class</code> or <code>struct</code> type
- that embodies a light-weight, read-only projection of one or more
- persistent objects or database tables or the result of a native SQL
- query execution.</p>
-
- <p>Some of the common applications of views include loading a subset
- of data members from objects or columns from database tables, executing
- and handling results of arbitrary SQL queries, including aggregate
- queries and stored procedure calls, as well as joining multiple
- objects and/or database tables using object relationships or custom
- join conditions.</p>
-
- <p>Many relational databases also define the concept of views. Note,
- however, that ODB views are not mapped to database views. Rather,
- by default, an ODB view is mapped to an SQL <code>SELECT</code>
- query. However, if desired, it is easy to create an ODB view
- that is based on a database view.</p>
-
- <p>Usually, views are defined in terms of other persistent entities,
- such as persistent objects, database tables, sequences, etc.
- Therefore, before we can examine our first view, we need to
- define a few persistent objects and a database table. We will
- use this model in examples throughout this chapter. Here we
- assume that you are familiar with ODB object relationship
- support (<a href="#6">Chapter 6, "Relationships"</a>).</p>
-
- <pre class="cxx">
-#pragma db object
-class country
-{
- ...
-
- #pragma db id
- std::string code_; // ISO 2-letter country code.
-
- std::string name_;
-};
-
-#pragma db object
-class employer
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- std::string name_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id
- unsigned long id_;
-
- std::string first_;
- std::string last_;
-
- unsigned short age_;
-
- shared_ptr&lt;country> residence_;
- shared_ptr&lt;country> nationality_;
-
- shared_ptr&lt;employer> employed_by_;
-};
- </pre>
-
- <p>Besides these objects, we also have the legacy
- <code>employee_extra</code> table that is not mapped to any persistent
- class. It has the following definition:</p>
-
- <pre class="sql">
-CREATE TABLE employee_extra(
- employee_id INTEGER NOT NULL,
- vacation_days INTEGER NOT NULL,
- previous_employer_id INTEGER)
- </pre>
-
- <p>The above persistent objects and database table as well as many of
- the views shown in this chapter are based on the
- <code>view</code> example which can be found in the
- <code>odb-examples</code> package of the ODB distribution.</p>
-
- <p>To declare a view we use the <code>db&nbsp;view</code> pragma,
- for example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee)
-struct employee_name
-{
- std::string first;
- std::string last;
-};
- </pre>
-
- <p>The above example shows one of the simplest views that we can create.
- It has a single associated object (<code>employee</code>) and its
- purpose is to extract the employee's first and last names without
- loading any other data, such as the referenced <code>country</code>
- and <code>employer</code> objects.</p>
-
- <p>Views use the same query facility (<a href="#4">Chapter 4, "Querying
- the Database"</a>) as persistent objects. Because support for queries
- is optional and views cannot be used without this support, you need
- to compile any header that defines a view with the
- <code>--generate-query</code> ODB compiler option.</p>
-
- <p>To query the database for a view we use the
- <code>database::query()</code>, <code>database::query_one()</code>, or
- <code>database::query_value()</code> functions in exactly the same way
- as we would use them to query the database for an object. For example,
- the following code fragment shows how we can find the names of all the
- employees that are younger than 31:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee_name> query;
-typedef odb::result&lt;employee_name> result;
-
-transaction t (db.begin ());
-
-result r (db.query&lt;employee_name> (query::age &lt; 31));
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
-{
- const employee_name&amp; en (*i);
- cout &lt;&lt; en.first &lt;&lt; " " &lt;&lt; en.last &lt;&lt; endl;
-}
-
-t.commit ();
- </pre>
-
- <p>A view can be defined as a projection of one or more objects, one
- or more tables, a combination of objects and tables, or it can be
- the result of a custom SQL query. The following sections discuss each
- of these kinds of view in more detail.</p>
-
- <h2><a name="10.1">10.1 Object Views</a></h2>
-
- <p>To associate one or more objects with a view we use the
- <code>db&nbsp;object</code> pragma (<a href="#14.2.1">Section
- 14.2.1, "<code>object</code>"</a>). We have already seen
- a simple, single-object view in the introduction to this chapter.
- To associate the second and subsequent objects we repeat the
- <code>db&nbsp;object</code> pragma for each additional object,
- for example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) object(employer)
-struct employee_employer
-{
- std::string first;
- std::string last;
- std::string name;
-};
- </pre>
-
- <p>The complete syntax of the <code>db&nbsp;object</code> pragma is
- shown below:</p>
-
- <p><code><b>object(</b><i>name</i>
- [<b>=</b> <i>alias</i>]
- [<i>join-type</i>]
- [<b>:</b> <i>join-condition</i>]<b>)</b></code></p>
-
- <p>The <i>name</i> part is a potentially qualified persistent class
- name that has been defined previously. The optional <i>alias</i>
- part gives this object an alias. If provided, the alias is used
- in several contexts instead of the object's unqualified name. We
- will discuss aliases further as we cover each of these contexts
- below. The optional <i>join-type</i> part specifies the way this
- object is associated. It can be <code>left</code>, <code>right</code>,
- <code>full</code>, <code>inner</code>, and <code>cross</code>
- with <code>left</code> being the default.
- Finally, the optional <i>join-condition</i> part provides the
- criteria which should be used to associate this object with any
- of the previously associated objects or, as we will see in
- <a href="#10.4">Section 10.4, "Mixed Views"</a>, tables. Note that
- while the first associated object can have an alias, it cannot
- have a join type or condition.</p>
-
- <p>For each subsequent associated object the ODB compiler needs
- a join condition and there are several ways to specify
- it. The easiest way is to omit it altogether and let the ODB
- compiler try to come up with a join condition automatically.
- To do this the ODB compiler will examine each previously
- associated object for object relationships
- (<a href="#6">Chapter 6, "Relationships"</a>) that
- may exist between these objects and the object being associated.
- If such a relationship exists and is unambiguous, that is
- there is only one such relationship, then the ODB compiler
- will automatically use it to come up with the join condition for
- this object. This is exactly what happens in the previous
- example: there is a single relationship
- (<code>employee::employed_by</code>) between the
- <code>employee</code> and <code>employer</code> objects.</p>
-
- <p>On the other hand, consider this view:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) object(country)
-struct employee_residence
-{
- std::string first;
- std::string last;
- std::string name;
-};
- </pre>
-
- <p>While there is a relationship between <code>country</code> and
- <code>employee</code>, it is ambiguous. It can be
- <code>employee::residence_</code> (which is what we want) or
- it can be <code>employee::nationality_</code> (which we don't
- want). As result, when compiling the above view, the ODB
- compiler will issue an error indicating an ambiguous object
- relationship. To resolve this ambiguity, we can explicitly
- specify the object relationship that should be used to create
- the join condition as the name of the corresponding data member.
- Here is how we can fix the <code>employee_residence</code>
- view:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) object(country: employee::residence_)
-struct employee_residence
-{
- std::string first;
- std::string last;
- std::string name;
-};
- </pre>
-
- <p>It is possible to associate the same object with a single view
- more than once using different join conditions. However, in
- this case, we have to use aliases to assign different names
- for each association. For example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- object(country = res_country: employee::residence_) \
- object(country = nat_country: employee::nationality_)
-struct employee_country
-{
- ...
-};
- </pre>
-
- <p>Note that correctly defining data members in this view requires
- the use of a mechanism that we haven't yet covered. We will
- see how to do this shortly.</p>
-
- <p>If we assign an alias to an object and refer to a data member of
- this object in one of the join conditions, we have to use the
- unqualified alias name instead of the potentially qualified
- object name. For example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee = ee) object(country: ee::residence_)
-struct employee_residence
-{
- ...
-};
- </pre>
-
- <p>The last way to specify a join condition is to provide a custom
- query expression. This method is primarily useful if you would
- like to associate an object using a condition that does not
- involve an object relationship. Consider, for example, a
- modified <code>employee</code> object from the beginning of
- the chapter with an added country of birth member. For one
- reason or another we have decided not to use a relationship to
- the <code>country</code> object, as we have done with
- residence and nationality.</p>
-
- <pre class="cxx">
-#pragma db object
-class employee
-{
- ...
-
- std::string birth_place_; // Country name.
-};
- </pre>
-
- <p>If we now want to create a view that returns the birth country code
- for an employee, then we have to use a custom join condition when
- associating the <code>country</code> object. For example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- object(country: employee::birth_place_ == country::name_)
-struct employee_birth_code
-{
- std::string first;
- std::string last;
- std::string code;
-};
- </pre>
-
- <p>The syntax of the query expression in custom join conditions
- is the same as in the query facility used to query the database
- for objects (<a href="#4">Chapter 4, "Querying the Database"</a>)
- except that for query members, instead of using
- <code>odb::query&lt;object>::member</code> names, we refer directly
- to object members.</p>
-
- <p>Looking at the views we have defined so far, you may be wondering
- how the ODB compiler knows which view data members correspond to which
- object data members. While the names are similar, they are not exactly
- the same, for example <code>employee_name::first</code> and
- <code>employee::first_</code>.</p>
-
- <p>As with join conditions, when it comes to associating data members,
- the ODB compiler tries to do this automatically. It first searches
- all the associated objects for an exact name match. If no match is
- found, then the ODB compiler compares the so-called public names.
- A public name of a member is obtained by removing the common member
- name decorations, such as leading and trailing underscores, the
- <code>m_</code> prefix, etc. In both of these searches the ODB
- compiler also makes sure that the types of the two members are the
- same or compatible.</p>
-
- <p>If one of the above searches returned a match and it is unambiguous, that
- is there is only one match, then the ODB compiler will automatically
- associate the two members. On the other hand, if no match is found
- or the match is ambiguous, the ODB compiler will issue an error.
- To associate two differently-named members or to resolve an ambiguity,
- we can explicitly specify the member association using the
- <code>db&nbsp;column</code> pragma (<a href="#14.4.9">Section 14.4.9,
- "<code>column</code>"</a>). For example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) object(employer)
-struct employee_employer
-{
- std::string first;
- std::string last;
-
- #pragma db column(employer::name_)
- std::string employer_name;
-};
- </pre>
-
- <p>If an object data member specifies the SQL type with
- the <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section
- 14.4.3, "<code>type</code>"</a>), then this type is also used for
- the associated view data members.</p>
-
- <p>Note also that similar to join conditions, if we assign an alias to
- an object and refer to a data member of this object in one of the
- <code>db&nbsp;column</code> pragmas, then we have to use the
- unqualified alias name instead of the potentially qualified
- object name. For example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- object(country = res_country: employee::residence_) \
- object(country = nat_country: employee::nationality_)
-struct employee_country
-{
- std::string first;
- std::string last;
-
- #pragma db column(res_country::name_)
- std::string res_country_name;
-
- #pragma db column(nat_country::name_)
- std::string nat_country_name;
-};
- </pre>
-
- <p>Besides specifying just the object member, we can also specify a
- <em>+-expression</em> in the <code>db&nbsp;column</code> pragma. A
- +-expression consists of string literals and object
- member references connected using the <code>+</code> operator.
- It is primarily useful for defining aggregate views based on
- SQL aggregate functions, for example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee)
-struct employee_count
-{
- #pragma db column("count(" + employee::id_ + ")")
- std::size_t count;
-};
- </pre>
-
- <p>When querying the database for a view, we may want to provide
- additional query criteria based on the objects associated with
- this view. To support this a view defines query members for all
- the associated objects which allows us to refer to such objects'
- members using the <code>odb::query&lt;view>::member</code> expressions.
- This is similar to how we can refer to object members using the
- <code>odb::query&lt;object>::member</code> expressions when
- querying the database for an object. For example:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee_count> query;
-
-transaction t (db.begin ());
-
-// Find the number of employees with the Doe last name. Result of this
-// aggregate query contains only one element so use the query_value()
-// shortcut function.
-//
-employee_count ec (
- db.query_value&lt;employee_count> (query::last == "Doe"));
-
-cout &lt;&lt; ec.count &lt;&lt; endl;
-
-t.commit ();
- </pre>
-
- <p>In the above query we used the last name data member from the associated
- <code>employee</code> object to only count employees with the specific
- name.</p>
-
- <p>When a view has only one associated object, the query members
- corresponding to this object are defined directly in the
- <code>odb::query&lt;view></code> scope. For instance,
- in the above example, we referred to the last name member as
- <code>odb::query&lt;employee_count>::last</code>. However, if
- a view has multiple associated objects, then query members
- corresponding to each such object are defined in a nested
- scope named after the object. As an example, consider
- the <code>employee_employer</code> view again:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) object(employer)
-struct employee_employer
-{
- std::string first;
- std::string last;
-
- #pragma db column(employer::name_)
- std::string employer_name;
-};
- </pre>
-
- <p>Now, to refer to the last name data member from the <code>employee</code>
- object we use the
- <code>odb::query&lt;...>::employee::last</code> expression.
- Similarly, to refer to the employer name, we use the
- <code>odb::query&lt;...>::employer::name</code> expression.
- For example:</p>
-
- <pre class="cxx">
-typedef odb::result&lt;employee_employer> result;
-typedef odb::query&lt;employee_employer> query;
-
-transaction t (db.begin ());
-
-result r (db.query&lt;employee_employer> (
- query::employee::last == "Doe" &amp;&amp;
- query::employer::name == "Simple Tech Ltd"));
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
- cout &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->employer_name &lt;&lt; endl;
-
-t.commit ();
- </pre>
-
- <p>If we assign an alias to an object, then this alias is used to
- name the query members scope instead of the object name. As an
- example, consider the <code>employee_country</code> view again:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- object(country = res_country: employee::residence_) \
- object(country = nat_country: employee::nationality_)
-struct employee_country
-{
- ...
-};
- </pre>
-
- <p>And a query which returns all the employees that have the same
- country of residence and nationality:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee_country> query;
-typedef odb::result&lt;employee_country> result;
-
-transaction t (db.begin ());
-
-result r (db.query&lt;employee_country> (
- query::res_country::name == query::nat_country::name));
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
- cout &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->res_country_name &lt;&lt; endl;
-
-t.commit ();
- </pre>
-
- <p>Note also that unlike object query members, view query members do
- no support referencing members in related objects. For example,
- the following query is invalid:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee_name> query;
-typedef odb::result&lt;employee_name> result;
-
-transaction t (db.begin ());
-
-result r (db.query&lt;employee_name> (
- query::employed_by->name == "Simple Tech Ltd"));
-
-t.commit ();
- </pre>
-
- <p>To get this behavior, we would instead need to associate the
- <code>employer</code> object with this view and then use the
- <code>query::employer::name</code> expression instead of
- <code>query::employed_by->name</code>.</p>
-
- <p>As we have discussed above, if specified, an object alias is
- used instead of the object name in the join condition, data
- member references in the <code>db&nbsp;column</code> pragma,
- as well as to name the query members scope. The object alias
- is also used as a table name alias in the underlying
- <code>SELECT</code> statement generated by the ODB compiler.
- Normally, you would not use the table alias directly with
- object views. However, if for some reason you need to refer
- to a table column directly, for example, as part of a native
- query expression, and you need to qualify the column with
- the table, then you will need to use the table alias instead.</p>
-
- <h2><a name="10.2">10.2 Object Loading Views</a></h2>
-
- <p>A special variant of object views is object loading views. Object
- loading views allow us to load one or more complete objects
- instead of, or in addition to, a subset of data member. While we
- can often achieve the same end result by calling
- <code>database::load()</code>, using a view has several advantages.</p>
-
- <p>If we need to load multiple objects, then using a view allows us
- to do this with a single <code>SELECT</code> statement execution
- instead of one for each object that would be necessary in case of
- <code>load()</code>. A view can also be useful for loading only
- a single object if the query criterion that we would like to use
- involves other, potentially unrelated, objects. We will examine
- concrete examples of these and other scenarios in the rest of this
- section.</p>
-
- <p>To load a complete object as part of a view we use a data member of
- the pointer to object type, just like for object relationships
- (<a href="#6">Chapter 6, "Relationships"</a>). As an example, here
- is how we can load both the <code>employee</code> and
- <code>employer</code> objects from the previous section with a single
- statement:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) object(employer)
-struct employee_employer
-{
- shared_ptr&lt;employee> ee;
- shared_ptr&lt;employer> er;
-};
- </pre>
-
- <p>We use an object loading view just like any other view. In the
- result of a query, as we would expect, the pointer data members
- point to the loaded objects. For example:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee_employer> query;
-
-transaction t (db.begin ());
-
-for (const employee_employer&amp; r:
- db.query&lt;employee_employer> (query::employee::age &lt; 31))
-{
- cout &lt;&lt; r.ee->age () &lt;&lt; " " &lt;&lt; r.er->name () &lt;&lt; endl;
-}
-
-t.commit ();
- </pre>
-
- <p>As another example, consider a query that loads the <code>employer</code>
- objects using some condition based on its employees. For instance, we
- want to find all the employers that employ people over 65 years old.
- We can use this object loading view to implement such a query (notice
- the <code>distinct</code> result modifier discussed later in
- <a href="#10.5">Section 10.5, "View Query Conditions"</a>):</p>
-
- <pre class="cxx">
-#pragma db view object(employer) object(employee) query(distinct)
-struct employer_view
-{
- shared_ptr&lt;employer> er;
-};
- </pre>
-
- <p>And this is how we can use this view to find all the employers that
- employ seniors:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employer_view> query;
-
-db.query&lt;employer_view> (query::employee::age > 65)
- </pre>
-
- <p>We can even use object loading views to load completely unrelated
- (from the ODB object relationships point of view) objects. For example,
- the following view will load all the employers that are named the
- same as a country (notice the <code>inner</code> join type):</p>
-
- <pre class="cxx">
-#pragma db view object(employer) \
- object(country inner: employer::name == country::name)
-struct employer_named_country
-{
- shared_ptr&lt;employer> e;
- shared_ptr&lt;country> c;
-};
- </pre>
-
- <p>An object loading view can contain ordinary data members
- in addition to object pointers. For example, if we are only
- interested in the country code in the above view, then we
- can reimplement it like this:</p>
-
- <pre class="cxx">
-#pragma db view object(employer) \
- object(country inner: employer::name == country::name)
-struct employer_named_country
-{
- shared_ptr&lt;employer> e;
- std::string code;
-};
- </pre>
-
- <p>Object loading views also have a few rules and restrictions.
- Firstly, the pointed-to object in the data member must be associated
- with the view. Furthermore, if the associated object has an alias,
- then the data member name must be the same as the alias (more
- precisely, the public name derived from the data member must
- match the alias; which means we can use normal data member
- decorations such as trailing underscores, etc., see the previous
- section for more information on public names). The following view
- illustrates the use of aliases as data member names:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- object(country = res: employee::residence_) \
- object(country = nat: employee::nationality_)
-struct employee_country
-{
- shared_ptr&lt;country> res;
- shared_ptr&lt;country> nat_;
-};
- </pre>
-
- <p>Finally, the object pointers must be direct data members of
- the view. Using, for example, a composite value that contains
- pointers as a view data member is not supported. Note also
- that depending on the join type you are using, some of the
- resulting pointers might be <code>NULL</code>.</p>
-
- <p>Up until now we have consistently used <code>shared_ptr</code>
- as an object pointer in our views. Can we use other pointers,
- such as <code>unique_ptr</code> or raw pointers? To answer
- this question we first need to discuss what happens with
- object pointers that may be inside objects that a view
- loads. As a concrete example, let us revisit the
- <code>employee_employer</code> view from the beginning of
- this section:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) object(employer)
-struct employee_employer
-{
- shared_ptr&lt;employee> ee;
- shared_ptr&lt;employer> er;
-};
- </pre>
-
- <p>This view loads two objects: <code>employee</code> and
- <code>employer</code>. The <code>employee</code> object,
- however, also contains a pointer to <code>employer</code>
- (see the <code>employed_by_</code> data member). In fact,
- this is the same object that the view loads since <code>employer</code>
- is associated with the view using this same relationship (ODB
- automatically uses it since it is the only one). The correct
- result of loading such a view is then clear: both <code>er</code> and
- <code>er->employed_by_</code> must point to (or share) the
- same instance.</p>
-
- <p>Just like object loading via the <code>database</code> class
- functions, views achieve this correct behavior of only loading
- a single instance of the same object with the help of session's
- object cache (<a href="#11">Chapter 11, "Session"</a>). In fact,
- object loading views enforce this by throwing the
- <code>session_required</code> exception if there is no current
- session and the view loads an object that is also indirectly
- loaded by one of the other objects. The ODB compiler will also
- issue diagnostics if such an object has session support
- disabled (<a href="#14.1.10">Section 14.1.10,
- "<code>session</code>"</a>).</p>
-
- <p>With this understanding we can now provide the correct implementation
- of our transaction that uses the <code>employee_employer</code> view:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee_employer> query;
-
-transaction t (db.begin ());
-odb::session s;
-
-for (const employee_employer&amp; r:
- db.query&lt;employee_employer> (query::employee::age &lt; 31))
-{
- assert (r.ee->employed_by_ == r.er);
- cout &lt;&lt; r.ee->age () &lt;&lt; " " &lt;&lt; r.er->name () &lt;&lt; endl;
-}
-
-t.commit ();
- </pre>
-
- <p>It might seem logical, then, to always load all the objects from
- all the eager relationships with the view. After all, this will
- lead to them all being loaded with a single statement. While
- this is theoretically true, the reality is slightly more nuanced.
- If there is a high probability of the object already have been
- loaded and sitting in the cache, then not loading the object
- as part of the view (and therefore not fetching all its data
- from the database) might result in better performance.</p>
-
- <p>Now we can also answer the question about which pointers we can
- use in object loading views. From the above discussion it should
- be clear that if an object that we are loading is also part of a
- relationship inside another object that we are loading, then we
- should use some form of a shared ownership pointer. If, however,
- there are no relationships involved, as is the case, for example,
- in our <code>employer_named_country</code> and
- <code>employee_country</code> views above, then we can use a
- unique ownership pointer such as <code>unique_ptr</code>.</p>
-
- <p>Note also that your choice of a pointer type can be limited by the
- "official" object pointer type assigned to the object
- (<a href="#3.3">Section 3.3, "Object and View Pointers"</a>).
- For example, if the object pointer type is <code>shared_ptr</code>,
- you will not be able to use <code>unique_ptr</code> to load
- such an object into a view since initializing <code>unique_ptr</code>
- from <code>shared_ptr</code> would be a mistake.</p>
-
- <p>Unless you want to perform your own object cleanup, raw object
- pointers in views are not particularly useful. They do have one
- special semantics, however: If a raw pointer is used as a view
- member, then, before creating a new instance, the implementation
- will check if the member is <code>NULL</code>. If it is not, then
- it is assumed to point to an existing instance and the implementation
- will load the data into it instead of creating a new one. The
- primary use of this special functionality is to implement by-value
- loading with the ability to detect <code>NULL</code> values.</p>
-
- <p>To illustrate this functionality, consider the following view that
- load the employee's residence country by value:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- object(country = res: employee::residence_) transient
-struct employee_res_country
-{
- typedef country* country_ptr;
-
- #pragma db member(res_) virtual(country_ptr) get(&amp;this.res) \
- set(this.res_null = ((?) == nullptr))
-
- country res;
- bool res_null;
-};
- </pre>
-
- <p>Here we are using a virtual data member
- (<a href="#14.4.13">Section 14.4.13, "<code>virtual</code>"</a>) to
- add an object pointer member to the view. Its accessor expression
- returns the pointer to the <code>res</code> member so that
- the implementation can load the data into it. The modifier
- expression checks the passed pointer to initialize the
- <code>NULL</code> value indicator. Here, the two possible
- values that can be passed to the modifier expression are
- the address of the <code>res</code> member that we returned
- earlier from the accessor and <code>NULL</code> (strictly
- speaking, there is a third possibility: the address of an
- object that was found in the session cache).</p>
-
- <p>If we are not interested in the <code>NULL</code> indicator,
- then the above view can simplified to this:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- object(country = res: employee::residence_) transient
-struct employee_res_country
-{
- typedef country* country_ptr;
-
- #pragma db member(res_) virtual(country_ptr) get(&amp;this.res) set()
-
- country res;
-};
- </pre>
-
- <p>That is, we specify an empty modifier expression which leads to
- the value being ignored.</p>
-
- <p>As another example of by-value loading, consider a view that allows
- us to load objects into existing instances that have been allocated
- outside the view:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- object(country = res: employee::residence_) \
- object(country = nat: employee::nationality_)
-struct employee_country
-{
- employee_country (country&amp; r, country&amp; n): res (&amp;r), nat (&amp;n) {}
-
- country* res;
- country* nat;
-};
- </pre>
-
- <p>And here is how we can use this view:</p>
-
- <pre class="cxx">
-typedef odb::result&lt;employee_country> result;
-
-transaction t (db.begin ());
-
-result r (db.query&lt;employee_country> (...);
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
-{
- country res, nat;
- employee_country v (res, nat);
- i.load (v);
-
- if (v.res != nullptr)
- ... // Result is in res.
-
- if (v.nat != nullptr)
- ... // Result is in nat.
-}
-
-t.commit ();
- </pre>
-
- <p>As a final example of the by-value loading, consider the following
- view which implements a slightly more advanced logic: if the object
- is already in the session cache, then it sets the pointer data member
- in the view (<code>er_p</code>) to that. Otherwise, it loads the data
- into the by-value instance (<code>er</code>). We can also check
- whether the pointer data member points to the instance to distinguish
- between the two outcomes. And we can check it for <code>nullptr</code>
- to detect <code>NULL</code> values.</p>
-
- <pre class="cxx">
-#pragma db view object(employer)
-struct employer_view
-{
- // Since we may be getting the pointer as both smart and raw, we
- // need to create a bit of support code to use in the modifier
- // expression.
- //
- void set_er (employer* p) {er_p = p;} // &amp;er or NULL.
- void set_er (shared_ptr&lt;employer> p) {er_p = p.get ();} // From cache.
-
- #pragma db get(&amp;this.er) set(set_er(?))
- employer* er_p;
-
- #pragma db transient
- employer er;
-
- // Return-by-value support (e.g., query_value()).
- //
- employer_view (): er_p (0) {}
- employer_view (const employer_view&amp; x)
- : er_p (x.er_p == &amp;x.er ? &amp;er : x.er_p), er (x.er) {}
-};
- </pre>
-
- <p>We can use object loading views with polymorphic objects
- (<a href="#8.2">Section 8.2, "Polymorphism Inheritance"</a>). Note,
- however, that when loading a derived object via the base pointer
- in a view, a separate statement will be executed to load the
- dynamic part of the object. There is no support for by-value
- loading for polymorphic objects.</p>
-
- <p>We can also use object loading views with objects without id
- (<a href="#14.1.6">Section 14.1.6, "<code>no_id</code>"</a>).
- Note, however, that for such objects, <code>NULL</code> values
- are not automatically detected (since there is no primary key,
- which is otherwise guaranteed to be not <code>NULL</code>, there
- might not be a column on which to base this detection). The
- workaround for this limitation is to load an otherwise not
- <code>NULL</code> column next to the object which will serve
- as an indicator. For example:</p>
-
- <pre class="cxx">
-#pragma db object no_id
-class object
-{
- ...
-
- int n; // NOT NULL
- std::string s;
-};
-
-#include &lt;odb/nullable.hxx>
-
-#pragma db view object(object)
-struct view
-{
-
- odb::nullable&lt;int> n; // If 'n' is NULL, then, logically, so is 'o'.
- unique_ptr&lt;object> o;
-};
- </pre>
-
- <h2><a name="10.3">10.3 Table Views</a></h2>
-
- <p>A table view is similar to an object view except that it is
- based on one or more database tables instead of persistent
- objects. Table views are primarily useful when dealing with
- ad-hoc tables that are not mapped to persistent classes.</p>
-
- <p>To associate one or more tables with a view we use the
- <code>db&nbsp;table</code> pragma (<a href="#14.2.2">Section 14.2.2,
- "<code>table</code>"</a>). To associate the second and subsequent
- tables we repeat the <code>db&nbsp;table</code> pragma for each
- additional table. For example, the following view is based on the
- <code>employee_extra</code> legacy table we have defined at the
- beginning of the chapter.</p>
-
- <pre class="cxx">
-#pragma db view table("employee_extra")
-struct employee_vacation
-{
- #pragma db column("employee_id") type("INTEGER")
- unsigned long employee_id;
-
- #pragma db column("vacation_days") type("INTEGER")
- unsigned short vacation_days;
-};
- </pre>
-
- <p>Besides the table name in the <code>db&nbsp;table</code> pragma
- we also have to specify the column name for each view data
- member. Note that unlike for object views, the ODB compiler
- does not try to automatically come up with column names for
- table views. Furthermore, we cannot use references to object
- members either, since there are no associated objects in table
- views. Instead, the actual column name or column expression
- must be specified as a string literal. The column name can
- also be qualified with a table name either in the
- <code>"table.column"</code> form or, if either a table
- or a column name contains a period, in the
- <code>"table"."column"</code> form. The following example
- illustrates the use of a column expression:</p>
-
- <pre class="cxx">
-#pragma db view table("employee_extra")
-struct employee_max_vacation
-{
- #pragma db column("max(vacation_days)") type("INTEGER")
- unsigned short max_vacation_days;
-};
- </pre>
-
- <p>Both the associated table names and the column names can be qualified
- with a database schema, for example:</p>
-
- <pre class="cxx">
-#pragma db view table("hr.employee_extra")
-struct employee_max_vacation
-{
- #pragma db column("hr.employee_extra.vacation_days") type("INTEGER")
- unsigned short vacation_days;
-};
- </pre>
-
- <p>For more information on database schemas and the format of the
- qualified names, refer to <a href="#14.1.8">Section 14.1.8,
- "<code>schema</code>"</a>.</p>
-
- <p>Note also that in the above examples we specified the SQL type
- for each of the columns to make sure that the ODB compiler
- has knowledge of the actual types as specified in the database
- schema. This is required to obtain correct and optimal
- generated code.</p>
-
-
- <p>The complete syntax of the <code>db&nbsp;table</code> pragma
- is similar to the <code>db&nbsp;object</code> pragma and is shown
- below:</p>
-
- <p><code><b>table("</b><i>name</i><b>"</b>
- [<b>=</b> <b>"</b><i>alias</i><b>"</b>]
- [<i>join-type</i>]
- [<b>:</b> <i>join-condition</i>]<b>)</b></code></p>
-
- <p>The <i>name</i> part is a database table name. The optional
- <i>alias</i> part gives this table an alias. If provided, the
- alias must be used instead of the table whenever a reference
- to a table is used. Contexts where such a reference may
- be needed include the join condition (discussed below),
- column names, and query expressions. The optional <i>join-type</i>
- part specifies the way this table is associated. It can
- be <code>left</code>, <code>right</code>, <code>full</code>,
- <code>inner</code>, and <code>cross</code> with <code>left</code>
- being the default. Finally, the optional <i>join-condition</i>
- part provides the criteria which should be used to associate this
- table with any of the previously associated tables or, as we will see in
- <a href="#10.4">Section 10.4, "Mixed Views"</a>, objects. Note that
- while the first associated table can have an alias, it cannot have
- a join type or condition.</p>
-
- <p>Similar to object views, for each subsequent associated table the
- ODB compiler needs a join condition. However, unlike for object views,
- for table views the ODB compiler does not try to come up with one
- automatically. Furthermore, we cannot use references to object
- members corresponding to object relationships either, since there
- are no associated objects in table views. Instead, for each
- subsequent associated table, a join condition must be
- specified as a custom query expression. While the syntax of the
- query expression is the same as in the query facility used to query
- the database for objects (<a href="#4">Chapter 4, "Querying the
- Database"</a>), a join condition for a table is normally specified
- as a single string literal containing a native SQL query expression.</p>
-
- <p>As an example of a multi-table view, consider the
- <code>employee_health</code> table that we define in addition
- to <code>employee_extra</code>:</p>
-
- <pre class="sql">
-CREATE TABLE employee_health(
- employee_id INTEGER NOT NULL,
- sick_leave_days INTEGER NOT NULL)
- </pre>
-
- <p>Given these two tables we can now define a view that returns both
- the vacation and sick leave information for each employee:</p>
-
- <pre class="cxx">
-#pragma db view table("employee_extra" = "extra") \
- table("employee_health" = "health": \
- "extra.employee_id = health.employee_id")
-struct employee_leave
-{
- #pragma db column("extra.employee_id") type("INTEGER")
- unsigned long employee_id;
-
- #pragma db column("vacation_days") type("INTEGER")
- unsigned short vacation_days;
-
- #pragma db column("sick_leave_days") type("INTEGER")
- unsigned short sick_leave_days;
-};
- </pre>
-
- <p>Querying the database for a table view is the same as for an
- object view except that we can only use native query expressions.
- For example:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee_leave> query;
-typedef odb::result&lt;employee_leave> result;
-
-transaction t (db.begin ());
-
-unsigned short v_min = ...
-unsigned short l_min = ...
-
-result r (db.query&lt;employee_leave> (
- "vacation_days > " + query::_val(v_min) + "AND"
- "sick_leave_days > " + query::_val(l_min)));
-
-t.commit ();
- </pre>
-
-
- <h2><a name="10.4">10.4 Mixed Views</a></h2>
-
- <p>A mixed view has both associated objects and tables. As a first
- example of a mixed view, let us improve <code>employee_vacation</code>
- from the previous section to return the employee's first
- and last names instead of the employee id. To achieve this we
- have to associate both the <code>employee</code> object and
- the <code>employee_extra</code> table with the view:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- table("employee_extra" = "extra": "extra.employee_id = " + employee::id_)
-struct employee_vacation
-{
- std::string first;
- std::string last;
-
- #pragma db column("extra.vacation_days") type("INTEGER")
- unsigned short vacation_days;
-};
- </pre>
-
- <p>When querying the database for a mixed view, we can use query members
- for the parts of the query expression that involves object members
- but have to fall back to using the native syntax for the parts that
- involve table columns. For example:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;employee_vacation> query;
-typedef odb::result&lt;employee_vacation> result;
-
-transaction t (db.begin ());
-
-result r (db.query&lt;employee_vacation> (
- (query::last == "Doe") + "AND extra.vacation_days &lt;> 0"));
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
- cout &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->vacation_days &lt;&lt; endl;
-
-t.commit ();
- </pre>
-
- <p>As another example, consider a more advanced view that associates
- two objects via a legacy table. This view allows us to find the
- previous employer name for each employee:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) \
- table("employee_extra" = "extra": "extra.employee_id = " + employee::id_) \
- object(employer: "extra.previous_employer_id = " + employer::id_)
-struct employee_prev_employer
-{
- std::string first;
- std::string last;
-
- // If previous_employer_id is NULL, then the name will be NULL as well.
- // We use the odb::nullable wrapper to handle this.
- //
- #pragma db column(employer::name_)
- odb::nullable&lt;std::string> prev_employer_name;
-};
- </pre>
-
- <h2><a name="10.5">10.5 View Query Conditions</a></h2>
-
- <p>Object, table, and mixed views can also specify an optional query
- condition that should be used whenever the database is queried for
- this view. To specify a query condition we use the
- <code>db&nbsp;query</code> pragma (<a href="#14.2.3">Section 14.2.3,
- "<code>query</code>"</a>).</p>
-
- <p>As an example, consider a view that returns some information about
- all the employees that are over a predefined retirement age.
- One way to implement this would be to define a standard object
- view as we have done in the previous sections and then use a
- query like this:</p>
-
- <pre class="cxx">
-result r (db.query&lt;employee_retirement> (query::age > 50));
- </pre>
-
- <p>The problem with the above approach is that we have to keep
- repeating the <code>query::age > 50</code> expression every
- time we execute the query, even though this expression always
- stays the same. View query conditions allow us to solve this
- problem. For example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) query(employee::age > 50)
-struct employee_retirement
-{
- std::string first;
- std::string last;
- unsigned short age;
-};
- </pre>
-
- <p>With this improvement we can rewrite our query like this:</p>
-
- <pre class="cxx">
-result r (db.query&lt;employee_retirement> ());
- </pre>
-
- <p>But what if we may also need to restrict the result set based on
- some varying criteria, such as the employee's last name? Or, in other
- words, we may need to combine a constant query expression specified
- in the <code>db&nbsp;query</code> pragma with the varying expression
- specified at the query execution time. To allow this, the
- <code>db&nbsp;query</code> pragma syntax supports the use of the special
- <code>(?)</code> placeholder that indicates the position in the
- constant query expression where the runtime expression should be
- inserted. For example:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) query(employee::age > 50 &amp;&amp; (?))
-struct employee_retirement
-{
- std::string first;
- std::string last;
- unsigned short name;
-};
- </pre>
-
- <p>With this change we can now use additional query criteria in our
- view:</p>
-
- <pre class="cxx">
-result r (db.query&lt;employee_retirement> (query::last == "Doe"));
- </pre>
-
- <p>The syntax of the expression in a query condition is the same as in
- the query facility used to query the database for objects
- (<a href="#4">Chapter 4, "Querying the Database"</a>) except for
- two differences. Firstly, for query members, instead of
- using <code>odb::query&lt;object>::member</code> names, we refer
- directly to object members, using the object alias instead of the
- object name if an alias was assigned. Secondly, query conditions
- support the special <code>(?)</code> placeholder which can be used
- both in the C++-integrated query expressions as was shown above
- and in native SQL expressions specified as string literals. The
- following view is an example of the latter case:</p>
-
- <pre class="cxx">
-#pragma db view table("employee_extra") \
- query("vacation_days &lt;> 0 AND (?)")
-struct employee_vacation
-{
- ...
-};
- </pre>
-
- <p>Another common use case for query conditions are views with the
- <code>ORDER BY</code> or <code>GROUP BY</code> clause. Such
- clauses are normally present in the same form in every query
- involving such views. As an example, consider an aggregate
- view which calculate the minimum and maximum ages of employees
- for each employer:</p>
-
- <pre class="cxx">
-#pragma db view object(employee) object(employer) \
- query((?) + "GROUP BY" + employer::name_)
-struct employer_age
-{
- #pragma db column(employer::name_)
- std::string employer_name;
-
- #pragma db column("min(" + employee::age_ + ")")
- unsigned short min_age;
-
- #pragma db column("max(" + employee::age_ + ")")
- unsigned short max_age;
-};
- </pre>
-
- <p>The query condition can be optionally followed (or replaced,
- if no constant query expression is needed) by one or more
- <em>result modifiers</em>. Currently supported result modifiers
- are <code>distinct</code> (which is translated to <code>SELECT
- DISTINCT</code>) and <code>for_update</code> (which is translated
- to <code>FOR UPDATE</code> or equivalent for database systems
- that support it). As an example, consider a view that
- allows us to get some information about employers ordered
- by the object id and without any duplicates:</p>
-
- <pre class="cxx">
-#pragma db view object(employer) object(employee) \
- query((?) + "ORDER BY" + employer::name_, distinct)
-struct employer_info
-{
- ...
-};
- </pre>
-
- <p>If we don't require ordering, then this view can be re-implemented
- like this:</p>
-
- <pre class="cxx">
-#pragma db view object(employer) object(employee) query(distinct)
-struct employer_info
-{
- ...
-};
- </pre>
-
- <h2><a name="10.6">10.6 Native Views</a></h2>
-
- <p>The last kind of view supported by ODB is a native view. Native
- views are a low-level mechanism for capturing results of native
- SQL queries, stored procedure calls, etc. Native views don't have
- associated tables or objects. Instead, we use the
- <code>db&nbsp;query</code> pragma to specify the native SQL query,
- which should normally include the select-list and, if applicable,
- the from-list. For example, here is how we can re-implement the
- <code>employee_vacation</code> table view from Section 10.3 above
- as a native view:</p>
-
- <pre class="cxx">
-#pragma db view query("SELECT employee_id, vacation_days " \
- "FROM employee_extra")
-struct employee_vacation
-{
- #pragma db type("INTEGER")
- unsigned long employee_id;
-
- #pragma db type("INTEGER")
- unsigned short vacation_days;
-};
- </pre>
-
- <p>In native views the columns in the query select-list are
- associated with the view data members in the order specified.
- That is, the first column is stored in the first member, the
- second column &mdash; in the second member, and so on. The ODB compiler
- does not perform any error checking in this association. As a result
- you must make sure that the number and order of columns in the
- query select-list match the number and order of data members
- in the view. This is also the reason why we are not
- required to provide the column name for each data member in native
- views, as is the case for object and table views.</p>
-
- <p>Note also that while it is always possible to implement a table
- view as a native view, the table views must be preferred since
- they are safer. In a native view, if you add, remove, or
- rearrange data members without updating the column list in the
- query, or vice versa, at best, this will result in a runtime
- error. In contrast, in a table view such changes will result
- in the query being automatically updated.</p>
-
- <p>Similar to object and table views, the query specified for
- a native view can contain the special <code>(?)</code>
- placeholder which is replaced with the query expression
- specified at the query execution time.
- If the native query does not contain a placeholder, as in
- the example above, then any query expression specified at
- the query execution time is appended to the query text
- along with the <code>WHERE</code> keyword, if required.
- The following example shows the usage of the placeholder:</p>
-
- <pre class="cxx">
-#pragma db view query("SELECT employee_id, vacation_days " \
- "FROM employee_extra " \
- "WHERE vacation_days &lt;> 0 AND (?)")
-struct employee_vacation
-{
- ...
-};
- </pre>
-
- <p>As another example, consider a view that returns the next
- value of a database sequence:</p>
-
- <pre class="cxx">
-#pragma db view query("SELECT nextval('my_seq')")
-struct sequence_value
-{
- unsigned long long value;
-};
- </pre>
-
- <p>While this implementation can be acceptable in some cases, it has
- a number of drawbacks. Firstly, the name of the sequence is
- fixed in the view, which means if we have a second sequence, we
- will have to define another, almost identical view. Similarly,
- the operation that we perform on the sequence is also fixed.
- In some situations, instead of returning the next value, we may
- need the last value.</p>
-
- <p>Note that we cannot use the placeholder mechanism to resolve
- these problems since placeholders can only be used in the
- <code>WHERE</code>, <code>GROUP BY</code>, and similar
- clauses. In other words, the following won't work:</p>
-
- <pre class="cxx">
-#pragma db view query("SELECT nextval('(?)')")
-struct sequence_value
-{
- unsigned long long value;
-};
-
-result r (db.query&lt;sequence_value> ("my_seq"));
- </pre>
-
- <p>To support these kinds of use cases, ODB allows us to specify the
- complete query for a native view at runtime rather than at the view
- definition. To indicate that a native view has a runtime query,
- we can either specify the empty <code>db&nbsp;query</code>
- pragma or omit the pragma altogether. For example:</p>
-
- <pre class="cxx">
-#pragma db view
-struct sequence_value
-{
- unsigned long long value;
-};
- </pre>
-
- <p>Given this view, we can perform the following queries:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;sequence_value> query;
-typedef odb::result&lt;sequence_value> result;
-
-string seq_name = ...
-
-result l (db.query&lt;sequence_value> (
- "SELECT lastval('" + seq_name + "')"));
-
-result n (db.query&lt;sequence_value> (
- "SELECT nextval('" + seq_name + "')"));
- </pre>
-
- <p>Native views can also be used to call and handle results of
- stored procedures. The semantics and limitations of stored
- procedures vary greatly between database systems while some
- do not support this functionality at all. As a result, support
- for calling stored procedures using native views is described
- for each database system in <a href="#II">Part II, "Database
- Systems"</a>.</p>
-
- <h2><a name="10.7">10.7 Other View Features and Limitations</a></h2>
-
- <p>Views cannot be derived from other views. However, you can derive
- a view from a transient C++ class. View data members cannot be
- object pointers. If you need to access data from a pointed-to
- object, then you will need to associate such an object with
- the view. Similarly, view data members cannot be containers.
- These two limitations also apply to composite value types that
- contain object pointers or containers. Such composite values
- cannot be used as view data members.</p>
-
- <p>On the other hand, composite values that do not contain object
- pointers or containers can be used in views. As an example,
- consider a modified version of the <code>employee</code> persistent
- class that stores a person's name as a composite value:</p>
-
- <pre class="cxx">
-#pragma db value
-class person_name
-{
- std::string first_;
- std::string last_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- person_name name_;
-
- ...
-};
- </pre>
-
- <p>Given this change, we can re-implement the <code>employee_name</code>
- view like this:</p>
-
- <pre class="cxx">
-#pragma db view object(employee)
-struct employee_name
-{
- person_name name;
-};
- </pre>
-
- <p>It is also possible to extract some or all of the nested members
- of a composite value into individual view data members. Here is
- how we could have defined the <code>employee_name</code> view
- if we wanted to keep its original structure:</p>
-
- <pre class="cxx">
-#pragma db view object(employee)
-struct employee_name
-{
- #pragma db column(employee::name.first_)
- std::string first;
-
- #pragma db column(employee::name.last_)
- std::string last;
-};
- </pre>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="11">11 Session</a></h1>
-
- <p>A session is an application's unit of work that may encompass several
- database transactions. In this version of ODB a session is just an
- object cache. In future versions it may provide additional
- functionality, such as delayed database operations and automatic
- object state change tracking. As discussed later in
- <a href="#11.2">Section 11.2, "Custom Sessions"</a>, it is also
- possible to provide a custom session implementation that provides
- these or other features.</p>
-
- <p>Session support is optional and can be enabled or disabled on the
- per object basis using the <code>db&nbsp;session</code> pragma, for
- example:</p>
-
- <pre class="cxx">
-#pragma db object session
-class person
-{
- ...
-};
- </pre>
-
- <p>We can also enable or disable session support for a group of
- objects at the namespace level:</p>
-
- <pre class="cxx">
-#pragma db namespace session
-namespace accounting
-{
- #pragma db object // Session support is enabled.
- class employee
- {
- ...
- };
-
- #pragma db object session(false) // Session support is disabled.
- class employer
- {
- ...
- };
-}
- </pre>
-
- <p>Finally, we can pass the <code>--generate-session</code> ODB compiler
- option to enable session support by default. With this option session
- support will be enabled for all the persistent classes except those
- for which it was explicitly disabled using the
- <code>db&nbsp;session</code>. An alternative to this method with the
- same effect is to enable session support for the global namespace:</p>
-
- <pre class="cxx">
-#pragma db namespace() session
- </pre>
-
- <p>Each thread of execution in an application can have only one active
- session at a time. A session is started by creating an instance of
- the <code>odb::session</code> class and is automatically terminated
- when this instance is destroyed. You will need to include the
- <code>&lt;odb/session.hxx></code> header file to make this class
- available in your application. For example:</p>
-
- <pre class="cxx">
-#include &lt;odb/database.hxx>
-#include &lt;odb/session.hxx>
-#include &lt;odb/transaction.hxx>
-
-using namespace odb::core;
-
-{
- session s;
-
- // First transaction.
- //
- {
- transaction t (db.begin ());
- ...
- t.commit ();
- }
-
- // Second transaction.
- //
- {
- transaction t (db.begin ());
- ...
- t.commit ();
- }
-
- // Session 's' is terminated here.
-}
- </pre>
-
- <p>The <code>session</code> class has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- class session
- {
- public:
- session (bool make_current = true);
- ~session ();
-
- // Copying or assignment of sessions is not supported.
- //
- private:
- session (const session&amp;);
- session&amp; operator= (const session&amp;);
-
- // Current session interface.
- //
- public:
- static session&amp;
- current ();
-
- static bool
- has_current ();
-
- static void
- current (session&amp;);
-
- static void
- reset_current ();
-
- static session*
- current_pointer ();
-
- static void
- current_pointer (session*);
-
- // Object cache interface.
- //
- public:
- template &lt;typename T>
- struct cache_position {...};
-
- template &lt;typename T>
- cache_position&lt;T>
- cache_insert (database&amp;,
- const object_traits&lt;T>::id_type&amp;,
- const object_traits&lt;T>::pointer_type&amp;);
-
- template &lt;typename T>
- object_traits&lt;T>::pointer_type
- cache_find (database&amp;, const object_traits&lt;T>::id_type&amp;) const;
-
- template &lt;typename T>
- void
- cache_erase (const cache_position&lt;T>&amp;);
-
- template &lt;typename T>
- void
- cache_erase (database&amp;, const object_traits&lt;T>::id_type&amp;);
- };
-}
- </pre>
-
- <p>The session constructor creates a new session and, if the
- <code>make_current</code> argument is <code>true</code>, sets it as a
- current session for this thread. If we try to make a session current
- while there is already another session in effect for this thread,
- then the constructor throws the <code>odb::already_in_session</code>
- exception. The destructor clears the current session for this
- thread if this session is the current one.</p>
-
- <p>The static <code>current()</code> accessor returns the currently active
- session for this thread. If there is no active session, this function
- throws the <code>odb::not_in_session</code> exception. We can check
- whether there is a session in effect in this thread using the
- <code>has_current()</code> static function.</p>
-
- <p>The static <code>current()</code> modifier allows us to set the
- current session for this thread. The <code>reset_current()</code>
- static function clears the current session. These two functions
- allow for more advanced use cases, such as multiplexing
- two or more sessions on the same thread.</p>
-
- <p>The static <code>current_pointer()</code> overloaded functions
- provided the same functionality but using pointers. Specifically,
- the <code>current_pointer()</code> accessor can be used to
- test whether there is a current session and get a pointer to it
- all with a single call.</p>
-
- <p>We normally don't use the object cache interface directly. However,
- it could be useful in some cases, for example, to find out whether
- an object has already been loaded. Note that when calling
- <code>cache_insert()</code>, <code>cache_find()</code>, or
- the second version of <code>cache_erase()</code>, you need to
- specify the template argument (object type) explicitly. It is
- also possible to access the underlying cache data structures
- directly. This can be useful if, for example, you want to
- iterate over the objects store in the cache. Refer to the ODB
- runtime header files for more details on this direct access.</p>
-
- <h2><a name="11.1">11.1 Object Cache</a></h2>
-
- <p>A session is an object cache. Every time a session-enabled object is
- made persistent by calling the <code>database::persist()</code> function
- (<a href="#3.8">Section 3.8, "Making Objects Persistent"</a>), loaded
- by calling the <code>database::load()</code> or
- <code>database::find()</code> function (the pointer-returning overloads
- only; <a href="#3.9">Section 3.9, "Loading Persistent Objects"</a>),
- or loaded by iterating over a query result (<a href="#4.4">Section 4.4,
- "Query Result"</a>), the pointer to the persistent object, in the form
- of the canonical object pointer (<a href="#3.3">Section 3.3, "Object
- and View Pointers"</a>), is stored in the session. For as long as the
- session is in effect, any subsequent calls to load the same object will
- return the cached instance. When an object's state is deleted from the
- database with the <code>database::erase()</code> function
- (<a href="#3.11">Section 3.11, "Deleting Persistent Objects"</a>), the
- cached object pointer is removed from the session. For example:</p>
-
- <pre class="cxx">
-shared_ptr&lt;person> p (new person ("John", "Doe"));
-
-session s;
-transaction t (db.begin ());
-
-unsigned long id (db.persist (p)); // p is cached in s.
-shared_ptr&lt;person> p1 (db.load&lt;person> (id)); // p1 same as p.
-
-t.commit ();
- </pre>
-
-
- <p>The per-object caching policies depend on the object pointer kind
- (<a href="#6.5">Section 6.5, "Using Custom Smart Pointers"</a>).
- Objects with a unique pointer, such as <code>std::auto_ptr</code>
- or <code>std::unique_ptr</code>, as an object pointer are never
- cached since it is not possible to have two such pointers pointing
- to the same object. When an object is persisted via a pointer or
- loaded as a dynamically allocated instance, objects with both raw
- and shared pointers as object pointers are cached. If an object is
- persisted as a reference or loaded into a pre-allocated instance,
- the object is only cached if its object pointer is a raw pointer.</p>
-
- <p>Also note that when we persist an object as a constant reference
- or constant pointer, the session caches such an object as
- unrestricted (non-<code>const</code>). This can lead to undefined
- behavior if the object being persisted was actually created as
- <code>const</code> and is later found in the session cache and
- used as non-<code>const</code>. As a result, when using sessions,
- it is recommended that all persistent objects be created as
- non-<code>const</code> instances. The following code fragment
- illustrates this point:</p>
-
- <pre class="cxx">
-void save (database&amp; db, shared_ptr&lt;const person> p)
-{
- transaction t (db.begin ());
- db.persist (p); // Persisted as const pointer.
- t.commit ();
-}
-
-session s;
-
-shared_ptr&lt;const person> p1 (new const person ("John", "Doe"));
-unsigned long id1 (save (db, p1)); // p1 is cached in s as non-const.
-
-{
- transaction t (db.begin ());
- shared_ptr&lt;person> p (db.load&lt;person> (id1)); // p == p1
- p->age (30); // Undefined behavior since p1 was created const.
- t.commit ();
-}
-
-shared_ptr&lt;const person> p2 (new person ("Jane", "Doe"));
-unsigned long id2 (save (db, p2)); // p2 is cached in s as non-const.
-
-{
- transaction t (db.begin ());
- shared_ptr&lt;person> p (db.load&lt;person> (id2)); // p == p2
- p->age (30); // Ok, since p2 was not created const.
- t.commit ();
-}
- </pre>
-
- <h2><a name="11.2">11.2 Custom Sessions</a></h2>
-
- <p>ODB can use a custom session implementation instead of the
- default <code>odb::session</code>. There could be multiple
- reasons for an application to provide its own session. For
- example, the application may already include a notion of an
- object cache or registry which ODB can re-use. A custom
- session can also provide additional functionality, such as
- automatic change tracking, delayed database operations, or
- object eviction. Finally, the session-per-thread approach used
- by <code>odb::session</code> may not be suitable for all
- applications. For instance, some may need a thread-safe
- session that can be shared among multiple threads. For
- an example of a custom session that implements automatic
- change tracking by keeping original copies of the objects,
- refer to the <code>common/session/custom</code> test
- in the <code>odb-tests</code> package.</p>
-
- <p>To use a custom session we need to specify its type with
- the <code>--session-type</code> ODB compiler command line
- option. We also need to include its definition into the
- generated header file. This can be achieved with the
- <code>--hxx-prologue</code> option. For example, if our
- custom session is called <code>app::session</code> and
- is defined in the <code>app/session.hxx</code> header
- file, then the corresponding ODB compiler options would
- look like this:</p>
-
- <pre class="terminal">
-odb --hxx-prologue "#include \"app/session.hxx\"" \
---session-type ::app::session ...
- </pre>
-
- <p>A custom session should provide the following interface:</p>
-
- <pre class="cxx">
-class custom_session
-{
-public:
- static bool
- _has_cache ();
-
- // Cache management functions.
- //
- template &lt;typename T>
- struct cache_position
- {
- ...
- };
-
- template &lt;typename T>
- static cache_position&lt;T>
- _cache_insert (odb::database&amp;,
- const typename odb::object_traits&lt;T>::id_type&amp;,
- const typename odb::object_traits&lt;T>::pointer_type&amp;);
-
- template &lt;typename T>
- static typename odb::object_traits&lt;T>::pointer_type
- _cache_find (odb::database&amp;,
- const typename odb::object_traits&lt;T>::id_type&amp;);
-
- template &lt;typename T>
- static void
- _cache_erase (const cache_position&lt;T>&amp;);
-
- // Notification functions.
- //
- template &lt;typename T>
- static void
- _cache_persist (const cache_position&lt;T>&amp;);
-
- template &lt;typename T>
- static void
- _cache_load (const cache_position&lt;T>&amp;);
-
- template &lt;typename T>
- static void
- _cache_update (odb::database&amp;, const T&amp; obj);
-
- template &lt;typename T>
- static void
- _cache_erase (odb::database&amp;,
- const typename odb::object_traits&lt;T>::id_type&amp;);
-};
- </pre>
-
- <p>The <code>_has_cache()</code> function shall return <code>true</code>
- if the object cache is in effect in the current thread.</p>
-
- <p>The <code>cache_position</code> class template represents a position
- in the cache of the inserted object. It should be default and
- copy-constructible as well as copy-assignable. The default
- constructor shall create a special empty/<code>NULL</code>
- position. A call of any of the cache management or notification
- functions with such an empty/<code>NULL</code> position shall be
- ignored.</p>
-
- <p>The <code>_cache_insert()</code> function shall add the object into
- the object cache and return its position. The <code>_cache_find()</code>
- function looks an object up in the object cache given its id.
- It returns a <code>NULL</code> pointer if the object is not
- found. The <code>_cache_erase()</code> cache management function
- shall remove the object from the cache. It is called
- if the database operation that caused the object to be inserted
- (for example, load) failed. Note also that after insertion the object
- state is undefined. You can only access the object state
- (for example, make a copy or clear a flag) from one of the
- notification functions discussed below.</p>
-
- <p>The notification functions are called after an object has
- been persisted, loaded, updated, or erased, respectively. If
- your session implementation does not need some of the
- notifications, you still have to provide their functions,
- however, you can leave their implementations empty.</p>
-
- <p>Notice also that all the cache management and notification
- functions are static. This is done in order to allow for a
- custom notion of a current session. Normally, the first
- step a non-empty implementation will perform is lookup the
- current session.</p>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="12">12 Optimistic Concurrency</a></h1>
-
- <p>The ODB transaction model (<a href="#3.5">Section 3.5,
- "Transactions"</a>) guarantees consistency as long as we perform all the
- database operations corresponding to a specific application transaction
- in a single database transaction. That is, if we load an object within a
- database transaction and update it in the same transaction, then we are
- guaranteed that the object state that we are updating in the database is
- exactly the same as the state we have loaded. In other words, it is
- impossible for another process or thread to modify the object state
- in the database between these load and update operations.</p>
-
- <p>In this chapter we use the term <em>application transaction</em>
- to refer to a set of operations on persistent objects that an
- application needs to perform in order to implement some
- application-specific functionality. The term <em>database
- transaction</em> refers to the set of database operations
- performed between the ODB <code>begin()</code> and <code>commit()</code>
- calls. Up until now we have treated application transactions and
- database transactions as essentially the same thing.</p>
-
- <p>While this model is easy to understand and straightforward to use,
- it may not be suitable for applications that have long application
- transactions. The canonical example of such a situation is an
- application transaction that requires user input between loading
- an object and updating it. Such an operation may take an arbitrary
- long time to complete and performing it within a single database
- transaction will consume database resources as well as prevent
- other processes/threads from updating the object for too long.</p>
-
- <p>The solution to this problem is to break up the long-lived
- application transaction into several short-lived database
- transactions. In our example that would mean loading the object
- in one database transaction, waiting for user input, and then
- updating the object in another database transaction. For example:</p>
-
- <pre class="cxx">
-unsigned long id = ...;
-person p;
-
-{
- transaction t (db.begin ());
- db.load (id, p);
- t.commit ();
-}
-
-cerr &lt;&lt; "enter age for " &lt;&lt; p.first () &lt;&lt; " " &lt;&lt; p.last () &lt;&lt; endl;
-unsigned short age;
-cin >> age;
-p.age (age);
-
-{
- transaction t (db.begin ());
- db.update (p);
- t.commit ();
-}
- </pre>
-
- <p>This approach works well if we only have one process/thread that can ever
- update the object. However, if we have multiple processes/threads
- modifying the same object, then this approach does not guarantee
- consistency anymore. Consider what happens in the above example if
- another process updates the person's last name while we are waiting for
- the user input. Since we loaded the object before this change occured,
- our version of the person's data will still have the old name. Once we
- receive the input from the user, we go ahead and update the object,
- overwriting both the old age with the new one (correct) and the new name
- with the old one (incorrect).</p>
-
- <p>While there is no way to restore the consistency guarantee in
- an application transaction that consists of multiple database
- transactions, ODB provides a mechanism, called optimistic
- concurrency, that allows applications to detect and potentially
- recover from such inconsistencies.</p>
-
- <p>In essence, the optimistic concurrency model detects mismatches
- between the current object state in the database and the state
- when it was loaded into the application memory. Such a mismatch
- would mean that the object was changed by another process or
- thread. There are several ways to implement such state mismatch
- detection. Currently, ODB uses object versioning while other
- methods, such as timestamps, may be supported in the future.</p>
-
- <p>To declare a persistent class with the optimistic concurrency model we
- use the <code>optimistic</code> pragma (<a href="#14.1.5">Section 14.1.5,
- "<code>optimistic</code>"</a>). We also use the <code>version</code>
- pragma (<a href="#14.4.16">Section 14.4.16, "<code>version</code>"</a>)
- to specify which data member will store the object version. For
- example:</p>
-
- <pre class="cxx">
-#pragma db object optimistic
-class person
-{
- ...
-
- #pragma db version
- unsigned long version_;
-};
- </pre>
-
- <p>The version data member is managed by ODB. It is initialized to
- <code>1</code> when the object is made persistent and incremented
- by <code>1</code> with each update. The <code>0</code> version value
- is not used by ODB and the application can use it as a special value,
- for example, to indicate that the object is transient. Note that
- for optimistic concurrency to function properly, the application
- should not modify the version member after making the object persistent
- or loading it from the database and until deleting the state of this
- object from the database. To avoid any accidental modifications
- to the version member, we can declare it <code>const</code>, for
- example:</p>
-
- <pre class="cxx">
-#pragma db object optimistic
-class person
-{
- ...
-
- #pragma db version
- const unsigned long version_;
-};
- </pre>
-
- <p>When we call the <code>database::update()</code> function
- (<a href="#3.10">Section 3.10, "Updating Persistent Objects"</a>) and pass
- an object that has an outdated state, the <code>odb::object_changed</code>
- exception is thrown. At this point the application has two
- recovery options: it can abort and potentially restart the
- application transaction or it can reload the new object
- state from the database, re-apply or merge the changes, and call
- <code>update()</code> again. Note that aborting an application
- transaction that performs updates in multiple database transactions
- may require reverting changes that have already been committed to
- the database. As a result, this strategy works best if all the
- updates are performed in the last database transaction of the
- application transaction. This way the changes can be reverted
- by simply rolling back this last database transaction.</p>
-
- <p>The following example shows how we can reimplement the above
- transaction using the second recovery option:</p>
-
- <pre class="cxx">
-unsigned long id = ...;
-person p;
-
-{
- transaction t (db.begin ());
- db.load (id, p);
- t.commit ();
-}
-
-cerr &lt;&lt; "enter age for " &lt;&lt; p.first () &lt;&lt; " " &lt;&lt; p.last () &lt;&lt; endl;
-unsigned short age;
-cin >> age;
-p.age (age);
-
-{
- transaction t (db.begin ());
-
- try
- {
- db.update (p);
- }
- catch (const object_changed&amp;)
- {
- db.reload (p);
- p.age (age);
- db.update (p);
- }
-
- t.commit ();
-}
- </pre>
-
- <p>An important point to note in the above code fragment is that the second
- <code>update()</code> call cannot throw the <code>object_changed</code>
- exception because we are reloading the state of the object
- and updating it within the same database transaction.</p>
-
- <p>Depending on the recovery strategy employed by the application,
- an application transaction with a failed update can be significantly
- more expensive than a successful one. As a result, optimistic
- concurrency works best for situations with low to medium contention
- levels where the majority of the application transactions complete
- without update conflicts. This is also the reason why this concurrency
- model is called optimistic.</p>
-
- <p>In addition to updates, ODB also performs state mismatch detection
- when we are deleting an object from the database
- (<a href="#3.11">Section 3.11, "Deleting Persistent Objects"</a>).
- To understand why this can be important, consider the following
- application transaction:</p>
-
- <pre class="cxx">
-unsigned long id = ...;
-person p;
-
-{
- transaction t (db.begin ());
- db.load (id, p);
- t.commit ();
-}
-
-string answer;
-cerr &lt;&lt; "age is " &lt;&lt; p.age () &lt;&lt; ", delete?" &lt;&lt; endl;
-getline (cin, answer);
-
-if (answer == "yes")
-{
- transaction t (db.begin ());
- db.erase (p);
- t.commit ();
-}
- </pre>
-
- <p>Consider again what happens if another process or thread updates
- the object by changing the person's age while we are waiting for
- the user input. In this case, the user makes the decision based on
- a certain age while we may delete (or not delete) an object that has
- a completely different age. Here is how we can fix this problem
- using optimistic concurrency:</p>
-
- <pre class="cxx">
-unsigned long id = ...;
-person p;
-
-{
- transaction t (db.begin ());
- db.load (id, p);
- t.commit ();
-}
-
-string answer;
-for (bool done (false); !done; )
-{
- if (answer.empty ())
- cerr &lt;&lt; "age is " &lt;&lt; p.age () &lt;&lt; ", delete?" &lt;&lt; endl;
- else
- cerr &lt;&lt; "age changed to " &lt;&lt; p.age () &lt;&lt; ", still delete?" &lt;&lt; endl;
-
- getline (cin, answer);
-
- if (answer == "yes")
- {
- transaction t (db.begin ());
-
- try
- {
- db.erase (p);
- done = true;
- }
- catch (const object_changed&amp;)
- {
- db.reload (p);
- }
-
- t.commit ();
- }
- else
- done = true;
-}
- </pre>
-
- <p>Note that state mismatch detection is performed only if we delete
- an object by passing the object instance to the <code>erase()</code>
- function. If we want to delete an object with the optimistic concurrency
- model regardless of its state, then we need to use the <code>erase()</code>
- function that deletes an object given its id, for example:</p>
-
- <pre class="cxx">
-{
- transaction t (db.begin ());
- db.erase (p.id ());
- t.commit ();
-}
- </pre>
-
- <p>Finally, note that for persistent classes with the optimistic concurrency
- model both the <code>update()</code> function as well as the
- <code>erase()</code> function that accepts an object instance as its
- argument no longer throw the <code>object_not_persistent</code>
- exception if there is no such object in the database. Instead,
- this condition is treated as a change of object state and the
- <code>object_changed</code> exception is thrown instead.</p>
-
- <p>For complete sample code that shows how to use optimistic
- concurrency, refer to the <code>optimistic</code> example in
- the <code>odb-examples</code> package.</p>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="13">13 Database Schema Evolution</a></h1>
-
- <p>When we add new persistent classes or change the existing ones, for
- example, by adding or deleting data members, the database schema
- necessary to store the new object model changes as well. At the
- same time, we may have existing databases that contain existing data.
- If new versions of your application don't need to handle
- old databases, then the schema creating functionality is all that
- you need. However, most applications will need to work with data
- stored by older versions of the same application.</p>
-
- <p>We will call <em>database schema evolution</em> the overall task
- of updating the database to match the changes in the object model.
- Schema evolution usually consists of two sub-tasks: <em>schema
- migration</em> and <em>data migration</em>. Schema migration
- modifies the database schema to correspond to the current
- object model. In a relational database, this, for example, could
- require adding or dropping tables and columns. The data migration
- task involves converting the data stored in the existing database
- from the old format to the new one.</p>
-
- <p>If performed manually, database schema evolution is a tedious and
- error-prone task. As a result, ODB provides comprehensive support
- for automated or, more precisely, semi-automated schema
- evolution. Specifically, ODB does fully-automatic schema
- migration and provides facilities to help you with data
- migration.</p>
-
- <p>The topic of schema evolution is a complex and sensitive
- issue since normally there would be valuable, production data at
- stake. As a result, the approach taken by ODB is to provide simple
- and bullet-proof elementary building blocks (or migration steps)
- that we can understand and trust. Using these elementary blocks we
- can then implement more complex migration scenarios. In particular,
- ODB does not try to handle data migration automatically since in most
- cases this requires understanding of application-specific semantics.
- In other words, there is no magic.</p>
-
- <p>There are two general approaches to working with older data: the
- application can either convert it to correspond to the new format
- or it can be made capable of working with multiple versions of this
- format. There is also a hybrid approach where the application
- may convert the data to the new format gradually as part of its
- normal functionality. ODB is capable of handling all these
- scenarios. That is, there is support for working with older
- models without performing any migration (schema or data).
- Alternatively, we can migrate the schema after
- which we have the choice of either also immediately migrating the
- data (<em>immediate data migration</em>) or doing it gradually
- (<em>gradual data migration</em>).</p>
-
- <p>Schema evolution is already a complex task and we should not
- unnecessarily use a more complex approach where a simpler one
- would be sufficient. From the above, the simplest approach is
- the immediate schema migration that does not require any data
- migration. An example of such a change would be adding a new
- data member with the default value (<a href="#14.3.4">Section
- 14.3.4, "<code>default</code>"</a>). This case ODB can handle
- completely automatically.</p>
-
- <p>If we do require data migration, then the next simplest approach
- is the immediate schema and data migration. Here we have to write
- custom migration code. However, it is separate from the rest of
- the core application logic and is executed at a well defined point
- (database migration). In other words, the core application logic
- need not be aware of older model versions. The potential drawback
- of this approach is performance. It may take a lot of resources
- and/or time to convert all the data upfront.</p>
-
- <p>If the immediate migration is not possible, then the next option
- is the immediate schema migration followed by the gradual data
- migration. With this approach, both old and new data must co-exist
- in the new database. We also have to change the application
- logic to both account for different sources of the same data (for
- example, when either an old or new version of the object is loaded)
- as well as migrate the data when appropriate (for example, when
- the old version of the object is updated). At some point, usually
- when the majority of the data has been converted, gradual migrations
- are terminated with an immediate migration.</p>
-
- <p>The most complex approach is working with multiple versions of
- the database without performing any migrations, schema or data.
- ODB does provide support for implementing this approach
- (<a href="#13.4">Section 13.4, "Soft Object Model Changes"</a>),
- however we will not cover it any further in this chapter.
- Generally, this will require embedding knowledge about each
- version into the core application logic which makes it hard
- to maintain for any non-trivial object model.</p>
-
- <p>Note also that when it comes to data migration, we can use
- the immediate variant for some changes and gradual for others.
- We will discuss various migration scenarios in greater detail
- in section <a href="#13.3">Section 13.3, "Data Migration"</a>.</p>
-
- <h2><a name="13.1">13.1 Object Model Version and Changelog</a></h2>
-
- <p>To enable schema evolution support in ODB we need to specify
- the object model version, or, more precisely, two versions.
- The first is the base model version. It is the lowest
- version from which we will be able to migrate. The second
- version is the current model version. In ODB we can migrate
- from multiple previous versions by successively migrating
- from one to the next until we reach the current version.
- We use the <code>db&nbsp;model&nbsp;version</code> pragma
- to specify both the base and current versions.</p>
-
- <p>When we enable schema evolution for the first time, our
- base and current versions will be the same, for example:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 1)
- </pre>
-
- <p>Once we release our application, its users may create databases
- with the schema corresponding to this version of the object
- model. This means that if we make any modifications to our
- object model that also change the schema, then we will need
- to be able to migrate the old databases to this new schema.
- As a result, before making any new changes after a release,
- we increment the current version, for example:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 2)
- </pre>
-
- <p>To put this another way, we can stay on the same version
- during development and keep adding new changes to it. But
- once we release it, any new changes to the object model will
- have to be done in a new version.</p>
-
- <p>It is easy to forget to increment the version before
- making new changes to the object model. To help solve this
- problem, the <code>db&nbsp;model&nbsp;version</code> pragma
- accepts a third optional argument that specify whether the
- current version is open or closed for changes. For example:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 2, open) // Can add new changes to
- // version 2.
- </pre>
-
- <pre class="cxx">
-#pragma db model version(1, 2, closed) // Can no longer add new
- // changes to version 2.
- </pre>
-
- <p>If the current version is closed, ODB will refuse to accept
- any new schema changes. In this situation you would
- normally increment the current version and mark it as open
- or you could re-open the existing version if, for example,
- you need to fix something. Note, however, that re-opening
- versions that have been released will most likely result
- in migration malfunctions. By default the version is open.</p>
-
- <p>Normally, an application will have a range of older database
- versions from which it is able to migrate. When we change
- this range by removing support for older versions, we also
- need to adjust the base model version. This will make sure
- that ODB does not keep unnecessary information around.</p>
-
- <p>A model version (both base and current) is a 64-bit unsigned
- integer (<code>unsigned&nbsp;long&nbsp;long</code>). <code>0</code>
- is reserved to signify special situations, such as the lack of
- schema in the database. Other than that, we can use any values
- as versions as long as they are monotonically increasing. In
- particular, we don't have to start with version <code>1</code>
- and can increase the versions by any increment.</p>
-
- <p>One versioning approach is to use an independent
- object model version by starting from version <code>1</code>
- and also incrementing by <code>1</code>. The alternative
- is to make the model version correspond to the application
- version. For example, if our application is using the
- <code>X.Y.Z</code> version format, then we could encode it
- as a hexadecimal number and use that as our model version,
- for example:</p>
-
- <pre class="cxx">
-#pragma db model version(0x020000, 0x020306) // 2.0.0-2.3.6
- </pre>
-
- <p>Most real-world object models will be spread over multiple
- header files and it will be burdensome to repeat the
- <code>db&nbsp;model&nbsp;version</code> pragma in each of
- them. The recommended way to handle this situation is to
- place the <code>version</code> pragma into a separate header
- file and include it into the object model files. If your
- project already has a header file that defines the
- application version, then it is natural to place this
- pragma there. For example:</p>
-
- <pre class="cxx">
-// version.hxx
-//
-// Define the application version.
-//
-
-#define MYAPP_VERSION 0x020306 // 2.3.6
-
-#ifdef ODB_COMPILER
-#pragma db model version(1, 7)
-#endif
- </pre>
-
- <p>Note that we can also use macros in the <code>version</code>
- pragma which allows us to specify all the versions in a single
- place. For example:</p>
-
- <pre class="cxx">
-#define MYAPP_VERSION 0x020306 // 2.3.6
-#define MYAPP_BASE_VERSION 0x020000 // 2.0.0
-
-#ifdef ODB_COMPILER
-#pragma db model version(MYAPP_BASE_VERSION, MYAPP_VERSION)
-#endif
- </pre>
-
- <p>It is also possible to have multiple object models within the
- same application that have different versions. Such models
- must be independent, that is, no headers from one model shall
- include a header from another. You will also need to assign
- different schema names to each model with the
- <code>--schema-name</code> ODB compiler option.</p>
-
- <p>Once we specify the object model version, the ODB compiler
- starts tracking database schema changes in a changelog file.
- Changelog has an XML-based, line-oriented format. It uses
- XML in order to provide human readability while also
- facilitating, if desired, processing and analysis with
- custom tools. The line orientation makes it easy to review
- with tools like <code>diff</code>.</p>
-
- <p>The changelog is maintained by the ODB compiler. Specifically,
- you do not need to make any manual changes to this file. You
- will, however, need to keep it around from one invocation of
- the ODB compiler to the next. In other words, the changelog
- file is both the input and the output of the ODB compiler. This,
- for example, means that if your project's source code is stored
- in a version control repository, then you will most likely want
- to store the changelog there as well. If you delete the changelog,
- then any ability to do schema migration will be lost.</p>
-
- <p>The only operation that you may want to perform with the
- changelog is to review the database schema changes that resulted
- from the C++ object model changes. For this you can use a tool
- like <code>diff</code> or, better yet, the change review facilities
- offered by your revision control system. For this purpose the
- contents of a changelog will be self-explanatory.</p>
-
- <p>As an example, consider the following initial object model:</p>
-
- <pre class="cxx">
-// person.hxx
-//
-
-#include &lt;string>
-
-#pragma db model version(1, 1)
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id auto
- unsigned long id_;
-
- std::string first_;
- std::string last_;
-};
- </pre>
-
- <p>We then compile this header file with the ODB compiler (using the
- PostgreSQL database as an example):</p>
-
- <pre class="terminal">
-odb --database pgsql --generate-schema person.hxx
- </pre>
-
- <p>If we now look at the list of generated files, then in addition to
- the now familiar <code>person-odb.?xx</code> and <code>person.sql</code>,
- we will also see <code>person.xml</code> &mdash; the changelog file.
- Just for illustration, below are the contents of this changelog.</p>
-
- <pre class="xml">
-&lt;changelog database="pgsql">
- &lt;model version="1">
- &lt;table name="person" kind="object">
- &lt;column name="id" type="BIGINT" null="false"/>
- &lt;column name="first" type="TEXT" null="false"/>
- &lt;column name="last" type="TEXT" null="false"/>
- &lt;primary-key auto="true">
- &lt;column name="id"/>
- &lt;/primary-key>
- &lt;/table>
- &lt;/model>
-&lt;/changelog>
- </pre>
-
- <p>Let's say we now would like to add another data member to the
- <code>person</code> class &mdash; the middle name. We increment
- the version and make the change:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 2)
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id auto
- unsigned long id_;
-
- std::string first_;
- std::string middle_;
- std::string last_;
-};
- </pre>
-
- <p>We use exactly the same command line to re-compile our file:</p>
-
- <pre class="terminal">
-odb --database pgsql --generate-schema person.hxx
- </pre>
-
- <p>This time the ODB compiler will read the old changelog, update
- it, and write out the new version. Again, for illustration only,
- below are the updated changelog contents:</p>
-
- <pre class="xml">
-&lt;changelog database="pgsql">
- &lt;changeset version="2">
- &lt;alter-table name="person">
- &lt;add-column name="middle" type="TEXT" null="false"/>
- &lt;/alter-table>
- &lt;/changeset>
-
- &lt;model version="1">
- &lt;table name="person" kind="object">
- &lt;column name="id" type="BIGINT" null="false"/>
- &lt;column name="first" type="TEXT" null="false"/>
- &lt;column name="last" type="TEXT" null="false"/>
- &lt;primary-key auto="true">
- &lt;column name="id"/>
- &lt;/primary-key>
- &lt;/table>
- &lt;/model>
-&lt;/changelog>
- </pre>
-
- <p>Just to reiterate, while the changelog may look like it could
- be written by hand, it is maintained completely automatically
- by the ODB compiler and the only reason you may want to look
- at its contents is to review the database schema changes. For
- example, if we compare the above two changelogs with
- <code>diff</code>, we will get the following summary of the
- database schema changes:</p>
-
- <pre class="xml">
---- person.xml.orig
-+++ person.xml
-@@ -1,4 +1,10 @@
-&lt;changelog database="pgsql">
-<span style="color: #009E00">+ &lt;changeset version="2">
-+ &lt;alter-table name="person">
-+ &lt;add-column name="middle" type="TEXT" null="false"/>
-+ &lt;/alter-table>
-+ &lt;/changeset>
-+</span>
- &lt;model version="1">
- &lt;table name="person" kind="object">
- &lt;column name="id" type="BIGINT" null="false"/>
- </pre>
-
- <p>The changelog is only written when we generate the database schema,
- that is, the <code>--generate-schema</code> option is specified.
- Invocations of the ODB compiler that only produce the database
- support code (C++) do not read or update the changelog. To put it
- another way, the changelog tracks changes in the resulting database
- schema, not the C++ object model.</p>
-
- <p>ODB ignores column order when comparing database schemas. This means
- that we can re-order data members in a class without causing any
- schema changes. Member renames, however, will result in schema
- changes since the column name changes as well (unless we specified
- the column name explicitly). From ODB's perspective such a rename
- looks like the deletion of one data member and the addition of
- another. If we don't want this to be treated as a schema change,
- then we will need to keep the old column name by explicitly
- specifying it with the <code>db&nbsp;column</code> pragma. For
- example, here is how we can rename <code>middle_</code> to
- <code>middle_name_</code> without causing any schema changes:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 2)
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db column("middle") // Keep the original column name.
- std::string middle_name_;
-
- ...
-};
- </pre>
-
- <p>If your object model consists of a large number of header files and
- you generate the database schema for each of them individually, then
- a changelog will be created for each of your header files. This may
- be what you want, however, the large number of changelogs can quickly
- become unwieldy. In fact, if you are generating the database schema
- as standalone SQL files, then you may have already experienced a
- similar problem caused by a large number of <code>.sql</code> files,
- one for each header.</p>
-
- <p>The solution to both of these problems is to generate a combined
- database schema file and a single changelog. For example, assume
- we have three header files in our object model:
- <code>person.hxx</code>, <code>employee.hxx</code>, and
- <code>employer.hxx</code>. To generate the database support code
- we compile them as usual but without specifying the
- <code>--generate-schema</code> option. In this case no changelog
- is created or updated:</p>
-
- <pre class="terminal">
-odb --database pgsql person.hxx
-odb --database pgsql employee.hxx
-odb --database pgsql employer.hxx
- </pre>
-
- <p>To generate the database schema, we perform a separate invocation
- of the ODB compiler. This time, however, we instruct it to only
- generate the schema (<code>--generate-schema-only</code>) and
- produce it combined (<code>--at-once</code>) for all the files
- in our object model:</p>
-
- <pre class="terminal">
-odb --database pgsql --generate-schema-only --at-once \
---input-name company person.hxx employee.hxx employer.hxx
- </pre>
-
- <p>The result of the above command is a single <code>company.sql</code>
- file (the name is derived from the <code>--input-name</code> value)
- that contains the database schema for our entire object model. There
- is also a single corresponding changelog file &mdash;
- <code>company.xml</code>.</p>
-
- <p>The same can be achieved for the embedded schema by instructing
- the ODB compiler to generate the database creation code into a
- separate C++ file (<code>--schema-format&nbsp;separate</code>):</p>
-
- <pre class="terminal">
-odb --database pgsql --generate-schema-only --schema-format separate \
---at-once --input-name company person.hxx employee.hxx employer.hxx
- </pre>
-
- <p>The result of this command is a single <code>company-schema.cxx</code>
- file and, again, <code>company.xml</code>.</p>
-
- <p>Note also that by default the changelog file is not placed into
- the directory specified with the <code>--output-dir</code> option.
- This is due to the changelog being both an input and an output file
- at the same time. As a result, by default, the ODB compiler will
- place it in the directory of the input header file.</p>
-
- <p>There is, however, a number of command line options (including
- <code>--changelog-dir</code>) that allow us to fine-tune the name and
- location of the changelog file. For example, you can instruct the ODB
- compiler to read the changelog from one file while writing it to
- another. This, for example, can be useful if you want to review
- the changes before discarding the old file. For more information
- on these options, refer to the
- <a href="http://www.codesynthesis.com/products/odb/doc/odb.xhtml">ODB
- Compiler Command Line Manual</a> and search for "changelog".</p>
-
- <p>When we were discussing version increments above, we used the
- terms <em>development</em> and <em>release</em>. Specifically,
- we talked about keeping the same object model versions during
- development periods and incrementing them after releases.
- What is a development period and a release in this context?
- These definitions can vary from project to project.
- Generally, during a development period we work on one or
- more changes to the object model that result in the changes
- to the database schema. A release is a point where we
- make our changes available to someone else who may have an
- older database to migrate from. In the traditional sense, a release
- is a point where you make a new version of your application available
- to its users. However, for schema evolution purposes, a release
- could also mean simply making your schema-altering changes
- available to other developers on your team. Let us consider
- two common scenarios to illustrate how all this fits together.</p>
-
- <p>One way to setup a project would be to re-use the application
- development period and application release for schema evolution.
- That is, during a new application version development we keep
- a single object model version and when we release the application,
- we increment the model version. In this case it makes sense to
- also reuse the application version as a model version for
- consistency. Here is a step-by-step guide for this setup:</p>
-
- <ol>
- <li>During development, keep the current object model version open.</li>
-
- <li>Before the release (for example, when entering a "feature freeze")
- close the version.</li>
-
- <li>After the release, update the version and open it.</li>
-
- <li>For each new feature, review the changeset at the top of the
- changelog, for example, with <code>diff</code> or your
- version control facilities. If you are using a version
- control, then this is best done just before committing
- your changes to the repository.</li>
- </ol>
-
- <p>An alternative way to setup schema versioning in a project would
- be to define the development period as working on a single
- feature and the release as making this feature available to
- other people (developers, testers, etc.) on your team, for
- example, by committing the changes to a public version control
- repository. In this case, the object model version will be
- independent of the application version and can simply be
- a sequence that starts with <code>1</code> and is
- incremented by <code>1</code>. Here is a step-by-step guide
- for this setup:</p>
-
- <ol>
- <li>Keep the current model version closed. Once a change is made
- that affects the database schema, the ODB compiler will refuse
- to update the changelog.</li>
-
- <li>If the change is legitimate, open a new version, that is,
- increment the current version and make it open.</li>
-
- <li>Once the feature is implemented and tested, review the final
- set of database changes (with <code>diff</code> or your
- version control facilities), close the version, and commit
- the changes to the version control repository (if using).</li>
- </ol>
-
- <p>If you are using a version control repository that supports
- pre-commit checks, then you may want to consider adding such
- a check to make sure the committed version is always closed.</p>
-
- <p>If we are just starting schema evolution in our project, which
- approach should we choose? The two approaches will work better
- in different situations since they have a different set of
- advantages and disadvantages. The first approach, which we
- can call version per application release, is best suited
- for simpler projects with smaller releases since otherwise
- a single migration will bundle a large number of unrelated
- actions corresponding to different features. This can
- become difficult to review and, if things go wrong, debug.</p>
-
- <p>The second approach, which we can call version per feature,
- is much more modular and provides a number of additional benefits.
- We can perform migrations for each feature as a discreet step
- which makes it easier to debug. We can also place each such
- migration step into a separate transaction further improving
- reliability. It also scales much better in larger teams
- where multiple developers can work concurrently on features
- that affect the database schema. For example, if you find
- yourself in a situation where another developer on your
- team used the same version as you and managed to commit his
- changes before you (that is, you have a merge conflict),
- then you can simply change the version to the next available
- one, regenerate the changelog, and continue with your commit.</p>
-
- <p>Overall, unless you have strong reasons to prefer the version
- per application release approach, rather choose version per
- feature even though it may seem more complex at the
- beginning. Also, if you do select the first approach, consider
- provisioning for switching to the second method by reserving
- a sub-version number. For example, for an application version
- in the form <code>2.3.4</code> you can make the object model
- version to be in the form <code>0x0203040000</code>, reserving
- the last two bytes for a sub-version. Later on you can use it to
- switch to the version per feature approach.</p>
-
- <h2><a name="13.2">13.2 Schema Migration</a></h2>
-
- <p>Once we enable schema evolution by specifying the object model
- version, in addition to the schema creation statements, the
- ODB compiler starts generating schema migration statements
- for each version all the way from the base to the current.
- As with schema creation, schema migration can be generated
- either as a set of SQL files or embedded into the generated
- C++ code (<code>--schema-format</code> option).</p>
-
- <p>For each migration step, that is from one version to the next,
- ODB generates two sets of statements: pre-migration and
- post-migration. The pre-migration statements <em>"relax"</em>
- the database schema so that both old and new data can co-exist.
- At this stage new columns and tables are added while old
- constraints are dropped. The post-migration statements
- <em>"tighten"</em> the database schema back so that only
- data conforming to the new format can remain. At this stage
- old columns and tables are dropped and new constraints are
- added. Now you can probably guess where the data
- migration fits into this &mdash; between the pre and post
- schema migrations where we can both access the old data
- and create the new one.</p>
-
- <p>If the schema is being generated as standalone SQL files,
- then we end up with a pair of files for each step: the pre-migration
- file and the post-migration file. For the <code>person</code>
- example we started in the previous section we will have the
- <code>person-002-pre.sql</code> and <code>person-002-post.sql</code>
- files. Here <code>002</code> is the version <em>to</em> which
- we are migrating while the <code>pre</code> and <code>post</code>
- suffixes specify the migration stage. So if we wanted to migrate
- a <code>person</code> database from version <code>1</code>
- to <code>2</code>, then we would first execute
- <code>person-002-pre.sql</code>, then migrate the data, if any
- (discussed in more detail in the next section), and finally
- execute <code>person-002-post.sql</code>. If our database is
- several versions behind, for example the database has version
- <code>1</code> while the current version is <code>5</code>,
- then we simply perform this set of steps for each version
- until we reach the current version.</p>
-
- <p>If we look at the contents of the <code>person-002-pre.sql</code>
- file, we will see the following (or equivalent, depending on the
- database used) statement:</p>
-
- <pre class="sql">
-ALTER TABLE "person"
- ADD COLUMN "middle" TEXT NULL;
- </pre>
-
- <p>As we would expect, this statement adds a new column corresponding
- to the new data member. An observant reader would notice,
- however, that the column is added as <code>NULL</code>
- even though we never requested this semantics in our object model.
- Why is the column added as <code>NULL</code>? If during migration
- the <code>person</code> table already contains rows (that is, existing
- objects), then an attempt to add a non-<code>NULL</code> column that
- doesn't have a default value will fail. As a result, ODB will initially
- add a new column that doesn't have a default value as <code>NULL</code>
- but then clean this up at the post-migration stage. This way your data
- migration code is given a chance to assign some meaningful values for
- the new data member for all the existing objects. Here are the contents
- of the <code>person-002-post.sql</code> file:</p>
-
- <pre class="sql">
-ALTER TABLE "person"
- ALTER COLUMN "middle" SET NOT NULL;
- </pre>
-
- <p>Currently ODB directly supports the following elementary database
- schema changes:</p>
-
- <ul class="list">
- <li>add table</li>
- <li>drop table</li>
- <li>add column</li>
- <li>drop column</li>
- <li>alter column, set <code>NULL</code>/<code>NOT NULL</code></li>
- <li>add foreign key</li>
- <li>drop foreign key</li>
- <li>add index</li>
- <li>drop index</li>
- </ul>
-
- <p>More complex changes can normally be implemented in terms of
- these building blocks. For example, to change a type of a
- data member (which leads to a change of a column type), we
- can add a new data member with the desired type (add column),
- migrate the data, and then delete the old data member (drop
- column). ODB will issue diagnostics for cases that are
- currently not supported directly. Note also that some database
- systems (notably SQLite) have a number of limitations in their
- support for schema changes. For more information on these
- database-specific limitations, refer to the "Limitations" sections
- in <a href="#II">Part II, "Database Systems"</a>.</p>
-
- <p>How do we know what the current database version is? That is, the
- version <em>from</em> which we need to migrate? We need to know this,
- for example, in order to determine the set of migrations we have to
- perform. By default, when schema evolution is enabled, ODB maintains
- this information in a special table called <code>schema_version</code>
- that has the following (or equivalent, depending on the database
- used) definition:</p>
-
- <pre class="sql">
-CREATE TABLE "schema_version" (
- "name" TEXT NOT NULL PRIMARY KEY,
- "version" BIGINT NOT NULL,
- "migration" BOOLEAN NOT NULL);
- </pre>
-
- <p>The <code>name</code> column is the schema name as specified with
- the <code>--schema-name</code> option. It is empty for the default
- schema. The <code>version</code> column contains the current database
- version. And, finally, the <code>migration</code> flag indicates
- whether we are in the process of migrating the database, that is,
- between the pre and post-migration stages.</p>
-
- <p>The schema creation statements (<code>person.sql</code> in our case)
- create this table and populate it with the initial model version. For
- example, if we executed <code>person.sql</code> corresponding to
- version <code>1</code> of our object model, then <code>name</code>
- would have been empty (which signifies the default schema since we
- didn't specify <code>--schema-name</code>), <code>version</code> will
- be <code>1</code> and <code>migration</code> will be
- <code>FALSE</code>.</p>
-
- <p>The pre-migration statements update the version and set the migration
- flag to <code>TRUE</code>. Continuing with our example, after executing
- <code>person-002-pre.sql</code>, <code>version</code> will
- become <code>2</code> and <code>migration</code> will be set to
- <code>TRUE</code>. The post-migration statements simply clear the
- migration flag. In our case, after running
- <code>person-002-post.sql</code>, <code>version</code> will
- remain <code>2</code> while <code>migration</code> will be reset
- to <code>FALSE</code>.</p>
-
- <p>Note also that above we mentioned that the schema creation statements
- (<code>person.sql</code>) create the <code>schema_version</code> table.
- This means that if we enable schema evolution support in the middle
- of a project, then we could already have existing databases that
- don't include this table. As a result, ODB will not be able to handle
- migrations for such databases unless we manually add the
- <code>schema_version</code> table and populate it with the correct
- version information. For this reason, it is highly recommended that
- you consider whether to use schema evolution and, if so, enable it
- from the beginning of your project.</p>
-
- <p>The <code>odb::database</code> class provides an API for accessing
- and modifying the current database version:</p>
-
- <pre class="cxx">
-namespace odb
-{
- typedef unsigned long long schema_version;
-
- struct LIBODB_EXPORT schema_version_migration
- {
- schema_version_migration (schema_version = 0,
- bool migration = false);
-
- schema_version version;
- bool migration;
-
- // This class also provides the ==, !=, &lt;, >, &lt;=, and >= operators.
- // Version ordering is as follows: {1,f} &lt; {2,t} &lt; {2,f} &lt; {3,t}.
- };
-
- class database
- {
- public:
- ...
-
- schema_version
- schema_version (const std::string&amp; name = "") const;
-
- bool
- schema_migration (const std::string&amp; name = "") const;
-
- const schema_version_migration&amp;
- schema_version_migration (const std::string&amp; name = "") const;
-
- // Set schema version and migration state manually.
- //
- void
- schema_version_migration (schema_version,
- bool migration,
- const std::string&amp; name = "");
-
- void
- schema_version_migration (const schema_version_migration&amp;,
- const std::string&amp; name = "");
-
- // Set default schema version table for all schemas.
- //
- void
- schema_version_table (const std::string&amp; table_name);
-
- // Set schema version table for a specific schema.
- //
- void
- schema_version_table (const std::string&amp; table_name,
- const std::string&amp; name);
- };
-}
- </pre>
-
- <p>The <code>schema_version()</code> and <code>schema_migration()</code>
- accessors return the current database version and migration flag,
- respectively. The optional <code>name</code> argument is the schema
- name. If the database schema hasn't been created (that is, there is
- no corresponding entry in the <code>schema_version</code> table or
- this table does not exist), then <code>schema_version()</code> returns
- <code>0</code>. The <code>schema_version_migration()</code> accessor
- returns both version and migration flag together in the
- <code>schema_version_migration</code> <code>struct</code>.</p>
-
- <p>You may already have a version table in your database or you (or your
- database administrator) may prefer to keep track of versions your own
- way. You can instruct ODB not to create the <code>schema_version</code>
- table with the <code>--suppress-schema-version</code> option. However,
- ODB still needs to know the current database version in order for certain
- schema evolution mechanisms to function properly. As a result, in
- this case, you will need to set the schema version on the database
- instance manually using the schema_version_migration() modifier.
- Note that the modifier API is not thread-safe. That is, you should
- not modify the schema version while other threads may be accessing
- or modifying the same information.</p>
-
- <p>Note also that the accessors we discussed above will only query the
- <code>schema_version</code> table once and, if the version could
- be determined, cache the result. If, however, the version could
- not be determined (that is, <code>schema_version()</code> returned
- 0), then a subsequent call will re-query the table. While it is
- probably a bad idea to modify the database schema while the
- application is running (other than via the <code>schema_catalog</code>
- API, as discussed below), if for some reason you need ODB to re-query
- the version, then you can manually set it to 0 using the
- <code>schema_version_migration()</code> modifier.</p>
-
- <p>It is also possible to change the name of the table that stores
- the schema version using the <code>--schema-version-table</code>
- option. You will also need to specify this alternative name on
- the <code>database</code> instance using the <code>schema_version_table()</code>
- modifier. The first version specifies the default table that is
- used for all the schema names. The second version specifies the
- table for a specific schema. The table name should be
- database-quoted, if necessary.</p>
-
- <p>If we are generating our schema migrations as standalone SQL files,
- then the migration workflow could look like this:</p>
-
- <ol>
- <li>The database administrator determines the current database version.
- If migration is required, then for each migration step (that
- is, from one version to the next), he performs the following:</li>
-
- <li>Execute the pre-migration file.</li>
-
- <li>Execute our application (or a separate migration program)
- to perform data migration (discussed later). Our application
- can determine that is is being executed in the "migration mode"
- by calling <code>schema_migration()</code> and then which
- migration code to run by calling <code>schema_version()</code>.</li>
-
- <li>Execute the post-migration file.</li>
- </ol>
-
- <p>These steps become more integrated and automatic if we embed the
- schema creation and migration code into the generated C++ code.
- Now we can perform schema creation, schema migration, and data
- migration as well as determine when each step is necessary
- programmatically from within the application.</p>
-
- <p>Schema evolution support adds the following extra functions to
- the <code>odb::schema_catalog</code> class, which we first discussed
- in <a href="#3.4">Section 3.4, "Database"</a>.</p>
-
- <pre class="cxx">
-namespace odb
-{
- class schema_catalog
- {
- public:
- ...
-
-
- // Schema migration.
- //
- static void
- migrate_schema_pre (database&amp;,
- schema_version,
- const std::string&amp; name = "");
-
- static void
- migrate_schema_post (database&amp;,
- schema_version,
- const std::string&amp; name = "");
-
- static void
- migrate_schema (database&amp;,
- schema_version,
- const std::string&amp; name = "");
-
- // Data migration.
- //
- // Discussed in the next section.
-
-
- // Combined schema and data migration.
- //
- static void
- migrate (database&amp;,
- schema_version = 0,
- const std::string&amp; name = "");
-
- // Schema version information.
- //
- static schema_version
- base_version (const database&amp;,
- const std::string&amp; name = "");
-
- static schema_version
- base_version (database_id,
- const std::string&amp; name = "");
-
- static schema_version
- current_version (const database&amp;,
- const std::string&amp; name = "");
-
- static schema_version
- current_version (database_id,
- const std::string&amp; name = "");
-
- static schema_version
- next_version (const database&amp;,
- schema_version = 0,
- const std::string&amp; name = "");
-
- static schema_version
- next_version (database_id,
- schema_version,
- const std::string&amp; name = "");
- };
-}
- </pre>
-
- <p>The <code>migrate_schema_pre()</code> and
- <code>migrate_schema_post()</code> static functions perform
- a single stage (that is, pre or post) of a single migration
- step (that is, from one version to the next). The <code>version</code>
- argument specifies the version we are migrating to. For
- instance, in our <code>person</code> example, if we know that
- the database version is <code>1</code> and the next version
- is <code>2</code>, then we can execute code like this:</p>
-
- <pre class="cxx">
-transaction t (db.begin ());
-
-schema_catalog::migrate_schema_pre (db, 2);
-
-// Data migration goes here.
-
-schema_catalog::migrate_schema_post (db, 2);
-
-t.commit ();
- </pre>
-
- <p>If you don't have any data migration code to run, then you can
- perform both stages with a single call using the
- <code>migrate_schema()</code> static function.</p>
-
- <p>The <code>migrate()</code> static function perform both schema
- and data migration (we discuss data migration in the next section).
- It can also perform several migration steps at once. If we don't
- specify its target version, then it will migrate (if necessary)
- all the way to the current model version. As an extra convenience,
- <code>migrate()</code> will also create the database schema if
- none exists. As a result, if we don't have any data migration
- code or we have registered it with <code>schema_catalog</code> (as
- discussed later), then the database schema creation and migration,
- whichever is necessary, if at all, can be performed with a single
- function call:</p>
-
- <pre class="cxx">
-transaction t (db.begin ());
-schema_catalog::migrate (db);
-t.commit ();
- </pre>
-
- <p>Note also that <code>schema_catalog</code> is integrated with the
- <code>odb::database</code> schema version API. In particular,
- <code>schema_catalog</code> functions will query and synchronize
- the schema version on the <code>database</code> instance if and
- when required.</p>
-
- <p>The <code>schema_catalog</code> class also allows you to iterate
- over known versions (remember, there could be "gaps" in version
- numbers) with the <code>base_version()</code>,
- <code>current_version()</code> and <code>next_version()</code>
- static functions. The <code>base_version()</code> and
- <code>current_version()</code> functions return the base and
- current object model versions, respectively. That is, the
- lowest version from which we can migrate and the version that
- we ultimately want to migrate to. The <code>next_version()</code>
- function returns the next known version. If the passed version is
- greater or equal to the current version, then this function
- will return the current version plus one (that is, one past
- current). If we don't specify the version, then
- <code>next_version()</code> will use the current database version
- as the starting point. Note also that the schema version information
- provided by these functions is only available if we embed the schema
- migration code into the generated C++ code. For standalone SQL file
- migrations this information is normally not needed since the migration
- process is directed by an external entity, such as a database
- administrator or a script.</p>
-
- <p>Most <code>schema_catalog</code> functions presented above also
- accept the optional schema name argument. If the passed schema
- name is not found, then the <code>odb::unknown_schema</code> exception
- is thrown. Similarly, functions that accept the schema version
- argument will throw the <code>odb::unknown_schema_version</code> exception
- if the passed version is invalid. Refer to <a href="#3.14">Section
- 3.14, "ODB Exceptions"</a> for more information on these exceptions.</p>
-
- <p>To illustrate how all these parts fit together, consider the
- following more realistic database schema management example.
- Here we want to handle the schema creation in a special way
- and perform each migration step in its own transaction.</p>
-
- <pre class="cxx">
-schema_version v (db.schema_version ());
-schema_version bv (schema_catalog::base_version (db));
-schema_version cv (schema_catalog::current_version (db));
-
-if (v == 0)
-{
- // No schema in the database. Create the schema and
- // initialize the database.
- //
- transaction t (db.begin ());
- schema_catalog::create_schema (db);
-
- // Populate the database with initial data, if any.
-
- t.commit ();
-}
-else if (v &lt; cv)
-{
- // Old schema (and data) in the database, migrate them.
- //
-
- if (v &lt; bv)
- {
- // Error: migration from this version is no longer supported.
- }
-
- for (v = schema_catalog::next_version (db, v);
- v &lt;= cv;
- v = schema_catalog::next_version (db, v))
- {
- transaction t (db.begin ());
- schema_catalog::migrate_schema_pre (db, v);
-
- // Data migration goes here.
-
- schema_catalog::migrate_schema_post (db, v);
- t.commit ();
- }
-}
-else if (v > cv)
-{
- // Error: old application trying to access new database.
-}
- </pre>
-
- <h2><a name="13.3">13.3 Data Migration</a></h2>
-
- <p>In quite a few cases specifying the default value for new data
- members will be all that's required to handle the existing objects.
- For example, the natural default value for the new middle name
- that we have added is an empty string. And we can handle
- this case with the <code>db&nbsp;default</code> pragma and without
- any extra C++ code:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 2)
-
-#pragma db object
-class person
-{
- ...
-
-
- #pragma db default("")
- std::string middle_;
-};
- </pre>
-
- <p>However, there will be situations where we would need to perform
- more elaborate data migrations, that is, convert old data to the
- new format. As an example, suppose we want to add gender to our
- <code>person</code> class. And, instead of leaving it unassigned
- for all the existing objects, we will try to guess it from the
- first name. This is not particularly accurate but it could be
- sufficient for our hypothetical application:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 3)
-
-enum gender {male, female};
-
-#pragma db object
-class person
-{
- ...
-
- gender gender_;
-};
- </pre>
-
- <p>As we have discussed earlier, there are two ways to perform data
- migration: immediate and gradual. To recap, with immediate
- migration we migrate all the existing objects at once, normally
- after the schema pre-migration statements but before the
- post-migration statements. With gradual migration, we make sure
- the new object model can accommodate both old and new data and
- gradually migrate existing objects as the application runs and
- the opportunities to do so arise, for example, an object is
- updated.</p>
-
- <p>There is also another option for data migration that is not
- discussed further in this section. Instead of using our C++
- object model we could execute ad-hoc SQL statements that
- perform the necessary conversions and migrations directly
- on the database server. While in certain cases this can be
- a better option from the performance point of view, this
- approach is often limited in terms of the migration logic
- that we can handle.</p>
-
- <h2><a name="13.3.1">13.3.1 Immediate Data Migration</a></h2>
-
- <p>Let's first see how we can implement an immediate migration for the
- new <code>gender_</code> data member we have added above. If we
- are using standalone SQL files for migration, then we could add
- code along these lines somewhere early in <code>main()</code>,
- before the main application logic:</p>
-
- <pre class="cxx">
-int
-main ()
-{
- ...
-
- odb::database&amp; db = ...
-
- // Migrate data if necessary.
- //
- if (db.schema_migration ())
- {
- switch (db.schema_version ())
- {
- case 3:
- {
- // Assign gender to all the existing objects.
- //
- transaction t (db.begin ());
-
- for (person&amp; p: db.query&lt;person> ())
- {
- p.gender (guess_gender (p.first ()));
- db.update (p);
- }
-
- t.commit ();
- break;
- }
- }
- }
-
- ...
-}
- </pre>
-
- <p>If you have a large number of objects to migrate, it may also be
- a good idea, from the performance point of view, to break one big
- transaction that we now have into multiple smaller transactions
- (<a href="#3.5">Section 3.5, "Transactions"</a>). For example:</p>
-
- <pre class="cxx">
-case 3:
- {
- transaction t (db.begin ());
-
- size_t n (0);
- for (person&amp; p: db.query&lt;person> ())
- {
- p.gender (guess_gender (p.first ()));
- db.update (p);
-
- // Commit the current transaction and start a new one after
- // every 100 updates.
- //
- if (n++ % 100 == 0)
- {
- t.commit ();
- t.reset (db.begin ());
- }
- }
-
- t.commit ();
- break;
- }
- </pre>
-
- <p>While it looks straightforward enough, as we add more migration
- snippets, this approach can quickly become unmaintainable. Instead
- of having all the migrations in a single function and determining
- when to run each piece ourselves, we can package each migration into
- a separate function, register it with the <code>schema_catalog</code>
- class, and let ODB figure out when to run which migration functions.
- To support this functionality, <code>schema_catalog</code> provides
- the following data migration API:</p>
-
- <pre class="cxx">
-namespace odb
-{
- class schema_catalog
- {
- public:
- ...
-
- // Data migration.
- //
- static std::size_t
- migrate_data (database&amp;,
- schema_version = 0,
- const std::string&amp; name = "");
-
- typedef void data_migration_function_type (database&amp;);
-
- // Common (for all the databases) data migration, C++98/03 version:
- //
- template &lt;schema_version v, schema_version base>
- static void
- data_migration_function (data_migration_function_type*,
- const std::string&amp; name = "");
-
- // Common (for all the databases) data migration, C++11 version:
- //
- template &lt;schema_version v, schema_version base>
- static void
- data_migration_function (std::function&lt;data_migration_function_type>,
- const std::string&amp; name = "");
-
- // Database-specific data migration, C++98/03 version:
- //
- template &lt;schema_version v, schema_version base>
- static void
- data_migration_function (database&amp;,
- data_migration_function_type*,
- const std::string&amp; name = "");
-
- template &lt;schema_version v, schema_version base>
- static void
- data_migration_function (database_id,
- data_migration_function_type*,
- const std::string&amp; name = "");
-
- // Database-specific data migration, C++11 version:
- //
- template &lt;schema_version v, schema_version base>
- static void
- data_migration_function (database&amp;,
- std::function&lt;data_migration_function_type>,
- const std::string&amp; name = "");
-
- template &lt;schema_version v, schema_version base>
- static void
- data_migration_function (database_id,
- std::function&lt;data_migration_function_type>,
- const std::string&amp; name = "");
- };
-
- // Static data migration function registration, C++98/03 version:
- //
- template &lt;schema_version v, schema_version base>
- struct data_migration_entry
- {
- data_migration_entry (data_migration_function_type*,
- const std::string&amp; name = "");
-
- data_migration_entry (database_id,
- data_migration_function_type*,
- const std::string&amp; name = "");
- };
-
- // Static data migration function registration, C++11 version:
- //
- template &lt;schema_version v, schema_version base>
- struct data_migration_entry
- {
- data_migration_entry (std::function&lt;data_migration_function_type>,
- const std::string&amp; name = "");
-
- data_migration_entry (database_id,
- std::function&lt;data_migration_function_type>,
- const std::string&amp; name = "");
- };
-}
- </pre>
-
- <p>The <code>migrate_data()</code> static function performs data
- migration for the specified version. If no version is specified,
- then it will use the current database version and also check
- whether the database is in migration, that is,
- <code>database::schema_migration()</code> returns <code>true</code>.
- As a result, all we need to do in our <code>main()</code> is call
- this function. It will check if migration is required and if so,
- call all the migration functions registered for this version. For
- example:</p>
-
- <pre class="cxx">
-int
-main ()
-{
- ...
-
- database&amp; db = ...
-
- // Check if we need to migrate any data and do so
- // if that's the case.
- //
- schema_catalog::migrate_data (db);
-
- ...
-}
- </pre>
-
- <p>The <code>migrate_data()</code> function returns the number of
- migration functions called. You can use this value for debugging
- or logging.</p>
-
- <p>The only other step that we need to perform is register our data
- migration functions with <code>schema_catalog</code>. At the
- lower level we can call the <code>data_migration_function()</code>
- static function for every migration function we have, for example,
- at the beginning of <code>main()</code>. For each version, data
- migration functions are called in the order of registration.</p>
-
- <p>A more convenient approach, however, is to use the
- <code>data_migration_entry</code> helper class template to register the
- migration functions during static initialization. This way we
- can keep the migration function and its registration code next
- to each other. Here is how we can reimplement our <code>gender</code>
- migration code to use this mechanism:</p>
-
- <pre class="cxx">
-static void
-migrate_gender (odb::database&amp; db)
-{
- transaction t (db.begin ());
-
- for (person&amp; p: db.query&lt;person> ())
- {
- p.gender (guess_gender (p.first ()));
- db.update (p);
- }
-
- t.commit ();
-}
-
-static const odb::data_migration_entry&lt;3, MYAPP_BASE_VERSION>
-migrate_gender_entry (&amp;migrate_gender);
- </pre>
-
- <p>The first template argument to the <code>data_migration_entry</code>
- class template is the version we want this data migration function
- to be called for. The second template argument is the base model
- version. This second argument is necessary to detect the situation
- where we no longer need this data migration function. Remember
- that when we move the base model version forward, migrations from
- any version below the new base are no longer possible. We, however,
- may still have migration functions registered for those lower
- versions. Since these functions will never be called, they are
- effectively dead code and it would be useful to identify and
- remove them. To assist with this, <code>data_migration_entry</code>
- (and lower lever <code>data_migration_function()</code>) will
- check at compile time (that is, <code>static_assert</code>) that
- the registration version is greater than the base model version.</p>
-
- <p>In the above example we use the <code>MYAPP_BASE_VERSION</code>
- macro that is presumably defined in a central place, for example,
- <code>version.hxx</code>. This is the recommended approach since
- we can update the base version in a single place and have the
- C++ compiler automatically identify all the data migration
- functions that can be removed.</p>
-
- <p>In C++11 we can also create a template alias so that we don't
- have to repeat the base model macro in every registration, for
- example:</p>
-
- <pre class="cxx">
-template &lt;schema_version v>
-using migration_entry = odb::data_migration_entry&lt;v, MYAPP_BASE_VERSION>;
-
-static const migration_entry&lt;3>
-migrate_gender_entry (&amp;migrate_gender);
- </pre>
-
- <p>For cases where you need to by-pass the base version check, for
- example, to implement your own registration helper, ODB also
- provides "unsafe" versions of the <code>data_migration_function()</code>
- functions that take the version as a function argument rather than
- as a template parameter.</p>
-
- <p>In C++11 we can also use lambdas as migration functions, which makes
- the migration code more concise:</p>
-
- <pre class="cxx">
-static const migration_entry&lt;3>
-migrate_gender_entry (
- [] (odb::database&amp; db)
- {
- transaction t (db.begin ());
-
- for (person&amp; p: db.query&lt;person> ())
- {
- p.gender (guess_gender (p.first ()));
- db.update (p);
- }
-
- t.commit ();
- });
- </pre>
-
- <p>If we are using embedded schema migrations, then both schema and
- data migration is integrated and can be performed with a single
- call to the <code>schema_catalog::migrate()</code> function that
- we discussed earlier. For example:</p>
-
-<pre class="cxx">
-int
-main ()
-{
- ...
-
- database&amp; db = ...
-
- // Check if we need to migrate the database and do so
- // if that's the case.
- //
- {
- transaction t (db.begin ());
- schema_catalog::migrate (db);
- t.commit ();
- }
-
- ...
-}
- </pre>
-
- <p>Note, however, that in this case we call <code>migrate()</code>
- within a transaction (for the schema migration part) which means
- that our migration functions will also be called within this
- transaction. As a result, we will need to adjust our migration
- functions not to start their own transaction:</p>
-
- <pre class="cxx">
-static void
-migrate_gender (odb::database&amp; db)
-{
- // Assume we are already in a transaction.
- //
- for (person&amp; p: db.query&lt;person> ())
- {
- p.gender (guess_gender (p.first ()));
- db.update (p);
- }
-}
- </pre>
-
- <p>If, however, we want more granular transactions, then we can
- use the lower-level <code>schema_catalog</code> functions to
- gain more control, as we have seen at the end of the previous
- section. Here is the relevant part of that example with
- an added data migration call:</p>
-
- <pre class="cxx">
- // Old schema (and data) in the database, migrate them.
- //
- for (v = schema_catalog::next_version (db, v);
- v &lt;= cv;
- v = schema_catalog::next_version (db, v))
- {
- transaction t (db.begin ());
- schema_catalog::migrate_schema_pre (db, v);
- schema_catalog::migrate_data (db, v);
- schema_catalog::migrate_schema_post (db, v);
- t.commit ();
- }
- </pre>
-
- <h2><a name="13.3.2">13.3.2 Gradual Data Migration</a></h2>
-
- <p>If the number of existing objects that require migration is large,
- then an all-at-once, immediate migration, while simple, may not
- be practical from a performance point of view. In this case,
- we can perform a gradual migration as the application does
- its normal functions.</p>
-
- <p>With gradual migrations, the object model must be capable of
- representing data that conforms to both old and new formats at
- the same time since, in general, the database will contain a
- mixture of old and new objects. For example, in case of our
- <code>gender</code> data member, we need a special value that
- represents the "no gender assigned yet" case (an old object).
- We also need to assign this special value to all the existing
- objects during the schema pre-migration stage. One way to do
- this would be add a special value to our <code>gender</code>
- enum and then make it the default value with the
- <code>db&nbsp;default</code> pragma. A cleaner and easier approach,
- however, is to use <code>NULL</code> as a special value. We
- can add support for the <code>NULL</code> value semantics
- to any existing type by wrapping it with
- <code>odb::nullable</code>, <code>boost::optional</code>
- or similar (<a href="#7.3">Section 7.3, "Pointers and <code>NULL</code>
- Value Semantics"</a>). We also don't need to specify the default value
- explicitly since <code>NULL</code> is used automatically. Here
- is how we can use this approach in our <code>gender</code>
- example:</p>
-
- <pre class="cxx">
-#include &lt;odb/nullable.hxx>
-
-#pragma db object
-class person
-{
- ...
-
- odb::nullable&lt;gender> gender_;
-};
- </pre>
-
- <p>A variety of strategies can be employed to implement gradual
- migrations. For example, we can migrate the data when the object
- is updated as part of the normal application logic. While there
- is no migration cost associated with this approach (the object
- is updated anyway), depending on how often objects are typically
- updated, this strategy can take a long time to complete. An
- alternative strategy would be to perform an update whenever
- an old object is loaded. Yet another strategy is to have a
- separate thread that slowly migrates all the old objects as
- the application runs.</p>
-
- <p>As an example, let us implement the first approach for our
- <code>gender</code> migration. While we could have added
- the necessary code throughout the application, from the
- maintenance point of view, it is best to try and localize
- the gradual migration logic to the persistent classes that
- it affects. And for this database operation callbacks
- (<a href="#14.1.7">Section 14.1.7, "<code>callback</code>"</a>)
- are a very useful mechanism. In our case, all we have to do is handle
- the <code>post_load</code> event where we guess the gender
- if it is <code>NULL</code>:</p>
-
- <pre class="cxx">
-#include &lt;odb/core.hxx> // odb::database
-#include &lt;odb/callback.hxx> // odb::callback_event
-#include &lt;odb/nullable.hxx>
-
-#pragma db object callback(migrate)
-class person
-{
- ...
-
- void
- migrate (odb::callback_event e, odb::database&amp;)
- {
- if (e == odb::callback_event::post_load)
- {
- // Guess gender if not assigned.
- //
- if (gender_.null ())
- gender_ = guess_gender (first_);
- }
- }
-
- odb::nullable&lt;gender> gender_;
-};
- </pre>
-
- <p>In particular, we don't have to touch any of the accessors
- or modifiers or the application logic &mdash; all of them
- can assume that the value can never be <code>NULL</code>.
- And when the object is next updated, the new <code>gender</code>
- value will be stored automatically.</p>
-
- <p>All gradual migrations normally end up with a terminating
- immediate migration some number of versions down the line,
- when the bulk of the objects has presumably been converted.
- This way we don't have to keep the gradual migration code
- around forever. Here is how we could implement a terminating
- migration for our example:</p>
-
- <pre class="cxx">
-// person.hxx
-//
-#pragma db model version(1, 4)
-
-#pragma db object
-class person
-{
- ...
-
- gender gender_;
-};
-
-// person.cxx
-//
-static void
-migrate_gender (odb::database&amp; db)
-{
- typedef odb::query&lt;person> query;
-
- for (person&amp; p: db.query&lt;person> (query::gender.is_null ()))
- {
- p.gender (guess_gender (p.first ()));
- db.update (p);
- }
-}
-
-static const odb::data_migration_entry&lt;4, MYAPP_BASE_VERSION>
-migrate_gender_entry (&amp;migrate_gender);
- </pre>
-
- <p>A couple of points to note about this code. Firstly, we
- removed all the gradual migration logic (the callback)
- from the class and replaced it with the immediate migration
- function. We also removed the <code>odb::nullable</code>
- wrapper (and therefore disallowed the <code>NULL</code> values)
- since after this migration all the objects will have been
- converted. Finally, in the migration function, we only query
- the database for objects that need migration, that is, have
- <code>NULL</code> gender.</p>
-
- <h2><a name="13.4">13.4 Soft Object Model Changes</a></h2>
-
- <p>Let us consider another common kind of object model change:
- we delete an old member, add a new one, and need to copy
- the data from the old to the new, perhaps applying some
- conversion. For example, we may realize that in our application
- it is a better idea to store a person's name as a single string
- rather than split it into three fields. So what we would like to do
- is add a new data member, let's call it <code>name_</code>, convert
- all the existing split names, and then delete the <code>first_</code>,
- <code>middle_</code>, and <code>last_</code> data members.</p>
-
- <p>While this sounds straightforward, there is a problem. If we
- delete (that is, physically remove from the source code) the
- old data members, then we won't be able to access the old
- data. The data will still be available in the database between
- the schema pre and post-migrations, it is just we will no longer
- be able to access it through our object model. And if we keep
- the old data members around, then the old data will remain
- stored in the database even after the schema post-migration.</p>
-
- <p>There is also a more subtle problem that has to do with existing
- migrations for the previous versions. Remember, in version <code>3</code>
- of our <code>person</code> example we added the <code>gender_</code>
- data member. We also have a data migration function which guesses
- the gender based on the first name. Deleting the <code>first_</code>
- data member from our class will obviously break this code. But
- even adding the new <code>name_</code> data member will cause
- problems because when we try to update the object in order to
- store the new gender, ODB will try to update <code>name_</code>
- as well. But there is no corresponding column in the database
- yet. When we run this migration function, we are still several
- versions away from the point where the <code>name</code> column
- will be added.</p>
-
- <p>This is a very subtle but also very important implication to
- understand. Unlike the main application logic, which only needs
- to deal with the current model version, data migration code works
- on databases that can be multiple versions behind the current
- version.</p>
-
- <p>How can we resolve this problem? It appears what we need is the
- ability to add or delete data members starting from a specific
- version. In ODB this mechanism is called soft member additions
- and deletions. A soft-added member is only treated as persistent
- starting from the addition version. A soft-deleted member is
- persistent until the deletion version (but including the migration
- stage). In its essence, soft model changes allow us to maintain
- multiple versions of our object model all with a single set of
- persistent classes. Let us now see how this functionality can
- help implement our changes:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 4)
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id auto
- unsigned long id_;
-
- #pragma db deleted(4)
- std::string first_;
-
- #pragma db deleted(4)
- std::string middle_;
-
- #pragma db deleted(4)
- std::string last_;
-
- #pragma db added(4)
- std::string name_;
-
- gender gender_;
-};
- </pre>
-
- <p>The migration function for this change could then look like
- this:</p>
-
- <pre class="cxx">
-static void
-migrate_name (odb::database&amp; db)
-{
- for (person&amp; p: db.query&lt;person> ())
- {
- p.name (p.first () + " " +
- p.middle () + (p.middle ().empty () ? "" : " ") +
- p.last ());
- db.update (p);
- }
-}
-
-static const odb::data_migration_entry&lt;4, MYAPP_BASE_VERSION>
-migrate_name_entry (&amp;migrate_name);
- </pre>
-
- <p>Note also that no changes are required to the gender migration
- function.</p>
-
- <p>As you may have noticed, in the code above we assumed that the
- <code>person</code> class still provides public accessors for
- the now deleted data members. This might not be ideal since now
- they should not be used by the application logic. The only code
- that may still need to access them is the migration functions. The
- recommended way to resolve this is to remove the accessors/modifiers
- corresponding to the deleted data member, make migration functions
- static functions of the class being migrated, and then access
- the deleted data members directly. For example:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 4)
-
-#pragma db object
-class person
-{
- ...
-
-private:
- friend class odb::access;
-
- #pragma db id auto
- unsigned long id_;
-
- #pragma db deleted(4)
- std::string first_;
-
- #pragma db deleted(4)
- std::string middle_;
-
- #pragma db deleted(4)
- std::string last_;
-
- #pragma db added(4)
- std::string name_;
-
- gender gender_;
-
-private:
- static void
- migrate_gender (odb::database&amp;);
-
- static void
- migrate_name (odb::database&amp;);
-};
-
-void person::
-migrate_gender (odb::database&amp; db)
-{
- for (person&amp; p: db.query&lt;person> ())
- {
- p.gender_ = guess_gender (p.first_);
- db.update (p);
- }
-}
-
-static const odb::data_migration_entry&lt;3, MYAPP_BASE_VERSION>
-migrate_name_entry (&amp;migrate_gender);
-
-void person::
-migrate_name (odb::database&amp; db)
-{
- for (person&amp; p: db.query&lt;person> ())
- {
- p.name_ = p.first_ + " " +
- p.middle_ + (p.middle_.empty () ? "" : " ") +
- p.last_;
- db.update (p);
- }
-}
-
-static const odb::data_migration_entry&lt;4, MYAPP_BASE_VERSION>
-migrate_name_entry (&amp;migrate_name);
- </pre>
-
- <p>Another potential issue with the soft-deletion is the requirement
- to keep the delete data members in the class. While they will not
- be initialized in the normal operation of the application (that
- is, not a migration), this can still be a problem if we need to
- minimize the memory footprint of our classes. For example, we may
- cache a large number of objects in memory and having three
- <code>std::string</code> data members can be a significant
- overhead.</p>
-
- <p>The recommended way to resolve this issue is to place all the
- deleted data members into a dynamically allocated composite
- value type. For example:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 4)
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id auto
- unsigned long id_;
-
- #pragma db added(4)
- std::string name_;
-
- gender gender_;
-
- #pragma db value
- struct deleted_data
- {
- #pragma db deleted(4)
- std::string first_;
-
- #pragma db deleted(4)
- std::string middle_;
-
- #pragma db deleted(4)
- std::string last_;
- };
-
- #pragma db column("")
- std::unique_ptr&lt;deleted_data> dd_;
-
- ...
-};
- </pre>
-
- <p>ODB will then automatically allocate the deleted value type if
- any of the deleted data members are being loaded. During the normal
- operation, however, the pointer will stay <code>NULL</code> and
- therefore reduce the common case overhead to a single pointer
- per class. Note that we make the composite value column prefix
- empty (the <code>db&nbsp;column("")</code> pragma) in order to
- keep the same column names for the deleted data members.</p>
-
- <p>Soft-added and deleted data members can be used in objects,
- composite values, views, and container value types. We can
- also soft-add and delete data members of simple, composite,
- pointer to object, and container types. Only special data
- members, such as the object id and the optimistic concurrency
- version, cannot be soft-added or deleted.</p>
-
- <p>It is also possible to soft-delete a persistent class. We
- can still work with the existing objects of such a class,
- however, no table is created in new databases for soft-deleted
- classes. To put it another way, a soft-delete class is like an
- abstract class (no table) but which can still be loaded, updated,
- etc. Soft-added persistent classes do not make much sense and
- are therefore not supported.</p>
-
- <p>As an example of a soft-deleted class, suppose we want to
- replace our <code>person</code> class with the new
- <code>employee</code> object and migrate the data. Here is
- how we could do this:</p>
-
- <pre class="cxx">
-#pragma db model version(1, 5)
-
-#pragma db object deleted(5)
-class person
-{
- ...
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db id auto
- unsigned long id_;
-
- std::string name_;
- gender gender_;
-
- static void
- migrate_person (odb::database&amp;);
-};
-
-void employee::
-migrate_person (odb::database&amp; db)
-{
- for (person&amp; p: db.query&lt;person> ())
- {
- employee e (p.name (), p.gender ());
- db.persist (e);
- }
-}
-
-static const odb::data_migration_entry&lt;5, MYAPP_BASE_VERSION>
-migrate_person_entry (&amp;migrate_person);
- </pre>
-
- <p>As we have seen above, hard member additions and deletions can
- (and most likely will) break existing data migration code. Why,
- then, not treat all the changes, or at least additions, as soft?
- ODB requires you to explicitly request this semantics because
- support for soft-added and deleted data members incurs runtime
- overhead. And there can be plenty of cases where there is no
- existing data migration and therefore hard additions and deletions
- are sufficient.</p>
-
- <p>In some cases a hard addition or deletion will result in a
- compile-time error. For example, one of the data migration
- functions may reference the data member we just deleted. In
- many cases, however, such errors can only be detected at
- runtime, and, worse yet, only when the migration function
- is executed. For example, we may hard-add a new data member
- that an existing migration function will try to indirectly
- store in the database as part of an object update. As a result,
- it is highly recommended that you always test your application
- with the database that starts at the base version so that every
- data migration function is called and therefore ensured to
- still work correctly.</p>
-
- <p>To help with this problem you can also instruct ODB to warn
- you about any hard additions or deletions with the
- <code>--warn-hard-add</code>, <code>--warn-hard-delete</code>,
- and <code>--warn-hard</code> command line options. ODB will
- only warn you about hard changes in the current version and
- only for as long as it is open, which makes this mechanism
- fairly usable.</p>
-
- <p>You may also be wondering why we have to specify the addition
- and deletion versions explicitly. It may seem like the ODB compiler
- should be able to figure this out automatically. While it is
- theoretically possible, to achieve this, ODB would have to also
- maintain a separate changelog of the C++ object model in
- addition to the database schema changelog it already maintains.
- While being a lot more complex, such an additional changelog
- would also complicate the workflow significantly. In this light,
- maintaining this change information as part of the original
- source files appears to be a cleaner and simpler approach.</p>
-
- <p>As we discussed before, when we move the base model version
- forward we essentially drop support for migrations from
- versions before the new base. As a result, it is no longer
- necessary to maintain the soft semantics of additions and
- deletions up to and including the new base version. ODB
- will issue diagnostics for all such members and classes.
- For soft deletions we can simply remove the data member or
- class entirely. For soft additions we only need to remove the
- <code>db&nbsp;added</code> pragma.</p>
-
- <h2><a name="13.4.1">13.4.1 Reuse Inheritance Changes</a></h2>
-
- <p>Besides adding and deleting data members, another way to alter
- the object's table is using reuse-style inheritance. If we add
- a new reuse base, then, from the database schema point of view,
- this is equivalent to adding all its columns to the derived
- object's table. Similarly, deleting reuse inheritance results in
- all the base's columns being deleted from the derived's table.</p>
-
- <p>In the future ODB may provide direct support for soft addition
- and deletion of inheritance. Currently, however, this semantics
- can be emulated with soft-added and deleted data members. The
- following table describes the most common scenarios depending
- on where columns are added or deleted, that is, base table,
- derived table, or both.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table class="scenarios" border="1">
- <tr>
- <th>DELETE</th>
- <th style="width: 40%">HARD</th>
- <th style="width: 40%">SOFT</th>
- </tr>
-
- <tr>
- <td>In both (delete inheritance and base)</td>
- <td>Delete inheritance and base. Move object id to derived.</td>
- <td>Soft-delete base. Mark all data members (except id) in
- base as soft-deleted.</td>
- </tr>
-
- <tr>
- <td>In base only (delete base)</td>
- <td>Option 1: mark base as abstract.<br/><br/>
- Option 2: move all the base member to derived, delete base.</td>
- <td>Soft-delete base.</td>
- </tr>
-
- <tr>
- <td>In derived only (delete inheritance)</td>
- <td>Delete inheritance, add object id to derived.</td>
- <td>Option 1: copy base to a new soft-deleted base, inherit
- from it instead. Mark all the data members (expect id) in
- this new base as soft-deleted. Note: we add the new base
- as soft-deleted to get notified when we can remove it.<br/><br/>
- Option 2: Copy all the data members from base to derived
- and mark them as soft-deleted in derived.</td>
- </tr>
- </table>
-
-
- <table class="scenarios" border="1">
- <tr>
- <th>ADD</th>
- <th style="width: 40%">HARD</th>
- <th style="width: 40%">SOFT</th>
- </tr>
-
- <tr>
- <td>In both (add new base and inheritance)</td>
- <td>Add new base and inheritance. Potentially move object id
- member from derived to base.</td>
- <td>Add new base and mark all its data members as soft-added.
- Add inheritance. Move object id from derived to base.</td>
- </tr>
-
- <tr>
- <td>In base only (refactor existing data to new base)</td>
- <td>Add new base and move data members from derived to base.
- Note: in most cases the new base will be made abstract
- which make this scenario non-schema changing.</td>
- <td>The same as HARD.</td>
- </tr>
-
- <tr>
- <td>In derived only (add inheritance to existing base)</td>
- <td>Add inheritance, delete object id in derived.</td>
- <td>Copy existing base to a new abstract base and inherit
- from it. Mark all the database members in the new base
- as soft-added (except object id). When notified by the
- ODB compiler that the soft addition of the data members
- is no longer necessary, delete the copy and inherit from
- the original base.</td>
- </tr>
- </table>
-
- <h2><a name="13.4.2">13.4.2 Polymorphism Inheritance Changes</a></h2>
-
- <p>Unlike reuse inheritance, adding or deleting a polymorphic base
- does not result in the base's data members being added or deleted
- from the derived object's table because each class in a polymorphic
- hierarchy is stored in a separate table. There are, however, other
- complications due to the presence of special columns (discriminator
- in the root table and object id links in derived tables) which makes
- altering the hierarchy structure difficult to handle automatically.
- Adding or deleting (including soft-deleting) of leaf classes (or
- leaf sub-hierarchies) in a polymorphic hierarchy is fully supported.
- Any more complex changes, such as adding or deleting the root or
- an intermediate base or getting an existing class into or out of
- a polymorphic hierarchy can be handled by creating a new leaf class
- (or leaf sub-hierarchy), soft-deleting the old class, and migrating
- the data.</p>
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="14">14 ODB Pragma Language</a></h1>
-
- <p>As we have already seen in previous chapters, ODB uses a pragma-based
- language to capture database-specific information about C++ types.
- This chapter describes the ODB pragma language in more detail. It
- can be read together with other chapters in the manual to get a
- sense of what kind of configurations and mapping fine-tuning are
- possible. You can also use this chapter as a reference at a later
- stage.</p>
-
- <p>An ODB pragma has the following syntax:</p>
-
- <p><code>#pragma db <i>qualifier</i> [<i>specifier</i> <i>specifier</i> ...]</code></p>
-
- <p>The <em>qualifier</em> tells the ODB compiler what kind of C++ construct
- this pragma describes. Valid qualifiers are <code>object</code>,
- <code>view</code>, <code>value</code>, <code>member</code>,
- <code>namespace</code>, <code>model</code>, <code>index</code>, and
- <code>map</code>.
- A pragma with the <code>object</code> qualifier describes a persistent
- object type. It tells the ODB compiler that the C++ class it describes
- is a persistent class. Similarly, pragmas with the <code>view</code>
- qualifier describe view types, the <code>value</code> qualifier
- describes value types and the <code>member</code> qualifier is used
- to describe data members of persistent object, view, and value types.
- The <code>namespace</code> qualifier is used to describe common
- properties of objects, views, and value types that belong to
- a C++ namespace while the <code>model</code> qualifier describes
- the whole C++ object model. The <code>index</code> qualifier defines
- a database index. And, finally, the <code>map</code> qualifier
- describes a mapping between additional database types and types
- for which ODB provides built-in support.</p>
-
- <p>The <em>specifier</em> informs the ODB compiler about a particular
- database-related property of the C++ declaration. For example, the
- <code>id</code> member specifier tells the ODB compiler that this
- member contains this object's identifier. Below is the declaration
- of the <code>person</code> class that shows how we can use ODB
- pragmas:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-private:
- #pragma db member id
- unsigned long id_;
- ...
-};
- </pre>
-
- <p>In the above example we don't explicitly specify which C++ class or
- data member the pragma belongs to. Rather, the pragma applies to
- a C++ declaration that immediately follows the pragma. Such pragmas
- are called <em>positioned pragmas</em>. In positioned pragmas that
- apply to data members, the <code>member</code> qualifier can be
- omitted for brevity, for example:</p>
-
- <pre class="cxx">
- #pragma db id
- unsigned long id_;
- </pre>
-
- <p>Note also that if the C++ declaration immediately following a
- position pragma is incompatible with the pragma qualifier, an
- error will be issued. For example:</p>
-
- <pre class="cxx">
- #pragma db object // Error: expected class instead of data member.
- unsigned long id_;
- </pre>
-
- <p>While keeping the C++ declarations and database declarations close
- together eases maintenance and increases readability, we can also
- place them in different parts of the same header file or even
- factor them to a separate file. To achieve this we use the so called
- <em>named pragmas</em>. Unlike positioned pragmas, named pragmas
- explicitly specify the C++ declaration to which they apply by
- adding the declaration name after the pragma qualifier. For example:</p>
-
- <pre class="cxx">
-class person
-{
- ...
-private:
- unsigned long id_;
- ...
-};
-
-#pragma db object(person)
-#pragma db member(person::id_) id
- </pre>
-
- <p>Note that in the named pragmas for data members the <code>member</code>
- qualifier is no longer optional. The C++ declaration name in the
- named pragmas is resolved using the standard C++ name resolution
- rules, for example:</p>
-
- <pre class="cxx">
-namespace db
-{
- class person
- {
- ...
- private:
- unsigned long id_;
- ...
- };
-}
-
-namespace db
-{
- #pragma db object(person) // Resolves db::person.
-}
-
-#pragma db member(db::person::id_) id
- </pre>
-
- <p>As another example, the following code fragment shows how to use the
- named value type pragma to map a C++ type to a native database type:</p>
-
- <pre class="cxx">
-#pragma db value(bool) type("INT")
-
-#pragma db object
-class person
-{
- ...
-private:
- bool married_; // Mapped to INT NOT NULL database type.
- ...
-};
- </pre>
-
- <p>If we would like to factor the ODB pragmas into a separate file,
- we can include this file into the original header file (the one
- that defines the persistent types) using the <code>#include</code>
- directive, for example:</p>
-
- <pre class="cxx">
-// person.hxx
-
-class person
-{
- ...
-};
-
-#ifdef ODB_COMPILER
-# include "person-pragmas.hxx"
-#endif
- </pre>
-
- <p>Alternatively, instead of using the <code>#include</code> directive,
- we can use the <code>--odb-epilogue</code> option to make the pragmas
- known to the ODB compiler when compiling the original header file,
- for example:</p>
-
- <pre class="terminal">
---odb-epilogue '#include "person-pragmas.hxx"'
- </pre>
-
- <p>The following sections cover the specifiers applicable to all the
- qualifiers mentioned above.</p>
-
- <p>The C++ header file that defines our persistent classes and
- normally contains one or more ODB pragmas is compiled by both
- the ODB compiler to generate the database support code and
- the C++ compiler to build the application. Some C++ compilers
- issue warnings about pragmas that they do not recognize. There
- are several ways to deal with this problem which are covered
- at the end of this chapter in <a href="#14.9">Section 14.9,
- "C++ Compiler Warnings"</a>.</p>
-
- <h2><a name="14.1">14.1 Object Type Pragmas</a></h2>
-
- <p>A pragma with the <code>object</code> qualifier declares a C++ class
- as a persistent object type. The qualifier can be optionally followed,
- in any order, by one or more specifiers summarized in the table below:</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table class="specifiers" border="1">
- <tr>
- <th>Specifier</th>
- <th>Summary</th>
- <th>Section</th>
- </tr>
-
- <tr>
- <td><code>table</code></td>
- <td>table name for a persistent class</td>
- <td><a href="#14.1.1">14.1.1</a></td>
- </tr>
-
- <tr>
- <td><code>pointer</code></td>
- <td>pointer type for a persistent class</td>
- <td><a href="#14.1.2">14.1.2</a></td>
- </tr>
-
- <tr>
- <td><code>abstract</code></td>
- <td>persistent class is abstract</td>
- <td><a href="#14.1.3">14.1.3</a></td>
- </tr>
-
- <tr>
- <td><code>readonly</code></td>
- <td>persistent class is read-only</td>
- <td><a href="#14.1.4">14.1.4</a></td>
- </tr>
-
- <tr>
- <td><code>optimistic</code></td>
- <td>persistent class with the optimistic concurrency model</td>
- <td><a href="#14.1.5">14.1.5</a></td>
- </tr>
-
- <tr>
- <td><code>no_id</code></td>
- <td>persistent class has no object id</td>
- <td><a href="#14.1.6">14.1.6</a></td>
- </tr>
-
- <tr>
- <td><code>callback</code></td>
- <td>database operations callback</td>
- <td><a href="#14.1.7">14.1.7</a></td>
- </tr>
-
- <tr>
- <td><code>schema</code></td>
- <td>database schema for a persistent class</td>
- <td><a href="#14.1.8">14.1.8</a></td>
- </tr>
-
- <tr>
- <td><code>polymorphic</code></td>
- <td>persistent class is polymorphic</td>
- <td><a href="#14.1.9">14.1.9</a></td>
- </tr>
-
- <tr>
- <td><code>session</code></td>
- <td>enable/disable session support for a persistent class</td>
- <td><a href="#14.1.10">14.1.10</a></td>
- </tr>
-
- <tr>
- <td><code>definition</code></td>
- <td>definition location for a persistent class</td>
- <td><a href="#14.1.11">14.1.11</a></td>
- </tr>
-
- <tr>
- <td><code>transient</code></td>
- <td>all non-virtual data members in a persistent class are transient</td>
- <td><a href="#14.1.12">14.1.12</a></td>
- </tr>
-
- <tr>
- <td><code>sectionable</code></td>
- <td>support addition of new sections in derived classes</td>
- <td><a href="#14.1.13">14.1.13</a></td>
- </tr>
-
- <tr>
- <td><code>deleted</code></td>
- <td>persistent class is soft-deleted</td>
- <td><a href="#14.1.14">14.1.14</a></td>
- </tr>
-
- <tr>
- <td><code>bulk</code></td>
- <td>enable bulk operations for a persistent class</td>
- <td><a href="#14.1.15">14.1.15</a></td>
- </tr>
-
- </table>
-
- <h3><a name="14.1.1">14.1.1 <code>table</code></a></h3>
-
- <p>The <code>table</code> specifier specifies the table name that should
- be used to store objects of the persistent class in a relational
- database. For example:</p>
-
- <pre class="cxx">
-#pragma db object table("people")
-class person
-{
- ...
-};
- </pre>
-
- <p>If the table name is not specified, the class name is used as the
- table name. The table name can be qualified with a database
- schema, for example:</p>
-
- <pre class="cxx">
-#pragma db object table("census.people")
-class person
-{
- ...
-};
- </pre>
-
- <p>For more information on database schemas and the format of the
- qualified names, refer to <a href="#14.1.8">Section 14.1.8,
- "<code>schema</code>"</a>.</p>
-
- <h3><a name="14.1.2">14.1.2 <code>pointer</code></a></h3>
-
- <p>The <code>pointer</code> specifier specifies the object pointer type
- for the persistent class. The object pointer type is used to return,
- pass, and cache dynamically allocated instances of a persistent
- class. For example:</p>
-
- <pre class="cxx">
-#pragma db object pointer(std::tr1::shared_ptr&lt;person>)
-class person
-{
- ...
-};
- </pre>
-
- <p>There are several ways to specify an object pointer with the
- <code>pointer</code> specifier. We can use a complete pointer
- type as shown in the example above. Alternatively, we can
- specify only the template name of a smart pointer in which
- case the ODB compiler will automatically append the class
- name as a template argument. The following example is therefore
- equivalent to the one above:</p>
-
- <pre class="cxx">
-#pragma db object pointer(std::tr1::shared_ptr)
-class person
-{
- ...
-};
- </pre>
-
- <p>If you would like to use the raw pointer as an object pointer,
- you can use <code>*</code> as a shortcut:</p>
-
- <pre class="cxx">
-#pragma db object pointer(*) // Same as pointer(person*)
-class person
-{
- ...
-};
- </pre>
-
- <p>If a pointer type is not explicitly specified, the default pointer,
- specified at the namespace level (<a href="#14.5.1">Section 14.5.1,
- "<code>pointer</code>"</a>) or with the <code>--default-pointer</code>
- ODB compiler option, is used. If neither of these two mechanisms is
- used to specify the pointer, then the raw pointer is used by default.</p>
-
- <p>For a more detailed discussion of object pointers, refer to
- <a href="#3.3">Section 3.3, "Object and View Pointers"</a>.</p>
-
- <h3><a name="14.1.3">14.1.3 <code>abstract</code></a></h3>
-
- <p>The <code>abstract</code> specifier specifies that the persistent class
- is abstract. An instance of an abstract class cannot be stored in
- the database and is normally used as a base for other persistent
- classes. For example:</p>
-
- <pre class="cxx">
-#pragma db object abstract
-class person
-{
- ...
-};
-
-#pragma db object
-class employee: public person
-{
- ...
-};
-
-#pragma db object
-class contractor: public person
-{
- ...
-};
- </pre>
-
- <p>Persistent classes with pure virtual functions are automatically
- treated as abstract by the ODB compiler. For a more detailed
- discussion of persistent class inheritance, refer to
- <a href="#8">Chapter 8, "Inheritance"</a>.</p>
-
- <h3><a name="14.1.4">14.1.4 <code>readonly</code></a></h3>
-
- <p>The <code>readonly</code> specifier specifies that the persistent class
- is read-only. The database state of read-only objects cannot be
- updated. In particular, this means that you cannot call the
- <code>database::update()</code> function (<a href="#3.10">Section 3.10,
- "Updating Persistent Objects"</a>) for such objects. For example:</p>
-
- <pre class="cxx">
-#pragma db object readonly
-class person
-{
- ...
-};
- </pre>
-
- <p>Read-only and read-write objects can derive from each other without
- any restrictions. When a read-only object derives from a read-write
- object, the resulting whole object is read-only, including the part
- corresponding to the read-write base. On the other hand, when a
- read-write object derives from a read-only object, all the data
- members that correspond to the read-only base are treated as
- read-only while the rest is treated as read-write.</p>
-
- <p>Note that it is also possible to declare individual data members
- (<a href="#14.4.12">Section 14.4.12, "<code>readonly</code>"</a>)
- as well as composite value types (<a href="#14.3.6">Section 14.3.6,
- "<code>readonly</code>"</a>) as read-only.</p>
-
- <h3><a name="14.1.5">14.1.5 <code>optimistic</code></a></h3>
-
- <p>The <code>optimistic</code> specifier specifies that the persistent class
- has the optimistic concurrency model. A class with the optimistic
- concurrency model must also specify the data member that is used to
- store the object version using the <code>version</code> pragma
- (<a href="#14.4.16">Section 14.4.16, "<code>version</code>"</a>).
- For example:</p>
-
- <pre class="cxx">
-#pragma db object optimistic
-class person
-{
- ...
-
- #pragma db version
- unsigned long version_;
-};
- </pre>
-
- <p>If a base class has the optimistic concurrency model, then all its derived
- classes will automatically have the optimistic concurrency model. The
- current implementation also requires that in any given inheritance
- hierarchy the object id and the version data members reside in the
- same class.</p>
-
- <p>For a more detailed discussion of optimistic concurrency, refer to
- <a href="#12">Chapter 12, "Optimistic Concurrency"</a>.</p>
-
- <h3><a name="14.1.6">14.1.6 <code>no_id</code></a></h3>
-
- <p>The <code>no_id</code> specifier specifies that the persistent class
- has no object id. For example:</p>
-
- <pre class="cxx">
-#pragma db object no_id
-class person
-{
- ...
-};
- </pre>
-
- <p>A persistent class without an object id has limited functionality.
- Such a class cannot be loaded with the <code>database::load()</code>
- or <code>database::find()</code> functions (<a href="#3.9">Section 3.9,
- "Loading Persistent Objects"</a>), updated with the
- <code>database::update()</code> function (<a href="#3.10">Section 3.10,
- "Updating Persistent Objects"</a>), or deleted with the
- <code>database::erase()</code> function (<a href="#3.11">Section 3.11,
- "Deleting Persistent Objects"</a>). To load and delete
- objects without ids you can use the <code>database::query()</code>
- (<a href="#4">Chapter 4, "Querying the Database"</a>) and
- <code>database::erase_query()</code> (<a href="#3.11">Section 3.11,
- "Deleting Persistent Objects"</a>) functions, respectively.
- There is no way to update such objects except by using native SQL
- statements (<a href="#3.12">Section 3.12, "Executing Native SQL
- Statements"</a>).</p>
-
- <p>Furthermore, persistent classes without object ids cannot have container
- data members nor can they be used in object relationships. Such objects
- are not entered into the session object cache
- (<a href="#11.1">Section 11.1, "Object Cache"</a>) either.</p>
-
- <p>To declare a persistent class with an object id, use the data member
- <code>id</code> specifier (<a href="#14.4.1">Section 14.4.1,
- "<code>id</code>"</a>).</p>
-
- <h3><a name="14.1.7">14.1.7 <code>callback</code></a></h3>
-
- <p>The <code>callback</code> specifier specifies the persist class
- member function that should be called before and after a
- database operation is performed on an object of this class.
- For example:</p>
-
- <pre class="cxx">
-#include &lt;odb/callback.hxx>
-
-#pragma db object callback(init)
-class person
-{
- ...
-
- void
- init (odb::callback_event, odb::database&amp;);
-};
- </pre>
-
- <p>The callback function has the following signature and can be
- overloaded for constant objects:</p>
-
- <pre class="cxx">
-void
-name (odb::callback_event, odb::database&amp;);
-
-void
-name (odb::callback_event, odb::database&amp;) const;
- </pre>
-
- <p>The first argument to the callback function is the event that
- triggered this call. The <code>odb::callback_event</code>
- enum-like type is defined in the <code>&lt;odb/callback.hxx></code>
- header file and has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- struct callback_event
- {
- enum value
- {
- pre_persist,
- post_persist,
- pre_load,
- post_load,
- pre_update,
- post_update,
- pre_erase,
- post_erase
- };
-
- callback_event (value v);
- operator value () const;
- };
-}
- </pre>
-
- <p>The second argument to the callback function is the database
- on which the operation is about to be performed or has just
- been performed. A callback function can be inline or virtual.</p>
-
- <p>The callback function for the <code>*_persist</code>,
- <code>*_update</code>, and <code>*_erase</code> events is always
- called on the constant object reference while for the <code>*_load</code>
- events &mdash; always on the unrestricted reference.</p>
-
- <p>If only the non-<code>const</code> version of the callback function
- is provided, then only the <code>*_load</code> events will be delivered.
- If only the <code>const</code> version is provided, then all the
- events will be delivered to this function. Finally, if both versions
- are provided, then the <code>*_load</code> events will be delivered
- to the non-<code>const</code> version while all others &mdash; to the
- <code>const</code> version. If you need to modify the object in one
- of the "<code>const</code>" events, then you can safely cast away
- <code>const</code>-ness using the <code>const_cast</code> operator if
- you know that none of the objects will be created const. Alternatively,
- if you cannot make this assumption, then you can declare the data
- members you wish to modify as <code>mutable</code>.</p>
-
- <p>A database operations callback can be used to implement object-specific
- pre and post initializations, registrations, and cleanups. As an example,
- the following code fragment outlines an implementation of a
- <code>person</code> class that maintains the transient <code>age</code>
- data member in addition to the persistent date of birth. A callback
- is used to calculate the value of the former from the latter every
- time a <code>person</code> object is loaded from the database.</p>
-
- <pre class="cxx">
-#include &lt;odb/core.hxx>
-#include &lt;odb/callback.hxx>
-
-#pragma db object callback(init)
-class person
-{
- ...
-
-private:
- friend class odb::access;
-
- date born_;
-
- #pragma db transient
- unsigned short age_;
-
- void
- init (odb::callback_event e, odb::database&amp;)
- {
- switch (e)
- {
- case odb::callback_event::post_load:
- {
- // Calculate age from the date of birth.
- ...
- break;
- }
- default:
- break;
- }
- }
-};
- </pre>
-
- <h3><a name="14.1.8">14.1.8 <code>schema</code></a></h3>
-
- <p>The <code>schema</code> specifier specifies a database schema
- that should be used for the persistent class.</p>
-
- <p>In relational databases the term schema can refer to two related
- but ultimately different concepts. Normally it means a collection
- of tables, indexes, sequences, etc., that are created in the
- database or the actual DDL statements that create these
- database objects. Some database implementations support what
- would be more accurately called a <em>database namespace</em>
- but is also called a schema. In this sense, a schema is a
- separate namespace in which tables, indexes, sequences, etc.,
- can be created. For example, two tables that have the same
- name can coexist in the same database if they belong to
- different schemas. In this section when we talk about a
- schema, we refer to the <em>database namespace</em> meaning
- of this term. </p>
-
- <p>When schemas are in use, a database object name is qualified
- with a schema. For example:</p>
-
- <pre class="sql">
-CREATE TABLE accounting.employee (...)
-
-SELECT ... FROM accounting.employee WHERE ...
- </pre>
-
- <p>In the above example <code>accounting</code> is the schema
- and the <code>employee</code> table belongs to this
- schema.</p>
-
- <p>Not all database implementations support schemas. Some
- implementation that don't support schemas (for example,
- MySQL, SQLite) allow the use of the above syntax to specify
- the database name. Yet others may support several levels
- of qualification. For example, Microsoft SQL Server has
- three levels starting with the linked database server,
- followed by the database, and then followed by
- the schema:
- <code>server1.company1.accounting.employee</code>.
- While the actual meaning of the qualifier in a qualified name
- vary from one database implementation to another, here we
- refer to all of them collectively as a schema.</p>
-
- <p>In ODB, a schema for a table of a persistent class can be
- specified at the class level, C++ namespace level, or the
- file level. To assign a schema to a specific persistent class
- we can use the <code>schema</code> specifier, for example:</p>
-
- <pre class="cxx">
-#pragma db object schema("accounting")
-class employee
-{
- ...
-};
- </pre>
-
- <p>If we are also assigning a table name, then we can use
- a shorter notation by specifying both the schema and
- the table name in the <code>table</code> specifier:</p>
-
- <pre class="cxx">
-#pragma db object table("accounting.employee")
-class employee
-{
- ...
-};
- </pre>
-
- <p>If we want to assign a schema to all the persistent classes
- in a C++ namespace, then, instead of specifying the schema
- for each class, we can specify it once at the C++ namespace level.
- For example:</p>
-
- <pre class="cxx">
-#pragma db namespace schema("accounting")
-namespace accounting
-{
- #pragma db object
- class employee
- {
- ...
- };
-
- #pragma db object
- class employer
- {
- ...
- };
-}
- </pre>
-
- <p>If we want to assign a schema to all the persistent classes in
- a file, then we can use the <code>--schema</code> ODB compiler
- option. For example:</p>
-
- <pre class="terminal">
-odb ... --schema accounting ...
- </pre>
-
- <p>An alternative to this approach with the same effect is to
- assign a schema to the global namespace:</p>
-
- <pre class="cxx">
-#pragma db namespace() schema("accounting")
- </pre>
-
- <p>By default schema qualifications are accumulated starting from
- the persistent class, continuing with the namespace hierarchy
- to which this class belongs, and finishing with the schema
- specified with the <code>--schema</code> option. For
- example:</p>
-
- <pre class="cxx">
-#pragma db namespace schema("audit_db")
-namespace audit
-{
- #pragma db namespace schema("accounting")
- namespace accounting
- {
- #pragma db object
- class employee
- {
- ...
- };
- }
-}
- </pre>
-
- <p>If we compile the above code fragment with the
- <code>--schema&nbsp;server1</code> option, then the
- <code>employee</code> table will have the
- <code>server1.audit_db.accounting.employee</code> qualified
- name.</p>
-
- <p>In some situations we may want to prevent such accumulation
- of the qualifications. To accomplish this we can use the
- so-called fully-qualified names, which have the empty leading
- name component. This is analogous to the C++ fully-qualified
- names in the <code>::accounting::employee</code> form. For
- example:</p>
-
- <pre class="cxx">
-#pragma db namespace schema("accounting")
-namespace accounting
-{
- #pragma db object schema(".hr")
- class employee
- {
- ...
- };
-
- #pragma db object
- class employer
- {
- ...
- };
-}
- </pre>
-
- <p>In the above code fragment, the <code>employee</code> table will
- have the <code>hr.employee</code> qualified name while the
- <code>employer</code> &mdash; <code>accounting.employer</code>.
- Note also that the empty leading name component is a special
- ODB syntax and is not propagated to the actual database names
- (using a name like <code>.hr.employee</code> to refer to a table
- will most likely result in an error).</p>
-
- <p>Auxiliary database objects for a persistent class, such as indexes,
- sequences, triggers, etc., are all created in the same schema
- as the class table. By default, this is also true for the
- container tables. However, if you need to store a container
- table in a different schema, then you can provide a qualified
- name using the <code>table</code> specifier, for example:</p>
-
- <pre class="cxx">
-#pragma db object table("accounting.employee")
-class employee
-{
- ...
-
- #pragma db object table("operations.projects")
- std::vector&lt;std::string> projects_;
-};
- </pre>
-
- <p>The standard syntax for qualified names used in the
- <code>schema</code> and <code>table</code> specifiers as well
- as the view <code>column</code> specifier (<a href="#14.4.10">Section
- 14.4.10, "<code>column</code> (view)"</a>) has the
- <code>"</code><i>name</i><code>.</code><i>name</i>...<code>"</code>
- form where, as discussed above, the leading name component
- can be empty to denote a fully qualified name. This form, however,
- doesn't work if one of the name components contains periods. To
- support such cases the alternative form is available:
- <code>"</code><i>name</i><code>"."</code><i>name</i><code>"</code>...
- For example:</p>
-
- <pre class="cxx">
-#pragma db object table("accounting_1.2"."employee")
-class employee
-{
- ...
-};
- </pre>
-
- <p>Finally, to specify an unqualified name that contains periods
- we can use the following special syntax:</p>
-
- <pre class="cxx">
-#pragma db object schema(."accounting_1.2") table("employee")
-class employee
-{
- ...
-};
- </pre>
-
- <p>Table prefixes (<a href="#14.5.2">Section 14.5.2, "<code>table</code>"</a>)
- can be used as an alternative to database schemas if the target
- database system does not support schemas.</p>
-
- <h3><a name="14.1.9">14.1.9 <code>polymorphic</code></a></h3>
-
- <p>The <code>polymorphic</code> specifier specifies that the persistent
- class is polymorphic. For more information on polymorphism support,
- refer to <a href="#8">Chapter 8, "Inheritance"</a>.</p>
-
- <h3><a name="14.1.10">14.1.10 <code>session</code></a></h3>
-
- <p>The <code>session</code> specifier specifies whether to enable
- session support for the persistent class. For example:</p>
-
- <pre class="cxx">
-#pragma db object session // Enable.
-class person
-{
- ...
-};
-
-#pragma db object session(true) // Enable.
-class employee
-{
- ...
-};
-
-#pragma db object session(false) // Disable.
-class employer
-{
- ...
-};
- </pre>
-
- <p>Session support is disabled by default unless the
- <code>--generate-session</code> ODB compiler option is specified
- or session support is enabled at the namespace level
- (<a href="#14.5.4">Section 14.5.4, "<code>session</code>"</a>).
- For more information on sessions, refer to <a href="#11">Chapter
- 11, "Session"</a>.</p>
-
- <h3><a name="14.1.11">14.1.11 <code>definition</code></a></h3>
-
- <p>The <code>definition</code> specifier specifies an alternative
- <em>definition location</em> for the persistent class. By
- default, the ODB compiler generates the database support code for
- a persistent class when we compile the header file that
- defines this class. However, if the <code>definition</code>
- specifier is used, then the ODB compiler will instead generate
- the database support code when we compile the header file
- containing this pragma.</p>
-
- <p>For more information on this functionality, refer to
- <a href="#14.3.7">Section 14.3.7, "<code>definition</code>"</a>.</p>
-
- <h3><a name="14.1.12">14.1.12 <code>transient</code></a></h3>
-
- <p>The <code>transient</code> specifier instructs the ODB compiler to
- treat all non-virtual data members in the persistent class as transient
- (<a href="#14.4.1">Section 14.4.1, "<code>transient</code>"</a>).
- This specifier is primarily useful when declaring virtual data
- members, as discussed in <a href="#14.4.13">Section 14.4.13,
- "<code>virtual</code>"</a>.</p>
-
- <h3><a name="14.1.13">14.1.13 <code>sectionable</code></a></h3>
-
- <p>The <code>sectionable</code> specifier instructs the ODB compiler
- to generate support for the addition of new object sections in
- derived classes in a hierarchy with the optimistic concurrency
- model. For more information on this functionality, refer to
- <a href="#9.2">Section 9.2, "Sections and Optimistic
- Concurrency"</a>.</p>
-
- <h3><a name="14.1.14">14.1.14 <code>deleted</code></a></h3>
-
- <p>The <code>deleted</code> specifier marks the persistent class as
- soft-deleted. The single required argument to this specifier is
- the deletion version. For more information on this functionality,
- refer to <a href="#13.4">Section 13.4, "Soft Object Model
- Changes"</a>.</p>
-
- <h3><a name="14.1.15">14.1.15 <code>bulk</code></a></h3>
-
- <p>The <code>bulk</code> specifier enables bulk operation support for
- the persistent class. The single required argument to this specifier
- is the batch size. For more information on this functionality, refer
- to <a href="#15.3">Section 15.3, "Bulk Database Operations"</a>.</p>
-
- <h2><a name="14.2">14.2 View Type Pragmas</a></h2>
-
- <p>A pragma with the <code>view</code> qualifier declares a C++ class
- as a view type. The qualifier can be optionally followed,
- in any order, by one or more specifiers summarized in the
- table below:</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table class="specifiers" border="1">
- <tr>
- <th>Specifier</th>
- <th>Summary</th>
- <th>Section</th>
- </tr>
-
- <tr>
- <td><code>object</code></td>
- <td>object associated with a view</td>
- <td><a href="#14.2.1">14.2.1</a></td>
- </tr>
-
- <tr>
- <td><code>table</code></td>
- <td>table associated with a view</td>
- <td><a href="#14.2.2">14.2.2</a></td>
- </tr>
-
- <tr>
- <td><code>query</code></td>
- <td>view query condition</td>
- <td><a href="#14.2.3">14.2.3</a></td>
- </tr>
-
- <tr>
- <td><code>pointer</code></td>
- <td>pointer type for a view</td>
- <td><a href="#14.2.4">14.2.4</a></td>
- </tr>
-
- <tr>
- <td><code>callback</code></td>
- <td>database operations callback</td>
- <td><a href="#14.2.5">14.2.5</a></td>
- </tr>
-
- <tr>
- <td><code>definition</code></td>
- <td>definition location for a view</td>
- <td><a href="#14.2.6">14.2.6</a></td>
- </tr>
-
- <tr>
- <td><code>transient</code></td>
- <td>all non-virtual data members in a view are transient</td>
- <td><a href="#14.2.7">14.2.7</a></td>
- </tr>
-
- </table>
-
- <p>For more information on view types refer to <a href="#10"> Chapter 10,
- "Views"</a>.</p>
-
- <h3><a name="14.2.1">14.2.1 <code>object</code></a></h3>
-
- <p>The <code>object</code> specifier specifies a persistent class
- that should be associated with the view. For more information
- on object associations refer to <a href="#10.1">Section 10.1, "Object
- Views"</a>.</p>
-
- <h3><a name="14.2.2">14.2.2 <code>table</code></a></h3>
-
- <p>The <code>table</code> specifier specifies a database table
- that should be associated with the view. For more information
- on table associations refer to <a href="#10.3">Section 10.3, "Table
- Views"</a>.</p>
-
- <h3><a name="14.2.3">14.2.3 <code>query</code></a></h3>
-
- <p>The <code>query</code> specifier specifies a query condition
- and, optionally, result modifiers for an object or table view
- or a native SQL query for a native view. An empty <code>query</code>
- specifier indicates that a native SQL query is provided at runtime.
- For more information on query conditions refer to
- <a href="#10.5">Section 10.5, "View Query Conditions"</a>. For
- more information on native SQL queries, refer to
- <a href="#10.6">Section 10.6, "Native Views"</a>.</p>
-
- <h3><a name="14.2.4">14.2.4 <code>pointer</code></a></h3>
-
- <p>The <code>pointer</code> specifier specifies the view pointer type
- for the view class. Similar to objects, the view pointer type is used
- to return dynamically allocated instances of a view class. The
- semantics of the <code>pointer</code> specifier for a view are the
- same as those of the <code>pointer</code> specifier for an object
- (<a href="#14.1.2">Section 14.1.2, "<code>pointer</code>"</a>).</p>
-
- <h3><a name="14.2.5">14.2.5 <code>callback</code></a></h3>
-
- <p>The <code>callback</code> specifier specifies the view class
- member function that should be called before and after an
- instance of this view class is created as part of the query
- result iteration. The semantics of the <code>callback</code>
- specifier for a view are similar to those of the
- <code>callback</code> specifier for an object
- (<a href="#14.1.7">Section 14.1.7, "<code>callback</code>"</a>)
- except that the only events that can trigger a callback
- call in the case of a view are <code>pre_load</code> and
- <code>post_load</code>.</p>
-
- <h3><a name="14.2.6">14.2.6 <code>definition</code></a></h3>
-
- <p>The <code>definition</code> specifier specifies an alternative
- <em>definition location</em> for the view class. By
- default, the ODB compiler generates the database support code for
- a view class when we compile the header file that
- defines this class. However, if the <code>definition</code>
- specifier is used, then the ODB compiler will instead generate
- the database support code when we compile the header file
- containing this pragma.</p>
-
- <p>For more information on this functionality, refer to
- <a href="#14.3.7">Section 14.3.7, "<code>definition</code>"</a>.</p>
-
- <h3><a name="14.2.7">14.2.7 <code>transient</code></a></h3>
-
- <p>The <code>transient</code> specifier instructs the ODB compiler
- to treat all non-virtual data members in the view class as transient
- (<a href="#14.4.1">Section 14.4.1, "<code>transient</code>"</a>).
- This specifier is primarily useful when declaring virtual data
- members, as discussed in <a href="#14.4.13">Section 14.4.13,
- "<code>virtual</code>"</a>.</p>
-
- <h2><a name="14.3">14.3 Value Type Pragmas</a></h2>
-
- <p>A pragma with the <code>value</code> qualifier describes a value
- type. It can be optionally followed, in any order, by one or more
- specifiers summarized in the table below:</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table class="specifiers" border="1">
- <tr>
- <th>Specifier</th>
- <th>Summary</th>
- <th>Section</th>
- </tr>
-
- <tr>
- <td><code>type</code></td>
- <td>database type for a value type</td>
- <td><a href="#14.3.1">14.3.1</a></td>
- </tr>
-
- <tr>
- <td><code>id_type</code></td>
- <td>database type for a value type when used as an object id</td>
- <td><a href="#14.3.2">14.3.2</a></td>
- </tr>
-
- <tr>
- <td><code>null</code>/<code>not_null</code></td>
- <td>type can/cannot be <code>NULL</code></td>
- <td><a href="#14.3.3">14.3.3</a></td>
- </tr>
-
- <tr>
- <td><code>default</code></td>
- <td>default value for a value type</td>
- <td><a href="#14.3.4">14.3.4</a></td>
- </tr>
-
- <tr>
- <td><code>options</code></td>
- <td>database options for a value type</td>
- <td><a href="#14.3.5">14.3.5</a></td>
- </tr>
-
- <tr>
- <td><code>readonly</code></td>
- <td>composite value type is read-only</td>
- <td><a href="#14.3.6">14.3.6</a></td>
- </tr>
-
- <tr>
- <td><code>definition</code></td>
- <td>definition location for a composite value type</td>
- <td><a href="#14.3.7">14.3.7</a></td>
- </tr>
-
- <tr>
- <td><code>transient</code></td>
- <td>all non-virtual data members in a composite value are transient</td>
- <td><a href="#14.3.8">14.3.8</a></td>
- </tr>
-
- <tr>
- <td><code>unordered</code></td>
- <td>ordered container should be stored unordered</td>
- <td><a href="#14.3.9">14.3.9</a></td>
- </tr>
-
- <tr>
- <td><code>index_type</code></td>
- <td>database type for a container's index type</td>
- <td><a href="#14.3.10">14.3.10</a></td>
- </tr>
-
- <tr>
- <td><code>key_type</code></td>
- <td>database type for a container's key type</td>
- <td><a href="#14.3.11">14.3.11</a></td>
- </tr>
-
- <tr>
- <td><code>value_type</code></td>
- <td>database type for a container's value type</td>
- <td><a href="#14.3.12">14.3.12</a></td>
- </tr>
-
- <tr>
- <td><code>value_null</code>/<code>value_not_null</code></td>
- <td>container's value can/cannot be <code>NULL</code></td>
- <td><a href="#14.3.13">14.3.13</a></td>
- </tr>
-
- <tr>
- <td><code>id_options</code></td>
- <td>database options for a container's id column</td>
- <td><a href="#14.3.14">14.3.14</a></td>
- </tr>
-
- <tr>
- <td><code>index_options</code></td>
- <td>database options for a container's index column</td>
- <td><a href="#14.3.15">14.3.15</a></td>
- </tr>
-
- <tr>
- <td><code>key_options</code></td>
- <td>database options for a container's key column</td>
- <td><a href="#14.3.16">14.3.16</a></td>
- </tr>
-
- <tr>
- <td><code>value_options</code></td>
- <td>database options for a container's value column</td>
- <td><a href="#14.3.17">14.3.17</a></td>
- </tr>
-
- <tr>
- <td><code>id_column</code></td>
- <td>column name for a container's object id</td>
- <td><a href="#14.3.18">14.3.18</a></td>
- </tr>
-
- <tr>
- <td><code>index_column</code></td>
- <td>column name for a container's index</td>
- <td><a href="#14.3.19">14.3.19</a></td>
- </tr>
-
- <tr>
- <td><code>key_column</code></td>
- <td>column name for a container's key</td>
- <td><a href="#14.3.20">14.3.20</a></td>
- </tr>
-
- <tr>
- <td><code>value_column</code></td>
- <td>column name for a container's value</td>
- <td><a href="#14.3.21">14.3.21</a></td>
- </tr>
-
- </table>
-
- <p>Many of the value type specifiers have corresponding member type
- specifiers with the same names (<a href="#14.4">Section 14.4,
- "Data Member Pragmas"</a>). The behavior of such specifiers
- for members is similar to that for value types. The only difference
- is the scope. A particular value type specifier applies to all the
- members of this value type that don't have a pre-member version
- of the specifier, while the member specifier always applies only
- to a single member. Also, with a few exceptions, member specifiers
- take precedence over and override parameters specified with value
- specifiers.</p>
-
- <h3><a name="14.3.1">14.3.1 <code>type</code></a></h3>
-
- <p>The <code>type</code> specifier specifies the native database type
- that should be used for data members of this type. For example:</p>
-
- <pre class="cxx">
-#pragma db value(bool) type("INT")
-
-#pragma db object
-class person
-{
- ...
-
- bool married_; // Mapped to INT NOT NULL database type.
-};
- </pre>
-
- <p>The ODB compiler provides the default mapping between common C++
- types, such as <code>bool</code>, <code>int</code>, and
- <code>std::string</code> and the database types for each supported
- database system. For more information on the default mapping,
- refer to <a href="#II">Part II, "Database Systems"</a>. The
- <code>null</code> and <code>not_null</code> (<a href="#14.3.3">Section
- 14.3.3, "<code>null</code>/<code>not_null</code>"</a>) specifiers
- can be used to control the <code>NULL</code> semantics of a type.</p>
-
- <p>In the above example we changed the mapping for the <code>bool</code>
- type which is now mapped to the <code>INT</code> database type. In
- this case, the <code>value</code> pragma is all that is necessary
- since the ODB compiler will be able to figure out how to store
- a boolean value as an integer in the database. However, there
- could be situations where the ODB compiler will not know how to
- handle the conversion between the C++ and database representations
- of a value. Consider, as an example, a situation where the
- boolean value is stored in the database as a string:</p>
-
- <pre class="cxx">
-#pragma db value(bool) type("VARCHAR(5)")
- </pre>
-
- <p>The possible database values for the C++ <code>true</code> value could
- be <code>"true"</code>, or <code>"TRUE"</code>, or <code>"True"</code>.
- Or, maybe, all of the above could be valid. The ODB compiler has no way
- of knowing how your application wants to convert <code>bool</code>
- to a string and back. To support such custom value type mappings,
- ODB allows you to provide your own database conversion functions
- by specializing the <code>value_traits</code> class template. The
- <code>mapping</code> example in the <code>odb-examples</code>
- package shows how to do this for all the supported database systems.</p>
-
- <h3><a name="14.3.2">14.3.2 <code>id_type</code></a></h3>
-
- <p>The <code>id_type</code> specifier specifies the native database type
- that should be used for data members of this type that are designated as
- object identifiers (<a href="#14.4.1">Section 14.4.1,
- "<code>id</code>"</a>). In combination with the <code>type</code>
- specifier (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>)
- <code>id_type</code> allows you to map a C++ type differently depending
- on whether it is used in an ordinary member or an object id. For
- example:</p>
-
- <pre class="cxx">
-#pragma db value(std::string) type("TEXT") id_type("VARCHAR(64)")
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id
- std::string email_; // Mapped to VARCHAR(64) NOT NULL.
-
- std::string name_; // Mapped to TEXT NOT NULL.
-};
- </pre>
-
- <p>Note that there is no corresponding member type specifier for
- <code>id_type</code> since the desired result can be achieved
- with just the <code>type</code> specifier, for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db id type("VARCHAR(128)")
- std::string email_;
-};
- </pre>
-
- <h3><a name="14.3.3">14.3.3 <code>null</code>/<code>not_null</code></a></h3>
-
- <p>The <code>null</code> and <code>not_null</code> specifiers specify that
- a value type or object pointer can or cannot be <code>NULL</code>,
- respectively. By default, value types are assumed not to allow
- <code>NULL</code> values while object pointers are assumed to
- allow <code>NULL</code> values. Data members of types that allow
- <code>NULL</code> values are mapped in a relational database to
- columns that allow <code>NULL</code> values. For example:</p>
-
- <pre class="cxx">
-using std::tr1::shared_ptr;
-
-typedef shared_ptr&lt;std::string> string_ptr;
-#pragma db value(string_ptr) type("TEXT") null
-
-#pragma db object
-class person
-{
- ...
-
- string_ptr name_; // Mapped to TEXT NULL.
-};
-
-typedef shared_ptr&lt;person> person_ptr;
-#pragma db value(person_ptr) not_null
- </pre>
-
- <p>The <code>NULL</code> semantics can also be specified on the
- per-member basis (<a href="#14.4.6">Section 14.4.6,
- "<code>null</code>/<code>not_null</code>"</a>). If both a type and
- a member have <code>null</code>/<code>not_null</code> specifiers,
- then the member specifier takes precedence. If a member specifier
- relaxes the <code>NULL</code> semantics (that is, if a member has
- the <code>null</code> specifier and the type has the explicit
- <code>not_null</code> specifier), then a warning is issued.</p>
-
- <p>It is also possible to override a previously specified
- <code>null</code>/<code>not_null</code> specifier. This is
- primarily useful if a third-party type, for example,
- one provided by a profile library (<a href="#III">Part III,
- "Profiles"</a>), allows <code>NULL</code> values but in your
- object model data members of this type should never be
- <code>NULL</code>. In this case you can use the <code>not_null</code>
- specifier to disable <code>NULL</code> values for this type for the
- entire translation unit. For example:</p>
-
- <pre class="cxx">
-// By default, null_string allows NULL values.
-//
-#include &lt;null-string.hxx>
-
-// Disable NULL values for all the null_string data members.
-//
-#pragma db value(null_string) not_null
- </pre>
-
- <p>For a more detailed discussion of the <code>NULL</code> semantics
- for values, refer to <a href="#7.3">Section 7.3, "Pointers and
- <code>NULL</code> Value Semantics"</a>. For a more detailed
- discussion of the <code>NULL</code> semantics for object pointers,
- refer to <a href="#6">Chapter 6, "Relationships"</a>.</p>
-
- <h3><a name="14.3.4">14.3.4 <code>default</code></a></h3>
-
- <p>The <code>default</code> specifier specifies the database default value
- that should be used for data members of this type. For example:</p>
-
- <pre class="cxx">
-#pragma db value(std::string) default("")
-
-#pragma db object
-class person
-{
- ...
-
- std::string name_; // Mapped to TEXT NOT NULL DEFAULT ''.
-};
- </pre>
-
- <p>The semantics of the <code>default</code> specifier for a value type
- are similar to those of the <code>default</code> specifier for a
- data member (<a href="#14.4.7">Section 14.4.7,
- "<code>default</code>"</a>).</p>
-
- <h3><a name="14.3.5">14.3.5 <code>options</code></a></h3>
-
- <p>The <code>options</code> specifier specifies additional column
- definition options that should be used for data members of this
- type. For example:</p>
-
- <pre class="cxx">
-#pragma db value(std::string) options("COLLATE binary")
-
-#pragma db object
-class person
-{
- ...
-
- std::string name_; // Mapped to TEXT NOT NULL COLLATE binary.
-};
- </pre>
-
- <p>The semantics of the <code>options</code> specifier for a value type
- are similar to those of the <code>options</code> specifier for a
- data member (<a href="#14.4.8">Section 14.4.8,
- "<code>options</code>"</a>).</p>
-
- <h3><a name="14.3.6">14.3.6 <code>readonly</code></a></h3>
-
- <p>The <code>readonly</code> specifier specifies that the composite
- value type is read-only. Changes to data members of a read-only
- composite value type are ignored when updating the database
- state of an object (<a href="#3.10">Section 3.10, "Updating Persistent
- Objects"</a>) containing such a value type. Note that this specifier
- is only valid for composite value types. For example:</p>
-
- <pre class="cxx">
-#pragma db value readonly
-class person_name
-{
- ...
-};
- </pre>
-
- <p>Read-only and read-write composite values can derive from each other
- without any restrictions. When a read-only value derives from a
- read-write value, the resulting whole value is read-only, including
- the part corresponding to the read-write base. On the other hand, when a
- read-write value derives from a read-only value, all the data
- members that correspond to the read-only base are treated as
- read-only while the rest is treated as read-write.</p>
-
- <p>Note that it is also possible to declare individual data members
- (<a href="#14.4.12">Section 14.4.12, "<code>readonly</code>"</a>)
- as well as whole objects (<a href="#14.1.4">Section 14.1.4,
- "<code>readonly</code>"</a>) as read-only.</p>
-
- <h3><a name="14.3.7">14.3.7 <code>definition</code></a></h3>
-
- <p>The <code>definition</code> specifier specifies an alternative
- <em>definition location</em> for the composite value type. By
- default, the ODB compiler generates the database support code for
- a composite value type when we compile the header file that
- defines this value type. However, if the <code>definition</code>
- specifier is used, then the ODB compiler will instead generate
- the database support code when we compile the header file containing
- this pragma.</p>
-
- <p>This mechanism is primarily useful for converting third-party
- types to ODB composite value types. In such cases we normally
- cannot modify the header files to add the necessary pragmas.
- It is also often inconvenient to compile these header files
- with the ODB compiler. With the <code>definition</code>
- specifier we can create a <em>wrapper header</em> that contains
- the necessary pragmas and instructs the ODB compiler to generate
- the database support code for a third-party type when we compile
- the wrapper header. As an example, consider <code>struct timeval</code>
- that is defined in the <code>&lt;sys/time.h></code> system header.
- This type has the following (or similar) definition:</p>
-
- <pre class="cxx">
-struct timeval
-{
- long tv_sec;
- long tv_usec;
-};
- </pre>
-
- <p>If we would like to make this type an ODB composite value type,
- then we can create a wrapper header, for example
- <code>time-mapping.hxx</code>, with the following content:</p>
-
- <pre class="cxx">
-#ifndef TIME_MAPPING_HXX
-#define TIME_MAPPING_HXX
-
-#include &lt;sys/time.h>
-
-#pragma db value(timeval) definition
-#pragma db member(timeval::tv_sec) column("sec")
-#pragma db member(timeval::tv_usec) column("usec")
-
-#endif // TIME_MAPPING_HXX
- </pre>
-
- <p>If we now compile this header with the ODB compiler, the
- resulting <code>time-mapping-odb.?xx</code> files will
- contain the database support code for <code>struct timeval</code>.
- To use <code>timeval</code> in our persistent classes, we simply
- include the <code>time-mapping.hxx</code> header:</p>
-
- <pre class="cxx">
-#include &lt;sys/time.h>
-#include "time-mapping.hxx"
-
-#pragma db object
-class object
-{
- timeval timestamp;
-};
- </pre>
-
- <h3><a name="14.3.8">14.3.8 <code>transient</code></a></h3>
-
- <p>The <code>transient</code> specifier instructs the ODB compiler
- to treat all non-virtual data members in the composite value type
- as transient (<a href="#14.4.1">Section 14.4.1,
- "<code>transient</code>"</a>). This specifier is primarily useful
- when declaring virtual data members, as discussed in
- <a href="#14.4.13">Section 14.4.13, "<code>virtual</code>"</a>.</p>
-
- <h3><a name="14.3.9">14.3.9 <code>unordered</code></a></h3>
-
- <p>The <code>unordered</code> specifier specifies that the ordered
- container should be stored unordered in the database. The database
- table for such a container will not contain the index column
- and the order in which elements are retrieved from the database may
- not be the same as the order in which they were stored. For example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;std::string> names;
-#pragma db value(names) unordered
- </pre>
-
- <p>For a more detailed discussion of ordered containers and their
- storage in the database, refer to <a href="#5.1">Section 5.1,
- "Ordered Containers"</a>.</p>
-
- <h3><a name="14.3.10">14.3.10 <code>index_type</code></a></h3>
-
- <p>The <code>index_type</code> specifier specifies the native
- database type that should be used for the ordered container's
- index column. The semantics of <code>index_type</code>
- are similar to those of the <code>type</code> specifier
- (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>). The native
- database type is expected to be an integer type. For example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;std::string> names;
-#pragma db value(names) index_type("SMALLINT UNSIGNED")
- </pre>
-
- <h3><a name="14.3.11">14.3.11 <code>key_type</code></a></h3>
-
- <p>The <code>key_type</code> specifier specifies the native
- database type that should be used for the map container's
- key column. The semantics of <code>key_type</code>
- are similar to those of the <code>type</code> specifier
- (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>). For
- example:</p>
-
- <pre class="cxx">
-typedef std::map&lt;unsigned short, float> age_weight_map;
-#pragma db value(age_weight_map) key_type("INT UNSIGNED")
- </pre>
-
- <h3><a name="14.3.12">14.3.12 <code>value_type</code></a></h3>
-
- <p>The <code>value_type</code> specifier specifies the native
- database type that should be used for the container's
- value column. The semantics of <code>value_type</code>
- are similar to those of the <code>type</code> specifier
- (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>). For
- example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;std::string> names;
-#pragma db value(names) value_type("VARCHAR(255)")
- </pre>
-
- <p>The <code>value_null</code> and <code>value_not_null</code>
- (<a href="#14.3.13">Section 14.3.13,
- "<code>value_null</code>/<code>value_not_null</code>"</a>) specifiers
- can be used to control the <code>NULL</code> semantics of a value
- column.</p>
-
- <h3><a name="14.3.13">14.3.13 <code>value_null</code>/<code>value_not_null</code></a></h3>
-
- <p>The <code>value_null</code> and <code>value_not_null</code> specifiers
- specify that the container type's element value can or cannot be
- <code>NULL</code>, respectively. The semantics of <code>value_null</code>
- and <code>value_not_null</code> are similar to those of the
- <code>null</code> and <code>not_null</code> specifiers
- (<a href="#14.3.3">Section 14.3.3, "<code>null</code>/<code>not_null</code>"</a>).
- For example:</p>
-
- <pre class="cxx">
-using std::tr1::shared_ptr;
-
-#pragma db object
-class account
-{
- ...
-};
-
-typedef std::vector&lt;shared_ptr&lt;account> > accounts;
-#pragma db value(accounts) value_not_null
- </pre>
-
- <p>For set and multiset containers (<a href="#5.2">Section 5.2, "Set and
- Multiset Containers"</a>) the element value is automatically treated
- as not allowing a <code>NULL</code> value.</p>
-
-
- <h3><a name="14.3.14">14.3.14 <code>id_options</code></a></h3>
-
- <p>The <code>id_options</code> specifier specifies additional
- column definition options that should be used for the container's
- id column. For example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;std::string> nicknames;
-#pragma db value(nicknames) id_options("COLLATE binary")
- </pre>
-
- <p>The semantics of the <code>id_options</code> specifier for a container
- type are similar to those of the <code>id_options</code> specifier for
- a container data member (<a href="#14.4.29">Section 14.4.29,
- "<code>id_options</code>"</a>).</p>
-
-
- <h3><a name="14.3.15">14.3.15 <code>index_options</code></a></h3>
-
- <p>The <code>index_options</code> specifier specifies additional
- column definition options that should be used for the container's
- index column. For example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;std::string> nicknames;
-#pragma db value(nicknames) index_options("ZEROFILL")
- </pre>
-
- <p>The semantics of the <code>index_options</code> specifier for a container
- type are similar to those of the <code>index_options</code> specifier for
- a container data member (<a href="#14.4.30">Section 14.4.30,
- "<code>index_options</code>"</a>).</p>
-
-
- <h3><a name="14.3.16">14.3.16 <code>key_options</code></a></h3>
-
- <p>The <code>key_options</code> specifier specifies additional
- column definition options that should be used for the container's
- key column. For example:</p>
-
- <pre class="cxx">
-typedef std::map&lt;std::string, std::string> properties;
-#pragma db value(properties) key_options("COLLATE binary")
- </pre>
-
- <p>The semantics of the <code>key_options</code> specifier for a container
- type are similar to those of the <code>key_options</code> specifier for
- a container data member (<a href="#14.4.31">Section 14.4.31,
- "<code>key_options</code>"</a>).</p>
-
-
- <h3><a name="14.3.17">14.3.17 <code>value_options</code></a></h3>
-
- <p>The <code>value_options</code> specifier specifies additional
- column definition options that should be used for the container's
- value column. For example:</p>
-
- <pre class="cxx">
-typedef std::set&lt;std::string> nicknames;
-#pragma db value(nicknames) value_options("COLLATE binary")
- </pre>
-
- <p>The semantics of the <code>value_options</code> specifier for a container
- type are similar to those of the <code>value_options</code> specifier for
- a container data member (<a href="#14.4.32">Section 14.4.32,
- "<code>value_options</code>"</a>).</p>
-
-
- <h3><a name="14.3.18">14.3.18 <code>id_column</code></a></h3>
-
- <p>The <code>id_column</code> specifier specifies the column
- name that should be used to store the object id in the
- container's table. For example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;std::string> names;
-#pragma db value(names) id_column("id")
- </pre>
-
- <p>If the column name is not specified, then <code>object_id</code>
- is used by default.</p>
-
- <h3><a name="14.3.19">14.3.19 <code>index_column</code></a></h3>
-
- <p>The <code>index_column</code> specifier specifies the column
- name that should be used to store the element index in the
- ordered container's table. For example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;std::string> names;
-#pragma db value(names) index_column("name_number")
- </pre>
-
- <p>If the column name is not specified, then <code>index</code>
- is used by default.</p>
-
- <h3><a name="14.3.20">14.3.20 <code>key_column</code></a></h3>
-
- <p>The <code>key_column</code> specifier specifies the column
- name that should be used to store the key in the map
- container's table. For example:</p>
-
- <pre class="cxx">
-typedef std::map&lt;unsigned short, float> age_weight_map;
-#pragma db value(age_weight_map) key_column("age")
- </pre>
-
- <p>If the column name is not specified, then <code>key</code>
- is used by default.</p>
-
- <h3><a name="14.3.21">14.3.21 <code>value_column</code></a></h3>
-
- <p>The <code>value_column</code> specifier specifies the column
- name that should be used to store the element value in the
- container's table. For example:</p>
-
- <pre class="cxx">
-typedef std::map&lt;unsigned short, float> age_weight_map;
-#pragma db value(age_weight_map) value_column("weight")
- </pre>
-
- <p>If the column name is not specified, then <code>value</code>
- is used by default.</p>
-
- <!-- Data Member Pragmas -->
-
-
- <h2><a name="14.4">14.4 Data Member Pragmas</a></h2>
-
- <p>A pragma with the <code>member</code> qualifier or a positioned
- pragma without a qualifier describes a data member. It can
- be optionally followed, in any order, by one or more specifiers
- summarized in the table below:</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table class="specifiers" border="1">
- <tr>
- <th>Specifier</th>
- <th>Summary</th>
- <th>Section</th>
- </tr>
-
- <tr>
- <td><code>id</code></td>
- <td>member is an object id</td>
- <td><a href="#14.4.1">14.4.1</a></td>
- </tr>
-
- <tr>
- <td><code>auto</code></td>
- <td>id is assigned by the database</td>
- <td><a href="#14.4.2">14.4.2</a></td>
- </tr>
-
- <tr>
- <td><code>type</code></td>
- <td>database type for a member</td>
- <td><a href="#14.4.3">14.4.3</a></td>
- </tr>
-
- <tr>
- <td><code>id_type</code></td>
- <td>database type for a member when used as an object id</td>
- <td><a href="#14.4.4">14.4.4</a></td>
- </tr>
-
- <tr>
- <td><code>get</code>/<code>set</code>/<code>access</code></td>
- <td>member accessor/modifier expressions</td>
- <td><a href="#14.4.5">14.4.5</a></td>
- </tr>
-
- <tr>
- <td><code>null</code>/<code>not_null</code></td>
- <td>member can/cannot be <code>NULL</code></td>
- <td><a href="#14.4.6">14.4.6</a></td>
- </tr>
-
- <tr>
- <td><code>default</code></td>
- <td>default value for a member</td>
- <td><a href="#14.4.7">14.4.7</a></td>
- </tr>
-
- <tr>
- <td><code>options</code></td>
- <td>database options for a member</td>
- <td><a href="#14.4.8">14.4.8</a></td>
- </tr>
-
- <tr>
- <td><code>column</code></td>
- <td>column name for a member of an object or composite value</td>
- <td><a href="#14.4.9">14.4.9</a></td>
- </tr>
-
- <tr>
- <td><code>column</code></td>
- <td>column name for a member of a view</td>
- <td><a href="#14.4.10">14.4.10</a></td>
- </tr>
-
- <tr>
- <td><code>transient</code></td>
- <td>member is not stored in the database</td>
- <td><a href="#14.4.11">14.4.11</a></td>
- </tr>
-
- <tr>
- <td><code>readonly</code></td>
- <td>member is read-only</td>
- <td><a href="#14.4.12">14.4.12</a></td>
- </tr>
-
- <tr>
- <td><code>virtual</code></td>
- <td>declare a virtual data member</td>
- <td><a href="#14.4.13">14.4.13</a></td>
- </tr>
-
- <tr>
- <td><code>inverse</code></td>
- <td>member is an inverse side of a bidirectional relationship</td>
- <td><a href="#14.4.14">14.4.14</a></td>
- </tr>
-
- <tr>
- <td><code>on_delete</code></td>
- <td><code>ON DELETE</code> clause for object pointer member</td>
- <td><a href="#14.4.15">14.4.15</a></td>
- </tr>
-
- <tr>
- <td><code>version</code></td>
- <td>member stores object version</td>
- <td><a href="#14.4.16">14.4.16</a></td>
- </tr>
-
- <tr>
- <td><code>index</code></td>
- <td>define database index for a member</td>
- <td><a href="#14.4.17">14.4.17</a></td>
- </tr>
-
- <tr>
- <td><code>unique</code></td>
- <td>define unique database index for a member</td>
- <td><a href="#14.4.18">14.4.18</a></td>
- </tr>
-
- <tr>
- <td><code>unordered</code></td>
- <td>ordered container should be stored unordered</td>
- <td><a href="#14.4.19">14.4.19</a></td>
- </tr>
-
- <tr>
- <td><code>table</code></td>
- <td>table name for a container</td>
- <td><a href="#14.4.20">14.4.20</a></td>
- </tr>
-
- <tr>
- <td><code>load</code>/<code>update</code></td>
- <td>loading/updating behavior for a section</td>
- <td><a href="#14.4.21">14.4.21</a></td>
- </tr>
-
- <tr>
- <td><code>section</code></td>
- <td>member belongs to a section</td>
- <td><a href="#14.4.22">14.4.22</a></td>
- </tr>
-
- <tr>
- <td><code>added</code></td>
- <td>member is soft-added</td>
- <td><a href="#14.4.23">14.4.23</a></td>
- </tr>
-
- <tr>
- <td><code>deleted</code></td>
- <td>member is soft-deleted</td>
- <td><a href="#14.4.24">14.4.24</a></td>
- </tr>
-
- <tr>
- <td><code>index_type</code></td>
- <td>database type for a container's index type</td>
- <td><a href="#14.4.25">14.4.25</a></td>
- </tr>
-
- <tr>
- <td><code>key_type</code></td>
- <td>database type for a container's key type</td>
- <td><a href="#14.4.26">14.4.26</a></td>
- </tr>
-
- <tr>
- <td><code>value_type</code></td>
- <td>database type for a container's value type</td>
- <td><a href="#14.4.27">14.4.27</a></td>
- </tr>
-
- <tr>
- <td><code>value_null</code>/<code>value_not_null</code></td>
- <td>container's value can/cannot be <code>NULL</code></td>
- <td><a href="#14.4.28">14.4.28</a></td>
- </tr>
-
- <tr>
- <td><code>id_options</code></td>
- <td>database options for a container's id column</td>
- <td><a href="#14.4.29">14.4.29</a></td>
- </tr>
-
- <tr>
- <td><code>index_options</code></td>
- <td>database options for a container's index column</td>
- <td><a href="#14.4.30">14.4.30</a></td>
- </tr>
-
- <tr>
- <td><code>key_options</code></td>
- <td>database options for a container's key column</td>
- <td><a href="#14.4.31">14.4.31</a></td>
- </tr>
-
- <tr>
- <td><code>value_options</code></td>
- <td>database options for a container's value column</td>
- <td><a href="#14.4.32">14.4.32</a></td>
- </tr>
-
- <tr>
- <td><code>id_column</code></td>
- <td>column name for a container's object id</td>
- <td><a href="#14.4.33">14.4.33</a></td>
- </tr>
-
- <tr>
- <td><code>index_column</code></td>
- <td>column name for a container's index</td>
- <td><a href="#14.4.34">14.4.34</a></td>
- </tr>
-
- <tr>
- <td><code>key_column</code></td>
- <td>column name for a container's key</td>
- <td><a href="#14.4.35">14.4.35</a></td>
- </tr>
-
- <tr>
- <td><code>value_column</code></td>
- <td>column name for a container's value</td>
- <td><a href="#14.4.36">14.4.36</a></td>
- </tr>
-
- </table>
-
- <p>Many of the member specifiers have corresponding value type
- specifiers with the same names (<a href="#14.3">Section 14.3,
- "Value Type Pragmas"</a>). The behavior of such specifiers
- for members is similar to that for value types. The only difference
- is the scope. A particular value type specifier applies to all the
- members of this value type that don't have a pre-member version
- of the specifier, while the member specifier always applies only
- to a single member. Also, with a few exceptions, member specifiers
- take precedence over and override parameters specified with value
- specifiers.</p>
-
- <h3><a name="14.4.1">14.4.1 <code>id</code></a></h3>
-
- <p>The <code>id</code> specifier specifies that the data member contains
- the object id. In a relational database, an identifier member is
- mapped to a primary key. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db id
- std::string email_;
-};
- </pre>
-
- <p>Normally, every persistent class has a data member designated as an
- object's identifier. However, it is possible to declare a
- persistent class without an id using the object <code>no_id</code>
- specifier (<a href="#14.1.6">Section 14.1.6, "<code>no_id</code>"</a>).</p>
-
- <p>Note also that the <code>id</code> specifier cannot be used for data
- members of composite value types or views.</p>
-
- <h3><a name="14.4.2">14.4.2 <code>auto</code></a></h3>
-
- <p>The <code>auto</code> specifier specifies that the object's identifier
- is automatically assigned by the database. Only a member that was
- designated as an object id can have this specifier. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db id auto
- unsigned long id_;
-};
- </pre>
-
- <p>Note that automatically-assigned object ids are not reused.
- If you have a high object turnover (that is, objects are routinely
- made persistent and then erased), then care must be taken not to
- run out of object ids. In such situations, using
- <code>unsigned&nbsp;long&nbsp;long</code> as the identifier type
- is a safe choice.</p>
-
- <p>For additional information on the automatic identifier assignment,
- refer to <a href="#3.8">Section 3.8, "Making Objects Persistent"</a>.</p>
-
- <p>Note also that the <code>auto</code> specifier cannot be specified
- for data members of composite value types or views.</p>
-
- <h3><a name="14.4.3">14.4.3 <code>type</code></a></h3>
-
- <p>The <code>type</code> specifier specifies the native database type
- that should be used for the data member. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db type("INT")
- bool married_;
-};
- </pre>
-
- <p>The <code>null</code> and <code>not_null</code> (<a href="#14.4.6">Section
- 14.4.6, "<code>null</code>/<code>not_null</code>"</a>) specifiers
- can be used to control the <code>NULL</code> semantics of a data member.
- It is also possible to specify the database type on the per-type instead
- of the per-member basis using the value <code>type</code>
- specifier (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>).</p>
-
- <h3><a name="14.4.4">14.4.4 <code>id_type</code></a></h3>
-
- <p>The <code>id_type</code> specifier specifies the native database type
- that should be used for the data member when it is part of an
- object identifier. This specifier only makes sense when applied to
- a member of a composite value type that is used for both id and
- non-id members. For example:</p>
-
- <pre class="cxx">
-#pragma db value
-class name
-{
- ...
-
- #pragma db type("VARCHAR(256)") id_type("VARCHAR(64)")
- std::string first_;
-
- #pragma db type("VARCHAR(256)") id_type("VARCHAR(64)")
- std::string last_;
-};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id
- name name_; // name_.first_, name_.last_ mapped to VARCHAR(64)
-
- name alias_; // alias_.first_, alias_.last_ mapped to VARCHAR(256)
-};
- </pre>
-
- <h3><a name="14.4.5">14.4.5 <code>get</code>/<code>set</code>/<code>access</code></a></h3>
-
- <p>The <code>get</code> and <code>set</code> specifiers specify the
- data member accessor and modifier expressions, respectively. If
- provided, the generated database support code will use these
- expressions to access and modify the data member when performing
- database operations. The <code>access</code> specifier can be used
- as a shortcut to specify both the accessor and modifier if they
- happen to be the same.</p>
-
- <p>In its simplest form the accessor or modifier expression can be
- just a name. Such a name should resolve either to another data
- member of the same type or to a suitable accessor or modifier
- member function. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
-public:
- const std::string&amp; name () const;
- void name (const std::string&amp;);
-private:
- #pragma db access(name)
- std::string name_;
-};
- </pre>
-
- <p>A suitable accessor function is a <code>const</code> member function
- that takes no arguments and whose return value can be implicitly
- converted to the <code>const</code> reference to the member type
- (<code>const&nbsp;std::string&amp;</code> in the example above).
- An accessor function that returns a <code>const</code> reference
- to the data member is called <em>by-reference accessor</em>.
- Otherwise, it is called <em>by-value accessor</em>.</p>
-
- <p>A suitable modifier function can be of two forms. It can be the
- so called <em>by-reference modifier</em> which is a member function
- that takes no arguments and returns a non-<code>const</code> reference
- to the data member (<code>std::string&amp;</code> in the example above).
- Alternatively, it can be the so called <em>by-value modifier</em> which
- is a member function taking a single argument &mdash; the new value
- &mdash; that can be implicitly initialized from a variable of the member
- type (<code>std::string</code> in the example above). The return value
- of a by-value modifier, if any, is ignored. If both by-reference and
- by-value modifiers are available, then ODB prefers the by-reference
- version since it is more efficient. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
-public:
- std::string get_name () const; // By-value accessor.
- std::string&amp; set_name (); // By-reference modifier.
- void set_name (std::string const&amp;); // By-value modifier.
-private:
- #pragma db get(get_name) \ // Uses by-value accessor.
- set(set_name) // Uses by-reference modifier.
- std::string name_;
-};
- </pre>
-
- <p>Note that in many cases it is not necessary to specify accessor and
- modifier functions explicitly since the ODB compiler will try to
- discover them automatically in case the data member will be inaccessible
- to the generated code. In particular, in both of the above examples
- the ODB compiler would have successfully discovered the necessary
- functions. For more information on this functionality, refer to
- <a href="#3.2">Section 3.2, "Declaring Persistent Objects and
- Values"</a>.</p>
-
- <p>Note also that by-value accessors and by-value modifiers cannot be
- used for certain data members in certain situations. These limitations
- are discussed in more detail later in this section.</p>
-
- <p>Accessor and modifier expressions can be more elaborate than simple
- names. An accessor expression is any C++ expression that can be
- used to initialize a <code>const</code> reference to the member
- type. Similar to accessor functions, which are just a special case
- of accessor expressions, an accessor expression that evaluates to a
- <code>const</code> reference to the data member is called
- <em>by-reference accessor expression</em>. Otherwise, it is
- called <em>by-value accessor expression</em>.</p>
-
- <p>Modifier expressions can also be of two forms: <em>by-reference
- modifier expression</em> and <em>by-value modifier expression</em>
- (again, modifier functions are just a special case of modifier
- expressions). A by-reference modifier expression is any C++
- expression that evaluates to the non-<code>const</code> reference
- to the member type. A by-value modifier expression can be a
- single or multiple (separated by semicolon) C++ statements
- with the effect of setting the new member value.</p>
-
- <p>There are two special placeholders that are recognized by the
- ODB compiler in accessor and modifier expressions. The first
- is the <code>this</code> keyword which denotes a reference
- (note: not a pointer) to the persistent object. In accessor
- expressions this reference is <code>const</code> while in
- modifier expressions it is non-<code>const</code>. If an
- expression does not contain the <code>this</code> placeholder,
- then the ODB compiler automatically prefixes it with <code>this.</code>
- sequence.</p>
-
- <p>The second placeholder, the <code>(?)</code> sequence, is used
- to denote the new value in by-value modifier expressions. The
- ODB compiler replaces the question mark with the variable name,
- keeping the surrounding parenthesis. The following example shows
- a few more interesting accessor and modifier expressions:</p>
-
- <pre class="cxx">
-#pragma db value
-struct point
-{
- point (int, int);
-
- int x;
- int y;
-};
-
-#pragma db object
-class person
-{
- ...
-
- public:
- const char* name () const;
- void name (const char*);
- private:
- #pragma db get(std::string (this.name ())) \
- set(name ((?).c_str ())) // The same as this.name (...).
- std::string name_;
-
- public:
- const std::unique_ptr&lt;account>&amp; acc () const;
- void acc (std::unique_ptr&lt;account>);
- private:
- #pragma db set(acc (std::move (?)))
- std::unique_ptr&lt;account> acc_;
-
- public:
- int loc_x () const
- int loc_y () const
- void loc_x (int);
- void loc_y (int);
- private:
- #pragma db get(point (this.loc_x (), this.loc_y ())) \
- set(this.loc_x ((?).x); this.loc_y ((?).y))
- point loc_;
-};
- </pre>
-
- <p>When the data member is of an array type, then the terms "reference"
- and "member type" in the above discussion should be replaced with
- "pointer" and "array element type", respectively. That is, the accessor
- expression for an array member is any C++ expression that can be
- used to initialize a <code>const</code> pointer to the array
- element type, and so on. The following example shows common
- accessor and modifier signatures for array members:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- public:
- const char* id () const; // By-reference accessor.
- void id (const char*); // By-value modifier.
- private:
- char id_[16];
-
- public:
- const char* pub_key () const; // By-reference accessor.
- char* pub_key (); // By-reference modifier.
- private:
- char pub_key_[2048];
-};
- </pre>
-
- <p>Accessor and modifier expressions can be used with data members
- of simple value, composite value, container, and object pointer
- types. They can be used for data members in persistent classes,
- composite value types, and views. There is also a mechanism
- related to accessors and modifiers called virtual data members
- and which is discussed in <a href="#14.4.13">Section 14.4.13,
- "<code>virtual</code>"</a>.</p>
-
- <p>There are, however, certain limitations when it comes to using
- by-value accessor and modifier expressions. First of all, if a
- by-value modifier is used, then the data member type should be
- default-constructible. Furthermore, a composite value type that
- has a container member cannot be modified with a by-value modifier.
- Only a by-reference modifier expression can be used. The ODB
- compiler will detect such cases and issue diagnostics. For
- example:</p>
-
- <pre class="cxx">
-#pragma db value
-struct name
-{
- std::string first_;
- std::string last_;
- std::vector&lt;std::string> aliases_;
-};
-
-#pragma db object
-class person
-{
- ...
-
-public:
- const name&amp; name () const;
- void name (const name&amp;);
-private:
- #pragma db access(name) // Error: by-value modifier.
- name name_;
-};
- </pre>
-
- <p>In certain database systems it is also not possible to use by-value
- accessor and modifier expression with certain database types.
- The ODB compiler is only able to detect such cases and issue diagnostics
- if you specified accessor/modifier function names as opposed to custom
- expressions. For more information on these database and type-specific
- limitations, refer to the "Limitations" sections in <a href="#II">Part
- II, "Database Systems"</a>.</p>
-
- <h3><a name="14.4.6">14.4.6 <code>null</code>/<code>not_null</code></a></h3>
-
- <p>The <code>null</code> and <code>not_null</code> specifiers specify that
- the data member can or cannot be <code>NULL</code>, respectively.
- By default, data members of basic value types for which database
- mapping is provided by the ODB compiler do not allow <code>NULL</code>
- values while data members of object pointers allow <code>NULL</code>
- values. Other value types, such as those provided by the profile
- libraries (<a href="#III">Part III, "Profiles"</a>), may or may
- not allow <code>NULL</code> values, depending on the semantics
- of each value type. Consult the relevant documentation to find
- out more about the <code>NULL</code> semantics for such value
- types. A data member containing the object id (<a href="#14.4.1">Section
- 14.4.1, "<code>id</code>"</a>) is automatically treated as not
- allowing a <code>NULL</code> value. Data members that
- allow <code>NULL</code> values are mapped in a relational database
- to columns that allow <code>NULL</code> values. For example:</p>
-
- <pre class="cxx">
-using std::tr1::shared_ptr;
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db null
- std::string name_;
-};
-
-#pragma db object
-class account
-{
- ...
-
- #pragma db not_null
- shared_ptr&lt;person> holder_;
-};
- </pre>
-
- <p>The <code>NULL</code> semantics can also be specified on the
- per-type basis (<a href="#14.3.3">Section 14.3.3,
- "<code>null</code>/<code>not_null</code>"</a>). If both a type and
- a member have <code>null</code>/<code>not_null</code> specifiers,
- then the member specifier takes precedence. If a member specifier
- relaxes the <code>NULL</code> semantics (that is, if a member has
- the <code>null</code> specifier and the type has the explicit
- <code>not_null</code> specifier), then a warning is issued.</p>
-
- <p>For a more detailed discussion of the <code>NULL</code> semantics
- for values, refer to <a href="#7.3">Section 7.3, "Pointers and
- <code>NULL</code> Value Semantics"</a>. For a more detailed
- discussion of the <code>NULL</code> semantics for object pointers,
- refer to <a href="#6">Chapter 6, "Relationships"</a>.</p>
-
- <h3><a name="14.4.7">14.4.7 <code>default</code></a></h3>
-
- <p>The <code>default</code> specifier specifies the database default value
- that should be used for the data member. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db default(-1)
- int age_; // Mapped to INT NOT NULL DEFAULT -1.
-};
- </pre>
-
- <p>A default value can be the special <code>null</code> keyword,
- a <code>bool</code> literal (<code>true</code> or <code>false</code>),
- an integer literal, a floating point literal, a string literal, or
- an enumerator name. If you need to specify a default value that is
- an expression, for example an SQL function call, then you can use
- the <code>options</code> specifier (<a href="#14.4.8">Section
- 14.4.8, "<code>options</code>"</a>) instead. For example:</p>
-
- <pre class="cxx">
-enum gender {male, female, undisclosed};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db default(null)
- odb::nullable&lt;std::string> middle_; // DEFAULT NULL
-
- #pragma db default(false)
- bool married_; // DEFAULT 0/FALSE
-
- #pragma db default(0.0)
- float weight_; // DEFAULT 0.0
-
- #pragma db default("Mr")
- string title_; // DEFAULT 'Mr'
-
- #pragma db default(undisclosed)
- gender gender_; // DEFAULT 2/'undisclosed'
-
- #pragma db options("DEFAULT CURRENT_TIMESTAMP()")
- date timestamp_; // DEFAULT CURRENT_TIMESTAMP()
-};
- </pre>
-
- <p>Default values specified as enumerators are only supported for
- members that are mapped to an <code>ENUM</code> or an integer
- type in the database, which is the case for the automatic
- mapping of C++ enums and enum classes to suitable database
- types as performed by the ODB compiler. If you have mapped
- a C++ enum or enum class to another database type, then you
- should use a literal corresponding to that type to specify
- the default value. For example:</p>
-
- <pre class="cxx">
-enum gender {male, female, undisclosed};
-#pragma db value(gender) type("VARCHAR(11)")
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db default("undisclosed")
- gender gender_; // DEFAULT 'undisclosed'
-};
- </pre>
-
- <p>A default value can also be specified on the per-type basis
- (<a href="#14.3.4">Section 14.3.4, "<code>default</code>"</a>).
- An empty <code>default</code> specifier can be used to reset
- a default value that was previously specified on the per-type
- basis. For example:</p>
-
- <pre class="cxx">
-#pragma db value(std::string) default("")
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db default()
- std::string name_; // No default value.
-};
- </pre>
-
- <p>A data member containing the object id (<a href="#14.4.1">Section
- 14.4.1, "<code>id</code>"</a> ) is automatically treated as not
- having a default value even if its type specifies a default value.</p>
-
- <p>Note also that default values do not affect the generated C++ code
- in any way. In particular, no automatic initialization of data members
- with their default values is performed at any point. If you need such
- an initialization, you will need to implement it yourself, for example,
- in your persistent class constructors. The default values only
- affect the generated database schemas and, in the context of ODB,
- are primarily useful for schema evolution.</p>
-
- <p>Additionally, the <code>default</code> specifier cannot be specified
- for view data members.</p>
-
- <h3><a name="14.4.8">14.4.8 <code>options</code></a></h3>
-
- <p>The <code>options</code> specifier specifies additional column
- definition options that should be used for the data member. For
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db options("CHECK(email != '')")
- std::string email_; // Mapped to TEXT NOT NULL CHECK(email != '').
-};
- </pre>
-
- <p>Options can also be specified on the per-type basis
- (<a href="#14.3.5">Section 14.3.5, "<code>options</code>"</a>).
- By default, options are accumulating. That is, the ODB compiler
- first adds all the options specified for a value type followed
- by all the options specified for a data member. To clear the
- accumulated options at any point in this sequence you can use
- an empty <code>options</code> specifier. For example:</p>
-
- <pre class="cxx">
-#pragma db value(std::string) options("COLLATE binary")
-
-#pragma db object
-class person
-{
- ...
-
- std::string first_; // TEXT NOT NULL COLLATE binary
-
- #pragma db options("CHECK(last != '')")
- std::string last_; // TEXT NOT NULL COLLATE binary CHECK(last != '')
-
- #pragma db options()
- std::string title_; // TEXT NOT NULL
-
- #pragma db options() options("CHECK(email != '')")
- std::string email_; // TEXT NOT NULL CHECK(email != '')
-};
- </pre>
-
- <p>ODB provides dedicated specifiers for specifying column types
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>),
- <code>NULL</code> constraints (<a href="#14.4.6">Section 14.4.6,
- "<code>null</code>/<code>not_null</code>"</a>), and default
- values (<a href="#14.4.7">Section 14.4.7, "<code>default</code>"</a>).
- For ODB to function correctly these specifiers should always be
- used instead of the opaque <code>options</code> specifier for
- these components of a column definition.</p>
-
- <p>Note also that the <code>options</code> specifier cannot be specified
- for view data members.</p>
-
- <h3><a name="14.4.9">14.4.9 <code>column</code> (object, composite value)</a></h3>
-
- <p>The <code>column</code> specifier specifies the column name
- that should be used to store the data member of a persistent class
- or composite value type in a relational database. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db id column("person_id")
- unsigned long id_;
-};
- </pre>
-
- <p>For a member of a composite value type, the <code>column</code> specifier
- specifies the column name prefix. Refer to <a href="#7.2.2">Section 7.2.2,
- "Composite Value Column and Table Names"</a> for details.</p>
-
- <p>If the column name is not specified, it is derived from the member's
- so-called public name. A public member name is obtained by removing
- the common data member name decorations, such as leading and trailing
- underscores, the <code>m_</code> prefix, etc.</p>
-
- <h3><a name="14.4.10">14.4.10 <code>column</code> (view)</a></h3>
-
- <p>The <code>column</code> specifier can be used to specify the associated
- object data member, the potentially qualified column name, or the column
- expression for the data member of a view class. For more information,
- refer to <a href="#10.1">Section 10.1, "Object Views"</a> and
- <a href="#10.3">Section 10.3, "Table Views"</a>.</p>
-
- <h3><a name="14.4.11">14.4.11 <code>transient</code></a></h3>
-
- <p>The <code>transient</code> specifier instructs the ODB compiler
- not to store the data member in the database. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- date born_;
-
- #pragma db transient
- unsigned short age_; // Computed from born_.
-};
- </pre>
-
- <p>This pragma is usually used on computed members, pointers and
- references that are only meaningful in the application's
- memory, as well as utility members such as mutexes, etc.</p>
-
- <h3><a name="14.4.12">14.4.12 <code>readonly</code></a></h3>
-
- <p>The <code>readonly</code> specifier specifies that the data member of
- an object or composite value type is read-only. Changes to a read-only
- data member are ignored when updating the database state of an object
- (<a href="#3.10">Section 3.10, "Updating Persistent Objects"</a>)
- containing such a member. Since views are read-only, it is not
- necessary to use this specifier for view data members. Object id
- (<a href="#14.4.1">Section 14.4.1, "<code>id</code>"</a>)
- and inverse (<a href="#14.4.14">Section 14.4.14,
- "<code>inverse</code>"</a>) data members are automatically treated
- as read-only and must not be explicitly declared as such. For
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db readonly
- date born_;
-};
- </pre>
-
- <p>Besides simple value members, object pointer, container, and composite
- value members can also be declared read-only. A change of a pointed-to
- object is ignored when updating the state of a read-only object
- pointer. Similarly, any changes to the number or order of
- elements or to the element values themselves are ignored when
- updating the state of a read-only container. Finally, any changes
- to the members of a read-only composite value type are also ignored
- when updating the state of such a composite value.</p>
-
- <p>ODB automatically treats <code>const</code> data members as read-only.
- For example, the following <code>person</code> object is equivalent
- to the above declaration for the database persistence purposes:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- const date born_; // Automatically read-only.
-};
- </pre>
-
- <p>When declaring an object pointer <code>const</code>, make sure to
- declare the pointer as <code>const</code> rather than (or in addition
- to) the object itself. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- const person* father_; // Read-write pointer to a read-only object.
- person* const mother_; // Read-only pointer to a read-write object.
-};
- </pre>
-
- <p>Note that in case of a wrapper type (<a href="#7.3">Section 7.3,
- "Pointers and <code>NULL</code> Value Semantics"</a>), both the
- wrapper and the wrapped type must be <code>const</code> in
- order for the ODB compiler to automatically treat the data
- member as read-only. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- const std::auto_ptr&lt;const date> born_;
-};
- </pre>
-
- <p>Read-only members are useful when dealing with
- asynchronous changes to the state of a data member in the
- database which should not be overwritten. In other cases,
- where the state of a data member never changes, declaring such a member
- read-only allows ODB to perform more efficient object updates.
- In such cases, however, it is conceptually more correct to
- declare such a data member as <code>const</code> rather than
- as read-only.</p>
-
- <p>Note that it is also possible to declare composite value types
- (<a href="#14.3.6">Section 14.3.6, "<code>readonly</code>"</a>)
- as well as whole objects (<a href="#14.1.4">Section 14.1.4,
- "<code>readonly</code>"</a>) as read-only.</p>
-
- <h3><a name="14.4.13">14.4.13 <code>virtual</code></a></h3>
-
- <p>The <code>virtual</code> specifier is used to declare a virtual
- data member in an object, view, or composite value type. A virtual
- data member is an <em>imaginary</em> data member that is only
- used for the purpose of database persistence. A virtual data
- member does not actually exist (that is, occupy space) in the
- C++ class. Note also that virtual data members have nothing to
- do with C++ virtual functions or virtual inheritance. Specifically,
- no virtual function call overhead is incurred when using virtual
- data members.</p>
-
- <p>To declare a virtual data member we must specify the data
- member name using the <code>member</code> specifier. We must
- also specify the data member type with the <code>virtual</code>
- specifier. Finally, the virtual data member declaration must
- also specify the accessor and modifier expressions, unless
- suitable accessor and modifier functions can automatically be
- found by the ODB compiler (<a href="#14.4.5">Section 14.4.5,
- "<code>get</code>/<code>set</code>/<code>access</code>"</a>).
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- // Transient real data member that actually stores the data.
- //
- #pragma db transient
- std::string name_;
-
- // Virtual data member.
- //
- #pragma db member(name) virtual(std::string) access(name_)
-};
- </pre>
-
- <p>From the pragma language point of view, a virtual data member
- behaves exactly like a normal data member. Specifically, we
- can reference the virtual data member after it has been
- declared and use positioned pragmas before its declaration.
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db transient
- std::string name_;
-
- #pragma db access(name_)
- #pragma db member(name) virtual(std::string)
-};
-
-#pragma db member(person::name) column("person_name")
-#pragma db index member(person::name)
- </pre>
-
- <p>We can also declare a virtual data member outside the class
- scope:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- std::string name_;
-};
-
-#pragma db member(person::name_) transient
-#pragma db member(person::name) virtual(std::string) access(name_)
- </pre>
-
- <p>While in the above examples using virtual data members doesn't
- seem to yield any benefits, this mechanism can be useful in a
- number of situations. As one example, consider the need to
- aggregate or dis-aggregate a data member:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db transient
- std::pair&lt;std::string, std::string> name_;
-
- #pragma db member(first) virtual(std::string) access(name_.first)
- #pragma db member(last) virtual(std::string) access(name_.second)
-};
- </pre>
-
- <p>We can also use virtual data members to implement composite
- object ids that are spread over multiple data members:</p>
-
- <pre class="cxx">
-#pragma db value
-struct name
-{
- name () {}
- name (std::string const&amp; f, std::string const&amp; l)
- : first (f), last(l) {}
-
- std::string first;
- std::string last;
-};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db transient
- std::string first_;
-
- #pragma db transient
- std::string last_;
-
- #pragma db member(name) virtual(name) id \
- get(::name (this.first_, this.last_)) \
- set(this.first_ = (?).first; this.last_ = (?).last)
-};
- </pre>
-
- <p>Another common situation that calls for virtual data members is
- a class that uses the pimpl idiom. While the following code
- fragment outlines the idea, for details refer to the
- <code>pimpl</code> example in the <code>odb-examples</code>
- package.</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
-public:
- std::string const&amp; name () const;
- void name (std::string const&amp;);
-
- unsigned short age () const;
- void age (unsigned short);
-
- ...
-
-private:
- class impl;
-
- #pragma db transient
- impl* pimpl_;
-
- #pragma db member(name) virtual(std::string) // Uses name().
- #pragma db member(age) virtual(unsigned short) // Uses age().
-};
- </pre>
-
- <p>The above example also shows that names used for virtual data
- members (<code>name</code> and <code>age</code> in our case) can
- be the same as the names of accessor/modifier functions. The only
- names that virtual data members cannot clash with are those of
- other data members, virtual or real.</p>
-
- <p>A common pattern in the above examples is the need to
- declare the real data member that actually stores the
- data as transient. If all the real data members in a
- class are treated as transient, then we can use the
- class-level <code>transient</code> specifier
- (<a href="#14.1.12">Section 14.1.12, "<code>transient</code>
- (object)"</a>,
- <a href="#14.3.8">Section 14.3.8, "<code>transient</code>
- (composite value)"</a>,
- <a href="#14.2.7">Section 14.2.7, "<code>transient</code>
- (view)"</a>)
- instead of doing it for each individual member. For example: </p>
-
- <pre class="cxx">
-#pragma db object transient
-class person
-{
- ...
-
- std::string first_; // Transient.
- std::string last_; // Transient.
-
- #pragma db member(name) virtual(name) ...
-};
- </pre>
-
- <p>The ability to treat all the real data members as transient
- becomes more important if we don't know the names of these
- data members. This is often the case when we are working
- with third-party types that document the accessor and
- modifier functions but not the names of their private data
- members. As an example, consider the <code>point</code> class
- defined in a third-party <code>&lt;point></code> header file:</p>
-
- <pre class="cxx">
-class point
-{
-public:
- point ();
- point (int x, int y);
-
- int x () const;
- int y () const;
-
- void x (int);
- void y (int);
-
-private:
- ...
-};
- </pre>
-
- <p>To convert this class to an ODB composite value type we could
- create the <code>point-mapping.hxx</code> file with the following
- content:</p>
-
- <pre class="cxx">
-#include &lt;point>
-
-#pragma db value(point) transient definition
-#pragma db member(point::x) virtual(int)
-#pragma db member(point::y) virtual(int)
- </pre>
-
- <p>Virtual data members can be of simple value, composite value,
- container, or object pointer types. They can be used in persistent
- classes, composite value types, and views.</p>
-
- <h3><a name="14.4.14">14.4.14 <code>inverse</code></a></h3>
-
- <p>The <code>inverse</code> specifier specifies that the data member of
- an object pointer or a container of object pointers type is an
- inverse side of a bidirectional object relationship. The single
- required argument to this specifier is the corresponding data
- member name in the referenced object. For example:</p>
-
- <pre class="cxx">
-using std::tr1::shared_ptr;
-using std::tr1::weak_ptr;
-
-class person;
-
-#pragma db object pointer(shared_ptr)
-class employer
-{
- ...
-
- std::vector&lt;shared_ptr&lt;person> > employees_;
-};
-
-#pragma db object pointer(shared_ptr)
-class person
-{
- ...
-
- #pragma db inverse(employee_)
- weak_ptr&lt;employer> employer_;
-};
- </pre>
-
- <p>An inverse member does not have a corresponding column or, in case
- of a container, table in the resulting database schema. Instead, the
- column or table from the referenced object is used to retrieve the
- relationship information. Only ordered and set containers can be used
- for inverse members. If an inverse member is of an ordered container
- type, it is automatically marked as unordered
- (<a href="#14.4.19">Section 14.4.19, "<code>unordered</code>"</a>).</p>
-
- <p>For a more detailed discussion of inverse members, refer to
- <a href="#6.2">Section 6.2, "Bidirectional Relationships"</a>.</p>
-
- <h3><a name="14.4.15">14.4.15 <code>on_delete</code></a></h3>
-
- <p>The <code>on_delete</code> specifier specifies the on-delete semantics
- for a data member of an object pointer or a container of object
- pointers type. The single required argument to this specifier must
- be either <code>cascade</code> or <code>set_null</code>.</p>
-
- <p>The <code>on_delete</code> specifier is translated directly to the
- corresponding <code>ON DELETE</code> SQL clause. That is, if
- <code>cascade</code> is specified, then when a pointed-to object
- is erased from the database, the database state of the pointing
- object is automatically erased as well. If <code>set_null</code> is
- specified, then when a pointed-to object is erased from the database,
- the database state of the pointing object is automatically updated
- to set the pointer column to <code>NULL</code>. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class employer
-{
- ...
-
- #pragma db id auto
- unsigned long id_;
-};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db on_delete(cascade)
- employer* employer_;
-};
-
-unsigned long id;
-
-{
- employer e;
- person p;
- p.employer_ = &amp;e;
-
- transaction t (db.begin ());
-
- id = db.persist (e);
- db.persist (p);
-
- t.commit ();
-}
-
-{
- transaction t (db.begin ());
-
- // Database state of the person object is erased as well.
- //
- db.erase&lt;employer> (id);
-
- t.commit ();
-}
- </pre>
-
-
- <p>Note that this is a database-level functionality and care must be
- taken in order not to end up with inconsistent object states in the
- application's memory and database. The following example illustrates
- the kind of problems one may encounter:</p>
-
- <pre class="cxx">
-#pragma db object
-class employer
-{
- ...
-};
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db on_delete(set_null)
- employer* employer_;
-};
-
-employer e;
-person p;
-p.employer_ = &amp;e;
-
-{
- transaction t (db.begin ());
- db.persist (e);
- db.persist (p);
- t.commit ();
-}
-
-{
- transaction t (db.begin ());
-
- // The employer column is set to NULL in the database but
- // not the p.employer_ data member in the application.
- //
- db.erase (e);
- t.commit ();
-}
-
-{
- transaction t (db.begin ());
-
- // Override the employer column with an invalid pointer.
- //
- db.update (p);
-
- t.commit ();
-}
- </pre>
-
- <p>Note that even optimistic concurrency will not resolve such
- issues unless you are using database-level support for optimistic
- concurrency as well (for example, <code>ROWVERSION</code> in SQL
- Server).</p>
-
- <p>The <code>on_delete</code> specifier is only valid for non-inverse
- object pointer data members. If the <code>set_null</code> semantics
- is used, then the pointer must allow the <code>NULL</code> value.</p>
-
- <h3><a name="14.4.16">14.4.16 <code>version</code></a></h3>
-
- <p>The <code>version</code> specifier specifies that the data member stores
- the object version used to support optimistic concurrency. If a class
- has a version data member, then it must also be declared as having the
- optimistic concurrency model using the <code>optimistic</code> pragma
- (<a href="#14.1.5">Section 14.1.5, "<code>optimistic</code>"</a>). For
- example:</p>
-
- <pre class="cxx">
-#pragma db object optimistic
-class person
-{
- ...
-
- #pragma db version
- unsigned long version_;
-};
- </pre>
-
- <p>A version member must be of an integral C++ type and must map to
- an integer or similar database type. Note also that object versions
- are not reused. If you have a high update frequency, then care must
- be taken not to run out of versions. In such situations, using
- <code>unsigned&nbsp;long&nbsp;long</code> as the version type is a safe
- choice.</p>
-
- <p>For a more detailed discussion of optimistic concurrency, refer to
- <a href="#12">Chapter 12, "Optimistic Concurrency"</a>.</p>
-
- <h3><a name="14.4.17">14.4.17 <code>index</code></a></h3>
-
- <p>The <code>index</code> specifier instructs the ODB compiler to define
- a database index for the data member. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db index
- std::string name_;
-};
- </pre>
-
- <p>For more information on defining database indexes, refer to
- <a href="#14.7">Section 14.7, "Index Definition Pragmas"</a>.</p>
-
- <h3><a name="14.4.18">14.4.18 <code>unique</code></a></h3>
-
- <p>The <code>index</code> specifier instructs the ODB compiler to define
- a unique database index for the data member. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db unique
- std::string name_;
-};
- </pre>
-
- <p>For more information on defining database indexes, refer to
- <a href="#14.7">Section 14.7, "Index Definition Pragmas"</a>.</p>
-
- <h3><a name="14.4.19">14.4.19 <code>unordered</code></a></h3>
-
- <p>The <code>unordered</code> specifier specifies that the member of
- an ordered container type should be stored unordered in the database.
- The database table for such a member will not contain the index column
- and the order in which elements are retrieved from the database may
- not be the same as the order in which they were stored. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db unordered
- std::vector&lt;std::string> nicknames_;
-};
- </pre>
-
- <p>For a more detailed discussion of ordered containers and their
- storage in the database, refer to <a href="#5.1">Section 5.1,
- "Ordered Containers"</a>.</p>
-
- <h3><a name="14.4.20">14.4.20 <code>table</code></a></h3>
-
- <p>The <code>table</code> specifier specifies the table name that should
- be used to store the contents of the container member. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db table("nicknames")
- std::vector&lt;std::string> nicknames_;
-};
- </pre>
-
- <p>If the table name is not specified, then the container table name
- is constructed by concatenating the object's table name, underscore,
- and the public member name. The public member name is obtained
- by removing the common member name decorations, such as leading and
- trailing underscores, the <code>m_</code> prefix, etc. In the example
- above, without the <code>table</code> specifier, the container's
- table name would have been <code>person_nicknames</code>.</p>
-
- <p>The <code>table</code> specifier can also be used for members of
- composite value types. In this case it specifies the table name
- prefix for container members inside the composite value type. Refer
- to <a href="#7.2.2">Section 7.2.2, "Composite Value Column and Table
- Names"</a> for details.</p>
-
- <p>The container table name can be qualified with a database
- schema, for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db table("extras.nicknames")
- std::vector&lt;std::string> nicknames_;
-};
- </pre>
-
- <p>For more information on database schemas and the format of the
- qualified names, refer to <a href="#14.1.8">Section 14.1.8,
- "<code>schema</code>"</a>.</p>
-
- <h3><a name="14.4.21">14.4.21 <code>load</code>/<code>update</code></a></h3>
-
- <p>The <code>load</code> and <code>update</code> specifiers specify the
- loading and updating behavior for an object section, respectively.
- Valid values for the <code>load</code> specifier are
- <code>eager</code> (default) and <code>lazy</code>. Valid values for
- the <code>update</code> specifier are <code>always</code> (default),
- <code>change</code>, and <code>manual</code>. For more information
- on object sections, refer to <a href="#9">Chapter 9, "Sections"</a>.</p>
-
- <h3><a name="14.4.22">14.4.22 <code>section</code></a></h3>
-
- <p>The <code>section</code> specifier indicates that a data member
- of a persistent class belongs to an object section. The single
- required argument to this specifier is the name of the section
- data member. This specifier can only be used on direct data
- members of a persistent class. For more information on object
- sections, refer to <a href="#9">Chapter 9, "Sections"</a>.</p>
-
- <h3><a name="14.4.23">14.4.23 <code>added</code></a></h3>
-
- <p>The <code>added</code> specifier marks the data member as
- soft-added. The single required argument to this specifier is
- the addition version. For more information on this functionality,
- refer to <a href="#13.4">Section 13.4, "Soft Object Model
- Changes"</a>.</p>
-
- <h3><a name="14.4.24">14.4.24 <code>deleted</code></a></h3>
-
- <p>The <code>deleted</code> specifier marks the data member as
- soft-deleted. The single required argument to this specifier is
- the deletion version. For more information on this functionality,
- refer to <a href="#13.4">Section 13.4, "Soft Object Model
- Changes"</a>.</p>
-
- <h3><a name="14.4.25">14.4.25 <code>index_type</code></a></h3>
-
- <p>The <code>index_type</code> specifier specifies the native
- database type that should be used for an ordered container's
- index column of the data member. The semantics of <code>index_type</code>
- are similar to those of the <code>type</code> specifier
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>). The native
- database type is expected to be an integer type. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db index_type("SMALLINT UNSIGNED")
- std::vector&lt;std::string> nicknames_;
-};
- </pre>
-
- <h3><a name="14.4.26">14.4.26 <code>key_type</code></a></h3>
-
- <p>The <code>key_type</code> specifier specifies the native
- database type that should be used for a map container's
- key column of the data member. The semantics of <code>key_type</code>
- are similar to those of the <code>type</code> specifier
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>). For
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db key_type("INT UNSIGNED")
- std::map&lt;unsigned short, float> age_weight_map_;
-};
- </pre>
-
- <h3><a name="14.4.27">14.4.27 <code>value_type</code></a></h3>
-
- <p>The <code>value_type</code> specifier specifies the native
- database type that should be used for a container's
- value column of the data member. The semantics of <code>value_type</code>
- are similar to those of the <code>type</code> specifier
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>). For
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db value_type("VARCHAR(255)")
- std::vector&lt;std::string> nicknames_;
-};
- </pre>
-
- <p>The <code>value_null</code> and <code>value_not_null</code>
- (<a href="#14.4.28">Section 14.4.28,
- "<code>value_null</code>/<code>value_not_null</code>"</a>) specifiers
- can be used to control the <code>NULL</code> semantics of a value
- column.</p>
-
- <h3><a name="14.4.28">14.4.28 <code>value_null</code>/<code>value_not_null</code></a></h3>
-
- <p>The <code>value_null</code> and <code>value_not_null</code> specifiers
- specify that a container's element value for the data member can or
- cannot be <code>NULL</code>, respectively. The semantics of
- <code>value_null</code> and <code>value_not_null</code> are similar
- to those of the <code>null</code> and <code>not_null</code> specifiers
- (<a href="#14.4.6">Section 14.4.6, "<code>null</code>/<code>not_null</code>"</a>).
- For example:</p>
-
- <pre class="cxx">
-using std::tr1::shared_ptr;
-
-#pragma db object
-class person
-{
- ...
-};
-
-#pragma db object
-class account
-{
- ...
-
- #pragma db value_not_null
- std::vector&lt;shared_ptr&lt;person> > holders_;
-};
- </pre>
-
- <p>For set and multiset containers (<a href="#5.2">Section 5.2, "Set and
- Multiset Containers"</a>) the element value is automatically treated
- as not allowing a <code>NULL</code> value.</p>
-
- <h3><a name="14.4.29">14.4.29 <code>id_options</code></a></h3>
-
- <p>The <code>id_options</code> specifier specifies additional
- column definition options that should be used for a container's
- id column of the data member. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db id options("COLLATE binary")
- std::string name_;
-
- #pragma db id_options("COLLATE binary")
- std::vector&lt;std::string> nicknames_;
-};
- </pre>
-
- <p>The semantics of <code>id_options</code> are similar to those
- of the <code>options</code> specifier (<a href="#14.4.8">Section
- 14.4.8, "<code>options</code>"</a>).</p>
-
- <h3><a name="14.4.30">14.4.30 <code>index_options</code></a></h3>
-
- <p>The <code>index_options</code> specifier specifies additional
- column definition options that should be used for a container's
- index column of the data member. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db index_options("ZEROFILL")
- std::vector&lt;std::string> nicknames_;
-};
- </pre>
-
- <p>The semantics of <code>index_options</code> are similar to those
- of the <code>options</code> specifier (<a href="#14.4.8">Section
- 14.4.8, "<code>options</code>"</a>).</p>
-
- <h3><a name="14.4.31">14.4.31 <code>key_options</code></a></h3>
-
- <p>The <code>key_options</code> specifier specifies additional
- column definition options that should be used for a container's
- key column of the data member. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db key_options("COLLATE binary")
- std::map&lt;std::string, std::string> properties_;
-};
- </pre>
-
- <p>The semantics of <code>key_options</code> are similar to those
- of the <code>options</code> specifier (<a href="#14.4.8">Section
- 14.4.8, "<code>options</code>"</a>).</p>
-
- <h3><a name="14.4.32">14.4.32 <code>value_options</code></a></h3>
-
- <p>The <code>value_options</code> specifier specifies additional
- column definition options that should be used for a container's
- value column of the data member. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db value_options("COLLATE binary")
- std::set&lt;std::string> nicknames_;
-};
- </pre>
-
- <p>The semantics of <code>value_options</code> are similar to those
- of the <code>options</code> specifier (<a href="#14.4.8">Section
- 14.4.8, "<code>options</code>"</a>).</p>
-
- <h3><a name="14.4.33">14.4.33 <code>id_column</code></a></h3>
-
- <p>The <code>id_column</code> specifier specifies the column
- name that should be used to store the object id in a
- container's table for the data member. The semantics of
- <code>id_column</code> are similar to those of the
- <code>column</code> specifier
- (<a href="#14.4.9">Section 14.4.9, "<code>column</code>"</a>).
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db id_column("person_id")
- std::vector&lt;std::string> nicknames_;
-};
- </pre>
-
- <p>If the column name is not specified, then <code>object_id</code>
- is used by default.</p>
-
- <h3><a name="14.4.34">14.4.34 <code>index_column</code></a></h3>
-
- <p>The <code>index_column</code> specifier specifies the column
- name that should be used to store the element index in an
- ordered container's table for the data member. The semantics of
- <code>index_column</code> are similar to those of the
- <code>column</code> specifier
- (<a href="#14.4.9">Section 14.4.9, "<code>column</code>"</a>).
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db index_column("nickname_number")
- std::vector&lt;std::string> nicknames_;
-};
- </pre>
-
- <p>If the column name is not specified, then <code>index</code>
- is used by default.</p>
-
- <h3><a name="14.4.35">14.4.35 <code>key_column</code></a></h3>
-
- <p>The <code>key_column</code> specifier specifies the column
- name that should be used to store the key in a map
- container's table for the data member. The semantics of
- <code>key_column</code> are similar to those of the
- <code>column</code> specifier
- (<a href="#14.4.9">Section 14.4.9, "<code>column</code>"</a>).
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db key_column("age")
- std::map&lt;unsigned short, float> age_weight_map_;
-};
- </pre>
-
- <p>If the column name is not specified, then <code>key</code>
- is used by default.</p>
-
- <h3><a name="14.4.36">14.4.36 <code>value_column</code></a></h3>
-
- <p>The <code>value_column</code> specifier specifies the column
- name that should be used to store the element value in a
- container's table for the data member. The semantics of
- <code>value_column</code> are similar to those of the
- <code>column</code> specifier
- (<a href="#14.4.9">Section 14.4.9, "<code>column</code>"</a>).
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db value_column("weight")
- std::map&lt;unsigned short, float> age_weight_map_;
-};
- </pre>
-
- <p>If the column name is not specified, then <code>value</code>
- is used by default.</p>
-
- <h2><a name="14.5">14.5 Namespace Pragmas</a></h2>
-
- <p>A pragma with the <code>namespace</code> qualifier describes a
- C++ namespace. Similar to other qualifiers, <code>namespace</code>
- can also refer to a named C++ namespace, for example:</p>
-
- <pre class="cxx">
-namespace test
-{
- ...
-}
-
-#pragma db namespace(test) ...
- </pre>
-
- <p>To refer to the global namespace in the <code>namespace</code>
- qualifier the following special syntax is used:</p>
-
- <pre class="cxx">
-#pragma db namespace() ....
- </pre>
-
- <p>The <code>namespace</code> qualifier can be optionally followed,
- in any order, by one or more specifiers summarized in the
- table below:</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table class="specifiers" border="1">
- <tr>
- <th>Specifier</th>
- <th>Summary</th>
- <th>Section</th>
- </tr>
-
- <tr>
- <td><code>pointer</code></td>
- <td>pointer type for persistent classes and views inside a namespace</td>
- <td><a href="#14.5.1">14.5.1</a></td>
- </tr>
-
- <tr>
- <td><code>table</code></td>
- <td>table name prefix for persistent classes inside a namespace</td>
- <td><a href="#14.5.2">14.5.2</a></td>
- </tr>
-
- <tr>
- <td><code>schema</code></td>
- <td>database schema for persistent classes inside a namespace</td>
- <td><a href="#14.5.3">14.5.3</a></td>
- </tr>
-
- <tr>
- <td><code>session</code></td>
- <td>enable/disable session support for persistent classes inside a namespace</td>
- <td><a href="#14.5.4">14.5.4</a></td>
- </tr>
-
- </table>
-
- <h3><a name="14.5.1">14.5.1 <code>pointer</code></a></h3>
-
- <p>The <code>pointer</code> specifier specifies the default pointer
- type for persistent classes and views inside the namespace. For
- example:</p>
-
- <pre class="cxx">
-#pragma db namespace pointer(std::tr1::shared_ptr)
-namespace accounting
-{
- #pragma db object
- class employee
- {
- ...
- };
-
- #pragma db object
- class employer
- {
- ...
- };
-}
- </pre>
-
- <p>There are only two valid ways to specify a pointer with the
- <code>pointer</code> specifier at the namespace level. We can
- specify the template name of a smart pointer in which
- case the ODB compiler will automatically append the class
- name as a template argument. Or we can use <code>*</code>
- to denote a raw pointer.</p>
-
- <p>Note also that we can always override the default pointer
- specified at the namespace level for any persistent class
- or view inside this namespace. For example:</p>
-
- <pre class="cxx">
-#pragma db namespace pointer(std::unique_ptr)
-namespace accounting
-{
- #pragma db object pointer(std::shared_ptr)
- class employee
- {
- ...
- };
-
- #pragma db object
- class employer
- {
- ...
- };
-}
- </pre>
-
- <p>For a more detailed discussion of object and view pointers, refer
- to <a href="#3.3">Section 3.3, "Object and View Pointers"</a>.</p>
-
- <h3><a name="14.5.2">14.5.2 <code>table</code></a></h3>
-
- <p>The <code>table</code> specifier specifies a table prefix
- that should be added to table names of persistent classes inside
- the namespace. For example:</p>
-
- <pre class="cxx">
-#pragma db namespace table("acc_")
-namespace accounting
-{
- #pragma db object table("employees")
- class employee
- {
- ...
- };
-
- #pragma db object table("employers")
- class employer
- {
- ...
- };
-}
- </pre>
-
- <p>In the above example the resulting table names will be
- <code>acc_employees</code> and <code>acc_employers</code>.</p>
-
- <p>The table name prefix can also be specified with the
- <code>--table-prefix</code> ODB compiler option. Note
- that table prefixes specified at the namespace level as well
- as with the command line option are accumulated. For example:</p>
-
- <pre class="cxx">
-#pragma db namespace() table("audit_")
-
-#pragma db namespace table("hr_")
-namespace hr
-{
- #pragma db object table("employees")
- class employee
- {
- ...
- };
-}
-
-#pragma db object table("employers")
-class employer
-{
- ...
-};
- </pre>
-
- <p>If we compile the above example with the
- <code>--table-prefix&nbsp;test_</code> option, then the
- <code>employee</code> class table will be called
- <code>test_audit_hr_employees</code> and <code>employer</code> &mdash;
- <code>test_audit_employers</code>.</p>
-
- <p>Table prefixes can be used as an alternative to database schemas
- (<a href="#14.1.8">Section 14.1.8, "<code>schema</code>"</a>) if
- the target database system does not support schemas.</p>
-
- <h3><a name="14.5.3">14.5.3 <code>schema</code></a></h3>
-
- <p>The <code>schema</code> specifier specifies a database schema
- that should be used for persistent classes inside the namespace.
- For more information on specifying a database schema refer to
- <a href="#14.1.8">Section 14.1.8, "<code>schema</code>"</a>.</p>
-
- <h3><a name="14.5.4">14.5.4 <code>session</code></a></h3>
-
- <p>The <code>session</code> specifier specifies whether to enable
- session support for persistent classes inside the namespace. For
- example:</p>
-
- <pre class="cxx">
-#pragma db namespace session
-namespace hr
-{
- #pragma db object // Enabled.
- class employee
- {
- ...
- };
-
- #pragma db object session(false) // Disabled.
- class employer
- {
- ...
- };
-}
- </pre>
-
- <p>Session support is disabled by default unless the
- <code>--generate-session</code> ODB compiler option is specified.
- Session support specified at the namespace level can be overridden
- on the per object basis (<a href="#14.1.10">Section 14.1.10,
- "<code>session</code>"</a>). For more information on sessions,
- refer to <a href="#11">Chapter 11, "Session"</a>.</p>
-
-<h2><a name="14.6">14.6 Object Model Pragmas</a></h2>
-
- <p>A pragma with the <code>model</code> qualifier describes the
- whole C++ object model. For example:</p>
-
- <pre class="cxx">
-#pragma db model ...
- </pre>
-
- <p>The <code>model</code> qualifier can be followed, in any order,
- by one or more specifiers summarized in the table below:</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table class="specifiers" border="1">
- <tr>
- <th>Specifier</th>
- <th>Summary</th>
- <th>Section</th>
- </tr>
-
- <tr>
- <td><code>version</code></td>
- <td>object model version</td>
- <td><a href="#14.6.1">14.6.1</a></td>
- </tr>
-
- </table>
-
- <h3><a name="14.6.1">14.6.1 <code>version</code></a></h3>
-
- <p>The <code>version</code> specifier specifies the object model
- version when schema evolution support is used. The first two
- required arguments to this specifier are the base and current
- model versions, respectively. The third optional argument
- specifies whether the current version is open for changes.
- Valid values for this argument are <code>open</code> (the
- default) and <code>closed</code>. For more information on
- this functionality, refer to <a href="#13">Chapter 13,
- "Database Schema Evolution"</a>.</p>
-
-
- <h2><a name="14.7">14.7 Index Definition Pragmas</a></h2>
-
- <p>While it is possible to manually add indexes to the generated
- database schema, it is more convenient to do this as part of
- the persistent class definitions. A pragma with the <code>index</code>
- qualifier describes a database index. It has the following
- general format:</p>
-
-<pre class="cxx">
-#pragma db index[("&lt;name>")] \
- [unique|type("&lt;type>")] \
- [method("&lt;method>")] \
- [options("&lt;index-options>")] \
- member(&lt;name>[, "&lt;column-options>"])... \
- members(&lt;name>[,&lt;name>...])...
-</pre>
-
- <p>The <code>index</code> qualifier can optionally specify the
- index name. If the index name is not specified, then one is
- automatically derived by appending the <code>_i</code> suffix
- to the column name of the index member. If the name is not
- specified and the index contains multiple members, then the
- index definition is invalid.</p>
-
- <p>The optional <code>type</code>, <code>method</code>, and
- <code>options</code> clauses specify the index type, for
- example <code>UNIQUE</code>, index method, for example
- <code>BTREE</code>, and index options, respectively. The
- <code>unique</code> clause is a shortcut for
- <code>type("UNIQUE")</code>. Note that not all database
- systems support specifying an index method or options.
- For more information on the database system-specific index
- types, methods, and options, refer to <a href="#II">Part II,
- "Database Systems"</a>.</p>
-
- <p>To specify index members we can use the <code>member</code>
- or <code>members</code> clauses, or a mix of the two. The
- <code>member</code> clause allows us to specify a single
- index member with optional column options, for example,
- <code>"ASC"</code>. If we need to create a composite
- index that contains multiple members, then we can repeat
- the <code>member</code> clause several times or, if the
- members don't have any column options, we can use a single
- <code>members</code> clause instead. Similar to the index
- type, method, and options, the format of column options is
- database system-specific. For more details, refer to
- <a href="#II">Part II, "Database Systems"</a>.</p>
-
- <p>The following code fragment shows some typical examples
- of index definitions:</p>
-
-<pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- int x;
- int y;
- int z1;
- int z2;
-
- // An index for member x with automatically-assigned name x_i.
- //
- #pragma db index member(x)
-
- // A unique index named y_index for member y which is sorted in
- // the descending order. The index is using the BTREE method.
- //
- #pragma db index("y_index") unique method("BTREE") member(y, "DESC")
-
- // A composite BITMAP index named z_i for members z1 and z2.
- //
- #pragma db index("z_i") type("BITMAP") members(z1, z2)
-};
-</pre>
-
- <p>ODB also offers a shortcut for defining an index with the default
- method and options for a single data member. Such an index can
- be defined using the <code>index</code> (<a href="#14.4.17">Section
- 14.4.17, "<code>index</code>"</a>) or <code>unique</code>
- (<a href="#14.4.18">Section 14.4.18, "<code>unique</code>"</a>)
- member specifier. For example:</p>
-
-<pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db index
- int x;
-
- #pragma db type("INT") unique
- int y;
-};
-</pre>
-
- <p>The above example is semantically equivalent to the following
- more verbose version:</p>
-
-<pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- int x;
-
- #pragma db type("INT")
- int y;
-
- #pragma db index member(x)
- #pragma db index unique member(y)
-};
-</pre>
-
- <p>While it is convenient to define an index inside a persistent
- class, it is also possible to do that out of the class body. In this
- case, the index name, if specified, must be prefixed with the
- potentially-qualified class name. For example:</p>
-
-<pre class="cxx">
-namespace n
-{
- #pragma db object
- class object
- {
- ...
-
- int x;
- int y;
- };
-
- // An index for member x in persistent class object with automatically-
- // assigned name x_i.
- //
- #pragma db index(object) member(x)
-}
-
-// An index named y_index for member y in persistent class n::object.
-//
-#pragma db index(n::object::"y_index") member(y)
-</pre>
-
- <p>It is possible to define an index on a member that is of a
- composite value type or on some of its nested members. For
- example:</p>
-
-<pre class="cxx">
-#pragma db value
-struct point
-{
- int x;
- int y;
- int z;
-};
-
-#pragma db object
-class object
-{
- // An index that includes all of the p1's nested members.
- //
- #pragma db index
- point p1;
-
- point p2;
-
- // An index that includes only p2.x and p2.y.
- //
- #pragma db index("p2_xy_i") members(p2.x, p2.y)
-};
-</pre>
-
- <p>When generating a schema for a container member (<a href="#5">Chapter 5,
- "Containers"</a>), ODB automatically defines two indexes on the container
- table. One is for the object id that references the object table and the
- other is for the index column in case the container is ordered
- (<a href="#5.1">Section 5.1, "Ordered Containers"</a>). By default these
- indexes use the default index name, type, method, and options. The
- <code>index</code> pragma allows us to customize these two indexes by
- recognizing the special <code>id</code> and <code>index</code> nested
- member names when specified after a container member. For example:</p>
-
-<pre class="cxx">
-#pragma db object
-class object
-{
- std::vector&lt;int> v;
-
- // Change the container id index name.
- //
- #pragma db index("id_index") member(v.id)
-
- // Change the container index index method.
- //
- #pragma db index method("BTREE") member(v.index)
-};
-</pre>
-
- <h2><a name="14.8">14.8 Database Type Mapping Pragmas</a></h2>
-
- <p>A pragma with the <code>map</code> qualifier describes a
- mapping between two database types. For each database system
- ODB provides built-in support for a core set of database types,
- such as integers, strings, binary, etc. However, many database
- systems provide extended types such as geospatial types,
- user-defined types, and collections (arrays, table types,
- key-value stores, etc). In order to support such extended types,
- ODB allows us to map them to one of the built-in types, normally
- a string or a binary. Given the text or binary representation
- of the data we can then extract it into our chosen C++ data type
- and thus establish a mapping between an extended database type and
- its C++ equivalent.</p>
-
- <p>The <code>map</code> pragma has the following format:</p>
-
-<pre class="cxx">
-#pragma db map type("regex") as("subst") [to("subst")] [from("subst")]
-</pre>
-
- <p>The <code>type</code> clause specifies the name of the database type
- that we are mapping. We will refer to it as the <em>mapped type</em>
- from now on. The name of the mapped type is a Perl-like regular
- expression pattern that is matched in the case-insensitive mode.</p>
-
- <p>The <code>as</code> clause specifies the name of the database type
- that we are mapping the mapped type to. We will refer to it as
- the <em>interface type</em> from now on. The name of the interface
- type is a regular expression substitution and should expand to a
- name of a database type for which ODB provides built-in support.</p>
-
- <p>The optional <code>to</code> and <code>from</code> clauses specify the
- database conversion expressions between the mapped type and the
- interface type. The <code>to</code> expression converts from the
- interface type to the mapped type and <code>from</code> converts
- in the other direction. If no explicit conversion is required for
- either direction, then the corresponding clause can be omitted.
- The conversion expressions are regular expression substitutions.
- They must contain the special <code>(?)</code> placeholder which will
- be replaced with the actual value to be converted. Turning on SQL
- statement tracing (<a href="#3.13">Section 3.13, "Tracing SQL
- Statement Execution"</a>) can be useful for debugging conversion
- expressions. This allows you to see the substituted expressions
- as used in the actual statements.</p>
-
- <p>As an example, the following <code>map</code> pragma maps the
- PostgreSQL array of <code>INTEGER</code>'s to <code>TEXT</code>:</p>
-
-<pre class="cxx">
-#pragma db map type("INTEGER *\\[(\\d*)\\]") \
- as("TEXT") \
- to("(?)::INTEGER[$1]") \
- from("(?)::TEXT")
-</pre>
-
- <p>With the above mapping we can now have a persistent class that
- has a member of the PostgreSQL array type:</p>
-
-<pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("INTEGER[]")
- std::string array_;
-};
-</pre>
-
- <p>In PostgreSQL the array literal has the <code>{n1,n2,...}</code> form.
- As a result, we need to make sure that we pass the correct text
- representation in the <code>array_</code> member, for example:</p>
-
-<pre class="cxx">
-object o;
-o.array_ = "{1,2,3}";
-db.persist (o);
-</pre>
-
- <p>Of course, <code>std::string</code> is not the most natural
- representation of an array of integers in C++. Instead,
- <code>std::vector&lt;int></code> would have been much more
- appropriate. To add support for mapping
- <code>std::vector&lt;int></code> to PostgreSQL <code>INTEGER[]</code>
- we need to provide a <code>value_traits</code> specialization
- that implements conversion between the PostgreSQL text representation
- of an array and <code>std::vector&lt;int></code>. Below is a sample
- implementation:</p>
-
-<pre class="cxx">
-namespace odb
-{
- namespace pgsql
- {
- template &lt;>
- class value_traits&lt;std::vector&lt;int>, id_string>
- {
- public:
- typedef std::vector&lt;int> value_type;
- typedef value_type query_type;
- typedef details::buffer image_type;
-
- static void
- set_value (value_type&amp; v,
- const details::buffer&amp; b,
- std::size_t n,
- bool is_null)
- {
- v.clear ();
-
- if (!is_null)
- {
- char c;
- std::istringstream is (std::string (b.data (), n));
-
- is >> c; // '{'
-
- for (c = static_cast&lt;char> (is.peek ()); c != '}'; is >> c)
- {
- v.push_back (int ());
- is >> v.back ();
- }
- }
- }
-
- static void
- set_image (details::buffer&amp; b,
- std::size_t&amp; n,
- bool&amp; is_null,
- const value_type&amp; v)
- {
- is_null = false;
- std::ostringstream os;
-
- os &lt;&lt; '{';
-
- for (value_type::const_iterator i (v.begin ()), e (v.end ());
- i != e;)
- {
- os &lt;&lt; *i;
-
- if (++i != e)
- os &lt;&lt; ',';
- }
-
- os &lt;&lt; '}';
-
- const std::string&amp; s (os.str ());
- n = s.size ();
-
- if (n > b.capacity ())
- b.capacity (n);
-
- std::memcpy (b.data (), s.c_str (), n);
- }
- };
- }
-}
-</pre>
-
- <p>Once this specialization is included in the generated code (see
- the <code>mapping</code> example in the <code>odb-examples</code>
- package for details), we can use <code>std::vector&lt;int></code>
- instead of <code>std::string</code> in our persistent class:</p>
-
-<pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("INTEGER[]")
- std::vector&lt;int> array_;
-};
-</pre>
-
- <p>If we wanted to always map <code>std::vector&lt;int></code>
- to PostgreSQL <code>INTEGER[]</code>, then we could instead
- write:</p>
-
-<pre class="cxx">
-typedef std::vector&lt;int> int_vector;
-#pragma db value(int_vector) type("INTEGER[]")
-
-#pragma db object
-class object
-{
- ...
-
- std::vector&lt;int> array_; // Mapped to INTEGER[].
-};
-</pre>
-
- <p>While the above example only shows how to handle PostgreSQL arrays,
- other types in PostgreSQL and in other databases can be supported
- in a similar way. The <code>odb-tests</code> package contains a
- set of tests in the <code>&lt;database>/custom</code> directories that,
- for each database, shows how to provide custom mapping for some of
- the extended types.</p>
-
- <h2><a name="14.9">14.9 C++ Compiler Warnings</a></h2>
-
- <p>When a C++ header file defining persistent classes and containing
- ODB pragmas is used to build the application, the C++ compiler may
- issue warnings about pragmas that it doesn't recognize. There
- are several ways to deal with this problem. The easiest is to
- disable such warnings using one of the compiler-specific command
- line options or warning control pragmas. This method is described
- in the following sub-section for popular C++ compilers.</p>
-
- <p>There are also several C++ compiler-independent methods that we
- can employ. The first is to use the <code>PRAGMA_DB</code> macro,
- defined in <code>&lt;odb/core.hxx></code>, instead of using
- <code>#pragma&nbsp;db</code> directly. This macro expands to the
- ODB pragma when compiled with the ODB compiler and to an empty
- declaration when compiled with other compilers. The following example
- shows how we can use this macro:</p>
-
- <pre class="cxx">
-#include &lt;odb/core.hxx>
-
-PRAGMA_DB(object)
-class person
-{
- ...
-
- PRAGMA_DB(id)
- unsigned long id_;
-};
- </pre>
-
- <p>An alternative to using the <code>PRAGMA_DB</code> macro is to
- group the <code>#pragma&nbsp;db</code> directives in blocks that are
- conditionally included into compilation only when compiled with the
- ODB compiler. For example:</p>
-
- <pre class="cxx">
-class person
-{
- ...
-
- unsigned long id_;
-};
-
-#ifdef ODB_COMPILER
-# pragma db object(person)
-# pragma db member(person::id_) id
-#endif
- </pre>
-
- <p>The disadvantage of this approach is that it can quickly become
- overly verbose when positioned pragmas are used.</p>
-
- <h3><a name="14.9.1">14.9.1 GNU C++</a></h3>
-
- <p>GNU g++ does not issue warnings about unknown pragmas
- unless requested with the <code>-Wall</code> command line option.
- To disable only the unknown pragma warning, we can add the
- <code>-Wno-unknown-pragmas</code> option after <code>-Wall</code>,
- for example:</p>
-
- <pre class="terminal">
-g++ -Wall -Wno-unknown-pragmas ...
- </pre>
-
- <h3><a name="14.9.2">14.9.2 Visual C++</a></h3>
-
- <p>Microsoft Visual C++ issues an unknown pragma warning (C4068) at
- warning level 1 or higher. This means that unless we have disabled
- the warnings altogether (level 0), we will see this warning.</p>
-
- <p>To disable this warning via the compiler command line, we can add
- the <code>/wd4068</code> C++ compiler option in Visual Studio 2008
- and earlier. In Visual Studio 2010 and later there is now a special
- GUI field where we can enter warning numbers that should be disabled.
- Simply enter 4068 into this field.</p>
-
- <p>We can also disable this warning for only a specific header or
- a fragment of a header using the warning control pragma. For
- example:</p>
-
- <pre class="cxx">
-#include &lt;odb/core.hxx>
-
-#pragma warning (push)
-#pragma warning (disable:4068)
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id
- unsigned long id_;
-};
-
-#pragma warning (pop)
- </pre>
-
- <h3><a name="14.9.3">14.9.3 Sun C++</a></h3>
-
- <p>The Sun C++ compiler does not issue warnings about unknown pragmas
- unless the <code>+w</code> or <code>+w2</code> option is specified.
- To disable only the unknown pragma warning we can add the
- <code>-erroff=unknownpragma</code> option anywhere on the
- command line, for example:</p>
-
- <pre class="terminal">
-CC +w -erroff=unknownpragma ...
- </pre>
-
- <h3><a name="14.9.4">14.9.4 IBM XL C++</a></h3>
-
- <p>IBM XL C++ issues an unknown pragma warning (1540-1401) by default.
- To disable this warning we can add the <code>-qsuppress=1540-1401</code>
- command line option, for example:</p>
-
- <pre class="terminal">
-xlC -qsuppress=1540-1401 ...
- </pre>
-
- <h3><a name="14.9.5">14.9.5 HP aC++</a></h3>
-
- <p>HP aC++ (aCC) issues an unknown pragma warning (2161) by default.
- To disable this warning we can add the <code>+W2161</code>
- command line option, for example:</p>
-
- <pre class="terminal">
-aCC +W2161 ...
- </pre>
-
- <h3><a name="14.9.6">14.9.6 Clang</a></h3>
-
- <p>Clang does not issue warnings about unknown pragmas
- unless requested with the <code>-Wall</code> command line option.
- To disable only the unknown pragma warning, we can add the
- <code>-Wno-unknown-pragmas</code> option after <code>-Wall</code>,
- for example:</p>
-
- <pre class="terminal">
-clang++ -Wall -Wno-unknown-pragmas ...
- </pre>
-
- <p>We can also disable this warning for only a specific header or
- a fragment of a header using the warning control pragma. For
- example:</p>
-
- <pre class="cxx">
-#include &lt;odb/core.hxx>
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunknown-pragmas"
-
-#pragma db object
-class person
-{
- ...
-
- #pragma db id
- unsigned long id_;
-};
-
-#pragma clang diagnostic pop
- </pre>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="15">15 Advanced Techniques and Mechanisms</a></h1>
-
- <p>This chapter covers more advanced techniques and mechanisms
- provided by ODB that may be useful in certain situations.</p>
-
- <h2><a name="15.1">15.1 Transaction Callbacks</a></h2>
-
- <p>The ODB transaction class (<code>odb::transaction</code>) allows
- an application to register a callback that will be called after
- the transaction is finalized, that is, committed or rolled back.
- This mechanism can be used, for example, to restore values that
- were updated during the transaction execution to their original
- states if the transaction is rolled back.</p>
-
- <p>The callback management interface of the <code>transaction</code>
- class is shown below.</p>
-
- <pre class="cxx">
-namespace odb
-{
- class transaction
- {
- ...
-
- public:
- static const unsigned short event_commit = 0x01;
- static const unsigned short event_rollback = 0x02;
- static const unsigned short event_all = event_commit | event_rollback;
-
- typedef void (*callback_type) (
- unsigned short event, void* key, unsigned long long data);
-
- void
- callback_register (callback_type callback,
- void* key,
- unsigned short event = event_all,
- unsigned long long data = 0,
- transaction** state = 0);
-
-
- void
- callback_unregister (void* key);
-
- void
- callback_update (void* key,
- unsigned short event,
- unsigned long long data = 0,
- transaction** state = 0);
- }
-}
- </pre>
-
- <p>The <code>callback_register()</code> function registers a
- post-commit/rollback callback. The <code>callback</code>
- argument is the function that should be called. The
- <code>key</code> argument is used by the transaction
- to identify this callback. It is also normally used
- to pass an address of the data object on which the
- callback function will work. The <code>event</code>
- argument is the bitwise-or of the events that should
- trigger the callback.</p>
-
- <p>The optional data argument can be used to store any POD
- user data that doesn't exceed 8 bytes in size and doesn't require
- alignment greater than <code>unsigned long long</code>. For
- example, we could store an old value of a flag or a counter
- that needs to be restored in case of a roll back.</p>
-
- <p>The optional <code>state</code> argument can be used to
- indicate that the callback has been unregistered because
- the transaction was finalized. In this case the transaction
- automatically resets the passed pointer to 0. This is
- primarily useful if we are interested in only one of
- the events (commit or rollback).</p>
-
- <p>The <code>callback_unregister()</code> function unregisters a previously
- registered callback. If the number of registered callbacks is
- large, then this can be a slow operation. Generally, the callback
- mechanism is optimized for cases where the callbacks stay
- registered until the transaction is finalized.</p>
-
- <p>Note also that you don't need to unregister a callback that has
- been called or auto-reset using the <code>state</code> argument
- passed to <code>callback_register()</code>. This function does nothing
- if the key is not found.</p>
-
- <p>The <code>callback_update()</code> function can be used to update
- the <code>event</code>, <code>data</code>, and <code>state</code>
- values of a previously registered callback. Similar to
- <code>callback_unregister()</code>, this is a potentially slow
- operation.</p>
-
- <p>When the callback is called, it is passed the event that
- triggered it, as well as the <code>key</code> and
- <code>data</code> values that were passed to the
- <code>callback_register()</code> function. Note also that the order
- in which the callbacks are called is unspecified. The rollback
- event can be triggered by an exception. In this case, if the
- callback throws, the program will be terminated.</p>
-
- <p>The following example shows how we can use transaction
- callbacks together with database operation callbacks
- (<a href="#14.1.7">Section 14.1.7, "<code>callback</code>"</a>)
- to manage the object's "dirty" flag.</p>
-
- <pre class="cxx">
-#include &lt;odb/callback.hxx>
-#include &lt;odb/transaction.hxx>
-
-#pragma db object callback(update)
-class object
-{
- ...
-
- #pragma db transient
- mutable bool dirty_;
-
- // Non-NULL value indicates that we are registered
- // with this transaction.
- //
- #pragma db transient
- mutable odb::transaction* tran_;
-
- void
- update (odb::callback_event e, odb::database&amp;) const
- {
- using namespace odb::core;
-
- if (e == callback_event::post_update)
- return;
-
- // Mark the object as clean again but register a
- // transaction callback in case the update is rolled
- // back.
- //
- tran_ = &amp;transaction::current ();
- tran_->callback_register (&amp;rollback,
- const_cast&lt;object*> (this),
- transaction::event_rollback,
- 0,
- &amp;tran_);
- dirty_ = false;
- }
-
- static void
- rollback (unsigned short, void* key, unsigned long long)
- {
- // Restore the dirty flag since the changes have been
- // rolled back.
- //
- object&amp; o (*static_cast&lt;object*> (key));
- o.dirty_ = true;
- }
-
- ~object ()
- {
- // Unregister the callback if we are going away before
- // the transaction.
- //
- if (tran_ != 0)
- tran_->callback_unregister (this);
- }
-};
- </pre>
-
- <h2><a name="15.2">15.2 Persistent Class Template Instantiations</a></h2>
-
- <p>Similar to composite value types (<a href="#7.2">Section 7.2, "Composite
- Value Types"</a>), a persistent object can be defined as an instantiation
- of a C++ class template, for example:</p>
-
- <pre class="cxx">
-template &lt;typename T>
-class person
-{
- ...
-
- T first_;
- T last_;
-};
-
-typedef person&lt;std::string> std_person;
-
-#pragma db object(std_person)
-#pragma db member(std_person::last_) id
- </pre>
-
- <p>Note that the database support code for such a persistent object
- is generated when compiling the header containing the
- <code>db&nbsp;object</code> pragma and not the header containing
- the template definition or the <code>typedef</code> name. This
- allows us to use templates defined in other files, for example:</p>
-
- <pre class="cxx">
-#include &lt;utility> // std::pair
-
-typedef std::pair&lt;unsigned int, std::string> person;
-#pragma db object(person)
-#pragma db member(person::first) id auto column("id")
-#pragma db member(person::second) column("name")
- </pre>
-
- <p>You may also have to explicitly specify the object type in
- calls to certain <code>database</code> class functions due
- to the inability do distinguish, at the API level, between
- smart pointers and persistent objects defined as class
- template instantiations. For example:</p>
-
- <pre class="cxx">
-person p;
-
-db.update (p); // Error.
-db.reload (p); // Error.
-db.erase (p); // Error.
-
-db.update&lt;person> (p); // Ok.
-db.reload&lt;person> (p); // Ok.
-db.erase&lt;person> (p); // Ok.
- </pre>
-
- <p>It also makes sense to factor persistent data members that do not
- depend on template arguments into a common, non-template base class.
- The following more realistic example illustrates this approach:</p>
-
- <pre class="cxx">
-#pragma db object abstract
-class base_common
-{
- ...
-
- #pragma db id auto
- unsigned long id;
-};
-
-template &lt;typename T>
-class base: public base_common
-{
- ...
-
- T value;
-};
-
-typedef base&lt;std::string> string_base;
-#pragma db object(string_base) abstract
-
-#pragma db object
-class derived: public string_base
-{
- ...
-};
- </pre>
-
- <h2><a name="15.3">15.3 Bulk Database Operations</a></h2>
-
- <p>Some database systems supported by ODB provide a mechanism, often
- called bulk or batch statement execution, that allows us to execute
- the same SQL statement on multiple sets of data at once and with a
- single database API call. 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 and Microsoft SQL Server are capable of bulk operations.
- 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.
- 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)
-class person
-{
- ...
-};
- </pre>
-
- <p>Note that while specifying the batch size at compile time might
- seem inflexible, this approach allows ODB to place internal
- arrays of the fixed batch size on the stack rather than
- allocating them in the dynamic memory. However, specifying the
- batch size at runtime may be supported in the future.</p>
-
- <p>Once the bulk support is enabled for a particular object, we can
- use the following <code>database</code> functions to perform bulk
- operations:</p>
-
- <pre class="cxx">
-template &lt;typename I>
-void
-persist (I begin, I end, bool continue_failed = true);
-
-template &lt;typename I>
-void
-update (I begin, I end, bool continue_failed = true);
-
-template &lt;typename I>
-void
-erase (I obj_begin, I obj_end, bool continue_failed = true);
-
-template &lt;typename T, typename I>
-void
-erase (I id_begin, I id_end, bool continue_failed = true);
- </pre>
-
- <p>Every bulk API function expects a range of elements, passed in
- the canonical C++ form as a pair of input iterators. In case of
- <code>persist()</code>, <code>update()</code>, and the first
- <code>erase()</code> overload, we pass a range of objects,
- either as references or as pointers, raw or smart. The following
- example illustrates the most common scenarios using the
- <code>persist()</code> call:</p>
-
- <pre class="cxx">
-// C array of objects.
-//
-person a[2] {{"John", "Doe"}, {"Jane", "Doe"}};
-
-db.persist (a, a + sizeof(a) / sizeof(a[0]));
-
-
-// Vector of objects.
-//
-std::vector&lt;person> v {{"John", "Doe"}, {"Jane", "Doe"}};
-
-db.persist (v.begin (), v.end ());
-
-
-// C array of raw pointers to objects.
-//
-person p1 ("John", "Doe");
-person p2 ("Jane", "Doe");
-person* pa[2] {&amp;p1, &amp;p2};
-
-db.persist (pa, pa + sizeof(pa) / sizeof(pa[0]));
-
-
-// Vector of raw pointers to objects.
-//
-std::vector&lt;person*> pv {&amp;p1, &amp;p2};
-
-db.persist (pv.begin (), pv.end ());
-
-
-// Vector of smart (shared) pointers to objects.
-//
-std::vector&lt;std::shared_ptr&lt;person>> sv {
- std::make_shared&lt;person> ("John", "Doe"),
- std::make_shared&lt;person> ("Jane", "Doe")};
-
-db.persist (sv.begin (), sv.end ());
- </pre>
-
- <p>The ability to perform a bulk operation on a range of raw pointers
- to objects can be especially useful when the application stores
- objects in a way that does not easily conform to the pair of
- iterators interface. In such cases we can create a temporary
- container of shallow pointers to objects and use that to perform
- the bulk operation, for example:</p>
-
- <pre class="cxx">
-struct person_entry
-{
- person obj;
-
- // Some additional data.
- ...
-};
-
-typedef std::vector&lt;person_entry> people;
-
-void
-persist (odb::database&amp; db, people&amp; p)
-{
- std::vector&lt;person*> tmp;
- tmp.reserve (p.size ());
- std::for_each (p.begin (),
- p.end (),
- [&amp;tmp] (person_entry&amp; pe)
- {
- tmp.push_back (&amp;pe.obj);
- });
-
-
- db.persist (tmp.begin (), tmp.end ());
-}
- </pre>
-
- <p>The second overload of the bulk <code>erase()</code> function
- allows us to pass a range of object ids rather than objects
- themselves. As with the corresponding non-bulk version, we
- have to specify the object type explicitly, for example:</p>
-
- <pre class="cxx">
-std::vector&lt;unsigned long> ids {1, 2};
-
-db.erase&lt;person> (ids.begin (), ids.end ());
- </pre>
-
- <p>Conceptually, a bulk operation is equivalent to performing the
- corresponding non-bulk version in a loop, except when it comes to the
- failure semantics. Both databases that currently are capable of
- bulk operations (Oracle and SQL Server) do not stop when a data
- set in a batch fails (for example, because of a unique constraint
- violation). Instead, they continue executing subsequent data
- sets until every element in the batch has been attempted. The
- <code>continue_failed</code> argument in the bulk functions listed
- above specifies whether ODB should extend this behavior and continue
- with subsequent batches if the one it has tried to execute has failed
- elements. The default behavior is to continue.</p>
-
- <p>The consequence of this failure semantics is that we may have
- multiple elements in the range failed for different reasons.
- For example, if we tried to persist a number of objects, some
- of them might have failed because they are already persistent
- while others &mdash; because of a unique constraint violation.
- As a result, ODB uses the special <code>odb::multiple_exceptions</code>
- class to report failures in the bulk API functions. This
- exception is thrown if one or more elements in the range have
- failed and it contains the error information in the form of other
- ODB exception for each failed position. The
- <code>multiple_exceptions</code> class has the following interface:</p>
-
- <pre class="cxx">
-struct multiple_exceptions: odb::exception
-{
- // Element type.
- //
- struct value_type
- {
- std::size_t
- position () const;
-
- const odb::exception&amp;
- exception () const;
-
- bool
- maybe () const;
- };
-
- // Iteration.
- //
- typedef std::set&lt;value_type> set_type;
-
- typedef set_type::const_iterator iterator;
- typedef set_type::const_iterator const_iterator;
-
- iterator
- begin () const;
-
- iterator
- end () const;
-
- // Lookup.
- //
- const value_type*
- operator[] (std::size_t) const;
-
- // Severity, failed and attempted counts.
- //
- std::size_t
- attempted () const;
-
- std::size_t
- failed () const;
-
- bool
- fatal () const;
-
- void
- fatal (bool);
-
- // Direct data access.
- //
- const set_type&amp;
- set () const;
-
- // odb::exception interface.
- //
- virtual const char*
- what () const throw ();
-};
- </pre>
-
- <p>The <code>multiple_exceptions</code> class has a map-like interface
- with the key being the position in the range and the value being
- the exception plus the <code>maybe</code> flag (discussed below).
- As a result, we can either iterate over the failed positions or
- we can check whether a specific position in the range has failed.
- The following example shows what a <code>catch</code>-handler for
- this exception might look like:</p>
-
- <pre class="cxx">
-std::vector&lt;person> objs {{"John", "Doe"}, {"Jane", "Doe"}};
-
-try
-{
- db.persist (objs.begin (), objs.end ());
-}
-catch (const odb::multiple_exceptions&amp; me)
-{
- for (const auto&amp; v: me)
- {
- size_t p (v.position ());
-
- try
- {
- throw v.exception ();
- }
- catch (const odb::object_already_persistent&amp;)
- {
- cerr &lt;&lt; p &lt;&lt; ": duplicate id: " &lt;&lt; objs[p].id () &lt;&lt; endl;
- }
- catch (const odb::exception&amp; e)
- {
- cerr &lt;&lt; p &lt;&lt; ": " &lt;&lt; e.what () &lt;&lt; endl;
- }
- }
-}
- </pre>
-
- <p>If, however, all we want is to show the diagnostics to the user,
- then the string returned by the <code>what()</code> function
- will contain the error information for each failed position.
- Here is what it might look like (using Oracle as an example):</p>
-
- <pre class="terminal">
-multiple exceptions, 4 elements attempted, 2 failed:
-[0] object already persistent
-[3] 1: ORA-00001: unique constraint (ODB_TEST.person_last_i) violated
- </pre>
-
- <p>Both databases that currently are capable of bulk operations return
- a total count of affected rows rather than individual counts for
- each data set. This limitation prevents ODB from being able to
- always determine which elements in the batch haven't affected
- any rows and, for the update and erase operations, translate
- this to the <code>object_not_persistent</code> exceptions. As
- a result, if some elements in the batch haven't affected any
- rows and ODB is unable to determine exactly which ones, it will mark
- all the elements in this batch as "maybe not persistent". That
- is, it will insert the <code>object_not_persistent</code> exception
- and set the <code>maybe</code> flag for every position in the
- batch. The diagnostics string returned by <code>what()</code>
- will also reflect this situation, for example (assuming batch
- size of 3):</p>
-
- <pre class="terminal">
-multiple exceptions, 4 elements attempted, 4 failed:
-[0-2] (some) object not persistent
-[3] object not persistent
- </pre>
-
- <p>The way to handle and recover from such "maybe failures" will have
- to be application-specific. For example, for some applications the
- fact that some objects no longer exist in the database when
- performing bulk erase might be an ignorable error. If, however,
- the application needs to determine exactly which elements in the batch
- have failed, then a <code>load()</code> call will be required for each
- element in the batch (or a query using a view to avoid loading all
- the data members; <a href="#10">Chapter 10, "Views"</a>). This is also
- something to keep in mind when selecting the batch size since for
- larger sizes it will be more expensive (more loads to perform) to
- handle such "maybe failures". If the failures are not uncommon, as
- is the case, for example, when using optimistic concurrency, then
- it may make sense to use a smaller batch.</p>
-
- <p>The lookup operator (<code>operator[]</code>) returns <code>NULL</code>
- if the element at this position has no exception. Note also that the
- returned value is <code>value_type*</code> and not
- <code>odb::exception*</code> in order to provide access to the
- <code>maybe</code> flag discussed above.</p>
-
- <p>The <code>multiple_exceptions</code> class also provides access
- to the number of positions attempted (the <code>attempted()</code>
- accessor) and failed (the <code>failed()</code> accessor). Note
- that the failed count includes the "maybe failed" positions.</p>
-
- <p>The <code>multiple_exceptions</code> exception can also be fatal.
- If the <code>fatal()</code> accessor returns <code>true</code>, then
- (some of) the exceptions were fatal. In this case, even for positions
- that did not fail, no attempts were made to complete the operation
- and the transaction must be aborted.</p>
-
- <p>If <code>fatal()</code> returns false, then the operation on the
- elements that don't have an exception has succeeded. The application
- can ignore the errors or try to correct the errors and re-attempt
- the operation on the elements that did fail. In either case, the
- transaction can be committed.</p>
-
- <p>An example of a fatal exception would be the situation where the
- execution of the underlying statement failed summarily, without
- attempting any data sets, for instance, because of an error in
- the statement itself.</p>
-
- <p>The <code>fatal()</code> modifier allows you to "upgrade" an
- exception to fatal, for example, for specific database error
- codes.</p>
-
-
- <!-- PART -->
-
-
- <hr class="page-break"/>
- <h1><a name="II">PART II&nbsp;&nbsp;
- <span style="font-weight: normal;">DATABASE SYSTEMS</span></a></h1>
-
- <p>Part II covers topics specific to the database system
- implementations and their support in ODB. The first chapter in
- Part II discusses how to use multiple database systems in the
- same application. The subsequent chapters describe the system-specific
- <code>database</code> classes as well as the default mapping
- between basic C++ value types and native database types. Part
- II consists of the following chapters.</p>
-
- <table class="toc">
- <tr><th>16</th><td><a href="#16">Multi-Database Support</a></td></tr>
- <tr><th>17</th><td><a href="#17">MySQL Database</a></td></tr>
- <tr><th>18</th><td><a href="#18">SQLite Database</a></td></tr>
- <tr><th>19</th><td><a href="#19">PostgreSQL Database</a></td></tr>
- <tr><th>20</th><td><a href="#20">Oracle Database</a></td></tr>
- <tr><th>21</th><td><a href="#21">Microsoft SQL Server Database</a></td></tr>
- </table>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="16">16 Multi-Database Support</a></h1>
-
- <p>Some applications may need to access multiple database systems, either
- simultaneously or one at a time. For example, an application may
- utilize an embedded database such as SQLite as a local cache and use
- a client-server database such as PostgreSQL for more permanent
- but slower to access remote storage. Or an application may need
- to be able to store its data in any database selected at runtime
- by the user. Yet another scenario is the data migration from one
- database system to another. In this case, multi-database support
- is only required for a short period. It is also plausible that an
- application implements all three of these scenarios, that is, it
- uses SQLite as a local cache, allows the user to select the remote
- database system, and supports data migration from one remote database
- system to another.</p>
-
- <p>ODB provides two types of multi-database support: <em>static</em>
- and <em>dynamic</em>. With static support we use the
- database system-specific interfaces to perform database
- operations. That is, instead of using <code>odb::database</code>,
- <code>odb::transaction</code>, or <code>odb::query</code>, we
- would use, for example, <code>odb::sqlite::database</code>,
- <code>odb::sqlite::transaction</code>, or
- <code>odb::sqlite::query</code> to access an SQLite database.</p>
-
- <p>In contrast, with <em>dynamic</em> multi-database support we can
- use the common interface to access any database without having to
- know which one it is. At runtime, ODB will automatically dispatch
- a call on the common interface to the specific database implementation
- based on the actual <code>database</code> instance being
- used. In fact, this mechanism is very similar to C++ virtual
- functions.</p>
-
- <p>Both static and dynamic multi-database support have a different set
- of advantages and disadvantages which makes them more or less suitable
- for different use cases. Static support has zero overhead compared
- to single-database support and allows us to use database
- system-specific features, extensions, etc. At the same time, the
- code that we write will be tied to the specific database system.
- As a result, this type of multi-database support is more
- suitable for situations where different parts of an application
- access different but specific database systems. For example,
- using SQLite as a local cache most likely falls into this
- category since we are using a specific database system (SQLite)
- and the code that will check the cache will most likely (but
- not necessarily) be separate from the code that interact with
- the remote database. Another example where static multi-database
- support might be more suitable is a once-off data migration from
- one database system to another. In this case both the source and
- target are specific database systems. In contrast, if data migration
- from one database system to another is a general feature in an
- application, then dynamic multi-database support might be more
- suitable.</p>
-
- <p>The main advantage of dynamic multi-database support is the
- database system-independence of the code that we write. The same
- application code will work with any database system supported by
- ODB and the generated database support code can be packaged into
- separate libraries and loaded dynamically by the application. The
- disadvantages of dynamic support are slight overhead and certain
- limitations in functionality compared to static support (see
- <a href="#16.2">Section 16.2, "Dynamic Multi-Database Support"</a>
- for details). As a result, dynamic multi-database support is most
- suitable to situations where we need the same code to
- work with a range of database systems. For example, if your
- application must be able to store its data in any database
- selected by the user, then dynamic support is probably the
- best option.</p>
-
- <p>Note also that it is possible to mix and match static and dynamic
- support in the same application. In fact, dynamic support is built
- on top of static support so it is possible to use the same database
- system both "statically" and "dynamically". In particular, the ability
- to "drop down" from dynamic to static support can be used to overcome
- the functionality limitations mentioned above. Finally,
- single-database support is just a special case of static
- multi-database support with a single database system.</p>
-
- <p>By default ODB assumes single-database support. To enable
- multi-database support we use the <code>--multi-database</code>
- (or <code>-m</code>) ODB compiler option. This option is also used to
- specify the support type: <code>static</code> or <code>dynamic</code>.
- For example:</p>
-
- <pre class="terminal">
-odb -m static ... person.hxx
- </pre>
-
- <p>With multi-database support enabled, we can now generate the database
- support code for several database systems. This can be accomplished
- either with a single ODB compiler invocation by specifying multiple
- <code>--database</code> (or <code>-d</code>) options or with multiple
- ODB compiler invocations. Both approaches produce the same result,
- for example:</p>
-
- <pre class="terminal">
-odb -m static -d common -d sqlite -d pgsql person.hxx
- </pre>
-
- <p>Is equivalent to:</p>
-
- <pre class="terminal">
-odb -m static -d common person.hxx
-odb -m static -d sqlite person.hxx
-odb -m static -d pgsql person.hxx
- </pre>
-
- <p>Notice that the first <code>-d</code> option has <code>common</code>
- as its value. This is not a real database system. Rather, it instructs
- the ODB compiler to generate code that is common to all the database
- systems and, in case of dynamic support, is also the common
- interfaces.</p>
-
- <p>If you look at the result of the above commands, you will also notice
- changes in the output file names. In the single-database mode the ODB
- compiler produces a single set of the <code>person-odb.?xx</code> files
- which contain both the common as well as the database specific
- generated code (since there is only one database system in use,
- there is no reason to split the two). In contrast, in the
- multi-database mode, the <code>person-odb.?xx</code> set of files
- contains the common code while the database system-specific code is
- written to files in the form <code>person-odb-&lt;db>.?xx</code>.
- That is, <code>person-odb-sqlite.?xx</code> for SQLite,
- <code>person-odb-pgsql.?xx</code> for PostgreSQL, etc.</p>
-
- <p>If we need dynamic support for some databases and static for
- others, then the <code>common</code> code must be generated
- in the dynamic mode. For example, if we need static support
- for SQLite and dynamic support for PostgreSQL and Oracle, then
- the ODB compiler invocations could look like this:</p>
-
- <pre class="terminal">
-odb -m dynamic -d common person.hxx
-odb -m static -d sqlite person.hxx
-odb -m dynamic -d pgsql person.hxx
-odb -m dynamic -d oracle person.hxx
- </pre>
-
- <p>With multi-database support enabled, it is possible to restrict ODB
- pragmas to apply only to a specific database system (unrestricted
- pragmas apply to all the databases). For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db pgsql:type("VARCHAR(128)") sqlite:type("TEXT")
- std::string name_;
-
- unsigned short age_;
-
- #pragma db pgsql index member(age_)
-};
- </pre>
-
- <p>Above, the pragma for the <code>name_</code> data member shows the
- use of a database prefix (for example, <code>pgsql:</code>) that
- only applies to the specifier that follows. The pragma that defines
- an index on the <code>age_</code> data member shows the use of a
- database prefix that applies to the whole pragma. In this case the
- database name must immediately follow the <code>db</code> keyword.</p>
-
-
- <p>Similar to pragmas, ODB compiler options that determine the kind
- (for example, <code>--schema-format</code>), names (for example,
- <code>--odb-file-suffix</code>), or content (for example, prologue
- and epilogue options) of the output files can be prefixed with the
- database name. For example:</p>
-
- <pre class="terminal">
-odb --odb-file-suffix common:-odb-common ...
- </pre>
-
- <p>Dynamic multi-database support requires consistent mapping across
- all the databases. That is, the same classes and data members
- should be mapped to objects, simple/composite values, etc., for
- all the databases. In contrast, static multi-database support
- does not have this restriction. Specifically, with static support,
- some data members can be transient for some database systems.
- Similarly, the same class (for example, <code>point</code>) can
- be mapped to a simple value in one database (for example, to the
- <code>POINT</code> PostgreSQL type) and to a composite value
- in another (for example, in SQLite, which does not have a
- built-in point type).</p>
-
- <p>The following sections discuss static and dynamic multi-database
- support in more detail.</p>
-
-
- <h2><a name="16.1">16.1 Static Multi-Database Support</a></h2>
-
- <p>With static multi-database support, instead of including
- <code>person-odb.hxx</code>, application source code has
- to include <code>person-odb-&lt;db>.hxx</code> header files
- corresponding to the database systems that will be used.</p>
-
- <p>The application code has to also use database system-specific
- interfaces when performing database operations. As an example,
- consider the following transaction in a single-database
- application. It uses the common interfaces, that is, classes
- from the <code>odb</code> namespace.</p>
-
- <pre class="cxx">
-#include "person-odb.hxx"
-
-odb::database&amp; db = ...
-
-typedef odb::query&lt;person> query;
-typedef odb::result&lt;person> result;
-
-odb::transaction t (db.begin ());
-result r (db.query&lt;person> (query::age &lt; 30));
-...
-t.commit ();
- </pre>
-
- <p>In an application that employs static multi-database support
- the same transaction for SQLite would be rewritten like this:</p>
-
- <pre class="cxx">
-#include "person-odb-sqlite.hxx"
-
-odb::sqlite::database&amp; db = ...
-
-typedef odb::sqlite::query&lt;person> query;
-typedef odb::result&lt;person> result; // odb:: not odb::sqlite::
-
-odb::sqlite::transaction t (db.begin ());
-result r (db.query&lt;person> (query::age &lt; 30));
-...
-t.commit ();
- </pre>
-
- <p>That is, the <code>database</code>, <code>transaction</code>, and
- <code>query</code> classes now come from the <code>odb::sqlite</code>
- namespace instead of <code>odb</code>. Other classes that have
- database system-specific interfaces are <code>connection</code>,
- <code>statement</code>, and <code>tracer</code>. Note that
- all of them derive from the corresponding common versions. It
- is also possible to use common <code>transaction</code>,
- <code>connection</code>, and <code>statement</code> classes
- with static support, if desired.</p>
-
- <p>Notice that we didn't use the <code>odb::sqlite</code> namespace
- for the <code>result</code> class template. This is because
- <code>result</code> is database system-independent. All other
- classes defined in namespace <code>odb</code>, except those
- specifically mentioned above, are database system-independent.
- In particular, <code>result</code>, <code>prepared_query</code>,
- <code>session</code>, <code>schema_catalog</code>, and all the
- exceptions are database system-independent.</p>
-
- <p>Writing <code>odb::sqlite::</code> before every name can quickly
- become burdensome. As we have seen before, in single-database
- applications that use the common interface we can add the
- <code>using namespace</code> directive to avoid qualifying
- each name. For example:</p>
-
- <pre class="cxx">
-#include "person-odb.hxx"
-
-odb::database&amp; db = ...
-
-{
- using namespace odb::core;
-
- typedef query&lt;person> person_query;
- typedef result&lt;person> person_result;
-
- transaction t (db.begin ());
- person_result r (db.query&lt;person> (person_query::age &lt; 30));
- ...
- t.commit ();
-}
- </pre>
-
- <p>A similar mechanism is available in multi-database support. Each
- database runtime defines the <code>odb::&lt;db>::core</code>
- namespace that contains all the database system-independent
- names as well as the database system-specific ones for this
- database. Here is how we can rewire the above transaction
- using this approach:</p>
-
- <pre class="cxx">
-#include "person-odb-sqlite.hxx"
-
-odb::sqlite::database&amp; db = ...
-
-{
- using namespace odb::sqlite::core;
-
- typedef query&lt;person> person_query;
- typedef result&lt;person> person_result;
-
- transaction t (db.begin ());
- person_result r (db.query&lt;person> (person_query::age &lt; 30));
- ...
- t.commit ();
-}
- </pre>
-
- <p>If the <code>using namespace</code> directive cannot be used, for
- example, because the same code fragment accesses several databases,
- then we can still make the namespace qualifications more concise
- by assigning shorter aliases to database namespaces. For example:</p>
-
- <pre class="cxx">
-#include "person-odb-pgsql.hxx"
-#include "person-odb-sqlite.hxx"
-
-namespace pg = odb::pgsql;
-namespace sl = odb::sqlite;
-
-pg::database&amp; pg_db = ...
-sl::database&amp; sl_db = ...
-
-typedef pg::query&lt;person> pg_query;
-typedef sl::query&lt;person> sl_query;
-typedef odb::result&lt;person> result;
-
-// First check the local cache.
-//
-odb::transaction t (sl_db.begin ()); // Note: using common transaction.
-result r (sl_db.query&lt;person> (sl_query::age &lt; 30));
-
-// If no hits, try the remote database.
-//
-if (r.empty ())
-{
- t.commit (); // End the SQLite transaction.
- t.reset (pg_db.begin ()); // Start the PostgreSQL transaction.
-
- r = pg_db.query&lt;person> (pg_query::age &lt; 30);
-}
-
-// Handle the result.
-//
-...
-
-t.commit ();
- </pre>
-
- <p>With static multi-database support we can make one of the databases
- the default database with the <code>--default-database</code> option.
- The default database can be accessed via the common interface, just
- like with single-database support. For example:</p>
-
- <pre class="terminal">
-odb -m static -d common -d pgsql -d sqlite --default-database pgsql ...
- </pre>
-
- <p>The default database mechanism can be useful when one of the
- databases is primary or when retrofitting multi-database support
- into an existing single-database application. For example, if
- we are adding SQLite as a local cache into an existing
- application that uses PostgreSQL as its only database, then
- by making PostgreSQL the default database we avoid having to
- change all the existing code. Note that if dynamic multi-database
- support is enabled, then the common (dynamic) interface is always
- made the default database.</p>
-
- <h2><a name="16.2">16.2 Dynamic Multi-Database Support</a></h2>
-
- <p>With dynamic multi-database support, application source code only
- needs to include the <code>person-odb.hxx</code> header file, just
- like with single-database support. In particular, we don't need
- to include any of the <code>person-odb-&lt;db>.hxx</code> files
- unless we would also like to use certain database systems in the
- static multi-database mode.</p>
-
- <p>When performing database operations, the application code
- uses the common interfaces from the <code>odb</code> namespace,
- just like with single-database support. As an example, consider
- a function that can be used to load an object either from a local
- SQLite cache or a remote PostgreSQL database (in reality, this
- function can be used with any database system support by ODB
- provided we generated the database support code for this database
- and linked it into our application):</p>
-
- <pre class="cxx">
-#include "person-odb.hxx"
-
-std::unique_ptr&lt;person>
-load (odb::database&amp; db, const std::string&amp; name)
-{
- odb::transaction t (db.begin ());
- std::unique_ptr&lt;person> p (db.find (name));
- t.commit ();
- return p;
-}
-
-odb::pgsql::database&amp; pg_db = ...
-odb::sqlite::database&amp; sl_db = ...
-
-// First try the local cache.
-//
-std::unique_ptr&lt;person> p (load (sl_db, "John Doe"));
-
-// If not found, try the remote database.
-//
-if (p == 0)
- p = load (pg_db, "John Doe");
-
-...
- </pre>
-
- <p>As you can see, we can use dynamic multi-database support just like
- single-database support except that now our code can work with
- different database systems. Note, however, one difference: with
- single-database support we could perform database operations using
- either the common <code>odb::database</code> or a database system-specific
- (for example, <code>odb::sqlite::database</code>) interface
- with the same effect. In contrast, with dynamic multi-database support,
- the use of the database system-specific interface results in the
- switch to the static mode (for which, as was mentioned earlier, we would
- need to include the corresponding <code>person-odb-&lt;db>.hxx</code>
- header file). As we will discuss shortly, switching from dynamic to
- static mode can be used to overcome limitations imposed by dynamic
- multi-database support.</p>
-
- <p>Dynamic multi-database support has certain overheads and limitations
- compared to static support. For database operations, the generated code
- maintains function tables that are used to dispatch calls to the database
- system-specific implementations. In single-database and static
- multi-database support, the <code>query</code> type implements a thin
- wrapper around the underlying database system's <code>SELECT</code>
- statement. With dynamic multi-database support, because the
- underlying database system is only known at query execution
- (or preparation) time, the <code>query</code> type stores a
- database system-independent representation of the query that
- is then translated to the database system-specific form. Because
- of this database system-independent representation, dynamic
- support queries have a number of limitations. Specifically, dynamic
- queries do not support parameter binding in native query fragments.
- They also make copies of by-value parameterd (by-reference parameters
- can be used to remove this overhead). Finally, parameters of array
- types (for example, <code>char[256]</code>) can only be bound
- by-reference.</p>
-
- <p>As we mentioned earlier, switching from dynamic to static mode
- can be an effective way to overcome these limitations. As an
- example, consider a function that prints the list of people of
- a certain age. The caller also specified the limit on the number
- of entries to print. Some database systems, for example, PostgreSQL,
- allow us to propagate this limit to the database server with the
- <code>LIMIT</code> clause. To add this clause we would need to
- construct a native query fragment and, as we discussed above, we
- won't be able to bind a parameter (the limit) while in the dynamic
- mode. The following implementation shows how we can overcome this
- by switching to the static mode and using the PostgreSQL-specific
- interface:</p>
-
- <pre class="cxx">
-#include "person-odb.hxx"
-#include "person-odb-pgsql.hxx" // Needed for static mode.
-
-void
-print (odb::database&amp; db, unsigned short age, unsigned long limit)
-{
- typedef odb::query&lt;person> query;
- typedef odb::result&lt;person> result;
-
- odb::transaction t (db.begin ());
-
- query q (query::age == age);
- result r;
-
- if (db.id () == odb::id_pgsql)
- {
- // We are using PostgreSQL. Drop down to the static mode and
- // add the LIMIT clause to the query.
- //
- namespace pg = odb::pgsql;
- typedef pg::query&lt;person> pg_query;
-
- pg::database&amp; pg_db (static_cast&lt;pg::database&amp;> (db));
- pg_query pg_q (pg_query (q) + "LIMIT" + pg_query::_val (limit));
- r = pg_db.query&lt;person> (pg_q);
- }
- else
- r = db.query&lt;person> (q);
-
- // Handle the result up to the limit elements.
- //
- ...
-
- t.commit ();
-}
-
-odb::pgsql::database&amp; pg_db = ...
-odb::sqlite::database&amp; sl_db = ...
-
-print (sl_db, 30, 100);
-print (sl_db, 30, 100);
- </pre>
-
- <p>A few things to note about this example. First, we use the
- <code>database::id()</code> function to determine the actual database
- system we use. This function has the following signature:</p>
-
- <pre class="cxx">
-namespace odb
-{
- enum database_id
- {
- id_mysql,
- id_sqlite,
- id_pgsql,
- id_oracle,
- id_mssql,
- id_common
- };
-
- class database
- {
- public:
- ...
-
- database_id
- id () const;
- }
-}
- </pre>
-
- <p>Note that <code>database::id()</code> can never return the
- <code>id_common</code> value.</p>
-
- <p>The other thing to note is how we translate the dynamic query
- to the database system-specific one (the <code>pg_query (q)</code>
- expression). Every <code>odb::&lt;db>::query</code> class provides
- such a translation constructor.</p>
-
- <h3><a name="16.2.2">16.2.2 Dynamic Loading of Database Support Code</a></h3>
-
- <p>With dynamic multi-database support, the generated database support
- code automatically registers itself with the function tables that
- we mentioned earlier. This makes it possible to package the generated
- code for each database into a separate dynamic-link library (Windows
- DLL) or dynamic shared object (Unix DSO; collectively referred to as
- DLLs from now on) and load/unload them from the application
- dynamically using APIs such as Win32 <code>LoadLibrary()</code> or
- POSIX <code>dlopen()</code>. This allows the application address
- space to contain code only for database systems that are actually
- needed in any particular moment. Another advantage of this approach
- is the ability to distribute individual database system support
- separately.</p>
-
- <p>This section provides an overview of how to package the generated
- database support code into DLLs for both Windows and Unix using
- GNU/Linux as an example. Note also that if static multi-database
- support is used for a particular database system, then the dynamic
- loading cannot be used for this database. It is, however, still
- possible to package the generated code into a DLL but this DLL
- will have to be linked to the executable at link-time rather
- than at runtime. If dynamic loading is desirable in this situation,
- then another alternative would be to package the functionality
- that requires static support together with the database support
- code into the DLL and import this functionality dynamically
- using the <code>GetProcAddress()</code> (Win32) or <code>dlsym()</code>
- (Unix) function.</p>
-
- <p>The first step in packaging the generated code into DLLs is to
- set up the symbol exporting. This step is required for
- Windows DLLs but is optional for Unix DSOs. Most modern Unix
- systems (such as GNU/Linux) provide control over symbol
- visibility, which is a mechanism similar to Windows symbol
- exporting. Notable advantages of using this mechanism to
- explicitly specify which symbols are visible include
- smaller Unix DSOs and faster load times. If, however, you are
- not planning to control symbol visibility on Unix, then you can
- skip directly to the second step below.</p>
-
- <p>An important point to understand is that we only need to export
- the common interface, that is, the classes defined in the
- <code>person-odb.hxx</code> header. In particular, we don't need
- to export the database system-specific classes defined in
- the <code>person-odb-&lt;db>.hxx</code>, unless we are also using
- this database in the static mode (in which case, the procedure
- described below will need to be repeated for that database as
- well).</p>
-
- <p>The ODB compiler provides two command line options,
- <code>--export-symbol</code> and <code>--extern-symbol</code>,
- which can be used to insert the export and extern
- macros in all the necessary places in the generated header file.
- You are probably familiar with the concept of export macro which
- expands to an export directive if we are building the DLL and to
- an import directive if we are building client code. The
- extern macro is a supplementary mechanism which is necessary to
- export explicit template instantiations used by the generated
- code when query support is enabled. As we will see shortly, the
- extern macro must expand into the <code>extern</code> C++ keyword
- in certain situations and must be left undefined in others. To
- manage all these macro definitions, it is customary to create the
- so called export header. Based on a single macro that is normally
- defined in the project file or on the command line and which
- indicates whether we are building the DLL or client code, the
- export header file sets the export and extern macros to their
- appropriate values. Continuing with our person example, on Windows
- the export header, which we will call <code>person-export.hxx</code>,
- could look like this:</p>
-
- <pre class="cxx">
-// person-export.hxx
-//
-// Define PERSON_BUILD_DLL if we are building the DLL. Leave it
-// undefined in client code.
-//
-#ifndef PERSON_EXPORT_HXX
-#define PERSON_EXPORT_HXX
-
-#ifdef PERSON_BUILD_DLL
-# define PERSON_EXPORT __declspec(dllexport)
-#else
-# define PERSON_EXPORT __declspec(dllimport)
-# define PERSON_EXTERN extern
-#endif
-
-#endif // PERSON_EXPORT_HXX
- </pre>
-
- <p>The equivalent export header for GCC on GNU/Linux is shown below.
- Note also that on GNU/Linux, by default, all symbols are visible
- and we need to add the GCC <code>-fvisibility=hidden</code> option to
- make them hidden by default.</p>
-
- <pre class="cxx">
-// person-export.hxx
-//
-#ifndef PERSON_EXPORT_HXX
-#define PERSON_EXPORT_HXX
-
-#define PERSON_EXPORT __attribute__ ((visibility ("default")))
-#define PERSON_EXTERN extern
-
-#endif // PERSON_EXPORT_HXX
- </pre>
-
- <p>Next we need to export the <code>person</code> persistent class
- using the export macro and re-compile our <code>person.hxx</code> file
- with the <code>--export-symbol</code> and <code>--extern-symbol</code>
- options. We will also need to include <code>person-export.hxx</code>
- into the generated <code>person-odb.hxx</code> file. For that we use
- the <code>--hxx-prologue</code> option. Here is how we can do
- this with multiple invocations of the ODB compiler:</p>
-
- <pre class="terminal">
-odb -m dynamic -d common --hxx-prologue "#include \"person-export.hxx\"" \
---export-symbol PERSON_EXPORT --extern-symbol PERSON_EXTERN person.hxx
-
-odb -m dynamic -d sqlite person.hxx
-odb -m dynamic -d pgsql person.hxx
- </pre>
-
- <p>It is also possible to achieve the same with a single invocation.
- Here we need to restrict some option values to apply only to the
- <code>common</code> database:</p>
-
- <pre class="terminal">
-odb -m dynamic -d common -d sqlite -d pgsql \
---hxx-prologue "common:#include \"person-export.hxx\"" \
---export-symbol common:PERSON_EXPORT --extern-symbol common:PERSON_EXTERN \
-person.hxx
- </pre>
-
- <p>The second step in packaging the generated code into DLLs is to
- decide where to place the generated common interface code. One
- option is to place it into a DLL of its own so that we will end
- up with (replace <code>*.dll</code> with <code>lib*.so</code> for
- Unix): <code>person.dll</code> plus <code>person-sqlite.dll</code> and
- <code>person-pgsql.dll</code>, which both link to <code>person.dll</code>,
- as well as <code>person.exe</code>, which links to <code>person.dll</code>
- and dynamically loads <code>person-sqlite.dll</code>
- and/or <code>person-pgsql.dll</code>. If this is the organization
- that you prefer, then the next step is to build all the DLLs as you
- normally would any other DLL, placing <code>person-odb.cxx</code>
- and <code>person.cxx</code> into <code>person.dll</code>,
- <code>person-odb-sqlite.cxx</code> into <code>person-sqlite.dll</code>,
- etc. Note that in the pure dynamic multi-database support,
- <code>person-sqlite.dll</code> and <code>person-pgsql.dll</code>
- do not export any symbols.</p>
-
- <p>We can improve on the above organization by getting rid of
- <code>person.dll</code>, which is not really necessary unless
- we have multiple executables sharing the same database support.
- To achieve this, we will place <code>person-odb.cxx</code> into
- <code>person.exe</code> and export its symbols from the executable
- instead of a DLL. Exporting symbols from an executable is a seldom
- used functionality, especially on Windows, however, it is well
- supported on both Windows and most Unix platforms. Note also that
- this approach won't work if we also use one of the databases in the
- static mode.</p>
-
- <p>On Windows all we have to do is place <code>person-odb.cxx</code>
- into the executable and compile it as we would in a DLL (that is,
- with the <code>PERSON_BUILD_DLL</code> macro defined). If Windows
- linker detects that an executable exports any symbols, then it
- will automatically create the corresponding import library
- (<code>person.lib</code> in our case). We then use this import
- library to build <code>person-sqlite.dll</code> and
- <code>person-pgsql.dll</code> as before.</p>
-
- <p>To export symbols from an executable on GNU/Linux all we need to
- do is add the <code>-rdynamic</code> option when linking our
- executable.</p>
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="17">17 MySQL Database</a></h1>
-
- <p>To generate support code for the MySQL database you will need
- to pass the "<code>--database&nbsp;mysql</code>"
- (or "<code>-d&nbsp;mysql</code>") option to the ODB compiler.
- Your application will also need to link to the MySQL ODB runtime
- library (<code>libodb-mysql</code>). All MySQL-specific ODB
- classes are defined in the <code>odb::mysql</code> namespace.</p>
-
- <h2><a name="17.1">17.1 MySQL Type Mapping</a></h2>
-
- <p>The following table summarizes the default mapping between basic
- C++ value types and MySQL database types. This mapping can be
- customized on the per-type and per-member basis using the ODB
- Pragma Language (<a href="#14">Chapter 14, "ODB Pragma
- Language"</a>).</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>C++ Type</th>
- <th>MySQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>bool</code></td>
- <td><code>TINYINT(1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char</code></td>
- <td><code>CHAR(1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>signed char</code></td>
- <td><code>TINYINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned char</code></td>
- <td><code>TINYINT UNSIGNED</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>short</code></td>
- <td><code>SMALLINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned short</code></td>
- <td><code>SMALLINT UNSIGNED</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>int</code></td>
- <td><code>INT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned int</code></td>
- <td><code>INT UNSIGNED</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long</code></td>
- <td><code>BIGINT UNSIGNED</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long long</code></td>
- <td><code>BIGINT UNSIGNED</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>float</code></td>
- <td><code>FLOAT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>double</code></td>
- <td><code>DOUBLE</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>std::string</code></td>
- <td><code>TEXT/VARCHAR(128)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char[N]</code></td>
- <td><code>VARCHAR(N-1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
- </table>
-
- <p>It is possible to map the <code>char</code> C++ type to an integer
- database type (for example, <code>TINYINT</code>) using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>).</p>
-
- <p>Note that the <code>std::string</code> type is mapped
- differently depending on whether a member of this type
- is an object id or not. If the member is an object id,
- then for this member <code>std::string</code> is mapped
- to the <code>VARCHAR(128)</code> MySQL type. Otherwise,
- it is mapped to <code>TEXT</code>.</p>
-
- <p>Additionally, by default, C++ enums and C++11 enum classes are
- automatically mapped to suitable MySQL types. Contiguous
- enumerations with the zero first enumerator are mapped to
- the MySQL <code>ENUM</code> type. All other enumerations
- are mapped to the MySQL types corresponding to their
- underlying integral types (see table above). In both
- cases the default <code>NULL</code> semantics is
- <code>NOT NULL</code>. For example:</p>
-
- <pre class="cxx">
-enum color {red, green, blue};
-enum class taste: unsigned char
-{
- bitter = 1, // Non-zero first enumerator.
- sweet,
- sour = 4, // Non-contiguous.
- salty
-};
-
-#pragma db object
-class object
-{
- ...
-
- color color_; // Mapped to ENUM ('red', 'green', 'blue') NOT NULL.
- taste taste_; // Mapped to TINYNT UNSIGNED NOT NULL.
-};
- </pre>
-
- <p>The only built-in mapping provided for the MySQL <code>DECIMAL</code>
- type is to <code>std::string/char[N]</code>, for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type ("DECIMAL(6,3)")
- std::string value_;
-};
- </pre>
-
- <p>You can, however, map <code>DECIMAL</code> to a custom C++ type by
- providing a suitable <code>odb::mysql::value_traits</code>
- specialization.</p>
-
- <p>It is also possible to add support for additional MySQL types,
- such as geospatial types. For more information, refer to
- <a href="#14.8">Section 14.8, "Database Type Mapping
- Pragmas"</a>.</p>
-
- <h3><a name="17.1.1">17.1.1 String Type Mapping</a></h3>
-
- <p>The MySQL ODB runtime library provides support for mapping the
- <code>std::string</code>, <code>char[N]</code>, and
- <code>std::array&lt;char, N></code> types to the MySQL <code>CHAR</code>,
- <code>VARCHAR</code>, <code>TEXT</code>, <code>NCHAR</code>, and
- <code>NVARCHAR</code> types. However, these mappings are not enabled
- by default (in particular, by default, <code>std::array</code> will
- be treated as a container). To enable the alternative mappings for
- these types we need to specify the database type explicitly using
- the <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section
- 14.4.3, "<code>type</code>"</a>), for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("CHAR(2)")
- char state_[2];
-
- #pragma db type("VARCHAR(128)")
- std::string name_;
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-#pragma db value(std::string) type("VARCHAR(128)")
-
-#pragma db object
-class object
-{
- ...
-
- std::string name_; // Mapped to VARCHAR(128).
-};
- </pre>
-
- <p>The <code>char[N]</code> and <code>std::array&lt;char, N></code> values
- may or may not be zero-terminated. When extracting such values from the
- database, ODB will append the zero terminator if there is enough
- space.</p>
-
- <h3><a name="17.1.2">17.1.2 Binary Type Mapping</a></h3>
-
- <p>The MySQL ODB runtime library provides support for mapping the
- <code>std::vector&lt;char></code>,
- <code>std::vector&lt;unsigned&nbsp;char></code>,
- <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
- <code>std::array&lt;char, N></code>, and
- <code>std::array&lt;unsigned char, N></code>
- types to the MySQL <code>BINARY</code>, <code>VARBINARY</code>,
- and <code>BLOB</code> types. However, these mappings are not enabled
- by default (in particular, by default, <code>std::vector</code> and
- <code>std::array</code> will be treated as containers). To enable the
- alternative mappings for these types we need to specify the database
- type explicitly using the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), for
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("BLOB")
- std::vector&lt;char> buf_;
-
- #pragma db type("BINARY(16)")
- unsigned char uuid_[16];
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;char> buffer;
-#pragma db value(buffer) type("BLOB")
-
-#pragma db object
-class object
-{
- ...
-
- buffer buf_; // Mapped to BLOB.
-};
- </pre>
-
- <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
- the Database"</a>) <code>char[N]</code> and
- <code>std::array&lt;char, N></code> parameters are by default passed
- as a string rather than a binary. To pass such parameters as a binary,
- we need to specify the database type explicitly in the
- <code>_val()</code>/<code>_ref()</code> calls. Note also that we
- don't need to do this for the integrated queries, for example:</p>
-
- <pre class="cxx">
-char u[16] = {...};
-
-db.query&lt;object> ("uuid = " + query::_val&lt;odb::mysql::id_blob> (u));
-db.query&lt;object> (query::uuid == query::_ref (u));
- </pre>
-
- <h2><a name="17.2">17.2 MySQL Database Class</a></h2>
-
- <p>The MySQL <code>database</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mysql
- {
- class database: public odb::database
- {
- public:
- database (const char* user,
- const char* passwd,
- const char* db,
- const char* host = 0,
- unsigned int port = 0,
- const char* socket = 0,
- const char* charset = 0,
- unsigned long client_flags = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; user,
- const std::string&amp; passwd,
- const std::string&amp; db,
- const std::string&amp; host = "",
- unsigned int port = 0,
- const std::string* socket = 0,
- const std::string&amp; charset = "",
- unsigned long client_flags = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; user,
- const std::string* passwd,
- const std::string&amp; db,
- const std::string&amp; host = "",
- unsigned int port = 0,
- const std::string* socket = 0,
- const std::string&amp; charset = "",
- unsigned long client_flags = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; user,
- const std::string&amp; passwd,
- const std::string&amp; db,
- const std::string&amp; host,
- unsigned int port,
- const std::string&amp; socket,
- const std::string&amp; charset = "",
- unsigned long client_flags = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; user,
- const std::string* passwd,
- const std::string&amp; db,
- const std::string&amp; host,
- unsigned int port,
- const std::string&amp; socket,
- const std::string&amp; charset = "",
- unsigned long client_flags = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (int&amp; argc,
- char* argv[],
- bool erase = false,
- const std::string&amp; charset = "",
- unsigned long client_flags = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- static void
- print_usage (std::ostream&amp;);
-
- public:
- const char*
- user () const;
-
- const char*
- password () const;
-
- const char*
- db () const;
-
- const char*
- host () const;
-
- unsigned int
- port () const;
-
- const char*
- socket () const;
-
- const char*
- charset () const;
-
- unsigned long
- client_flags () const;
-
- public:
- connection_ptr
- connection ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/mysql/database.hxx></code>
- header file to make this class available in your application.</p>
-
- <p>The overloaded <code>database</code> constructors allow us
- to specify MySQL database parameters that should be used when
- connecting to the database. In MySQL <code>NULL</code> and an
- empty string are treated as the same values for all the
- string parameters except <code>password</code> and
- <code>socket</code>.</p>
-
- <p>The <code>charset</code> argument allows us to specify the client
- character set, that is, the character set in which the application
- will encode its text data. Note that this can be different from
- the MySQL server character set. If this argument is not specified or
- is empty, then the default MySQL client character set is used, normally
- <code>latin1</code>. Commonly used values for this argument are
- <code>latin1</code> (equivalent to Windows cp1252 and similar to
- ISO-8859-1) and <code>utf8</code>. For other possible values
- as well as more information on character set support in MySQL,
- refer to the MySQL documentation.</p>
-
- <p>The <code>client_flags</code> argument allows us to specify various
- MySQL client library flags. For more information on the possible
- values, refer to the MySQL C API documentation. The
- <code>CLIENT_FOUND_ROWS</code> flag is always set by the MySQL ODB
- runtime regardless of whether it was passed in the
- <code>client_flags</code> argument.</p>
-
- <p>The last constructor extracts the database parameters
- from the command line. The following options are recognized:</p>
-
- <pre class="terminal">
- --user &lt;login>
- --password &lt;password>
- --database &lt;name>
- --host &lt;host>
- --port &lt;integer>
- --socket &lt;socket>
- --options-file &lt;file>
- </pre>
-
- <p>The <code>--options-file</code> option allows us to specify some
- or all of the database options in a file with each option appearing
- on a separate line followed by a space and an option value.</p>
-
- <p>If the <code>erase</code> argument to this constructor is true,
- then the above options are removed from the <code>argv</code>
- array and the <code>argc</code> count is updated accordingly.
- This is primarily useful if your application accepts other
- options or arguments and you would like to get the MySQL
- options out of the <code>argv</code> array.</p>
-
- <p>This constructor throws the <code>odb::mysql::cli_exception</code>
- exception if the MySQL option values are missing or invalid.
- See section <a href="#17.4">Section 17.4, "MySQL Exceptions"</a>
- for more information on this exception.</p>
-
- <p>The static <code>print_usage()</code> function prints the list of options
- with short descriptions that are recognized by this constructor.</p>
-
- <p>The last argument to all of the constructors is a pointer to the
- connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
- in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
- non-<code>NULL</code> value, the database instance assumes ownership
- of the factory instance. The connection factory interface as well as
- the available implementations are described in the next section.</p>
-
- <p>The set of accessor functions following the constructors allows us
- to query the parameters of the <code>database</code> instance.</p>
-
- <p>The <code>connection()</code> function returns a pointer to the
- MySQL database connection encapsulated by the
- <code>odb::mysql::connection</code> class. For more information
- on <code>mysql::connection</code>, refer to <a href="#17.3">Section
- 17.3, "MySQL Connection and Connection Factory"</a>.</p>
-
- <h2><a name="17.3">17.3 MySQL Connection and Connection Factory</a></h2>
-
- <p>The <code>mysql::connection</code> class has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mysql
- {
- class connection: public odb::connection
- {
- public:
- connection (database&amp;);
- connection (database&amp;, MYSQL*);
-
- MYSQL*
- handle ();
- };
-
- typedef details::shared_ptr&lt;connection> connection_ptr;
- }
-}
- </pre>
-
- <p>For more information on the <code>odb::connection</code> interface,
- refer to <a href="#3.6">Section 3.6, "Connections"</a>. The first
- overloaded <code>mysql::connection</code> constructor establishes a
- new MySQL connection. The second constructor allows us to create
- a <code>connection</code> instance by providing an already connected
- native MySQL handle. Note that the <code>connection</code>
- instance assumes ownership of this handle. The <code>handle()</code>
- accessor returns the MySQL handle corresponding to the connection.</p>
-
- <p>The <code>mysql::connection_factory</code> abstract class has the
- following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mysql
- {
- class connection_factory
- {
- public:
- virtual void
- database (database&amp;) = 0;
-
- virtual connection_ptr
- connect () = 0;
- };
- }
-}
- </pre>
-
- <p>The <code>database()</code> function is called when a connection
- factory is associated with a database instance. This happens in
- the <code>odb::mysql::database</code> class constructors. The
- <code>connect()</code> function is called whenever a database
- connection is requested.</p>
-
- <p>The two implementations of the <code>connection_factory</code>
- interface provided by the MySQL ODB runtime are
- <code>new_connection_factory</code> and
- <code>connection_pool_factory</code>. You will need to include
- the <code>&lt;odb/mysql/connection-factory.hxx></code>
- header file to make the <code>connection_factory</code> interface
- and these implementation classes available in your application.</p>
-
- <p>The <code>new_connection_factory</code> class creates a new
- connection whenever one is requested. When a connection is no
- longer needed, it is released and closed. The
- <code>new_connection_factory</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mysql
- {
- class new_connection_factory: public connection_factory
- {
- public:
- new_connection_factory ();
- };
-};
- </pre>
-
- <p>The <code>connection_pool_factory</code> class implements a
- connection pool. It has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mysql
- {
- class connection_pool_factory: public connection_factory
- {
- public:
- connection_pool_factory (std::size_t max_connections = 0,
- std::size_t min_connections = 0,
- bool ping = true);
-
- protected:
- class pooled_connection: public connection
- {
- public:
- pooled_connection (database_type&amp;);
- pooled_connection (database_type&amp;, MYSQL*);
- };
-
- typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
-
- virtual pooled_connection_ptr
- create ();
- };
-};
- </pre>
-
- <p>The <code>max_connections</code> argument in the
- <code>connection_pool_factory</code> constructor specifies the maximum
- number of concurrent connections that this pool factory will
- maintain. Similarly, the <code>min_connections</code> argument
- specifies the minimum number of available connections that
- should be kept open. The <code>ping</code> argument specifies
- whether the factory should validate the connection before
- returning it to the caller.</p>
-
- <p>Whenever a connection is requested, the pool factory first
- checks if there is an unused connection that can be returned.
- If there is none, the pool factory checks the
- <code>max_connections</code> value to see if a new connection
- can be created. If the total number of connections maintained
- by the pool is less than this value, then a new connection is
- created and returned. Otherwise, the caller is blocked until
- a connection becomes available.</p>
-
- <p>When a connection is released, the pool factory first checks
- if there are blocked callers waiting for a connection. If so, then
- one of them is unblocked and is given the connection. Otherwise,
- the pool factory checks whether the total number of connections
- maintained by the pool is greater than the <code>min_connections</code>
- value. If that's the case, the connection is closed. Otherwise, the
- connection is added to the pool of available connections to be
- returned on the next request. In other words, if the number of
- connections maintained by the pool exceeds <code>min_connections</code>
- and there are no callers waiting for a new connection,
- then the pool will close the excess connections.</p>
-
- <p>If the <code>max_connections</code> value is 0, then the pool will
- create a new connection whenever all of the existing connections
- are in use. If the <code>min_connections</code> value is 0, then
- the pool will never close a connection and instead maintain all
- the connections that were ever created.</p>
-
- <p>Connection validation (the <code>ping</code> argument) is useful
- if your application may experience long periods of inactivity. In
- such cases the MySQL server may close network connections that have
- been inactive for too long. If during connection validation the pool
- factory detects that the connection has been terminated, it silently
- closes it and tries to find or create another connection instead.</p>
-
- <p>The <code>create()</code> virtual function is called whenever the
- pool needs to create a new connection. By deriving from the
- <code>connection_pool_factory</code> class and overriding this
- function we can implement custom connection establishment
- and configuration.</p>
-
- <p>If you pass <code>NULL</code> as the connection factory to
- one of the <code>database</code> constructors, then the
- <code>connection_pool_factory</code> instance will be
- created by default with the min and max connections values
- set to <code>0</code> and connection validation enabled.
- The following code fragment shows how we can pass our own
- connection factory instance:</p>
-
- <pre class="cxx">
-#include &lt;odb/database.hxx>
-
-#include &lt;odb/mysql/database.hxx>
-#include &lt;odb/mysql/connection-factory.hxx>
-
-int
-main (int argc, char* argv[])
-{
- auto_ptr&lt;odb::mysql::connection_factory> f (
- new odb::mysql::connection_pool_factory (20));
-
- auto_ptr&lt;odb::database> db (
- new mysql::database (argc, argv, false, 0, f));
-}
- </pre>
-
- <h2><a name="17.4">17.4 MySQL Exceptions</a></h2>
-
- <p>The MySQL ODB runtime library defines the following MySQL-specific
- exceptions:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mysql
- {
- class database_exception: odb::database_exception
- {
- public:
- unsigned int
- error () const;
-
- const std::string&amp;
- sqlstate () const;
-
- const std::string&amp;
- message () const;
-
- virtual const char*
- what () const throw ();
- };
-
- class cli_exception: odb::exception
- {
- public:
- virtual const char*
- what () const throw ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/mysql/exceptions.hxx></code>
- header file to make these exceptions available in your application.</p>
-
- <p>The <code>odb::mysql::database_exception</code> is thrown if
- a MySQL database operation fails. The MySQL-specific error
- information is accessible via the <code>error()</code>,
- <code>sqlstate()</code>, and <code>message()</code> functions.
- All this information is also combined and returned in a
- human-readable form by the <code>what()</code> function.</p>
-
- <p>The <code>odb::mysql::cli_exception</code> is thrown by the
- command line parsing constructor of the <code>odb::mysql::database</code>
- class if the MySQL option values are missing or invalid. The
- <code>what()</code> function returns a human-readable description
- of an error.</p>
-
- <h2><a name="17.5">17.5 MySQL Limitations</a></h2>
-
- <p>The following sections describe MySQL-specific limitations imposed
- by the current MySQL and ODB runtime versions.</p>
-
- <h3><a name="17.5.1">17.5.1 Foreign Key Constraints</a></h3>
-
- <p>ODB relies on standard SQL behavior which requires that foreign
- key constraints checking is deferred until the transaction is
- committed. The only behaviors supported by MySQL are to either
- check such constraints immediately (InnoDB engine) or to ignore
- foreign key constraints altogether (all other engines). As a
- result, by default, schemas generated by the ODB compiler for
- MySQL have foreign key definitions commented out. They are
- retained only for documentation.</p>
-
- <p>You can override the default behavior and instruct the ODB
- compiler to generate non-deferrable foreign keys by specifying
- the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
- compiler option. Note, however, that in this case the order in
- which you persist, update, and erase objects within a transaction
- becomes important.</p>
-
- <h2><a name="17.6">17.6 MySQL Index Definitions</a></h2>
-
- <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
- "Index Definition Pragmas"</a>) is used to define a MySQL index,
- the <code>type</code> clause specifies the index type (for example,
- <code>UNIQUE</code>, <code>FULLTEXT</code>, <code>SPATIAL</code>),
- the <code>method</code> clause specifies the index method (for
- example, <code>BTREE</code>, <code>HASH</code>), and the
- <code>options</code> clause is not used. The column options
- can be used to specify column length limits and the sort order.
- For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- std::string name_;
-
- #pragma db index method("HASH") member(name_, "(100) DESC")
-};
- </pre>
-
- <h2><a name="17.7">17.7 MySQL Stored Procedures</a></h2>
-
- <p>ODB native views (<a href="#10.6">Section 10.6, "Native Views"</a>)
- can be used to call MySQL stored procedures. For example, assuming
- we are using the <code>person</code> class from <a href="#2">Chapter
- 2, "Hello World Example"</a> (and the corresponding <code>person</code>
- table), we can create a stored procedure that given the min and max
- ages returns some information about all the people in that range:</p>
-
- <pre class="sql">
-CREATE PROCEDURE person_range (
- IN min_age SMALLINT,
- IN max_age SMALLINT)
-BEGIN
- SELECT age, first, last FROM person
- WHERE age >= min_age AND age &lt;= max_age;
-END
- </pre>
-
- <p>Given the above stored procedure we can then define an ODB view
- that can be used to call it and retrieve its result:</p>
-
- <pre class="cxx">
-#pragma db view query("CALL person_range((?))")
-struct person_range
-{
- unsigned short age;
- std::string first;
- std::string last;
-};
- </pre>
-
- <p>The following example shows how we can use the above view to
- print the list of people in a specific age range:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;person_range> query;
-typedef odb::result&lt;person_range> result;
-
-transaction t (db.begin ());
-
-result r (
- db.query&lt;person_range> (
- query::_val (1) + "," + query::_val (18)));
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
- cerr &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->age &lt;&lt; endl;
-
-t.commit ();
- </pre>
-
- <p>Note that as with all native views, the order and types of data members
- must match those of columns in the <code>SELECT</code> list inside
- the stored procedure.</p>
-
- <p>There are also a number of limitations when it comes to support for
- MySQL stored procedures in ODB views. First of all, you have to use
- MySQL server and client libraries version 5.5.3 or later since this
- is the version in which support for calling stored procedures with
- prepared statements was first added (the
- <code>mysql_stmt_next_result()</code> function).</p>
-
- <p>In MySQL, a stored procedure can produce multiple results.
- For example, if a stored procedure executes several
- <code>SELECT</code> statements, then the result of calling such
- a procedure consists of two row sets, one for each <code>SELECT</code>
- statement. Additionally, if the procedure has any <code>OUT</code>
- or <code>INOUT</code> parameters, then their values are returned as
- an additional special row set containing only a single row.
- Because such multiple row sets can contain varying number
- and type of columns, they cannot be all extracted into a
- single view. As a result, an ODB view will only extract the
- data from the first row set and ignore all the subsequent
- ones.</p>
-
- <p>In particular, this means that we can use an ODB view to extract
- the values of the <code>OUT</code> and <code>INOUT</code>
- parameters provided that the stored procedure does not generate
- any other row sets. For example:</p>
-
- <pre class="sql">
-CREATE PROCEDURE person_min_max_age (
- OUT min_age SMALLINT,
- OUT max_age SMALLINT)
-BEGIN
- SELECT MIN(age), MAX(age) INTO min_age, max_age FROM person;
-END
- </pre>
-
- <pre class="cxx">
-#pragma db view query("CALL person_min_max_age((?))")
-struct person_min_max_age
-{
- unsigned short min_age;
- unsigned short max_age;
-};
- </pre>
-
- <pre class="cxx">
-typedef odb::query&lt;person_min_max_age> query;
-
-transaction t (db.begin ());
-
-// We know this query always returns a single row, so use query_value().
-// We have to pass dummy values for OUT parameters.
-//
-person_min_max_age mma (
- db.query_value&lt;person_min_max_age> (
- query::_val (0) + "," + query::_val (0)));
-
-cerr &lt;&lt; mma.min_age &lt;&lt; " " &lt;&lt; mma.max_age &lt;&lt; endl;
-
-t.commit ();
- </pre>
-
- <p>Another limitation that stems from having multiple results is the
- inability to cache the result of a stored procedure call. In
- other words, a MySQL stored procedure call always produces an
- uncached query result (<a href="#4.4">Section 4.4, "Query
- Result"</a>).</p>
-
- <hr class="page-break"/>
- <h1><a name="18">18 SQLite Database</a></h1>
-
- <p>To generate support code for the SQLite database you will need
- to pass the "<code>--database&nbsp;sqlite</code>"
- (or "<code>-d&nbsp;sqlite</code>") option to the ODB compiler.
- Your application will also need to link to the SQLite ODB runtime
- library (<code>libodb-sqlite</code>). All SQLite-specific ODB
- classes are defined in the <code>odb::sqlite</code> namespace.</p>
-
- <h2><a name="18.1">18.1 SQLite Type Mapping</a></h2>
-
- <p>The following table summarizes the default mapping between basic
- C++ value types and SQLite database types. This mapping can be
- customized on the per-type and per-member basis using the ODB
- Pragma Language (<a href="#14">Chapter 14, "ODB Pragma
- Language"</a>).</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>C++ Type</th>
- <th>SQLite Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>bool</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char</code></td>
- <td><code>TEXT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>signed char</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned char</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>short</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned short</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>int</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned int</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long long</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long long</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>float</code></td>
- <td><code>REAL</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>double</code></td>
- <td><code>REAL</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>std::string</code></td>
- <td><code>TEXT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char[N]</code></td>
- <td><code>TEXT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>std::wstring (Windows only)</code></td>
- <td><code>TEXT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>wchar_t[N] (Windows only)</code></td>
- <td><code>TEXT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>odb::sqlite::text</code></td>
- <td><code>TEXT (STREAM)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>odb::sqlite::blob</code></td>
- <td><code>BLOB (STREAM)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
- </table>
-
- <p>It is possible to map the <code>char</code> C++ type to the
- <code>INTEGER</code> SQLite type using the <code>db&nbsp;type</code>
- pragma (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>).</p>
-
- <p>SQLite represents the <code>NaN</code> <code>FLOAT</code> value
- as a <code>NULL</code> value. As a result, columns of the
- <code>float</code> and <code>double</code> types are by default
- declared as <code>NULL</code>. However, you can override this by
- explicitly declaring them as <code>NOT NULL</code> with the
- <code>db&nbsp;not_null</code> pragma (<a href="#14.4.6">Section
- 14.4.6, "<code>null/not_null</code>"</a>).</p>
-
- <p>Additionally, by default, C++ enums and C++11 enum classes are
- automatically mapped to the SQLite <code>INTEGER</code> type with
- the default <code>NULL</code> semantics being <code>NOT NULL</code>.
- For example:</p>
-
- <pre class="cxx">
-enum color {red, green, blue};
-enum class taste: unsigned char
-{
- bitter = 1,
- sweet,
- sour = 4,
- salty
-};
-
-#pragma db object
-class object
-{
- ...
-
- color color_; // Automatically mapped to INTEGER.
- taste taste_; // Automatically mapped to INTEGER.
-};
- </pre>
-
- <p>Note also that SQLite only operates with signed integers and the largest
- value that an SQLite database can store is a signed 64-bit integer. As
- a result, greater <code>unsigned&nbsp;long</code> and
- <code>unsigned&nbsp;long&nbsp;long</code> values will be represented in
- the database as negative values.</p>
-
- <p>It is also possible to add support for additional SQLite types,
- such as <code>NUMERIC</code>. For more information, refer to
- <a href="#14.8">Section 14.8, "Database Type Mapping
- Pragmas"</a>.</p>
-
- <h3><a name="18.1.1">18.1.1 String Type Mapping</a></h3>
-
- <p>The SQLite ODB runtime library provides support for mapping the
- <code>std::array&lt;char, N></code> and, on Windows,
- <code>std::array&lt;wchar_t, N></code> types to the SQLite
- <code>TEXT</code> type. However, this mapping is not enabled by
- default (in particular, by default, <code>std::array</code> will
- be treated as a container). To enable the alternative mapping for
- this type we need to specify the database type explicitly using
- the <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section
- 14.4.3, "<code>type</code>"</a>), for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("TEXT")
- std::array&lt;char, 128> name_;
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-typedef std::array&lt;char, 128> name_type;
-#pragma db value(name_type) type("TEXT")
-
-#pragma db object
-class object
-{
- ...
-
- name_type name_; // Mapped to TEXT.
-};
- </pre>
-
- <p>The <code>char[N]</code>, <code>std::array&lt;char, N></code>,
- <code>wchar_t[N]</code>, and <code>std::array&lt;wchar_t, N></code>
- values may or may not be zero-terminated. When extracting such values
- from the database, ODB will append the zero terminator if there is
- enough space.</p>
-
- <h3><a name="18.1.2">18.1.2 Binary Type Mapping</a></h3>
-
- <p>The SQLite ODB runtime library provides support for mapping the
- <code>std::vector&lt;char></code>,
- <code>std::vector&lt;unsigned&nbsp;char></code>,
- <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
- <code>std::array&lt;char, N></code>, and
- <code>std::array&lt;unsigned char, N></code>
- types to the SQLite <code>BLOB</code> type. However, these mappings
- are not enabled by default (in particular, by default,
- <code>std::vector</code> and <code>std::array</code> will be treated
- as containers). To enable the alternative mappings for these types
- we need to specify the database type explicitly using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>), for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("BLOB")
- std::vector&lt;char> buf_;
-
- #pragma db type("BLOB")
- unsigned char uuid_[16];
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;char> buffer;
-#pragma db value(buffer) type("BLOB")
-
-#pragma db object
-class object
-{
- ...
-
- buffer buf_; // Mapped to BLOB.
-};
- </pre>
-
- <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
- the Database"</a>) <code>char[N]</code> and
- <code>std::array&lt;char, N></code> parameters are by default passed
- as a string rather than a binary. To pass such parameters as a binary,
- we need to specify the database type explicitly in the
- <code>_val()</code>/<code>_ref()</code> calls. Note also that we
- don't need to do this for the integrated queries, for example:</p>
-
- <pre class="cxx">
-char u[16] = {...};
-
-db.query&lt;object> ("uuid = " + query::_val&lt;odb::sqlite::id_blob> (u));
-db.query&lt;object> (query::uuid == query::_ref (u));
- </pre>
-
- <h3><a name="18.1.3">18.1.3 Incremental <code>BLOB</code>/<code>TEXT</code> I/O</a></h3>
-
- <p>This section describes the SQLite ODB runtime library support for
- incremental reading and writing of <code>BLOB</code> and
- <code>TEXT</code> values. The provided API is a thin wrapper
- around the native SQLite <code>sqlite3_blob_*()</code> function
- family. As a result, it is highly recommended that you familiarize
- yourself with the semantics of this SQLite functionality before
- continuing with this section.</p>
-
- <p>The SQLite runtime provides the <code>blob</code> and
- <code>text</code> types that can be used to represent
- <code>BLOB</code> and <code>TEXT</code> data members
- that will be read/written using the incremental I/O.
- For example:</p>
-
- <pre class="cxx">
-#include &lt;odb/sqlite/blob.hxx>
-#include &lt;odb/sqlite/text.hxx>
-
-#pragma db object
-class object
-{
-public
- #pragma db id auto
- unsigned long id;
-
- odb::sqlite::blob b; // Mapped to BLOB.
- odb::sqlite::text t; // Mapped to TEXT.
-};
- </pre>
-
- <p>The <code>blob</code> and <code>text</code> types should be
- viewed as <em>descriptors</em> of the <code>BLOB</code> and
- <code>TEXT</code> values (rather than the values themselves)
- that can be used to <em>open</em> the values for reading or
- writing. These two types have an identical interface that
- is presented below. Notice that it is essentially the list
- of arguments (except for <code>size</code> which is discussed
- below) to the <code>sqlite3_blob_open()</code> function:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace sqlite
- {
- class blob|text
- {
- public:
- explicit
- blob|text (std::size_t = 0);
-
- std::size_t size ()
- void size (std::size_t);
-
- const std::string&amp; db () const;
- const std::string&amp; table () const;
- const std::string&amp; column () const;
- long long rowid () const;
-
- void
- clear ();
- };
- }
-}
- </pre>
-
- <p>To read/write data from/to a incremental <code>BLOB</code> or
- <code>TEXT</code> value we use the corresponding
- <code>blob_stream</code> and <code>text_stream</code>
- stream types. Their interfaces closely mimic the
- underlying <code>sqlite3_blob_*()</code> functions
- and are presented below. Note that in order to create
- a stream we have to pass the corresponding descriptor:</p>
-
- <pre class="cxx">
-#include &lt;odb/sqlite/stream.hxx>
-
-namespace odb
-{
- namespace sqlite
- {
- class stream
- {
- public:
- stream (const char* db,
- const char* table,
- const char* column,
- long long rowid,
- bool rw);
-
- std::size_t
- size () const;
-
- // The following two functions throw std::invalid_argument if
- // offset + n is past size().
- //
- void
- read (void* buf, std::size_t n, std::size_t offset = 0);
-
- void
- write (const void* buf, std::size_t n, std::size_t offset = 0);
-
- sqlite3_blob*
- handle () const;
-
- // Close without reporting errors, if any.
- //
- ~stream ();
-
- // Close with reporting errors, if any.
- //
- void
- close ();
-
- // Open the same BLOB but in a different row. Can be faster
- // than creating a new stream instance. Note that the stream
- // must be in the open state prior to calling this function.
- //
- void
- reopen (long long rowid);
- };
- }
-}
-
-#include &lt;odb/sqlite/blob-stream.hxx>
-
-namespace odb
-{
- namespace sqlite
- {
- class blob_stream: public stream
- {
- public:
- blob_stream (const blob&amp;, bool rw);
- };
- }
-}
-
-#include &lt;odb/sqlite/text-stream.hxx>
-
-namespace odb
-{
- namespace sqlite
- {
- class text_stream: public stream
- {
- public:
- text_stream (const text&amp;, bool rw);
- };
- }
-}
- </pre>
-
- <p>The <code>rw</code> argument to the constructors above
- specifies whether to open the value for reading only
- (<code>false</code>) or to read and write
- (<code>true</code>).</p>
-
- <p>In SQLite the incremental <code>BLOB</code> and
- <code>TEXT</code> sizes are fixed in the sense that
- they must be specified before the object is persisted
- or updated and the following write operations can
- only write that much data. This is what the <code>size</code>
- data member in the descriptors is for. You can also determine
- the size of the opened value, for both reading and writing,
- using the <code>size()</code> stream function. The
- following example puts all of this together:</p>
-
- <pre class="cxx">
-#include &lt;odb/sqlite/blob-stream.hxx>
-#include &lt;odb/sqlite/text-stream.hxx>
-
-string txt (1024 * 1024, 't');
-vector&lt;char> blb (1024 * 1024, 'b');
-
-object o;
-
-// Persist.
-//
-{
- transaction tx (db.begin ());
-
- // Specify the sizes of the values before calling persist().
- //
- o.t.size (txt.size ());
- o.b.size (blb.size ());
-
- db.persist (o);
-
- // Write the data.
- //
- blob_stream bs (o.b, true); // Open for read/write.
- assert (bs.size () == blb.size ());
- bs.write (blb.data (), blb.size ());
-
- text_stream ts (o.t, true); // Open for read/write.
- assert (ts.size () == txt.size ());
- ts.write (txt.data (), txt.size ());
-
- tx.commit ();
-}
-
-// Load.
-//
-{
- transaction tx (db.begin ());
- auto_ptr&lt;object> p (db.load&lt;object> (o.id));
-
- text_stream ts (p->t, false); // Open for reading.
- vector&lt;char> t (ts.size () + 1, '\0');
- ts.read (t.data (), t.size () - 1);
- assert (string (t.data ()) == txt);
-
- blob_stream bs (p->b, false); // Open for reading.
- vector&lt;char> b (bs.size (), '\0');
- bs.read (b.data (), b.size ());
- assert (b == blb);
-
- tx.commit ();
-}
-
-// Update
-//
-txt.resize (txt.size () + 1, 't');
-txt[0] = 'A';
-txt[txt.size () - 1] = 'Z';
-
-blb.resize (blb.size () - 1);
-blb.front () = 'A';
-blb.back () = 'Z';
-
-{
- transaction tx (db.begin ());
-
- // Specify the new sizes of the values before calling update().
- //
- o.t.size (txt.size ());
- o.b.size (blb.size ());
-
- db.update (o);
-
- // Write the data.
- //
- blob_stream bs (o.b, true);
- bs.write (blb.data (), blb.size ());
-
- text_stream ts (o.t, true);
- ts.write (txt.data (), txt.size ());
-
- tx.commit ();
-}
- </pre>
-
- <p>For the most part, the incremental <code>BLOB</code> and
- <code>TEXT</code> descriptors can be used as any other
- simple values. Specifically, they can be used as container
- elements (<a href="#5">Chapter 5, "Containers"</a>), as
- <code>NULL</code>-able values (<a href="#7.3">Section 7.3,
- "Pointers and NULL Value Semantics"</a>), and in views
- (<a href="#10">Chapter 10, "Views"</a>). The following
- example illustrates the use within a view:</p>
-
- <pre class="cxx">
-#pragma db view object(object)
-struct load_b
-{
- odb::sqlite::blob b;
-};
-
-typedef odb::query&lt;load_b> query;
-
-transaction tx (db.begin ());
-
-for (load_b&amp; lb: db.query&lt;load_b> (query::t == "test"))
-{
- blob_stream bs (lb.b, false);
- vector&lt;char> b (bs.size (), '\0');
- bs.read (b.data (), b.size ());
-}
-
-tx.commit ();
- </pre>
-
- <p>However, being a low-level, SQLite-specific mechanism, the
- incremental I/O has a number of nuances that should be kept in
- mind. Firstly, the streams should be opened within a transaction
- and, unless already closed, they will be automatically closed
- when the transaction is committed or rolled back. The following
- modification of the previous example helps to illustrate this
- point:</p>
-
- <pre class="cxx">
-{
- transaction tx (db.begin ());
-
- // ...
-
- db.persist (o);
-
- blob_stream bs (o.b, true);
-
- tx.commit ();
-
- // ERROR: stream is closed.
- //
- bs.write (blb.data (), blb.size ());
-}
-
-// ERROR: not in transaction.
-//
-text_stream ts (o.t, true);
- </pre>
-
- <p>Because loading an object with an incremental <code>BLOB</code> or
- <code>TEXT</code> value involves additional actions after the
- database function returns (that is, reading the actual data),
- certain commonly-expected "round-trip" assumptions will no
- longer hold unless special steps are taken, for instance
- (again, continuing with our example):</p>
-
- <pre class="cxx">
-transaction tx (db.begin ());
-
-auto_ptr&lt;object> p (db.load&lt;object> (o.id));
-p->name = "foo"; // Update some other member.
-db.update (*p); // Bad behavior: incremental BLOB/TEXT invalidated.
-
-tx.commit ();
- </pre>
-
- <p>One way to restore the expected behavior is to place the
- incremental <code>BLOB</code> and <code>TEXT</code> values
- into their own, separately loaded/updated sections
- (<a href="#9">Chapter 9, "Sections"</a>). The alternative
- approach would be to perform the incremental I/O as part
- of the database operation <code>post_*</code> callbacks
- (<a href="#14.1.7">Section 14.1.7, "<code>callback</code>"</a>).</p>
-
- <p>Finally, note that when using incremental <code>TEXT</code>
- values, the data that we read/write is the raw bytes in
- the encoding used by the database (<code>UTF-8</code> by
- default; see SQLite <code>PRAGMA encoding</code> documentation
- for details).</p>
-
- <h2><a name="18.2">18.2 SQLite Database Class</a></h2>
-
- <p>The SQLite <code>database</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace sqlite
- {
- class database: public odb::database
- {
- public:
- database (const std::string&amp; name,
- int flags = SQLITE_OPEN_READWRITE,
- bool foreign_keys = true,
- const std::string&amp; vfs = "",
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
-#ifdef _WIN32
- database (const std::wstring&amp; name,
- int flags = SQLITE_OPEN_READWRITE,
- bool foreign_keys = true,
- const std::string&amp; vfs = "",
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-#endif
-
- database (int&amp; argc,
- char* argv[],
- bool erase = false,
- int flags = SQLITE_OPEN_READWRITE,
- bool foreign_keys = true,
- const std::string&amp; vfs = "",
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- static void
- print_usage (std::ostream&amp;);
-
- public:
- const std::string&amp;
- name () const;
-
- int
- flags () const;
-
- public:
- transaction
- begin_immediate ();
-
- transaction
- begin_exclusive ();
-
- public:
- connection_ptr
- connection ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/sqlite/database.hxx></code>
- header file to make this class available in your application.</p>
-
- <p>The first constructor opens the specified SQLite database. The
- <code>name</code> argument is the database file name to open in
- the UTF-8 encoding. If this argument is empty, then a temporary,
- on-disk database is created. If this argument is the
- <code>:memory:</code> special value, then a temporary, in-memory
- database is created. The <code>flags</code> argument allows us to
- specify SQLite opening flags. For more information on the possible
- values, refer to the <code>sqlite3_open_v2()</code> function description
- in the SQLite C API documentation. The <code>foreign_keys</code>
- argument specifies whether foreign key constraints checking
- should be enabled. See <a href="#18.5.3">Section 18.5.3,
- "Foreign Key Constraints"</a> for more information on foreign
- keys. The <code>vfs</code> argument specifies the SQLite
- virtual file system module that should be used to access the
- database. If this argument is empty, then the default vfs module
- is used. Again, refer to the <code>sqlite3_open_v2()</code> function
- documentation for detail.</p>
-
- <p>The following example shows how we can open the <code>test.db</code>
- database in the read-write mode and create it if it does not exist:</p>
-
- <pre class="cxx">
-auto_ptr&lt;odb::database> db (
- new odb::sqlite::database (
- "test.db",
- SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE));
- </pre>
-
- <p>The second constructor is the same as the first except that the database
- name is passes as <code>std::wstring</code> in the UTF-16 encoding. This
- constructor is only available when compiling for Windows.</p>
-
- <p>The third constructor extracts the database parameters from the
- command line. The following options are recognized:</p>
-
- <pre class="terminal">
- --database &lt;name>
- --create
- --read-only
- --options-file &lt;file>
- </pre>
-
- <p>By default, this constructor opens the database in the read-write mode
- (<code>SQLITE_OPEN_READWRITE</code> flag). If the <code>--create</code>
- flag is specified, then the database file is created if it does
- not already exist (<code>SQLITE_OPEN_CREATE</code> flag). If the
- <code>--read-only</code> flag is specified, then the database is
- opened in the read-only mode (<code>SQLITE_OPEN_READONLY</code>
- flag instead of <code>SQLITE_OPEN_READWRITE</code>). The
- <code>--options-file</code> option allows us to specify some
- or all of the database options in a file with each option appearing
- on a separate line followed by a space and an option value.</p>
-
- <p>If the <code>erase</code> argument to this constructor is true,
- then the above options are removed from the <code>argv</code>
- array and the <code>argc</code> count is updated accordingly.
- This is primarily useful if your application accepts other
- options or arguments and you would like to get the SQLite
- options out of the <code>argv</code> array.</p>
-
- <p>The <code>flags</code> argument has the same semantics as in
- the first constructor. Flags from the command line always override
- the corresponding values specified with this argument.</p>
-
- <p>The third constructor throws the <code>odb::sqlite::cli_exception</code>
- exception if the SQLite option values are missing or invalid.
- See <a href="#18.4">Section 18.4, "SQLite Exceptions"</a>
- for more information on this exception.</p>
-
- <p>The static <code>print_usage()</code> function prints the list of options
- with short descriptions that are recognized by the third constructor.</p>
-
- <p>The last argument to all of the constructors is a pointer to the
- connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
- in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
- non-<code>NULL</code> value, the database instance assumes ownership
- of the factory instance. The connection factory interface as well as
- the available implementations are described in the next section.</p>
-
- <p>The set of accessor functions following the constructors allows us
- to query the parameters of the <code>database</code> instance.</p>
-
- <p>The <code>begin_immediate()</code> and <code>begin_exclusive()</code>
- functions are the SQLite-specific extensions to the standard
- <code>odb::database::begin()</code> function (see
- <a href="#3.5">Section 3.5, "Transactions"</a>). They allow us
- to start an immediate (<code>BEGIN IMMEDIATE</code>) and an exclusive
- (<code>BEGIN EXCLUSIVE</code>) SQLite transaction, respectively.
- For more information on the semantics of the immediate and exclusive
- transactions, refer to the <code>BEGIN</code> statement description
- in the SQLite documentation.</p>
-
- <p>The <code>connection()</code> function returns a pointer to the
- SQLite database connection encapsulated by the
- <code>odb::sqlite::connection</code> class. For more information
- on <code>sqlite::connection</code>, refer to <a href="#18.3">Section
- 18.3, "SQLite Connection and Connection Factory"</a>.</p>
-
- <h2><a name="18.3">18.3 SQLite Connection and Connection Factory</a></h2>
-
- <p>The <code>sqlite::connection</code> class has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace sqlite
- {
- class connection: public odb::connection
- {
- public:
- connection (database&amp;, int extra_flags = 0);
- connection (database&amp;, sqlite3*);
-
- transaction
- begin_immediate ();
-
- transaction
- begin_exclusive ();
-
- sqlite3*
- handle ();
- };
-
- typedef details::shared_ptr&lt;connection> connection_ptr;
- }
-}
- </pre>
-
- <p>For more information on the <code>odb::connection</code> interface,
- refer to <a href="#3.6">Section 3.6, "Connections"</a>. The first
- overloaded <code>sqlite::connection</code> constructor opens
- a new SQLite connection. The <code>extra_flags</code> argument can
- be used to specify extra <code>sqlite3_open_v2()</code> flags
- that are combined with the flags specified in the
- <code>sqlite::database</code> constructor. The second constructor
- allows us to create a <code>connection</code> instance by providing
- an already open native SQLite handle. Note that the
- <code>connection</code> instance assumes ownership of this handle.</p>
-
- <p>The <code>begin_immediate()</code> and <code>begin_exclusive()</code>
- functions allow us to start an immediate and an exclusive SQLite
- transaction on the connection, respectively. Their semantics are
- equivalent to the corresponding functions defined in the
- <code>sqlite::database</code> class (<a href="#18.2">Section 18.2,
- "SQLite Database Class"</a>). The <code>handle()</code> accessor
- returns the SQLite handle corresponding to the connection.</p>
-
- <p>The <code>sqlite::connection_factory</code> abstract class has the
- following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace sqlite
- {
- class connection_factory
- {
- public:
- virtual void
- database (database&amp;) = 0;
-
- virtual connection_ptr
- connect () = 0;
- };
- }
-}
- </pre>
-
- <p>The <code>database()</code> function is called when a connection
- factory is associated with a database instance. This happens in
- the <code>odb::sqlite::database</code> class constructors. The
- <code>connect()</code> function is called whenever a database
- connection is requested.</p>
-
- <p>The three implementations of the <code>connection_factory</code>
- interface provided by the SQLite ODB runtime library are
- <code>single_connection_factory</code>,
- <code>new_connection_factory</code>, and
- <code>connection_pool_factory</code>. You will need to include
- the <code>&lt;odb/sqlite/connection-factory.hxx></code>
- header file to make the <code>connection_factory</code> interface
- and these implementation classes available in your application.</p>
-
- <p>The <code>single_connection_factory</code> class creates a
- single connection that is shared between all the threads in
- an application. If the connection is currently not in use,
- then it is returned to the caller. Otherwise, the caller is
- blocked until the connection becomes available. The
- <code>single_connection_factory</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace sqlite
- {
- class single_connection_factory: public connection_factory
- {
- public:
- single_connection_factory ();
-
- protected:
- class single_connection: public connection
- {
- public:
- single_connection (database&amp;, int extra_flags = 0);
- single_connection (database&amp;, sqlite3*);
- };
-
- typedef details::shared_ptr&lt;single_connection> single_connection_ptr;
-
- virtual single_connection_ptr
- create ();
- };
-};
- </pre>
-
- <p>The <code>create()</code> virtual function is called when the
- factory needs to create the connection. By deriving from the
- <code>single_connection_factory</code> class and overriding this
- function we can implement custom connection establishment
- and configuration.</p>
-
- <p>The <code>new_connection_factory</code> class creates a new
- connection whenever one is requested. When a connection is no
- longer needed, it is released and closed. The
- <code>new_connection_factory</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace sqlite
- {
- class new_connection_factory: public connection_factory
- {
- public:
- new_connection_factory ();
- };
-};
- </pre>
-
- <p>The <code>connection_pool_factory</code> class implements a
- connection pool. It has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace sqlite
- {
- class connection_pool_factory: public connection_factory
- {
- public:
- connection_pool_factory (std::size_t max_connections = 0,
- std::size_t min_connections = 0);
-
- protected:
- class pooled_connection: public connection
- {
- public:
- pooled_connection (database_type&amp;, int extra_flags = 0);
- pooled_connection (database_type&amp;, sqlite3*);
- };
-
- typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
-
- virtual pooled_connection_ptr
- create ();
- };
-};
- </pre>
-
- <p>The <code>max_connections</code> argument in the
- <code>connection_pool_factory</code> constructor specifies the maximum
- number of concurrent connections that this pool factory will
- maintain. Similarly, the <code>min_connections</code> argument
- specifies the minimum number of available connections that
- should be kept open.</p>
-
- <p>Whenever a connection is requested, the pool factory first
- checks if there is an unused connection that can be returned.
- If there is none, the pool factory checks the
- <code>max_connections</code> value to see if a new connection
- can be created. If the total number of connections maintained
- by the pool is less than this value, then a new connection is
- created and returned. Otherwise, the caller is blocked until
- a connection becomes available.</p>
-
- <p>When a connection is released, the pool factory first checks
- if there are blocked callers waiting for a connection. If so, then
- one of them is unblocked and is given the connection. Otherwise,
- the pool factory checks whether the total number of connections
- maintained by the pool is greater than the <code>min_connections</code>
- value. If that's the case, the connection is closed. Otherwise, the
- connection is added to the pool of available connections to be
- returned on the next request. In other words, if the number of
- connections maintained by the pool exceeds <code>min_connections</code>
- and there are no callers waiting for a new connection,
- then the pool will close the excess connections.</p>
-
- <p>If the <code>max_connections</code> value is 0, then the pool will
- create a new connection whenever all of the existing connections
- are in use. If the <code>min_connections</code> value is 0, then
- the pool will never close a connection and instead maintain all
- the connections that were ever created.</p>
-
- <p>The <code>create()</code> virtual function is called whenever the
- pool needs to create a new connection. By deriving from the
- <code>connection_pool_factory</code> class and overriding this
- function we can implement custom connection establishment
- and configuration.</p>
-
- <p>By default, connections created by <code>new_connection_factory</code>
- and <code>connection_pool_factory</code> enable the SQLite shared cache
- mode and use the unlock notify functionality to aid concurrency. To
- disable the shared cache mode you can pass the
- <code>SQLITE_OPEN_PRIVATECACHE</code> flag when creating the database
- instance. For more information on the shared cache mode refer to the
- SQLite documentation.</p>
-
- <p>If you pass <code>NULL</code> as the connection factory to one of the
- <code>database</code> constructors, then the <code>connection_pool_factory</code>
- instance will be created by default with the min and max connections
- values set to <code>0</code>. The following code fragment shows how we
- can pass our own connection factory instance:</p>
-
- <pre class="cxx">
-#include &lt;odb/database.hxx>
-
-#include &lt;odb/sqlite/database.hxx>
-#include &lt;odb/sqlite/connection-factory.hxx>
-
-int
-main (int argc, char* argv[])
-{
- auto_ptr&lt;odb::sqlite::connection_factory> f (
- new odb::sqlite::connection_pool_factory (20));
-
- auto_ptr&lt;odb::database> db (
- new sqlite::database (argc, argv, false, SQLITE_OPEN_READWRITE, f));
-}
- </pre>
-
- <h2><a name="18.4">18.4 SQLite Exceptions</a></h2>
-
- <p>The SQLite ODB runtime library defines the following SQLite-specific
- exceptions:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace sqlite
- {
- class forced_rollback: odb::recoverable
- {
- public:
- virtual const char*
- what () const throw ();
- };
-
- class database_exception: odb::database_exception
- {
- public:
- int
- error () const
-
- int
- extended_error () const;
-
- const std::string&amp;
- message () const;
-
- virtual const char*
- what () const throw ();
- };
-
- class cli_exception: odb::exception
- {
- public:
- virtual const char*
- what () const throw ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/sqlite/exceptions.hxx></code>
- header file to make these exceptions available in your application.</p>
-
- <p>The <code>odb::sqlite::forced_rollback</code> exception is thrown if
- SQLite is forcing the current transaction to roll back. For more
- information on this behavior refer to <a href="#18.5.6">Section 18.5.6,
- "Forced Rollback"</a>.</p>
-
- <p>The <code>odb::sqlite::database_exception</code> is thrown if
- an SQLite database operation fails. The SQLite-specific error
- information is accessible via the <code>error()</code>,
- <code>extended_error()</code>, and <code>message()</code> functions.
- All this information is also combined and returned in a
- human-readable form by the <code>what()</code> function.</p>
-
- <p>The <code>odb::sqlite::cli_exception</code> is thrown by the
- command line parsing constructor of the <code>odb::sqlite::database</code>
- class if the SQLite option values are missing or invalid. The
- <code>what()</code> function returns a human-readable description
- of an error.</p>
-
-
- <h2><a name="18.5">18.5 SQLite Limitations</a></h2>
-
- <p>The following sections describe SQLite-specific limitations imposed by
- the current SQLite and ODB runtime versions.</p>
-
- <h3><a name="18.5.1">18.5.1 Query Result Caching</a></h3>
-
- <p>SQLite ODB runtime implementation does not perform query result caching
- (<a href="#4.4">Section 4.4, "Query Result"</a>) even when explicitly
- requested. The SQLite API supports interleaving execution of multiple
- prepared statements on a single connection. As a result, with SQLite, it
- is possible to have multiple uncached results and calls to other database
- functions do not invalidate them. The only limitation of the uncached
- SQLite results is the unavailability of the <code>result::size()</code>
- function. If you call this function on an SQLite query result, then
- the <code>odb::result_not_cached</code> exception
- (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>) is always
- thrown. Future versions of the SQLite ODB runtime library may add support
- for result caching.</p>
-
- <h3><a name="18.5.2">18.5.2 Automatic Assignment of Object Ids</a></h3>
-
- <p>Due to SQLite API limitations, every automatically assigned object id
- (<a href="#14.4.2">Section 14.4.2, "<code>auto</code>"</a>) should have
- the <code>INTEGER</code> SQLite type. While SQLite will treat other
- integer type names (such as <code>INT</code>, <code>BIGINT</code>, etc.)
- as <code>INTEGER</code>, automatic id assignment will not work. By default,
- ODB maps all C++ integral types to <code>INTEGER</code>. This means that
- the only situation that requires consideration is the assignment of a
- custom database type using the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>). For
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- //#pragma db id auto type("INT") // Will not work.
- //#pragma db id auto type("INTEGER") // Ok.
- #pragma db id auto // Ok, Mapped to INTEGER.
- unsigned int id_;
-};
- </pre>
-
- <h3><a name="18.5.3">18.5.3 Foreign Key Constraints</a></h3>
-
- <p>By default the SQLite ODB runtime enables foreign key constraints
- checking (<code>PRAGMA foreign_keys=ON</code>). You can disable foreign
- keys by passing <code>false</code> as the <code>foreign_keys</code>
- argument to one of the <code>odb::sqlite::database</code> constructors.
- Foreign keys will also be disabled if the SQLite library is built without
- support for foreign keys (<code>SQLITE_OMIT_FOREIGN_KEY</code> and
- <code>SQLITE_OMIT_TRIGGER</code> macros) or if you are using
- an SQLite version prior to 3.6.19, which does not support foreign
- key constraints checking.</p>
-
- <p>If foreign key constraints checking is disabled or not available,
- then inconsistencies in object relationships will not be detected.
- Furthermore, using the <code>erase_query()</code> function
- (<a href="#3.11">Section 3.11, "Deleting Persistent Objects"</a>)
- to delete persistent objects that contain containers will not work
- correctly. Container data for such objects will not be deleted.</p>
-
- <p>When foreign key constraints checking is enabled, then you may
- get the "foreign key constraint failed" error while re-creating the
- database schema. This error is due to bugs in the SQLite DDL foreign
- keys support. The recommended work-around for this problem is to
- temporarily disable foreign key constraints checking while
- re-creating the schema. The following code fragment shows how
- this can be done:</p>
-
- <pre class="cxx">
-#include &lt;odb/connection.hxx>
-#include &lt;odb/transaction.hxx>
-#include &lt;odb/schema-catalog.hxx>
-
-odb::database&amp; db = ...
-
-{
- odb::connection_ptr c (db.connection ());
-
- c->execute ("PRAGMA foreign_keys=OFF");
-
- odb::transaction t (c->begin ());
- odb::schema_catalog::create_schema (db);
- t.commit ();
-
- c->execute ("PRAGMA foreign_keys=ON");
-}
- </pre>
-
- <p>Finally, ODB assumes the standard SQL behavior which requires
- that foreign key constraints checking is deferred until the
- transaction is committed. Default SQLite behavior is to check such
- constraints immediately. As a result, when used with ODB, a custom
- database schema that defines foreign key constraints may need to
- declare such constraints as <code>DEFERRABLE INITIALLY DEFERRED</code>,
- as shown in the following example. By default, schemas generated by
- the ODB compiler meet this requirement automatically.</p>
-
- <pre class="sql">
-CREATE TABLE Employee (
- ...
- employer INTEGER REFERENCES Employer(id)
- DEFERRABLE INITIALLY DEFERRED);
- </pre>
-
- <p>You can override the default behavior and instruct the ODB
- compiler to generate non-deferrable foreign keys by specifying
- the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
- compiler option. Note, however, that in this case the order in
- which you persist, update, and erase objects within a transaction
- becomes important.</p>
-
- <h3><a name="18.5.4">18.5.4 Constraint Violations</a></h3>
-
- <p>Due to the granularity of the SQLite error codes, it is impossible
- to distinguish between the duplicate primary key and other constraint
- violations. As a result, when making an object persistent, the SQLite
- ODB runtime will translate all constraint violation errors to the
- <code>object_already_persistent</code> exception (<a href="#3.14">Section
- 3.14, "ODB Exceptions"</a>).</p>
-
- <h3><a name="18.5.5">18.5.5 Sharing of Queries</a></h3>
-
- <p>As discussed in <a href="#4.3">Section 4.3, "Executing a Query"</a>, a
- query instance that does not have any by-reference parameters is
- immutable and can be shared between multiple threads without
- synchronization. Currently, the SQLite ODB runtime does not support this
- functionality. Future versions of the library will remove this
- limitation.</p>
-
- <h3><a name="18.5.6">18.5.6 Forced Rollback</a></h3>
-
- <p>In SQLite 3.7.11 or later, if one of the connections participating in
- the shared cache rolls back a transaction, then ongoing transactions
- on other connections in the shared cache may also be forced to roll back.
- An example of such behavior would be a read-only transaction that is
- forced to roll back while iterating over the query result because another
- transaction on another connection was rolled back.</p>
-
- <p>If a transaction is being forced to roll back by SQLite, then ODB
- throws <code>odb::sqlite::forced_rollback</code>
- (<a href="#18.4">Section 18.4, "SQLite Exceptions"</a>) which is
- a recoverable exception (<a href="#3.7">3.7 Error Handling and
- Recovery</a>). As a result, the recommended way to handle this
- exception is to re-execute the affected transaction.</p>
-
- <h3><a name="18.5.7">18.5.7 Database Schema Evolution</a></h3>
-
- <p>From the list of schema migration changes supported by ODB
- (<a href="#13.2">Section 13.2, "Schema Migration"</a>), the
- following are not supported by SQLite:</p>
-
- <ul class="list">
- <li>drop column</li>
- <li>alter column, set <code>NULL</code>/<code>NOT NULL</code></li>
- <li>add foreign key</li>
- <li>drop foreign key</li>
- </ul>
-
- <p>The biggest problem is the lack of support for dropping columns.
- This means that it would be impossible to delete a data member
- in a persistent class. To work around this limitation ODB
- implements <em>logical delete</em> for columns that allow
- <code>NULL</code> values. In this case, instead of dropping
- the column (in the post-migration stage), the schema migration
- statements will automatically reset this column in all the
- existing rows to <code>NULL</code>. Any new rows that are
- inserted later will also automatically have this column set
- to <code>NULL</code> (unless the column specifies a default
- value).</p>
-
- <p>Since it is also impossible to change the column's
- <code>NULL</code>/<code>NOT NULL</code> attribute after it
- has been added, to make schema evolution support usable in
- SQLite, all the columns should be added as <code>NULL</code>
- even if semantically they should not allow <code>NULL</code>
- values. We should also normally refrain from assigning
- default values to columns (<a href="#14.4.7">Section 14.4.7,
- <code>default</code></a>), unless the space overhead of
- a default value is not a concern. Explicitly making all
- the data members <code>NULL</code> would be burdensome
- and ODB provides the <code>--sqlite-override-null</code>
- command line option that forces all the columns, even those
- that were explicitly marked <code>NOT NULL</code>, to be
- <code>NULL</code> in SQLite.</p>
-
- <p>SQLite only supports adding foreign keys as part of the
- column addition. As a result, we can only add a new
- data member of an object pointer type if it points
- to an object with a simple (single-column) object id.</p>
-
- <p>SQLite also doesn't support dropping foreign keys.
- Leaving a foreign key around works well with logical
- delete unless we also want to delete the pointed-to
- object. In this case we will have to leave an
- empty table corresponding to the pointed-to object
- around. An alternative would be to make a copy of the
- pointing object without the object pointer, migrate the
- data, and then delete both the old pointing and the
- pointed-to objects. Since this will result in dropping
- the pointing table, the foreign key will be dropped
- as well. Yet another, more radical, solution to this
- problem is to disable foreign keys checking altogether
- (see the <code>foreign_keys</code> SQLite pragma).</p>
-
- <p>To summarize, to make schema evolution support usable
- in SQLite we should pass the <code>--sqlite-override-null</code>
- option when compiling our persistent classes and also refrain
- from assigning default values to data members. Note also that
- this has to be done from the start so that every column is added
- as <code>NULL</code> and therefore can be logically deleted later.
- In particular, you cannot add the <code>--sqlite-override-null</code>
- option when you realize you need to delete a data member. At this
- point it is too late since the column has already been added
- as <code>NOT NULL</code> in existing databases. We should also
- avoid composite object ids if we are planning to use object
- relationships.</p>
-
- <h2><a name="18.6">18.6 SQLite Index Definitions</a></h2>
-
- <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
- "Index Definition Pragmas"</a>) is used to define an SQLite index,
- the <code>type</code> clause specifies the index type (for example,
- <code>UNIQUE</code>) while the <code>method</code> and
- <code>options</code> clauses are not used. The column options
- can be used to specify collations and the sort order. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- std::string name_;
-
- #pragma db index member(name_, "COLLATE binary DESC")
-};
- </pre>
-
- <p>Index names in SQLite are database-global. To avoid name clashes,
- ODB automatically prefixes each index name with the table name on
- which it is defined.</p>
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="19">19 PostgreSQL Database</a></h1>
-
- <p>To generate support code for the PostgreSQL database you will need
- to pass the "<code>--database&nbsp;pgsql</code>"
- (or "<code>-d&nbsp;pgsql</code>") option to the ODB compiler.
- Your application will also need to link to the PostgreSQL ODB runtime
- library (<code>libodb-pgsql</code>). All PostgreSQL-specific ODB
- classes are defined in the <code>odb::pgsql</code> namespace.</p>
-
- <p>ODB utilizes prepared statements extensively. Support for prepared
- statements was added in PostgreSQL version 7.4 with the introduction
- of the messaging protocol version 3.0. For this reason, ODB supports
- only PostgreSQL version 7.4 and later.</p>
-
- <h2><a name="19.1">19.1 PostgreSQL Type Mapping</a></h2>
-
- <p>The following table summarizes the default mapping between basic
- C++ value types and PostgreSQL database types. This mapping can be
- customized on the per-type and per-member basis using the ODB
- Pragma Language (<a href="#14">Chapter 14, "ODB Pragma
- Language"</a>).</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>C++ Type</th>
- <th>PostgreSQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>bool</code></td>
- <td><code>BOOLEAN</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char</code></td>
- <td><code>CHAR(1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>signed char</code></td>
- <td><code>SMALLINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned char</code></td>
- <td><code>SMALLINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>short</code></td>
- <td><code>SMALLINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned short</code></td>
- <td><code>SMALLINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>int</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned int</code></td>
- <td><code>INTEGER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>float</code></td>
- <td><code>REAL</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>double</code></td>
- <td><code>DOUBLE PRECISION</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>std::string</code></td>
- <td><code>TEXT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char[N]</code></td>
- <td><code>VARCHAR(N-1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
- </table>
-
- <p>It is possible to map the <code>char</code> C++ type to an integer
- database type (for example, <code>SMALLINT</code>) using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>).</p>
-
- <p>Additionally, by default, C++ enums and C++11 enum classes are
- automatically mapped to the PostgreSQL types corresponding to their
- underlying integral types (see table above). The default
- <code>NULL</code> semantics is <code>NOT NULL</code>. For
- example:</p>
-
- <pre class="cxx">
-enum color {red, green, blue};
-enum class taste: unsigned char
-{
- bitter = 1,
- sweet,
- sour = 4,
- salty
-};
-
-#pragma db object
-class object
-{
- ...
-
- color color_; // Automatically mapped to INTEGER.
- taste taste_; // Automatically mapped to SMALLINT.
-};
- </pre>
-
- <p>Note also that because PostgreSQL does not support unsigned integers,
- the <code>unsigned&nbsp;short</code>, <code>unsigned&nbsp;int</code>, and
- <code>unsigned&nbsp;long</code>/<code>unsigned&nbsp;long&nbsp;long</code> C++ types
- are by default mapped to the <code>SMALLINT</code>, <code>INTEGER</code>,
- and <code>BIGINT</code> PostgreSQL types, respectively. The sign bit
- of the value stored by the database for these types will contain
- the most significant bit of the actual unsigned value being
- persisted.</p>
-
- <p>It is also possible to add support for additional PostgreSQL types,
- such as <code>NUMERIC</code>, geometry types, <code>XML</code>,
- <code>JSON</code>, enumeration types, composite types, arrays,
- geospatial types, and the key-value store (<code>HSTORE</code>).
- For more information, refer to <a href="#14.8">Section 14.8,
- "Database Type Mapping Pragmas"</a>.</p>
-
- <h3><a name="19.1.1">19.1.1 String Type Mapping</a></h3>
-
- <p>The PostgreSQL ODB runtime library provides support for mapping the
- <code>std::string</code>, <code>char[N]</code>, and
- <code>std::array&lt;char, N></code> types to the PostgreSQL
- <code>CHAR</code>, <code>VARCHAR</code>, and <code>TEXT</code>
- types. However, these mappings are not enabled by default (in
- particular, by default, <code>std::array</code> will be treated
- as a container). To enable the alternative mappings for these
- types we need to specify the database type explicitly using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>), for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("CHAR(2)")
- char state_[2];
-
- #pragma db type("VARCHAR(128)")
- std::string name_;
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-#pragma db value(std::string) type("VARCHAR(128)")
-
-#pragma db object
-class object
-{
- ...
-
- std::string name_; // Mapped to VARCHAR(128).
-};
- </pre>
-
- <p>The <code>char[N]</code> and <code>std::array&lt;char, N></code> values
- may or may not be zero-terminated. When extracting such values from the
- database, ODB will append the zero terminator if there is enough
- space.</p>
-
- <h3><a name="19.1.2">19.1.2 Binary Type and <code>UUID</code> Mapping</a></h3>
-
- <p>The PostgreSQL ODB runtime library provides support for mapping the
- <code>std::vector&lt;char></code>,
- <code>std::vector&lt;unsigned&nbsp;char></code>,
- <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
- <code>std::array&lt;char, N></code>, and
- <code>std::array&lt;unsigned char, N></code> types to the PostgreSQL
- <code>BYTEA</code> type. There is also support for mapping the
- <code>char[16]</code> array to the PostgreSQL <code>UUID</code> type.
- However, these mappings are not enabled by default (in particular, by
- default, <code>std::vector</code> and <code>std::array</code> will be
- treated as containers). To enable the alternative mappings for these
- types we need to specify the database type explicitly using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>), for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("UUID")
- char uuid_[16];
-
- #pragma db type("BYTEA")
- std::vector&lt;char> buf_;
-
- #pragma db type("BYTEA")
- unsigned char data_[256];
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;char> buffer;
-#pragma db value(buffer) type("BYTEA")
-
-#pragma db object
-class object
-{
- ...
-
- buffer buf_; // Mapped to BYTEA.
-};
- </pre>
-
- <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
- the Database"</a>) <code>char[N]</code> and
- <code>std::array&lt;char, N></code> parameters are by default passed
- as a string rather than a binary. To pass such parameters as a binary,
- we need to specify the database type explicitly in the
- <code>_val()</code>/<code>_ref()</code> calls. Note also that we
- don't need to do this for the integrated queries, for example:</p>
-
- <pre class="cxx">
-char u[16] = {...};
-
-db.query&lt;object> ("uuid = " + query::_val&lt;odb::pgsql::id_uuid> (u));
-db.query&lt;object> ("buf = " + query::_val&lt;odb::pgsql::id_bytea> (u));
-db.query&lt;object> (query::uuid == query::_ref (u));
- </pre>
-
- <h2><a name="19.2">19.2 PostgreSQL Database Class</a></h2>
-
- <p>The PostgreSQL <code>database</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace pgsql
- {
- class database: public odb::database
- {
- public:
- database (const std::string&amp; user,
- const std::string&amp; password,
- const std::string&amp; db,
- const std::string&amp; host = "",
- unsigned int port = 0,
- const std::string&amp; extra_conninfo = "",
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; user,
- const std::string&amp; password,
- const std::string&amp; db,
- const std::string&amp; host,
- const std::string&amp; socket_ext,
- const std::string&amp; extra_conninfo = "",
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; conninfo,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (int&amp; argc,
- char* argv[],
- bool erase = false,
- const std::string&amp; extra_conninfo = "",
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- static void
- print_usage (std::ostream&amp;);
-
- public:
- const std::string&amp;
- user () const;
-
- const std::string&amp;
- password () const;
-
- const std::string&amp;
- db () const;
-
- const std::string&amp;
- host () const;
-
- unsigned int
- port () const;
-
- const std::string&amp;
- socket_ext () const;
-
- const std::string&amp;
- extra_conninfo () const;
-
- const std::string&amp;
- conninfo () const;
-
- public:
- connection_ptr
- connection ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/pgsql/database.hxx></code>
- header file to make this class available in your application.</p>
-
- <p>The overloaded <code>database</code> constructors allow us to specify
- the PostgreSQL database parameters that should be used when connecting
- to the database. The <code>port</code> argument in the first constructor
- is an integer value specifying the TCP/IP port number to connect to. A
- zero port number indicates that the default port should be used.
- The <code>socket_ext</code> argument in the second constructor is a
- string value specifying the UNIX-domain socket file name extension.</p>
-
- <p>The third constructor allows us to specify all the database parameters
- as a single <code>conninfo</code> string. All other constructors
- accept additional database connection parameters as the
- <code>extra_conninfo</code> argument. For more information
- about the format of the <code>conninfo</code> string, refer to
- the <code>PQconnectdb()</code> function description in the PostgreSQL
- documentation. In the case of <code>extra_conninfo</code>, all the
- database parameters provided in this string will take precedence
- over those explicitly specified with other constructor arguments.</p>
-
- <p>The last constructor extracts the database parameters
- from the command line. The following options are recognized:</p>
-
- <pre class="terminal">
- --user &lt;login> | --username &lt;login>
- --password &lt;password>
- --database &lt;name> | --dbname &lt;name>
- --host &lt;host>
- --port &lt;integer>
- --options-file &lt;file>
- </pre>
-
- <p>The <code>--options-file</code> option allows us to specify some
- or all of the database options in a file with each option appearing
- on a separate line followed by a space and an option value.</p>
-
- <p>If the <code>erase</code> argument to this constructor is true,
- then the above options are removed from the <code>argv</code>
- array and the <code>argc</code> count is updated accordingly.
- This is primarily useful if your application accepts other
- options or arguments and you would like to get the PostgreSQL
- options out of the <code>argv</code> array.</p>
-
- <p>This constructor throws the <code>odb::pgsql::cli_exception</code>
- exception if the PostgreSQL option values are missing or invalid.
- See section <a href="#19.4">Section 19.4, "PostgreSQL Exceptions"</a>
- for more information on this exception.</p>
-
- <p>The static <code>print_usage()</code> function prints the list of options
- with short descriptions that are recognized by this constructor.</p>
-
- <p>The last argument to all of the constructors is a pointer to the
- connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
- in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
- non-<code>NULL</code> value, the database instance assumes ownership
- of the factory instance. The connection factory interface as well as
- the available implementations are described in the next section.</p>
-
- <p>The set of accessor functions following the constructors allows us
- to query the parameters of the <code>database</code> instance. Note that
- the <code>conninfo()</code> accessor returns a complete
- <code>conninfo</code> string which includes parameters that were
- explicitly specified with the various constructor arguments, as well as
- the extra parameters passed in the <code>extra_conninfo</code> argument.
- The <code>extra_conninfo()</code> accessor will return the
- <code>conninfo</code> string as passed in the <code>extra_conninfo</code>
- argument.</p>
-
- <p>The <code>connection()</code> function returns a pointer to the
- PostgreSQL database connection encapsulated by the
- <code>odb::pgsql::connection</code> class. For more information
- on <code>pgsql::connection</code>, refer to <a href="#19.3">Section
- 19.3, "PostgreSQL Connection and Connection Factory"</a>.</p>
-
- <h2><a name="19.3">19.3 PostgreSQL Connection and Connection Factory</a></h2>
-
- <p>The <code>pgsql::connection</code> class has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace pgsql
- {
- class connection: public odb::connection
- {
- public:
- connection (database&amp;);
- connection (database&amp;, PGconn*);
-
- PGconn*
- handle ();
- };
-
- typedef details::shared_ptr&lt;connection> connection_ptr;
- }
-}
- </pre>
-
- <p>For more information on the <code>odb::connection</code> interface,
- refer to <a href="#3.6">Section 3.6, "Connections"</a>. The first
- overloaded <code>pgsql::connection</code> constructor establishes a
- new PostgreSQL connection. The second constructor allows us to create
- a <code>connection</code> instance by providing an already connected
- native PostgreSQL handle. Note that the <code>connection</code>
- instance assumes ownership of this handle. The <code>handle()</code>
- accessor returns the PostgreSQL handle corresponding to the connection.</p>
-
- <p>The <code>pgsql::connection_factory</code> abstract class has the
- following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace pgsql
- {
- class connection_factory
- {
- public:
- virtual void
- database (database&amp;) = 0;
-
- virtual connection_ptr
- connect () = 0;
- };
- }
-}
- </pre>
-
- <p>The <code>database()</code> function is called when a connection
- factory is associated with a database instance. This happens in
- the <code>odb::pgsql::database</code> class constructors. The
- <code>connect()</code> function is called whenever a database
- connection is requested.</p>
-
- <p>The two implementations of the <code>connection_factory</code>
- interface provided by the PostgreSQL ODB runtime are
- <code>new_connection_factory</code> and
- <code>connection_pool_factory</code>. You will need to include
- the <code>&lt;odb/pgsql/connection-factory.hxx></code>
- header file to make the <code>connection_factory</code> interface
- and these implementation classes available in your application.</p>
-
- <p>The <code>new_connection_factory</code> class creates a new
- connection whenever one is requested. When a connection is no
- longer needed, it is released and closed. The
- <code>new_connection_factory</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace pgsql
- {
- class new_connection_factory: public connection_factory
- {
- public:
- new_connection_factory ();
- };
-};
- </pre>
-
- <p>The <code>connection_pool_factory</code> class implements a
- connection pool. It has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace pgsql
- {
- class connection_pool_factory: public connection_factory
- {
- public:
- connection_pool_factory (std::size_t max_connections = 0,
- std::size_t min_connections = 0);
-
- protected:
- class pooled_connection: public connection
- {
- public:
- pooled_connection (database_type&amp;);
- pooled_connection (database_type&amp;, PGconn*);
- };
-
- typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
-
- virtual pooled_connection_ptr
- create ();
- };
-};
- </pre>
-
- <p>The <code>max_connections</code> argument in the
- <code>connection_pool_factory</code> constructor specifies the maximum
- number of concurrent connections that this pool factory will
- maintain. Similarly, the <code>min_connections</code> argument
- specifies the minimum number of available connections that
- should be kept open.</p>
-
- <p>Whenever a connection is requested, the pool factory first
- checks if there is an unused connection that can be returned.
- If there is none, the pool factory checks the
- <code>max_connections</code> value to see if a new connection
- can be created. If the total number of connections maintained
- by the pool is less than this value, then a new connection is
- created and returned. Otherwise, the caller is blocked until
- a connection becomes available.</p>
-
- <p>When a connection is released, the pool factory first checks
- if there are blocked callers waiting for a connection. If so, then
- one of them is unblocked and is given the connection. Otherwise,
- the pool factory checks whether the total number of connections
- maintained by the pool is greater than the <code>min_connections</code>
- value. If that's the case, the connection is closed. Otherwise, the
- connection is added to the pool of available connections to be
- returned on the next request. In other words, if the number of
- connections maintained by the pool exceeds <code>min_connections</code>
- and there are no callers waiting for a new connection,
- the pool will close the excess connections.</p>
-
- <p>If the <code>max_connections</code> value is 0, then the pool will
- create a new connection whenever all of the existing connections
- are in use. If the <code>min_connections</code> value is 0, then
- the pool will never close a connection and instead maintain all
- the connections that were ever created.</p>
-
- <p>The <code>create()</code> virtual function is called whenever the
- pool needs to create a new connection. By deriving from the
- <code>connection_pool_factory</code> class and overriding this
- function we can implement custom connection establishment
- and configuration.</p>
-
- <p>If you pass <code>NULL</code> as the connection factory to one of the
- <code>database</code> constructors, then the
- <code>connection_pool_factory</code> instance will be created by default
- with the min and max connections values set to <code>0</code>. The
- following code fragment shows how we can pass our own connection factory
- instance:</p>
-
- <pre class="cxx">
-#include &lt;odb/database.hxx>
-
-#include &lt;odb/pgsql/database.hxx>
-#include &lt;odb/pgsql/connection-factory.hxx>
-
-int
-main (int argc, char* argv[])
-{
- auto_ptr&lt;odb::pgsql::connection_factory> f (
- new odb::pgsql::connection_pool_factory (20));
-
- auto_ptr&lt;odb::database> db (
- new pgsql::database (argc, argv, false, "", f));
-}
- </pre>
-
- <h2><a name="19.4">19.4 PostgreSQL Exceptions</a></h2>
-
- <p>The PostgreSQL ODB runtime library defines the following
- PostgreSQL-specific exceptions:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace pgsql
- {
- class database_exception: odb::database_exception
- {
- public:
- const std::string&amp;
- message () const;
-
- const std::string&amp;
- sqlstate () const;
-
- virtual const char*
- what () const throw ();
- };
-
- class cli_exception: odb::exception
- {
- public:
- virtual const char*
- what () const throw ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/pgsql/exceptions.hxx></code>
- header file to make these exceptions available in your application.</p>
-
- <p>The <code>odb::pgsql::database_exception</code> is thrown if
- a PostgreSQL database operation fails. The PostgreSQL-specific error
- information is accessible via the <code>message()</code> and
- <code>sqlstate()</code> functions. All this information is also
- combined and returned in a human-readable form by the <code>what()</code>
- function.</p>
-
- <p>The <code>odb::pgsql::cli_exception</code> is thrown by the
- command line parsing constructor of the <code>odb::pgsql::database</code>
- class if the PostgreSQL option values are missing or invalid. The
- <code>what()</code> function returns a human-readable description
- of an error.</p>
-
- <h2><a name="19.5">19.5 PostgreSQL Limitations</a></h2>
-
- <p>The following sections describe PostgreSQL-specific limitations imposed
- by the current PostgreSQL and ODB runtime versions.</p>
-
- <h3><a name="19.5.1">19.5.1 Query Result Caching</a></h3>
-
- <p>The PostgreSQL ODB runtime implementation will always return a
- cached query result (<a href="#4.4">Section 4.4, "Query Result"</a>)
- even when explicitly requested not to. This is a limitation of the
- PostgreSQL client library (<code>libpq</code>) which does not
- support uncached (streaming) query results.</p>
-
- <h3><a name="19.5.2">19.5.2 Foreign Key Constraints</a></h3>
-
- <p>ODB assumes the standard SQL behavior which requires that
- foreign key constraints checking is deferred until the
- transaction is committed. Default PostgreSQL behavior is
- to check such constraints immediately. As a result, when
- used with ODB, a custom database schema that defines foreign
- key constraints may need to declare such constraints as
- <code>INITIALLY DEFERRED</code>, as shown in the following example.
- By default, schemas generated by the ODB compiler meet this requirement
- automatically.</p>
-
- <pre class="sql">
-CREATE TABLE Employee (
- ...
- employer BIGINT REFERENCES Employer(id) INITIALLY DEFERRED);
- </pre>
-
- <p>You can override the default behavior and instruct the ODB
- compiler to generate non-deferrable foreign keys by specifying
- the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
- compiler option. Note, however, that in this case the order in
- which you persist, update, and erase objects within a transaction
- becomes important.</p>
-
- <h3><a name="19.5.3">19.5.3 Unique Constraint Violations</a></h3>
-
- <p>Due to the granularity of the PostgreSQL error codes, it is impossible
- to distinguish between the duplicate primary key and other unique
- constraint violations. As a result, when making an object persistent,
- the PostgreSQL ODB runtime will translate all unique constraint violation
- errors to the <code>object_already_persistent</code> exception
- (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>).</p>
-
- <h3><a name="19.5.4">19.5.4 Date-Time Format</a></h3>
-
- <p>ODB expects the PostgreSQL server to use integers as a binary
- format for the date-time types, which is the default for most
- PostgreSQL configurations. When creating a connection, ODB
- examines the <code>integer_datetimes</code> PostgreSQL server
- parameter and if it is <code>false</code>,
- <code>odb::pgsql::database_exception</code> is thrown. You may
- check the value of this parameter for your server by executing
- the following SQL query:</p>
-
- <pre class="sql">
-SHOW integer_datetimes
- </pre>
-
- <h3><a name="19.5.5">19.5.5 Timezones</a></h3>
-
- <p>ODB does not currently natively support the PostgreSQL date-time types
- with timezone information. However, these types can be accessed by
- mapping them to one of the natively supported types, as discussed
- in <a href="#14.8">Section 14.8, "Database Type Mapping Pragmas"</a>.</p>
-
- <h3><a name="19.5.6">19.5.6 <code>NUMERIC</code> Type Support</a></h3>
-
- <p>Support for the PostgreSQL <code>NUMERIC</code> type is limited
- to providing a binary buffer containing the binary representation
- of the value. For more information on the binary format used to
- store <code>NUMERIC</code> values refer to the PostgreSQL
- documentation. An alternative approach to accessing <code>NUMERIC</code>
- values is to map this type to one of the natively supported
- ones, as discussed in <a href="#14.8">Section 14.8, "Database
- Type Mapping Pragmas"</a>.</p>
-
-
- <h2><a name="19.6">19.6 PostgreSQL Index Definitions</a></h2>
-
- <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
- "Index Definition Pragmas"</a>) is used to define a PostgreSQL index,
- the <code>type</code> clause specifies the index type (for example,
- <code>UNIQUE</code>), the <code>method</code> clause specifies the
- index method (for example, <code>BTREE</code>, <code>HASH</code>,
- <code>GIN</code>, etc.), and the <code>options</code> clause
- specifies additional index options, such as storage parameters,
- table spaces, and the <code>WHERE</code> predicate. To support
- the definition of concurrent indexes, the <code>type</code>
- clause can end with the word <code>CONCURRENTLY</code> (upper and
- lower cases are recognized). The column options can be used to
- specify collations, operator classes, and the sort order. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- std::string name_;
-
- #pragma db index \
- type("UNIQUE CONCURRENTLY") \
- method("HASH") \
- member(name_, "DESC") \
- options("WITH(FILLFACTOR = 80)")
-};
- </pre>
-
- <p>Index names in PostgreSQL are schema-global. To avoid name clashes,
- ODB automatically prefixes each index name with the table name on
- which it is defined.</p>
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="20">20 Oracle Database</a></h1>
-
- <p>To generate support code for the Oracle database you will need
- to pass the "<code>--database&nbsp;oracle</code>"
- (or "<code>-d&nbsp;oracle</code>") option to the ODB compiler.
- Your application will also need to link to the Oracle ODB runtime
- library (<code>libodb-oracle</code>). All Oracle-specific ODB
- classes are defined in the <code>odb::oracle</code> namespace.</p>
-
- <h2><a name="20.1">20.1 Oracle Type Mapping</a></h2>
-
- <p>The following table summarizes the default mapping between basic
- C++ value types and Oracle database types. This mapping can be
- customized on the per-type and per-member basis using the ODB
- Pragma Language (<a href="#14">Chapter 14, "ODB Pragma
- Language"</a>).</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>C++ Type</th>
- <th>Oracle Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>bool</code></td>
- <td><code>NUMBER(1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char</code></td>
- <td><code>CHAR(1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>signed char</code></td>
- <td><code>NUMBER(3)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned char</code></td>
- <td><code>NUMBER(3)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>short</code></td>
- <td><code>NUMBER(5)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned short</code></td>
- <td><code>NUMBER(5)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>int</code></td>
- <td><code>NUMBER(10)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned int</code></td>
- <td><code>NUMBER(10)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long</code></td>
- <td><code>NUMBER(19)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long</code></td>
- <td><code>NUMBER(20)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long long</code></td>
- <td><code>NUMBER(19)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long long</code></td>
- <td><code>NUMBER(20)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>float</code></td>
- <td><code>BINARY_FLOAT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>double</code></td>
- <td><code>BINARY_DOUBLE</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>std::string</code></td>
- <td><code>VARCHAR2(512)</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char[N]</code></td>
- <td><code>VARCHAR2(N-1)</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>It is possible to map the <code>char</code> C++ type to an integer
- database type (for example, <code>NUMBER(3)</code>) using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>).</p>
-
- <p>In Oracle empty <code>VARCHAR2</code> and <code>NVARCHAR2</code>
- strings are represented as a <code>NULL</code> value. As a result,
- columns of the <code>std::string</code> and <code>char[N]</code>
- types are by default declared as <code>NULL</code> except for
- primary key columns. However, you can override this by explicitly
- declaring such columns as <code>NOT NULL</code> with the
- <code>db&nbsp;not_null</code> pragma (<a href="#14.4.6">Section
- 14.4.6, "<code>null/not_null</code>"</a>). This also means that for
- object ids that are mapped to these Oracle types, an empty string is
- an invalid value.</p>
-
- <p>Additionally, by default, C++ enums and C++11 enum classes are
- automatically mapped to the Oracle types corresponding to their
- underlying integral types (see table above). The default
- <code>NULL</code> semantics is <code>NOT NULL</code>. For
- example:</p>
-
- <pre class="cxx">
-enum color {red, green, blue};
-enum class taste: unsigned char
-{
- bitter = 1,
- sweet,
- sour = 4,
- salty
-};
-
-#pragma db object
-class object
-{
- ...
-
- color color_; // Automatically mapped to NUMBER(10).
- taste taste_; // Automatically mapped to NUMBER(3).
-};
- </pre>
-
- <p>It is also possible to add support for additional Oracle types,
- such as <code>XML</code>, geospatial types, user-defined types,
- and collections (arrays, table types). For more information, refer to
- <a href="#14.8">Section 14.8, "Database Type Mapping
- Pragmas"</a>.</p>
-
- <h3><a name="20.1.1">20.1.1 String Type Mapping</a></h3>
-
- <p>The Oracle ODB runtime library provides support for mapping the
- <code>std::string</code>, <code>char[N]</code>, and
- <code>std::array&lt;char, N></code> types to the Oracle <code>CHAR</code>,
- <code>VARCHAR2</code>, <code>CLOB</code>, <code>NCHAR</code>,
- <code>NVARCHAR2</code>, and <code>NCLOB</code> types. However,
- these mappings are not enabled by default (in particular, by
- default, <code>std::array</code> will be treated as a container).
- To enable the alternative mappings for these types we need to
- specify the database type explicitly using the <code>db&nbsp;type</code>
- pragma (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>),
- for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type ("CHAR(2)")
- char state_[2];
-
- #pragma db type ("VARCHAR(128)") null
- std::string name_;
-
- #pragma db type ("CLOB")
- std::string text_;
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-#pragma db value(std::string) type("VARCHAR(128)") null
-
-#pragma db object
-class object
-{
- ...
-
- std::string name_; // Mapped to VARCHAR(128).
-
- #pragma db type ("CLOB")
- std::string text_; // Mapped to CLOB.
-};
- </pre>
-
- <p>The <code>char[N]</code> and <code>std::array&lt;char, N></code> values
- may or may not be zero-terminated. When extracting such values from the
- database, ODB will append the zero terminator if there is enough
- space.</p>
-
- <h3><a name="20.1.2">20.1.2 Binary Type Mapping</a></h3>
-
- <p>The Oracle ODB runtime library provides support for mapping the
- <code>std::vector&lt;char></code>,
- <code>std::vector&lt;unsigned&nbsp;char></code>,
- <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
- <code>std::array&lt;char, N></code>, and
- <code>std::array&lt;unsigned char, N></code>
- types to the Oracle <code>BLOB</code> and <code>RAW</code> types.
- However, these mappings are not enabled by default (in particular, by
- default, <code>std::vector</code> and <code>std::array</code> will be
- treated as containers). To enable the alternative mappings for these
- types we need to specify the database type explicitly using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>), for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("BLOB")
- std::vector&lt;char> buf_;
-
- #pragma db type("RAW(16)")
- unsigned char uuid_[16];
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;char> buffer;
-#pragma db value(buffer) type("BLOB")
-
-#pragma db object
-class object
-{
- ...
-
- buffer buf_; // Mapped to BLOB.
-};
- </pre>
-
- <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
- the Database"</a>) <code>char[N]</code> and
- <code>std::array&lt;char, N></code> parameters are by default passed
- as a string rather than a binary. To pass such parameters as a binary,
- we need to specify the database type explicitly in the
- <code>_val()</code>/<code>_ref()</code> calls. Note also that we
- don't need to do this for the integrated queries, for example:</p>
-
- <pre class="cxx">
-char u[16] = {...};
-
-db.query&lt;object> ("uuid = " + query::_val&lt;odb::oracle::id_raw> (u));
-db.query&lt;object> (query::uuid == query::_ref (u));
- </pre>
-
- <h2><a name="20.2">20.2 Oracle Database Class</a></h2>
-
- <p>The Oracle <code>database</code> class encapsulates the OCI environment
- handle as well as the database connection string and user credentials
- that are used to establish connections to the database. It has the
- following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace oracle
- {
- class database: public odb::database
- {
- public:
- database (const std::string&amp; user,
- const std::string&amp; password,
- const std::string&amp; db,
- ub2 charset = 0,
- ub2 ncharset = 0,
- OCIEnv* environment = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; user,
- const std::string&amp; password,
- const std::string&amp; service,
- const std::string&amp; host,
- unsigned int port = 0,
- ub2 charset = 0,
- ub2 ncharset = 0,
- OCIEnv* environment = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (int&amp; argc,
- char* argv[],
- bool erase = false,
- ub2 charset = 0,
- ub2 ncharset = 0,
- OCIEnv* environment = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- static void
- print_usage (std::ostream&amp;);
-
- public:
- const std::string&amp;
- user () const;
-
- const std::string&amp;
- password () const;
-
- const std::string&amp;
- db () const;
-
- const std::string&amp;
- service () const;
-
- const std::string&amp;
- host () const;
-
- unsigned int
- port () const;
-
- ub2
- charset () const;
-
- ub2
- ncharset () const;
-
- OCIEnv*
- environment ();
-
- public:
- connection_ptr
- connection ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/oracle/database.hxx></code>
- header file to make this class available in your application.</p>
-
- <p>The overloaded <code>database</code> constructors allow us to specify the
- Oracle database parameters that should be used when connecting to the
- database. The <code>db</code> argument in the first constructor is a
- connection identifier that specifies the database to connect to. For more
- information on the format of the connection identifier, refer to the
- Oracle documentation.</p>
-
- <p>The second constructor allows us to specify the individual components
- of a connection identifier as the <code>service</code>, <code>host</code>,
- and <code>port</code> arguments. If the <code>host</code> argument is
- empty, then localhost is used by default. Similarly, if the
- <code>port</code> argument is zero, then the default port is used.</p>
-
- <p>The last constructor extracts the database parameters
- from the command line. The following options are recognized:</p>
-
- <pre class="terminal">
- --user &lt;login>
- --password &lt;password>
- --database &lt;connect-id>
- --service &lt;name>
- --host &lt;host>
- --port &lt;integer>
- --options-file &lt;file>
- </pre>
-
- <p>The <code>--options-file</code> option allows us to specify some
- or all of the database options in a file with each option appearing
- on a separate line followed by a space and an option value. Note that it
- is invalid to specify the <code>--database</code> option
- together with <code>--service</code>, <code>--host</code>, or
- <code>--port</code> options.</p>
-
- <p>If the <code>erase</code> argument to this constructor is true,
- then the above options are removed from the <code>argv</code>
- array and the <code>argc</code> count is updated accordingly.
- This is primarily useful if your application accepts other
- options or arguments and you would like to get the Oracle
- options out of the <code>argv</code> array.</p>
-
- <p>This constructor throws the <code>odb::oracle::cli_exception</code>
- exception if the Oracle option values are missing or invalid. See section
- <a href="#20.4">Section 20.4, "Oracle Exceptions"</a> for more
- information on this exception.</p>
-
- <p>The static <code>print_usage()</code> function prints the list of options
- with short descriptions that are recognized by this constructor.</p>
-
- <p>Additionally, all the constructors have the <code>charset</code>,
- <code>ncharset</code>, and <code>environment</code> arguments.
- The <code>charset</code> argument specifies the client-side database
- character encoding. Character data corresponding to the <code>CHAR</code>,
- <code>VARCHAR2</code>, and <code>CLOB</code> types will be delivered
- to and received from the application in this encoding. Similarly,
- the <code>ncharset</code> argument specifies the client-side national
- character encoding. Character data corresponding to the <code>NCHAR</code>,
- <code>NVARCHAR2</code>, and <code>NCLOB</code> types will be delivered
- to and received from the application in this encoding. For the complete
- list of available character encoding values, refer to the Oracle
- documentation. Commonly used encoding values are <code>873</code>
- (UTF-8), <code>31</code> (ISO-8859-1), and <code>1000</code> (UTF-16).
- If the database character encoding is not specified, then the
- <code>NLS_LANG</code> environment/registry variable is used. Similarly,
- if the national character encoding is not specified, then the
- <code>NLS_NCHAR</code> environment/registry variable is used. For more
- information on character encodings, refer to the
- <code>OCIEnvNlsCreate()</code> function in the Oracle Call Interface
- (OCI) documentation.</p>
-
- <p>The <code>environment</code> argument allows us to provide a custom
- OCI environment handle. If this argument is not <code>NULL</code>,
- then the passed handle is used in all the OCI function calls made
- by this <code>database</code> class instance. Note also that the
- <code>database</code> instance does not assume ownership of the
- passed environment handle and this handle should be valid for
- the lifetime of the <code>database</code> instance. If a custom
- environment handle is used, then the <code>charset</code> and
- <code>ncharset</code> arguments have no effect.</p>
-
- <p>The last argument to all of the constructors is a pointer to the
- connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
- in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
- non-<code>NULL</code> value, the database instance assumes ownership
- of the factory instance. The connection factory interface as well as
- the available implementations are described in the next section.</p>
-
- <p>The set of accessor functions following the constructors allows us
- to query the parameters of the <code>database</code> instance.</p>
-
- <p>The <code>connection()</code> function returns a pointer to the
- Oracle database connection encapsulated by the
- <code>odb::oracle::connection</code> class. For more information
- on <code>oracle::connection</code>, refer to <a href="#20.3">Section
- 20.3, "Oracle Connection and Connection Factory"</a>.</p>
-
- <h2><a name="20.3">20.3 Oracle Connection and Connection Factory</a></h2>
-
- <p>The <code>oracle::connection</code> class has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace oracle
- {
- class connection: public odb::connection
- {
- public:
- connection (database&amp;);
- connection (database&amp;, OCISvcCtx*);
-
- OCISvcCtx*
- handle ();
-
- OCIError*
- error_handle ();
-
- details::buffer&amp;
- lob_buffer ();
- };
-
- typedef details::shared_ptr&lt;connection> connection_ptr;
- }
-}
- </pre>
-
- <p>For more information on the <code>odb::connection</code> interface, refer
- to <a href="#3.6">Section 3.6, "Connections"</a>. The first overloaded
- <code>oracle::connection</code> constructor creates a new OCI service
- context. The OCI statement caching is enabled for the underlying session
- while the OCI connection pooling and session pooling are not used. The
- second constructor allows us to create a <code>connection</code> instance by
- providing an already connected Oracle service context. Note that the
- <code>connection</code> instance assumes ownership of this handle. The
- <code>handle()</code> accessor returns the OCI service context handle
- associated with the <code>connection</code> instance.</p>
-
- <p>An OCI error handle is allocated for each <code>connection</code>
- instance and is available via the <code>error_handle()</code> accessor
- function.</p>
-
- <p>Additionally, each <code>connection</code> instance maintains a large
- object (LOB) buffer. This buffer is used by the Oracle ODB runtime
- as an intermediate storage for piecewise handling of LOB data.
- By default, the LOB buffer has zero initial capacity and is
- expanded to 4096 bytes when the first LOB operation is performed.
- If your application requires a bigger or smaller LOB buffer, you can
- specify a custom capacity using the <code>lob_buffer()</code>
- accessor.</p>
-
- <p>The <code>oracle::connection_factory</code> abstract class has the
- following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace oracle
- {
- class connection_factory
- {
- public:
- virtual void
- database (database&amp;) = 0;
-
- virtual connection_ptr
- connect () = 0;
- };
- }
-}
- </pre>
-
- <p>The <code>database()</code> function is called when a connection
- factory is associated with a database instance. This happens in
- the <code>odb::oracle::database</code> class constructors. The
- <code>connect()</code> function is called whenever a database
- connection is requested.</p>
-
- <p>The two implementations of the <code>connection_factory</code>
- interface provided by the Oracle ODB runtime are
- <code>new_connection_factory</code> and
- <code>connection_pool_factory</code>. You will need to include
- the <code>&lt;odb/oracle/connection-factory.hxx></code>
- header file to make the <code>connection_factory</code> interface
- and these implementation classes available in your application.</p>
-
- <p>The <code>new_connection_factory</code> class creates a new
- connection whenever one is requested. When a connection is no
- longer needed, it is released and closed. The
- <code>new_connection_factory</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace oracle
- {
- class new_connection_factory: public connection_factory
- {
- public:
- new_connection_factory ();
- };
-};
- </pre>
-
- <p>The <code>connection_pool_factory</code> class implements a
- connection pool. It has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace oracle
- {
- class connection_pool_factory: public connection_factory
- {
- public:
- connection_pool_factory (std::size_t max_connections = 0,
- std::size_t min_connections = 0);
-
- protected:
- class pooled_connection: public connection
- {
- public:
- pooled_connection (database_type&amp;);
- pooled_connection (database_type&amp;, OCISvcCtx*);
- };
-
- typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
-
- virtual pooled_connection_ptr
- create ();
- };
-};
- </pre>
-
- <p>The <code>max_connections</code> argument in the
- <code>connection_pool_factory</code> constructor specifies the maximum
- number of concurrent connections that this pool factory will
- maintain. Similarly, the <code>min_connections</code> argument
- specifies the minimum number of available connections that
- should be kept open.</p>
-
- <p>Whenever a connection is requested, the pool factory first
- checks if there is an unused connection that can be returned.
- If there is none, the pool factory checks the
- <code>max_connections</code> value to see if a new connection
- can be created. If the total number of connections maintained
- by the pool is less than this value, then a new connection is
- created and returned. Otherwise, the caller is blocked until
- a connection becomes available.</p>
-
- <p>When a connection is released, the pool factory first checks
- if there are blocked callers waiting for a connection. If so, then
- one of them is unblocked and is given the connection. Otherwise,
- the pool factory checks whether the total number of connections
- maintained by the pool is greater than the <code>min_connections</code>
- value. If that's the case, the connection is closed. Otherwise, the
- connection is added to the pool of available connections to be
- returned on the next request. In other words, if the number of
- connections maintained by the pool exceeds <code>min_connections</code>
- and there are no callers waiting for a new connection,
- the pool will close the excess connections.</p>
-
- <p>If the <code>max_connections</code> value is 0, then the pool will
- create a new connection whenever all of the existing connections
- are in use. If the <code>min_connections</code> value is 0, then
- the pool will never close a connection and instead maintain all
- the connections that were ever created.</p>
-
- <p>The <code>create()</code> virtual function is called whenever the
- pool needs to create a new connection. By deriving from the
- <code>connection_pool_factory</code> class and overriding this
- function we can implement custom connection establishment
- and configuration.</p>
-
- <p>If you pass <code>NULL</code> as the connection factory to one of the
- <code>database</code> constructors, then the
- <code>connection_pool_factory</code> instance will be created by default
- with the min and max connections values set to <code>0</code>. The
- following code fragment shows how we can pass our own connection factory
- instance:</p>
-
- <pre class="cxx">
-#include &lt;odb/database.hxx>
-
-#include &lt;odb/oracle/database.hxx>
-#include &lt;odb/oracle/connection-factory.hxx>
-
-int
-main (int argc, char* argv[])
-{
- auto_ptr&lt;odb::oracle::connection_factory> f (
- new odb::oracle::connection_pool_factory (20));
-
- auto_ptr&lt;odb::database> db (
- new oracle::database (argc, argv, false, 0, 0, 0, f));
-}
- </pre>
-
- <h2><a name="20.4">20.4 Oracle Exceptions</a></h2>
-
- <p>The Oracle ODB runtime library defines the following
- Oracle-specific exceptions:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace oracle
- {
- class database_exception: odb::database_exception
- {
- public:
- class record
- {
- public:
- sb4
- error () const;
-
- const std::string&amp;
- message () const;
- };
-
- typedef std::vector&lt;record> records;
-
- typedef records::size_type size_type;
- typedef records::const_iterator iterator;
-
- iterator
- begin () const;
-
- iterator
- end () const;
-
- size_type
- size () const;
-
- virtual const char*
- what () const throw ();
- };
-
- class cli_exception: odb::exception
- {
- public:
- virtual const char*
- what () const throw ();
- };
-
- class invalid_oci_handle: odb::exception
- {
- public:
- virtual const char*
- what () const throw ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/oracle/exceptions.hxx></code>
- header file to make these exceptions available in your application.</p>
-
- <p>The <code>odb::oracle::database_exception</code> is thrown if
- an Oracle database operation fails. The Oracle-specific error
- information is stored as a series of records, each containing
- the error code as a signed 4-byte integer and the message string.
- All this information is also combined and returned in a
- human-readable form by the <code>what()</code> function.</p>
-
- <p>The <code>odb::oracle::cli_exception</code> is thrown by the
- command line parsing constructor of the <code>odb::oracle::database</code>
- class if the Oracle option values are missing or invalid. The
- <code>what()</code> function returns a human-readable description
- of an error.</p>
-
- <p>The <code>odb::oracle::invalid_oci_handle</code> is thrown if an
- invalid handle is passed to an OCI function or if an OCI function
- was unable to allocate a handle. The former normally indicates
- a programming error while the latter indicates an out of memory
- condition. The <code>what()</code> function returns a human-readable
- description of an error.</p>
-
- <h2><a name="20.5">20.5 Oracle Limitations</a></h2>
-
- <p>The following sections describe Oracle-specific limitations imposed
- by the current Oracle and ODB runtime versions.</p>
-
- <h3><a name="20.5.1">20.5.1 Identifier Truncation</a></h3>
-
- <p>Oracle limits the length of database identifiers (table, column, etc.,
- names) to 30 characters. The ODB compiler automatically truncates
- any identifier that is longer than 30 characters. This, however,
- can lead to duplicate names. A common symptom of this problem
- are errors during the database schema creation indicating
- that a database object with the same name already exists. To
- resolve this problem we can assign custom, shorter identifiers
- using the <code>db&nbsp;table</code> and <code>db&nbsp;column</code>
- pragmas (<a href="#14">Chapter 14, "ODB Pragma Language")</a>. For
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class long_class_name
-{
- ...
-
- std::vector&lt;int> long_container_x_;
- std::vector&lt;int> long_container_y_;
-};
- </pre>
-
- <p>In the above example, the names of the two container tables will be
- <code>long_class_name_long_container_x_</code> and
- <code>long_class_name_long_container_y_</code>. However, when
- truncated to 30 characters, they both become
- <code>long_class_name_long_container</code>. To resolve this
- collision we can assign a custom table name for each container:</p>
-
- <pre class="cxx">
-#pragma db object
-class long_class_name
-{
- ...
-
- #pragma db table("long_class_name_cont_x")
- std::vector&lt;int> long_container_x_;
-
- #pragma db table("long_class_name_cont_y")
- std::vector&lt;int> long_container_y_;
-};
- </pre>
-
- <h3><a name="20.5.2">20.5.2 Query Result Caching</a></h3>
-
- <p>Oracle ODB runtime implementation does not perform query result caching
- (<a href="#4.4">Section 4.4, "Query Result"</a>) even when explicitly
- requested. The OCI API supports interleaving execution of multiple
- prepared statements on a single connection. As a result, with OCI,
- it is possible to have multiple uncached results and calls to other
- database functions do not invalidate them. The only limitation of
- the uncached Oracle results is the unavailability of the
- <code>result::size()</code> function. If you call this function on
- an Oracle query result, then the <code>odb::result_not_cached</code>
- exception (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>) is
- always thrown. Future versions of the Oracle ODB runtime library
- may add support for result caching.</p>
-
- <h3><a name="20.5.3">20.5.3 Foreign Key Constraints</a></h3>
-
- <p>ODB assumes the standard SQL behavior which requires that
- foreign key constraints checking is deferred until the
- transaction is committed. Default Oracle behavior is
- to check such constraints immediately. As a result, when
- used with ODB, a custom database schema that defines foreign
- key constraints may need to declare such constraints as
- <code>INITIALLY DEFERRED</code>, as shown in the following example.
- By default, schemas generated by the ODB compiler meet this requirement
- automatically.</p>
-
- <pre class="sql">
-CREATE TABLE Employee (
- ...
- employer NUMBER(20) REFERENCES Employer(id)
- DEFERRABLE INITIALLY DEFERRED);
- </pre>
-
- <p>You can override the default behavior and instruct the ODB
- compiler to generate non-deferrable foreign keys by specifying
- the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
- compiler option. Note, however, that in this case the order in
- which you persist, update, and erase objects within a transaction
- becomes important.</p>
-
- <h3><a name="20.5.4">20.5.4 Unique Constraint Violations</a></h3>
-
- <p>Due to the granularity of the Oracle error codes, it is impossible
- to distinguish between the duplicate primary key and other unique
- constraint violations. As a result, when making an object persistent,
- the Oracle ODB runtime will translate all unique constraint violation
- errors to the <code>object_already_persistent</code> exception
- (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>).</p>
-
- <h3><a name="20.5.5">20.5.5 Large <code>FLOAT</code> and
- <code>NUMBER</code> Types</a></h3>
-
- <p>The Oracle <code>FLOAT</code> type with a binary precision greater
- than 53 and fixed-point <code>NUMBER</code> type with a decimal
- precision greater than 15 cannot be automatically extracted
- into the C++ <code>float</code> and <code>double</code> types.
- Instead, the Oracle ODB runtime uses a 21-byte buffer containing
- the binary representation of a value as an image type for such
- <code>FLOAT</code> and <code>NUMBER</code> types. In order to
- convert them into an application-specific large number representation,
- you will need to provide a suitable <code>value_traits</code>
- template specialization. For more information on the binary format
- used to store the <code>FLOAT</code> and <code>NUMBER</code> values,
- refer to the Oracle Call Interface (OCI) documentation.</p>
-
- <p>An alternative approach to accessing large <code>FLOAT</code> and
- <code>NUMBER</code> values is to map these type to one of the
- natively supported ones, as discussed in <a href="#14.8">Section
- 14.8, "Database Type Mapping Pragmas"</a>.</p>
-
- <p>Note that a <code>NUMBER</code> type that is used to represent a
- floating point number (declared by specifying <code>NUMBER</code>
- without any range and scale) can be extracted into the C++
- <code>float</code> and <code>double</code> types.</p>
-
- <h3><a name="20.5.6">20.5.6 Timezones</a></h3>
-
- <p>ODB does not currently support the Oracle date-time types with timezone
- information. However, these types can be accessed by mapping them to
- one of the natively supported types, as discussed in
- <a href="#14.8">Section 14.8, "Database Type Mapping Pragmas"</a>.</p>
-
- <h3><a name="20.5.7">20.5.7 <code>LONG</code> Types</a></h3>
-
- <p>ODB does not support the deprecated Oracle <code>LONG</code> and
- <code>LONG RAW</code> data types. However, these types can be accessed
- by mapping them to one of the natively supported types, as discussed
- in <a href="#14.8">Section 14.8, "Database Type Mapping Pragmas"</a>.</p>
-
- <h3><a name="20.5.8">20.5.8 LOB Types and By-Value Accessors/Modifiers</a></h3>
-
- <p>As discussed in <a href="#14.4.5">Section 14.4.5,
- "<code>get</code>/<code>set</code>/<code>access</code>"</a>, by-value
- accessor and modifier expressions cannot be used with data members
- of Oracle large object (LOB) data types: <code>BLOB</code>,
- <code>CLOB</code>, and <code>NCLOB</code>. The Oracle ODB runtime
- uses streaming for reading/writing LOB data directly from/to
- data members. As a result, by-reference accessors and modifiers
- should be used for these data types.</p>
-
- <h3><a name="20.5.9">20.5.9 Database Schema Evolution</a></h3>
-
- <p>In Oracle, the type of the <code>name</code> column in the
- <code>schema_version</code> table is <code>VARCHAR2(512)</code>.
- Because this column is a primary key and <code>VARCHAR2</code>
- represents empty strings as <code>NULL</code> values, it is
- impossible to store an empty string in this column, which
- is what is used to represent the default schema name. As a
- result, in Oracle, the empty schema name is stored as a
- string containing a single space character. ODB performs
- all the necessary translations automatically and normally
- you do not need to worry about this implementation detail
- unless you are querying or modifying the <code>schema_version</code>
- table directly.</p>
-
- <h2><a name="20.6">20.6 Oracle Index Definitions</a></h2>
-
- <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
- "Index Definition Pragmas"</a>) is used to define an Oracle index,
- the <code>type</code> clause specifies the index type (for example,
- <code>UNIQUE</code>, <code>BITMAP</code>), the <code>method</code>
- clause is not used, and the <code>options</code> clause specifies
- additional index properties, such as partitioning, table spaces, etc.
- The column options can be used to specify the sort order. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- std::string name_;
-
- #pragma db index \
- type("BITMAP") \
- member(name_, "DESC") \
- options("TABLESPACE TBS1")
-};
- </pre>
-
- <p>Index names in Oracle are schema-global. To avoid name clashes,
- ODB automatically prefixes each index name with the table name on
- which it is defined.</p>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="21">21 Microsoft SQL Server Database</a></h1>
-
- <p>To generate support code for the SQL Server database you will need
- to pass the "<code>--database&nbsp;mssql</code>"
- (or "<code>-d&nbsp;mssql</code>") option to the ODB compiler.
- Your application will also need to link to the SQL Server ODB runtime
- library (<code>libodb-mssql</code>). All SQL Server-specific ODB
- classes are defined in the <code>odb::mssql</code> namespace.</p>
-
- <h2><a name="21.1">21.1 SQL Server Type Mapping</a></h2>
-
- <p>The following table summarizes the default mapping between basic
- C++ value types and SQL Server database types. This mapping can be
- customized on the per-type and per-member basis using the ODB
- Pragma Language (<a href="#14">Chapter 14, "ODB Pragma Language"</a>).</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>C++ Type</th>
- <th>SQL Server Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>bool</code></td>
- <td><code>BIT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char</code></td>
- <td><code>CHAR(1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>signed char</code></td>
- <td><code>TINYINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned char</code></td>
- <td><code>TINYINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>short</code></td>
- <td><code>SMALLINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned short</code></td>
- <td><code>SMALLINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>int</code></td>
- <td><code>INT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned int</code></td>
- <td><code>INT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>long long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>unsigned long long</code></td>
- <td><code>BIGINT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>float</code></td>
- <td><code>REAL</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>double</code></td>
- <td><code>FLOAT</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>std::string</code></td>
- <td><code>VARCHAR(512)/VARCHAR(256)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>char[N]</code></td>
- <td><code>VARCHAR(N-1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>std::wstring</code></td>
- <td><code>NVARCHAR(512)/NVARCHAR(256)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>wchar_t[N]</code></td>
- <td><code>NVARCHAR(N-1)</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- <tr>
- <td><code>GUID</code></td>
- <td><code>UNIQUEIDENTIFIER</code></td>
- <td><code>NOT NULL</code></td>
- </tr>
-
- </table>
-
- <p>It is possible to map the <code>char</code> C++ type to an integer
- database type (for example, <code>TINYINT</code>) using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>).</p>
-
- <p>Note that the <code>std::string</code> and <code>std::wstring</code>
- types are mapped differently depending on whether a member of one of
- these types is an object id or not. If the member is an object id,
- then for this member <code>std::string</code> is mapped
- to <code>VARCHAR(256)</code> and <code>std::wstring</code> &mdash;
- to <code>NVARCHAR(256)</code>. Otherwise, <code>std::string</code>
- is mapped to <code>VARCHAR(512)</code> and <code>std::wstring</code>
- &mdash; to <code>NVARCHAR(512)</code>. Note also that you can
- always change this mapping using the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>).</p>
-
- <p>Additionally, by default, C++ enums and C++11 enum classes are
- automatically mapped to the SQL Server types corresponding to their
- underlying integral types (see table above). The default
- <code>NULL</code> semantics is <code>NOT NULL</code>. For
- example:</p>
-
- <pre class="cxx">
-enum color {red, green, blue};
-enum class taste: unsigned char
-{
- bitter = 1,
- sweet,
- sour = 4,
- salty
-};
-
-#pragma db object
-class object
-{
- ...
-
- color color_; // Automatically mapped to INT.
- taste taste_; // Automatically mapped to TINYINT.
-};
- </pre>
-
- <p>Note also that because SQL Server does not support unsigned integers,
- the <code>unsigned&nbsp;short</code>, <code>unsigned&nbsp;int</code>, and
- <code>unsigned&nbsp;long</code>/<code>unsigned&nbsp;long&nbsp;long</code> C++ types
- are by default mapped to the <code>SMALLINT</code>, <code>INT</code>,
- and <code>BIGINT</code> SQL Server types, respectively. The sign bit
- of the value stored by the database for these types will contain
- the most significant bit of the actual unsigned value being
- persisted. Similarly, because there is no signed version of the
- <code>TINYINT</code> SQL Server type, by default, the
- <code>signed char</code> C++ type is mapped to <code>TINYINT</code>.
- As a result, the most significant bit of the value stored by the
- database for this type will contain the sign bit of the actual
- signed value being persisted.</p>
-
- <p>It is also possible to add support for additional SQL Server types,
- such as geospatial types, <code>XML</code>, and user-defined types.
- For more information, refer to <a href="#14.8">Section 14.8, "Database
- Type Mapping Pragmas"</a>.</p>
-
- <h3><a name="21.1.1">21.1.1 String Type Mapping</a></h3>
-
- <p>The SQL Server ODB runtime library provides support for mapping the
- <code>std::string</code>, <code>char[N]</code>, and
- <code>std::array&lt;char, N></code> types to the SQL Server
- <code>CHAR</code>, <code>VARCHAR</code>, and <code>TEXT</code>
- types as well as the <code>std::wstring</code>, <code>wchar_t[N]</code>,
- and <code>std::array&lt;wchar_t, N></code> types to <code>NCHAR</code>,
- <code>NVARCHAR</code>, and <code>NTEXT</code>. However, these mappings
- are not enabled by default (in particular, by default,
- <code>std::array</code> will be treated as a container). To enable the
- alternative mappings for these types we need to specify the database
- type explicitly using the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), for
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type ("CHAR(2)")
- char state_[2];
-
- #pragma db type ("NVARCHAR(max)")
- std::wstring text_;
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-#pragma db value(std::wstring) type("NVARCHAR(max)")
-
-#pragma db object
-class object
-{
- ...
-
- std::wstring text_; // Mapped to NVARCHAR(max).
-};
- </pre>
-
- <p>The <code>char[N]</code>, <code>std::array&lt;char, N></code>,
- <code>wchar_t[N]</code>, and <code>std::array&lt;wchar_t, N></code>
- values may or may not be zero-terminated. When extracting such values
- from the database, ODB will append the zero terminator if there is
- enough space.</p>
-
- <p>See also <a href="#21.1.4">Section 21.1.4, "Long String and Binary
- Types"</a> for certain limitations of long string types.</p>
-
- <h3><a name="21.1.2">21.1.2 Binary Type and <code>UNIQUEIDENTIFIER</code> Mapping</a></h3>
-
- <p>The SQL Server ODB runtime library also provides support for mapping the
- <code>std::vector&lt;char></code>,
- <code>std::vector&lt;unsigned&nbsp;char></code>,
- <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
- <code>std::array&lt;char, N></code>, and <code>std::array&lt;unsigned char, N></code>
- types to the SQL Server <code>BINARY</code>, <code>VARBINARY</code>, and
- <code>IMAGE</code> types. There is also support for mapping the
- <code>char[16]</code> array to the SQL Server <code>UNIQUEIDENTIFIER</code>
- type. However, these mappings are not enabled by default (in particular,
- by default, <code>std::vector</code> and <code>std::array</code> will
- be treated as containers). To enable the alternative mappings for these
- types we need to specify the database type explicitly using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>), for example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- #pragma db type("UNIQUEIDENTIFIER")
- char uuid_[16];
-
- #pragma db type("VARBINARY(max)")
- std::vector&lt;char> buf_;
-
- #pragma db type("BINARY(256)")
- unsigned char data_[256];
-};
- </pre>
-
- <p>Alternatively, this can be done on the per-type basis, for example:</p>
-
- <pre class="cxx">
-typedef std::vector&lt;char> buffer;
-#pragma db value(buffer) type("VARBINARY(max)")
-
-#pragma db object
-class object
-{
- ...
-
- buffer buf_; // Mapped to VARBINARY(max).
-};
- </pre>
-
- <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
- the Database"</a>) <code>char[N]</code> and
- <code>std::array&lt;char, N></code> parameters are by default passed
- as a string rather than a binary. To pass such parameters as a binary,
- we need to specify the database type explicitly in the
- <code>_val()</code>/<code>_ref()</code> calls. Note also that we
- don't need to do this for the integrated queries, for example:</p>
-
- <pre class="cxx">
-char u[16] = {...};
-
-db.query&lt;object> ("uuid = " + query::_val&lt;odb::mssql::id_binary> (u));
-db.query&lt;object> (
- "uuid = " + query::_val&lt;odb::mssql::id_uniqueidentifier> (u));
-db.query&lt;object> (query::uuid == query::_ref (u));
- </pre>
-
- <p>See also <a href="#21.1.4">Section 21.1.4, "Long String and Binary
- Types"</a> for certain limitations of long binary types.</p>
-
- <h3><a name="21.1.3">21.1.3 <code>ROWVERSION</code> Mapping</a></h3>
-
- <p><code>ROWVERSION</code> is a special SQL Server data type that is
- automatically incremented by the database server whenever a row
- is inserted or updated. As such, it is normally used to implement
- optimistic concurrency and ODB provides support for using
- <code>ROWVERSION</code> instead of the more portable approach
- for optimistic concurrency (<a href="#12">Chapter 12, "Optimistic
- Concurrency"</a>).</p>
-
- <p><code>ROWVERSION</code> is a 64-bit value which is mapped by ODB
- to <code>unsigned long long</code>. As a result, to use
- <code>ROWVERSION</code> for optimistic concurrency we need to
- make sure that the version column is of the <code>unsigned long
- long</code> type. We also need to explicitly specify that it
- should be mapped to the <code>ROWVERSION</code> data type. For
- example:</p>
-
- <pre class="cxx">
-#pragma db object optimistic
-class person
-{
- ...
-
- #pragma db version type("ROWVERSION")
- unsigned long long version_;
-};
- </pre>
-
- <h3><a name="21.1.4">21.1.4 Long String and Binary Types</a></h3>
-
- <p>For SQL Server, ODB handles character, national character, and
- binary data in two different ways depending on its maximum length.
- If the maximum length (in bytes) is less than or equal to the limit
- specified with the <code>--mssql-short-limit</code> ODB compiler
- option (1024 by default), then it is treated as <i>short data</i>,
- otherwise &mdash; <i>long data</i>. For short data ODB pre-allocates
- an intermediate buffer of the maximum size and binds it directly
- to a parameter or result column. This way the underlying database
- API (ODBC) can read/write directly from/to this buffer. In the case
- of long data, the data is read/written in chunks using the
- <code>SQLGetData()</code>/<code>SQLPutData()</code> ODBC functions.
- While the long data approach reduces the amount of memory used by
- the application, it may require greater CPU resources.</p>
-
- <p>Long data has a number of limitations. In particular, when setting
- a custom short data limit, make sure that it is sufficiently large
- so that no object id in the application is treated as long data.
- It is also impossible to load an object or view with long data more
- than once as part of a query result iteration (<a href="#4.4">Section
- 4.4, "Query Result"</a>). Any such attempt will result in the
- <code>odb::mssql::long_data_reload</code> exception
- (<a href="#21.4">Section 21.4, "SQL Server Exceptions"</a>). For
- example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- int num_;
-
- #pragma db type("VARCHAR(max)") // Long data.
- std::string str_;
-};
-
-typedef odb::query&lt;object> query;
-typedef odb::result&lt;object> result;
-
-transaction t (db.begin ());
-
-result r (db.query&lt;object> (query::num &lt; 100));
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
-{
- if (!i->str_.empty ()) // First load.
- {
- object o;
- i.load (o); // Error: second load, long_data_reload is thrown.
- }
-}
-
-t.commit ();
- </pre>
-
- <p>Finally, if a native view (<a href="#10.6">Section 10.6, "Native
- Views"</a>) contains one or more long data members, then such
- members should come last both in the select-list of the native
- SQL query and the list of data members in the C++ class.</p>
-
- <h2><a name="21.2">21.2 SQL Server Database Class</a></h2>
-
- <p>The SQL Server <code>database</code> class encapsulates the ODBC
- environment handle as well as the server instance address and
- user credentials that are used to establish connections to the
- database. It has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mssql
- {
- enum protocol
- {
- protocol_auto,
- protocol_tcp, // TCP/IP.
- protocol_lpc, // Shared memory (local procedure call).
- protocol_np // Named pipes.
- };
-
- enum transaction_isolation
- {
- isolation_read_uncommitted,
- isolation_read_committed, // SQL Server default.
- isolation_repeatable_read,
- isolation_snapshot,
- isolation_serializable
- };
-
- class database: public odb::database
- {
- public:
- typedef protocol protocol_type;
- typedef transaction_isolation transaction_isolation_type;
-
- database (const std::string&amp; user,
- const std::string&amp; password,
- const std::string&amp; db,
- const std::string&amp; server,
- const std::string&amp; driver = "",
- const std::string&amp; extra_connect_string = "",
- transaction_isolation_type = isolation_read_committed,
- SQLHENV environment = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; user,
- const std::string&amp; password,
- const std::string&amp; db,
- protocol_type protocol = protocol_auto,
- const std::string&amp; host = "",
- const std::string&amp; instance = "",
- const std::string&amp; driver = "",
- const std::string&amp; extra_connect_string = "",
- transaction_isolation_type = isolation_read_committed,
- SQLHENV environment = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; user,
- const std::string&amp; password,
- const std::string&amp; db,
- const std::string&amp; host,
- unsigned int port,
- const std::string&amp; driver = "",
- const std::string&amp; extra_connect_string = "",
- transaction_isolation_type = isolation_read_committed,
- SQLHENV environment = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (const std::string&amp; connect_string,
- transaction_isolation_type = isolation_read_committed,
- SQLHENV environment = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- database (int&amp; argc,
- char* argv[],
- bool erase = false,
- const std::string&amp; extra_connect_string = "",
- transaction_isolation_type = isolation_read_committed,
- SQLHENV environment = 0,
- std::[auto|unique]_ptr&lt;connection_factory> = 0);
-
- static void
- print_usage (std::ostream&amp;);
-
- public:
- const std::string&amp;
- user () const;
-
- const std::string&amp;
- password () const;
-
- const std::string&amp;
- db () const;
-
- protocol_type
- protocol () const;
-
- const std::string&amp;
- host () const;
-
- const std::string&amp;
- instance () const;
-
- unsigned int
- port () const;
-
- const std::string&amp;
- server () const;
-
- const std::string&amp;
- driver () const;
-
- const std::string&amp;
- extra_connect_string () const;
-
- transaction_isolation_type
- transaction_isolation () const;
-
- const std::string&amp;
- connect_string () const;
-
- SQLHENV
- environment ();
-
- public:
- connection_ptr
- connection ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/mssql/database.hxx></code>
- header file to make this class available in your application.</p>
-
- <p>The overloaded <code>database</code> constructors allow us to specify the
- SQL Server database parameters that should be used when connecting to the
- database. The <code>user</code> and <code>password</code> arguments
- specify the login name and password. If <code>user</code> is empty,
- then Windows authentication is used and the <code>password</code>
- argument is ignored. The <code>db</code> argument specifies the
- database name to open. If it is empty, then the default database for
- the user is used.</p>
-
- <p>The <code>server</code> argument in the first constructor specifies
- the SQL Server instance address in the standard SQL Server address
- format:</p>
-
- <p>
- <code>[<i>protocol</i><b>:</b>]<i>host</i>[<b>\</b><i>instance</i>][<b>,</b><i>port</i>]</code>
- </p>
-
- <p>Where <code><i>protocol</i></code> can be <code>tcp</code>
- (TCP/IP), <code>lpc</code> (shared memory), or
- <code>np</code> (named pipe). If protocol is not specified, then a
- suitable protocol is automatically selected based on the SQL Server
- Native Client configuration. The <code><i>host</i></code> component
- can be a host name or an IP address. If <code><i>instance</i></code>
- is not specified, then the default SQL Server instance is assumed.
- If port is not specified, then the default SQL Server port is
- used (1433). Note that you would normally specify either the
- instance name or the port, but not both. If both are specified,
- then the instance name is ignored by the SQL Server Native Client
- ODBC driver. For more information on the format of the SQL
- Server address, refer to the SQL Server Native Client ODBC
- driver documentation.</p>
-
- <p>The second and third constructors allow us to specify all these address
- components (protocol, host, instance, and port) as separate
- arguments. The third constructor always connects using TCP/IP
- to the specified host and port.</p>
-
- <p>The <code>driver</code> argument specifies the SQL Server Native
- Client ODBC driver that should be used to connect to the database.
- If not specified, then the latest available version is used. The
- following examples show common ways of connecting to the database
- using the first three constructors:</p>
-
- <pre class="cxx">
-// Connect to the default SQL Server instance on the local machine
-// using the default protocol. Login as 'test' with password 'secret'
-// and open the 'example_db' database.
-//
-odb::mssql::database db1 ("test",
- "secret",
- "example_db");
-
-// As above except use Windows authentication and open the default
-// database for this user.
-//
-odb::mssql::database db2 ("",
- "",
- "");
-
-// Connect to the default SQL Server instance on 'onega' using the
-// default protocol. Login as 'test' with password 'secret' and open
-// the 'example_db' database.
-//
-odb::mssql::database db3 ("test",
- "secret",
- "example_db"
- "onega");
-
-// As above but connect to the 'production' SQL Server instance.
-//
-odb::mssql::database db4 ("test",
- "secret",
- "example_db"
- "onega\\production");
-
-// Same as above but specify protocol, host, and instance as separate
-// arguments.
-//
-odb::mssql::database db5 ("test",
- "secret",
- "example_db",
- odb::mssql::protocol_auto,
- "onega",
- "production");
-
-// As above, but use TCP/IP as the protocol.
-//
-odb::mssql::database db6 ("test",
- "secret",
- "example_db"
- "tcp:onega\\production");
-
-// Same as above but using separate arguments.
-//
-odb::mssql::database db7 ("test",
- "secret",
- "example_db",
- odb::mssql::protocol_tcp,
- "onega",
- "production");
-
-// As above, but use TCP/IP port instead of the instance name.
-//
-odb::mssql::database db8 ("test",
- "secret",
- "example_db"
- "tcp:onega,1435");
-
-// Same as above but using separate arguments. Note that here we
-// don't need to specify protocol explicitly since it can only
-// be TCP/IP.
-//
-odb::mssql::database db9 ("test",
- "secret",
- "example_db",
- "onega",
- 1435);
-
-// As above but use the specific SQL Server Native Client ODBC
-// driver version.
-//
-odb::mssql::database dbA ("test",
- "secret",
- "example_db"
- "tcp:onega,1435",
- "SQL Server Native Client 10.0");
- </pre>
-
-
- <p>The fourth constructor allows us to pass a custom ODBC connection
- string that provides all the information necessary to connect to
- the database. Note also that all the other constructors have the
- <code>extra_connect_string</code> argument which can be used to
- specify additional ODBC connection attributes. For more information
- on the format of the ODBC connection string, refer to the SQL
- Server Native Client ODBC driver documentation.</p>
-
- <p>The last constructor extracts the database parameters
- from the command line. The following options are recognized:</p>
-
- <pre class="terminal">
- --user | -U &lt;login>
- --password | -P &lt;password>
- --database | -d &lt;name>
- --server | -S &lt;address>
- --driver &lt;name>
- --options-file &lt;file>
- </pre>
-
- <p>The <code>--options-file</code> option allows us to specify some
- or all of the database options in a file with each option appearing
- on a separate line followed by a space and an option value.</p>
-
- <p>If the <code>erase</code> argument to this constructor is true,
- then the above options are removed from the <code>argv</code>
- array and the <code>argc</code> count is updated accordingly.
- This is primarily useful if your application accepts other
- options or arguments and you would like to get the SQL Server
- options out of the <code>argv</code> array.</p>
-
- <p>This constructor throws the <code>odb::mssql::cli_exception</code>
- exception if the SQL Server option values are missing or invalid. See
- section <a href="#21.4">Section 21.4, "SQL Server Exceptions"</a> for
- more information on this exception.</p>
-
- <p>The static <code>print_usage()</code> function prints the list of options
- with short descriptions that are recognized by this constructor.</p>
-
- <p>Additionally, all the constructors have the <code>transaction_isolation</code>
- and <code>environment</code> arguments. The <code>transaction_isolation</code>
- argument allows us to specify an alternative transaction isolation level
- that should be used by all the connections created by this database instance.
- The <code>environment</code> argument allows us to provide a custom ODBC
- environment handle. If this argument is not <code>NULL</code>, then the
- passed handle is used in all the ODBC function calls made by this
- <code>database</code> instance. Note also that the <code>database</code>
- instance does not assume ownership of the passed environment handle and
- this handle should be valid for the lifetime of the <code>database</code>
- instance.</p>
-
- <p>The last argument to all of the constructors is a pointer to the
- connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
- in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
- non-<code>NULL</code> value, the database instance assumes ownership
- of the factory instance. The connection factory interface as well as
- the available implementations are described in the next section.</p>
-
- <p>The set of accessor functions following the constructors allows us
- to query the parameters of the <code>database</code> instance.</p>
-
- <p>The <code>connection()</code> function returns a pointer to the
- SQL Server database connection encapsulated by the
- <code>odb::mssql::connection</code> class. For more information
- on <code>mssql::connection</code>, refer to <a href="#21.3">Section
- 21.3, "SQL Server Connection and Connection Factory"</a>.</p>
-
- <h2><a name="21.3">21.3 SQL Server Connection and Connection Factory</a></h2>
-
- <p>The <code>mssql::connection</code> class has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mssql
- {
- class connection: public odb::connection
- {
- public:
- connection (database&amp;);
- connection (database&amp;, SQLHDBC handle);
-
- SQLHDBC
- handle ();
-
- details::buffer&amp;
- long_data_buffer ();
- };
-
- typedef details::shared_ptr&lt;connection> connection_ptr;
- }
-}
- </pre>
-
- <p>For more information on the <code>odb::connection</code> interface, refer
- to <a href="#3.6">Section 3.6, "Connections"</a>. The first overloaded
- <code>mssql::connection</code> constructor creates a new ODBC connection.
- The created connection is configured to use the manual commit mode with
- multiple active result sets (MARS) enabled. The second constructor allows
- us to create a <code>connection</code> instance by providing an already
- established ODBC connection. Note that the <code>connection</code>
- instance assumes ownership of this handle. The <code>handle()</code>
- accessor returns the underlying ODBC connection handle associated with
- the <code>connection</code> instance.</p>
-
- <p>Additionally, each <code>connection</code> instance maintains a long
- data buffer. This buffer is used by the SQL Server ODB runtime
- as an intermediate storage for piecewise handling of long data.
- By default, the long data buffer has zero initial capacity and is
- expanded to 4096 bytes when the first long data operation is performed.
- If your application requires a bigger or smaller long data buffer,
- you can specify a custom capacity using the <code>long_data_buffer()</code>
- accessor.</p>
-
- <p>The <code>mssql::connection_factory</code> abstract class has the
- following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mssql
- {
- class connection_factory
- {
- public:
- virtual void
- database (database&amp;) = 0;
-
- virtual connection_ptr
- connect () = 0;
- };
- }
-}
- </pre>
-
- <p>The <code>database()</code> function is called when a connection
- factory is associated with a database instance. This happens in
- the <code>odb::mssql::database</code> class constructors. The
- <code>connect()</code> function is called whenever a database
- connection is requested.</p>
-
- <p>The two implementations of the <code>connection_factory</code>
- interface provided by the SQL Server ODB runtime are
- <code>new_connection_factory</code> and
- <code>connection_pool_factory</code>. You will need to include
- the <code>&lt;odb/mssql/connection-factory.hxx></code>
- header file to make the <code>connection_factory</code> interface
- and these implementation classes available in your application.</p>
-
- <p>The <code>new_connection_factory</code> class creates a new
- connection whenever one is requested. When a connection is no
- longer needed, it is released and closed. The
- <code>new_connection_factory</code> class has the following
- interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mssql
- {
- class new_connection_factory: public connection_factory
- {
- public:
- new_connection_factory ();
- };
-};
- </pre>
-
- <p>The <code>connection_pool_factory</code> class implements a
- connection pool. It has the following interface:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mssql
- {
- class connection_pool_factory: public connection_factory
- {
- public:
- connection_pool_factory (std::size_t max_connections = 0,
- std::size_t min_connections = 0);
-
- protected:
- class pooled_connection: public connection
- {
- public:
- pooled_connection (database_type&amp;);
- pooled_connection (database_type&amp;, SQLHDBC handle);
- };
-
- typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
-
- virtual pooled_connection_ptr
- create ();
- };
-};
- </pre>
-
- <p>The <code>max_connections</code> argument in the
- <code>connection_pool_factory</code> constructor specifies the maximum
- number of concurrent connections that this pool factory will
- maintain. Similarly, the <code>min_connections</code> argument
- specifies the minimum number of available connections that
- should be kept open.</p>
-
- <p>Whenever a connection is requested, the pool factory first
- checks if there is an unused connection that can be returned.
- If there is none, the pool factory checks the
- <code>max_connections</code> value to see if a new connection
- can be created. If the total number of connections maintained
- by the pool is less than this value, then a new connection is
- created and returned. Otherwise, the caller is blocked until
- a connection becomes available.</p>
-
- <p>When a connection is released, the pool factory first checks
- if there are blocked callers waiting for a connection. If so, then
- one of them is unblocked and is given the connection. Otherwise,
- the pool factory checks whether the total number of connections
- maintained by the pool is greater than the <code>min_connections</code>
- value. If that's the case, the connection is closed. Otherwise, the
- connection is added to the pool of available connections to be
- returned on the next request. In other words, if the number of
- connections maintained by the pool exceeds <code>min_connections</code>
- and there are no callers waiting for a new connection,
- the pool will close the excess connections.</p>
-
- <p>If the <code>max_connections</code> value is 0, then the pool will
- create a new connection whenever all of the existing connections
- are in use. If the <code>min_connections</code> value is 0, then
- the pool will never close a connection and instead maintain all
- the connections that were ever created.</p>
-
- <p>The <code>create()</code> virtual function is called whenever the
- pool needs to create a new connection. By deriving from the
- <code>connection_pool_factory</code> class and overriding this
- function we can implement custom connection establishment
- and configuration.</p>
-
- <p>If you pass <code>NULL</code> as the connection factory to one of the
- <code>database</code> constructors, then the
- <code>connection_pool_factory</code> instance will be created by default
- with the min and max connections values set to <code>0</code>. The
- following code fragment shows how we can pass our own connection factory
- instance:</p>
-
- <pre class="cxx">
-#include &lt;odb/database.hxx>
-
-#include &lt;odb/mssql/database.hxx>
-#include &lt;odb/mssql/connection-factory.hxx>
-
-int
-main (int argc, char* argv[])
-{
- auto_ptr&lt;odb::mssql::connection_factory> f (
- new odb::mssql::connection_pool_factory (20));
-
- auto_ptr&lt;odb::database> db (
- new mssql::database (argc, argv, false, "", 0, f));
-}
- </pre>
-
- <h2><a name="21.4">21.4 SQL Server Exceptions</a></h2>
-
- <p>The SQL Server ODB runtime library defines the following
- SQL Server-specific exceptions:</p>
-
- <pre class="cxx">
-namespace odb
-{
- namespace mssql
- {
- class database_exception: odb::database_exception
- {
- public:
- class record
- {
- public:
- SQLINTEGER
- error () const;
-
- const std::string&amp;
- sqlstate () const;
-
- const std::string&amp;
- message () const;
- };
-
- typedef std::vector&lt;record> records;
-
- typedef records::size_type size_type;
- typedef records::const_iterator iterator;
-
- iterator
- begin () const;
-
- iterator
- end () const;
-
- size_type
- size () const;
-
- virtual const char*
- what () const throw ();
- };
-
- class cli_exception: odb::exception
- {
- public:
- virtual const char*
- what () const throw ();
- };
-
- class long_data_reload: odb::exception
- {
- public:
- virtual const char*
- what () const throw ();
- };
- }
-}
- </pre>
-
- <p>You will need to include the <code>&lt;odb/mssql/exceptions.hxx></code>
- header file to make these exceptions available in your application.</p>
-
- <p>The <code>odb::mssql::database_exception</code> is thrown if
- an SQL Server database operation fails. The SQL Server-specific error
- information is stored as a series of records, each containing
- the error code as a signed 4-byte integer, the SQLSTATE code,
- and the message string. All this information is also combined
- and returned in a human-readable form by the <code>what()</code>
- function.</p>
-
- <p>The <code>odb::mssql::cli_exception</code> is thrown by the
- command line parsing constructor of the <code>odb::mssql::database</code>
- class if the SQL Server option values are missing or invalid. The
- <code>what()</code> function returns a human-readable description
- of an error.</p>
-
- <p>The <code>odb::mssql::long_data_reload</code> is thrown if an
- attempt is made to re-load an object or view with long data as
- part of a query result iteration. For more information, refer
- to <a href="#21.1">Section 21.1, "SQL Server Type Mapping"</a>.</p>
-
- <h2><a name="21.5">21.5 SQL Server Limitations</a></h2>
-
- <p>The following sections describe SQL Server-specific limitations imposed
- by the current SQL Server and ODB runtime versions.</p>
-
- <h3><a name="21.5.1">21.5.1 Query Result Caching</a></h3>
-
- <p>SQL Server ODB runtime implementation does not perform query result
- caching (<a href="#4.4">Section 4.4, "Query Result"</a>) even when
- explicitly requested. The ODBC API and the SQL Server Native Client ODBC
- driver support interleaving execution of multiple prepared statements
- on a single connection. As a result, it is possible to have multiple
- uncached results and calls to other database functions do not invalidate
- them. The only limitation of the uncached SQL Server results is the
- unavailability of the <code>result::size()</code> function. If you
- call this function on an SQL Server query result, then the
- <code>odb::result_not_cached</code> exception (<a href="#3.14">Section
- 3.14, "ODB Exceptions"</a>) is always thrown. Future versions of the
- SQL Server ODB runtime library may add support for result caching.</p>
-
- <h3><a name="21.5.2">21.5.2 Foreign Key Constraints</a></h3>
-
- <p>ODB assumes the standard SQL behavior which requires that foreign
- key constraints checking is deferred until the transaction is
- committed. The only behavior supported by SQL Server is to check
- such constraints immediately. As a result, by default, schemas
- generated by the ODB compiler for SQL Server have foreign key
- definitions commented out. They are retained only for documentation.</p>
-
- <p>You can override the default behavior and instruct the ODB
- compiler to generate non-deferrable foreign keys by specifying
- the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
- compiler option. Note, however, that in this case the order in
- which you persist, update, and erase objects within a transaction
- becomes important.</p>
-
- <h3><a name="21.5.3">21.5.3 Unique Constraint Violations</a></h3>
-
- <p>Due to the granularity of the ODBC error codes, it is impossible
- to distinguish between the duplicate primary key and other unique
- constraint violations. As a result, when making an object persistent,
- the SQL Server ODB runtime will translate all unique constraint violation
- errors to the <code>object_already_persistent</code> exception
- (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>).</p>
-
- <h3><a name="21.5.4">21.5.4 Multi-threaded Windows Applications</a></h3>
-
- <p>Multi-threaded Windows applications must use the
- <code>_beginthread()</code>/<code>_beginthreadex()</code> and
- <code>_endthread()</code>/<code>_endthreadex()</code> CRT functions
- instead of the <code>CreateThread()</code> and <code>EndThread()</code>
- Win32 functions to start and terminate threads. This is a limitation of
- the ODBC implementation on Windows.</p>
-
- <h3><a name="21.5.5">21.5.5 Affected Row Count and DDL Statements</a></h3>
-
- <p>SQL Server always returns zero as the number of affected rows
- for DDL statements. In particular, this means that the
- <code>database::execute()</code> (<a href="#3.12">Section 3.12,
- "Executing Native SQL Statements"</a>) function will always
- return zero for such statements.</p>
-
- <h3><a name="21.5.6">21.5.6 Long Data and Auto Object Ids, <code>ROWVERSION</code></a></h3>
-
- <p>SQL Server 2005 has a bug that causes it to fail on an <code>INSERT</code>
- or <code>UPDATE</code> statement with the <code>OUTPUT</code> clause
- (used to return automatically assigned object ids as well as
- <code>ROWVERSION</code> values) if one of the inserted columns
- is long data. The symptom of this bug in ODB is an exception thrown
- by the <code>database::persist()</code> or <code>database::update()</code>
- function when used on an object that contains long data and has an
- automatically assigned object id or uses <code>ROWVERSION</code>-based
- optimistic concurrency (<a href="#21.1.1">Section 21.1.1,
- "<code>ROWVERSION</code> Support"</a>). The error message reads "This
- operation conflicts with another pending operation on this transaction.
- The operation failed."</p>
-
- <p>For automatically assigned object ids ODB includes a workaround for
- this bug which uses a less efficient method to obtain id values for
- objects that contain long data. To enable this workaround you need
- to specify that the generated code will be used with SQL Server 2005
- or later by passing the <code>--mssql-server-version&nbsp;9.0</code>
- ODB compiler option.</p>
-
- <p>For <code>ROWVERSION</code>-based optimistic concurrency no workaround
- is currently provided. The ODB compiler will issue an error for
- objects that use <code>ROWVERSION</code> for optimistic concurrency
- and containing long data.</p>
-
- <h3><a name="21.5.7">21.5.7 Long Data and By-Value Accessors/Modifiers</a></h3>
-
- <p>As discussed in <a href="#14.4.5">Section 14.4.5,
- "<code>get</code>/<code>set</code>/<code>access</code>"</a>, by-value
- accessor and modifier expressions cannot be used with data members
- of long data types. The SQL Server ODB runtime uses streaming for
- reading/writing long data directly from/to data members. As a result,
- by-reference accessors and modifiers should be used for these data
- types.</p>
-
- <h3><a name="21.5.8">21.5.8 Bulk Update and <code>ROWVERSION</code></a></h3>
-
- <p>The bulk update operation (<a href="#15.3">Section 15.3, "Bulk Database
- Operations"</a>) is not yet supported for persistent classes that use
- <code>ROWVERSION</code>-based optimistic concurrency. For such classes
- the bulk <code>update()</code> function is not available. The bulk
- persist and erase support is still provided.</p>
-
- <h2><a name="21.6">21.6 SQL Server Index Definitions</a></h2>
-
- <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
- "Index Definition Pragmas"</a>) is used to define an SQL Server index,
- the <code>type</code> clause specifies the index type (for example,
- <code>UNIQUE</code>, <code>CLUSTERED</code>), the <code>method</code>
- clause is not used, and the <code>options</code> clause specifies
- additional index properties. The column options can be used to specify
- the sort order. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- std::string name_;
-
- #pragma db index \
- type("UNIQUE CLUSTERED") \
- member(name_, "DESC") \
- options("WITH(FILLFACTOR = 80)")
-};
- </pre>
-
- <h2><a name="21.7">21.7 SQL Server Stored Procedures</a></h2>
-
- <p>ODB native views (<a href="#10.6">Section 10.6, "Native Views"</a>)
- can be used to call SQL Server stored procedures. For example, assuming
- we are using the <code>person</code> class from <a href="#2">Chapter
- 2, "Hello World Example"</a> (and the corresponding <code>person</code>
- table), we can create a stored procedure that given the min and max
- ages returns some information about all the people in that range:</p>
-
- <pre class="sql">
-CREATE PROCEDURE dbo.person_range (
- @min_age SMALLINT,
- @max_age SMALLINT)
-AS
- SELECT age, first, last FROM person
- WHERE age >= @min_age AND age &lt;= @max_age;
- </pre>
-
- <p>Given the above stored procedure we can then define an ODB view
- that can be used to call it and retrieve its result:</p>
-
- <pre class="cxx">
-#pragma db view query("EXEC person_range (?)")
-struct person_range
-{
- unsigned short age;
- std::string first;
- std::string last;
-};
- </pre>
-
- <p>The following example shows how we can use the above view to
- print the list of people in a specific age range:</p>
-
- <pre class="cxx">
-typedef odb::query&lt;person_range> query;
-typedef odb::result&lt;person_range> result;
-
-transaction t (db.begin ());
-
-result r (
- db.query&lt;person_range> (
- query::_val (1) + "," + query::_val (18)));
-
-for (result::iterator i (r.begin ()); i != r.end (); ++i)
- cerr &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->age &lt;&lt; endl;
-
-t.commit ();
- </pre>
-
- <p>Note that as with all native views, the order and types of data members
- must match those of columns in the <code>SELECT</code> list inside
- the stored procedure.</p>
-
- <p>There are also a number of limitations when it comes to calling
- SQL Server stored procedures with ODB views. There is currently
- no support for output parameters, however, this is planned for
- a future version. In the meantime, to call a stored procedure
- that has output parameters we have to use a wrapper procedure
- that converts such parameters to a <code>SELECT</code>
- result. For example, given the following procedure that
- calculates the age range of the people in our database:</p>
-
- <pre class="sql">
-CREATE PROCEDURE dbo.person_age_range (
- @min_age SMALLINT = NULL OUTPUT,
- @max_age SMALLINT = NULL OUTPUT)
-AS
- SELECT @min_age = MIN(age), @max_age = MAX(max) FROM person;
- </pre>
-
- <p>We can create a wrapper procedure like this:</p>
-
- <pre class="sql">
-CREATE PROCEDURE dbo.person_age_range_odb
-AS
- DECLARE @min_age SMALLINT, @max_age SMALLINT;
- EXEC person_age_range @min_age OUTPUT, @max_age OUTPUT;
- SELECT @min_age, @max_age;
- </pre>
-
- <p>And a view like this:</p>
-
- <pre class="cxx">
-#pragma db view query("EXEC person_age_range_odb")
-struct person_age_range
-{
- unsigned short min_age;
- unsigned short max_age;
-};
- </pre>
-
- <p>Which we can then use to call the stored procedure:</p>
-
- <pre class="cxx">
-transaction t (db.begin ());
-
-person_age_range ar (db.query_value&lt;person_age_range> ());
-cerr &lt;&lt; ar.min_age &lt;&lt; " " &lt;&lt; ar.max_age &lt;&lt; endl;
-
-t.commit ();
- </pre>
-
- <p>In SQL Server, a stored procedure can produce multiple results.
- For example, if a stored procedure executes several
- <code>SELECT</code> statements, then the result of calling such
- a procedure consists of two row sets, one for each <code>SELECT</code>
- statement. Because such multiple row sets can contain varying number
- and type of columns, they cannot be all extracted into a
- single view. Consequently, these kind of stored procedures are
- currently not supported.</p>
-
- <p>A stored procedure may also produce no row sets at all. For
- example, a stored procedure that only executes DML statements
- would exhibit this behavior. To call such a procedure we use
- an empty view, for example:</p>
-
- <pre class="sql">
-CREATE PROCEDURE dbo.insert_person (
- @first VARCHAR(512),
- @last VARCHAR(512),
- @age SMALLINT)
-AS
- INSERT INTO person(first, last, age)
- VALUES(@first, @last, @age);
- </pre>
-
- <pre class="cxx">
-#pragma db view
-struct no_result {};
-
-transaction t (db.begin ());
-
-db.query_one&lt;no_result> (
- "EXEC insert_person" +
- query::_val ("John") + "," +
- query::_val ("Doe") + "," +
- query::_val (21));
-
-t.commit ();
- </pre>
-
- <p>Finally, an SQL Server stored procedure can also return an
- integer status code. Similar to output parameters, this code
- can only be observed by an ODB view if it is converted to a
- <code>SELECT</code> result. For more information on how to
- do this and for other examples of stored procedure calls,
- refer to the <code>mssql/stored-proc</code> test in the
- <code>odb-tests</code> package.</p>
-
- <!-- PART -->
-
-
- <hr class="page-break"/>
- <h1><a name="III">PART III&nbsp;&nbsp;
- <span style="font-weight: normal;">PROFILES</span></a></h1>
-
- <p>Part III covers the integration of ODB with popular C++ frameworks
- and libraries. It consists of the following chapters.</p>
-
- <table class="toc">
- <tr><th>22</th><td><a href="#22">Profiles Introduction</a></td></tr>
- <tr><th>23</th><td><a href="#23">Boost Profile</a></td></tr>
- <tr><th>24</th><td><a href="#24">Qt Profile</a></td></tr>
- </table>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="22">22 Profiles Introduction</a></h1>
-
- <p>ODB profiles are a generic mechanism for integrating ODB with
- widely-used C++ frameworks and libraries. A profile provides glue
- code which allows you to seamlessly persist various components, such
- as smart pointers, containers, and value types found in these
- frameworks or libraries. The code necessary to implement a profile
- is packaged into the so called profile library. For example, the
- Boost profile implementation is provided by the <code>libodb-boost</code>
- profile library.</p>
-
- <p>Besides linking the profile library to our application, it is also
- necessary to let the ODB compiler know which profiles we
- are using. This is accomplished with the <code>--profile</code>
- (or <code>-p</code> alias) option. For example:</p>
-
- <pre class="terminal">
-odb --profile boost ...
- </pre>
-
- <p>Some profiles, especially those covering frameworks or libraries that
- consist of multiple sub-libraries, provide sub-profiles that allow you
- to pick and choose which components you would like to use in your
- application. For example, the <code>boost</code> profile contains
- the <code>boost/data-time</code> sub-profile. If we are only
- interested in the <code>date_time</code> types, then we can
- pass <code>boost/data-time</code> instead of <code>boost</code>
- to the <code>--profile</code> option, for example:</p>
-
- <pre class="terminal">
-odb --profile boost/date-time ...
- </pre>
-
- <p>To summarize, you will need to perform the following steps in order
- to make use of a profile in your application:</p>
-
- <ol>
- <li>ODB compiler: if necessary, specify the path to the profile library
- headers (<code>-I</code> option).</li>
- <li>ODB compiler: specify the profile you would like to use with
- the <code>--profile</code> option.</li>
- <li>C++ compiler: if necessary, specify the path to the profile library
- headers (normally <code>-I</code> option).</li>
- <li>Linker: link the profile library to the application.</li>
- </ol>
-
- <p>The remaining chapters in this part of the manual describe the
- standard profiles provided by ODB.</p>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="23">23 Boost Profile</a></h1>
-
- <p>The ODB profile implementation for Boost is provided by the
- <code>libodb-boost</code> library and consists of multiple sub-profiles
- corresponding to the individual Boost libraries. To enable all the
- available Boost sub-profiles, pass <code>boost</code> as the profile
- name to the <code>--profile</code> ODB compiler option. Alternatively,
- you can enable only specific sub-profiles by passing individual
- sub-profile names to <code>--profile</code>. The following sections in
- this chapter discuss each Boost sub-profile in detail. The
- <code>boost</code> example in the <code>odb-examples</code>
- package shows how to enable and use the Boost profile.</p>
-
- <p>Some sub-profiles may throw exceptions to indicate error conditions,
- such as the inability to store a specific value in a particular database
- system. All such exceptions derive from the
- <code>odb::boost::exception</code> class which in turn derives from
- the root of the ODB exception hierarchy, class <code>odb::exception</code>
- (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>). The
- <code>odb::boost::exception</code> class is defined in the
- <code>&lt;odb/boost/exception.hxx></code> header file and has the
- same interface as <code>odb::exception</code>. Concrete exceptions
- that can be thrown by the Boost sub-profiles are described in the
- following sections.</p>
-
- <h2><a name="23.1">23.1 Smart Pointers Library</a></h2>
-
- <p>The <code>smart-ptr</code> sub-profile provides persistence
- support for a subset of smart pointers from the Boost
- <code>smart_ptr</code> library. To enable only this profile,
- pass <code>boost/smart-ptr</code> to the <code>--profile</code>
- ODB compiler option.</p>
-
- <p>The currently supported smart pointers are
- <code>boost::shared_ptr</code> and <code>boost::weak_ptr</code>. For
- more information on using smart pointers as pointers to objects and
- views, refer to <a href="#3.3">Section 3.3, "Object and View Pointers"</a>
- and <a href="#6">Chapter 6, "Relationships"</a>. For more information
- on using smart pointers as pointers to values, refer to
- <a href="#7.3">Section 7.3, "Pointers and <code>NULL</code> Value
- Semantics"</a>. When used as a pointer to a value, only
- <code>boost::shared_ptr</code> is supported. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db null
- boost::shared_ptr&lt;std::string> middle_name_;
-};
- </pre>
-
- <p>To provide finer grained control over object relationship loading,
- the <code>smart-ptr</code> sub-profile also provides the lazy
- counterparts for the above pointers: <code>odb::boost::lazy_shared_ptr</code> and
- <code>odb::boost::lazy_weak_ptr</code>. You will need to include the
- <code>&lt;odb/boost/lazy-ptr.hxx></code> header file to make the lazy
- variants available in your application. For a description of the lazy
- pointer interface and semantics refer to <a href="#6.4">Section 6.4,
- "Lazy Pointers"</a>. The following example shows how we can use these
- smart pointers to establish a relationship between persistent objects.</p>
-
- <pre class="cxx">
-class employee;
-
-#pragma db object
-class position
-{
- ...
-
- #pragma db inverse(position_)
- odb::boost::lazy_weak_ptr&lt;employee> employee_;
-};
-
-#pragma db object
-class employee
-{
- ...
-
- #pragma db not_null
- boost::shared_ptr&lt;position> position_;
-};
- </pre>
-
- <p>Besides providing persistence support for the above smart pointers,
- the <code>smart-ptr</code> sub-profile also changes the default
- pointer (<a href="#3.3">Section 3.3, "Object and View Pointers"</a>)
- to <code>boost::shared_ptr</code>. In particular, this means that
- database functions that return dynamically allocated objects and views
- will return them as <code>boost::shared_ptr</code> pointers. To override
- this behavior, add the <code>--default-pointer</code> option specifying
- the alternative pointer type after the <code>--profile</code> option.</p>
-
- <h2><a name="23.2">23.2 Unordered Containers Library</a></h2>
-
- <p>The <code>unordered</code> sub-profile provides persistence support for
- the containers from the Boost <code>unordered</code> library. To enable
- only this profile, pass <code>boost/unordered</code> to
- the <code>--profile</code> ODB compiler option.</p>
-
- <p>The supported containers are <code>boost::unordered_set</code>,
- <code>boost::unordered_map</code>, <code>boost::unordered_multiset</code>,
- and <code>boost::unordered_multimap</code>. For more information on using
- the set and multiset containers with ODB, refer to <a href="#5.2">Section
- 5.2, "Set and Multiset Containers"</a>. For more information on using the
- map and multimap containers with ODB, refer to <a href="#5.3"> Section
- 5.3, "Map and Multimap Containers"</a>. The following example shows how
- the <code>unordered_set</code> container may be used within a persistent
- object.</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
- boost::unordered_set&lt;std::string&gt; emails_;
-};
- </pre>
-
- <h2><a name="23.3">23.3 Multi-Index Container Library</a></h2>
-
- <p>The <code>multi-index</code> sub-profile provides persistence support for
- <code>boost::multi_index_container</code> from the Boost Multi-Index
- library. To enable only this profile, pass <code>boost/multi-index</code>
- to the <code>--profile</code> ODB compiler option. The following example
- shows how <code>multi_index_container</code> may be used within a
- persistent object.</p>
-
- <pre class="cxx">
-namespace mi = boost::multi_index;
-
-#pragma db object
-class person
-{
- ...
-
- typedef
- mi::multi_index_container&lt;
- std::string,
- mi::indexed_by&lt;
- mi::sequenced&lt;>,
- mi::ordered_unique&lt;mi::identity&lt;std::string> >
- >
- > emails;
-
- emails emails_;
-};
- </pre>
-
- <p>Note that a <code>multi_index_container</code> instantiation is
- stored differently in the database depending on whether it has
- any <code>sequenced</code> or <code>random_access</code> indexes.
- If it does, then it is treated as an ordered container
- (<a href="#5.1">Section 5.1, "Ordered Containers"</a>) with the
- first such index establishing the order. Otherwise, it is treated
- as a set container (<a href="#5.2">Section 5.2, "Set and Multiset
- Containers"</a>).</p>
-
- <p>Note also that there is a terminology clash between ODB and Boost
- Multi-Index. The ODB term <em>ordered container</em> translates
- to Multi-Index terms <em>sequenced index</em> and <em>random access
- index</em> while the ODB term <em>set container</em> translates
- to Multi-Index terms <em>ordered index</em> and <em>hashed
- index</em>.</p>
-
- <p>The <code>emails</code> container from the above example is stored
- as an ordered container. In contrast, the following <code>aliases</code>
- container is stored as a set.</p>
-
- <pre class="cxx">
-namespace mi = boost::multi_index;
-
-#pragma db value
-struct name
-{
- std::string first;
- std::string last;
-};
-
-bool operator&lt; (const name&amp;, const name&amp;);
-
-#pragma db object
-class person
-{
- ...
-
- typedef
- mi::multi_index_container&lt;
- name,
- mi::indexed_by&lt;
- mi::ordered_unique&lt;mi::identity&lt;name> >
- mi::ordered_non_unique&lt;
- mi::member&lt;name, std::string, &amp;name::first>
- >,
- mi::ordered_non_unique&lt;
- mi::member&lt;name, std::string, &amp;name::last>
- >
- >
- > aliases;
-
- aliases aliases_;
-};
- </pre>
-
- <h2><a name="23.4">23.4 Optional Library</a></h2>
-
- <p>The <code>optional</code> sub-profile provides persistence support for
- the <code>boost::optional</code> container from the Boost
- <code>optional</code> library. To enable only this profile, pass
- <code>boost/optional</code> to the <code>--profile</code> ODB compiler
- option.</p>
-
- <p>In a relational database <code>boost::optional</code> is mapped to
- a column that can have a <code>NULL</code> value. Similar to
- <code>odb::nullable</code> (<a href="#7.3">Section 7.3, "Pointers and
- <code>NULL</code> Value Semantics"</a>), it can be used to add the
- <code>NULL</code> semantics to existing C++ types. For example:</p>
-
- <pre class="cxx">
-#include &lt;boost/optional.hpp>
-
-#pragma db object
-class person
-{
- ...
-
- std::string first_; // TEXT NOT NULL
- boost::optional&lt;std::string> middle_; // TEXT NULL
- std::string last_; // TEXT NOT NULL
-};
- </pre>
-
- <p>Note also that similar to <code>odb::nullable</code>, when
- this profile is used, the <code>NULL</code> values are automatically
- enabled for data members of the <code>boost::optional</code> type.</p>
-
- <h2><a name="23.5">23.5 Date Time Library</a></h2>
-
- <p>The <code>date-time</code> sub-profile provides persistence support for a
- subset of types from the Boost <code>date_time</code> library. It is
- further subdivided into two sub-profiles, <code>gregorian</code>
- and <code>posix_time</code>. The <code>gregorian</code> sub-profile
- provides support for types from the <code>boost::gregorian</code>
- namespace, while the <code>posix-time</code> sub-profile provides support
- for types from the <code>boost::posix_time</code> namespace. To enable
- the entire <code>date-time</code> sub-profile, pass
- <code>boost/date-time</code> to the <code>--profile</code> ODB compiler
- option. To enable only the <code>gregorian</code> sub-profile, pass
- <code>boost/date-time/gregorian</code>, and to enable only the
- <code>posix-time</code> sub-profile, pass
- <code>boost/date-time/posix-time</code>.</p>
-
- <p>The only type that the <code>gregorian</code> sub-profile currently
- supports is <code>gregorian::date</code>. The types currently supported
- by the <code>posix-time</code> sub-profile are
- <code>posix_time::ptime</code> and
- <code>posix_time::time_duration</code>. The manner in which these types
- are persisted is database system dependent and is discussed in the
- sub-sections that follow. The example below shows how
- <code>gregorian::date</code> may be used within a persistent object.</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
- boost::gregorian::date date_of_birth_;
-};
- </pre>
-
- <p>Concrete exceptions that can be thrown by the <code>date-time</code>
- sub-profile implementation are presented below.</p>
-
-
- <pre class="cxx">
-namespace odb
-{
- namespace boost
- {
- namespace date_time
- {
- struct special_value: odb::boost::exception
- {
- virtual const char*
- what () const throw ();
- };
-
- struct value_out_of_range: odb::boost::exception
- {
- virtual const char*
- what () const throw ();
- };
- }
- }
-}
- </pre>
-
- <p>You will need to include the
- <code>&lt;odb/boost/date-time/exceptions.hxx&gt;</code> header file to
- make these exceptions available in your application.</p>
-
- <p>The <code>special_value</code> exception is thrown if an attempt is made
- to store a Boost date-time special value that cannot be represented in
- the target database. The <code>value_out_of_range</code> exception is
- thrown if an attempt is made to store a date-time value that is out of
- the target database range. The specific conditions under which these
- exceptions are thrown are database system dependent and are discussed in
- more detail in the following sub-sections.</p>
-
- <h3><a name="23.5.1">23.5.1 MySQL Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Boost <code>date_time</code> types and the MySQL database
- types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost <code>date_time</code> Type</th>
- <th>MySQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>gregorian::date</code></td>
- <td><code>DATE</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::ptime</code></td>
- <td><code>DATETIME</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::time_duration</code></td>
- <td><code>TIME</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
- as a <code>NULL</code> value in a MySQL database.</p>
-
- <p>The <code>posix-time</code> sub-profile implementation also provides
- support for mapping <code>posix_time::ptime</code> to the
- <code>TIMESTAMP</code> MySQL type. However, this mapping has to be
- explicitly requested using the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), as shown in
- the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
- #pragma db type("TIMESTAMP") not_null
- boost::posix_time::ptime updated_;
-};
- </pre>
-
- <p>Starting with MySQL version 5.6.4 it is possible to store fractional
- seconds up to microsecond precision in <code>TIME</code>,
- <code>DATETIME</code>, and <code>TIMESTAMP</code> columns. However,
- to enable sub-second precision, the corresponding type with the
- desired precision has to be specified explicitly, as shown in the
- following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
- #pragma db type("DATETIME(6)") // Microsecond precision.
- boost::posix_time::ptime updated_;
-};
- </pre>
-
- <p>Alternatively, you can enable sub-second precision on the per-type
- basis, for example:</p>
-
- <pre class="cxx">
-#pragma db value(boost::posix_time::ptime) type("DATETIME(6)")
-
-#pragma db object
-class person
-{
- ...
- boost::posix_time::ptime created_; // Microsecond precision.
- boost::posix_time::ptime updated_; // Microsecond precision.
-};
- </pre>
-
- <p>Some valid Boost date-time values cannot be stored in a MySQL database.
- An attempt to persist any Boost date-time special value other than
- <code>date_time::not_a_date_time</code> will result in the
- <code>special_value</code> exception. An attempt to persist a Boost
- date-time value that is out of the MySQL type range will result in
- the <code>out_of_range</code> exception. Refer to the MySQL
- documentation for more information on the MySQL data type ranges.</p>
-
- <h3><a name="23.5.2">23.5.2 SQLite Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Boost <code>date_time</code> types and the SQLite database
- types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost <code>date_time</code> Type</th>
- <th>SQLite Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>gregorian::date</code></td>
- <td><code>TEXT</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::ptime</code></td>
- <td><code>TEXT</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::time_duration</code></td>
- <td><code>TEXT</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
- as a <code>NULL</code> value in an SQLite database.</p>
-
- <p>The <code>date-time</code> sub-profile implementation also provides
- support for mapping <code>gregorian::date</code> and
- <code>posix_time::ptime</code> to the <code>INTEGER</code> SQLite type,
- with the integer value representing the UNIX time. Similarly, an
- alternative mapping for <code>posix_time::time_duration</code> to the
- <code>INTEGER</code> type represents the duration as a number of
- seconds. These mappings have to be explicitly requested using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>), as shown in the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
- #pragma db type("INTEGER")
- boost::gregorian::date born_;
-};
- </pre>
-
- <!--
-
- <p>The Boost UNIX time interface does not support 64 bit time arithmetic.
- As a result, the UNIX time representations of <code>gregorian::date</code>
- and <code>posix_time::ptime</code> are restricted to the 32 bit range.
- The minimum and maximum date representable by
- <code>gregorian::date</code> is 1901-12-14 and 2038-01-19 respectively,
- while the minimum and maximum date-time representable by
- <code>posix_time::ptime</code> is 1901-12-13&nbsp;20:45:54 GMT and
- 2038-01-19&nbsp;03:14:07&nbsp;GMT respectively. Persisting and loading
- of values outside of these ranges will result in undefined behavior.</p>
-
- -->
-
- <p>Some valid Boost date-time values cannot be stored in an SQLite database.
- An attempt to persist any Boost date-time special value other than
- <code>date_time::not_a_date_time</code> will result in the
- <code>special_value</code> exception. An attempt to persist a negative
- <code>posix_time::time_duration</code> value as SQLite <code>TEXT</code>
- will result in the <code>out_of_range</code> exception.</p>
-
-
- <h3><a name="23.5.3">23.5.3 PostgreSQL Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Boost <code>date_time</code> types and the PostgreSQL database
- types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost <code>date_time</code> Type</th>
- <th>PostgreSQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>gregorian::date</code></td>
- <td><code>DATE</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::ptime</code></td>
- <td><code>TIMESTAMP</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::time_duration</code></td>
- <td><code>TIME</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
- as a <code>NULL</code> value in a PostgreSQL database.
- <code>posix_time::ptime</code> values representing the special values
- <code>date_time::pos_infin</code> and <code>date_time::neg_infin</code>
- are stored as the special PostgreSQL TIMESTAMP values
- <code>infinity</code> and <code>-infinity</code>, respectively.</p>
-
- <p>Some valid Boost date-time values cannot be stored in a PostgreSQL
- database. The PostgreSQL TIME type represents a clock time, and can
- therefore only store positive durations with a total length of time less
- than 24 hours. An attempt to persist a
- <code>posix_time::time_duration</code> value outside of this range will
- result in the <code>value_out_of_range</code> exception. An attempt to
- persist a <code>posix_time::time_duration</code> value representing any
- special value other than <code>date_time::not_a_date_time</code> will
- result in the <code>special_value</code> exception.</p>
-
-
- <h3><a name="23.5.4">23.5.4 Oracle Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Boost <code>date_time</code> types and the Oracle database
- types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost <code>date_time</code> Type</th>
- <th>Oracle Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>gregorian::date</code></td>
- <td><code>DATE</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::ptime</code></td>
- <td><code>TIMESTAMP</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::time_duration</code></td>
- <td><code>INTERVAL DAY TO SECOND</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
- as a <code>NULL</code> value in an Oracle database.</p>
-
- <p>The <code>date-time</code> sub-profile implementation also provides
- support for mapping <code>posix_time::ptime</code> to the
- <code>DATE</code> Oracle type with fractional seconds that may be
- stored in a <code>ptime</code> instance being ignored. This
- alternative mapping has to be explicitly requested using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>), as shown in the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
- #pragma db type("DATE")
- boost::posix_time::ptime updated_;
-};
- </pre>
-
- <p>Some valid Boost date-time values cannot be stored in an Oracle database.
- An attempt to persist a <code>gregorian::date</code>,
- <code>posix_time::ptime</code>, or
- <code>posix_time::time_duration</code> value representing any special
- value other than <code>date_time::not_a_date_time</code> will result in
- the <code>special_value</code> exception.</p>
-
-
- <h3><a name="23.5.5">23.5.5 SQL Server Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Boost <code>date_time</code> types and the SQL Server database
- types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost <code>date_time</code> Type</th>
- <th>SQL Server Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>gregorian::date</code></td>
- <td><code>DATE</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::ptime</code></td>
- <td><code>DATETIME2</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>posix_time::time_duration</code></td>
- <td><code>TIME</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
- as a <code>NULL</code> value in an SQL Server database.</p>
-
- <p>Note that the <code>DATE</code>, <code>TIME</code>, and
- <code>DATETIME2</code> types are only available in SQL Server 2008 and
- later. SQL Server 2005 only supports the <code>DATETIME</code> and
- <code>SMALLDATETIME</code> date-time types. The new types are
- also unavailable when connecting to an SQL Server 2008 or
- later with the SQL Server 2005 Native Client ODBC driver.</p>
-
- <p>The <code>date-time</code> sub-profile implementation provides
- support for mapping <code>posix_time::ptime</code> to the
- <code>DATETIME</code> and <code>SMALLDATETIME</code> types,
- however, this mapping has to be explicitly requested using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>), as shown in the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
- #pragma db type("DATETIME")
- boost::posix_time::ptime updated_;
-};
- </pre>
-
- <p>Some valid Boost date-time values cannot be stored in an SQL Server
- database. An attempt to persist a <code>gregorian::date</code>,
- <code>posix_time::ptime</code>, or <code>posix_time::time_duration</code>
- value representing any special value other than
- <code>date_time::not_a_date_time</code> will result in the
- <code>special_value</code> exception. The range of the <code>TIME</code>
- type in SQL server is from <code>00:00:00.0000000</code> to
- <code>23:59:59.9999999</code>. An attempt to persist a
- <code>posix_time::time_duration</code> value out of this range will
- result in the <code>value_out_of_range</code> exception.</p>
-
- <h2><a name="23.6">23.6 Uuid Library</a></h2>
-
- <p>The <code>uuid</code> sub-profile provides persistence support for the
- <code>uuid</code> type from the Boost <code>uuid</code> library. To
- enable only this profile, pass <code>boost/uuid</code> to the
- <code>--profile</code> ODB compiler option.</p>
-
- <p>The manner in which these types are persisted is database system
- dependent and is discussed in the sub-sections that follow. By
- default a data member of the <code>uuid</code> type is mapped to a
- database column with <code>NULL</code> enabled and nil <code>uuid</code>
- instances are stored as a <code>NULL</code> value. However, you can
- change this behavior by declaring the data member <code>NOT NULL</code>
- with the <code>not_null</code> pragma (<a href="#14.4.6">Section
- 14.4.6, "<code>null</code>/<code>not_null</code>"</a>). In this
- case, or if the data member is an object id, the implementation
- will store nil <code>uuid</code> instances as zero UUID values
- (<code>{00000000-0000-0000-0000-000000000000}</code>). For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- boost::uuids::uuid x_; // Nil values stored as NULL.
-
- #pragma db not_null
- boost::uuids::uuid y_; // Nil values stored as zero.
-};
- </pre>
-
- <h3><a name="23.6.1">23.6.1 MySQL Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the Boost
- <code>uuid</code> type and the MySQL database type.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost Type</th>
- <th>MySQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>boost::uuids::uuid</code></td>
- <td><code>BINARY(16)</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <h3><a name="23.6.2">23.6.2 SQLite Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the Boost
- <code>uuid</code> type and the SQLite database type.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost Type</th>
- <th>SQLite Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>boost::uuids::uuid</code></td>
- <td><code>BLOB</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <h3><a name="23.6.3">23.6.3 PostgreSQL Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the Boost
- <code>uuid</code> type and the PostgreSQL database type.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost Type</th>
- <th>PostgreSQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>boost::uuids::uuid</code></td>
- <td><code>UUID</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <h3><a name="23.6.4">23.6.4 Oracle Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the Boost
- <code>uuid</code> type and the Oracle database type.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost Type</th>
- <th>Oracle Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>boost::uuids::uuid</code></td>
- <td><code>RAW(16)</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <h3><a name="23.6.5">23.6.5 SQL Server Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the Boost
- <code>uuid</code> type and the SQL Server database type.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Boost Type</th>
- <th>SQL Server Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>boost::uuids::uuid</code></td>
- <td><code>UNIQUEIDENTIFIER</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
-
- <!-- CHAPTER -->
-
-
- <hr class="page-break"/>
- <h1><a name="24">24 Qt Profile</a></h1>
-
- <p>The ODB profile implementation for Qt is provided by the
- <code>libodb-qt</code> library. Both Qt4 and Qt5 as well
- as C++98/03 and C++11 are supported.</p>
-
- <p>The Qt profile consists of multiple sub-profiles
- corresponding to the common type groups within Qt. Currently,
- only types from the <code>QtCore</code> module are supported. To
- enable all the available Qt sub-profiles, pass <code>qt</code> as the
- profile name to the <code>--profile</code> ODB compiler option.
- Alternatively, you can enable only specific sub-profiles by passing
- individual sub-profile names to <code>--profile</code>. The following
- sections in this chapter discuss each Qt sub-profile in detail. The
- <code>qt</code> example in the <code>odb-examples</code>
- package shows how to enable and use the Qt profile.</p>
-
- <p>Some sub-profiles may throw exceptions to indicate error conditions,
- such as the inability to store a specific value in a particular database
- system. All such exceptions derive from the
- <code>odb::qt::exception</code> class which in turn derives from
- the root of the ODB exception hierarchy, class <code>odb::exception</code>
- (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>). The
- <code>odb::qt::exception</code> class is defined in the
- <code>&lt;odb/qt/exception.hxx></code> header file and has the
- same interface as <code>odb::exception</code>. Concrete exceptions
- that can be thrown by the Qt sub-profiles are described in the
- following sections.</p>
-
- <h2><a name="24.1">24.1 Basic Types Library</a></h2>
-
- <p>The <code>basic</code> sub-profile provides persistence support for basic
- types defined by Qt. To enable only this profile, pass
- <code>qt/basic</code> to the <code>--profile</code> ODB compiler
- option.</p>
-
- <p>The currently supported basic types are <code>QString</code>,
- <code>QByteArray</code>, and <code>QUuid</code>. The manner in
- which these types are persisted is database system dependent
- and is discussed in the sub-sections that follow. The example
- below shows how <code>QString</code> may be used within a
- persistent object.</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
- QString name_;
-};
- </pre>
-
- <p>By default a data member of the <code>QUuid</code> type is mapped to a
- database column with <code>NULL</code> enabled and null <code>QUuid</code>
- instances are stored as a <code>NULL</code> value. However, you can
- change this behavior by declaring the data member <code>NOT NULL</code>
- with the <code>not_null</code> pragma (<a href="#14.4.6">Section
- 14.4.6, "<code>null</code>/<code>not_null</code>"</a>). In this
- case, or if the data member is an object id, the implementation
- will store null <code>QUuid</code> instances as zero UUID values
- (<code>{00000000-0000-0000-0000-000000000000}</code>). For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class object
-{
- ...
-
- QUuid x_; // Null values stored as NULL.
-
- #pragma db not_null
- QUuid y_; // Null values stored as zero.
-};
- </pre>
-
- <h3><a name="24.1.1">24.1.1 MySQL Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported basic Qt types and the MySQL database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Type</th>
- <th>MySQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QString</code></td>
- <td><code>TEXT/VARCHAR(128)</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QByteArray</code></td>
- <td><code>BLOB</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QUuid</code></td>
- <td><code>BINARY(16)</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QString</code> and <code>QByteArray</code>
- types are stored as a <code>NULL</code> value if their
- <code>isNull()</code> member function returns <code>true</code>.</p>
-
- <p>Note also that the <code>QString</code> type is mapped
- differently depending on whether a member of this type
- is an object id or not. If the member is an object id,
- then for this member <code>QString</code> is mapped
- to the <code>VARCHAR(128)</code> MySQL type. Otherwise,
- it is mapped to <code>TEXT</code>.</p>
-
- <p>The <code>basic</code> sub-profile also provides support
- for mapping <code>QString</code> to the <code>CHAR</code>,
- <code>NCHAR</code>, and <code>NVARCHAR</code> MySQL types.
- However, these alternative mappings have to be explicitly
- requested using the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "type"</a>), as shown in
- the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
-
- #pragma db type("CHAR(2)") not_null
- QString licenseState_;
-};
- </pre>
-
-
- <h3><a name="24.1.2">24.1.2 SQLite Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported basic Qt types and the SQLite database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Type</th>
- <th>SQLite Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QString</code></td>
- <td><code>TEXT</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QByteArray</code></td>
- <td><code>BLOB</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QUuid</code></td>
- <td><code>BLOB</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QString</code> and <code>QByteArray</code> types
- are stored as a <code>NULL</code> value if their <code>isNull()</code>
- member function returns <code>true</code>.</p>
-
- <h3><a name="24.1.3">24.1.3 PostgreSQL Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported basic Qt types and the PostgreSQL database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Type</th>
- <th>PostgreSQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QString</code></td>
- <td><code>TEXT</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QByteArray</code></td>
- <td><code>BYTEA</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QUuid</code></td>
- <td><code>UUID</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QString</code> and <code>QByteArray</code> types
- are stored as a <code>NULL</code> value if their <code>isNull()</code>
- member function returns <code>true</code>.</p>
-
- <p>The <code>basic</code> sub-profile also provides support
- for mapping <code>QString</code> to the <code>CHAR</code>
- and <code>VARCHAR</code> PostgreSQL types.
- However, these alternative mappings have to be explicitly
- requested using the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "type"</a>), as shown in
- the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
-
- #pragma db type("CHAR(2)") not_null
- QString licenseState_;
-};
- </pre>
-
- <h3><a name="24.1.4">24.1.4 Oracle Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported basic Qt types and the Oracle database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Type</th>
- <th>Oracle Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QString</code></td>
- <td><code>VARCHAR2(512)</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QByteArray</code></td>
- <td><code>BLOB</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QUuid</code></td>
- <td><code>RAW(16)</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QString</code> and <code>QByteArray</code> types
- are stored as a <code>NULL</code> value if their <code>isNull()</code>
- member function returns <code>true</code>.</p>
-
- <p>The <code>basic</code> sub-profile also provides support
- for mapping <code>QString</code> to the <code>CHAR</code>,
- <code>NCHAR</code>, <code>NVARCHAR</code>, <code>CLOB</code>, and
- <code>NCLOB</code> Oracle types, and for mapping <code>QByteArray</code>
- to the <code>RAW</code> Oracle type. However, these alternative
- mappings have to be explicitly requested using the <code>db&nbsp;type</code>
- pragma (<a href="#14.4.3">Section 14.4.3, "type"</a>), as shown in the
- following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
-
- #pragma db type("CLOB") not_null
- QString firstName_;
-
- #pragma db type("RAW(16)") null
- QByteArray uuid_;
-};
- </pre>
-
- <h3><a name="24.1.5">24.1.5 SQL Server Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported basic Qt types and the SQL Server database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Type</th>
- <th>SQL Server Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QString</code></td>
- <td><code>VARCHAR(512)/VARCHAR(256)</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QByteArray</code></td>
- <td><code>VARBINARY(max)</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QUuid</code></td>
- <td><code>UNIQUEIDENTIFIER</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QString</code> and <code>QByteArray</code> types
- are stored as a <code>NULL</code> value if their <code>isNull()</code>
- member function returns <code>true</code>.</p>
-
- <p>Note also that the <code>QString</code> type is mapped
- differently depending on whether a member of this type
- is an object id or not. If the member is an object id,
- then for this member <code>QString</code> is mapped
- to the <code>VARCHAR(256)</code> SQL Server type. Otherwise,
- it is mapped to <code>VARCHAR(512)</code>.</p>
-
- <p>The <code>basic</code> sub-profile also provides support
- for mapping <code>QString</code> to the <code>CHAR</code>,
- <code>NCHAR</code>, <code>NVARCHAR</code>, <code>TEXT</code>, and
- <code>NTEXT</code> SQL Server types, and for mapping
- <code>QByteArray</code> to the <code>BINARY</code> and
- <code>IMAGE</code> SQL Server types. However, these alternative
- mappings have to be explicitly requested using the <code>db&nbsp;type</code>
- pragma (<a href="#14.4.3">Section 14.4.3, "type"</a>), as shown in the
- following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
-
- #pragma db type("NVARCHAR(256)") not_null
- QString firstName_;
-
- #pragma db type("BINARY(16)") null
- QByteArray uuid_;
-};
- </pre>
-
- <h2><a name="24.2">24.2 Smart Pointers Library</a></h2>
-
- <p>The <code>smart-ptr</code> sub-profile provides persistence support the
- Qt smart pointers. To enable only this profile, pass
- <code>qt/smart-ptr</code> to the <code>--profile</code> ODB compiler
- option.</p>
-
- <p>The currently supported smart pointers are
- <code>QSharedPointer</code> and <code>QWeakPointer</code>.
- For more information on using smart pointers as pointers to objects
- and views, refer to <a href="#3.3">Section 3.3, "Object and View
- Pointers"</a> and <a href="#6">Chapter 6, "Relationships"</a>. For
- more information on using smart pointers as pointers to values, refer
- to <a href="#7.3">Section 7.3, "Pointers and <code>NULL</code> Value
- Semantics"</a>. When used as a pointer to a value, only
- <code>QSharedPointer</code> is supported. For example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
-
- #pragma db null
- QSharedPointer&lt;QString> middle_name_;
-};
- </pre>
-
- <p>To provide finer grained control over object relationship loading,
- the <code>smart-ptr</code> sub-profile also provides the lazy
- counterparts for the above pointers: <code>QLazySharedPointer</code>
- and <code>QLazyWeakPointer</code>. You will need to include the
- <code>&lt;odb/qt/lazy-ptr.hxx></code> header file to make the lazy
- variants available in your application. For a description of the lazy
- pointer interface and semantics refer to <a href="#6.4">Section 6.4,
- "Lazy Pointers"</a>. The following example shows how we can use these
- smart pointers to establish a relationship between persistent objects.</p>
-
- <pre class="cxx">
-class Employee;
-
-#pragma db object
-class Position
-{
- ...
-
- #pragma db inverse(position_)
- QLazyWeakPointer&lt;Employee> employee_;
-};
-
-#pragma db object
-class Employee
-{
- ...
-
- #pragma db not_null
- QSharedPointer&lt;Position> position_;
-};
- </pre>
-
- <p>Besides providing persistence support for the above smart pointers,
- the <code>smart-ptr</code> sub-profile also changes the default
- pointer (<a href="#3.3">Section 3.3, "Object and View Pointers"</a>)
- to <code>QSharedPointer</code>. In particular, this means that
- database functions that return dynamically allocated objects and views
- will return them as <code>QSharedPointer</code> pointers. To override
- this behavior, add the <code>--default-pointer</code> option specifying
- the alternative pointer type after the <code>--profile</code> option.</p>
-
- <h2><a name="24.3">24.3 Containers Library</a></h2>
-
- <p>The <code>containers</code> sub-profile provides persistence support for
- Qt containers. To enable only this profile, pass
- <code>qt/containers</code> to the <code>--profile</code> ODB compiler
- option.</p>
-
- <p>The currently supported ordered containers are <code>QVector</code>,
- <code>QList</code>, and <code>QLinkedList</code>. Supported map
- containers are <code>QMap</code>, <code>QMultiMap</code>,
- <code>QHash</code>, and <code>QMultiHash</code>. The supported set
- container is <code>QSet</code>. For more information on using
- containers with ODB, refer to <a href="#5">Chapter 5, "Containers"</a>.
- The following example shows how the <code>QSet</code> container may
- be used within a persistent object.</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
- QSet&lt;QString> emails_;
-};
- </pre>
-
- <p>The <code>containers</code> sub-profile also provide a change-tracking
- equivalent for <code>QList</code> (<a href="#24.3.1">Section 24.3.1,
- "Change-Tracking <code>QList</code>"</a>) with support for other Qt
- container equivalents planned for future releases. For general information
- on change-tracking containers refer to <a href="#5.4">Section 5.4,
- "Change-Tracking Containers"</a>.</p>
-
- <h3><a name="24.3.1">24.3.1 Change-Tracking <code>QList</code></a></h3>
-
- <p>Class template <code>QOdbList</code>, defined in
- <code>&lt;odb/qt/list.hxx></code>, is a change-tracking
- equivalent for <code>QList</code>. It
- is implemented in terms of <code>QList</code> and is
- implicit-convertible to and implicit-constructible from
- <code>const QList&amp;</code>. In particular, this
- means that we can use <code>QOdbList</code> instance
- anywhere <code>const QList&amp;</code> is
- expected. In addition, <code>QOdbList</code> constant
- iterator (<code>const_iterator</code>) is the same type as
- that of <code>QList</code>.</p>
-
- <p><code>QOdbList</code> incurs 2-bit per element overhead
- in order to store the change state. It cannot
- be stored unordered in the database (<a href="#14.4.19">Section
- 14.4.19 "<code>unordered</code>"</a>) but can be used as an inverse
- side of a relationship (<a href="#6.2">6.2 "Bidirectional
- Relationships"</a>). In this case, no change tracking is performed
- since no state for such a container is stored in the database.</p>
-
- <p>The number of database operations required to update the state
- of <code>QOdbList</code> corresponds well to the complexity
- of <code>QList</code> functions, except for
- <code>prepend()</code>/<code>push_front()</code>. In particular, adding
- or removing an element from the back of the list (for example,
- with <code>append()</code>/<code>push_back()</code> and
- <code>removeLast()</code>/<code>pop_back()</code>),
- requires only a single database statement execution. In contrast,
- inserting or erasing an element at the beginning or in the middle
- of the list will require a database statement for every element that
- follows it.</p>
-
- <p><code>QOdbList</code> replicates most of the <code>QList</code>
- interface as defined in both Qt4 and Qt5 and includes support for
- C++11. However, functions and operators that provide direct write
- access to the elements had to be altered or disabled in order to
- support change tracking. Additional functions used to interface with
- <code>QList</code> and to control the change tracking state
- were also added. The following listing summarizes the differences
- between the <code>QOdbList</code> and <code>QList</code>
- interfaces. Any <code>QList</code> function or operator
- not mentioned in this listing has exactly the same signature
- and semantics in <code>QOdbList</code>. Functions and
- operators that were disabled are shown as commented out and
- are followed by functions/operators that replace them.</p>
-
- <pre class="cxx">
-template &lt;typename T>
-class QOdbList
-{
- ...
-
- // Element access.
- //
-
- //T&amp; operator[] (int);
- T&amp; modify (int);
-
- //T&amp; first();
- T&amp; modifyFirst();
-
- //T&amp; last();
- T&amp; modifyLast();
-
- //T&amp; front();
- T&amp; modify_front();
-
- //T&amp; back();
- T&amp; modify_back();
-
- // Iterators.
- //
- typedef typename QList&lt;T>::const_iterator const_iterator;
-
- class iterator
- {
- ...
-
- // Element Access.
- //
-
- //reference operator* () const;
- const_reference operator* () const;
- reference modify () const;
-
- //pointer operator-> () const;
- const_pointer operator-> () const;
-
- //reference operator[] (difference_type);
- const_reference operator[] (difference_type);
- reference modify (difference_type) const;
-
- // Interfacing with QList::iterator.
- //
- typename QList&lt;T>::iterator base () const;
- };
-
- // Return QList iterators. The begin() functions mark all
- // the elements as modified.
- //
- typename QList&lt;T>::iterator mbegin ();
- typename QList&lt;T>::iterator modifyBegin ();
- typename QList&lt;T>::iterator mend ();
- typename QList&lt;T>::iterator modifyEnd ();
-
- // Interfacing with QList.
- //
- QOdbList (const QList&lt;T>&amp;);
- QOdbList (QList&lt;T>&amp;&amp;); // C++11 only.
-
- QOdbList&amp; operator= (const QList&lt;T>&amp;);
- QOdbList&amp; operator= (QList&lt;T>&amp;&amp;);
-
- operator const QList&lt;T>&amp; () const;
- QList&lt;T>&amp; base ();
- const QList&lt;T>&amp; base () const;
-
- // Change tracking.
- //
- bool _tracking () const;
- void _start () const;
- void _stop () const;
- void _arm (transaction&amp;) const;
-};
- </pre>
-
- <p>The following example highlights some of the differences between
- the two interfaces. <code>QList</code> versions are commented
- out.</p>
-
- <pre class="cxx">
-#include &lt;QtCore/QList>
-#include &lt;odb/qt/list.hxx>
-
-void f (const QList&lt;int>&amp;);
-
-QOdbList&lt;int> l ({1, 2, 3});
-
-f (l); // Ok, implicit conversion.
-
-if (l[1] == 2) // Ok, const access.
- //l[1]++;
- l.modify (1)++;
-
-//l.last () = 4;
-l.modifyLast () = 4;
-
-for (auto i (l.begin ()); i != l.end (); ++i)
-{
- if (*i != 0) // Ok, const access.
- //*i += 10;
- i.modify () += 10;
-}
-
-qSort (l.modifyBegin (), l.modifyEnd ());
- </pre>
-
- <p>Note also the subtle difference between copy/move construction
- and copy/move assignment of <code>QOdbList</code> instances.
- While copy/move constructor will copy/move both the elements as
- well as their change state, in contrast, assignment is tracked
- as any other change to the vector content.</p>
-
- <p>The <code>QListIterator</code> and <code>QMutableListIterator</code>
- equivalents are also provided. These are <code>QOdbListIterator</code>
- and <code>QMutableOdbListIterator</code> and are defined in
- <code>&lt;odb/qt/list-iterator.hxx></code> and
- <code>&lt;odb/qt/mutable-list-iterator.hxx></code>, respectively.</p>
-
- <p><code>QOdbListIterator</code> has exactly the same interface and
- semantics as <code>QListIterator</code>. In fact, we can use
- <code>QListIterator</code> to iterate over a <code>QOdbList</code>
- instance.</p>
-
- <p><code>QMutableOdbListIterator</code> also has exactly the same
- interface as <code>QMutableListIterator</code>. Note, however,
- that any element that such an iterator passes over with the
- call to <code>next()</code> is marked as modified.</p>
-
- <h2><a name="24.4">24.4 Date Time Library</a></h2>
-
- <p>The <code>date-time</code> sub-profile provides persistence support for
- the Qt date-time types. To enable only this profile, pass
- <code>qt/date-time</code> to the <code>--profile</code> ODB compiler
- option.</p>
-
- <p>The currently supported date-time types are <code>QDate</code>,
- <code>QTime</code>, and <code>QDateTime</code>. The manner in which
- these types are persisted is database system dependent and is
- discussed in the sub-sections that follow. The example below shows how
- <code>QDate</code> may be used within a persistent object.</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
- QDate dateOfBirth_;
-};
- </pre>
-
- <p>The single concrete exception that can be thrown by the
- <code>date-time</code> sub-profile implementation is presented below.</p>
-
-
- <pre class="cxx">
-namespace odb
-{
- namespace qt
- {
- namespace date_time
- {
- struct value_out_of_range: odb::qt::exception
- {
- virtual const char*
- what () const throw ();
- };
- }
- }
-}
- </pre>
-
- <p>You will need to include the
- <code>&lt;odb/qt/date-time/exceptions.hxx&gt;</code> header file to
- make this exception available in your application.</p>
-
- <p>The <code>value_out_of_range</code> exception is thrown if an attempt
- is made to store a date-time value that is out of the target database
- range. The specific conditions under which it is thrown is database
- system dependent and is discussed in more detail in the
- following sub-sections.</p>
-
- <h3><a name="24.4.1">24.4.1 MySQL Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Qt date-time types and the MySQL database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Date Time Type</th>
- <th>MySQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QDate</code></td>
- <td><code>DATE</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QTime</code></td>
- <td><code>TIME</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QDateTime</code></td>
- <td><code>DATETIME</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
- <code>QDateTime</code> types are stored as a <code>NULL</code> value
- if their <code>isNull()</code> member function returns true.</p>
-
- <p>The <code>date-time</code> sub-profile implementation also provides
- support for mapping <code>QDateTime</code> to the <code>TIMESTAMP</code>
- MySQL type. However, this mapping has to be explicitly requested using
- the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), as shown in
- the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
- #pragma db type("TIMESTAMP") not_null
- QDateTime updated_;
-};
- </pre>
-
- <p>Starting with MySQL version 5.6.4 it is possible to store fractional
- seconds up to microsecond precision in <code>TIME</code>,
- <code>DATETIME</code>, and <code>TIMESTAMP</code> columns. However,
- to enable sub-second precision, the corresponding type with the
- desired precision has to be specified explicitly, as shown in the
- following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
- #pragma db type("DATETIME(3)") // Millisecond precision.
- QDateTime updated_;
-};
- </pre>
-
- <p>Alternatively, you can enable sub-second precision on the per-type
- basis, for example:</p>
-
- <pre class="cxx">
-#pragma db value(QDateTime) type("DATETIME(3)")
-
-#pragma db object
-class Person
-{
- ...
- QDateTime created_; // Millisecond precision.
- QDateTime updated_; // Millisecond precision.
-};
- </pre>
-
- <p>Some valid Qt date-time values cannot be stored in a MySQL database. An
- attempt to persist a Qt date-time value that is out of the MySQL type
- range will result in the <code>out_of_range</code> exception. Refer to
- the MySQL documentation for more information on the MySQL data type
- ranges.</p>
-
- <h3><a name="24.4.2">24.4.2 SQLite Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Qt date-time types and the SQLite database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Date Time Type</th>
- <th>SQLite Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QDate</code></td>
- <td><code>TEXT</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QTime</code></td>
- <td><code>TEXT</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QDateTime</code></td>
- <td><code>TEXT</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
- <code>QDateTime</code> types are stored as a <code>NULL</code> value
- if their <code>isNull()</code> member function returns true.</p>
-
- <p>The <code>date-time</code> sub-profile implementation also provides
- support for mapping <code>QDate</code> and <code>QDateTime</code> to the
- SQLite <code>INTEGER</code> type, with the integer value representing the
- UNIX time. Similarly, an alternative mapping for <code>QTime</code> to
- the <code>INTEGER</code> type represents a clock time as the number of
- seconds since midnight. These mappings have to be explicitly requested
- using the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), as shown
- in the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class Person
-{
- ...
- #pragma db type("INTEGER")
- QDate born_;
-};
- </pre>
-
- <p>Some valid Qt date-time values cannot be stored in an SQLite database.
- An attempt to persist any Qt date-time value representing a negative UNIX
- time (any point in time prior to the 1970-01-01&nbsp;00:00:00 UNIX time
- epoch) as an SQLite <code>INTEGER</code> will result in the
- <code>out_of_range</code> exception.</p>
-
- <h3><a name="24.4.3">24.4.3 PostgreSQL Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Qt date-time types and the PostgreSQL database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Date Time Type</th>
- <th>PostgreSQL Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QDate</code></td>
- <td><code>DATE</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QTime</code></td>
- <td><code>TIME</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QDateTime</code></td>
- <td><code>TIMESTAMP</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
- <code>QDateTime</code> types are stored as a <code>NULL</code> value
- if their <code>isNull()</code> member function returns true.</p>
-
- <h3><a name="24.4.4">24.4.4 Oracle Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Qt date-time types and the Oracle database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Date Time Type</th>
- <th>Oracle Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QDate</code></td>
- <td><code>DATE</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QTime</code></td>
- <td><code>INTERVAL DAY(0) TO SECOND(3)</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QDateTime</code></td>
- <td><code>TIMESTAMP(3)</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
- <code>QDateTime</code> types are stored as a <code>NULL</code> value
- if their <code>isNull()</code> member function returns true.</p>
-
- <p>The <code>date-time</code> sub-profile implementation also provides
- support for mapping <code>QDateTime</code> to the
- <code>DATE</code> Oracle type with fractional seconds that may be
- stored in a <code>QDateTime</code> instance being ignored. This
- alternative mapping has to be explicitly requested using the
- <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
- "<code>type</code>"</a>), as shown in the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
- #pragma db type("DATE")
- QDateTime updated_;
-};
- </pre>
-
- <h3><a name="24.4.5">24.4.5 SQL Server Database Type Mapping</a></h3>
-
- <p>The following table summarizes the default mapping between the currently
- supported Qt date-time types and the SQL Server database types.</p>
-
- <!-- border="1" is necessary for html2ps -->
- <table id="mapping" border="1">
- <tr>
- <th>Qt Date Time Type</th>
- <th>SQL Server Type</th>
- <th>Default <code>NULL</code> Semantics</th>
- </tr>
-
- <tr>
- <td><code>QDate</code></td>
- <td><code>DATE</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QTime</code></td>
- <td><code>TIME(3)</code></td>
- <td><code>NULL</code></td>
- </tr>
-
- <tr>
- <td><code>QDateTime</code></td>
- <td><code>DATETIME2(3)</code></td>
- <td><code>NULL</code></td>
- </tr>
- </table>
-
- <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
- <code>QDateTime</code> types are stored as a <code>NULL</code> value
- if their <code>isNull()</code> member function returns true.</p>
-
- <p>Note that the <code>DATE</code>, <code>TIME</code>, and
- <code>DATETIME2</code> types are only available in SQL Server 2008 and
- later. SQL Server 2005 only supports the <code>DATETIME</code> and
- <code>SMALLDATETIME</code> date-time types. The new types are
- also unavailable when connecting to an SQL Server 2008 or
- later with the SQL Server 2005 Native Client ODBC driver.</p>
-
- <p>The <code>date-time</code> sub-profile implementation provides
- support for mapping <code>QDateTime</code> to the <code>DATETIME</code>
- and <code>SMALLDATETIME</code> types, however, this mapping has to
- be explicitly requested using the <code>db&nbsp;type</code> pragma
- (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), as
- shown in the following example:</p>
-
- <pre class="cxx">
-#pragma db object
-class person
-{
- ...
- #pragma db type("DATETIME")
- QDateTime updated_;
-};
- </pre>
-
- </div>
-</div>
-
-</body>
-</html>
diff --git a/libodb-boost/.gitignore b/libodb-boost/.gitignore
new file mode 100644
index 0000000..1c363a0
--- /dev/null
+++ b/libodb-boost/.gitignore
@@ -0,0 +1,25 @@
+# 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/libodb-boost/GPLv2 b/libodb-boost/GPLv2
new file mode 100644
index 0000000..3912109
--- /dev/null
+++ b/libodb-boost/GPLv2
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) 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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. 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.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+ 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
+convey 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 2 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, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This 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 Library General
+Public License instead of this License.
diff --git a/libodb-boost/INSTALL b/libodb-boost/INSTALL
new file mode 100644
index 0000000..5c67dce
--- /dev/null
+++ b/libodb-boost/INSTALL
@@ -0,0 +1,6 @@
+The easiest way to build this package is with the bpkg package manager:
+
+$ bpkg build libodb-boost
+
+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/libodb-boost/LICENSE b/libodb-boost/LICENSE
new file mode 100644
index 0000000..d96b938
--- /dev/null
+++ b/libodb-boost/LICENSE
@@ -0,0 +1,20 @@
+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 2 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, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
diff --git a/libodb-boost/NEWS b/libodb-boost/NEWS
new file mode 120000
index 0000000..0fae0f8
--- /dev/null
+++ b/libodb-boost/NEWS
@@ -0,0 +1 @@
+../NEWS \ No newline at end of file
diff --git a/libodb-boost/README b/libodb-boost/README
new file mode 100644
index 0000000..7ad79b8
--- /dev/null
+++ b/libodb-boost/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 Boost ODB profile library. The Boost profile
+provides support for persisting Boost smart pointers, containers, and
+value types with the ODB system.
+
+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.
+
+Send questions, bug reports, or any other feedback to the
+odb-users@codesynthesis.com mailing list.
diff --git a/build/.gitignore b/libodb-boost/build/.gitignore
index 4a730a3..4a730a3 100644
--- a/build/.gitignore
+++ b/libodb-boost/build/.gitignore
diff --git a/libodb-boost/build/bootstrap.build b/libodb-boost/build/bootstrap.build
new file mode 100644
index 0000000..1adfcd3
--- /dev/null
+++ b/libodb-boost/build/bootstrap.build
@@ -0,0 +1,10 @@
+# file : build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = libodb-boost
+
+using version
+using config
+using dist
+using test
+using install
diff --git a/libodb-boost/build/export.build b/libodb-boost/build/export.build
new file mode 100644
index 0000000..27e02dd
--- /dev/null
+++ b/libodb-boost/build/export.build
@@ -0,0 +1,9 @@
+# file : build/export.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+$out_root/
+{
+ include odb/boost/
+}
+
+export $out_root/odb/boost/lib{odb-boost}
diff --git a/libodb-boost/build/root.build b/libodb-boost/build/root.build
new file mode 100644
index 0000000..882047d
--- /dev/null
+++ b/libodb-boost/build/root.build
@@ -0,0 +1,17 @@
+# file : build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+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
diff --git a/libodb-boost/buildfile b/libodb-boost/buildfile
new file mode 100644
index 0000000..8a0d6d9
--- /dev/null
+++ b/libodb-boost/buildfile
@@ -0,0 +1,9 @@
+# file : buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -build/} doc{INSTALL NEWS README} legal{LICENSE} manifest
+
+# Don't install tests or the INSTALL file.
+#
+tests/: install = false
+doc{INSTALL}@./: install = false
diff --git a/libodb-boost/manifest b/libodb-boost/manifest
new file mode 100644
index 0000000..c3e3cb5
--- /dev/null
+++ b/libodb-boost/manifest
@@ -0,0 +1,25 @@
+: 1
+name: libodb-boost
+version: 2.5.0-b.26.z
+project: odb
+summary: Boost ODB profile library
+license: GPL-2.0-only
+license: other: proprietary ; Not free/open source.
+topics: C++, ORM, Boost, SQL
+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
+requires: c++11
+requires: libboost ; Requires not yet packaged libboost.
+
+# @@ TMP Bump the toolchain version to 0.17.0 after it is released.
+#
+depends: * build2 >= 0.16.0-
+depends: * bpkg >= 0.16.0-
+
+depends: libodb == $
diff --git a/libodb-boost/odb/boost.options b/libodb-boost/odb/boost.options
new file mode 100644
index 0000000..bf8796e
--- /dev/null
+++ b/libodb-boost/odb/boost.options
@@ -0,0 +1,10 @@
+# file : odb/boost.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/smart-ptr
+--profile boost/optional
+--profile boost/unordered
+--profile boost/date-time
+--profile boost/multi-index
+--profile boost/uuid
+
diff --git a/libodb-boost/odb/boost/buildfile b/libodb-boost/odb/boost/buildfile
new file mode 100644
index 0000000..b8dd6d1
--- /dev/null
+++ b/libodb-boost/odb/boost/buildfile
@@ -0,0 +1,65 @@
+# file : odb/boost/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+../
+{
+ define options: file
+ options{*}: extension = options
+
+ # Install into the odb/boost/ subdirectory of, say, /usr/include/
+ # recreating subdirectories.
+ #
+ {hxx ixx txx options}{*}:
+ {
+ install = include/odb/
+ install.subdirs = true
+ }
+
+ boost/
+ {
+ import int_libs = libodb%lib{odb}
+ imp_libs =
+
+ lib{odb-boost}: {hxx ixx txx cxx}{** -version} hxx{version} \
+ options{**} ../options{boost} \
+ $imp_libs $int_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)
+ }
+
+ # For pre-releases use the complete version to make sure they cannot be
+ # used in place of another pre-release or the final version. See the
+ # version module for details on the version.* variable values.
+ #
+ if $version.pre_release
+ lib{odb-boost}: bin.lib.version = @"-$version.project_id"
+ else
+ lib{odb-boost}: bin.lib.version = @"-$version.major.$version.minor"
+
+ # Build options.
+ #
+ cxx.poptions =+ "-I$out_root" "-I$src_root"
+
+ obja{*}: cxx.poptions += -DLIBODB_BOOST_STATIC_BUILD
+ objs{*}: cxx.poptions += -DLIBODB_BOOST_SHARED_BUILD
+
+ # Export options.
+ #
+ lib{odb-boost}:
+ {
+ cxx.export.poptions = "-I$out_root" "-I$src_root"
+ cxx.export.libs = $int_libs
+ }
+
+ liba{odb-boost}: cxx.export.poptions += -DLIBODB_BOOST_STATIC
+ libs{odb-boost}: cxx.export.poptions += -DLIBODB_BOOST_SHARED
+ }
+}
diff --git a/libodb-boost/odb/boost/date-time.options b/libodb-boost/odb/boost/date-time.options
new file mode 100644
index 0000000..a3bd489
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time.options
@@ -0,0 +1,5 @@
+# file : odb/boost/date-time.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/date-time/gregorian
+--profile boost/date-time/posix-time
diff --git a/libodb-boost/odb/boost/date-time/exceptions.cxx b/libodb-boost/odb/boost/date-time/exceptions.cxx
new file mode 100644
index 0000000..3ba8132
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/exceptions.cxx
@@ -0,0 +1,37 @@
+// file : odb/boost/date-time/exceptions.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/boost/date-time/exceptions.hxx>
+
+namespace odb
+{
+ namespace boost
+ {
+ namespace date_time
+ {
+ const char* special_value::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "unrepresentable date/time special value";
+ }
+
+ special_value* special_value::
+ clone () const
+ {
+ return new special_value (*this);
+ }
+
+ const char* value_out_of_range::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "date/time value out of range";
+ }
+
+ value_out_of_range* value_out_of_range::
+ clone () const
+ {
+ return new value_out_of_range (*this);
+ }
+ }
+ }
+}
diff --git a/libodb-boost/odb/boost/date-time/exceptions.hxx b/libodb-boost/odb/boost/date-time/exceptions.hxx
new file mode 100644
index 0000000..321f8da
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/exceptions.hxx
@@ -0,0 +1,43 @@
+// file : odb/boost/date-time/exceptions.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_EXCEPTIONS_HXX
+#define ODB_BOOST_DATE_TIME_EXCEPTIONS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+
+#include <odb/boost/exception.hxx>
+#include <odb/boost/details/export.hxx>
+
+namespace odb
+{
+ namespace boost
+ {
+ namespace date_time
+ {
+ struct LIBODB_BOOST_EXPORT special_value: exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual special_value*
+ clone () const;
+ };
+
+ struct LIBODB_BOOST_EXPORT value_out_of_range: exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual value_out_of_range*
+ clone () const;
+ };
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_BOOST_DATE_TIME_EXCEPTIONS_HXX
diff --git a/libodb-boost/odb/boost/date-time/gregorian-common.options b/libodb-boost/odb/boost/date-time/gregorian-common.options
new file mode 100644
index 0000000..dfb0b87
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/gregorian-common.options
@@ -0,0 +1,4 @@
+# file : odb/boost/date-time/gregorian-common.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
diff --git a/libodb-boost/odb/boost/date-time/gregorian-mssql.options b/libodb-boost/odb/boost/date-time/gregorian-mssql.options
new file mode 100644
index 0000000..971d19d
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/gregorian-mssql.options
@@ -0,0 +1,11 @@
+# file : odb/boost/date-time/gregorian-mssql.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/boost/date-time/mssql/gregorian-mapping.hxx>'
+
+--hxx-prologue '#include <odb/boost/date-time/mssql/gregorian-traits.hxx>'
diff --git a/libodb-boost/odb/boost/date-time/gregorian-mysql.options b/libodb-boost/odb/boost/date-time/gregorian-mysql.options
new file mode 100644
index 0000000..927dc70
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/gregorian-mysql.options
@@ -0,0 +1,11 @@
+# file : odb/boost/date-time/gregorian-mysql.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/boost/date-time/mysql/gregorian-mapping.hxx>'
+
+--hxx-prologue '#include <odb/boost/date-time/mysql/gregorian-traits.hxx>'
diff --git a/libodb-boost/odb/boost/date-time/gregorian-oracle.options b/libodb-boost/odb/boost/date-time/gregorian-oracle.options
new file mode 100644
index 0000000..fca3670
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/gregorian-oracle.options
@@ -0,0 +1,11 @@
+# file : odb/boost/date-time/gregorian-oracle.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/boost/date-time/oracle/gregorian-mapping.hxx>'
+
+--hxx-prologue '#include <odb/boost/date-time/oracle/gregorian-traits.hxx>'
diff --git a/libodb-boost/odb/boost/date-time/gregorian-pgsql.options b/libodb-boost/odb/boost/date-time/gregorian-pgsql.options
new file mode 100644
index 0000000..bf6726a
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/gregorian-pgsql.options
@@ -0,0 +1,11 @@
+# file : odb/boost/date-time/gregorian-pgsql.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/boost/date-time/pgsql/gregorian-mapping.hxx>'
+
+--hxx-prologue '#include <odb/boost/date-time/pgsql/gregorian-traits.hxx>'
diff --git a/libodb-boost/odb/boost/date-time/gregorian-sqlite.options b/libodb-boost/odb/boost/date-time/gregorian-sqlite.options
new file mode 100644
index 0000000..355791c
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/gregorian-sqlite.options
@@ -0,0 +1,11 @@
+# file : odb/boost/date-time/gregorian-sqlite.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/boost/date-time/sqlite/gregorian-mapping.hxx>'
+
+--hxx-prologue '#include <odb/boost/date-time/sqlite/gregorian-traits.hxx>'
diff --git a/libodb-boost/odb/boost/date-time/mssql/gregorian-mapping.hxx b/libodb-boost/odb/boost/date-time/mssql/gregorian-mapping.hxx
new file mode 100644
index 0000000..9c64542
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/mssql/gregorian-mapping.hxx
@@ -0,0 +1,15 @@
+// file : odb/boost/date-time/mssql/gregorian-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_MSSQL_GREGORIAN_MAPPING_HXX
+#define ODB_BOOST_DATE_TIME_MSSQL_GREGORIAN_MAPPING_HXX
+
+#include <boost/date_time/gregorian/gregorian_types.hpp>
+
+// By default map boost::gregorian::date to SQL Server DATE (available
+// only since SQL Server 2008). We use the NULL value to represent
+// not_a_date_time.
+//
+#pragma db value(boost::gregorian::date) type("DATE") null
+
+#endif // ODB_BOOST_DATE_TIME_MSSQL_GREGORIAN_MAPPING_HXX
diff --git a/libodb-boost/odb/boost/date-time/mssql/gregorian-traits.hxx b/libodb-boost/odb/boost/date-time/mssql/gregorian-traits.hxx
new file mode 100644
index 0000000..b2b9a16
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/mssql/gregorian-traits.hxx
@@ -0,0 +1,68 @@
+// file : odb/boost/date-time/mssql/gregorian-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_MSSQL_GREGORIAN_TRAITS_HXX
+#define ODB_BOOST_DATE_TIME_MSSQL_GREGORIAN_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <boost/date_time/gregorian/gregorian_types.hpp>
+
+#include <odb/core.hxx>
+#include <odb/mssql/traits.hxx>
+#include <odb/boost/date-time/exceptions.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <>
+ class default_value_traits< ::boost::gregorian::date, id_date>
+ {
+ public:
+ typedef ::boost::gregorian::date value_type;
+ typedef value_type query_type;
+ typedef date image_type;
+
+ static void
+ set_value (value_type& v, const date& i, bool is_null)
+ {
+ if (is_null)
+ v = value_type (::boost::date_time::not_a_date_time);
+ else
+ v = value_type (static_cast<value_type::year_type> (i.year),
+ static_cast<value_type::month_type> (i.month),
+ static_cast<value_type::day_type> (i.day));
+ }
+
+ static void
+ set_image (date& i, bool& is_null, const value_type& v)
+ {
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date ())
+ is_null = true;
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else
+ {
+ is_null = false;
+ i.year = static_cast<SQLSMALLINT> (v.year ());
+ i.month = static_cast<SQLUSMALLINT> (v.month ());
+ i.day = static_cast<SQLUSMALLINT> (v.day ());
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits< ::boost::gregorian::date>
+ {
+ static const database_type_id db_type_id = id_date;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_BOOST_DATE_TIME_MSSQL_GREGORIAN_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/date-time/mssql/posix-time-mapping.hxx b/libodb-boost/odb/boost/date-time/mssql/posix-time-mapping.hxx
new file mode 100644
index 0000000..0682dda
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/mssql/posix-time-mapping.hxx
@@ -0,0 +1,21 @@
+// file : odb/boost/date-time/mssql/posix-time-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_MSSQL_POSIX_TIME_MAPPING_HXX
+#define ODB_BOOST_DATE_TIME_MSSQL_POSIX_TIME_MAPPING_HXX
+
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+// By default map boost::posix_time::ptime to SQL Server DATETIME2
+// (available only since SQL Server 2008). We use the NULL value to
+// represent not_a_date_time.
+//
+#pragma db value(boost::posix_time::ptime) type("DATETIME2") null
+
+// By default map boost::posix_time::time_duration to SQL Server TIME
+// (available only since SQL Server 2008). We use the NULL value to
+// represent not_a_date_time.
+//
+#pragma db value(boost::posix_time::time_duration) type("TIME") null
+
+#endif // ODB_BOOST_DATE_TIME_MSSQL_POSIX_TIME_MAPPING_HXX
diff --git a/libodb-boost/odb/boost/date-time/mssql/posix-time-traits.hxx b/libodb-boost/odb/boost/date-time/mssql/posix-time-traits.hxx
new file mode 100644
index 0000000..88d0c82
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/mssql/posix-time-traits.hxx
@@ -0,0 +1,194 @@
+// file : odb/boost/date-time/mssql/posix-time-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_MSSQL_POSIX_TIME_TRAITS_HXX
+#define ODB_BOOST_DATE_TIME_MSSQL_POSIX_TIME_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+#include <odb/core.hxx>
+#include <odb/mssql/traits.hxx>
+#include <odb/boost/date-time/exceptions.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <>
+ class default_value_traits< ::boost::posix_time::ptime, id_datetime>
+ {
+ public:
+ typedef ::boost::posix_time::ptime ptime;
+ typedef ::boost::posix_time::time_duration time_duration;
+ typedef ::boost::gregorian::date date;
+ typedef ptime value_type;
+ typedef ptime query_type;
+ typedef datetime image_type;
+
+ static void
+ set_value (ptime& v, const datetime& i, bool is_null)
+ {
+ if (is_null)
+ v = ptime (::boost::date_time::not_a_date_time);
+ else
+ {
+ unsigned long long fract_s (i.fraction);
+ fract_s = fract_s * time_duration::ticks_per_second () /
+ 1000000000ULL;
+
+ v = ptime (
+ date (static_cast<date::year_type> (i.year),
+ static_cast<date::month_type> (i.month),
+ static_cast<date::day_type> (i.day)),
+ time_duration (
+ static_cast<time_duration::hour_type> (i.hour),
+ static_cast<time_duration::min_type> (i.minute),
+ static_cast<time_duration::sec_type> (i.second),
+ static_cast<time_duration::fractional_seconds_type> (fract_s)));
+ }
+ }
+
+ static void
+ set_image (datetime& i, unsigned short s, bool& is_null, const ptime& v)
+ {
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date_time ())
+ is_null = true;
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else
+ {
+ const date& d (v.date ());
+ const time_duration& t (v.time_of_day ());
+
+ is_null = false;
+ i.year = static_cast<SQLSMALLINT> (d.year ());
+ i.month = static_cast<SQLUSMALLINT> (d.month ());
+ i.day = static_cast<SQLUSMALLINT> (d.day ());
+ i.hour = static_cast<SQLUSMALLINT> (t.hours ());
+ i.minute = static_cast<SQLUSMALLINT> (t.minutes ());
+
+ // Scale value 8 indicates we are dealing with SMALLDATETIME
+ // which has the minutes precision.
+ //
+ if (s != 8)
+ {
+ i.second = static_cast<SQLUSMALLINT> (t.seconds ());
+
+ unsigned long long ns (t.fractional_seconds ());
+ ns = ns * 1000000000ULL / time_duration::ticks_per_second ();
+
+ const unsigned int divider[8] =
+ {
+ 1000000000,
+ 100000000,
+ 10000000,
+ 1000000,
+ 100000,
+ 10000,
+ 1000,
+ 100
+ };
+
+ i.fraction = static_cast<SQLUINTEGER> (ns - ns % divider[s]);
+ }
+ else
+ {
+ i.second = 0;
+ i.fraction = 0;
+ }
+ }
+ }
+ };
+
+ template <>
+ class default_value_traits< ::boost::posix_time::time_duration, id_time>
+ {
+ public:
+ typedef ::boost::posix_time::time_duration time_duration;
+ typedef time_duration value_type;
+ typedef time_duration query_type;
+ typedef time image_type;
+
+ static void
+ set_value (time_duration& v, const time& i, bool is_null)
+ {
+ if (is_null)
+ v = time_duration (::boost::date_time::not_a_date_time);
+ else
+ {
+ unsigned long long fract_s (i.fraction);
+ fract_s = fract_s * time_duration::ticks_per_second () /
+ 1000000000ULL;
+
+ v = time_duration (
+ static_cast<time_duration::hour_type> (i.hour),
+ static_cast<time_duration::min_type> (i.minute),
+ static_cast<time_duration::sec_type> (i.second),
+ static_cast<time_duration::fractional_seconds_type> (fract_s));
+ }
+ }
+
+ static void
+ set_image (time& i,
+ unsigned short s,
+ bool& is_null,
+ const time_duration& v)
+ {
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date_time ())
+ is_null = true;
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else if (v.is_negative () || v.hours () > 23)
+ throw odb::boost::date_time::value_out_of_range ();
+ else
+ {
+ is_null = false;
+ i.hour = static_cast<SQLUSMALLINT> (v.hours ());
+ i.minute = static_cast<SQLUSMALLINT> (v.minutes ());
+ i.second = static_cast<SQLUSMALLINT> (v.seconds ());
+
+ unsigned long long ns (v.fractional_seconds ());
+ ns = ns * 1000000000ULL / time_duration::ticks_per_second ();
+
+ const unsigned int divider[8] =
+ {
+ 1000000000,
+ 100000000,
+ 10000000,
+ 1000000,
+ 100000,
+ 10000,
+ 1000,
+ 100
+ };
+
+ i.fraction = static_cast<SQLUINTEGER> (ns - ns % divider[s]);
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits< ::boost::posix_time::ptime>
+ {
+ static const database_type_id db_type_id = id_datetime;
+ };
+
+ template <>
+ struct default_type_traits< ::boost::posix_time::time_duration>
+ {
+ static const database_type_id db_type_id = id_time;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_BOOST_DATE_TIME_MSSQL_POSIX_TIME_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/date-time/mysql/gregorian-mapping.hxx b/libodb-boost/odb/boost/date-time/mysql/gregorian-mapping.hxx
new file mode 100644
index 0000000..9d23274
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/mysql/gregorian-mapping.hxx
@@ -0,0 +1,14 @@
+// file : odb/boost/date-time/mysql/gregorian-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_MYSQL_GREGORIAN_MAPPING_HXX
+#define ODB_BOOST_DATE_TIME_MYSQL_GREGORIAN_MAPPING_HXX
+
+#include <boost/date_time/gregorian/gregorian_types.hpp>
+
+// By default map boost::gregorian::date to MySQL DATE. We use the
+// NULL value to represent not_a_date_time.
+//
+#pragma db value(boost::gregorian::date) type("DATE") null
+
+#endif // ODB_BOOST_DATE_TIME_MYSQL_GREGORIAN_MAPPING_HXX
diff --git a/libodb-boost/odb/boost/date-time/mysql/gregorian-traits.hxx b/libodb-boost/odb/boost/date-time/mysql/gregorian-traits.hxx
new file mode 100644
index 0000000..6b29dc5
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/mysql/gregorian-traits.hxx
@@ -0,0 +1,72 @@
+// file : odb/boost/date-time/mysql/gregorian-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_MYSQL_GREGORIAN_TRAITS_HXX
+#define ODB_BOOST_DATE_TIME_MYSQL_GREGORIAN_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <boost/date_time/gregorian/gregorian_types.hpp>
+
+#include <odb/core.hxx>
+#include <odb/mysql/traits.hxx>
+#include <odb/boost/date-time/exceptions.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <>
+ struct default_value_traits< ::boost::gregorian::date, id_date>
+ {
+ typedef ::boost::gregorian::date date;
+ typedef date value_type;
+ typedef date query_type;
+ typedef MYSQL_TIME image_type;
+
+ static void
+ set_value (date& v, const MYSQL_TIME& i, bool is_null)
+ {
+ if (is_null)
+ v = date (::boost::date_time::not_a_date_time);
+ else
+ v = date (i.year, i.month, i.day);
+ }
+
+ static void
+ set_image (MYSQL_TIME& i, bool& is_null, const date& v)
+ {
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date ())
+ is_null = true;
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else
+ {
+ is_null = false;
+ i.neg = false;
+ i.year = v.year ();
+ i.month = v.month ();
+ i.day = v.day ();
+
+ i.hour = 0;
+ i.minute = 0;
+ i.second = 0;
+ i.second_part = 0;
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits< ::boost::gregorian::date>
+ {
+ static const database_type_id db_type_id = id_date;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_BOOST_DATE_TIME_MYSQL_GREGORIAN_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/date-time/mysql/posix-time-mapping.hxx b/libodb-boost/odb/boost/date-time/mysql/posix-time-mapping.hxx
new file mode 100644
index 0000000..f3908b3
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/mysql/posix-time-mapping.hxx
@@ -0,0 +1,19 @@
+// file : odb/boost/date-time/mysql/posix-time-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_MYSQL_POSIX_TIME_MAPPING_HXX
+#define ODB_BOOST_DATE_TIME_MYSQL_POSIX_TIME_MAPPING_HXX
+
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+// By default map boost::posix_time::ptime to MySQL DATETIME. We use
+// the NULL value to represent not_a_date_time.
+//
+#pragma db value(boost::posix_time::ptime) type("DATETIME") null
+
+// By default map boost::posix_time::time_duration to MySQL TIME. We
+// use the NULL value to represent not_a_date_time.
+//
+#pragma db value(boost::posix_time::time_duration) type("TIME") null
+
+#endif // ODB_BOOST_DATE_TIME_MYSQL_POSIX_TIME_MAPPING_HXX
diff --git a/libodb-boost/odb/boost/date-time/mysql/posix-time-traits.hxx b/libodb-boost/odb/boost/date-time/mysql/posix-time-traits.hxx
new file mode 100644
index 0000000..28c42c1
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/mysql/posix-time-traits.hxx
@@ -0,0 +1,238 @@
+// file : odb/boost/date-time/mysql/posix-time-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_MYSQL_POSIX_TIME_TRAITS_HXX
+#define ODB_BOOST_DATE_TIME_MYSQL_POSIX_TIME_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstdlib> // std::abs
+
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+#include <odb/core.hxx>
+#include <odb/mysql/traits.hxx>
+#include <odb/boost/date-time/exceptions.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <>
+ struct default_value_traits< ::boost::posix_time::ptime, id_datetime>
+ {
+ typedef ::boost::posix_time::ptime ptime;
+ typedef ::boost::posix_time::time_duration time_duration;
+ typedef ::boost::gregorian::date date;
+ typedef ptime value_type;
+ typedef ptime query_type;
+ typedef MYSQL_TIME image_type;
+
+ static void
+ set_value (ptime& v, const MYSQL_TIME& i, bool is_null)
+ {
+ if (is_null)
+ v = ptime (::boost::date_time::not_a_date_time);
+ else
+ {
+ // Since MySQL 5.6.4, the microseconds part is no longer ignored.
+ //
+ unsigned long long fract_s (i.second_part);
+ fract_s = fract_s * time_duration::ticks_per_second () / 1000000ULL;
+
+ v = ptime (
+ date (i.year, i.month, i.day),
+ time_duration (
+ i.hour,
+ i.minute,
+ i.second,
+ static_cast<time_duration::fractional_seconds_type> (fract_s)));
+ }
+ }
+
+ static void
+ set_image (MYSQL_TIME& i, bool& is_null, const ptime& v)
+ {
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date_time ())
+ is_null = true;
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else
+ {
+ is_null = false;
+ i.neg = false;
+
+ const date& d (v.date ());
+ i.year = d.year ();
+ i.month = d.month ();
+ i.day = d.day ();
+
+ const time_duration& t (v.time_of_day ());
+ i.hour = t.hours ();
+ i.minute = t.minutes ();
+ i.second = t.seconds ();
+
+ unsigned long long ms (t.fractional_seconds ());
+ ms = ms * 1000000ULL / time_duration::ticks_per_second ();
+ i.second_part = static_cast<unsigned long> (ms);
+ }
+ }
+ };
+
+ template <>
+ struct default_value_traits< ::boost::posix_time::ptime, id_timestamp>
+ {
+ typedef ::boost::posix_time::ptime ptime;
+ typedef ::boost::posix_time::time_duration time_duration;
+ typedef ::boost::gregorian::date date;
+ typedef ptime value_type;
+ typedef ptime query_type;
+ typedef MYSQL_TIME image_type;
+
+ static void
+ set_value (ptime& v, const MYSQL_TIME& i, bool is_null)
+ {
+ if (is_null)
+ v = ptime (::boost::date_time::not_a_date_time);
+ else
+ {
+ // Since MySQL 5.6.4, the microseconds part is no longer ignored.
+ //
+ unsigned long long fract_s (i.second_part);
+ fract_s = fract_s * time_duration::ticks_per_second () / 1000000ULL;
+
+ v = ptime (
+ date (i.year, i.month, i.day),
+ time_duration (
+ i.hour,
+ i.minute,
+ i.second,
+ static_cast<time_duration::fractional_seconds_type> (fract_s)));
+ }
+ }
+
+ static void
+ set_image (MYSQL_TIME& i, bool& is_null, const ptime& v)
+ {
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date_time ())
+ is_null = true;
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else if (v < ptime (date (1970, ::boost::date_time::Jan, 1),
+ time_duration (0, 0, 1)) ||
+ v > ptime (date (2038, ::boost::date_time::Jan, 19),
+ time_duration (3, 14, 7)))
+ throw odb::boost::date_time::value_out_of_range ();
+ else
+ {
+ is_null = false;
+ i.neg = false;
+
+ const date& d (v.date ());
+ i.year = d.year ();
+ i.month = d.month ();
+ i.day = d.day ();
+
+ const time_duration& t (v.time_of_day ());
+ i.hour = t.hours ();
+ i.minute = t.minutes ();
+ i.second = t.seconds ();
+
+ unsigned long long ms (t.fractional_seconds ());
+ ms = ms * 1000000ULL / time_duration::ticks_per_second ();
+ i.second_part = static_cast<unsigned long> (ms);
+ }
+ }
+ };
+
+ template <>
+ struct default_value_traits< ::boost::posix_time::time_duration, id_time>
+ {
+ typedef ::boost::posix_time::time_duration time_duration;
+ typedef time_duration value_type;
+ typedef time_duration query_type;
+ typedef MYSQL_TIME image_type;
+
+ static const unsigned short max_hours = 838;
+
+ static void
+ set_value (time_duration& v, const MYSQL_TIME& i, bool is_null)
+ {
+ if (is_null)
+ v = time_duration (::boost::date_time::not_a_date_time);
+ else
+ {
+ // Since MySQL 5.6.4, the microseconds part is no longer ignored.
+ //
+ unsigned long long fract_s (i.second_part);
+ fract_s = fract_s * time_duration::ticks_per_second () / 1000000ULL;
+
+ v = time_duration (
+ i.hour,
+ i.minute,
+ i.second,
+ static_cast<time_duration::fractional_seconds_type> (fract_s));
+
+ if (i.neg)
+ v = v.invert_sign ();
+ }
+ }
+
+ static void
+ set_image (MYSQL_TIME& i, bool& is_null, const time_duration& v)
+ {
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date_time ())
+ is_null = true;
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else if (std::abs (v.hours ()) > max_hours)
+ throw odb::boost::date_time::value_out_of_range ();
+ else
+ {
+ is_null = false;
+ i.neg = v.is_negative ();
+
+ i.year = 0;
+ i.month = 0;
+ i.day = 0;
+
+ i.hour = std::abs (v.hours ());
+ i.minute = std::abs (v.minutes ());
+ i.second = std::abs (v.seconds ());
+
+ // Some compilers, e.g., VC8, don't have 64-bit abs() overload.
+ //
+ time_duration::fractional_seconds_type sms (v.fractional_seconds ());
+ unsigned long long ms (sms >= 0 ? sms : -sms);
+ ms = ms * 1000000ULL / time_duration::ticks_per_second ();
+ i.second_part = static_cast<unsigned long> (ms);
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits< ::boost::posix_time::ptime>
+ {
+ static const database_type_id db_type_id = id_datetime;
+ };
+
+ template <>
+ struct default_type_traits< ::boost::posix_time::time_duration>
+ {
+ static const database_type_id db_type_id = id_time;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_BOOST_DATE_TIME_MYSQL_POSIX_TIME_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/date-time/oracle/gregorian-mapping.hxx b/libodb-boost/odb/boost/date-time/oracle/gregorian-mapping.hxx
new file mode 100644
index 0000000..65ad008
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/oracle/gregorian-mapping.hxx
@@ -0,0 +1,14 @@
+// file : odb/boost/date-time/oracle/gregorian-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_ORACLE_GREGORIAN_MAPPING_HXX
+#define ODB_BOOST_DATE_TIME_ORACLE_GREGORIAN_MAPPING_HXX
+
+#include <boost/date_time/gregorian/gregorian_types.hpp>
+
+// By default map boost::gregorian::date to Oracle DATE. We use the
+// NULL value to represent not_a_date_time.
+//
+#pragma db value(boost::gregorian::date) type("DATE") null
+
+#endif // ODB_BOOST_DATE_TIME_ORACLE_GREGORIAN_MAPPING_HXX
diff --git a/libodb-boost/odb/boost/date-time/oracle/gregorian-traits.hxx b/libodb-boost/odb/boost/date-time/oracle/gregorian-traits.hxx
new file mode 100644
index 0000000..9e02e25
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/oracle/gregorian-traits.hxx
@@ -0,0 +1,83 @@
+// file : odb/boost/date-time/oracle/gregorian-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_ORACLE_GREGORIAN_TRAITS_HXX
+#define ODB_BOOST_DATE_TIME_ORACLE_GREGORIAN_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <boost/date_time/gregorian/gregorian_types.hpp>
+
+#include <odb/core.hxx>
+
+#include <odb/oracle/traits.hxx>
+#include <odb/oracle/details/date.hxx>
+
+#include <odb/boost/date-time/exceptions.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <>
+ struct default_value_traits< ::boost::gregorian::date, id_date>
+ {
+ typedef ::boost::gregorian::date date;
+ typedef date value_type;
+ typedef date query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (date& v, const char* b, bool is_null)
+ {
+ if (is_null)
+ v = date (::boost::date_time::not_a_date_time);
+ else
+ {
+ short y;
+ unsigned char m, d, h, minute, s;
+
+ details::get_date (b, y, m, d, h, minute, s);
+
+ v = date (static_cast<date::year_type> (y),
+ static_cast<date::month_type> (m),
+ static_cast<date::day_type> (d));
+ }
+ }
+
+ static void
+ set_image (char* b, bool& is_null, const date& v)
+ {
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date ())
+ is_null = true;
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else
+ {
+ is_null = false;
+
+ details::set_date (b,
+ static_cast<short> (v.year ()),
+ static_cast<unsigned char> (v.month ()),
+ static_cast<unsigned char> (v.day ()),
+ 0,
+ 0,
+ 0);
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits< ::boost::gregorian::date>
+ {
+ static const database_type_id db_type_id = id_date;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_BOOST_DATE_TIME_ORACLE_GREGORIAN_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/date-time/oracle/posix-time-mapping.hxx b/libodb-boost/odb/boost/date-time/oracle/posix-time-mapping.hxx
new file mode 100644
index 0000000..26c642f
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/oracle/posix-time-mapping.hxx
@@ -0,0 +1,20 @@
+// file : odb/boost/date-time/oracle/posix-time-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_ORACLE_POSIX_TIME_MAPPING_HXX
+#define ODB_BOOST_DATE_TIME_ORACLE_POSIX_TIME_MAPPING_HXX
+
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+// By default map boost::posix_time::ptime to Oracle TIMESTAMP. We use the NULL
+// value to represent not_a_date_time.
+//
+#pragma db value(boost::posix_time::ptime) type("TIMESTAMP") null
+
+// By default map boost::posix_time::time_duration to Oracle
+// INTERVAL DAY TO SECOND. We use the NULL value to represent not_a_date_time.
+//
+#pragma db value(boost::posix_time::time_duration) \
+ type("INTERVAL DAY TO SECOND") null
+
+#endif // ODB_BOOST_DATE_TIME_ORACLE_POSIX_TIME_MAPPING_HXX
diff --git a/libodb-boost/odb/boost/date-time/oracle/posix-time-traits.hxx b/libodb-boost/odb/boost/date-time/oracle/posix-time-traits.hxx
new file mode 100644
index 0000000..1f830e0
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/oracle/posix-time-traits.hxx
@@ -0,0 +1,237 @@
+// file : odb/boost/date-time/oracle/posix-time-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_ORACLE_POSIX_TIME_TRAITS_HXX
+#define ODB_BOOST_DATE_TIME_ORACLE_POSIX_TIME_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+#include <odb/core.hxx>
+
+#include <odb/oracle/traits.hxx>
+#include <odb/oracle/oracle-fwd.hxx> // ub1, sb2, ub4
+#include <odb/oracle/oracle-types.hxx> // odb::oracle::{datetime interval_ds}
+#include <odb/oracle/details/date.hxx>
+
+#include <odb/boost/date-time/exceptions.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <>
+ struct default_value_traits< ::boost::posix_time::ptime, id_timestamp>
+ {
+ typedef ::boost::posix_time::ptime ptime;
+ typedef ::boost::posix_time::time_duration time_duration;
+ typedef ::boost::gregorian::date date;
+
+ typedef ptime value_type;
+ typedef ptime query_type;
+ typedef datetime image_type;
+
+ static void
+ set_value (ptime& v, const datetime& i, bool is_null)
+ {
+ if (is_null)
+ v = ptime (::boost::date_time::not_a_date_time);
+ else
+ {
+ sb2 y;
+ ub1 m, d, h, minute, s;
+ ub4 ns;
+
+ i.get (y, m, d, h, minute, s, ns);
+
+ unsigned long long fract_s (ns);
+ fract_s = fract_s * time_duration::ticks_per_second () /
+ 1000000000ULL;
+
+ v = ptime (
+ date (static_cast<date::year_type> (y),
+ static_cast<date::month_type> (m),
+ static_cast<date::day_type> (d)),
+ time_duration (
+ static_cast<time_duration::hour_type> (h),
+ static_cast<time_duration::min_type> (minute),
+ static_cast<time_duration::sec_type> (s),
+ static_cast<time_duration::fractional_seconds_type> (fract_s)));
+ }
+ }
+
+ static void
+ set_image (datetime& i, bool& is_null, const ptime& v)
+ {
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date_time ())
+ is_null = true;
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else
+ {
+ is_null = false;
+
+ const date& d (v.date ());
+ const time_duration& t (v.time_of_day ());
+
+ unsigned long long ns (t.fractional_seconds ());
+ ns = ns * 1000000000ULL / time_duration::ticks_per_second ();
+
+ i.set (static_cast<sb2> (d.year ()),
+ static_cast<ub1> (d.month ()),
+ static_cast<ub1> (d.day ()),
+ static_cast<ub1> (t.hours ()),
+ static_cast<ub1> (t.minutes ()),
+ static_cast<ub1> (t.seconds ()),
+ static_cast<ub4> (ns));
+ }
+ }
+ };
+
+ template <>
+ struct default_value_traits< ::boost::posix_time::ptime, id_date>
+ {
+ typedef ::boost::posix_time::ptime ptime;
+ typedef ::boost::posix_time::time_duration time_duration;
+ typedef ::boost::gregorian::date date;
+
+ typedef ptime value_type;
+ typedef ptime query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (ptime& v, const char* b, bool is_null)
+ {
+ if (is_null)
+ v = ptime (::boost::date_time::not_a_date_time);
+ else
+ {
+ short y;
+ unsigned char m, d, h, minute, s;
+
+ details::get_date (b, y, m, d, h, minute, s);
+
+ v = ptime (
+ date (static_cast<date::year_type> (y),
+ static_cast<date::month_type> (m),
+ static_cast<date::day_type> (d)),
+ time_duration (
+ static_cast<time_duration::hour_type> (h),
+ static_cast<time_duration::min_type> (minute),
+ static_cast<time_duration::sec_type> (s),
+ 0));
+ }
+ }
+
+ static void
+ set_image (char* b, bool& is_null, const ptime& v)
+ {
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date_time ())
+ is_null = true;
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else
+ {
+ is_null = false;
+
+ const date& d (v.date ());
+ const time_duration& t (v.time_of_day ());
+
+ details::set_date (b,
+ static_cast<short> (d.year ()),
+ static_cast<unsigned char> (d.month ()),
+ static_cast<unsigned char> (d.day ()),
+ static_cast<unsigned char> (t.hours ()),
+ static_cast<unsigned char> (t.minutes ()),
+ static_cast<unsigned char> (t.seconds ()));
+ }
+ }
+ };
+
+ template <>
+ struct default_value_traits< ::boost::posix_time::time_duration,
+ id_interval_ds>
+ {
+ typedef ::boost::posix_time::time_duration time_duration;
+
+ typedef time_duration value_type;
+ typedef time_duration query_type;
+ typedef interval_ds image_type;
+
+ static void
+ set_value (time_duration& v,
+ const interval_ds& i,
+ bool is_null)
+ {
+ if (is_null)
+ v = time_duration (::boost::date_time::not_a_date_time);
+ else
+ {
+ sb4 d, h, m, s, ns;
+ i.get (d, h, m, s, ns);
+
+ unsigned long long fract_s (ns);
+ fract_s = fract_s * time_duration::ticks_per_second () /
+ 1000000000ULL;
+
+ v = time_duration (
+ static_cast<time_duration::hour_type> (
+ static_cast<unsigned long long> (d) * 24 + h),
+ static_cast<time_duration::min_type> (m),
+ static_cast<time_duration::sec_type> (s),
+ static_cast<time_duration::fractional_seconds_type> (fract_s));
+ }
+ }
+
+ static void
+ set_image (interval_ds& i,
+ bool& is_null,
+ const time_duration& v)
+ {
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date_time ())
+ is_null = true;
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else
+ {
+ is_null = false;
+
+ unsigned long long ns (v.fractional_seconds ());
+ ns = ns * 1000000000ULL / time_duration::ticks_per_second ();
+
+ i.set (0,
+ static_cast<sb4> (v.hours ()),
+ static_cast<sb4> (v.minutes ()),
+ static_cast<sb4> (v.seconds ()),
+ static_cast<sb4> (ns));
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits< ::boost::posix_time::ptime>
+ {
+ static const database_type_id db_type_id = id_timestamp;
+ };
+
+ template <>
+ struct default_type_traits< ::boost::posix_time::time_duration>
+ {
+ static const database_type_id db_type_id = id_interval_ds;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_BOOST_DATE_TIME_ORACLE_POSIX_TIME_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/date-time/pgsql/gregorian-mapping.hxx b/libodb-boost/odb/boost/date-time/pgsql/gregorian-mapping.hxx
new file mode 100644
index 0000000..36a9334
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/pgsql/gregorian-mapping.hxx
@@ -0,0 +1,14 @@
+// file : odb/boost/date-time/pgsql/gregorian-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_PGSQL_GREGORIAN_MAPPING_HXX
+#define ODB_BOOST_DATE_TIME_PGSQL_GREGORIAN_MAPPING_HXX
+
+#include <boost/date_time/gregorian/gregorian_types.hpp>
+
+// By default map boost::gregorian::date to PostgreSQL DATE. We use the
+// NULL value to represent not_a_date_time.
+//
+#pragma db value(boost::gregorian::date) type("DATE") null
+
+#endif // ODB_BOOST_DATE_TIME_PGSQL_GREGORIAN_MAPPING_HXX
diff --git a/libodb-boost/odb/boost/date-time/pgsql/gregorian-traits.hxx b/libodb-boost/odb/boost/date-time/pgsql/gregorian-traits.hxx
new file mode 100644
index 0000000..a0dcce2
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/pgsql/gregorian-traits.hxx
@@ -0,0 +1,69 @@
+// file : odb/boost/date-time/pgsql/gregorian-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_PGSQL_GREGORIAN_TRAITS_HXX
+#define ODB_BOOST_DATE_TIME_PGSQL_GREGORIAN_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <boost/date_time/gregorian/gregorian_types.hpp>
+
+#include <odb/core.hxx>
+#include <odb/pgsql/traits.hxx>
+#include <odb/boost/date-time/exceptions.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <>
+ struct default_value_traits< ::boost::gregorian::date, id_date>
+ {
+ typedef ::boost::gregorian::date date;
+
+ typedef details::endian_traits endian_traits;
+
+ typedef date value_type;
+ typedef date query_type;
+ typedef int image_type;
+
+ static void
+ set_value (date& v, int i, bool is_null)
+ {
+ if (is_null)
+ v = date (::boost::date_time::not_a_date_time);
+ else
+ v = date (2000, 1, 1) + ::boost::gregorian::date_duration (
+ endian_traits::ntoh (i));
+ }
+
+ static void
+ set_image (int& i, bool& is_null, const date& v)
+ {
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date ())
+ is_null = true;
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else
+ {
+ is_null = false;
+ i = endian_traits::hton (
+ static_cast<int> ((v - date (2000, 1, 1)).days ()));
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits< ::boost::gregorian::date>
+ {
+ static const database_type_id db_type_id = id_date;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_BOOST_DATE_TIME_PGSQL_GREGORIAN_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/date-time/pgsql/posix-time-mapping.hxx b/libodb-boost/odb/boost/date-time/pgsql/posix-time-mapping.hxx
new file mode 100644
index 0000000..ace906d
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/pgsql/posix-time-mapping.hxx
@@ -0,0 +1,19 @@
+// file : odb/boost/date-time/pgsql/posix-time-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_PGSQL_POSIX_TIME_MAPPING_HXX
+#define ODB_BOOST_DATE_TIME_PGSQL_POSIX_TIME_MAPPING_HXX
+
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+// By default map boost::posix_time::ptime to PostgreSQL TIMESTAMP. We use
+// the NULL value to represent not_a_date_time.
+//
+#pragma db value(boost::posix_time::ptime) type("TIMESTAMP") null
+
+// By default map boost::posix_time::time_duration to PostgreSQL TIME. We
+// use the NULL value to represent not_a_date_time.
+//
+#pragma db value(boost::posix_time::time_duration) type("TIME") null
+
+#endif // ODB_BOOST_DATE_TIME_PGSQL_POSIX_TIME_MAPPING_HXX
diff --git a/libodb-boost/odb/boost/date-time/pgsql/posix-time-traits.hxx b/libodb-boost/odb/boost/date-time/pgsql/posix-time-traits.hxx
new file mode 100644
index 0000000..6713adc
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/pgsql/posix-time-traits.hxx
@@ -0,0 +1,169 @@
+// file : odb/boost/date-time/pgsql/posix-time-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_PGSQL_POSIX_TIME_TRAITS_HXX
+#define ODB_BOOST_DATE_TIME_PGSQL_POSIX_TIME_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+#include <odb/core.hxx>
+#include <odb/pgsql/traits.hxx>
+#include <odb/boost/date-time/exceptions.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ // Implementation of the mapping between boost::posix_time::ptime and
+ // PostgreSQL TIMESTAMP. TIMESTAMP values are stored as micro-seconds
+ // since the PostgreSQL epoch 2000-01-01.
+ //
+ template <>
+ struct default_value_traits< ::boost::posix_time::ptime, id_timestamp>
+ {
+ typedef ::boost::gregorian::date date;
+ typedef ::boost::posix_time::ptime ptime;
+ typedef ::boost::posix_time::time_duration time_duration;
+
+ typedef details::endian_traits endian_traits;
+
+ typedef ptime value_type;
+ typedef ptime query_type;
+ typedef long long image_type;
+
+ static const long long neg_inf = -0x7fffffffffffffffLL - 1;
+ static const long long pos_inf = 0x7fffffffffffffffLL;
+
+ static void
+ set_value (ptime& v, long long i, bool is_null)
+ {
+ if (is_null)
+ v = ptime (::boost::date_time::not_a_date_time);
+ else
+ {
+ i = endian_traits::ntoh (i);
+
+ if (i == neg_inf)
+ v = ptime (::boost::date_time::neg_infin);
+ else if (i == pos_inf)
+ v = ptime (::boost::date_time::pos_infin);
+ else
+ {
+ const ptime pg_epoch (date (2000, 1, 1),
+ time_duration (0, 0, 0));
+
+ // We need to split the microsecond image into hours and
+ // microseconds to avoid overflow during the fractional seconds
+ // calculation.
+ //
+ time_duration::hour_type h (
+ static_cast<time_duration::hour_type> (i / 3600000000LL));
+
+ i -= static_cast<long long> (h) * 3600000000LL;
+
+ v = pg_epoch +
+ time_duration (
+ h,
+ 0,
+ 0,
+ static_cast<time_duration::fractional_seconds_type> (
+ i * time_duration::ticks_per_second () / 1000000LL));
+ }
+ }
+ }
+
+ static void
+ set_image (long long& i, bool& is_null, const ptime& v)
+ {
+ is_null = false;
+
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date_time ())
+ is_null = true;
+ else if (v.is_neg_infinity ())
+ i = endian_traits::hton (neg_inf);
+ else if (v.is_pos_infinity ())
+ i = endian_traits::hton (pos_inf);
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else
+ {
+ const ptime pg_epoch (date (2000, 1, 1), time_duration (0, 0, 0));
+ i = endian_traits::hton (
+ static_cast<long long> ((v - pg_epoch).total_microseconds ()));
+ }
+ }
+ };
+
+ // Implementation of the mapping between boost::posix_time::time_duration
+ // and PostgreSQL TIME. The TIME values are stored as micro-seconds since
+ // 00:00:00.
+ //
+ template <>
+ struct default_value_traits< ::boost::posix_time::time_duration, id_time>
+ {
+ typedef ::boost::posix_time::time_duration time_duration;
+
+ typedef details::endian_traits endian_traits;
+
+ typedef time_duration value_type;
+ typedef time_duration query_type;
+ typedef long long image_type;
+
+ static void
+ set_value (time_duration& v, long long i, bool is_null)
+ {
+ if (is_null)
+ v = time_duration (::boost::date_time::not_a_date_time);
+ else
+ v = time_duration (
+ 0,
+ 0,
+ 0,
+ static_cast<time_duration::fractional_seconds_type> (
+ endian_traits::ntoh (i) * time_duration::ticks_per_second () /
+ 1000000LL));
+ }
+
+ static void
+ set_image (long long& i, bool& is_null, const time_duration& v)
+ {
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date_time ())
+ is_null = true;
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else if (v.hours () < 0 || v.hours () > 24)
+ throw odb::boost::date_time::value_out_of_range ();
+ else
+ {
+ is_null = false;
+ i = endian_traits::hton (
+ static_cast<long long> (v.total_microseconds ()));
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits< ::boost::posix_time::ptime>
+ {
+ static const database_type_id db_type_id = id_timestamp;
+ };
+
+ template <>
+ struct default_type_traits< ::boost::posix_time::time_duration>
+ {
+ static const database_type_id db_type_id = id_time;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_BOOST_DATE_TIME_PGSQL_POSIX_TIME_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/date-time/posix-time-common.options b/libodb-boost/odb/boost/date-time/posix-time-common.options
new file mode 100644
index 0000000..9421eeb
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/posix-time-common.options
@@ -0,0 +1,4 @@
+# file : odb/boost/date-time/posix-time-common.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
diff --git a/libodb-boost/odb/boost/date-time/posix-time-mssql.options b/libodb-boost/odb/boost/date-time/posix-time-mssql.options
new file mode 100644
index 0000000..656c718
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/posix-time-mssql.options
@@ -0,0 +1,11 @@
+# file : odb/boost/date-time/posix-time-mssql.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/boost/date-time/mssql/posix-time-mapping.hxx>'
+
+--hxx-prologue '#include <odb/boost/date-time/mssql/posix-time-traits.hxx>'
diff --git a/libodb-boost/odb/boost/date-time/posix-time-mysql.options b/libodb-boost/odb/boost/date-time/posix-time-mysql.options
new file mode 100644
index 0000000..5d5677d
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/posix-time-mysql.options
@@ -0,0 +1,11 @@
+# file : odb/boost/date-time/posix-time-mysql.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/boost/date-time/mysql/posix-time-mapping.hxx>'
+
+--hxx-prologue '#include <odb/boost/date-time/mysql/posix-time-traits.hxx>'
diff --git a/libodb-boost/odb/boost/date-time/posix-time-oracle.options b/libodb-boost/odb/boost/date-time/posix-time-oracle.options
new file mode 100644
index 0000000..0812568
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/posix-time-oracle.options
@@ -0,0 +1,11 @@
+# file : odb/boost/date-time/posix-time-oracle.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/boost/date-time/oracle/posix-time-mapping.hxx>'
+
+--hxx-prologue '#include <odb/boost/date-time/oracle/posix-time-traits.hxx>'
diff --git a/libodb-boost/odb/boost/date-time/posix-time-pgsql.options b/libodb-boost/odb/boost/date-time/posix-time-pgsql.options
new file mode 100644
index 0000000..d2830ff
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/posix-time-pgsql.options
@@ -0,0 +1,11 @@
+# file : odb/boost/date-time/posix-time-pgsql.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/boost/date-time/pgsql/posix-time-mapping.hxx>'
+
+--hxx-prologue '#include <odb/boost/date-time/pgsql/posix-time-traits.hxx>'
diff --git a/libodb-boost/odb/boost/date-time/posix-time-sqlite.options b/libodb-boost/odb/boost/date-time/posix-time-sqlite.options
new file mode 100644
index 0000000..bbe52dd
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/posix-time-sqlite.options
@@ -0,0 +1,11 @@
+# file : odb/boost/date-time/posix-time-sqlite.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/boost/date-time/sqlite/posix-time-mapping.hxx>'
+
+--hxx-prologue '#include <odb/boost/date-time/sqlite/posix-time-traits.hxx>'
diff --git a/libodb-boost/odb/boost/date-time/sqlite/gregorian-mapping.hxx b/libodb-boost/odb/boost/date-time/sqlite/gregorian-mapping.hxx
new file mode 100644
index 0000000..9880927
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/sqlite/gregorian-mapping.hxx
@@ -0,0 +1,14 @@
+// file : odb/boost/date-time/sqlite/gregorian-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_SQLITE_GREGORIAN_MAPPING_HXX
+#define ODB_BOOST_DATE_TIME_SQLITE_GREGORIAN_MAPPING_HXX
+
+#include <boost/date_time/gregorian/gregorian_types.hpp>
+
+// By default map boost::gregorian::date to TEXT. We use the
+// NULL value to represent not_a_date_time.
+//
+#pragma db value(boost::gregorian::date) type("TEXT") null
+
+#endif // ODB_BOOST_DATE_TIME_SQLITE_GREGORIAN_MAPPING_HXX
diff --git a/libodb-boost/odb/boost/date-time/sqlite/gregorian-traits.hxx b/libodb-boost/odb/boost/date-time/sqlite/gregorian-traits.hxx
new file mode 100644
index 0000000..b48b4dd
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/sqlite/gregorian-traits.hxx
@@ -0,0 +1,133 @@
+// file : odb/boost/date-time/sqlite/gregorian-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_SQLITE_GREGORIAN_TRAITS_HXX
+#define ODB_BOOST_DATE_TIME_SQLITE_GREGORIAN_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+#include <cstring> // std::memcpy
+#include <ctime> // std::time_t
+
+#include <boost/date_time/gregorian/gregorian_types.hpp> // date
+#include <boost/date_time/posix_time/posix_time_types.hpp> // time_duration
+#include <boost/date_time/gregorian/parsers.hpp> // from_simple_string
+#include <boost/date_time/gregorian/formatters.hpp> // to_iso_extended_string
+#include <boost/date_time/posix_time/conversion.hpp> // from_time_t
+
+#include <odb/core.hxx>
+#include <odb/details/buffer.hxx>
+#include <odb/sqlite/traits.hxx>
+
+#include <odb/boost/date-time/exceptions.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <>
+ struct default_value_traits< ::boost::gregorian::date, id_text>
+ {
+ typedef ::boost::gregorian::date date;
+ typedef date value_type;
+ typedef date query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (date& v,
+ const details::buffer& i,
+ std::size_t n,
+ bool is_null)
+ {
+ if (is_null)
+ v = date (::boost::date_time::not_a_date_time);
+ else
+ v = ::boost::gregorian::from_simple_string (
+ std::string (i.data (), n));
+ }
+
+ static void
+ set_image (details::buffer& i,
+ std::size_t& n,
+ bool& is_null,
+ const date& v)
+ {
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date ())
+ is_null = true;
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else
+ {
+ is_null = false;
+
+ const std::string& s (
+ ::boost::gregorian::to_iso_extended_string (v));
+
+ n = s.size ();
+
+ if (n > i.capacity ())
+ i.capacity (n);
+
+ std::memcpy (i.data (), s.data (), n);
+ }
+ }
+ };
+
+ template <>
+ struct default_value_traits< ::boost::gregorian::date, id_integer>
+ {
+ typedef ::boost::gregorian::date date;
+ typedef ::boost::posix_time::time_duration time_duration;
+ typedef ::boost::posix_time::ptime ptime;
+ typedef date value_type;
+ typedef date query_type;
+ typedef long long image_type;
+
+ static void
+ set_value (date& v, long long i, bool is_null)
+ {
+ if (is_null)
+ v = date (::boost::date_time::not_a_date_time);
+ else
+ v = ::boost::posix_time::from_time_t (
+ static_cast<std::time_t> (i)).date ();
+ }
+
+ static void
+ set_image (long long& i, bool& is_null, const date& v)
+ {
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date ())
+ is_null = true;
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else
+ {
+ is_null = false;
+
+ ptime epoch (date (1970, 1, 1), time_duration (0, 0, 0));
+ i = static_cast<long long> (
+ (ptime (v, time_duration (0, 0, 0)) - epoch).ticks () /
+ time_duration::ticks_per_second ());
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits< ::boost::gregorian::date>
+ {
+ static const database_type_id db_type_id = id_text;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_BOOST_DATE_TIME_SQLITE_GREGORIAN_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/date-time/sqlite/posix-time-mapping.hxx b/libodb-boost/odb/boost/date-time/sqlite/posix-time-mapping.hxx
new file mode 100644
index 0000000..544a7aa
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/sqlite/posix-time-mapping.hxx
@@ -0,0 +1,19 @@
+// file : odb/boost/date-time/sqlite/posix-time-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_SQLITE_POSIX_TIME_MAPPING_HXX
+#define ODB_BOOST_DATE_TIME_SQLITE_POSIX_TIME_MAPPING_HXX
+
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+// By default map boost::posix_time::ptime to TEXT. We use
+// the NULL value to represent not_a_date_time.
+//
+#pragma db value(boost::posix_time::ptime) type("TEXT") null
+
+// By default map boost::posix_time::time_duration to TEXT. We
+// use the NULL value to represent not_a_date_time.
+//
+#pragma db value(boost::posix_time::time_duration) type("TEXT") null
+
+#endif // ODB_BOOST_DATE_TIME_SQLITE_POSIX_TIME_MAPPING_HXX
diff --git a/libodb-boost/odb/boost/date-time/sqlite/posix-time-traits.hxx b/libodb-boost/odb/boost/date-time/sqlite/posix-time-traits.hxx
new file mode 100644
index 0000000..482f203
--- /dev/null
+++ b/libodb-boost/odb/boost/date-time/sqlite/posix-time-traits.hxx
@@ -0,0 +1,238 @@
+// file : odb/boost/date-time/sqlite/posix-time-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DATE_TIME_SQLITE_POSIX_TIME_TRAITS_HXX
+#define ODB_BOOST_DATE_TIME_SQLITE_POSIX_TIME_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+#include <cstring> // std::memcpy
+#include <ctime> // std::time_t
+
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include <boost/date_time/posix_time/time_parsers.hpp> // time_from_string
+#include <boost/date_time/posix_time/time_formatters.hpp> // to_simple_string
+#include <boost/date_time/posix_time/conversion.hpp> // from_time_t
+
+#include <odb/core.hxx>
+#include <odb/details/buffer.hxx>
+#include <odb/sqlite/traits.hxx>
+
+#include <odb/boost/date-time/exceptions.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <>
+ struct default_value_traits< ::boost::posix_time::ptime, id_text>
+ {
+ typedef ::boost::posix_time::ptime ptime;
+ typedef ptime value_type;
+ typedef ptime query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (ptime& v,
+ const details::buffer& i,
+ std::size_t n,
+ bool is_null)
+ {
+ if (is_null)
+ v = ptime (::boost::date_time::not_a_date_time);
+ else
+ v = ::boost::posix_time::time_from_string (
+ std::string (i.data (), n));
+ }
+
+ static void
+ set_image (details::buffer& i,
+ std::size_t& n,
+ bool& is_null,
+ const ptime& v)
+ {
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date_time ())
+ is_null = true;
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else
+ {
+ is_null = false;
+
+ std::string s (::boost::posix_time::to_iso_extended_string (v));
+
+ // Replace ',' in iso string with '.'. SQLite requires the
+ // latter for date/time functions.
+ //
+ std::size_t p = s.rfind (',');
+ if (p != std::string::npos)
+ s[p] = '.';
+
+ // Remove 'T' separator as Boost is unable to parse correctly.
+ //
+ p = s.find ('T');
+ if (p != std::string::npos)
+ s[p] = ' ';
+
+ n = s.size ();
+ if (n > i.capacity ())
+ i.capacity (n);
+
+ std::memcpy (i.data (), s.data (), n);
+ }
+ }
+ };
+
+ // Implementation of the mapping between boost::posix_time::ptime and
+ // SQLite INTEGER. The integer value represents UNIX time.
+ //
+ template <>
+ struct default_value_traits< ::boost::posix_time::ptime, id_integer>
+ {
+ typedef ::boost::gregorian::date date;
+ typedef ::boost::posix_time::ptime ptime;
+ typedef ::boost::posix_time::time_duration time_duration;
+ typedef ptime value_type;
+ typedef ptime query_type;
+ typedef long long image_type;
+
+ static void
+ set_value (ptime& v, long long i, bool is_null)
+ {
+ if (is_null)
+ v = ptime (::boost::date_time::not_a_date_time);
+ else
+ v = ::boost::posix_time::from_time_t (static_cast<std::time_t> (i));
+ }
+
+ static void
+ set_image (long long& i, bool& is_null, const ptime& v)
+ {
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date_time ())
+ is_null = true;
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else
+ {
+ is_null = false;
+
+ ptime epoch (date (1970, 1, 1), time_duration (0, 0, 0));
+ i = static_cast<long long> (
+ (v - epoch).ticks () / time_duration::ticks_per_second ());
+ }
+ }
+ };
+
+ template <>
+ struct default_value_traits< ::boost::posix_time::time_duration, id_text>
+ {
+ typedef ::boost::posix_time::time_duration time_duration;
+ typedef time_duration value_type;
+ typedef time_duration query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (time_duration& v,
+ const details::buffer& i,
+ std::size_t n,
+ bool is_null)
+ {
+ if (is_null)
+ v = time_duration (::boost::date_time::not_a_date_time);
+ else
+ {
+ v = ::boost::posix_time::duration_from_string (
+ std::string (i.data (), n));
+ }
+ }
+
+ static void
+ set_image (details::buffer& i,
+ std::size_t& n,
+ bool& is_null,
+ const time_duration& v)
+ {
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date_time ())
+ is_null = true;
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else if (v.total_seconds () < 0)
+ throw odb::boost::date_time::value_out_of_range ();
+ else
+ {
+ is_null = false;
+
+ const std::string& s (::boost::posix_time::to_simple_string (v));
+
+ n = s.size ();
+ if (n > i.capacity ())
+ i.capacity (n);
+
+ std::memcpy (i.data (), s.data (), n);
+ }
+ }
+ };
+
+ template <>
+ struct default_value_traits< ::boost::posix_time::time_duration, id_integer>
+ {
+ typedef ::boost::posix_time::time_duration time_duration;
+ typedef time_duration value_type;
+ typedef time_duration query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (time_duration& v, long long i, bool is_null)
+ {
+ if (is_null)
+ v = time_duration (::boost::date_time::not_a_date_time);
+ else
+ v = time_duration (0, 0, static_cast<time_duration::sec_type> (i));
+ }
+
+ static void
+ set_image (long long& i, bool& is_null, const time_duration& v)
+ {
+ if (v.is_special ())
+ {
+ if (v.is_not_a_date_time ())
+ is_null = true;
+ else
+ throw odb::boost::date_time::special_value ();
+ }
+ else
+ {
+ is_null = false;
+ i = static_cast<long long> (v.total_seconds ());
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits< ::boost::posix_time::ptime>
+ {
+ static const database_type_id db_type_id = id_text;
+ };
+
+ template <>
+ struct default_type_traits< ::boost::posix_time::time_duration>
+ {
+ static const database_type_id db_type_id = id_text;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_BOOST_DATE_TIME_SQLITE_POSIX_TIME_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/details/config.hxx b/libodb-boost/odb/boost/details/config.hxx
new file mode 100644
index 0000000..d5ddef8
--- /dev/null
+++ b/libodb-boost/odb/boost/details/config.hxx
@@ -0,0 +1,15 @@
+// file : odb/boost/details/config.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DETAILS_CONFIG_HXX
+#define ODB_BOOST_DETAILS_CONFIG_HXX
+
+// no pre
+
+#ifdef ODB_COMPILER
+# define LIBODB_BOOST_STATIC
+#endif
+
+// no post
+
+#endif // ODB_BOOST_DETAILS_CONFIG_HXX
diff --git a/libodb-boost/odb/boost/details/export.hxx b/libodb-boost/odb/boost/details/export.hxx
new file mode 100644
index 0000000..fa4e53c
--- /dev/null
+++ b/libodb-boost/odb/boost/details/export.hxx
@@ -0,0 +1,46 @@
+// file : odb/boost/details/export.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_DETAILS_EXPORT_HXX
+#define ODB_BOOST_DETAILS_EXPORT_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/boost/details/config.hxx> // LIBODB_BOOST_STATIC if ODB_COMPILER
+
+// Normally we don't export class templates (but do complete specializations),
+// inline functions, and classes with only inline member functions. Exporting
+// classes that inherit from non-exported/imported bases (e.g., std::string)
+// will end up badly. The only known workarounds are to not inherit or to not
+// export. Also, MinGW GCC doesn't like seeing non-exported function being
+// used before their inline definition. The workaround is to reorder code. In
+// the end it's all trial and error.
+
+#if defined(LIBODB_BOOST_STATIC) // Using static.
+# define LIBODB_BOOST_EXPORT
+#elif defined(LIBODB_BOOST_STATIC_BUILD) // Building static.
+# define LIBODB_BOOST_EXPORT
+#elif defined(LIBODB_BOOST_SHARED) // Using shared.
+# ifdef _WIN32
+# define LIBODB_BOOST_EXPORT __declspec(dllimport)
+# else
+# define LIBODB_BOOST_EXPORT
+# endif
+#elif defined(LIBODB_BOOST_SHARED_BUILD) // Building shared.
+# ifdef _WIN32
+# define LIBODB_BOOST_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_BOOST_EXPORT
+# endif
+#else
+// If none of the above macros are defined, then we assume we are being used
+// by some third-party build system that cannot/doesn't signal the library
+// type. Note that this fallback works for both static and shared but in case
+// of shared will be sub-optimal compared to having dllimport.
+//
+# define LIBODB_BOOST_EXPORT // Using static or shared.
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_BOOST_DETAILS_EXPORT_HXX
diff --git a/libodb-boost/odb/boost/exception.hxx b/libodb-boost/odb/boost/exception.hxx
new file mode 100644
index 0000000..f616a30
--- /dev/null
+++ b/libodb-boost/odb/boost/exception.hxx
@@ -0,0 +1,28 @@
+// file : odb/boost/exception.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_EXCEPTION_HXX
+#define ODB_BOOST_EXCEPTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/exceptions.hxx>
+
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+#include <odb/boost/details/export.hxx>
+
+namespace odb
+{
+ namespace boost
+ {
+ struct LIBODB_BOOST_EXPORT exception: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT = 0;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_BOOST_EXCEPTION_HXX
diff --git a/libodb-boost/odb/boost/lazy-ptr.hxx b/libodb-boost/odb/boost/lazy-ptr.hxx
new file mode 100644
index 0000000..35a8acf
--- /dev/null
+++ b/libodb-boost/odb/boost/lazy-ptr.hxx
@@ -0,0 +1,9 @@
+// file : odb/boost/lazy-ptr.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_LAZY_PTR_HXX
+#define ODB_BOOST_LAZY_PTR_HXX
+
+#include <odb/boost/smart-ptr/lazy-ptr.hxx>
+
+#endif // ODB_BOOST_LAZY_PTR_HXX
diff --git a/libodb-boost/odb/boost/multi-index.options b/libodb-boost/odb/boost/multi-index.options
new file mode 100644
index 0000000..731d197
--- /dev/null
+++ b/libodb-boost/odb/boost/multi-index.options
@@ -0,0 +1,7 @@
+# file : odb/boost/multi-index.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
+
+--odb-epilogue '#include <odb/boost/multi-index/container-traits.hxx>'
+--hxx-prologue '#include <odb/boost/multi-index/container-traits.hxx>'
diff --git a/libodb-boost/odb/boost/multi-index/container-traits.hxx b/libodb-boost/odb/boost/multi-index/container-traits.hxx
new file mode 100644
index 0000000..b027886
--- /dev/null
+++ b/libodb-boost/odb/boost/multi-index/container-traits.hxx
@@ -0,0 +1,215 @@
+// file : odb/boost/multi-index/container-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_MULTI_INDEX_CONTAINER_TRAITS_HXX
+#define ODB_BOOST_MULTI_INDEX_CONTAINER_TRAITS_HXX
+
+#include <boost/version.hpp>
+
+// Multi-index container is available since 1.32.0.
+//
+#if BOOST_VERSION >= 103200
+
+#include <odb/pre.hxx>
+
+#include <utility> // std::move
+
+#include <boost/mpl/find_if.hpp>
+#include <boost/mpl/distance.hpp>
+#include <boost/mpl/begin_end.hpp>
+
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/multi_index/random_access_index.hpp>
+
+#include <odb/container-traits.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ // Multi-index container can be ordered or set. Whether it is ordered
+ // depends on whether it has any sequenced or random_access indexes.
+ // If it is ordered, then we need to use one of these indexes to store
+ // elements in a specific order. Otherwise, we can use the first index
+ // since the order of elements in the database is not important. Note
+ // that there is a terminology clash between ODB and Boost multi-index.
+ // What ODB calls ordered containers, multi-index calls sequenced and
+ // random_access. And what ODB calls set containers, multi-index calls
+ // ordered and hashed.
+ //
+
+ // Test whether index is ordered.
+ //
+ template <typename I>
+ struct multi_index_ordered
+ {
+ static const bool value = false;
+ };
+
+ template <typename T>
+ struct multi_index_ordered< ::boost::multi_index::sequenced<T> >
+ {
+ static const bool value = true;
+ };
+
+ template <typename T>
+ struct multi_index_ordered< ::boost::multi_index::random_access<T> >
+ {
+ static const bool value = true;
+ };
+
+ // Get the index of the first ordered sub-index or -1 if none exists.
+ //
+ template <typename B, typename I, typename E>
+ struct multi_index_ordered_index
+ {
+ static const int value = ::boost::mpl::distance<B, I>::value;
+ };
+
+ template <typename B, typename E>
+ struct multi_index_ordered_index<B, E, E>
+ {
+ static const int value = -1;
+ };
+
+ template <typename V,
+ typename ISP,
+ typename A,
+ int N =
+ multi_index_ordered_index<
+ typename ::boost::mpl::begin<ISP>::type,
+ typename ::boost::mpl::find_if<
+ ISP, multi_index_ordered< ::boost::mpl::_1 > >::type,
+ typename ::boost::mpl::end<ISP>::type>::value>
+ class multi_index_traits
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = false;
+
+ typedef ::boost::multi_index_container<V, ISP, A> container_type;
+ typedef typename container_type::template nth_index<N>::type
+ ordered_container_type;
+
+ typedef V value_type;
+ typedef typename ordered_container_type::size_type index_type;
+
+ typedef ordered_functions<index_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ const ordered_container_type& oc (c.template get<N> ());
+ index_type i (0);
+ for (typename ordered_container_type::const_iterator j (oc.begin ()),
+ e (oc.end ()); j != e; ++j)
+ f.insert (i++, *j);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ ordered_container_type& oc (c.template get<N> ());
+ oc.clear ();
+
+ while (more)
+ {
+ index_type dummy;
+ value_type v;
+ more = f.select (dummy, v);
+#ifdef ODB_CXX11
+ oc.push_back (std::move (v));
+#else
+ oc.push_back (v);
+#endif
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ const ordered_container_type& oc (c.template get<N> ());
+ index_type i (0);
+ for (typename ordered_container_type::const_iterator j (oc.begin ()),
+ e (oc.end ()); j != e; ++j)
+ f.insert (i++, *j);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+
+ // Set specialization.
+ //
+ template <typename V, typename ISP, typename A>
+ class multi_index_traits<V, ISP, A, -1>
+ {
+ public:
+ static const container_kind kind = ck_set;
+ static const bool smart = false;
+
+ typedef ::boost::multi_index_container<V, ISP, A> container_type;
+ typedef V value_type;
+
+ typedef set_functions<value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ value_type v;
+ more = f.select (v);
+#ifdef ODB_CXX11
+ c.insert (std::move (v));
+#else
+ c.insert (v);
+#endif
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+
+ template <typename V, typename ISP, typename A>
+ class access::container_traits< ::boost::multi_index_container<V, ISP, A> >
+ : public multi_index_traits<V, ISP, A>
+ {
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // BOOST_VERSION
+#endif // ODB_BOOST_MULTI_INDEX_CONTAINER_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/optional.options b/libodb-boost/odb/boost/optional.options
new file mode 100644
index 0000000..d57b22b
--- /dev/null
+++ b/libodb-boost/odb/boost/optional.options
@@ -0,0 +1,7 @@
+# file : odb/boost/optional.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
+
+--odb-epilogue '#include <odb/boost/optional/wrapper-traits.hxx>'
+--hxx-prologue '#include <odb/boost/optional/wrapper-traits.hxx>'
diff --git a/libodb-boost/odb/boost/optional/wrapper-traits.hxx b/libodb-boost/odb/boost/optional/wrapper-traits.hxx
new file mode 100644
index 0000000..80d4319
--- /dev/null
+++ b/libodb-boost/odb/boost/optional/wrapper-traits.hxx
@@ -0,0 +1,63 @@
+// file : odb/boost/optional/wrapper-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_OPTIONAL_WRAPPER_TRAITS_HXX
+#define ODB_BOOST_OPTIONAL_WRAPPER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <boost/none.hpp>
+#include <boost/optional.hpp>
+
+#include <odb/wrapper-traits.hxx>
+
+namespace odb
+{
+ template <typename T>
+ class wrapper_traits< ::boost::optional<T> >
+ {
+ public:
+ typedef T wrapped_type;
+ typedef ::boost::optional<T> wrapper_type;
+
+ // T can be const.
+ //
+ typedef
+ typename odb::details::meta::remove_const<T>::result
+ unrestricted_wrapped_type;
+
+ static const bool null_handler = true;
+ static const bool null_default = true;
+
+ static bool
+ get_null (const wrapper_type& o)
+ {
+ return !o;
+ }
+
+ static void
+ set_null (wrapper_type& o)
+ {
+ o = ::boost::none;
+ }
+
+ static const wrapped_type&
+ get_ref (const wrapper_type& o)
+ {
+ return *o;
+ }
+
+ static unrestricted_wrapped_type&
+ set_ref (wrapper_type& o)
+ {
+ if (!o)
+ o = unrestricted_wrapped_type ();
+
+ return const_cast<unrestricted_wrapped_type&> (*o);
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_BOOST_OPTIONAL_WRAPPER_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/smart-ptr.options b/libodb-boost/odb/boost/smart-ptr.options
new file mode 100644
index 0000000..2b0f2a4
--- /dev/null
+++ b/libodb-boost/odb/boost/smart-ptr.options
@@ -0,0 +1,19 @@
+# file : odb/boost/smart-ptr.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
+
+# Make boost::shared_ptr the default object pointer.
+#
+--hxx-prologue '#include <boost/shared_ptr.hpp>'
+--default-pointer ::boost::shared_ptr
+
+# Include pointer traits.
+#
+--odb-epilogue '#include <odb/boost/smart-ptr/pointer-traits.hxx>'
+--hxx-prologue '#include <odb/boost/smart-ptr/pointer-traits.hxx>'
+
+# Include wrapper traits.
+#
+--odb-epilogue '#include <odb/boost/smart-ptr/wrapper-traits.hxx>'
+--hxx-prologue '#include <odb/boost/smart-ptr/wrapper-traits.hxx>'
diff --git a/libodb-boost/odb/boost/smart-ptr/lazy-pointer-traits.hxx b/libodb-boost/odb/boost/smart-ptr/lazy-pointer-traits.hxx
new file mode 100644
index 0000000..92b5441
--- /dev/null
+++ b/libodb-boost/odb/boost/smart-ptr/lazy-pointer-traits.hxx
@@ -0,0 +1,61 @@
+// file : odb/boost/smart-ptr/lazy-pointer-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_SMART_PTR_LAZY_POINTER_TRAITS_HXX
+#define ODB_BOOST_SMART_PTR_LAZY_POINTER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/pointer-traits.hxx>
+#include <odb/boost/smart-ptr/lazy-ptr.hxx>
+
+namespace odb
+{
+ template <typename T>
+ class pointer_traits<boost::lazy_shared_ptr<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_shared;
+ static const bool lazy = true;
+
+ typedef T element_type;
+ typedef boost::lazy_shared_ptr<element_type> pointer_type;
+ typedef ::boost::shared_ptr<element_type> eager_pointer_type;
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ template <class O /* = T */>
+ static typename object_traits<O>::id_type
+ object_id (const pointer_type& p)
+ {
+ return p.template object_id<O> ();
+ }
+ };
+
+ template <typename T>
+ class pointer_traits<boost::lazy_weak_ptr<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_weak;
+ static const bool lazy = true;
+
+ typedef T element_type;
+ typedef boost::lazy_weak_ptr<element_type> pointer_type;
+ typedef boost::lazy_shared_ptr<element_type> strong_pointer_type;
+ typedef ::boost::weak_ptr<element_type> eager_pointer_type;
+
+ static strong_pointer_type
+ lock (const pointer_type& p)
+ {
+ return p.lock ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_BOOST_SMART_PTR_LAZY_POINTER_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/smart-ptr/lazy-ptr.hxx b/libodb-boost/odb/boost/smart-ptr/lazy-ptr.hxx
new file mode 100644
index 0000000..ec25eb1
--- /dev/null
+++ b/libodb-boost/odb/boost/smart-ptr/lazy-ptr.hxx
@@ -0,0 +1,269 @@
+// file : odb/boost/smart-ptr/lazy-ptr.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_SMART_PTR_LAZY_PTR_HXX
+#define ODB_BOOST_SMART_PTR_LAZY_PTR_HXX
+
+#include <odb/pre.hxx>
+
+#include <memory> // std::auto_ptr
+
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+
+#include <odb/forward.hxx> // odb::database
+#include <odb/traits.hxx>
+#include <odb/lazy-ptr-impl.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ namespace boost
+ {
+ template <class T>
+ class lazy_weak_ptr;
+
+ //
+ //
+ template <class T>
+ class lazy_shared_ptr
+ {
+ // The standard shared_ptr interface.
+ //
+ public:
+ typedef T element_type;
+
+ lazy_shared_ptr ();
+ template <class Y> explicit lazy_shared_ptr (Y*);
+ template <class Y, class D> lazy_shared_ptr (Y*, D);
+ template <class Y, class D, class A> lazy_shared_ptr (Y*, D, A);
+
+ lazy_shared_ptr (const lazy_shared_ptr&);
+ template <class Y> lazy_shared_ptr (const lazy_shared_ptr<Y>&);
+ template <class Y> explicit lazy_shared_ptr (const lazy_weak_ptr<Y>&);
+ template <class Y> explicit lazy_shared_ptr (std::auto_ptr<Y>&);
+
+ ~lazy_shared_ptr ();
+
+ lazy_shared_ptr& operator= (const lazy_shared_ptr&);
+ template <class Y> lazy_shared_ptr& operator= (const lazy_shared_ptr<Y>&);
+ template <class Y> lazy_shared_ptr& operator= (std::auto_ptr<Y>&);
+
+ void swap (lazy_shared_ptr&);
+ void reset ();
+ template <class Y> void reset (Y*);
+ template <class Y, class D> void reset (Y*, D);
+ template <class Y, class D, class A> void reset (Y*, D, A);
+
+ T& operator* () const;
+ T* operator-> () const;
+ T* get () const;
+
+ bool unique () const;
+ long use_count () const;
+
+ typedef ::boost::shared_ptr<T> lazy_shared_ptr::*unspecified_bool_type;
+ operator unspecified_bool_type () const
+ {
+ return (p_ || i_) ? &lazy_shared_ptr::p_ : 0;
+ }
+
+ // Initialization/assignment from shared_ptr and weak_ptr.
+ //
+ public:
+ template <class Y> lazy_shared_ptr (const ::boost::shared_ptr<Y>&);
+ template <class Y> explicit lazy_shared_ptr (const ::boost::weak_ptr<Y>&);
+
+ template <class Y> lazy_shared_ptr& operator= (const ::boost::shared_ptr<Y>&);
+
+ // Lazy loading interface.
+ //
+ public:
+ typedef odb::database database_type;
+
+ // 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;
+
+ ::boost::shared_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.
+ //
+ ::boost::shared_ptr<T> get_eager () const;
+
+ template <class DB, class ID> lazy_shared_ptr (DB&, const ID&);
+ template <class DB, class Y> lazy_shared_ptr (DB&, Y*);
+ template <class DB, class Y, class D> lazy_shared_ptr (DB&, Y*, D);
+ template <class DB, class Y, class D, class A> lazy_shared_ptr (DB&, Y*, D, A);
+ template <class DB, class Y> lazy_shared_ptr (DB&, std::auto_ptr<Y>&);
+ template <class DB, class Y> lazy_shared_ptr (DB&, const ::boost::shared_ptr<Y>&);
+ template <class DB, class Y> lazy_shared_ptr (DB&, const ::boost::weak_ptr<Y>&);
+
+ template <class DB, class ID> void reset (DB&, const ID&);
+ template <class DB, class Y> void reset (DB&, Y*);
+ template <class DB, class Y, class D> void reset (DB&, Y*, D);
+ template <class DB, class Y, class D, class A> void reset (DB&, Y*, D, A);
+ template <class DB, class Y> void reset (DB&, std::auto_ptr<Y>&);
+ template <class DB, class Y> void reset (DB&, const ::boost::shared_ptr<Y>&);
+
+#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+ template <class O = T>
+#else
+ template <class O /* = T */>
+#endif
+ typename object_traits<O>::id_type object_id () const;
+
+ database_type& database () const;
+
+ // Helpers.
+ //
+ public:
+ template <class Y> bool equal (const lazy_shared_ptr<Y>&) const;
+
+ private:
+ template <class Y> friend class lazy_shared_ptr;
+ template <class Y> friend class lazy_weak_ptr;
+
+ // For lazy_weak_ptr::lock().
+ //
+ lazy_shared_ptr (const ::boost::shared_ptr<T>& p,
+ const lazy_ptr_impl<T>& i)
+ : p_ (p), i_ (i) {}
+
+ private:
+ mutable ::boost::shared_ptr<T> p_;
+ mutable lazy_ptr_impl<T> i_;
+ };
+
+ // operator< and operator<< are not provided.
+ //
+ template<class T, class Y>
+ bool operator== (const lazy_shared_ptr<T>&, const lazy_shared_ptr<Y>&);
+
+ template<class T, class Y>
+ bool operator!= (const lazy_shared_ptr<T>&, const lazy_shared_ptr<Y>&);
+
+ template<class T> void swap (lazy_shared_ptr<T>&, lazy_shared_ptr<T>&);
+
+ template<class D, class T>
+ D* get_deleter (const lazy_shared_ptr<T>&);
+
+ //
+ //
+ template <class T>
+ class lazy_weak_ptr
+ {
+ // The standard weak_ptr interface.
+ //
+ public:
+ typedef T element_type;
+
+ lazy_weak_ptr ();
+ template <class Y> lazy_weak_ptr (const lazy_shared_ptr<Y>&);
+ lazy_weak_ptr (const lazy_weak_ptr&);
+ template <class Y> lazy_weak_ptr (const lazy_weak_ptr<Y>&);
+
+ ~lazy_weak_ptr ();
+
+ lazy_weak_ptr& operator= (const lazy_weak_ptr&);
+ template <class Y> lazy_weak_ptr& operator= (const lazy_weak_ptr<Y>&);
+ template <class Y> lazy_weak_ptr& operator= (const lazy_shared_ptr<Y>&);
+
+ void swap (lazy_weak_ptr<T>&);
+ void reset ();
+
+ long use_count () const;
+ bool expired () const;
+
+ lazy_shared_ptr<T> lock () const;
+
+ // Initialization/assignment from shared_ptr and weak_ptr.
+ //
+ public:
+ template <class Y> lazy_weak_ptr (const ::boost::weak_ptr<Y>&);
+ template <class Y> lazy_weak_ptr (const ::boost::shared_ptr<Y>&);
+
+ template <class Y> lazy_weak_ptr& operator= (const ::boost::weak_ptr<Y>&);
+ template <class Y> lazy_weak_ptr& operator= (const ::boost::shared_ptr<Y>&);
+
+ // Lazy loading interface.
+ //
+ public:
+ typedef odb::database database_type;
+
+ // expired() loaded()
+ //
+ // true true expired pointer to transient object
+ // false true valid pointer to persistent object
+ // true false expired pointer to persistent object
+ // false false valid pointer to transient object
+ //
+ bool loaded () const;
+
+ // Performs both lock and load.
+ //
+ ::boost::shared_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.
+ //
+ ::boost::weak_ptr<T> get_eager () const;
+
+ template <class DB, class ID> lazy_weak_ptr (DB&, const ID&);
+ template <class DB, class Y> lazy_weak_ptr (DB&, const ::boost::shared_ptr<Y>&);
+ template <class DB, class Y> lazy_weak_ptr (DB&, const ::boost::weak_ptr<Y>&);
+
+ template <class DB, class ID> void reset (DB&, const ID&);
+ template <class DB, class Y> void reset (DB&, const ::boost::shared_ptr<Y>&);
+ template <class DB, class Y> void reset (DB&, const ::boost::weak_ptr<Y>&);
+
+ // The object_id() function can only be called when the object is
+ // persistent, or: expired() XOR loaded() (can use != for XOR).
+ //
+#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+ template <class O = T>
+#else
+ template <class O /* = T */>
+#endif
+ typename object_traits<O>::id_type object_id () const;
+
+ database_type& database () const;
+
+ private:
+ template <class Y> friend class lazy_shared_ptr;
+ template <class Y> friend class lazy_weak_ptr;
+
+ mutable ::boost::weak_ptr<T> p_;
+ mutable lazy_ptr_impl<T> i_;
+ };
+
+ // operator< is not provided.
+ //
+ template<class T> void swap (lazy_weak_ptr<T>&, lazy_weak_ptr<T>&);
+ }
+}
+
+#include <odb/boost/smart-ptr/lazy-ptr.ixx>
+#include <odb/boost/smart-ptr/lazy-ptr.txx>
+
+#include <odb/boost/smart-ptr/lazy-pointer-traits.hxx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_BOOST_SMART_PTR_LAZY_PTR_HXX
diff --git a/libodb-boost/odb/boost/smart-ptr/lazy-ptr.ixx b/libodb-boost/odb/boost/smart-ptr/lazy-ptr.ixx
new file mode 100644
index 0000000..4baad87
--- /dev/null
+++ b/libodb-boost/odb/boost/smart-ptr/lazy-ptr.ixx
@@ -0,0 +1,675 @@
+// file : odb/boost/smart-ptr/lazy-ptr.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace boost
+ {
+ //
+ // lazy_shared_ptr
+ //
+
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr () {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (Y* p): p_ (p) {}
+
+ template <class T>
+ template <class Y, class D>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (Y* p, D d): p_ (p, d) {}
+
+ template <class T>
+ template <class Y, class D, class A>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (Y* p, D d, A a): p_ (p, d, a) {}
+
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const lazy_shared_ptr& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const lazy_shared_ptr<Y>& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const lazy_weak_ptr<Y>& r): i_ (r.i_)
+ {
+ // If the pointer has expired but can be re-loaded, then don't throw.
+ //
+ p_ = r.lock ().get_eager ();
+
+ if (!p_ && !i_)
+ throw ::boost::bad_weak_ptr ();
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (std::auto_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ ~lazy_shared_ptr () {}
+
+ template <class T>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (const lazy_shared_ptr& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (const lazy_shared_ptr<Y>& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (std::auto_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ inline void lazy_shared_ptr<T>::
+ swap (lazy_shared_ptr& b)
+ {
+ p_.swap (b.p_);
+ i_.swap (b.i_);
+ }
+
+ template <class T>
+ inline void lazy_shared_ptr<T>::
+ reset ()
+ {
+ p_.reset ();
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (Y* p)
+ {
+ p_.reset (p);
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class Y, class D>
+ inline void lazy_shared_ptr<T>::
+ reset (Y* p, D d)
+ {
+ p_.reset (p, d);
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class Y, class D, class A>
+ inline void lazy_shared_ptr<T>::
+ reset (Y* p, D d, A a)
+ {
+ p_.reset (p, d, a);
+ i_.reset ();
+ }
+
+ template <class T>
+ inline T& lazy_shared_ptr<T>::
+ operator* () const
+ {
+ return *p_;
+ }
+
+ template <class T>
+ inline T* lazy_shared_ptr<T>::
+ operator-> () const
+ {
+ return p_.operator-> ();
+ }
+
+ template <class T>
+ inline T* lazy_shared_ptr<T>::
+ get () const
+ {
+ return p_.get ();
+ }
+
+ template <class T>
+ inline bool lazy_shared_ptr<T>::
+ unique () const
+ {
+ return p_.unique ();
+ }
+
+ template <class T>
+ inline long lazy_shared_ptr<T>::
+ use_count () const
+ {
+ return p_.use_count ();
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const ::boost::shared_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const ::boost::weak_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (const ::boost::shared_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ inline bool lazy_shared_ptr<T>::
+ loaded () const
+ {
+ bool i (i_);
+ return !p_ != i; // !p_ XOR i_
+ }
+
+ template <class T>
+ inline ::boost::shared_ptr<T> lazy_shared_ptr<T>::
+ load () const
+ {
+ if (!p_ && i_)
+ p_ = i_.template load<T> (true); // Reset id.
+
+ return p_;
+ }
+
+ template <class T>
+ inline void lazy_shared_ptr<T>::
+ unload () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ if (p_)
+ {
+ if (i_.database () != 0)
+ i_.reset_id (object_traits<object_type>::id (*p_));
+
+ p_.reset ();
+ }
+ }
+
+ template <class T>
+ inline ::boost::shared_ptr<T> lazy_shared_ptr<T>::
+ get_eager () const
+ {
+ return p_;
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, const ID& id): i_ (db, id) {}
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, Y* p)
+ : p_ (p)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y, class D>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, Y* p, D d)
+ : p_ (p, d)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y, class D, class A>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, Y* p, D d, A a)
+ : p_ (p, d, a)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, std::auto_ptr<Y>& r)
+ : p_ (r)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, const ::boost::shared_ptr<Y>& r)
+ : p_ (r)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, const ::boost::weak_ptr<Y>& r)
+ : p_ (r)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, const ID& id)
+ {
+ p_.reset ();
+ i_.reset (db, id);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, Y* p)
+ {
+ p_.reset (p);
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y, class D>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, Y* p, D d)
+ {
+ p_.reset (p, d);
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y, class D, class A>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, Y* p, D d, A a)
+ {
+ p_.reset (p, d, a);
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, std::auto_ptr<Y>& r)
+ {
+ p_ = r;
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, const ::boost::shared_ptr<Y>& r)
+ {
+ p_ = r;
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class O>
+ inline typename object_traits<O>::id_type lazy_shared_ptr<T>::
+ object_id () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ return p_
+ ? object_traits<object_type>::id (*p_)
+ : i_.template object_id<O> ();
+ }
+
+ template <class T>
+ inline typename lazy_shared_ptr<T>::database_type& lazy_shared_ptr<T>::
+ database () const
+ {
+ return *i_.database ();
+ }
+
+ template<class T, class Y>
+ inline bool
+ operator== (const lazy_shared_ptr<T>& a, const lazy_shared_ptr<Y>& b)
+ {
+ return a.equal (b);
+ }
+
+ template<class T, class Y>
+ inline bool
+ operator!= (const lazy_shared_ptr<T>& a, const lazy_shared_ptr<Y>& b)
+ {
+ return !a.equal (b);
+ }
+
+ template<class T>
+ inline void
+ swap (lazy_shared_ptr<T>& a, lazy_shared_ptr<T>& b)
+ {
+ a.swap (b);
+ }
+
+ template<class D, class T>
+ inline D*
+ get_deleter (const lazy_shared_ptr<T>& p)
+ {
+ return ::boost::get_deleter<D> (p.p_);
+ }
+
+
+ //
+ // lazy_weak_ptr
+ //
+
+ template <class T>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr () {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const lazy_shared_ptr<Y>& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const lazy_weak_ptr& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const lazy_weak_ptr<Y>& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ inline lazy_weak_ptr<T>::
+ ~lazy_weak_ptr () {}
+
+ template <class T>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const lazy_weak_ptr& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const lazy_weak_ptr<Y>& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const lazy_shared_ptr<Y>& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ inline void lazy_weak_ptr<T>::
+ swap (lazy_weak_ptr<T>& r)
+ {
+ p_.swap (r.p_);
+ i_.swap (r.i_);
+ }
+
+ template <class T>
+ inline void lazy_weak_ptr<T>::
+ reset ()
+ {
+ p_.reset ();
+ i_.reset ();
+ }
+
+ template <class T>
+ inline long lazy_weak_ptr<T>::
+ use_count () const
+ {
+ return p_.use_count ();
+ }
+
+ template <class T>
+ inline bool lazy_weak_ptr<T>::
+ expired () const
+ {
+ return p_.expired ();
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const ::boost::weak_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const ::boost::shared_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const ::boost::weak_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const ::boost::shared_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ inline bool lazy_weak_ptr<T>::
+ loaded () const
+ {
+ bool i (i_);
+ return expired () != i; // expired () XOR i_
+ }
+
+ template <class T>
+ inline lazy_shared_ptr<T> lazy_weak_ptr<T>::
+ lock () const
+ {
+ return lazy_shared_ptr<T> (p_.lock (), i_);
+ }
+
+ template <class T>
+ inline ::boost::shared_ptr<T> lazy_weak_ptr<T>::
+ load () const
+ {
+ ::boost::shared_ptr<T> r (p_.lock ());
+
+ if (!r && i_)
+ {
+ r = i_.template load<T> (false); // Keep id.
+ p_ = r;
+ }
+
+ return r;
+ }
+
+ template <class T>
+ inline void lazy_weak_ptr<T>::
+ unload () const
+ {
+ // With weak pointer we always keep i_ up to date.
+ //
+ p_.reset ();
+ }
+
+ template <class T>
+ inline ::boost::weak_ptr<T> lazy_weak_ptr<T>::
+ get_eager () const
+ {
+ return p_;
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (DB& db, const ID& id): i_ (db, id) {}
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (DB& db, const ::boost::shared_ptr<Y>& r)
+ : p_ (r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ if (r)
+ i_.reset (db, object_traits<object_type>::id (*r));
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (DB& db, const ::boost::weak_ptr<Y>& r)
+ : p_ (r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ ::boost::shared_ptr<T> sp (p_.lock ());
+
+ if (sp)
+ i_.reset (db, object_traits<object_type>::id (*sp));
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline void lazy_weak_ptr<T>::
+ reset (DB& db, const ID& id)
+ {
+ p_.reset ();
+ i_.reset (db, id);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_weak_ptr<T>::
+ reset (DB& db, const ::boost::shared_ptr<Y>& r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ p_ = r;
+
+ if (r)
+ i_.reset (db, object_traits<object_type>::id (*r));
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_weak_ptr<T>::
+ reset (DB& db, const ::boost::weak_ptr<Y>& r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ p_ = r;
+ ::boost::shared_ptr<T> sp (p_.lock ());
+
+ if (sp)
+ i_.reset (db, object_traits<object_type>::id (*sp));
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class O>
+ inline typename object_traits<O>::id_type lazy_weak_ptr<T>::
+ object_id () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ ::boost::shared_ptr<T> sp (p_.lock ());
+ return sp
+ ? object_traits<object_type>::id (*sp)
+ : i_.template object_id<O> ();
+ }
+
+ template <class T>
+ inline typename lazy_weak_ptr<T>::database_type& lazy_weak_ptr<T>::
+ database () const
+ {
+ return *i_.database ();
+ }
+
+ template<class T>
+ inline void
+ swap (lazy_weak_ptr<T>& a, lazy_weak_ptr<T>& b)
+ {
+ a.swap (b);
+ }
+ }
+}
diff --git a/libodb-boost/odb/boost/smart-ptr/lazy-ptr.txx b/libodb-boost/odb/boost/smart-ptr/lazy-ptr.txx
new file mode 100644
index 0000000..c0cebd9
--- /dev/null
+++ b/libodb-boost/odb/boost/smart-ptr/lazy-ptr.txx
@@ -0,0 +1,43 @@
+// file : odb/boost/smart-ptr/lazy-ptr.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace boost
+ {
+ //
+ // lazy_shared_ptr
+ //
+
+ template <class T>
+ template <class Y>
+ bool lazy_shared_ptr<T>::
+ equal (const lazy_shared_ptr<Y>& r) const
+ {
+ bool t1 (!p_ == loaded ());
+ bool t2 (!r.p_ == r.loaded ());
+
+ // If both are transient, then compare the underlying pointers.
+ //
+ if (t1 && t2)
+ return p_ == r.p_;
+
+ // If one is transient and the other is persistent, then compare
+ // the underlying pointers but only if they are non NULL. Note
+ // that an unloaded persistent object is always unequal to a
+ // transient object.
+ //
+ if (t1 || t2)
+ return p_ == r.p_ && p_;
+
+ // If both objects are persistent, then we compare databases and
+ // object ids.
+ //
+ typedef typename object_traits<T>::object_type object_type1;
+ typedef typename object_traits<Y>::object_type object_type2;
+
+ return i_.database () == r.i_.database () &&
+ object_id<object_type1> () == r.template object_id<object_type2> ();
+ }
+ }
+}
diff --git a/libodb-boost/odb/boost/smart-ptr/pointer-traits.hxx b/libodb-boost/odb/boost/smart-ptr/pointer-traits.hxx
new file mode 100644
index 0000000..8882822
--- /dev/null
+++ b/libodb-boost/odb/boost/smart-ptr/pointer-traits.hxx
@@ -0,0 +1,110 @@
+// file : odb/boost/smart-ptr/pointer-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_SMART_PTR_POINTER_TRAITS_HXX
+#define ODB_BOOST_SMART_PTR_POINTER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+
+#include <odb/pointer-traits.hxx>
+#include <odb/details/meta/remove-const.hxx>
+
+namespace odb
+{
+ // Specialization for boost::shared_ptr.
+ //
+ template <typename T>
+ class pointer_traits< ::boost::shared_ptr<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_shared;
+ static const bool lazy = false;
+
+ typedef T element_type;
+ typedef ::boost::shared_ptr<element_type> pointer_type;
+ typedef ::boost::shared_ptr<const element_type> const_pointer_type;
+ typedef typename odb::details::meta::remove_const<element_type>::result
+ unrestricted_element_type;
+ typedef ::boost::shared_ptr<unrestricted_element_type>
+ unrestricted_pointer_type;
+ typedef smart_ptr_guard<pointer_type> guard;
+
+ static element_type*
+ get_ptr (const pointer_type& p)
+ {
+ return p.get ();
+ }
+
+ static element_type&
+ get_ref (const pointer_type& p)
+ {
+ return *p;
+ }
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ static unrestricted_pointer_type
+ const_pointer_cast (const pointer_type& p)
+ {
+ return ::boost::const_pointer_cast<unrestricted_element_type> (p);
+ }
+
+ template <typename T1>
+ static ::boost::shared_ptr<T1>
+ static_pointer_cast (const pointer_type& p)
+ {
+ return ::boost::static_pointer_cast<T1> (p);
+ }
+
+ template <typename T1>
+ static ::boost::shared_ptr<T1>
+ dynamic_pointer_cast (const pointer_type& p)
+ {
+ return ::boost::dynamic_pointer_cast<T1> (p);
+ }
+
+ public:
+ static void*
+ allocate (std::size_t n)
+ {
+ return operator new (n);
+ }
+
+ static void
+ free (void* p)
+ {
+ operator delete (p);
+ }
+ };
+
+ // Specialization for boost::weak_ptr.
+ //
+ template <typename T>
+ class pointer_traits< ::boost::weak_ptr<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_weak;
+ static const bool lazy = false;
+
+ typedef T element_type;
+ typedef ::boost::weak_ptr<element_type> pointer_type;
+ typedef ::boost::shared_ptr<element_type> strong_pointer_type;
+
+ static strong_pointer_type
+ lock (const pointer_type& p)
+ {
+ return p.lock ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_BOOST_SMART_PTR_POINTER_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/smart-ptr/wrapper-traits.hxx b/libodb-boost/odb/boost/smart-ptr/wrapper-traits.hxx
new file mode 100644
index 0000000..bc3229d
--- /dev/null
+++ b/libodb-boost/odb/boost/smart-ptr/wrapper-traits.hxx
@@ -0,0 +1,64 @@
+// file : odb/boost/smart-ptr/wrapper-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_SMART_PTR_WRAPPER_TRAITS_HXX
+#define ODB_BOOST_SMART_PTR_WRAPPER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <boost/shared_ptr.hpp>
+
+#include <odb/wrapper-traits.hxx>
+
+namespace odb
+{
+ // Specialization for boost::shared_ptr.
+ //
+ template <typename T>
+ class wrapper_traits< ::boost::shared_ptr<T> >
+ {
+ public:
+ typedef T wrapped_type;
+ typedef ::boost::shared_ptr<T> wrapper_type;
+
+ // T can be const.
+ //
+ typedef
+ typename odb::details::meta::remove_const<T>::result
+ unrestricted_wrapped_type;
+
+ static const bool null_handler = true;
+ static const bool null_default = false;
+
+ static bool
+ get_null (const wrapper_type& p)
+ {
+ return !p;
+ }
+
+ static void
+ set_null (wrapper_type& p)
+ {
+ p.reset ();
+ }
+
+ static const wrapped_type&
+ get_ref (const wrapper_type& p)
+ {
+ return *p;
+ }
+
+ static unrestricted_wrapped_type&
+ set_ref (wrapper_type& p)
+ {
+ if (!p)
+ p.reset (new unrestricted_wrapped_type);
+
+ return const_cast<unrestricted_wrapped_type&> (*p);
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_BOOST_SMART_PTR_WRAPPER_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/unordered.options b/libodb-boost/odb/boost/unordered.options
new file mode 100644
index 0000000..56ee6c7
--- /dev/null
+++ b/libodb-boost/odb/boost/unordered.options
@@ -0,0 +1,7 @@
+# file : odb/boost/unordered.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
+
+--odb-epilogue '#include <odb/boost/unordered/container-traits.hxx>'
+--hxx-prologue '#include <odb/boost/unordered/container-traits.hxx>'
diff --git a/libodb-boost/odb/boost/unordered/container-traits.hxx b/libodb-boost/odb/boost/unordered/container-traits.hxx
new file mode 100644
index 0000000..7814971
--- /dev/null
+++ b/libodb-boost/odb/boost/unordered/container-traits.hxx
@@ -0,0 +1,275 @@
+// file : odb/boost/unordered/container-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_UNORDERED_CONTAINER_TRAITS_HXX
+#define ODB_BOOST_UNORDERED_CONTAINER_TRAITS_HXX
+
+#include <boost/version.hpp>
+
+// Unordered containers are available since 1.36.0.
+//
+#if BOOST_VERSION >= 103600
+
+#include <odb/pre.hxx>
+
+#include <utility> // std::move
+
+#include <boost/unordered_set.hpp>
+#include <boost/unordered_map.hpp>
+
+#include <odb/container-traits.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ // unordered_set
+ //
+ template <typename V, typename H, typename P, typename A>
+ class access::container_traits< ::boost::unordered_set<V, H, P, A> >
+ {
+ public:
+ static const container_kind kind = ck_set;
+ static const bool smart = false;
+
+ typedef ::boost::unordered_set<V, H, P, A> container_type;
+ typedef V value_type;
+
+ typedef set_functions<value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ value_type v;
+ more = f.select (v);
+#ifdef ODB_CXX11
+ c.insert (std::move (v));
+#else
+ c.insert (v);
+#endif
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+
+ // unordered_multiset
+ //
+ // @@ Does multiset preserve insertion order of equal elements? The
+ // current implementation in the generated code does not guarantee
+ // this.
+ //
+ template <typename V, typename H, typename P, typename A>
+ class access::container_traits< ::boost::unordered_multiset<V, H, P, A> >
+ {
+ public:
+ static const container_kind kind = ck_multiset;
+ static const bool smart = false;
+
+ typedef ::boost::unordered_multiset<V, H, P, A> container_type;
+ typedef V value_type;
+
+ typedef set_functions<value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ value_type v;
+ more = f.select (v);
+#ifdef ODB_CXX11
+ c.insert (std::move (v));
+#else
+ c.insert (v);
+#endif
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+
+ // unordered_map
+ //
+ template <typename K, typename V, typename H, typename P, typename A>
+ class access::container_traits< ::boost::unordered_map<K, V, H, P, A> >
+ {
+ public:
+ static const container_kind kind = ck_map;
+ static const bool smart = false;
+
+ typedef ::boost::unordered_map<K, V, H, P, A> container_type;
+
+ typedef K key_type;
+ typedef V value_type;
+ typedef typename container_type::value_type pair_type;
+
+ typedef map_functions<key_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ key_type k;
+ value_type v;
+ more = f.select (k, v);
+
+#ifdef ODB_CXX11
+ c.insert (pair_type (std::move (k), std::move (v)));
+#else
+ c.insert (pair_type (k, v));
+#endif
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+
+ // unordered_multimap
+ //
+ // @@ Does multimap preserve insertion order of equal elements? The
+ // current implementation in the generated code does not guarantee
+ // this.
+ //
+ template <typename K, typename V, typename H, typename P, typename A>
+ class access::container_traits< ::boost::unordered_multimap<K, V, H, P, A> >
+ {
+ public:
+ static const container_kind kind = ck_multimap;
+ static const bool smart = false;
+
+ typedef ::boost::unordered_multimap<K, V, H, P, A> container_type;
+
+ typedef K key_type;
+ typedef V value_type;
+ typedef typename container_type::value_type pair_type;
+
+ typedef map_functions<key_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ key_type k;
+ value_type v;
+ more = f.select (k, v);
+
+#ifdef ODB_CXX11
+ c.insert (pair_type (std::move (k), std::move (v)));
+#else
+ c.insert (pair_type (k, v));
+#endif
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // BOOST_VERSION
+#endif // ODB_BOOST_UNORDERED_CONTAINER_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/uuid.options b/libodb-boost/odb/boost/uuid.options
new file mode 100644
index 0000000..8b77aa6
--- /dev/null
+++ b/libodb-boost/odb/boost/uuid.options
@@ -0,0 +1,4 @@
+# file : odb/boost/uuid.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/uuid/uuid
diff --git a/libodb-boost/odb/boost/uuid/mssql/uuid-mapping.hxx b/libodb-boost/odb/boost/uuid/mssql/uuid-mapping.hxx
new file mode 100644
index 0000000..8da4c7d
--- /dev/null
+++ b/libodb-boost/odb/boost/uuid/mssql/uuid-mapping.hxx
@@ -0,0 +1,22 @@
+// file : odb/boost/uuid/mssql/uuid-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_UUID_MSSQL_UUID_MAPPING_HXX
+#define ODB_BOOST_UUID_MSSQL_UUID_MAPPING_HXX
+
+#include <boost/version.hpp>
+
+// UUID library is available since 1.42.0.
+//
+#if BOOST_VERSION >= 104200
+
+#include <boost/uuid/uuid.hpp>
+
+// By default map boost::uuids::uuid to SQL Server UNIQUEIDENTIFIER and
+// use NULL to represent nil UUIDs. If NULL is disabled (e.g., at the
+// member level), then we store the nil UUID (i.e., all bytes are zero).
+//
+#pragma db value(boost::uuids::uuid) type("UNIQUEIDENTIFIER") null
+
+#endif // BOOST_VERSION
+#endif // ODB_BOOST_UUID_MSSQL_UUID_MAPPING_HXX
diff --git a/libodb-boost/odb/boost/uuid/mssql/uuid-traits.hxx b/libodb-boost/odb/boost/uuid/mssql/uuid-traits.hxx
new file mode 100644
index 0000000..ead1d85
--- /dev/null
+++ b/libodb-boost/odb/boost/uuid/mssql/uuid-traits.hxx
@@ -0,0 +1,65 @@
+// file : odb/boost/uuid/mssql/uuid-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_UUID_MSSQL_UUID_TRAITS_HXX
+#define ODB_BOOST_UUID_MSSQL_UUID_TRAITS_HXX
+
+#include <boost/version.hpp>
+
+// UUID library is available since 1.42.0.
+//
+#if BOOST_VERSION >= 104200
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy, std::memset
+
+#include <boost/uuid/uuid.hpp>
+
+#include <odb/mssql/traits.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <>
+ class default_value_traits< ::boost::uuids::uuid, id_uniqueidentifier>
+ {
+ public:
+ typedef ::boost::uuids::uuid value_type;
+ typedef value_type query_type;
+ typedef uniqueidentifier image_type;
+
+ static void
+ set_value (value_type& v, const uniqueidentifier& i, bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v.data, &i, 16);
+ else
+ std::memset (v.data, 0, 16);
+ }
+
+ static void
+ set_image (uniqueidentifier& i, bool& is_null, const value_type& v)
+ {
+ // If we can, store nil as NULL. Otherwise, store it as a value.
+ //
+ is_null = is_null && v.is_nil ();
+
+ if (!is_null)
+ std::memcpy (&i, v.data, 16);
+ }
+ };
+
+ template <>
+ struct default_type_traits< ::boost::uuids::uuid>
+ {
+ static const database_type_id db_type_id = id_uniqueidentifier;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // BOOST_VERSION
+#endif // ODB_BOOST_UUID_MSSQL_UUID_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/uuid/mysql/uuid-mapping.hxx b/libodb-boost/odb/boost/uuid/mysql/uuid-mapping.hxx
new file mode 100644
index 0000000..a1de637
--- /dev/null
+++ b/libodb-boost/odb/boost/uuid/mysql/uuid-mapping.hxx
@@ -0,0 +1,22 @@
+// file : odb/boost/uuid/mysql/uuid-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_UUID_MYSQL_UUID_MAPPING_HXX
+#define ODB_BOOST_UUID_MYSQL_UUID_MAPPING_HXX
+
+#include <boost/version.hpp>
+
+// UUID library is available since 1.42.0.
+//
+#if BOOST_VERSION >= 104200
+
+#include <boost/uuid/uuid.hpp>
+
+// By default map boost::uuids::uuid to MySQL BINARY(16) and use NULL to
+// represent nil UUIDs. If NULL is disabled (e.g., at the member level),
+// then we store the nil UUID (i.e., all bytes are zero).
+//
+#pragma db value(boost::uuids::uuid) type("BINARY(16)") null
+
+#endif // BOOST_VERSION
+#endif // ODB_BOOST_UUID_MYSQL_UUID_MAPPING_HXX
diff --git a/libodb-boost/odb/boost/uuid/mysql/uuid-traits.hxx b/libodb-boost/odb/boost/uuid/mysql/uuid-traits.hxx
new file mode 100644
index 0000000..b2ce1b0
--- /dev/null
+++ b/libodb-boost/odb/boost/uuid/mysql/uuid-traits.hxx
@@ -0,0 +1,81 @@
+// file : odb/boost/uuid/mysql/uuid-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_UUID_MYSQL_UUID_TRAITS_HXX
+#define ODB_BOOST_UUID_MYSQL_UUID_TRAITS_HXX
+
+#include <boost/version.hpp>
+
+// UUID library is available since 1.42.0.
+//
+#if BOOST_VERSION >= 104200
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy, std::memset
+#include <cassert>
+
+#include <boost/uuid/uuid.hpp>
+
+#include <odb/mysql/traits.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <>
+ struct default_value_traits< ::boost::uuids::uuid, id_blob>
+ {
+ typedef ::boost::uuids::uuid 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)
+ {
+ if (!is_null)
+ {
+ assert (n == 16);
+ std::memcpy (v.data, b.data (), 16);
+ }
+ else
+ std::memset (v.data, 0, 16);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ // If we can, store nil as NULL. Otherwise, store it as a value.
+ //
+ is_null = is_null && v.is_nil ();
+
+ if (!is_null)
+ {
+ n = 16;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v.data, n);
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits< ::boost::uuids::uuid>
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // BOOST_VERSION
+#endif // ODB_BOOST_UUID_MYSQL_UUID_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/uuid/oracle/uuid-mapping.hxx b/libodb-boost/odb/boost/uuid/oracle/uuid-mapping.hxx
new file mode 100644
index 0000000..bd49122
--- /dev/null
+++ b/libodb-boost/odb/boost/uuid/oracle/uuid-mapping.hxx
@@ -0,0 +1,22 @@
+// file : odb/boost/uuid/oracle/uuid-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_UUID_ORACLE_UUID_MAPPING_HXX
+#define ODB_BOOST_UUID_ORACLE_UUID_MAPPING_HXX
+
+#include <boost/version.hpp>
+
+// UUID library is available since 1.42.0.
+//
+#if BOOST_VERSION >= 104200
+
+#include <boost/uuid/uuid.hpp>
+
+// By default map boost::uuids::uuid to Oracle RAW(16) and use NULL to
+// represent nil UUIDs. If NULL is disabled (e.g., at the member level),
+// then we store the nil UUID (i.e., all bytes are zero).
+//
+#pragma db value(boost::uuids::uuid) type("RAW(16)") null
+
+#endif // BOOST_VERSION
+#endif // ODB_BOOST_UUID_ORACLE_UUID_MAPPING_HXX
diff --git a/libodb-boost/odb/boost/uuid/oracle/uuid-traits.hxx b/libodb-boost/odb/boost/uuid/oracle/uuid-traits.hxx
new file mode 100644
index 0000000..d1ced34
--- /dev/null
+++ b/libodb-boost/odb/boost/uuid/oracle/uuid-traits.hxx
@@ -0,0 +1,77 @@
+// file : odb/boost/uuid/oracle/uuid-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_UUID_ORACLE_UUID_TRAITS_HXX
+#define ODB_BOOST_UUID_ORACLE_UUID_TRAITS_HXX
+
+#include <boost/version.hpp>
+
+// UUID library is available since 1.42.0.
+//
+#if BOOST_VERSION >= 104200
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy, std::memset
+#include <cassert>
+
+#include <boost/uuid/uuid.hpp>
+
+#include <odb/oracle/traits.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <>
+ struct default_value_traits< ::boost::uuids::uuid, id_raw>
+ {
+ public:
+ typedef ::boost::uuids::uuid value_type;
+ typedef value_type query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (value_type& v, const char* b, std::size_t n, bool is_null)
+ {
+ if (!is_null)
+ {
+ assert (n == 16);
+ std::memcpy (v.data, b, 16);
+ }
+ else
+ std::memset (v.data, 0, 16);
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ // If we can, store nil as NULL. Otherwise, store it as a value.
+ //
+ is_null = is_null && v.is_nil ();
+
+ if (!is_null)
+ {
+ n = 16;
+ assert (c >= n);
+ std::memcpy (b, v.data, n);
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits< ::boost::uuids::uuid>
+ {
+ static const database_type_id db_type_id = id_raw;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // BOOST_VERSION
+#endif // ODB_BOOST_UUID_ORACLE_UUID_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/uuid/pgsql/uuid-mapping.hxx b/libodb-boost/odb/boost/uuid/pgsql/uuid-mapping.hxx
new file mode 100644
index 0000000..517dfa6
--- /dev/null
+++ b/libodb-boost/odb/boost/uuid/pgsql/uuid-mapping.hxx
@@ -0,0 +1,22 @@
+// file : odb/boost/uuid/pgsql/uuid-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_UUID_PGSQL_UUID_MAPPING_HXX
+#define ODB_BOOST_UUID_PGSQL_UUID_MAPPING_HXX
+
+#include <boost/version.hpp>
+
+// UUID library is available since 1.42.0.
+//
+#if BOOST_VERSION >= 104200
+
+#include <boost/uuid/uuid.hpp>
+
+// By default map boost::uuids::uuid to PostgreSQL UUID and use NULL to
+// represent nil UUIDs. If NULL is disabled (e.g., at the member level),
+// then we store the nil UUID (i.e., all bytes are zero).
+//
+#pragma db value(boost::uuids::uuid) type("UUID") null
+
+#endif // BOOST_VERSION
+#endif // ODB_BOOST_UUID_PGSQL_UUID_MAPPING_HXX
diff --git a/libodb-boost/odb/boost/uuid/pgsql/uuid-traits.hxx b/libodb-boost/odb/boost/uuid/pgsql/uuid-traits.hxx
new file mode 100644
index 0000000..482ca0e
--- /dev/null
+++ b/libodb-boost/odb/boost/uuid/pgsql/uuid-traits.hxx
@@ -0,0 +1,70 @@
+// file : odb/boost/uuid/pgsql/uuid-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_UUID_PGSQL_UUID_TRAITS_HXX
+#define ODB_BOOST_UUID_PGSQL_UUID_TRAITS_HXX
+
+#include <boost/version.hpp>
+
+// UUID library is available since 1.42.0.
+//
+#if BOOST_VERSION >= 104200
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy, std::memset
+
+#include <boost/uuid/uuid.hpp>
+
+#include <odb/pgsql/traits.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <>
+ class default_value_traits< ::boost::uuids::uuid, id_uuid>
+ {
+ public:
+ typedef ::boost::uuids::uuid value_type;
+ typedef value_type query_type;
+ typedef unsigned char* image_type;
+
+ // PostgreSQL binary UUID representation is big-endian in the RFC 4122,
+ // section 4.1.2 order. Lucky for us, that also the representation used
+ // by Boost.
+ //
+
+ static void
+ set_value (value_type& v, const unsigned char* i, bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v.data, i, 16);
+ else
+ std::memset (v.data, 0, 16);
+ }
+
+ static void
+ set_image (unsigned char* i, bool& is_null, const value_type& v)
+ {
+ // If we can, store nil as NULL. Otherwise, store it as a value.
+ //
+ is_null = is_null && v.is_nil ();
+
+ if (!is_null)
+ std::memcpy (i, v.data, 16);
+ }
+ };
+
+ template <>
+ struct default_type_traits< ::boost::uuids::uuid>
+ {
+ static const database_type_id db_type_id = id_uuid;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // BOOST_VERSION
+#endif // ODB_BOOST_UUID_PGSQL_UUID_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/uuid/sqlite/uuid-mapping.hxx b/libodb-boost/odb/boost/uuid/sqlite/uuid-mapping.hxx
new file mode 100644
index 0000000..3fe2b6e
--- /dev/null
+++ b/libodb-boost/odb/boost/uuid/sqlite/uuid-mapping.hxx
@@ -0,0 +1,22 @@
+// file : odb/boost/uuid/sqlite/uuid-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_UUID_SQLITE_UUID_MAPPING_HXX
+#define ODB_BOOST_UUID_SQLITE_UUID_MAPPING_HXX
+
+#include <boost/version.hpp>
+
+// UUID library is available since 1.42.0.
+//
+#if BOOST_VERSION >= 104200
+
+#include <boost/uuid/uuid.hpp>
+
+// By default map boost::uuids::uuid to SQLite BLOB and use NULL to
+// represent nil UUIDs. If NULL is disabled (e.g., at the member level),
+// then we store the nil UUID (i.e., all bytes are zero).
+//
+#pragma db value(boost::uuids::uuid) type("BLOB") null
+
+#endif // BOOST_VERSION
+#endif // ODB_BOOST_UUID_SQLITE_UUID_MAPPING_HXX
diff --git a/libodb-boost/odb/boost/uuid/sqlite/uuid-traits.hxx b/libodb-boost/odb/boost/uuid/sqlite/uuid-traits.hxx
new file mode 100644
index 0000000..7a333c8
--- /dev/null
+++ b/libodb-boost/odb/boost/uuid/sqlite/uuid-traits.hxx
@@ -0,0 +1,81 @@
+// file : odb/boost/uuid/sqlite/uuid-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BOOST_UUID_SQLITE_UUID_TRAITS_HXX
+#define ODB_BOOST_UUID_SQLITE_UUID_TRAITS_HXX
+
+#include <boost/version.hpp>
+
+// UUID library is available since 1.42.0.
+//
+#if BOOST_VERSION >= 104200
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy, std::memset
+#include <cassert>
+
+#include <boost/uuid/uuid.hpp>
+
+#include <odb/sqlite/traits.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <>
+ struct default_value_traits< ::boost::uuids::uuid, id_blob>
+ {
+ typedef ::boost::uuids::uuid 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)
+ {
+ if (!is_null)
+ {
+ assert (n == 16);
+ std::memcpy (v.data, b.data (), 16);
+ }
+ else
+ std::memset (v.data, 0, 16);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ // If we can, store nil as NULL. Otherwise, store it as a value.
+ //
+ is_null = is_null && v.is_nil ();
+
+ if (!is_null)
+ {
+ n = 16;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v.data, n);
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits< ::boost::uuids::uuid>
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // BOOST_VERSION
+#endif // ODB_BOOST_UUID_SQLITE_UUID_TRAITS_HXX
diff --git a/libodb-boost/odb/boost/uuid/uuid-common.options b/libodb-boost/odb/boost/uuid/uuid-common.options
new file mode 100644
index 0000000..3d9fa50
--- /dev/null
+++ b/libodb-boost/odb/boost/uuid/uuid-common.options
@@ -0,0 +1,4 @@
+# file : odb/boost/uuid/uuid-common.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
diff --git a/libodb-boost/odb/boost/uuid/uuid-mssql.options b/libodb-boost/odb/boost/uuid/uuid-mssql.options
new file mode 100644
index 0000000..f6a8144
--- /dev/null
+++ b/libodb-boost/odb/boost/uuid/uuid-mssql.options
@@ -0,0 +1,11 @@
+# file : odb/boost/uuid/uuid-mssql.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/boost/uuid/mssql/uuid-mapping.hxx>'
+
+--hxx-prologue '#include <odb/boost/uuid/mssql/uuid-traits.hxx>'
diff --git a/libodb-boost/odb/boost/uuid/uuid-mysql.options b/libodb-boost/odb/boost/uuid/uuid-mysql.options
new file mode 100644
index 0000000..889a452
--- /dev/null
+++ b/libodb-boost/odb/boost/uuid/uuid-mysql.options
@@ -0,0 +1,11 @@
+# file : odb/boost/uuid/uuid-mysql.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/boost/uuid/mysql/uuid-mapping.hxx>'
+
+--hxx-prologue '#include <odb/boost/uuid/mysql/uuid-traits.hxx>'
diff --git a/libodb-boost/odb/boost/uuid/uuid-oracle.options b/libodb-boost/odb/boost/uuid/uuid-oracle.options
new file mode 100644
index 0000000..3022dbf
--- /dev/null
+++ b/libodb-boost/odb/boost/uuid/uuid-oracle.options
@@ -0,0 +1,11 @@
+# file : odb/boost/uuid/uuid-oracle.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/boost/uuid/oracle/uuid-mapping.hxx>'
+
+--hxx-prologue '#include <odb/boost/uuid/oracle/uuid-traits.hxx>'
diff --git a/libodb-boost/odb/boost/uuid/uuid-pgsql.options b/libodb-boost/odb/boost/uuid/uuid-pgsql.options
new file mode 100644
index 0000000..63268dc
--- /dev/null
+++ b/libodb-boost/odb/boost/uuid/uuid-pgsql.options
@@ -0,0 +1,11 @@
+# file : odb/boost/uuid/uuid-pgsql.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/boost/uuid/pgsql/uuid-mapping.hxx>'
+
+--hxx-prologue '#include <odb/boost/uuid/pgsql/uuid-traits.hxx>'
diff --git a/libodb-boost/odb/boost/uuid/uuid-sqlite.options b/libodb-boost/odb/boost/uuid/uuid-sqlite.options
new file mode 100644
index 0000000..ea515e9
--- /dev/null
+++ b/libodb-boost/odb/boost/uuid/uuid-sqlite.options
@@ -0,0 +1,11 @@
+# file : odb/boost/uuid/uuid-sqlite.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile boost/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/boost/uuid/sqlite/uuid-mapping.hxx>'
+
+--hxx-prologue '#include <odb/boost/uuid/sqlite/uuid-traits.hxx>'
diff --git a/libodb-boost/odb/boost/version.hxx b/libodb-boost/odb/boost/version.hxx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libodb-boost/odb/boost/version.hxx
diff --git a/libodb-boost/odb/boost/version.hxx.in b/libodb-boost/odb/boost/version.hxx.in
new file mode 100644
index 0000000..15aed4e
--- /dev/null
+++ b/libodb-boost/odb/boost/version.hxx.in
@@ -0,0 +1,74 @@
+// file : odb/boost/version.hxx.in
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef LIBODB_BOOST_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 LIBODB_BOOST_VERSION_FULL $libodb_boost.version.project_number$ULL
+#define LIBODB_BOOST_VERSION_STR "$libodb_boost.version.project$"
+#define LIBODB_BOOST_VERSION_ID "$libodb_boost.version.project_id$"
+
+#define LIBODB_BOOST_VERSION_MAJOR $libodb_boost.version.major$
+#define LIBODB_BOOST_VERSION_MINOR $libodb_boost.version.minor$
+#define LIBODB_BOOST_VERSION_PATCH $libodb_boost.version.patch$
+
+#define LIBODB_BOOST_PRE_RELEASE $libodb_boost.version.pre_release$
+
+#define LIBODB_BOOST_SNAPSHOT $libodb_boost.version.snapshot_sn$ULL
+#define LIBODB_BOOST_SNAPSHOT_ID "$libodb_boost.version.snapshot_id$"
+
+#include <odb/version.hxx>
+
+$libodb.check(LIBODB_VERSION_FULL, LIBODB_SNAPSHOT)$
+
+// 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
+//
+
+// ODB Boost interface version: odb interface version plus the Boost interface
+// version.
+//
+// NOTE: also hardcoded in *.options.
+//
+#define ODB_BOOST_VERSION 2047600
+#define ODB_BOOST_VERSION_STR "2.5.0-b.26"
+
+// libodb-boost version: odb interface version plus the bugfix version. Note
+// that LIBODB_BOOST_VERSION is always greater or equal to ODB_BOOST_VERSION
+// since if the Boost interface virsion is incremented then the bugfix version
+// must be incremented as well.
+//
+#define LIBODB_BOOST_VERSION 2049976
+
+#endif // LIBODB_BOOST_VERSION
diff --git a/libodb-boost/odb/boost/version.options b/libodb-boost/odb/boost/version.options
new file mode 100644
index 0000000..904dcc5
--- /dev/null
+++ b/libodb-boost/odb/boost/version.options
@@ -0,0 +1,12 @@
+# file : odb/boost/version.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+# Make sure the options files as seen by the ODB compiler and header
+# files as seen by the C++ compiler have the same Boost interface
+# version.
+#
+--hxx-prologue '#include <odb/boost/version.hxx>'
+
+--hxx-prologue '#if ODB_BOOST_VERSION != 2047600 // 2.5.0-b.26'
+--hxx-prologue '# error ODB and C++ compilers see different libodb-boost interface versions'
+--hxx-prologue '#endif'
diff --git a/libodb-boost/tests/.gitignore b/libodb-boost/tests/.gitignore
new file mode 100644
index 0000000..e54525b
--- /dev/null
+++ b/libodb-boost/tests/.gitignore
@@ -0,0 +1 @@
+driver
diff --git a/libodb-boost/tests/basics/buildfile b/libodb-boost/tests/basics/buildfile
new file mode 100644
index 0000000..c148079
--- /dev/null
+++ b/libodb-boost/tests/basics/buildfile
@@ -0,0 +1,6 @@
+# file : tests/basics/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libs = libodb-boost%lib{odb-boost}
+
+exe{driver}: {hxx cxx}{*} $libs
diff --git a/libodb-boost/tests/basics/driver.cxx b/libodb-boost/tests/basics/driver.cxx
new file mode 100644
index 0000000..a9c9c3d
--- /dev/null
+++ b/libodb-boost/tests/basics/driver.cxx
@@ -0,0 +1,22 @@
+// file : tests/basics/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Basic test to make sure the library is usable. Functionality testing
+// is done in the odb-tests package.
+
+#include <odb/boost/exception.hxx>
+#include <odb/boost/date-time/exceptions.hxx>
+
+using namespace odb;
+
+int
+main ()
+{
+ try
+ {
+ throw boost::date_time::value_out_of_range ();
+ }
+ catch (const boost::exception&)
+ {
+ }
+}
diff --git a/tests/build/.gitignore b/libodb-boost/tests/build/.gitignore
index 4a730a3..4a730a3 100644
--- a/tests/build/.gitignore
+++ b/libodb-boost/tests/build/.gitignore
diff --git a/libodb-boost/tests/build/bootstrap.build b/libodb-boost/tests/build/bootstrap.build
new file mode 100644
index 0000000..6ee38db
--- /dev/null
+++ b/libodb-boost/tests/build/bootstrap.build
@@ -0,0 +1,8 @@
+# file : tests/build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = # Unnamed subproject.
+
+using config
+using dist
+using test
diff --git a/libodb-boost/tests/build/root.build b/libodb-boost/tests/build/root.build
new file mode 100644
index 0000000..6c5a90b
--- /dev/null
+++ b/libodb-boost/tests/build/root.build
@@ -0,0 +1,23 @@
+# file : tests/build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+cxx.std = latest
+
+using cxx
+
+hxx{*}: extension = hxx
+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
+
+# Every exe{} in this subproject is by default a test.
+#
+exe{*}: test = true
+
+# Specify the test target for cross-testing.
+#
+test.target = $cxx.target
diff --git a/libodb-boost/tests/buildfile b/libodb-boost/tests/buildfile
new file mode 100644
index 0000000..57588a4
--- /dev/null
+++ b/libodb-boost/tests/buildfile
@@ -0,0 +1,4 @@
+# file : tests/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -build/}
diff --git a/libodb-mssql/.gitignore b/libodb-mssql/.gitignore
new file mode 100644
index 0000000..1c363a0
--- /dev/null
+++ b/libodb-mssql/.gitignore
@@ -0,0 +1,25 @@
+# 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/libodb-mssql/INSTALL b/libodb-mssql/INSTALL
new file mode 100644
index 0000000..b6e2be5
--- /dev/null
+++ b/libodb-mssql/INSTALL
@@ -0,0 +1,6 @@
+The easiest way to build this package is with the bpkg package manager:
+
+$ bpkg build libodb-mssql
+
+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/libodb-mssql/LICENSE b/libodb-mssql/LICENSE
new file mode 100644
index 0000000..2581d22
--- /dev/null
+++ b/libodb-mssql/LICENSE
@@ -0,0 +1,21 @@
+Copyright (c) 2009-2024 Code Synthesis.
+
+Permission is granted to use, copy, modify, and distribute this
+program under the ODB Non-Commercial Use and Evaluation License
+(NCUEL) as published by Code Synthesis.
+
+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
+ODB Non-Commercial Use and Evaluation License for details.
+
+You should have received a copy of the ODB Non-Commercial Use and
+Evaluation License (normally located in the NCUEL file that is
+accompanying the distribution); if not, contact Code Synthesis
+Tools CC at info@codesynthesis.com.
diff --git a/libodb-mssql/NCUEL b/libodb-mssql/NCUEL
new file mode 100644
index 0000000..f5211c1
--- /dev/null
+++ b/libodb-mssql/NCUEL
@@ -0,0 +1,294 @@
+ ODB NON-COMMERCIAL USE AND EVALUATION LICENSE (NCUEL)
+
+INTENT
+
+The intent of this license is to allow you to use ODB with commercial
+databases, such as Oracle, Microsoft SQL Server, IBM DB/2, etc., free
+of charge non-commercially or for evaluation.
+
+Furthermore, if a commercial database has a free edition, often called
+express edition, such as Oracle Express, Microsoft SQL Server Express,
+IBM DB/2 Express-C, etc., that can be used for commercial purposes free
+of charge, then this license allows you to use ODB with such an edition
+for commercial purposes also free of charge.
+
+Note also that the development of an application that will be used for
+commercial purposes constitutes a commercial use and is not allowed,
+except with a free edition of a commercial database. However, this
+license allows you to evaluate ODB; that is, to use the Software for
+a reasonable period for the purpose of determining its suitability for
+a particular application as well as to conduct exploratory development
+or proof-of-concept prototyping.
+
+Finally, any application that uses ODB under this license, whether non-
+commercially, for evaluation, or commercially with a free edition of a
+database, is subject to the terms and conditions similar to that of the
+GPL version 2. In particular, this means that if and when you distribute
+your application, you are required to also release its source code.
+
+If you have any questions concerning this License, please contact us at:
+info@codesynthesis.com.
+
+LEGAL TERMS AND CONDITIONS
+
+This Code Synthesis Non-Commercial Use and Evaluation License
+Agreement for ODB Software ("License") is a legal agreement between
+you, the Licensee, (either an individual or a single entity) and Code
+Synthesis Tools CC ("Code Synthesis") for non-commercially using,
+copying, distributing and modifying the Software and any work derived
+from the Software, as defined hereinbelow. Any commercial use, except
+as expressly provided in Section 2.1, is subject to a different license.
+
+By using, modifying, or distributing the Software or any work derived
+from the Software, Licensee indicates acceptance of this License and
+agrees to be bound by all its terms and conditions for using, copying,
+distributing, or modifying the Software and works derived from the
+Software. If Licensee is agreeing to this License on behalf of an entity
+other than an individual person, Licensee represents that Licensee is
+binding and have the right to bind the entity to the terms and conditions
+of this agreement.
+
+These terms and conditions only apply to the ODB components that are
+explicitly licensed under this License (normally ODB runtime libraries
+for commercial databases). Other ODB components may be licensed under
+other licenses and are not affected in any way by the terms and
+conditions found in this License. Similarly, ODB components licensed
+under this License are not affected by the terms and conditions found
+in other licenses. If you are using several ODB components that are
+licensed under different licenses, you must comply with the terms and
+conditions of each such license.
+
+No rights are granted to the Software except as expressly set forth
+herein. Nothing other than this License grants Licensee permission to
+use, copy, distribute or modify the Software or any work derived from
+the Software. Licensee may not use, copy, distribute or modify the
+Software or any work derived from the Software except as expressly
+provided under this License. If Licensee does not accept the terms and
+conditions of this License, Licensee shall not use, copy, distribute
+or modify the Software.
+
+In consideration for Licensee's forbearance of commercial use of the
+Software, except as expressly provided in Section 2.1, Code Synthesis
+grants Licensee non-exclusive, royalty-free and without fees rights
+as expressly provided herein.
+
+1. DEFINITIONS.
+
+A "commercial database" is a database product that has associated
+fees and/or royalties payable for production and/or commercial use
+of the database product. Commercial databases include, but are not
+limited to, Oracle, Microsoft SQL Server, and IBM DB/2.
+
+A "free edition of a commercial database" is a special, limited edition
+of a commercial database, often called express edition, that does not
+require fees and/or royalties for production and/or commercial use.
+Free editions of commercial databases include, but are not limited to,
+Oracle Express, Microsoft SQL Server Express, and IBM DB/2 Express-C.
+
+The "Software" is one of the ODB runtime libraries for one of the
+commercial databases, including, but not limited to, demo programs,
+associated media and printed materials, and any included "on-line"
+documentation.
+
+A "work derived from the Software" is any derivative work as defined
+in the copyright law of the nation or state where rights to the work
+derived from the Software are exercisable; that is to say, a program
+which is linked with or otherwise incorporates the ODB runtime library
+or a translation, improvement, enhancement, extension or other
+modification of the Software which has sufficient originality to
+qualify in such a nation or state as a copyrightable work is a work
+derived from the Software.
+
+To "use" means to execute (i.e. run) the Software.
+
+To "copy" means to create one or more copies of the Software.
+
+To "distribute" means to broadcast, publish, transfer, post, upload,
+download or otherwise disseminate in any medium to any third party.
+
+To "modify" means to create a work derived from the Software.
+
+To "evaluate" means to use the Software for a reasonable period for
+the purpose of determining its suitability for a particular application
+as well as to conduct exploratory development or proof-of-concept
+prototyping.
+
+A "commercial use" is:
+
+(1) the use of the Software or any work derived from the Software in
+connection with, for or in aid of the generation of revenue, such as
+in the conduct of Licensee's daily business operations; or
+
+(2) any copying, distribution or modification of the Software or any
+work derived from the Software to any party where payment or other
+consideration is made in connection with such copying, distribution or
+modification, whether directly (as in payment for a copy of the
+Software) or indirectly (including but not limited to payment for some
+good or service related to the Software, or payment for some product
+or service that includes a copy of the Software "without charge").
+However, the following actions which involve payment do not in and
+of themselves constitute a commercial use:
+
+(a) posting the Software on a public access information storage and
+retrieval service for which a fee is received for retrieving
+information (such as an on-line service), provided that the fee is not
+content-dependent. Such fees which are not content dependent include,
+but are not limited to, fees which are based solely on the storage
+capacity required to store the information, and fees which are based
+solely on the time required to transfer the information from/to the
+public access information storage and retrieval service; and
+
+(b) distributing the Software on a CD-ROM, provided that the Software
+is reproduced entirely and verbatim on such CD-ROM, and provided further
+that all information on such CD-ROM may be distributed in a manner which
+does not constitute a commercial use.
+
+2. GRANT OF LICENSE.
+
+2.1. LICENSE TO USE.
+Licensee may use the Software provided that such use does not constitute
+a commercial use.
+
+Licensee may also use the Software commercially with a free edition of a
+commercial database, if such an edition is available. If Licensee
+distributes works derived from the Software and such works may be used
+commercially by third parties, Licensee must cause such commercial use
+to be limited to a free edition of a commercial database.
+
+2.2. LICENSE TO EVALUATE.
+Licensee may evaluation the Software for commercial use.
+
+2.3. LICENSE TO COPY AND DISTRIBUTE.
+Licensee may copy and distribute literal (i.e., verbatim) copies of the
+Software as Licensee receives it throughout the world, in any medium,
+provided that Licensee distributes an unmodified, easily-readable copy
+of this License with the Software, and provided further that such
+distribution does not constitute a commercial use.
+
+2.4. LICENSE TO CREATE WORKS DERIVED FROM THE SOFTWARE.
+Licensee may create works derived from the Software, provided that any
+such work derived from the Software carries prominent notices stating
+both the manner in which Licensee has created a work derived from the
+Software (for example, notices stating that the work derived from the
+Software is linked with or otherwise incorporates the ODB runtime
+library, or notices stating that the work derived from the Software
+is an enhancement to the Software which Licensee has created) and the
+date any such work derived from the Software was created.
+
+2.5. LICENSE TO COPY AND DISTRIBUTE WORKS DERIVED FROM THE SOFTWARE.
+Licensee may copy and distribute works derived from the Software
+throughout the world, provided that Licensee distributes an
+unmodified, easily-readable copy of this License with such works
+derived from the Software, and provided further that such distribution
+does not constitute a commercial use. Licensee must cause any work
+derived from the Software that Licensee distributes to be licensed as
+a whole and at no charge to all third parties under the terms of this
+License or another free/open source license that does not restrict any
+rights of any third party that would have been granted should such work
+have been licensed under this License.
+
+Any work derived from the Software must be accompanied by the complete
+corresponding machine-readable source code of such work derived from
+the Software, delivered on a medium customarily used for software
+interchange. The source code for the work derived from the Software
+means the preferred form of the work derived from the Software for
+making modifications to it. For an executable work derived from the
+Software, complete source code means all of the source code for all
+modules of the work derived from the Software, all associated
+interface definition files and all scripts used to control compilation
+and installation of all or any part of the work derived from the
+Software. However, the source code delivered need not include anything
+that is normally distributed, in either source code or binary (object-
+code) form, with major components (including but not limited to
+compilers, linkers, and kernels) of the operating system on which the
+executable work derived from the Software runs, unless that component
+itself accompanies the executable code of the work derived from the
+Software.
+
+Furthermore, if the executable code or object code of the work derived
+from the Software may be copied from a designated place, and if the
+source code of the work derived from the Software may be copied from
+the same place, then the work derived from the Software shall be
+construed as accompanied by the complete corresponding machine-readable
+source code of such work derived from the Software, even though third
+parties are not compelled to copy the source code along with the
+executable code or object code.
+
+If the work derived from the Software normally reads commands
+interactively when run, Licensee must cause the work derived from the
+Software, at each time it commences operation, to print or display an
+announcement including either a notice consisting of the verbatim
+warranty and liability provisions of this License, or a notice that
+Licensee, and not Code Synthesis provides a warranty.
+
+Licensee may not impose any further restrictions on the exercise of
+the rights granted herein by any recipient of any work derived from
+the Software.
+
+3. RESTRICTIONS.
+
+Licensee acknowledges that the Software is protected by copyright laws
+and international copyright treaties, as well as other intellectual
+property laws and treaties. The Software is licensed, not sold. All
+title and copyrights in and to the Software are owned exclusively by
+Code Synthesis.
+
+Licensee may not sublicense, assign or transfer this License, the
+Software or any work derived from the Software except as permitted by
+this License.
+
+Licensee is expressly prohibited from using, copying, distributing,
+studying the source code, or otherwise examining the Software for
+the purpose of reverse engineering or duplicating its functionality
+(unless enforcement of this restrictions is prohibited by applicable
+law).
+
+4. LIMITED WARRANTY.
+
+4.1 NO WARRANTIES.
+CODE SYNTHESIS EXPRESSLY DISCLAIMS ANY WARRANTY FOR THE SOFTWARE. THE
+SOFTWARE IS PROVIDED TO LICENSEE "AS IS," WITHOUT WARRANTY OF ANY KIND,
+EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT OF THIRD PARTY RIGHTS. THE ENTIRE RISK AS TO THE USE,
+QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH LICENSEE. SHOULD THE
+SOFTWARE PROVE DEFECTIVE, LICENSEE ASSUMES THE COST OF ALL NECESSARY
+SERVICING, REPAIR OR CORRECTION.
+
+4.2. NO LIABILITY FOR DAMAGES.
+IN NO EVENT WILL CODE SYNTHESIS, OR ANY OTHER PARTY WHO MAY COPY,
+DISTRIBUTE OR MODIFY THE SOFTWARE AS PERMITTED HEREIN, BE LIABLE FOR
+ANY GENERAL, DIRECT, INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL
+DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF
+BUSINESS PROFITS, BUSINESS INTERRUPTION, INACCURATE INFORMATION, LOSS
+OF INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OR
+INABILITY TO USE THE SOFTWARE, EVEN IF CODE SYNTHESIS OR SUCH OTHER
+PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+5. TERMINATION.
+
+Any violation or any attempt to violate any of the terms and conditions
+of this License will automatically terminate Licensee's rights under
+this License. Licensee further agrees upon such termination to cease
+any and all using, copying, distributing and modifying of the Software
+and any work derived from the Software, and further to destroy any and
+all of Licensee's copies of the Software and any work derived from the
+Software.
+
+However, parties who have received copies of the Software or copies of
+any work derived from the Software, or rights, from Licensee under this
+License will not have their licenses terminated so long as such parties
+remain in full compliance with this License.
+
+6. LICENSE SCOPE AND MODIFICATION.
+
+This License sets forth the entire agreement between Licensee and Code
+Synthesis and supersedes all prior agreements and understandings between
+the parties relating to the subject matter hereof. None of the terms of
+this License may be waived or modified except as expressly agreed in
+writing by both Licensee and Code Synthesis.
+
+7. SEVERABILITY.
+
+Should any provision of this License be declared void or unenforceable,
+the validity of the remaining provisions shall not be affected thereby.
diff --git a/libodb-mssql/NEWS b/libodb-mssql/NEWS
new file mode 120000
index 0000000..0fae0f8
--- /dev/null
+++ b/libodb-mssql/NEWS
@@ -0,0 +1 @@
+../NEWS \ No newline at end of file
diff --git a/libodb-mssql/README b/libodb-mssql/README
new file mode 100644
index 0000000..21671b7
--- /dev/null
+++ b/libodb-mssql/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 Microsoft SQL Server ODB runtime library.
+Every application that includes code generated for the SQL Server
+database will need to link to this library.
+
+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.
+
+Send questions, bug reports, or any other feedback to the
+odb-users@codesynthesis.com mailing list.
diff --git a/libodb-mssql/build/.gitignore b/libodb-mssql/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-mssql/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-mssql/build/bootstrap.build b/libodb-mssql/build/bootstrap.build
new file mode 100644
index 0000000..fddf8ad
--- /dev/null
+++ b/libodb-mssql/build/bootstrap.build
@@ -0,0 +1,10 @@
+# file : build/bootstrap.build
+# license : ODB NCUEL; see accompanying LICENSE file
+
+project = libodb-mssql
+
+using version
+using config
+using dist
+using test
+using install
diff --git a/libodb-mssql/build/export.build b/libodb-mssql/build/export.build
new file mode 100644
index 0000000..ad90a96
--- /dev/null
+++ b/libodb-mssql/build/export.build
@@ -0,0 +1,9 @@
+# file : build/export.build
+# license : ODB NCUEL; see accompanying LICENSE file
+
+$out_root/
+{
+ include odb/mssql/
+}
+
+export $out_root/odb/mssql/lib{odb-mssql}
diff --git a/libodb-mssql/build/root.build b/libodb-mssql/build/root.build
new file mode 100644
index 0000000..794e083
--- /dev/null
+++ b/libodb-mssql/build/root.build
@@ -0,0 +1,19 @@
+# file : build/root.build
+# license : ODB NCUEL; see accompanying LICENSE file
+
+config [bool] config.libodb_mssql.develop ?= false
+
+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
diff --git a/libodb-mssql/buildfile b/libodb-mssql/buildfile
new file mode 100644
index 0000000..db1aceb
--- /dev/null
+++ b/libodb-mssql/buildfile
@@ -0,0 +1,9 @@
+# file : buildfile
+# license : ODB NCUEL; see accompanying LICENSE file
+
+./: {*/ -build/} doc{INSTALL NEWS README} legal{NCUEL LICENSE} manifest
+
+# Don't install tests or the INSTALL file.
+#
+tests/: install = false
+doc{INSTALL}@./: install = false
diff --git a/libodb-mssql/manifest b/libodb-mssql/manifest
new file mode 100644
index 0000000..856b67a
--- /dev/null
+++ b/libodb-mssql/manifest
@@ -0,0 +1,47 @@
+: 1
+name: libodb-mssql
+version: 2.5.0-b.26.z
+project: odb
+summary: Microsoft SQL Server ODB runtime library
+license: other: ODB NCUEL ; Non-Commercial Use and Evaluation License.
+license: other: proprietary ; Not free/open source.
+topics: C++, ORM, Microsoft SQL Server, SQL
+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
+requires: c++11
+# @@ DEP
+#requires: ? libunixodbc ; Only on UNIX.
+
+# @@ TMP Bump the toolchain version to 0.17.0 after it is released.
+#
+depends: * build2 >= 0.16.0-
+depends: * bpkg >= 0.16.0-
+
+depends: libodb == $
+depends: * cli ^1.2.0- ? ($config.libodb_mssql.develop)
+
+# @@ TMP: drop develop (also drop in odb-tests/build/root.build).
+#
+tests: odb-tests == $ \
+ ? ($config.odb_tests.develop && !$defined(config.odb_tests.database)) config.odb_tests.database=mssql
+
+# @@ TMP
+#
+builds: windows ; Requires not yet packaged libunixodbc (unixODBC).
+#builds: windows mssql
+
+# Only build this package configuration where it can be tested via odb-tests
+# package (see its manifest for details).
+#
+#multi-builds: mssql
+#multi-build-config:
+#\
+#{ 'config.odb_tests.database=mssql sqlite' }+ odb-tests
+#;
+#Enable testing in dynamic multi-database mode.
+#\
diff --git a/libodb-mssql/odb/mssql/auto-handle.cxx b/libodb-mssql/odb/mssql/auto-handle.cxx
new file mode 100644
index 0000000..ca42040
--- /dev/null
+++ b/libodb-mssql/odb/mssql/auto-handle.cxx
@@ -0,0 +1,17 @@
+// file : odb/mssql/auto-handle.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/mssql.hxx>
+#include <odb/mssql/auto-handle.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ void
+ free_handle (SQLHANDLE h, SQLSMALLINT htype)
+ {
+ SQLFreeHandle (htype, h);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/auto-handle.hxx b/libodb-mssql/odb/mssql/auto-handle.hxx
new file mode 100644
index 0000000..f6934e4
--- /dev/null
+++ b/libodb-mssql/odb/mssql/auto-handle.hxx
@@ -0,0 +1,88 @@
+// file : odb/mssql/auto-handle.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_AUTO_HANDLE_HXX
+#define ODB_MSSQL_AUTO_HANDLE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/version.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ LIBODB_MSSQL_EXPORT void
+ free_handle (SQLHANDLE, SQLSMALLINT htype);
+
+ template <SQLSMALLINT htype>
+ class auto_handle
+ {
+ public:
+ auto_handle (SQLHANDLE h = 0)
+ : h_ (h)
+ {
+ }
+
+ ~auto_handle ()
+ {
+ if (h_ != 0)
+ free_handle (h_, htype);
+ }
+
+ operator SQLHANDLE () const
+ {
+ return h_;
+ }
+
+ SQLHANDLE
+ get () const
+ {
+ return h_;
+ }
+
+ SQLHANDLE
+ release ()
+ {
+ SQLHANDLE h (h_);
+ h_ = 0;
+ return h;
+ }
+
+ void
+ reset (SQLHANDLE h = 0)
+ {
+ if (h_ != 0)
+ free_handle (h_, htype);
+
+ h_ = h;
+ }
+
+#ifdef ODB_CXX11
+ auto_handle (auto_handle&& ah) noexcept: h_ (ah.release ()) {}
+ auto_handle& operator= (auto_handle&& ah) noexcept
+ {
+ if (this != &ah)
+ reset (ah.release ());
+ return *this;
+ }
+#endif
+
+ private:
+ auto_handle (const auto_handle&);
+ auto_handle& operator= (const auto_handle&);
+
+ private:
+ SQLHANDLE h_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_AUTO_HANDLE_HXX
diff --git a/libodb-mssql/odb/mssql/binding.hxx b/libodb-mssql/odb/mssql/binding.hxx
new file mode 100644
index 0000000..c7d1bd8
--- /dev/null
+++ b/libodb-mssql/odb/mssql/binding.hxx
@@ -0,0 +1,65 @@
+// file : odb/mssql/binding.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_BINDING_HXX
+#define ODB_MSSQL_BINDING_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/mssql-types.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class LIBODB_MSSQL_EXPORT binding
+ {
+ public:
+ typedef mssql::bind bind_type;
+ typedef mssql::change_callback change_callback_type;
+
+ binding ()
+ : bind (0), count (0), version (0),
+ batch (0), skip (0), status (0),
+ change_callback (0) {}
+
+ binding (bind_type* b, std::size_t n)
+ : bind (b), count (n), version (0),
+ batch (1), skip (0), status (0),
+ change_callback (0)
+ {
+ }
+
+ binding (bind_type* b, std::size_t n,
+ std::size_t bt, std::size_t s, SQLUSMALLINT* st)
+ : bind (b), count (n), version (0),
+ batch (bt), skip (s), status (st),
+ change_callback (0)
+ {
+ }
+
+ bind_type* bind;
+ std::size_t count;
+ std::size_t version;
+
+ std::size_t batch;
+ std::size_t skip;
+ SQLUSMALLINT* status; // Batch status array.
+
+ change_callback_type* change_callback;
+
+ private:
+ binding (const binding&);
+ binding& operator= (const binding&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_BINDING_HXX
diff --git a/libodb-mssql/odb/mssql/buildfile b/libodb-mssql/odb/mssql/buildfile
new file mode 100644
index 0000000..a2b838f
--- /dev/null
+++ b/libodb-mssql/odb/mssql/buildfile
@@ -0,0 +1,142 @@
+# file : odb/mssql/buildfile
+# license : ODB NCUEL; see accompanying LICENSE file
+
+define cli: file
+cli{*}: extension = cli
+
+import int_libs = libodb%lib{odb}
+
+# On Windows ODBC is a pre-installed system library so we pass it to the
+# linker directly
+#
+imp_libs =
+
+if ($cc.target.class != 'windows')
+ import imp_libs = libunixodbc%lib{odbc}
+
+lib{odb-mssql}: {hxx ixx txx cxx}{* -version} {hxx}{version} \
+ details/{hxx ixx txx cxx}{* -options} \
+ $imp_libs $int_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.
+#
+cxx.poptions =+ "-I$out_root" "-I$src_root"
+
+obja{*}: cxx.poptions += -DLIBODB_MSSQL_STATIC_BUILD
+objs{*}: cxx.poptions += -DLIBODB_MSSQL_SHARED_BUILD
+
+if ($cc.target.class == 'windows')
+ cxx.libs += ($cxx.target.system == "mingw32" ? -lodbc32 : odbc32.lib)
+
+# Export options.
+#
+lib{odb-mssql}:
+{
+ cxx.export.poptions = "-I$out_root" "-I$src_root"
+ cxx.export.libs = $int_libs
+}
+
+liba{odb-mssql}: cxx.export.poptions += -DLIBODB_MSSQL_STATIC
+libs{odb-mssql}: cxx.export.poptions += -DLIBODB_MSSQL_SHARED
+
+# For pre-releases use the complete version to make sure they cannot be used
+# in place of another pre-release or the final version. See the version module
+# for details on the version.* variable values.
+#
+if $version.pre_release
+ lib{odb-mssql}: bin.lib.version = @"-$version.project_id"
+else
+ lib{odb-mssql}: bin.lib.version = @"-$version.major.$version.minor"
+
+develop = $config.libodb_mssql.develop
+
+## Consumption build ($develop == false).
+#
+
+# Use pregenerated versions in the consumption build.
+#
+lib{odb-mssql}: details/pregenerated/{hxx ixx cxx}{**}: include = (!$develop)
+
+if! $develop
+ cxx.poptions =+ "-I($src_base/details/pregenerated)" # Note: must come first.
+
+# Don't install pregenerated headers since they are only used internally in
+# the database implementation (also below).
+#
+details/pregenerated/{hxx ixx}{*}: install = false
+
+# Distribute pregenerated versions only in the consumption build.
+#
+details/pregenerated/{hxx ixx cxx}{*}: dist = (!$develop)
+
+#
+##
+
+## Development build ($develop == true).
+#
+
+lib{odb-mssql}: details/{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.
+#
+<details/{hxx ixx cxx}{options}>: details/cli{options} $cli
+{
+ install = false
+ dist = ($develop ? pregenerated/odb/mssql/details/ : false)
+
+ # Symlink the generated code in src for convenience of development.
+ #
+ backlink = true
+}
+%
+if $develop
+{{
+ options = --include-with-brackets --include-prefix odb/mssql/details \
+ --guard-prefix LIBODB_MSSQL_DETAILS --generate-file-scanner \
+ --cli-namespace odb::mssql::details::cli --long-usage \
+ --generate-specifier --no-combined-flags
+
+ $cli $options -o $out_base/details/ $path($<[0])
+
+ # If the result differs from the pregenerated version, copy it over.
+ #
+ d = [dir_path] $src_base/details/pregenerated/odb/mssql/details/
+
+ if diff $d/options.hxx $path($>[0]) >- && \
+ diff $d/options.ixx $path($>[1]) >- && \
+ diff $d/options.cxx $path($>[2]) >-
+ exit
+ end
+
+ cp $path($>[0]) $d/options.hxx
+ cp $path($>[1]) $d/options.ixx
+ cp $path($>[2]) $d/options.cxx
+}}
+
+# Install into the odb/mssql/ subdirectory of, say, /usr/include/
+# recreating subdirectories.
+#
+install_include = [dir_path] include/odb/mssql/
+
+{hxx ixx txx}{*}:
+{
+ install = $install_include
+ install.subdirs = true
+}
diff --git a/libodb-mssql/odb/mssql/connection-factory.cxx b/libodb-mssql/odb/mssql/connection-factory.cxx
new file mode 100644
index 0000000..f673b92
--- /dev/null
+++ b/libodb-mssql/odb/mssql/connection-factory.cxx
@@ -0,0 +1,159 @@
+// file : odb/mssql/connection-factory.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/connection-factory.hxx>
+#include <odb/mssql/exceptions.hxx>
+
+#include <odb/details/lock.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ using namespace details;
+
+ namespace mssql
+ {
+ // new_connection_factory
+ //
+ connection_ptr new_connection_factory::
+ connect ()
+ {
+ return connection_ptr (new (shared) connection (*this));
+ }
+
+ // connection_pool_factory
+ //
+ connection_pool_factory::pooled_connection_ptr connection_pool_factory::
+ create ()
+ {
+ return pooled_connection_ptr (new (shared) pooled_connection (*this));
+ }
+
+ connection_pool_factory::
+ ~connection_pool_factory ()
+ {
+ // Wait for all the connections currently in use to return to
+ // the pool.
+ //
+ lock l (mutex_);
+ while (in_use_ != 0)
+ {
+ waiters_++;
+ cond_.wait (l);
+ waiters_--;
+ }
+ }
+
+ connection_ptr connection_pool_factory::
+ connect ()
+ {
+ lock l (mutex_);
+
+ while (true)
+ {
+ // See if we have a spare connection.
+ //
+ if (connections_.size () != 0)
+ {
+ shared_ptr<pooled_connection> c (connections_.back ());
+ connections_.pop_back ();
+
+ c->callback_ = &c->cb_;
+ in_use_++;
+ return c;
+ }
+
+ // See if we can create a new one.
+ //
+ if (max_ == 0 || in_use_ < max_)
+ {
+ shared_ptr<pooled_connection> c (create ());
+ c->callback_ = &c->cb_;
+ in_use_++;
+ return c;
+ }
+
+ // Wait until someone releases a connection.
+ //
+ waiters_++;
+ cond_.wait (l);
+ waiters_--;
+ }
+ }
+
+ void connection_pool_factory::
+ database (database_type& db)
+ {
+ bool first (db_ == 0);
+
+ connection_factory::database (db);
+
+ if (!first)
+ return;
+
+ if (min_ > 0)
+ {
+ connections_.reserve (min_);
+
+ for(size_t i (0); i < min_; ++i)
+ connections_.push_back (create ());
+ }
+ }
+
+ bool connection_pool_factory::
+ release (pooled_connection* c)
+ {
+ c->callback_ = 0;
+
+ lock l (mutex_);
+
+ // Determine if we need to keep or free this connection.
+ //
+ bool keep (!c->failed () &&
+ (waiters_ != 0 ||
+ min_ == 0 ||
+ (connections_.size () + in_use_ <= min_)));
+
+ in_use_--;
+
+ if (keep)
+ {
+ connections_.push_back (pooled_connection_ptr (inc_ref (c)));
+ connections_.back ()->recycle ();
+ }
+
+ if (waiters_ != 0)
+ cond_.signal ();
+
+ return !keep;
+ }
+
+ //
+ // connection_pool_factory::pooled_connection
+ //
+
+ connection_pool_factory::pooled_connection::
+ pooled_connection (connection_pool_factory& f)
+ : connection (f)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ connection_pool_factory::pooled_connection::
+ pooled_connection (connection_pool_factory& f, SQLHDBC handle)
+ : connection (f, handle)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ bool connection_pool_factory::pooled_connection::
+ zero_counter (void* arg)
+ {
+ pooled_connection* c (static_cast<pooled_connection*> (arg));
+ return static_cast<connection_pool_factory&> (c->factory_).release (c);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/connection-factory.hxx b/libodb-mssql/odb/mssql/connection-factory.hxx
new file mode 100644
index 0000000..14861a5
--- /dev/null
+++ b/libodb-mssql/odb/mssql/connection-factory.hxx
@@ -0,0 +1,134 @@
+// file : odb/mssql/connection-factory.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_CONNECTION_FACTORY_HXX
+#define ODB_MSSQL_CONNECTION_FACTORY_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cstddef> // std::size_t
+#include <cassert>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/connection.hxx>
+
+#include <odb/details/mutex.hxx>
+#include <odb/details/condition.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class LIBODB_MSSQL_EXPORT new_connection_factory: public connection_factory
+ {
+ public:
+ new_connection_factory () {}
+
+ virtual connection_ptr
+ connect ();
+
+ private:
+ new_connection_factory (const new_connection_factory&);
+ new_connection_factory& operator= (const new_connection_factory&);
+ };
+
+ class LIBODB_MSSQL_EXPORT connection_pool_factory:
+ public connection_factory
+ {
+ public:
+ // The max_connections argument specifies the maximum number of
+ // concurrent connections this pool will maintain. If this value
+ // is 0 then the pool will create a new connection every time all
+ // of the existing connections are in use.
+ //
+ // The min_connections argument specifies the minimum number of
+ // connections that should be maintained by the pool. If the
+ // number of connections maintained by the pool exceeds this
+ // number and there are no active waiters for a new connection,
+ // then the pool will release the excess connections. If this
+ // value is 0 then the pool will maintain all the connections
+ // that were ever created.
+ //
+ connection_pool_factory (std::size_t max_connections = 0,
+ std::size_t min_connections = 0)
+ : max_ (max_connections),
+ min_ (min_connections),
+ in_use_ (0),
+ waiters_ (0),
+ cond_ (mutex_)
+ {
+ // max_connections == 0 means unlimited.
+ //
+ assert (max_connections == 0 || max_connections >= min_connections);
+ }
+
+ virtual connection_ptr
+ connect ();
+
+ virtual void
+ database (database_type&);
+
+ virtual
+ ~connection_pool_factory ();
+
+ private:
+ connection_pool_factory (const connection_pool_factory&);
+ connection_pool_factory& operator= (const connection_pool_factory&);
+
+ protected:
+ class LIBODB_MSSQL_EXPORT pooled_connection: public connection
+ {
+ public:
+ pooled_connection (connection_pool_factory&);
+ pooled_connection (connection_pool_factory&, SQLHDBC);
+
+ private:
+ static bool
+ zero_counter (void*);
+
+ private:
+ friend class connection_pool_factory;
+
+ shared_base::refcount_callback cb_;
+ };
+
+ friend class pooled_connection;
+
+ typedef details::shared_ptr<pooled_connection> pooled_connection_ptr;
+ typedef std::vector<pooled_connection_ptr> connections;
+
+ // This function is called whenever the pool needs to create a new
+ // connection.
+ //
+ virtual pooled_connection_ptr
+ create ();
+
+ protected:
+ // Return true if the connection should be deleted, false otherwise.
+ //
+ bool
+ release (pooled_connection*);
+
+ protected:
+ const std::size_t max_;
+ const std::size_t min_;
+
+ std::size_t in_use_; // Number of connections currently in use.
+ std::size_t waiters_; // Number of threads waiting for a connection.
+
+ connections connections_;
+
+ details::mutex mutex_;
+ details::condition cond_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_CONNECTION_FACTORY_HXX
diff --git a/libodb-mssql/odb/mssql/connection.cxx b/libodb-mssql/odb/mssql/connection.cxx
new file mode 100644
index 0000000..5181fab
--- /dev/null
+++ b/libodb-mssql/odb/mssql/connection.cxx
@@ -0,0 +1,287 @@
+// file : odb/mssql/connection.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <string>
+#include <cstdint> //intptr_t
+
+#include <odb/mssql/mssql.hxx>
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/transaction.hxx>
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/statement-cache.hxx>
+#include <odb/mssql/error.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ static const intptr_t transaction_isolation_map[] =
+ {
+ SQL_TXN_READ_UNCOMMITTED,
+ SQL_TXN_READ_COMMITTED,
+ SQL_TXN_REPEATABLE_READ,
+ SQL_TXN_SS_SNAPSHOT,
+ SQL_TXN_SERIALIZABLE
+ };
+
+ connection::
+ connection (connection_factory& cf)
+ : odb::connection (cf),
+ state_ (state_disconnected),
+ statement_cache_ (new statement_cache_type (*this)),
+ long_data_buffer_ (0)
+ {
+ SQLRETURN r;
+
+ database_type& db (database ());
+
+ // Allocate the connection handle.
+ //
+ {
+ SQLHANDLE h;
+ r = SQLAllocHandle (SQL_HANDLE_DBC, db.environment (), &h);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, db.environment (), SQL_HANDLE_ENV);
+
+ handle_.reset (h);
+ }
+
+ // Set the manual commit mode.
+ //
+ r = SQLSetConnectAttrA (handle_,
+ SQL_ATTR_AUTOCOMMIT,
+ (SQLPOINTER) SQL_AUTOCOMMIT_OFF,
+ 0);
+
+ if (!SQL_SUCCEEDED (r))
+ // Still use the handle version of translate_error since there
+ // is no connection yet.
+ //
+ translate_error (r, handle_, SQL_HANDLE_DBC);
+
+ // Enable Multiple Active Result Sets (MARS).
+ //
+ r = SQLSetConnectAttrA (handle_,
+ SQL_COPT_SS_MARS_ENABLED,
+ (SQLPOINTER) SQL_MARS_ENABLED_YES,
+ SQL_IS_UINTEGER);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, handle_, SQL_HANDLE_DBC);
+
+ // Set transaction isolation level.
+ //
+ transaction_isolation ti (db.transaction_isolation ());
+ switch (ti)
+ {
+ case isolation_read_committed:
+ {
+ break; // SQL Server default.
+ }
+ case isolation_read_uncommitted:
+ case isolation_repeatable_read:
+ case isolation_serializable:
+ {
+ r = SQLSetConnectAttrA (handle_,
+ SQL_ATTR_TXN_ISOLATION,
+ (SQLPOINTER) transaction_isolation_map[ti],
+ 0);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, handle_, SQL_HANDLE_DBC);
+ break;
+ }
+ case isolation_snapshot:
+ {
+ r = SQLSetConnectAttrA (handle_,
+ SQL_COPT_SS_TXN_ISOLATION,
+ (SQLPOINTER) transaction_isolation_map[ti],
+ SQL_IS_INTEGER);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, handle_, SQL_HANDLE_DBC);
+ break;
+ }
+ }
+
+ // Connect.
+ //
+ {
+ SQLSMALLINT out_conn_str_size;
+ r = SQLDriverConnectA (handle_,
+ 0, // Parent window handle.
+ (SQLCHAR*) db.connect_string ().c_str (),
+ SQL_NTS,
+ 0, // Output connection string buffer.
+ 0, // Size of output connection string buffer.
+ &out_conn_str_size,
+ SQL_DRIVER_NOPROMPT);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, handle_, SQL_HANDLE_DBC);
+
+ state_ = state_connected;
+ }
+
+ // If an exception is thrown after this line, we will not disconnect
+ // the connection.
+ //
+ }
+
+ connection::
+ connection (connection_factory& cf, SQLHDBC handle)
+ : odb::connection (cf),
+ handle_ (handle),
+ state_ (state_connected),
+ statement_cache_ (new statement_cache_type (*this)),
+ long_data_buffer_ (0)
+ {
+ }
+
+ connection::
+ ~connection ()
+ {
+ // Deallocate prepared statements before we close the connection.
+ //
+ recycle ();
+ clear_prepared_map ();
+ statement_cache_.reset ();
+ direct_stmt_.reset ();
+
+ if (state_ != state_disconnected)
+ SQLDisconnect (handle_); // Ignore any errors.
+ }
+
+ transaction_impl* connection::
+ begin ()
+ {
+ return new transaction_impl (connection_ptr (inc_ref (this)));
+ }
+
+ unsigned long long connection::
+ execute (const char* s, std::size_t n)
+ {
+ {
+ odb::tracer* t;
+ if ((t = transaction_tracer ()) ||
+ (t = tracer ()) ||
+ (t = database ().tracer ()))
+ {
+ string str (s, n);
+ t->execute (*this, str.c_str ());
+ }
+ }
+
+ SQLRETURN r;
+
+ // Allocate the statement if necessary.
+ //
+ if (direct_stmt_ == 0)
+ {
+ SQLHANDLE h;
+ r = SQLAllocHandle (SQL_HANDLE_STMT, handle_, &h);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, *this);
+
+ direct_stmt_.reset (h);
+
+ // Disable escape sequences.
+ //
+ r = SQLSetStmtAttr (direct_stmt_,
+ SQL_ATTR_NOSCAN,
+ (SQLPOINTER) SQL_NOSCAN_OFF,
+ 0);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, *this, direct_stmt_);
+
+ // Disable data retrieval for SELECT statements.
+ //
+ r = SQLSetStmtAttr (direct_stmt_,
+ SQL_ATTR_RETRIEVE_DATA,
+ (SQLPOINTER) SQL_RD_OFF,
+ 0);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, *this, direct_stmt_);
+ }
+
+ // Execute.
+ //
+ r = SQLExecDirectA (direct_stmt_, (SQLCHAR*) s, (SQLINTEGER) n);
+
+ // SQL_NO_DATA indicates that a DML statement hasn't affected
+ // any rows.
+ //
+ if (r == SQL_NO_DATA)
+ return 0;
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, *this, direct_stmt_);
+
+ // Get the number of affected/returned rows.
+ //
+ SQLLEN rows;
+
+ // See if this is a select statement.
+ //
+ SQLSMALLINT cols;
+ r = SQLNumResultCols (direct_stmt_, &cols);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, *this, direct_stmt_);
+
+ if (cols != 0)
+ {
+ for (rows = 0;; ++rows)
+ {
+ r = SQLFetch (direct_stmt_);
+
+ if (r == SQL_NO_DATA)
+ break;
+ else if (!SQL_SUCCEEDED (r))
+ translate_error (r, *this, direct_stmt_);
+ }
+
+ r = SQLCloseCursor (direct_stmt_);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, *this, direct_stmt_);
+ }
+ else
+ {
+ r = SQLRowCount (direct_stmt_, &rows);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, *this, direct_stmt_);
+
+ // -1 means the row count is not available. In particular, the
+ // Native Client ODBC driver returns this value for DDL statements.
+ //
+ if (rows == -1)
+ rows = 0;
+ }
+
+ return static_cast<unsigned long long> (rows);
+ }
+
+ // connection_factory
+ //
+ connection_factory::
+ ~connection_factory ()
+ {
+ }
+
+ void connection_factory::
+ database (database_type& db)
+ {
+ odb::connection_factory::db_ = &db;
+ db_ = &db;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/connection.hxx b/libodb-mssql/odb/mssql/connection.hxx
new file mode 100644
index 0000000..204d37e
--- /dev/null
+++ b/libodb-mssql/odb/mssql/connection.hxx
@@ -0,0 +1,187 @@
+// file : odb/mssql/connection.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_CONNECTION_HXX
+#define ODB_MSSQL_CONNECTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/connection.hxx>
+
+#include <odb/details/buffer.hxx>
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/unique-ptr.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/query.hxx>
+#include <odb/mssql/tracer.hxx>
+#include <odb/mssql/transaction-impl.hxx>
+#include <odb/mssql/auto-handle.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class statement_cache;
+ class connection_factory;
+
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+
+ class LIBODB_MSSQL_EXPORT connection: public odb::connection
+ {
+ public:
+ typedef mssql::statement_cache statement_cache_type;
+ typedef mssql::database database_type;
+
+ virtual
+ ~connection ();
+
+ connection (connection_factory&);
+ connection (connection_factory&, SQLHDBC handle);
+
+ database_type&
+ database ();
+
+ public:
+ virtual transaction_impl*
+ begin ();
+
+ public:
+ using odb::connection::execute;
+
+ virtual unsigned long long
+ execute (const char* statement, std::size_t length);
+
+ // Query preparation.
+ //
+ public:
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const mssql::query_base&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const odb::query_base&);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef mssql::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ using odb::connection::tracer;
+
+ public:
+ bool
+ failed () const
+ {
+ return state_ == state_failed;
+ }
+
+ void
+ mark_failed ()
+ {
+ state_ = state_failed;
+ }
+
+ public:
+ SQLHDBC
+ handle ()
+ {
+ return handle_;
+ }
+
+ statement_cache_type&
+ statement_cache ()
+ {
+ return *statement_cache_;
+ }
+
+ details::buffer&
+ long_data_buffer ()
+ {
+ return long_data_buffer_;
+ }
+
+ private:
+ connection (const connection&);
+ connection& operator= (const connection&);
+
+ private:
+ friend class transaction_impl; // invalidate_results()
+
+ private:
+ auto_handle<SQL_HANDLE_DBC> handle_;
+
+ enum
+ {
+ state_disconnected,
+ state_connected,
+ state_failed
+ } state_;
+
+ // Statement handle for direct execution.
+ //
+ auto_handle<SQL_HANDLE_STMT> direct_stmt_;
+ details::unique_ptr<statement_cache_type> statement_cache_;
+ details::buffer long_data_buffer_;
+ };
+
+ class LIBODB_MSSQL_EXPORT connection_factory:
+ public odb::connection_factory
+ {
+ public:
+ typedef mssql::database database_type;
+
+ virtual void
+ database (database_type&);
+
+ database_type&
+ database () {return *db_;}
+
+ virtual connection_ptr
+ connect () = 0;
+
+ virtual
+ ~connection_factory ();
+
+ connection_factory (): db_ (0) {}
+
+ // Needed to break the circular connection_factory-database dependency
+ // (odb::connection_factory has the odb::database member).
+ //
+ protected:
+ database_type* db_;
+ };
+ }
+}
+
+#include <odb/mssql/connection.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_CONNECTION_HXX
diff --git a/libodb-mssql/odb/mssql/connection.ixx b/libodb-mssql/odb/mssql/connection.ixx
new file mode 100644
index 0000000..8ec8294
--- /dev/null
+++ b/libodb-mssql/odb/mssql/connection.ixx
@@ -0,0 +1,44 @@
+// file : odb/mssql/connection.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mssql
+ {
+ inline database& connection::
+ database ()
+ {
+ return static_cast<connection_factory&> (factory_).database ();
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const mssql::query_base& q)
+ {
+ return query_<T, id_mssql>::call (*this, n, q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return prepare_query<T> (n, mssql::query_base (q));
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/container-statements.hxx b/libodb-mssql/odb/mssql/container-statements.hxx
new file mode 100644
index 0000000..16f4bf9
--- /dev/null
+++ b/libodb-mssql/odb/mssql/container-statements.hxx
@@ -0,0 +1,353 @@
+// file : odb/mssql/container-statements.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_CONTAINER_STATEMENTS_HXX
+#define ODB_MSSQL_CONTAINER_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/mssql-types.hxx>
+#include <odb/mssql/statement.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class connection;
+
+ // Template argument is the generated abstract container traits type.
+ // That is, it doesn't need to provide column counts and statements.
+ //
+ template <typename T>
+ class container_statements
+ {
+ public:
+ typedef T traits;
+
+ typedef typename traits::data_image_type data_image_type;
+ typedef typename traits::functions_type functions_type;
+
+ typedef mssql::insert_statement insert_statement_type;
+ typedef mssql::select_statement select_statement_type;
+ typedef mssql::delete_statement delete_statement_type;
+
+ typedef mssql::connection connection_type;
+
+ container_statements (connection_type&, binding& id_binding);
+
+ connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // Functions.
+ //
+ functions_type&
+ functions ()
+ {
+ return functions_;
+ }
+
+ // Schema version.
+ //
+ const schema_version_migration&
+ version_migration () const {return *svm_;}
+
+ void
+ version_migration (const schema_version_migration& svm) {svm_ = &svm;}
+
+ // Id image binding (external).
+ //
+ const binding&
+ id_binding ()
+ {
+ return id_binding_;
+ }
+
+ // Data image. The image is split into the id (that comes as a
+ // binding) and index/key plus value which are in data_image_type.
+ // The select binding is a subset of the full binding (no id).
+ //
+ data_image_type&
+ data_image ()
+ {
+ return data_image_;
+ }
+
+ bind*
+ data_bind ()
+ {
+ return insert_image_binding_.bind;
+ }
+
+ bool
+ data_binding_test_version () const
+ {
+ return data_id_binding_version_ != id_binding_.version ||
+ data_image_version_ != data_image_.version ||
+ insert_image_binding_.version == 0;
+ }
+
+ void
+ data_binding_update_version ()
+ {
+ data_id_binding_version_ = id_binding_.version;
+ data_image_version_ = data_image_.version;
+ insert_image_binding_.version++;
+ select_image_binding_.version++;
+ }
+
+ //
+ // Statements.
+ //
+
+ insert_statement_type&
+ insert_statement ()
+ {
+ if (insert_ == 0)
+ insert_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ insert_text_,
+ versioned_, // Process if versioned.
+ insert_image_binding_,
+ false,
+ false,
+ 0,
+ false));
+
+ return *insert_;
+ }
+
+ select_statement_type&
+ select_statement ()
+ {
+ if (select_ == 0)
+ select_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ select_text_,
+ versioned_, // Process if versioned.
+ false, // Don't optimize.
+ id_binding_,
+ select_image_binding_,
+ false));
+
+ return *select_;
+ }
+
+ delete_statement_type&
+ delete_statement ()
+ {
+ if (delete_ == 0)
+ delete_.reset (
+ new (details::shared) delete_statement_type (
+ conn_, delete_text_, id_binding_, false));
+
+ return *delete_;
+ }
+
+ private:
+ container_statements (const container_statements&);
+ container_statements& operator= (const container_statements&);
+
+ protected:
+ connection_type& conn_;
+ binding& id_binding_;
+
+ functions_type functions_;
+
+ data_image_type data_image_;
+ std::size_t data_image_version_;
+ std::size_t data_id_binding_version_;
+
+ binding insert_image_binding_;
+ binding select_image_binding_;
+
+ const char* insert_text_;
+ const char* select_text_;
+ const char* delete_text_;
+
+ bool versioned_;
+ const schema_version_migration* svm_;
+
+ details::shared_ptr<insert_statement_type> insert_;
+ details::shared_ptr<select_statement_type> select_;
+ details::shared_ptr<delete_statement_type> delete_;
+ };
+
+ template <typename T>
+ class smart_container_statements: public container_statements<T>
+ {
+ public:
+ typedef T traits;
+ typedef typename traits::cond_image_type cond_image_type;
+
+ typedef mssql::update_statement update_statement_type;
+ typedef mssql::delete_statement delete_statement_type;
+
+ typedef mssql::connection connection_type;
+
+ smart_container_statements (connection_type&, binding& id_binding);
+
+ // Condition image. The image is split into the id (that comes as
+ // a binding) and index/key/value which is in cond_image_type.
+ //
+ cond_image_type&
+ cond_image ()
+ {
+ return cond_image_;
+ }
+
+ bind*
+ cond_bind ()
+ {
+ return cond_image_binding_.bind;
+ }
+
+ bool
+ cond_binding_test_version () const
+ {
+ return cond_id_binding_version_ != this->id_binding_.version ||
+ cond_image_version_ != cond_image_.version ||
+ cond_image_binding_.version == 0;
+ }
+
+ void
+ cond_binding_update_version ()
+ {
+ cond_id_binding_version_ = this->id_binding_.version;
+ cond_image_version_ = cond_image_.version;
+ cond_image_binding_.version++;
+ }
+
+ // Update image. The image is split as follows: value comes
+ // from the data image, id comes as binding, and index/key
+ // comes from the condition image.
+ //
+ bind*
+ update_bind ()
+ {
+ return update_image_binding_.bind;
+ }
+
+ bool
+ update_binding_test_version () const
+ {
+ return update_id_binding_version_ != this->id_binding_.version ||
+ update_cond_image_version_ != cond_image_.version ||
+ update_data_image_version_ != this->data_image_.version ||
+ update_image_binding_.version == 0;
+ }
+
+ void
+ update_binding_update_version ()
+ {
+ update_id_binding_version_ = this->id_binding_.version;
+ update_cond_image_version_ = cond_image_.version;
+ update_data_image_version_ = this->data_image_.version;
+ update_image_binding_.version++;
+ }
+
+ //
+ // Statements.
+ //
+
+ delete_statement_type&
+ delete_statement ()
+ {
+ if (this->delete_ == 0)
+ this->delete_.reset (
+ new (details::shared) delete_statement_type (
+ this->conn_,
+ this->delete_text_,
+ this->cond_image_binding_,
+ false));
+
+ return *this->delete_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ this->conn_,
+ update_text_,
+ this->versioned_, // Process if versioned.
+ update_image_binding_,
+ 0,
+ false));
+
+ return *update_;
+ }
+
+ protected:
+ cond_image_type cond_image_;
+ std::size_t cond_image_version_;
+ std::size_t cond_id_binding_version_;
+ binding cond_image_binding_;
+
+ std::size_t update_id_binding_version_;
+ std::size_t update_cond_image_version_;
+ std::size_t update_data_image_version_;
+ binding update_image_binding_;
+
+ const char* update_text_;
+
+ details::shared_ptr<update_statement_type> update_;
+ };
+
+ // Template argument is the generated concrete container traits type.
+ //
+ template <typename T>
+ class container_statements_impl: public T::statements_type
+ {
+ public:
+ typedef T traits;
+ typedef typename T::statements_type base;
+ typedef mssql::connection connection_type;
+
+ container_statements_impl (connection_type&, binding&);
+
+ private:
+ container_statements_impl (const container_statements_impl&);
+ container_statements_impl& operator= (const container_statements_impl&);
+
+ private:
+ bind data_image_bind_[traits::data_column_count];
+ };
+
+ template <typename T>
+ class smart_container_statements_impl: public container_statements_impl<T>
+ {
+ public:
+ typedef T traits;
+ typedef mssql::connection connection_type;
+
+ smart_container_statements_impl (connection_type&, binding&);
+
+ private:
+ bind cond_image_bind_[traits::cond_column_count];
+ bind update_image_bind_[traits::value_column_count +
+ traits::cond_column_count];
+ };
+ }
+}
+
+#include <odb/mssql/container-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_CONTAINER_STATEMENTS_HXX
diff --git a/libodb-mssql/odb/mssql/container-statements.txx b/libodb-mssql/odb/mssql/container-statements.txx
new file mode 100644
index 0000000..6a45086
--- /dev/null
+++ b/libodb-mssql/odb/mssql/container-statements.txx
@@ -0,0 +1,96 @@
+// file : odb/mssql/container-statements.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace mssql
+ {
+ // container_statements
+ //
+ template <typename T>
+ container_statements<T>::
+ container_statements (connection_type& conn, binding& id)
+ : conn_ (conn),
+ id_binding_ (id),
+ functions_ (this),
+ insert_image_binding_ (0, 0), // Initialized by impl.
+ select_image_binding_ (0, 0), // Initialized by impl.
+ svm_ (0)
+ {
+ functions_.insert_ = &traits::insert;
+ functions_.select_ = &traits::select;
+ functions_.delete__ = &traits::delete_;
+
+ data_image_.version = 0;
+ data_image_version_ = 0;
+ data_id_binding_version_ = 0;
+ }
+
+ // smart_container_statements
+ //
+ template <typename T>
+ smart_container_statements<T>::
+ smart_container_statements (connection_type& conn, binding& id)
+ : container_statements<T> (conn, id),
+ cond_image_binding_ (0, 0), // Initialized by impl.
+ update_image_binding_ (0, 0) // Initialized by impl.
+ {
+ this->functions_.update_ = &traits::update;
+
+ cond_image_.version = 0;
+ cond_image_version_ = 0;
+ cond_id_binding_version_ = 0;
+
+ update_id_binding_version_ = 0;
+ update_cond_image_version_ = 0;
+ update_data_image_version_ = 0;
+ }
+
+ // container_statements_impl
+ //
+ template <typename T>
+ container_statements_impl<T>::
+ container_statements_impl (connection_type& conn, binding& id)
+ : base (conn, id)
+ {
+ this->insert_image_binding_.bind = data_image_bind_;
+ this->insert_image_binding_.count = traits::data_column_count;
+
+ this->select_image_binding_.bind = data_image_bind_ +
+ traits::id_column_count;
+ this->select_image_binding_.count = traits::data_column_count -
+ traits::id_column_count;
+
+ std::memset (data_image_bind_, 0, sizeof (data_image_bind_));
+
+ this->insert_text_ = traits::insert_statement;
+ this->select_text_ = traits::select_statement;
+ this->delete_text_ = traits::delete_statement;
+
+ this->versioned_ = traits::versioned;
+ }
+
+ // smart_container_statements_impl
+ //
+ template <typename T>
+ smart_container_statements_impl<T>::
+ smart_container_statements_impl (connection_type& conn, binding& id)
+ : container_statements_impl<T> (conn, id)
+ {
+ this->cond_image_binding_.bind = cond_image_bind_;
+ this->cond_image_binding_.count = traits::cond_column_count;
+
+ this->update_image_binding_.bind = update_image_bind_;
+ this->update_image_binding_.count = traits::value_column_count +
+ traits::cond_column_count;
+
+ std::memset (cond_image_bind_, 0, sizeof (cond_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+
+ this->update_text_ = traits::update_statement;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/database.cxx b/libodb-mssql/odb/mssql/database.cxx
new file mode 100644
index 0000000..6e68bcb
--- /dev/null
+++ b/libodb-mssql/odb/mssql/database.cxx
@@ -0,0 +1,580 @@
+// file : odb/mssql/database.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstring> // std::strcmp, std::strncmp
+#include <sstream>
+
+#include <odb/mssql/mssql.hxx>
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/transaction.hxx>
+#include <odb/mssql/exceptions.hxx>
+#include <odb/mssql/error.hxx>
+
+#include <odb/mssql/details/options.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ using odb::details::transfer_ptr;
+
+ database::
+ database (const string& user,
+ const string& password,
+ const string& db,
+ const string& server,
+ const string& driver,
+ const string& extra_connect_string,
+ transaction_isolation_type transaction_isolation,
+ SQLHENV environment,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mssql),
+ user_ (user),
+ password_ (password),
+ db_ (db),
+ protocol_ (protocol_auto),
+ port_ (0),
+ server_ (server),
+ driver_ (driver),
+ extra_connect_string_ (extra_connect_string),
+ transaction_isolation_ (transaction_isolation),
+ environment_ (environment),
+ factory_ (factory.transfer ())
+ {
+ init ();
+ }
+
+ database::
+ database (const string& user,
+ const string& password,
+ const string& db,
+ protocol_type protocol,
+ const string& host,
+ const string& instance,
+ const string& driver,
+ const string& extra_connect_string,
+ transaction_isolation_type transaction_isolation,
+ SQLHENV environment,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mssql),
+ user_ (user),
+ password_ (password),
+ db_ (db),
+ protocol_ (protocol),
+ host_ (host),
+ instance_ (instance),
+ port_ (0),
+ driver_ (driver),
+ extra_connect_string_ (extra_connect_string),
+ transaction_isolation_ (transaction_isolation),
+ environment_ (environment),
+ factory_ (factory.transfer ())
+ {
+ init ();
+ }
+
+ database::
+ database (const string& user,
+ const string& password,
+ const string& db,
+ const string& host,
+ unsigned int port,
+ const string& driver,
+ const string& extra_connect_string,
+ transaction_isolation_type transaction_isolation,
+ SQLHENV environment,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mssql),
+ user_ (user),
+ password_ (password),
+ db_ (db),
+ protocol_ (protocol_tcp),
+ host_ (host),
+ port_ (port),
+ driver_ (driver),
+ extra_connect_string_ (extra_connect_string),
+ transaction_isolation_ (transaction_isolation),
+ environment_ (environment),
+ factory_ (factory.transfer ())
+ {
+ init ();
+ }
+
+ database::
+ database (const string& connect_string,
+ transaction_isolation_type transaction_isolation,
+ SQLHENV environment,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mssql),
+ protocol_ (protocol_auto),
+ port_ (0),
+ transaction_isolation_ (transaction_isolation),
+ connect_string_ (connect_string),
+ environment_ (environment),
+ factory_ (factory.transfer ())
+ {
+ init ();
+ }
+
+ database::
+ database (int& argc,
+ char* argv[],
+ bool erase,
+ const string& extra_connect_string,
+ transaction_isolation_type transaction_isolation,
+ SQLHENV environment,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mssql),
+ protocol_ (protocol_auto),
+ port_ (0),
+ extra_connect_string_ (extra_connect_string),
+ transaction_isolation_ (transaction_isolation),
+ environment_ (environment),
+ factory_ (factory.transfer ())
+ {
+ using namespace details;
+
+ try
+ {
+ cli::argv_file_scanner scan (argc, argv, "--options-file", erase);
+ options ops (scan, cli::unknown_mode::skip, cli::unknown_mode::skip);
+
+ user_ = ops.user ();
+ password_ = ops.password ();
+ db_ = ops.database ();
+ server_ = ops.server ();
+ driver_ = ops.driver ();
+ }
+ catch (const cli::exception& e)
+ {
+ ostringstream oss;
+ oss << e;
+ throw cli_exception (oss.str ());
+ }
+
+ init ();
+ }
+
+ /*
+
+ NOTE: This code hasn't been tested.
+
+ void database::
+ parse ()
+ {
+ // Parse the server string and extract individual parts (protocol,
+ // host, instance, and port).
+ //
+ string port;
+
+ if (server_.compare (0, 4, "lpc:") == 0)
+ {
+ // lpc:<host>[\<instance>]
+ //
+ protocol_ = protocol_shm;
+ string::size_type p (server_.find (4, '\\'));
+
+ if (p == string::npos)
+ host_.assign (server_, 4, string::npos);
+ else
+ {
+ host_.assign (server_, 4, p - 4);
+ instance_.assign (server_, p + 1, string::npos);
+ }
+ }
+ else if (server_.compare (0, 3, "np:") == 0)
+ {
+ // np:<host>\pipe\[MSSQL$<instance>\]sql\query
+ //
+ protocol_ = protocol_pipe;
+
+ string::size_type p (server_.find (3, '\\'));
+
+ if (p != string::npos)
+ {
+ host_.assign (server_, 3, p - 3);
+
+ p = server_.find (p + 1, '$');
+
+ if (p != string::npos)
+ {
+ p++;
+ instance_.assign (server_, p, server_.find (p, '\\') - p);
+ }
+ }
+ }
+ else
+ {
+ // <host>[\<instance>][,<port>]
+ // tcp:<host>[\<instance>][,<port>]
+ //
+ string::size_type p1 (0), p2;
+
+ if (server_.compare (0, 4, "tcp:") == 0)
+ {
+ protocol_ = protocol_tcp;
+ p1 = 4;
+ }
+
+ p2 = server_.find (p1, '\\');
+
+ if (p2 == string::npos)
+ {
+ p2 = server_.find (p1, ',');
+
+ if (p2 == string::npos)
+ host_.assign (server_, p1, string::npos);
+ else
+ {
+ host_.assign (server_, 4, p2 - p1);
+ port.assign (server_, p2 + 1, string::npos);
+ }
+ }
+ else
+ {
+ host_.assign (server_, 4, p2 - p1);
+
+ p1 = server_.find (p2 + 1, ',');
+
+ if (p1 == string::npos)
+ instance_.assign (server_, p2 + 1, string::npos);
+ else
+ {
+ instance_.assign (server_, p2 + 1, p1 - p2 - 1);
+ port.assign (server_, p1 + 1, string::npos);
+ }
+ }
+ }
+
+ if (!port.empty ())
+ {
+ istringstream is (port);
+ is >> port;
+ protocol_ = protocol_tcp;
+ }
+ }
+ */
+
+ void database::
+ init ()
+ {
+ SQLRETURN r;
+
+ if (environment_ == 0)
+ {
+ r = SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &environment_);
+
+ if (!SQL_SUCCEEDED (r))
+ throw database_exception (
+ 0, "?????", "unable to allocate environment handle");
+
+ auto_environment_.reset (environment_);
+
+ // Set ODBC version.
+ //
+ r = SQLSetEnvAttr (environment_,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ 0);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, environment_, SQL_HANDLE_ENV);
+ }
+
+ // Build the connection string.
+ //
+ if (connect_string_.empty ())
+ {
+ // Find the driver.
+ //
+ if (driver_.empty ())
+ {
+ for (bool first (true);; )
+ {
+ char desc[256];
+ SQLSMALLINT desc_size, attr_size;
+
+ r = SQLDriversA (environment_,
+ first ? SQL_FETCH_FIRST : SQL_FETCH_NEXT,
+ (SQLCHAR*) desc,
+ sizeof (desc),
+ &desc_size,
+ 0,
+ 0,
+ &attr_size);
+
+ if (r == SQL_NO_DATA)
+ break;
+ else if (!SQL_SUCCEEDED (r))
+ translate_error (r, environment_, SQL_HANDLE_ENV);
+
+ // Native Client 9.0 (first version).
+ //
+ if (strcmp (desc, "SQL Native Client") == 0 ||
+ strncmp (desc, "SQL Server Native Client", 24) == 0)
+ {
+ // Compare driver strings lexicographically. Provided that
+ // Microsoft keeps its naming consistent, we will get the
+ // correct result. For example, "SQL Server Native Client
+ // 10.0" (SQL Server 2008) will be greater than "SQL Native
+ // Client" (SQL Server 2005). Similarly, "SQL Server Native
+ // Client 11.0" (SQL Server 2012) will be preferred over
+ // "SQL Server Native Client 10.0" (SQL Server 2008).
+ //
+ if (desc > driver_)
+ driver_ = desc;
+ }
+
+ if (first)
+ first = false;
+ }
+ }
+
+ connect_string_ += "DRIVER={";
+ connect_string_ += driver_;
+ connect_string_ += "};";
+
+ // If necessary, assemble the server address string, depending
+ // on which protocol we are using.
+ if (server_.empty ())
+ {
+ switch (protocol_)
+ {
+ case protocol_auto:
+ {
+ server_ = (host_.empty () ? "localhost" : host_.c_str ());
+
+ if (!instance_.empty ())
+ {
+ server_ += '\\';
+ server_ += instance_;
+ }
+
+ break;
+ }
+ case protocol_tcp:
+ {
+ server_ = "tcp:";
+ server_ += (host_.empty () ? "localhost" : host_.c_str ());
+
+ // Port seems to take precedence over instance. For example,
+ // if you specify both, and the instance name is invalid, the
+ // Native Client driver still connects without any problems.
+ //
+ if (port_ != 0)
+ {
+ ostringstream os;
+ os << port_;
+ server_ += ',';
+ server_ += os.str ();
+ }
+ else if (!instance_.empty ())
+ {
+ server_ += '\\';
+ server_ += instance_;
+ }
+
+ break;
+ }
+ case protocol_lpc:
+ {
+ server_ = "lpc:";
+ server_ += (host_.empty () ? "localhost" : host_.c_str ());
+
+ if (!instance_.empty ())
+ {
+ server_ += '\\';
+ server_ += instance_;
+ }
+
+ break;
+ }
+ case protocol_np:
+ {
+ server_ = "np:\\\\";
+ server_ += (host_.empty () ? "." : host_.c_str ());
+ server_ += "\\pipe\\";
+
+ if (!instance_.empty ())
+ {
+ server_ += "MSSQL$";
+ server_ += instance_;
+ server_ += '\\';
+ }
+
+ server_ += "sql\\query";
+ break;
+ }
+ }
+ }
+
+ // The Address attribute seems to be preferred over SERVER. However,
+ // SQL Server 2005 Native Client only seem to support Address since
+ // SP1. Since we don't know the exact driver version, for now always
+ // use SERVER with SQL Server 2005 driver.
+ //
+ connect_string_ += (driver_ == "SQL Native Client"
+ ? "SERVER={"
+ : "Address={");
+
+ connect_string_ += server_;
+ connect_string_ += "};";
+
+ // Add login information.
+ //
+ if (user_.empty ())
+ // Windows authentication.
+ //
+ connect_string_ += "Trusted_Connection=yes;";
+ else
+ {
+ connect_string_ += "UID={";
+ connect_string_ += user_;
+ connect_string_ += "};";
+
+ if (!password_.empty ())
+ {
+ connect_string_ += "PWD={";
+ connect_string_ += password_;
+ connect_string_ += "};";
+ }
+ }
+
+ // Add database.
+ //
+ if (!db_.empty ())
+ {
+ connect_string_ += "Database={";
+ connect_string_ += db_;
+ connect_string_ += "};";
+ }
+
+ // Add any extra connection attributes.
+ //
+ if (!extra_connect_string_.empty ())
+ connect_string_ += extra_connect_string_;
+ }
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ void database::
+ print_usage (ostream& os)
+ {
+ details::options::print_usage (os);
+ }
+
+ database::
+ ~database ()
+ {
+ }
+
+ transaction_impl* database::
+ begin ()
+ {
+ return new transaction_impl (*this);
+ }
+
+ odb::connection* database::
+ connection_ ()
+ {
+ connection_ptr c (factory_->connect ());
+ return c.release ();
+ }
+
+ const database::schema_version_info& database::
+ load_schema_version (const string& name) const
+ {
+ schema_version_info& svi (schema_version_map_[name]);
+
+ // Construct the SELECT statement text.
+ //
+ string text ("SELECT [version], [migration] FROM ");
+
+ if (!svi.version_table.empty ())
+ text += svi.version_table; // Already quoted.
+ else if (!schema_version_table_.empty ())
+ text += schema_version_table_; // Already quoted.
+ else
+ text += "[schema_version]";
+
+ text += " WHERE [name] = ?";
+
+ // Bind parameters and results.
+ //
+ SQLLEN psize[1] = {static_cast<SQLLEN> (name.size ())};
+ bind pbind[1] = {
+ {bind::string, const_cast<char*> (name.c_str ()), &psize[0], 0}};
+ binding param (pbind, 1);
+ param.version++;
+
+ signed char migration;
+ SQLLEN rsize[2];
+ bind rbind[2] = {{bind::bigint, &svi.version, &rsize[0], 0},
+ {bind::bit, &migration, &rsize[1], 0}};
+ binding result (rbind, 2);
+ result.version++;
+
+ // If we are not in transaction, start one.
+ //
+ transaction t;
+ if (!transaction::has_current ())
+ t.reset (factory_->connect ()->begin (), false);
+
+ mssql::connection& c (
+ t.finalized ()
+ ? transaction::current ().connection (const_cast<database&> (*this))
+ : t.connection (const_cast<database&> (*this)));
+
+ try
+ {
+ select_statement st (c,
+ text.c_str (),
+ false, // Don't process.
+ false, // Don't optimize.
+ param,
+ result,
+ false);
+
+ st.execute ();
+ auto_result ar (st);
+
+ switch (st.fetch ())
+ {
+ case select_statement::success:
+ {
+ svi.migration = migration != 0;
+ assert (st.fetch () == select_statement::no_data);
+ break;
+ }
+ case select_statement::no_data:
+ {
+ svi.version = 0; // No schema.
+ break;
+ }
+ }
+ }
+ catch (const database_exception& e)
+ {
+ // Detect the case where there is no version table. The SQL Server-
+ // specific error code (208) seems to be too generic.
+ //
+ if (e.begin ()->sqlstate () == "42S02")
+ svi.version = 0; // No schema.
+ else
+ throw;
+ }
+
+ if (!t.finalized ())
+ t.commit ();
+
+ return svi;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/database.hxx b/libodb-mssql/odb/mssql/database.hxx
new file mode 100644
index 0000000..5367bc5
--- /dev/null
+++ b/libodb-mssql/odb/mssql/database.hxx
@@ -0,0 +1,629 @@
+// file : odb/mssql/database.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_DATABASE_HXX
+#define ODB_MSSQL_DATABASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <iosfwd> // std::ostream
+
+#include <odb/database.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+#include <odb/details/unique-ptr.hxx>
+#include <odb/details/transfer-ptr.hxx>
+
+#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/query.hxx>
+#include <odb/mssql/tracer.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/connection-factory.hxx>
+#include <odb/mssql/auto-handle.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class transaction_impl;
+
+ 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 LIBODB_MSSQL_EXPORT database: public odb::database
+ {
+ public:
+ typedef mssql::protocol protocol_type;
+ typedef mssql::transaction_isolation transaction_isolation_type;
+
+ // Connect to the specified server using the latest available SQL
+ // Server Native Client ODBC driver by default. If user is empty,
+ // then use Windows authentication. If db is empty, then use the
+ // default database for this user.
+ //
+ 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,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // By default connect to the default instance on localhost using
+ // default protocol and the latest available SQL Server Native
+ // Client ODBC driver. If user is empty, then use Windows
+ // authentication. If db is empty, then use the default database
+ // for this user.
+ //
+ 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,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Connect using TCP/IP to the specified host and port. If port is
+ // 0, use the default port (1433).
+ //
+ 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,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Connect using a custom SQL Server Native Client ODBC driver
+ // conection string.
+ //
+ database (const std::string& connect_string,
+ transaction_isolation_type = isolation_read_committed,
+ SQLHENV environment = 0,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Extract the database parameters from the command line. The
+ // following options are recognized:
+ //
+ // --user | -U
+ // --password | -P
+ // --database | -d
+ // --server | -S
+ // --driver
+ // --options-file
+ //
+ // For more information, see the output of the print_usage() function
+ // below. If erase is true, the above options are removed from the
+ // argv array and the argc count is updated accordingly. This
+ // constructor may throw the cli_exception exception.
+ //
+ database (int& argc,
+ char* argv[],
+ bool erase = false,
+ const std::string& extra_connect_string = "",
+ transaction_isolation_type = isolation_read_committed,
+ SQLHENV environment = 0,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Move-constructible but not move-assignable.
+ //
+ // Note: noexcept is not specified since odb::database(odb::database&&)
+ // can throw.
+ //
+#ifdef ODB_CXX11
+ database (database&&);
+#endif
+
+ static void
+ print_usage (std::ostream&);
+
+ // Object persistence API.
+ //
+ public:
+
+ // Make the object persistent.
+ //
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Bulk persist. Can be a range of references or pointers (including
+ // smart pointers) to objects.
+ //
+ template <typename I>
+ void
+ persist (I begin, I end, bool continue_failed = true);
+
+ // Load an object. Throw object_not_persistent if not found.
+ //
+ 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);
+
+ // Load (or reload, if it is already loaded) a section of an object.
+ //
+ template <typename T>
+ void
+ load (T& object, section&);
+
+ // Reload an object.
+ //
+ template <typename T>
+ void
+ reload (T& object);
+
+ template <typename T>
+ void
+ reload (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ reload (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Loan an object if found. Return NULL/false if not found.
+ //
+ 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);
+
+ // Update the state of a modified objects.
+ //
+ template <typename T>
+ void
+ update (T& object);
+
+ template <typename T>
+ void
+ update (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ update (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Bulk update. Can be a range of references or pointers (including
+ // smart pointers) to objects.
+ //
+ template <typename I>
+ void
+ update (I begin, I end, bool continue_failed = true);
+
+ // Update a section of an object. Throws the section_not_loaded
+ // exception if the section is not loaded. Note also that this
+ // function does not clear the changed flag if it is set.
+ //
+ template <typename T>
+ void
+ update (const T& object, const section&);
+
+ // Make the object transient. Throw object_not_persistent if not
+ // found.
+ //
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::id_type& id);
+
+ template <typename T>
+ void
+ erase (T& object);
+
+ template <typename T>
+ void
+ erase (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Bulk erase.
+ //
+ template <typename T, typename I>
+ void
+ erase (I id_begin, I id_end, bool continue_failed = true);
+
+ // Can be a range of references or pointers (including smart pointers)
+ // to objects.
+ //
+ template <typename I>
+ void
+ erase (I obj_begin, I obj_end, bool continue_failed = true);
+
+ // Erase multiple objects matching a query predicate.
+ //
+ template <typename T>
+ unsigned long long
+ erase_query ();
+
+ template <typename T>
+ unsigned long long
+ erase_query (const char*);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const std::string&);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const mssql::query_base&);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const odb::query_base&);
+
+ // Query API.
+ //
+ template <typename T>
+ result<T>
+ query ();
+
+ template <typename T>
+ result<T>
+ query (const char*);
+
+ template <typename T>
+ result<T>
+ query (const std::string&);
+
+ template <typename T>
+ result<T>
+ query (const mssql::query_base&);
+
+ template <typename T>
+ result<T>
+ query (const odb::query_base&);
+
+ // Query one API.
+ //
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one ();
+
+ template <typename T>
+ bool
+ query_one (T& object);
+
+ template <typename T>
+ T
+ query_value ();
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const char*);
+
+ template <typename T>
+ bool
+ query_one (const char*, T& object);
+
+ template <typename T>
+ T
+ query_value (const char*);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const std::string&);
+
+ template <typename T>
+ bool
+ query_one (const std::string&, T& object);
+
+ template <typename T>
+ T
+ query_value (const std::string&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const mssql::query_base&);
+
+ template <typename T>
+ bool
+ query_one (const mssql::query_base&, T& object);
+
+ template <typename T>
+ T
+ query_value (const mssql::query_base&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const odb::query_base&);
+
+ template <typename T>
+ bool
+ query_one (const odb::query_base&, T& object);
+
+ template <typename T>
+ T
+ query_value (const odb::query_base&);
+
+ // Query preparation.
+ //
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const mssql::query_base&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const odb::query_base&);
+
+ // Transactions.
+ //
+ public:
+ virtual transaction_impl*
+ begin ();
+
+ public:
+ connection_ptr
+ connection ();
+
+ public:
+ const std::string&
+ user () const
+ {
+ return user_;
+ }
+
+ const std::string&
+ password () const
+ {
+ return password_;
+ }
+
+ const std::string&
+ db () const
+ {
+ return db_;
+ }
+
+ protocol_type
+ protocol () const
+ {
+ return protocol_;
+ }
+
+ const std::string&
+ host () const
+ {
+ return host_;
+ }
+
+ const std::string&
+ instance () const
+ {
+ return instance_;
+ }
+
+ unsigned int
+ port () const
+ {
+ return port_;
+ }
+
+ const std::string&
+ server () const
+ {
+ return server_;
+ }
+
+ const std::string&
+ driver () const
+ {
+ return driver_;
+ }
+
+ const std::string&
+ extra_connect_string () const
+ {
+ return extra_connect_string_;
+ }
+
+ transaction_isolation_type
+ transaction_isolation () const
+ {
+ return transaction_isolation_;
+ }
+
+ const std::string&
+ connect_string () const
+ {
+ return connect_string_;
+ }
+
+ SQLHENV
+ environment ()
+ {
+ return environment_;
+ }
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef mssql::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::database::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::database::tracer (t);
+ }
+
+ using odb::database::tracer;
+
+ // Database schema version.
+ //
+ protected:
+ virtual const schema_version_info&
+ load_schema_version (const std::string& schema_name) const;
+
+ public:
+ // Database id constant (useful for meta-programming).
+ //
+ static const odb::database_id database_id = id_mssql;
+
+ public:
+ virtual
+ ~database ();
+
+ protected:
+ virtual odb::connection*
+ connection_ ();
+
+ private:
+ void
+ init ();
+
+ private:
+ // Note: remember to update move ctor if adding any new members.
+ //
+ std::string user_;
+ std::string password_;
+ std::string db_;
+ protocol_type protocol_;
+ std::string host_;
+ std::string instance_;
+ unsigned int port_;
+ std::string server_;
+ std::string driver_;
+ std::string extra_connect_string_;
+ transaction_isolation_type transaction_isolation_;
+ std::string connect_string_;
+
+ auto_handle<SQL_HANDLE_ENV> auto_environment_;
+ SQLHENV environment_;
+
+ details::unique_ptr<connection_factory> factory_;
+ };
+ }
+}
+
+#include <odb/mssql/database.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_DATABASE_HXX
diff --git a/libodb-mssql/odb/mssql/database.ixx b/libodb-mssql/odb/mssql/database.ixx
new file mode 100644
index 0000000..ae1b83b
--- /dev/null
+++ b/libodb-mssql/odb/mssql/database.ixx
@@ -0,0 +1,644 @@
+// file : odb/mssql/database.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <utility> // move()
+
+#include <odb/mssql/transaction.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+#ifdef ODB_CXX11
+ inline database::
+ database (database&& db) // Has to be inline.
+ : odb::database (std::move (db)),
+ user_ (std::move (db.user_)),
+ password_ (std::move (db.password_)),
+ db_ (std::move (db.db_)),
+ protocol_ (db.protocol_),
+ host_ (std::move (db.host_)),
+ instance_ (std::move (db.instance_)),
+ port_ (db.port_),
+ server_ (std::move (db.server_)),
+ driver_ (std::move (db.driver_)),
+ extra_connect_string_ (std::move (db.extra_connect_string_)),
+ transaction_isolation_ (db.transaction_isolation_),
+ connect_string_ (std::move (db.connect_string_)),
+ auto_environment_ (std::move (db.auto_environment_)),
+ environment_ (db.environment_),
+ factory_ (std::move (db.factory_))
+ {
+ factory_->database (*this); // New database instance.
+ }
+#endif
+
+ inline connection_ptr database::
+ connection ()
+ {
+ // Go through the virtual connection_() function instead of
+ // directly to allow overriding.
+ //
+ return connection_ptr (
+ static_cast<mssql::connection*> (connection_ ()));
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T& obj)
+ {
+ return persist_<T, id_mssql> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const T& obj)
+ {
+ return persist_<const T, id_mssql> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T>& p)
+ {
+ const P<T>& cr (p);
+ return persist<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ return persist<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const typename object_traits<T>::pointer_type& pobj)
+ {
+ return persist_<T, id_mssql> (pobj);
+ }
+
+ template <typename I>
+ inline void database::
+ persist (I b, I e, bool cont)
+ {
+ persist_<I, id_mssql> (b, e, cont);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ load (const typename object_traits<T>::id_type& id)
+ {
+ return load_<T, id_mssql> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ load (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return load_<T, id_mssql> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ load (T& obj, section& s)
+ {
+ return load_<T, id_mssql> (obj, s);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ find (const typename object_traits<T>::id_type& id)
+ {
+ return find_<T, id_mssql> (id);
+ }
+
+ template <typename T>
+ inline bool database::
+ find (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return find_<T, id_mssql> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T& obj)
+ {
+ reload_<T, id_mssql> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T* p)
+ {
+ reload<T> (*p);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (const P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (const P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T>
+ inline void database::
+ reload (const typename object_traits<T>::pointer_type& pobj)
+ {
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ reload (odb::pointer_traits<pointer_type>::get_ref (pobj));
+ }
+
+ template <typename T>
+ inline void database::
+ update (T& obj)
+ {
+ update_<T, id_mssql> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ update (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (P<T>& p)
+ {
+ const P<T>& cr (p);
+ update<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ update<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const typename object_traits<T>::pointer_type& pobj)
+ {
+ update_<T, id_mssql> (pobj);
+ }
+
+ template <typename I>
+ inline void database::
+ update (I b, I e, bool cont)
+ {
+ update_<I, id_mssql> (b, e, cont);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const T& obj, const section& s)
+ {
+ update_<T, id_mssql> (obj, s);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::id_type& id)
+ {
+ return erase_<T, id_mssql> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T& obj)
+ {
+ return erase_<T, id_mssql> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (P<T>& p)
+ {
+ const P<T>& cr (p);
+ erase<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ erase<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::pointer_type& pobj)
+ {
+ erase_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, typename I>
+ inline void database::
+ erase (I idb, I ide, bool cont)
+ {
+ erase_id_<I, T, id_mssql> (idb, ide, cont);
+ }
+
+ template <typename I>
+ inline void database::
+ erase (I ob, I oe, bool cont)
+ {
+ erase_object_<I, id_mssql> (ob, oe, cont);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query ()
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (mssql::query_base ());
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const char* q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const std::string& q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const mssql::query_base& q)
+ {
+ // T is always object_type.
+ //
+ return object_traits_impl<T, id_mssql>::erase_query (*this, q);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return erase_query<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query ()
+ {
+ return query<T> (mssql::query_base ());
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const char* q)
+ {
+ return query<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const std::string& q)
+ {
+ return query<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const mssql::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_<T, id_mssql>::call (*this, q);
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one ()
+ {
+ return query_one<T> (mssql::query_base ());
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (T& o)
+ {
+ return query_one<T> (mssql::query_base (), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value ()
+ {
+ return query_value<T> (mssql::query_base ());
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const char* q)
+ {
+ return query_one<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const char* q, T& o)
+ {
+ return query_one<T> (mssql::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const char* q)
+ {
+ return query_value<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const std::string& q)
+ {
+ return query_one<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const std::string& q, T& o)
+ {
+ return query_one<T> (mssql::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const std::string& q)
+ {
+ return query_value<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const mssql::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_one_<T, id_mssql> (q);
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const mssql::query_base& q, T& o)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_one_<T, id_mssql> (q, o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const mssql::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_value_<T, id_mssql> (q);
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query_one<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const odb::query_base& q, T& o)
+ {
+ // Translate to native query.
+ //
+ return query_one<T> (mssql::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query_value<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const mssql::query_base& q)
+ {
+ // Throws if not in transaction.
+ //
+ mssql::connection& c (transaction::current ().connection (*this));
+ return c.prepare_query<T> (n, q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return prepare_query<T> (n, mssql::query_base (q));
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/details/.gitignore b/libodb-mssql/odb/mssql/details/.gitignore
new file mode 100644
index 0000000..b298f89
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/.gitignore
@@ -0,0 +1 @@
+/options.?xx
diff --git a/libodb-mssql/odb/mssql/details/config.hxx b/libodb-mssql/odb/mssql/details/config.hxx
new file mode 100644
index 0000000..1459f2f
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/config.hxx
@@ -0,0 +1,15 @@
+// file : odb/mssql/details/config.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_DETAILS_CONFIG_HXX
+#define ODB_MSSQL_DETAILS_CONFIG_HXX
+
+// no pre
+
+#ifdef ODB_COMPILER
+# error libodb-mssql header included in odb-compiled header
+#endif
+
+// no post
+
+#endif // ODB_MSSQL_DETAILS_CONFIG_HXX
diff --git a/libodb-mssql/odb/mssql/details/conversion.hxx b/libodb-mssql/odb/mssql/details/conversion.hxx
new file mode 100644
index 0000000..35f368d
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/conversion.hxx
@@ -0,0 +1,58 @@
+// file : odb/mssql/details/conversion.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_DETAILS_CONVERSION_HXX
+#define ODB_MSSQL_DETAILS_CONVERSION_HXX
+
+#include <odb/mssql/traits.hxx>
+
+#include <odb/details/meta/answer.hxx>
+
+namespace odb
+{
+ // @@ Revise this.
+ //
+ namespace details {}
+
+ namespace mssql
+ {
+ namespace details
+ {
+ using namespace odb::details;
+
+ // Detect whether conversion is specified in type_traits.
+ //
+ template <typename T>
+ meta::yes
+ conversion_p_test (typename type_traits<T>::conversion*);
+
+ template <typename T>
+ meta::no
+ conversion_p_test (...);
+
+ template <typename T>
+ struct conversion_p
+ {
+ static const bool value =
+ sizeof (conversion_p_test<T> (0)) == sizeof (meta::yes);
+ };
+
+ template <typename T, bool = conversion_p<T>::value>
+ struct conversion;
+
+ template <typename T>
+ struct conversion<T, true>
+ {
+ static const char* to () {return type_traits<T>::conversion::to ();}
+ };
+
+ template <typename T>
+ struct conversion<T, false>
+ {
+ static const char* to () {return 0;}
+ };
+ }
+ }
+}
+
+#endif // ODB_MSSQL_DETAILS_CONVERSION_HXX
diff --git a/libodb-mssql/odb/mssql/details/export.hxx b/libodb-mssql/odb/mssql/details/export.hxx
new file mode 100644
index 0000000..cd74078
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/export.hxx
@@ -0,0 +1,50 @@
+// file : odb/mssql/details/export.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_DETAILS_EXPORT_HXX
+#define ODB_MSSQL_DETAILS_EXPORT_HXX
+
+#include <odb/pre.hxx>
+
+// Note: do this check directly instead of including config.hxx.
+//
+#ifdef ODB_COMPILER
+# error libodb-mssql header included in odb-compiled header
+#endif
+
+// Normally we don't export class templates (but do complete specializations),
+// inline functions, and classes with only inline member functions. Exporting
+// classes that inherit from non-exported/imported bases (e.g., std::string)
+// will end up badly. The only known workarounds are to not inherit or to not
+// export. Also, MinGW GCC doesn't like seeing non-exported function being
+// used before their inline definition. The workaround is to reorder code. In
+// the end it's all trial and error.
+
+#if defined(LIBODB_MSSQL_STATIC) // Using static.
+# define LIBODB_MSSQL_EXPORT
+#elif defined(LIBODB_MSSQL_STATIC_BUILD) // Building static.
+# define LIBODB_MSSQL_EXPORT
+#elif defined(LIBODB_MSSQL_SHARED) // Using shared.
+# ifdef _WIN32
+# define LIBODB_MSSQL_EXPORT __declspec(dllimport)
+# else
+# define LIBODB_MSSQL_EXPORT
+# endif
+#elif defined(LIBODB_MSSQL_SHARED_BUILD) // Building shared.
+# ifdef _WIN32
+# define LIBODB_MSSQL_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_MSSQL_EXPORT
+# endif
+#else
+// If none of the above macros are defined, then we assume we are being used
+// by some third-party build system that cannot/doesn't signal the library
+// type. Note that this fallback works for both static and shared but in case
+// of shared will be sub-optimal compared to having dllimport.
+//
+# define LIBODB_MSSQL_EXPORT // Using static or shared.
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_DETAILS_EXPORT_HXX
diff --git a/libodb-mssql/odb/mssql/details/options.cli b/libodb-mssql/odb/mssql/details/options.cli
new file mode 100644
index 0000000..dcf92e5
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/options.cli
@@ -0,0 +1,63 @@
+// file : odb/mssql/details/options.cli
+// license : ODB NCUEL; see accompanying LICENSE file
+
+include <string>;
+
+namespace odb
+{
+ namespace mssql
+ {
+ namespace details
+ {
+ class options
+ {
+ std::string --user | -U
+ {
+ "<name>",
+ "SQL Server database user. If not specified, then Windows
+ authentication is used."
+ };
+
+ std::string --password | -P
+ {
+ "<str>",
+ "SQL Server database password. Omit this option if the user
+ password is blank or Windows authentication is used."
+ };
+
+ std::string --database | -d
+ {
+ "<name>",
+ "SQL Server database name. If not specified, then the default
+ database for this user is used."
+ };
+
+ std::string --server | -S
+ {
+ "<addr>",
+ "SQL Server instance address in the
+ \c{[\i{protocol}\b{:}]\i{host}[\b{\\}\i{instance}][\b{,}\i{port}]}
+ format, where \ci{protocol} can be \cb{tcp} (TCP/IP),
+ \cb{lpc} (shared memory), or \cb{np} (named pipe). If not specifid,
+ then \cb{localhost} is used."
+ };
+
+ std::string --driver
+ {
+ "<name>",
+ "SQL Server Native Client ODBC driver name. If not specified, then
+ the latest available driver is used."
+ };
+
+ std::string --options-file
+ {
+ "<file>",
+ "Read additional options from <file>. Each option should appear on a
+ separate line optionally followed by space or equal sign (\cb{=})
+ and an option value. Empty lines and lines starting with \cb{#} are
+ ignored."
+ };
+ };
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.cxx b/libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.cxx
new file mode 100644
index 0000000..905beb8
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.cxx
@@ -0,0 +1,1125 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <odb/mssql/details/options.hxx>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <utility>
+#include <ostream>
+#include <sstream>
+#include <cstring>
+#include <fstream>
+
+namespace odb
+{
+ namespace mssql
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ // unknown_option
+ //
+ unknown_option::
+ ~unknown_option () throw ()
+ {
+ }
+
+ void unknown_option::
+ print (::std::ostream& os) const
+ {
+ os << "unknown option '" << option ().c_str () << "'";
+ }
+
+ const char* unknown_option::
+ what () const throw ()
+ {
+ return "unknown option";
+ }
+
+ // unknown_argument
+ //
+ unknown_argument::
+ ~unknown_argument () throw ()
+ {
+ }
+
+ void unknown_argument::
+ print (::std::ostream& os) const
+ {
+ os << "unknown argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unknown_argument::
+ what () const throw ()
+ {
+ return "unknown argument";
+ }
+
+ // missing_value
+ //
+ missing_value::
+ ~missing_value () throw ()
+ {
+ }
+
+ void missing_value::
+ print (::std::ostream& os) const
+ {
+ os << "missing value for option '" << option ().c_str () << "'";
+ }
+
+ const char* missing_value::
+ what () const throw ()
+ {
+ return "missing option value";
+ }
+
+ // invalid_value
+ //
+ invalid_value::
+ ~invalid_value () throw ()
+ {
+ }
+
+ void invalid_value::
+ print (::std::ostream& os) const
+ {
+ os << "invalid value '" << value ().c_str () << "' for option '"
+ << option ().c_str () << "'";
+
+ if (!message ().empty ())
+ os << ": " << message ().c_str ();
+ }
+
+ const char* invalid_value::
+ what () const throw ()
+ {
+ return "invalid option value";
+ }
+
+ // eos_reached
+ //
+ void eos_reached::
+ print (::std::ostream& os) const
+ {
+ os << what ();
+ }
+
+ const char* eos_reached::
+ what () const throw ()
+ {
+ return "end of argument stream reached";
+ }
+
+ // file_io_failure
+ //
+ file_io_failure::
+ ~file_io_failure () throw ()
+ {
+ }
+
+ void file_io_failure::
+ print (::std::ostream& os) const
+ {
+ os << "unable to open file '" << file ().c_str () << "' or read failure";
+ }
+
+ const char* file_io_failure::
+ what () const throw ()
+ {
+ return "unable to open file or read failure";
+ }
+
+ // unmatched_quote
+ //
+ unmatched_quote::
+ ~unmatched_quote () throw ()
+ {
+ }
+
+ void unmatched_quote::
+ print (::std::ostream& os) const
+ {
+ os << "unmatched quote in argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unmatched_quote::
+ what () const throw ()
+ {
+ return "unmatched quote";
+ }
+
+ // scanner
+ //
+ scanner::
+ ~scanner ()
+ {
+ }
+
+ // argv_scanner
+ //
+ bool argv_scanner::
+ more ()
+ {
+ return i_ < argc_;
+ }
+
+ const char* argv_scanner::
+ peek ()
+ {
+ if (i_ < argc_)
+ return argv_[i_];
+ else
+ throw eos_reached ();
+ }
+
+ const char* argv_scanner::
+ next ()
+ {
+ if (i_ < argc_)
+ {
+ const char* r (argv_[i_]);
+
+ if (erase_)
+ {
+ for (int i (i_ + 1); i < argc_; ++i)
+ argv_[i - 1] = argv_[i];
+
+ --argc_;
+ argv_[argc_] = 0;
+ }
+ else
+ ++i_;
+
+ ++start_position_;
+ return r;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ void argv_scanner::
+ skip ()
+ {
+ if (i_ < argc_)
+ {
+ ++i_;
+ ++start_position_;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ std::size_t argv_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ // argv_file_scanner
+ //
+ int argv_file_scanner::zero_argc_ = 0;
+ std::string argv_file_scanner::empty_string_;
+
+ bool argv_file_scanner::
+ more ()
+ {
+ if (!args_.empty ())
+ return true;
+
+ while (base::more ())
+ {
+ // See if the next argument is the file option.
+ //
+ const char* a (base::peek ());
+ const option_info* oi = 0;
+ const char* ov = 0;
+
+ if (!skip_)
+ {
+ if ((oi = find (a)) != 0)
+ {
+ base::next ();
+
+ if (!base::more ())
+ throw missing_value (a);
+
+ ov = base::next ();
+ }
+ else if (std::strncmp (a, "-", 1) == 0)
+ {
+ if ((ov = std::strchr (a, '=')) != 0)
+ {
+ std::string o (a, 0, ov - a);
+ if ((oi = find (o.c_str ())) != 0)
+ {
+ base::next ();
+ ++ov;
+ }
+ }
+ }
+ }
+
+ if (oi != 0)
+ {
+ if (oi->search_func != 0)
+ {
+ std::string f (oi->search_func (ov, oi->arg));
+
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ load (ov);
+
+ if (!args_.empty ())
+ return true;
+ }
+ else
+ {
+ if (!skip_)
+ skip_ = (std::strcmp (a, "--") == 0);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ const char* argv_file_scanner::
+ peek ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? base::peek () : args_.front ().value.c_str ();
+ }
+
+ const std::string& argv_file_scanner::
+ peek_file ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? empty_string_ : *args_.front ().file;
+ }
+
+ std::size_t argv_file_scanner::
+ peek_line ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? 0 : args_.front ().line;
+ }
+
+ const char* argv_file_scanner::
+ next ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::next ();
+ else
+ {
+ hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value);
+ args_.pop_front ();
+ ++start_position_;
+ return hold_[i_].c_str ();
+ }
+ }
+
+ void argv_file_scanner::
+ skip ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::skip ();
+ else
+ {
+ args_.pop_front ();
+ ++start_position_;
+ }
+ }
+
+ const argv_file_scanner::option_info* argv_file_scanner::
+ find (const char* a) const
+ {
+ for (std::size_t i (0); i < options_count_; ++i)
+ if (std::strcmp (a, options_[i].option) == 0)
+ return &options_[i];
+
+ return 0;
+ }
+
+ std::size_t argv_file_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ void argv_file_scanner::
+ load (const std::string& file)
+ {
+ using namespace std;
+
+ ifstream is (file.c_str ());
+
+ if (!is.is_open ())
+ throw file_io_failure (file);
+
+ files_.push_back (file);
+
+ arg a;
+ a.file = &*files_.rbegin ();
+
+ for (a.line = 1; !is.eof (); ++a.line)
+ {
+ string line;
+ getline (is, line);
+
+ if (is.fail () && !is.eof ())
+ throw file_io_failure (file);
+
+ string::size_type n (line.size ());
+
+ // Trim the line from leading and trailing whitespaces.
+ //
+ if (n != 0)
+ {
+ const char* f (line.c_str ());
+ const char* l (f + n);
+
+ const char* of (f);
+ while (f < l && (*f == ' ' || *f == '\t' || *f == '\r'))
+ ++f;
+
+ --l;
+
+ const char* ol (l);
+ while (l > f && (*l == ' ' || *l == '\t' || *l == '\r'))
+ --l;
+
+ if (f != of || l != ol)
+ line = f <= l ? string (f, l - f + 1) : string ();
+ }
+
+ // Ignore empty lines, those that start with #.
+ //
+ if (line.empty () || line[0] == '#')
+ continue;
+
+ string::size_type p (string::npos);
+ if (line.compare (0, 1, "-") == 0)
+ {
+ p = line.find (' ');
+
+ string::size_type q (line.find ('='));
+ if (q != string::npos && q < p)
+ p = q;
+ }
+
+ string s1;
+ if (p != string::npos)
+ {
+ s1.assign (line, 0, p);
+
+ // Skip leading whitespaces in the argument.
+ //
+ if (line[p] == '=')
+ ++p;
+ else
+ {
+ n = line.size ();
+ for (++p; p < n; ++p)
+ {
+ char c (line[p]);
+ if (c != ' ' && c != '\t' && c != '\r')
+ break;
+ }
+ }
+ }
+ else if (!skip_)
+ skip_ = (line == "--");
+
+ string s2 (line, p != string::npos ? p : 0);
+
+ // If the string (which is an option value or argument) is
+ // wrapped in quotes, remove them.
+ //
+ n = s2.size ();
+ char cf (s2[0]), cl (s2[n - 1]);
+
+ if (cf == '"' || cf == '\'' || cl == '"' || cl == '\'')
+ {
+ if (n == 1 || cf != cl)
+ throw unmatched_quote (s2);
+
+ s2 = string (s2, 1, n - 2);
+ }
+
+ if (!s1.empty ())
+ {
+ // See if this is another file option.
+ //
+ const option_info* oi;
+ if (!skip_ && (oi = find (s1.c_str ())))
+ {
+ if (s2.empty ())
+ throw missing_value (oi->option);
+
+ if (oi->search_func != 0)
+ {
+ string f (oi->search_func (s2.c_str (), oi->arg));
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ {
+ // If the path of the file being parsed is not simple and the
+ // path of the file that needs to be loaded is relative, then
+ // complete the latter using the former as a base.
+ //
+#ifndef _WIN32
+ string::size_type p (file.find_last_of ('/'));
+ bool c (p != string::npos && s2[0] != '/');
+#else
+ string::size_type p (file.find_last_of ("/\\"));
+ bool c (p != string::npos && s2[1] != ':');
+#endif
+ if (c)
+ s2.insert (0, file, 0, p + 1);
+
+ load (s2);
+ }
+
+ continue;
+ }
+
+ a.value = s1;
+ args_.push_back (a);
+ }
+
+ a.value = s2;
+ args_.push_back (a);
+ }
+ }
+
+ template <typename X>
+ struct parser
+ {
+ static void
+ parse (X& x, bool& xs, scanner& s)
+ {
+ using namespace std;
+
+ const char* o (s.next ());
+ if (s.more ())
+ {
+ string v (s.next ());
+ istringstream is (v);
+ if (!(is >> x && is.peek () == istringstream::traits_type::eof ()))
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<bool>
+ {
+ static void
+ parse (bool& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ const char* v (s.next ());
+
+ if (std::strcmp (v, "1") == 0 ||
+ std::strcmp (v, "true") == 0 ||
+ std::strcmp (v, "TRUE") == 0 ||
+ std::strcmp (v, "True") == 0)
+ x = true;
+ else if (std::strcmp (v, "0") == 0 ||
+ std::strcmp (v, "false") == 0 ||
+ std::strcmp (v, "FALSE") == 0 ||
+ std::strcmp (v, "False") == 0)
+ x = false;
+ else
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<std::string>
+ {
+ static void
+ parse (std::string& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ x = s.next ();
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X>
+ struct parser<std::pair<X, std::size_t> >
+ {
+ static void
+ parse (std::pair<X, std::size_t>& x, bool& xs, scanner& s)
+ {
+ x.second = s.position ();
+ parser<X>::parse (x.first, xs, s);
+ }
+ };
+
+ template <typename X>
+ struct parser<std::vector<X> >
+ {
+ static void
+ parse (std::vector<X>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.push_back (x);
+ xs = true;
+ }
+ };
+
+ template <typename X, typename C>
+ struct parser<std::set<X, C> >
+ {
+ static void
+ parse (std::set<X, C>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.insert (x);
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::map<K, V, C> >
+ {
+ static void
+ parse (std::map<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m[k] = v;
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::multimap<K, V, C> >
+ {
+ static void
+ parse (std::multimap<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m.insert (typename std::multimap<K, V, C>::value_type (k, v));
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X, typename T, T X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, s);
+ }
+
+ template <typename X, bool X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ s.next ();
+ x.*M = true;
+ }
+
+ template <typename X, typename T, T X::*M, bool X::*S>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, x.*S, s);
+ }
+ }
+ }
+ }
+}
+
+#include <map>
+
+namespace odb
+{
+ namespace mssql
+ {
+ namespace details
+ {
+ // options
+ //
+
+ options::
+ options ()
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ server_ (),
+ server_specified_ (false),
+ driver_ (),
+ driver_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ }
+
+ options::
+ options (int& argc,
+ char** argv,
+ bool erase,
+ ::odb::mssql::details::cli::unknown_mode opt,
+ ::odb::mssql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ server_ (),
+ server_specified_ (false),
+ driver_ (),
+ driver_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::mssql::details::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ options::
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ ::odb::mssql::details::cli::unknown_mode opt,
+ ::odb::mssql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ server_ (),
+ server_specified_ (false),
+ driver_ (),
+ driver_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::mssql::details::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ options::
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::odb::mssql::details::cli::unknown_mode opt,
+ ::odb::mssql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ server_ (),
+ server_specified_ (false),
+ driver_ (),
+ driver_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::mssql::details::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ options::
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::odb::mssql::details::cli::unknown_mode opt,
+ ::odb::mssql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ server_ (),
+ server_specified_ (false),
+ driver_ (),
+ driver_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::mssql::details::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ options::
+ options (::odb::mssql::details::cli::scanner& s,
+ ::odb::mssql::details::cli::unknown_mode opt,
+ ::odb::mssql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ server_ (),
+ server_specified_ (false),
+ driver_ (),
+ driver_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ _parse (s, opt, arg);
+ }
+
+ ::odb::mssql::details::cli::usage_para options::
+ print_usage (::std::ostream& os, ::odb::mssql::details::cli::usage_para p)
+ {
+ CLI_POTENTIALLY_UNUSED (os);
+
+ if (p != ::odb::mssql::details::cli::usage_para::none)
+ os << ::std::endl;
+
+ os << "--user|-U <name> SQL Server database user. If not specified, then Windows" << ::std::endl
+ << " authentication is used." << ::std::endl;
+
+ os << std::endl
+ << "--password|-P <str> SQL Server database password. Omit this option if the" << ::std::endl
+ << " user password is blank or Windows authentication is used." << ::std::endl;
+
+ os << std::endl
+ << "--database|-d <name> SQL Server database name. If not specified, then the" << ::std::endl
+ << " default database for this user is used." << ::std::endl;
+
+ os << std::endl
+ << "--server|-S <addr> SQL Server instance address in the" << ::std::endl
+ << " [protocol:]host[\\instance][,port] format, where protocol" << ::std::endl
+ << " can be tcp (TCP/IP), lpc (shared memory), or np (named" << ::std::endl
+ << " pipe). If not specifid, then localhost is used." << ::std::endl;
+
+ os << std::endl
+ << "--driver <name> SQL Server Native Client ODBC driver name. If not" << ::std::endl
+ << " specified, then the latest available driver is used." << ::std::endl;
+
+ os << std::endl
+ << "--options-file <file> Read additional options from <file>. Each option should" << ::std::endl
+ << " appear on a separate line optionally followed by space or" << ::std::endl
+ << " equal sign (=) and an option value. Empty lines and lines" << ::std::endl
+ << " starting with # are ignored." << ::std::endl;
+
+ p = ::odb::mssql::details::cli::usage_para::option;
+
+ return p;
+ }
+
+ typedef
+ std::map<std::string, void (*) (options&, ::odb::mssql::details::cli::scanner&)>
+ _cli_options_map;
+
+ static _cli_options_map _cli_options_map_;
+
+ struct _cli_options_map_init
+ {
+ _cli_options_map_init ()
+ {
+ _cli_options_map_["--user"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::user_,
+ &options::user_specified_ >;
+ _cli_options_map_["-U"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::user_,
+ &options::user_specified_ >;
+ _cli_options_map_["--password"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::password_,
+ &options::password_specified_ >;
+ _cli_options_map_["-P"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::password_,
+ &options::password_specified_ >;
+ _cli_options_map_["--database"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::database_,
+ &options::database_specified_ >;
+ _cli_options_map_["-d"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::database_,
+ &options::database_specified_ >;
+ _cli_options_map_["--server"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::server_,
+ &options::server_specified_ >;
+ _cli_options_map_["-S"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::server_,
+ &options::server_specified_ >;
+ _cli_options_map_["--driver"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::driver_,
+ &options::driver_specified_ >;
+ _cli_options_map_["--options-file"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::options_file_,
+ &options::options_file_specified_ >;
+ }
+ };
+
+ static _cli_options_map_init _cli_options_map_init_;
+
+ bool options::
+ _parse (const char* o, ::odb::mssql::details::cli::scanner& s)
+ {
+ _cli_options_map::const_iterator i (_cli_options_map_.find (o));
+
+ if (i != _cli_options_map_.end ())
+ {
+ (*(i->second)) (*this, s);
+ return true;
+ }
+
+ return false;
+ }
+
+ bool options::
+ _parse (::odb::mssql::details::cli::scanner& s,
+ ::odb::mssql::details::cli::unknown_mode opt_mode,
+ ::odb::mssql::details::cli::unknown_mode arg_mode)
+ {
+ bool r = false;
+ bool opt = true;
+
+ while (s.more ())
+ {
+ const char* o = s.peek ();
+
+ if (std::strcmp (o, "--") == 0)
+ {
+ opt = false;
+ s.skip ();
+ r = true;
+ continue;
+ }
+
+ if (opt)
+ {
+ if (_parse (o, s))
+ {
+ r = true;
+ continue;
+ }
+
+ if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0')
+ {
+ // Handle combined option values.
+ //
+ std::string co;
+ if (const char* v = std::strchr (o, '='))
+ {
+ co.assign (o, 0, v - o);
+ ++v;
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (co.c_str ()),
+ const_cast<char*> (v)
+ };
+
+ ::odb::mssql::details::cli::argv_scanner ns (0, ac, av);
+
+ if (_parse (co.c_str (), ns))
+ {
+ // Parsed the option but not its value?
+ //
+ if (ns.end () != 2)
+ throw ::odb::mssql::details::cli::invalid_value (co, v);
+
+ s.next ();
+ r = true;
+ continue;
+ }
+ else
+ {
+ // Set the unknown option and fall through.
+ //
+ o = co.c_str ();
+ }
+ }
+
+ switch (opt_mode)
+ {
+ case ::odb::mssql::details::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::odb::mssql::details::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::odb::mssql::details::cli::unknown_mode::fail:
+ {
+ throw ::odb::mssql::details::cli::unknown_option (o);
+ }
+ }
+
+ break;
+ }
+ }
+
+ switch (arg_mode)
+ {
+ case ::odb::mssql::details::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::odb::mssql::details::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::odb::mssql::details::cli::unknown_mode::fail:
+ {
+ throw ::odb::mssql::details::cli::unknown_argument (o);
+ }
+ }
+
+ break;
+ }
+
+ return r;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
diff --git a/libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.hxx b/libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.hxx
new file mode 100644
index 0000000..104395b
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.hxx
@@ -0,0 +1,562 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+#ifndef LIBODB_MSSQL_DETAILS_OPTIONS_HXX
+#define LIBODB_MSSQL_DETAILS_OPTIONS_HXX
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <list>
+#include <deque>
+#include <iosfwd>
+#include <string>
+#include <cstddef>
+#include <exception>
+
+#ifndef CLI_POTENTIALLY_UNUSED
+# if defined(_MSC_VER) || defined(__xlC__)
+# define CLI_POTENTIALLY_UNUSED(x) (void*)&x
+# else
+# define CLI_POTENTIALLY_UNUSED(x) (void)x
+# endif
+#endif
+
+namespace odb
+{
+ namespace mssql
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ class usage_para
+ {
+ public:
+ enum value
+ {
+ none,
+ text,
+ option
+ };
+
+ usage_para (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ class unknown_mode
+ {
+ public:
+ enum value
+ {
+ skip,
+ stop,
+ fail
+ };
+
+ unknown_mode (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ // Exceptions.
+ //
+
+ class exception: public std::exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const = 0;
+ };
+
+ ::std::ostream&
+ operator<< (::std::ostream&, const exception&);
+
+ class unknown_option: public exception
+ {
+ public:
+ virtual
+ ~unknown_option () throw ();
+
+ unknown_option (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class unknown_argument: public exception
+ {
+ public:
+ virtual
+ ~unknown_argument () throw ();
+
+ unknown_argument (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ class missing_value: public exception
+ {
+ public:
+ virtual
+ ~missing_value () throw ();
+
+ missing_value (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class invalid_value: public exception
+ {
+ public:
+ virtual
+ ~invalid_value () throw ();
+
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message = std::string ());
+
+ const std::string&
+ option () const;
+
+ const std::string&
+ value () const;
+
+ const std::string&
+ message () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ std::string value_;
+ std::string message_;
+ };
+
+ class eos_reached: public exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class file_io_failure: public exception
+ {
+ public:
+ virtual
+ ~file_io_failure () throw ();
+
+ file_io_failure (const std::string& file);
+
+ const std::string&
+ file () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string file_;
+ };
+
+ class unmatched_quote: public exception
+ {
+ public:
+ virtual
+ ~unmatched_quote () throw ();
+
+ unmatched_quote (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ // Command line argument scanner interface.
+ //
+ // The values returned by next() are guaranteed to be valid
+ // for the two previous arguments up until a call to a third
+ // peek() or next().
+ //
+ // The position() function returns a monotonically-increasing
+ // number which, if stored, can later be used to determine the
+ // relative position of the argument returned by the following
+ // call to next(). Note that if multiple scanners are used to
+ // extract arguments from multiple sources, then the end
+ // position of the previous scanner should be used as the
+ // start position of the next.
+ //
+ class scanner
+ {
+ public:
+ virtual
+ ~scanner ();
+
+ virtual bool
+ more () = 0;
+
+ virtual const char*
+ peek () = 0;
+
+ virtual const char*
+ next () = 0;
+
+ virtual void
+ skip () = 0;
+
+ virtual std::size_t
+ position () = 0;
+ };
+
+ class argv_scanner: public scanner
+ {
+ public:
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ int
+ end () const;
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ protected:
+ std::size_t start_position_;
+ int i_;
+ int& argc_;
+ char** argv_;
+ bool erase_;
+ };
+
+ class argv_file_scanner: public argv_scanner
+ {
+ public:
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t start_position = 0);
+
+ struct option_info
+ {
+ // If search_func is not NULL, it is called, with the arg
+ // value as the second argument, to locate the options file.
+ // If it returns an empty string, then the file is ignored.
+ //
+ const char* option;
+ std::string (*search_func) (const char*, void* arg);
+ void* arg;
+ };
+
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const option_info* options = 0,
+ std::size_t options_count = 0,
+ std::size_t start_position = 0);
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ // Return the file path if the peeked at argument came from a file and
+ // the empty string otherwise. The reference is guaranteed to be valid
+ // till the end of the scanner lifetime.
+ //
+ const std::string&
+ peek_file ();
+
+ // Return the 1-based line number if the peeked at argument came from
+ // a file and zero otherwise.
+ //
+ std::size_t
+ peek_line ();
+
+ private:
+ const option_info*
+ find (const char*) const;
+
+ void
+ load (const std::string& file);
+
+ typedef argv_scanner base;
+
+ const std::string option_;
+ option_info option_info_;
+ const option_info* options_;
+ std::size_t options_count_;
+
+ struct arg
+ {
+ std::string value;
+ const std::string* file;
+ std::size_t line;
+ };
+
+ std::deque<arg> args_;
+ std::list<std::string> files_;
+
+ // Circular buffer of two arguments.
+ //
+ std::string hold_[2];
+ std::size_t i_;
+
+ bool skip_;
+
+ static int zero_argc_;
+ static std::string empty_string_;
+ };
+
+ template <typename X>
+ struct parser;
+ }
+ }
+ }
+}
+
+#include <string>
+
+namespace odb
+{
+ namespace mssql
+ {
+ namespace details
+ {
+ class options
+ {
+ public:
+ options ();
+
+ options (int& argc,
+ char** argv,
+ bool erase = false,
+ ::odb::mssql::details::cli::unknown_mode option = ::odb::mssql::details::cli::unknown_mode::fail,
+ ::odb::mssql::details::cli::unknown_mode argument = ::odb::mssql::details::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ ::odb::mssql::details::cli::unknown_mode option = ::odb::mssql::details::cli::unknown_mode::fail,
+ ::odb::mssql::details::cli::unknown_mode argument = ::odb::mssql::details::cli::unknown_mode::stop);
+
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::odb::mssql::details::cli::unknown_mode option = ::odb::mssql::details::cli::unknown_mode::fail,
+ ::odb::mssql::details::cli::unknown_mode argument = ::odb::mssql::details::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::odb::mssql::details::cli::unknown_mode option = ::odb::mssql::details::cli::unknown_mode::fail,
+ ::odb::mssql::details::cli::unknown_mode argument = ::odb::mssql::details::cli::unknown_mode::stop);
+
+ options (::odb::mssql::details::cli::scanner&,
+ ::odb::mssql::details::cli::unknown_mode option = ::odb::mssql::details::cli::unknown_mode::fail,
+ ::odb::mssql::details::cli::unknown_mode argument = ::odb::mssql::details::cli::unknown_mode::stop);
+
+ // Option accessors.
+ //
+ const std::string&
+ user () const;
+
+ bool
+ user_specified () const;
+
+ const std::string&
+ password () const;
+
+ bool
+ password_specified () const;
+
+ const std::string&
+ database () const;
+
+ bool
+ database_specified () const;
+
+ const std::string&
+ server () const;
+
+ bool
+ server_specified () const;
+
+ const std::string&
+ driver () const;
+
+ bool
+ driver_specified () const;
+
+ const std::string&
+ options_file () const;
+
+ bool
+ options_file_specified () const;
+
+ // Print usage information.
+ //
+ static ::odb::mssql::details::cli::usage_para
+ print_usage (::std::ostream&,
+ ::odb::mssql::details::cli::usage_para = ::odb::mssql::details::cli::usage_para::none);
+
+ // Implementation details.
+ //
+ protected:
+ bool
+ _parse (const char*, ::odb::mssql::details::cli::scanner&);
+
+ private:
+ bool
+ _parse (::odb::mssql::details::cli::scanner&,
+ ::odb::mssql::details::cli::unknown_mode option,
+ ::odb::mssql::details::cli::unknown_mode argument);
+
+ public:
+ std::string user_;
+ bool user_specified_;
+ std::string password_;
+ bool password_specified_;
+ std::string database_;
+ bool database_specified_;
+ std::string server_;
+ bool server_specified_;
+ std::string driver_;
+ bool driver_specified_;
+ std::string options_file_;
+ bool options_file_specified_;
+ };
+ }
+ }
+}
+
+#include <odb/mssql/details/options.ixx>
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
+#endif // LIBODB_MSSQL_DETAILS_OPTIONS_HXX
diff --git a/libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.ixx b/libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.ixx
new file mode 100644
index 0000000..a406dc4
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.ixx
@@ -0,0 +1,372 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <cassert>
+
+namespace odb
+{
+ namespace mssql
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ // usage_para
+ //
+ inline usage_para::
+ usage_para (value v)
+ : v_ (v)
+ {
+ }
+
+ // unknown_mode
+ //
+ inline unknown_mode::
+ unknown_mode (value v)
+ : v_ (v)
+ {
+ }
+
+ // exception
+ //
+ inline ::std::ostream&
+ operator<< (::std::ostream& os, const exception& e)
+ {
+ e.print (os);
+ return os;
+ }
+
+ // unknown_option
+ //
+ inline unknown_option::
+ unknown_option (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& unknown_option::
+ option () const
+ {
+ return option_;
+ }
+
+ // unknown_argument
+ //
+ inline unknown_argument::
+ unknown_argument (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unknown_argument::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // missing_value
+ //
+ inline missing_value::
+ missing_value (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& missing_value::
+ option () const
+ {
+ return option_;
+ }
+
+ // invalid_value
+ //
+ inline invalid_value::
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message)
+ : option_ (option),
+ value_ (value),
+ message_ (message)
+ {
+ }
+
+ inline const std::string& invalid_value::
+ option () const
+ {
+ return option_;
+ }
+
+ inline const std::string& invalid_value::
+ value () const
+ {
+ return value_;
+ }
+
+ inline const std::string& invalid_value::
+ message () const
+ {
+ return message_;
+ }
+
+ // file_io_failure
+ //
+ inline file_io_failure::
+ file_io_failure (const std::string& file)
+ : file_ (file)
+ {
+ }
+
+ inline const std::string& file_io_failure::
+ file () const
+ {
+ return file_;
+ }
+
+ // unmatched_quote
+ //
+ inline unmatched_quote::
+ unmatched_quote (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unmatched_quote::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // argv_scanner
+ //
+ inline argv_scanner::
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + 1),
+ i_ (1),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline argv_scanner::
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + static_cast<std::size_t> (start)),
+ i_ (start),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline int argv_scanner::
+ end () const
+ {
+ return i_;
+ }
+
+ // argv_file_scanner
+ //
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+
+ load (file);
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const option_info* options,
+ std::size_t options_count,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ load (file);
+ }
+ }
+ }
+ }
+}
+
+namespace odb
+{
+ namespace mssql
+ {
+ namespace details
+ {
+ // options
+ //
+
+ inline const std::string& options::
+ user () const
+ {
+ return this->user_;
+ }
+
+ inline bool options::
+ user_specified () const
+ {
+ return this->user_specified_;
+ }
+
+ inline const std::string& options::
+ password () const
+ {
+ return this->password_;
+ }
+
+ inline bool options::
+ password_specified () const
+ {
+ return this->password_specified_;
+ }
+
+ inline const std::string& options::
+ database () const
+ {
+ return this->database_;
+ }
+
+ inline bool options::
+ database_specified () const
+ {
+ return this->database_specified_;
+ }
+
+ inline const std::string& options::
+ server () const
+ {
+ return this->server_;
+ }
+
+ inline bool options::
+ server_specified () const
+ {
+ return this->server_specified_;
+ }
+
+ inline const std::string& options::
+ driver () const
+ {
+ return this->driver_;
+ }
+
+ inline bool options::
+ driver_specified () const
+ {
+ return this->driver_specified_;
+ }
+
+ inline const std::string& options::
+ options_file () const
+ {
+ return this->options_file_;
+ }
+
+ inline bool options::
+ options_file_specified () const
+ {
+ return this->options_file_specified_;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
diff --git a/libodb-mssql/odb/mssql/error.cxx b/libodb-mssql/odb/mssql/error.cxx
new file mode 100644
index 0000000..897d415
--- /dev/null
+++ b/libodb-mssql/odb/mssql/error.cxx
@@ -0,0 +1,273 @@
+// file : odb/mssql/error.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <string>
+#include <cstring> // std::strlen
+
+#include <odb/mssql/mssql.hxx>
+#include <odb/mssql/error.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/exceptions.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ static void
+ translate_error (SQLRETURN r,
+ SQLHANDLE h,
+ SQLSMALLINT htype,
+ connection* conn,
+ bool end_tran,
+ size_t pos,
+ multiple_exceptions* mex)
+ {
+ // First see if we have one of the errors indicated via the
+ // return error code.
+ //
+ switch (r)
+ {
+ case SQL_STILL_EXECUTING:
+ {
+ throw database_exception (0, "?????", "statement still executing");
+ break;
+ }
+ case SQL_NEED_DATA:
+ case SQL_NO_DATA:
+#if ODBCVER >= 0x0380
+ case SQL_PARAM_DATA_AVAILABLE:
+#endif
+ {
+ throw database_exception (
+ 0, "?????", "unhandled SQL_*_DATA condition");
+ break;
+ }
+ case SQL_INVALID_HANDLE:
+ {
+ throw database_exception (0, "?????", "invalid handle");
+ break;
+ }
+ }
+
+ // Otherwise the diagnostics is stored in the handle.
+ //
+ char sqlstate[SQL_SQLSTATE_SIZE + 1];
+ SQLINTEGER native_code; // Will be 0 if no natve code.
+ char msg[512]; // Will be truncated if doesn't fit.
+ SQLSMALLINT msg_size;
+
+ // We need to translate certain sqlstate codes to special exceptions,
+ // such as deadlock, timeout, etc. The problem is we can have multiple
+ // records potentially with different sqlstate codes. If we have both,
+ // say, a deadlock code and some other code, then we should probably
+ // throw database_exception, which is more severe. To implement this
+ // we are going to pre-scan the records looking for the codes we are
+ // interested in. If in the process we see any other code, then we
+ // stop and go ahead to prepare and throw database_exception.
+ //
+ enum code
+ {
+ code_none,
+ code_deadlock,
+ code_timeout,
+ code_connection_lost
+ };
+
+ code c (code_none);
+
+ for (SQLSMALLINT i (1);; ++i)
+ {
+ r = SQLGetDiagRecA (htype,
+ h,
+ i,
+ (SQLCHAR*) sqlstate,
+ &native_code,
+ 0,
+ 0,
+ &msg_size);
+
+ if (r == SQL_NO_DATA)
+ break;
+ else if (SQL_SUCCEEDED (r))
+ {
+ code nc;
+ string s (sqlstate);
+
+ if (s == "40001") // Serialization failure (native code 1205).
+ nc = code_deadlock;
+ else if (s == "HYT00") // Timeout expired.
+ nc = code_timeout;
+ else if (s == "HYT01") // Connection timeout expired.
+ {
+ nc = code_timeout;
+
+ if (conn != 0)
+ conn->mark_failed ();
+ }
+ else if (s == "08S01") // Link failure.
+ {
+ nc = code_connection_lost;
+
+ if (conn != 0)
+ conn->mark_failed ();
+ }
+ else if (s == "01000") // General warning.
+ continue;
+ else
+ {
+ c = code_none;
+ break;
+ }
+
+ // If a call to SQLEndTran() fails, then the connection is
+ // put into the so called "suspended state" and should be
+ // disconnected unless we know the transaction was rolled
+ // back. See SQLEndTran() documentation for details.
+ //
+ if (end_tran &&
+ s != "25S03" && // Transaction is rolled back.
+ s != "40001" && // Serialization failure.
+ s != "40002" && // Integrity constraint.
+ s != "HYC00") // Optional feature not implemented.
+ conn->mark_failed ();
+
+ if (c != code_none && c != nc)
+ {
+ // Several different codes.
+ //
+ c = code_none;
+ break;
+ }
+
+ c = nc;
+ }
+ else
+ {
+ c = code_none;
+ break;
+ }
+ }
+
+ switch (c)
+ {
+ case code_deadlock:
+ throw deadlock ();
+ case code_timeout:
+ throw timeout ();
+ case code_connection_lost:
+ throw connection_lost ();
+ case code_none:
+ break;
+ }
+
+ // Some other error code. Prepare database_exception.
+ //
+ database_exception e;
+
+ for (SQLSMALLINT i (1);; ++i)
+ {
+ // If this is for a batch, filter out based on row association.
+ // Here we only ignore records that have the associated row
+ // number and this number doesn't match ours. In particular,
+ // this means that all the un-associated records which will be
+ // duplicated for all the failed rows, which seems like the
+ // correct thing to do.
+ //
+ if (mex != 0)
+ {
+ SQLLEN n;
+ r = SQLGetDiagField (htype,
+ h,
+ i,
+ SQL_DIAG_ROW_NUMBER,
+ &n,
+ 0,
+ 0);
+
+ if (r == SQL_NO_DATA)
+ break;
+ else if (SQL_SUCCEEDED (r) &&
+ n != SQL_NO_ROW_NUMBER &&
+ n != SQL_ROW_NUMBER_UNKNOWN &&
+ n != static_cast<SQLLEN> (pos + 1)) // 1-based
+ continue;
+ }
+
+ r = SQLGetDiagRecA (htype,
+ h,
+ i,
+ (SQLCHAR*) sqlstate,
+ &native_code,
+ (SQLCHAR*) msg,
+ sizeof (msg),
+ &msg_size);
+
+ if (r == SQL_NO_DATA)
+ break;
+ else if (SQL_SUCCEEDED (r))
+ {
+ if (conn != 0)
+ {
+ string s (sqlstate);
+
+ if (s == "08S01" || // Link failure.
+ s == "HYT01" || // Connection timeout.
+ (end_tran &&
+ s != "25S03" &&
+ s != "40001" &&
+ s != "40002" &&
+ s != "HYC00"))
+ conn->mark_failed ();
+ }
+
+ // Get rid of a trailing newline if there is one.
+ //
+ size_t n (strlen (msg));
+ if (n != 0 && msg[n - 1] == '\n')
+ msg[n - 1] = '\0';
+
+ e.append (native_code, sqlstate, msg);
+ }
+ else
+ e.append (0, "?????", "unable to extract information for this "
+ "diagnostic record");
+ }
+
+ if (e.size () == 0)
+ e.append (0, "?????", "no diagnostic record (using wrong handle?)");
+
+ if (mex == 0)
+ throw e;
+ else
+ // It could be that some of these errors are fatal. I guess we
+ // will just have to learn from experience which ones are. The
+ // client code can always treat specific error codes as fatal.
+ //
+ mex->insert (pos, e);
+ }
+
+ void
+ translate_error (SQLRETURN r, connection& c, bool end_tran)
+ {
+ translate_error (r, c.handle (), SQL_HANDLE_DBC, &c, end_tran, 0, 0);
+ }
+
+ void
+ translate_error (SQLRETURN r,
+ connection& c,
+ const auto_handle<SQL_HANDLE_STMT>& h,
+ size_t pos,
+ multiple_exceptions* mex)
+ {
+ translate_error (r, h, SQL_HANDLE_STMT, &c, false, pos, mex);
+ }
+
+ void
+ translate_error (SQLRETURN r, SQLHANDLE h, SQLSMALLINT htype)
+ {
+ translate_error (r, h, htype, 0, false, 0, 0);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/error.hxx b/libodb-mssql/odb/mssql/error.hxx
new file mode 100644
index 0000000..cb8cc7c
--- /dev/null
+++ b/libodb-mssql/odb/mssql/error.hxx
@@ -0,0 +1,40 @@
+// file : odb/mssql/error.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_ERROR_HXX
+#define ODB_MSSQL_ERROR_HXX
+
+#include <odb/pre.hxx>
+#include <cstddef> // std::size_t
+
+#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx> // connection, multiple_exceptions
+#include <odb/mssql/auto-handle.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ // Translate ODBC error given a handle and throw (or return, in case
+ // multiple_exceptions is not NULL) an appropriate exception.
+ //
+ LIBODB_MSSQL_EXPORT void
+ translate_error (SQLRETURN, connection&, bool end_tran = false);
+
+ LIBODB_MSSQL_EXPORT void
+ translate_error (SQLRETURN,
+ connection&,
+ const auto_handle<SQL_HANDLE_STMT>&,
+ std::size_t pos = 0, multiple_exceptions* = 0);
+
+ LIBODB_MSSQL_EXPORT void
+ translate_error (SQLRETURN, SQLHANDLE, SQLSMALLINT htype);
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_ERROR_HXX
diff --git a/libodb-mssql/odb/mssql/exceptions.cxx b/libodb-mssql/odb/mssql/exceptions.cxx
new file mode 100644
index 0000000..606678d
--- /dev/null
+++ b/libodb-mssql/odb/mssql/exceptions.cxx
@@ -0,0 +1,109 @@
+// file : odb/mssql/exceptions.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/mssql/exceptions.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // database_exception
+ //
+
+ database_exception::record::
+ record (SQLINTEGER e, const string& s, const string& m)
+ : error_ (e), sqlstate_ (s), message_ (m)
+ {
+ }
+
+ database_exception::
+ ~database_exception () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ database_exception::
+ database_exception ()
+ {
+ }
+
+ database_exception::
+ database_exception (SQLINTEGER e, const string& s, const string& m)
+ {
+ append (e, s, m);
+ }
+
+ void database_exception::
+ append (SQLINTEGER e, const string& s, const string& m)
+ {
+ records_.push_back (record (e, s, m));
+
+ if (!what_.empty ())
+ what_ += '\n';
+
+ ostringstream ostr;
+ ostr << e << " (" << s << "): " << m;
+ what_ += ostr.str ();
+ }
+
+ const char* database_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ database_exception* database_exception::
+ clone () const
+ {
+ return new database_exception (*this);
+ }
+
+ //
+ // cli_exception
+ //
+
+ cli_exception::
+ cli_exception (const string& what)
+ : what_ (what)
+ {
+ }
+
+ cli_exception::
+ ~cli_exception () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ const char* cli_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ cli_exception* cli_exception::
+ clone () const
+ {
+ return new cli_exception (*this);
+ }
+
+ //
+ // long_data_reload
+ //
+
+ const char* long_data_reload::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "attempt to re-load object or view with long data "
+ "from query result";
+ }
+
+ long_data_reload* long_data_reload::
+ clone () const
+ {
+ return new long_data_reload (*this);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/exceptions.hxx b/libodb-mssql/odb/mssql/exceptions.hxx
new file mode 100644
index 0000000..5240d8d
--- /dev/null
+++ b/libodb-mssql/odb/mssql/exceptions.hxx
@@ -0,0 +1,138 @@
+// file : odb/mssql/exceptions.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_EXCEPTIONS_HXX
+#define ODB_MSSQL_EXCEPTIONS_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <vector>
+
+#include <odb/exceptions.hxx>
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ struct LIBODB_MSSQL_EXPORT database_exception: odb::database_exception
+ {
+ struct record
+ {
+ record (SQLINTEGER error,
+ const std::string& sqlstate,
+ const std::string& message);
+
+ SQLINTEGER
+ error () const
+ {
+ return error_;
+ }
+
+ const std::string&
+ sqlstate () const
+ {
+ return sqlstate_;
+ }
+
+ const std::string&
+ message () const
+ {
+ return message_;
+ }
+
+ private:
+ SQLINTEGER error_;
+ std::string sqlstate_;
+ std::string message_;
+ };
+
+ typedef std::vector<record> records;
+
+ typedef records::size_type size_type;
+ typedef records::const_iterator iterator;
+
+ iterator
+ begin () const
+ {
+ return records_.begin ();
+ }
+
+ iterator
+ end () const
+ {
+ return records_.end ();
+ }
+
+ size_type
+ size () const
+ {
+ return records_.size ();
+ }
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual database_exception*
+ clone () const;
+
+ public:
+ ~database_exception () ODB_NOTHROW_NOEXCEPT;
+
+ database_exception ();
+ database_exception (SQLINTEGER error,
+ const std::string& sqlstate,
+ const std::string& message);
+
+ void
+ append (SQLINTEGER error,
+ const std::string& sqlstate,
+ const std::string& message);
+
+ private:
+ records records_;
+ std::string what_;
+ };
+
+ struct LIBODB_MSSQL_EXPORT cli_exception: odb::exception
+ {
+ cli_exception (const std::string& what);
+ ~cli_exception () ODB_NOTHROW_NOEXCEPT;
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual cli_exception*
+ clone () const;
+
+ private:
+ std::string what_;
+ };
+
+ struct LIBODB_MSSQL_EXPORT long_data_reload: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual long_data_reload*
+ clone () const;
+ };
+
+ namespace core
+ {
+ using mssql::database_exception;
+ using mssql::cli_exception;
+ using mssql::long_data_reload;
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_EXCEPTIONS_HXX
diff --git a/libodb-mssql/odb/mssql/forward.hxx b/libodb-mssql/odb/mssql/forward.hxx
new file mode 100644
index 0000000..4f32b22
--- /dev/null
+++ b/libodb-mssql/odb/mssql/forward.hxx
@@ -0,0 +1,91 @@
+// file : odb/mssql/forward.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_FORWARD_HXX
+#define ODB_MSSQL_FORWARD_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ namespace core
+ {
+ using namespace odb::common;
+ }
+
+ //
+ //
+ class database;
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+ class connection_factory;
+ class statement;
+ class transaction;
+ class tracer;
+
+ namespace core
+ {
+ using mssql::database;
+ using mssql::connection;
+ using mssql::connection_ptr;
+ using mssql::transaction;
+ using mssql::statement;
+ }
+
+ // Implementation details.
+ //
+ enum statement_kind
+ {
+ statement_select,
+ statement_insert,
+ statement_update,
+ statement_delete
+ };
+
+ class binding;
+ class select_statement;
+
+ template <typename T>
+ class object_statements;
+
+ template <typename T>
+ class polymorphic_root_object_statements;
+
+ template <typename T>
+ class polymorphic_derived_object_statements;
+
+ template <typename T>
+ class no_id_object_statements;
+
+ template <typename T>
+ class view_statements;
+
+ template <typename T>
+ class container_statements;
+
+ template <typename T>
+ class smart_container_statements;
+
+ template <typename T, typename ST>
+ class section_statements;
+
+ class query_base;
+ }
+
+ namespace details
+ {
+ template <>
+ struct counter_type<mssql::connection>
+ {
+ typedef shared_base counter;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_FORWARD_HXX
diff --git a/libodb-mssql/odb/mssql/mssql-fwd.hxx b/libodb-mssql/odb/mssql/mssql-fwd.hxx
new file mode 100644
index 0000000..44ac428
--- /dev/null
+++ b/libodb-mssql/odb/mssql/mssql-fwd.hxx
@@ -0,0 +1,216 @@
+// file : odb/mssql/mssql-fwd.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_MSSQL_FWD_HXX
+#define ODB_MSSQL_MSSQL_FWD_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+// Forward declaration for some of the types defined in sqltypes.h or
+// sqlncli.h. This allows us to avoid having to include these files
+// in public headers.
+//
+#ifdef _WIN32
+
+// Keep consistent with Windows ODBC headers.
+//
+
+typedef long SQLINTEGER;
+typedef unsigned long SQLUINTEGER;
+
+#ifdef _WIN64
+typedef __int64 SQLLEN;
+typedef unsigned __int64 SQLULEN;
+#else
+#ifndef SQLLEN
+typedef SQLINTEGER SQLLEN;
+typedef SQLUINTEGER SQLULEN;
+#endif
+#endif
+
+#else // _WIN32
+
+// Keep consistent with unixODBC headers.
+//
+
+template <std::size_t sizeof_long>
+struct odbc_types;
+
+template <>
+struct odbc_types<4>
+{
+ typedef long integer;
+ typedef unsigned long uinteger;
+
+ typedef integer len;
+ typedef uinteger ulen;
+};
+
+template <>
+struct odbc_types<8>
+{
+ typedef int integer;
+ typedef unsigned int uinteger;
+
+ typedef long len;
+ typedef unsigned long ulen;
+};
+
+typedef odbc_types<sizeof (long)>::integer SQLINTEGER;
+typedef odbc_types<sizeof (long)>::uinteger SQLUINTEGER;
+
+#ifndef SQLLEN
+typedef odbc_types<sizeof (long)>::len SQLLEN;
+typedef odbc_types<sizeof (long)>::ulen SQLULEN;
+#endif
+
+#endif // _WIN32
+
+typedef short SQLSMALLINT;
+typedef unsigned short SQLUSMALLINT;
+
+typedef SQLSMALLINT SQLRETURN;
+
+typedef void* SQLHANDLE;
+typedef SQLHANDLE SQLHENV;
+typedef SQLHANDLE SQLHDBC;
+typedef SQLHANDLE SQLHSTMT;
+typedef SQLHANDLE SQLHDESC;
+
+// If you get a redefinition error or warning for one of these macros,
+// then that means you included this header (or one that includes it),
+// before <sql.h> or <sqlext.h>. As a general rule, include <sql.h> or
+// <sqlext.h> before any of the ODB headers.
+//
+#ifndef SQL_HANDLE_ENV
+# define SQL_HANDLE_ENV 1
+# define SQL_HANDLE_DBC 2
+# define SQL_HANDLE_STMT 3
+# define SQL_HANDLE_DESC 4
+#endif
+
+#ifndef SQL_NULL_DATA
+# define SQL_NULL_DATA (-1)
+# define SQL_DATA_AT_EXEC (-2)
+# define SQL_NO_TOTAL (-4)
+#endif
+
+// The following types are our own equivalents of ODBC and Native Client
+// ODBC driver types. They are all PODs and should be layout-compatible
+// with the original types, which means they can be used interchangeably.
+//
+namespace odb
+{
+ namespace mssql
+ {
+ // UCS-2 character type (SQLWCHAR).
+ //
+#ifdef _WIN32
+ typedef wchar_t ucs2_char;
+#else
+ typedef unsigned short ucs2_char;
+#endif
+
+ // SQL_NUMERIC_STRUCT
+ //
+#ifndef SQL_MAX_NUMERIC_LEN
+#define SQL_MAX_NUMERIC_LEN 16
+#else
+# if SQL_MAX_NUMERIC_LEN != 16
+# error unexpected SQL_NUMERIC_STRUCT value
+# endif
+#endif
+
+ struct decimal
+ {
+ unsigned char precision;
+ signed char scale;
+ unsigned char sign; // 1 - positive, 0 - negative
+ unsigned char val[SQL_MAX_NUMERIC_LEN];
+ };
+
+ // DBMONEY
+ //
+ struct money
+ {
+ // 8-byte signed integer containing value * 10,000.
+ //
+ int high;
+ unsigned int low;
+ };
+
+ // DBMONEY4
+ //
+ struct smallmoney
+ {
+ int value; // 4-byte signed integer containing value * 10,000.
+ };
+
+ // SQL_DATE_STRUCT
+ //
+ struct date
+ {
+ SQLSMALLINT year;
+ SQLUSMALLINT month;
+ SQLUSMALLINT day;
+ };
+
+ // SQL_SS_TIME2_STRUCT
+ //
+#pragma pack(push,8)
+ struct time
+ {
+ SQLUSMALLINT hour;
+ SQLUSMALLINT minute;
+ SQLUSMALLINT second;
+ SQLUINTEGER fraction;
+ };
+#pragma pack(pop)
+
+ // SQL_TIMESTAMP_STRUCT
+ //
+ struct datetime
+ {
+ SQLSMALLINT year;
+ SQLUSMALLINT month;
+ SQLUSMALLINT day;
+ SQLUSMALLINT hour;
+ SQLUSMALLINT minute;
+ SQLUSMALLINT second;
+ SQLUINTEGER fraction;
+ };
+
+ // SQL_SS_TIMESTAMPOFFSET_STRUCT
+ //
+#pragma pack(push,8)
+ struct datetimeoffset
+ {
+ SQLSMALLINT year;
+ SQLUSMALLINT month;
+ SQLUSMALLINT day;
+ SQLUSMALLINT hour;
+ SQLUSMALLINT minute;
+ SQLUSMALLINT second;
+ SQLUINTEGER fraction;
+ SQLSMALLINT timezone_hour;
+ SQLSMALLINT timezone_minute;
+ };
+#pragma pack(pop)
+
+ // SQLGUID
+ //
+ struct uniqueidentifier
+ {
+ unsigned int data1;
+ unsigned short data2;
+ unsigned short data3;
+ unsigned char data4[8];
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_MSSQL_FWD_HXX
diff --git a/libodb-mssql/odb/mssql/mssql-types.hxx b/libodb-mssql/odb/mssql/mssql-types.hxx
new file mode 100644
index 0000000..b07aeb6
--- /dev/null
+++ b/libodb-mssql/odb/mssql/mssql-types.hxx
@@ -0,0 +1,161 @@
+// file : odb/mssql/mssql-types.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_MSSQL_TYPES_HXX
+#define ODB_MSSQL_MSSQL_TYPES_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/version.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ enum chunk_type
+ {
+ chunk_null,
+ chunk_one,
+ chunk_first,
+ chunk_next,
+ chunk_last,
+ };
+
+ typedef void (*param_callback_type) (
+ const void* context, // User context.
+ std::size_t* position, // Position context. An implementation is free
+ // to use this to track position information. It
+ // is initialized to zero before the first call.
+ const void** buffer, // [in/out] Buffer contaning the data. On the
+ // the first call it contains a pointer to the
+ // long_callback struct (used for redirections).
+ std::size_t* size, // [out] Data size.
+ chunk_type*, // [out] The position of this chunk of data.
+ void* temp_buffer, // A temporary buffer that may be used by the
+ // implementation.
+ std::size_t capacity); // Capacity of the temporary buffer.
+
+ typedef void (*result_callback_type) (
+ void* context, // User context.
+ std::size_t* position, // Position context. An implementation is free
+ // to use this to track position information. It
+ // is initialized to zero before the first call.
+ void** buffer, // [in/out] Buffer to copy the data to. On the
+ // the first call it contains a pointer to the
+ // long_callback struct (used for redirections).
+ std::size_t* size, // [in/out] In: amount of data copied into the
+ // buffer after the previous call. Out: capacity
+ // of the buffer.
+ chunk_type, // The position of this chunk; chunk_first means
+ // this is the first call, chunk_last means there
+ // is no more data, chunk_null means this value is
+ // NULL, and chunk_one means the value is empty.
+ std::size_t size_left, // Contains the amount of data left or 0 if this
+ // information is not available.
+ void* temp_buffer, // A temporary buffer that may be used by the
+ // implementation.
+ std::size_t capacity); // Capacity of the temporary buffer.
+
+ struct long_callback
+ {
+ union
+ {
+ param_callback_type param;
+ result_callback_type result;
+ } callback;
+
+ union
+ {
+ const void* param;
+ void* result;
+ } context;
+ };
+
+ struct bind
+ {
+ // This enumeration identifies the possible buffer types that can be
+ // bound to a parameter or result. In most cases, these map directly
+ // to SQL_XXX/SQL_C_XXX codes.
+ //
+ enum buffer_type
+ {
+ bit, // Buffer is a 1-byte integer.
+ tinyint, // Buffer is a 1-byte integer.
+ smallint, // Buffer is a 2-byte integer.
+ int_, // Buffer is a 4-byte integer.
+ bigint, // Buffer is an 8-byte integer.
+
+ decimal, // Buffer is a decimal struct (SQL_NUMERIC_STRUCT).
+
+ smallmoney, // Buffer is a smallmoney struct (DBMONEY4).
+ money, // Buffer is a money struct (DBMONEY).
+
+ float4, // Buffer is a float.
+ float8, // Buffer is a double.
+
+ string, // Buffer is a char array.
+ long_string, // Buffer is a long_callback.
+
+ nstring, // Buffer is a ucs2_char array.
+ long_nstring, // Buffer is a long_callback.
+
+ binary, // Buffer is a byte array.
+ long_binary, // Buffer is a long_callback.
+
+ date, // Buffer is a date struct (SQL_DATE_STRUCT).
+ time, // Buffer is a time struct (SQL_SS_TIME2_STRUCT).
+ datetime, // Buffer is a datetime struct
+ // (SQL_TIMESTAMP_STRUCT).
+ datetimeoffset, // Buffer is a datetimeoffset
+ // (SQL_SS_TIMESTAMPOFFSET_STRUCT).
+
+ uniqueidentifier, // Buffer is a uniqueidentifier struct (SQLGUID).
+ rowversion, // Buffer is an 8-byte array.
+
+ last // Used as an end of list marker.
+ };
+
+ buffer_type type; // The buffer type.
+ void* buffer; // The buffer. For long data this is a long_callback.
+ SQLLEN* size_ind; // Pointer to the size/inidicator variable. Size is
+ // ignored except for variable-size, [small]money, and
+ // rowversion types. Sepcial indicator values are
+ // SQL_NULL_DATA (value is NULL) and SQL_DATA_AT_EXEC
+ // (should be set for the long_* data types).
+ SQLLEN capacity; // Buffer capacity. Only used for variable-size
+ // types as well as to pass column/size precisions
+ // as follows: For string/binary parameters this
+ // value (minus one character for strings) is used
+ // as maximum column size. For decimal parameters
+ // it contains precision (p) and scale (s) encoded
+ // as (p * 100 + s). For float4 and float8 it
+ // contains precision. For time, datetime, and
+ // datatimeoffset it contains fractional seconds
+ // (scale). In case of datetime, the special
+ // value 8 indicates the SMALLDATETIME type
+ // which has no seconds.
+ };
+
+ // An instance of this structure specifies the function to invoke and
+ // the context to pass when the object/view image is about to be
+ // modified. This mechanism is used by the query machinery to save the
+ // image between result iteration and dereferencing if something gets
+ // executed between these two operations that would overwrite the
+ // image.
+ //
+ struct change_callback
+ {
+ change_callback (): callback (0), context (0) {};
+
+ void (*callback) (void*);
+ void* context;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_MSSQL_TYPES_HXX
diff --git a/libodb-mssql/odb/mssql/mssql.hxx b/libodb-mssql/odb/mssql/mssql.hxx
new file mode 100644
index 0000000..b0d2355
--- /dev/null
+++ b/libodb-mssql/odb/mssql/mssql.hxx
@@ -0,0 +1,69 @@
+// file : odb/mssql/mssql.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_MSSQL_HXX
+#define ODB_MSSQL_MSSQL_HXX
+
+#include <odb/pre.hxx>
+
+// This file should always be included before mssql-fwd.hxx.
+//
+#ifdef ODB_MSSQL_MSSQL_FWD_HXX
+# error odb/mssql/mssql-fwd.hxx included before odb/mssql/mssql.hxx
+#endif
+
+#ifdef _WIN32
+# include <odb/details/win32/windows.hxx>
+#endif
+
+#include <sqlext.h> // Standard ODBC.
+
+//#define _SQLNCLI_ODBC_
+//#include <sqlncli.h> // SQL Server Native Client driver specifics.
+
+// Instead of having a dependency on <sqlncli.h> (which, BTW, is not
+// currently available for the Linux version of the Native Client),
+// we are going to provide the few definitions that we need ourselves.
+//
+#ifndef SQL_SS_LENGTH_UNLIMITED
+# define SQL_SS_LENGTH_UNLIMITED 0
+#endif
+
+#ifndef SQL_COPT_SS_BASE
+# define SQL_COPT_SS_BASE 1200
+#endif
+
+#ifndef SQL_COPT_SS_MARS_ENABLED
+# define SQL_COPT_SS_MARS_ENABLED (SQL_COPT_SS_BASE + 24)
+#endif
+
+#ifndef SQL_MARS_ENABLED_NO
+# define SQL_MARS_ENABLED_NO 0L
+# define SQL_MARS_ENABLED_YES 1L
+#endif
+
+#ifndef SQL_COPT_SS_TXN_ISOLATION
+# define SQL_COPT_SS_TXN_ISOLATION (SQL_COPT_SS_BASE + 27)
+#endif
+
+#ifndef SQL_TXN_SS_SNAPSHOT
+# define SQL_TXN_SS_SNAPSHOT 0x00000020L
+#endif
+
+#ifndef SQL_SS_TIME2
+# define SQL_SS_TIME2 (-154)
+# define SQL_SS_TIMESTAMPOFFSET (-155)
+#endif
+
+// unixODBC doesn't define SQL_PARAM_DATA_AVAILABLE even though it
+// claims ODBC version 3.80.
+//
+#if ODBCVER >= 0x0380
+# ifndef SQL_PARAM_DATA_AVAILABLE
+# define SQL_PARAM_DATA_AVAILABLE 101
+# endif
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_MSSQL_HXX
diff --git a/libodb-mssql/odb/mssql/no-id-object-result.hxx b/libodb-mssql/odb/mssql/no-id-object-result.hxx
new file mode 100644
index 0000000..ad674bc
--- /dev/null
+++ b/libodb-mssql/odb/mssql/no-id-object-result.hxx
@@ -0,0 +1,85 @@
+// file : odb/mssql/no-id-object-result.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_NO_ID_OBJECT_RESULT_HXX
+#define ODB_MSSQL_NO_ID_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/no-id-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx> // query_base
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ class no_id_object_result_impl: public odb::no_id_object_result_impl<T>
+ {
+ public:
+ typedef odb::no_id_object_result_impl<T> base_type;
+
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_mssql> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~no_id_object_result_impl ();
+
+ no_id_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type&);
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ typedef mssql::change_callback change_callback_type;
+
+ static void
+ change_callback (void* context);
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ bool can_load_;
+ bool use_copy_;
+ typename object_traits::image_type* image_copy_;
+ };
+ }
+}
+
+#include <odb/mssql/no-id-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_NO_ID_OBJECT_RESULT_HXX
diff --git a/libodb-mssql/odb/mssql/no-id-object-result.txx b/libodb-mssql/odb/mssql/no-id-object-result.txx
new file mode 100644
index 0000000..06b7d15
--- /dev/null
+++ b/libodb-mssql/odb/mssql/no-id-object-result.txx
@@ -0,0 +1,154 @@
+// file : odb/mssql/no-id-object-result.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/mssql/exceptions.hxx> // long_data_reload
+#include <odb/mssql/no-id-object-statements.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ no_id_object_result_impl<T>::
+ ~no_id_object_result_impl ()
+ {
+ invalidate ();
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ invalidate ()
+ {
+ change_callback_type& cc (statements_.image ().change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ delete image_copy_;
+ image_copy_ = 0;
+
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ no_id_object_result_impl<T>::
+ no_id_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ use_copy_ (false),
+ image_copy_ (0)
+ {
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ load (object_type& obj)
+ {
+ if (!can_load_)
+ throw long_data_reload ();
+
+ object_traits::callback (this->db_, obj, callback_event::pre_load);
+
+ tc_.init (obj,
+ use_copy_ ? *image_copy_ : statements_.image (),
+ &this->db_);
+
+ // If we are using a copy, make sure the callback information for
+ // long data also comes from the copy.
+ //
+ can_load_ = !statement_->stream_result (
+ use_copy_ ? &statements_.image () : 0,
+ use_copy_ ? image_copy_ : 0);
+
+ object_traits::callback (this->db_, obj, callback_event::post_load);
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ next ()
+ {
+ can_load_ = true;
+ this->current (pointer_type ());
+
+ typename object_traits::image_type& im (statements_.image ());
+ change_callback_type& cc (im.change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ use_copy_ = false;
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ }
+
+ if (statement_->fetch () == select_statement::no_data)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ else
+ {
+ cc.callback = &change_callback;
+ cc.context = this;
+ }
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t no_id_object_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ change_callback (void* c)
+ {
+ no_id_object_result_impl<T>* r (
+ static_cast<no_id_object_result_impl<T>*> (c));
+
+ typename object_traits::image_type im (r->statements_.image ());
+
+ if (r->image_copy_ == 0)
+ r->image_copy_ = new typename object_traits::image_type (im);
+ else
+ *r->image_copy_ = im;
+
+ im.change_callback_.callback = 0;
+ im.change_callback_.context = 0;
+
+ r->use_copy_ = true;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/no-id-object-statements.hxx b/libodb-mssql/odb/mssql/no-id-object-statements.hxx
new file mode 100644
index 0000000..8d49355
--- /dev/null
+++ b/libodb-mssql/odb/mssql/no-id-object-statements.hxx
@@ -0,0 +1,137 @@
+// file : odb/mssql/no-id-object-statements.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_NO_ID_OBJECT_STATEMENTS_HXX
+#define ODB_MSSQL_NO_ID_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/mssql-types.hxx>
+#include <odb/mssql/binding.hxx>
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/statements-base.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // Implementation for objects without object id.
+ //
+
+ template <typename T>
+ class no_id_object_statements: public statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_mssql> object_traits;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::image_type image_type;
+
+ typedef mssql::insert_statement insert_statement_type;
+
+ public:
+ no_id_object_statements (connection_type&);
+
+ virtual
+ ~no_id_object_statements ();
+
+ // Object image.
+ //
+ image_type&
+ image (std::size_t i = 0)
+ {
+ return image_[i];
+ }
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Select binding (needed for query support).
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ false,
+ false,
+ 0,
+ false));
+
+ return *persist_;
+ }
+
+ public:
+ // select = total
+ // insert = total - inverse; inverse == 0 for object without id
+ //
+ static const std::size_t insert_column_count =
+ object_traits::column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count;
+
+ private:
+ no_id_object_statements (const no_id_object_statements&);
+ no_id_object_statements& operator= (const no_id_object_statements&);
+
+ private:
+ image_type image_[object_traits::batch];
+ SQLUSMALLINT status_[object_traits::batch];
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+ binding select_image_binding_;
+ bind select_image_bind_[select_column_count];
+
+ // Insert binding.
+ //
+ std::size_t insert_image_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[insert_column_count];
+
+ details::shared_ptr<insert_statement_type> persist_;
+ };
+ }
+}
+
+#include <odb/mssql/no-id-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_NO_ID_OBJECT_STATEMENTS_HXX
diff --git a/libodb-mssql/odb/mssql/no-id-object-statements.txx b/libodb-mssql/odb/mssql/no-id-object-statements.txx
new file mode 100644
index 0000000..30cc438
--- /dev/null
+++ b/libodb-mssql/odb/mssql/no-id-object-statements.txx
@@ -0,0 +1,39 @@
+// file : odb/mssql/no-id-object-statements.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ no_id_object_statements<T>::
+ ~no_id_object_statements ()
+ {
+ }
+
+ template <typename T>
+ no_id_object_statements<T>::
+ no_id_object_statements (connection_type& conn)
+ : statements_base (conn),
+ select_image_binding_ (select_image_bind_, select_column_count),
+ insert_image_binding_ (insert_image_bind_,
+ insert_column_count,
+ object_traits::batch,
+ sizeof (image_type),
+ status_)
+ {
+ image_[0].version = 0; // Only version in the first element used.
+ select_image_version_ = 0;
+ insert_image_version_ = 0;
+
+ // SELECT statements only use the first element (no batches).
+ //
+ select_image_binding_.change_callback = image_[0].change_callback ();
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/polymorphic-object-result.hxx b/libodb-mssql/odb/mssql/polymorphic-object-result.hxx
new file mode 100644
index 0000000..5ee9642
--- /dev/null
+++ b/libodb-mssql/odb/mssql/polymorphic-object-result.hxx
@@ -0,0 +1,99 @@
+// file : odb/mssql/polymorphic-object-result.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_POLYMORPHIC_OBJECT_RESULT_HXX
+#define ODB_MSSQL_POLYMORPHIC_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/polymorphic-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx> // query_base
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ class polymorphic_object_result_impl:
+ public odb::polymorphic_object_result_impl<T>
+ {
+ public:
+ typedef odb::polymorphic_object_result_impl<T> base_type;
+
+ typedef typename base_type::id_type id_type;
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_mssql> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename base_type::root_type root_type;
+ typedef typename base_type::discriminator_type discriminator_type;
+
+ typedef object_traits_impl<root_type, id_mssql> root_traits;
+
+ typedef typename object_traits::image_type image_type;
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~polymorphic_object_result_impl ();
+
+ polymorphic_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type*, bool fetch);
+
+ virtual id_type
+ load_id ();
+
+ virtual discriminator_type
+ load_discriminator ();
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ typedef mssql::change_callback change_callback_type;
+
+ static void
+ change_callback (void* context);
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ bool can_load_;
+ bool use_copy_;
+ image_type* image_copy_;
+ };
+ }
+}
+
+#include <odb/mssql/polymorphic-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_POLYMORPHIC_OBJECT_RESULT_HXX
diff --git a/libodb-mssql/odb/mssql/polymorphic-object-result.txx b/libodb-mssql/odb/mssql/polymorphic-object-result.txx
new file mode 100644
index 0000000..159010f
--- /dev/null
+++ b/libodb-mssql/odb/mssql/polymorphic-object-result.txx
@@ -0,0 +1,325 @@
+// file : odb/mssql/polymorphic-object-result.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/mssql/exceptions.hxx> // long_data_reload
+#include <odb/mssql/polymorphic-object-statements.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ polymorphic_object_result_impl<T>::
+ ~polymorphic_object_result_impl ()
+ {
+ invalidate ();
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ invalidate ()
+ {
+ change_callback_type& cc (
+ statements_.root_statements ().image ().change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.context = 0;
+ cc.callback = 0;
+ }
+
+ if (image_copy_ != 0)
+ {
+ object_traits::free_image (image_copy_);
+ image_copy_ = 0;
+ }
+
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ polymorphic_object_result_impl<T>::
+ polymorphic_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> st,
+ statements_type& sts,
+ const schema_version_migration* svm)
+ : base_type (sts.connection ()),
+ statement_ (st),
+ statements_ (sts),
+ tc_ (svm),
+ use_copy_ (false),
+ image_copy_ (0)
+ {
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ load (object_type* pobj, bool)
+ {
+ if (!can_load_)
+ throw long_data_reload ();
+
+ typename statements_type::root_statements_type& rsts (
+ statements_.root_statements ());
+
+ // This is a top-level call so the statements cannot be locked.
+ //
+ assert (!rsts.locked ());
+ typename statements_type::auto_lock l (rsts);
+
+ image_type& i (use_copy_ ? *image_copy_ : statements_.image ());
+ typename root_traits::image_type& ri (
+ use_copy_ ? object_traits::root_image (i) : rsts.image ());
+
+ id_type id (root_traits::id (ri));
+
+ // Determine this object's dynamic type.
+ //
+ typedef typename root_traits::info_type info_type;
+ discriminator_type d (root_traits::discriminator (ri));
+
+ // Use the polymorphic_info() helper to get concrete_info if
+ // object_type is concrete and NULL if it is abstract.
+ //
+ const info_type* spi (polymorphic_info (object_traits::info));
+ const info_type& pi (
+ spi != 0 && spi->discriminator == d
+ ? *spi
+ : root_traits::map->find (d));
+
+ typedef typename root_traits::pointer_type root_pointer_type;
+ typedef typename root_traits::pointer_traits root_pointer_traits;
+
+ typename object_traits::pointer_cache_traits::insert_guard ig;
+
+ if (pobj == 0)
+ {
+ // Need to create a new instance of the dynamic type.
+ //
+ root_pointer_type rp (pi.create ());
+ pointer_type p (
+ root_pointer_traits::template static_pointer_cast<object_type> (rp));
+
+ // Insert it as a root pointer (for non-unique pointers, rp should
+ // still be valid and for unique pointers this is a no-op).
+ //
+ ig.reset (
+ object_traits::pointer_cache_traits::insert (this->db_, id, rp));
+
+ pobj = &pointer_traits::get_ref (p);
+ current (p);
+ }
+ else
+ {
+ // We are loading into an existing instance. If the static and
+ // dynamic types differ, then make sure the instance is at least
+ // of the dynamic type.
+ //
+ if (&pi != &object_traits::info)
+ {
+ const info_type& dpi (root_traits::map->find (typeid (*pobj)));
+
+ if (&dpi != &pi && dpi.derived (pi))
+ throw object_not_persistent (); // @@ type_mismatch ?
+ }
+ }
+
+ callback_event ce (callback_event::pre_load);
+ pi.dispatch (info_type::call_callback, this->db_, pobj, &ce);
+
+ tc_.init (*pobj, i, &this->db_);
+
+ // If we are using a copy, make sure the callback information for
+ // long data also comes from the copy.
+ //
+ can_load_ = !statement_->stream_result (
+ use_copy_ ? &statements_.image () : 0,
+ use_copy_ ? image_copy_ : 0);
+
+ // Initialize the id image and binding and load the rest of the object
+ // (containers, dynamic part, etc).
+ //
+ typename object_traits::id_image_type& idi (statements_.id_image ());
+ root_traits::init (idi, id);
+
+ binding& idb (statements_.id_image_binding ());
+ if (idi.version != statements_.id_image_version () || idb.version == 0)
+ {
+ object_traits::bind (idb.bind, idi);
+ statements_.id_image_version (idi.version);
+ idb.version++;
+ }
+
+ tc_.load_ (statements_, *pobj, false);
+
+ // Load the dynamic part of the object unless static and dynamic
+ // types are the same.
+ //
+ if (&pi != &object_traits::info)
+ {
+ std::size_t d (object_traits::depth);
+ pi.dispatch (info_type::call_load, this->db_, pobj, &d);
+ };
+
+ rsts.load_delayed (tc_.version ());
+ l.unlock ();
+
+ ce = callback_event::post_load;
+ pi.dispatch (info_type::call_callback, this->db_, pobj, &ce);
+ object_traits::pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+
+ template <typename T>
+ typename polymorphic_object_result_impl<T>::id_type
+ polymorphic_object_result_impl<T>::
+ load_id ()
+ {
+ typename root_traits::image_type& i (
+ use_copy_
+ ? object_traits::root_image (*image_copy_)
+ : statements_.root_statements ().image ());
+
+ return root_traits::id (i);
+ }
+
+ template <typename T>
+ typename polymorphic_object_result_impl<T>::discriminator_type
+ polymorphic_object_result_impl<T>::
+ load_discriminator ()
+ {
+ typename root_traits::image_type& i (
+ use_copy_
+ ? object_traits::root_image (*image_copy_)
+ : statements_.root_statements ().image ());
+
+ return root_traits::discriminator (i);
+ }
+
+ template <typename T, typename R>
+ struct polymorphic_image_rebind
+ {
+ // Derived type version.
+ //
+ typedef object_traits_impl<T, id_mssql> traits;
+
+ static void
+ rebind (typename traits::statements_type& sts,
+ const schema_version_migration* svm)
+ {
+ typename traits::image_type& im (sts.image ());
+
+ if (traits::check_version (sts.select_image_versions (), im))
+ {
+ binding& b (sts.select_image_binding (traits::depth));
+ object_traits_calls<T> tc (svm);
+ tc.bind (b.bind, 0, 0, im, statement_select);
+ traits::update_version (
+ sts.select_image_versions (), im, sts.select_image_bindings ());
+ }
+ }
+ };
+
+ template <typename R>
+ struct polymorphic_image_rebind<R, R>
+ {
+ // Root type version.
+ //
+ typedef object_traits_impl<R, id_mssql> traits;
+
+ static void
+ rebind (typename traits::statements_type& sts,
+ const schema_version_migration* svm)
+ {
+ typename traits::image_type& im (sts.image ());
+
+ if (im.version != sts.select_image_version ())
+ {
+ binding& b (sts.select_image_binding ());
+ object_traits_calls<R> tc (svm);
+ tc.bind (b.bind, im, statement_select);
+ sts.select_image_version (im.version);
+ b.version++;
+ }
+ }
+ };
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ next ()
+ {
+ can_load_ = true;
+ this->current (pointer_type ());
+
+ change_callback_type& cc (
+ statements_.root_statements ().image ().change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ use_copy_ = false;
+ polymorphic_image_rebind<object_type, root_type>::rebind (
+ statements_, tc_.version ());
+
+ if (statement_->fetch () == select_statement::no_data)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ else
+ {
+ cc.callback = &change_callback;
+ cc.context = this;
+ }
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t polymorphic_object_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ change_callback (void* c)
+ {
+ polymorphic_object_result_impl<T>* r (
+ static_cast<polymorphic_object_result_impl<T>*> (c));
+ image_type& im (r->statements_.image ());
+
+ if (r->image_copy_ == 0)
+ r->image_copy_ = object_traits::clone_image (im);
+ else
+ object_traits::copy_image (*r->image_copy_, im);
+
+ typename root_traits::image_type& rim (
+ r->statements_.root_statements ().image ());
+
+ rim.change_callback_.callback = 0;
+ rim.change_callback_.context = 0;
+
+ r->use_copy_ = true;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/polymorphic-object-statements.hxx b/libodb-mssql/odb/mssql/polymorphic-object-statements.hxx
new file mode 100644
index 0000000..c115502
--- /dev/null
+++ b/libodb-mssql/odb/mssql/polymorphic-object-statements.hxx
@@ -0,0 +1,469 @@
+// file : odb/mssql/polymorphic-object-statements.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_POLYMORPHIC_OBJECT_STATEMENTS_HXX
+#define ODB_MSSQL_POLYMORPHIC_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/mssql-types.hxx>
+#include <odb/mssql/binding.hxx>
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/statements-base.hxx>
+#include <odb/mssql/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // Implementation for polymorphic objects.
+ //
+
+ template <typename T>
+ class polymorphic_root_object_statements: public object_statements<T>
+ {
+ public:
+ typedef typename object_statements<T>::connection_type connection_type;
+ typedef typename object_statements<T>::object_traits object_traits;
+ typedef typename object_statements<T>::id_image_type id_image_type;
+
+ typedef
+ typename object_traits::discriminator_image_type
+ discriminator_image_type;
+
+ typedef
+ typename object_statements<T>::select_statement_type
+ select_statement_type;
+
+ public:
+ // Interface compatibility with derived_object_statements.
+ //
+ typedef polymorphic_root_object_statements root_statements_type;
+
+ root_statements_type&
+ root_statements ()
+ {
+ return *this;
+ }
+
+ public:
+ // Discriminator binding.
+ //
+ discriminator_image_type&
+ discriminator_image () {return discriminator_image_;}
+
+ std::size_t
+ discriminator_image_version () const
+ {return discriminator_image_version_;}
+
+ void
+ discriminator_image_version (std::size_t v)
+ {discriminator_image_version_ = v;}
+
+ binding&
+ discriminator_image_binding () {return discriminator_image_binding_;}
+
+ // Id binding for discriminator retrieval.
+ //
+ id_image_type&
+ discriminator_id_image () {return discriminator_id_image_;}
+
+ std::size_t
+ discriminator_id_image_version () const
+ {return discriminator_id_image_version_;}
+
+ void
+ discriminator_id_image_version (std::size_t v)
+ {discriminator_id_image_version_ = v;}
+
+ binding&
+ discriminator_id_image_binding ()
+ {return discriminator_id_image_binding_;}
+
+ //
+ //
+ select_statement_type&
+ find_discriminator_statement ()
+ {
+ if (find_discriminator_ == 0)
+ find_discriminator_.reset (
+ new (details::shared) select_statement_type (
+ this->conn_,
+ object_traits::find_discriminator_statement,
+ false, // Doesn't need to be processed.
+ false, // Don't optimize.
+ discriminator_id_image_binding_,
+ discriminator_image_binding_,
+ false));
+
+ return *find_discriminator_;
+ }
+
+ public:
+ polymorphic_root_object_statements (connection_type&);
+
+ virtual
+ ~polymorphic_root_object_statements ();
+
+ // Static "override" (statements type).
+ //
+ void
+ load_delayed (const schema_version_migration* svm)
+ {
+ assert (this->locked ());
+
+ if (!this->delayed_.empty ())
+ this->template load_delayed_<polymorphic_root_object_statements> (
+ svm);
+ }
+
+ public:
+ static const std::size_t id_column_count =
+ object_statements<T>::id_column_count;
+
+ static const std::size_t discriminator_column_count =
+ object_traits::discriminator_column_count;
+
+ static const std::size_t managed_optimistic_column_count =
+ object_traits::managed_optimistic_column_count;
+
+ private:
+ // Discriminator image.
+ //
+ discriminator_image_type discriminator_image_;
+ std::size_t discriminator_image_version_;
+ binding discriminator_image_binding_;
+ bind discriminator_image_bind_[discriminator_column_count +
+ managed_optimistic_column_count];
+
+ // Id image for discriminator retrieval (only used as a parameter).
+ //
+ id_image_type discriminator_id_image_;
+ std::size_t discriminator_id_image_version_;
+ binding discriminator_id_image_binding_;
+ bind discriminator_id_image_bind_[id_column_count];
+
+ details::shared_ptr<select_statement_type> find_discriminator_;
+ };
+
+ template <typename T>
+ class polymorphic_derived_object_statements: public statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_mssql> object_traits;
+ typedef typename object_traits::id_type id_type;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::id_image_type id_image_type;
+ typedef typename object_traits::image_type image_type;
+
+ typedef typename object_traits::root_type root_type;
+ typedef
+ polymorphic_root_object_statements<root_type>
+ root_statements_type;
+
+ typedef typename object_traits::base_type base_type;
+ typedef
+ typename object_traits::base_traits::statements_type
+ base_statements_type;
+
+ typedef
+ typename object_traits::extra_statement_cache_type
+ extra_statement_cache_type;
+
+ typedef mssql::insert_statement insert_statement_type;
+ typedef mssql::select_statement select_statement_type;
+ typedef mssql::update_statement update_statement_type;
+ typedef mssql::delete_statement delete_statement_type;
+
+ typedef typename root_statements_type::auto_lock auto_lock;
+
+ public:
+ polymorphic_derived_object_statements (connection_type&);
+
+ virtual
+ ~polymorphic_derived_object_statements ();
+
+ public:
+ // Delayed loading.
+ //
+ static void
+ delayed_loader (odb::database&,
+ const id_type&,
+ root_type&,
+ const schema_version_migration*);
+
+ public:
+ // Root and immediate base statements.
+ //
+ root_statements_type&
+ root_statements ()
+ {
+ return root_statements_;
+ }
+
+ base_statements_type&
+ base_statements ()
+ {
+ return base_statements_;
+ }
+
+ public:
+ // Object image.
+ //
+ image_type&
+ image ()
+ {
+ return image_;
+ }
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ std::size_t
+ insert_id_binding_version () const { return insert_id_binding_version_;}
+
+ void
+ insert_id_binding_version (std::size_t v) {insert_id_binding_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_binding_version () const { return update_id_binding_version_;}
+
+ void
+ update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t*
+ select_image_versions () { return select_image_versions_;}
+
+ binding*
+ select_image_bindings () {return select_image_bindings_;}
+
+ binding&
+ select_image_binding (std::size_t d)
+ {
+ return select_image_bindings_[object_traits::depth - d];
+ }
+
+ // Object id binding (comes from the root statements).
+ //
+ id_image_type&
+ id_image () {return root_statements_.id_image ();}
+
+ std::size_t
+ id_image_version () const {return root_statements_.id_image_version ();}
+
+ void
+ id_image_version (std::size_t v) {root_statements_.id_image_version (v);}
+
+ binding&
+ id_image_binding () {return root_statements_.id_image_binding ();}
+
+ binding&
+ optimistic_id_image_binding () {
+ return root_statements_.optimistic_id_image_binding ();}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ // Auto id and version are in the root.
+ //
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ false,
+ false,
+ 0));
+
+ return *persist_;
+ }
+
+ select_statement_type&
+ find_statement (std::size_t d)
+ {
+ std::size_t i (object_traits::depth - d);
+ details::shared_ptr<select_statement_type>& p (find_[i]);
+
+ if (p == 0)
+ p.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ object_traits::find_statements[i],
+ object_traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ root_statements_.id_image_binding (),
+ select_image_bindings_[i],
+ false));
+
+ return *p;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ object_traits::update_statement,
+ object_traits::versioned, // Process if versioned.
+ update_image_binding_,
+ 0,
+ false));
+
+ return *update_;
+ }
+
+ delete_statement_type&
+ erase_statement ()
+ {
+ if (erase_ == 0)
+ erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::erase_statement,
+ root_statements_.id_image_binding (),
+ false));
+
+ return *erase_;
+ }
+
+ // Extra (container, section) statement cache.
+ //
+ extra_statement_cache_type&
+ extra_statement_cache ()
+ {
+ return extra_statement_cache_.get (
+ conn_,
+ image_,
+ id_image (),
+ id_image_binding (),
+ &id_image_binding ()); // Note, not id+version.
+ }
+
+ public:
+ // select = total - id - separate_load + base::select
+ // insert = total - inverse
+ // update = total - inverse - id - readonly - separate_update
+ //
+ static const std::size_t id_column_count =
+ object_traits::id_column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count -
+ id_column_count -
+ object_traits::separate_load_column_count +
+ base_statements_type::select_column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count -
+ object_traits::inverse_column_count;
+
+ static const std::size_t update_column_count = insert_column_count -
+ object_traits::id_column_count -
+ object_traits::readonly_column_count -
+ object_traits::separate_update_column_count;
+
+ private:
+ polymorphic_derived_object_statements (
+ const polymorphic_derived_object_statements&);
+
+ polymorphic_derived_object_statements&
+ operator= (const polymorphic_derived_object_statements&);
+
+ private:
+ root_statements_type& root_statements_;
+ base_statements_type& base_statements_;
+
+ extra_statement_cache_ptr<extra_statement_cache_type,
+ image_type,
+ id_image_type> extra_statement_cache_;
+
+ image_type image_;
+
+ // Select binding. Here we are have an array of statements/bindings
+ // one for each depth. In other words, if we have classes root, base,
+ // and derived, then we have the following array of statements:
+ //
+ // [0] d + b + r
+ // [1] d + b
+ // [2] d
+ //
+ // Also, because we have a chain of images bound to these statements,
+ // we have an array of versions, one entry for each base plus one for
+ // our own image.
+ //
+ // A poly-abstract class only needs the first statement and in this
+ // case we have only one entry in the the bindings and statements
+ // arrays (but not versions; we still have a chain of images).
+ //
+ std::size_t select_image_versions_[object_traits::depth];
+ binding select_image_bindings_[
+ object_traits::abstract ? 1 : object_traits::depth];
+ bind select_image_bind_[select_column_count];
+
+ // Insert binding. The id binding is copied from the hierarchy root.
+ //
+ std::size_t insert_image_version_;
+ std::size_t insert_id_binding_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[insert_column_count];
+
+ // Update binding. The id suffix binding is copied from the hierarchy
+ // root.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_binding_version_;
+ binding update_image_binding_;
+ bind update_image_bind_[update_column_count + id_column_count];
+
+ details::shared_ptr<insert_statement_type> persist_;
+ details::shared_ptr<select_statement_type> find_[
+ object_traits::abstract ? 1 : object_traits::depth];
+ details::shared_ptr<update_statement_type> update_;
+ details::shared_ptr<delete_statement_type> erase_;
+ };
+ }
+}
+
+#include <odb/mssql/polymorphic-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_POLYMORPHIC_OBJECT_STATEMENTS_HXX
diff --git a/libodb-mssql/odb/mssql/polymorphic-object-statements.txx b/libodb-mssql/odb/mssql/polymorphic-object-statements.txx
new file mode 100644
index 0000000..0ba437a
--- /dev/null
+++ b/libodb-mssql/odb/mssql/polymorphic-object-statements.txx
@@ -0,0 +1,140 @@
+// file : odb/mssql/polymorphic-object-statements.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/transaction.hxx>
+#include <odb/mssql/statement-cache.hxx>
+#include <odb/mssql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // polymorphic_root_object_statements
+ //
+
+ template <typename T>
+ polymorphic_root_object_statements<T>::
+ ~polymorphic_root_object_statements ()
+ {
+ }
+
+ template <typename T>
+ polymorphic_root_object_statements<T>::
+ polymorphic_root_object_statements (connection_type& conn)
+ : object_statements<T> (conn),
+ discriminator_image_binding_ (discriminator_image_bind_,
+ discriminator_column_count +
+ managed_optimistic_column_count),
+ discriminator_id_image_binding_ (discriminator_id_image_bind_,
+ id_column_count)
+ {
+ discriminator_image_.version = 0;
+ discriminator_id_image_.version = 0;
+
+ discriminator_image_version_ = 0;
+ discriminator_id_image_version_ = 0;
+
+ std::memset (
+ discriminator_image_bind_, 0, sizeof (discriminator_image_bind_));
+ std::memset (
+ discriminator_id_image_bind_, 0, sizeof (discriminator_id_image_bind_));
+ }
+
+ //
+ // polymorphic_derived_object_statements
+ //
+
+ template <typename T>
+ polymorphic_derived_object_statements<T>::
+ ~polymorphic_derived_object_statements ()
+ {
+ }
+
+ template <typename T>
+ polymorphic_derived_object_statements<T>::
+ polymorphic_derived_object_statements (connection_type& conn)
+ : statements_base (conn),
+ root_statements_ (conn.statement_cache ().find_object<root_type> ()),
+ base_statements_ (conn.statement_cache ().find_object<base_type> ()),
+ insert_image_binding_ (insert_image_bind_, insert_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count)
+ {
+ image_.base = &base_statements_.image ();
+ image_.version = 0;
+
+ for (std::size_t i (0); i < object_traits::depth; ++i)
+ select_image_versions_[i] = 0;
+
+ for (std::size_t i (0);
+ i < (object_traits::abstract ? 1 : object_traits::depth);
+ ++i)
+ {
+ select_image_bindings_[i].bind = select_image_bind_;
+ select_image_bindings_[i].count = object_traits::find_column_counts[i];
+ select_image_bindings_[i].change_callback = 0;
+ }
+
+ // Statements other than the first one (which goes all the way to
+ // the root) can never override the image because they are used to
+ // load up the dynamic part of the object only after the static
+ // part has been loaded (and triggered the callback if necessary).
+ //
+ select_image_bindings_[0].change_callback =
+ root_statements_.image ().change_callback ();
+
+ insert_image_version_ = 0;
+ insert_id_binding_version_ = 0;
+ update_image_version_ = 0;
+ update_id_binding_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ }
+
+ template <typename T>
+ void polymorphic_derived_object_statements<T>::
+ delayed_loader (odb::database& db,
+ const id_type& id,
+ root_type& robj,
+ const schema_version_migration* svm)
+ {
+ connection_type& conn (transaction::current ().connection (db));
+ polymorphic_derived_object_statements& sts (
+ conn.statement_cache ().find_object<object_type> ());
+ root_statements_type& rsts (sts.root_statements ());
+
+ object_type& obj (static_cast<object_type&> (robj));
+
+ // The same code as in object_statements::load_delayed_().
+ //
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (sts, &id))
+ throw object_not_persistent ();
+
+ auto_result ar (*sts.find_[0]);
+
+ object_traits::callback (db, obj, callback_event::pre_load);
+ tc.init (obj, sts.image (), &db);
+ sts.find_[0]->stream_result ();
+ ar.free ();
+ tc.load_ (sts, obj, false); // Load containers, etc.
+
+ rsts.load_delayed (svm);
+
+ {
+ typename root_statements_type::auto_unlock u (rsts);
+ object_traits::callback (db, obj, callback_event::post_load);
+ }
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/prepared-query.cxx b/libodb-mssql/odb/mssql/prepared-query.cxx
new file mode 100644
index 0000000..ae0389d
--- /dev/null
+++ b/libodb-mssql/odb/mssql/prepared-query.cxx
@@ -0,0 +1,15 @@
+// file : odb/mssql/prepared-query.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/prepared-query.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ prepared_query_impl::
+ ~prepared_query_impl ()
+ {
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/prepared-query.hxx b/libodb-mssql/odb/mssql/prepared-query.hxx
new file mode 100644
index 0000000..730f7ec
--- /dev/null
+++ b/libodb-mssql/odb/mssql/prepared-query.hxx
@@ -0,0 +1,34 @@
+// file : odb/mssql/prepared-query.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_PREPARED_QUERY_HXX
+#define ODB_MSSQL_PREPARED_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/prepared-query.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/query.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ struct LIBODB_MSSQL_EXPORT prepared_query_impl: odb::prepared_query_impl
+ {
+ virtual
+ ~prepared_query_impl ();
+
+ prepared_query_impl (odb::connection& c): odb::prepared_query_impl (c) {}
+
+ mssql::query_base query;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_PREPARED_QUERY_HXX
diff --git a/libodb-mssql/odb/mssql/query-const-expr.cxx b/libodb-mssql/odb/mssql/query-const-expr.cxx
new file mode 100644
index 0000000..4e28fad
--- /dev/null
+++ b/libodb-mssql/odb/mssql/query-const-expr.cxx
@@ -0,0 +1,14 @@
+// file : odb/mssql/query-const-expr.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/query.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ // Sun CC cannot handle this in query.cxx.
+ //
+ const query_base query_base::true_expr (true);
+ }
+}
diff --git a/libodb-mssql/odb/mssql/query-dynamic.cxx b/libodb-mssql/odb/mssql/query-dynamic.cxx
new file mode 100644
index 0000000..fb49b2b
--- /dev/null
+++ b/libodb-mssql/odb/mssql/query-dynamic.cxx
@@ -0,0 +1,157 @@
+// file : odb/mssql/query-dynamic.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+
+#include <odb/mssql/query-dynamic.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ static const char* logic_operators[] = {") AND (", ") OR ("};
+ static const char* comp_operators[] = {"=", "!=", "<", ">", "<=", ">="};
+
+ static void
+ translate (query_base& q, const odb::query_base& s, size_t p)
+ {
+ typedef odb::query_base::clause_part part;
+
+ const part& x (s.clause ()[p]);
+
+ switch (x.kind)
+ {
+ case part::kind_column:
+ {
+ const query_column_base* c (
+ static_cast<const query_column_base*> (
+ x.native_info[id_mssql].column));
+
+ q.append (c->table (), c->column ());
+ break;
+ }
+ case part::kind_param_val:
+ case part::kind_param_ref:
+ {
+ const query_column_base& qc (
+ *static_cast<const query_column_base*> (
+ x.native_info[id_mssql].column));
+
+ query_param_factory f (
+ reinterpret_cast<query_param_factory> (
+ x.native_info[id_mssql].param_factory));
+
+ const odb::query_param* p (
+ reinterpret_cast<const odb::query_param*> (x.data));
+
+ q.append (f (p->value, qc, x.kind == part::kind_param_ref),
+ qc.conversion ());
+ break;
+ }
+ case part::kind_native:
+ {
+ q.append (s.strings ()[x.data]);
+ break;
+ }
+ case part::kind_true:
+ case part::kind_false:
+ {
+ q.append (x.kind == part::kind_true);
+ break;
+ }
+ case part::op_add:
+ {
+ translate (q, s, x.data);
+ translate (q, s, p - 1);
+ break;
+ }
+ case part::op_and:
+ case part::op_or:
+ {
+ q += "(";
+ translate (q, s, x.data);
+ q += logic_operators[x.kind - part::op_and];
+ translate (q, s, p - 1);
+ q += ")";
+ break;
+ }
+ case part::op_not:
+ {
+ q += "NOT (";
+ translate (q, s, p - 1);
+ q += ")";
+ break;
+ }
+ case part::op_null:
+ case part::op_not_null:
+ {
+ translate (q, s, p - 1);
+ q += (x.kind == part::op_null ? "IS NULL" : "IS NOT NULL");
+ break;
+ }
+ case part::op_in:
+ {
+ if (x.data != 0)
+ {
+ size_t b (p - x.data);
+
+ translate (q, s, b - 1); // column
+ q += "IN (";
+
+ for (size_t i (b); i != p; ++i)
+ {
+ if (i != b)
+ q += ",";
+
+ translate (q, s, i);
+ }
+
+ q += ")";
+ }
+ else
+ q.append (false);
+
+ break;
+ }
+ case part::op_like:
+ {
+ translate (q, s, p - 2); // column
+ q += "LIKE";
+ translate (q, s, p - 1); // pattern
+ break;
+ }
+ case part::op_like_escape:
+ {
+ translate (q, s, p - 3); // column
+ q += "LIKE";
+ translate (q, s, p - 2); // pattern
+ q += "ESCAPE";
+ translate (q, s, p - 1); // escape
+ break;
+ }
+ case part::op_eq:
+ case part::op_ne:
+ case part::op_lt:
+ case part::op_gt:
+ case part::op_le:
+ case part::op_ge:
+ {
+ translate (q, s, x.data);
+ q += comp_operators[x.kind - part::op_eq];
+ translate (q, s, p - 1);
+ break;
+ }
+ }
+ }
+
+ query_base::
+ query_base (const odb::query_base& q)
+ : binding_ (0, 0)
+ {
+ if (!q.empty ())
+ translate (*this, q, q.clause ().size () - 1);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/query-dynamic.hxx b/libodb-mssql/odb/mssql/query-dynamic.hxx
new file mode 100644
index 0000000..ceb423b
--- /dev/null
+++ b/libodb-mssql/odb/mssql/query-dynamic.hxx
@@ -0,0 +1,32 @@
+// file : odb/mssql/query-dynamic.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_QUERY_DYNAMIC_HXX
+#define ODB_MSSQL_QUERY_DYNAMIC_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/query.hxx>
+#include <odb/query-dynamic.hxx>
+
+#include <odb/mssql/query.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ typedef details::shared_ptr<query_param> (*query_param_factory) (
+ const void* val, const query_column_base&, bool by_ref);
+
+ template <typename T, database_type_id ID>
+ details::shared_ptr<query_param>
+ query_param_factory_impl (const void*, const query_column_base&, bool);
+ }
+}
+
+#include <odb/mssql/query-dynamic.ixx>
+#include <odb/mssql/query-dynamic.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_QUERY_DYNAMIC_HXX
diff --git a/libodb-mssql/odb/mssql/query-dynamic.ixx b/libodb-mssql/odb/mssql/query-dynamic.ixx
new file mode 100644
index 0000000..005175f
--- /dev/null
+++ b/libodb-mssql/odb/mssql/query-dynamic.ixx
@@ -0,0 +1,30 @@
+// file : odb/mssql/query-dynamic.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ //
+ template <typename T, database_type_id ID>
+ inline query_column<T, ID>::
+ query_column (odb::query_column<T>& qc,
+ const char* table,
+ const char* column,
+ const char* conv,
+ unsigned short prec,
+ unsigned short scale)
+ : query_column_base (table, column, conv, prec, scale)
+ {
+ native_column_info& ci (qc.native_info[id_mssql]);
+ ci.column = static_cast<query_column_base*> (this);
+
+ // For some reason GCC needs this statically-typed pointer in
+ // order to instantiate the functions.
+ //
+ query_param_factory f (&query_param_factory_impl<T, ID>);
+ ci.param_factory = reinterpret_cast<void*> (f);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/query-dynamic.txx b/libodb-mssql/odb/mssql/query-dynamic.txx
new file mode 100644
index 0000000..8683c0f
--- /dev/null
+++ b/libodb-mssql/odb/mssql/query-dynamic.txx
@@ -0,0 +1,25 @@
+// file : odb/mssql/query-dynamic.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T, database_type_id ID>
+ details::shared_ptr<query_param>
+ query_param_factory_impl (const void* val,
+ const query_column_base& qc,
+ bool by_ref)
+ {
+ const T& v (*static_cast<const T*> (val));
+
+ unsigned short p (qc.prec ());
+ unsigned short s (qc.scale ());
+
+ return details::shared_ptr<query_param> (
+ by_ref
+ ? new (details::shared) query_param_impl<T, ID> (ref_bind<T> (v, p, s))
+ : new (details::shared) query_param_impl<T, ID> (val_bind<T> (v, p, s)));
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/query.cxx b/libodb-mssql/odb/mssql/query.cxx
new file mode 100644
index 0000000..6dd87e7
--- /dev/null
+++ b/libodb-mssql/odb/mssql/query.cxx
@@ -0,0 +1,406 @@
+// file : odb/mssql/query.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset, std::memcpy
+
+#include <odb/mssql/query.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ // query_param
+ //
+ query_param::
+ ~query_param ()
+ {
+ }
+
+ // query_base
+ //
+ query_base::
+ query_base (const query_base& q)
+ : clause_ (q.clause_),
+ parameters_ (q.parameters_),
+ bind_ (q.bind_),
+ binding_ (0, 0)
+ {
+ // Here and below we want to maintain up to date binding info so
+ // that the call to parameters_binding() below is an immutable
+ // operation, provided the query does not have any by-reference
+ // parameters. This way a by-value-only query can be shared
+ // between multiple threads without the need for synchronization.
+ //
+ if (size_t n = bind_.size ())
+ {
+ binding_.bind = &bind_[0];
+ binding_.count = n;
+ binding_.version++;
+ }
+ }
+
+ query_base& query_base::
+ operator= (const query_base& q)
+ {
+ if (this != &q)
+ {
+ clause_ = q.clause_;
+ parameters_ = q.parameters_;
+ bind_ = q.bind_;
+
+ size_t n (bind_.size ());
+ binding_.bind = n != 0 ? &bind_[0] : 0;
+ binding_.count = n;
+ binding_.version++;
+ }
+
+ return *this;
+ }
+
+ query_base& query_base::
+ operator+= (const query_base& q)
+ {
+ clause_.insert (clause_.end (), q.clause_.begin (), q.clause_.end ());
+
+ size_t n (bind_.size ());
+
+ parameters_.insert (
+ parameters_.end (), q.parameters_.begin (), q.parameters_.end ());
+
+ bind_.insert (
+ bind_.end (), q.bind_.begin (), q.bind_.end ());
+
+ if (n != bind_.size ())
+ {
+ binding_.bind = &bind_[0];
+ binding_.count = bind_.size ();
+ binding_.version++;
+ }
+
+ return *this;
+ }
+
+ void query_base::
+ append (const string& q)
+ {
+ if (!clause_.empty () &&
+ clause_.back ().kind == clause_part::kind_native)
+ {
+ string& s (clause_.back ().part);
+
+ char first (!q.empty () ? q[0] : ' ');
+ char last (!s.empty () ? s[s.size () - 1] : ' ');
+
+ // We don't want extra spaces after '(' as well as before ','
+ // and ')'.
+ //
+ if (last != ' ' && last != '\n' && last != '(' &&
+ first != ' ' && first != '\n' && first != ',' && first != ')')
+ s += ' ';
+
+ s += q;
+ }
+ else
+ clause_.push_back (clause_part (clause_part::kind_native, q));
+ }
+
+ void query_base::
+ append (const char* table, const char* column)
+ {
+ string s (table);
+ s += '.';
+ s += column;
+
+ clause_.push_back (clause_part (clause_part::kind_column, s));
+ }
+
+ void query_base::
+ append (details::shared_ptr<query_param> p, const char* conv)
+ {
+ clause_.push_back (clause_part (clause_part::kind_param));
+
+ if (conv != 0)
+ clause_.back ().part = conv;
+
+ parameters_.push_back (p);
+ bind_.push_back (bind ());
+ binding_.bind = &bind_[0];
+ binding_.count = bind_.size ();
+ binding_.version++;
+
+ bind* b (&bind_.back ());
+ memset (b, 0, sizeof (bind));
+ p->bind (b);
+ }
+
+ void query_base::
+ init_parameters () const
+ {
+ bool inc_ver (false);
+
+ for (size_t i (0); i < parameters_.size (); ++i)
+ {
+ query_param& p (*parameters_[i]);
+
+ if (p.reference ())
+ {
+ if (p.init ())
+ {
+ p.bind (&bind_[i]);
+ inc_ver = true;
+ }
+ }
+ }
+
+ if (inc_ver)
+ binding_.version++;
+ }
+
+ static bool
+ check_prefix (const string& s)
+ {
+ string::size_type n;
+
+ // It is easier to compare to upper and lower-case versions
+ // rather than getting involved with the portable case-
+ // insensitive string comparison mess.
+ //
+ if (s.compare (0, (n = 5), "WHERE") == 0 ||
+ s.compare (0, (n = 5), "where") == 0 ||
+ s.compare (0, (n = 6), "SELECT") == 0 ||
+ s.compare (0, (n = 6), "select") == 0 ||
+ s.compare (0, (n = 8), "ORDER BY") == 0 ||
+ s.compare (0, (n = 8), "order by") == 0 ||
+ s.compare (0, (n = 8), "GROUP BY") == 0 ||
+ s.compare (0, (n = 8), "group by") == 0 ||
+ s.compare (0, (n = 6), "HAVING") == 0 ||
+ s.compare (0, (n = 6), "having") == 0 ||
+ s.compare (0, (n = 4), "EXEC") == 0 ||
+ s.compare (0, (n = 4), "exec") == 0 ||
+ s.compare (0, (n = 7), "EXECUTE") == 0 ||
+ s.compare (0, (n = 7), "execute") == 0)
+ {
+ // It either has to be an exact match, or there should be
+ // a whitespace following the keyword.
+ //
+ if (s.size () == n || s[n] == ' ' || s[n] == '\n' || s[n] =='\t')
+ return true;
+ }
+
+ return false;
+ }
+
+ void query_base::
+ optimize ()
+ {
+ // Remove a single TRUE literal or one that is followe by one of
+ // the other clauses. This avoids useless WHERE clauses like
+ //
+ // WHERE TRUE GROUP BY foo
+ //
+ clause_type::iterator i (clause_.begin ()), e (clause_.end ());
+
+ if (i != e && i->kind == clause_part::kind_bool && i->bool_part)
+ {
+ clause_type::iterator j (i + 1);
+
+ if (j == e ||
+ (j->kind == clause_part::kind_native && check_prefix (j->part)))
+ clause_.erase (i);
+ }
+ }
+
+ const char* query_base::
+ clause_prefix () const
+ {
+ if (!clause_.empty ())
+ {
+ const clause_part& p (clause_.front ());
+
+ if (p.kind == clause_part::kind_native && check_prefix (p.part))
+ return "";
+
+ return "WHERE ";
+ }
+
+ return "";
+ }
+
+ string query_base::
+ clause () const
+ {
+ string r;
+
+ for (clause_type::const_iterator i (clause_.begin ()),
+ end (clause_.end ());
+ i != end;
+ ++i)
+ {
+ char last (!r.empty () ? r[r.size () - 1] : ' ');
+
+ switch (i->kind)
+ {
+ case clause_part::kind_column:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ r += i->part;
+ break;
+ }
+ case clause_part::kind_param:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ // Add the conversion expression, if any.
+ //
+ string::size_type p (0);
+ if (!i->part.empty ())
+ {
+ p = i->part.find ("(?)");
+ r.append (i->part, 0, p);
+ }
+
+ r += '?';
+
+ if (!i->part.empty ())
+ r.append (i->part, p + 3, string::npos);
+
+ break;
+ }
+ case clause_part::kind_native:
+ {
+ // We don't want extra spaces after '(' as well as before ','
+ // and ')'.
+ //
+ const string& p (i->part);
+ char first (!p.empty () ? p[0] : ' ');
+
+ if (last != ' ' && last != '\n' && last != '(' &&
+ first != ' ' && first != '\n' && first != ',' && first != ')')
+ r += ' ';
+
+ r += p;
+ break;
+ }
+ case clause_part::kind_bool:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ // SQL Server does not have "true" TRUE and FALSE boolean
+ // constants (there seem to be TRUE and FALSE literals but
+ // they are interpreted as integers). Boolean values seem
+ // to only be created as the result of boolean expressions.
+ //
+ r += i->bool_part ? "1 = 1" : "1 = 0";
+ break;
+ }
+ }
+ }
+
+ return clause_prefix () + r;
+ }
+
+ query_base
+ operator&& (const query_base& x, const query_base& y)
+ {
+ // Optimize cases where one or both sides are constant truth.
+ //
+ bool xt (x.const_true ()), yt (y.const_true ());
+
+ if (xt && yt)
+ return x;
+
+ if (xt)
+ return y;
+
+ if (yt)
+ return x;
+
+ query_base r ("(");
+ r += x;
+ r += ") AND (";
+ r += y;
+ r += ")";
+ return r;
+ }
+
+ query_base
+ operator|| (const query_base& x, const query_base& y)
+ {
+ query_base r ("(");
+ r += x;
+ r += ") OR (";
+ r += y;
+ r += ")";
+ return r;
+ }
+
+ query_base
+ operator! (const query_base& x)
+ {
+ query_base r ("NOT (");
+ r += x;
+ r += ")";
+ return r;
+ }
+
+ //
+ // long_query_param_impl
+ //
+
+ void long_query_param_impl::
+ copy ()
+ {
+ size_ = 0;
+
+ // The below code is the same as in statement.cxx.
+ //
+ const void* buf (&callback_);
+ char tmp_buf[1024];
+
+ size_t position (0);
+ for (;;)
+ {
+ size_t n;
+ chunk_type chunk;
+
+ callback_.callback.param (
+ callback_.context.param,
+ &position,
+ &buf,
+ &n,
+ &chunk,
+ tmp_buf,
+ sizeof (tmp_buf));
+
+ if (chunk != chunk_null)
+ {
+ if (buf_.capacity () < size_ + n)
+ buf_.capacity (size_ + n, size_);
+
+ memcpy (buf_.data () + size_, buf, n);
+ size_ += n;
+ }
+
+ if (chunk == chunk_one || chunk == chunk_last)
+ {
+ // Make sure the resulting buffer is not NULL (failed that, this
+ // parameter will be skipped by the query machinery).
+ //
+ if (size_ == 0)
+ buf_.capacity (1);
+
+ break;
+ }
+ else if (chunk == chunk_null)
+ break;
+ }
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/query.hxx b/libodb-mssql/odb/mssql/query.hxx
new file mode 100644
index 0000000..18db752
--- /dev/null
+++ b/libodb-mssql/odb/mssql/query.hxx
@@ -0,0 +1,2711 @@
+// file : odb/mssql/query.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_QUERY_HXX
+#define ODB_MSSQL_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx> // odb::query_column
+#include <odb/query.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/traits.hxx>
+#include <odb/mssql/binding.hxx>
+#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/mssql-types.hxx>
+
+#include <odb/details/buffer.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/details/export.hxx>
+#include <odb/mssql/details/conversion.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ // For precision, 0 is invalid so we can use it as a special value
+ // to indicate that the precision has not been specified. For scale,
+ // however, 0 is a valid value and for some types (e.g., TIME) if
+ // the scale is not specified, it defaults to something other than
+ // 0. As a result, for scale, the not specific special value will
+ // be 0xFFFF (USHORT_MAX).
+ //
+ template <typename T>
+ struct val_bind
+ {
+ typedef const T& type;
+
+ explicit
+ val_bind (type v, unsigned short p = 0, unsigned short s = 0xFFFF)
+ : val (v), prec (p), scale (s) {}
+
+ type val;
+
+ unsigned short prec;
+ unsigned short scale;
+ };
+
+ template <typename T, std::size_t N>
+ struct val_bind<T[N]>
+ {
+ typedef const T* type;
+
+ explicit
+ val_bind (type v, unsigned short p = 0, unsigned short s = 0xFFFF)
+ : val (v), prec (p), scale (s) {}
+
+ type val;
+
+ unsigned short prec;
+ unsigned short scale;
+ };
+
+ template <typename T>
+ struct ref_bind
+ {
+ typedef const T& type;
+
+ explicit
+ ref_bind (type r, unsigned short p = 0, unsigned short s = 0xFFFF)
+ : ref (r), prec (p), scale (s) {}
+
+ const void*
+ ptr () const {return &ref;}
+
+ type ref;
+
+ unsigned short prec;
+ unsigned short scale;
+ };
+
+ template <typename T, std::size_t N>
+ struct ref_bind<T[N]>
+ {
+ typedef const T* type;
+
+ explicit
+ ref_bind (type r, unsigned short p = 0, unsigned short s = 0xFFFF)
+ : ref (r), prec (p), scale (s) {}
+
+ // Allow implicit conversion from decayed ref_bind's.
+ //
+ ref_bind (ref_bind<T*> r): ref (r.ref), prec (r.prec), scale (r.scale) {}
+ ref_bind (ref_bind<const T*> r)
+ : ref (r.ref), prec (r.prec), scale (r.scale) {}
+
+ const void*
+ ptr () const {return ref;}
+
+ type ref;
+
+ unsigned short prec;
+ unsigned short scale;
+ };
+
+ template <typename T, database_type_id ID>
+ struct val_bind_typed: val_bind<T>
+ {
+ explicit
+ val_bind_typed (typename val_bind<T>::type v,
+ unsigned short p = 0,
+ unsigned short s = 0xFFFF): val_bind<T> (v, p, s) {}
+ };
+
+ template <typename T, database_type_id ID>
+ struct ref_bind_typed: ref_bind<T>
+ {
+ explicit
+ ref_bind_typed (typename ref_bind<T>::type r,
+ unsigned short p = 0,
+ unsigned short s = 0xFFFF): ref_bind<T> (r, p, s) {}
+ };
+
+ struct LIBODB_MSSQL_EXPORT query_param: details::shared_base
+ {
+ typedef mssql::bind bind_type;
+
+ virtual
+ ~query_param ();
+
+ bool
+ reference () const
+ {
+ return value_ != 0;
+ }
+
+ virtual bool
+ init () = 0;
+
+ virtual void
+ bind (bind_type*) = 0;
+
+ protected:
+ query_param (const void* value)
+ : value_ (value)
+ {
+ }
+
+ protected:
+ const void* value_;
+ };
+
+ //
+ //
+ template <typename T, database_type_id ID>
+ struct query_column;
+
+ class LIBODB_MSSQL_EXPORT query_base
+ {
+ public:
+ struct clause_part
+ {
+ enum kind_type
+ {
+ kind_column,
+ kind_param,
+ kind_native,
+ kind_bool
+ };
+
+ clause_part (kind_type k): kind (k), bool_part (false) {}
+ clause_part (kind_type k, const std::string& p)
+ : kind (k), part (p), bool_part (false) {}
+ clause_part (bool p): kind (kind_bool), bool_part (p) {}
+
+ kind_type kind;
+ std::string part; // If kind is param, then part is conversion expr.
+ bool bool_part;
+ };
+
+ query_base ()
+ : binding_ (0, 0)
+ {
+ }
+
+ // True or false literal.
+ //
+ explicit
+ query_base (bool v)
+ : binding_ (0, 0)
+ {
+ append (v);
+ }
+
+ explicit
+ query_base (const char* native)
+ : binding_ (0, 0)
+ {
+ clause_.push_back (clause_part (clause_part::kind_native, native));
+ }
+
+ explicit
+ query_base (const std::string& native)
+ : binding_ (0, 0)
+ {
+ clause_.push_back (clause_part (clause_part::kind_native, native));
+ }
+
+ query_base (const char* table, const char* column)
+ : binding_ (0, 0)
+ {
+ append (table, column);
+ }
+
+ template <typename T>
+ explicit
+ query_base (val_bind<T> v)
+ : binding_ (0, 0)
+ {
+ *this += v;
+ }
+
+ template <typename T, database_type_id ID>
+ explicit
+ query_base (val_bind_typed<T, ID> v)
+ : binding_ (0, 0)
+ {
+ *this += v;
+ }
+
+ template <typename T>
+ explicit
+ query_base (ref_bind<T> r)
+ : binding_ (0, 0)
+ {
+ *this += r;
+ }
+
+ template <typename T, database_type_id ID>
+ explicit
+ query_base (ref_bind_typed<T, ID> r)
+ : binding_ (0, 0)
+ {
+ *this += r;
+ }
+
+ template <database_type_id ID>
+ query_base (const query_column<bool, ID>&);
+
+ // Translate common query representation to SQL Server native. Defined
+ // in query-dynamic.cxx
+ //
+ query_base (const odb::query_base&);
+
+ // Copy c-tor and assignment.
+ //
+ query_base (const query_base&);
+
+ query_base&
+ operator= (const query_base&);
+
+ public:
+ std::string
+ clause () const;
+
+ const char*
+ clause_prefix () const;
+
+ // Initialize the by-reference parameters from bound variables.
+ //
+ void
+ init_parameters () const;
+
+ binding&
+ parameters_binding () const;
+
+ public:
+ bool
+ empty () const
+ {
+ return clause_.empty ();
+ }
+
+ static const query_base true_expr;
+
+ bool
+ const_true () const
+ {
+ return clause_.size () == 1 &&
+ clause_.front ().kind == clause_part::kind_bool &&
+ clause_.front ().bool_part;
+ }
+
+ void
+ optimize ();
+
+ public:
+ template <typename T>
+ static val_bind<T>
+ _val (const T& x, unsigned short prec = 0, unsigned short scale = 0xFFFF)
+ {
+ return val_bind<T> (x, prec, scale);
+ }
+
+ template <database_type_id ID, typename T>
+ static val_bind_typed<T, ID>
+ _val (const T& x, unsigned short prec = 0, unsigned short scale = 0xFFFF)
+ {
+ return val_bind_typed<T, ID> (x, prec, scale);
+ }
+
+ template <typename T>
+ static ref_bind<T>
+ _ref (const T& x, unsigned short prec = 0, unsigned short scale = 0xFFFF)
+ {
+ return ref_bind<T> (x, prec, scale);
+ }
+
+ template <database_type_id ID, typename T>
+ static ref_bind_typed<T, ID>
+ _ref (const T& x, unsigned short prec = 0, unsigned short scale = 0xFFFF)
+ {
+ return ref_bind_typed<T, ID> (x, prec, scale);
+ }
+
+ // Some compilers (notably VC++), when deducing const T& from const
+ // array do not strip const from the array type. As a result, in the
+ // above signatures we get, for example, T = const char[4] instead
+ // of T = char[4], which is what we want. So to "fix" such compilers,
+ // we will have to provide the following specializations of the above
+ // functions.
+ //
+ template <typename T, std::size_t N>
+ static val_bind<T[N]>
+ _val (const T (&x) [N], unsigned short p = 0, unsigned short s = 0xFFFF)
+ {
+ return val_bind<T[N]> (x, p, s);
+ }
+
+ template <database_type_id ID, typename T, std::size_t N>
+ static val_bind_typed<T[N], ID>
+ _val (const T (&x) [N], unsigned short p = 0, unsigned short s = 0xFFFF)
+ {
+ return val_bind_typed<T[N], ID> (x, p, s);
+ }
+
+ template <typename T, std::size_t N>
+ static ref_bind<T[N]>
+ _ref (const T (&x) [N], unsigned short p = 0, unsigned short s = 0xFFFF)
+ {
+ return ref_bind<T[N]> (x, p, s);
+ }
+
+ template <database_type_id ID, typename T, std::size_t N>
+ static ref_bind_typed<T[N], ID>
+ _ref (const T (&x) [N], unsigned short p = 0, unsigned short s = 0xFFFF)
+ {
+ return ref_bind_typed<T[N], ID> (x, p, s);
+ }
+
+ public:
+ query_base&
+ operator+= (const query_base&);
+
+ query_base&
+ operator+= (const std::string& q)
+ {
+ append (q);
+ return *this;
+ }
+
+ template <typename T>
+ query_base&
+ operator+= (val_bind<T> v)
+ {
+ append<T, type_traits<T>::db_type_id> (
+ v, details::conversion<T>::to ());
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (val_bind_typed<T, ID> v)
+ {
+ // We are not using default type_traits so no default conversion
+ // either.
+ //
+ append<T, ID> (v, 0);
+ return *this;
+ }
+
+ template <typename T>
+ query_base&
+ operator+= (ref_bind<T> r)
+ {
+ append<T, type_traits<T>::db_type_id> (
+ r, details::conversion<T>::to ());
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (ref_bind_typed<T, ID> r)
+ {
+ // We are not using default type_traits so no default conversion
+ // either.
+ //
+ append<T, ID> (r, 0);
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (const query_column<T, ID>&);
+
+ // Implementation details.
+ //
+ public:
+ template <typename T, database_type_id ID>
+ void
+ append (val_bind<T>, const char* conv);
+
+ template <typename T, database_type_id ID>
+ void
+ append (ref_bind<T>, const char* conv);
+
+ void
+ append (details::shared_ptr<query_param>, const char* conv);
+
+ void
+ append (bool v)
+ {
+ clause_.push_back (clause_part (v));
+ }
+
+ void
+ append (const std::string& native);
+
+ void
+ append (const char* native) // Clashes with append(bool).
+ {
+ append (std::string (native));
+ }
+
+ void
+ append (const char* table, const char* column);
+
+ private:
+ typedef std::vector<clause_part> clause_type;
+ typedef std::vector<details::shared_ptr<query_param> > parameters_type;
+
+ clause_type clause_;
+ parameters_type parameters_;
+ mutable std::vector<bind> bind_;
+ mutable binding binding_;
+ };
+
+ inline query_base
+ operator+ (const query_base& x, const query_base& y)
+ {
+ query_base r (x);
+ r += y;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, val_bind<T> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (val_bind<T> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, val_bind_typed<T, ID> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (val_bind_typed<T, ID> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, ref_bind<T> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (ref_bind<T> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, ref_bind_typed<T, ID> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (ref_bind_typed<T, ID> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const query_base& q, const std::string& s)
+ {
+ query_base r (q);
+ r += s;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const std::string& s, const query_base& q)
+ {
+ query_base r (s);
+ r += q;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, val_bind<T> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (val_bind<T> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, val_bind_typed<T, ID> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (val_bind_typed<T, ID> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, ref_bind<T> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (ref_bind<T> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, ref_bind_typed<T, ID> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (ref_bind_typed<T, ID> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ LIBODB_MSSQL_EXPORT query_base
+ operator&& (const query_base& x, const query_base& y);
+
+ LIBODB_MSSQL_EXPORT query_base
+ operator|| (const query_base& x, const query_base& y);
+
+ LIBODB_MSSQL_EXPORT query_base
+ operator! (const query_base& x);
+
+ // query_column
+ //
+ struct LIBODB_MSSQL_EXPORT query_column_base
+ {
+ // Note that we keep shallow copies of the table, column, and conversion
+ // expression. The latter can be NULL.
+ //
+ query_column_base (const char* table,
+ const char* column,
+ const char* conv,
+ unsigned short prec,
+ unsigned short scale)
+ : table_ (table), column_ (column), conversion_ (conv),
+ prec_ (prec), scale_ (scale)
+ {
+ }
+
+ const char*
+ table () const
+ {
+ return table_;
+ }
+
+ const char*
+ column () const
+ {
+ return column_;
+ }
+
+ // Can be NULL.
+ //
+ const char*
+ conversion () const
+ {
+ return conversion_;
+ }
+
+ unsigned short
+ prec () const
+ {
+ return prec_;
+ }
+
+ unsigned short
+ scale () const
+ {
+ return scale_;
+ }
+
+ protected:
+ const char* table_;
+ const char* column_;
+ const char* conversion_;
+
+ unsigned short prec_;
+ unsigned short scale_;
+ };
+
+ template <typename T, database_type_id ID>
+ struct query_column: query_column_base
+ {
+ typedef typename decay_traits<T>::type decayed_type;
+
+ // Note that we keep shalow copies of the table, column, and conversion
+ // expression. The latter can be NULL.
+ //
+ query_column (const char* table,
+ const char* column,
+ const char* conv,
+ unsigned short prec = 0,
+ unsigned short scale = 0xFFFF)
+ : query_column_base (table, column, conv, prec, scale) {}
+
+ // Implementation is in query-dynamic.ixx.
+ //
+ query_column (odb::query_column<T>&,
+ const char* table,
+ const char* column,
+ const char* conv,
+ unsigned short prec = 0,
+ unsigned short scale = 0xFFFF);
+
+ // is_null, is_not_null
+ //
+ public:
+ query_base
+ is_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NULL";
+ return q;
+ }
+
+ query_base
+ is_not_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NOT NULL";
+ return q;
+ }
+
+ // in
+ //
+ public:
+ query_base
+ in (decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type, decayed_type,
+ decayed_type) const;
+
+ template <typename I>
+ query_base
+ in_range (I begin, I end) const;
+
+ // like
+ //
+ public:
+ query_base
+ like (decayed_type pattern) const
+ {
+ return like (val_bind<T> (pattern));
+ }
+
+ query_base
+ like (val_bind<T> pattern) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern) const
+ {
+ return like (val_bind<T> (decayed_type (pattern.val)));
+ }
+
+ query_base
+ like (ref_bind<T> pattern) const;
+
+ query_base
+ like (decayed_type pattern, decayed_type escape) const
+ {
+ return like (val_bind<T> (pattern), escape);
+ }
+
+ query_base
+ like (val_bind<T> pattern, decayed_type escape) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern, decayed_type escape) const
+ {
+ return like (val_bind<T> (decayed_type (pattern.val)), escape);
+ }
+
+ query_base
+ like (ref_bind<T> pattern, decayed_type escape) const;
+
+ // =
+ //
+ public:
+ query_base
+ equal (decayed_type v) const
+ {
+ return equal (val_bind<T> (v));
+ }
+
+ query_base
+ equal (val_bind<T> v) const
+ {
+ v.prec = prec_;
+ v.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ equal (val_bind<T2> v) const
+ {
+ return equal (val_bind<T> (decayed_type (v.val)));;
+ }
+
+ query_base
+ equal (ref_bind<T> r) const
+ {
+ r.prec = prec_;
+ r.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator== (const query_column& c, decayed_type v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (decayed_type v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, val_bind<T> v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (val_bind<T> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (const query_column& c, val_bind<T2> v)
+ {
+ return c.equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (val_bind<T2> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, ref_bind<T> r)
+ {
+ return c.equal (r);
+ }
+
+ friend query_base
+ operator== (ref_bind<T> r, const query_column& c)
+ {
+ return c.equal (r);
+ }
+
+ // !=
+ //
+ public:
+ query_base
+ unequal (decayed_type v) const
+ {
+ return unequal (val_bind<T> (v));
+ }
+
+ query_base
+ unequal (val_bind<T> v) const
+ {
+ v.prec = prec_;
+ v.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "!=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ unequal (val_bind<T2> v) const
+ {
+ return unequal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ unequal (ref_bind<T> r) const
+ {
+ r.prec = prec_;
+ r.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "!=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator!= (const query_column& c, decayed_type v)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (decayed_type v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (const query_column& c, val_bind<T> v)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (val_bind<T> v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (const query_column& c, val_bind<T2> v)
+ {
+ return c.unequal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (val_bind<T2> v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (const query_column& c, ref_bind<T> r)
+ {
+ return c.unequal (r);
+ }
+
+ friend query_base
+ operator!= (ref_bind<T> r, const query_column& c)
+ {
+ return c.unequal (r);
+ }
+
+ // <
+ //
+ public:
+ query_base
+ less (decayed_type v) const
+ {
+ return less (val_bind<T> (v));
+ }
+
+ query_base
+ less (val_bind<T> v) const
+ {
+ v.prec = prec_;
+ v.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "<";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ less (val_bind<T2> v) const
+ {
+ return less (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ less (ref_bind<T> r) const
+ {
+ r.prec = prec_;
+ r.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "<";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator< (const query_column& c, decayed_type v)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator< (decayed_type v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator< (const query_column& c, val_bind<T> v)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator< (val_bind<T> v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (const query_column& c, val_bind<T2> v)
+ {
+ return c.less (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator< (const query_column& c, ref_bind<T> r)
+ {
+ return c.less (r);
+ }
+
+ friend query_base
+ operator< (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater (r);
+ }
+
+ // >
+ //
+ public:
+ query_base
+ greater (decayed_type v) const
+ {
+ return greater (val_bind<T> (v));
+ }
+
+ query_base
+ greater (val_bind<T> v) const
+ {
+ v.prec = prec_;
+ v.scale = scale_;
+
+ query_base q (table_, column_);
+ q += ">";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ greater (val_bind<T2> v) const
+ {
+ return greater (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ greater (ref_bind<T> r) const
+ {
+ r.prec = prec_;
+ r.scale = scale_;
+
+ query_base q (table_, column_);
+ q += ">";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator> (const query_column& c, decayed_type v)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator> (decayed_type v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator> (const query_column& c, val_bind<T> v)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator> (val_bind<T> v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (val_bind<T2> v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator> (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater (r);
+ }
+
+ friend query_base
+ operator> (ref_bind<T> r, const query_column& c)
+ {
+ return c.less (r);
+ }
+
+ // <=
+ //
+ public:
+ query_base
+ less_equal (decayed_type v) const
+ {
+ return less_equal (val_bind<T> (v));
+ }
+
+ query_base
+ less_equal (val_bind<T> v) const
+ {
+ v.prec = prec_;
+ v.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "<=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ less_equal (val_bind<T2> v) const
+ {
+ return less_equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ less_equal (ref_bind<T> r) const
+ {
+ r.prec = prec_;
+ r.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "<=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator<= (const query_column& c, decayed_type v)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator<= (decayed_type v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator<= (const query_column& c, val_bind<T> v)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator<= (val_bind<T> v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (const query_column& c, val_bind<T2> v)
+ {
+ return c.less_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator<= (const query_column& c, ref_bind<T> r)
+ {
+ return c.less_equal (r);
+ }
+
+ friend query_base
+ operator<= (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater_equal (r);
+ }
+
+ // >=
+ //
+ public:
+ query_base
+ greater_equal (decayed_type v) const
+ {
+ return greater_equal (val_bind<T> (v));
+ }
+
+ query_base
+ greater_equal (val_bind<T> v) const
+ {
+ v.prec = prec_;
+ v.scale = scale_;
+
+ query_base q (table_, column_);
+ q += ">=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ greater_equal (val_bind<T2> v) const
+ {
+ return greater_equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ greater_equal (ref_bind<T> r) const
+ {
+ r.prec = prec_;
+ r.scale = scale_;
+
+ query_base q (table_, column_);
+ q += ">=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator>= (const query_column& c, decayed_type v)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator>= (decayed_type v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator>= (const query_column& c, val_bind<T> v)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator>= (val_bind<T> v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (val_bind<T2> v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator>= (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater_equal (r);
+ }
+
+ friend query_base
+ operator>= (ref_bind<T> r, const query_column& c)
+ {
+ return c.less_equal (r);
+ }
+
+ // Column comparison.
+ //
+ public:
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator== (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () ==
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator!= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () !=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "!=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator< (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "<";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator> (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += ">";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator<= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "<=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator>= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += ">=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+ };
+
+ // Provide operator+() for using columns to construct native
+ // query fragments (e.g., ORDER BY).
+ //
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_column<T, ID>& c, const std::string& s)
+ {
+ query_base q (c.table (), c.column ());
+ q += s;
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, const query_column<T, ID>& c)
+ {
+ query_base q (s);
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_column<T, ID>& c, const query_base& q)
+ {
+ query_base r (c.table (), c.column ());
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, const query_column<T, ID>& c)
+ {
+ query_base r (q);
+ r.append (c.table (), c.column ());
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base& query_base::
+ operator+= (const query_column<T, ID>& c)
+ {
+ append (c.table (), c.column ());
+ return *this;
+ }
+
+ //
+ //
+ template <typename T, database_type_id>
+ struct query_param_impl;
+
+ // id_bit.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_bit>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::bit;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_bit>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ unsigned char image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_tinyint.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_tinyint>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::tinyint;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_tinyint>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ unsigned char image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_smallint.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_smallint>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::smallint;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_smallint>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ short image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_int.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_int>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::int_;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_int>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ int image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_bigint.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_bigint>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::bigint;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_bigint>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ long long image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_decimal.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_decimal>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ prec_ (r.prec != 0 ? r.prec : 18), // Default is 18.
+ scale_ (r.scale != 0xFFFF ? r.scale : 0) // Default is 0.
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ prec_ (v.prec != 0 ? v.prec : 18), // Default is 18.
+ scale_ (v.scale)
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::decimal;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ // Encode precision (p) and scale (s) as (p * 100 + s).
+ //
+ b->capacity = static_cast<SQLLEN> (prec_ * 100 + scale_);
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_decimal>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ unsigned short prec_;
+ unsigned short scale_;
+
+ decimal image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_smallmoney.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_smallmoney>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::smallmoney;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_smallmoney>::set_image (image_, is_null, v);
+ size_ind_ = 4;
+ }
+
+ private:
+ smallmoney image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_money.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_money>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::money;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_money>::set_image (image_, is_null, v);
+ size_ind_ = 8;
+ }
+
+ private:
+ money image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_float4.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_float4>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ prec_ (r.prec != 0 ? r.prec : 24)
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ prec_ (v.prec != 0 ? v.prec : 24)
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::float4;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ b->capacity = static_cast<SQLLEN> (prec_);
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_float4>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ unsigned short prec_;
+ float image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_float8.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_float8>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ prec_ (r.prec != 0 ? r.prec : 53)
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ prec_ (v.prec != 0 ? v.prec : 53)
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::float8;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ b->capacity = static_cast<SQLLEN> (prec_);
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_float8>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ unsigned short prec_;
+ double image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_string.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_string>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ // Default to short data max (1024).
+ buf_ (r.prec != 0 ? r.prec : 1024)
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ // Default to short data max (1024).
+ buf_ (v.prec != 0 ? v.prec : 1024)
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::string;
+ b->buffer = buf_.data ();
+ b->size_ind = &size_ind_;
+ // Extra byte for the null terminator (convention).
+ //
+ b->capacity = static_cast<SQLLEN> (buf_.capacity () + 1);
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0);
+ value_traits<T, id_string>::set_image (
+ buf_.data (), buf_.capacity (), size, is_null, v);
+ size_ind_ = static_cast<SQLLEN> (size);
+ }
+
+ private:
+ details::buffer buf_;
+ SQLLEN size_ind_;
+ };
+
+ // id_nstring.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_nstring>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ // Precision is in 2-byte chars. Default to short data max (1024).
+ buf_ (r.prec != 0 ? r.prec * 2 : 1024)
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ // Precision is in 2-byte chars. Default to short data max (1024).
+ buf_ (v.prec != 0 ? v.prec * 2 : 1024)
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::nstring;
+ b->buffer = buf_.data ();
+ b->size_ind = &size_ind_;
+ // Extra two bytes for the null terminator (convention).
+ //
+ b->capacity = static_cast<SQLLEN> (buf_.capacity () + 2);
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0);
+ value_traits<T, id_nstring>::set_image (
+ reinterpret_cast<ucs2_char*> (buf_.data ()),
+ buf_.capacity () / 2,
+ size,
+ is_null,
+ v);
+ size_ind_ = static_cast<SQLLEN> (size * 2);
+ }
+
+ private:
+ details::buffer buf_;
+ SQLLEN size_ind_;
+ };
+
+ // id_binary.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_binary>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ // Default to short data max (1024).
+ buf_ (r.prec != 0 ? r.prec : 1024)
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ // Default to short data max (1024).
+ buf_ (v.prec != 0 ? v.prec : 1024)
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::binary;
+ b->buffer = buf_.data ();
+ b->size_ind = &size_ind_;
+ b->capacity = static_cast<SQLLEN> (buf_.capacity ());
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0);
+ value_traits<T, id_binary>::set_image (
+ buf_.data (), buf_.capacity (), size, is_null, v);
+ size_ind_ = static_cast<SQLLEN> (size);
+ }
+
+ private:
+ details::buffer buf_;
+ SQLLEN size_ind_;
+ };
+
+ // long_query_param_impl
+ //
+ // For long data we cannot assume that the by-value parameter will
+ // still be alive when we execute the query (and when the callback
+ // will be called to provide the data). So we have to make a private
+ // copy of the data and use that when the time comes.
+ //
+ class LIBODB_MSSQL_EXPORT long_query_param_impl
+ {
+ protected:
+ long_query_param_impl (): buf_ (0) {}
+
+ void
+ copy ();
+
+ protected:
+ details::buffer buf_;
+ std::size_t size_;
+ long_callback callback_;
+ };
+
+ // id_long_string.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_long_string>: query_param,
+ long_query_param_impl
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()), prec_ (r.prec)
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0), prec_ (v.prec)
+ {
+ init (v.val);
+ copy ();
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ // If this is a by-value parameter then we already have the
+ // buffer containing all the data. So instead of using the
+ // callback mechanism, bind the buffer directly using the
+ // short data approach. SQLLEN (int on 32-bit platforms)
+ // can represent maximum size of 2^31 bytes which happens
+ // to be greater than the maximum size of VARCHAR(max) or
+ // TEXT (both 2^31-1 bytes).
+ //
+ if (reference ())
+ {
+ b->type = bind::long_string;
+ b->buffer = &this->long_query_param_impl::callback_;
+ b->size_ind = &size_ind_;
+ b->capacity = static_cast<SQLLEN> (prec_);
+ size_ind_ = SQL_DATA_AT_EXEC;
+ }
+ else
+ {
+ b->type = bind::string;
+ b->buffer = buf_.data ();
+ b->size_ind = &size_ind_;
+ // Extra byte for the null terminator (convention).
+ //
+ b->capacity = static_cast<SQLLEN> (prec_ != 0 ? prec_ + 1 : 0);
+ size_ind_ = static_cast<SQLLEN> (size_);
+ }
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_long_string>::set_image (
+ long_query_param_impl::callback_.callback.param,
+ long_query_param_impl::callback_.context.param,
+ is_null,
+ v);
+ }
+
+ private:
+ unsigned short prec_;
+ SQLLEN size_ind_;
+ };
+
+ // id_long_nstring.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_long_nstring>: query_param,
+ long_query_param_impl
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()), prec_ (r.prec)
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0), prec_ (v.prec)
+ {
+ init (v.val);
+ copy ();
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ // If this is a by-value parameter then we already have the
+ // buffer containing all the data. So instead of using the
+ // callback mechanism, bind the buffer directly using the
+ // short data approach. SQLLEN (int on 32-bit platforms)
+ // can represent maximum size of 2^31 bytes which happens
+ // to be greater than the maximum size of NVARCHAR(max) or
+ // NTEXT (both 2^31-1 bytes).
+ //
+ if (reference ())
+ {
+ b->type = bind::long_nstring;
+ b->buffer = &this->long_query_param_impl::callback_;
+ b->size_ind = &size_ind_;
+ b->capacity = static_cast<SQLLEN> (prec_ * 2); // In bytes.
+ size_ind_ = SQL_DATA_AT_EXEC;
+ }
+ else
+ {
+ b->type = bind::nstring;
+ b->buffer = buf_.data ();
+ b->size_ind = &size_ind_;
+ // In bytes, extra character for the null terminator (convention).
+ //
+ b->capacity = static_cast<SQLLEN> (prec_ != 0 ? (prec_ + 1) * 2 : 0);
+ size_ind_ = static_cast<SQLLEN> (size_);
+ }
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_long_nstring>::set_image (
+ long_query_param_impl::callback_.callback.param,
+ long_query_param_impl::callback_.context.param,
+ is_null,
+ v);
+ }
+
+ private:
+ unsigned short prec_;
+ SQLLEN size_ind_;
+ };
+
+ // id_long_binary.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_long_binary>: query_param,
+ long_query_param_impl
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()), prec_ (r.prec)
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0), prec_ (v.prec)
+ {
+ init (v.val);
+ copy ();
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ // If this is a by-value parameter then we already have the
+ // buffer containing all the data. So instead of using the
+ // callback mechanism, bind the buffer directly using the
+ // short data approach. SQLLEN (int on 32-bit platforms)
+ // can represent maximum size of 2^31 bytes which happens
+ // to be greater than the maximum size of VARBINARY(max)
+ // or IMAGE (both 2^31-1 bytes).
+ //
+ if (reference ())
+ {
+ b->type = bind::long_binary;
+ b->buffer = &this->long_query_param_impl::callback_;
+ b->size_ind = &size_ind_;
+ size_ind_ = SQL_DATA_AT_EXEC;
+ }
+ else
+ {
+ b->type = bind::binary;
+ b->buffer = buf_.data ();
+ b->size_ind = &size_ind_;
+ size_ind_ = static_cast<SQLLEN> (size_);
+ }
+
+ b->capacity = static_cast<SQLLEN> (prec_);
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_long_binary>::set_image (
+ long_query_param_impl::callback_.callback.param,
+ long_query_param_impl::callback_.context.param,
+ is_null,
+ v);
+ }
+
+ private:
+ unsigned short prec_;
+ SQLLEN size_ind_;
+ };
+
+ // id_date.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_date>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::date;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_date>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ date image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_time.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_time>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ scale_ (r.scale != 0xFFFF ? r.scale : 7) // Default is 7.
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ scale_ (v.scale != 0xFFFF ? v.scale : 7) // Default is 7.
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::time;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ b->capacity = static_cast<SQLLEN> (scale_);
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_time>::set_image (image_, scale_, is_null, v);
+ size_ind_ = static_cast<SQLLEN> (sizeof (image_));
+ }
+
+ private:
+ unsigned short scale_;
+ time image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_datetime.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_datetime>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ scale_ (r.scale != 0xFFFF ? r.scale : 7) // Default to datetime2/7.
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ scale_ (v.scale != 0xFFFF ? v.scale : 7) // Default to datetime2/7.
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::datetime;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ b->capacity = static_cast<SQLLEN> (scale_);
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_datetime>::set_image (image_, scale_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ unsigned short scale_;
+ datetime image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_datetimeoffset.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_datetimeoffset>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ scale_ (r.scale != 0xFFFF ? r.scale : 7) // Default is 7.
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ scale_ (v.scale != 0xFFFF ? v.scale : 7) // Default is 7.
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::datetimeoffset;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ b->capacity = static_cast<SQLLEN> (scale_);
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_datetimeoffset>::set_image (
+ image_, scale_, is_null, v);
+ size_ind_ = static_cast<SQLLEN> (sizeof (image_));
+ }
+
+ private:
+ unsigned short scale_;
+ datetimeoffset image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_uniqueidentifier.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_uniqueidentifier>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::uniqueidentifier;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_uniqueidentifier>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ uniqueidentifier image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_rowversion.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_rowversion>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::rowversion;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_rowversion>::set_image (image_, is_null, v);
+ size_ind_ = 8;
+ }
+
+ private:
+ unsigned char image_[8];
+ SQLLEN size_ind_;
+ };
+ }
+}
+
+// odb::mssql::query and odb::query specialization for SQL Server.
+//
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ class query: public query_base,
+ public query_selector<T, id_mssql>::columns_type
+ {
+ public:
+ // We don't define any typedefs here since they may clash with
+ // column names defined by our base type.
+ //
+
+ query ()
+ {
+ }
+
+ explicit
+ query (bool v)
+ : query_base (v)
+ {
+ }
+
+ explicit
+ query (const char* q)
+ : query_base (q)
+ {
+ }
+
+ explicit
+ query (const std::string& q)
+ : query_base (q)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (val_bind<T2> v)
+ : query_base (v)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (ref_bind<T2> r)
+ : query_base (r)
+ {
+ }
+
+ query (const query_base& q)
+ : query_base (q)
+ {
+ }
+
+ template <database_type_id ID>
+ query (const query_column<bool, ID>& qc)
+ : query_base (qc)
+ {
+ }
+
+ query (const odb::query_base& q)
+ : query_base (q)
+ {
+ }
+ };
+
+ namespace core
+ {
+ using mssql::query;
+ }
+ }
+
+ // Derive odb::query from odb::mssql::query so that it can be
+ // implicitly converted in mssql::database::query() calls.
+ //
+ template <typename T>
+ class query<T, mssql::query_base>: public mssql::query<T>
+ {
+ public:
+ // We don't define any typedefs here since they may clash with
+ // column names defined by our base type.
+ //
+
+ query ()
+ {
+ }
+
+ explicit
+ query (bool v)
+ : mssql::query<T> (v)
+ {
+ }
+
+ explicit
+ query (const char* q)
+ : mssql::query<T> (q)
+ {
+ }
+
+ explicit
+ query (const std::string& q)
+ : mssql::query<T> (q)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (mssql::val_bind<T2> v)
+ : mssql::query<T> (v)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (mssql::ref_bind<T2> r)
+ : mssql::query<T> (r)
+ {
+ }
+
+ query (const mssql::query_base& q)
+ : mssql::query<T> (q)
+ {
+ }
+
+ template <mssql::database_type_id ID>
+ query (const mssql::query_column<bool, ID>& qc)
+ : mssql::query<T> (qc)
+ {
+ }
+ };
+}
+
+#include <odb/mssql/query.ixx>
+#include <odb/mssql/query.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_QUERY_HXX
diff --git a/libodb-mssql/odb/mssql/query.ixx b/libodb-mssql/odb/mssql/query.ixx
new file mode 100644
index 0000000..0467667
--- /dev/null
+++ b/libodb-mssql/odb/mssql/query.ixx
@@ -0,0 +1,34 @@
+// file : odb/mssql/query.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mssql
+ {
+ inline binding& query_base::
+ parameters_binding () const
+ {
+ return binding_;
+ }
+
+ template <typename T, database_type_id ID>
+ inline void query_base::
+ append (val_bind<T> v, const char* conv)
+ {
+ append (
+ details::shared_ptr<query_param> (
+ new (details::shared) query_param_impl<T, ID> (v)),
+ conv);
+ }
+
+ template <typename T, database_type_id ID>
+ inline void query_base::
+ append (ref_bind<T> r, const char* conv)
+ {
+ append (
+ details::shared_ptr<query_param> (
+ new (details::shared) query_param_impl<T, ID> (r)),
+ conv);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/query.txx b/libodb-mssql/odb/mssql/query.txx
new file mode 100644
index 0000000..d01747c
--- /dev/null
+++ b/libodb-mssql/odb/mssql/query.txx
@@ -0,0 +1,169 @@
+// file : odb/mssql/query.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // query_base
+ //
+
+ template <database_type_id ID>
+ query_base::
+ query_base (const query_column<bool, ID>& c)
+ : binding_ (0, 0)
+ {
+ // Cannot use IS TRUE here since database type can be a non-
+ // integral type.
+ //
+ append (c.table (), c.column ());
+ append ("=");
+ append<bool, ID> (val_bind<bool> (true, c.prec (), c.scale ()),
+ c.conversion ());
+ }
+
+ //
+ // query_column
+ //
+
+ // in
+ //
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2, prec_, scale_), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3, prec_, scale_), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3,
+ decayed_type v4) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v4, prec_, scale_), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3, decayed_type v4,
+ decayed_type v5) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v4, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v5, prec_, scale_), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ template <typename I>
+ query_base query_column<T, ID>::
+ in_range (I begin, I end) const
+ {
+ if (begin != end)
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+
+ for (I i (begin); i != end; ++i)
+ {
+ if (i != begin)
+ q += ",";
+
+ q.append<T, ID> (val_bind<T> (*i, prec_, scale_), conversion_);
+ }
+
+ q += ")";
+ return q;
+ }
+ else
+ return query_base (false);
+ }
+
+ // like
+ //
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (val_bind<T> p) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (ref_bind<T> p) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (val_bind<T> p, decayed_type e) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ q += "ESCAPE";
+ q.append<T, ID> (val_bind<T> (e), conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (ref_bind<T> p, decayed_type e) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ q += "ESCAPE";
+ q.append<T, ID> (val_bind<T> (e), conversion_);
+ return q;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/section-statements.hxx b/libodb-mssql/odb/mssql/section-statements.hxx
new file mode 100644
index 0000000..0f3500d
--- /dev/null
+++ b/libodb-mssql/odb/mssql/section-statements.hxx
@@ -0,0 +1,201 @@
+// file : odb/mssql/section-statements.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_SECTION_STATEMENTS_HXX
+#define ODB_MSSQL_SECTION_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/mssql-types.hxx>
+#include <odb/mssql/binding.hxx>
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class connection;
+
+ // Template argument is the section traits type.
+ //
+ template <typename T, typename ST>
+ class section_statements
+ {
+ public:
+ typedef ST traits;
+
+ typedef typename traits::image_type image_type;
+ typedef typename traits::id_image_type id_image_type;
+
+ typedef mssql::select_statement select_statement_type;
+ typedef mssql::update_statement update_statement_type;
+
+ typedef mssql::connection connection_type;
+
+ section_statements (connection_type&,
+ image_type&, id_image_type&,
+ binding& id, binding& idv);
+
+ connection_type&
+ connection () {return conn_;}
+
+ const schema_version_migration&
+ version_migration (const char* name = "") const
+ {
+ if (svm_ == 0)
+ svm_ = &conn_.database ().schema_version_migration (name);
+
+ return *svm_;
+ }
+
+ image_type&
+ image () {return image_;}
+
+ id_image_type&
+ id_image () {return id_image_;}
+
+ const binding&
+ id_binding () {return id_binding_;}
+
+ // Id and optimistic concurrency version (if any).
+ //
+ const binding&
+ idv_binding () {return idv_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_binding_version () const { return update_id_binding_version_;}
+
+ void
+ update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ //
+ // Statements.
+ //
+
+ select_statement_type&
+ select_statement ()
+ {
+ if (select_ == 0)
+ select_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ traits::select_statement,
+ traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ id_binding_,
+ select_image_binding_,
+ false));
+
+ return *select_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ traits::update_statement,
+ traits::versioned, // Process if versioned.
+ update_image_binding_,
+ (traits::rowversion ? &idv_binding_ : 0),
+ false));
+
+ return *update_;
+ }
+
+ public:
+ static const std::size_t id_column_count = traits::id_column_count;
+ static const std::size_t managed_optimistic_load_column_count =
+ traits::managed_optimistic_load_column_count;
+ static const std::size_t managed_optimistic_update_column_count =
+ traits::managed_optimistic_update_column_count;
+ static const std::size_t select_column_count = traits::load_column_count;
+ static const std::size_t update_column_count =
+ traits::update_column_count;
+
+ private:
+ section_statements (const section_statements&);
+ section_statements& operator= (const section_statements&);
+
+ protected:
+ connection_type& conn_;
+ mutable const schema_version_migration* svm_;
+
+ // These come from object_statements.
+ //
+ image_type& image_;
+ id_image_type& id_image_;
+ binding& id_binding_;
+ binding& idv_binding_;
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+
+ static const std::size_t select_bind_count =
+ select_column_count != 0 || managed_optimistic_load_column_count != 0
+ ? select_column_count + managed_optimistic_load_column_count
+ : 1;
+
+ binding select_image_binding_;
+ bind select_image_bind_[select_bind_count];
+
+ // Update binding.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_binding_version_;
+
+ static const std::size_t update_bind_count =
+ update_column_count != 0 || managed_optimistic_update_column_count != 0
+ ? update_column_count + id_column_count +
+ managed_optimistic_update_column_count
+ : 1;
+
+ binding update_image_binding_;
+ bind update_image_bind_[update_bind_count];
+
+ details::shared_ptr<select_statement_type> select_;
+ details::shared_ptr<update_statement_type> update_;
+ };
+ }
+}
+
+#include <odb/mssql/section-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_SECTION_STATEMENTS_HXX
diff --git a/libodb-mssql/odb/mssql/section-statements.txx b/libodb-mssql/odb/mssql/section-statements.txx
new file mode 100644
index 0000000..7389d82
--- /dev/null
+++ b/libodb-mssql/odb/mssql/section-statements.txx
@@ -0,0 +1,50 @@
+// file : odb/mssql/section-statements.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T, typename ST>
+ section_statements<T, ST>::
+ section_statements (connection_type& conn,
+ image_type& im, id_image_type& idim,
+ binding& id, binding& idv)
+ : conn_ (conn),
+ svm_ (0),
+ image_ (im),
+ id_image_ (idim),
+ id_binding_ (id),
+ idv_binding_ (idv),
+ select_image_binding_ (select_image_bind_,
+ select_column_count +
+ managed_optimistic_load_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count +
+ managed_optimistic_update_column_count)
+ {
+ select_image_version_ = 0;
+ update_image_version_ = 0;
+ update_id_binding_version_ = 0;
+
+ // If we are using optimistic concurrency, then the select statement
+ // will override the version member in the object image.
+ //
+ if (managed_optimistic_load_column_count != 0)
+ {
+ // Getting to the root image in polymorphic case is tricky.
+ //
+ typedef object_traits_impl<T, id_mssql> object_traits;
+
+ select_image_binding_.change_callback =
+ root_image<object_traits, object_traits::polymorphic>::get (
+ image_).change_callback ();
+ }
+
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/simple-object-result.hxx b/libodb-mssql/odb/mssql/simple-object-result.hxx
new file mode 100644
index 0000000..dcf8243
--- /dev/null
+++ b/libodb-mssql/odb/mssql/simple-object-result.hxx
@@ -0,0 +1,89 @@
+// file : odb/mssql/simple-object-result.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_SIMPLE_OBJECT_RESULT_HXX
+#define ODB_MSSQL_SIMPLE_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/simple-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx> // query_base
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ class object_result_impl: public odb::object_result_impl<T>
+ {
+ public:
+ typedef odb::object_result_impl<T> base_type;
+
+ typedef typename base_type::id_type id_type;
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_mssql> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~object_result_impl ();
+
+ object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type&, bool fetch);
+
+ virtual id_type
+ load_id ();
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ typedef mssql::change_callback change_callback_type;
+
+ static void
+ change_callback (void* context);
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ bool can_load_;
+ bool use_copy_;
+ typename object_traits::image_type* image_copy_;
+ };
+ }
+}
+
+#include <odb/mssql/simple-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_SIMPLE_OBJECT_RESULT_HXX
diff --git a/libodb-mssql/odb/mssql/simple-object-result.txx b/libodb-mssql/odb/mssql/simple-object-result.txx
new file mode 100644
index 0000000..3a0e984
--- /dev/null
+++ b/libodb-mssql/odb/mssql/simple-object-result.txx
@@ -0,0 +1,186 @@
+// file : odb/mssql/simple-object-result.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/mssql/exceptions.hxx> // long_data_reload
+#include <odb/mssql/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ object_result_impl<T>::
+ ~object_result_impl ()
+ {
+ invalidate ();
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ invalidate ()
+ {
+ change_callback_type& cc (statements_.image ().change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ delete image_copy_;
+ image_copy_ = 0;
+
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ object_result_impl<T>::
+ object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ use_copy_ (false),
+ image_copy_ (0)
+ {
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ load (object_type& obj, bool)
+ {
+ if (!can_load_)
+ throw long_data_reload ();
+
+ // This is a top-level call so the statements cannot be locked.
+ //
+ assert (!statements_.locked ());
+ typename statements_type::auto_lock l (statements_);
+
+ object_traits::callback (this->db_, obj, callback_event::pre_load);
+
+ typename object_traits::image_type& i (
+ use_copy_ ? *image_copy_ : statements_.image ());
+
+ tc_.init (obj, i, &this->db_);
+
+ // If we are using a copy, make sure the callback information for
+ // long data also comes from the copy.
+ //
+ can_load_ = !statement_->stream_result (
+ use_copy_ ? &statements_.image () : 0,
+ use_copy_ ? image_copy_ : 0);
+
+ // Initialize the id image and binding and load the rest of the object
+ // (containers, etc).
+ //
+ typename object_traits::id_image_type& idi (statements_.id_image ());
+ object_traits::init (idi, object_traits::id (i));
+
+ binding& idb (statements_.id_image_binding ());
+ if (idi.version != statements_.id_image_version () || idb.version == 0)
+ {
+ object_traits::bind (idb.bind, idi);
+ statements_.id_image_version (idi.version);
+ idb.version++;
+ }
+
+ tc_.load_ (statements_, obj, false);
+ statements_.load_delayed (tc_.version ());
+ l.unlock ();
+ object_traits::callback (this->db_, obj, callback_event::post_load);
+ }
+
+ template <typename T>
+ typename object_result_impl<T>::id_type
+ object_result_impl<T>::
+ load_id ()
+ {
+ return object_traits::id (
+ use_copy_ ? *image_copy_ : statements_.image ());
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ next ()
+ {
+ can_load_ = true;
+ this->current (pointer_type ());
+
+ typename object_traits::image_type& im (statements_.image ());
+ change_callback_type& cc (im.change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ use_copy_ = false;
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ }
+
+ if (statement_->fetch () == select_statement::no_data)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ else
+ {
+ cc.callback = &change_callback;
+ cc.context = this;
+ }
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t object_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ change_callback (void* c)
+ {
+ object_result_impl<T>* r (static_cast<object_result_impl<T>*> (c));
+ typename object_traits::image_type& im (r->statements_.image ());
+
+ if (r->image_copy_ == 0)
+ r->image_copy_ = new typename object_traits::image_type (im);
+ else
+ *r->image_copy_ = im;
+
+ im.change_callback_.callback = 0;
+ im.change_callback_.context = 0;
+
+ r->use_copy_ = true;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/simple-object-statements.cxx b/libodb-mssql/odb/mssql/simple-object-statements.cxx
new file mode 100644
index 0000000..5d7e8b6
--- /dev/null
+++ b/libodb-mssql/odb/mssql/simple-object-statements.cxx
@@ -0,0 +1,15 @@
+// file : odb/mssql/simple-object-statements.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ object_statements_base::
+ ~object_statements_base ()
+ {
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/simple-object-statements.hxx b/libodb-mssql/odb/mssql/simple-object-statements.hxx
new file mode 100644
index 0000000..9cece2c
--- /dev/null
+++ b/libodb-mssql/odb/mssql/simple-object-statements.hxx
@@ -0,0 +1,606 @@
+// file : odb/mssql/simple-object-statements.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_SIMPLE_OBJECT_STATEMENTS_HXX
+#define ODB_MSSQL_SIMPLE_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cassert>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/mssql-types.hxx>
+#include <odb/mssql/binding.hxx>
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/statements-base.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ // The extra_statement_cache class is only defined (and used) in
+ // the generated source file. However, object_statements may be
+ // referenced from another source file in the case of a polymorphic
+ // hierarchy (though in this case the extra statement cache is
+ // not used). As a result, we cannot have a by-value member and
+ // instead will store a pointer and lazily allocate the cache if
+ // and when needed. We will also need to store a pointer to the
+ // deleter function which will be initialized during allocation
+ // (at that point we know that the cache class is defined).
+ //
+ template <typename T, typename I, typename ID>
+ struct extra_statement_cache_ptr
+ {
+ typedef I image_type;
+ typedef ID id_image_type;
+ typedef mssql::connection connection_type;
+
+ extra_statement_cache_ptr (): p_ (0) {}
+ ~extra_statement_cache_ptr ()
+ {
+ if (p_ != 0)
+ (this->*deleter_) (0, 0, 0, 0, 0);
+ }
+
+ T&
+ get (connection_type& c,
+ image_type& im, id_image_type& idim,
+ binding& id, binding* idv)
+ {
+ if (p_ == 0)
+ allocate (&c, &im, &idim, &id, (idv != 0 ? idv : &id));
+
+ return *p_;
+ }
+
+ private:
+ void
+ allocate (connection_type*,
+ image_type*, id_image_type*,
+ binding*, binding*);
+
+ private:
+ T* p_;
+ void (extra_statement_cache_ptr::*deleter_) (
+ connection_type*, image_type*, id_image_type*, binding*, binding*);
+ };
+
+ template <typename T, typename I, typename ID>
+ void extra_statement_cache_ptr<T, I, ID>::
+ allocate (connection_type* c,
+ image_type* im, id_image_type* idim,
+ binding* id, binding* idv)
+ {
+ // To reduce object code size, this function acts as both allocator
+ // and deleter.
+ //
+ if (p_ == 0)
+ {
+ p_ = new T (*c, *im, *idim, *id, *idv);
+ deleter_ = &extra_statement_cache_ptr<T, I, ID>::allocate;
+ }
+ else
+ delete p_;
+ }
+
+ //
+ // Implementation for objects with object id.
+ //
+
+ class LIBODB_MSSQL_EXPORT object_statements_base: public statements_base
+ {
+ // Locking.
+ //
+ public:
+ void
+ lock ()
+ {
+ assert (!locked_);
+ locked_ = true;
+ }
+
+ void
+ unlock ()
+ {
+ assert (locked_);
+ locked_ = false;
+ }
+
+ bool
+ locked () const
+ {
+ return locked_;
+ }
+
+ struct auto_unlock
+ {
+ // Unlocks the statement on construction and re-locks it on
+ // destruction.
+ //
+ auto_unlock (object_statements_base&);
+ ~auto_unlock ();
+
+ private:
+ auto_unlock (const auto_unlock&);
+ auto_unlock& operator= (const auto_unlock&);
+
+ private:
+ object_statements_base& s_;
+ };
+
+ public:
+ virtual
+ ~object_statements_base ();
+
+ protected:
+ object_statements_base (connection_type& conn)
+ : statements_base (conn), locked_ (false)
+ {
+ }
+
+ protected:
+ bool locked_;
+ };
+
+ template <typename T, bool optimistic>
+ struct optimistic_data;
+
+ template <typename T>
+ struct optimistic_data<T, true>
+ {
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_mssql> object_traits;
+
+ optimistic_data (bind*, std::size_t skip, SQLUSMALLINT* status);
+
+ binding*
+ id_image_binding () {return &id_image_binding_;}
+
+ // The id + optimistic column binding.
+ //
+ binding id_image_binding_;
+
+ details::shared_ptr<delete_statement> erase_;
+ };
+
+ template <typename T>
+ struct optimistic_data<T, false>
+ {
+ optimistic_data (bind*, std::size_t, SQLUSMALLINT*) {}
+
+ binding*
+ id_image_binding () {return 0;}
+ };
+
+ template <typename T>
+ class object_statements: public object_statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_mssql> object_traits;
+ typedef typename object_traits::id_type id_type;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::image_type image_type;
+ typedef typename object_traits::id_image_type id_image_type;
+
+ typedef
+ typename object_traits::pointer_cache_traits
+ pointer_cache_traits;
+
+ typedef
+ typename object_traits::extra_statement_cache_type
+ extra_statement_cache_type;
+
+ typedef mssql::insert_statement insert_statement_type;
+ typedef mssql::select_statement select_statement_type;
+ typedef mssql::update_statement update_statement_type;
+ typedef mssql::delete_statement delete_statement_type;
+
+ // Automatic lock.
+ //
+ struct auto_lock
+ {
+ // Lock the statements unless they are already locked in which
+ // case subsequent calls to locked() will return false.
+ //
+ auto_lock (object_statements&);
+
+ // Unlock the statements if we are holding the lock and clear
+ // the delayed loads. This should only happen in case an
+ // exception is thrown. In normal circumstances, the user
+ // should call unlock() explicitly.
+ //
+ ~auto_lock ();
+
+ // Return true if this auto_lock instance holds the lock.
+ //
+ bool
+ locked () const;
+
+ // Unlock the statements.
+ //
+ void
+ unlock ();
+
+ private:
+ auto_lock (const auto_lock&);
+ auto_lock& operator= (const auto_lock&);
+
+ private:
+ object_statements& s_;
+ bool locked_;
+ };
+
+ public:
+ object_statements (connection_type&);
+
+ virtual
+ ~object_statements ();
+
+ // Delayed loading.
+ //
+ typedef void (*loader_function) (odb::database&,
+ const id_type&,
+ object_type&,
+ const schema_version_migration*);
+
+ void
+ delay_load (const id_type& id,
+ object_type& obj,
+ const typename pointer_cache_traits::position_type& p,
+ loader_function l = 0)
+ {
+ delayed_.push_back (delayed_load (id, obj, p, l));
+ }
+
+ void
+ load_delayed (const schema_version_migration* svm)
+ {
+ assert (locked ());
+
+ if (!delayed_.empty ())
+ load_delayed_<object_statements> (svm);
+ }
+
+ void
+ clear_delayed ()
+ {
+ if (!delayed_.empty ())
+ clear_delayed_ ();
+ }
+
+ // Object image.
+ //
+ image_type&
+ image (std::size_t i = 0)
+ {
+ return images_[i].obj;
+ }
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_image_version () const { return update_id_image_version_;}
+
+ void
+ update_id_image_version (std::size_t v) {update_id_image_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ // Object id image and binding.
+ //
+ id_image_type&
+ id_image (std::size_t i = 0) {return images_[i].id;}
+
+ std::size_t
+ id_image_version () const {return id_image_version_;}
+
+ void
+ id_image_version (std::size_t v) {id_image_version_ = v;}
+
+ binding&
+ id_image_binding () {return id_image_binding_;}
+
+ // Optimistic id + managed column image binding. It points to
+ // the same suffix as id binding and they are always updated
+ // at the same time.
+ //
+ binding&
+ optimistic_id_image_binding () {return *od_.id_image_binding ();}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ object_traits::auto_id,
+ object_traits::rowversion,
+ (object_traits::rowversion
+ ? &optimistic_id_image_binding ()
+ : (object_traits::auto_id ? &id_image_binding () : 0)),
+ false));
+
+ return *persist_;
+ }
+
+ select_statement_type&
+ find_statement ()
+ {
+ if (find_ == 0)
+ find_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ object_traits::find_statement,
+ object_traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ id_image_binding_,
+ select_image_binding_,
+ false));
+
+ return *find_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ object_traits::update_statement,
+ true, // Unique (0 or 1).
+ object_traits::versioned, // Process if versioned.
+ update_image_binding_,
+ object_traits::rowversion ? &optimistic_id_image_binding () : 0,
+ false));
+
+ return *update_;
+ }
+
+ delete_statement_type&
+ erase_statement ()
+ {
+ if (erase_ == 0)
+ erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::erase_statement,
+ true, // Unique (0 or 1 affected rows).
+ id_image_binding_,
+ false));
+
+ return *erase_;
+ }
+
+ delete_statement_type&
+ optimistic_erase_statement ()
+ {
+ if (od_.erase_ == 0)
+ {
+ od_.erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::optimistic_erase_statement,
+ true, // Unique (0 or 1 affected rows).
+ od_.id_image_binding_,
+ false));
+ }
+
+ return *od_.erase_;
+ }
+
+ // Extra (container, section) statement cache.
+ //
+ extra_statement_cache_type&
+ extra_statement_cache ()
+ {
+ return extra_statement_cache_.get (
+ conn_,
+ images_[0].obj, images_[0].id,
+ id_image_binding_, od_.id_image_binding ());
+ }
+
+ public:
+ // select = total - separate_load
+ // insert = total - inverse - managed_optimistic - auto_id
+ // update = total - inverse - managed_optimistic - id - readonly
+ // - separate_update
+ //
+ static const std::size_t id_column_count =
+ object_traits::id_column_count;
+
+ static const std::size_t managed_optimistic_column_count =
+ object_traits::managed_optimistic_column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count -
+ object_traits::separate_load_column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count -
+ object_traits::inverse_column_count -
+ object_traits::managed_optimistic_column_count -
+ (object_traits::auto_id ? id_column_count : 0);
+
+ static const std::size_t update_column_count =
+ insert_column_count -
+ (object_traits::auto_id ? 0 : id_column_count) -
+ object_traits::readonly_column_count -
+ object_traits::separate_update_column_count;
+
+ private:
+ object_statements (const object_statements&);
+ object_statements& operator= (const object_statements&);
+
+ protected:
+ template <typename STS>
+ void
+ load_delayed_ (const schema_version_migration*);
+
+ void
+ clear_delayed_ ();
+
+ protected:
+ template <typename T1>
+ friend class polymorphic_derived_object_statements;
+
+ extra_statement_cache_ptr<extra_statement_cache_type,
+ image_type,
+ id_image_type> extra_statement_cache_;
+
+ // The UPDATE statement uses both the object and id image. Keep
+ // them next to each other so that the same skip distance can
+ // be used in batch binding.
+ //
+ struct images
+ {
+ image_type obj;
+ id_image_type id;
+ };
+
+ images images_[object_traits::batch];
+ SQLUSMALLINT status_[object_traits::batch];
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+ binding select_image_binding_;
+ bind select_image_bind_[select_column_count];
+
+ // Insert binding.
+ //
+ std::size_t insert_image_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[
+ insert_column_count != 0 ? insert_column_count : 1];
+
+ // Update binding. Note that the id suffix is bound to id_image_
+ // below instead of image_ which makes this binding effectively
+ // bound to two images. As a result, we have to track versions
+ // for both of them. If this object uses optimistic concurrency,
+ // then the binding for the managed column (version, timestamp,
+ // etc) comes after the id and the image for such a column is
+ // stored as part of the id image.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_image_version_;
+ binding update_image_binding_;
+ bind update_image_bind_[update_column_count + id_column_count +
+ managed_optimistic_column_count];
+
+ // Id image binding (only used as a parameter or in OUTPUT for
+ // auto id and version). Uses the suffix in the update bind.
+ //
+ std::size_t id_image_version_;
+ binding id_image_binding_;
+
+ // Extra data for objects with optimistic concurrency support.
+ //
+ optimistic_data<T, managed_optimistic_column_count != 0> od_;
+
+ details::shared_ptr<insert_statement_type> persist_;
+ details::shared_ptr<select_statement_type> find_;
+ details::shared_ptr<update_statement_type> update_;
+ details::shared_ptr<delete_statement_type> erase_;
+
+ // Delayed loading.
+ //
+ struct delayed_load
+ {
+ typedef typename pointer_cache_traits::position_type position_type;
+
+ delayed_load () {}
+ delayed_load (const id_type& i,
+ object_type& o,
+ const position_type& p,
+ loader_function l)
+ : id (i), obj (&o), pos (p), loader (l)
+ {
+ }
+
+ id_type id;
+ object_type* obj;
+ position_type pos;
+ loader_function loader;
+ };
+
+ typedef std::vector<delayed_load> delayed_loads;
+ delayed_loads delayed_;
+
+ // Delayed vectors swap guard. See the load_delayed_() function for
+ // details.
+ //
+ struct swap_guard
+ {
+ swap_guard (object_statements& os, delayed_loads& dl)
+ : os_ (os), dl_ (dl)
+ {
+ dl_.swap (os_.delayed_);
+ }
+
+ ~swap_guard ()
+ {
+ os_.clear_delayed ();
+ dl_.swap (os_.delayed_);
+ }
+
+ private:
+ object_statements& os_;
+ delayed_loads& dl_;
+ };
+ };
+ }
+}
+
+#include <odb/mssql/simple-object-statements.ixx>
+#include <odb/mssql/simple-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_SIMPLE_OBJECT_STATEMENTS_HXX
diff --git a/libodb-mssql/odb/mssql/simple-object-statements.ixx b/libodb-mssql/odb/mssql/simple-object-statements.ixx
new file mode 100644
index 0000000..1066850
--- /dev/null
+++ b/libodb-mssql/odb/mssql/simple-object-statements.ixx
@@ -0,0 +1,68 @@
+// file : odb/mssql/simple-object-statements.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // auto_unlock
+ //
+ inline object_statements_base::auto_unlock::
+ auto_unlock (object_statements_base& s)
+ : s_ (s)
+ {
+ s_.unlock ();
+ }
+
+ inline object_statements_base::auto_unlock::
+ ~auto_unlock ()
+ {
+ s_.lock ();
+ }
+
+ //
+ // auto_lock
+ //
+ template <typename T>
+ inline object_statements<T>::auto_lock::
+ auto_lock (object_statements& s)
+ : s_ (s)
+ {
+ if (!s_.locked ())
+ {
+ s_.lock ();
+ locked_ = true;
+ }
+ else
+ locked_ = false;
+ }
+
+ template <typename T>
+ inline object_statements<T>::auto_lock::
+ ~auto_lock ()
+ {
+ if (locked_)
+ {
+ s_.unlock ();
+ s_.clear_delayed ();
+ }
+ }
+
+ template <typename T>
+ inline bool object_statements<T>::auto_lock::
+ locked () const
+ {
+ return locked_;
+ }
+
+ template <typename T>
+ inline void object_statements<T>::auto_lock::
+ unlock ()
+ {
+ assert (locked_);
+ s_.unlock ();
+ locked_ = false;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/simple-object-statements.txx b/libodb-mssql/odb/mssql/simple-object-statements.txx
new file mode 100644
index 0000000..06a0651
--- /dev/null
+++ b/libodb-mssql/odb/mssql/simple-object-statements.txx
@@ -0,0 +1,173 @@
+// file : odb/mssql/simple-object-statements.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // optimistic_data
+ //
+
+ template <typename T>
+ optimistic_data<T, true>::
+ optimistic_data (bind* b, std::size_t skip, SQLUSMALLINT* status)
+ : id_image_binding_ (
+ b,
+ object_traits::id_column_count +
+ object_traits::managed_optimistic_column_count,
+ object_traits::batch,
+ skip,
+ status)
+ {
+ }
+
+ //
+ // object_statements
+ //
+
+ template <typename T>
+ object_statements<T>::
+ ~object_statements ()
+ {
+ }
+
+ template <typename T>
+ object_statements<T>::
+ object_statements (connection_type& conn)
+ : object_statements_base (conn),
+ select_image_binding_ (select_image_bind_, select_column_count),
+ insert_image_binding_ (insert_image_bind_,
+ insert_column_count,
+ object_traits::batch,
+ sizeof (images),
+ status_),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count +
+ managed_optimistic_column_count,
+ // No support for bulk update and ROWVERSION.
+ //
+ (object_traits::rowversion
+ ? 1
+ : object_traits::batch),
+ sizeof (images),
+ status_),
+ id_image_binding_ (update_image_bind_ + update_column_count,
+ id_column_count,
+ object_traits::batch,
+ sizeof (images),
+ status_),
+ od_ (update_image_bind_ + update_column_count,
+ sizeof (images),
+ status_)
+ {
+ // Only versions in the first element used.
+ //
+ images_[0].obj.version = 0;
+ images_[0].id.version = 0;
+
+ select_image_version_ = 0;
+ insert_image_version_ = 0;
+ update_image_version_ = 0;
+ update_id_image_version_ = 0;
+ id_image_version_ = 0;
+
+ // SELECT statements only use the first element (no batches).
+ //
+ select_image_binding_.change_callback =
+ images_[0].obj.change_callback ();
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ }
+
+ template <typename T>
+ template <typename STS>
+ void object_statements<T>::
+ load_delayed_ (const schema_version_migration* svm)
+ {
+ database& db (connection ().database ());
+
+ delayed_loads dls;
+ swap_guard sg (*this, dls);
+
+ while (!dls.empty ())
+ {
+ delayed_load l (dls.back ());
+ typename pointer_cache_traits::insert_guard ig (l.pos);
+ dls.pop_back ();
+
+ if (l.loader == 0)
+ {
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (static_cast<STS&> (*this), &l.id))
+ throw object_not_persistent ();
+
+ // Our find_() version delays result freeing.
+ //
+ auto_result ar (*find_);
+
+ object_traits::callback (db, *l.obj, callback_event::pre_load);
+
+ // Our calls to init/load below can result in additional delayed
+ // loads being added to the delayed_ vector. We need to process
+ // those before we call the post callback.
+ //
+ tc.init (*l.obj, image (), &db);
+ find_->stream_result ();
+ ar.free ();
+
+ // Load containers, etc.
+ //
+ tc.load_ (static_cast<STS&> (*this), *l.obj, false);
+
+ if (!delayed_.empty ())
+ load_delayed_<STS> (svm);
+
+ // Temporarily unlock the statement for the post_load call so that
+ // it can load objects of this type recursively. This is safe to do
+ // because we have completely loaded the current object. Also the
+ // delayed_ list is clear before the unlock and should be clear on
+ // re-lock (since a callback can only call public API functions
+ // which will make sure all the delayed loads are processed before
+ // returning).
+ //
+ {
+ auto_unlock u (*this);
+ object_traits::callback (db, *l.obj, callback_event::post_load);
+ }
+ }
+ else
+ l.loader (db, l.id, *l.obj, svm);
+
+ pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+ }
+
+ template <typename T>
+ void object_statements<T>::
+ clear_delayed_ ()
+ {
+ // Remove the objects from the session cache.
+ //
+ for (typename delayed_loads::iterator i (delayed_.begin ()),
+ e (delayed_.end ()); i != e; ++i)
+ {
+ pointer_cache_traits::erase (i->pos);
+ }
+
+ delayed_.clear ();
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/statement-cache.hxx b/libodb-mssql/odb/mssql/statement-cache.hxx
new file mode 100644
index 0000000..cf06b94
--- /dev/null
+++ b/libodb-mssql/odb/mssql/statement-cache.hxx
@@ -0,0 +1,59 @@
+// file : odb/mssql/statement-cache.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_STATEMENT_CACHE_HXX
+#define ODB_MSSQL_STATEMENT_CACHE_HXX
+
+#include <odb/pre.hxx>
+
+#include <map>
+#include <typeinfo>
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/statements-base.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/type-info.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class LIBODB_MSSQL_EXPORT statement_cache
+ {
+ public:
+ statement_cache (connection& conn)
+ : conn_ (conn),
+ version_seq_ (conn_.database ().schema_version_sequence ()) {}
+
+ template <typename T>
+ typename object_traits_impl<T, id_mssql>::statements_type&
+ find_object ();
+
+ template <typename T>
+ view_statements<T>&
+ find_view ();
+
+ private:
+ typedef std::map<const std::type_info*,
+ details::shared_ptr<statements_base>,
+ details::type_info_comparator> map;
+
+ connection& conn_;
+ unsigned int version_seq_;
+ map map_;
+ };
+ }
+}
+
+#include <odb/mssql/statement-cache.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_STATEMENT_CACHE_HXX
diff --git a/libodb-mssql/odb/mssql/statement-cache.txx b/libodb-mssql/odb/mssql/statement-cache.txx
new file mode 100644
index 0000000..84424e3
--- /dev/null
+++ b/libodb-mssql/odb/mssql/statement-cache.txx
@@ -0,0 +1,60 @@
+// file : odb/mssql/statement-cache.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/database.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ typename object_traits_impl<T, id_mssql>::statements_type&
+ statement_cache::
+ find_object ()
+ {
+ typedef
+ typename object_traits_impl<T, id_mssql>::statements_type
+ statements_type;
+
+ // Clear the cache if the database version has changed. This
+ // makes sure we don't re-use statements that correspond to
+ // the old schema.
+ //
+ if (version_seq_ != conn_.database ().schema_version_sequence ())
+ {
+ map_.clear ();
+ version_seq_ = conn_.database ().schema_version_sequence ();
+ }
+
+ map::iterator i (map_.find (&typeid (T)));
+
+ if (i != map_.end ())
+ return static_cast<statements_type&> (*i->second);
+
+ details::shared_ptr<statements_type> p (
+ new (details::shared) statements_type (conn_));
+
+ map_.insert (map::value_type (&typeid (T), p));
+ return *p;
+ }
+
+ template <typename T>
+ view_statements<T>& statement_cache::
+ find_view ()
+ {
+ // We don't cache any statements for views so no need to clear
+ // the cache.
+
+ map::iterator i (map_.find (&typeid (T)));
+
+ if (i != map_.end ())
+ return static_cast<view_statements<T>&> (*i->second);
+
+ details::shared_ptr<view_statements<T> > p (
+ new (details::shared) view_statements<T> (conn_));
+
+ map_.insert (map::value_type (&typeid (T), p));
+ return *p;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/statement-processing.cxx b/libodb-mssql/odb/mssql/statement-processing.cxx
new file mode 100644
index 0000000..0c3072a
--- /dev/null
+++ b/libodb-mssql/odb/mssql/statement-processing.cxx
@@ -0,0 +1,356 @@
+// file : odb/mssql/statement-processing.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/statement-processing-common.hxx>
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+# include <iostream>
+#endif
+
+#include <odb/mssql/statement.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ typedef bind bind_type;
+
+ void statement::
+ process_select (std::string& r,
+ const char* s,
+ const bind_type* bind,
+ std::size_t bind_size,
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ bool optimize
+#else
+ bool
+#endif
+ )
+ {
+ // This implementation is pretty much the same as the generic one
+ // except for two things:
+ //
+ // 1. When checking for the fast case, take into account long data
+ // columns which we may have to re-arrange.
+ //
+ // 2. Create the column list in two passes, ordinary columns first
+ // followed by the long data columns.
+ //
+
+ bool empty (true); // Empty case (if none present).
+ bool fast (true); // Fast case (if all present and none are long data).
+ for (size_t i (0); i != bind_size && (empty || fast); ++i)
+ {
+ const bind_type& b (bind[i]);
+
+ if (b.buffer != 0)
+ empty = false;
+ else
+ fast = false;
+
+ if (b.type == bind_type::long_string ||
+ b.type == bind_type::long_nstring ||
+ b.type == bind_type::long_binary)
+ fast = false;
+ }
+
+ // Empty.
+ //
+ if (empty)
+ {
+ r.clear ();
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+ if (*s != '\0')
+ cerr << endl
+ << "old: '" << s << "'" << endl << endl
+ << "new: '" << r << "'" << endl << endl;
+#endif
+ return;
+ }
+
+ // Fast path: just remove the "structure".
+ //
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ if (fast && !optimize)
+ {
+ process_fast (s, r);
+ return;
+ }
+#endif
+
+ // Scan the statement and store the positions of various parts.
+ //
+ size_t n (traits::length (s));
+ const char* e (s + n);
+
+ // Header.
+ //
+ const char* p (find (s, e, '\n'));
+ assert (p != 0);
+ size_t header_size (p - s);
+ p++;
+
+ // Column list.
+ //
+ const char* columns_begin (p);
+ for (const char* ce (comma_begin (p, e)); ce != 0; comma_next (p, ce, e))
+ ;
+
+ // FROM.
+ assert (traits::compare (p, "FROM ", 5) == 0);
+ const char* from_begin (p);
+ p = find (p, e, '\n'); // May not end with '\n'.
+ if (p == 0)
+ p = e;
+ size_t from_size (p - from_begin);
+ if (p != e)
+ p++;
+
+ // JOIN list.
+ //
+ const char* joins_begin (0), *joins_end (0);
+ if (e - p > 5 && fuzzy_prefix (p, e, "JOIN ", 5))
+ {
+ joins_begin = p;
+
+ // Find the end of the JOIN list.
+ //
+ for (const char* je (newline_begin (p, e));
+ je != 0; newline_next (p, je, e, "JOIN ", 5, true))
+ ;
+
+ joins_end = (p != e ? p - 1 : p);
+ }
+
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ if (fast && joins_begin == 0)
+ {
+ // No JOINs to optimize so can still take the fast path.
+ //
+ process_fast (s, r);
+ return;
+ }
+#endif
+
+ // Trailer (WHERE, ORDER BY, etc).
+ //
+ const char* trailer_begin (0);
+ size_t trailer_size (0);
+ if (e - p != 0)
+ {
+ trailer_begin = p;
+ trailer_size = e - p;
+ }
+
+ // Assume the same size as the original. It can only shrink, and in
+ // most cases only slightly. So this is a good approximation.
+ //
+ r.reserve (n);
+ r.assign (s, header_size);
+
+ // Column list.
+ //
+ {
+ r += ' ';
+
+ size_t i (0);
+ bool need_second (false);
+
+ // First pass: non-long data columns.
+ //
+ {
+ size_t bi (0);
+ for (const char *c (columns_begin), *ce (comma_begin (c, e));
+ ce != 0; comma_next (c, ce, e))
+ {
+ const bind_type& b (bind[bi++]);
+
+ // See if the column is present in the bind array and if it
+ // is of the right kind.
+ //
+ if (b.buffer == 0)
+ continue;
+
+ if (b.type == bind_type::long_string ||
+ b.type == bind_type::long_nstring ||
+ b.type == bind_type::long_binary)
+ {
+ need_second = true;
+ continue;
+ }
+
+ // Append the column.
+ //
+ if (i++ != 0)
+ r += ", "; // Add the space for consistency with the fast path.
+
+ r.append (c, ce - c);
+ }
+ }
+
+ // Second pass: long data columns.
+ //
+ if (need_second)
+ {
+ size_t bi (0);
+ for (const char *c (columns_begin), *ce (comma_begin (c, e));
+ ce != 0; comma_next (c, ce, e))
+ {
+ const bind_type& b (bind[bi++]);
+
+ // See if the column is present in the bind array and if it
+ // is of the right kind.
+ //
+ if (b.buffer == 0 ||
+ (b.type != bind_type::long_string &&
+ b.type != bind_type::long_nstring &&
+ b.type != bind_type::long_binary))
+ continue;
+
+ // Append the column.
+ //
+ if (i++ != 0)
+ r += ", "; // Add the space for consistency with the fast path.
+
+ r.append (c, ce - c);
+ }
+ }
+ }
+
+ // From.
+ //
+ r += ' ';
+ r.append (from_begin, from_size);
+
+ // JOIN list, pass 1.
+ //
+ size_t join_pos (0);
+ if (joins_begin != 0)
+ {
+ // Fill in the JOIN "area" with spaces.
+ //
+ r.resize (r.size () + joins_end - joins_begin + 1, ' ');
+ join_pos = r.size () + 1; // End of the last JOIN.
+ }
+
+ // Trailer.
+ //
+ if (trailer_size != 0)
+ {
+ r += ' ';
+ r.append (trailer_begin, trailer_size);
+ }
+
+ // JOIN list, pass 2.
+ //
+ if (joins_begin != 0)
+ {
+ // Splice the JOINs into the pre-allocated area.
+ //
+ for (const char* je (joins_end), *j (newline_rbegin (je, joins_begin));
+ j != 0; newline_rnext (j, je, joins_begin))
+ {
+ size_t n (je - j);
+
+ // Get the alias or, if none used, the table name.
+ //
+ p = find (j, je, "JOIN ", 5) + 5; // Skip past "JOIN ".
+ const char* table_begin (p);
+ p = find (p, je, ' '); // End of the table name.
+ const char* table_end (p);
+ p++; // Skip space.
+
+ // We may or may not have the AS keyword.
+ //
+ const char* alias_begin (0);
+ size_t alias_size (0);
+ if (p != je && // Not the end.
+ (je - p < 4 || traits::compare (p, "ON ", 3) != 0))
+ {
+ // Something other than "ON ", so got to be an alias.
+ //
+ p += 3;
+ alias_begin = p;
+ p = find (p, je, ' '); // There might be no ON (CROSS JOIN).
+ alias_size = (p != 0 ? p : je) - alias_begin;
+ }
+ else
+ {
+ // Just the table.
+ //
+ alias_begin = table_begin;
+ alias_size = table_end - alias_begin;
+ }
+
+ // The alias must be quoted.
+ //
+ assert (*alias_begin == '[' &&
+ *(alias_begin + alias_size - 1) == ']');
+
+ // We now need to see if the alias is used in either the SELECT
+ // list, the WHERE conditions, or the ON condition of any of the
+ // JOINs that we have already processed and decided to keep.
+ //
+ // Instead of re-parsing the whole thing again, we are going to
+ // take a shortcut and simply search for the alias in the statement
+ // we have constructed so far (that's why we have added the
+ // trailer before filling in the JOINs). To make it more robust,
+ // we are going to do a few extra sanity checks, specifically,
+ // that the alias is a top level identifier and is followed by
+ // only a single identifer (column). This will catch cases like
+ // [s].[t].[c] where [s] is also used as an alias or LEFT JOIN [t]
+ // where [t] is also used as an alias in another JOIN.
+ //
+ bool found (false);
+ for (size_t p (r.find (alias_begin, 0, alias_size));
+ p != string::npos;
+ p = r.find (alias_begin, p + alias_size, alias_size))
+ {
+ size_t e (p + alias_size);
+
+ // If we are not a top-level qualifier or not a bottom-level,
+ // then we are done (3 is for at least "[a]").
+ //
+ if ((p != 0 && r[p - 1] == '.') ||
+ (e + 3 >= r.size () || (r[e] != '.' || r[e + 1] != '[')))
+ continue;
+
+ // The only way to distinguish the [a].[c] from FROM [a].[c] or
+ // JOIN [a].[c] is by checking the prefix.
+ //
+ if ((p > 5 && r.compare (p - 5, 5, "FROM ") == 0) ||
+ (p > 5 && r.compare (p - 5, 5, "JOIN ") == 0))
+ continue;
+
+ // Check that we are followed by a single identifier.
+ //
+ e = r.find (']', e + 2);
+ if (e == string::npos || (e + 1 != r.size () && r[e + 1] == '.'))
+ continue;
+
+ found = true;
+ break;
+ }
+
+ join_pos -= n + 1; // Extra one for space.
+ if (found)
+ r.replace (join_pos, n, j, n);
+ else
+ r.erase (join_pos - 1, n + 1); // Extra one for space.
+ }
+ }
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+ if (r.size () != n)
+ cerr << endl
+ << "old: '" << s << "'" << endl << endl
+ << "new: '" << r << "'" << endl << endl;
+#endif
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/statement.cxx b/libodb-mssql/odb/mssql/statement.cxx
new file mode 100644
index 0000000..ede4bb6
--- /dev/null
+++ b/libodb-mssql/odb/mssql/statement.cxx
@@ -0,0 +1,1740 @@
+// file : odb/mssql/statement.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstring> // std::strlen, std::strstr, std::memset, std::memcpy
+#include <cassert>
+
+#include <odb/tracer.hxx>
+
+#include <odb/mssql/mssql.hxx>
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/exceptions.hxx>
+#include <odb/mssql/error.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ // Mapping of bind::buffer_type to SQL_* SQL types.
+ //
+ static const SQLSMALLINT sql_type_lookup [bind::last] =
+ {
+ SQL_BIT, // bind::bit
+ SQL_TINYINT, // bind::tinyint
+ SQL_SMALLINT, // bind::smallint
+ SQL_INTEGER, // bind::int_
+ SQL_BIGINT, // bind::bigint
+
+ SQL_DECIMAL, // bind::decimal
+ SQL_DECIMAL, // bind::smallmoney
+ SQL_DECIMAL, // bind::money
+
+ SQL_FLOAT, // bind::float4
+ SQL_FLOAT, // bind::float8
+
+ SQL_VARCHAR, // bind::string
+ SQL_VARCHAR, // bind::long_string
+
+ SQL_WVARCHAR, // bind::nstring
+ SQL_WVARCHAR, // bind::long_nstring
+
+ SQL_VARBINARY, // bind::binary
+ SQL_VARBINARY, // bind::long_binary
+
+ SQL_TYPE_DATE, // bind::date
+ SQL_SS_TIME2, // bind::time
+ SQL_TYPE_TIMESTAMP, // bind::datetime
+ SQL_SS_TIMESTAMPOFFSET, // bind::datetimeoffset
+
+ SQL_GUID, // bind::uniqueidentifier
+ SQL_BINARY // bind::rowversion
+ };
+
+ // Mapping of bind::buffer_type to SQL_C_* C types.
+ //
+ static const SQLSMALLINT c_type_lookup [bind::last] =
+ {
+ SQL_C_BIT, // bind::bit
+ SQL_C_UTINYINT, // bind::tinyint
+ SQL_C_SSHORT, // bind::smallint
+ SQL_C_SLONG, // bind::int_
+ SQL_C_SBIGINT, // bind::bigint
+
+ SQL_C_NUMERIC, // bind::decimal
+ SQL_C_BINARY, // bind::smallmoney
+ SQL_C_BINARY, // bind::money
+
+ SQL_C_FLOAT, // bind::float4
+ SQL_C_DOUBLE, // bind::float8
+
+ SQL_C_CHAR, // bind::string
+ SQL_C_CHAR, // bind::long_string
+
+ SQL_C_WCHAR, // bind::nstring
+ SQL_C_WCHAR, // bind::long_nstring
+
+ SQL_C_BINARY, // bind::binary
+ SQL_C_BINARY, // bind::long_binary
+
+ SQL_C_TYPE_DATE, // bind::date
+ SQL_C_BINARY, // bind::time
+ SQL_C_TYPE_TIMESTAMP, // bind::datetime
+ SQL_C_BINARY, // bind::datetimeoffset
+
+ SQL_C_GUID, // bind::uniqueidentifier
+ SQL_C_BINARY // bind::rowversion
+ };
+
+ // Mapping of bind::buffer_type to fixed buffer capacity values.
+ //
+ static const SQLLEN capacity_lookup [bind::last] =
+ {
+ 1, // bind::bit
+ 1, // bind::tinyint
+ 2, // bind::smallint
+ 4, // bind::int_
+ 8, // bind::bigint
+
+ sizeof (decimal), // bind::decimal
+ 4, // bind::smallmoney
+ 8, // bind::money
+
+ 4, // bind::float4
+ 8, // bind::float8
+
+ 0, // bind::string
+ 0, // bind::long_string
+
+ 0, // bind::nstring
+ 0, // bind::long_nstring
+
+ 0, // bind::binary
+ 0, // bind::long_binary
+
+ sizeof (date), // bind::date
+ sizeof (time), // bind::time
+ sizeof (datetime), // bind::datetime
+ sizeof (datetimeoffset), // bind::datetimeoffset
+
+ 16, // bind::uniqueidentifier
+ 8 // bind::rowversion
+ };
+
+ //
+ // statement
+ //
+
+ statement::
+ statement (connection_type& conn,
+ const string& text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize)
+ : conn_ (conn)
+ {
+ if (process == 0)
+ {
+ text_copy_ = text;
+ text_ = text_copy_.c_str ();
+ }
+ else
+ text_ = text.c_str (); // Temporary, see init().
+
+ init (text.size (), sk, process, optimize);
+ }
+
+ statement::
+ statement (connection_type& conn,
+ const char* text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize,
+ bool copy)
+ : conn_ (conn)
+ {
+ size_t n;
+
+ if (process == 0 && copy)
+ {
+ text_copy_ = text;
+ text_ = text_copy_.c_str ();
+ n = text_copy_.size ();
+ }
+ else
+ {
+ text_ = text;
+ n = strlen (text_); // Potentially temporary, see init().
+ }
+
+ init (n, sk, process, optimize);
+ }
+
+ void statement::
+ init (size_t text_size,
+ statement_kind sk,
+ const binding* proc,
+ bool optimize)
+ {
+ if (proc != 0)
+ {
+ switch (sk)
+ {
+ case statement_select:
+ process_select (text_copy_,
+ text_,
+ proc->bind, proc->count,
+ optimize);
+ break;
+ case statement_insert:
+ process_insert (text_copy_,
+ text_,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ '?');
+ break;
+ case statement_update:
+ process_update (text_copy_,
+ text_,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ '?');
+ break;
+ case statement_delete:
+ assert (false);
+ }
+
+ text_ = text_copy_.c_str ();
+ text_size = text_copy_.size ();
+ }
+
+ // Empty statement.
+ //
+ if (*text_ == '\0')
+ return;
+
+ SQLRETURN r;
+
+ // Allocate the handle.
+ //
+ {
+ SQLHANDLE h;
+ r = SQLAllocHandle (SQL_HANDLE_STMT, conn_.handle (), &h);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_);
+
+ stmt_.reset (h);
+ }
+
+ // Disable escape sequences.
+ //
+ r = SQLSetStmtAttr (stmt_,
+ SQL_ATTR_NOSCAN,
+ (SQLPOINTER) SQL_NOSCAN_OFF,
+ 0);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->prepare (conn_, *this);
+ }
+
+ // Prepare the statement.
+ //
+ r = SQLPrepareA (stmt_, (SQLCHAR*) text_, (SQLINTEGER) text_size);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+
+ statement::
+ ~statement ()
+ {
+ if (stmt_ != 0)
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->deallocate (conn_, *this);
+ }
+ }
+
+ const char* statement::
+ text () const
+ {
+ return text_;
+ }
+
+ void statement::
+ bind_param (bind* b, size_t n)
+ {
+ SQLRETURN r;
+
+ SQLUSMALLINT i (0);
+ for (bind* end (b + n); b != end; ++b)
+ {
+ if (b->buffer == 0) // Skip NULL entries.
+ continue;
+
+ i++; // Column index is 1-based.
+
+ SQLULEN col_size (0);
+ SQLSMALLINT digits (0);
+ SQLPOINTER buf;
+
+ switch (b->type)
+ {
+ case bind::decimal:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = (SQLULEN) (b->capacity / 100); // precision
+ digits = (SQLSMALLINT) (b->capacity % 100); // scale
+ break;
+ }
+ case bind::smallmoney:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = 10;
+ digits = 4;
+ break;
+ }
+ case bind::money:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = 19;
+ digits = 4;
+ break;
+ }
+ case bind::float4:
+ case bind::float8:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = (SQLULEN) b->capacity; // precision
+ break;
+ }
+ case bind::long_string:
+ case bind::long_binary:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = b->capacity != 0
+ ? (SQLULEN) b->capacity
+ : SQL_SS_LENGTH_UNLIMITED;
+ break;
+ }
+ case bind::long_nstring:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = b->capacity != 0
+ ? (SQLULEN) b->capacity / 2 // In characters, not bytes.
+ : SQL_SS_LENGTH_UNLIMITED;
+ break;
+ }
+ case bind::string:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = b->capacity != 0
+ ? (SQLULEN) b->capacity - 1 // Sans the null-terminator.
+ : SQL_SS_LENGTH_UNLIMITED;
+ break;
+ }
+ case bind::binary:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = b->capacity != 0
+ ? (SQLULEN) b->capacity
+ : SQL_SS_LENGTH_UNLIMITED;
+ break;
+ }
+ case bind::nstring:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = b->capacity != 0
+ // In characters, not bytes, and sans the null-terminator.
+ ? (SQLULEN) (b->capacity / 2 - 1)
+ : SQL_SS_LENGTH_UNLIMITED;
+ break;
+ }
+ case bind::date:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ // Native Client 10.0 requires the correct precision.
+ //
+ col_size = 10;
+ break;
+ }
+ case bind::time:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ digits = (SQLSMALLINT) b->capacity;
+
+ // Native Client 10.0 requires the correct precision.
+ //
+ if (digits == 0)
+ col_size = 8;
+ else
+ col_size = (SQLULEN) (digits + 9);
+
+ break;
+ }
+ case bind::datetime:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ digits = (SQLSMALLINT) b->capacity;
+
+ // Native Client 10.0 requires the correct precision.
+ //
+ if (digits == 0)
+ col_size = 19;
+ else if (digits == 8)
+ {
+ // This is a SMALLDATETIME column which only has the minutes
+ // precision. Documentation indicates that the correct numeric
+ // precision value for this type is 16.
+ //
+ digits = 0;
+ col_size = 16;
+ }
+ else
+ col_size = (SQLULEN) (digits + 20);
+
+ break;
+ }
+ case bind::datetimeoffset:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ digits = (SQLSMALLINT) b->capacity;
+
+ // Native Client 10.0 requires the correct precision.
+ //
+ if (digits == 0)
+ col_size = 26;
+ else
+ col_size = (SQLULEN) (digits + 27);
+
+ break;
+ }
+ case bind::rowversion:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = 8;
+ break;
+ }
+ default:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ break;
+ }
+ }
+
+ r = SQLBindParameter (
+ stmt_,
+ i,
+ SQL_PARAM_INPUT,
+ c_type_lookup[b->type],
+ sql_type_lookup[b->type],
+ col_size,
+ digits,
+ buf,
+ 0, // buffer capacity (shouldn't be needed for input parameters)
+ b->size_ind);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+ }
+
+ SQLUSMALLINT statement::
+ bind_result (bind* b, size_t n, SQLUSMALLINT& long_count)
+ {
+ long_count = 0;
+ SQLRETURN r;
+
+ SQLUSMALLINT i (0);
+ for (bind* end (b + n); b != end; ++b)
+ {
+ if (b->buffer == 0) // Skip NULL entries.
+ continue;
+
+ SQLLEN cap (capacity_lookup[b->type]);
+
+ switch (b->type)
+ {
+ case bind::string:
+ case bind::nstring:
+ case bind::binary:
+ {
+ cap = b->capacity;
+ break;
+ }
+ case bind::long_string:
+ case bind::long_nstring:
+ case bind::long_binary:
+ {
+ // Long data is not bound.
+ //
+ long_count++;
+ continue;
+ }
+ case bind::last:
+ {
+ assert (false);
+ break;
+ }
+ default:
+ break;
+ }
+
+ r = SQLBindCol (stmt_,
+ ++i, // Column index is 1-based.
+ c_type_lookup[b->type],
+ (SQLPOINTER) b->buffer,
+ cap,
+ b->size_ind);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+
+ return i;
+ }
+
+ SQLRETURN statement::
+ execute ()
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ SQLRETURN r (SQLExecute (stmt_));
+
+ if (r == SQL_NEED_DATA)
+ {
+ details::buffer& tmp_buf (conn_.long_data_buffer ());
+
+ if (tmp_buf.capacity () == 0)
+ tmp_buf.capacity (4096);
+
+ long_callback* pcb;
+ for (;;)
+ {
+ // ODBC seems to already offset the returned pointer for us
+ // in case of a batch.
+ //
+ r = SQLParamData (stmt_, (SQLPOINTER*) &pcb);
+
+ // If we get anything other than SQL_NEED_DATA, then this is
+ // the return code of SQLExecute().
+ //
+ if (r != SQL_NEED_DATA)
+ break;
+
+ // Store the pointer to the long_callback struct in buf on the
+ // first call to the callback. This allows the callback to
+ // redirect further calls to some other callback.
+ //
+ long_callback cb (*pcb);
+ const void* buf (&cb);
+
+ size_t position (0);
+ for (;;)
+ {
+ size_t n;
+ chunk_type chunk;
+
+ cb.callback.param (
+ cb.context.param,
+ &position,
+ &buf,
+ &n,
+ &chunk,
+ tmp_buf.data (),
+ tmp_buf.capacity ());
+
+ r = SQLPutData (
+ stmt_,
+ (SQLPOINTER) (buf != 0 ? buf : &buf), // Always pass non-NULL.
+ chunk != chunk_null ? (SQLLEN) n : SQL_NULL_DATA);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ if (chunk == chunk_one ||
+ chunk == chunk_last ||
+ chunk == chunk_null)
+ break;
+ }
+ }
+ }
+
+ return r;
+ }
+
+ void statement::
+ stream_result (SQLUSMALLINT i, bind* b, size_t n, void* obase, void* nbase)
+ {
+ details::buffer& tmp_buf (conn_.long_data_buffer ());
+
+ if (tmp_buf.capacity () == 0)
+ tmp_buf.capacity (4096);
+
+ SQLRETURN r;
+
+ for (bind* end (b + n); b != end; ++b)
+ {
+ if (b->buffer == 0) // Skip NULL entries.
+ continue;
+
+ bool char_data;
+ switch (b->type)
+ {
+ case bind::long_string:
+ case bind::long_nstring:
+ {
+ char_data = true;
+ break;
+ }
+ case bind::long_binary:
+ {
+ char_data = false;
+ break;
+ }
+ default:
+ {
+ continue; // Not long data.
+ }
+ }
+
+ void* cbp;
+
+ if (obase == 0)
+ cbp = b->buffer;
+ else
+ {
+ // Re-base the pointer.
+ //
+ char* p (static_cast<char*> (b->buffer));
+ char* ob (static_cast<char*> (obase));
+ char* nb (static_cast<char*> (nbase));
+
+ assert (ob <= p);
+ cbp = nb + (p - ob);
+ }
+
+ long_callback cb (*static_cast<long_callback*> (cbp));
+
+ // First determine if the value is NULL as well as try to
+ // get the total data size.
+ //
+ SQLLEN si;
+ r = SQLGetData (stmt_,
+ ++i,
+ c_type_lookup[b->type],
+ tmp_buf.data (), // Dummy value.
+ 0,
+ &si);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ // Store the pointer to the long_callback struct in buf on the
+ // first call to the callback. This allows the callback to
+ // redirect further calls to some other callback.
+ //
+ void* buf (&cb);
+ size_t size (0);
+ size_t position (0);
+ size_t size_left (si == SQL_NO_TOTAL ? 0 : static_cast<size_t> (si));
+
+ chunk_type c (si == SQL_NULL_DATA
+ ? chunk_null
+ : (si == 0 ? chunk_one : chunk_first));
+
+ for (;;)
+ {
+ cb.callback.result (
+ cb.context.result,
+ &position,
+ &buf,
+ &size,
+ c,
+ size_left,
+ tmp_buf.data (),
+ tmp_buf.capacity ());
+
+ if (c == chunk_last || c == chunk_one || c == chunk_null)
+ break;
+
+ // SQLGetData() can keep returning SQL_SUCCESS_WITH_INFO (truncated)
+ // with SQL_NO_TOTAL for all the calls except the last one. For the
+ // last call we should get SQL_SUCCESS and the size_indicator should
+ // contain a valid value.
+ //
+ r = SQLGetData (stmt_,
+ i,
+ c_type_lookup[b->type],
+ (SQLPOINTER) buf,
+ (SQLLEN) size,
+ &si);
+
+ if (r == SQL_SUCCESS)
+ {
+ assert (si != SQL_NO_TOTAL);
+
+ // Actual amount of data copied to the buffer (appears not to
+ // include the NULL terminator).
+ //
+ size = static_cast<size_t> (si);
+ c = chunk_last;
+ }
+ else if (r == SQL_SUCCESS_WITH_INFO)
+ {
+ if (char_data)
+ size--; // NULL terminator.
+
+ c = chunk_next;
+ }
+ else
+ translate_error (r, conn_, stmt_);
+
+ // Update the total.
+ //
+ if (size_left != 0)
+ size_left -= size;
+ }
+ }
+ }
+
+ //
+ // bulk_statement
+ //
+ bulk_statement::
+ ~bulk_statement () {}
+
+ void bulk_statement::
+ init (size_t skip)
+ {
+ // Setup row-wise batch operation. We set the actual number of
+ // parameter sets in the batch in execute().
+ //
+ SQLRETURN r;
+
+ r = SQLSetStmtAttr (stmt_,
+ SQL_ATTR_PARAM_BIND_TYPE,
+ (SQLPOINTER) skip,
+ 0);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ r = SQLSetStmtAttr (stmt_,
+ SQL_ATTR_PARAMS_PROCESSED_PTR,
+ (SQLPOINTER) &processed_,
+ 0);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ r = SQLSetStmtAttr (stmt_,
+ SQL_ATTR_PARAM_STATUS_PTR,
+ (SQLPOINTER) status_,
+ 0);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+
+ SQLRETURN bulk_statement::
+ execute (size_t n, multiple_exceptions* mex)
+ {
+ mex_ = mex;
+
+ if (status_ != 0)
+ {
+ SQLRETURN r (SQLSetStmtAttr (stmt_,
+ SQL_ATTR_PARAMSET_SIZE,
+ (SQLPOINTER) n,
+ 0));
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ // Some SQL* functions would only update the status in case of
+ // an error.
+ //
+ memset (status_, 0, sizeof (status_[0]) * n);
+ }
+
+ processed_ = 0;
+ SQLRETURN r (statement::execute ());
+ bool ok (SQL_SUCCEEDED (r) || r == SQL_NO_DATA);
+
+ // If we have a batch of 1 parameter set, SQL Server ODBC driver
+ // returns the error via SQLExecute() rather than via the status
+ // array even if we set all the attributes necessary for row-wise
+ // binding. So what we are going to do here is convert this case
+ // to the batch way of reporting errors (not that we also check
+ // processed_ so that we only do this is the parameter set was
+ // actually attempted).
+ //
+ if (!ok && status_ != 0 && n == 1 && processed_ == 1)
+ {
+ status_[0] = SQL_PARAM_ERROR;
+ r = SQL_SUCCESS;
+ ok = true;
+ }
+
+ // If the statement failed as a whole, assume no parameter sets
+ // were attempted in case of a batch. Otherwise, the documentation
+ // says that the native client driver keeps processing remaining
+ // sets even in case of an error.
+ //
+ i_ = 0;
+ n_ = (ok ? n : (status_ == 0 ? 1 : 0));
+
+ if (mex_ != 0)
+ {
+ mex_->current (i_);
+ mex_->attempted (processed_);
+ }
+
+ if (!ok)
+ {
+ if (mex_ != 0)
+ mex_->fatal (true); // An incomplete batch is always fatal.
+
+ return r;
+ }
+
+ return r;
+ }
+
+ size_t bulk_statement::
+ extract_errors ()
+ {
+ size_t e (0);
+
+ for (size_t i (0); i != n_; ++i)
+ {
+ if (status_[i] != SQL_PARAM_SUCCESS &&
+ status_[i] != SQL_PARAM_SUCCESS_WITH_INFO)
+ {
+ translate_error (SQL_ERROR, conn_, stmt_, i, mex_);
+ e++;
+ }
+ }
+
+ return e;
+ }
+
+ unsigned long long bulk_statement::
+ affected (SQLRETURN r, size_t errors, bool unique)
+ {
+ unsigned long long rows (0);
+
+ // SQL_NO_DATA indicates that the statement hasn't affected any rows.
+ //
+ if (r != SQL_NO_DATA)
+ {
+ SQLLEN n;
+ r = SQLRowCount (stmt_, &n);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ // If all the parameter sets failed, then the returned count is -1,
+ // which means "not available" according to the documentation.
+ //
+ rows = (n != -1 ? static_cast<unsigned long long> (n) : 0);
+ }
+
+ if (n_ > 1) // Batch.
+ {
+ if (rows != 0) // Some rows did get affected.
+ {
+ // Subtract the parameter sets that failed since they haven't
+ // affected any rows.
+ //
+ size_t p (n_ - errors);
+
+ if (p > 1) // True batch.
+ {
+ if (unique) // Each can affect 0 or 1 row.
+ {
+ rows = (p == static_cast<size_t> (rows)
+ ? 1
+ : result_unknown);
+ }
+ else
+ rows = result_unknown;
+ }
+ }
+ }
+
+ return rows;
+ }
+
+ //
+ // select_statement
+ //
+ select_statement::
+ ~select_statement ()
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ bool optimize,
+ binding& param,
+ binding& result)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ result_ (result)
+ {
+ if (!empty ())
+ {
+ bind_param (param.bind, param.count);
+ result_count_ = bind_result (result.bind, result.count, long_count_);
+ }
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ bool optimize,
+ binding& param,
+ binding& result,
+ bool copy_text)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize,
+ copy_text),
+ result_ (result)
+ {
+ if (!empty ())
+ {
+ bind_param (param.bind, param.count);
+ result_count_ = bind_result (result.bind, result.count, long_count_);
+ }
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ bool optimize,
+ binding& result)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ result_ (result)
+ {
+ if (!empty ())
+ result_count_ = bind_result (result.bind, result.count, long_count_);
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ bool optimize,
+ binding& result,
+ bool copy_text)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize,
+ copy_text),
+ result_ (result)
+ {
+ if (!empty ())
+ result_count_ = bind_result (result.bind, result.count, long_count_);
+ }
+
+ void select_statement::
+ execute ()
+ {
+ SQLRETURN r (statement::execute ());
+
+ // Skip empty result sets that seem to be added as a result of
+ // executing DML statements in stored procedures (e.g., INSERT
+ // INTO EXEC).
+ //
+ if (r == SQL_NO_DATA)
+ {
+ r = SQLMoreResults (stmt_);
+
+ if (r == SQL_NO_DATA)
+ {
+ throw database_exception (
+ 0,
+ "?????",
+ "another result set expected after SQL_NO_DATA");
+ }
+ }
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ // Skip result sets that have no columns. These seem to be added
+ // by DML statements that don't produce any result (e.g., EXEC).
+ //
+ for (columns_ = 0; columns_ == 0;)
+ {
+ {
+ SQLSMALLINT c;
+ r = SQLNumResultCols (stmt_, &c);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ columns_ = static_cast<SQLUSMALLINT> (c);
+ }
+
+ if (columns_ == 0)
+ {
+ r = SQLMoreResults (stmt_);
+
+ if (r == SQL_NO_DATA)
+ break;
+ else if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+ }
+
+ // Make sure that the number of columns in the result returned by
+ // the database matches the number that we expect. A common cause
+ // of this assertion is a native view with a number of data members
+ // not matching the number of columns in the SELECT-list.
+ //
+ assert (columns_ == result_count_ + long_count_);
+ }
+
+ select_statement::result select_statement::
+ fetch ()
+ {
+ change_callback* cc (result_.change_callback);
+
+ if (cc != 0 && cc->callback != 0)
+ (cc->callback) (cc->context);
+
+ // Don't bother calling SQLFetch() if there are no columns.
+ //
+ if (columns_ == 0)
+ return no_data;
+ else
+ {
+ SQLRETURN r (SQLFetch (stmt_));
+
+ if (r == SQL_NO_DATA)
+ return no_data;
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ return success;
+ }
+ }
+
+ void select_statement::
+ free_result ()
+ {
+ // Use SQLFreeStmt(SQL_CLOSE) instead of SQLCloseCursor() to avoid an
+ // error if a cursor is already closed. This can happens, for example,
+ // if we are trying to close the cursor after the transaction has been
+ // committed (e.g., when destroying the query result) which also closes
+ // the cursor.
+ //
+ SQLRETURN r (SQLFreeStmt (stmt_, SQL_CLOSE));
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+
+ //
+ // insert_statement
+ //
+
+ insert_statement::
+ ~insert_statement ()
+ {
+ }
+
+ insert_statement::
+ insert_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ binding& param,
+ bool returning_id,
+ bool returning_version,
+ binding* returning)
+ : bulk_statement (conn,
+ text, statement_insert,
+ (process ? &param : 0), false,
+ param.batch, param.skip, param.status),
+ returning_id_ (returning_id),
+ returning_version_ (returning_version),
+ ret_ (returning)
+ {
+ bind_param (param.bind, param.count);
+
+ if (ret_ != 0)
+ init_result ();
+ }
+
+ insert_statement::
+ insert_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ binding& param,
+ bool returning_id,
+ bool returning_version,
+ binding* returning,
+ bool copy_text)
+ : bulk_statement (conn,
+ text, statement_insert,
+ (process ? &param : 0), false,
+ param.batch, param.skip, param.status,
+ copy_text),
+ returning_id_ (returning_id),
+ returning_version_ (returning_version),
+ ret_ (returning)
+ {
+ bind_param (param.bind, param.count);
+
+ if (ret_ != 0)
+ init_result ();
+ }
+
+ template <typename T>
+ static inline T*
+ offset (T* base, size_t count, size_t size)
+ {
+ return reinterpret_cast<T*> (
+ reinterpret_cast<char*> (base) + count * size);
+ }
+
+ void insert_statement::
+ init_result ()
+ {
+ // Figure out if we are using the OUTPUT clause or a batch of
+ // INSERT and SELECT statements. The latter is used to work
+ // around a bug in SQL Server 2005 that causes it to fail
+ // on an INSERT statement with the OUTPUT clause if data
+ // for one of the inserted columns is supplied at execution
+ // (long data).
+ //
+ text_batch_ = (strstr (text_, "OUTPUT INSERTED.") == 0 &&
+ strstr (text_, "output inserted.") == 0);
+
+ // It might seem logical to set up the array of results if this is a
+ // batch (i.e., the SQL_ATTR_ROW_BIND_TYPE, SQL_ATTR_ROW_ARRAY_SIZE).
+ // This won't work because what we are getting is multiple result
+ // sets (each containing a single row) and not multiple rows. As a
+ // result, the SQL Server ODBC driver will always store the data in
+ // the first element of our array. A bit counter-intuitive.
+ //
+ // At the same time it would be conceptually cleaner to have the
+ // returned data extracted into the batch array instead of always
+ // the first element. This is also how other database runtimes (e.g.,
+ // Oracle) behave. So what we are going to do here is emulate this
+ // by making the ODBC driver store the data into the last element
+ // of the batch array and then copying it into the right place
+ // after processing each result set (see fetch() below).
+ //
+ SQLRETURN r;
+ SQLUSMALLINT col (1);
+
+ size_t last (ret_->batch - 1);
+
+ if (returning_id_)
+ {
+ bind& b (ret_->bind[0]); // Auto id is the first element.
+
+ r = SQLBindCol (stmt_,
+ col++,
+ c_type_lookup[b.type],
+ (SQLPOINTER) offset (b.buffer, last, ret_->skip),
+ capacity_lookup[b.type],
+ offset (b.size_ind, last, ret_->skip));
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+
+ if (returning_version_)
+ {
+ bind& b (ret_->bind[ret_->count - 1]); // Version is the last element.
+
+ r = SQLBindCol (stmt_,
+ col++,
+ c_type_lookup[b.type],
+ (SQLPOINTER) offset (b.buffer, last, ret_->skip),
+ capacity_lookup[b.type],
+ offset (b.size_ind, last, ret_->skip));
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+ }
+
+ size_t insert_statement::
+ execute (size_t n, multiple_exceptions* mex)
+ {
+ // The batch INSERT works in two different ways, depending on
+ // whether we have the OUTPUT clause. If there is no OUTPUT, then
+ // all the parameter sets are processed inside the SQLExecute()
+ // call. If, however, there is OUTPUT, then the sets are
+ // processed one at a time as we consume the results with
+ // the SQLMoreResults() call below. Thus we in effect have
+ // two counts: the "processed so far" as set by the API
+ // (SQL_ATTR_PARAMS_PROCESSED_PTR) and the "to be processed"
+ // (value in n_). Note that in the OUTPUT case if there is an
+ // error, the processed count seems to jump by 2 for some reason.
+ //
+ // The OUTPUT case can be handled in two different ways: we can
+ // "execute" (with SQLMoreResults()) each set as the user moves
+ // from one result to the next (result() call). The advantage of
+ // this approach is that the returned data ends up in the right
+ // place automatically. The potential drawback is that the total
+ // affected row count will only be available at the end. As a
+ // result, this approach probably won't work if we need to handle,
+ // say, UPDATE with OUTPUT (SQLRowCount() does not return an
+ // intermediate total, at least not for INSERT).
+ //
+ // The alternative implementation would call SQLMoreResults()
+ // inside execute() until all the parameter sets are executed.
+ // In this case we will have to copy the extracted data into
+ // the right place in the bindings (or update the binding before
+ // each call to SQLMoreResults()). It is also not clear whether
+ // the diagnostic records for the failed sets would accumulate.
+ // If not, those will have to be stashed into mex on each
+ // iteration.
+ //
+ SQLRETURN r (bulk_statement::execute (n, mex));
+
+ // Statement failed as a whole, assume no parameter sets were
+ // attempted in case of a batch.
+ //
+ if (!SQL_SUCCEEDED (r))
+ {
+ fetch (r);
+ return n_;
+ }
+
+ if (status_ == 0) // Non-batch case.
+ fetch (SQL_SUCCESS);
+ else
+ fetch (status_[i_] == SQL_PARAM_SUCCESS ||
+ status_[i_] == SQL_PARAM_SUCCESS_WITH_INFO
+ ? SQL_SUCCESS : SQL_ERROR);
+
+ return n_;
+ }
+
+ void insert_statement::
+ fetch (SQLRETURN r)
+ {
+ result_ = true;
+
+ if (!SQL_SUCCEEDED (r))
+ {
+ // An auto-assigned object id should never cause a duplicate primary
+ // key.
+ //
+ if (!returning_id_)
+ {
+ // Translate the integrity contraint violation (SQLSTATE 23000)
+ // to the false result value. This code is similar to that found
+ // in translate_error().
+ //
+ char sqlstate[SQL_SQLSTATE_SIZE + 1];
+ SQLINTEGER native_code;
+ SQLSMALLINT msg_size;
+
+ bool cv (false);
+
+ for (SQLSMALLINT i (1);; ++i)
+ {
+ SQLRETURN r;
+
+ // Filter based on row association.
+ //
+ if (mex_ != 0)
+ {
+ SQLLEN n;
+ r = SQLGetDiagField (SQL_HANDLE_STMT,
+ stmt_,
+ i,
+ SQL_DIAG_ROW_NUMBER,
+ &n,
+ 0,
+ 0);
+
+ if (r == SQL_NO_DATA)
+ break;
+ else if (!SQL_SUCCEEDED (r))
+ continue;
+
+ if (n == SQL_NO_ROW_NUMBER ||
+ n == SQL_ROW_NUMBER_UNKNOWN ||
+ n != static_cast<SQLLEN> (i_ + 1)) // 1-based
+ continue;
+ }
+
+ r= SQLGetDiagRecA (SQL_HANDLE_STMT,
+ stmt_,
+ i,
+ (SQLCHAR*) sqlstate,
+ &native_code,
+ 0,
+ 0,
+ &msg_size);
+
+ if (r == SQL_NO_DATA)
+ break;
+ else if (SQL_SUCCEEDED (r))
+ {
+ string s (sqlstate);
+
+ if (s == "23000") // Integrity contraint violation.
+ cv = true;
+ else if (s != "01000") // General warning.
+ {
+ // Some other code.
+ //
+ cv = false;
+ break;
+ }
+ }
+ else // SQLGetDiagRec() failure.
+ {
+ cv = false;
+ break;
+ }
+ }
+
+ if (cv)
+ result_ = false;
+ }
+
+ if (result_)
+ {
+ translate_error (r, conn_, stmt_, i_, mex_); // Can return.
+ result_ = false; // Prevent id/version extraction below or.
+ }
+ }
+
+ // Fetch the row containing the id/version if this statement is
+ // returning.
+ //
+ if (result_ && ret_ != 0)
+ {
+ if (text_batch_)
+ {
+ r = SQLMoreResults (stmt_);
+
+ if (r == SQL_NO_DATA)
+ {
+ throw database_exception (
+ 0,
+ "?????",
+ "multiple result sets expected from a batch of statements");
+ }
+ else if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+
+ r = SQLFetch (stmt_);
+
+ if (r != SQL_NO_DATA && !SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ if (r == SQL_NO_DATA)
+ throw database_exception (
+ 0,
+ "?????",
+ "result set expected from a statement with the OUTPUT clause");
+
+ // See init_result() for details on what's going here.
+ //
+ size_t last (ret_->batch - 1);
+ if (i_ != last)
+ {
+ if (returning_id_)
+ {
+ bind& b (ret_->bind[0]); // Auto id is the first element.
+
+ memcpy (offset (b.buffer, i_, ret_->skip),
+ offset (b.buffer, last, ret_->skip),
+ capacity_lookup[b.type]);
+
+ memcpy (offset (b.size_ind, i_, ret_->skip),
+ offset (b.size_ind, last, ret_->skip),
+ sizeof (*b.size_ind));
+ }
+
+ if (returning_version_)
+ {
+ bind& b (ret_->bind[ret_->count - 1]); // Version is the last.
+
+ memcpy (offset (b.buffer, i_, ret_->skip),
+ offset (b.buffer, last, ret_->skip),
+ capacity_lookup[b.type]);
+
+ memcpy (offset (b.size_ind, i_, ret_->skip),
+ offset (b.size_ind, last, ret_->skip),
+ sizeof (*b.size_ind));
+ }
+ }
+ }
+ }
+
+ bool insert_statement::
+ result (size_t i)
+ {
+ assert ((i_ == i || i_ + 1 == i) && i < n_);
+
+ SQLRETURN r;
+
+ // Get to the next result set if necessary.
+ //
+ if (i != i_)
+ {
+ mex_->current (++i_); // mex cannot be NULL since this is a batch.
+
+ // Only in case of the OUTPUT clause do we have multiple result sets.
+ //
+ if (ret_ != 0)
+ {
+ r = SQLMoreResults (stmt_);
+
+ // The actually processed count could have changed (see execute()).
+ //
+ mex_->attempted (processed_);
+
+ if (r == SQL_NO_DATA)
+ {
+ throw database_exception (
+ 0,
+ "?????",
+ "multiple result sets expected from an array of parameters");
+ }
+ }
+
+ fetch (status_[i_] == SQL_PARAM_SUCCESS ||
+ status_[i_] == SQL_PARAM_SUCCESS_WITH_INFO
+ ? SQL_SUCCESS : SQL_ERROR);
+ }
+
+ // Close the cursor if we are done.
+ //
+ if (ret_ != 0 && i_ + 1 == n_)
+ {
+ // Use SQLFreeStmt(SQL_CLOSE) instead of SQLCloseCursor() to avoid
+ // an error if a cursor is not open. This seem to happen if the
+ // statement failure was translated to a parameter set failure in
+ // bulk_statement for batches of one.
+ //
+ r = SQLFreeStmt (stmt_, SQL_CLOSE);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+
+ return result_;
+ }
+
+ //
+ // update_statement
+ //
+
+ update_statement::
+ ~update_statement ()
+ {
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ binding& param,
+ binding* returning)
+ : bulk_statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false,
+ param.batch, param.skip, param.status),
+ unique_ (false),
+ returning_ (returning != 0)
+ {
+ assert (param.batch == 1); // Specify unique_hint explicitly.
+ init (param, returning);
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const string& text,
+ bool unique,
+ bool process,
+ binding& param,
+ binding* returning)
+ : bulk_statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false,
+ param.batch, param.skip, param.status),
+ unique_ (unique),
+ returning_ (returning != 0)
+ {
+ init (param, returning);
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ binding& param,
+ binding* returning,
+ bool copy_text)
+ : bulk_statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false,
+ param.batch, param.skip, param.status,
+ copy_text),
+ unique_ (false),
+ returning_ (returning != 0)
+ {
+ assert (param.batch == 1); // Specify unique_hint explicitly.
+ init (param, returning);
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const char* text,
+ bool unique,
+ bool process,
+ binding& param,
+ binding* returning,
+ bool copy_text)
+ : bulk_statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false,
+ param.batch, param.skip, param.status,
+ copy_text),
+ unique_ (unique),
+ returning_ (returning != 0)
+ {
+ init (param, returning);
+ }
+
+ void update_statement::
+ init (binding& param, binding* ret)
+ {
+ if (!empty ())
+ {
+ bind_param (param.bind, param.count);
+
+ if (ret != 0)
+ {
+ bind& b (ret->bind[ret->count - 1]); // Version is the last element.
+
+ SQLRETURN r (SQLBindCol (stmt_,
+ 1,
+ c_type_lookup[b.type],
+ (SQLPOINTER) b.buffer,
+ capacity_lookup[b.type],
+ b.size_ind));
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+ }
+ }
+
+ size_t update_statement::
+ execute (size_t n, multiple_exceptions* mex)
+ {
+ // In batch UPDATE without the OUTPUT clause (which is the
+ // only kind we currently support) all the parameter sets
+ // are processed inside SQLExecute() and the total count of
+ // affected rows is available after it returns.
+ //
+ assert (!returning_ || status_ == 0);
+
+ SQLRETURN r (bulk_statement::execute (n, mex));
+
+ // Statement failed as a whole, assume no parameter sets were
+ // attempted in case of a batch.
+ //
+ if (!(SQL_SUCCEEDED (r) || r == SQL_NO_DATA))
+ {
+ translate_error (r, conn_, stmt_, 0, mex_);
+ return n_;
+ }
+
+ if (status_ == 0) // Non-batch case.
+ {
+ // Fetch the row containing the data if this statement is
+ // returning. We still need to close the cursor even if we
+ // haven't updated any rows.
+ //
+ if (returning_)
+ {
+ r = SQLFetch (stmt_);
+
+ if (r != SQL_NO_DATA && !SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ // We have to get the result after fetching the OUTPUT data
+ // but before closing the cursor.
+ //
+ result_ = affected (SQL_SUCCESS, 0, unique_);
+
+ {
+ SQLRETURN r (SQLCloseCursor (stmt_)); // Don't overwrite r.
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+
+ if (result_ != 0 && r == SQL_NO_DATA)
+ throw database_exception (
+ 0,
+ "?????",
+ "result set expected from a statement with the OUTPUT clause");
+ }
+ else
+ result_ = affected (r, 0, unique_);
+ }
+ else
+ {
+ // Extract error information for failed parameter sets. If we do
+ // this after calling SQLRowCount(), all the diagnostics records
+ // that we need will be gone.
+ //
+ size_t errors (extract_errors ());
+
+ // Figure out the affected row count.
+ //
+ result_ = affected (r, errors, unique_);
+ }
+
+ return n_;
+ }
+
+ //
+ // delete_statement
+ //
+
+ delete_statement::
+ ~delete_statement ()
+ {
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const string& text,
+ binding& param)
+ : bulk_statement (conn,
+ text, statement_delete,
+ 0, false,
+ param.batch, param.skip, param.status),
+ unique_ (false)
+ {
+ assert (param.batch == 1); // Specify unique_hint explicitly.
+ bind_param (param.bind, param.count);
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const string& text,
+ bool unique,
+ binding& param)
+ : bulk_statement (conn,
+ text, statement_delete,
+ 0, false,
+ param.batch, param.skip, param.status),
+ unique_ (unique)
+ {
+ bind_param (param.bind, param.count);
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const char* text,
+ binding& param,
+ bool copy_text)
+ : bulk_statement (conn,
+ text, statement_delete,
+ 0, false,
+ param.batch, param.skip, param.status,
+ copy_text),
+ unique_ (false)
+ {
+ assert (param.batch == 1); // Specify unique_hint explicitly.
+ bind_param (param.bind, param.count);
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const char* text,
+ bool unique,
+ binding& param,
+ bool copy_text)
+ : bulk_statement (conn,
+ text, statement_delete,
+ 0, false,
+ param.batch, param.skip, param.status,
+ copy_text),
+ unique_ (unique)
+ {
+ bind_param (param.bind, param.count);
+ }
+
+ size_t delete_statement::
+ execute (size_t n, multiple_exceptions* mex)
+ {
+ // In batch DELETE without the OUTPUT clause (which is the
+ // only kind we currently support) all the parameter sets
+ // are processed inside SQLExecute() and the total count of
+ // affected rows is available after it returns.
+ //
+
+ SQLRETURN r (bulk_statement::execute (n, mex));
+
+ // Statement failed as a whole, assume no parameter sets were
+ // attempted in case of a batch.
+ //
+ if (!(SQL_SUCCEEDED (r) || r == SQL_NO_DATA))
+ {
+ translate_error (r, conn_, stmt_, 0, mex_);
+ return n_;
+ }
+
+ // Extract error information for failed parameter sets. If we do
+ // this after calling SQLRowCount(), all the diagnostics records
+ // that we need will be gone.
+ //
+ size_t errors (status_ != 0 ? extract_errors () : 0);
+
+ // Figure out the affected row count.
+ //
+ result_ = affected (r, errors, unique_);
+
+ return n_;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/statement.hxx b/libodb-mssql/odb/mssql/statement.hxx
new file mode 100644
index 0000000..74326a0
--- /dev/null
+++ b/libodb-mssql/odb/mssql/statement.hxx
@@ -0,0 +1,558 @@
+// file : odb/mssql/statement.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_STATEMENT_HXX
+#define ODB_MSSQL_STATEMENT_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+
+#include <odb/statement.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/binding.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/auto-handle.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class connection;
+
+ class LIBODB_MSSQL_EXPORT statement: public odb::statement
+ {
+ public:
+ typedef mssql::connection connection_type;
+
+ virtual
+ ~statement () = 0;
+
+ SQLHSTMT
+ handle () const
+ {
+ return stmt_;
+ }
+
+ virtual const char*
+ text () const;
+
+ virtual connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // A statement can be empty. This is used to handle situations
+ // where a SELECT or UPDATE statement ends up not having any
+ // columns after processing. An empty statement cannot be
+ // executed.
+ //
+ bool
+ empty () const
+ {
+ return stmt_ == 0;
+ }
+
+ protected:
+ // We keep two versions to take advantage of std::string COW.
+ //
+ statement (connection_type&,
+ const std::string& text,
+ statement_kind,
+ const binding* process,
+ bool optimize);
+
+ statement (connection_type&,
+ const char* text,
+ statement_kind,
+ const binding* process,
+ bool optimize,
+ bool copy_text);
+
+ private:
+ void
+ init (std::size_t text_size,
+ statement_kind,
+ const binding* process,
+ bool optimize);
+
+ // Custom implementation for SQL Server that also moves long data
+ // columns to the end.
+ //
+ static void
+ process_select (std::string& result,
+ const char* statement,
+ const bind*,
+ std::size_t bind_size,
+ bool optimize);
+
+ protected:
+ void
+ bind_param (bind*, std::size_t count);
+
+ // Return the actual number of columns bound.
+ //
+ SQLUSMALLINT
+ bind_result (bind*, std::size_t count, SQLUSMALLINT& long_count);
+
+ SQLRETURN
+ execute ();
+
+ // The old_base and new_base arguments can be used to "re-base"
+ // the long_callback struct pointer (stored in bind::buffer).
+ // This is used by the query machinery to cause stream_result()
+ // to use the callback information from a copy of the image
+ // instead of the bound image.
+ //
+ void
+ stream_result (SQLUSMALLINT start_col,
+ bind*,
+ std::size_t count,
+ void* old_base = 0,
+ void* new_base = 0);
+
+ protected:
+ connection_type& conn_;
+ std::string text_copy_;
+ const char* text_;
+ auto_handle<SQL_HANDLE_STMT> stmt_;
+ };
+
+ class LIBODB_MSSQL_EXPORT bulk_statement: public statement
+ {
+ public:
+ virtual
+ ~bulk_statement () = 0;
+
+ protected:
+ bulk_statement (connection_type&,
+ const std::string& text,
+ statement_kind,
+ const binding* process,
+ bool optimize,
+ std::size_t batch,
+ std::size_t skip,
+ SQLUSMALLINT* status);
+
+ bulk_statement (connection_type&,
+ const char* text,
+ statement_kind,
+ const binding* process,
+ bool optimize,
+ std::size_t batch,
+ std::size_t skip,
+ SQLUSMALLINT* status,
+ bool copy_text);
+
+ // Call SQLExecute() and set up the batch tracking variables (see
+ // below). Note that this function does not treat SQL_NO_DATA as
+ // an error since for DELETE and UPDATE statements this is a
+ // shortcut notation for zero rows affected.
+ //
+ SQLRETURN
+ execute (std::size_t n, multiple_exceptions*);
+
+ // Return the number of failed parameter sets.
+ //
+ std::size_t
+ extract_errors ();
+
+ static const unsigned long long result_unknown = ~0ULL;
+
+ unsigned long long
+ affected (SQLRETURN, std::size_t errors, bool unique);
+
+ private:
+ void
+ init (std::size_t skip);
+
+ protected:
+ SQLULEN processed_; // Number of parameter sets processed so far.
+ SQLUSMALLINT* status_; // Parameter sets status array.
+ std::size_t n_; // Actual batch size.
+ std::size_t i_; // Position in result.
+ multiple_exceptions* mex_;
+ };
+
+ class LIBODB_MSSQL_EXPORT select_statement: public statement
+ {
+ public:
+ virtual
+ ~select_statement ();
+
+ // While the long data columns can appear in any order in the
+ // result binding, they should appear last in the statement
+ // text.
+ //
+ select_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ binding& param,
+ binding& result);
+
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ bool optimize_text,
+ binding& param,
+ binding& result,
+ bool copy_text = true);
+
+ select_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ binding& result);
+
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ bool optimize_text,
+ binding& result,
+ bool copy_text = true);
+
+ enum result
+ {
+ success,
+ no_data
+ };
+
+ void
+ execute ();
+
+ result
+ fetch ();
+
+ // Return true if any long data was streamed.
+ //
+ bool
+ stream_result (void* old_base = 0, void* new_base = 0)
+ {
+ if (long_count_ != 0)
+ statement::stream_result (result_count_,
+ result_.bind,
+ result_.count,
+ old_base,
+ new_base);
+ return long_count_ != 0;
+ }
+
+ void
+ free_result ();
+
+ private:
+ select_statement (const select_statement&);
+ select_statement& operator= (const select_statement&);
+
+ private:
+ binding& result_;
+ SQLUSMALLINT result_count_; // Actual number of columns bound.
+ SQLUSMALLINT long_count_; // Number of long data columns.
+ SQLUSMALLINT columns_; // Number of columns in result set.
+ };
+
+ struct LIBODB_MSSQL_EXPORT auto_result
+ {
+ explicit auto_result (select_statement& s): s_ (&s) {}
+ ~auto_result () {free ();}
+
+ // Extended interface to support delayed freeing.
+ //
+ auto_result (): s_ (0) {}
+
+ void
+ set (select_statement& s) {s_ = &s;}
+
+ void
+ free ()
+ {
+ if (s_ != 0)
+ {
+ s_->free_result ();
+ s_ = 0;
+ }
+ }
+
+ void
+ release () {s_ = 0;}
+
+ private:
+ auto_result (const auto_result&);
+ auto_result& operator= (const auto_result&);
+
+ private:
+ select_statement* s_;
+ };
+
+ class LIBODB_MSSQL_EXPORT insert_statement: public bulk_statement
+ {
+ public:
+ virtual
+ ~insert_statement ();
+
+ insert_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ binding& param,
+ bool returning_id,
+ bool returning_version,
+ binding* returning);
+
+ insert_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ binding& param,
+ bool returning_id,
+ bool returning_version,
+ binding* returning,
+ bool copy_text = true);
+
+ // Return the number of parameter sets (out of n) that were attempted.
+ //
+ std::size_t
+ execute (std::size_t n, multiple_exceptions& mex)
+ {
+ return execute (n, &mex);
+ }
+
+ // Return true if successful and false if this row is a duplicate.
+ // All other errors are reported by throwing exceptions.
+ //
+ bool
+ result (std::size_t i);
+
+ bool
+ execute ()
+ {
+ execute (1, 0);
+ return result (0);
+ }
+
+ private:
+ insert_statement (const insert_statement&);
+ insert_statement& operator= (const insert_statement&);
+
+ private:
+ void
+ init_result ();
+
+ std::size_t
+ execute (std::size_t, multiple_exceptions*);
+
+ void
+ fetch (SQLRETURN);
+
+ private:
+ bool returning_id_;
+ bool returning_version_;
+ binding* ret_;
+ bool text_batch_;
+
+ bool result_;
+ };
+
+ class LIBODB_MSSQL_EXPORT update_statement: public bulk_statement
+ {
+ public:
+ virtual
+ ~update_statement ();
+
+ // SQL Server native client ODBC driver does not expose individual
+ // affected row counts for batch operations, even though it says it
+ // does (SQLGetInfo(SQL_PARAM_ARRAY_ROW_COUNTS) returns SQL_PARC_BATCH).
+ // Instead, it adds them all up and returns a single count. This is
+ // bad news for us.
+ //
+ // In case of updating by primary key (the affected row count is
+ // either 1 or 0), we can recognize the presumably successful case
+ // where the total affected row count is equal to the batch size
+ // (we can also recognize the "all unsuccessful" case where the
+ // total affected row count is 0). The unique_hint argument in the
+ // constructors below indicates whether this is a "0 or 1" UPDATE
+ // statement.
+ //
+ // In all other situations (provided this is a batch), the result()
+ // function below returns the special result_unknown value.
+ //
+ update_statement (connection_type& conn,
+ const std::string& text,
+ bool process,
+ binding& param,
+ binding* returning);
+
+ update_statement (connection_type& conn,
+ const std::string& text,
+ bool unique_hint,
+ bool process,
+ binding& param,
+ binding* returning);
+
+ update_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ binding& param,
+ binding* returning,
+ bool copy_text = true);
+
+ update_statement (connection_type& conn,
+ const char* text,
+ bool unique_hint,
+ bool process,
+ binding& param,
+ binding* returning,
+ bool copy_text = true);
+
+ // Return the number of parameter sets (out of n) that were attempted.
+ //
+ std::size_t
+ execute (std::size_t n, multiple_exceptions& mex)
+ {
+ return execute (n, &mex);
+ }
+
+ // Return the number of rows affected (updated) by the parameter
+ // set. If this is a batch (n > 1 in execute() call above) and it
+ // is impossible to determine the affected row count for each
+ // parameter set, then this function returns result_unknown. All
+ // other errors are reported by throwing exceptions.
+ //
+ using bulk_statement::result_unknown;
+
+ unsigned long long
+ result (std::size_t i)
+ {
+ if (i != i_)
+ mex_->current (++i_); // mex cannot be NULL since this is a batch.
+
+ return result_;
+ }
+
+ unsigned long long
+ execute ()
+ {
+ execute (1, 0);
+ return result (0);
+ }
+
+ private:
+ update_statement (const update_statement&);
+ update_statement& operator= (const update_statement&);
+
+ private:
+ void
+ init (binding& param, binding* ret);
+
+ std::size_t
+ execute (std::size_t, multiple_exceptions*);
+
+ private:
+ bool unique_;
+ bool returning_;
+
+ unsigned long long result_;
+ };
+
+ class LIBODB_MSSQL_EXPORT delete_statement: public bulk_statement
+ {
+ public:
+ virtual
+ ~delete_statement ();
+
+ // SQL Server native client ODBC driver does not expose individual
+ // affected row counts for batch operations, even though it says it
+ // does (SQLGetInfo(SQL_PARAM_ARRAY_ROW_COUNTS) returns SQL_PARC_BATCH).
+ // Instead, it adds them all up and returns a single count. This is
+ // bad news for us.
+ //
+ // In case of deleting by primary key (the affected row count is
+ // either 1 or 0), we can recognize the presumably successful case
+ // where the total affected row count is equal to the batch size
+ // (we can also recognize the "all unsuccessful" case where the
+ // total affected row count is 0). The unique_hint argument in the
+ // constructors below indicates whether this is a "0 or 1" DELETE
+ // statement.
+ //
+ // In all other situations (provided this is a batch), the result()
+ // function below returns the special result_unknown value.
+ //
+ delete_statement (connection_type& conn,
+ const std::string& text,
+ binding& param);
+
+ delete_statement (connection_type& conn,
+ const std::string& text,
+ bool unique_hint,
+ binding& param);
+
+ delete_statement (connection_type& conn,
+ const char* text,
+ binding& param,
+ bool copy_text = true);
+
+ delete_statement (connection_type& conn,
+ const char* text,
+ bool unique_hint,
+ binding& param,
+ bool copy_text = true);
+
+ // Return the number of parameter sets (out of n) that were attempted.
+ //
+ std::size_t
+ execute (std::size_t n, multiple_exceptions& mex)
+ {
+ return execute (n, &mex);
+ }
+
+ // Return the number of rows affected (deleted) by the parameter
+ // set. If this is a batch (n > 1 in execute() call above) and it
+ // is impossible to determine the affected row count for each
+ // parameter set, then this function returns result_unknown. All
+ // other errors are reported by throwing exceptions.
+ //
+ using bulk_statement::result_unknown;
+
+ unsigned long long
+ result (std::size_t i)
+ {
+ if (i != i_)
+ mex_->current (++i_); // mex cannot be NULL since this is a batch.
+
+ return result_;
+ }
+
+ unsigned long long
+ execute ()
+ {
+ execute (1, 0);
+ return result (0);
+ }
+
+ private:
+ delete_statement (const delete_statement&);
+ delete_statement& operator= (const delete_statement&);
+
+ private:
+ std::size_t
+ execute (std::size_t, multiple_exceptions*);
+
+ private:
+ bool unique_;
+ unsigned long long result_;
+ };
+ }
+}
+
+#include <odb/mssql/statement.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_STATEMENT_HXX
diff --git a/libodb-mssql/odb/mssql/statement.ixx b/libodb-mssql/odb/mssql/statement.ixx
new file mode 100644
index 0000000..12cce80
--- /dev/null
+++ b/libodb-mssql/odb/mssql/statement.ixx
@@ -0,0 +1,41 @@
+// file : odb/mssql/statement.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mssql
+ {
+ inline bulk_statement::
+ bulk_statement (connection_type& c,
+ const std::string& text,
+ statement_kind k,
+ const binding* process,
+ bool optimize,
+ std::size_t batch,
+ std::size_t skip,
+ SQLUSMALLINT* status)
+ : statement (c, text, k, process, optimize),
+ status_ (batch == 1 ? 0 : status)
+ {
+ if (status_ != 0 && !empty ())
+ init (skip);
+ }
+
+ inline bulk_statement::
+ bulk_statement (connection_type& c,
+ const char* text,
+ statement_kind k,
+ const binding* process,
+ bool optimize,
+ std::size_t batch,
+ std::size_t skip,
+ SQLUSMALLINT* status,
+ bool copy_text)
+ : statement (c, text, k, process, optimize, copy_text),
+ status_ (batch == 1 ? 0 : status)
+ {
+ if (status_ != 0 && !empty ())
+ init (skip);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/statements-base.cxx b/libodb-mssql/odb/mssql/statements-base.cxx
new file mode 100644
index 0000000..9f302e3
--- /dev/null
+++ b/libodb-mssql/odb/mssql/statements-base.cxx
@@ -0,0 +1,15 @@
+// file : odb/mssql/statements-base.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/statements-base.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ statements_base::
+ ~statements_base ()
+ {
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/statements-base.hxx b/libodb-mssql/odb/mssql/statements-base.hxx
new file mode 100644
index 0000000..4506628
--- /dev/null
+++ b/libodb-mssql/odb/mssql/statements-base.hxx
@@ -0,0 +1,63 @@
+// file : odb/mssql/statements-base.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_STATEMENTS_BASE_HXX
+#define ODB_MSSQL_STATEMENTS_BASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/schema-version.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/database.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class LIBODB_MSSQL_EXPORT statements_base: public details::shared_base
+ {
+ public:
+ typedef mssql::connection connection_type;
+
+ connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // Schema version. database::schema_version_migration() is thread-
+ // safe which means it is also slow. Cache the result in statements
+ // so we can avoid the mutex lock. This is thread-safe since if the
+ // version is updated, then the statements cache will be expired.
+ //
+ const schema_version_migration&
+ version_migration (const char* name = "") const
+ {
+ if (svm_ == 0)
+ svm_ = &conn_.database ().schema_version_migration (name);
+
+ return *svm_;
+ }
+
+ public:
+ virtual
+ ~statements_base ();
+
+ protected:
+ statements_base (connection_type& conn): conn_ (conn), svm_ (0) {}
+
+ protected:
+ connection_type& conn_;
+ mutable const schema_version_migration* svm_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_STATEMENTS_BASE_HXX
diff --git a/libodb-mssql/odb/mssql/tracer.cxx b/libodb-mssql/odb/mssql/tracer.cxx
new file mode 100644
index 0000000..3fe62c9
--- /dev/null
+++ b/libodb-mssql/odb/mssql/tracer.cxx
@@ -0,0 +1,60 @@
+// file : odb/mssql/tracer.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/tracer.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/statement.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ tracer::
+ ~tracer ()
+ {
+ }
+
+ void tracer::
+ prepare (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ execute (connection& c, const statement& s)
+ {
+ execute (c, s.text ());
+ }
+
+ void tracer::
+ deallocate (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ prepare (odb::connection& c, const odb::statement& s)
+ {
+ prepare (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const odb::statement& s)
+ {
+ execute (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const char* s)
+ {
+ execute (static_cast<connection&> (c), s);
+ }
+
+ void tracer::
+ deallocate (odb::connection& c, const odb::statement& s)
+ {
+ deallocate (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/tracer.hxx b/libodb-mssql/odb/mssql/tracer.hxx
new file mode 100644
index 0000000..feaf5f0
--- /dev/null
+++ b/libodb-mssql/odb/mssql/tracer.hxx
@@ -0,0 +1,61 @@
+// file : odb/mssql/tracer.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_TRACER_HXX
+#define ODB_MSSQL_TRACER_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/tracer.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class LIBODB_MSSQL_EXPORT tracer: private odb::tracer
+ {
+ public:
+ virtual
+ ~tracer ();
+
+ 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&);
+
+ private:
+ // Allow these classes to convert mssql::tracer to odb::tracer.
+ //
+ friend class database;
+ friend class connection;
+ friend class transaction;
+
+ virtual void
+ prepare (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const char* statement);
+
+ virtual void
+ deallocate (odb::connection&, const odb::statement&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_TRACER_HXX
diff --git a/libodb-mssql/odb/mssql/traits-calls.hxx b/libodb-mssql/odb/mssql/traits-calls.hxx
new file mode 100644
index 0000000..6b20dbd
--- /dev/null
+++ b/libodb-mssql/odb/mssql/traits-calls.hxx
@@ -0,0 +1,190 @@
+// file : odb/mssql/traits-calls.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_TRAITS_CALLS_HXX
+#define ODB_MSSQL_TRAITS_CALLS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/mssql-types.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // object_traits_calls
+ //
+
+ template <typename T,
+ bool versioned = object_traits_impl<T, id_mssql>::versioned>
+ struct object_traits_calls;
+
+ template <typename T>
+ struct object_traits_calls<T, false>
+ {
+ typedef object_traits_impl<T, id_mssql> traits;
+ typedef typename traits::image_type image_type;
+ typedef mssql::bind bind_type;
+
+ object_traits_calls (const schema_version_migration*) {}
+
+ const schema_version_migration*
+ version () const {return 0;}
+
+ static void
+ bind (bind_type* b, image_type& i, statement_kind sk)
+ {
+ traits::bind (b, i, sk);
+ }
+
+ // Poly-derived version.
+ //
+ static void
+ bind (bind_type* b,
+ const bind_type* id, std::size_t id_size,
+ image_type& i,
+ statement_kind sk)
+ {
+ traits::bind (b, id, id_size, i, sk);
+ }
+
+ static void
+ init (T& o, const image_type& i, odb::database* db)
+ {
+ traits::init (o, i, db);
+ }
+
+ static bool
+ find_ (typename traits::statements_type& sts,
+ const typename traits::id_type* id)
+ {
+ return traits::find_ (sts, id);
+ }
+
+ static void
+ load_ (typename traits::statements_type& sts, T& o, bool reload)
+ {
+ return traits::load_ (sts, o, reload);
+ }
+ };
+
+ template <typename T>
+ struct object_traits_calls<T, true>
+ {
+ typedef object_traits_impl<T, id_mssql> traits;
+ typedef typename traits::image_type image_type;
+ typedef mssql::bind bind_type;
+
+ object_traits_calls (const schema_version_migration* svm): svm_ (*svm) {}
+
+ const schema_version_migration*
+ version () const {return &svm_;}
+
+ void
+ bind (bind_type* b, image_type& i, statement_kind sk) const
+ {
+ traits::bind (b, i, sk, svm_);
+ }
+
+ // Poly-derived version.
+ //
+ void
+ bind (bind_type* b,
+ const bind_type* id, std::size_t id_size,
+ image_type& i,
+ statement_kind sk) const
+ {
+ traits::bind (b, id, id_size, i, sk, svm_);
+ }
+
+ void
+ init (T& o, const image_type& i, odb::database* db) const
+ {
+ traits::init (o, i, db, svm_);
+ }
+
+ bool
+ find_ (typename traits::statements_type& sts,
+ const typename traits::id_type* id) const
+ {
+ return traits::find_ (sts, id, svm_);
+ }
+
+ void
+ load_ (typename traits::statements_type& sts, T& o, bool reload) const
+ {
+ return traits::load_ (sts, o, reload, svm_);
+ }
+
+ private:
+ const schema_version_migration& svm_;
+ };
+
+ //
+ // view_traits_calls
+ //
+
+ template <typename T,
+ bool versioned = view_traits_impl<T, id_mssql>::versioned>
+ struct view_traits_calls;
+
+ template <typename T>
+ struct view_traits_calls<T, false>
+ {
+ typedef view_traits_impl<T, id_mssql> traits;
+ typedef typename traits::image_type image_type;
+ typedef mssql::bind bind_type;
+
+ view_traits_calls (const schema_version_migration*) {}
+
+ static void
+ bind (bind_type* b, image_type& i)
+ {
+ traits::bind (b, i);
+ }
+
+ static void
+ init (T& o, const image_type& i, odb::database* db)
+ {
+ traits::init (o, i, db);
+ }
+ };
+
+ template <typename T>
+ struct view_traits_calls<T, true>
+ {
+ typedef view_traits_impl<T, id_mssql> traits;
+ typedef typename traits::image_type image_type;
+ typedef mssql::bind bind_type;
+
+ view_traits_calls (const schema_version_migration* svm): svm_ (*svm) {}
+
+ void
+ bind (bind_type* b, image_type& i) const
+ {
+ traits::bind (b, i, svm_);
+ }
+
+ void
+ init (T& o, const image_type& i, odb::database* db) const
+ {
+ traits::init (o, i, db, svm_);
+ }
+
+ private:
+ const schema_version_migration& svm_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_TRAITS_CALLS_HXX
diff --git a/libodb-mssql/odb/mssql/traits.cxx b/libodb-mssql/odb/mssql/traits.cxx
new file mode 100644
index 0000000..9a9d4fa
--- /dev/null
+++ b/libodb-mssql/odb/mssql/traits.cxx
@@ -0,0 +1,616 @@
+// file : odb/mssql/traits.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/mssql/traits.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // c_array_value_traits_base
+ //
+ void c_array_value_traits_base::
+ set_value (char* const& v,
+ const char* b,
+ size_t n,
+ bool is_null,
+ size_t N)
+ {
+ if (!is_null)
+ {
+ n = n < N ? n : N;
+
+ if (n != 0)
+ memcpy (v, b, n);
+ }
+ else
+ n = 0;
+
+ if (n != N) // Append '\0' if there is space.
+ v[n] = '\0';
+ }
+
+ void c_array_value_traits_base::
+ set_image (char* b,
+ size_t c,
+ size_t& n,
+ bool& is_null,
+ const char* v,
+ size_t N)
+ {
+ is_null = false;
+
+ // Figure out the length. We cannot use strlen since it may
+ // not be 0-terminated (strnlen is not standard).
+ //
+ for (n = 0; n != N && v[n] != '\0'; ++n) ;
+
+ if (n > c)
+ n = c;
+
+ if (n != 0)
+ memcpy (b, v, n);
+ }
+
+ //
+ // default_value_traits<std::string, id_long_string>
+ //
+ void default_value_traits<string, id_long_string>::
+ param_callback (const void* context,
+ size_t*,
+ const void** buffer,
+ size_t* size,
+ chunk_type* chunk,
+ void*,
+ size_t)
+ {
+ const string& str (*static_cast<const string*> (context));
+
+ *buffer = str.c_str ();
+ *size = str.size ();
+ *chunk = chunk_one;
+ }
+
+ void default_value_traits<string, id_long_string>::
+ result_callback (void* context,
+ size_t* position,
+ void** buffer,
+ size_t* size,
+ chunk_type chunk,
+ size_t size_left,
+ void* tmp_buf,
+ size_t tmp_capacity)
+ {
+ string& str (*static_cast<string*> (context));
+
+ switch (chunk)
+ {
+ case chunk_null:
+ case chunk_one:
+ {
+ str.clear ();
+ break;
+ }
+ case chunk_first:
+ {
+ // If the total size is available, then pre-allocate the string
+ // and copy the data directly into its buffer in one go. While
+ // this kind of direct modification of the std::string buffer
+ // is not sanctioned by the standard, this is known to work
+ // with all the implementations we care to support. We just
+ // need to make sure the underlying buffer is not shared with
+ // any other instance if the implementation uses COW.
+ //
+ if (size_left != 0)
+ {
+ size_left++; // One extra for the null terminator.
+
+ if (str.size () != size_left)
+ str.resize (size_left);
+ else
+ str[0] = '\0'; // Force copy in a COW implementation.
+
+ *buffer = const_cast<char*> (str.c_str ());
+ *size = size_left;
+ *position = 0; // Indicator.
+ }
+ else
+ {
+ // If the total size is not available, do the short string
+ // optimization by first returning a small temporary buffer.
+ // If the data fits, then we copy it over to the string and
+ // thus get the precise buffer allocation. If the data does
+ // not fit, then we resort to the exponential buffer growth.
+ //
+ *buffer = tmp_buf;
+ *size = tmp_capacity > 128 ? 128 : tmp_capacity;
+ *position = 1; // Indicator.
+ }
+
+ break;
+ }
+ case chunk_next:
+ {
+ // We should only end up here if the short string optimization
+ // didn't work out.
+ //
+ assert (*position != 0);
+
+ if (*position == 1)
+ {
+ // First chunk_next call. There is some data in the temp
+ // buffer which we need to copy over.
+ //
+ str.reserve (256);
+ str.assign (static_cast<char*> (tmp_buf), *size);
+ *position = *size; // Size of actual data in str, which got to be
+ // greater than 128, or we would have gotten
+ // chunk_last.
+ str.resize (256);
+ }
+ else
+ {
+ // Subsequent chunk_next call. Double the buffer and continue.
+ //
+ *position += *size;
+ str.resize (str.size () * 2);
+ }
+
+ *buffer = const_cast<char*> (str.c_str ()) + *position;
+ *size = str.size () - *position;
+ break;
+ }
+ case chunk_last:
+ {
+ if (*position == 0)
+ str.resize (*size);
+ else if (*position == 1)
+ // Short string optimization worked out. There is some data
+ // in the temp buffer which we need to copy over.
+ //
+ str.assign (static_cast<char*> (tmp_buf), *size);
+ else
+ str.resize (*position + *size);
+
+ break;
+ }
+ }
+ }
+
+ //
+ // c_long_string_value_traits
+ //
+ void c_string_long_value_traits::
+ param_callback (const void* context,
+ size_t*,
+ const void** buffer,
+ size_t* size,
+ chunk_type* chunk,
+ void*,
+ size_t)
+ {
+ *buffer = context;
+ *size = strlen (static_cast<const char*> (context));
+ *chunk = chunk_one;
+ }
+
+ //
+ // c_warray_value_traits_base
+ //
+ void c_warray_value_traits_base::
+ set_value (wchar_t* const& v,
+ const ucs2_char* b,
+ size_t n,
+ bool is_null,
+ size_t N)
+ {
+ if (!is_null)
+ {
+ n = n < N ? n : N;
+
+ if (n != 0)
+ functions::assign (v, b, n);
+ }
+ else
+ n = 0;
+
+ if (n != N) // Append '\0' if there is space.
+ v[n] = L'\0';
+ }
+
+ void c_warray_value_traits_base::
+ set_image (ucs2_char* b,
+ size_t c,
+ size_t& n,
+ bool& is_null,
+ const wchar_t* v,
+ size_t N)
+ {
+ is_null = false;
+
+ // Figure out the length. We cannot use wcslen since it may
+ // not be 0-terminated (wcsnlen is not standard).
+ //
+ for (n = 0; n != N && v[n] != L'\0'; ++n) ;
+
+ if (n > c)
+ n = c;
+
+ if (n != 0)
+ functions::assign (b, v, n);
+ }
+
+ //
+ // wstring_long_value_traits<2>
+ //
+ void wstring_long_value_traits<2>::
+ param_callback (const void* context,
+ size_t*,
+ const void** buffer,
+ size_t* size,
+ chunk_type* chunk,
+ void*,
+ size_t)
+ {
+ const wstring& str (*static_cast<const wstring*> (context));
+
+ *buffer = str.c_str ();
+ *size = str.size () * 2; // In bytes.
+ *chunk = chunk_one;
+ }
+
+ void wstring_long_value_traits<2>::
+ result_callback (void* context,
+ size_t*,
+ void** buffer,
+ size_t* size,
+ chunk_type chunk,
+ size_t size_left,
+ void*,
+ size_t)
+ {
+ wstring& str (*static_cast<wstring*> (context));
+
+ switch (chunk)
+ {
+ case chunk_null:
+ case chunk_one:
+ {
+ str.clear ();
+ break;
+ }
+ case chunk_first:
+ {
+ // The Native Client ODBC driver seems to always be able to
+ // return the total size for national character strings. This
+ // makes things simple and efficient.
+ //
+ assert (size_left != 0);
+
+ size_left /= 2; // Convert to characters.
+ size_left++; // One extra for the null terminator.
+
+ if (str.size () != size_left)
+ str.resize (size_left);
+ else
+ str[0] = L'\0'; // Force copy in a COW implementation.
+
+ *buffer = const_cast<wchar_t*> (str.c_str ());
+ *size = size_left * 2; // In bytes.
+ break;
+ }
+ case chunk_next:
+ {
+ // We should never get here.
+ //
+ assert (false);
+ break;
+ }
+ case chunk_last:
+ {
+ str.resize (*size / 2); // Get rid of the null terminator.
+ break;
+ }
+ }
+ }
+
+ //
+ // wstring_long_value_traits<4>
+ //
+#ifndef _WIN32
+ void wstring_long_value_traits<4>::
+ param_callback (const void* context,
+ size_t* position,
+ const void** buffer,
+ size_t* size,
+ chunk_type* chunk,
+ void* tmp_buf,
+ size_t tmp_capacity)
+ {
+ const wstring& str (*static_cast<const wstring*> (context));
+
+ // Here we cannot just return the pointer to the underlying buffer
+ // since the character sizes are different. Instead we copy the
+ // data to the temporary buffer.
+ //
+ *buffer = tmp_buf;
+ *size = str.size () - *position; // In UCS-2 characters.
+
+ if (*size > tmp_capacity / 2)
+ {
+ *size = tmp_capacity / 2;
+ *chunk = chunk_next;
+ }
+ else
+ *chunk = chunk_last;
+
+ wstring_functions<>::assign (static_cast<ucs2_char*> (tmp_buf),
+ str.c_str () + *position,
+ *size);
+ if (*position == 0)
+ {
+ if (*chunk == chunk_last)
+ *chunk = chunk_one;
+ else
+ *chunk = chunk_first;
+ }
+
+ *position += *size;
+ *size *= 2; // Convert to bytes.
+ }
+
+ void wstring_long_value_traits<4>::
+ result_callback (void* context,
+ size_t*,
+ void** buffer,
+ size_t* size,
+ chunk_type chunk,
+ size_t size_left,
+ void* tmp_buf,
+ size_t tmp_capacity)
+ {
+ wstring& str (*static_cast<wstring*> (context));
+
+ // Again, we cannot do direct buffer copy and have to use the
+ // temporary buffer instead.
+ //
+ switch (chunk)
+ {
+ case chunk_null:
+ case chunk_one:
+ {
+ str.clear ();
+ break;
+ }
+ case chunk_first:
+ {
+ // The Native Client ODBC driver seems to always be able to
+ // return the total size for national character strings. Use
+ // this to reserve enough space in the string.
+ //
+ assert (size_left != 0);
+ str.reserve (size_left / 2);
+ break;
+ }
+ case chunk_next:
+ case chunk_last:
+ {
+ // Append the data from the temporary buffer.
+ //
+ ucs2_char* p (static_cast<ucs2_char*> (tmp_buf));
+ str.append (p, p + *size / 2);
+ break;
+ }
+ }
+
+ if (chunk == chunk_first || chunk == chunk_next)
+ {
+ *buffer = tmp_buf;
+ *size = tmp_capacity;
+ }
+ }
+#endif // _WIN32
+
+ //
+ // c_wstring_long_value_traits<2>
+ //
+ void c_wstring_long_value_traits<2>::
+ param_callback (const void* context,
+ size_t*,
+ const void** buffer,
+ size_t* size,
+ chunk_type* chunk,
+ void*,
+ size_t)
+ {
+ *buffer = context;
+ *size = wcslen (static_cast<const wchar_t*> (context)) * 2; // In bytes.
+ *chunk = chunk_one;
+ }
+
+ //
+ // c_wstring_long_value_traits<4>
+ //
+#ifndef _WIN32
+ void c_wstring_long_value_traits<4>::
+ param_callback (const void* context,
+ size_t* position,
+ const void** buffer,
+ size_t* size,
+ chunk_type* chunk,
+ void* tmp_buf,
+ size_t tmp_capacity)
+ {
+ const wchar_t* str (static_cast<const wchar_t*> (context));
+
+ // Here we cannot just return the pointer to the buffer since the
+ // character sizes are different. Instead we copy the data to the
+ // temporary buffer.
+ //
+ *buffer = tmp_buf;
+ *size = wcslen (str) - *position; // In UCS-2 characters.
+
+ if (*size > tmp_capacity / 2)
+ {
+ *size = tmp_capacity / 2;
+ *chunk = chunk_next;
+ }
+ else
+ *chunk = chunk_last;
+
+ wstring_functions<>::assign (static_cast<ucs2_char*> (tmp_buf),
+ str + *position,
+ *size);
+ if (*position == 0)
+ {
+ if (*chunk == chunk_last)
+ *chunk = chunk_one;
+ else
+ *chunk = chunk_first;
+ }
+
+ *position += *size;
+ *size *= 2; // Convert to bytes.
+ }
+#endif // _WIN32
+
+ //
+ // default_value_traits<std::vector<char>, id_long_binary>
+ //
+ // std::vector has to be qualified for Sun CC.
+ //
+ void default_value_traits<std::vector<char>, id_long_binary>::
+ param_callback (const void* context,
+ size_t*,
+ const void** buffer,
+ size_t* size,
+ chunk_type* chunk,
+ void*,
+ size_t)
+ {
+ const value_type& v (*static_cast<const value_type*> (context));
+
+ *buffer = v.empty () ? 0 : &v.front ();
+ *size = v.size ();
+ *chunk = chunk_one;
+ }
+
+ void default_value_traits<std::vector<char>, id_long_binary>::
+ result_callback (void* context,
+ size_t*,
+ void** buffer,
+ size_t* size,
+ chunk_type chunk,
+ size_t size_left,
+ void*,
+ size_t)
+ {
+ value_type& v (*static_cast<value_type*> (context));
+
+ switch (chunk)
+ {
+ case chunk_null:
+ case chunk_one:
+ {
+ v.clear ();
+ break;
+ }
+ case chunk_first:
+ {
+ // The Native Client ODBC driver seems to always be able to
+ // return the total size. This makes things simple and
+ // efficient.
+ //
+ assert (size_left != 0);
+
+ v.resize (size_left);
+ *buffer = &v.front ();
+ *size = size_left;
+ break;
+ }
+ case chunk_next:
+ {
+ // We should never get here.
+ //
+ assert (false);
+ break;
+ }
+ case chunk_last:
+ {
+ // Nothing to do here. The vector is already of the correct size
+ // and should contain the data.
+ break;
+ }
+ }
+ }
+
+ //
+ // default_value_traits<std::vector<unsigned char>, id_long_binary>
+ //
+ // std::vector has to be qualified for Sun CC.
+ //
+ void default_value_traits<std::vector<unsigned char>, id_long_binary>::
+ param_callback (const void* context,
+ size_t*,
+ const void** buffer,
+ size_t* size,
+ chunk_type* chunk,
+ void*,
+ size_t)
+ {
+ const value_type& v (*static_cast<const value_type*> (context));
+
+ *buffer = v.empty () ? 0 : &v.front ();
+ *size = v.size ();
+ *chunk = chunk_one;
+ }
+
+ void default_value_traits<std::vector<unsigned char>, id_long_binary>::
+ result_callback (void* context,
+ size_t*,
+ void** buffer,
+ size_t* size,
+ chunk_type chunk,
+ size_t size_left,
+ void*,
+ size_t)
+ {
+ // The code is exactly the same as in the vector<char> specialization.
+ //
+ value_type& v (*static_cast<value_type*> (context));
+
+ switch (chunk)
+ {
+ case chunk_null:
+ case chunk_one:
+ {
+ v.clear ();
+ break;
+ }
+ case chunk_first:
+ {
+ assert (size_left != 0);
+
+ v.resize (size_left);
+ *buffer = &v.front ();
+ *size = size_left;
+ break;
+ }
+ case chunk_next:
+ {
+ assert (false);
+ break;
+ }
+ case chunk_last:
+ {
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/traits.hxx b/libodb-mssql/odb/mssql/traits.hxx
new file mode 100644
index 0000000..f53e535
--- /dev/null
+++ b/libodb-mssql/odb/mssql/traits.hxx
@@ -0,0 +1,2176 @@
+// file : odb/mssql/traits.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_TRAITS_HXX
+#define ODB_MSSQL_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+#include <cstring> // std::memcpy, std::memset, std::strlen
+#include <cwchar> // std::wcslen
+
+#ifdef ODB_CXX11
+# include <array>
+#endif
+
+#ifdef _WIN32
+typedef struct _GUID GUID;
+#endif
+
+#include <odb/traits.hxx>
+#include <odb/wrapper-traits.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/mssql-types.hxx>
+
+#include <odb/details/buffer.hxx>
+#include <odb/details/wrapper-p.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ enum database_type_id
+ {
+ id_bit,
+ id_tinyint,
+ id_smallint,
+ id_int,
+ id_bigint,
+
+ id_decimal, // DECIMAL; NUMERIC
+
+ id_smallmoney,
+ id_money,
+
+ id_float4, // REAL; FLOAT(n) with n <= 24
+ id_float8, // FLOAT(n) with n > 24
+
+ id_string, // CHAR(n), VARCHAR(n) with n <= N
+ id_long_string, // CHAR(n), VARCHAR(n) with n > N; TEXT
+
+ id_nstring, // NCHAR(n), NVARCHAR(n) with 2*n <= N
+ id_long_nstring, // NCHAR(n), NVARCHAR(n) with 2*n > N; NTEXT
+
+ id_binary, // BINARY(n), VARBINARY(n) with n <= N
+ id_long_binary, // BINARY(n), VARBINARY(n) with n > N; IMAGE
+
+ id_date, // DATE
+ id_time, // TIME
+ id_datetime, // DATETIME; DATETIME2; SMALLDATETIME
+ id_datetimeoffset, // DATETIMEOFFSET
+
+ id_uniqueidentifier, // UNIQUEIDENTIFIER
+ id_rowversion // ROWVERSION; TIMESTAMP
+ };
+
+ //
+ // image_traits
+ //
+
+ template <database_type_id>
+ struct image_traits;
+
+ template <>
+ struct image_traits<id_bit> {typedef unsigned char image_type;};
+
+ template <>
+ struct image_traits<id_tinyint> {typedef unsigned char image_type;};
+
+ template <>
+ struct image_traits<id_smallint> {typedef short image_type;};
+
+ template <>
+ struct image_traits<id_int> {typedef int image_type;};
+
+ template <>
+ struct image_traits<id_bigint> {typedef long long image_type;};
+
+ template <>
+ struct image_traits<id_decimal> {typedef decimal image_type;};
+
+ template <>
+ struct image_traits<id_smallmoney> {typedef smallmoney image_type;};
+
+ template <>
+ struct image_traits<id_money> {typedef money image_type;};
+
+ template <>
+ struct image_traits<id_float4> {typedef float image_type;};
+
+ template <>
+ struct image_traits<id_float8> {typedef double image_type;};
+
+ template <>
+ struct image_traits<id_string> {typedef char* image_type;};
+
+ template <>
+ struct image_traits<id_long_string> {typedef long_callback image_type;};
+
+ template <>
+ struct image_traits<id_nstring> {typedef ucs2_char* image_type;};
+
+ template <>
+ struct image_traits<id_long_nstring> {typedef long_callback image_type;};
+
+ template <>
+ struct image_traits<id_binary> {typedef char* image_type;};
+
+ template <>
+ struct image_traits<id_long_binary> {typedef long_callback image_type;};
+
+ template <>
+ struct image_traits<id_date> {typedef date image_type;};
+
+ template <>
+ struct image_traits<id_time> {typedef time image_type;};
+
+ template <>
+ struct image_traits<id_datetime> {typedef datetime image_type;};
+
+ template <>
+ struct image_traits<id_datetimeoffset>
+ {
+ typedef datetimeoffset image_type;
+ };
+
+ template <>
+ struct image_traits<id_uniqueidentifier>
+ {
+ typedef uniqueidentifier image_type;
+ };
+
+ // Image is an 8-byte sequence.
+ //
+ template <>
+ struct image_traits<id_rowversion> {typedef unsigned char* image_type;};
+
+ //
+ // value_traits
+ //
+
+ template <typename W, database_type_id, bool null_handler>
+ struct wrapped_value_traits;
+
+ template <typename T, database_type_id>
+ struct default_value_traits;
+
+ template <typename T, database_type_id, bool w = details::wrapper_p<T>::r>
+ struct select_traits;
+
+ template <typename T, database_type_id ID>
+ struct select_traits<T, ID, false>
+ {
+ typedef default_value_traits<T, ID> type;
+ };
+
+ template <typename W, database_type_id ID>
+ struct select_traits<W, ID, true>
+ {
+ typedef
+ wrapped_value_traits<W, ID, wrapper_traits<W>::null_handler>
+ type;
+ };
+
+ template <typename T, database_type_id ID>
+ class value_traits: public select_traits<T, ID>::type
+ {
+ };
+
+ // The wrapped_value_traits specializations should be able to handle
+ // any value type which means we have to have every possible signature
+ // of the set_value() and set_image() functions.
+ //
+ template <typename W, database_type_id ID>
+ struct wrapped_value_traits<W, ID, false>
+ {
+ typedef wrapper_traits<W> wtraits;
+ typedef typename wtraits::unrestricted_wrapped_type wrapped_type;
+
+ typedef W value_type;
+ typedef wrapped_type query_type;
+ typedef typename image_traits<ID>::image_type image_type;
+
+ typedef value_traits<wrapped_type, ID> vtraits;
+
+ static void
+ set_value (W& v, const image_type& i, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, const W& v)
+ {
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+
+ // string, binary.
+ //
+ static void
+ set_value (W& v, const char* i, std::size_t n, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, n, is_null);
+ }
+
+ static void
+ set_image (char* i,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v));
+ }
+
+ // nstring.
+ //
+ static void
+ set_value (W& v, const ucs2_char* i, std::size_t n, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, n, is_null);
+ }
+
+ static void
+ set_image (ucs2_char* i,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v));
+ }
+
+ // long_string, long_nstring, long_binary.
+ //
+ static void
+ set_value (W& v, result_callback_type& cb, void*& context)
+ {
+ vtraits::set_value (wtraits::set_ref (v), cb, context);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const W& v)
+ {
+ vtraits::set_image (cb, context, is_null, wtraits::get_ref (v));
+ }
+
+ // time, datetime, datetimeoffset.
+ //
+ static void
+ set_image (image_type& i, unsigned short s, bool& is_null, const W& v)
+ {
+ vtraits::set_image (i, s, is_null, wtraits::get_ref (v));
+ }
+ };
+
+ template <typename W, database_type_id ID>
+ struct wrapped_value_traits<W, ID, true>
+ {
+ typedef wrapper_traits<W> wtraits;
+ typedef typename wtraits::unrestricted_wrapped_type wrapped_type;
+
+ typedef W value_type;
+ typedef wrapped_type query_type;
+ typedef typename image_traits<ID>::image_type image_type;
+
+ typedef value_traits<wrapped_type, ID> vtraits;
+
+ static void
+ set_value (W& v, const image_type& i, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+
+ // string, binary.
+ //
+ static void
+ set_value (W& v, const char* i, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, n, is_null);
+ }
+
+ static void
+ set_image (char* i,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v));
+ }
+
+ // nstring.
+ //
+ static void
+ set_value (W& v, const ucs2_char* i, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, n, is_null);
+ }
+
+ static void
+ set_image (ucs2_char* i,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v));
+ }
+
+ // long_string, long_nstring, long_binary.
+ //
+ static void
+ set_value (W& v, result_callback_type& cb, void*& context)
+ {
+ // We have to use our own callback since the NULL information
+ // is only available during streaming.
+ //
+ cb = &result_callback;
+ context = &v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (cb, context, is_null, wtraits::get_ref (v));
+ }
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ // time, datetime, datetimeoffset.
+ //
+ static void
+ set_image (image_type& i, unsigned short s, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, s, is_null, wtraits::get_ref (v));
+ }
+ };
+
+ template <typename T, database_type_id ID>
+ struct default_value_traits
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef typename image_traits<ID>::image_type image_type;
+
+ static void
+ set_value (T& v, const image_type& i, bool is_null)
+ {
+ if (!is_null)
+ v = T (i);
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, T v)
+ {
+ is_null = false;
+ i = image_type (v);
+ }
+ };
+
+ // smallmoney as float/double.
+ //
+ template <typename T>
+ struct smallmoney_float_value_traits
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef smallmoney image_type;
+
+ static void
+ set_value (T& v, const smallmoney& i, bool is_null)
+ {
+ if (!is_null)
+ v = T (i.value / 10000) + T (i.value % 10000) / 10000;
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (smallmoney& i, bool& is_null, T v)
+ {
+ is_null = false;
+ i.value = static_cast<int> (v) * 10000 +
+ static_cast<int> (v * 10000) % 10000;
+ }
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<float, id_smallmoney>:
+ smallmoney_float_value_traits<float>
+ {
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<double, id_smallmoney>:
+ smallmoney_float_value_traits<double>
+ {
+ };
+
+ // smallmoney as integer.
+ //
+ template <typename T>
+ struct default_value_traits<T, id_smallmoney>
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef smallmoney image_type;
+
+ static void
+ set_value (T& v, const smallmoney& i, bool is_null)
+ {
+ if (!is_null)
+ v = static_cast<T> (i.value);
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (smallmoney& i, bool& is_null, T v)
+ {
+ is_null = false;
+ i.value = static_cast<int> (v);
+ }
+ };
+
+ // money as float/double.
+ //
+ template <typename T>
+ struct money_float_value_traits
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef money image_type;
+
+ static void
+ set_value (T& v, const money& i, bool is_null)
+ {
+ if (!is_null)
+ {
+ long long iv ((static_cast<long long> (i.high) << 32) | i.low);
+ v = T (iv / 10000) + T (iv % 10000) / 10000;
+ }
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (money& i, bool& is_null, T v)
+ {
+ is_null = false;
+ long long iv (static_cast<long long> (v) * 10000 +
+ static_cast<long long> (v * 10000) % 10000);
+ i.high = static_cast<int> (iv >> 32);
+ i.low = static_cast<unsigned int> (iv);
+ }
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<float, id_money>:
+ money_float_value_traits<float>
+ {
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<double, id_money>:
+ money_float_value_traits<double>
+ {
+ };
+
+ // money as integer.
+ //
+ template <typename T>
+ struct default_value_traits<T, id_money>
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef money image_type;
+
+ static void
+ set_value (T& v, const money& i, bool is_null)
+ {
+ if (!is_null)
+ {
+ long long iv ((static_cast<long long> (i.high) << 32) | i.low);
+ v = static_cast<T> (iv);
+ }
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (money& i, bool& is_null, T v)
+ {
+ is_null = false;
+ long long iv (static_cast<long long> (v));
+ i.high = static_cast<int> (iv >> 32);
+ i.low = static_cast<unsigned int> (iv);
+ }
+ };
+
+ // std::string specialization for string.
+ //
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<std::string, id_string>
+ {
+ typedef std::string value_type;
+ typedef std::string query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (std::string& v,
+ const char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ v.assign (b, n);
+ else
+ v.erase ();
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const std::string& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > c)
+ n = c;
+
+ if (n != 0)
+ std::memcpy (b, v.c_str (), n);
+ }
+ };
+
+ // char*/const char* specialization for string.
+ //
+ // Specialization for const char* which only supports initialization
+ // of an image from the value but not the other way around. This way
+ // we can pass such values to the queries.
+ //
+ class LIBODB_MSSQL_EXPORT c_string_value_traits
+ {
+ public:
+ typedef const char* value_type;
+ typedef char* image_type;
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ is_null = false;
+ n = std::strlen (v);
+
+ if (n > c)
+ n = c;
+
+ if (n != 0)
+ std::memcpy (b, v, n);
+ }
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<char*, id_string>:
+ c_string_value_traits {};
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<const char*, id_string>:
+ c_string_value_traits {};
+
+ // char[N] specialization.
+ //
+ struct LIBODB_MSSQL_EXPORT c_array_value_traits_base
+ {
+ static void
+ set_value (char* const& v,
+ const char* b,
+ std::size_t n,
+ bool is_null,
+ std::size_t N);
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const char* v,
+ std::size_t N);
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_string>
+ {
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char* const& v,
+ const char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (v, b, n, is_null, N);
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ c_array_value_traits_base::set_image (b, c, n, is_null, v, N);
+ }
+ };
+
+ // std::array<char, N> (string) specialization.
+ //
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_string>
+ {
+ typedef std::array<char, N> value_type;
+ typedef std::array<char, N> query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (v.data (), b, n, is_null, N);
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_array_value_traits_base::set_image (b, c, n, is_null, v.data (), N);
+ }
+ };
+#endif
+
+ // char specialization.
+ //
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<char, id_string>
+ {
+ typedef char value_type;
+ typedef char query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char& v,
+ const char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (&v, b, n, is_null, 1);
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ char v)
+ {
+ c_array_value_traits_base::set_image (b, c, n, is_null, &v, 1);
+ }
+ };
+
+ // std::string specialization for long_string.
+ //
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<std::string,
+ id_long_string>
+ {
+ typedef std::string value_type;
+ typedef std::string query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (std::string& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &result_callback;
+ context = &v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const std::string& v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = &v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+
+ // char*/const char* specialization for long_string.
+ //
+ class LIBODB_MSSQL_EXPORT c_string_long_value_traits
+ {
+ public:
+ typedef const char* value_type;
+ typedef long_callback image_type;
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const char* v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<
+ char*, id_long_string>: c_string_long_value_traits {};
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<
+ const char*, id_long_string>: c_string_long_value_traits {};
+
+ // char[N] specialization for long_string.
+ //
+ template <std::size_t N>
+ struct c_array_long_value_traits_base
+ {
+ static void
+ set_value (char* const& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &result_callback;
+ context = v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const char* v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_long_string>
+ {
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef long_callback image_type;
+
+ static void
+ set_value (char* const& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ c_array_long_value_traits_base<N>::set_value (v, cb, context);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const char* v)
+ {
+ c_array_long_value_traits_base<N>::set_image (cb, context, is_null, v);
+ }
+ };
+
+ // std::array<char, N> (long_string) specialization.
+ //
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_long_string>
+ {
+ typedef std::array<char, N> value_type;
+ typedef std::array<char, N> query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (value_type& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ c_array_long_value_traits_base<N>::set_value (v.data (), cb, context);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_array_long_value_traits_base<N>::set_image (
+ cb, context, is_null, v.data ());
+ }
+ };
+#endif
+
+ // std::wstring specialization for nstring.
+ //
+ template <std::size_t = sizeof (wchar_t)>
+ struct wstring_functions
+ {
+ static void
+ assign (std::wstring& s, const ucs2_char* b, std::size_t n)
+ {
+ s.assign (b, b + n);
+ }
+
+ static void
+ assign (ucs2_char* b, const wchar_t* s, std::size_t n)
+ {
+ for (std::size_t i (0); i < n; ++i)
+ b[i] = static_cast<ucs2_char> (s[i]);
+ }
+
+ // Even though this is not used when ucs2_char == wchar_t, the
+ // compiler will still compile the signatures and complain.
+ //
+#ifndef _WIN32
+ static void
+ assign (wchar_t* s, const ucs2_char* b, std::size_t n)
+ {
+ for (std::size_t i (0); i < n; ++i)
+ s[i] = static_cast<wchar_t> (b[i]);
+ }
+#endif
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT wstring_functions<sizeof (ucs2_char)>
+ {
+ static void
+ assign (std::wstring& s, const ucs2_char* b, std::size_t n)
+ {
+ s.assign (reinterpret_cast<const wchar_t*> (b), n);
+ }
+
+ static void
+ assign (ucs2_char* b, const wchar_t* s, std::size_t n)
+ {
+ std::memcpy (b, s, n * sizeof (ucs2_char));
+ }
+
+ // On Windows ucs2_char is wchar_t which makes this function
+ // the same as the one above.
+ //
+#ifndef _WIN32
+ static void
+ assign (wchar_t* s, const ucs2_char* b, std::size_t n)
+ {
+ std::memcpy (s, b, n * sizeof (ucs2_char));
+ }
+#endif
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<std::wstring, id_nstring>
+ {
+ typedef std::wstring value_type;
+ typedef std::wstring query_type;
+ typedef ucs2_char* image_type;
+
+ typedef wstring_functions<> functions;
+
+ static void
+ set_value (std::wstring& v,
+ const ucs2_char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ functions::assign (v, b, n);
+ else
+ v.erase ();
+ }
+
+ static void
+ set_image (ucs2_char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const std::wstring& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > c)
+ n = c;
+
+ if (n != 0)
+ functions::assign (b, v.c_str (), n);
+ }
+ };
+
+ // wchar_t*/const wchar_t* specialization for nstring.
+ //
+ class LIBODB_MSSQL_EXPORT c_wstring_value_traits
+ {
+ public:
+ typedef const wchar_t* value_type;
+ typedef ucs2_char* image_type;
+
+ typedef wstring_functions<> functions;
+
+ static void
+ set_image (ucs2_char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const wchar_t* v)
+ {
+ is_null = false;
+ n = std::wcslen (v);
+
+ if (n > c)
+ n = c;
+
+ if (n != 0)
+ functions::assign (b, v, n);
+ }
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<wchar_t*, id_nstring>:
+ c_wstring_value_traits {};
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<
+ const wchar_t*, id_nstring>: c_wstring_value_traits {};
+
+ // wchar_t[N] specialization.
+ //
+ struct LIBODB_MSSQL_EXPORT c_warray_value_traits_base
+ {
+ typedef wstring_functions<> functions;
+
+ static void
+ set_value (wchar_t* const& v,
+ const ucs2_char* b,
+ std::size_t n,
+ bool is_null,
+ std::size_t N);
+
+ static void
+ set_image (ucs2_char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const wchar_t* v,
+ std::size_t N);
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<wchar_t[N], id_nstring>
+ {
+ typedef wchar_t* value_type;
+ typedef wchar_t query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (wchar_t* const& v,
+ const ucs2_char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_warray_value_traits_base::set_value (v, b, n, is_null, N);
+ }
+
+ static void
+ set_image (ucs2_char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const wchar_t* v)
+ {
+ c_warray_value_traits_base::set_image (b, c, n, is_null, v, N);
+ }
+ };
+
+ // std::array<wchar_t, N> (string) specialization.
+ //
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_value_traits<std::array<wchar_t, N>, id_nstring>
+ {
+ typedef std::array<wchar_t, N> value_type;
+ typedef std::array<wchar_t, N> query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const ucs2_char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_warray_value_traits_base::set_value (v.data (), b, n, is_null, N);
+ }
+
+ static void
+ set_image (ucs2_char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_warray_value_traits_base::set_image (b, c, n, is_null, v.data (), N);
+ }
+ };
+#endif
+
+ // wchar_t specialization.
+ //
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<wchar_t, id_nstring>
+ {
+ typedef wchar_t value_type;
+ typedef wchar_t query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (wchar_t& v,
+ const ucs2_char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_warray_value_traits_base::set_value (&v, b, n, is_null, 1);
+ }
+
+ static void
+ set_image (ucs2_char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ wchar_t v)
+ {
+ c_warray_value_traits_base::set_image (b, c, n, is_null, &v, 1);
+ }
+ };
+
+ // std::wstring specialization for long_nstring.
+ //
+ template <std::size_t = sizeof (wchar_t)>
+ struct wstring_long_value_traits;
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT wstring_long_value_traits<2>
+ {
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+
+#ifndef _WIN32
+ template <>
+ struct LIBODB_MSSQL_EXPORT wstring_long_value_traits<4>
+ {
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+#endif
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<std::wstring,
+ id_long_nstring>
+ {
+ typedef std::wstring value_type;
+ typedef std::wstring query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (std::wstring& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &wstring_long_value_traits<>::result_callback;
+ context = &v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const std::wstring& v)
+ {
+ is_null = false;
+ cb = &wstring_long_value_traits<>::param_callback;
+ context = &v;
+ }
+ };
+
+ // wchar_t*/const wchar_t* specialization for long_nstring.
+ //
+ template <std::size_t = sizeof (wchar_t)>
+ struct c_wstring_long_value_traits;
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT c_wstring_long_value_traits<2>
+ {
+ typedef const wchar_t* value_type;
+ typedef long_callback image_type;
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const wchar_t* v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+
+#ifndef _WIN32
+ template <>
+ struct LIBODB_MSSQL_EXPORT c_wstring_long_value_traits<4>
+ {
+ typedef const wchar_t* value_type;
+ typedef long_callback image_type;
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const wchar_t* v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+#endif
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<
+ wchar_t*, id_long_nstring>: c_wstring_long_value_traits<> {};
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<
+ const wchar_t*, id_long_nstring>: c_wstring_long_value_traits<> {};
+
+ // char[N] specialization for long_nstring.
+ //
+ template <std::size_t N, std::size_t = sizeof (wchar_t)>
+ struct c_warray_long_value_traits_base;
+
+ template <std::size_t N>
+ struct c_warray_long_value_traits_base<N, 2>
+ {
+ static void
+ set_value (wchar_t* const& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &result_callback;
+ context = v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const wchar_t* v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+
+#ifndef _WIN32
+ template <std::size_t N>
+ struct c_warray_long_value_traits_base<N, 4>
+ {
+ static void
+ set_value (wchar_t* const& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &result_callback;
+ context = v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const wchar_t* v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+#endif
+
+ template <std::size_t N>
+ struct default_value_traits<wchar_t[N], id_long_nstring>
+ {
+ typedef wchar_t* value_type;
+ typedef wchar_t query_type[N];
+ typedef long_callback image_type;
+
+ static void
+ set_value (wchar_t* const& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ c_warray_long_value_traits_base<N>::set_value (v, cb, context);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const wchar_t* v)
+ {
+ c_warray_long_value_traits_base<N>::set_image (
+ cb, context, is_null, v);
+ }
+ };
+
+ // std::array<wchar_t, N> (long_nstring) specialization.
+ //
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_value_traits<std::array<wchar_t, N>, id_long_nstring>
+ {
+ typedef std::array<wchar_t, N> value_type;
+ typedef std::array<wchar_t, N> query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (value_type& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ c_warray_long_value_traits_base<N>::set_value (
+ v.data (), cb, context);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_warray_long_value_traits_base<N>::set_image (
+ cb, context, is_null, v.data ());
+ }
+ };
+#endif
+
+ // std::vector (buffer) specialization for binary.
+ //
+ template <typename C>
+ struct vector_binary_value_traits
+ {
+ typedef std::vector<C> value_type;
+ typedef std::vector<C> query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (value_type& v, const char* b, std::size_t n, bool is_null)
+ {
+ if (!is_null)
+ {
+ const C* cb (reinterpret_cast<const C*> (b));
+ v.assign (cb, cb + n);
+ }
+ else
+ v.clear ();
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > c)
+ n = c;
+
+ // std::vector::data() may not be available in older compilers.
+ //
+ if (n != 0)
+ std::memcpy (b, &v.front (), n);
+ }
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<std::vector<char>,
+ id_binary>:
+ vector_binary_value_traits<char>
+ {
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<std::vector<unsigned char>,
+ id_binary>:
+ vector_binary_value_traits<unsigned char>
+ {
+ };
+
+ // C array (buffer) specialization for binary.
+ //
+ template <typename C, std::size_t N>
+ struct c_array_binary_value_traits
+ {
+ typedef C* value_type;
+ typedef C query_type[N];
+ typedef char* image_type;
+
+ static void
+ set_value (C* const& v, const char* b, std::size_t n, bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, b, n < N ? n : N);
+ else
+ std::memset (v, 0, N);
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const C* v)
+ {
+ is_null = false;
+ n = c > N ? N : c;
+ std::memcpy (b, v, n);
+ }
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_binary>:
+ c_array_binary_value_traits<char, N>
+ {
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<unsigned char[N], id_binary>:
+ c_array_binary_value_traits<unsigned char, N>
+ {
+ };
+
+#ifdef ODB_CXX11
+ // std::array (buffer) specialization for binary.
+ //
+ template <typename C, std::size_t N>
+ struct array_binary_value_traits
+ {
+ typedef std::array<C, N> value_type;
+ typedef value_type query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (value_type& v, const char* b, std::size_t n, bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v.data (), b, n < N ? n : N);
+ else
+ std::memset (v.data (), 0, N);
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = c > N ? N : c;
+ std::memcpy (b, v.data (), n);
+ }
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_binary>:
+ array_binary_value_traits<char, N>
+ {
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<std::array<unsigned char, N>, id_binary>:
+ array_binary_value_traits<unsigned char, N>
+ {
+ };
+#endif
+
+ // std::vector<char> (buffer) specialization for long_binary.
+ //
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<std::vector<char>,
+ id_long_binary>
+ {
+ typedef std::vector<char> value_type;
+ typedef std::vector<char> query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (value_type& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &result_callback;
+ context = &v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = &v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+
+ // std::vector<unsigned char> (buffer) specialization for long_binary.
+ //
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<std::vector<unsigned char>,
+ id_long_binary>
+ {
+ typedef std::vector<unsigned char> value_type;
+ typedef std::vector<unsigned char> query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (value_type& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &result_callback;
+ context = &v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = &v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+
+ // C array (buffer) specialization for long_binary.
+ //
+ template <typename C, std::size_t N>
+ struct c_array_long_binary_value_traits
+ {
+ static void
+ set_value (C* const& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &result_callback;
+ context = v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const C* v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_long_binary>:
+ c_array_long_binary_value_traits<char, N>
+ {
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef long_callback image_type;
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<unsigned char[N], id_long_binary>:
+ c_array_long_binary_value_traits<unsigned char, N>
+ {
+ typedef unsigned char* value_type;
+ typedef unsigned char query_type[N];
+ typedef long_callback image_type;
+ };
+
+#ifdef ODB_CXX11
+ // std::array (buffer) specialization for long_binary.
+ //
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_long_binary>
+ {
+ typedef std::array<char, N> value_type;
+ typedef value_type query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (value_type& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ c_array_long_binary_value_traits<char, N>::set_value (
+ v.data (), cb, context);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_array_long_binary_value_traits<char, N>::set_image (
+ cb, context, is_null, v.data ());
+ }
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<std::array<unsigned char, N>, id_long_binary>
+ {
+ typedef std::array<unsigned char, N> value_type;
+ typedef value_type query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (value_type& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ c_array_long_binary_value_traits<unsigned char, N>::set_value (
+ v.data (), cb, context);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_array_long_binary_value_traits<unsigned char, N>::set_image (
+ cb, context, is_null, v.data ());
+ }
+ };
+#endif
+
+ // GUID specialization for uniqueidentifier.
+ //
+#ifdef _WIN32
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<GUID, id_uniqueidentifier>
+ {
+ typedef GUID value_type;
+ typedef GUID query_type;
+ typedef uniqueidentifier image_type;
+
+ static void
+ set_value (GUID& v, const uniqueidentifier& i, bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (&v, &i, 16);
+ else
+ std::memset (&v, 0, 16);
+ }
+
+ static void
+ set_image (uniqueidentifier& i, bool& is_null, const GUID& v)
+ {
+ is_null = false;
+ std::memcpy (&i, &v, 16);
+ }
+ };
+#endif
+
+ // char[16] specialization for uniqueidentifier.
+ //
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<char[16],
+ id_uniqueidentifier>
+ {
+ typedef char* value_type;
+ typedef char query_type[16];
+ typedef uniqueidentifier image_type;
+
+ static void
+ set_value (char* const& v, const uniqueidentifier& i, bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, &i, 16);
+ else
+ std::memset (v, 0, 16);
+ }
+
+ static void
+ set_image (uniqueidentifier& i, bool& is_null, const char* v)
+ {
+ is_null = false;
+ std::memcpy (&i, v, 16);
+ }
+ };
+
+ // unsigned long long specialization for rowversion.
+ //
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<unsigned long long,
+ id_rowversion>
+ {
+ typedef unsigned long long value_type;
+ typedef unsigned long long query_type;
+ typedef unsigned char* image_type;
+
+ static void
+ set_value (unsigned long long& v, const unsigned char* i, bool is_null)
+ {
+ if (!is_null)
+ {
+ // The value is in the big-endian format.
+ //
+ unsigned char* p (reinterpret_cast<unsigned char*> (&v));
+ p[0] = i[7];
+ p[1] = i[6];
+ p[2] = i[5];
+ p[3] = i[4];
+ p[4] = i[3];
+ p[5] = i[2];
+ p[6] = i[1];
+ p[7] = i[0];
+ }
+ else
+ v = 0;
+ }
+
+ static void
+ set_image (unsigned char* i, bool& is_null, unsigned long long v)
+ {
+ is_null = false;
+
+ // The value is in the big-endian format.
+ //
+ const unsigned char* p (reinterpret_cast<const unsigned char*> (&v));
+ i[0] = p[7];
+ i[1] = p[6];
+ i[2] = p[5];
+ i[3] = p[4];
+ i[4] = p[3];
+ i[5] = p[2];
+ i[6] = p[1];
+ i[7] = p[0];
+ }
+ };
+
+ //
+ // type_traits
+ //
+
+ template <typename T>
+ struct default_type_traits;
+
+ template <typename T>
+ class type_traits: public default_type_traits<T>
+ {
+ };
+
+ // Integral types.
+ //
+ template <>
+ struct default_type_traits<bool>
+ {
+ static const database_type_id db_type_id = id_bit;
+ };
+
+ template <>
+ struct default_type_traits<signed char>
+ {
+ static const database_type_id db_type_id = id_tinyint;
+ };
+
+ template <>
+ struct default_type_traits<unsigned char>
+ {
+ static const database_type_id db_type_id = id_tinyint;
+ };
+
+ template <>
+ struct default_type_traits<short>
+ {
+ static const database_type_id db_type_id = id_smallint;
+ };
+
+ template <>
+ struct default_type_traits<unsigned short>
+ {
+ static const database_type_id db_type_id = id_smallint;
+ };
+
+ template <>
+ struct default_type_traits<int>
+ {
+ static const database_type_id db_type_id = id_int;
+ };
+
+ template <>
+ struct default_type_traits<unsigned int>
+ {
+ static const database_type_id db_type_id = id_int;
+ };
+
+ template <>
+ struct default_type_traits<long>
+ {
+ static const database_type_id db_type_id = id_bigint;
+ };
+
+ template <>
+ struct default_type_traits<unsigned long>
+ {
+ static const database_type_id db_type_id = id_bigint;
+ };
+
+ template <>
+ struct default_type_traits<long long>
+ {
+ static const database_type_id db_type_id = id_bigint;
+ };
+
+ template <>
+ struct default_type_traits<unsigned long long>
+ {
+ static const database_type_id db_type_id = id_bigint;
+ };
+
+ // Float types.
+ //
+ template <>
+ struct default_type_traits<float>
+ {
+ static const database_type_id db_type_id = id_float4;
+ };
+
+ template <>
+ struct default_type_traits<double>
+ {
+ static const database_type_id db_type_id = id_float8;
+ };
+
+ // String types.
+ //
+ template <>
+ struct default_type_traits<std::string>
+ {
+ static const database_type_id db_type_id = id_long_string;
+ };
+
+ template <>
+ struct default_type_traits<char*>
+ {
+ static const database_type_id db_type_id = id_long_string;
+ };
+
+ template <>
+ struct default_type_traits<const char*>
+ {
+ static const database_type_id db_type_id = id_long_string;
+ };
+
+ template <std::size_t N>
+ struct default_type_traits<char[N]>
+ {
+ // Use short string by default to minimize code bloat. Can
+ // always be overridden with _val()/_ref().
+ //
+ static const database_type_id db_type_id = id_string;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<char, N> >
+ {
+ // Ditto.
+ //
+ static const database_type_id db_type_id = id_string;
+ };
+#endif
+
+ template <>
+ struct default_type_traits<char>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ // Wide string types.
+ //
+ template <>
+ struct default_type_traits<std::wstring>
+ {
+ static const database_type_id db_type_id = id_long_nstring;
+ };
+
+ template <>
+ struct default_type_traits<wchar_t*>
+ {
+ static const database_type_id db_type_id = id_long_nstring;
+ };
+
+ template <>
+ struct default_type_traits<const wchar_t*>
+ {
+ static const database_type_id db_type_id = id_long_nstring;
+ };
+
+ template <std::size_t N>
+ struct default_type_traits<wchar_t[N]>
+ {
+ // Use short string by default to minimize code bloat. Can
+ // always be overridden with _val()/_ref().
+ //
+ static const database_type_id db_type_id = id_nstring;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<wchar_t, N> >
+ {
+ // Ditto.
+ //
+ static const database_type_id db_type_id = id_nstring;
+ };
+#endif
+
+ template <>
+ struct default_type_traits<wchar_t>
+ {
+ static const database_type_id db_type_id = id_nstring;
+ };
+
+ // Binary types.
+ //
+ template <std::size_t N>
+ struct default_type_traits<unsigned char[N]>
+ {
+ static const database_type_id db_type_id = id_long_binary;
+ };
+
+ template <>
+ struct default_type_traits<std::vector<char> >
+ {
+ static const database_type_id db_type_id = id_long_binary;
+ };
+
+ template <>
+ struct default_type_traits<std::vector<unsigned char> >
+ {
+ static const database_type_id db_type_id = id_long_binary;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<unsigned char, N> >
+ {
+ static const database_type_id db_type_id = id_long_binary;
+ };
+#endif
+
+ // GUID.
+ //
+#ifdef _WIN32
+ template <>
+ struct default_type_traits<GUID>
+ {
+ static const database_type_id db_type_id = id_uniqueidentifier;
+ };
+#endif
+ }
+}
+
+#include <odb/mssql/traits.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_TRAITS_HXX
diff --git a/libodb-mssql/odb/mssql/traits.txx b/libodb-mssql/odb/mssql/traits.txx
new file mode 100644
index 0000000..683054c
--- /dev/null
+++ b/libodb-mssql/odb/mssql/traits.txx
@@ -0,0 +1,399 @@
+// file : odb/mssql/traits.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cassert>
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // wrapped_value_traits<W, ID, true>
+ //
+ template <typename W, database_type_id ID>
+ void wrapped_value_traits<W, ID, true>::
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity)
+ {
+ W& v (*static_cast<W*> (context));
+
+ if (chunk == chunk_null)
+ wtraits::set_null (v);
+ else
+ {
+ long_callback& c (*static_cast<long_callback*> (*buffer));
+
+ // Redirect all further calls.
+ //
+ vtraits::set_value (wtraits::set_ref (v),
+ c.callback.result,
+ c.context.result);
+
+ // Forward this call.
+ //
+ c.callback.result (
+ c.context.result,
+ position,
+ buffer,
+ size,
+ chunk,
+ size_left,
+ tmp_buffer,
+ tmp_capacity);
+ }
+ }
+
+ //
+ // c_array_long_value_traits_base
+ //
+ template <std::size_t N>
+ void c_array_long_value_traits_base<N>::
+ param_callback (const void* context,
+ std::size_t*,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void*,
+ std::size_t)
+ {
+ // Figure out the length. We cannot use strlen since it may
+ // not be 0-terminated (strnlen is not standard).
+ //
+ size_t n (0);
+ for (; n != N && static_cast<const char*> (context)[n] != '\0'; ++n);
+
+ *buffer = context;
+ *size = n;
+ *chunk = chunk_one;
+ }
+
+ template <std::size_t N>
+ void c_array_long_value_traits_base<N>::
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t,
+ void* tmp_buf,
+ std::size_t tmp_capacity)
+ {
+ char* p (static_cast<char*> (context));
+
+ switch (chunk)
+ {
+ case chunk_null:
+ case chunk_one:
+ {
+ *p = '\0';
+ break;
+ }
+ case chunk_first:
+ {
+ *buffer = p;
+ *size = N;
+ break;
+ }
+ case chunk_next:
+ {
+ // ODBC insists on appending '\0' to each chunk it returns.
+ // As a result, we can get here if the last character did not
+ // fit into our buffer. There could also be more data but since
+ // there is no way to stop until we read all the data, dump
+ // the remainder into the temporary buffer.
+ //
+
+ // Use position to indicate whether this is the first "next
+ // chunk".
+ //
+ if (*position == 0)
+ *position = 1;
+ else if (*position == 1)
+ {
+ p[N - 1] = *static_cast<const char*> (tmp_buf);
+ *position = 2;
+ }
+
+ *buffer = tmp_buf;
+ *size = tmp_capacity;
+ break;
+ }
+ case chunk_last:
+ {
+ if (*position == 0)
+ {
+ if (*size < N) // Append '\0' if there is space.
+ p[*size] = '\0';
+ }
+ else if (*position == 1)
+ p[N - 1] = *static_cast<const char*> (tmp_buf);
+
+ break;
+ }
+ }
+ }
+
+ //
+ // c_warray_long_value_traits_base<2>
+ //
+ template <std::size_t N>
+ void c_warray_long_value_traits_base<N, 2>::
+ param_callback (const void* context,
+ std::size_t*,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void*,
+ std::size_t)
+ {
+ // Figure out the length. We cannot use wcslen since it may
+ // not be 0-terminated (wcsnlen is not standard).
+ //
+ size_t n (0);
+ for (; n != N && static_cast<const wchar_t*> (context)[n] != L'\0'; ++n);
+
+ *buffer = context;
+ *size = n * 2; // In bytes.
+ *chunk = chunk_one;
+ }
+
+ template <std::size_t N>
+ void c_warray_long_value_traits_base<N, 2>::
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t,
+ void* tmp_buf,
+ std::size_t tmp_capacity)
+ {
+ wchar_t* p (static_cast<wchar_t*> (context));
+
+ switch (chunk)
+ {
+ case chunk_null:
+ case chunk_one:
+ {
+ *p = L'\0';
+ break;
+ }
+ case chunk_first:
+ {
+ *buffer = p;
+ *size = N * 2; // In bytes
+ break;
+ }
+ case chunk_next:
+ {
+ // ODBC insists on appending '\0' to each chunk it returns.
+ // As a result, we can get here if the last character did not
+ // fit into our buffer. There could also be more data but since
+ // there is no way to stop until we read all the data, dump
+ // the remainder into the temporary buffer.
+ //
+
+ // Use position to indicate whether this is the first "next
+ // chunk".
+ //
+ if (*position == 0)
+ *position = 1;
+ else if (*position == 1)
+ {
+ p[N - 1] = *static_cast<const wchar_t*> (tmp_buf);
+ *position = 2;
+ }
+
+ *buffer = tmp_buf;
+ *size = tmp_capacity;
+ break;
+ }
+ case chunk_last:
+ {
+ if (*position == 0)
+ {
+ size_t n (*size / 2); // In wide characters.
+ if (n < N) // Append '\0' if there is space.
+ p[n] = L'\0';
+ }
+ else if (*position == 1)
+ p[N - 1] = *static_cast<const wchar_t*> (tmp_buf);
+
+ break;
+ }
+ }
+ }
+
+#ifndef _WIN32
+ //
+ // c_warray_long_value_traits_base<4>
+ //
+ template <std::size_t N>
+ void c_warray_long_value_traits_base<N, 4>::
+ param_callback (const void* context,
+ std::size_t* pos,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buf,
+ std::size_t tmp_capacity)
+ {
+ // Here we cannot just return the pointer to the underlying buffer
+ // since the character sizes are different. Instead we copy the
+ // data to the temporary buffer.
+ //
+ ucs2_char* d (static_cast<ucs2_char*> (tmp_buf));
+ const wchar_t* s (static_cast<const wchar_t*> (context) + *pos);
+
+ size_t n (0);
+ tmp_capacity /= 2; // In UCS-2 character.
+ for (const wchar_t* e (s + N);
+ s != e && *s != L'\0' && n != tmp_capacity;
+ ++n, ++s)
+ d[n] = static_cast<ucs2_char> (*s);
+
+ *buffer = d;
+ *size = n * 2; // In bytes.
+ *chunk = (n != tmp_capacity ? chunk_last : chunk_next);
+
+ if (*pos == 0)
+ {
+ if (*chunk == chunk_last)
+ *chunk = chunk_one;
+ else
+ *chunk = chunk_first;
+ }
+
+ *pos += n;
+ }
+
+ template <std::size_t N>
+ void c_warray_long_value_traits_base<N, 4>::
+ result_callback (void* context,
+ std::size_t* pos,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t,
+ void* tmp_buf,
+ std::size_t tmp_capacity)
+ {
+ wchar_t* d (static_cast<wchar_t*> (context));
+ const ucs2_char* s (static_cast<const ucs2_char*> (tmp_buf));
+
+ // Again, we cannot do direct buffer copy and have to use the
+ // temporary buffer instead.
+ //
+ switch (chunk)
+ {
+ case chunk_null:
+ case chunk_one:
+ {
+ *d = L'\0';
+ break;
+ }
+ case chunk_first:
+ {
+ break;
+ }
+ case chunk_next:
+ case chunk_last:
+ {
+ // Append the data from the temporary buffer.
+ //
+ if (*pos < N)
+ {
+ *size /= 2; // In UCS-2 characters.
+ size_t n (N - *pos);
+ n = *size < n ? *size : n;
+
+ wstring_functions<>::assign (d + *pos, s, n);
+ *pos += n;
+
+ if (*pos < N) // Append '\0' if there is space.
+ d[*pos] = L'\0';
+ }
+
+ break;
+ }
+ }
+
+ if (chunk == chunk_first || chunk == chunk_next)
+ {
+ *buffer = tmp_buf;
+ *size = tmp_capacity;
+ }
+ }
+#endif
+
+ //
+ // c_array_long_binary_value_traits
+ //
+ template <typename C, std::size_t N>
+ void c_array_long_binary_value_traits<C, N>::
+ param_callback (const void* context,
+ std::size_t*,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void*,
+ std::size_t)
+ {
+ *buffer = context;
+ *size = N;
+ *chunk = chunk_one;
+ }
+
+ template <typename C, std::size_t N>
+ void c_array_long_binary_value_traits<C, N>::
+ result_callback (void* context,
+ std::size_t*,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buf,
+ std::size_t tmp_capacity)
+ {
+ // The code is similar to the vector<char> specialization.
+ //
+ switch (chunk)
+ {
+ case chunk_null:
+ case chunk_one:
+ {
+ std::memset (context, 0, N);
+ break;
+ }
+ case chunk_first:
+ {
+ assert (size_left != 0);
+
+ *buffer = context;
+ *size = size_left < N ? size_left : N;
+ break;
+ }
+ case chunk_next:
+ {
+ // We can get here if total size is greater than N. There is
+ // no way to stop until we read all the data, so dump the
+ // remainder into the temporary buffer.
+ //
+ *buffer = tmp_buf;
+ *size = tmp_capacity;
+ break;
+ }
+ case chunk_last:
+ {
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/transaction-impl.cxx b/libodb-mssql/odb/mssql/transaction-impl.cxx
new file mode 100644
index 0000000..a44e83f
--- /dev/null
+++ b/libodb-mssql/odb/mssql/transaction-impl.cxx
@@ -0,0 +1,103 @@
+// file : odb/mssql/transaction-impl.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/tracer.hxx>
+
+#include <odb/mssql/mssql.hxx>
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/transaction-impl.hxx>
+#include <odb/mssql/error.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ transaction_impl::
+ transaction_impl (database_type& db)
+ : odb::transaction_impl (db)
+ {
+ }
+
+ transaction_impl::
+ transaction_impl (connection_ptr c)
+ : odb::transaction_impl (c->database (), *c), connection_ (c)
+ {
+ }
+
+ transaction_impl::
+ ~transaction_impl ()
+ {
+ }
+
+ void transaction_impl::
+ start ()
+ {
+ // Grab a connection if we don't already have one.
+ //
+ if (connection_ == 0)
+ {
+ connection_ = static_cast<database_type&> (database_).connection ();
+ odb::transaction_impl::connection_ = connection_.get ();
+ }
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "BEGIN");
+ }
+
+ // In ODBC a transaction is started automatically before the first
+ // statement is executed.
+ //
+ }
+
+ void transaction_impl::
+ commit ()
+ {
+ // Invalidate query results.
+ //
+ connection_->invalidate_results ();
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "COMMIT");
+ }
+
+ SQLRETURN r (
+ SQLEndTran (SQL_HANDLE_DBC, connection_->handle (), SQL_COMMIT));
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, *connection_, true);
+
+ // Release the connection.
+ //
+ connection_.reset ();
+ }
+
+ void transaction_impl::
+ rollback ()
+ {
+ // Invalidate query results.
+ //
+ connection_->invalidate_results ();
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "ROLLBACK");
+ }
+
+ SQLRETURN r (
+ SQLEndTran (SQL_HANDLE_DBC, connection_->handle (), SQL_ROLLBACK));
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, *connection_, true);
+
+ // Release the connection.
+ //
+ connection_.reset ();
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/transaction-impl.hxx b/libodb-mssql/odb/mssql/transaction-impl.hxx
new file mode 100644
index 0000000..f7189f2
--- /dev/null
+++ b/libodb-mssql/odb/mssql/transaction-impl.hxx
@@ -0,0 +1,49 @@
+// file : odb/mssql/transaction-impl.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_TRANSACTION_IMPL_HXX
+#define ODB_MSSQL_TRANSACTION_IMPL_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class LIBODB_MSSQL_EXPORT transaction_impl: public odb::transaction_impl
+ {
+ public:
+ typedef mssql::database database_type;
+ typedef mssql::connection connection_type;
+
+ transaction_impl (database_type&);
+ transaction_impl (connection_ptr);
+
+ virtual
+ ~transaction_impl ();
+
+ virtual void
+ start ();
+
+ virtual void
+ commit ();
+
+ virtual void
+ rollback ();
+
+ private:
+ connection_ptr connection_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_TRANSACTION_IMPL_HXX
diff --git a/libodb-mssql/odb/mssql/transaction.cxx b/libodb-mssql/odb/mssql/transaction.cxx
new file mode 100644
index 0000000..bb51697
--- /dev/null
+++ b/libodb-mssql/odb/mssql/transaction.cxx
@@ -0,0 +1,26 @@
+// file : odb/mssql/transaction.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/mssql/transaction.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ transaction& transaction::
+ current ()
+ {
+ // While the impl type can be of the concrete type, the transaction
+ // object can be created as either odb:: or odb::mssql:: type. To
+ // work around that we are going to hard-cast one to the other
+ // relying on the fact that they have the same representation and
+ // no virtual functions. The former is checked in the tests.
+ //
+ odb::transaction& b (odb::transaction::current ());
+ assert (dynamic_cast<transaction_impl*> (&b.implementation ()) != 0);
+ return reinterpret_cast<transaction&> (b);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/transaction.hxx b/libodb-mssql/odb/mssql/transaction.hxx
new file mode 100644
index 0000000..8c86515
--- /dev/null
+++ b/libodb-mssql/odb/mssql/transaction.hxx
@@ -0,0 +1,88 @@
+// file : odb/mssql/transaction.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_TRANSACTION_HXX
+#define ODB_MSSQL_TRANSACTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/tracer.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class transaction_impl;
+
+ class LIBODB_MSSQL_EXPORT transaction: public odb::transaction
+ {
+ public:
+ typedef mssql::database database_type;
+ typedef mssql::connection connection_type;
+
+ explicit
+ transaction (transaction_impl*, bool make_current = true);
+
+ transaction ();
+
+ // Return the database this transaction is on.
+ //
+ database_type&
+ database ();
+
+ // Return the underlying database connection for this transaction.
+ //
+ connection_type&
+ connection ();
+
+ connection_type&
+ connection (odb::database&);
+
+ // Return current transaction or throw if there is no transaction
+ // in effect.
+ //
+ static transaction&
+ current ();
+
+ // Set the current thread's transaction.
+ //
+ static void
+ current (transaction&);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef mssql::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ using odb::transaction::tracer;
+
+ public:
+ transaction_impl&
+ implementation ();
+ };
+ }
+}
+
+#include <odb/mssql/transaction.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_TRANSACTION_HXX
diff --git a/libodb-mssql/odb/mssql/transaction.ixx b/libodb-mssql/odb/mssql/transaction.ixx
new file mode 100644
index 0000000..ac819bc
--- /dev/null
+++ b/libodb-mssql/odb/mssql/transaction.ixx
@@ -0,0 +1,57 @@
+// file : odb/mssql/transaction.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/transaction-impl.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ inline transaction::
+ transaction (transaction_impl* impl, bool make_current)
+ : odb::transaction (impl, make_current)
+ {
+ }
+
+ inline transaction::
+ transaction ()
+ : odb::transaction ()
+ {
+ }
+
+ inline transaction_impl& transaction::
+ implementation ()
+ {
+ // We can use static_cast here since we have an instance of
+ // mssql::transaction.
+ //
+ return static_cast<transaction_impl&> (
+ odb::transaction::implementation ());
+ }
+
+ inline transaction::database_type& transaction::
+ database ()
+ {
+ return static_cast<database_type&> (odb::transaction::database ());
+ }
+
+ inline transaction::connection_type& transaction::
+ connection ()
+ {
+ return static_cast<connection_type&> (odb::transaction::connection ());
+ }
+
+ inline transaction::connection_type& transaction::
+ connection (odb::database& db)
+ {
+ return static_cast<connection_type&> (odb::transaction::connection (db));
+ }
+
+ inline void transaction::
+ current (transaction& t)
+ {
+ odb::transaction::current (t);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/version.hxx b/libodb-mssql/odb/mssql/version.hxx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libodb-mssql/odb/mssql/version.hxx
diff --git a/libodb-mssql/odb/mssql/version.hxx.in b/libodb-mssql/odb/mssql/version.hxx.in
new file mode 100644
index 0000000..02a404f
--- /dev/null
+++ b/libodb-mssql/odb/mssql/version.hxx.in
@@ -0,0 +1,60 @@
+// file : odb/mssql/version.hxx.in
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef LIBODB_MSSQL_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 LIBODB_MSSQL_VERSION_FULL $libodb_mssql.version.project_number$ULL
+#define LIBODB_MSSQL_VERSION_STR "$libodb_mssql.version.project$"
+#define LIBODB_MSSQL_VERSION_ID "$libodb_mssql.version.project_id$"
+
+#define LIBODB_MSSQL_VERSION_MAJOR $libodb_mssql.version.major$
+#define LIBODB_MSSQL_VERSION_MINOR $libodb_mssql.version.minor$
+#define LIBODB_MSSQL_VERSION_PATCH $libodb_mssql.version.patch$
+
+#define LIBODB_MSSQL_PRE_RELEASE $libodb_mssql.version.pre_release$
+
+#define LIBODB_MSSQL_SNAPSHOT $libodb_mssql.version.snapshot_sn$ULL
+#define LIBODB_MSSQL_SNAPSHOT_ID "$libodb_mssql.version.snapshot_id$"
+
+#include <odb/version.hxx>
+
+$libodb.check(LIBODB_VERSION_FULL, LIBODB_SNAPSHOT)$
+
+// 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 LIBODB_MSSQL_VERSION 2049976
+
+#endif // LIBODB_MSSQL_VERSION
diff --git a/libodb-mssql/odb/mssql/view-result.hxx b/libodb-mssql/odb/mssql/view-result.hxx
new file mode 100644
index 0000000..41e5b8e
--- /dev/null
+++ b/libodb-mssql/odb/mssql/view-result.hxx
@@ -0,0 +1,85 @@
+// file : odb/mssql/view-result.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_VIEW_RESULT_HXX
+#define ODB_MSSQL_VIEW_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/view-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx> // query_base, view_statements
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ class view_result_impl: public odb::view_result_impl<T>
+ {
+ public:
+ typedef odb::view_result_impl<T> base_type;
+
+ typedef typename base_type::view_type view_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef view_traits_impl<view_type, id_mssql> view_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef view_statements<view_type> statements_type;
+
+ virtual
+ ~view_result_impl ();
+
+ view_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (view_type&);
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ typedef mssql::change_callback change_callback_type;
+
+ static void
+ change_callback (void* context);
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ view_traits_calls<view_type> tc_;
+ bool can_load_;
+ bool use_copy_;
+ typename view_traits::image_type* image_copy_;
+ };
+ }
+}
+
+#include <odb/mssql/view-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_VIEW_RESULT_HXX
diff --git a/libodb-mssql/odb/mssql/view-result.txx b/libodb-mssql/odb/mssql/view-result.txx
new file mode 100644
index 0000000..7818d36
--- /dev/null
+++ b/libodb-mssql/odb/mssql/view-result.txx
@@ -0,0 +1,153 @@
+// file : odb/mssql/view-result.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/mssql/exceptions.hxx> // long_data_reload
+#include <odb/mssql/view-statements.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ view_result_impl<T>::
+ ~view_result_impl ()
+ {
+ invalidate ();
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ invalidate ()
+ {
+ change_callback_type& cc (statements_.image ().change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ delete image_copy_;
+ image_copy_ = 0;
+
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ view_result_impl<T>::
+ view_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ use_copy_ (false),
+ image_copy_ (0)
+ {
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ load (view_type& view)
+ {
+ if (!can_load_)
+ throw long_data_reload ();
+
+ view_traits::callback (this->db_, view, callback_event::pre_load);
+
+ tc_.init (view,
+ use_copy_ ? *image_copy_ : statements_.image (),
+ &this->db_);
+
+ // If we are using a copy, make sure the callback information for
+ // long data also comes from the copy.
+ //
+ can_load_ = !statement_->stream_result (
+ use_copy_ ? &statements_.image () : 0,
+ use_copy_ ? image_copy_ : 0);
+
+ view_traits::callback (this->db_, view, callback_event::post_load);
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ next ()
+ {
+ can_load_ = true;
+ this->current (pointer_type ());
+
+ typename view_traits::image_type& im (statements_.image ());
+ change_callback_type& cc (im.change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ use_copy_ = false;
+
+ if (im.version != statements_.image_version ())
+ {
+ binding& b (statements_.image_binding ());
+ tc_.bind (b.bind, im);
+ statements_.image_version (im.version);
+ b.version++;
+ }
+
+ if (statement_->fetch () == select_statement::no_data)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ else
+ {
+ cc.callback = &change_callback;
+ cc.context = this;
+ }
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t view_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ change_callback (void* c)
+ {
+ view_result_impl<T>* r (static_cast<view_result_impl<T>*> (c));
+
+ typename view_traits::image_type& im (r->statements_.image ());
+
+ if (r->image_copy_ == 0)
+ r->image_copy_ = new typename view_traits::image_type (im);
+ else
+ *r->image_copy_ = im;
+
+ im.change_callback_.callback = 0;
+ im.change_callback_.context = 0;
+
+ r->use_copy_ = true;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/view-statements.hxx b/libodb-mssql/odb/mssql/view-statements.hxx
new file mode 100644
index 0000000..1742cab
--- /dev/null
+++ b/libodb-mssql/odb/mssql/view-statements.hxx
@@ -0,0 +1,83 @@
+// file : odb/mssql/view-statements.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_VIEW_STATEMENTS_HXX
+#define ODB_MSSQL_VIEW_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mssql/mssql-types.hxx>
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/statements-base.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ class view_statements: public statements_base
+ {
+ public:
+ typedef T view_type;
+ typedef view_traits_impl<view_type, id_mssql> view_traits;
+ typedef typename view_traits::pointer_type pointer_type;
+ typedef typename view_traits::image_type image_type;
+
+ public:
+ view_statements (connection_type&);
+
+ virtual
+ ~view_statements ();
+
+ // View image.
+ //
+ image_type&
+ image ()
+ {
+ return image_;
+ }
+
+ std::size_t
+ image_version () const
+ {
+ return image_version_;
+ }
+
+ void
+ image_version (std::size_t v)
+ {
+ image_version_ = v;
+ }
+
+ binding&
+ image_binding ()
+ {
+ return image_binding_;
+ }
+
+ private:
+ view_statements (const view_statements&);
+ view_statements& operator= (const view_statements&);
+
+ private:
+ image_type image_;
+ std::size_t image_version_;
+ binding image_binding_;
+ bind image_bind_[view_traits::column_count != 0
+ ? view_traits::column_count
+ : 1];
+ };
+ }
+}
+
+#include <odb/mssql/view-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_VIEW_STATEMENTS_HXX
diff --git a/libodb-mssql/odb/mssql/view-statements.txx b/libodb-mssql/odb/mssql/view-statements.txx
new file mode 100644
index 0000000..e217523
--- /dev/null
+++ b/libodb-mssql/odb/mssql/view-statements.txx
@@ -0,0 +1,30 @@
+// file : odb/mssql/view-statements.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ view_statements<T>::
+ ~view_statements ()
+ {
+ }
+
+ template <typename T>
+ view_statements<T>::
+ view_statements (connection_type& conn)
+ : statements_base (conn),
+ image_binding_ (image_bind_, view_traits::column_count)
+ {
+ image_.version = 0;
+ image_version_ = 0;
+
+ image_binding_.change_callback = image_.change_callback ();
+
+ std::memset (image_bind_, 0, sizeof (image_bind_));
+ }
+ }
+}
diff --git a/libodb-mssql/tests/.gitignore b/libodb-mssql/tests/.gitignore
new file mode 100644
index 0000000..e54525b
--- /dev/null
+++ b/libodb-mssql/tests/.gitignore
@@ -0,0 +1 @@
+driver
diff --git a/libodb-mssql/tests/basics/buildfile b/libodb-mssql/tests/basics/buildfile
new file mode 100644
index 0000000..ee213f5
--- /dev/null
+++ b/libodb-mssql/tests/basics/buildfile
@@ -0,0 +1,6 @@
+# file : tests/basics/buildfile
+# license : ODB NCUEL; see accompanying LICENSE file
+
+import libs = libodb-mssql%lib{odb-mssql}
+
+exe{driver}: {hxx cxx}{*} $libs
diff --git a/libodb-mssql/tests/basics/driver.cxx b/libodb-mssql/tests/basics/driver.cxx
new file mode 100644
index 0000000..04d7231
--- /dev/null
+++ b/libodb-mssql/tests/basics/driver.cxx
@@ -0,0 +1,37 @@
+// file : tests/basics/driver.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+// Basic test to make sure the library is usable. Functionality testing
+// is done in the odb-tests package.
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/exceptions.hxx>
+#include <odb/mssql/transaction.hxx>
+
+using namespace odb::mssql;
+
+int
+main ()
+{
+ {
+ std::ostringstream os;
+ database::print_usage (os);
+ assert (!os.str ().empty ());
+ }
+
+ // We can't really do much here since that would require a database. We can
+ // create a fake database object as long as we don't expect to get a valid
+ // connection.
+ //
+ database db ("john", "secret", "dummy whammy");
+
+ try
+ {
+ transaction t (db.begin ());
+ assert (false);
+ }
+ catch (const database_exception&) {}
+}
diff --git a/libodb-mssql/tests/build/.gitignore b/libodb-mssql/tests/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-mssql/tests/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-mssql/tests/build/bootstrap.build b/libodb-mssql/tests/build/bootstrap.build
new file mode 100644
index 0000000..895126c
--- /dev/null
+++ b/libodb-mssql/tests/build/bootstrap.build
@@ -0,0 +1,8 @@
+# file : tests/build/bootstrap.build
+# license : ODB NCUEL; see accompanying LICENSE file
+
+project = # Unnamed subproject.
+
+using config
+using dist
+using test
diff --git a/libodb-mssql/tests/build/root.build b/libodb-mssql/tests/build/root.build
new file mode 100644
index 0000000..bbd3781
--- /dev/null
+++ b/libodb-mssql/tests/build/root.build
@@ -0,0 +1,23 @@
+# file : tests/build/root.build
+# license : ODB NCUEL; see accompanying LICENSE file
+
+cxx.std = latest
+
+using cxx
+
+hxx{*}: extension = hxx
+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
+
+# Every exe{} in this subproject is by default a test.
+#
+exe{*}: test = true
+
+# Specify the test target for cross-testing.
+#
+test.target = $cxx.target
diff --git a/libodb-mssql/tests/buildfile b/libodb-mssql/tests/buildfile
new file mode 100644
index 0000000..fd73adc
--- /dev/null
+++ b/libodb-mssql/tests/buildfile
@@ -0,0 +1,4 @@
+# file : tests/buildfile
+# license : ODB NCUEL; see accompanying LICENSE file
+
+./: {*/ -build/}
diff --git a/libodb-mysql/.gitignore b/libodb-mysql/.gitignore
new file mode 100644
index 0000000..1c363a0
--- /dev/null
+++ b/libodb-mysql/.gitignore
@@ -0,0 +1,25 @@
+# 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/libodb-mysql/GPLv2 b/libodb-mysql/GPLv2
new file mode 100644
index 0000000..3912109
--- /dev/null
+++ b/libodb-mysql/GPLv2
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) 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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. 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.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+ 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
+convey 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 2 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, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This 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 Library General
+Public License instead of this License.
diff --git a/libodb-mysql/INSTALL b/libodb-mysql/INSTALL
new file mode 100644
index 0000000..ef46275
--- /dev/null
+++ b/libodb-mysql/INSTALL
@@ -0,0 +1,6 @@
+The easiest way to build this package is with the bpkg package manager:
+
+$ bpkg build libodb-mysql
+
+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/libodb-mysql/LICENSE b/libodb-mysql/LICENSE
new file mode 100644
index 0000000..d96b938
--- /dev/null
+++ b/libodb-mysql/LICENSE
@@ -0,0 +1,20 @@
+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 2 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, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
diff --git a/libodb-mysql/NEWS b/libodb-mysql/NEWS
new file mode 120000
index 0000000..0fae0f8
--- /dev/null
+++ b/libodb-mysql/NEWS
@@ -0,0 +1 @@
+../NEWS \ No newline at end of file
diff --git a/libodb-mysql/README b/libodb-mysql/README
new file mode 100644
index 0000000..5e2315a
--- /dev/null
+++ b/libodb-mysql/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 MySQL ODB runtime library. Every application
+that includes code generated for the MySQL database will need to link
+to this library.
+
+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.
+
+Send questions, bug reports, or any other feedback to the
+odb-users@codesynthesis.com mailing list.
diff --git a/libodb-mysql/build/.gitignore b/libodb-mysql/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-mysql/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-mysql/build/bootstrap.build b/libodb-mysql/build/bootstrap.build
new file mode 100644
index 0000000..fb04583
--- /dev/null
+++ b/libodb-mysql/build/bootstrap.build
@@ -0,0 +1,10 @@
+# file : build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = libodb-mysql
+
+using version
+using config
+using dist
+using test
+using install
diff --git a/libodb-mysql/build/export.build b/libodb-mysql/build/export.build
new file mode 100644
index 0000000..459f96d
--- /dev/null
+++ b/libodb-mysql/build/export.build
@@ -0,0 +1,9 @@
+# file : build/export.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+$out_root/
+{
+ include odb/mysql/
+}
+
+export $out_root/odb/mysql/lib{odb-mysql}
diff --git a/libodb-mysql/build/root.build b/libodb-mysql/build/root.build
new file mode 100644
index 0000000..c98d520
--- /dev/null
+++ b/libodb-mysql/build/root.build
@@ -0,0 +1,45 @@
+# file : build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+config [bool] config.libodb_mysql.develop ?= false
+
+# Configure which database client library to use for build2 versions greater
+# than 0.12.0 and always use MySQL client library otherwise (due to the lack
+# of the project configuration variables support).
+#
+if ($build.version.number > 12000000000)
+{
+ # Whether to use the MySQL or MariaDB client library.
+ #
+ config [string] config.libodb_mysql.client_lib ?= 'mysql'
+
+ # Verify the config.libodb_mysql.client_lib configuration variable value and
+ # provide the short alias for it.
+ #
+ switch $config.libodb_mysql.client_lib
+ {
+ case 'mysql'
+ case 'mariadb'
+ client_lib = $config.libodb_mysql.client_lib
+
+ default
+ fail "invalid config.libodb_mysql.client_lib value '$config.libodb_mysql.client_lib'"
+ }
+}
+else
+ client_lib = 'mysql'
+
+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
diff --git a/libodb-mysql/buildfile b/libodb-mysql/buildfile
new file mode 100644
index 0000000..a04e206
--- /dev/null
+++ b/libodb-mysql/buildfile
@@ -0,0 +1,9 @@
+# file : buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -build/} doc{INSTALL NEWS README} legal{GPLv2 LICENSE} manifest
+
+# Don't install tests or the INSTALL file.
+#
+tests/: install = false
+doc{INSTALL}@./: install = false
diff --git a/libodb-mysql/manifest b/libodb-mysql/manifest
new file mode 100644
index 0000000..e0b04f7
--- /dev/null
+++ b/libodb-mysql/manifest
@@ -0,0 +1,173 @@
+: 1
+name: libodb-mysql
+version: 2.5.0-b.26.z
+project: odb
+summary: MySQL ODB runtime library
+license: GPL-2.0-only
+license: other: proprietary ; Not free/open source.
+topics: C++, ORM, MySQL, MariaDB, SQL
+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
+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: libmysqlclient >= 5.0.3 | libmariadb ^10.2.2
+depends: libmysqlclient >= 5.0.3
+depends: libodb == $
+depends: * cli ^1.2.0- ? ($config.libodb_mysql.develop)
+
+tests: odb-tests == $ \
+ ? (!$defined(config.odb_tests.database)) config.odb_tests.database=mysql
+
+builds: all
+builds: -wasm
+build-auxiliary: *-mysql_*
+default-build-config:
+\
+{
+ config.odb_tests.mysql.user=$getenv(DATABASE_USER)
+ config.odb_tests.mysql.database=$getenv(DATABASE_NAME)
+ config.odb_tests.mysql.host=$getenv(DATABASE_HOST)
+ config.odb_tests.mysql.port=$getenv(DATABASE_PORT)
+}+ odb-tests
+\
+
+# Only build this package configuration where it can be tested via odb-tests
+# package (see its manifest for details).
+#
+multi-builds: all
+multi-builds: -wasm
+multi-builds: -( +windows -gcc )
+multi-builds: &gcc
+multi-builds: &gcc-5+
+multi-builds: -static
+multi-build-config:
+\
+{
+ config.odb_tests.multi_database=true
+
+ config.odb_tests.mysql.user=$getenv(DATABASE_USER)
+ config.odb_tests.mysql.database=$getenv(DATABASE_NAME)
+ config.odb_tests.mysql.host=$getenv(DATABASE_HOST)
+ config.odb_tests.mysql.port=$getenv(DATABASE_PORT)
+}+ odb-tests
+\
+
+# Complements the default configuration (see odb-tests for background).
+#
+# Note: derives build-auxiliary from default.
+#
+custom-builds: latest
+custom-builds: -wasm
+custom-builds: -static ; Implementation uses plugins and requires -fPIC.
+#custom-build-bot: -- see below.
+custom-build-config:
+\
+{
+ config.odb_tests.mysql.user=$getenv(DATABASE_USER)
+ config.odb_tests.mysql.database=$getenv(DATABASE_NAME)
+ config.odb_tests.mysql.host=$getenv(DATABASE_HOST)
+ config.odb_tests.mysql.port=$getenv(DATABASE_PORT)
+}+ odb-tests
+\
+
+# Complements the multi configuration (see odb-tests for background).
+#
+# Note: derives build-auxiliary from default.
+#
+custom-multi-builds: latest
+custom-multi-builds: -wasm
+custom-multi-builds: -static ; Implementation uses plugins and requires -fPIC.
+#custom-multi-build-bot: -- see below.
+custom-multi-build-config:
+\
+{
+ config.odb_tests.multi_database=true
+
+ config.odb_tests.mysql.user=$getenv(DATABASE_USER)
+ config.odb_tests.mysql.database=$getenv(DATABASE_NAME)
+ config.odb_tests.mysql.host=$getenv(DATABASE_HOST)
+ config.odb_tests.mysql.port=$getenv(DATABASE_PORT)
+}+ odb-tests
+\
+
+custom-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuF4YmJmPHY52Q6N+YO0M
+lt/fCovdezleb2tVplyTnvbyAiPdmYCIIjVrsqUn3y46PdFtWEiSdsrCcncoxi6H
+8KelOB/oQ9pNTyEvwGKEH5ZIU7noLZYdXEfoNdvdL/pbY/7uLBZOSekfdQShZtbe
+uOZCM2Mhg2DD76TP/VAwaXuDCnEvxxU/yneUl5ZaBo62AWNrYJuSGAliCOpVpl6X
+X1kbHOvnCx7c9e3LxgaVivPaeZRKYg0OaFt96SBYEZzNPvjA8pMuKuj/vatHaCQ3
+NO9+r3TJ+4dQd7qN6Ju3zUJq9J/ndSh4lPvUalvvhdykecefhcyHwRZOG4xyFMFE
+nJM4sM+aZu6WoKATIKtk7On70inVr0sZJXwJ4Lt4oqaK2VthcSTby3wf2Yv4p5hL
+zNo31cCPmBRYzABcIc6ADYvexVK4uCwaim8xs7RK5Ug2Gv6vUWoRNZW8grIgDwUY
+5pZ4Zk3hW4ii2vehTaVrrmdW6XipIsT+ayiVX7eWuHHNxAeCojXVjOJu9B0ExMlD
+5tHZCs+SNdV5MceexecbptB7fZtRebP120yjLiSnZ5FpaQ1stusr0hSg+VQaX4np
+f5m1W/CcDr53PKWg/ayY9nWMUQaIwH4b69kLM+VTpYSbzu5UQJkmNBNq2EOHgoTv
+9MLA+cE/nNJ/rMI//MZ1+kcCAwEAAQ==
+-----END PUBLIC KEY-----
+\
+
+custom-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuLYZ68rotGDAtWViFHOe
+XEsmZB8BGI+af1ixh9JOy9BE4ohGTfPr1YsjGDzh+PhOVLAtyykOoT/qG7cuGB0T
+gBInoRrgVB2/ZKTMwxeDGb/TA3uykaXxcw7/liTsizHAY+phCNTbke8iER5Y78js
+9GlnTPmNhwFqEj2fwCz+2o08eyZvZ9Vj1fH/bFDCmDmU33JR3crtJlC8wPiF70Ho
+FJzHFdaFQl3MxvEV92HjOsyqozMi6tAVVefN1vapVQeNtjkB0Di18p0/EMugEuGU
+OxktjDHQWNaV8Ao6cCDk6OkJnM3ZNL1no3cV4cuF+/xI8UZzwfPoBnwg/s183Qzu
+pHHKOSHmuO0oVE/XohJhepSw3tb+wf5BwejRhYHikIjqCxJdm9H0QTiqXT82y24K
+yg3gkRMOgqnVxERKKP4ZknLSMQCEKiND/t2zdLJ/lxH9eHZdPHKk3OZZG292j+Bh
+fknxcTKNk1Dmf32Irs5hVrjsoU8eAutbItovzXdBaj//rn/ry/kUlCa1Ov6iLIDJ
+gyxmsDlgKNR/uE9ogmDn0ishJIoCmxeqenRfJkttr9pEsDsUFuB425QGqiSxa1jh
+PCNca3iRtO44wADXaQMTGpvLzBfdfVc8LoFpn+kynN0V1MvxAX4mHRXxw8ERXd3U
+dpHDhOthPLolJQrYKb/YyW8CAwEAAQ==
+-----END PUBLIC KEY-----
+\
+
+custom-multi-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuF4YmJmPHY52Q6N+YO0M
+lt/fCovdezleb2tVplyTnvbyAiPdmYCIIjVrsqUn3y46PdFtWEiSdsrCcncoxi6H
+8KelOB/oQ9pNTyEvwGKEH5ZIU7noLZYdXEfoNdvdL/pbY/7uLBZOSekfdQShZtbe
+uOZCM2Mhg2DD76TP/VAwaXuDCnEvxxU/yneUl5ZaBo62AWNrYJuSGAliCOpVpl6X
+X1kbHOvnCx7c9e3LxgaVivPaeZRKYg0OaFt96SBYEZzNPvjA8pMuKuj/vatHaCQ3
+NO9+r3TJ+4dQd7qN6Ju3zUJq9J/ndSh4lPvUalvvhdykecefhcyHwRZOG4xyFMFE
+nJM4sM+aZu6WoKATIKtk7On70inVr0sZJXwJ4Lt4oqaK2VthcSTby3wf2Yv4p5hL
+zNo31cCPmBRYzABcIc6ADYvexVK4uCwaim8xs7RK5Ug2Gv6vUWoRNZW8grIgDwUY
+5pZ4Zk3hW4ii2vehTaVrrmdW6XipIsT+ayiVX7eWuHHNxAeCojXVjOJu9B0ExMlD
+5tHZCs+SNdV5MceexecbptB7fZtRebP120yjLiSnZ5FpaQ1stusr0hSg+VQaX4np
+f5m1W/CcDr53PKWg/ayY9nWMUQaIwH4b69kLM+VTpYSbzu5UQJkmNBNq2EOHgoTv
+9MLA+cE/nNJ/rMI//MZ1+kcCAwEAAQ==
+-----END PUBLIC KEY-----
+\
+
+custom-multi-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuLYZ68rotGDAtWViFHOe
+XEsmZB8BGI+af1ixh9JOy9BE4ohGTfPr1YsjGDzh+PhOVLAtyykOoT/qG7cuGB0T
+gBInoRrgVB2/ZKTMwxeDGb/TA3uykaXxcw7/liTsizHAY+phCNTbke8iER5Y78js
+9GlnTPmNhwFqEj2fwCz+2o08eyZvZ9Vj1fH/bFDCmDmU33JR3crtJlC8wPiF70Ho
+FJzHFdaFQl3MxvEV92HjOsyqozMi6tAVVefN1vapVQeNtjkB0Di18p0/EMugEuGU
+OxktjDHQWNaV8Ao6cCDk6OkJnM3ZNL1no3cV4cuF+/xI8UZzwfPoBnwg/s183Qzu
+pHHKOSHmuO0oVE/XohJhepSw3tb+wf5BwejRhYHikIjqCxJdm9H0QTiqXT82y24K
+yg3gkRMOgqnVxERKKP4ZknLSMQCEKiND/t2zdLJ/lxH9eHZdPHKk3OZZG292j+Bh
+fknxcTKNk1Dmf32Irs5hVrjsoU8eAutbItovzXdBaj//rn/ry/kUlCa1Ov6iLIDJ
+gyxmsDlgKNR/uE9ogmDn0ishJIoCmxeqenRfJkttr9pEsDsUFuB425QGqiSxa1jh
+PCNca3iRtO44wADXaQMTGpvLzBfdfVc8LoFpn+kynN0V1MvxAX4mHRXxw8ERXd3U
+dpHDhOthPLolJQrYKb/YyW8CAwEAAQ==
+-----END PUBLIC KEY-----
+\
diff --git a/libodb-mysql/odb/mysql/auto-handle.hxx b/libodb-mysql/odb/mysql/auto-handle.hxx
new file mode 100644
index 0000000..27e9f2b
--- /dev/null
+++ b/libodb-mysql/odb/mysql/auto-handle.hxx
@@ -0,0 +1,94 @@
+// file : odb/mysql/auto-handle.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_AUTO_HANDLE_HXX
+#define ODB_MYSQL_AUTO_HANDLE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/mysql.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename H>
+ struct handle_traits;
+
+ template <>
+ struct handle_traits<MYSQL>
+ {
+ static void
+ release (MYSQL* h)
+ {
+ mysql_close (h);
+ }
+ };
+
+ template <>
+ struct handle_traits<MYSQL_STMT>
+ {
+ static void
+ release (MYSQL_STMT* h)
+ {
+ mysql_stmt_close (h);
+ }
+ };
+
+ template <typename H>
+ class auto_handle
+ {
+ public:
+ auto_handle (H* h = 0)
+ : h_ (h)
+ {
+ }
+
+ ~auto_handle ()
+ {
+ if (h_ != 0)
+ handle_traits<H>::release (h_);
+ }
+
+ H*
+ get () const
+ {
+ return h_;
+ }
+
+ void
+ reset (H* h = 0)
+ {
+ if (h_ != 0)
+ handle_traits<H>::release (h_);
+
+ h_ = h;
+ }
+
+ H*
+ release ()
+ {
+ H* h (h_);
+ h_ = 0;
+ return h;
+ }
+
+ operator H* () const
+ {
+ return h_;
+ }
+
+ private:
+ auto_handle (const auto_handle&);
+ auto_handle& operator= (const auto_handle&);
+
+ private:
+ H* h_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_AUTO_HANDLE_HXX
diff --git a/libodb-mysql/odb/mysql/binding.hxx b/libodb-mysql/odb/mysql/binding.hxx
new file mode 100644
index 0000000..628233d
--- /dev/null
+++ b/libodb-mysql/odb/mysql/binding.hxx
@@ -0,0 +1,45 @@
+// file : odb/mysql/binding.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_BINDING_HXX
+#define ODB_MYSQL_BINDING_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/mysql-types.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class LIBODB_MYSQL_EXPORT binding
+ {
+ public:
+ binding (): bind (0), count (0), version (0) {}
+
+ binding (MYSQL_BIND* b, std::size_t n)
+ : bind (b), count (n), version (0)
+ {
+ }
+
+ MYSQL_BIND* bind;
+ std::size_t count;
+ std::size_t version;
+
+ private:
+ binding (const binding&);
+ binding& operator= (const binding&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_BINDING_HXX
diff --git a/libodb-mysql/odb/mysql/buildfile b/libodb-mysql/odb/mysql/buildfile
new file mode 100644
index 0000000..42e5e88
--- /dev/null
+++ b/libodb-mysql/odb/mysql/buildfile
@@ -0,0 +1,142 @@
+# file : odb/mysql/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+define cli: file
+cli{*}: extension = cli
+
+import int_libs = libodb%lib{odb}
+
+import int_libs += ($client_lib == 'mysql' \
+ ? libmysqlclient%lib{mysqlclient} \
+ : libmariadb%lib{mariadb})
+
+lib{odb-mysql}: {hxx ixx txx cxx}{* -version} {hxx}{version} \
+ details/{hxx ixx txx cxx}{* -options} \
+ $int_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.
+#
+cxx.poptions =+ "-I$out_root" "-I$src_root"
+
+if ($client_lib == 'mariadb')
+ cxx.poptions += -DLIBODB_MYSQL_MARIADB
+
+obja{*}: cxx.poptions += -DLIBODB_MYSQL_STATIC_BUILD
+objs{*}: cxx.poptions += -DLIBODB_MYSQL_SHARED_BUILD
+
+# Export options.
+#
+lib{odb-mysql}:
+{
+ cxx.export.poptions = "-I$out_root" "-I$src_root"
+ cxx.export.libs = $int_libs
+}
+
+if ($client_lib == 'mariadb')
+ lib{odb-mysql}: cxx.export.poptions += -DLIBODB_MYSQL_MARIADB
+
+liba{odb-mysql}: cxx.export.poptions += -DLIBODB_MYSQL_STATIC
+libs{odb-mysql}: cxx.export.poptions += -DLIBODB_MYSQL_SHARED
+
+# For pre-releases use the complete version to make sure they cannot be used
+# in place of another pre-release or the final version. See the version module
+# for details on the version.* variable values.
+#
+if $version.pre_release
+ lib{odb-mysql}: bin.lib.version = @"-$version.project_id"
+else
+ lib{odb-mysql}: bin.lib.version = @"-$version.major.$version.minor"
+
+develop = $config.libodb_mysql.develop
+
+## Consumption build ($develop == false).
+#
+
+# Use pregenerated versions in the consumption build.
+#
+lib{odb-mysql}: details/pregenerated/{hxx ixx cxx}{**}: include = (!$develop)
+
+if! $develop
+ cxx.poptions =+ "-I($src_base/details/pregenerated)" # Note: must come first.
+
+# Don't install pregenerated headers since they are only used internally in
+# the database implementation (also below).
+#
+details/pregenerated/{hxx ixx}{*}: install = false
+
+# Distribute pregenerated versions only in the consumption build.
+#
+details/pregenerated/{hxx ixx cxx}{*}: dist = (!$develop)
+
+#
+##
+
+
+## Development build ($develop == true).
+#
+
+lib{odb-mysql}: details/{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.
+#
+<details/{hxx ixx cxx}{options}>: details/cli{options} $cli
+{
+ install = false
+ dist = ($develop ? pregenerated/odb/mysql/details/ : false)
+
+ # Symlink the generated code in src for convenience of development.
+ #
+ backlink = true
+}
+%
+if $develop
+{{
+ options = --include-with-brackets --include-prefix odb/mysql/details \
+ --guard-prefix LIBODB_MYSQL_DETAILS --generate-file-scanner \
+ --cli-namespace odb::mysql::details::cli --long-usage \
+ --generate-specifier --no-combined-flags
+
+ $cli $options -o $out_base/details/ $path($<[0])
+
+ # If the result differs from the pregenerated version, copy it over.
+ #
+ d = [dir_path] $src_base/details/pregenerated/odb/mysql/details/
+
+ if diff $d/options.hxx $path($>[0]) >- && \
+ diff $d/options.ixx $path($>[1]) >- && \
+ diff $d/options.cxx $path($>[2]) >-
+ exit
+ end
+
+ cp $path($>[0]) $d/options.hxx
+ cp $path($>[1]) $d/options.ixx
+ cp $path($>[2]) $d/options.cxx
+}}
+
+# Install into the odb/mysql/ subdirectory of, say, /usr/include/
+# recreating subdirectories.
+#
+install_include = [dir_path] include/odb/mysql/
+
+{hxx ixx txx}{*}:
+{
+ install = $install_include
+ install.subdirs = true
+}
diff --git a/libodb-mysql/odb/mysql/connection-factory.cxx b/libodb-mysql/odb/mysql/connection-factory.cxx
new file mode 100644
index 0000000..d53a4f4
--- /dev/null
+++ b/libodb-mysql/odb/mysql/connection-factory.cxx
@@ -0,0 +1,301 @@
+// file : odb/mysql/connection-factory.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/config.hxx> // ODB_THREADS_*
+#include <odb/mysql/details/config.hxx> // LIBODB_MYSQL_THR_KEY_VISIBLE
+
+#if defined(ODB_THREADS_POSIX) && defined(LIBODB_MYSQL_THR_KEY_VISIBLE)
+# include <pthread.h>
+#endif
+
+#include <cstdlib> // abort
+
+#include <odb/details/tls.hxx>
+#include <odb/details/lock.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/connection-factory.hxx>
+#include <odb/mysql/exceptions.hxx>
+
+// This key is in the mysql client library. We use it to resolve the
+// following problem: Some pthread implementations zero-out slots that
+// don't have destructors during thread termination. As a result, when
+// our destructor gets called and we call mysql_thread_end(), the thread-
+// specific slot used by MySQL may have been reset to 0 and as a result
+// MySQL thinks the data has been freed.
+//
+// To work around this problem we are going to cache the MySQL's slot
+// value and if, during destruction, we see that it is 0, we will restore
+// the original value before calling mysql_thread_end(). This will work
+// fine for as long as the following conditions are met:
+//
+// 1. MySQL doesn't use the destructor itself.
+// 2. Nobody else tried to call mysql_thread_end() before us.
+//
+// Note: in 5.7 the key has been made static and is no longer accessible.
+//
+#if defined(ODB_THREADS_POSIX) && defined(LIBODB_MYSQL_THR_KEY_VISIBLE)
+extern pthread_key_t THR_KEY_mysys;
+#endif
+
+using namespace std;
+
+namespace odb
+{
+ using namespace details;
+
+ namespace mysql
+ {
+ namespace
+ {
+ static bool main_thread_init_;
+
+ struct mysql_thread_init
+ {
+#ifndef ODB_THREADS_NONE
+ mysql_thread_init ()
+ : init_ (false)
+ {
+ if (!main_thread_init_)
+ {
+ if (::mysql_thread_init ())
+ {
+ throw database_exception (
+ CR_UNKNOWN_ERROR, "?????", "thread initialization failed");
+ }
+
+ init_ = true;
+
+#if defined(ODB_THREADS_POSIX) && defined(LIBODB_MYSQL_THR_KEY_VISIBLE)
+ value_ = pthread_getspecific (THR_KEY_mysys);
+#endif
+ }
+ }
+
+ ~mysql_thread_init ()
+ {
+ if (init_)
+ {
+#if defined(ODB_THREADS_POSIX) && defined(LIBODB_MYSQL_THR_KEY_VISIBLE)
+ if (pthread_getspecific (THR_KEY_mysys) == 0)
+ pthread_setspecific (THR_KEY_mysys, value_);
+#endif
+ mysql_thread_end ();
+ }
+ }
+
+ private:
+ bool init_;
+#if defined(ODB_THREADS_POSIX) && defined(LIBODB_MYSQL_THR_KEY_VISIBLE)
+ void* value_;
+#endif
+#endif // ODB_THREADS_NONE
+ };
+
+ static ODB_TLS_OBJECT (mysql_thread_init) mysql_thread_init_;
+
+ struct mysql_process_init
+ {
+ mysql_process_init ()
+ {
+ // Force allocation of our thread-specific key before THR_KEY_mysys
+ // in MySQL. This will (hopefully) get us the desired order of TLS
+ // destructor calls (i.e., our destructor before zeroing-out the
+ // THR_KEY_mysys value). This is pretty much the only way (except
+ // maybe guessing the THR_KEY_mysys value) to get clean thread
+ // termination if THR_KEY_mysys symbol is hidden, as is the case
+ // in the Fedora build of libmysqlclient. See also the comment
+ // at the beginning of this file.
+ //
+ main_thread_init_ = true;
+ tls_get (mysql_thread_init_);
+ main_thread_init_ = false;
+
+ if (mysql_library_init (0 ,0, 0))
+ abort ();
+ }
+
+ ~mysql_process_init ()
+ {
+ mysql_library_end ();
+
+ // Finalize the main thread now in case TLS destruction
+ // doesn't happen for the main thread.
+ //
+ tls_free (mysql_thread_init_);
+ }
+ };
+
+ static mysql_process_init mysql_process_init_;
+ }
+
+ // new_connection_factory
+ //
+ connection_ptr new_connection_factory::
+ connect ()
+ {
+ tls_get (mysql_thread_init_);
+
+ return connection_ptr (new (shared) connection (*this));
+ }
+
+ // connection_pool_factory
+ //
+ connection_pool_factory::pooled_connection_ptr connection_pool_factory::
+ create ()
+ {
+ return pooled_connection_ptr (new (shared) pooled_connection (*this));
+ }
+
+ connection_pool_factory::
+ ~connection_pool_factory ()
+ {
+ // Wait for all the connections currently in use to return to
+ // the pool.
+ //
+ lock l (mutex_);
+ while (in_use_ != 0)
+ {
+ waiters_++;
+ cond_.wait (l);
+ waiters_--;
+ }
+ }
+
+ connection_ptr connection_pool_factory::
+ connect ()
+ {
+ tls_get (mysql_thread_init_);
+
+ // The outer loop checks whether the connection we were
+ // given is still valid.
+ //
+ while (true)
+ {
+ pooled_connection_ptr c;
+
+ lock l (mutex_);
+
+ // The inner loop tries to find a free connection.
+ //
+ while (true)
+ {
+ // See if we have a spare connection.
+ //
+ if (connections_.size () != 0)
+ {
+ c = connections_.back ();
+ connections_.pop_back ();
+
+ c->callback_ = &c->cb_;
+ in_use_++;
+ break;
+ }
+
+ // See if we can create a new one.
+ //
+ if(max_ == 0 || in_use_ < max_)
+ {
+ // For new connections we don't need to ping so we
+ // can return immediately.
+ //
+ c = create ();
+ c->callback_ = &c->cb_;
+ in_use_++;
+ return c;
+ }
+
+ // Wait until someone releases a connection.
+ //
+ waiters_++;
+ cond_.wait (l);
+ waiters_--;
+ }
+
+ l.unlock ();
+
+ if (!ping_ || c->ping ())
+ return c;
+ }
+
+ return pooled_connection_ptr (); // Never reached.
+ }
+
+ void connection_pool_factory::
+ database (database_type& db)
+ {
+ tls_get (mysql_thread_init_);
+
+ bool first (db_ == 0);
+
+ connection_factory::database (db);
+
+ if (!first)
+ return;
+
+ if (min_ > 0)
+ {
+ connections_.reserve (min_);
+
+ for(size_t i (0); i < min_; ++i)
+ connections_.push_back (create ());
+ }
+ }
+
+ bool connection_pool_factory::
+ release (pooled_connection* c)
+ {
+ c->clear ();
+ c->callback_ = 0;
+
+ lock l (mutex_);
+
+ // Determine if we need to keep or free this connection.
+ //
+ bool keep (!c->failed () &&
+ (waiters_ != 0 ||
+ min_ == 0 ||
+ (connections_.size () + in_use_ <= min_)));
+
+ in_use_--;
+
+ if (keep)
+ {
+ connections_.push_back (pooled_connection_ptr (inc_ref (c)));
+ connections_.back ()->recycle ();
+ }
+
+ if (waiters_ != 0)
+ cond_.signal ();
+
+ return !keep;
+ }
+
+ //
+ // connection_pool_factory::pooled_connection
+ //
+
+ connection_pool_factory::pooled_connection::
+ pooled_connection (connection_pool_factory& f)
+ : connection (f)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ connection_pool_factory::pooled_connection::
+ pooled_connection (connection_pool_factory& f, MYSQL* handle)
+ : connection (f, handle)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ bool connection_pool_factory::pooled_connection::
+ zero_counter (void* arg)
+ {
+ pooled_connection* c (static_cast<pooled_connection*> (arg));
+ return static_cast<connection_pool_factory&> (c->factory_).release (c);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/connection-factory.hxx b/libodb-mysql/odb/mysql/connection-factory.hxx
new file mode 100644
index 0000000..12bd4d2
--- /dev/null
+++ b/libodb-mysql/odb/mysql/connection-factory.hxx
@@ -0,0 +1,140 @@
+// file : odb/mysql/connection-factory.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_CONNECTION_FACTORY_HXX
+#define ODB_MYSQL_CONNECTION_FACTORY_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cstddef> // std::size_t
+#include <cassert>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/connection.hxx>
+
+#include <odb/details/mutex.hxx>
+#include <odb/details/condition.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class LIBODB_MYSQL_EXPORT new_connection_factory: public connection_factory
+ {
+ public:
+ new_connection_factory () {}
+
+ virtual connection_ptr
+ connect ();
+
+ private:
+ new_connection_factory (const new_connection_factory&);
+ new_connection_factory& operator= (const new_connection_factory&);
+ };
+
+ class LIBODB_MYSQL_EXPORT connection_pool_factory:
+ public connection_factory
+ {
+ public:
+ // The max_connections argument specifies the maximum number of
+ // concurrent connections this pool will maintain. If this value
+ // is 0 then the pool will create a new connection every time all
+ // of the existing connections are in use.
+ //
+ // The min_connections argument specifies the minimum number of
+ // connections that should be maintained by the pool. If the
+ // number of connections maintained by the pool exceeds this
+ // number and there are no active waiters for a new connection,
+ // then the pool will release the excess connections. If this
+ // value is 0 then the pool will maintain all the connections
+ // that were ever created.
+ //
+ // The ping argument specifies whether to ping the connection to
+ // make sure it is still alive before returning it to the caller.
+ //
+ connection_pool_factory (std::size_t max_connections = 0,
+ std::size_t min_connections = 0,
+ bool ping = true)
+ : max_ (max_connections),
+ min_ (min_connections),
+ ping_ (ping),
+ in_use_ (0),
+ waiters_ (0),
+ cond_ (mutex_)
+ {
+ // max_connections == 0 means unlimited.
+ //
+ assert (max_connections == 0 || max_connections >= min_connections);
+ }
+
+ virtual connection_ptr
+ connect ();
+
+ virtual void
+ database (database_type&);
+
+ virtual
+ ~connection_pool_factory ();
+
+ private:
+ connection_pool_factory (const connection_pool_factory&);
+ connection_pool_factory& operator= (const connection_pool_factory&);
+
+ protected:
+ class LIBODB_MYSQL_EXPORT pooled_connection: public connection
+ {
+ public:
+ pooled_connection (connection_pool_factory&);
+ pooled_connection (connection_pool_factory&, MYSQL*);
+
+ private:
+ static bool
+ zero_counter (void*);
+
+ private:
+ friend class connection_pool_factory;
+
+ shared_base::refcount_callback cb_;
+ };
+
+ friend class pooled_connection;
+
+ typedef details::shared_ptr<pooled_connection> pooled_connection_ptr;
+ typedef std::vector<pooled_connection_ptr> connections;
+
+ // This function is called whenever the pool needs to create a new
+ // connection.
+ //
+ virtual pooled_connection_ptr
+ create ();
+
+ protected:
+ // Return true if the connection should be deleted, false otherwise.
+ //
+ bool
+ release (pooled_connection*);
+
+ protected:
+ const std::size_t max_;
+ const std::size_t min_;
+ const bool ping_;
+
+ std::size_t in_use_; // Number of connections currently in use.
+ std::size_t waiters_; // Number of threads waiting for a connection.
+
+ connections connections_;
+
+ details::mutex mutex_;
+ details::condition cond_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_CONNECTION_FACTORY_HXX
diff --git a/libodb-mysql/odb/mysql/connection.cxx b/libodb-mysql/odb/mysql/connection.cxx
new file mode 100644
index 0000000..96baf3e
--- /dev/null
+++ b/libodb-mysql/odb/mysql/connection.cxx
@@ -0,0 +1,218 @@
+// file : odb/mysql/connection.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <new> // std::bad_alloc
+#include <string>
+
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/transaction.hxx>
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/error.hxx>
+#include <odb/mysql/exceptions.hxx>
+#include <odb/mysql/statement-cache.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mysql
+ {
+ connection::
+ connection (connection_factory& cf)
+ : odb::connection (cf), failed_ (false), active_ (0)
+ {
+ if (mysql_init (&mysql_) == 0)
+ throw bad_alloc ();
+
+ handle_.reset (&mysql_);
+
+ database_type& db (database ());
+
+ if (*db.charset () != '\0')
+ // Can only fail if we pass an unknown option.
+ //
+ mysql_options (handle_, MYSQL_SET_CHARSET_NAME, db.charset ());
+
+ // Force the CLIENT_FOUND_ROWS flag so that UPDATE returns the
+ // number of found rows, not the number of changed rows. This
+ // is necessary to distinguish between the object-not-persistent
+ // and nothing-changed conditions.
+ //
+ if (mysql_real_connect (handle_,
+ db.host (),
+ db.user (),
+ db.password (),
+ db.db (),
+ db.port (),
+ db.socket (),
+ db.client_flags () | CLIENT_FOUND_ROWS) == 0)
+ {
+ // We cannot use translate_error() here since there is no connection
+ // yet.
+ //
+ unsigned int e (mysql_errno (handle_));
+
+ if (e == CR_OUT_OF_MEMORY)
+ throw bad_alloc ();
+
+ throw database_exception (
+ e, mysql_sqlstate (handle_), mysql_error (handle_));
+ }
+
+ // Do this after we have established the connection.
+ //
+ statement_cache_.reset (new statement_cache_type (*this));
+ }
+
+ connection::
+ connection (connection_factory& cf, MYSQL* handle)
+ : odb::connection (cf),
+ failed_ (false),
+ handle_ (handle),
+ active_ (0),
+ statement_cache_ (new statement_cache_type (*this))
+ {
+ }
+
+ connection::
+ ~connection ()
+ {
+ active_ = 0;
+
+ // Destroy prepared query statements before freeing the connections.
+ //
+ recycle ();
+ clear_prepared_map ();
+
+ if (stmt_handles_.size () > 0)
+ free_stmt_handles ();
+ }
+
+ transaction_impl* connection::
+ begin ()
+ {
+ return new transaction_impl (connection_ptr (inc_ref (this)));
+ }
+
+ unsigned long long connection::
+ execute (const char* s, size_t n)
+ {
+ clear ();
+
+ {
+ odb::tracer* t;
+ if ((t = transaction_tracer ()) ||
+ (t = tracer ()) ||
+ (t = database ().tracer ()))
+ {
+ string str (s, n);
+ t->execute (*this, str.c_str ());
+ }
+ }
+
+ if (mysql_real_query (handle_, s, static_cast<unsigned long> (n)))
+ translate_error (*this);
+
+ // Get the affected row count, if any. If the statement has a result
+ // set (e.g., SELECT), we first need to call mysql_store_result().
+ //
+ unsigned long long r (0);
+
+ if (mysql_field_count (handle_) == 0)
+ r = static_cast<unsigned long long> (mysql_affected_rows (handle_));
+ else
+ {
+ if (MYSQL_RES* rs = mysql_store_result (handle_))
+ {
+ r = static_cast<unsigned long long> (mysql_num_rows (rs));
+ mysql_free_result (rs);
+ }
+ else
+ translate_error (*this);
+ }
+
+ return r;
+ }
+
+ bool connection::
+ ping ()
+ {
+ if (failed ())
+ return false;
+
+ if (!mysql_ping (handle_))
+ return true;
+
+ switch (mysql_errno (handle_))
+ {
+ case CR_SERVER_LOST:
+ case CR_SERVER_GONE_ERROR:
+ {
+ mark_failed ();
+ return false;
+ }
+ default:
+ {
+ translate_error (*this);
+ return false; // Never reached.
+ }
+ }
+ }
+
+ void connection::
+ clear_ ()
+ {
+ active_->cancel (); // Should clear itself from active_.
+ }
+
+ MYSQL_STMT* connection::
+ alloc_stmt_handle ()
+ {
+ MYSQL_STMT* stmt (mysql_stmt_init (handle_));
+
+ if (stmt == 0)
+ throw bad_alloc ();
+
+ return stmt;
+ }
+
+ void connection::
+ free_stmt_handle (auto_handle<MYSQL_STMT>& stmt)
+ {
+ if (active_ == 0)
+ stmt.reset ();
+ else
+ {
+ stmt_handles_.push_back (stmt); // May throw.
+ stmt.release ();
+ }
+ }
+
+ void connection::
+ free_stmt_handles ()
+ {
+ for (stmt_handles::iterator i (stmt_handles_.begin ()),
+ e (stmt_handles_.end ()); i != e; ++i)
+ {
+ mysql_stmt_close (*i);
+ }
+
+ stmt_handles_.clear ();
+ }
+
+ // connection_factory
+ //
+ connection_factory::
+ ~connection_factory ()
+ {
+ }
+
+ void connection_factory::
+ database (database_type& db)
+ {
+ odb::connection_factory::db_ = &db;
+ db_ = &db;
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/connection.hxx b/libodb-mysql/odb/mysql/connection.hxx
new file mode 100644
index 0000000..31b24ed
--- /dev/null
+++ b/libodb-mysql/odb/mysql/connection.hxx
@@ -0,0 +1,231 @@
+// file : odb/mysql/connection.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_CONNECTION_HXX
+#define ODB_MYSQL_CONNECTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+
+#include <odb/connection.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/query.hxx>
+#include <odb/mysql/tracer.hxx>
+#include <odb/mysql/transaction-impl.hxx>
+#include <odb/mysql/auto-handle.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/unique-ptr.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class statement_cache;
+ class connection_factory;
+
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+
+ class LIBODB_MYSQL_EXPORT connection: public odb::connection
+ {
+ public:
+ typedef mysql::statement_cache statement_cache_type;
+ typedef mysql::database database_type;
+
+ virtual
+ ~connection ();
+
+ connection (connection_factory&);
+ connection (connection_factory&, MYSQL* handle);
+
+ database_type&
+ database ();
+
+ public:
+ virtual transaction_impl*
+ begin ();
+
+ public:
+ using odb::connection::execute;
+
+ virtual unsigned long long
+ execute (const char* statement, std::size_t length);
+
+ // Query preparation.
+ //
+ public:
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const mysql::query_base&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const odb::query_base&);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef mysql::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ using odb::connection::tracer;
+
+ public:
+ bool
+ failed () const
+ {
+ return failed_;
+ }
+
+ void
+ mark_failed ()
+ {
+ failed_ = true;
+ }
+
+ // Ping the server to make sure the connection is still alive. Return
+ // true if successful, mark the connection as failed and return false
+ // otherwise. This function can also throw database_exception.
+ //
+ bool
+ ping ();
+
+ public:
+ MYSQL*
+ handle ()
+ {
+ return handle_;
+ }
+
+ statement_cache_type&
+ statement_cache ()
+ {
+ return *statement_cache_;
+ }
+
+ public:
+ statement*
+ active ()
+ {
+ return active_;
+ }
+
+ void
+ active (statement* s)
+ {
+ active_ = s;
+
+ if (s == 0 && stmt_handles_.size () > 0)
+ free_stmt_handles ();
+ }
+
+ // Cancel and clear the active statement, if any.
+ //
+ void
+ clear ()
+ {
+ if (active_ != 0)
+ clear_ ();
+ }
+
+ public:
+ MYSQL_STMT*
+ alloc_stmt_handle ();
+
+ void
+ free_stmt_handle (auto_handle<MYSQL_STMT>&);
+
+ private:
+ connection (const connection&);
+ connection& operator= (const connection&);
+
+ private:
+ void
+ free_stmt_handles ();
+
+ void
+ clear_ ();
+
+ private:
+ friend class transaction_impl; // invalidate_results()
+
+ private:
+ bool failed_;
+
+ MYSQL mysql_;
+ auto_handle<MYSQL> handle_;
+
+ statement* active_;
+
+ // Keep statement_cache_ after handle_ so that it is destroyed before
+ // the connection is closed.
+ //
+ details::unique_ptr<statement_cache_type> statement_cache_;
+
+ // List of "delayed" statement handles to be freed next time there
+ // is no active statement.
+ //
+ typedef std::vector<MYSQL_STMT*> stmt_handles;
+ stmt_handles stmt_handles_;
+ };
+
+ class LIBODB_MYSQL_EXPORT connection_factory:
+ public odb::connection_factory
+ {
+ public:
+ typedef mysql::database database_type;
+
+ virtual void
+ database (database_type&);
+
+ database_type&
+ database () {return *db_;}
+
+ virtual connection_ptr
+ connect () = 0;
+
+ virtual
+ ~connection_factory ();
+
+ connection_factory (): db_ (0) {}
+
+ // Needed to break the circular connection_factory-database dependency
+ // (odb::connection_factory has the odb::database member).
+ //
+ protected:
+ database_type* db_;
+ };
+ }
+}
+
+#include <odb/mysql/connection.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_CONNECTION_HXX
diff --git a/libodb-mysql/odb/mysql/connection.ixx b/libodb-mysql/odb/mysql/connection.ixx
new file mode 100644
index 0000000..d2f09ac
--- /dev/null
+++ b/libodb-mysql/odb/mysql/connection.ixx
@@ -0,0 +1,44 @@
+// file : odb/mysql/connection.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mysql
+ {
+ inline database& connection::
+ database ()
+ {
+ return static_cast<connection_factory&> (factory_).database ();
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const mysql::query_base& q)
+ {
+ return query_<T, id_mysql>::call (*this, n, q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return prepare_query<T> (n, mysql::query_base (q));
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/container-statements.hxx b/libodb-mysql/odb/mysql/container-statements.hxx
new file mode 100644
index 0000000..48e769d
--- /dev/null
+++ b/libodb-mysql/odb/mysql/container-statements.hxx
@@ -0,0 +1,360 @@
+// file : odb/mysql/container-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_CONTAINER_STATEMENTS_HXX
+#define ODB_MYSQL_CONTAINER_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/statement.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class connection;
+
+ // Template argument is the generated abstract container traits type.
+ // That is, it doesn't need to provide column counts and statements.
+ //
+ template <typename T>
+ class container_statements
+ {
+ public:
+ typedef T traits;
+
+ typedef typename traits::data_image_type data_image_type;
+ typedef typename traits::functions_type functions_type;
+
+ typedef mysql::insert_statement insert_statement_type;
+ typedef mysql::select_statement select_statement_type;
+ typedef mysql::delete_statement delete_statement_type;
+
+ typedef mysql::connection connection_type;
+
+ container_statements (connection_type&, binding& id_binding);
+
+ connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // Functions.
+ //
+ functions_type&
+ functions ()
+ {
+ return functions_;
+ }
+
+ // Schema version.
+ //
+ const schema_version_migration&
+ version_migration () const {return *svm_;}
+
+ void
+ version_migration (const schema_version_migration& svm) {svm_ = &svm;}
+
+ // Id image binding (external).
+ //
+ const binding&
+ id_binding ()
+ {
+ return id_binding_;
+ }
+
+ // Data image. The image is split into the id (that comes as a
+ // binding) and index/key plus value which are in data_image_type.
+ // The select binding is a subset of the full binding (no id).
+ //
+ data_image_type&
+ data_image ()
+ {
+ return data_image_;
+ }
+
+ MYSQL_BIND*
+ data_bind ()
+ {
+ return insert_image_binding_.bind;
+ }
+
+ bool
+ data_binding_test_version () const
+ {
+ return data_id_binding_version_ != id_binding_.version ||
+ data_image_version_ != data_image_.version ||
+ insert_image_binding_.version == 0;
+ }
+
+ void
+ data_binding_update_version ()
+ {
+ data_id_binding_version_ = id_binding_.version;
+ data_image_version_ = data_image_.version;
+ insert_image_binding_.version++;
+ select_image_binding_.version++;
+ }
+
+ my_bool*
+ select_image_truncated ()
+ {
+ return select_image_truncated_;
+ }
+
+ //
+ // Statements.
+ //
+
+ insert_statement_type&
+ insert_statement ()
+ {
+ if (insert_ == 0)
+ insert_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ insert_text_,
+ versioned_, // Process if versioned.
+ insert_image_binding_,
+ 0,
+ false));
+
+ return *insert_;
+ }
+
+ select_statement_type&
+ select_statement ()
+ {
+ if (select_ == 0)
+ select_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ select_text_,
+ versioned_, // Process if versioned.
+ false, // Don't optimize.
+ id_binding_,
+ select_image_binding_,
+ false));
+
+ return *select_;
+ }
+
+ delete_statement_type&
+ delete_statement ()
+ {
+ if (delete_ == 0)
+ delete_.reset (
+ new (details::shared) delete_statement_type (
+ conn_, delete_text_, id_binding_, false));
+
+ return *delete_;
+ }
+
+ private:
+ container_statements (const container_statements&);
+ container_statements& operator= (const container_statements&);
+
+ protected:
+ connection_type& conn_;
+ binding& id_binding_;
+
+ functions_type functions_;
+
+ data_image_type data_image_;
+ std::size_t data_image_version_;
+ std::size_t data_id_binding_version_;
+
+ binding insert_image_binding_;
+
+ binding select_image_binding_;
+ my_bool* select_image_truncated_;
+
+ const char* insert_text_;
+ const char* select_text_;
+ const char* delete_text_;
+
+ bool versioned_;
+ const schema_version_migration* svm_;
+
+ details::shared_ptr<insert_statement_type> insert_;
+ details::shared_ptr<select_statement_type> select_;
+ details::shared_ptr<delete_statement_type> delete_;
+ };
+
+ template <typename T>
+ class smart_container_statements: public container_statements<T>
+ {
+ public:
+ typedef T traits;
+ typedef typename traits::cond_image_type cond_image_type;
+
+ typedef mysql::update_statement update_statement_type;
+ typedef mysql::delete_statement delete_statement_type;
+
+ typedef mysql::connection connection_type;
+
+ smart_container_statements (connection_type&, binding& id_binding);
+
+ // Condition image. The image is split into the id (that comes as
+ // a binding) and index/key/value which is in cond_image_type.
+ //
+ cond_image_type&
+ cond_image ()
+ {
+ return cond_image_;
+ }
+
+ MYSQL_BIND*
+ cond_bind ()
+ {
+ return cond_image_binding_.bind;
+ }
+
+ bool
+ cond_binding_test_version () const
+ {
+ return cond_id_binding_version_ != this->id_binding_.version ||
+ cond_image_version_ != cond_image_.version ||
+ cond_image_binding_.version == 0;
+ }
+
+ void
+ cond_binding_update_version ()
+ {
+ cond_id_binding_version_ = this->id_binding_.version;
+ cond_image_version_ = cond_image_.version;
+ cond_image_binding_.version++;
+ }
+
+ // Update image. The image is split as follows: value comes
+ // from the data image, id comes as binding, and index/key
+ // comes from the condition image.
+ //
+ MYSQL_BIND*
+ update_bind ()
+ {
+ return update_image_binding_.bind;
+ }
+
+ bool
+ update_binding_test_version () const
+ {
+ return update_id_binding_version_ != this->id_binding_.version ||
+ update_cond_image_version_ != cond_image_.version ||
+ update_data_image_version_ != this->data_image_.version ||
+ update_image_binding_.version == 0;
+ }
+
+ void
+ update_binding_update_version ()
+ {
+ update_id_binding_version_ = this->id_binding_.version;
+ update_cond_image_version_ = cond_image_.version;
+ update_data_image_version_ = this->data_image_.version;
+ update_image_binding_.version++;
+ }
+
+ //
+ // Statements.
+ //
+
+ delete_statement_type&
+ delete_statement ()
+ {
+ if (this->delete_ == 0)
+ this->delete_.reset (
+ new (details::shared) delete_statement_type (
+ this->conn_,
+ this->delete_text_,
+ this->cond_image_binding_,
+ false));
+
+ return *this->delete_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ this->conn_,
+ update_text_,
+ this->versioned_, // Process if versioned.
+ update_image_binding_,
+ false));
+
+ return *update_;
+ }
+
+ protected:
+ cond_image_type cond_image_;
+ std::size_t cond_image_version_;
+ std::size_t cond_id_binding_version_;
+ binding cond_image_binding_;
+
+ std::size_t update_id_binding_version_;
+ std::size_t update_cond_image_version_;
+ std::size_t update_data_image_version_;
+ binding update_image_binding_;
+
+ const char* update_text_;
+
+ details::shared_ptr<update_statement_type> update_;
+ };
+
+ // Template argument is the generated concrete container traits type.
+ //
+ template <typename T>
+ class container_statements_impl: public T::statements_type
+ {
+ public:
+ typedef T traits;
+ typedef typename T::statements_type base;
+ typedef mysql::connection connection_type;
+
+ container_statements_impl (connection_type&, binding&);
+
+ private:
+ container_statements_impl (const container_statements_impl&);
+ container_statements_impl& operator= (const container_statements_impl&);
+
+ private:
+ MYSQL_BIND data_image_bind_[traits::data_column_count];
+ my_bool select_image_truncated_array_[traits::data_column_count -
+ traits::id_column_count];
+ };
+
+ template <typename T>
+ class smart_container_statements_impl: public container_statements_impl<T>
+ {
+ public:
+ typedef T traits;
+ typedef mysql::connection connection_type;
+
+ smart_container_statements_impl (connection_type&, binding&);
+
+ private:
+ MYSQL_BIND cond_image_bind_[traits::cond_column_count];
+ MYSQL_BIND update_image_bind_[traits::value_column_count +
+ traits::cond_column_count];
+ };
+ }
+}
+
+#include <odb/mysql/container-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_CONTAINER_STATEMENTS_HXX
diff --git a/libodb-mysql/odb/mysql/container-statements.txx b/libodb-mysql/odb/mysql/container-statements.txx
new file mode 100644
index 0000000..55bc5fe
--- /dev/null
+++ b/libodb-mysql/odb/mysql/container-statements.txx
@@ -0,0 +1,107 @@
+// file : odb/mysql/container-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace mysql
+ {
+ // container_statements
+ //
+ template <typename T>
+ container_statements<T>::
+ container_statements (connection_type& conn, binding& id)
+ : conn_ (conn),
+ id_binding_ (id),
+ functions_ (this),
+ insert_image_binding_ (0, 0), // Initialized by impl.
+ select_image_binding_ (0, 0), // Initialized by impl.
+ svm_ (0)
+ {
+ functions_.insert_ = &traits::insert;
+ functions_.select_ = &traits::select;
+ functions_.delete__ = &traits::delete_;
+
+ data_image_.version = 0;
+ data_image_version_ = 0;
+ data_id_binding_version_ = 0;
+ }
+
+ // smart_container_statements
+ //
+ template <typename T>
+ smart_container_statements<T>::
+ smart_container_statements (connection_type& conn, binding& id)
+ : container_statements<T> (conn, id),
+ cond_image_binding_ (0, 0), // Initialized by impl.
+ update_image_binding_ (0, 0) // Initialized by impl.
+ {
+ this->functions_.update_ = &traits::update;
+
+ cond_image_.version = 0;
+ cond_image_version_ = 0;
+ cond_id_binding_version_ = 0;
+
+ update_id_binding_version_ = 0;
+ update_cond_image_version_ = 0;
+ update_data_image_version_ = 0;
+ }
+
+ // container_statements_impl
+ //
+ template <typename T>
+ container_statements_impl<T>::
+ container_statements_impl (connection_type& conn, binding& id)
+ : base (conn, id)
+ {
+ this->select_image_truncated_ = select_image_truncated_array_;
+
+ this->insert_image_binding_.bind = data_image_bind_;
+ this->insert_image_binding_.count = traits::data_column_count;
+
+ this->select_image_binding_.bind = data_image_bind_ +
+ traits::id_column_count;
+ this->select_image_binding_.count = traits::data_column_count -
+ traits::id_column_count;
+
+ std::memset (data_image_bind_, 0, sizeof (data_image_bind_));
+ std::memset (select_image_truncated_array_,
+ 0,
+ sizeof (select_image_truncated_array_));
+
+ for (std::size_t i (0);
+ i < traits::data_column_count - traits::id_column_count;
+ ++i)
+ data_image_bind_[i + traits::id_column_count].error =
+ select_image_truncated_array_ + i;
+
+ this->insert_text_ = traits::insert_statement;
+ this->select_text_ = traits::select_statement;
+ this->delete_text_ = traits::delete_statement;
+
+ this->versioned_ = traits::versioned;
+ }
+
+ // smart_container_statements_impl
+ //
+ template <typename T>
+ smart_container_statements_impl<T>::
+ smart_container_statements_impl (connection_type& conn, binding& id)
+ : container_statements_impl<T> (conn, id)
+ {
+ this->cond_image_binding_.bind = cond_image_bind_;
+ this->cond_image_binding_.count = traits::cond_column_count;
+
+ this->update_image_binding_.bind = update_image_bind_;
+ this->update_image_binding_.count = traits::value_column_count +
+ traits::cond_column_count;
+
+ std::memset (cond_image_bind_, 0, sizeof (cond_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+
+ this->update_text_ = traits::update_statement;
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/database.cxx b/libodb-mysql/odb/mysql/database.cxx
new file mode 100644
index 0000000..6c0f1d6
--- /dev/null
+++ b/libodb-mysql/odb/mysql/database.cxx
@@ -0,0 +1,357 @@
+// file : odb/mysql/database.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <sstream>
+#include <cstring> // std::memset
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/connection-factory.hxx>
+#include <odb/mysql/transaction.hxx>
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/exceptions.hxx>
+
+#include <odb/mysql/details/options.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mysql
+ {
+ using odb::details::transfer_ptr;
+
+ database::
+ ~database ()
+ {
+ }
+
+ database::
+ database (const char* user,
+ const char* passwd,
+ const char* db,
+ const char* host,
+ unsigned int port,
+ const char* socket,
+ const char* charset,
+ unsigned long client_flags,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mysql),
+ user_ (user ? user : ""),
+ passwd_str_ (passwd ? passwd : ""),
+ passwd_ (passwd ? passwd_str_.c_str () : 0),
+ db_ (db ? db : ""),
+ host_ (host ? host : ""),
+ port_ (port),
+ socket_str_ (socket ? socket : ""),
+ socket_ (socket ? socket_str_.c_str () : 0),
+ charset_ (charset == 0 ? "" : charset),
+ client_flags_ (client_flags),
+ factory_ (factory.transfer ())
+ {
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (const string& user,
+ const string& passwd,
+ const string& db,
+ const string& host,
+ unsigned int port,
+ const string* socket,
+ const string& charset,
+ unsigned long client_flags,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mysql),
+ user_ (user),
+ passwd_str_ (passwd),
+ passwd_ (passwd_str_.c_str ()),
+ db_ (db),
+ host_ (host),
+ port_ (port),
+ socket_str_ (socket ? *socket : ""),
+ socket_ (socket ? socket_str_.c_str () : 0),
+ charset_ (charset),
+ client_flags_ (client_flags),
+ factory_ (factory.transfer ())
+ {
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (const string& user,
+ const string* passwd,
+ const string& db,
+ const string& host,
+ unsigned int port,
+ const string* socket,
+ const string& charset,
+ unsigned long client_flags,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mysql),
+ user_ (user),
+ passwd_str_ (passwd ? *passwd : ""),
+ passwd_ (passwd ? passwd_str_.c_str () : 0),
+ db_ (db),
+ host_ (host),
+ port_ (port),
+ socket_str_ (socket ? *socket : ""),
+ socket_ (socket ? socket_str_.c_str () : 0),
+ charset_ (charset),
+ client_flags_ (client_flags),
+ factory_ (factory.transfer ())
+ {
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (const string& user,
+ const string& passwd,
+ const string& db,
+ const string& host,
+ unsigned int port,
+ const string& socket,
+ const string& charset,
+ unsigned long client_flags,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mysql),
+ user_ (user),
+ passwd_str_ (passwd),
+ passwd_ (passwd_str_.c_str ()),
+ db_ (db),
+ host_ (host),
+ port_ (port),
+ socket_str_ (socket),
+ socket_ (socket_str_.c_str ()),
+ charset_ (charset),
+ client_flags_ (client_flags),
+ factory_ (factory.transfer ())
+ {
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (const string& user,
+ const string* passwd,
+ const string& db,
+ const string& host,
+ unsigned int port,
+ const string& socket,
+ const string& charset,
+ unsigned long client_flags,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mysql),
+ user_ (user),
+ passwd_str_ (passwd ? *passwd : ""),
+ passwd_ (passwd ? passwd_str_.c_str () : 0),
+ db_ (db),
+ host_ (host),
+ port_ (port),
+ socket_str_ (socket),
+ socket_ (socket_str_.c_str ()),
+ charset_ (charset),
+ client_flags_ (client_flags),
+ factory_ (factory.transfer ())
+ {
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (int& argc,
+ char* argv[],
+ bool erase,
+ const string& charset,
+ unsigned long client_flags,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mysql),
+ passwd_ (0),
+ socket_ (0),
+ charset_ (charset),
+ client_flags_ (client_flags),
+ factory_ (factory.transfer ())
+ {
+ using namespace details;
+
+ try
+ {
+ cli::argv_file_scanner scan (argc, argv, "--options-file", erase);
+ options ops (scan, cli::unknown_mode::skip, cli::unknown_mode::skip);
+
+ user_ = ops.user ();
+
+ if (ops.password_specified ())
+ {
+ passwd_str_ = ops.password ();
+ passwd_ = passwd_str_.c_str ();
+ }
+
+ db_ = ops.database ();
+ host_ = ops.host ();
+ port_ = ops.port ();
+
+ if (ops.socket_specified ())
+ {
+ socket_str_ = ops.socket ();
+ socket_ = socket_str_.c_str ();
+ }
+ }
+ catch (const cli::exception& e)
+ {
+ ostringstream ostr;
+ ostr << e;
+ throw cli_exception (ostr.str ());
+ }
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ void database::
+ print_usage (ostream& os)
+ {
+ details::options::print_usage (os);
+ }
+
+ transaction_impl* database::
+ begin ()
+ {
+ return new transaction_impl (*this);
+ }
+
+ odb::connection* database::
+ connection_ ()
+ {
+ connection_ptr c (factory_->connect ());
+ return c.release ();
+ }
+
+ const database::schema_version_info& database::
+ load_schema_version (const string& name) const
+ {
+ schema_version_info& svi (schema_version_map_[name]);
+
+ // Construct the SELECT statement text.
+ //
+ string text ("SELECT `version`, `migration` FROM ");
+
+ if (!svi.version_table.empty ())
+ text += svi.version_table; // Already quoted.
+ else if (!schema_version_table_.empty ())
+ text += schema_version_table_; // Already quoted.
+ else
+ text += "`schema_version`";
+
+ text += " WHERE `name` = ?";
+
+ // Bind parameters and results.
+ //
+ unsigned long psize[1] = {static_cast<unsigned long> (name.size ())};
+ my_bool pnull[1] = {0};
+ MYSQL_BIND pbind[1];
+ binding param (pbind, 1);
+
+ memset (pbind, 0, sizeof (pbind));
+
+ pbind[0].buffer_type = MYSQL_TYPE_STRING;
+ pbind[0].buffer = const_cast<char*> (name.c_str ());
+ pbind[0].buffer_length = psize[0];
+ pbind[0].length = &psize[0];
+ pbind[0].is_null = &pnull[0];
+
+ param.version++;
+
+ signed char migration;
+ my_bool rnull[2];
+ MYSQL_BIND rbind[2];
+ binding result (rbind, 2);
+
+ memset (rbind, 0, sizeof (rbind));
+
+ rbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
+ rbind[0].is_unsigned = 1;
+ rbind[0].buffer = &svi.version;
+ rbind[0].is_null = &rnull[0];
+
+ rbind[1].buffer_type = MYSQL_TYPE_TINY;
+ rbind[1].is_unsigned = 0;
+ rbind[1].buffer = &migration;
+ rbind[1].is_null = &rnull[1];
+
+ result.version++;
+
+ // If we are not in transaction, MySQL will use an implicit one
+ // (i.e., autocommit mode), which suits us just fine.
+ //
+ connection_ptr cp;
+ if (!transaction::has_current ())
+ cp = factory_->connect ();
+
+ mysql::connection& c (
+ cp != 0
+ ? *cp
+ : transaction::current ().connection (const_cast<database&> (*this)));
+
+ try
+ {
+ select_statement st (c,
+ text.c_str (),
+ false, // Don't process.
+ false, // Don't optimize.
+ param,
+ result,
+ false);
+ st.execute ();
+ auto_result ar (st);
+
+ switch (st.fetch ())
+ {
+ case select_statement::success:
+ {
+ svi.migration = migration != 0;
+ assert (st.fetch () == select_statement::no_data);
+ break;
+ }
+ case select_statement::no_data:
+ {
+ svi.version = 0; // No schema.
+ break;
+ }
+ case select_statement::truncated:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+ catch (const database_exception& e)
+ {
+ // Detect the case where there is no version table.
+ //
+ if (e.error () == ER_NO_SUCH_TABLE)
+ svi.version = 0; // No schema.
+ else
+ throw;
+ }
+
+ return svi;
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/database.hxx b/libodb-mysql/odb/mysql/database.hxx
new file mode 100644
index 0000000..e73b7c9
--- /dev/null
+++ b/libodb-mysql/odb/mysql/database.hxx
@@ -0,0 +1,540 @@
+// file : odb/mysql/database.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_DATABASE_HXX
+#define ODB_MYSQL_DATABASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <iosfwd> // std::ostream
+
+#include <odb/database.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+#include <odb/details/unique-ptr.hxx>
+#include <odb/details/transfer-ptr.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/query.hxx>
+#include <odb/mysql/tracer.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/connection-factory.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class transaction_impl;
+
+ class LIBODB_MYSQL_EXPORT database: public odb::database
+ {
+ public:
+ // In MySQL, NULL and empty string are treated as the same value
+ // for all the arguments except passwd and socket.
+ //
+ 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,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ 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,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ 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,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ 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,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ 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,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Extract the database parameters from the command line. The
+ // following options are recognized:
+ //
+ // --user
+ // --password
+ // --database
+ // --host
+ // --port
+ // --socket
+ // --options-file
+ //
+ // For more information, see the output of the print_usage() function
+ // below. If erase is true, the above options are removed from the
+ // argv array and the argc count is updated accordingly. This
+ // constructor may throw the cli_exception exception.
+ //
+ database (int& argc,
+ char* argv[],
+ bool erase = false,
+ const std::string& charset = "",
+ unsigned long client_flags = 0,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Move-constructible but not move-assignable.
+ //
+ // Note: noexcept is not specified since odb::database(odb::database&&)
+ // can throw.
+ //
+#ifdef ODB_CXX11
+ database (database&&);
+#endif
+
+ static void
+ print_usage (std::ostream&);
+
+ public:
+ const char*
+ user () const
+ {
+ return user_.c_str ();
+ }
+
+ const char*
+ password () const
+ {
+ return passwd_;
+ }
+
+ const char*
+ db () const
+ {
+ return db_.c_str ();
+ }
+
+ const char*
+ host () const
+ {
+ return host_.c_str ();
+ }
+
+ unsigned int
+ port () const
+ {
+ return port_;
+ }
+
+ const char*
+ socket () const
+ {
+ return socket_;
+ }
+
+ const char*
+ charset () const
+ {
+ return charset_.c_str ();
+ }
+
+ unsigned long
+ client_flags () const
+ {
+ return client_flags_;
+ }
+
+ // Object persistence API.
+ //
+ public:
+
+ // Make the object persistent.
+ //
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Load an object. Throw object_not_persistent if not found.
+ //
+ 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);
+
+ // Load (or reload, if it is already loaded) a section of an object.
+ //
+ template <typename T>
+ void
+ load (T& object, section&);
+
+ // Reload an object.
+ //
+ template <typename T>
+ void
+ reload (T& object);
+
+ template <typename T>
+ void
+ reload (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ reload (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Loan an object if found. Return NULL/false if not found.
+ //
+ 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);
+
+ // Update the state of a modified objects.
+ //
+ template <typename T>
+ void
+ update (T& object);
+
+ template <typename T>
+ void
+ update (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ update (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Update a section of an object. Throws the section_not_loaded
+ // exception if the section is not loaded. Note also that this
+ // function does not clear the changed flag if it is set.
+ //
+ template <typename T>
+ void
+ update (const T& object, const section&);
+
+ // Make the object transient. Throw object_not_persistent if not
+ // found.
+ //
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::id_type& id);
+
+ template <typename T>
+ void
+ erase (T& object);
+
+ template <typename T>
+ void
+ erase (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Erase multiple objects matching a query predicate.
+ //
+ template <typename T>
+ unsigned long long
+ erase_query ();
+
+ template <typename T>
+ unsigned long long
+ erase_query (const char*);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const std::string&);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const mysql::query_base&);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const odb::query_base&);
+
+ // Query API.
+ //
+ template <typename T>
+ result<T>
+ query (bool cache = true);
+
+ template <typename T>
+ result<T>
+ query (const char*, bool cache = true);
+
+ template <typename T>
+ result<T>
+ query (const std::string&, bool cache = true);
+
+ template <typename T>
+ result<T>
+ query (const mysql::query_base&, bool cache = true);
+
+ template <typename T>
+ result<T>
+ query (const odb::query_base&, bool cache = true);
+
+ // Query one API.
+ //
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one ();
+
+ template <typename T>
+ bool
+ query_one (T& object);
+
+ template <typename T>
+ T
+ query_value ();
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const char*);
+
+ template <typename T>
+ bool
+ query_one (const char*, T& object);
+
+ template <typename T>
+ T
+ query_value (const char*);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const std::string&);
+
+ template <typename T>
+ bool
+ query_one (const std::string&, T& object);
+
+ template <typename T>
+ T
+ query_value (const std::string&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const mysql::query_base&);
+
+ template <typename T>
+ bool
+ query_one (const mysql::query_base&, T& object);
+
+ template <typename T>
+ T
+ query_value (const mysql::query_base&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const odb::query_base&);
+
+ template <typename T>
+ bool
+ query_one (const odb::query_base&, T& object);
+
+ template <typename T>
+ T
+ query_value (const odb::query_base&);
+
+ // Query preparation.
+ //
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const mysql::query_base&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const odb::query_base&);
+
+ // Transactions.
+ //
+ public:
+ virtual transaction_impl*
+ begin ();
+
+ public:
+ connection_ptr
+ connection ();
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef mysql::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::database::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::database::tracer (t);
+ }
+
+ using odb::database::tracer;
+
+ // Database schema version.
+ //
+ protected:
+ virtual const schema_version_info&
+ load_schema_version (const std::string& schema_name) const;
+
+ public:
+ // Database id constant (useful for meta-programming).
+ //
+ static const odb::database_id database_id = id_mysql;
+
+ public:
+ virtual
+ ~database ();
+
+ protected:
+ virtual odb::connection*
+ connection_ ();
+
+ private:
+ // Note: remember to update move ctor if adding any new members.
+ //
+ std::string user_;
+ std::string passwd_str_;
+ const char* passwd_;
+ std::string db_;
+ std::string host_;
+ unsigned int port_;
+ std::string socket_str_;
+ const char* socket_;
+ std::string charset_;
+ unsigned long client_flags_;
+ details::unique_ptr<connection_factory> factory_;
+ };
+ }
+}
+
+#include <odb/mysql/database.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_DATABASE_HXX
diff --git a/libodb-mysql/odb/mysql/database.ixx b/libodb-mysql/odb/mysql/database.ixx
new file mode 100644
index 0000000..f9b62dd
--- /dev/null
+++ b/libodb-mysql/odb/mysql/database.ixx
@@ -0,0 +1,617 @@
+// file : odb/mysql/database.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <utility> // move()
+
+#include <odb/mysql/transaction.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+#ifdef ODB_CXX11
+ inline database::
+ database (database&& db) // Has to be inline.
+ : odb::database (std::move (db)),
+ user_ (std::move (db.user_)),
+ passwd_str_ (std::move (db.passwd_str_)),
+ passwd_ (db.passwd_ != 0 ? passwd_str_.c_str () : 0),
+ db_ (std::move (db.db_)),
+ host_ (std::move (db.host_)),
+ port_ (db.port_),
+ socket_str_ (std::move (db.socket_str_)),
+ socket_ (db.socket_ != 0 ? socket_str_.c_str () : 0),
+ charset_ (std::move (db.charset_)),
+ client_flags_ (db.client_flags_),
+ factory_ (std::move (db.factory_))
+ {
+ factory_->database (*this); // New database instance.
+ }
+#endif
+
+ inline connection_ptr database::
+ connection ()
+ {
+ // Go through the virtual connection_() function instead of
+ // directly to allow overriding.
+ //
+ return connection_ptr (
+ static_cast<mysql::connection*> (connection_ ()));
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T& obj)
+ {
+ return persist_<T, id_mysql> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const T& obj)
+ {
+ return persist_<const T, id_mysql> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_mysql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_mysql> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_mysql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T>& p)
+ {
+ const P<T>& cr (p);
+ return persist<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ return persist<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const typename object_traits<T>::pointer_type& pobj)
+ {
+ return persist_<T, id_mysql> (pobj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ load (const typename object_traits<T>::id_type& id)
+ {
+ return load_<T, id_mysql> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ load (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return load_<T, id_mysql> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ load (T& obj, section& s)
+ {
+ return load_<T, id_mysql> (obj, s);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ find (const typename object_traits<T>::id_type& id)
+ {
+ return find_<T, id_mysql> (id);
+ }
+
+ template <typename T>
+ inline bool database::
+ find (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return find_<T, id_mysql> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T& obj)
+ {
+ reload_<T, id_mysql> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T* p)
+ {
+ reload<T> (*p);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (const P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (const P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T>
+ inline void database::
+ reload (const typename object_traits<T>::pointer_type& pobj)
+ {
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ reload (odb::pointer_traits<pointer_type>::get_ref (pobj));
+ }
+
+ template <typename T>
+ inline void database::
+ update (T& obj)
+ {
+ update_<T, id_mysql> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ update (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_mysql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_mysql> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_mysql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (P<T>& p)
+ {
+ const P<T>& cr (p);
+ update<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ update<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const typename object_traits<T>::pointer_type& pobj)
+ {
+ update_<T, id_mysql> (pobj);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const T& obj, const section& s)
+ {
+ update_<T, id_mysql> (obj, s);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::id_type& id)
+ {
+ return erase_<T, id_mysql> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T& obj)
+ {
+ return erase_<T, id_mysql> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_mysql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_mysql> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_mysql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (P<T>& p)
+ {
+ const P<T>& cr (p);
+ erase<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ erase<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::pointer_type& pobj)
+ {
+ erase_<T, id_mysql> (pobj);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query ()
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (mysql::query_base ());
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const char* q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const std::string& q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const mysql::query_base& q)
+ {
+ // T is always object_type.
+ //
+ return object_traits_impl<T, id_mysql>::erase_query (*this, q);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return erase_query<T> (mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (bool cache)
+ {
+ return query<T> (mysql::query_base (), cache);
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const char* q, bool cache)
+ {
+ return query<T> (mysql::query_base (q), cache);
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const std::string& q, bool cache)
+ {
+ return query<T> (mysql::query_base (q), cache);
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const mysql::query_base& q, bool cache)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ result<T> r (query_<T, id_mysql>::call (*this, q));
+
+ if (cache)
+ r.cache ();
+
+ return r;
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const odb::query_base& q, bool cache)
+ {
+ // Translate to native query.
+ //
+ return query<T> (mysql::query_base (q), cache);
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one ()
+ {
+ return query_one<T> (mysql::query_base ());
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (T& o)
+ {
+ return query_one<T> (mysql::query_base (), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value ()
+ {
+ return query_value<T> (mysql::query_base ());
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const char* q)
+ {
+ return query_one<T> (mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const char* q, T& o)
+ {
+ return query_one<T> (mysql::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const char* q)
+ {
+ return query_value<T> (mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const std::string& q)
+ {
+ return query_one<T> (mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const std::string& q, T& o)
+ {
+ return query_one<T> (mysql::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const std::string& q)
+ {
+ return query_value<T> (mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const mysql::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_one_<T, id_mysql> (q);
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const mysql::query_base& q, T& o)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_one_<T, id_mysql> (q, o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const mysql::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_value_<T, id_mysql> (q);
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query_one<T> (mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const odb::query_base& q, T& o)
+ {
+ // Translate to native query.
+ //
+ return query_one<T> (mysql::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query_value<T> (mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const mysql::query_base& q)
+ {
+ // Throws if not in transaction.
+ //
+ mysql::connection& c (transaction::current ().connection (*this));
+ return c.prepare_query<T> (n, q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return prepare_query<T> (n, mysql::query_base (q));
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/details/.gitignore b/libodb-mysql/odb/mysql/details/.gitignore
new file mode 100644
index 0000000..b298f89
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/.gitignore
@@ -0,0 +1 @@
+/options.?xx
diff --git a/libodb-mysql/odb/mysql/details/config-vc.h b/libodb-mysql/odb/mysql/details/config-vc.h
new file mode 100644
index 0000000..fb01864
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/config-vc.h
@@ -0,0 +1,12 @@
+/* file : odb/mysql/details/config-vc.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+#ifndef ODB_MYSQL_DETAILS_CONFIG_VC_H
+#define ODB_MYSQL_DETAILS_CONFIG_VC_H
+
+#if !defined(LIBODB_MYSQL_INCLUDE_SHORT) && !defined (LIBODB_MYSQL_INCLUDE_LONG)
+# define LIBODB_MYSQL_INCLUDE_SHORT 1
+#endif
+
+#endif /* ODB_MYSQL_DETAILS_CONFIG_VC_H */
diff --git a/libodb-mysql/odb/mysql/details/config.h.in b/libodb-mysql/odb/mysql/details/config.h.in
new file mode 100644
index 0000000..3b7869b
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/config.h.in
@@ -0,0 +1,17 @@
+/* file : odb/mysql/details/config.h.in
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+/* This file is automatically processed by configure. */
+
+#ifndef ODB_MYSQL_DETAILS_CONFIG_H
+#define ODB_MYSQL_DETAILS_CONFIG_H
+
+#undef LIBODB_MYSQL_STATIC_LIB
+
+#undef LIBODB_MYSQL_INCLUDE_SHORT
+#undef LIBODB_MYSQL_INCLUDE_LONG
+
+#undef LIBODB_MYSQL_THR_KEY_VISIBLE
+
+#endif /* ODB_MYSQL_DETAILS_CONFIG_H */
diff --git a/libodb-mysql/odb/mysql/details/config.hxx b/libodb-mysql/odb/mysql/details/config.hxx
new file mode 100644
index 0000000..f093e9d
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/config.hxx
@@ -0,0 +1,23 @@
+// file : odb/mysql/details/config.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_DETAILS_CONFIG_HXX
+#define ODB_MYSQL_DETAILS_CONFIG_HXX
+
+// no pre
+
+#ifdef ODB_COMPILER
+# error libodb-mysql header included in odb-compiled header
+#endif
+
+// @@ TMP: drop (along with LIBODB_MYSQL_THR_KEY_VISIBLE) after 2.5.0.
+//
+#ifdef LIBODB_MYSQL_INCLUDE_SHORT
+# error mysql headers must be included with mysql/ prefix
+#elif !defined(LIBODB_MYSQL_INCLUDE_LONG)
+# define LIBODB_MYSQL_INCLUDE_LONG 1
+#endif
+
+// no post
+
+#endif // ODB_MYSQL_DETAILS_CONFIG_HXX
diff --git a/libodb-mysql/odb/mysql/details/conversion.hxx b/libodb-mysql/odb/mysql/details/conversion.hxx
new file mode 100644
index 0000000..5d1f575
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/conversion.hxx
@@ -0,0 +1,58 @@
+// file : odb/mysql/details/conversion.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_DETAILS_CONVERSION_HXX
+#define ODB_MYSQL_DETAILS_CONVERSION_HXX
+
+#include <odb/mysql/traits.hxx>
+
+#include <odb/details/meta/answer.hxx>
+
+namespace odb
+{
+ // @@ Revise this.
+ //
+ namespace details {}
+
+ namespace mysql
+ {
+ namespace details
+ {
+ using namespace odb::details;
+
+ // Detect whether conversion is specified in type_traits.
+ //
+ template <typename T>
+ meta::yes
+ conversion_p_test (typename type_traits<T>::conversion*);
+
+ template <typename T>
+ meta::no
+ conversion_p_test (...);
+
+ template <typename T>
+ struct conversion_p
+ {
+ static const bool value =
+ sizeof (conversion_p_test<T> (0)) == sizeof (meta::yes);
+ };
+
+ template <typename T, bool = conversion_p<T>::value>
+ struct conversion;
+
+ template <typename T>
+ struct conversion<T, true>
+ {
+ static const char* to () {return type_traits<T>::conversion::to ();}
+ };
+
+ template <typename T>
+ struct conversion<T, false>
+ {
+ static const char* to () {return 0;}
+ };
+ }
+ }
+}
+
+#endif // ODB_MYSQL_DETAILS_CONVERSION_HXX
diff --git a/libodb-mysql/odb/mysql/details/export.hxx b/libodb-mysql/odb/mysql/details/export.hxx
new file mode 100644
index 0000000..817c7a4
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/export.hxx
@@ -0,0 +1,50 @@
+// file : odb/mysql/details/export.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_DETAILS_EXPORT_HXX
+#define ODB_MYSQL_DETAILS_EXPORT_HXX
+
+#include <odb/pre.hxx>
+
+// Note: do this check directly instead of including config.hxx.
+//
+#ifdef ODB_COMPILER
+# error libodb-mysql header included in odb-compiled header
+#endif
+
+// Normally we don't export class templates (but do complete specializations),
+// inline functions, and classes with only inline member functions. Exporting
+// classes that inherit from non-exported/imported bases (e.g., std::string)
+// will end up badly. The only known workarounds are to not inherit or to not
+// export. Also, MinGW GCC doesn't like seeing non-exported function being
+// used before their inline definition. The workaround is to reorder code. In
+// the end it's all trial and error.
+
+#if defined(LIBODB_MYSQL_STATIC) // Using static.
+# define LIBODB_MYSQL_EXPORT
+#elif defined(LIBODB_MYSQL_STATIC_BUILD) // Building static.
+# define LIBODB_MYSQL_EXPORT
+#elif defined(LIBODB_MYSQL_SHARED) // Using shared.
+# ifdef _WIN32
+# define LIBODB_MYSQL_EXPORT __declspec(dllimport)
+# else
+# define LIBODB_MYSQL_EXPORT
+# endif
+#elif defined(LIBODB_MYSQL_SHARED_BUILD) // Building shared.
+# ifdef _WIN32
+# define LIBODB_MYSQL_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_MYSQL_EXPORT
+# endif
+#else
+// If none of the above macros are defined, then we assume we are being used
+// by some third-party build system that cannot/doesn't signal the library
+// type. Note that this fallback works for both static and shared but in case
+// of shared will be sub-optimal compared to having dllimport.
+//
+# define LIBODB_MYSQL_EXPORT // Using static or shared.
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_DETAILS_EXPORT_HXX
diff --git a/libodb-mysql/odb/mysql/details/options.cli b/libodb-mysql/odb/mysql/details/options.cli
new file mode 100644
index 0000000..845c292
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/options.cli
@@ -0,0 +1,61 @@
+// file : odb/mysql/details/options.cli
+// license : GNU GPL v2; see accompanying LICENSE file
+
+include <string>;
+
+namespace odb
+{
+ namespace mysql
+ {
+ namespace details
+ {
+ class options
+ {
+ std::string --user
+ {
+ "<name>",
+ "MySQL database user."
+ };
+
+ std::string --password
+ {
+ "<str>",
+ "MySQL database password"
+ };
+
+ std::string --database
+ {
+ "<name>",
+ "MySQL database name."
+ };
+
+ std::string --host
+ {
+ "<addr>",
+ "MySQL database host name or address (localhost by default)."
+ };
+
+ unsigned int --port = 0
+ {
+ "<integer>",
+ "MySQL database port number."
+ };
+
+ std::string --socket
+ {
+ "<name>",
+ "MySQL database socket name."
+ };
+
+ std::string --options-file
+ {
+ "<file>",
+ "Read additional options from <file>. Each option should appear on a
+ separate line optionally followed by space or equal sign (\cb{=})
+ and an option value. Empty lines and lines starting with \cb{#} are
+ ignored."
+ };
+ };
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.cxx b/libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.cxx
new file mode 100644
index 0000000..1e15af6
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.cxx
@@ -0,0 +1,1125 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <odb/mysql/details/options.hxx>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <utility>
+#include <ostream>
+#include <sstream>
+#include <cstring>
+#include <fstream>
+
+namespace odb
+{
+ namespace mysql
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ // unknown_option
+ //
+ unknown_option::
+ ~unknown_option () throw ()
+ {
+ }
+
+ void unknown_option::
+ print (::std::ostream& os) const
+ {
+ os << "unknown option '" << option ().c_str () << "'";
+ }
+
+ const char* unknown_option::
+ what () const throw ()
+ {
+ return "unknown option";
+ }
+
+ // unknown_argument
+ //
+ unknown_argument::
+ ~unknown_argument () throw ()
+ {
+ }
+
+ void unknown_argument::
+ print (::std::ostream& os) const
+ {
+ os << "unknown argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unknown_argument::
+ what () const throw ()
+ {
+ return "unknown argument";
+ }
+
+ // missing_value
+ //
+ missing_value::
+ ~missing_value () throw ()
+ {
+ }
+
+ void missing_value::
+ print (::std::ostream& os) const
+ {
+ os << "missing value for option '" << option ().c_str () << "'";
+ }
+
+ const char* missing_value::
+ what () const throw ()
+ {
+ return "missing option value";
+ }
+
+ // invalid_value
+ //
+ invalid_value::
+ ~invalid_value () throw ()
+ {
+ }
+
+ void invalid_value::
+ print (::std::ostream& os) const
+ {
+ os << "invalid value '" << value ().c_str () << "' for option '"
+ << option ().c_str () << "'";
+
+ if (!message ().empty ())
+ os << ": " << message ().c_str ();
+ }
+
+ const char* invalid_value::
+ what () const throw ()
+ {
+ return "invalid option value";
+ }
+
+ // eos_reached
+ //
+ void eos_reached::
+ print (::std::ostream& os) const
+ {
+ os << what ();
+ }
+
+ const char* eos_reached::
+ what () const throw ()
+ {
+ return "end of argument stream reached";
+ }
+
+ // file_io_failure
+ //
+ file_io_failure::
+ ~file_io_failure () throw ()
+ {
+ }
+
+ void file_io_failure::
+ print (::std::ostream& os) const
+ {
+ os << "unable to open file '" << file ().c_str () << "' or read failure";
+ }
+
+ const char* file_io_failure::
+ what () const throw ()
+ {
+ return "unable to open file or read failure";
+ }
+
+ // unmatched_quote
+ //
+ unmatched_quote::
+ ~unmatched_quote () throw ()
+ {
+ }
+
+ void unmatched_quote::
+ print (::std::ostream& os) const
+ {
+ os << "unmatched quote in argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unmatched_quote::
+ what () const throw ()
+ {
+ return "unmatched quote";
+ }
+
+ // scanner
+ //
+ scanner::
+ ~scanner ()
+ {
+ }
+
+ // argv_scanner
+ //
+ bool argv_scanner::
+ more ()
+ {
+ return i_ < argc_;
+ }
+
+ const char* argv_scanner::
+ peek ()
+ {
+ if (i_ < argc_)
+ return argv_[i_];
+ else
+ throw eos_reached ();
+ }
+
+ const char* argv_scanner::
+ next ()
+ {
+ if (i_ < argc_)
+ {
+ const char* r (argv_[i_]);
+
+ if (erase_)
+ {
+ for (int i (i_ + 1); i < argc_; ++i)
+ argv_[i - 1] = argv_[i];
+
+ --argc_;
+ argv_[argc_] = 0;
+ }
+ else
+ ++i_;
+
+ ++start_position_;
+ return r;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ void argv_scanner::
+ skip ()
+ {
+ if (i_ < argc_)
+ {
+ ++i_;
+ ++start_position_;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ std::size_t argv_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ // argv_file_scanner
+ //
+ int argv_file_scanner::zero_argc_ = 0;
+ std::string argv_file_scanner::empty_string_;
+
+ bool argv_file_scanner::
+ more ()
+ {
+ if (!args_.empty ())
+ return true;
+
+ while (base::more ())
+ {
+ // See if the next argument is the file option.
+ //
+ const char* a (base::peek ());
+ const option_info* oi = 0;
+ const char* ov = 0;
+
+ if (!skip_)
+ {
+ if ((oi = find (a)) != 0)
+ {
+ base::next ();
+
+ if (!base::more ())
+ throw missing_value (a);
+
+ ov = base::next ();
+ }
+ else if (std::strncmp (a, "-", 1) == 0)
+ {
+ if ((ov = std::strchr (a, '=')) != 0)
+ {
+ std::string o (a, 0, ov - a);
+ if ((oi = find (o.c_str ())) != 0)
+ {
+ base::next ();
+ ++ov;
+ }
+ }
+ }
+ }
+
+ if (oi != 0)
+ {
+ if (oi->search_func != 0)
+ {
+ std::string f (oi->search_func (ov, oi->arg));
+
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ load (ov);
+
+ if (!args_.empty ())
+ return true;
+ }
+ else
+ {
+ if (!skip_)
+ skip_ = (std::strcmp (a, "--") == 0);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ const char* argv_file_scanner::
+ peek ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? base::peek () : args_.front ().value.c_str ();
+ }
+
+ const std::string& argv_file_scanner::
+ peek_file ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? empty_string_ : *args_.front ().file;
+ }
+
+ std::size_t argv_file_scanner::
+ peek_line ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? 0 : args_.front ().line;
+ }
+
+ const char* argv_file_scanner::
+ next ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::next ();
+ else
+ {
+ hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value);
+ args_.pop_front ();
+ ++start_position_;
+ return hold_[i_].c_str ();
+ }
+ }
+
+ void argv_file_scanner::
+ skip ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::skip ();
+ else
+ {
+ args_.pop_front ();
+ ++start_position_;
+ }
+ }
+
+ const argv_file_scanner::option_info* argv_file_scanner::
+ find (const char* a) const
+ {
+ for (std::size_t i (0); i < options_count_; ++i)
+ if (std::strcmp (a, options_[i].option) == 0)
+ return &options_[i];
+
+ return 0;
+ }
+
+ std::size_t argv_file_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ void argv_file_scanner::
+ load (const std::string& file)
+ {
+ using namespace std;
+
+ ifstream is (file.c_str ());
+
+ if (!is.is_open ())
+ throw file_io_failure (file);
+
+ files_.push_back (file);
+
+ arg a;
+ a.file = &*files_.rbegin ();
+
+ for (a.line = 1; !is.eof (); ++a.line)
+ {
+ string line;
+ getline (is, line);
+
+ if (is.fail () && !is.eof ())
+ throw file_io_failure (file);
+
+ string::size_type n (line.size ());
+
+ // Trim the line from leading and trailing whitespaces.
+ //
+ if (n != 0)
+ {
+ const char* f (line.c_str ());
+ const char* l (f + n);
+
+ const char* of (f);
+ while (f < l && (*f == ' ' || *f == '\t' || *f == '\r'))
+ ++f;
+
+ --l;
+
+ const char* ol (l);
+ while (l > f && (*l == ' ' || *l == '\t' || *l == '\r'))
+ --l;
+
+ if (f != of || l != ol)
+ line = f <= l ? string (f, l - f + 1) : string ();
+ }
+
+ // Ignore empty lines, those that start with #.
+ //
+ if (line.empty () || line[0] == '#')
+ continue;
+
+ string::size_type p (string::npos);
+ if (line.compare (0, 1, "-") == 0)
+ {
+ p = line.find (' ');
+
+ string::size_type q (line.find ('='));
+ if (q != string::npos && q < p)
+ p = q;
+ }
+
+ string s1;
+ if (p != string::npos)
+ {
+ s1.assign (line, 0, p);
+
+ // Skip leading whitespaces in the argument.
+ //
+ if (line[p] == '=')
+ ++p;
+ else
+ {
+ n = line.size ();
+ for (++p; p < n; ++p)
+ {
+ char c (line[p]);
+ if (c != ' ' && c != '\t' && c != '\r')
+ break;
+ }
+ }
+ }
+ else if (!skip_)
+ skip_ = (line == "--");
+
+ string s2 (line, p != string::npos ? p : 0);
+
+ // If the string (which is an option value or argument) is
+ // wrapped in quotes, remove them.
+ //
+ n = s2.size ();
+ char cf (s2[0]), cl (s2[n - 1]);
+
+ if (cf == '"' || cf == '\'' || cl == '"' || cl == '\'')
+ {
+ if (n == 1 || cf != cl)
+ throw unmatched_quote (s2);
+
+ s2 = string (s2, 1, n - 2);
+ }
+
+ if (!s1.empty ())
+ {
+ // See if this is another file option.
+ //
+ const option_info* oi;
+ if (!skip_ && (oi = find (s1.c_str ())))
+ {
+ if (s2.empty ())
+ throw missing_value (oi->option);
+
+ if (oi->search_func != 0)
+ {
+ string f (oi->search_func (s2.c_str (), oi->arg));
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ {
+ // If the path of the file being parsed is not simple and the
+ // path of the file that needs to be loaded is relative, then
+ // complete the latter using the former as a base.
+ //
+#ifndef _WIN32
+ string::size_type p (file.find_last_of ('/'));
+ bool c (p != string::npos && s2[0] != '/');
+#else
+ string::size_type p (file.find_last_of ("/\\"));
+ bool c (p != string::npos && s2[1] != ':');
+#endif
+ if (c)
+ s2.insert (0, file, 0, p + 1);
+
+ load (s2);
+ }
+
+ continue;
+ }
+
+ a.value = s1;
+ args_.push_back (a);
+ }
+
+ a.value = s2;
+ args_.push_back (a);
+ }
+ }
+
+ template <typename X>
+ struct parser
+ {
+ static void
+ parse (X& x, bool& xs, scanner& s)
+ {
+ using namespace std;
+
+ const char* o (s.next ());
+ if (s.more ())
+ {
+ string v (s.next ());
+ istringstream is (v);
+ if (!(is >> x && is.peek () == istringstream::traits_type::eof ()))
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<bool>
+ {
+ static void
+ parse (bool& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ const char* v (s.next ());
+
+ if (std::strcmp (v, "1") == 0 ||
+ std::strcmp (v, "true") == 0 ||
+ std::strcmp (v, "TRUE") == 0 ||
+ std::strcmp (v, "True") == 0)
+ x = true;
+ else if (std::strcmp (v, "0") == 0 ||
+ std::strcmp (v, "false") == 0 ||
+ std::strcmp (v, "FALSE") == 0 ||
+ std::strcmp (v, "False") == 0)
+ x = false;
+ else
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<std::string>
+ {
+ static void
+ parse (std::string& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ x = s.next ();
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X>
+ struct parser<std::pair<X, std::size_t> >
+ {
+ static void
+ parse (std::pair<X, std::size_t>& x, bool& xs, scanner& s)
+ {
+ x.second = s.position ();
+ parser<X>::parse (x.first, xs, s);
+ }
+ };
+
+ template <typename X>
+ struct parser<std::vector<X> >
+ {
+ static void
+ parse (std::vector<X>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.push_back (x);
+ xs = true;
+ }
+ };
+
+ template <typename X, typename C>
+ struct parser<std::set<X, C> >
+ {
+ static void
+ parse (std::set<X, C>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.insert (x);
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::map<K, V, C> >
+ {
+ static void
+ parse (std::map<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m[k] = v;
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::multimap<K, V, C> >
+ {
+ static void
+ parse (std::multimap<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m.insert (typename std::multimap<K, V, C>::value_type (k, v));
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X, typename T, T X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, s);
+ }
+
+ template <typename X, bool X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ s.next ();
+ x.*M = true;
+ }
+
+ template <typename X, typename T, T X::*M, bool X::*S>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, x.*S, s);
+ }
+ }
+ }
+ }
+}
+
+#include <map>
+
+namespace odb
+{
+ namespace mysql
+ {
+ namespace details
+ {
+ // options
+ //
+
+ options::
+ options ()
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (0),
+ port_specified_ (false),
+ socket_ (),
+ socket_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ }
+
+ options::
+ options (int& argc,
+ char** argv,
+ bool erase,
+ ::odb::mysql::details::cli::unknown_mode opt,
+ ::odb::mysql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (0),
+ port_specified_ (false),
+ socket_ (),
+ socket_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::mysql::details::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ options::
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ ::odb::mysql::details::cli::unknown_mode opt,
+ ::odb::mysql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (0),
+ port_specified_ (false),
+ socket_ (),
+ socket_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::mysql::details::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ options::
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::odb::mysql::details::cli::unknown_mode opt,
+ ::odb::mysql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (0),
+ port_specified_ (false),
+ socket_ (),
+ socket_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::mysql::details::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ options::
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::odb::mysql::details::cli::unknown_mode opt,
+ ::odb::mysql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (0),
+ port_specified_ (false),
+ socket_ (),
+ socket_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::mysql::details::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ options::
+ options (::odb::mysql::details::cli::scanner& s,
+ ::odb::mysql::details::cli::unknown_mode opt,
+ ::odb::mysql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (0),
+ port_specified_ (false),
+ socket_ (),
+ socket_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ _parse (s, opt, arg);
+ }
+
+ ::odb::mysql::details::cli::usage_para options::
+ print_usage (::std::ostream& os, ::odb::mysql::details::cli::usage_para p)
+ {
+ CLI_POTENTIALLY_UNUSED (os);
+
+ if (p != ::odb::mysql::details::cli::usage_para::none)
+ os << ::std::endl;
+
+ os << "--user <name> MySQL database user." << ::std::endl;
+
+ os << std::endl
+ << "--password <str> MySQL database password" << ::std::endl;
+
+ os << std::endl
+ << "--database <name> MySQL database name." << ::std::endl;
+
+ os << std::endl
+ << "--host <addr> MySQL database host name or address (localhost by" << ::std::endl
+ << " default)." << ::std::endl;
+
+ os << std::endl
+ << "--port <integer> MySQL database port number." << ::std::endl;
+
+ os << std::endl
+ << "--socket <name> MySQL database socket name." << ::std::endl;
+
+ os << std::endl
+ << "--options-file <file> Read additional options from <file>. Each option should" << ::std::endl
+ << " appear on a separate line optionally followed by space or" << ::std::endl
+ << " equal sign (=) and an option value. Empty lines and lines" << ::std::endl
+ << " starting with # are ignored." << ::std::endl;
+
+ p = ::odb::mysql::details::cli::usage_para::option;
+
+ return p;
+ }
+
+ typedef
+ std::map<std::string, void (*) (options&, ::odb::mysql::details::cli::scanner&)>
+ _cli_options_map;
+
+ static _cli_options_map _cli_options_map_;
+
+ struct _cli_options_map_init
+ {
+ _cli_options_map_init ()
+ {
+ _cli_options_map_["--user"] =
+ &::odb::mysql::details::cli::thunk< options, std::string, &options::user_,
+ &options::user_specified_ >;
+ _cli_options_map_["--password"] =
+ &::odb::mysql::details::cli::thunk< options, std::string, &options::password_,
+ &options::password_specified_ >;
+ _cli_options_map_["--database"] =
+ &::odb::mysql::details::cli::thunk< options, std::string, &options::database_,
+ &options::database_specified_ >;
+ _cli_options_map_["--host"] =
+ &::odb::mysql::details::cli::thunk< options, std::string, &options::host_,
+ &options::host_specified_ >;
+ _cli_options_map_["--port"] =
+ &::odb::mysql::details::cli::thunk< options, unsigned int, &options::port_,
+ &options::port_specified_ >;
+ _cli_options_map_["--socket"] =
+ &::odb::mysql::details::cli::thunk< options, std::string, &options::socket_,
+ &options::socket_specified_ >;
+ _cli_options_map_["--options-file"] =
+ &::odb::mysql::details::cli::thunk< options, std::string, &options::options_file_,
+ &options::options_file_specified_ >;
+ }
+ };
+
+ static _cli_options_map_init _cli_options_map_init_;
+
+ bool options::
+ _parse (const char* o, ::odb::mysql::details::cli::scanner& s)
+ {
+ _cli_options_map::const_iterator i (_cli_options_map_.find (o));
+
+ if (i != _cli_options_map_.end ())
+ {
+ (*(i->second)) (*this, s);
+ return true;
+ }
+
+ return false;
+ }
+
+ bool options::
+ _parse (::odb::mysql::details::cli::scanner& s,
+ ::odb::mysql::details::cli::unknown_mode opt_mode,
+ ::odb::mysql::details::cli::unknown_mode arg_mode)
+ {
+ bool r = false;
+ bool opt = true;
+
+ while (s.more ())
+ {
+ const char* o = s.peek ();
+
+ if (std::strcmp (o, "--") == 0)
+ {
+ opt = false;
+ s.skip ();
+ r = true;
+ continue;
+ }
+
+ if (opt)
+ {
+ if (_parse (o, s))
+ {
+ r = true;
+ continue;
+ }
+
+ if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0')
+ {
+ // Handle combined option values.
+ //
+ std::string co;
+ if (const char* v = std::strchr (o, '='))
+ {
+ co.assign (o, 0, v - o);
+ ++v;
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (co.c_str ()),
+ const_cast<char*> (v)
+ };
+
+ ::odb::mysql::details::cli::argv_scanner ns (0, ac, av);
+
+ if (_parse (co.c_str (), ns))
+ {
+ // Parsed the option but not its value?
+ //
+ if (ns.end () != 2)
+ throw ::odb::mysql::details::cli::invalid_value (co, v);
+
+ s.next ();
+ r = true;
+ continue;
+ }
+ else
+ {
+ // Set the unknown option and fall through.
+ //
+ o = co.c_str ();
+ }
+ }
+
+ switch (opt_mode)
+ {
+ case ::odb::mysql::details::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::odb::mysql::details::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::odb::mysql::details::cli::unknown_mode::fail:
+ {
+ throw ::odb::mysql::details::cli::unknown_option (o);
+ }
+ }
+
+ break;
+ }
+ }
+
+ switch (arg_mode)
+ {
+ case ::odb::mysql::details::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::odb::mysql::details::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::odb::mysql::details::cli::unknown_mode::fail:
+ {
+ throw ::odb::mysql::details::cli::unknown_argument (o);
+ }
+ }
+
+ break;
+ }
+
+ return r;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
diff --git a/libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.hxx b/libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.hxx
new file mode 100644
index 0000000..daa7d52
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.hxx
@@ -0,0 +1,570 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+#ifndef LIBODB_MYSQL_DETAILS_OPTIONS_HXX
+#define LIBODB_MYSQL_DETAILS_OPTIONS_HXX
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <list>
+#include <deque>
+#include <iosfwd>
+#include <string>
+#include <cstddef>
+#include <exception>
+
+#ifndef CLI_POTENTIALLY_UNUSED
+# if defined(_MSC_VER) || defined(__xlC__)
+# define CLI_POTENTIALLY_UNUSED(x) (void*)&x
+# else
+# define CLI_POTENTIALLY_UNUSED(x) (void)x
+# endif
+#endif
+
+namespace odb
+{
+ namespace mysql
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ class usage_para
+ {
+ public:
+ enum value
+ {
+ none,
+ text,
+ option
+ };
+
+ usage_para (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ class unknown_mode
+ {
+ public:
+ enum value
+ {
+ skip,
+ stop,
+ fail
+ };
+
+ unknown_mode (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ // Exceptions.
+ //
+
+ class exception: public std::exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const = 0;
+ };
+
+ ::std::ostream&
+ operator<< (::std::ostream&, const exception&);
+
+ class unknown_option: public exception
+ {
+ public:
+ virtual
+ ~unknown_option () throw ();
+
+ unknown_option (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class unknown_argument: public exception
+ {
+ public:
+ virtual
+ ~unknown_argument () throw ();
+
+ unknown_argument (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ class missing_value: public exception
+ {
+ public:
+ virtual
+ ~missing_value () throw ();
+
+ missing_value (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class invalid_value: public exception
+ {
+ public:
+ virtual
+ ~invalid_value () throw ();
+
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message = std::string ());
+
+ const std::string&
+ option () const;
+
+ const std::string&
+ value () const;
+
+ const std::string&
+ message () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ std::string value_;
+ std::string message_;
+ };
+
+ class eos_reached: public exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class file_io_failure: public exception
+ {
+ public:
+ virtual
+ ~file_io_failure () throw ();
+
+ file_io_failure (const std::string& file);
+
+ const std::string&
+ file () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string file_;
+ };
+
+ class unmatched_quote: public exception
+ {
+ public:
+ virtual
+ ~unmatched_quote () throw ();
+
+ unmatched_quote (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ // Command line argument scanner interface.
+ //
+ // The values returned by next() are guaranteed to be valid
+ // for the two previous arguments up until a call to a third
+ // peek() or next().
+ //
+ // The position() function returns a monotonically-increasing
+ // number which, if stored, can later be used to determine the
+ // relative position of the argument returned by the following
+ // call to next(). Note that if multiple scanners are used to
+ // extract arguments from multiple sources, then the end
+ // position of the previous scanner should be used as the
+ // start position of the next.
+ //
+ class scanner
+ {
+ public:
+ virtual
+ ~scanner ();
+
+ virtual bool
+ more () = 0;
+
+ virtual const char*
+ peek () = 0;
+
+ virtual const char*
+ next () = 0;
+
+ virtual void
+ skip () = 0;
+
+ virtual std::size_t
+ position () = 0;
+ };
+
+ class argv_scanner: public scanner
+ {
+ public:
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ int
+ end () const;
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ protected:
+ std::size_t start_position_;
+ int i_;
+ int& argc_;
+ char** argv_;
+ bool erase_;
+ };
+
+ class argv_file_scanner: public argv_scanner
+ {
+ public:
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t start_position = 0);
+
+ struct option_info
+ {
+ // If search_func is not NULL, it is called, with the arg
+ // value as the second argument, to locate the options file.
+ // If it returns an empty string, then the file is ignored.
+ //
+ const char* option;
+ std::string (*search_func) (const char*, void* arg);
+ void* arg;
+ };
+
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const option_info* options = 0,
+ std::size_t options_count = 0,
+ std::size_t start_position = 0);
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ // Return the file path if the peeked at argument came from a file and
+ // the empty string otherwise. The reference is guaranteed to be valid
+ // till the end of the scanner lifetime.
+ //
+ const std::string&
+ peek_file ();
+
+ // Return the 1-based line number if the peeked at argument came from
+ // a file and zero otherwise.
+ //
+ std::size_t
+ peek_line ();
+
+ private:
+ const option_info*
+ find (const char*) const;
+
+ void
+ load (const std::string& file);
+
+ typedef argv_scanner base;
+
+ const std::string option_;
+ option_info option_info_;
+ const option_info* options_;
+ std::size_t options_count_;
+
+ struct arg
+ {
+ std::string value;
+ const std::string* file;
+ std::size_t line;
+ };
+
+ std::deque<arg> args_;
+ std::list<std::string> files_;
+
+ // Circular buffer of two arguments.
+ //
+ std::string hold_[2];
+ std::size_t i_;
+
+ bool skip_;
+
+ static int zero_argc_;
+ static std::string empty_string_;
+ };
+
+ template <typename X>
+ struct parser;
+ }
+ }
+ }
+}
+
+#include <string>
+
+namespace odb
+{
+ namespace mysql
+ {
+ namespace details
+ {
+ class options
+ {
+ public:
+ options ();
+
+ options (int& argc,
+ char** argv,
+ bool erase = false,
+ ::odb::mysql::details::cli::unknown_mode option = ::odb::mysql::details::cli::unknown_mode::fail,
+ ::odb::mysql::details::cli::unknown_mode argument = ::odb::mysql::details::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ ::odb::mysql::details::cli::unknown_mode option = ::odb::mysql::details::cli::unknown_mode::fail,
+ ::odb::mysql::details::cli::unknown_mode argument = ::odb::mysql::details::cli::unknown_mode::stop);
+
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::odb::mysql::details::cli::unknown_mode option = ::odb::mysql::details::cli::unknown_mode::fail,
+ ::odb::mysql::details::cli::unknown_mode argument = ::odb::mysql::details::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::odb::mysql::details::cli::unknown_mode option = ::odb::mysql::details::cli::unknown_mode::fail,
+ ::odb::mysql::details::cli::unknown_mode argument = ::odb::mysql::details::cli::unknown_mode::stop);
+
+ options (::odb::mysql::details::cli::scanner&,
+ ::odb::mysql::details::cli::unknown_mode option = ::odb::mysql::details::cli::unknown_mode::fail,
+ ::odb::mysql::details::cli::unknown_mode argument = ::odb::mysql::details::cli::unknown_mode::stop);
+
+ // Option accessors.
+ //
+ const std::string&
+ user () const;
+
+ bool
+ user_specified () const;
+
+ const std::string&
+ password () const;
+
+ bool
+ password_specified () const;
+
+ const std::string&
+ database () const;
+
+ bool
+ database_specified () const;
+
+ const std::string&
+ host () const;
+
+ bool
+ host_specified () const;
+
+ const unsigned int&
+ port () const;
+
+ bool
+ port_specified () const;
+
+ const std::string&
+ socket () const;
+
+ bool
+ socket_specified () const;
+
+ const std::string&
+ options_file () const;
+
+ bool
+ options_file_specified () const;
+
+ // Print usage information.
+ //
+ static ::odb::mysql::details::cli::usage_para
+ print_usage (::std::ostream&,
+ ::odb::mysql::details::cli::usage_para = ::odb::mysql::details::cli::usage_para::none);
+
+ // Implementation details.
+ //
+ protected:
+ bool
+ _parse (const char*, ::odb::mysql::details::cli::scanner&);
+
+ private:
+ bool
+ _parse (::odb::mysql::details::cli::scanner&,
+ ::odb::mysql::details::cli::unknown_mode option,
+ ::odb::mysql::details::cli::unknown_mode argument);
+
+ public:
+ std::string user_;
+ bool user_specified_;
+ std::string password_;
+ bool password_specified_;
+ std::string database_;
+ bool database_specified_;
+ std::string host_;
+ bool host_specified_;
+ unsigned int port_;
+ bool port_specified_;
+ std::string socket_;
+ bool socket_specified_;
+ std::string options_file_;
+ bool options_file_specified_;
+ };
+ }
+ }
+}
+
+#include <odb/mysql/details/options.ixx>
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
+#endif // LIBODB_MYSQL_DETAILS_OPTIONS_HXX
diff --git a/libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.ixx b/libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.ixx
new file mode 100644
index 0000000..66bc5df
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.ixx
@@ -0,0 +1,384 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <cassert>
+
+namespace odb
+{
+ namespace mysql
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ // usage_para
+ //
+ inline usage_para::
+ usage_para (value v)
+ : v_ (v)
+ {
+ }
+
+ // unknown_mode
+ //
+ inline unknown_mode::
+ unknown_mode (value v)
+ : v_ (v)
+ {
+ }
+
+ // exception
+ //
+ inline ::std::ostream&
+ operator<< (::std::ostream& os, const exception& e)
+ {
+ e.print (os);
+ return os;
+ }
+
+ // unknown_option
+ //
+ inline unknown_option::
+ unknown_option (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& unknown_option::
+ option () const
+ {
+ return option_;
+ }
+
+ // unknown_argument
+ //
+ inline unknown_argument::
+ unknown_argument (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unknown_argument::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // missing_value
+ //
+ inline missing_value::
+ missing_value (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& missing_value::
+ option () const
+ {
+ return option_;
+ }
+
+ // invalid_value
+ //
+ inline invalid_value::
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message)
+ : option_ (option),
+ value_ (value),
+ message_ (message)
+ {
+ }
+
+ inline const std::string& invalid_value::
+ option () const
+ {
+ return option_;
+ }
+
+ inline const std::string& invalid_value::
+ value () const
+ {
+ return value_;
+ }
+
+ inline const std::string& invalid_value::
+ message () const
+ {
+ return message_;
+ }
+
+ // file_io_failure
+ //
+ inline file_io_failure::
+ file_io_failure (const std::string& file)
+ : file_ (file)
+ {
+ }
+
+ inline const std::string& file_io_failure::
+ file () const
+ {
+ return file_;
+ }
+
+ // unmatched_quote
+ //
+ inline unmatched_quote::
+ unmatched_quote (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unmatched_quote::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // argv_scanner
+ //
+ inline argv_scanner::
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + 1),
+ i_ (1),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline argv_scanner::
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + static_cast<std::size_t> (start)),
+ i_ (start),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline int argv_scanner::
+ end () const
+ {
+ return i_;
+ }
+
+ // argv_file_scanner
+ //
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+
+ load (file);
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const option_info* options,
+ std::size_t options_count,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ load (file);
+ }
+ }
+ }
+ }
+}
+
+namespace odb
+{
+ namespace mysql
+ {
+ namespace details
+ {
+ // options
+ //
+
+ inline const std::string& options::
+ user () const
+ {
+ return this->user_;
+ }
+
+ inline bool options::
+ user_specified () const
+ {
+ return this->user_specified_;
+ }
+
+ inline const std::string& options::
+ password () const
+ {
+ return this->password_;
+ }
+
+ inline bool options::
+ password_specified () const
+ {
+ return this->password_specified_;
+ }
+
+ inline const std::string& options::
+ database () const
+ {
+ return this->database_;
+ }
+
+ inline bool options::
+ database_specified () const
+ {
+ return this->database_specified_;
+ }
+
+ inline const std::string& options::
+ host () const
+ {
+ return this->host_;
+ }
+
+ inline bool options::
+ host_specified () const
+ {
+ return this->host_specified_;
+ }
+
+ inline const unsigned int& options::
+ port () const
+ {
+ return this->port_;
+ }
+
+ inline bool options::
+ port_specified () const
+ {
+ return this->port_specified_;
+ }
+
+ inline const std::string& options::
+ socket () const
+ {
+ return this->socket_;
+ }
+
+ inline bool options::
+ socket_specified () const
+ {
+ return this->socket_specified_;
+ }
+
+ inline const std::string& options::
+ options_file () const
+ {
+ return this->options_file_;
+ }
+
+ inline bool options::
+ options_file_specified () const
+ {
+ return this->options_file_specified_;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
diff --git a/libodb-mysql/odb/mysql/enum.cxx b/libodb-mysql/odb/mysql/enum.cxx
new file mode 100644
index 0000000..1e53b29
--- /dev/null
+++ b/libodb-mysql/odb/mysql/enum.cxx
@@ -0,0 +1,28 @@
+// file : odb/mysql/enums.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memmove
+#include <cassert>
+
+#include <odb/mysql/enum.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ void enum_traits::
+ strip_value (const details::buffer& i, unsigned long& size)
+ {
+ char* d (const_cast<char*> (i.data ()));
+
+ unsigned long p (0);
+ for (; p < size && d[p] != ' '; ++p) ;
+ assert (p != size);
+
+ p++; // Skip space;
+ size -= p;
+
+ std::memmove (d, d + p, size);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/enum.hxx b/libodb-mysql/odb/mysql/enum.hxx
new file mode 100644
index 0000000..da0436d
--- /dev/null
+++ b/libodb-mysql/odb/mysql/enum.hxx
@@ -0,0 +1,129 @@
+// file : odb/mysql/enums.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_ENUMS_HXX
+#define ODB_MYSQL_ENUMS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memmove
+#include <cassert>
+
+#include <odb/details/buffer.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/traits.hxx>
+#include <odb/mysql/version.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ // Common interface for working with the dual enum image (integer or
+ // string). Used by the generated code and query machinery.
+ //
+ // We use overload resolution instead of template specialization to
+ // avoid having to specify the image type explicitly.
+ //
+ struct LIBODB_MYSQL_EXPORT enum_traits
+ {
+ //
+ // Integer image.
+ //
+ static void
+ bind (MYSQL_BIND& b, unsigned short& i, unsigned long&, my_bool* is_null)
+ {
+ b.buffer_type = MYSQL_TYPE_SHORT;
+ b.is_unsigned = 1;
+ b.buffer = &i;
+ b.is_null = is_null;
+ }
+
+ static bool
+ grow (unsigned short&, unsigned long&)
+ {
+ return false;
+ }
+
+ template <typename T>
+ static bool
+ set_image (unsigned short& i, unsigned long&, bool& is_null, const T& v)
+ {
+ value_traits<T, id_enum>::set_image (i, is_null, v);
+ return false;
+ }
+
+ template <typename T>
+ static void
+ set_value (T& v, unsigned short i, unsigned long, bool is_null)
+ {
+ value_traits<T, id_enum>::set_value (v, i, is_null);
+ }
+
+ //
+ // String image.
+ //
+
+ static void
+ bind (MYSQL_BIND& b,
+ details::buffer& i,
+ unsigned long& size,
+ my_bool* is_null)
+ {
+ b.buffer_type = MYSQL_TYPE_STRING;
+ b.buffer = i.data ();
+ b.buffer_length = static_cast<unsigned long> (i.capacity ());
+ b.length = &size;
+ b.is_null = is_null;
+ }
+
+ static bool
+ grow (details::buffer& i, unsigned long& size)
+ {
+ i.capacity (size);
+ return true;
+ }
+
+ template <typename T>
+ static bool
+ set_image (details::buffer& i,
+ unsigned long& size,
+ bool& is_null,
+ const T& v)
+ {
+ std::size_t n (0), c (i.capacity ());
+ value_traits<T, id_enum>::set_image (i, n, is_null, v);
+ size = static_cast<unsigned long> (n);
+ return c != i.capacity ();
+ }
+
+ template <typename T>
+ static void
+ set_value (T& v,
+ const details::buffer& i,
+ unsigned long size,
+ bool is_null)
+ {
+ // The buffer has the following content: "<num> <str>". Get rid of
+ // the numeric value so that we have "<str>". For more information
+ // why it is done this way, see mysql::object_columns class in the
+ // ODB compiler.
+ //
+ strip_value (i, size);
+
+ value_traits<T, id_enum>::set_value (v, i, size, is_null);
+ }
+
+ private:
+ static void
+ strip_value (const details::buffer& i, unsigned long& size);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_ENUMS_HXX
diff --git a/libodb-mysql/odb/mysql/error.cxx b/libodb-mysql/odb/mysql/error.cxx
new file mode 100644
index 0000000..26380ec
--- /dev/null
+++ b/libodb-mysql/odb/mysql/error.cxx
@@ -0,0 +1,76 @@
+// file : odb/mysql/error.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <new> // std::bad_alloc
+#include <string>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/exceptions.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mysql
+ {
+ static void
+ translate_error (connection& c,
+ unsigned int e,
+ const string& sqlstate,
+ string msg)
+ {
+ switch (e)
+ {
+ case CR_OUT_OF_MEMORY:
+ {
+ throw bad_alloc ();
+ }
+ case ER_LOCK_DEADLOCK:
+ {
+ throw deadlock ();
+ }
+ case CR_SERVER_LOST:
+ case CR_SERVER_GONE_ERROR:
+ {
+ c.mark_failed ();
+ throw connection_lost ();
+ }
+ case CR_UNKNOWN_ERROR:
+ {
+ c.mark_failed ();
+ }
+ // Fall through.
+ default:
+ {
+ // Get rid of a trailing newline if there is one.
+ //
+ string::size_type n (msg.size ());
+ if (n != 0 && msg[n - 1] == '\n')
+ msg.resize (n - 1);
+
+ throw database_exception (e, sqlstate, msg);
+ }
+ }
+ }
+
+ void
+ translate_error (connection& c)
+ {
+ MYSQL* h (c.handle ());
+ translate_error (c,
+ mysql_errno (h),
+ mysql_sqlstate (h),
+ mysql_error (h));
+ }
+
+ void
+ translate_error (connection& c, MYSQL_STMT* h)
+ {
+ translate_error (c,
+ mysql_stmt_errno (h),
+ mysql_stmt_sqlstate (h),
+ mysql_stmt_error (h));
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/error.hxx b/libodb-mysql/odb/mysql/error.hxx
new file mode 100644
index 0000000..0893841
--- /dev/null
+++ b/libodb-mysql/odb/mysql/error.hxx
@@ -0,0 +1,34 @@
+// file : odb/mysql/error.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_ERROR_HXX
+#define ODB_MYSQL_ERROR_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/version.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class connection;
+
+ // Translate MySQL error and throw an appropriate exception. Also,
+ // if the error code indicates that the connection is no longer
+ // usable, mark it as failed.
+ //
+ LIBODB_MYSQL_EXPORT void
+ translate_error (connection&);
+
+ LIBODB_MYSQL_EXPORT void
+ translate_error (connection&, MYSQL_STMT*);
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_ERROR_HXX
diff --git a/libodb-mysql/odb/mysql/exceptions.cxx b/libodb-mysql/odb/mysql/exceptions.cxx
new file mode 100644
index 0000000..b15d316
--- /dev/null
+++ b/libodb-mysql/odb/mysql/exceptions.cxx
@@ -0,0 +1,71 @@
+// file : odb/mysql/exceptions.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/mysql/exceptions.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mysql
+ {
+ //
+ // database_exception
+ //
+
+ database_exception::
+ ~database_exception () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ database_exception::
+ database_exception (unsigned int e, const string& s, const string& m)
+ : error_ (e), sqlstate_ (s), message_ (m)
+ {
+ ostringstream ostr;
+ ostr << error_ << " (" << sqlstate_ << "): " << message_;
+ what_ = ostr.str ();
+ }
+
+ const char* database_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ database_exception* database_exception::
+ clone () const
+ {
+ return new database_exception (*this);
+ }
+
+ //
+ // cli_exception
+ //
+
+ cli_exception::
+ cli_exception (const std::string& what)
+ : what_ (what)
+ {
+ }
+
+ cli_exception::
+ ~cli_exception () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ const char* cli_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ cli_exception* cli_exception::
+ clone () const
+ {
+ return new cli_exception (*this);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/exceptions.hxx b/libodb-mysql/odb/mysql/exceptions.hxx
new file mode 100644
index 0000000..5e9f9f1
--- /dev/null
+++ b/libodb-mysql/odb/mysql/exceptions.hxx
@@ -0,0 +1,87 @@
+// file : odb/mysql/exceptions.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_EXCEPTIONS_HXX
+#define ODB_MYSQL_EXCEPTIONS_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+
+#include <odb/exceptions.hxx>
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ struct LIBODB_MYSQL_EXPORT database_exception: odb::database_exception
+ {
+ database_exception (unsigned int,
+ const std::string& sqlstate,
+ const std::string& message);
+
+ ~database_exception () ODB_NOTHROW_NOEXCEPT;
+
+ unsigned int
+ error () const
+ {
+ return error_;
+ }
+
+ const std::string&
+ sqlstate () const
+ {
+ return sqlstate_;
+ }
+
+ const std::string&
+ message () const
+ {
+ return message_;
+ }
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual database_exception*
+ clone () const;
+
+ private:
+ unsigned int error_;
+ std::string sqlstate_;
+ std::string message_;
+ std::string what_;
+ };
+
+ struct LIBODB_MYSQL_EXPORT cli_exception: odb::exception
+ {
+ cli_exception (const std::string& what);
+ ~cli_exception () ODB_NOTHROW_NOEXCEPT;
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual cli_exception*
+ clone () const;
+
+ private:
+ std::string what_;
+ };
+
+ namespace core
+ {
+ using mysql::database_exception;
+ using mysql::cli_exception;
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_EXCEPTIONS_HXX
diff --git a/libodb-mysql/odb/mysql/forward.hxx b/libodb-mysql/odb/mysql/forward.hxx
new file mode 100644
index 0000000..243c94e
--- /dev/null
+++ b/libodb-mysql/odb/mysql/forward.hxx
@@ -0,0 +1,91 @@
+// file : odb/mysql/forward.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_FORWARD_HXX
+#define ODB_MYSQL_FORWARD_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ namespace core
+ {
+ using namespace odb::common;
+ }
+
+ //
+ //
+ class database;
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+ class connection_factory;
+ class statement;
+ class transaction;
+ class tracer;
+
+ namespace core
+ {
+ using mysql::database;
+ using mysql::connection;
+ using mysql::connection_ptr;
+ using mysql::transaction;
+ using mysql::statement;
+ }
+
+ // Implementation details.
+ //
+ enum statement_kind
+ {
+ statement_select,
+ statement_insert,
+ statement_update,
+ statement_delete
+ };
+
+ class binding;
+ class select_statement;
+
+ template <typename T>
+ class object_statements;
+
+ template <typename T>
+ class polymorphic_root_object_statements;
+
+ template <typename T>
+ class polymorphic_derived_object_statements;
+
+ template <typename T>
+ class no_id_object_statements;
+
+ template <typename T>
+ class view_statements;
+
+ template <typename T>
+ class container_statements;
+
+ template <typename T>
+ class smart_container_statements;
+
+ template <typename T, typename ST>
+ class section_statements;
+
+ class query_base;
+ }
+
+ namespace details
+ {
+ template <>
+ struct counter_type<mysql::connection>
+ {
+ typedef shared_base counter;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_FORWARD_HXX
diff --git a/libodb-mysql/odb/mysql/mysql-types.hxx b/libodb-mysql/odb/mysql/mysql-types.hxx
new file mode 100644
index 0000000..af7135b
--- /dev/null
+++ b/libodb-mysql/odb/mysql/mysql-types.hxx
@@ -0,0 +1,42 @@
+// file : odb/mysql/mysql-types.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_MYSQL_TYPES_HXX
+#define ODB_MYSQL_MYSQL_TYPES_HXX
+
+#include <odb/mysql/details/config.hxx>
+
+#include <odb/mysql/version.hxx>
+
+// Starting with 8.0.1 instead of my_bool MySQL uses bool directly. We keep
+// using the alias for compatibility with previous versions.
+//
+#if !defined(LIBODB_MYSQL_MARIADB) && MYSQL_VERSION_ID >= 80001
+typedef bool my_bool;
+#else
+typedef char my_bool;
+#endif
+
+// Starting with 8.0.11 MySQL renamed st_mysql_bind to MYSQL_BIND.
+//
+#if !defined(LIBODB_MYSQL_MARIADB) && MYSQL_VERSION_ID >= 80011
+struct MYSQL_BIND;
+#else
+typedef struct st_mysql_bind MYSQL_BIND;
+#endif
+
+// MariaDB defines time types directly in mysql.h. Note that MariaDB is only
+// supported by the build2 build so we include the header as <mysql/mysql.h>
+// unconditionally.
+//
+#ifdef LIBODB_MYSQL_MARIADB
+# include <mysql/mysql.h>
+#else
+# ifdef LIBODB_MYSQL_INCLUDE_SHORT
+# include <mysql_time.h>
+# else
+# include <mysql/mysql_time.h>
+# endif
+#endif
+
+#endif // ODB_MYSQL_MYSQL_TYPES_HXX
diff --git a/libodb-mysql/odb/mysql/mysql.hxx b/libodb-mysql/odb/mysql/mysql.hxx
new file mode 100644
index 0000000..38fdd62
--- /dev/null
+++ b/libodb-mysql/odb/mysql/mysql.hxx
@@ -0,0 +1,31 @@
+// file : odb/mysql/mysql.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_MYSQL_HXX
+#define ODB_MYSQL_MYSQL_HXX
+
+#include <odb/mysql/details/config.hxx>
+
+#include <odb/mysql/mysql-types.hxx>
+
+#ifdef _WIN32
+# ifndef NOMINMAX // No min and max macros.
+# define NOMINMAX
+# include <winsock2.h>
+# undef NOMINMAX
+# else
+# include <winsock2.h>
+# endif
+#endif
+
+#ifdef LIBODB_MYSQL_INCLUDE_SHORT
+# include <mysql.h>
+# include <errmsg.h>
+# include <mysqld_error.h>
+#else
+# include <mysql/mysql.h>
+# include <mysql/errmsg.h>
+# include <mysql/mysqld_error.h>
+#endif
+
+#endif // ODB_MYSQL_MYSQL_HXX
diff --git a/libodb-mysql/odb/mysql/no-id-object-result.hxx b/libodb-mysql/odb/mysql/no-id-object-result.hxx
new file mode 100644
index 0000000..7a38120
--- /dev/null
+++ b/libodb-mysql/odb/mysql/no-id-object-result.hxx
@@ -0,0 +1,81 @@
+// file : odb/mysql/no-id-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_NO_ID_OBJECT_RESULT_HXX
+#define ODB_MYSQL_NO_ID_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/no-id-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx> // query_base
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ class no_id_object_result_impl: public odb::no_id_object_result_impl<T>
+ {
+ public:
+ typedef odb::no_id_object_result_impl<T> base_type;
+
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_mysql> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~no_id_object_result_impl ();
+
+ no_id_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type&);
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ void
+ fetch ();
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ std::size_t count_;
+ };
+ }
+}
+
+#include <odb/mysql/no-id-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_NO_ID_OBJECT_RESULT_HXX
diff --git a/libodb-mysql/odb/mysql/no-id-object-result.txx b/libodb-mysql/odb/mysql/no-id-object-result.txx
new file mode 100644
index 0000000..21168b9
--- /dev/null
+++ b/libodb-mysql/odb/mysql/no-id-object-result.txx
@@ -0,0 +1,178 @@
+// file : odb/mysql/no-id-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/mysql/no-id-object-statements.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ no_id_object_result_impl<T>::
+ ~no_id_object_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ no_id_object_result_impl<T>::
+ no_id_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ count_ (0)
+ {
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ load (object_type& obj)
+ {
+ if (count_ > statement_->fetched ())
+ fetch ();
+
+ object_traits::callback (this->db_, obj, callback_event::pre_load);
+ tc_.init (obj, statements_.image (), &this->db_);
+ object_traits::callback (this->db_, obj, callback_event::post_load);
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (this->end_)
+ return;
+
+ // If we are cached, simply increment the position and
+ // postpone the actual row fetching until later. This way
+ // if the same object is loaded in between iteration, the
+ // image won't be messed up.
+ //
+ count_++;
+
+ if (statement_->cached ())
+ this->end_ = count_ > statement_->result_size ();
+ else
+ fetch ();
+
+ if (this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ fetch ()
+ {
+ // If the result is cached, the image can grow between calls
+ // to fetch() as a result of other statements execution.
+ //
+ if (statement_->cached ())
+ {
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ }
+ }
+
+ while (!this->end_ && count_ > statement_->fetched ())
+ {
+ select_statement::result r (statement_->fetch ());
+
+ switch (r)
+ {
+ case select_statement::truncated:
+ {
+ // Don't re-fetch data we are skipping.
+ //
+ if (count_ != statement_->fetched ())
+ continue;
+
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (tc_.grow (im, statements_.select_image_truncated ()))
+ im.version++;
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ statement_->refetch ();
+ }
+ // Fall throught.
+ }
+ case select_statement::success:
+ {
+ break;
+ }
+ case select_statement::no_data:
+ {
+ this->end_ = true;
+ break;
+ }
+ }
+ }
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ cache ()
+ {
+ if (!this->end_ && !statement_->cached ())
+ {
+ statement_->cache ();
+
+ if (count_ == statement_->result_size ())
+ {
+ statement_->free_result ();
+ count_++; // One past the result size.
+ this->end_ = true;
+ }
+ }
+ }
+
+ template <typename T>
+ std::size_t no_id_object_result_impl<T>::
+ size ()
+ {
+ if (!this->end_)
+ {
+ if (!statement_->cached ())
+ throw result_not_cached ();
+
+ return statement_->result_size ();
+ }
+ else
+ return count_ - 1; // One past the result size.
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/no-id-object-statements.hxx b/libodb-mysql/odb/mysql/no-id-object-statements.hxx
new file mode 100644
index 0000000..b554b72
--- /dev/null
+++ b/libodb-mysql/odb/mysql/no-id-object-statements.hxx
@@ -0,0 +1,135 @@
+// file : odb/mysql/no-id-object-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_NO_ID_OBJECT_STATEMENTS_HXX
+#define ODB_MYSQL_NO_ID_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/binding.hxx>
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/statements-base.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ //
+ // Implementation for objects without object id.
+ //
+
+ template <typename T>
+ class no_id_object_statements: public statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_mysql> object_traits;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::image_type image_type;
+
+ typedef mysql::insert_statement insert_statement_type;
+
+ public:
+ no_id_object_statements (connection_type&);
+
+ virtual
+ ~no_id_object_statements ();
+
+ // Object image.
+ //
+ image_type&
+ image () {return image_;}
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Select binding (needed for query support).
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ my_bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ 0,
+ false));
+
+ return *persist_;
+ }
+
+ public:
+ // select = total
+ // insert = total - inverse; inverse == 0 for object without id
+ //
+ static const std::size_t select_column_count =
+ object_traits::column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count;
+
+ private:
+ no_id_object_statements (const no_id_object_statements&);
+ no_id_object_statements& operator= (const no_id_object_statements&);
+
+ private:
+ image_type image_;
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+ binding select_image_binding_;
+ MYSQL_BIND select_image_bind_[select_column_count];
+ my_bool select_image_truncated_[select_column_count];
+
+ // Insert binding.
+ //
+ std::size_t insert_image_version_;
+ binding insert_image_binding_;
+ MYSQL_BIND insert_image_bind_[insert_column_count];
+
+ details::shared_ptr<insert_statement_type> persist_;
+ };
+ }
+}
+
+#include <odb/mysql/no-id-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_NO_ID_OBJECT_STATEMENTS_HXX
diff --git a/libodb-mysql/odb/mysql/no-id-object-statements.txx b/libodb-mysql/odb/mysql/no-id-object-statements.txx
new file mode 100644
index 0000000..5917461
--- /dev/null
+++ b/libodb-mysql/odb/mysql/no-id-object-statements.txx
@@ -0,0 +1,36 @@
+// file : odb/mysql/no-id-object-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ no_id_object_statements<T>::
+ ~no_id_object_statements ()
+ {
+ }
+
+ template <typename T>
+ no_id_object_statements<T>::
+ no_id_object_statements (connection_type& conn)
+ : statements_base (conn),
+ select_image_binding_ (select_image_bind_, select_column_count),
+ insert_image_binding_ (insert_image_bind_, insert_column_count)
+ {
+ image_.version = 0;
+ select_image_version_ = 0;
+ insert_image_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+
+ for (std::size_t i (0); i < select_column_count; ++i)
+ select_image_bind_[i].error = select_image_truncated_ + i;
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/polymorphic-object-result.hxx b/libodb-mysql/odb/mysql/polymorphic-object-result.hxx
new file mode 100644
index 0000000..1ccc313
--- /dev/null
+++ b/libodb-mysql/odb/mysql/polymorphic-object-result.hxx
@@ -0,0 +1,94 @@
+// file : odb/mysql/polymorphic-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_POLYMORPHIC_OBJECT_RESULT_HXX
+#define ODB_MYSQL_POLYMORPHIC_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/polymorphic-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx> // query_base
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ class polymorphic_object_result_impl:
+ public odb::polymorphic_object_result_impl<T>
+ {
+ public:
+ typedef odb::polymorphic_object_result_impl<T> base_type;
+
+ typedef typename base_type::id_type id_type;
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_mysql> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename base_type::root_type root_type;
+ typedef typename base_type::discriminator_type discriminator_type;
+
+ typedef object_traits_impl<root_type, id_mysql> root_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~polymorphic_object_result_impl ();
+
+ polymorphic_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type*, bool fetch);
+
+ virtual id_type
+ load_id ();
+
+ virtual discriminator_type
+ load_discriminator ();
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ void
+ fetch (bool next = true);
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ std::size_t count_;
+ };
+ }
+}
+
+#include <odb/mysql/polymorphic-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_POLYMORPHIC_OBJECT_RESULT_HXX
diff --git a/libodb-mysql/odb/mysql/polymorphic-object-result.txx b/libodb-mysql/odb/mysql/polymorphic-object-result.txx
new file mode 100644
index 0000000..e587f6f
--- /dev/null
+++ b/libodb-mysql/odb/mysql/polymorphic-object-result.txx
@@ -0,0 +1,370 @@
+// file : odb/mysql/polymorphic-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/mysql/polymorphic-object-statements.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ polymorphic_object_result_impl<T>::
+ ~polymorphic_object_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ polymorphic_object_result_impl<T>::
+ polymorphic_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> st,
+ statements_type& sts,
+ const schema_version_migration* svm)
+ : base_type (sts.connection ()),
+ statement_ (st),
+ statements_ (sts),
+ tc_ (svm),
+ count_ (0)
+ {
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ load (object_type* pobj, bool f)
+ {
+ if (count_ > statement_->fetched ())
+ fetch ();
+ else if (f && statement_->cached ())
+ {
+ // We have to re-load the image in case it has been overwritten
+ // between the last time we fetched and this call to load().
+ //
+ fetch (false);
+ }
+
+ typename statements_type::root_statements_type& rsts (
+ statements_.root_statements ());
+
+ // This is a top-level call so the statements cannot be locked.
+ //
+ assert (!rsts.locked ());
+ typename statements_type::auto_lock l (rsts);
+
+ typename object_traits::image_type& i (statements_.image ());
+ typename root_traits::image_type& ri (rsts.image ());
+
+ id_type id (root_traits::id (ri));
+
+ // Determine this object's dynamic type.
+ //
+ typedef typename root_traits::info_type info_type;
+ discriminator_type d (root_traits::discriminator (ri));
+ discriminator_type disc (d);
+
+ // Use the polymorphic_info() helper to get concrete_info if
+ // object_type is concrete and NULL if it is abstract.
+ //
+ const info_type* spi (polymorphic_info (object_traits::info));
+ const info_type& pi (
+ spi != 0 && spi->discriminator == d
+ ? *spi
+ : root_traits::map->find (d));
+
+ typedef typename root_traits::pointer_type root_pointer_type;
+ typedef typename root_traits::pointer_traits root_pointer_traits;
+
+ typename object_traits::pointer_cache_traits::insert_guard ig;
+
+ if (pobj == 0)
+ {
+ // Need to create a new instance of the dynamic type.
+ //
+ root_pointer_type rp (pi.create ());
+ pointer_type p (
+ root_pointer_traits::template static_pointer_cast<object_type> (rp));
+
+ // Insert it as a root pointer (for non-unique pointers, rp should
+ // still be valid and for unique pointers this is a no-op).
+ //
+ ig.reset (
+ object_traits::pointer_cache_traits::insert (this->db_, id, rp));
+
+ pobj = &pointer_traits::get_ref (p);
+ current (p);
+ }
+ else
+ {
+ // We are loading into an existing instance. If the static and
+ // dynamic types differ, then make sure the instance is at least
+ // of the dynamic type.
+ //
+ if (&pi != &object_traits::info)
+ {
+ const info_type& dpi (root_traits::map->find (typeid (*pobj)));
+
+ if (&dpi != &pi && dpi.derived (pi))
+ throw object_not_persistent (); // @@ type_mismatch ?
+ }
+ }
+
+ callback_event ce (callback_event::pre_load);
+ pi.dispatch (info_type::call_callback, this->db_, pobj, &ce);
+
+ tc_.init (*pobj, i, &this->db_);
+
+ // Initialize the id image and binding and load the rest of the object
+ // (containers, dynamic part, etc).
+ //
+ typename object_traits::id_image_type& idi (statements_.id_image ());
+ root_traits::init (idi, id);
+
+ binding& idb (statements_.id_image_binding ());
+ if (idi.version != statements_.id_image_version () || idb.version == 0)
+ {
+ object_traits::bind (idb.bind, idi);
+ statements_.id_image_version (idi.version);
+ idb.version++;
+ }
+
+ tc_.load_ (statements_, *pobj, false);
+
+ // Load the dynamic part of the object unless static and dynamic
+ // types are the same.
+ //
+ if (&pi != &object_traits::info)
+ {
+ std::size_t d (object_traits::depth);
+ pi.dispatch (info_type::call_load, this->db_, pobj, &d);
+ };
+
+ rsts.load_delayed (tc_.version ());
+ l.unlock ();
+
+ ce = callback_event::post_load;
+ pi.dispatch (info_type::call_callback, this->db_, pobj, &ce);
+ object_traits::pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+
+ template <typename T>
+ typename polymorphic_object_result_impl<T>::id_type
+ polymorphic_object_result_impl<T>::
+ load_id ()
+ {
+ if (count_ > statement_->fetched ())
+ fetch ();
+ else if (statement_->cached ())
+ {
+ // We have to re-load the image in case it has been overwritten
+ // between the last time we fetched and this call to load_id().
+ //
+ fetch (false);
+ }
+
+ return root_traits::id (statements_.root_statements ().image ());
+ }
+
+ template <typename T>
+ typename polymorphic_object_result_impl<T>::discriminator_type
+ polymorphic_object_result_impl<T>::
+ load_discriminator ()
+ {
+ if (count_ > statement_->fetched ())
+ fetch ();
+ else if (statement_->cached ())
+ {
+ // We have to re-load the image in case it has been overwritten
+ // between the last time we fetched and this call to
+ // load_discriminator().
+ //
+ fetch (false);
+ }
+
+ return root_traits::discriminator (
+ statements_.root_statements ().image ());
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (this->end_)
+ return;
+
+ // If we are cached, simply increment the position and
+ // postpone the actual row fetching until later. This way
+ // if the same object is loaded in between iteration, the
+ // image won't be messed up.
+ //
+ count_++;
+
+ if (statement_->cached ())
+ this->end_ = count_ > statement_->result_size ();
+ else
+ fetch ();
+
+ if (this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T, typename R>
+ struct polymorphic_image_rebind
+ {
+ // Derived type version.
+ //
+ typedef object_traits_impl<T, id_mysql> traits;
+
+ static bool
+ rebind (typename traits::statements_type& sts,
+ const schema_version_migration* svm)
+ {
+ typename traits::image_type& im (sts.image ());
+
+ if (traits::check_version (sts.select_image_versions (), im))
+ {
+ binding& b (sts.select_image_binding (traits::depth));
+ object_traits_calls<T> tc (svm);
+ tc.bind (b.bind, 0, 0, im, statement_select);
+ traits::update_version (
+ sts.select_image_versions (), im, sts.select_image_bindings ());
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+ template <typename R>
+ struct polymorphic_image_rebind<R, R>
+ {
+ // Root type version.
+ //
+ typedef object_traits_impl<R, id_mysql> traits;
+
+ static bool
+ rebind (typename traits::statements_type& sts,
+ const schema_version_migration* svm)
+ {
+ typename traits::image_type& im (sts.image ());
+
+ if (im.version != sts.select_image_version ())
+ {
+ binding& b (sts.select_image_binding ());
+ object_traits_calls<R> tc (svm);
+ tc.bind (b.bind, im, statement_select);
+ sts.select_image_version (im.version);
+ b.version++;
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ fetch (bool next)
+ {
+ typedef polymorphic_image_rebind<object_type, root_type> image_rebind;
+
+ // If the result is cached, the image can grow between calls
+ // to fetch() as a result of other statements execution.
+ //
+ if (statement_->cached ())
+ image_rebind::rebind (statements_, tc_.version ());
+
+ while (!this->end_ && (!next || count_ > statement_->fetched ()))
+ {
+ select_statement::result r (statement_->fetch (next));
+
+ switch (r)
+ {
+ case select_statement::truncated:
+ {
+ // Don't re-fetch data we are skipping.
+ //
+ if (next && count_ != statement_->fetched ())
+ continue;
+
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (tc_.grow (im, statements_.select_image_truncated ()))
+ im.version++;
+
+ if (image_rebind::rebind (statements_, tc_.version ()))
+ statement_->refetch ();
+
+ // Fall throught.
+ }
+ case select_statement::success:
+ {
+ break;
+ }
+ case select_statement::no_data:
+ {
+ this->end_ = true;
+ break;
+ }
+ }
+
+ // If we are refetching the current row, then we are done.
+ //
+ if (!next)
+ break;
+ }
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ cache ()
+ {
+ if (!this->end_ && !statement_->cached ())
+ {
+ statement_->cache ();
+
+ if (count_ == statement_->result_size ())
+ {
+ statement_->free_result ();
+ count_++; // One past the result size.
+ this->end_ = true;
+ }
+ }
+ }
+
+ template <typename T>
+ std::size_t polymorphic_object_result_impl<T>::
+ size ()
+ {
+ if (!this->end_)
+ {
+ if (!statement_->cached ())
+ throw result_not_cached ();
+
+ return statement_->result_size ();
+ }
+ else
+ return count_ - 1; // One past the result size.
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/polymorphic-object-statements.hxx b/libodb-mysql/odb/mysql/polymorphic-object-statements.hxx
new file mode 100644
index 0000000..0eacac4
--- /dev/null
+++ b/libodb-mysql/odb/mysql/polymorphic-object-statements.hxx
@@ -0,0 +1,474 @@
+// file : odb/mysql/polymorphic-object-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_POLYMORPHIC_OBJECT_STATEMENTS_HXX
+#define ODB_MYSQL_POLYMORPHIC_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/binding.hxx>
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/statements-base.hxx>
+#include <odb/mysql/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ //
+ // Implementation for polymorphic objects.
+ //
+
+ template <typename T>
+ class polymorphic_root_object_statements: public object_statements<T>
+ {
+ public:
+ typedef typename object_statements<T>::connection_type connection_type;
+ typedef typename object_statements<T>::object_traits object_traits;
+ typedef typename object_statements<T>::id_image_type id_image_type;
+
+ typedef
+ typename object_traits::discriminator_image_type
+ discriminator_image_type;
+
+ typedef
+ typename object_statements<T>::select_statement_type
+ select_statement_type;
+
+ public:
+ // Interface compatibility with derived_object_statements.
+ //
+ typedef polymorphic_root_object_statements root_statements_type;
+
+ root_statements_type&
+ root_statements ()
+ {
+ return *this;
+ }
+
+ public:
+ // Discriminator binding.
+ //
+ discriminator_image_type&
+ discriminator_image () {return discriminator_image_;}
+
+ std::size_t
+ discriminator_image_version () const
+ {return discriminator_image_version_;}
+
+ void
+ discriminator_image_version (std::size_t v)
+ {discriminator_image_version_ = v;}
+
+ binding&
+ discriminator_image_binding () {return discriminator_image_binding_;}
+
+ my_bool*
+ discriminator_image_truncated () {return discriminator_image_truncated_;}
+
+ // Id binding for discriminator retrieval.
+ //
+ id_image_type&
+ discriminator_id_image () {return discriminator_id_image_;}
+
+ std::size_t
+ discriminator_id_image_version () const
+ {return discriminator_id_image_version_;}
+
+ void
+ discriminator_id_image_version (std::size_t v)
+ {discriminator_id_image_version_ = v;}
+
+ binding&
+ discriminator_id_image_binding ()
+ {return discriminator_id_image_binding_;}
+
+ //
+ //
+ select_statement_type&
+ find_discriminator_statement ()
+ {
+ if (find_discriminator_ == 0)
+ find_discriminator_.reset (
+ new (details::shared) select_statement_type (
+ this->conn_,
+ object_traits::find_discriminator_statement,
+ false, // Doesn't need to be processed.
+ false, // Don't optimize.
+ discriminator_id_image_binding_,
+ discriminator_image_binding_,
+ false));
+
+ return *find_discriminator_;
+ }
+
+ public:
+ polymorphic_root_object_statements (connection_type&);
+
+ virtual
+ ~polymorphic_root_object_statements ();
+
+ // Static "override" (statements type).
+ //
+ void
+ load_delayed (const schema_version_migration* svm)
+ {
+ assert (this->locked ());
+
+ if (!this->delayed_.empty ())
+ this->template load_delayed_<polymorphic_root_object_statements> (
+ svm);
+ }
+
+ public:
+ static const std::size_t id_column_count =
+ object_statements<T>::id_column_count;
+
+ static const std::size_t discriminator_column_count =
+ object_traits::discriminator_column_count;
+
+ static const std::size_t managed_optimistic_column_count =
+ object_traits::managed_optimistic_column_count;
+
+ private:
+ // Discriminator image.
+ //
+ discriminator_image_type discriminator_image_;
+ std::size_t discriminator_image_version_;
+ binding discriminator_image_binding_;
+ MYSQL_BIND discriminator_image_bind_[discriminator_column_count +
+ managed_optimistic_column_count];
+ my_bool discriminator_image_truncated_[discriminator_column_count +
+ managed_optimistic_column_count];
+
+ // Id image for discriminator retrieval (only used as a parameter).
+ //
+ id_image_type discriminator_id_image_;
+ std::size_t discriminator_id_image_version_;
+ binding discriminator_id_image_binding_;
+ MYSQL_BIND discriminator_id_image_bind_[id_column_count];
+
+ details::shared_ptr<select_statement_type> find_discriminator_;
+ };
+
+ template <typename T>
+ class polymorphic_derived_object_statements: public statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_mysql> object_traits;
+ typedef typename object_traits::id_type id_type;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::id_image_type id_image_type;
+ typedef typename object_traits::image_type image_type;
+
+ typedef typename object_traits::root_type root_type;
+ typedef
+ polymorphic_root_object_statements<root_type>
+ root_statements_type;
+
+ typedef typename object_traits::base_type base_type;
+ typedef
+ typename object_traits::base_traits::statements_type
+ base_statements_type;
+
+ typedef
+ typename object_traits::extra_statement_cache_type
+ extra_statement_cache_type;
+
+ typedef mysql::insert_statement insert_statement_type;
+ typedef mysql::select_statement select_statement_type;
+ typedef mysql::update_statement update_statement_type;
+ typedef mysql::delete_statement delete_statement_type;
+
+ typedef typename root_statements_type::auto_lock auto_lock;
+
+ public:
+ polymorphic_derived_object_statements (connection_type&);
+
+ virtual
+ ~polymorphic_derived_object_statements ();
+
+ public:
+ // Delayed loading.
+ //
+ static void
+ delayed_loader (odb::database&,
+ const id_type&,
+ root_type&,
+ const schema_version_migration*);
+
+ public:
+ // Root and immediate base statements.
+ //
+ root_statements_type&
+ root_statements ()
+ {
+ return root_statements_;
+ }
+
+ base_statements_type&
+ base_statements ()
+ {
+ return base_statements_;
+ }
+
+ public:
+ // Object image.
+ //
+ image_type&
+ image ()
+ {
+ return image_;
+ }
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ std::size_t
+ insert_id_binding_version () const { return insert_id_binding_version_;}
+
+ void
+ insert_id_binding_version (std::size_t v) {insert_id_binding_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_binding_version () const { return update_id_binding_version_;}
+
+ void
+ update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t*
+ select_image_versions () { return select_image_versions_;}
+
+ binding*
+ select_image_bindings () {return select_image_bindings_;}
+
+ binding&
+ select_image_binding (std::size_t d)
+ {
+ return select_image_bindings_[object_traits::depth - d];
+ }
+
+ my_bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Object id binding (comes from the root statements).
+ //
+ id_image_type&
+ id_image () {return root_statements_.id_image ();}
+
+ std::size_t
+ id_image_version () const {return root_statements_.id_image_version ();}
+
+ void
+ id_image_version (std::size_t v) {root_statements_.id_image_version (v);}
+
+ binding&
+ id_image_binding () {return root_statements_.id_image_binding ();}
+
+ binding&
+ optimistic_id_image_binding () {
+ return root_statements_.optimistic_id_image_binding ();}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ 0,
+ false));
+
+ return *persist_;
+ }
+
+ select_statement_type&
+ find_statement (std::size_t d)
+ {
+ std::size_t i (object_traits::depth - d);
+ details::shared_ptr<select_statement_type>& p (find_[i]);
+
+ if (p == 0)
+ p.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ object_traits::find_statements[i],
+ object_traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ root_statements_.id_image_binding (),
+ select_image_bindings_[i],
+ false));
+
+ return *p;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ object_traits::update_statement,
+ object_traits::versioned, // Process if versioned.
+ update_image_binding_,
+ false));
+
+ return *update_;
+ }
+
+ delete_statement_type&
+ erase_statement ()
+ {
+ if (erase_ == 0)
+ erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::erase_statement,
+ root_statements_.id_image_binding (),
+ false));
+
+ return *erase_;
+ }
+
+ // Extra (container, section) statement cache.
+ //
+ extra_statement_cache_type&
+ extra_statement_cache ()
+ {
+ return extra_statement_cache_.get (
+ conn_,
+ image_,
+ id_image (),
+ id_image_binding (),
+ &id_image_binding ()); // Note, not id+version.
+ }
+
+ public:
+ // select = total - id - separate_load + base::select
+ // insert = total - inverse
+ // update = total - inverse - id - readonly - separate_update
+ //
+ static const std::size_t id_column_count =
+ object_traits::id_column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count -
+ id_column_count -
+ object_traits::separate_load_column_count +
+ base_statements_type::select_column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count -
+ object_traits::inverse_column_count;
+
+ static const std::size_t update_column_count = insert_column_count -
+ object_traits::id_column_count -
+ object_traits::readonly_column_count -
+ object_traits::separate_update_column_count;
+
+ private:
+ polymorphic_derived_object_statements (
+ const polymorphic_derived_object_statements&);
+
+ polymorphic_derived_object_statements&
+ operator= (const polymorphic_derived_object_statements&);
+
+ private:
+ root_statements_type& root_statements_;
+ base_statements_type& base_statements_;
+
+ extra_statement_cache_ptr<extra_statement_cache_type,
+ image_type,
+ id_image_type> extra_statement_cache_;
+
+ image_type image_;
+
+ // Select binding. Here we are have an array of statements/bindings
+ // one for each depth. In other words, if we have classes root, base,
+ // and derived, then we have the following array of statements:
+ //
+ // [0] d + b + r
+ // [1] d + b
+ // [2] d
+ //
+ // Also, because we have a chain of images bound to these statements,
+ // we have an array of versions, one entry for each base plus one for
+ // our own image.
+ //
+ // A poly-abstract class only needs the first statement and in this
+ // case we have only one entry in the the bindings and statements
+ // arrays (but not versions; we still have a chain of images).
+ //
+ std::size_t select_image_versions_[object_traits::depth];
+ binding select_image_bindings_[
+ object_traits::abstract ? 1 : object_traits::depth];
+ MYSQL_BIND select_image_bind_[select_column_count];
+ my_bool select_image_truncated_[select_column_count];
+
+ // Insert binding. The id binding is copied from the hierarchy root.
+ //
+ std::size_t insert_image_version_;
+ std::size_t insert_id_binding_version_;
+ binding insert_image_binding_;
+ MYSQL_BIND insert_image_bind_[insert_column_count];
+
+ // Update binding. The id suffix binding is copied from the hierarchy
+ // root.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_binding_version_;
+ binding update_image_binding_;
+ MYSQL_BIND update_image_bind_[update_column_count + id_column_count];
+
+ details::shared_ptr<insert_statement_type> persist_;
+ details::shared_ptr<select_statement_type> find_[
+ object_traits::abstract ? 1 : object_traits::depth];
+ details::shared_ptr<update_statement_type> update_;
+ details::shared_ptr<delete_statement_type> erase_;
+ };
+ }
+}
+
+#include <odb/mysql/polymorphic-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_POLYMORPHIC_OBJECT_STATEMENTS_HXX
diff --git a/libodb-mysql/odb/mysql/polymorphic-object-statements.txx b/libodb-mysql/odb/mysql/polymorphic-object-statements.txx
new file mode 100644
index 0000000..5899bc0
--- /dev/null
+++ b/libodb-mysql/odb/mysql/polymorphic-object-statements.txx
@@ -0,0 +1,145 @@
+// file : odb/mysql/polymorphic-object-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/transaction.hxx>
+#include <odb/mysql/statement-cache.hxx>
+#include <odb/mysql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ //
+ // polymorphic_root_object_statements
+ //
+
+ template <typename T>
+ polymorphic_root_object_statements<T>::
+ ~polymorphic_root_object_statements ()
+ {
+ }
+
+ template <typename T>
+ polymorphic_root_object_statements<T>::
+ polymorphic_root_object_statements (connection_type& conn)
+ : object_statements<T> (conn),
+ discriminator_image_binding_ (discriminator_image_bind_,
+ discriminator_column_count +
+ managed_optimistic_column_count),
+ discriminator_id_image_binding_ (discriminator_id_image_bind_,
+ id_column_count)
+ {
+ discriminator_image_.version = 0;
+ discriminator_id_image_.version = 0;
+
+ discriminator_image_version_ = 0;
+ discriminator_id_image_version_ = 0;
+
+ std::memset (discriminator_image_bind_,
+ 0,
+ sizeof (discriminator_image_bind_));
+ std::memset (discriminator_id_image_bind_,
+ 0,
+ sizeof (discriminator_id_image_bind_));
+ std::memset (discriminator_image_truncated_,
+ 0,
+ sizeof (discriminator_image_truncated_));
+
+ for (std::size_t i (0);
+ i < discriminator_column_count + managed_optimistic_column_count;
+ ++i)
+ {
+ discriminator_image_bind_[i].error =
+ discriminator_image_truncated_ + i;
+ }
+ }
+
+ //
+ // polymorphic_derived_object_statements
+ //
+
+ template <typename T>
+ polymorphic_derived_object_statements<T>::
+ ~polymorphic_derived_object_statements ()
+ {
+ }
+
+ template <typename T>
+ polymorphic_derived_object_statements<T>::
+ polymorphic_derived_object_statements (connection_type& conn)
+ : statements_base (conn),
+ root_statements_ (conn.statement_cache ().find_object<root_type> ()),
+ base_statements_ (conn.statement_cache ().find_object<base_type> ()),
+ insert_image_binding_ (insert_image_bind_, insert_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count)
+ {
+ image_.base = &base_statements_.image ();
+ image_.version = 0;
+
+ for (std::size_t i (0); i < object_traits::depth; ++i)
+ select_image_versions_[i] = 0;
+
+ for (std::size_t i (0);
+ i < (object_traits::abstract ? 1 : object_traits::depth);
+ ++i)
+ {
+ select_image_bindings_[i].bind = select_image_bind_;
+ select_image_bindings_[i].count = object_traits::find_column_counts[i];
+ }
+
+ insert_image_version_ = 0;
+ insert_id_binding_version_ = 0;
+ update_image_version_ = 0;
+ update_id_binding_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+
+ for (std::size_t i (0); i < select_column_count; ++i)
+ select_image_bind_[i].error = select_image_truncated_ + i;
+ }
+
+ template <typename T>
+ void polymorphic_derived_object_statements<T>::
+ delayed_loader (odb::database& db,
+ const id_type& id,
+ root_type& robj,
+ const schema_version_migration* svm)
+ {
+ connection_type& conn (transaction::current ().connection (db));
+ polymorphic_derived_object_statements& sts (
+ conn.statement_cache ().find_object<object_type> ());
+ root_statements_type& rsts (sts.root_statements ());
+
+ object_type& obj (static_cast<object_type&> (robj));
+
+ // The same code as in object_statements::load_delayed_().
+ //
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (sts, &id))
+ throw object_not_persistent ();
+
+ object_traits::callback (db, obj, callback_event::pre_load);
+ tc.init (obj, sts.image (), &db);
+ tc.load_ (sts, obj, false); // Load containers, etc.
+
+ rsts.load_delayed (svm);
+
+ {
+ typename root_statements_type::auto_unlock u (rsts);
+ object_traits::callback (db, obj, callback_event::post_load);
+ }
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/prepared-query.cxx b/libodb-mysql/odb/mysql/prepared-query.cxx
new file mode 100644
index 0000000..498d147
--- /dev/null
+++ b/libodb-mysql/odb/mysql/prepared-query.cxx
@@ -0,0 +1,15 @@
+// file : odb/mysql/prepared-query.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/mysql/prepared-query.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ prepared_query_impl::
+ ~prepared_query_impl ()
+ {
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/prepared-query.hxx b/libodb-mysql/odb/mysql/prepared-query.hxx
new file mode 100644
index 0000000..d892fec
--- /dev/null
+++ b/libodb-mysql/odb/mysql/prepared-query.hxx
@@ -0,0 +1,34 @@
+// file : odb/mysql/prepared-query.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_PREPARED_QUERY_HXX
+#define ODB_MYSQL_PREPARED_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/prepared-query.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/query.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ struct LIBODB_MYSQL_EXPORT prepared_query_impl: odb::prepared_query_impl
+ {
+ virtual
+ ~prepared_query_impl ();
+
+ prepared_query_impl (odb::connection& c): odb::prepared_query_impl (c) {}
+
+ mysql::query_base query;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_PREPARED_QUERY_HXX
diff --git a/libodb-mysql/odb/mysql/query-const-expr.cxx b/libodb-mysql/odb/mysql/query-const-expr.cxx
new file mode 100644
index 0000000..4c1dd37
--- /dev/null
+++ b/libodb-mysql/odb/mysql/query-const-expr.cxx
@@ -0,0 +1,14 @@
+// file : odb/mysql/query-const-expr.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/mysql/query.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ // Sun CC cannot handle this in query.cxx.
+ //
+ const query_base query_base::true_expr (true);
+ }
+}
diff --git a/libodb-mysql/odb/mysql/query-dynamic.cxx b/libodb-mysql/odb/mysql/query-dynamic.cxx
new file mode 100644
index 0000000..89041ca
--- /dev/null
+++ b/libodb-mysql/odb/mysql/query-dynamic.cxx
@@ -0,0 +1,157 @@
+// file : odb/mysql/query-dynamic.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+
+#include <odb/mysql/query-dynamic.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mysql
+ {
+ static const char* logic_operators[] = {") AND (", ") OR ("};
+ static const char* comp_operators[] = {"=", "!=", "<", ">", "<=", ">="};
+
+ static void
+ translate (query_base& q, const odb::query_base& s, size_t p)
+ {
+ typedef odb::query_base::clause_part part;
+
+ const part& x (s.clause ()[p]);
+
+ switch (x.kind)
+ {
+ case part::kind_column:
+ {
+ const query_column_base* c (
+ static_cast<const query_column_base*> (
+ x.native_info[id_mysql].column));
+
+ q.append (c->table (), c->column ());
+ break;
+ }
+ case part::kind_param_val:
+ case part::kind_param_ref:
+ {
+ const query_column_base* c (
+ static_cast<const query_column_base*> (
+ x.native_info[id_mysql].column));
+
+ query_param_factory f (
+ reinterpret_cast<query_param_factory> (
+ x.native_info[id_mysql].param_factory));
+
+ const odb::query_param* p (
+ reinterpret_cast<const odb::query_param*> (x.data));
+
+ q.append (f (p->value, x.kind == part::kind_param_ref),
+ c->conversion ());
+ break;
+ }
+ case part::kind_native:
+ {
+ q.append (s.strings ()[x.data]);
+ break;
+ }
+ case part::kind_true:
+ case part::kind_false:
+ {
+ q.append (x.kind == part::kind_true);
+ break;
+ }
+ case part::op_add:
+ {
+ translate (q, s, x.data);
+ translate (q, s, p - 1);
+ break;
+ }
+ case part::op_and:
+ case part::op_or:
+ {
+ q += "(";
+ translate (q, s, x.data);
+ q += logic_operators[x.kind - part::op_and];
+ translate (q, s, p - 1);
+ q += ")";
+ break;
+ }
+ case part::op_not:
+ {
+ q += "NOT (";
+ translate (q, s, p - 1);
+ q += ")";
+ break;
+ }
+ case part::op_null:
+ case part::op_not_null:
+ {
+ translate (q, s, p - 1);
+ q += (x.kind == part::op_null ? "IS NULL" : "IS NOT NULL");
+ break;
+ }
+ case part::op_in:
+ {
+ if (x.data != 0)
+ {
+ size_t b (p - x.data);
+
+ translate (q, s, b - 1); // column
+ q += "IN (";
+
+ for (size_t i (b); i != p; ++i)
+ {
+ if (i != b)
+ q += ",";
+
+ translate (q, s, i);
+ }
+
+ q += ")";
+ }
+ else
+ q.append (false);
+
+ break;
+ }
+ case part::op_like:
+ {
+ translate (q, s, p - 2); // column
+ q += "LIKE";
+ translate (q, s, p - 1); // pattern
+ break;
+ }
+ case part::op_like_escape:
+ {
+ translate (q, s, p - 3); // column
+ q += "LIKE";
+ translate (q, s, p - 2); // pattern
+ q += "ESCAPE";
+ translate (q, s, p - 1); // escape
+ break;
+ }
+ case part::op_eq:
+ case part::op_ne:
+ case part::op_lt:
+ case part::op_gt:
+ case part::op_le:
+ case part::op_ge:
+ {
+ translate (q, s, x.data);
+ q += comp_operators[x.kind - part::op_eq];
+ translate (q, s, p - 1);
+ break;
+ }
+ }
+ }
+
+ query_base::
+ query_base (const odb::query_base& q)
+ : binding_ (0, 0)
+ {
+ if (!q.empty ())
+ translate (*this, q, q.clause ().size () - 1);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/query-dynamic.hxx b/libodb-mysql/odb/mysql/query-dynamic.hxx
new file mode 100644
index 0000000..f6a571f
--- /dev/null
+++ b/libodb-mysql/odb/mysql/query-dynamic.hxx
@@ -0,0 +1,32 @@
+// file : odb/mysql/query-dynamic.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_QUERY_DYNAMIC_HXX
+#define ODB_MYSQL_QUERY_DYNAMIC_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/query.hxx>
+#include <odb/query-dynamic.hxx>
+
+#include <odb/mysql/query.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ typedef details::shared_ptr<query_param> (*query_param_factory) (
+ const void* val, bool by_ref);
+
+ template <typename T, database_type_id ID>
+ details::shared_ptr<query_param>
+ query_param_factory_impl (const void*, bool);
+ }
+}
+
+#include <odb/mysql/query-dynamic.ixx>
+#include <odb/mysql/query-dynamic.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_QUERY_DYNAMIC_HXX
diff --git a/libodb-mysql/odb/mysql/query-dynamic.ixx b/libodb-mysql/odb/mysql/query-dynamic.ixx
new file mode 100644
index 0000000..f9f3984
--- /dev/null
+++ b/libodb-mysql/odb/mysql/query-dynamic.ixx
@@ -0,0 +1,26 @@
+// file : odb/mysql/query-dynamic.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mysql
+ {
+ //
+ //
+ template <typename T, database_type_id ID>
+ inline query_column<T, ID>::
+ query_column (odb::query_column<T>& qc,
+ const char* table, const char* column, const char* conv)
+ : query_column_base (table, column, conv)
+ {
+ native_column_info& ci (qc.native_info[id_mysql]);
+ ci.column = static_cast<query_column_base*> (this);
+
+ // For some reason GCC needs this statically-typed pointer in
+ // order to instantiate the functions.
+ //
+ query_param_factory f (&query_param_factory_impl<T, ID>);
+ ci.param_factory = reinterpret_cast<void*> (f);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/query-dynamic.txx b/libodb-mysql/odb/mysql/query-dynamic.txx
new file mode 100644
index 0000000..52e653b
--- /dev/null
+++ b/libodb-mysql/odb/mysql/query-dynamic.txx
@@ -0,0 +1,20 @@
+// file : odb/mysql/query-dynamic.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T, database_type_id ID>
+ details::shared_ptr<query_param>
+ query_param_factory_impl (const void* val, bool by_ref)
+ {
+ const T& v (*static_cast<const T*> (val));
+
+ return details::shared_ptr<query_param> (
+ by_ref
+ ? new (details::shared) query_param_impl<T, ID> (ref_bind<T> (v))
+ : new (details::shared) query_param_impl<T, ID> (val_bind<T> (v)));
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/query.cxx b/libodb-mysql/odb/mysql/query.cxx
new file mode 100644
index 0000000..a420495
--- /dev/null
+++ b/libodb-mysql/odb/mysql/query.cxx
@@ -0,0 +1,346 @@
+// file : odb/mysql/query.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+
+#include <odb/mysql/query.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mysql
+ {
+ // query_param
+ //
+ query_param::
+ ~query_param ()
+ {
+ }
+
+ // query_base
+ //
+ query_base::
+ query_base (const query_base& q)
+ : clause_ (q.clause_),
+ parameters_ (q.parameters_),
+ bind_ (q.bind_),
+ binding_ (0, 0)
+ {
+ // Here and below we want to maintain up to date binding info so
+ // that the call to parameters_binding() below is an immutable
+ // operation, provided the query does not have any by-reference
+ // parameters. This way a by-value-only query can be shared
+ // between multiple threads without the need for synchronization.
+ //
+ if (size_t n = bind_.size ())
+ {
+ binding_.bind = &bind_[0];
+ binding_.count = n;
+ binding_.version++;
+ }
+ }
+
+ query_base& query_base::
+ operator= (const query_base& q)
+ {
+ if (this != &q)
+ {
+ clause_ = q.clause_;
+ parameters_ = q.parameters_;
+ bind_ = q.bind_;
+
+ size_t n (bind_.size ());
+ binding_.bind = n != 0 ? &bind_[0] : 0;
+ binding_.count = n;
+ binding_.version++;
+ }
+
+ return *this;
+ }
+
+ query_base& query_base::
+ operator+= (const query_base& q)
+ {
+ clause_.insert (clause_.end (), q.clause_.begin (), q.clause_.end ());
+
+ size_t n (bind_.size ());
+
+ parameters_.insert (
+ parameters_.end (), q.parameters_.begin (), q.parameters_.end ());
+
+ bind_.insert (
+ bind_.end (), q.bind_.begin (), q.bind_.end ());
+
+ if (n != bind_.size ())
+ {
+ binding_.bind = &bind_[0];
+ binding_.count = bind_.size ();
+ binding_.version++;
+ }
+
+ return *this;
+ }
+
+ void query_base::
+ append (const string& q)
+ {
+ if (!clause_.empty () &&
+ clause_.back ().kind == clause_part::kind_native)
+ {
+ string& s (clause_.back ().part);
+
+ char first (!q.empty () ? q[0] : ' ');
+ char last (!s.empty () ? s[s.size () - 1] : ' ');
+
+ // We don't want extra spaces after '(' as well as before ','
+ // and ')'.
+ //
+ if (last != ' ' && last != '\n' && last != '(' &&
+ first != ' ' && first != '\n' && first != ',' && first != ')')
+ s += ' ';
+
+ s += q;
+ }
+ else
+ clause_.push_back (clause_part (clause_part::kind_native, q));
+ }
+
+ void query_base::
+ append (const char* table, const char* column)
+ {
+ string s (table);
+ s += '.';
+ s += column;
+
+ clause_.push_back (clause_part (clause_part::kind_column, s));
+ }
+
+ void query_base::
+ append (details::shared_ptr<query_param> p, const char* conv)
+ {
+ clause_.push_back (clause_part (clause_part::kind_param));
+
+ if (conv != 0)
+ clause_.back ().part = conv;
+
+ parameters_.push_back (p);
+ bind_.push_back (MYSQL_BIND ());
+ binding_.bind = &bind_[0];
+ binding_.count = bind_.size ();
+ binding_.version++;
+
+ MYSQL_BIND* b (&bind_.back ());
+ memset (b, 0, sizeof (MYSQL_BIND));
+ p->bind (b);
+ }
+
+ void query_base::
+ init_parameters () const
+ {
+ bool inc_ver (false);
+
+ for (size_t i (0); i < parameters_.size (); ++i)
+ {
+ query_param& p (*parameters_[i]);
+
+ if (p.reference ())
+ {
+ if (p.init ())
+ {
+ p.bind (&bind_[i]);
+ inc_ver = true;
+ }
+ }
+ }
+
+ if (inc_ver)
+ binding_.version++;
+ }
+
+ static bool
+ check_prefix (const string& s)
+ {
+ string::size_type n;
+
+ // It is easier to compare to upper and lower-case versions
+ // rather than getting involved with the portable case-
+ // insensitive string comparison mess.
+ //
+ if (s.compare (0, (n = 5), "WHERE") == 0 ||
+ s.compare (0, (n = 5), "where") == 0 ||
+ s.compare (0, (n = 6), "SELECT") == 0 ||
+ s.compare (0, (n = 6), "select") == 0 ||
+ s.compare (0, (n = 8), "ORDER BY") == 0 ||
+ s.compare (0, (n = 8), "order by") == 0 ||
+ s.compare (0, (n = 8), "GROUP BY") == 0 ||
+ s.compare (0, (n = 8), "group by") == 0 ||
+ s.compare (0, (n = 6), "HAVING") == 0 ||
+ s.compare (0, (n = 6), "having") == 0 ||
+ s.compare (0, (n = 4), "CALL") == 0 ||
+ s.compare (0, (n = 4), "call") == 0)
+ {
+ // It either has to be an exact match, or there should be
+ // a whitespace following the keyword.
+ //
+ if (s.size () == n || s[n] == ' ' || s[n] == '\n' || s[n] =='\t')
+ return true;
+ }
+
+ return false;
+ }
+
+ void query_base::
+ optimize ()
+ {
+ // Remove a single TRUE literal or one that is followe by one of
+ // the other clauses. This avoids useless WHERE clauses like
+ //
+ // WHERE TRUE GROUP BY foo
+ //
+ clause_type::iterator i (clause_.begin ()), e (clause_.end ());
+
+ if (i != e && i->kind == clause_part::kind_bool && i->bool_part)
+ {
+ clause_type::iterator j (i + 1);
+
+ if (j == e ||
+ (j->kind == clause_part::kind_native && check_prefix (j->part)))
+ clause_.erase (i);
+ }
+ }
+
+ const char* query_base::
+ clause_prefix () const
+ {
+ if (!clause_.empty ())
+ {
+ const clause_part& p (clause_.front ());
+
+ if (p.kind == clause_part::kind_native && check_prefix (p.part))
+ return "";
+
+ return "WHERE ";
+ }
+
+ return "";
+ }
+
+ string query_base::
+ clause () const
+ {
+ string r;
+
+ for (clause_type::const_iterator i (clause_.begin ()),
+ end (clause_.end ());
+ i != end;
+ ++i)
+ {
+ char last (!r.empty () ? r[r.size () - 1] : ' ');
+
+ switch (i->kind)
+ {
+ case clause_part::kind_column:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ r += i->part;
+ break;
+ }
+ case clause_part::kind_param:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ // Add the conversion expression, if any.
+ //
+ string::size_type p (0);
+ if (!i->part.empty ())
+ {
+ p = i->part.find ("(?)");
+ r.append (i->part, 0, p);
+ }
+
+ r += '?';
+
+ if (!i->part.empty ())
+ r.append (i->part, p + 3, string::npos);
+
+ break;
+ }
+ case clause_part::kind_native:
+ {
+ // We don't want extra spaces after '(' as well as before ','
+ // and ')'.
+ //
+ const string& p (i->part);
+ char first (!p.empty () ? p[0] : ' ');
+
+ if (last != ' ' && last != '\n' && last != '(' &&
+ first != ' ' && first != '\n' && first != ',' && first != ')')
+ r += ' ';
+
+ r += p;
+ break;
+ }
+ case clause_part::kind_bool:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ r += i->bool_part ? "TRUE" : "FALSE";
+ break;
+ }
+ }
+ }
+
+ return clause_prefix () + r;
+ }
+
+ query_base
+ operator&& (const query_base& x, const query_base& y)
+ {
+ // Optimize cases where one or both sides are constant truth.
+ //
+ bool xt (x.const_true ()), yt (y.const_true ());
+
+ if (xt && yt)
+ return x;
+
+ if (xt)
+ return y;
+
+ if (yt)
+ return x;
+
+ query_base r ("(");
+ r += x;
+ r += ") AND (";
+ r += y;
+ r += ")";
+ return r;
+ }
+
+ query_base
+ operator|| (const query_base& x, const query_base& y)
+ {
+ query_base r ("(");
+ r += x;
+ r += ") OR (";
+ r += y;
+ r += ")";
+ return r;
+ }
+
+ query_base
+ operator! (const query_base& x)
+ {
+ query_base r ("NOT (");
+ r += x;
+ r += ")";
+ return r;
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/query.hxx b/libodb-mysql/odb/mysql/query.hxx
new file mode 100644
index 0000000..d0972c9
--- /dev/null
+++ b/libodb-mysql/odb/mysql/query.hxx
@@ -0,0 +1,2277 @@
+// file : odb/mysql/query.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_QUERY_HXX
+#define ODB_MYSQL_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx> // odb::query_column
+#include <odb/query.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/traits.hxx>
+#include <odb/mysql/enum.hxx>
+#include <odb/mysql/binding.hxx>
+
+#include <odb/details/buffer.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/details/export.hxx>
+#include <odb/mysql/details/conversion.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ struct val_bind
+ {
+ typedef const T& type;
+
+ explicit
+ val_bind (type v): val (v) {}
+
+ type val;
+ };
+
+ template <typename T, std::size_t N>
+ struct val_bind<T[N]>
+ {
+ typedef const T* type;
+
+ explicit
+ val_bind (type v): val (v) {}
+
+ type val;
+ };
+
+ template <typename T>
+ struct ref_bind
+ {
+ typedef const T& type;
+
+ explicit
+ ref_bind (type r): ref (r) {}
+
+ const void*
+ ptr () const {return &ref;}
+
+ type ref;
+ };
+
+ template <typename T, std::size_t N>
+ struct ref_bind<T[N]>
+ {
+ typedef const T* type;
+
+ explicit
+ ref_bind (type r): ref (r) {}
+
+ // Allow implicit conversion from decayed ref_bind's.
+ //
+ ref_bind (ref_bind<T*> r): ref (r.ref) {}
+ ref_bind (ref_bind<const T*> r): ref (r.ref) {}
+
+ const void*
+ ptr () const {return ref;}
+
+ type ref;
+ };
+
+ template <typename T, database_type_id ID>
+ struct val_bind_typed: val_bind<T>
+ {
+ explicit
+ val_bind_typed (typename val_bind<T>::type v): val_bind<T> (v) {}
+ };
+
+ template <typename T, database_type_id ID>
+ struct ref_bind_typed: ref_bind<T>
+ {
+ explicit
+ ref_bind_typed (typename ref_bind<T>::type r): ref_bind<T> (r) {}
+ };
+
+ struct LIBODB_MYSQL_EXPORT query_param: details::shared_base
+ {
+ virtual
+ ~query_param ();
+
+ bool
+ reference () const
+ {
+ return value_ != 0;
+ }
+
+ virtual bool
+ init () = 0;
+
+ virtual void
+ bind (MYSQL_BIND*) = 0;
+
+ protected:
+ query_param (const void* value) : value_ (value) {}
+
+ protected:
+ const void* value_;
+ };
+
+ //
+ //
+ template <typename T, database_type_id ID>
+ struct query_column;
+
+ class LIBODB_MYSQL_EXPORT query_base
+ {
+ public:
+ struct clause_part
+ {
+ enum kind_type
+ {
+ kind_column,
+ kind_param,
+ kind_native,
+ kind_bool
+ };
+
+ clause_part (kind_type k): kind (k), bool_part (false) {}
+ clause_part (kind_type k, const std::string& p)
+ : kind (k), part (p), bool_part (false) {}
+ clause_part (bool p): kind (kind_bool), bool_part (p) {}
+
+ kind_type kind;
+ std::string part; // If kind is param, then part is conversion expr.
+ bool bool_part;
+ };
+
+ query_base ()
+ : binding_ (0, 0)
+ {
+ }
+
+ // True or false literal.
+ //
+ explicit
+ query_base (bool v)
+ : binding_ (0, 0)
+ {
+ append (v);
+ }
+
+ explicit
+ query_base (const char* native)
+ : binding_ (0, 0)
+ {
+ clause_.push_back (clause_part (clause_part::kind_native, native));
+ }
+
+ explicit
+ query_base (const std::string& native)
+ : binding_ (0, 0)
+ {
+ clause_.push_back (clause_part (clause_part::kind_native, native));
+ }
+
+ query_base (const char* table, const char* column)
+ : binding_ (0, 0)
+ {
+ append (table, column);
+ }
+
+ template <typename T>
+ explicit
+ query_base (val_bind<T> v)
+ : binding_ (0, 0)
+ {
+ *this += v;
+ }
+
+ template <typename T, database_type_id ID>
+ explicit
+ query_base (val_bind_typed<T, ID> v)
+ : binding_ (0, 0)
+ {
+ *this += v;
+ }
+
+ template <typename T>
+ explicit
+ query_base (ref_bind<T> r)
+ : binding_ (0, 0)
+ {
+ *this += r;
+ }
+
+ template <typename T, database_type_id ID>
+ explicit
+ query_base (ref_bind_typed<T, ID> r)
+ : binding_ (0, 0)
+ {
+ *this += r;
+ }
+
+ template <database_type_id ID>
+ query_base (const query_column<bool, ID>&);
+
+ // Translate common query representation to MySQL native. Defined
+ // in query-dynamic.cxx
+ //
+ query_base (const odb::query_base&);
+
+ // Copy c-tor and assignment.
+ //
+ query_base (const query_base&);
+
+ query_base&
+ operator= (const query_base&);
+
+ public:
+ std::string
+ clause () const;
+
+ const char*
+ clause_prefix () const;
+
+ // Initialize the by-reference parameters from bound variables.
+ //
+ void
+ init_parameters () const;
+
+ binding&
+ parameters_binding () const;
+
+ public:
+ bool
+ empty () const
+ {
+ return clause_.empty ();
+ }
+
+ static const query_base true_expr;
+
+ bool
+ const_true () const
+ {
+ return clause_.size () == 1 &&
+ clause_.front ().kind == clause_part::kind_bool &&
+ clause_.front ().bool_part;
+ }
+
+ void
+ optimize ();
+
+ public:
+ template <typename T>
+ static val_bind<T>
+ _val (const T& x)
+ {
+ return val_bind<T> (x);
+ }
+
+ template <database_type_id ID, typename T>
+ static val_bind_typed<T, ID>
+ _val (const T& x)
+ {
+ return val_bind_typed<T, ID> (x);
+ }
+
+ template <typename T>
+ static ref_bind<T>
+ _ref (const T& x)
+ {
+ return ref_bind<T> (x);
+ }
+
+ template <database_type_id ID, typename T>
+ static ref_bind_typed<T, ID>
+ _ref (const T& x)
+ {
+ return ref_bind_typed<T, ID> (x);
+ }
+
+ // Some compilers (notably VC++), when deducing const T& from const
+ // array do not strip const from the array type. As a result, in the
+ // above signatures we get, for example, T = const char[4] instead
+ // of T = char[4], which is what we want. So to "fix" such compilers,
+ // we will have to provide the following specializations of the above
+ // functions.
+ //
+ template <typename T, std::size_t N>
+ static val_bind<T[N]>
+ _val (const T (&x) [N])
+ {
+ return val_bind<T[N]> (x);
+ }
+
+ template <database_type_id ID, typename T, std::size_t N>
+ static val_bind_typed<T[N], ID>
+ _val (const T (&x) [N])
+ {
+ return val_bind_typed<T[N], ID> (x);
+ }
+
+ template <typename T, std::size_t N>
+ static ref_bind<T[N]>
+ _ref (const T (&x) [N])
+ {
+ return ref_bind<T[N]> (x);
+ }
+
+ template <database_type_id ID, typename T, std::size_t N>
+ static ref_bind_typed<T[N], ID>
+ _ref (const T (&x) [N])
+ {
+ return ref_bind_typed<T[N], ID> (x);
+ }
+
+ public:
+ query_base&
+ operator+= (const query_base&);
+
+ query_base&
+ operator+= (const std::string& q)
+ {
+ append (q);
+ return *this;
+ }
+
+ template <typename T>
+ query_base&
+ operator+= (val_bind<T> v)
+ {
+ append<T, type_traits<T>::db_type_id> (
+ v, details::conversion<T>::to ());
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (val_bind_typed<T, ID> v)
+ {
+ // We are not using default type_traits so no default conversion
+ // either.
+ //
+ append<T, ID> (v, 0);
+ return *this;
+ }
+
+ template <typename T>
+ query_base&
+ operator+= (ref_bind<T> r)
+ {
+ append<T, type_traits<T>::db_type_id> (
+ r, details::conversion<T>::to ());
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (ref_bind_typed<T, ID> r)
+ {
+ // We are not using default type_traits so no default conversion
+ // either.
+ //
+ append<T, ID> (r, 0);
+ return *this;
+ }
+
+ // Implementation details.
+ //
+ public:
+ template <typename T, database_type_id ID>
+ void
+ append (val_bind<T>, const char* conv);
+
+ template <typename T, database_type_id ID>
+ void
+ append (ref_bind<T>, const char* conv);
+
+ void
+ append (details::shared_ptr<query_param>, const char* conv);
+
+ void
+ append (bool v)
+ {
+ clause_.push_back (clause_part (v));
+ }
+
+ void
+ append (const std::string& native);
+
+ void
+ append (const char* native) // Clashes with append(bool).
+ {
+ append (std::string (native));
+ }
+
+ void
+ append (const char* table, const char* column);
+
+ private:
+ typedef std::vector<clause_part> clause_type;
+ typedef std::vector<details::shared_ptr<query_param> > parameters_type;
+
+ clause_type clause_;
+ parameters_type parameters_;
+ mutable std::vector<MYSQL_BIND> bind_;
+ mutable binding binding_;
+ };
+
+ inline query_base
+ operator+ (const query_base& x, const query_base& y)
+ {
+ query_base r (x);
+ r += y;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, val_bind<T> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (val_bind<T> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, val_bind_typed<T, ID> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (val_bind_typed<T, ID> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, ref_bind<T> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (ref_bind<T> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, ref_bind_typed<T, ID> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (ref_bind_typed<T, ID> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const query_base& q, const std::string& s)
+ {
+ query_base r (q);
+ r += s;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const std::string& s, const query_base& q)
+ {
+ query_base r (s);
+ r += q;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, val_bind<T> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (val_bind<T> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, val_bind_typed<T, ID> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (val_bind_typed<T, ID> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, ref_bind<T> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (ref_bind<T> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, ref_bind_typed<T, ID> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (ref_bind_typed<T, ID> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ LIBODB_MYSQL_EXPORT query_base
+ operator&& (const query_base& x, const query_base& y);
+
+ LIBODB_MYSQL_EXPORT query_base
+ operator|| (const query_base& x, const query_base& y);
+
+ LIBODB_MYSQL_EXPORT query_base
+ operator! (const query_base& x);
+
+ // query_column
+ //
+ struct LIBODB_MYSQL_EXPORT query_column_base
+ {
+ // Note that we keep shallow copies of the table, column, and conversion
+ // expression. The latter can be NULL.
+ //
+ query_column_base (const char* table,
+ const char* column,
+ const char* conv)
+ : table_ (table), column_ (column), conversion_ (conv)
+ {
+ }
+
+ const char*
+ table () const
+ {
+ return table_;
+ }
+
+ const char*
+ column () const
+ {
+ return column_;
+ }
+
+ // Can be NULL.
+ //
+ const char*
+ conversion () const
+ {
+ return conversion_;
+ }
+
+ protected:
+ const char* table_;
+ const char* column_;
+ const char* conversion_;
+ };
+
+ template <typename T, database_type_id ID>
+ struct query_column: query_column_base
+ {
+ typedef typename decay_traits<T>::type decayed_type;
+
+ // Note that we keep shallow copies of the table, column, and conversion
+ // expression. The latter can be NULL.
+ //
+ query_column (const char* table, const char* column, const char* conv)
+ : query_column_base (table, column, conv) {}
+
+ // Implementation is in query-dynamic.ixx.
+ //
+ query_column (odb::query_column<T>&,
+ const char* table, const char* column, const char* conv);
+
+ // is_null, is_not_null
+ //
+ public:
+ query_base
+ is_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NULL";
+ return q;
+ }
+
+ query_base
+ is_not_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NOT NULL";
+ return q;
+ }
+
+ // in
+ //
+ public:
+ query_base
+ in (decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type, decayed_type,
+ decayed_type) const;
+
+ template <typename I>
+ query_base
+ in_range (I begin, I end) const;
+
+ // like
+ //
+ public:
+ query_base
+ like (decayed_type pattern) const
+ {
+ return like (val_bind<T> (pattern));
+ }
+
+ query_base
+ like (val_bind<T> pattern) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern) const
+ {
+ return like (val_bind<T> (decayed_type (pattern.val)));
+ }
+
+ query_base
+ like (ref_bind<T> pattern) const;
+
+ query_base
+ like (decayed_type pattern, decayed_type escape) const
+ {
+ return like (val_bind<T> (pattern), escape);
+ }
+
+ query_base
+ like (val_bind<T> pattern, decayed_type escape) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern, decayed_type escape) const
+ {
+ return like (val_bind<T> (decayed_type (pattern.val)), escape);
+ }
+
+ query_base
+ like (ref_bind<T> pattern, decayed_type escape) const;
+
+ // =
+ //
+ public:
+ query_base
+ equal (decayed_type v) const
+ {
+ return equal (val_bind<T> (v));
+ }
+
+ query_base
+ equal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ equal (val_bind<T2> v) const
+ {
+ return equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ equal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator== (const query_column& c, decayed_type v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (decayed_type v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, val_bind<T> v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (val_bind<T> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (const query_column& c, val_bind<T2> v)
+ {
+ return c.equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (val_bind<T2> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, ref_bind<T> r)
+ {
+ return c.equal (r);
+ }
+
+ friend query_base
+ operator== (ref_bind<T> r, const query_column& c)
+ {
+ return c.equal (r);
+ }
+
+ // !=
+ //
+ public:
+ query_base
+ unequal (decayed_type v) const
+ {
+ return unequal (val_bind<T> (v));
+ }
+
+ query_base
+ unequal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "!=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ unequal (val_bind<T2> v) const
+ {
+ return unequal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ unequal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "!=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator!= (const query_column& c, decayed_type v)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (decayed_type v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (const query_column& c, val_bind<T> v)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (val_bind<T> v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (const query_column& c, val_bind<T2> v)
+ {
+ return c.unequal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (val_bind<T2> v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (const query_column& c, ref_bind<T> r)
+ {
+ return c.unequal (r);
+ }
+
+ friend query_base
+ operator!= (ref_bind<T> r, const query_column& c)
+ {
+ return c.unequal (r);
+ }
+
+ // <
+ //
+ public:
+ query_base
+ less (decayed_type v) const
+ {
+ return less (val_bind<T> (v));
+ }
+
+ query_base
+ less (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "<";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ less (val_bind<T2> v) const
+ {
+ return less (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ less (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "<";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator< (const query_column& c, decayed_type v)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator< (decayed_type v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator< (const query_column& c, val_bind<T> v)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator< (val_bind<T> v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (const query_column& c, val_bind<T2> v)
+ {
+ return c.less (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator< (const query_column& c, ref_bind<T> r)
+ {
+ return c.less (r);
+ }
+
+ friend query_base
+ operator< (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater (r);
+ }
+
+ // >
+ //
+ public:
+ query_base
+ greater (decayed_type v) const
+ {
+ return greater (val_bind<T> (v));
+ }
+
+ query_base
+ greater (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += ">";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ greater (val_bind<T2> v) const
+ {
+ return greater (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ greater (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += ">";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator> (const query_column& c, decayed_type v)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator> (decayed_type v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator> (const query_column& c, val_bind<T> v)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator> (val_bind<T> v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (val_bind<T2> v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator> (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater (r);
+ }
+
+ friend query_base
+ operator> (ref_bind<T> r, const query_column& c)
+ {
+ return c.less (r);
+ }
+
+ // <=
+ //
+ public:
+ query_base
+ less_equal (decayed_type v) const
+ {
+ return less_equal (val_bind<T> (v));
+ }
+
+ query_base
+ less_equal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "<=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ less_equal (val_bind<T2> v) const
+ {
+ return less_equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ less_equal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "<=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator<= (const query_column& c, decayed_type v)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator<= (decayed_type v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator<= (const query_column& c, val_bind<T> v)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator<= (val_bind<T> v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (const query_column& c, val_bind<T2> v)
+ {
+ return c.less_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator<= (const query_column& c, ref_bind<T> r)
+ {
+ return c.less_equal (r);
+ }
+
+ friend query_base
+ operator<= (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater_equal (r);
+ }
+
+ // >=
+ //
+ public:
+ query_base
+ greater_equal (decayed_type v) const
+ {
+ return greater_equal (val_bind<T> (v));
+ }
+
+ query_base
+ greater_equal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += ">=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ greater_equal (val_bind<T2> v) const
+ {
+ return greater_equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ greater_equal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += ">=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator>= (const query_column& c, decayed_type v)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator>= (decayed_type v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator>= (const query_column& c, val_bind<T> v)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator>= (val_bind<T> v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (val_bind<T2> v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator>= (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater_equal (r);
+ }
+
+ friend query_base
+ operator>= (ref_bind<T> r, const query_column& c)
+ {
+ return c.less_equal (r);
+ }
+
+ // Column comparison.
+ //
+ public:
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator== (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () ==
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator!= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () !=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "!=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator< (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "<";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator> (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += ">";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator<= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "<=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator>= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += ">=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+ };
+
+ // Provide operator+() for using columns to construct native
+ // query fragments (e.g., ORDER BY).
+ //
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_column<T, ID>& c, const std::string& s)
+ {
+ query_base q (c.table (), c.column ());
+ q += s;
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, const query_column<T, ID>& c)
+ {
+ query_base q (s);
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_column<T, ID>& c, const query_base& q)
+ {
+ query_base r (c.table (), c.column ());
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, const query_column<T, ID>& c)
+ {
+ query_base r (q);
+ r.append (c.table (), c.column ());
+ return r;
+ }
+
+ //
+ //
+ template <typename T, database_type_id>
+ struct query_param_impl;
+
+ // TINY
+ //
+ template <typename T>
+ struct query_param_impl<T, id_tiny>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_TINY;
+ b->is_unsigned = false;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_tiny>::set_image (image_, is_null, v);
+ }
+
+ private:
+ signed char image_;
+ };
+
+ template <typename T>
+ struct query_param_impl<T, id_utiny>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_TINY;
+ b->is_unsigned = true;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_utiny>::set_image (image_, is_null, v);
+ }
+
+ private:
+ unsigned char image_;
+ };
+
+ // SHORT
+ //
+ template <typename T>
+ struct query_param_impl<T, id_short>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_SHORT;
+ b->is_unsigned = false;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_short>::set_image (image_, is_null, v);
+ }
+
+ private:
+ short image_;
+ };
+
+ template <typename T>
+ struct query_param_impl<T, id_ushort>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_SHORT;
+ b->is_unsigned = true;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_ushort>::set_image (image_, is_null, v);
+ }
+
+ private:
+ unsigned short image_;
+ };
+
+ // LONG
+ //
+ template <typename T>
+ struct query_param_impl<T, id_long>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_LONG;
+ b->is_unsigned = false;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_long>::set_image (image_, is_null, v);
+ }
+
+ private:
+ int image_;
+ };
+
+ template <typename T>
+ struct query_param_impl<T, id_ulong>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_LONG;
+ b->is_unsigned = true;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_ulong>::set_image (image_, is_null, v);
+ }
+
+ private:
+ unsigned int image_;
+ };
+
+ // LONGLONG
+ //
+ template <typename T>
+ struct query_param_impl<T, id_longlong>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_LONGLONG;
+ b->is_unsigned = false;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_longlong>::set_image (image_, is_null, v);
+ }
+
+ private:
+ long long image_;
+ };
+
+ template <typename T>
+ struct query_param_impl<T, id_ulonglong>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_LONGLONG;
+ b->is_unsigned = true;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_ulonglong>::set_image (image_, is_null, v);
+ }
+
+ private:
+ unsigned long long image_;
+ };
+
+ // FLOAT
+ //
+ template <typename T>
+ struct query_param_impl<T, id_float>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_FLOAT;
+ b->is_unsigned = false;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_float>::set_image (image_, is_null, v);
+ }
+
+ private:
+ float image_;
+ };
+
+ // DOUBLE
+ //
+ template <typename T>
+ struct query_param_impl<T, id_double>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_DOUBLE;
+ b->is_unsigned = false;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_double>::set_image (image_, is_null, v);
+ }
+
+ private:
+ double image_;
+ };
+
+ // DECIMAL
+ //
+ template <typename T>
+ struct query_param_impl<T, id_decimal>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_NEWDECIMAL;
+ b->buffer = buffer_.data ();
+ b->buffer_length = static_cast<unsigned long> (buffer_.capacity ());
+ b->length = &size_;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0), cap (buffer_.capacity ());
+ value_traits<T, id_decimal>::set_image (buffer_, size, is_null, v);
+ size_ = static_cast<unsigned long> (size);
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::buffer buffer_;
+ unsigned long size_;
+ };
+
+ // DATE
+ //
+ template <typename T>
+ struct query_param_impl<T, id_date>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_DATE;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_date>::set_image (image_, is_null, v);
+ }
+
+ private:
+ MYSQL_TIME image_;
+ };
+
+ // TIME
+ //
+ template <typename T>
+ struct query_param_impl<T, id_time>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_TIME;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_time>::set_image (image_, is_null, v);
+ }
+
+ private:
+ MYSQL_TIME image_;
+ };
+
+ // DATETIME
+ //
+ template <typename T>
+ struct query_param_impl<T, id_datetime>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_DATETIME;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_datetime>::set_image (image_, is_null, v);
+ }
+
+ private:
+ MYSQL_TIME image_;
+ };
+
+ // TIMESTAMP
+ //
+ template <typename T>
+ struct query_param_impl<T, id_timestamp>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_TIMESTAMP;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_timestamp>::set_image (image_, is_null, v);
+ }
+
+ private:
+ MYSQL_TIME image_;
+ };
+
+ // YEAR
+ //
+ template <typename T>
+ struct query_param_impl<T, id_year>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_SHORT;
+ b->is_unsigned = false;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_year>::set_image (image_, is_null, v);
+ }
+
+ private:
+ short image_;
+ };
+
+ // STRING
+ //
+ template <typename T>
+ struct query_param_impl<T, id_string>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_STRING;
+ b->buffer = buffer_.data ();
+ b->buffer_length = static_cast<unsigned long> (buffer_.capacity ());
+ b->length = &size_;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0), cap (buffer_.capacity ());
+ value_traits<T, id_string>::set_image (buffer_, size, is_null, v);
+ size_ = static_cast<unsigned long> (size);
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::buffer buffer_;
+ unsigned long size_;
+ };
+
+ // BLOB
+ //
+ template <typename T>
+ struct query_param_impl<T, id_blob>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_BLOB;
+ b->buffer = buffer_.data ();
+ b->buffer_length = static_cast<unsigned long> (buffer_.capacity ());
+ b->length = &size_;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0), cap (buffer_.capacity ());
+ value_traits<T, id_blob>::set_image (buffer_, size, is_null, v);
+ size_ = static_cast<unsigned long> (size);
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::buffer buffer_;
+ unsigned long size_;
+ };
+
+ // BIT
+ //
+ template <typename T>
+ struct query_param_impl<T, id_bit>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_BLOB;
+ b->buffer = buffer_;
+ b->buffer_length = static_cast<unsigned long> (sizeof (buffer_));
+ b->length = &size_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0);
+ value_traits<T, id_bit>::set_image (
+ buffer_, sizeof (buffer_), size, is_null, v);
+ size_ = static_cast<unsigned long> (size);
+ }
+
+ private:
+ // Max 64 bit.
+ //
+ unsigned char buffer_[8];
+ unsigned long size_;
+ };
+
+ // ENUM
+ //
+ // The image type can be either integer or string.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_enum>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ enum_traits::bind (*b, image_, size_, 0);
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ return enum_traits::set_image (image_, size_, is_null, v);
+ }
+
+ private:
+ typename value_traits<T, id_enum>::image_type image_;
+ unsigned long size_; // Keep size in case it is a string.
+ };
+
+ // SET
+ //
+ template <typename T>
+ struct query_param_impl<T, id_set>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_STRING;
+ b->buffer = buffer_.data ();
+ b->buffer_length = static_cast<unsigned long> (buffer_.capacity ());
+ b->length = &size_;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0), cap (buffer_.capacity ());
+ value_traits<T, id_set>::set_image (buffer_, size, is_null, v);
+ size_ = static_cast<unsigned long> (size);
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::buffer buffer_;
+ unsigned long size_;
+ };
+ }
+}
+
+// odb::mysql::query and odb::query specialization for MySQL.
+//
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ class query: public query_base,
+ public query_selector<T, id_mysql>::columns_type
+ {
+ public:
+ // We don't define any typedefs here since they may clash with
+ // column names defined by our base type.
+ //
+
+ query ()
+ {
+ }
+
+ explicit
+ query (bool v)
+ : query_base (v)
+ {
+ }
+
+ explicit
+ query (const char* q)
+ : query_base (q)
+ {
+ }
+
+ explicit
+ query (const std::string& q)
+ : query_base (q)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (val_bind<T2> v)
+ : query_base (v)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (ref_bind<T2> r)
+ : query_base (r)
+ {
+ }
+
+ query (const query_base& q)
+ : query_base (q)
+ {
+ }
+
+ template <database_type_id ID>
+ query (const query_column<bool, ID>& qc)
+ : query_base (qc)
+ {
+ }
+
+ query (const odb::query_base& q)
+ : query_base (q)
+ {
+ }
+ };
+
+ namespace core
+ {
+ using mysql::query;
+ }
+ }
+
+ // Derive odb::query from odb::mysql::query so that it can be
+ // implicitly converted in mysql::database::query() calls.
+ //
+ template <typename T>
+ class query<T, mysql::query_base>: public mysql::query<T>
+ {
+ public:
+ // We don't define any typedefs here since they may clash with
+ // column names defined by our base type.
+ //
+
+ query ()
+ {
+ }
+
+ explicit
+ query (bool v)
+ : mysql::query<T> (v)
+ {
+ }
+
+ explicit
+ query (const char* q)
+ : mysql::query<T> (q)
+ {
+ }
+
+ explicit
+ query (const std::string& q)
+ : mysql::query<T> (q)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (mysql::val_bind<T2> v)
+ : mysql::query<T> (v)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (mysql::ref_bind<T2> r)
+ : mysql::query<T> (r)
+ {
+ }
+
+ query (const mysql::query_base& q)
+ : mysql::query<T> (q)
+ {
+ }
+
+ template <mysql::database_type_id ID>
+ query (const mysql::query_column<bool, ID>& qc)
+ : mysql::query<T> (qc)
+ {
+ }
+ };
+}
+
+#include <odb/mysql/query.ixx>
+#include <odb/mysql/query.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_QUERY_HXX
diff --git a/libodb-mysql/odb/mysql/query.ixx b/libodb-mysql/odb/mysql/query.ixx
new file mode 100644
index 0000000..ea1ec03
--- /dev/null
+++ b/libodb-mysql/odb/mysql/query.ixx
@@ -0,0 +1,34 @@
+// file : odb/mysql/query.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mysql
+ {
+ inline binding& query_base::
+ parameters_binding () const
+ {
+ return binding_;
+ }
+
+ template <typename T, database_type_id ID>
+ inline void query_base::
+ append (val_bind<T> v, const char* conv)
+ {
+ append (
+ details::shared_ptr<query_param> (
+ new (details::shared) query_param_impl<T, ID> (v)),
+ conv);
+ }
+
+ template <typename T, database_type_id ID>
+ inline void query_base::
+ append (ref_bind<T> r, const char* conv)
+ {
+ append (
+ details::shared_ptr<query_param> (
+ new (details::shared) query_param_impl<T, ID> (r)),
+ conv);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/query.txx b/libodb-mysql/odb/mysql/query.txx
new file mode 100644
index 0000000..e09ed85
--- /dev/null
+++ b/libodb-mysql/odb/mysql/query.txx
@@ -0,0 +1,168 @@
+// file : odb/mysql/query.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mysql
+ {
+ //
+ // query_base
+ //
+
+ template <database_type_id ID>
+ query_base::
+ query_base (const query_column<bool, ID>& c)
+ : binding_ (0, 0)
+ {
+ // Cannot use IS TRUE here since database type can be a non-
+ // integral type.
+ //
+ append (c.table (), c.column ());
+ append ("=");
+ append<bool, ID> (val_bind<bool> (true), c.conversion ());
+ }
+
+ //
+ // query_column
+ //
+
+ // in
+ //
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3,
+ decayed_type v4) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v4), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3, decayed_type v4,
+ decayed_type v5) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v4), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v5), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ template <typename I>
+ query_base query_column<T, ID>::
+ in_range (I begin, I end) const
+ {
+ if (begin != end)
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+
+ for (I i (begin); i != end; ++i)
+ {
+ if (i != begin)
+ q += ",";
+
+ q.append<T, ID> (val_bind<T> (*i), conversion_);
+ }
+
+ q += ")";
+ return q;
+ }
+ else
+ return query_base (false);
+ }
+
+ // like
+ //
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (val_bind<T> p) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (ref_bind<T> p) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (val_bind<T> p, decayed_type e) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ q += "ESCAPE";
+ q.append<T, ID> (val_bind<T> (e), conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (ref_bind<T> p, decayed_type e) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ q += "ESCAPE";
+ q.append<T, ID> (val_bind<T> (e), conversion_);
+ return q;
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/section-statements.hxx b/libodb-mysql/odb/mysql/section-statements.hxx
new file mode 100644
index 0000000..a88683f
--- /dev/null
+++ b/libodb-mysql/odb/mysql/section-statements.hxx
@@ -0,0 +1,200 @@
+// file : odb/mysql/section-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_SECTION_STATEMENTS_HXX
+#define ODB_MYSQL_SECTION_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/binding.hxx>
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class connection;
+
+ // Template argument is the section traits type.
+ //
+ template <typename T, typename ST>
+ class section_statements
+ {
+ public:
+ typedef ST traits;
+
+ typedef typename traits::image_type image_type;
+ typedef typename traits::id_image_type id_image_type;
+
+ typedef mysql::select_statement select_statement_type;
+ typedef mysql::update_statement update_statement_type;
+
+ typedef mysql::connection connection_type;
+
+ section_statements (connection_type&,
+ image_type&, id_image_type&,
+ binding& id, binding& idv);
+
+ connection_type&
+ connection () {return conn_;}
+
+ const schema_version_migration&
+ version_migration (const char* name = "") const
+ {
+ if (svm_ == 0)
+ svm_ = &conn_.database ().schema_version_migration (name);
+
+ return *svm_;
+ }
+
+ image_type&
+ image () {return image_;}
+
+ const binding&
+ id_binding () {return id_binding_;}
+
+ // Id and optimistic concurrency version (if any).
+ //
+ const binding&
+ idv_binding () {return idv_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ my_bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_binding_version () const { return update_id_binding_version_;}
+
+ void
+ update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ //
+ // Statements.
+ //
+
+ select_statement_type&
+ select_statement ()
+ {
+ if (select_ == 0)
+ select_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ traits::select_statement,
+ traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ id_binding_,
+ select_image_binding_,
+ false));
+
+ return *select_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ traits::update_statement,
+ traits::versioned, // Process if versioned.
+ update_image_binding_,
+ false));
+
+ return *update_;
+ }
+
+ public:
+ static const std::size_t id_column_count = traits::id_column_count;
+ static const std::size_t managed_optimistic_load_column_count =
+ traits::managed_optimistic_load_column_count;
+ static const std::size_t managed_optimistic_update_column_count =
+ traits::managed_optimistic_update_column_count;
+ static const std::size_t select_column_count = traits::load_column_count;
+ static const std::size_t update_column_count =
+ traits::update_column_count;
+
+ private:
+ section_statements (const section_statements&);
+ section_statements& operator= (const section_statements&);
+
+ protected:
+ connection_type& conn_;
+ mutable const schema_version_migration* svm_;
+
+ // These come from object_statements.
+ //
+ image_type& image_;
+ binding& id_binding_;
+ binding& idv_binding_;
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+
+ static const std::size_t select_bind_count =
+ select_column_count != 0 || managed_optimistic_load_column_count != 0
+ ? select_column_count + managed_optimistic_load_column_count
+ : 1;
+
+ binding select_image_binding_;
+ MYSQL_BIND select_image_bind_[select_bind_count];
+ my_bool select_image_truncated_[select_bind_count];
+
+ // Update binding.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_binding_version_;
+
+ static const std::size_t update_bind_count =
+ update_column_count != 0 || managed_optimistic_update_column_count != 0
+ ? update_column_count + id_column_count +
+ managed_optimistic_update_column_count
+ : 1;
+
+ binding update_image_binding_;
+ MYSQL_BIND update_image_bind_[update_bind_count];
+
+ details::shared_ptr<select_statement_type> select_;
+ details::shared_ptr<update_statement_type> update_;
+ };
+ }
+}
+
+#include <odb/mysql/section-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_SECTION_STATEMENTS_HXX
diff --git a/libodb-mysql/odb/mysql/section-statements.txx b/libodb-mysql/odb/mysql/section-statements.txx
new file mode 100644
index 0000000..5c0672e
--- /dev/null
+++ b/libodb-mysql/odb/mysql/section-statements.txx
@@ -0,0 +1,40 @@
+// file : odb/mysql/section-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T, typename ST>
+ section_statements<T, ST>::
+ section_statements (connection_type& conn,
+ image_type& im, id_image_type&,
+ binding& id, binding& idv)
+ : conn_ (conn),
+ svm_ (0),
+ image_ (im),
+ id_binding_ (id),
+ idv_binding_ (idv),
+ select_image_binding_ (select_image_bind_,
+ select_column_count +
+ managed_optimistic_load_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count +
+ managed_optimistic_update_column_count)
+ {
+ select_image_version_ = 0;
+ update_image_version_ = 0;
+ update_id_binding_version_ = 0;
+
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+
+ for (std::size_t i (0); i < select_bind_count; ++i)
+ select_image_bind_[i].error = select_image_truncated_ + i;
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/simple-object-result.hxx b/libodb-mysql/odb/mysql/simple-object-result.hxx
new file mode 100644
index 0000000..ae0848c
--- /dev/null
+++ b/libodb-mysql/odb/mysql/simple-object-result.hxx
@@ -0,0 +1,85 @@
+// file : odb/mysql/simple-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_SIMPLE_OBJECT_RESULT_HXX
+#define ODB_MYSQL_SIMPLE_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/simple-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx> // query_base
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ class object_result_impl: public odb::object_result_impl<T>
+ {
+ public:
+ typedef odb::object_result_impl<T> base_type;
+
+ typedef typename base_type::id_type id_type;
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_mysql> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~object_result_impl ();
+
+ object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type&, bool fetch);
+
+ virtual id_type
+ load_id ();
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ void
+ fetch (bool next = true);
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ std::size_t count_;
+ };
+ }
+}
+
+#include <odb/mysql/simple-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_SIMPLE_OBJECT_RESULT_HXX
diff --git a/libodb-mysql/odb/mysql/simple-object-result.txx b/libodb-mysql/odb/mysql/simple-object-result.txx
new file mode 100644
index 0000000..5c6e198
--- /dev/null
+++ b/libodb-mysql/odb/mysql/simple-object-result.txx
@@ -0,0 +1,235 @@
+// file : odb/mysql/simple-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/mysql/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ object_result_impl<T>::
+ ~object_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ object_result_impl<T>::
+ object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ count_ (0)
+ {
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ load (object_type& obj, bool f)
+ {
+ if (count_ > statement_->fetched ())
+ fetch ();
+ else if (f && statement_->cached ())
+ {
+ // We have to re-load the image in case it has been overwritten
+ // between the last time we fetched and this call to load().
+ //
+ fetch (false);
+ }
+
+ // This is a top-level call so the statements cannot be locked.
+ //
+ assert (!statements_.locked ());
+ typename statements_type::auto_lock l (statements_);
+
+ object_traits::callback (this->db_, obj, callback_event::pre_load);
+
+ typename object_traits::image_type& i (statements_.image ());
+ tc_.init (obj, i, &this->db_);
+
+ // Initialize the id image and binding and load the rest of the object
+ // (containers, etc).
+ //
+ typename object_traits::id_image_type& idi (statements_.id_image ());
+ object_traits::init (idi, object_traits::id (i));
+
+ binding& idb (statements_.id_image_binding ());
+ if (idi.version != statements_.id_image_version () || idb.version == 0)
+ {
+ object_traits::bind (idb.bind, idi);
+ statements_.id_image_version (idi.version);
+ idb.version++;
+ }
+
+ tc_.load_ (statements_, obj, false);
+ statements_.load_delayed (tc_.version ());
+ l.unlock ();
+ object_traits::callback (this->db_, obj, callback_event::post_load);
+ }
+
+ template <typename T>
+ typename object_result_impl<T>::id_type
+ object_result_impl<T>::
+ load_id ()
+ {
+ if (count_ > statement_->fetched ())
+ fetch ();
+ else if (statement_->cached ())
+ {
+ // We have to re-load the image in case it has been overwritten
+ // between the last time we fetched and this call to load_id().
+ //
+ fetch (false);
+ }
+
+ return object_traits::id (statements_.image ());
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (this->end_)
+ return;
+
+ // If we are cached, simply increment the position and
+ // postpone the actual row fetching until later. This way
+ // if the same object is loaded in between iteration, the
+ // image won't be messed up.
+ //
+ count_++;
+
+ if (statement_->cached ())
+ this->end_ = count_ > statement_->result_size ();
+ else
+ fetch ();
+
+ if (this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ fetch (bool next)
+ {
+ // If the result is cached, the image can grow between calls
+ // to fetch() as a result of other statements execution.
+ //
+ if (statement_->cached ())
+ {
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ }
+ }
+
+ while (!this->end_ && (!next || count_ > statement_->fetched ()))
+ {
+ select_statement::result r (statement_->fetch (next));
+
+ switch (r)
+ {
+ case select_statement::truncated:
+ {
+ // Don't re-fetch data we are skipping.
+ //
+ if (next && count_ != statement_->fetched ())
+ continue;
+
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (tc_.grow (im, statements_.select_image_truncated ()))
+ im.version++;
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ statement_->refetch ();
+ }
+ // Fall throught.
+ }
+ case select_statement::success:
+ {
+ break;
+ }
+ case select_statement::no_data:
+ {
+ this->end_ = true;
+ break;
+ }
+ }
+
+ // If we are refetching the current row, then we are done.
+ //
+ if (!next)
+ break;
+ }
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ cache ()
+ {
+ if (!this->end_ && !statement_->cached ())
+ {
+ statement_->cache ();
+
+ if (count_ == statement_->result_size ())
+ {
+ statement_->free_result ();
+ count_++; // One past the result size.
+ this->end_ = true;
+ }
+ }
+ }
+
+ template <typename T>
+ std::size_t object_result_impl<T>::
+ size ()
+ {
+ if (!this->end_)
+ {
+ if (!statement_->cached ())
+ throw result_not_cached ();
+
+ return statement_->result_size ();
+ }
+ else
+ return count_ - 1; // One past the result size.
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/simple-object-statements.cxx b/libodb-mysql/odb/mysql/simple-object-statements.cxx
new file mode 100644
index 0000000..af0df6b
--- /dev/null
+++ b/libodb-mysql/odb/mysql/simple-object-statements.cxx
@@ -0,0 +1,15 @@
+// file : odb/mysql/simple-object-statements.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/mysql/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ object_statements_base::
+ ~object_statements_base ()
+ {
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/simple-object-statements.hxx b/libodb-mysql/odb/mysql/simple-object-statements.hxx
new file mode 100644
index 0000000..5292b66
--- /dev/null
+++ b/libodb-mysql/odb/mysql/simple-object-statements.hxx
@@ -0,0 +1,587 @@
+// file : odb/mysql/simple-object-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_SIMPLE_OBJECT_STATEMENTS_HXX
+#define ODB_MYSQL_SIMPLE_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cassert>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/binding.hxx>
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/statements-base.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ // The extra_statement_cache class is only defined (and used) in
+ // the generated source file. However, object_statements may be
+ // referenced from another source file in the case of a polymorphic
+ // hierarchy (though in this case the extra statement cache is
+ // not used). As a result, we cannot have a by-value member and
+ // instead will store a pointer and lazily allocate the cache if
+ // and when needed. We will also need to store a pointer to the
+ // deleter function which will be initialized during allocation
+ // (at that point we know that the cache class is defined).
+ //
+ template <typename T, typename I, typename ID>
+ struct extra_statement_cache_ptr
+ {
+ typedef I image_type;
+ typedef ID id_image_type;
+ typedef mysql::connection connection_type;
+
+ extra_statement_cache_ptr (): p_ (0) {}
+ ~extra_statement_cache_ptr ()
+ {
+ if (p_ != 0)
+ (this->*deleter_) (0, 0, 0, 0, 0);
+ }
+
+ T&
+ get (connection_type& c,
+ image_type& im, id_image_type& idim,
+ binding& id, binding* idv)
+ {
+ if (p_ == 0)
+ allocate (&c, &im, &idim, &id, (idv != 0 ? idv : &id));
+
+ return *p_;
+ }
+
+ private:
+ void
+ allocate (connection_type*,
+ image_type*, id_image_type*,
+ binding*, binding*);
+
+ private:
+ T* p_;
+ void (extra_statement_cache_ptr::*deleter_) (
+ connection_type*, image_type*, id_image_type*, binding*, binding*);
+ };
+
+ template <typename T, typename I, typename ID>
+ void extra_statement_cache_ptr<T, I, ID>::
+ allocate (connection_type* c,
+ image_type* im, id_image_type* idim,
+ binding* id, binding* idv)
+ {
+ // To reduce object code size, this function acts as both allocator
+ // and deleter.
+ //
+ if (p_ == 0)
+ {
+ p_ = new T (*c, *im, *idim, *id, *idv);
+ deleter_ = &extra_statement_cache_ptr<T, I, ID>::allocate;
+ }
+ else
+ delete p_;
+ }
+
+ //
+ // Implementation for objects with object id.
+ //
+
+ class LIBODB_MYSQL_EXPORT object_statements_base: public statements_base
+ {
+ // Locking.
+ //
+ public:
+ void
+ lock ()
+ {
+ assert (!locked_);
+ locked_ = true;
+ }
+
+ void
+ unlock ()
+ {
+ assert (locked_);
+ locked_ = false;
+ }
+
+ bool
+ locked () const
+ {
+ return locked_;
+ }
+
+ struct auto_unlock
+ {
+ // Unlocks the statement on construction and re-locks it on
+ // destruction.
+ //
+ auto_unlock (object_statements_base&);
+ ~auto_unlock ();
+
+ private:
+ auto_unlock (const auto_unlock&);
+ auto_unlock& operator= (const auto_unlock&);
+
+ private:
+ object_statements_base& s_;
+ };
+
+ public:
+ virtual
+ ~object_statements_base ();
+
+ protected:
+ object_statements_base (connection_type& conn)
+ : statements_base (conn), locked_ (false)
+ {
+ }
+
+ protected:
+ bool locked_;
+ };
+
+ template <typename T, bool optimistic>
+ struct optimistic_data;
+
+ template <typename T>
+ struct optimistic_data<T, true>
+ {
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_mysql> object_traits;
+
+ optimistic_data (MYSQL_BIND*);
+
+ binding*
+ id_image_binding () {return &id_image_binding_;}
+
+ // The id + optimistic column binding.
+ //
+ binding id_image_binding_;
+
+ details::shared_ptr<delete_statement> erase_;
+ };
+
+ template <typename T>
+ struct optimistic_data<T, false>
+ {
+ optimistic_data (MYSQL_BIND*) {}
+
+ binding*
+ id_image_binding () {return 0;}
+ };
+
+ template <typename T>
+ class object_statements: public object_statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_mysql> object_traits;
+ typedef typename object_traits::id_type id_type;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::image_type image_type;
+ typedef typename object_traits::id_image_type id_image_type;
+
+ typedef
+ typename object_traits::pointer_cache_traits
+ pointer_cache_traits;
+
+ typedef
+ typename object_traits::extra_statement_cache_type
+ extra_statement_cache_type;
+
+ typedef mysql::insert_statement insert_statement_type;
+ typedef mysql::select_statement select_statement_type;
+ typedef mysql::update_statement update_statement_type;
+ typedef mysql::delete_statement delete_statement_type;
+
+ // Automatic lock.
+ //
+ struct auto_lock
+ {
+ // Lock the statements unless they are already locked in which
+ // case subsequent calls to locked() will return false.
+ //
+ auto_lock (object_statements&);
+
+ // Unlock the statemens if we are holding the lock and clear
+ // the delayed loads. This should only happen in case an
+ // exception is thrown. In normal circumstances, the user
+ // should call unlock() explicitly.
+ //
+ ~auto_lock ();
+
+ // Return true if this auto_lock instance holds the lock.
+ //
+ bool
+ locked () const;
+
+ // Unlock the statemens.
+ //
+ void
+ unlock ();
+
+ private:
+ auto_lock (const auto_lock&);
+ auto_lock& operator= (const auto_lock&);
+
+ private:
+ object_statements& s_;
+ bool locked_;
+ };
+
+ public:
+ object_statements (connection_type&);
+
+ virtual
+ ~object_statements ();
+
+ // Delayed loading.
+ //
+ typedef void (*loader_function) (odb::database&,
+ const id_type&,
+ object_type&,
+ const schema_version_migration*);
+
+ void
+ delay_load (const id_type& id,
+ object_type& obj,
+ const typename pointer_cache_traits::position_type& p,
+ loader_function l = 0)
+ {
+ delayed_.push_back (delayed_load (id, obj, p, l));
+ }
+
+ void
+ load_delayed (const schema_version_migration* svm)
+ {
+ assert (locked ());
+
+ if (!delayed_.empty ())
+ load_delayed_<object_statements> (svm);
+ }
+
+ void
+ clear_delayed ()
+ {
+ if (!delayed_.empty ())
+ clear_delayed_ ();
+ }
+
+ // Object image.
+ //
+ image_type&
+ image () {return image_;}
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_image_version () const { return update_id_image_version_;}
+
+ void
+ update_id_image_version (std::size_t v) {update_id_image_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ my_bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Object id image and binding.
+ //
+ id_image_type&
+ id_image () {return id_image_;}
+
+ std::size_t
+ id_image_version () const {return id_image_version_;}
+
+ void
+ id_image_version (std::size_t v) {id_image_version_ = v;}
+
+ binding&
+ id_image_binding () {return id_image_binding_;}
+
+ // Optimistic id + managed column image binding. It points to
+ // the same suffix as id binding and they are always updated
+ // at the same time.
+ //
+ binding&
+ optimistic_id_image_binding () {return od_.id_image_binding_;}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ (object_traits::auto_id ? &id_image_binding_ : 0),
+ false));
+
+ return *persist_;
+ }
+
+ select_statement_type&
+ find_statement ()
+ {
+ if (find_ == 0)
+ find_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ object_traits::find_statement,
+ object_traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ id_image_binding_,
+ select_image_binding_,
+ false));
+
+ return *find_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ object_traits::update_statement,
+ object_traits::versioned, // Process if versioned.
+ update_image_binding_,
+ false));
+
+ return *update_;
+ }
+
+ delete_statement_type&
+ erase_statement ()
+ {
+ if (erase_ == 0)
+ erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::erase_statement,
+ id_image_binding_,
+ false));
+
+ return *erase_;
+ }
+
+ delete_statement_type&
+ optimistic_erase_statement ()
+ {
+ if (od_.erase_ == 0)
+ {
+ od_.erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::optimistic_erase_statement,
+ od_.id_image_binding_,
+ false));
+ }
+
+ return *od_.erase_;
+ }
+
+ // Extra (container, section) statement cache.
+ //
+ extra_statement_cache_type&
+ extra_statement_cache ()
+ {
+ return extra_statement_cache_.get (
+ conn_,
+ image_, id_image_,
+ id_image_binding_, od_.id_image_binding ());
+ }
+
+ public:
+ // select = total - separate_load
+ // insert = total - inverse - managed_optimistic
+ // update = total - inverse - managed_optimistic - id - readonly
+ // - separate_update
+ //
+ static const std::size_t id_column_count =
+ object_traits::id_column_count;
+
+ static const std::size_t managed_optimistic_column_count =
+ object_traits::managed_optimistic_column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count -
+ object_traits::separate_load_column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count -
+ object_traits::inverse_column_count -
+ object_traits::managed_optimistic_column_count;
+
+ static const std::size_t update_column_count =
+ insert_column_count -
+ id_column_count -
+ object_traits::readonly_column_count -
+ object_traits::separate_update_column_count;
+
+ private:
+ object_statements (const object_statements&);
+ object_statements& operator= (const object_statements&);
+
+ protected:
+ template <typename STS>
+ void
+ load_delayed_ (const schema_version_migration*);
+
+ void
+ clear_delayed_ ();
+
+ protected:
+ template <typename T1>
+ friend class polymorphic_derived_object_statements;
+
+ extra_statement_cache_ptr<extra_statement_cache_type,
+ image_type,
+ id_image_type> extra_statement_cache_;
+
+ image_type image_;
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+ binding select_image_binding_;
+ MYSQL_BIND select_image_bind_[select_column_count];
+ my_bool select_image_truncated_[select_column_count];
+
+ // Insert binding.
+ //
+ std::size_t insert_image_version_;
+ binding insert_image_binding_;
+ MYSQL_BIND insert_image_bind_[insert_column_count];
+
+ // Update binding. Note that the id suffix is bound to id_image_
+ // below instead of image_ which makes this binding effectively
+ // bound to two images. As a result, we have to track versions
+ // for both of them. If this object uses optimistic concurrency,
+ // then the binding for the managed column (version, timestamp,
+ // etc) comes after the id and the image for such a column is
+ // stored as part of the id image.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_image_version_;
+ binding update_image_binding_;
+ MYSQL_BIND update_image_bind_[update_column_count + id_column_count +
+ managed_optimistic_column_count];
+
+ // Id image binding (only used as a parameter). Uses the suffix in
+ // the update bind.
+ //
+ id_image_type id_image_;
+ std::size_t id_image_version_;
+ binding id_image_binding_;
+
+ // Extra data for objects with optimistic concurrency support.
+ //
+ optimistic_data<T, managed_optimistic_column_count != 0> od_;
+
+ details::shared_ptr<insert_statement_type> persist_;
+ details::shared_ptr<select_statement_type> find_;
+ details::shared_ptr<update_statement_type> update_;
+ details::shared_ptr<delete_statement_type> erase_;
+
+ // Delayed loading.
+ //
+ struct delayed_load
+ {
+ typedef typename pointer_cache_traits::position_type position_type;
+
+ delayed_load () {}
+ delayed_load (const id_type& i,
+ object_type& o,
+ const position_type& p,
+ loader_function l)
+ : id (i), obj (&o), pos (p), loader (l)
+ {
+ }
+
+ id_type id;
+ object_type* obj;
+ position_type pos;
+ loader_function loader;
+ };
+
+ typedef std::vector<delayed_load> delayed_loads;
+ delayed_loads delayed_;
+
+ // Delayed vectors swap guard. See the load_delayed_() function for
+ // details.
+ //
+ struct swap_guard
+ {
+ swap_guard (object_statements& os, delayed_loads& dl)
+ : os_ (os), dl_ (dl)
+ {
+ dl_.swap (os_.delayed_);
+ }
+
+ ~swap_guard ()
+ {
+ os_.clear_delayed ();
+ dl_.swap (os_.delayed_);
+ }
+
+ private:
+ object_statements& os_;
+ delayed_loads& dl_;
+ };
+ };
+ }
+}
+
+#include <odb/mysql/simple-object-statements.ixx>
+#include <odb/mysql/simple-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_SIMPLE_OBJECT_STATEMENTS_HXX
diff --git a/libodb-mysql/odb/mysql/simple-object-statements.ixx b/libodb-mysql/odb/mysql/simple-object-statements.ixx
new file mode 100644
index 0000000..a75fc0d
--- /dev/null
+++ b/libodb-mysql/odb/mysql/simple-object-statements.ixx
@@ -0,0 +1,68 @@
+// file : odb/mysql/simple-object-statements.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mysql
+ {
+ //
+ // auto_unlock
+ //
+ inline object_statements_base::auto_unlock::
+ auto_unlock (object_statements_base& s)
+ : s_ (s)
+ {
+ s_.unlock ();
+ }
+
+ inline object_statements_base::auto_unlock::
+ ~auto_unlock ()
+ {
+ s_.lock ();
+ }
+
+ //
+ // auto_lock
+ //
+ template <typename T>
+ inline object_statements<T>::auto_lock::
+ auto_lock (object_statements& s)
+ : s_ (s)
+ {
+ if (!s_.locked ())
+ {
+ s_.lock ();
+ locked_ = true;
+ }
+ else
+ locked_ = false;
+ }
+
+ template <typename T>
+ inline object_statements<T>::auto_lock::
+ ~auto_lock ()
+ {
+ if (locked_)
+ {
+ s_.unlock ();
+ s_.clear_delayed ();
+ }
+ }
+
+ template <typename T>
+ inline bool object_statements<T>::auto_lock::
+ locked () const
+ {
+ return locked_;
+ }
+
+ template <typename T>
+ inline void object_statements<T>::auto_lock::
+ unlock ()
+ {
+ assert (locked_);
+ s_.unlock ();
+ locked_ = false;
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/simple-object-statements.txx b/libodb-mysql/odb/mysql/simple-object-statements.txx
new file mode 100644
index 0000000..2c04c71
--- /dev/null
+++ b/libodb-mysql/odb/mysql/simple-object-statements.txx
@@ -0,0 +1,146 @@
+// file : odb/mysql/simple-object-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ //
+ // optimistic_data
+ //
+
+ template <typename T>
+ optimistic_data<T, true>::
+ optimistic_data (MYSQL_BIND* b)
+ : id_image_binding_ (
+ b,
+ object_traits::id_column_count +
+ object_traits::managed_optimistic_column_count)
+ {
+ }
+
+ //
+ // object_statements
+ //
+
+ template <typename T>
+ object_statements<T>::
+ ~object_statements ()
+ {
+ }
+
+ template <typename T>
+ object_statements<T>::
+ object_statements (connection_type& conn)
+ : object_statements_base (conn),
+ select_image_binding_ (select_image_bind_, select_column_count),
+ insert_image_binding_ (insert_image_bind_, insert_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count +
+ managed_optimistic_column_count),
+ id_image_binding_ (update_image_bind_ + update_column_count,
+ id_column_count),
+ od_ (update_image_bind_ + update_column_count)
+ {
+ image_.version = 0;
+ select_image_version_ = 0;
+ insert_image_version_ = 0;
+ update_image_version_ = 0;
+ update_id_image_version_ = 0;
+
+ id_image_.version = 0;
+ id_image_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+
+ for (std::size_t i (0); i < select_column_count; ++i)
+ select_image_bind_[i].error = select_image_truncated_ + i;
+ }
+
+ template <typename T>
+ template <typename STS>
+ void object_statements<T>::
+ load_delayed_ (const schema_version_migration* svm)
+ {
+ database& db (connection ().database ());
+
+ delayed_loads dls;
+ swap_guard sg (*this, dls);
+
+ while (!dls.empty ())
+ {
+ delayed_load l (dls.back ());
+ typename pointer_cache_traits::insert_guard ig (l.pos);
+ dls.pop_back ();
+
+ if (l.loader == 0)
+ {
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (static_cast<STS&> (*this), &l.id))
+ throw object_not_persistent ();
+
+ object_traits::callback (db, *l.obj, callback_event::pre_load);
+
+ // Our calls to init/load below can result in additional delayed
+ // loads being added to the delayed_ vector. We need to process
+ // those before we call the post callback.
+ //
+ tc.init (*l.obj, image (), &db);
+
+ // Load containers, etc.
+ //
+ tc.load_ (static_cast<STS&> (*this), *l.obj, false);
+
+ if (!delayed_.empty ())
+ load_delayed_<STS> (svm);
+
+ // Temporarily unlock the statement for the post_load call so that
+ // it can load objects of this type recursively. This is safe to do
+ // because we have completely loaded the current object. Also the
+ // delayed_ list is clear before the unlock and should be clear on
+ // re-lock (since a callback can only call public API functions
+ // which will make sure all the delayed loads are processed before
+ // returning).
+ //
+ {
+ auto_unlock u (*this);
+ object_traits::callback (db, *l.obj, callback_event::post_load);
+ }
+ }
+ else
+ l.loader (db, l.id, *l.obj, svm);
+
+ pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+ }
+
+ template <typename T>
+ void object_statements<T>::
+ clear_delayed_ ()
+ {
+ // Remove the objects from the session cache.
+ //
+ for (typename delayed_loads::iterator i (delayed_.begin ()),
+ e (delayed_.end ()); i != e; ++i)
+ {
+ pointer_cache_traits::erase (i->pos);
+ }
+
+ delayed_.clear ();
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/statement-cache.hxx b/libodb-mysql/odb/mysql/statement-cache.hxx
new file mode 100644
index 0000000..1b62864
--- /dev/null
+++ b/libodb-mysql/odb/mysql/statement-cache.hxx
@@ -0,0 +1,60 @@
+// file : odb/mysql/statement-cache.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_STATEMENT_CACHE_HXX
+#define ODB_MYSQL_STATEMENT_CACHE_HXX
+
+#include <odb/pre.hxx>
+
+#include <map>
+#include <typeinfo>
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/statements-base.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/type-info.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class LIBODB_MYSQL_EXPORT statement_cache
+ {
+ public:
+ statement_cache (connection& conn)
+ : conn_ (conn),
+ version_seq_ (conn_.database ().schema_version_sequence ()) {}
+
+ template <typename T>
+ typename object_traits_impl<T, id_mysql>::statements_type&
+ find_object ();
+
+
+ template <typename T>
+ view_statements<T>&
+ find_view ();
+
+ private:
+ typedef std::map<const std::type_info*,
+ details::shared_ptr<statements_base>,
+ details::type_info_comparator> map;
+
+ connection& conn_;
+ unsigned int version_seq_;
+ map map_;
+ };
+ }
+}
+
+#include <odb/mysql/statement-cache.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_STATEMENT_CACHE_HXX
diff --git a/libodb-mysql/odb/mysql/statement-cache.txx b/libodb-mysql/odb/mysql/statement-cache.txx
new file mode 100644
index 0000000..c134f8d
--- /dev/null
+++ b/libodb-mysql/odb/mysql/statement-cache.txx
@@ -0,0 +1,60 @@
+// file : odb/mysql/statement-cache.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/mysql/database.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ typename object_traits_impl<T, id_mysql>::statements_type&
+ statement_cache::
+ find_object ()
+ {
+ typedef
+ typename object_traits_impl<T, id_mysql>::statements_type
+ statements_type;
+
+ // Clear the cache if the database version has changed. This
+ // makes sure we don't re-use statements that correspond to
+ // the old schema.
+ //
+ if (version_seq_ != conn_.database ().schema_version_sequence ())
+ {
+ map_.clear ();
+ version_seq_ = conn_.database ().schema_version_sequence ();
+ }
+
+ map::iterator i (map_.find (&typeid (T)));
+
+ if (i != map_.end ())
+ return static_cast<statements_type&> (*i->second);
+
+ details::shared_ptr<statements_type> p (
+ new (details::shared) statements_type (conn_));
+
+ map_.insert (map::value_type (&typeid (T), p));
+ return *p;
+ }
+
+ template <typename T>
+ view_statements<T>& statement_cache::
+ find_view ()
+ {
+ // We don't cache any statements for views so no need to clear
+ // the cache.
+
+ map::iterator i (map_.find (&typeid (T)));
+
+ if (i != map_.end ())
+ return static_cast<view_statements<T>&> (*i->second);
+
+ details::shared_ptr<view_statements<T> > p (
+ new (details::shared) view_statements<T> (conn_));
+
+ map_.insert (map::value_type (&typeid (T), p));
+ return *p;
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/statement.cxx b/libodb-mysql/odb/mysql/statement.cxx
new file mode 100644
index 0000000..83f294a
--- /dev/null
+++ b/libodb-mysql/odb/mysql/statement.cxx
@@ -0,0 +1,814 @@
+// file : odb/mysql/statement.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::strlen, std::memmove, std::memset
+#include <cassert>
+
+#include <odb/tracer.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/error.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mysql
+ {
+ // statement
+ //
+
+ statement::
+ statement (connection_type& conn,
+ const string& text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize)
+ : conn_ (conn)
+ {
+ if (process == 0)
+ {
+ text_copy_ = text;
+ text_ = text_copy_.c_str ();
+ }
+ else
+ text_ = text.c_str (); // Temporary, see init().
+
+ init (text.size (), sk, process, optimize);
+ }
+
+ statement::
+ statement (connection_type& conn,
+ const char* text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize,
+ bool copy)
+ : conn_ (conn)
+ {
+ size_t n;
+
+ if (process == 0 && copy)
+ {
+ text_copy_ = text;
+ text_ = text_copy_.c_str ();
+ n = text_copy_.size ();
+ }
+ else
+ {
+ text_ = text; // Potentially temporary, see init().
+ n = strlen (text_);
+ }
+
+ init (n, sk, process, optimize);
+ }
+
+ void statement::
+ init (size_t text_size,
+ statement_kind sk,
+ const binding* proc,
+ bool optimize)
+ {
+ if (proc != 0)
+ {
+ switch (sk)
+ {
+ case statement_select:
+ process_select (text_copy_,
+ text_,
+ &proc->bind->buffer, proc->count, sizeof (MYSQL_BIND),
+ '`', '`',
+ optimize);
+ break;
+ case statement_insert:
+ process_insert (text_copy_,
+ text_,
+ &proc->bind->buffer, proc->count, sizeof (MYSQL_BIND),
+ '?');
+ break;
+ case statement_update:
+ process_update (text_copy_,
+ text_,
+ &proc->bind->buffer, proc->count, sizeof (MYSQL_BIND),
+ '?');
+ break;
+ case statement_delete:
+ assert (false);
+ }
+
+ text_ = text_copy_.c_str ();
+ text_size = text_copy_.size ();
+ }
+
+ // Empty statement.
+ //
+ if (*text_ == '\0')
+ return;
+
+ stmt_.reset (conn_.alloc_stmt_handle ());
+
+ conn_.clear ();
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->prepare (conn_, *this);
+ }
+
+ if (mysql_stmt_prepare (stmt_,
+ text_,
+ static_cast<unsigned long> (text_size)) != 0)
+ translate_error (conn_, stmt_);
+ }
+
+ size_t statement::
+ process_bind (MYSQL_BIND* b, size_t n)
+ {
+ size_t shifts (0);
+ for (MYSQL_BIND* e (b + n); b != e;)
+ {
+ if (b->buffer == 0)
+ {
+ // It is possible that this array has already been processed
+ // (shared among multiple statements).
+ //
+ if (b->length != 0)
+ {
+ n -= e - b;
+ break;
+ }
+
+ e--;
+
+ // Shift the rest of the entries to the left.
+ //
+ memmove (b, b + 1, (e - b) * sizeof (MYSQL_BIND));
+
+ // Store the original position of the NULL entry at the end.
+ //
+ e->buffer = 0;
+ e->length = reinterpret_cast<unsigned long*> (b + shifts);
+
+ shifts++;
+ continue;
+ }
+
+ b++;
+ }
+
+ return n - shifts;
+ }
+
+ void statement::
+ restore_bind (MYSQL_BIND* b, size_t n)
+ {
+ for (MYSQL_BIND* e (b + n - 1); e->buffer == 0 && e->length != 0;)
+ {
+ MYSQL_BIND* p (reinterpret_cast<MYSQL_BIND*> (e->length));
+
+ // Shift the entries from the specified position to the right.
+ //
+ memmove (p + 1, p, (e - p) * sizeof (MYSQL_BIND));
+
+ // Restore the original NULL entry.
+ //
+ memset (p, 0, sizeof (MYSQL_BIND));
+ }
+ }
+
+ statement::
+ ~statement ()
+ {
+ if (stmt_ != 0)
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->deallocate (conn_, *this);
+ }
+
+ // Let the connection handle the release of the statement (it
+ // may delay the actual freeing if it will mess up the currently
+ // active statement).
+ //
+ conn_.free_stmt_handle (stmt_);
+ }
+ }
+
+ const char* statement::
+ text () const
+ {
+ return text_;
+ }
+
+ void statement::
+ cancel ()
+ {
+ }
+
+ // select_statement
+ //
+
+ select_statement::
+ ~select_statement ()
+ {
+ assert (freed_);
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ bool optimize,
+ binding& param,
+ binding& result)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ end_ (false),
+ cached_ (false),
+ freed_ (true),
+ rows_ (0),
+ param_ (&param),
+ param_version_ (0),
+ result_ (result),
+ result_version_ (0)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ bool optimize,
+ binding& param,
+ binding& result,
+ bool copy_text)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize,
+ copy_text),
+ end_ (false),
+ cached_ (false),
+ freed_ (true),
+ rows_ (0),
+ param_ (&param),
+ param_version_ (0),
+ result_ (result),
+ result_version_ (0)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ bool optimize,
+ binding& result)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ end_ (false),
+ cached_ (false),
+ freed_ (true),
+ rows_ (0),
+ param_ (0),
+ result_ (result),
+ result_version_ (0)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ bool optimize,
+ binding& result,
+ bool copy_text)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize,
+ copy_text),
+ end_ (false),
+ cached_ (false),
+ freed_ (true),
+ rows_ (0),
+ param_ (0),
+ result_ (result),
+ result_version_ (0)
+ {
+ }
+
+ void select_statement::
+ execute ()
+ {
+ assert (freed_);
+
+ conn_.clear ();
+
+ end_ = false;
+ rows_ = 0;
+
+ if (mysql_stmt_reset (stmt_))
+ translate_error (conn_, stmt_);
+
+ if (param_ != 0 && param_version_ != param_->version)
+ {
+ // For now cannot have NULL entries.
+ //
+ if (mysql_stmt_bind_param (stmt_, param_->bind))
+ translate_error (conn_, stmt_);
+
+ param_version_ = param_->version;
+ }
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ if (mysql_stmt_execute (stmt_))
+ translate_error (conn_, stmt_);
+
+ // This flag appears to be cleared once we start processing the
+ // result, so we have to cache it for free_result() below.
+ //
+#if MYSQL_VERSION_ID >= 50503
+ out_params_ = (conn_.handle ()->server_status & SERVER_PS_OUT_PARAMS);
+#endif
+
+ freed_ = false;
+ conn_.active (this);
+ }
+
+ void select_statement::
+ cache ()
+ {
+ if (!cached_)
+ {
+ if (!end_)
+ {
+ if (mysql_stmt_store_result (stmt_))
+ translate_error (conn_, stmt_);
+
+ // mysql_stmt_num_rows() returns the number of rows that have been
+ // fetched by store_result.
+ //
+ size_ = rows_ + static_cast<size_t> (mysql_stmt_num_rows (stmt_));
+ }
+ else
+ size_ = rows_;
+
+ cached_ = true;
+ }
+ }
+
+ select_statement::result select_statement::
+ fetch (bool next)
+ {
+ if (result_version_ != result_.version)
+ {
+ size_t count (process_bind (result_.bind, result_.count));
+
+ // Make sure that the number of columns in the result returned by
+ // the database matches the number that we expect. A common cause
+ // of this assertion is a native view with a number of data members
+ // not matching the number of columns in the SELECT-list.
+ //
+ assert (mysql_stmt_field_count (stmt_) == count);
+
+ if (mysql_stmt_bind_result (stmt_, result_.bind))
+ translate_error (conn_, stmt_);
+
+ if (count != result_.count)
+ restore_bind (result_.bind, result_.count);
+
+ result_version_ = result_.version;
+ }
+
+ if (!next && rows_ != 0)
+ {
+ assert (cached_);
+ mysql_stmt_data_seek (stmt_, static_cast<my_ulonglong> (rows_ - 1));
+ }
+
+ int r (mysql_stmt_fetch (stmt_));
+
+ switch (r)
+ {
+ case 0:
+ {
+ if (next)
+ rows_++;
+ return success;
+ }
+ case MYSQL_NO_DATA:
+ {
+ end_ = true;
+ return no_data;
+ }
+ case MYSQL_DATA_TRUNCATED:
+ {
+ if (next)
+ rows_++;
+ return truncated;
+ }
+ default:
+ {
+ translate_error (conn_, stmt_);
+ return no_data; // Never reached.
+ }
+ }
+ }
+
+ void select_statement::
+ refetch ()
+ {
+ // Re-fetch columns that were truncated.
+ //
+ unsigned int col (0);
+ for (size_t i (0); i < result_.count; ++i)
+ {
+ MYSQL_BIND& b (result_.bind[i]);
+
+ if (b.buffer == 0) // Skip NULL entries.
+ continue;
+
+ if (*b.error)
+ {
+ *b.error = 0;
+
+ if (mysql_stmt_fetch_column (stmt_, &b, col, 0))
+ translate_error (conn_, stmt_);
+ }
+
+ col++;
+ }
+ }
+
+ void select_statement::
+ free_result ()
+ {
+ if (!freed_)
+ {
+ // If this is a stored procedure call, then we have multiple
+ // results. The first is the rowset that is the result of the
+ // procedure (actually, it can be several rowsets if, for
+ // example, the procedure executes multiple SELECT statements,
+ // but we don't support this). This result we have just handled.
+ // Next, there could be another rowset with just one row which
+ // contains the output variable values (OUT and INOUT; actually,
+ // if the procedure does not have any SELECT statements, then
+ // this will be the first result and we have just handled it).
+ // Finally, the last result is always the stored procedure status
+ // (not clear how to get this value; all MySQL sample code simply
+ // ignores it).
+ //
+ // So what we need to do here is read and ignore these other
+ // results since otherwise MySQL won't let us execute any
+ // subsequent statements. Calling mysql_stmt_next_result()
+ // until it tells us there is no more results seems to do
+ // the trick.
+ //
+ // mysql_stmt_next_result() is only available since 5.5.3.
+ // Checking the source code reveals that it does call
+ // mysql_next_result() (which is available prior to 5.5.3)
+ // but also does some other house keeping. So it is not
+ // clear if it is possible to emulate the below logic for
+ // prior MySQL versions.
+ //
+
+ // Handling OUT parameters requires a special Voodoo dance:
+ // we have to fetch the row itself and we have to call fetch
+ // again and get MYSQL_NO_DATA. Without doing these exact
+ // steps the server simply drops the connection. Go figure.
+ //
+#if MYSQL_VERSION_ID >= 50503
+ if (out_params_)
+ {
+ if (mysql_stmt_fetch (stmt_) != MYSQL_NO_DATA)
+ translate_error (conn_, stmt_);
+ }
+#endif
+
+ if (mysql_stmt_free_result (stmt_))
+ translate_error (conn_, stmt_);
+
+#if MYSQL_VERSION_ID >= 50503
+ {
+ int s;
+ while ((s = mysql_stmt_next_result (stmt_)) == 0)
+ {
+ if (mysql_stmt_field_count (stmt_) != 0)
+ {
+ // The same Voodoo dance as above.
+ //
+ if (conn_.handle ()->server_status & SERVER_PS_OUT_PARAMS)
+ {
+ if (mysql_stmt_fetch (stmt_))
+ translate_error (conn_, stmt_);
+
+ if (mysql_stmt_fetch (stmt_) != MYSQL_NO_DATA)
+ translate_error (conn_, stmt_);
+ }
+
+ if (mysql_stmt_free_result (stmt_))
+ translate_error (conn_, stmt_);
+ }
+ }
+
+ if (s != -1)
+ translate_error (conn_, stmt_);
+ }
+#endif
+
+ if (conn_.active () == this)
+ conn_.active (0);
+
+ end_ = true;
+ cached_ = false;
+ freed_ = true;
+ rows_ = 0;
+ }
+ }
+
+ void select_statement::
+ cancel ()
+ {
+ // If we cached the result, don't free it just yet.
+ //
+ if (!cached_ || end_)
+ free_result ();
+ else
+ conn_.active (0);
+ }
+
+ // insert_statement
+ //
+
+ insert_statement::
+ ~insert_statement ()
+ {
+ }
+
+ insert_statement::
+ insert_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ binding& param,
+ binding* returning)
+ : statement (conn,
+ text, statement_insert,
+ (process ? &param : 0), false),
+ param_ (param),
+ param_version_ (0),
+ returning_ (returning)
+ {
+ }
+
+ insert_statement::
+ insert_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ binding& param,
+ binding* returning,
+ bool copy_text)
+ : statement (conn,
+ text, statement_insert,
+ (process ? &param : 0), false,
+ copy_text),
+ param_ (param),
+ param_version_ (0),
+ returning_ (returning)
+ {
+ }
+
+ bool insert_statement::
+ execute ()
+ {
+ conn_.clear ();
+
+ if (mysql_stmt_reset (stmt_))
+ translate_error (conn_, stmt_);
+
+ if (param_version_ != param_.version)
+ {
+ size_t count (process_bind (param_.bind, param_.count));
+
+ if (mysql_stmt_bind_param (stmt_, param_.bind))
+ translate_error (conn_, stmt_);
+
+ if (count != param_.count)
+ restore_bind (param_.bind, param_.count);
+
+ param_version_ = param_.version;
+ }
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ if (mysql_stmt_execute (stmt_))
+ {
+ // An auto-assigned object id should never cause a duplicate
+ // primary key.
+ //
+ if (returning_ == 0 && mysql_stmt_errno (stmt_) == ER_DUP_ENTRY)
+ return false;
+ else
+ translate_error (conn_, stmt_);
+ }
+
+ if (returning_ != 0)
+ {
+ unsigned long long i (mysql_stmt_insert_id (stmt_));
+
+ MYSQL_BIND& b (returning_->bind[0]);
+ void* v (b.buffer);
+
+ switch (b.buffer_type)
+ {
+ case MYSQL_TYPE_TINY:
+ *static_cast<unsigned char*> (v) = static_cast<unsigned char> (i);
+ break;
+ case MYSQL_TYPE_SHORT:
+ *static_cast<unsigned short*> (v) = static_cast<unsigned short> (i);
+ break;
+ case MYSQL_TYPE_LONG:
+ *static_cast<unsigned int*> (v) = static_cast<unsigned int> (i);
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ *static_cast<unsigned long long*> (v) = i;
+ break;
+ default:
+ assert (false); // Auto id column type is not an integer.
+ }
+
+ *b.is_null = false;
+ }
+
+ return true;
+ }
+
+ // update_statement
+ //
+
+ update_statement::
+ ~update_statement ()
+ {
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ binding& param)
+ : statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false),
+ param_ (param),
+ param_version_ (0)
+ {
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ binding& param,
+ bool copy_text)
+ : statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false,
+ copy_text),
+ param_ (param),
+ param_version_ (0)
+ {
+ }
+
+ unsigned long long update_statement::
+ execute ()
+ {
+ conn_.clear ();
+
+ if (mysql_stmt_reset (stmt_))
+ translate_error (conn_, stmt_);
+
+ if (param_version_ != param_.version)
+ {
+ size_t count (process_bind (param_.bind, param_.count));
+
+ if (mysql_stmt_bind_param (stmt_, param_.bind))
+ translate_error (conn_, stmt_);
+
+ if (count != param_.count)
+ restore_bind (param_.bind, param_.count);
+
+ param_version_ = param_.version;
+ }
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ if (mysql_stmt_execute (stmt_))
+ translate_error (conn_, stmt_);
+
+ my_ulonglong r (mysql_stmt_affected_rows (stmt_));
+
+ if (r == static_cast<my_ulonglong> (-1))
+ translate_error (conn_, stmt_);
+
+ return static_cast<unsigned long long> (r);
+ }
+
+ // delete_statement
+ //
+
+ delete_statement::
+ ~delete_statement ()
+ {
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const string& text,
+ binding& param)
+ : statement (conn,
+ text, statement_delete,
+ 0, false),
+ param_ (param),
+ param_version_ (0)
+ {
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const char* text,
+ binding& param,
+ bool copy_text)
+ : statement (conn,
+ text, statement_delete,
+ 0, false,
+ copy_text),
+ param_ (param),
+ param_version_ (0)
+ {
+ }
+
+ unsigned long long delete_statement::
+ execute ()
+ {
+ conn_.clear ();
+
+ if (mysql_stmt_reset (stmt_))
+ translate_error (conn_, stmt_);
+
+ if (param_version_ != param_.version)
+ {
+ // Cannot have NULL entries for now.
+ //
+ if (mysql_stmt_bind_param (stmt_, param_.bind))
+ translate_error (conn_, stmt_);
+
+ param_version_ = param_.version;
+ }
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ if (mysql_stmt_execute (stmt_))
+ translate_error (conn_, stmt_);
+
+ my_ulonglong r (mysql_stmt_affected_rows (stmt_));
+
+ if (r == static_cast<my_ulonglong> (-1))
+ translate_error (conn_, stmt_);
+
+ return static_cast<unsigned long long> (r);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/statement.hxx b/libodb-mysql/odb/mysql/statement.hxx
new file mode 100644
index 0000000..b1465ea
--- /dev/null
+++ b/libodb-mysql/odb/mysql/statement.hxx
@@ -0,0 +1,327 @@
+// file : odb/mysql/statement.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_STATEMENT_HXX
+#define ODB_MYSQL_STATEMENT_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+
+#include <odb/statement.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/binding.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/auto-handle.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class connection;
+
+ class LIBODB_MYSQL_EXPORT statement: public odb::statement
+ {
+ public:
+ typedef mysql::connection connection_type;
+
+ virtual
+ ~statement () = 0;
+
+ MYSQL_STMT*
+ handle () const
+ {
+ return stmt_;
+ }
+
+ virtual const char*
+ text () const;
+
+ virtual connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // A statement can be empty. This is used to handle situations
+ // where a SELECT or UPDATE statement ends up not having any
+ // columns after processing. An empty statement cannot be
+ // executed.
+ //
+ bool
+ empty () const
+ {
+ return stmt_ == 0;
+ }
+
+ // Cancel the statement execution (e.g., result fetching) so
+ // that another statement can be executed on the connection.
+ //
+ virtual void
+ cancel ();
+
+ protected:
+ // We keep two versions to take advantage of std::string COW.
+ //
+ statement (connection_type&,
+ const std::string& text,
+ statement_kind,
+ const binding* process,
+ bool optimize);
+
+ statement (connection_type&,
+ const char* text,
+ statement_kind,
+ const binding* process,
+ bool optimize,
+ bool copy_text);
+
+ // Process the bind array so that all non-NULL entries are at
+ // the beginning. Return the actual number of bound columns.
+ //
+ static std::size_t
+ process_bind (MYSQL_BIND*, std::size_t n);
+
+ // Restore the original locations of the NULL entries in the bind
+ // array.
+ //
+ static void
+ restore_bind (MYSQL_BIND*, std::size_t n);
+
+ private:
+ void
+ init (std::size_t text_size,
+ statement_kind,
+ const binding* process,
+ bool optimize);
+
+ protected:
+ connection_type& conn_;
+ std::string text_copy_;
+ const char* text_;
+ auto_handle<MYSQL_STMT> stmt_;
+ };
+
+ class LIBODB_MYSQL_EXPORT select_statement: public statement
+ {
+ public:
+ virtual
+ ~select_statement ();
+
+ select_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ binding& param,
+ binding& result);
+
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ bool optimize_text,
+ binding& param,
+ binding& result,
+ bool copy_text = true);
+
+ select_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ binding& result);
+
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ bool optimize_text,
+ binding& result,
+ bool copy_text = true);
+
+ enum result
+ {
+ success,
+ no_data,
+ truncated
+ };
+
+ void
+ execute ();
+
+ void
+ cache ();
+
+ bool
+ cached () const
+ {
+ return cached_;
+ }
+
+ // Can only be called on a cached result.
+ //
+ std::size_t
+ result_size () const
+ {
+ return size_;
+ }
+
+ // Number of rows already fetched.
+ //
+ std::size_t
+ fetched () const
+ {
+ return rows_;
+ }
+
+ // Fetch next or current row depending on the next argument.
+ // Note that fetching of the current row is only supported
+ // if the result is cached.
+ //
+ result
+ fetch (bool next = true);
+
+ void
+ refetch ();
+
+ void
+ free_result ();
+
+ virtual void
+ cancel ();
+
+ private:
+ select_statement (const select_statement&);
+ select_statement& operator= (const select_statement&);
+
+ private:
+ bool end_;
+ bool cached_;
+ bool freed_;
+ std::size_t rows_;
+ std::size_t size_;
+
+#if MYSQL_VERSION_ID >= 50503
+ bool out_params_;
+#endif
+
+ binding* param_;
+ std::size_t param_version_;
+
+ binding& result_;
+ std::size_t result_version_;
+ };
+
+ struct LIBODB_MYSQL_EXPORT auto_result
+ {
+ explicit auto_result (select_statement& s): s_ (s) {}
+ ~auto_result () {s_.free_result ();}
+
+ private:
+ auto_result (const auto_result&);
+ auto_result& operator= (const auto_result&);
+
+ private:
+ select_statement& s_;
+ };
+
+ class LIBODB_MYSQL_EXPORT insert_statement: public statement
+ {
+ public:
+ virtual
+ ~insert_statement ();
+
+ insert_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ binding& param,
+ binding* returning);
+
+ insert_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ binding& param,
+ binding* returning,
+ bool copy_text = true);
+
+ // Return true if successful and false if the row is a duplicate.
+ // All other errors are reported by throwing exceptions.
+ //
+ bool
+ execute ();
+
+ private:
+ insert_statement (const insert_statement&);
+ insert_statement& operator= (const insert_statement&);
+
+ private:
+ binding& param_;
+ std::size_t param_version_;
+
+ binding* returning_;
+ };
+
+ class LIBODB_MYSQL_EXPORT update_statement: public statement
+ {
+ public:
+ virtual
+ ~update_statement ();
+
+ update_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ binding& param);
+
+ update_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ binding& param,
+ bool copy_text = true);
+
+ unsigned long long
+ execute ();
+
+ private:
+ update_statement (const update_statement&);
+ update_statement& operator= (const update_statement&);
+
+ private:
+ binding& param_;
+ std::size_t param_version_;
+ };
+
+ class LIBODB_MYSQL_EXPORT delete_statement: public statement
+ {
+ public:
+ virtual
+ ~delete_statement ();
+
+ delete_statement (connection_type& conn,
+ const std::string& text,
+ binding& param);
+
+ delete_statement (connection_type& conn,
+ const char* text,
+ binding& param,
+ bool copy_text = true);
+
+ unsigned long long
+ execute ();
+
+ private:
+ delete_statement (const delete_statement&);
+ delete_statement& operator= (const delete_statement&);
+
+ private:
+ binding& param_;
+ std::size_t param_version_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_STATEMENT_HXX
diff --git a/libodb-mysql/odb/mysql/statements-base.cxx b/libodb-mysql/odb/mysql/statements-base.cxx
new file mode 100644
index 0000000..3e1bcc6
--- /dev/null
+++ b/libodb-mysql/odb/mysql/statements-base.cxx
@@ -0,0 +1,15 @@
+// file : odb/mysql/statements-base.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/mysql/statements-base.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ statements_base::
+ ~statements_base ()
+ {
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/statements-base.hxx b/libodb-mysql/odb/mysql/statements-base.hxx
new file mode 100644
index 0000000..93e72d2
--- /dev/null
+++ b/libodb-mysql/odb/mysql/statements-base.hxx
@@ -0,0 +1,63 @@
+// file : odb/mysql/statements-base.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_STATEMENTS_BASE_HXX
+#define ODB_MYSQL_STATEMENTS_BASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/schema-version.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/database.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class LIBODB_MYSQL_EXPORT statements_base: public details::shared_base
+ {
+ public:
+ typedef mysql::connection connection_type;
+
+ connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // Schema version. database::schema_version_migration() is thread-
+ // safe which means it is also slow. Cache the result in statements
+ // so we can avoid the mutex lock. This is thread-safe since if the
+ // version is updated, then the statements cache will be expired.
+ //
+ const schema_version_migration&
+ version_migration (const char* name = "") const
+ {
+ if (svm_ == 0)
+ svm_ = &conn_.database ().schema_version_migration (name);
+
+ return *svm_;
+ }
+
+ public:
+ virtual
+ ~statements_base ();
+
+ protected:
+ statements_base (connection_type& conn): conn_ (conn), svm_ (0) {}
+
+ protected:
+ connection_type& conn_;
+ mutable const schema_version_migration* svm_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_STATEMENTS_BASE_HXX
diff --git a/libodb-mysql/odb/mysql/tracer.cxx b/libodb-mysql/odb/mysql/tracer.cxx
new file mode 100644
index 0000000..da39633
--- /dev/null
+++ b/libodb-mysql/odb/mysql/tracer.cxx
@@ -0,0 +1,60 @@
+// file : odb/mysql/tracer.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/mysql/tracer.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/statement.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ tracer::
+ ~tracer ()
+ {
+ }
+
+ void tracer::
+ prepare (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ execute (connection& c, const statement& s)
+ {
+ execute (c, s.text ());
+ }
+
+ void tracer::
+ deallocate (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ prepare (odb::connection& c, const odb::statement& s)
+ {
+ prepare (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const odb::statement& s)
+ {
+ execute (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const char* s)
+ {
+ execute (static_cast<connection&> (c), s);
+ }
+
+ void tracer::
+ deallocate (odb::connection& c, const odb::statement& s)
+ {
+ deallocate (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/tracer.hxx b/libodb-mysql/odb/mysql/tracer.hxx
new file mode 100644
index 0000000..03ca6df
--- /dev/null
+++ b/libodb-mysql/odb/mysql/tracer.hxx
@@ -0,0 +1,61 @@
+// file : odb/mysql/tracer.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_TRACER_HXX
+#define ODB_MYSQL_TRACER_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/tracer.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class LIBODB_MYSQL_EXPORT tracer: private odb::tracer
+ {
+ public:
+ virtual
+ ~tracer ();
+
+ 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&);
+
+ private:
+ // Allow these classes to convert mysql::tracer to odb::tracer.
+ //
+ friend class database;
+ friend class connection;
+ friend class transaction;
+
+ virtual void
+ prepare (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const char* statement);
+
+ virtual void
+ deallocate (odb::connection&, const odb::statement&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_TRACER_HXX
diff --git a/libodb-mysql/odb/mysql/traits-calls.hxx b/libodb-mysql/odb/mysql/traits-calls.hxx
new file mode 100644
index 0000000..f6b2ab6
--- /dev/null
+++ b/libodb-mysql/odb/mysql/traits-calls.hxx
@@ -0,0 +1,210 @@
+// file : odb/mysql/traits-calls.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_TRAITS_CALLS_HXX
+#define ODB_MYSQL_TRAITS_CALLS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/mysql-types.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ //
+ // object_traits_calls
+ //
+
+ template <typename T,
+ bool versioned = object_traits_impl<T, id_mysql>::versioned>
+ struct object_traits_calls;
+
+ template <typename T>
+ struct object_traits_calls<T, false>
+ {
+ typedef object_traits_impl<T, id_mysql> traits;
+ typedef typename traits::image_type image_type;
+
+ object_traits_calls (const schema_version_migration*) {}
+
+ const schema_version_migration*
+ version () const {return 0;}
+
+ static bool
+ grow (image_type& i, my_bool* t)
+ {
+ return traits::grow (i, t);
+ }
+
+ static void
+ bind (MYSQL_BIND* b, image_type& i, statement_kind sk)
+ {
+ traits::bind (b, i, sk);
+ }
+
+ // Poly-derived version.
+ //
+ static void
+ bind (MYSQL_BIND* b,
+ const MYSQL_BIND* id, std::size_t id_size,
+ image_type& i,
+ statement_kind sk)
+ {
+ traits::bind (b, id, id_size, i, sk);
+ }
+
+ static void
+ init (T& o, const image_type& i, odb::database* db)
+ {
+ traits::init (o, i, db);
+ }
+
+ static bool
+ find_ (typename traits::statements_type& sts,
+ const typename traits::id_type* id)
+ {
+ return traits::find_ (sts, id);
+ }
+
+ static void
+ load_ (typename traits::statements_type& sts, T& o, bool reload)
+ {
+ return traits::load_ (sts, o, reload);
+ }
+ };
+
+ template <typename T>
+ struct object_traits_calls<T, true>
+ {
+ typedef object_traits_impl<T, id_mysql> traits;
+ typedef typename traits::image_type image_type;
+
+ object_traits_calls (const schema_version_migration* svm): svm_ (*svm) {}
+
+ const schema_version_migration*
+ version () const {return &svm_;}
+
+ bool
+ grow (image_type& i, my_bool* t) const
+ {
+ return traits::grow (i, t, svm_);
+ }
+
+ void
+ bind (MYSQL_BIND* b, image_type& i, statement_kind sk) const
+ {
+ traits::bind (b, i, sk, svm_);
+ }
+
+ // Poly-derived version.
+ //
+ void
+ bind (MYSQL_BIND* b,
+ const MYSQL_BIND* id, std::size_t id_size,
+ image_type& i,
+ statement_kind sk) const
+ {
+ traits::bind (b, id, id_size, i, sk, svm_);
+ }
+
+ void
+ init (T& o, const image_type& i, odb::database* db) const
+ {
+ traits::init (o, i, db, svm_);
+ }
+
+ bool
+ find_ (typename traits::statements_type& sts,
+ const typename traits::id_type* id) const
+ {
+ return traits::find_ (sts, id, svm_);
+ }
+
+ void
+ load_ (typename traits::statements_type& sts, T& o, bool reload) const
+ {
+ return traits::load_ (sts, o, reload, svm_);
+ }
+
+ private:
+ const schema_version_migration& svm_;
+ };
+
+ //
+ // view_traits_calls
+ //
+
+ template <typename T,
+ bool versioned = view_traits_impl<T, id_mysql>::versioned>
+ struct view_traits_calls;
+
+ template <typename T>
+ struct view_traits_calls<T, false>
+ {
+ typedef view_traits_impl<T, id_mysql> traits;
+ typedef typename traits::image_type image_type;
+
+ view_traits_calls (const schema_version_migration*) {}
+
+ static bool
+ grow (image_type& i, my_bool* t)
+ {
+ return traits::grow (i, t);
+ }
+
+ static void
+ bind (MYSQL_BIND* b, image_type& i)
+ {
+ traits::bind (b, i);
+ }
+
+ static void
+ init (T& o, const image_type& i, odb::database* db)
+ {
+ traits::init (o, i, db);
+ }
+ };
+
+ template <typename T>
+ struct view_traits_calls<T, true>
+ {
+ typedef view_traits_impl<T, id_mysql> traits;
+ typedef typename traits::image_type image_type;
+
+ view_traits_calls (const schema_version_migration* svm): svm_ (*svm) {}
+
+ bool
+ grow (image_type& i, my_bool* t) const
+ {
+ return traits::grow (i, t, svm_);
+ }
+
+ void
+ bind (MYSQL_BIND* b, image_type& i) const
+ {
+ traits::bind (b, i, svm_);
+ }
+
+ void
+ init (T& o, const image_type& i, odb::database* db) const
+ {
+ traits::init (o, i, db, svm_);
+ }
+
+ private:
+ const schema_version_migration& svm_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_TRAITS_CALLS_HXX
diff --git a/libodb-mysql/odb/mysql/traits.cxx b/libodb-mysql/odb/mysql/traits.cxx
new file mode 100644
index 0000000..9fbdbb4
--- /dev/null
+++ b/libodb-mysql/odb/mysql/traits.cxx
@@ -0,0 +1,145 @@
+// file : odb/mysql/traits.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/mysql/traits.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mysql
+ {
+ using details::buffer;
+
+ //
+ // string_value_traits
+ //
+
+ void string_value_traits::
+ set_image (buffer& b,
+ size_t& n,
+ bool& is_null,
+ const string& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v.c_str (), n);
+ }
+
+ //
+ // c_string_value_traits
+ //
+
+ void c_string_value_traits::
+ set_image (buffer& b,
+ size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ is_null = false;
+ n = strlen (v);
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v, n);
+ }
+
+ //
+ // c_array_value_traits_base
+ //
+ void c_array_value_traits_base::
+ set_value (char* const& v,
+ const details::buffer& b,
+ size_t n,
+ bool is_null,
+ size_t N)
+ {
+ if (!is_null)
+ {
+ n = n < N ? n : N;
+
+ if (n != 0)
+ memcpy (v, b.data (), n);
+ }
+ else
+ n = 0;
+
+ if (n != N) // Append '\0' if there is space.
+ v[n] = '\0';
+ }
+
+ void c_array_value_traits_base::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const char* v,
+ size_t N)
+ {
+ is_null = false;
+
+ // Figure out the length. We cannot use strlen since it may
+ // not be 0-terminated (strnlen is not standard).
+ //
+ for (n = 0; n != N && v[n] != '\0'; ++n) ;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v, n);
+ }
+
+ //
+ // default_value_traits<vector<char>, id_blob>
+ //
+ // std::vector has to be qualified for Sun CC.
+ //
+ void default_value_traits<std::vector<char>, id_blob>::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ // std::vector::data() may not be available in older compilers.
+ //
+ if (n != 0)
+ memcpy (b.data (), &v.front (), n);
+ }
+
+ //
+ // default_value_traits<vector<unsigned char>, id_blob>
+ //
+ // std::vector has to be qualified for Sun CC.
+ //
+ void default_value_traits<std::vector<unsigned char>, id_blob>::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ // std::vector::data() may not be available in older compilers.
+ //
+ if (n != 0)
+ memcpy (b.data (), &v.front (), n);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/traits.hxx b/libodb-mysql/odb/mysql/traits.hxx
new file mode 100644
index 0000000..e76f7e7
--- /dev/null
+++ b/libodb-mysql/odb/mysql/traits.hxx
@@ -0,0 +1,988 @@
+// file : odb/mysql/traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_TRAITS_HXX
+#define ODB_MYSQL_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+#include <cstring> // std::memcpy, std::memset, std::strlen
+
+#ifdef ODB_CXX11
+# include <array>
+#endif
+
+#include <odb/traits.hxx>
+#include <odb/wrapper-traits.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/mysql-types.hxx>
+
+#include <odb/details/buffer.hxx>
+#include <odb/details/wrapper-p.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ enum database_type_id
+ {
+ id_tiny,
+ id_utiny,
+ id_short,
+ id_ushort,
+ id_long,
+ id_ulong,
+ id_longlong,
+ id_ulonglong,
+
+ id_float,
+ id_double,
+ id_decimal,
+
+ id_date,
+ id_time,
+ id_datetime,
+ id_timestamp,
+ id_year,
+
+ id_string,
+ id_blob,
+
+ id_bit,
+ id_enum,
+ id_set
+ };
+
+ //
+ // image_traits
+ //
+
+ template <database_type_id>
+ struct image_traits;
+
+ template <>
+ struct image_traits<id_tiny> {typedef signed char image_type;};
+
+ template <>
+ struct image_traits<id_utiny> {typedef unsigned char image_type;};
+
+ template <>
+ struct image_traits<id_short> {typedef short image_type;};
+
+ template <>
+ struct image_traits<id_ushort> {typedef unsigned short image_type;};
+
+ template <>
+ struct image_traits<id_long> {typedef int image_type;};
+
+ template <>
+ struct image_traits<id_ulong> {typedef unsigned int image_type;};
+
+ template <>
+ struct image_traits<id_longlong> {typedef long long image_type;};
+
+ template <>
+ struct image_traits<id_ulonglong> {typedef unsigned long long image_type;};
+
+ template <>
+ struct image_traits<id_float> {typedef float image_type;};
+
+ template <>
+ struct image_traits<id_double> {typedef double image_type;};
+
+ template <>
+ struct image_traits<id_decimal> {typedef details::buffer image_type;};
+
+ template <>
+ struct image_traits<id_date> {typedef MYSQL_TIME image_type;};
+
+ template <>
+ struct image_traits<id_time> {typedef MYSQL_TIME image_type;};
+
+ template <>
+ struct image_traits<id_datetime> {typedef MYSQL_TIME image_type;};
+
+ template <>
+ struct image_traits<id_timestamp> {typedef MYSQL_TIME image_type;};
+
+ template <>
+ struct image_traits<id_year> {typedef short image_type;};
+
+ template <>
+ struct image_traits<id_string> {typedef details::buffer image_type;};
+
+ template <>
+ struct image_traits<id_blob> {typedef details::buffer image_type;};
+
+ template <>
+ struct image_traits<id_bit> {typedef unsigned char* image_type;};
+
+ // Note: default mapping is to integer. Alternative mapping is to
+ // string.
+ //
+ template <>
+ struct image_traits<id_enum> {typedef unsigned short image_type;};
+
+ template <>
+ struct image_traits<id_set> {typedef details::buffer image_type;};
+
+ //
+ // value_traits
+ //
+
+ template <typename W, database_type_id, bool null_handler>
+ struct wrapped_value_traits;
+
+ template <typename T, database_type_id>
+ struct default_value_traits;
+
+ template <typename T, database_type_id, bool w = details::wrapper_p<T>::r>
+ struct select_traits;
+
+ template <typename T, database_type_id ID>
+ struct select_traits<T, ID, false>
+ {
+ typedef default_value_traits<T, ID> type;
+ };
+
+ template <typename W, database_type_id ID>
+ struct select_traits<W, ID, true>
+ {
+ typedef
+ wrapped_value_traits<W, ID, wrapper_traits<W>::null_handler>
+ type;
+ };
+
+ template <typename T, database_type_id ID>
+ class value_traits: public select_traits<T, ID>::type
+ {
+ };
+
+ // The wrapped_value_traits specializations should be able to handle
+ // any value type which means we have to have every possible signature
+ // of the set_value() and set_image() functions.
+ //
+ template <typename W, database_type_id ID>
+ struct wrapped_value_traits<W, ID, false>
+ {
+ typedef wrapper_traits<W> wtraits;
+ typedef typename wtraits::unrestricted_wrapped_type wrapped_type;
+
+ typedef W value_type;
+ typedef wrapped_type query_type;
+ typedef typename image_traits<ID>::image_type image_type;
+
+ typedef value_traits<wrapped_type, ID> vtraits;
+
+ static void
+ set_value (W& v, const image_type& i, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, const W& v)
+ {
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+
+ // String, BLOB, ENUM, and SET.
+ //
+ static void
+ set_value (W& v, const details::buffer& b, std::size_t n, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (details::buffer& b, std::size_t& n, bool& is_null, const W& v)
+ {
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+
+ // BIT.
+ //
+ static void
+ set_value (W& v, const unsigned char* i, std::size_t n, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, n, is_null);
+ }
+
+ static void
+ set_image (unsigned char* i,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v));
+ }
+ };
+
+ template <typename W, database_type_id ID>
+ struct wrapped_value_traits<W, ID, true>
+ {
+ typedef wrapper_traits<W> wtraits;
+ typedef typename wtraits::unrestricted_wrapped_type wrapped_type;
+
+ typedef W value_type;
+ typedef wrapped_type query_type;
+ typedef typename image_traits<ID>::image_type image_type;
+
+ typedef value_traits<wrapped_type, ID> vtraits;
+
+ static void
+ set_value (W& v, const image_type& i, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+
+ // String, BLOB, ENUM, and SET.
+ //
+ static void
+ set_value (W& v, const details::buffer& b, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (details::buffer& b, std::size_t& n, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+
+ // BIT.
+ //
+ static void
+ set_value (W& v, const unsigned char* i, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, n, is_null);
+ }
+
+ static void
+ set_image (unsigned char* i,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v));
+ }
+ };
+
+ template <typename T, database_type_id ID>
+ struct default_value_traits
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef typename image_traits<ID>::image_type image_type;
+
+ static void
+ set_value (T& v, const image_type& i, bool is_null)
+ {
+ if (!is_null)
+ v = T (i);
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, T v)
+ {
+ is_null = false;
+ i = image_type (v);
+ }
+ };
+
+ // Specialization for numeric enum representations (C++ enum, integer
+ // types, etc). In particular, this specialization works only for C++
+ // enum type as long as its numeric value space starts with 0, is
+ // ascending and contiguous (i.e., the default enumerator assignment).
+ //
+ template <typename T>
+ struct default_value_traits<T, id_enum>
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef unsigned short image_type;
+
+ static void
+ set_value (T& v, unsigned short i, bool is_null)
+ {
+ // In MySQL first enumerator has index 1.
+ //
+ if (!is_null)
+ v = static_cast<T> (i - 1);
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (unsigned short& i, bool& is_null, const T& v)
+ {
+ is_null = false;
+ i = static_cast<unsigned short> (v) + 1;
+ }
+ };
+
+ // std::string specialization.
+ //
+ class LIBODB_MYSQL_EXPORT string_value_traits
+ {
+ public:
+ typedef std::string value_type;
+ typedef std::string query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (std::string& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ v.assign (b.data (), n);
+ else
+ v.erase ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const std::string&);
+ };
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<std::string, id_string>:
+ string_value_traits
+ {
+ };
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<std::string, id_decimal>:
+ string_value_traits
+ {
+ };
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<std::string, id_enum>:
+ string_value_traits
+ {
+ };
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<std::string, id_set>:
+ string_value_traits
+ {
+ };
+
+ // char*/const char* specialization
+ //
+ // Specialization for const char* which only supports initialization
+ // of an image from the value but not the other way around. This way
+ // we can pass such values to the queries.
+ //
+ class LIBODB_MYSQL_EXPORT c_string_value_traits
+ {
+ public:
+ typedef const char* value_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const char*);
+ };
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<char*, id_string>:
+ c_string_value_traits {};
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<char*, id_decimal>:
+ c_string_value_traits {};
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<char*, id_enum>:
+ c_string_value_traits {};
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<char*, id_set>:
+ c_string_value_traits {};
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<const char*, id_string>:
+ c_string_value_traits {};
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<const char*, id_decimal>:
+ c_string_value_traits {};
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<const char*, id_enum>:
+ c_string_value_traits {};
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<const char*, id_set>:
+ c_string_value_traits {};
+
+ // char[N] specializations.
+ //
+ struct LIBODB_MYSQL_EXPORT c_array_value_traits_base
+ {
+ static void
+ set_value (char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null,
+ std::size_t N);
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const char* v,
+ std::size_t N);
+ };
+
+ template <std::size_t N>
+ struct c_array_value_traits
+ {
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (v, b, n, is_null, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ c_array_value_traits_base::set_image (b, n, is_null, v, N);
+ }
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_string>: c_array_value_traits<N>
+ {};
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_decimal>: c_array_value_traits<N>
+ {};
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_enum>: c_array_value_traits<N>
+ {};
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_set>: c_array_value_traits<N>
+ {};
+
+ // std::array<char, N> (string) specialization.
+ //
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct std_array_value_traits
+ {
+ typedef std::array<char, N> value_type;
+ typedef std::array<char, N> 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)
+ {
+ c_array_value_traits_base::set_value (v.data (), b, n, is_null, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_array_value_traits_base::set_image (b, n, is_null, v.data (), N);
+ }
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_string>:
+ std_array_value_traits<N> {};
+
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_decimal>:
+ std_array_value_traits<N> {};
+
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_enum>:
+ std_array_value_traits<N> {};
+
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_set>:
+ std_array_value_traits<N> {};
+#endif
+
+ // char specialization.
+ //
+ struct LIBODB_MYSQL_EXPORT char_value_traits
+ {
+ typedef char value_type;
+ typedef char query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (&v, b, n, is_null, 1);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ char v)
+ {
+ c_array_value_traits_base::set_image (b, n, is_null, &v, 1);
+ }
+ };
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<char, id_string>:
+ char_value_traits {};
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<char, id_enum>:
+ char_value_traits {};
+
+ // std::vector<char> (buffer) specialization.
+ //
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<std::vector<char>, id_blob>
+ {
+ public:
+ typedef std::vector<char> value_type;
+ typedef std::vector<char> 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)
+ {
+ if (!is_null)
+ v.assign (b.data (), b.data () + n);
+ else
+ v.clear ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const value_type&);
+ };
+
+ // std::vector<unsigned char> (buffer) specialization.
+ //
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<
+ std::vector<unsigned char>, id_blob>
+ {
+ public:
+ typedef std::vector<unsigned char> value_type;
+ typedef std::vector<unsigned char> 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)
+ {
+ if (!is_null)
+ {
+ const unsigned char* d (
+ reinterpret_cast<const unsigned char*> (b.data ()));
+ v.assign (d, d + n);
+ }
+ else
+ v.clear ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const value_type&);
+ };
+
+ // char[N] (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_blob>
+ {
+ public:
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, b.data (), (n < N ? n : N));
+ else
+ std::memset (v, 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v, n);
+ }
+ };
+
+ // unsigned char[N] (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<unsigned char[N], id_blob>
+ {
+ public:
+ typedef unsigned char* value_type;
+ typedef unsigned char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (unsigned char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, b.data (), (n < N ? n : N));
+ else
+ std::memset (v, 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const unsigned char* v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v, n);
+ }
+ };
+
+#ifdef ODB_CXX11
+ // std::array<char, N> (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_blob>
+ {
+ public:
+ typedef std::array<char, N> 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)
+ {
+ if (!is_null)
+ std::memcpy (v.data (), b.data (), (n < N ? n : N));
+ else
+ std::memset (v.data (), 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v.data (), n);
+ }
+ };
+
+ // std::array<unsigned char, N> (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<std::array<unsigned char, N>, id_blob>
+ {
+ public:
+ typedef std::array<unsigned char, N> 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)
+ {
+ if (!is_null)
+ std::memcpy (v.data (), b.data (), (n < N ? n : N));
+ else
+ std::memset (v.data (), 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v.data (), n);
+ }
+ };
+#endif
+
+ //
+ // type_traits
+ //
+
+ template <typename T>
+ struct default_type_traits;
+
+ template <typename T>
+ class type_traits: public default_type_traits<T>
+ {
+ };
+
+ // Integral types.
+ //
+ template <>
+ struct default_type_traits<bool>
+ {
+ static const database_type_id db_type_id = id_tiny;
+ };
+
+ template <>
+ struct default_type_traits<signed char>
+ {
+ static const database_type_id db_type_id = id_tiny;
+ };
+
+ template <>
+ struct default_type_traits<unsigned char>
+ {
+ static const database_type_id db_type_id = id_utiny;
+ };
+
+ template <>
+ struct default_type_traits<short>
+ {
+ static const database_type_id db_type_id = id_short;
+ };
+
+ template <>
+ struct default_type_traits<unsigned short>
+ {
+ static const database_type_id db_type_id = id_ushort;
+ };
+
+ template <>
+ struct default_type_traits<int>
+ {
+ static const database_type_id db_type_id = id_long;
+ };
+
+ template <>
+ struct default_type_traits<unsigned int>
+ {
+ static const database_type_id db_type_id = id_ulong;
+ };
+
+ template <>
+ struct default_type_traits<long>
+ {
+ static const database_type_id db_type_id = id_longlong;
+ };
+
+ template <>
+ struct default_type_traits<unsigned long>
+ {
+ static const database_type_id db_type_id = id_ulonglong;
+ };
+
+ template <>
+ struct default_type_traits<long long>
+ {
+ static const database_type_id db_type_id = id_longlong;
+ };
+
+ template <>
+ struct default_type_traits<unsigned long long>
+ {
+ static const database_type_id db_type_id = id_ulonglong;
+ };
+
+ // Float types.
+ //
+ template <>
+ struct default_type_traits<float>
+ {
+ static const database_type_id db_type_id = id_float;
+ };
+
+ template <>
+ struct default_type_traits<double>
+ {
+ static const database_type_id db_type_id = id_double;
+ };
+
+ // String types.
+ //
+ template <>
+ struct default_type_traits<std::string>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ template <>
+ struct default_type_traits<char*>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ template <>
+ struct default_type_traits<const char*>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ template <std::size_t N>
+ struct default_type_traits<char[N]>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<char, N> >
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+#endif
+
+ template <>
+ struct default_type_traits<char>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ // Binary types.
+ //
+ template <std::size_t N>
+ struct default_type_traits<unsigned char[N]>
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+
+ template <>
+ struct default_type_traits<std::vector<char> >
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+
+ template <>
+ struct default_type_traits<std::vector<unsigned char> >
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<unsigned char, N> >
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+#endif
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_TRAITS_HXX
diff --git a/libodb-mysql/odb/mysql/transaction-impl.cxx b/libodb-mysql/odb/mysql/transaction-impl.cxx
new file mode 100644
index 0000000..0ca1546
--- /dev/null
+++ b/libodb-mysql/odb/mysql/transaction-impl.cxx
@@ -0,0 +1,108 @@
+// file : odb/mysql/transaction-impl.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/tracer.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/error.hxx>
+#include <odb/mysql/transaction-impl.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ transaction_impl::
+ transaction_impl (database_type& db)
+ : odb::transaction_impl (db)
+ {
+ }
+
+ transaction_impl::
+ transaction_impl (connection_ptr c)
+ : odb::transaction_impl (c->database (), *c), connection_ (c)
+ {
+ }
+
+ transaction_impl::
+ ~transaction_impl ()
+ {
+ }
+
+ void transaction_impl::
+ start ()
+ {
+ // Grab a connection if we don't already have one.
+ //
+ if (connection_ == 0)
+ {
+ connection_ = static_cast<database_type&> (database_).connection ();
+ odb::transaction_impl::connection_ = connection_.get ();
+ }
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "BEGIN");
+ }
+
+ if (mysql_real_query (connection_->handle (), "begin", 5) != 0)
+ translate_error (*connection_);
+ }
+
+ void transaction_impl::
+ commit ()
+ {
+ // Invalidate query results.
+ //
+ connection_->invalidate_results ();
+
+ // Cancel and clear the active statement if any. This normally
+ // should happen automatically, however, if an exception is
+ // thrown, this may not be the case.
+ //
+ connection_->clear ();
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "COMMIT");
+ }
+
+ if (mysql_real_query (connection_->handle (), "commit", 6) != 0)
+ translate_error (*connection_);
+
+ // Release the connection.
+ //
+ connection_.reset ();
+ }
+
+ void transaction_impl::
+ rollback ()
+ {
+ // Invalidate query results.
+ //
+ connection_->invalidate_results ();
+
+ // Cancel and clear the active statement if any. This normally
+ // should happen automatically, however, if an exception is
+ // thrown, this may not be the case.
+ //
+ connection_->clear ();
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "ROLLBACK");
+ }
+
+ if (mysql_real_query (connection_->handle (), "rollback", 8) != 0)
+ translate_error (*connection_);
+
+ // Release the connection.
+ //
+ connection_.reset ();
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/transaction-impl.hxx b/libodb-mysql/odb/mysql/transaction-impl.hxx
new file mode 100644
index 0000000..d87ad35
--- /dev/null
+++ b/libodb-mysql/odb/mysql/transaction-impl.hxx
@@ -0,0 +1,49 @@
+// file : odb/mysql/transaction-impl.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_TRANSACTION_IMPL_HXX
+#define ODB_MYSQL_TRANSACTION_IMPL_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class LIBODB_MYSQL_EXPORT transaction_impl: public odb::transaction_impl
+ {
+ public:
+ typedef mysql::database database_type;
+ typedef mysql::connection connection_type;
+
+ transaction_impl (database_type&);
+ transaction_impl (connection_ptr);
+
+ virtual
+ ~transaction_impl ();
+
+ virtual void
+ start ();
+
+ virtual void
+ commit ();
+
+ virtual void
+ rollback ();
+
+ private:
+ connection_ptr connection_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_TRANSACTION_IMPL_HXX
diff --git a/libodb-mysql/odb/mysql/transaction.cxx b/libodb-mysql/odb/mysql/transaction.cxx
new file mode 100644
index 0000000..5d59fad
--- /dev/null
+++ b/libodb-mysql/odb/mysql/transaction.cxx
@@ -0,0 +1,26 @@
+// file : odb/mysql/transaction.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/mysql/transaction.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ transaction& transaction::
+ current ()
+ {
+ // While the impl type can be of the concrete type, the transaction
+ // object can be created as either odb:: or odb::mysql:: type. To
+ // work around that we are going to hard-cast one two the other
+ // relying on the fact that they have the same representation and
+ // no virtual functions. The former is checked in the tests.
+ //
+ odb::transaction& b (odb::transaction::current ());
+ assert (dynamic_cast<transaction_impl*> (&b.implementation ()) != 0);
+ return reinterpret_cast<transaction&> (b);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/transaction.hxx b/libodb-mysql/odb/mysql/transaction.hxx
new file mode 100644
index 0000000..0f5ae6f
--- /dev/null
+++ b/libodb-mysql/odb/mysql/transaction.hxx
@@ -0,0 +1,88 @@
+// file : odb/mysql/transaction.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_TRANSACTION_HXX
+#define ODB_MYSQL_TRANSACTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/tracer.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class transaction_impl;
+
+ class LIBODB_MYSQL_EXPORT transaction: public odb::transaction
+ {
+ public:
+ typedef mysql::database database_type;
+ typedef mysql::connection connection_type;
+
+ explicit
+ transaction (transaction_impl*, bool make_current = true);
+
+ transaction ();
+
+ // Return the database this transaction is on.
+ //
+ database_type&
+ database ();
+
+ // Return the underlying database connection for this transaction.
+ //
+ connection_type&
+ connection ();
+
+ connection_type&
+ connection (odb::database&);
+
+ // Return current transaction or throw if there is no transaction
+ // in effect.
+ //
+ static transaction&
+ current ();
+
+ // Set the current thread's transaction.
+ //
+ static void
+ current (transaction&);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef mysql::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ using odb::transaction::tracer;
+
+ public:
+ transaction_impl&
+ implementation ();
+ };
+ }
+}
+
+#include <odb/mysql/transaction.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_TRANSACTION_HXX
diff --git a/libodb-mysql/odb/mysql/transaction.ixx b/libodb-mysql/odb/mysql/transaction.ixx
new file mode 100644
index 0000000..15af6a5
--- /dev/null
+++ b/libodb-mysql/odb/mysql/transaction.ixx
@@ -0,0 +1,57 @@
+// file : odb/mysql/transaction.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/transaction-impl.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ inline transaction::
+ transaction (transaction_impl* impl, bool make_current)
+ : odb::transaction (impl, make_current)
+ {
+ }
+
+ inline transaction::
+ transaction ()
+ : odb::transaction ()
+ {
+ }
+
+ inline transaction_impl& transaction::
+ implementation ()
+ {
+ // We can use static_cast here since we have an instance of
+ // mysql::transaction.
+ //
+ return static_cast<transaction_impl&> (
+ odb::transaction::implementation ());
+ }
+
+ inline transaction::database_type& transaction::
+ database ()
+ {
+ return static_cast<database_type&> (odb::transaction::database ());
+ }
+
+ inline transaction::connection_type& transaction::
+ connection ()
+ {
+ return static_cast<connection_type&> (odb::transaction::connection ());
+ }
+
+ inline transaction::connection_type& transaction::
+ connection (odb::database& db)
+ {
+ return static_cast<connection_type&> (odb::transaction::connection (db));
+ }
+
+ inline void transaction::
+ current (transaction& t)
+ {
+ odb::transaction::current (t);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/version.hxx b/libodb-mysql/odb/mysql/version.hxx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libodb-mysql/odb/mysql/version.hxx
diff --git a/libodb-mysql/odb/mysql/version.hxx.in b/libodb-mysql/odb/mysql/version.hxx.in
new file mode 100644
index 0000000..0526554
--- /dev/null
+++ b/libodb-mysql/odb/mysql/version.hxx.in
@@ -0,0 +1,81 @@
+// file : odb/mysql/version.hxx.in
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef LIBODB_MYSQL_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 LIBODB_MYSQL_VERSION_FULL $libodb_mysql.version.project_number$ULL
+#define LIBODB_MYSQL_VERSION_STR "$libodb_mysql.version.project$"
+#define LIBODB_MYSQL_VERSION_ID "$libodb_mysql.version.project_id$"
+
+#define LIBODB_MYSQL_VERSION_MAJOR $libodb_mysql.version.major$
+#define LIBODB_MYSQL_VERSION_MINOR $libodb_mysql.version.minor$
+#define LIBODB_MYSQL_VERSION_PATCH $libodb_mysql.version.patch$
+
+#define LIBODB_MYSQL_PRE_RELEASE $libodb_mysql.version.pre_release$
+
+#define LIBODB_MYSQL_SNAPSHOT $libodb_mysql.version.snapshot_sn$ULL
+#define LIBODB_MYSQL_SNAPSHOT_ID "$libodb_mysql.version.snapshot_id$"
+
+#ifdef LIBODB_MYSQL_MARIADB
+# include <mysql/mariadb_version.h>
+
+// We support MariaDB starting from 10.2.2 when the library started to be
+// named as as mariadb, rather than mysqlclient. Before that we just don't
+// distinguish it from the MySQL client library.
+//
+# if !defined(MYSQL_VERSION_ID) || MYSQL_VERSION_ID < 100202
+# error unexpected MariaDB version detected
+# endif
+#else
+# include <mysql/mysql_version.h>
+
+// Check that we have a compatible MySQL version (5.0.3 or later).
+//
+# if !defined(MYSQL_VERSION_ID) || MYSQL_VERSION_ID < 50003
+# error incompatible MySQL version detected
+# endif
+#endif
+
+#include <odb/version.hxx>
+
+$libodb.check(LIBODB_VERSION_FULL, LIBODB_SNAPSHOT)$
+
+
+// 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 LIBODB_MYSQL_VERSION 2049976
+
+#endif // LIBODB_MYSQL_VERSION
diff --git a/libodb-mysql/odb/mysql/view-result.hxx b/libodb-mysql/odb/mysql/view-result.hxx
new file mode 100644
index 0000000..2a7682b
--- /dev/null
+++ b/libodb-mysql/odb/mysql/view-result.hxx
@@ -0,0 +1,81 @@
+// file : odb/mysql/view-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_VIEW_RESULT_HXX
+#define ODB_MYSQL_VIEW_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/view-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx> // query_base, view_statements
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ class view_result_impl: public odb::view_result_impl<T>
+ {
+ public:
+ typedef odb::view_result_impl<T> base_type;
+
+ typedef typename base_type::view_type view_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef view_traits_impl<view_type, id_mysql> view_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef view_statements<view_type> statements_type;
+
+ virtual
+ ~view_result_impl ();
+
+ view_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (view_type&);
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ void
+ fetch ();
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ view_traits_calls<view_type> tc_;
+ std::size_t count_;
+ };
+ }
+}
+
+#include <odb/mysql/view-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_VIEW_RESULT_HXX
diff --git a/libodb-mysql/odb/mysql/view-result.txx b/libodb-mysql/odb/mysql/view-result.txx
new file mode 100644
index 0000000..c480920
--- /dev/null
+++ b/libodb-mysql/odb/mysql/view-result.txx
@@ -0,0 +1,178 @@
+// file : odb/mysql/view-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/mysql/view-statements.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ view_result_impl<T>::
+ ~view_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ view_result_impl<T>::
+ view_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ count_ (0)
+ {
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ load (view_type& view)
+ {
+ if (count_ > statement_->fetched ())
+ fetch ();
+
+ view_traits::callback (this->db_, view, callback_event::pre_load);
+ tc_.init (view, statements_.image (), &this->db_);
+ view_traits::callback (this->db_, view, callback_event::post_load);
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (this->end_)
+ return;
+
+ // If we are cached, simply increment the position and
+ // postpone the actual row fetching until later. This way
+ // if the same view is loaded in between iteration, the
+ // image won't be messed up.
+ //
+ count_++;
+
+ if (statement_->cached ())
+ this->end_ = count_ > statement_->result_size ();
+ else
+ fetch ();
+
+ if (this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ fetch ()
+ {
+ // If the result is cached, the image can grow between calls
+ // to fetch() as a result of other statements execution.
+ //
+ if (statement_->cached ())
+ {
+ typename view_traits::image_type& im (statements_.image ());
+
+ if (im.version != statements_.image_version ())
+ {
+ binding& b (statements_.image_binding ());
+ tc_.bind (b.bind, im);
+ statements_.image_version (im.version);
+ b.version++;
+ }
+ }
+
+ while (!this->end_ && count_ > statement_->fetched ())
+ {
+ select_statement::result r (statement_->fetch ());
+
+ switch (r)
+ {
+ case select_statement::truncated:
+ {
+ // Don't re-fetch data we are skipping.
+ //
+ if (count_ != statement_->fetched ())
+ continue;
+
+ typename view_traits::image_type& im (statements_.image ());
+
+ if (tc_.grow (im, statements_.image_truncated ()))
+ im.version++;
+
+ if (im.version != statements_.image_version ())
+ {
+ binding& b (statements_.image_binding ());
+ tc_.bind (b.bind, im);
+ statements_.image_version (im.version);
+ b.version++;
+ statement_->refetch ();
+ }
+ // Fall throught.
+ }
+ case select_statement::success:
+ {
+ break;
+ }
+ case select_statement::no_data:
+ {
+ this->end_ = true;
+ break;
+ }
+ }
+ }
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ cache ()
+ {
+ if (!this->end_ && !statement_->cached ())
+ {
+ statement_->cache ();
+
+ if (count_ == statement_->result_size ())
+ {
+ statement_->free_result ();
+ count_++; // One past the result size.
+ this->end_ = true;
+ }
+ }
+ }
+
+ template <typename T>
+ std::size_t view_result_impl<T>::
+ size ()
+ {
+ if (!this->end_)
+ {
+ if (!statement_->cached ())
+ throw result_not_cached ();
+
+ return statement_->result_size ();
+ }
+ else
+ return count_ - 1; // One past the result size.
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/view-statements.hxx b/libodb-mysql/odb/mysql/view-statements.hxx
new file mode 100644
index 0000000..cff4bc1
--- /dev/null
+++ b/libodb-mysql/odb/mysql/view-statements.hxx
@@ -0,0 +1,88 @@
+// file : odb/mysql/view-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_VIEW_STATEMENTS_HXX
+#define ODB_MYSQL_VIEW_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/statements-base.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ class view_statements: public statements_base
+ {
+ public:
+ typedef T view_type;
+ typedef view_traits_impl<view_type, id_mysql> view_traits;
+ typedef typename view_traits::pointer_type pointer_type;
+ typedef typename view_traits::image_type image_type;
+
+ public:
+ view_statements (connection_type&);
+
+ virtual
+ ~view_statements ();
+
+ // View image.
+ //
+ image_type&
+ image ()
+ {
+ return image_;
+ }
+
+ std::size_t
+ image_version () const
+ {
+ return image_version_;
+ }
+
+ void
+ image_version (std::size_t v)
+ {
+ image_version_ = v;
+ }
+
+ binding&
+ image_binding ()
+ {
+ return image_binding_;
+ }
+
+ my_bool*
+ image_truncated ()
+ {
+ return image_truncated_;
+ }
+
+ private:
+ view_statements (const view_statements&);
+ view_statements& operator= (const view_statements&);
+
+ private:
+ image_type image_;
+ std::size_t image_version_;
+ binding image_binding_;
+ MYSQL_BIND image_bind_[view_traits::column_count];
+ my_bool image_truncated_[view_traits::column_count];
+ };
+ }
+}
+
+#include <odb/mysql/view-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_VIEW_STATEMENTS_HXX
diff --git a/libodb-mysql/odb/mysql/view-statements.txx b/libodb-mysql/odb/mysql/view-statements.txx
new file mode 100644
index 0000000..b425227
--- /dev/null
+++ b/libodb-mysql/odb/mysql/view-statements.txx
@@ -0,0 +1,33 @@
+// file : odb/mysql/view-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ view_statements<T>::
+ ~view_statements ()
+ {
+ }
+
+ template <typename T>
+ view_statements<T>::
+ view_statements (connection_type& conn)
+ : statements_base (conn),
+ image_binding_ (image_bind_, view_traits::column_count)
+ {
+ image_.version = 0;
+ image_version_ = 0;
+
+ std::memset (image_bind_, 0, sizeof (image_bind_));
+ std::memset (image_truncated_, 0, sizeof (image_truncated_));
+
+ for (std::size_t i (0); i < view_traits::column_count; ++i)
+ image_bind_[i].error = image_truncated_ + i;
+ }
+ }
+}
diff --git a/libodb-mysql/tests/.gitignore b/libodb-mysql/tests/.gitignore
new file mode 100644
index 0000000..e54525b
--- /dev/null
+++ b/libodb-mysql/tests/.gitignore
@@ -0,0 +1 @@
+driver
diff --git a/libodb-mysql/tests/basics/buildfile b/libodb-mysql/tests/basics/buildfile
new file mode 100644
index 0000000..8899347
--- /dev/null
+++ b/libodb-mysql/tests/basics/buildfile
@@ -0,0 +1,6 @@
+# file : tests/basics/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libs = libodb-mysql%lib{odb-mysql}
+
+exe{driver}: {hxx cxx}{*} $libs
diff --git a/libodb-mysql/tests/basics/driver.cxx b/libodb-mysql/tests/basics/driver.cxx
new file mode 100644
index 0000000..81ebb19
--- /dev/null
+++ b/libodb-mysql/tests/basics/driver.cxx
@@ -0,0 +1,37 @@
+// file : tests/basics/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Basic test to make sure the library is usable. Functionality testing
+// is done in the odb-tests package.
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/exceptions.hxx>
+#include <odb/mysql/transaction.hxx>
+
+using namespace odb::mysql;
+
+int
+main ()
+{
+ {
+ std::ostringstream os;
+ database::print_usage (os);
+ assert (!os.str ().empty ());
+ }
+
+ // We can't really do much here since that would require a database. We can
+ // create a fake database object as long as we don't expect to get a valid
+ // connection.
+ //
+ database db ("john", "secret", "dummy whammy");
+
+ try
+ {
+ transaction t (db.begin ());
+ assert (false);
+ }
+ catch (const database_exception&) {}
+}
diff --git a/libodb-mysql/tests/build/.gitignore b/libodb-mysql/tests/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-mysql/tests/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-mysql/tests/build/bootstrap.build b/libodb-mysql/tests/build/bootstrap.build
new file mode 100644
index 0000000..6ee38db
--- /dev/null
+++ b/libodb-mysql/tests/build/bootstrap.build
@@ -0,0 +1,8 @@
+# file : tests/build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = # Unnamed subproject.
+
+using config
+using dist
+using test
diff --git a/libodb-mysql/tests/build/root.build b/libodb-mysql/tests/build/root.build
new file mode 100644
index 0000000..6c5a90b
--- /dev/null
+++ b/libodb-mysql/tests/build/root.build
@@ -0,0 +1,23 @@
+# file : tests/build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+cxx.std = latest
+
+using cxx
+
+hxx{*}: extension = hxx
+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
+
+# Every exe{} in this subproject is by default a test.
+#
+exe{*}: test = true
+
+# Specify the test target for cross-testing.
+#
+test.target = $cxx.target
diff --git a/libodb-mysql/tests/buildfile b/libodb-mysql/tests/buildfile
new file mode 100644
index 0000000..57588a4
--- /dev/null
+++ b/libodb-mysql/tests/buildfile
@@ -0,0 +1,4 @@
+# file : tests/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -build/}
diff --git a/libodb-oracle/.gitignore b/libodb-oracle/.gitignore
new file mode 100644
index 0000000..1c363a0
--- /dev/null
+++ b/libodb-oracle/.gitignore
@@ -0,0 +1,25 @@
+# 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/libodb-oracle/INSTALL b/libodb-oracle/INSTALL
new file mode 100644
index 0000000..51daf9e
--- /dev/null
+++ b/libodb-oracle/INSTALL
@@ -0,0 +1,6 @@
+The easiest way to build this package is with the bpkg package manager:
+
+$ bpkg build libodb-oracle
+
+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/libodb-oracle/LICENSE b/libodb-oracle/LICENSE
new file mode 100644
index 0000000..2581d22
--- /dev/null
+++ b/libodb-oracle/LICENSE
@@ -0,0 +1,21 @@
+Copyright (c) 2009-2024 Code Synthesis.
+
+Permission is granted to use, copy, modify, and distribute this
+program under the ODB Non-Commercial Use and Evaluation License
+(NCUEL) as published by Code Synthesis.
+
+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
+ODB Non-Commercial Use and Evaluation License for details.
+
+You should have received a copy of the ODB Non-Commercial Use and
+Evaluation License (normally located in the NCUEL file that is
+accompanying the distribution); if not, contact Code Synthesis
+Tools CC at info@codesynthesis.com.
diff --git a/libodb-oracle/NCUEL b/libodb-oracle/NCUEL
new file mode 100644
index 0000000..f5211c1
--- /dev/null
+++ b/libodb-oracle/NCUEL
@@ -0,0 +1,294 @@
+ ODB NON-COMMERCIAL USE AND EVALUATION LICENSE (NCUEL)
+
+INTENT
+
+The intent of this license is to allow you to use ODB with commercial
+databases, such as Oracle, Microsoft SQL Server, IBM DB/2, etc., free
+of charge non-commercially or for evaluation.
+
+Furthermore, if a commercial database has a free edition, often called
+express edition, such as Oracle Express, Microsoft SQL Server Express,
+IBM DB/2 Express-C, etc., that can be used for commercial purposes free
+of charge, then this license allows you to use ODB with such an edition
+for commercial purposes also free of charge.
+
+Note also that the development of an application that will be used for
+commercial purposes constitutes a commercial use and is not allowed,
+except with a free edition of a commercial database. However, this
+license allows you to evaluate ODB; that is, to use the Software for
+a reasonable period for the purpose of determining its suitability for
+a particular application as well as to conduct exploratory development
+or proof-of-concept prototyping.
+
+Finally, any application that uses ODB under this license, whether non-
+commercially, for evaluation, or commercially with a free edition of a
+database, is subject to the terms and conditions similar to that of the
+GPL version 2. In particular, this means that if and when you distribute
+your application, you are required to also release its source code.
+
+If you have any questions concerning this License, please contact us at:
+info@codesynthesis.com.
+
+LEGAL TERMS AND CONDITIONS
+
+This Code Synthesis Non-Commercial Use and Evaluation License
+Agreement for ODB Software ("License") is a legal agreement between
+you, the Licensee, (either an individual or a single entity) and Code
+Synthesis Tools CC ("Code Synthesis") for non-commercially using,
+copying, distributing and modifying the Software and any work derived
+from the Software, as defined hereinbelow. Any commercial use, except
+as expressly provided in Section 2.1, is subject to a different license.
+
+By using, modifying, or distributing the Software or any work derived
+from the Software, Licensee indicates acceptance of this License and
+agrees to be bound by all its terms and conditions for using, copying,
+distributing, or modifying the Software and works derived from the
+Software. If Licensee is agreeing to this License on behalf of an entity
+other than an individual person, Licensee represents that Licensee is
+binding and have the right to bind the entity to the terms and conditions
+of this agreement.
+
+These terms and conditions only apply to the ODB components that are
+explicitly licensed under this License (normally ODB runtime libraries
+for commercial databases). Other ODB components may be licensed under
+other licenses and are not affected in any way by the terms and
+conditions found in this License. Similarly, ODB components licensed
+under this License are not affected by the terms and conditions found
+in other licenses. If you are using several ODB components that are
+licensed under different licenses, you must comply with the terms and
+conditions of each such license.
+
+No rights are granted to the Software except as expressly set forth
+herein. Nothing other than this License grants Licensee permission to
+use, copy, distribute or modify the Software or any work derived from
+the Software. Licensee may not use, copy, distribute or modify the
+Software or any work derived from the Software except as expressly
+provided under this License. If Licensee does not accept the terms and
+conditions of this License, Licensee shall not use, copy, distribute
+or modify the Software.
+
+In consideration for Licensee's forbearance of commercial use of the
+Software, except as expressly provided in Section 2.1, Code Synthesis
+grants Licensee non-exclusive, royalty-free and without fees rights
+as expressly provided herein.
+
+1. DEFINITIONS.
+
+A "commercial database" is a database product that has associated
+fees and/or royalties payable for production and/or commercial use
+of the database product. Commercial databases include, but are not
+limited to, Oracle, Microsoft SQL Server, and IBM DB/2.
+
+A "free edition of a commercial database" is a special, limited edition
+of a commercial database, often called express edition, that does not
+require fees and/or royalties for production and/or commercial use.
+Free editions of commercial databases include, but are not limited to,
+Oracle Express, Microsoft SQL Server Express, and IBM DB/2 Express-C.
+
+The "Software" is one of the ODB runtime libraries for one of the
+commercial databases, including, but not limited to, demo programs,
+associated media and printed materials, and any included "on-line"
+documentation.
+
+A "work derived from the Software" is any derivative work as defined
+in the copyright law of the nation or state where rights to the work
+derived from the Software are exercisable; that is to say, a program
+which is linked with or otherwise incorporates the ODB runtime library
+or a translation, improvement, enhancement, extension or other
+modification of the Software which has sufficient originality to
+qualify in such a nation or state as a copyrightable work is a work
+derived from the Software.
+
+To "use" means to execute (i.e. run) the Software.
+
+To "copy" means to create one or more copies of the Software.
+
+To "distribute" means to broadcast, publish, transfer, post, upload,
+download or otherwise disseminate in any medium to any third party.
+
+To "modify" means to create a work derived from the Software.
+
+To "evaluate" means to use the Software for a reasonable period for
+the purpose of determining its suitability for a particular application
+as well as to conduct exploratory development or proof-of-concept
+prototyping.
+
+A "commercial use" is:
+
+(1) the use of the Software or any work derived from the Software in
+connection with, for or in aid of the generation of revenue, such as
+in the conduct of Licensee's daily business operations; or
+
+(2) any copying, distribution or modification of the Software or any
+work derived from the Software to any party where payment or other
+consideration is made in connection with such copying, distribution or
+modification, whether directly (as in payment for a copy of the
+Software) or indirectly (including but not limited to payment for some
+good or service related to the Software, or payment for some product
+or service that includes a copy of the Software "without charge").
+However, the following actions which involve payment do not in and
+of themselves constitute a commercial use:
+
+(a) posting the Software on a public access information storage and
+retrieval service for which a fee is received for retrieving
+information (such as an on-line service), provided that the fee is not
+content-dependent. Such fees which are not content dependent include,
+but are not limited to, fees which are based solely on the storage
+capacity required to store the information, and fees which are based
+solely on the time required to transfer the information from/to the
+public access information storage and retrieval service; and
+
+(b) distributing the Software on a CD-ROM, provided that the Software
+is reproduced entirely and verbatim on such CD-ROM, and provided further
+that all information on such CD-ROM may be distributed in a manner which
+does not constitute a commercial use.
+
+2. GRANT OF LICENSE.
+
+2.1. LICENSE TO USE.
+Licensee may use the Software provided that such use does not constitute
+a commercial use.
+
+Licensee may also use the Software commercially with a free edition of a
+commercial database, if such an edition is available. If Licensee
+distributes works derived from the Software and such works may be used
+commercially by third parties, Licensee must cause such commercial use
+to be limited to a free edition of a commercial database.
+
+2.2. LICENSE TO EVALUATE.
+Licensee may evaluation the Software for commercial use.
+
+2.3. LICENSE TO COPY AND DISTRIBUTE.
+Licensee may copy and distribute literal (i.e., verbatim) copies of the
+Software as Licensee receives it throughout the world, in any medium,
+provided that Licensee distributes an unmodified, easily-readable copy
+of this License with the Software, and provided further that such
+distribution does not constitute a commercial use.
+
+2.4. LICENSE TO CREATE WORKS DERIVED FROM THE SOFTWARE.
+Licensee may create works derived from the Software, provided that any
+such work derived from the Software carries prominent notices stating
+both the manner in which Licensee has created a work derived from the
+Software (for example, notices stating that the work derived from the
+Software is linked with or otherwise incorporates the ODB runtime
+library, or notices stating that the work derived from the Software
+is an enhancement to the Software which Licensee has created) and the
+date any such work derived from the Software was created.
+
+2.5. LICENSE TO COPY AND DISTRIBUTE WORKS DERIVED FROM THE SOFTWARE.
+Licensee may copy and distribute works derived from the Software
+throughout the world, provided that Licensee distributes an
+unmodified, easily-readable copy of this License with such works
+derived from the Software, and provided further that such distribution
+does not constitute a commercial use. Licensee must cause any work
+derived from the Software that Licensee distributes to be licensed as
+a whole and at no charge to all third parties under the terms of this
+License or another free/open source license that does not restrict any
+rights of any third party that would have been granted should such work
+have been licensed under this License.
+
+Any work derived from the Software must be accompanied by the complete
+corresponding machine-readable source code of such work derived from
+the Software, delivered on a medium customarily used for software
+interchange. The source code for the work derived from the Software
+means the preferred form of the work derived from the Software for
+making modifications to it. For an executable work derived from the
+Software, complete source code means all of the source code for all
+modules of the work derived from the Software, all associated
+interface definition files and all scripts used to control compilation
+and installation of all or any part of the work derived from the
+Software. However, the source code delivered need not include anything
+that is normally distributed, in either source code or binary (object-
+code) form, with major components (including but not limited to
+compilers, linkers, and kernels) of the operating system on which the
+executable work derived from the Software runs, unless that component
+itself accompanies the executable code of the work derived from the
+Software.
+
+Furthermore, if the executable code or object code of the work derived
+from the Software may be copied from a designated place, and if the
+source code of the work derived from the Software may be copied from
+the same place, then the work derived from the Software shall be
+construed as accompanied by the complete corresponding machine-readable
+source code of such work derived from the Software, even though third
+parties are not compelled to copy the source code along with the
+executable code or object code.
+
+If the work derived from the Software normally reads commands
+interactively when run, Licensee must cause the work derived from the
+Software, at each time it commences operation, to print or display an
+announcement including either a notice consisting of the verbatim
+warranty and liability provisions of this License, or a notice that
+Licensee, and not Code Synthesis provides a warranty.
+
+Licensee may not impose any further restrictions on the exercise of
+the rights granted herein by any recipient of any work derived from
+the Software.
+
+3. RESTRICTIONS.
+
+Licensee acknowledges that the Software is protected by copyright laws
+and international copyright treaties, as well as other intellectual
+property laws and treaties. The Software is licensed, not sold. All
+title and copyrights in and to the Software are owned exclusively by
+Code Synthesis.
+
+Licensee may not sublicense, assign or transfer this License, the
+Software or any work derived from the Software except as permitted by
+this License.
+
+Licensee is expressly prohibited from using, copying, distributing,
+studying the source code, or otherwise examining the Software for
+the purpose of reverse engineering or duplicating its functionality
+(unless enforcement of this restrictions is prohibited by applicable
+law).
+
+4. LIMITED WARRANTY.
+
+4.1 NO WARRANTIES.
+CODE SYNTHESIS EXPRESSLY DISCLAIMS ANY WARRANTY FOR THE SOFTWARE. THE
+SOFTWARE IS PROVIDED TO LICENSEE "AS IS," WITHOUT WARRANTY OF ANY KIND,
+EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT OF THIRD PARTY RIGHTS. THE ENTIRE RISK AS TO THE USE,
+QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH LICENSEE. SHOULD THE
+SOFTWARE PROVE DEFECTIVE, LICENSEE ASSUMES THE COST OF ALL NECESSARY
+SERVICING, REPAIR OR CORRECTION.
+
+4.2. NO LIABILITY FOR DAMAGES.
+IN NO EVENT WILL CODE SYNTHESIS, OR ANY OTHER PARTY WHO MAY COPY,
+DISTRIBUTE OR MODIFY THE SOFTWARE AS PERMITTED HEREIN, BE LIABLE FOR
+ANY GENERAL, DIRECT, INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL
+DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF
+BUSINESS PROFITS, BUSINESS INTERRUPTION, INACCURATE INFORMATION, LOSS
+OF INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OR
+INABILITY TO USE THE SOFTWARE, EVEN IF CODE SYNTHESIS OR SUCH OTHER
+PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+5. TERMINATION.
+
+Any violation or any attempt to violate any of the terms and conditions
+of this License will automatically terminate Licensee's rights under
+this License. Licensee further agrees upon such termination to cease
+any and all using, copying, distributing and modifying of the Software
+and any work derived from the Software, and further to destroy any and
+all of Licensee's copies of the Software and any work derived from the
+Software.
+
+However, parties who have received copies of the Software or copies of
+any work derived from the Software, or rights, from Licensee under this
+License will not have their licenses terminated so long as such parties
+remain in full compliance with this License.
+
+6. LICENSE SCOPE AND MODIFICATION.
+
+This License sets forth the entire agreement between Licensee and Code
+Synthesis and supersedes all prior agreements and understandings between
+the parties relating to the subject matter hereof. None of the terms of
+this License may be waived or modified except as expressly agreed in
+writing by both Licensee and Code Synthesis.
+
+7. SEVERABILITY.
+
+Should any provision of this License be declared void or unenforceable,
+the validity of the remaining provisions shall not be affected thereby.
diff --git a/libodb-oracle/NEWS b/libodb-oracle/NEWS
new file mode 120000
index 0000000..0fae0f8
--- /dev/null
+++ b/libodb-oracle/NEWS
@@ -0,0 +1 @@
+../NEWS \ No newline at end of file
diff --git a/libodb-oracle/README b/libodb-oracle/README
new file mode 100644
index 0000000..3e51928
--- /dev/null
+++ b/libodb-oracle/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 Oracle ODB runtime library. Every application
+that includes code generated for the Oracle database will need to link
+to this library.
+
+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.
+
+Send questions, bug reports, or any other feedback to the
+odb-users@codesynthesis.com mailing list.
diff --git a/libodb-oracle/build/.gitignore b/libodb-oracle/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-oracle/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-oracle/build/bootstrap.build b/libodb-oracle/build/bootstrap.build
new file mode 100644
index 0000000..8bb932b
--- /dev/null
+++ b/libodb-oracle/build/bootstrap.build
@@ -0,0 +1,10 @@
+# file : build/bootstrap.build
+# license : ODB NCUEL; see accompanying LICENSE file
+
+project = libodb-oracle
+
+using version
+using config
+using dist
+using test
+using install
diff --git a/libodb-oracle/build/export.build b/libodb-oracle/build/export.build
new file mode 100644
index 0000000..00ecc72
--- /dev/null
+++ b/libodb-oracle/build/export.build
@@ -0,0 +1,9 @@
+# file : build/export.build
+# license : ODB NCUEL; see accompanying LICENSE file
+
+$out_root/
+{
+ include odb/oracle/
+}
+
+export $out_root/odb/oracle/lib{odb-oracle}
diff --git a/libodb-oracle/build/root.build b/libodb-oracle/build/root.build
new file mode 100644
index 0000000..316c276
--- /dev/null
+++ b/libodb-oracle/build/root.build
@@ -0,0 +1,19 @@
+# file : build/root.build
+# license : ODB NCUEL; see accompanying LICENSE file
+
+config [bool] config.libodb_oracle.develop ?= false
+
+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
diff --git a/libodb-oracle/buildfile b/libodb-oracle/buildfile
new file mode 100644
index 0000000..db1aceb
--- /dev/null
+++ b/libodb-oracle/buildfile
@@ -0,0 +1,9 @@
+# file : buildfile
+# license : ODB NCUEL; see accompanying LICENSE file
+
+./: {*/ -build/} doc{INSTALL NEWS README} legal{NCUEL LICENSE} manifest
+
+# Don't install tests or the INSTALL file.
+#
+tests/: install = false
+doc{INSTALL}@./: install = false
diff --git a/libodb-oracle/manifest b/libodb-oracle/manifest
new file mode 100644
index 0000000..7a8ae9f
--- /dev/null
+++ b/libodb-oracle/manifest
@@ -0,0 +1,42 @@
+: 1
+name: libodb-oracle
+version: 2.5.0-b.26.z
+project: odb
+summary: Oracle ODB runtime library
+license: other: ODB NCUEL ; Non-Commercial Use and Evaluation License.
+license: other: proprietary ; Not free/open source.
+topics: C++, ORM, Oracle, SQL
+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
+requires: c++11
+requires: oci ; Oracle Call Interface library.
+
+# @@ TMP Bump the toolchain version to 0.17.0 after it is released.
+#
+depends: * build2 >= 0.16.0-
+depends: * bpkg >= 0.16.0-
+
+depends: libodb == $
+depends: * cli ^1.2.0- ? ($config.libodb_oracle.develop)
+
+# @@ TMP: drop develop (also drop in odb-tests/build/root.build).
+#
+tests: odb-tests == $ \
+ ? ($config.odb_tests.develop && !$defined(config.odb_tests.database)) config.odb_tests.database=oracle
+
+# @@ TMP
+#
+builds: none ; Requires proprietary Oracle Call Interface library.
+#builds: oracle
+
+#multi-build-config:
+#\
+#{ 'config.odb_tests.database=oracle sqlite' }+ odb-tests
+#;
+#Enable testing in dynamic multi-database mode.
+#\
diff --git a/libodb-oracle/odb/oracle/auto-descriptor.cxx b/libodb-oracle/odb/oracle/auto-descriptor.cxx
new file mode 100644
index 0000000..fa83086
--- /dev/null
+++ b/libodb-oracle/odb/oracle/auto-descriptor.cxx
@@ -0,0 +1,27 @@
+// file : odb/oracle/auto-descriptor.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <oci.h>
+
+#include <odb/oracle/auto-descriptor.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ static const ub4 oci_descriptor_types[] =
+ {
+ OCI_DTYPE_PARAM,
+ OCI_DTYPE_LOB,
+ OCI_DTYPE_TIMESTAMP,
+ OCI_DTYPE_INTERVAL_YM,
+ OCI_DTYPE_INTERVAL_DS
+ };
+
+ void
+ oci_descriptor_free (void* d, descriptor_type type)
+ {
+ OCIDescriptorFree (d, oci_descriptor_types[type]);
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/auto-descriptor.hxx b/libodb-oracle/odb/oracle/auto-descriptor.hxx
new file mode 100644
index 0000000..b856a2c
--- /dev/null
+++ b/libodb-oracle/odb/oracle/auto-descriptor.hxx
@@ -0,0 +1,127 @@
+// file : odb/oracle/auto-descriptor.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_AUTO_DESCRIPTOR_HXX
+#define ODB_ORACLE_AUTO_DESCRIPTOR_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/oracle-fwd.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ enum descriptor_type
+ {
+ dt_param,
+ dt_lob,
+ dt_timestamp,
+ dt_interval_ym,
+ dt_interval_ds,
+ dt_default
+ };
+
+ LIBODB_ORACLE_EXPORT void
+ oci_descriptor_free (void* descriptor, descriptor_type type);
+
+ //
+ // descriptor_type_traits
+ //
+
+ template <typename D>
+ struct default_descriptor_type_traits;
+
+ template <>
+ struct default_descriptor_type_traits<OCIParam>
+ { static const descriptor_type dtype = dt_param; };
+
+ template <>
+ struct default_descriptor_type_traits<OCILobLocator>
+ { static const descriptor_type dtype = dt_lob; };
+
+ //
+ // auto_descriptor_base
+ //
+
+ template <typename D, descriptor_type type>
+ struct auto_descriptor_base
+ {
+ static void
+ release (D* d)
+ {
+ oci_descriptor_free (d, type);
+ }
+ };
+
+ template <typename D>
+ struct auto_descriptor_base<D, dt_default>
+ {
+ static void
+ release (D* d)
+ {
+ oci_descriptor_free (d, default_descriptor_type_traits<D>::dtype);
+ }
+ };
+
+ //
+ // auto_descriptor
+ //
+
+ template <typename D, descriptor_type type = dt_default>
+ class auto_descriptor: auto_descriptor_base<D, type>
+ {
+ public:
+ auto_descriptor (D* d = 0)
+ : d_ (d)
+ {
+ }
+
+ ~auto_descriptor ()
+ {
+ if (d_ != 0)
+ this->release (d_);
+ }
+
+ operator D* () const
+ {
+ return d_;
+ }
+
+ D*&
+ get ()
+ {
+ return d_;
+ }
+
+ D*
+ get () const
+ {
+ return d_;
+ }
+
+ void
+ reset (D* d = 0)
+ {
+ if (d_ != 0)
+ this->release (d_);
+
+ d_ = d;
+ }
+
+ private:
+ auto_descriptor (const auto_descriptor&);
+ auto_descriptor& operator= (const auto_descriptor&);
+
+ protected:
+ D* d_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_AUTO_DESCRIPTOR_HXX
diff --git a/libodb-oracle/odb/oracle/auto-handle.cxx b/libodb-oracle/odb/oracle/auto-handle.cxx
new file mode 100644
index 0000000..55614d4
--- /dev/null
+++ b/libodb-oracle/odb/oracle/auto-handle.cxx
@@ -0,0 +1,37 @@
+// file : odb/oracle/auto-handle.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <oci.h>
+
+#include <odb/oracle/auto-handle.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ void
+ oci_handle_free (void* h, ub4 t)
+ {
+ OCIHandleFree (h, t);
+ }
+
+ void handle_traits<OCISvcCtx>::
+ release (OCISvcCtx* h, OCIError* e)
+ {
+ OCISessionRelease (h, e, 0, 0, OCI_DEFAULT);
+ }
+
+ void handle_traits<OCIStmt>::
+ release (OCIStmt* h, ub4 m, OCIError* e)
+ {
+ OCIStmtRelease (h, e, 0, 0, m);
+ }
+
+ const ub4 handle_type_traits<OCIEnv>::htype = OCI_HTYPE_ENV;
+ const ub4 handle_type_traits<OCIError>::htype = OCI_HTYPE_ERROR;
+ const ub4 handle_type_traits<OCISvcCtx>::htype = OCI_HTYPE_SVCCTX;
+ const ub4 handle_type_traits<OCIStmt>::htype = OCI_HTYPE_STMT;
+ const ub4 handle_type_traits<OCIAuthInfo>::htype = OCI_HTYPE_AUTHINFO;
+ const ub4 handle_type_traits<OCITrans>::htype = OCI_HTYPE_TRANS;
+ }
+}
diff --git a/libodb-oracle/odb/oracle/auto-handle.hxx b/libodb-oracle/odb/oracle/auto-handle.hxx
new file mode 100644
index 0000000..4da1cdd
--- /dev/null
+++ b/libodb-oracle/odb/oracle/auto-handle.hxx
@@ -0,0 +1,290 @@
+// file : odb/oracle/auto-handle.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_AUTO_HANDLE_HXX
+#define ODB_ORACLE_AUTO_HANDLE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/oracle-fwd.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ //
+ // handle_type_traits
+ //
+
+ template <typename H>
+ struct handle_type_traits;
+
+ template <>
+ struct LIBODB_ORACLE_EXPORT handle_type_traits<OCIEnv>
+ { static const ub4 htype; };
+
+ template <>
+ struct LIBODB_ORACLE_EXPORT handle_type_traits<OCIError>
+ { static const ub4 htype; };
+
+ template <>
+ struct LIBODB_ORACLE_EXPORT handle_type_traits<OCISvcCtx>
+ { static const ub4 htype; };
+
+ template <>
+ struct LIBODB_ORACLE_EXPORT handle_type_traits<OCIStmt>
+ { static const ub4 htype; };
+
+ template <>
+ struct LIBODB_ORACLE_EXPORT handle_type_traits<OCIAuthInfo>
+ { static const ub4 htype; };
+
+ template <>
+ struct LIBODB_ORACLE_EXPORT handle_type_traits<OCITrans>
+ { static const ub4 htype; };
+
+ //
+ // handle_traits
+ //
+
+ LIBODB_ORACLE_EXPORT void
+ oci_handle_free (void* handle, ub4 type);
+
+ template <typename H>
+ struct handle_traits
+ {
+ static void
+ release (H* h)
+ {
+ oci_handle_free (h, handle_type_traits<H>::htype);
+ }
+ };
+
+ template <>
+ struct LIBODB_ORACLE_EXPORT handle_traits<OCISvcCtx>
+ {
+ static void
+ release (OCISvcCtx*, OCIError*);
+ };
+
+ template <>
+ struct LIBODB_ORACLE_EXPORT handle_traits<OCIStmt>
+ {
+ static void
+ release (OCIStmt*, ub4 release_mode, OCIError*);
+ };
+
+ //
+ // auto_handle
+ //
+
+ template <typename H>
+ class auto_handle
+ {
+ public:
+ auto_handle (H* h = 0)
+ : h_ (h)
+ {
+ }
+
+ ~auto_handle ()
+ {
+ if (h_ != 0)
+ handle_traits<H>::release (h_);
+ }
+
+ operator H* () const
+ {
+ return h_;
+ }
+
+ H*
+ get () const
+ {
+ return h_;
+ }
+
+ H*
+ release ()
+ {
+ H* h (h_);
+ h_ = 0;
+ return h;
+ }
+
+ void
+ reset (H* h = 0)
+ {
+ if (h_ != 0)
+ handle_traits<H>::release (h_);
+
+ h_ = h;
+ }
+
+#ifdef ODB_CXX11
+ auto_handle (auto_handle&& ah) noexcept: h_ (ah.release ()) {}
+ auto_handle& operator= (auto_handle&& ah) noexcept
+ {
+ if (this != &ah)
+ reset (ah.release ());
+ return *this;
+ }
+#endif
+
+ private:
+ auto_handle (const auto_handle&);
+ auto_handle& operator= (const auto_handle&);
+
+ private:
+ H* h_;
+ };
+
+ //
+ // auto_handle<OCISvcCtx>
+ //
+
+ template <>
+ class LIBODB_ORACLE_EXPORT auto_handle<OCISvcCtx>
+ {
+ public:
+ auto_handle ()
+ : h_ (0)
+ {
+ }
+
+ auto_handle (OCISvcCtx* h, OCIError* e)
+ : h_ (h), e_ (e)
+ {
+ }
+
+ ~auto_handle ()
+ {
+ if (h_ != 0)
+ handle_traits<OCISvcCtx>::release (h_, e_);
+ }
+
+ operator OCISvcCtx* () const
+ {
+ return h_;
+ }
+
+ OCISvcCtx*
+ get () const
+ {
+ return h_;
+ }
+
+ OCISvcCtx*
+ release ()
+ {
+ OCISvcCtx* h (h_);
+ h_ = 0;
+
+ return h;
+ }
+
+ void
+ reset ()
+ {
+ if (h_ != 0)
+ {
+ handle_traits<OCISvcCtx>::release (h_, e_);
+ h_ = 0;
+ }
+ }
+
+ void
+ reset (OCISvcCtx* h, OCIError* e)
+ {
+ if (h_ != 0)
+ handle_traits<OCISvcCtx>::release (h_, e_);
+
+ h_ = h;
+ e_ = e;
+ }
+
+ private:
+ auto_handle (const auto_handle&);
+ auto_handle& operator= (const auto_handle&);
+
+ private:
+ OCISvcCtx* h_;
+ OCIError* e_;
+ };
+
+ //
+ // auto_handle<OCIStmt>
+ //
+
+ template <>
+ class LIBODB_ORACLE_EXPORT auto_handle<OCIStmt>
+ {
+ public:
+ auto_handle ()
+ : h_ (0)
+ {
+ }
+
+ auto_handle (OCIStmt* h, ub4 release_mode, OCIError* e)
+ : h_ (h), release_mode_ (release_mode), e_ (e)
+ {
+ }
+
+ ~auto_handle ()
+ {
+ if (h_ != 0)
+ handle_traits<OCIStmt>::release (h_, release_mode_, e_);
+ }
+
+ operator OCIStmt* () const
+ {
+ return h_;
+ }
+
+ OCIStmt*
+ get () const
+ {
+ return h_;
+ }
+
+ void
+ reset ()
+ {
+ if (h_ != 0)
+ {
+ handle_traits<OCIStmt>::release (h_, release_mode_, e_);
+ h_ = 0;
+ }
+ }
+
+ void
+ reset (OCIStmt* h, ub4 release_mode, OCIError* e)
+ {
+ if (h_ != 0)
+ handle_traits<OCIStmt>::release (h_, release_mode_, e_);
+
+ h_ = h;
+ release_mode_ = release_mode;
+ e_ = e;
+ }
+
+ private:
+ auto_handle (const auto_handle&);
+ auto_handle& operator= (const auto_handle&);
+
+ private:
+ OCIStmt* h_;
+ ub4 release_mode_;
+ OCIError* e_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_AUTO_HANDLE_HXX
diff --git a/libodb-oracle/odb/oracle/binding.hxx b/libodb-oracle/odb/oracle/binding.hxx
new file mode 100644
index 0000000..0b3c1d9
--- /dev/null
+++ b/libodb-oracle/odb/oracle/binding.hxx
@@ -0,0 +1,66 @@
+// file : odb/oracle/binding.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_BINDING_HXX
+#define ODB_ORACLE_BINDING_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/oracle-types.hxx>
+#include <odb/oracle/auto-handle.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ class LIBODB_ORACLE_EXPORT binding
+ {
+ public:
+ typedef oracle::bind bind_type;
+ typedef oracle::change_callback change_callback_type;
+
+ binding ()
+ : bind (0), count (0), version (0),
+ batch (0), skip (0), status (0),
+ change_callback (0) {}
+
+ binding (bind_type* b, std::size_t n)
+ : bind (b), count (n), version (0),
+ batch (1), skip (0), status (0),
+ change_callback (0)
+ {
+ }
+
+ binding (bind_type* b, std::size_t n,
+ std::size_t bt, std::size_t s, sb4* st)
+ : bind (b), count (n), version (0),
+ batch (bt), skip (s), status (st),
+ change_callback (0)
+ {
+ }
+
+ bind_type* bind;
+ std::size_t count;
+ std::size_t version;
+
+ std::size_t batch;
+ std::size_t skip;
+ sb4* status; // Batch status array.
+
+ change_callback_type* change_callback;
+
+ private:
+ binding (const binding&);
+ binding& operator= (const binding&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_BINDING_HXX
diff --git a/libodb-oracle/odb/oracle/buildfile b/libodb-oracle/odb/oracle/buildfile
new file mode 100644
index 0000000..8f6716a
--- /dev/null
+++ b/libodb-oracle/odb/oracle/buildfile
@@ -0,0 +1,138 @@
+# file : odb/oracle/buildfile
+# license : ODB NCUEL; see accompanying LICENSE file
+
+define cli: file
+cli{*}: extension = cli
+
+import int_libs = libodb%lib{odb}
+
+# Note: to build with MinGW rename oci.lib to liboci.dll.a.
+#
+if ($cc.target.class != 'windows')
+ import imp_libs = liboci%lib{clntsh}
+else
+ import imp_libs = liboci%lib{oci}
+
+lib{odb-oracle}: {hxx ixx txx cxx}{* -version} {hxx}{version} \
+ details/{hxx ixx txx cxx}{* -options} \
+ $imp_libs $int_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.
+#
+cxx.poptions =+ "-I$out_root" "-I$src_root"
+
+obja{*}: cxx.poptions += -DLIBODB_ORACLE_STATIC_BUILD
+objs{*}: cxx.poptions += -DLIBODB_ORACLE_SHARED_BUILD
+
+# Export options.
+#
+lib{odb-oracle}:
+{
+ cxx.export.poptions = "-I$out_root" "-I$src_root"
+ cxx.export.libs = $int_libs
+}
+
+liba{odb-oracle}: cxx.export.poptions += -DLIBODB_ORACLE_STATIC
+libs{odb-oracle}: cxx.export.poptions += -DLIBODB_ORACLE_SHARED
+
+# For pre-releases use the complete version to make sure they cannot be used
+# in place of another pre-release or the final version. See the version module
+# for details on the version.* variable values.
+#
+if $version.pre_release
+ lib{odb-oracle}: bin.lib.version = @"-$version.project_id"
+else
+ lib{odb-oracle}: bin.lib.version = @"-$version.major.$version.minor"
+
+develop = $config.libodb_oracle.develop
+
+## Consumption build ($develop == false).
+#
+
+# Use pregenerated versions in the consumption build.
+#
+lib{odb-oracle}: details/pregenerated/{hxx ixx cxx}{**}: include = (!$develop)
+
+if! $develop
+ cxx.poptions =+ "-I($src_base/details/pregenerated)" # Note: must come first.
+
+# Don't install pregenerated headers since they are only used internally in
+# the database implementation (also below).
+#
+details/pregenerated/{hxx ixx}{*}: install = false
+
+# Distribute pregenerated versions only in the consumption build.
+#
+details/pregenerated/{hxx ixx cxx}{*}: dist = (!$develop)
+
+#
+##
+
+## Development build ($develop == true).
+#
+
+lib{odb-oracle}: details/{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.
+#
+<details/{hxx ixx cxx}{options}>: details/cli{options} $cli
+{
+ install = false
+ dist = ($develop ? pregenerated/odb/oracle/details/ : false)
+
+ # Symlink the generated code in src for convenience of development.
+ #
+ backlink = true
+}
+%
+if $develop
+{{
+ options = --include-with-brackets --include-prefix odb/oracle/details \
+ --guard-prefix LIBODB_ORACLE_DETAILS --generate-file-scanner \
+ --cli-namespace odb::oracle::details::cli --long-usage \
+ --generate-specifier --no-combined-flags
+
+ $cli $options -o $out_base/details/ $path($<[0])
+
+ # If the result differs from the pregenerated version, copy it over.
+ #
+ d = [dir_path] $src_base/details/pregenerated/odb/oracle/details/
+
+ if diff $d/options.hxx $path($>[0]) >- && \
+ diff $d/options.ixx $path($>[1]) >- && \
+ diff $d/options.cxx $path($>[2]) >-
+ exit
+ end
+
+ cp $path($>[0]) $d/options.hxx
+ cp $path($>[1]) $d/options.ixx
+ cp $path($>[2]) $d/options.cxx
+}}
+
+# Install into the odb/oracle/ subdirectory of, say, /usr/include/
+# recreating subdirectories.
+#
+install_include = [dir_path] include/odb/oracle/
+
+{hxx ixx txx}{*}:
+{
+ install = $install_include
+ install.subdirs = true
+}
diff --git a/libodb-oracle/odb/oracle/connection-factory.cxx b/libodb-oracle/odb/oracle/connection-factory.cxx
new file mode 100644
index 0000000..9ad9474
--- /dev/null
+++ b/libodb-oracle/odb/oracle/connection-factory.cxx
@@ -0,0 +1,159 @@
+// file : odb/oracle/connection-factory.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/oracle/connection-factory.hxx>
+#include <odb/oracle/exceptions.hxx>
+
+#include <odb/details/lock.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ using namespace details;
+
+ namespace oracle
+ {
+ // new_connection_factory
+ //
+ connection_ptr new_connection_factory::
+ connect ()
+ {
+ return connection_ptr (new (shared) connection (*this));
+ }
+
+ // connection_pool_factory
+ //
+ connection_pool_factory::pooled_connection_ptr connection_pool_factory::
+ create ()
+ {
+ return pooled_connection_ptr (new (shared) pooled_connection (*this));
+ }
+
+ connection_pool_factory::
+ ~connection_pool_factory ()
+ {
+ // Wait for all the connections currently in use to return to
+ // the pool.
+ //
+ lock l (mutex_);
+ while (in_use_ != 0)
+ {
+ waiters_++;
+ cond_.wait (l);
+ waiters_--;
+ }
+ }
+
+ connection_ptr connection_pool_factory::
+ connect ()
+ {
+ lock l (mutex_);
+
+ while (true)
+ {
+ // See if we have a spare connection.
+ //
+ if (connections_.size () != 0)
+ {
+ shared_ptr<pooled_connection> c (connections_.back ());
+ connections_.pop_back ();
+
+ c->callback_ = &c->cb_;
+ in_use_++;
+ return c;
+ }
+
+ // See if we can create a new one.
+ //
+ if (max_ == 0 || in_use_ < max_)
+ {
+ shared_ptr<pooled_connection> c (create ());
+ c->callback_ = &c->cb_;
+ in_use_++;
+ return c;
+ }
+
+ // Wait until someone releases a connection.
+ //
+ waiters_++;
+ cond_.wait (l);
+ waiters_--;
+ }
+ }
+
+ void connection_pool_factory::
+ database (database_type& db)
+ {
+ bool first (db_ == 0);
+
+ connection_factory::database (db);
+
+ if (!first)
+ return;
+
+ if (min_ > 0)
+ {
+ connections_.reserve (min_);
+
+ for (size_t i (0); i < min_; ++i)
+ connections_.push_back (create ());
+ }
+ }
+
+ bool connection_pool_factory::
+ release (pooled_connection* c)
+ {
+ c->callback_ = 0;
+
+ lock l (mutex_);
+
+ // Determine if we need to keep or free this connection.
+ //
+ bool keep (!c->failed () &&
+ (waiters_ != 0 ||
+ min_ == 0 ||
+ (connections_.size () + in_use_ <= min_)));
+
+ in_use_--;
+
+ if (keep)
+ {
+ connections_.push_back (pooled_connection_ptr (inc_ref (c)));
+ connections_.back ()->recycle ();
+ }
+
+ if (waiters_ != 0)
+ cond_.signal ();
+
+ return !keep;
+ }
+
+ //
+ // connection_pool_factory::pooled_connection
+ //
+
+ connection_pool_factory::pooled_connection::
+ pooled_connection (connection_pool_factory& f)
+ : connection (f)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ connection_pool_factory::pooled_connection::
+ pooled_connection (connection_pool_factory& f, OCISvcCtx* handle)
+ : connection (f, handle)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ bool connection_pool_factory::pooled_connection::
+ zero_counter (void* arg)
+ {
+ pooled_connection* c (static_cast<pooled_connection*> (arg));
+ return static_cast<connection_pool_factory&> (c->factory_).release (c);
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/connection-factory.hxx b/libodb-oracle/odb/oracle/connection-factory.hxx
new file mode 100644
index 0000000..835759e
--- /dev/null
+++ b/libodb-oracle/odb/oracle/connection-factory.hxx
@@ -0,0 +1,134 @@
+// file : odb/oracle/connection-factory.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_CONNECTION_FACTORY_HXX
+#define ODB_ORACLE_CONNECTION_FACTORY_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cstddef> // std::size_t
+#include <cassert>
+
+#include <odb/details/mutex.hxx>
+#include <odb/details/condition.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx>
+#include <odb/oracle/connection.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ class LIBODB_ORACLE_EXPORT new_connection_factory: public connection_factory
+ {
+ public:
+ new_connection_factory () {}
+
+ virtual connection_ptr
+ connect ();
+
+ private:
+ new_connection_factory (const new_connection_factory&);
+ new_connection_factory& operator= (const new_connection_factory&);
+ };
+
+ class LIBODB_ORACLE_EXPORT connection_pool_factory:
+ public connection_factory
+ {
+ public:
+ // The max_connections argument specifies the maximum number of
+ // concurrent connections this pool will maintain. If this value
+ // is 0 then the pool will create a new connection every time all
+ // of the existing connections are in use.
+ //
+ // The min_connections argument specifies the minimum number of
+ // connections that should be maintained by the pool. If the
+ // number of connections maintained by the pool exceeds this
+ // number and there are no active waiters for a new connection,
+ // then the pool will release the excess connections. If this
+ // value is 0 then the pool will maintain all the connections
+ // that were ever created.
+ //
+ connection_pool_factory (std::size_t max_connections = 0,
+ std::size_t min_connections = 0)
+ : max_ (max_connections),
+ min_ (min_connections),
+ in_use_ (0),
+ waiters_ (0),
+ cond_ (mutex_)
+ {
+ // max_connections == 0 means unlimited.
+ //
+ assert (max_connections == 0 || max_connections >= min_connections);
+ }
+
+ virtual connection_ptr
+ connect ();
+
+ virtual void
+ database (database_type&);
+
+ virtual
+ ~connection_pool_factory ();
+
+ private:
+ connection_pool_factory (const connection_pool_factory&);
+ connection_pool_factory& operator= (const connection_pool_factory&);
+
+ protected:
+ class LIBODB_ORACLE_EXPORT pooled_connection: public connection
+ {
+ public:
+ pooled_connection (connection_pool_factory&);
+ pooled_connection (connection_pool_factory&, OCISvcCtx*);
+
+ private:
+ static bool
+ zero_counter (void*);
+
+ private:
+ friend class connection_pool_factory;
+
+ shared_base::refcount_callback cb_;
+ };
+
+ friend class pooled_connection;
+
+ typedef details::shared_ptr<pooled_connection> pooled_connection_ptr;
+ typedef std::vector<pooled_connection_ptr> connections;
+
+ // This function is called whenever the pool needs to create a new
+ // connection.
+ //
+ virtual pooled_connection_ptr
+ create ();
+
+ protected:
+ // Return true if the connection should be deleted, false otherwise.
+ //
+ bool
+ release (pooled_connection*);
+
+ protected:
+ const std::size_t max_;
+ const std::size_t min_;
+
+ std::size_t in_use_; // Number of connections currently in use.
+ std::size_t waiters_; // Number of threads waiting for a connection.
+
+ connections connections_;
+
+ details::mutex mutex_;
+ details::condition cond_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_CONNECTION_FACTORY_HXX
diff --git a/libodb-oracle/odb/oracle/connection.cxx b/libodb-oracle/odb/oracle/connection.cxx
new file mode 100644
index 0000000..5c2f7a7
--- /dev/null
+++ b/libodb-oracle/odb/oracle/connection.cxx
@@ -0,0 +1,175 @@
+// file : odb/oracle/connection.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <oci.h>
+
+#include <string>
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/connection.hxx>
+#include <odb/oracle/transaction.hxx>
+#include <odb/oracle/statement.hxx>
+#include <odb/oracle/error.hxx>
+#include <odb/oracle/exceptions.hxx>
+#include <odb/oracle/auto-descriptor.hxx>
+#include <odb/oracle/statement-cache.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace oracle
+ {
+ connection::
+ connection (connection_factory& cf)
+ : odb::connection (cf),
+ failed_ (false),
+ statement_cache_ (new statement_cache_type (*this)),
+ lob_buffer_ (0)
+ {
+ sword r (0);
+
+ database_type& db (database ());
+
+ {
+ OCIError* e (0);
+ r = OCIHandleAlloc (db.environment (),
+ reinterpret_cast<void**> (&e),
+ OCI_HTYPE_ERROR,
+ 0,
+ 0);
+
+ if (r != OCI_SUCCESS)
+ throw invalid_oci_handle ();
+
+ error_.reset (e);
+ }
+
+ auto_handle<OCIAuthInfo> auth_info;
+ {
+ OCIAuthInfo* a (0);
+ r = OCIHandleAlloc (db.environment (),
+ reinterpret_cast<void**> (&a),
+ OCI_HTYPE_AUTHINFO,
+ 0,
+ 0);
+
+ if (r != OCI_SUCCESS)
+ throw invalid_oci_handle ();
+
+ auth_info.reset (a);
+ }
+
+ r = OCIAttrSet (
+ auth_info,
+ OCI_HTYPE_AUTHINFO,
+ reinterpret_cast<OraText*> (const_cast<char*> (db.user ().c_str ())),
+ static_cast <ub4> (db.user ().size ()),
+ OCI_ATTR_USERNAME,
+ error_);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (error_, r);
+
+ r = OCIAttrSet (
+ auth_info,
+ OCI_HTYPE_AUTHINFO,
+ reinterpret_cast<OraText*> (
+ const_cast<char*> (db.password ().c_str ())),
+ static_cast<ub4> (db.password ().size ()),
+ OCI_ATTR_PASSWORD,
+ error_);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (error_, r);
+
+ {
+ OCISvcCtx* s (0);
+
+ r = OCISessionGet (
+ db.environment (),
+ error_,
+ &s,
+ auth_info,
+ reinterpret_cast<OraText*> (const_cast<char*> (db.db ().c_str ())),
+ static_cast<ub4> (db.db ().size ()),
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ OCI_SESSGET_STMTCACHE);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (error_, r);
+
+ handle_.reset (s, error_);
+ }
+ }
+
+ connection::
+ connection (connection_factory& cf, OCISvcCtx* handle)
+ : odb::connection (cf),
+ failed_ (false),
+ statement_cache_ (new statement_cache_type (*this)),
+ lob_buffer_ (0)
+ {
+ sword r (0);
+
+ database_type& db (database ());
+
+ {
+ OCIError* e (0);
+ r = OCIHandleAlloc (db.environment (),
+ reinterpret_cast<void**> (&e),
+ OCI_HTYPE_ERROR,
+ 0,
+ 0);
+
+ if (r != OCI_SUCCESS)
+ throw invalid_oci_handle ();
+
+ error_.reset (e);
+ }
+
+ handle_.reset (handle, error_);
+ }
+
+ connection::
+ ~connection ()
+ {
+ // Deallocate prepared statements before we close the connection.
+ //
+ recycle ();
+ clear_prepared_map ();
+ statement_cache_.reset ();
+ }
+
+ transaction_impl* connection::
+ begin ()
+ {
+ return new transaction_impl (connection_ptr (inc_ref (this)));
+ }
+
+ unsigned long long connection::
+ execute (const char* s, std::size_t n)
+ {
+ generic_statement st (*this, string (s, n));
+ return st.execute ();
+ }
+
+ // connection_factory
+ //
+ connection_factory::
+ ~connection_factory ()
+ {
+ }
+
+ void connection_factory::
+ database (database_type& db)
+ {
+ odb::connection_factory::db_ = &db;
+ db_ = &db;
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/connection.hxx b/libodb-oracle/odb/oracle/connection.hxx
new file mode 100644
index 0000000..4192728
--- /dev/null
+++ b/libodb-oracle/odb/oracle/connection.hxx
@@ -0,0 +1,189 @@
+// file : odb/oracle/connection.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_CONNECTION_HXX
+#define ODB_ORACLE_CONNECTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/connection.hxx>
+
+#include <odb/details/buffer.hxx>
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/unique-ptr.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx>
+#include <odb/oracle/query.hxx>
+#include <odb/oracle/tracer.hxx>
+#include <odb/oracle/transaction-impl.hxx>
+#include <odb/oracle/auto-handle.hxx>
+#include <odb/oracle/oracle-fwd.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ class statement_cache;
+ class connection_factory;
+
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+
+ class LIBODB_ORACLE_EXPORT connection: public odb::connection
+ {
+ public:
+ typedef oracle::statement_cache statement_cache_type;
+ typedef oracle::database database_type;
+
+ virtual
+ ~connection ();
+
+ connection (connection_factory&);
+ connection (connection_factory&, OCISvcCtx* handle);
+
+ database_type&
+ database ();
+
+ public:
+ virtual transaction_impl*
+ begin ();
+
+ public:
+ using odb::connection::execute;
+
+ virtual unsigned long long
+ execute (const char* statement, std::size_t length);
+
+ // Query preparation.
+ //
+ public:
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const oracle::query_base&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const odb::query_base&);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef oracle::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ using odb::connection::tracer;
+
+ public:
+ bool
+ failed () const
+ {
+ return failed_;
+ }
+
+ void
+ mark_failed ()
+ {
+ failed_ = true;
+ }
+
+ public:
+ OCISvcCtx*
+ handle ()
+ {
+ return handle_;
+ }
+
+ OCIError*
+ error_handle ()
+ {
+ return error_;
+ }
+
+ statement_cache_type&
+ statement_cache ()
+ {
+ return *statement_cache_;
+ }
+
+ details::buffer&
+ lob_buffer ()
+ {
+ return lob_buffer_;
+ }
+
+ private:
+ connection (const connection&);
+ connection& operator= (const connection&);
+
+ private:
+ friend class transaction_impl; // invalidate_results()
+
+ private:
+ // It is important that the error_ member is declared before the
+ // handle_ member as handle_ depends on error_ during destruction.
+ //
+ auto_handle<OCIError> error_;
+
+ auto_handle<OCISvcCtx> handle_;
+ bool failed_;
+
+ details::unique_ptr<statement_cache_type> statement_cache_;
+ details::buffer lob_buffer_;
+ };
+
+ class LIBODB_ORACLE_EXPORT connection_factory:
+ public odb::connection_factory
+ {
+ public:
+ typedef oracle::database database_type;
+
+ virtual void
+ database (database_type&);
+
+ database_type&
+ database () {return *db_;}
+
+ virtual connection_ptr
+ connect () = 0;
+
+ virtual
+ ~connection_factory ();
+
+ connection_factory (): db_ (0) {}
+
+ // Needed to break the circular connection_factory-database dependency
+ // (odb::connection_factory has the odb::database member).
+ //
+ protected:
+ database_type* db_;
+ };
+ }
+}
+
+#include <odb/oracle/connection.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_CONNECTION_HXX
diff --git a/libodb-oracle/odb/oracle/connection.ixx b/libodb-oracle/odb/oracle/connection.ixx
new file mode 100644
index 0000000..5abaddc
--- /dev/null
+++ b/libodb-oracle/odb/oracle/connection.ixx
@@ -0,0 +1,44 @@
+// file : odb/oracle/connection.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace oracle
+ {
+ inline database& connection::
+ database ()
+ {
+ return static_cast<connection_factory&> (factory_).database ();
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const oracle::query_base& q)
+ {
+ return query_<T, id_oracle>::call (*this, n, q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return prepare_query<T> (n, oracle::query_base (q));
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/container-statements.hxx b/libodb-oracle/odb/oracle/container-statements.hxx
new file mode 100644
index 0000000..23e1564
--- /dev/null
+++ b/libodb-oracle/odb/oracle/container-statements.hxx
@@ -0,0 +1,345 @@
+// file : odb/oracle/container-statements.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_CONTAINER_STATEMENTS_HXX
+#define ODB_ORACLE_CONTAINER_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/oracle-types.hxx>
+#include <odb/oracle/statement.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ class connection;
+
+ // Template argument is the generated abstract container traits type.
+ // That is, it doesn't need to provide column counts and statements.
+ //
+ template <typename T>
+ class container_statements
+ {
+ public:
+ typedef T traits;
+
+ typedef typename traits::data_image_type data_image_type;
+ typedef typename traits::functions_type functions_type;
+
+ typedef oracle::insert_statement insert_statement_type;
+ typedef oracle::select_statement select_statement_type;
+ typedef oracle::delete_statement delete_statement_type;
+
+ typedef oracle::connection connection_type;
+
+ container_statements (connection_type&, binding& id_binding);
+
+ connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // Functions.
+ //
+ functions_type&
+ functions ()
+ {
+ return functions_;
+ }
+
+ // Schema version.
+ //
+ const schema_version_migration&
+ version_migration () const {return *svm_;}
+
+ void
+ version_migration (const schema_version_migration& svm) {svm_ = &svm;}
+
+ // Id image binding (external).
+ //
+ const binding&
+ id_binding ()
+ {
+ return id_binding_;
+ }
+
+ // Data image. The image is split into the id (that comes as a
+ // binding) and index/key plus value which are in data_image_type.
+ // The select binding is a subset of the full binding (no id).
+ //
+ data_image_type&
+ data_image ()
+ {
+ return data_image_;
+ }
+
+ bind*
+ data_bind ()
+ {
+ return insert_image_binding_.bind;
+ }
+
+ bool
+ data_binding_test_version () const
+ {
+ return data_id_binding_version_ != id_binding_.version ||
+ data_image_version_ != data_image_.version ||
+ insert_image_binding_.version == 0;
+ }
+
+ void
+ data_binding_update_version ()
+ {
+ data_id_binding_version_ = id_binding_.version;
+ data_image_version_ = data_image_.version;
+ insert_image_binding_.version++;
+ select_image_binding_.version++;
+ }
+
+ //
+ // Statements.
+ //
+
+ insert_statement_type&
+ insert_statement ()
+ {
+ if (insert_ == 0)
+ insert_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ insert_text_,
+ versioned_, // Process if versioned.
+ insert_image_binding_,
+ 0));
+
+ return *insert_;
+ }
+
+ select_statement_type&
+ select_statement ()
+ {
+ if (select_ == 0)
+ select_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ select_text_,
+ versioned_, // Process if versioned.
+ false, // Don't optimize.
+ id_binding_,
+ select_image_binding_,
+ 4096)); // Hardcode a 4kB LOB prefetch size.
+
+ return *select_;
+ }
+
+ delete_statement_type&
+ delete_statement ()
+ {
+ if (delete_ == 0)
+ delete_.reset (
+ new (details::shared) delete_statement_type (
+ conn_, delete_text_, id_binding_));
+
+ return *delete_;
+ }
+
+ private:
+ container_statements (const container_statements&);
+ container_statements& operator= (const container_statements&);
+
+ protected:
+ connection_type& conn_;
+ binding& id_binding_;
+
+ functions_type functions_;
+
+ data_image_type data_image_;
+ std::size_t data_image_version_;
+ std::size_t data_id_binding_version_;
+
+ binding insert_image_binding_;
+ binding select_image_binding_;
+
+ const char* insert_text_;
+ const char* select_text_;
+ const char* delete_text_;
+
+ bool versioned_;
+ const schema_version_migration* svm_;
+
+ details::shared_ptr<insert_statement_type> insert_;
+ details::shared_ptr<select_statement_type> select_;
+ details::shared_ptr<delete_statement_type> delete_;
+ };
+
+ template <typename T>
+ class smart_container_statements: public container_statements<T>
+ {
+ public:
+ typedef T traits;
+ typedef typename traits::cond_image_type cond_image_type;
+
+ typedef oracle::update_statement update_statement_type;
+ typedef oracle::delete_statement delete_statement_type;
+
+ typedef oracle::connection connection_type;
+
+ smart_container_statements (connection_type&, binding& id_binding);
+
+ // Condition image. The image is split into the id (that comes as
+ // a binding) and index/key/value which is in cond_image_type.
+ //
+ cond_image_type&
+ cond_image ()
+ {
+ return cond_image_;
+ }
+
+ bind*
+ cond_bind ()
+ {
+ return cond_image_binding_.bind;
+ }
+
+ bool
+ cond_binding_test_version () const
+ {
+ return cond_id_binding_version_ != this->id_binding_.version ||
+ cond_image_version_ != cond_image_.version ||
+ cond_image_binding_.version == 0;
+ }
+
+ void
+ cond_binding_update_version ()
+ {
+ cond_id_binding_version_ = this->id_binding_.version;
+ cond_image_version_ = cond_image_.version;
+ cond_image_binding_.version++;
+ }
+
+ // Update image. The image is split as follows: value comes
+ // from the data image, id comes as binding, and index/key
+ // comes from the condition image.
+ //
+ bind*
+ update_bind ()
+ {
+ return update_image_binding_.bind;
+ }
+
+ bool
+ update_binding_test_version () const
+ {
+ return update_id_binding_version_ != this->id_binding_.version ||
+ update_cond_image_version_ != cond_image_.version ||
+ update_data_image_version_ != this->data_image_.version ||
+ update_image_binding_.version == 0;
+ }
+
+ void
+ update_binding_update_version ()
+ {
+ update_id_binding_version_ = this->id_binding_.version;
+ update_cond_image_version_ = cond_image_.version;
+ update_data_image_version_ = this->data_image_.version;
+ update_image_binding_.version++;
+ }
+
+ //
+ // Statements.
+ //
+
+ delete_statement_type&
+ delete_statement ()
+ {
+ if (this->delete_ == 0)
+ this->delete_.reset (
+ new (details::shared) delete_statement_type (
+ this->conn_, this->delete_text_, this->cond_image_binding_));
+
+ return *this->delete_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ this->conn_,
+ update_text_,
+ this->versioned_, // Process if versioned.
+ update_image_binding_));
+
+ return *update_;
+ }
+
+ protected:
+ cond_image_type cond_image_;
+ std::size_t cond_image_version_;
+ std::size_t cond_id_binding_version_;
+ binding cond_image_binding_;
+
+ std::size_t update_id_binding_version_;
+ std::size_t update_cond_image_version_;
+ std::size_t update_data_image_version_;
+ binding update_image_binding_;
+
+ const char* update_text_;
+
+ details::shared_ptr<update_statement_type> update_;
+ };
+
+ // Template argument is the generated concrete container traits type.
+ //
+ template <typename T>
+ class container_statements_impl: public T::statements_type
+ {
+ public:
+ typedef T traits;
+ typedef typename T::statements_type base;
+ typedef oracle::connection connection_type;
+
+ container_statements_impl (connection_type&, binding&);
+
+ private:
+ container_statements_impl (const container_statements_impl&);
+ container_statements_impl& operator= (const container_statements_impl&);
+
+ private:
+ bind data_image_bind_[traits::data_column_count];
+ };
+
+ template <typename T>
+ class smart_container_statements_impl: public container_statements_impl<T>
+ {
+ public:
+ typedef T traits;
+ typedef oracle::connection connection_type;
+
+ smart_container_statements_impl (connection_type&, binding&);
+
+ private:
+ bind cond_image_bind_[traits::cond_column_count];
+ bind update_image_bind_[traits::value_column_count +
+ traits::cond_column_count];
+ };
+ }
+}
+
+#include <odb/oracle/container-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_CONTAINER_STATEMENTS_HXX
diff --git a/libodb-oracle/odb/oracle/container-statements.txx b/libodb-oracle/odb/oracle/container-statements.txx
new file mode 100644
index 0000000..6a6c53b
--- /dev/null
+++ b/libodb-oracle/odb/oracle/container-statements.txx
@@ -0,0 +1,96 @@
+// file : odb/oracle/container-statements.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace oracle
+ {
+ // container_statements
+ //
+ template <typename T>
+ container_statements<T>::
+ container_statements (connection_type& conn, binding& id)
+ : conn_ (conn),
+ id_binding_ (id),
+ functions_ (this),
+ insert_image_binding_ (0, 0), // Initialized by impl.
+ select_image_binding_ (0, 0), // Initialized by impl.
+ svm_ (0)
+ {
+ functions_.insert_ = &traits::insert;
+ functions_.select_ = &traits::select;
+ functions_.delete__ = &traits::delete_;
+
+ data_image_.version = 0;
+ data_image_version_ = 0;
+ data_id_binding_version_ = 0;
+ }
+
+ // smart_container_statements
+ //
+ template <typename T>
+ smart_container_statements<T>::
+ smart_container_statements (connection_type& conn, binding& id)
+ : container_statements<T> (conn, id),
+ cond_image_binding_ (0, 0), // Initialized by impl.
+ update_image_binding_ (0, 0) // Initialized by impl.
+ {
+ this->functions_.update_ = &traits::update;
+
+ cond_image_.version = 0;
+ cond_image_version_ = 0;
+ cond_id_binding_version_ = 0;
+
+ update_id_binding_version_ = 0;
+ update_cond_image_version_ = 0;
+ update_data_image_version_ = 0;
+ }
+
+ // container_statements_impl
+ //
+ template <typename T>
+ container_statements_impl<T>::
+ container_statements_impl (connection_type& conn, binding& id)
+ : base (conn, id)
+ {
+ this->insert_image_binding_.bind = data_image_bind_;
+ this->insert_image_binding_.count = traits::data_column_count;
+
+ this->select_image_binding_.bind = data_image_bind_ +
+ traits::id_column_count;
+ this->select_image_binding_.count = traits::data_column_count -
+ traits::id_column_count;
+
+ std::memset (data_image_bind_, 0, sizeof (data_image_bind_));
+
+ this->insert_text_ = traits::insert_statement;
+ this->select_text_ = traits::select_statement;
+ this->delete_text_ = traits::delete_statement;
+
+ this->versioned_ = traits::versioned;
+ }
+
+ // smart_container_statements_impl
+ //
+ template <typename T>
+ smart_container_statements_impl<T>::
+ smart_container_statements_impl (connection_type& conn, binding& id)
+ : container_statements_impl<T> (conn, id)
+ {
+ this->cond_image_binding_.bind = cond_image_bind_;
+ this->cond_image_binding_.count = traits::cond_column_count;
+
+ this->update_image_binding_.bind = update_image_bind_;
+ this->update_image_binding_.count = traits::value_column_count +
+ traits::cond_column_count;
+
+ std::memset (cond_image_bind_, 0, sizeof (cond_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+
+ this->update_text_ = traits::update_statement;
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/database.cxx b/libodb-oracle/odb/oracle/database.cxx
new file mode 100644
index 0000000..3b720be
--- /dev/null
+++ b/libodb-oracle/odb/oracle/database.cxx
@@ -0,0 +1,352 @@
+// file : odb/oracle/database.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <oci.h>
+
+#include <sstream>
+
+#include <odb/oracle/traits.hxx>
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/statement.hxx>
+#include <odb/oracle/transaction.hxx>
+#include <odb/oracle/exceptions.hxx>
+#include <odb/oracle/error.hxx>
+
+#include <odb/oracle/details/options.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace oracle
+ {
+ using odb::details::transfer_ptr;
+
+ database::
+ database (const string& user,
+ const string& password,
+ const string& db,
+ ub2 charset,
+ ub2 ncharset,
+ OCIEnv* environment,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_oracle),
+ user_ (user),
+ password_ (password),
+ db_ (db),
+ port_ (0),
+ charset_ (charset),
+ ncharset_ (ncharset),
+ environment_ (environment),
+ factory_ (factory.transfer ())
+ {
+ if (environment_ == 0)
+ {
+ sword s (OCIEnvNlsCreate (&environment_,
+ OCI_THREADED,
+ 0, 0, 0, 0, 0, 0,
+ charset,
+ ncharset));
+
+ if (s == OCI_ERROR)
+ translate_error (environment_);
+
+ auto_environment_.reset (environment_);
+ }
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (const string& user,
+ const string& password,
+ const string& service,
+ const string& host,
+ unsigned int port,
+ ub2 charset,
+ ub2 ncharset,
+ OCIEnv* environment,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_oracle),
+ user_ (user),
+ password_ (password),
+ service_ (service),
+ host_ (host),
+ port_ (port),
+ charset_ (charset),
+ ncharset_ (ncharset),
+ environment_ (environment),
+ factory_ (factory.transfer ())
+ {
+ if (environment_ == 0)
+ {
+ sword s (OCIEnvNlsCreate (&environment_,
+ OCI_THREADED,
+ 0, 0, 0, 0, 0, 0,
+ charset,
+ ncharset));
+
+ if (s == OCI_ERROR)
+ translate_error (environment_);
+
+ auto_environment_.reset (environment_);
+ }
+
+ ostringstream ss;
+
+ if (!host.empty ())
+ {
+ ss << "//" << host_;
+
+ if (port != 0)
+ ss << ":" << port;
+ }
+
+ if (!service_.empty ())
+ {
+ if (!host.empty ())
+ ss << "/" << service_;
+ else
+ ss << service_;
+ }
+
+ db_ = ss.str ();
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (int& argc,
+ char* argv[],
+ bool erase,
+ ub2 charset,
+ ub2 ncharset,
+ OCIEnv* environment,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_oracle),
+ port_ (0),
+ charset_ (charset),
+ ncharset_ (ncharset),
+ environment_ (environment),
+ factory_ (factory.transfer ())
+ {
+ if (environment_ == 0)
+ {
+ sword s (OCIEnvNlsCreate (&environment_,
+ OCI_THREADED,
+ 0, 0, 0, 0, 0, 0,
+ charset,
+ ncharset));
+
+ if (s == OCI_ERROR)
+ translate_error (environment_);
+
+ auto_environment_.reset (environment_);
+ }
+
+ using namespace details;
+
+ try
+ {
+ cli::argv_file_scanner scan (argc, argv, "--options-file", erase);
+ options ops (scan, cli::unknown_mode::skip, cli::unknown_mode::skip);
+
+ if (ops.user_specified ())
+ user_ = ops.user ();
+
+ if (ops.password_specified ())
+ password_ = ops.password ();
+
+ if (ops.database_specified ())
+ {
+ if (ops.host_specified () ||
+ ops.port_specified () ||
+ ops.service_specified ())
+
+ throw cli_exception ("--host, --port, or --service options "
+ "cannot be specified together with "
+ "--database option");
+ db_ = ops.database ();
+ }
+ else
+ {
+ bool host_present (false);
+ ostringstream oss;
+
+ if (ops.host_specified () && !ops.host ().empty ())
+ {
+ host_present = true;
+
+ host_ = ops.host ();
+ oss << "//" << host_;
+
+ if (ops.port_specified ())
+ {
+ port_ = ops.port ();
+
+ if (port_ != 0)
+ oss << ":" << port_;
+ }
+ }
+
+ if (ops.service_specified () && !ops.service ().empty ())
+ {
+ service_ = ops.service ();
+
+ if (host_present)
+ oss << "/" << service_;
+ else
+ oss << service_;
+ }
+
+ db_ = oss.str ();
+ }
+ }
+ catch (const cli::exception& e)
+ {
+ ostringstream oss;
+ oss << e;
+ throw cli_exception (oss.str ());
+ }
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ void database::
+ print_usage (ostream& os)
+ {
+ details::options::print_usage (os);
+ }
+
+ database::
+ ~database ()
+ {
+ }
+
+ transaction_impl* database::
+ begin ()
+ {
+ return new transaction_impl (*this);
+ }
+
+ odb::connection* database::
+ connection_ ()
+ {
+ connection_ptr c (factory_->connect ());
+ return c.release ();
+ }
+
+ const database::schema_version_info& database::
+ load_schema_version (const string& name) const
+ {
+ schema_version_info& svi (schema_version_map_[name]);
+
+ // Construct the SELECT statement text.
+ //
+ string text ("SELECT \"version\", \"migration\" FROM ");
+
+ if (!svi.version_table.empty ())
+ text += svi.version_table; // Already quoted.
+ else if (!schema_version_table_.empty ())
+ text += schema_version_table_; // Already quoted.
+ else
+ text += "\"schema_version\"";
+
+ text += " WHERE \"name\" = :1";
+
+ // Bind parameters and results. If the schema name is empty, replace
+ // it with a single space to workaround the VARCHAR2 empty/NULL issue.
+ //
+ string n (name.empty () ? string (" ") : name);
+ ub2 psize[1] = {static_cast<ub2> (n.size ())};
+ sb2 pind[1] = {0};
+ bind pbind[1] = {{bind::string,
+ const_cast<char*> (n.c_str ()),
+ &psize[0],
+ psize[0],
+ &pind[0],
+ 0}};
+ binding param (pbind, 1);
+ param.version++;
+
+ char version[12];
+ unsigned int migration;
+ ub2 rsize[1];
+ sb2 rind[2];
+ bind rbind[2] = {
+ {bind::number,
+ version,
+ &rsize[0],
+ static_cast<ub4> (sizeof (version)),
+ &rind[0],
+ 0},
+
+ {bind::uinteger, &migration, 0, 4, &rind[1], 0}
+ };
+ binding result (rbind, 2);
+ result.version++;
+
+ // If we are not in transaction, then OCI will start an implicit one
+ // but only if we try to modify anything. Since our statement is read-
+ // only, we can run without a transaction.
+ //
+ connection_ptr cp;
+ if (!transaction::has_current ())
+ cp = factory_->connect ();
+
+ oracle::connection& c (
+ cp != 0
+ ? *cp
+ : transaction::current ().connection (const_cast<database&> (*this)));
+
+ try
+ {
+ select_statement st (c,
+ text,
+ false, // Don't process.
+ false, // Don't optimize.
+ param,
+ result);
+ st.execute ();
+ auto_result ar (st);
+
+ switch (st.fetch ())
+ {
+ case select_statement::success:
+ {
+ value_traits<unsigned long long, id_big_int>::set_value (
+ svi.version, version, rsize[0], rind[0] == -1);
+ svi.migration = migration != 0;
+ assert (st.fetch () == select_statement::no_data);
+ break;
+ }
+ case select_statement::no_data:
+ {
+ svi.version = 0; // No schema.
+ break;
+ }
+ }
+ }
+ catch (const database_exception& e)
+ {
+ // Detect the case where there is no version table.
+ //
+ if (e.size () != 0 && e.begin ()->error () == 942)
+ svi.version = 0; // No schema.
+ else
+ throw;
+ }
+
+ return svi;
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/database.hxx b/libodb-oracle/odb/oracle/database.hxx
new file mode 100644
index 0000000..0b66999
--- /dev/null
+++ b/libodb-oracle/odb/oracle/database.hxx
@@ -0,0 +1,542 @@
+// file : odb/oracle/database.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_DATABASE_HXX
+#define ODB_ORACLE_DATABASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <iosfwd> // std::ostream
+
+#include <odb/database.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+#include <odb/details/unique-ptr.hxx>
+#include <odb/details/transfer-ptr.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx>
+#include <odb/oracle/query.hxx>
+#include <odb/oracle/tracer.hxx>
+#include <odb/oracle/connection.hxx>
+#include <odb/oracle/connection-factory.hxx>
+#include <odb/oracle/auto-handle.hxx>
+#include <odb/oracle/oracle-fwd.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ class transaction_impl;
+
+ class LIBODB_ORACLE_EXPORT 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,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ 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,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Extract the database parameters from the command line. The
+ // following options are recognized:
+ //
+ // --user
+ // --password
+ // --database
+ // --service
+ // --host
+ // --port
+ // --options-file
+ //
+ // For more information, see the output of the print_usage() function
+ // below. If erase is true, the above options are removed from the
+ // argv array and the argc count is updated accordingly. This
+ // constructor may throw the cli_exception exception.
+ //
+ database (int& argc,
+ char* argv[],
+ bool erase = false,
+ ub2 charset = 0,
+ ub2 ncharset = 0,
+ OCIEnv* environment = 0,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Move-constructible but not move-assignable.
+ //
+ // Note: noexcept is not specified since odb::database(odb::database&&)
+ // can throw.
+ //
+#ifdef ODB_CXX11
+ database (database&&);
+#endif
+
+ static void
+ print_usage (std::ostream&);
+
+ // Object persistence API.
+ //
+ public:
+
+ // Make the object persistent.
+ //
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Bulk persist. Can be a range of references or pointers (including
+ // smart pointers) to objects.
+ //
+ template <typename I>
+ void
+ persist (I begin, I end, bool continue_failed = true);
+
+ // Load an object. Throw object_not_persistent if not found.
+ //
+ 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);
+
+ // Load (or reload, if it is already loaded) a section of an object.
+ //
+ template <typename T>
+ void
+ load (T& object, section&);
+
+ // Reload an object.
+ //
+ template <typename T>
+ void
+ reload (T& object);
+
+ template <typename T>
+ void
+ reload (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ reload (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Loan an object if found. Return NULL/false if not found.
+ //
+ 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);
+
+ // Update the state of a modified objects.
+ //
+ template <typename T>
+ void
+ update (T& object);
+
+ template <typename T>
+ void
+ update (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ update (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Bulk update. Can be a range of references or pointers (including
+ // smart pointers) to objects.
+ //
+ template <typename I>
+ void
+ update (I begin, I end, bool continue_failed = true);
+
+ // Update a section of an object. Throws the section_not_loaded
+ // exception if the section is not loaded. Note also that this
+ // function does not clear the changed flag if it is set.
+ //
+ template <typename T>
+ void
+ update (const T& object, const section&);
+
+ // Make the object transient. Throw object_not_persistent if not
+ // found.
+ //
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::id_type& id);
+
+ template <typename T>
+ void
+ erase (T& object);
+
+ template <typename T>
+ void
+ erase (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Bulk erase.
+ //
+ template <typename T, typename I>
+ void
+ erase (I id_begin, I id_end, bool continue_failed = true);
+
+ // Can be a range of references or pointers (including smart pointers)
+ // to objects.
+ //
+ template <typename I>
+ void
+ erase (I obj_begin, I obj_end, bool continue_failed = true);
+
+ // Erase multiple objects matching a query predicate.
+ //
+ template <typename T>
+ unsigned long long
+ erase_query ();
+
+ template <typename T>
+ unsigned long long
+ erase_query (const char*);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const std::string&);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const oracle::query_base&);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const odb::query_base&);
+
+ // Query API.
+ //
+ template <typename T>
+ result<T>
+ query ();
+
+ template <typename T>
+ result<T>
+ query (const char*);
+
+ template <typename T>
+ result<T>
+ query (const std::string&);
+
+ template <typename T>
+ result<T>
+ query (const oracle::query_base&);
+
+ template <typename T>
+ result<T>
+ query (const odb::query_base&);
+
+ // Query one API.
+ //
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one ();
+
+ template <typename T>
+ bool
+ query_one (T& object);
+
+ template <typename T>
+ T
+ query_value ();
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const char*);
+
+ template <typename T>
+ bool
+ query_one (const char*, T& object);
+
+ template <typename T>
+ T
+ query_value (const char*);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const std::string&);
+
+ template <typename T>
+ bool
+ query_one (const std::string&, T& object);
+
+ template <typename T>
+ T
+ query_value (const std::string&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const oracle::query_base&);
+
+ template <typename T>
+ bool
+ query_one (const oracle::query_base&, T& object);
+
+ template <typename T>
+ T
+ query_value (const oracle::query_base&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const odb::query_base&);
+
+ template <typename T>
+ bool
+ query_one (const odb::query_base&, T& object);
+
+ template <typename T>
+ T
+ query_value (const odb::query_base&);
+
+ // Query preparation.
+ //
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const oracle::query_base&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const odb::query_base&);
+
+ // Transactions.
+ //
+ public:
+ virtual transaction_impl*
+ begin ();
+
+ public:
+ connection_ptr
+ connection ();
+
+ public:
+ const std::string&
+ user () const
+ {
+ return user_;
+ }
+
+ const std::string&
+ password () const
+ {
+ return password_;
+ }
+
+ const std::string&
+ db () const
+ {
+ return db_;
+ }
+
+ const std::string&
+ service () const
+ {
+ return service_;
+ }
+
+ const std::string&
+ host () const
+ {
+ return host_;
+ }
+
+ unsigned int
+ port () const
+ {
+ return port_;
+ }
+
+ ub2
+ charset () const
+ {
+ return charset_;
+ }
+
+ ub2
+ ncharset () const
+ {
+ return ncharset_;
+ }
+
+ OCIEnv*
+ environment ()
+ {
+ return environment_;
+ }
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef oracle::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::database::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::database::tracer (t);
+ }
+
+ using odb::database::tracer;
+
+ // Database schema version.
+ //
+ protected:
+ virtual const schema_version_info&
+ load_schema_version (const std::string& schema_name) const;
+
+ public:
+ // Database id constant (useful for meta-programming).
+ //
+ static const odb::database_id database_id = id_oracle;
+
+ public:
+ virtual
+ ~database ();
+
+ protected:
+ virtual odb::connection*
+ connection_ ();
+
+ private:
+ // Note: remember to update move ctor if adding any new members.
+ //
+ std::string user_;
+ std::string password_;
+
+ std::string db_;
+
+ std::string service_;
+ std::string host_;
+ unsigned int port_;
+
+ ub2 charset_;
+ ub2 ncharset_;
+
+ auto_handle<OCIEnv> auto_environment_;
+ OCIEnv* environment_;
+
+ details::unique_ptr<connection_factory> factory_;
+ };
+ }
+}
+
+#include <odb/oracle/database.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_DATABASE_HXX
diff --git a/libodb-oracle/odb/oracle/database.ixx b/libodb-oracle/odb/oracle/database.ixx
new file mode 100644
index 0000000..ea41aca
--- /dev/null
+++ b/libodb-oracle/odb/oracle/database.ixx
@@ -0,0 +1,640 @@
+// file : odb/oracle/database.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <utility> // move()
+
+#include <odb/oracle/transaction.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+#ifdef ODB_CXX11
+ inline database::
+ database (database&& db) // Has to be inline.
+ : odb::database (std::move (db)),
+ user_ (std::move (db.user_)),
+ password_ (std::move (db.password_)),
+ db_ (std::move (db.db_)),
+ service_ (std::move (db.service_)),
+ host_ (std::move (db.host_)),
+ port_ (db.port_),
+ charset_ (db.charset_),
+ ncharset_ (db.ncharset_),
+ auto_environment_ (std::move (db.auto_environment_)),
+ environment_ (db.environment_),
+ factory_ (std::move (db.factory_))
+ {
+ factory_->database (*this); // New database instance.
+ }
+#endif
+
+ inline connection_ptr database::
+ connection ()
+ {
+ // Go through the virtual connection_() function instead of
+ // directly to allow overriding.
+ //
+ return connection_ptr (
+ static_cast<oracle::connection*> (connection_ ()));
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T& obj)
+ {
+ return persist_<T, id_oracle> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const T& obj)
+ {
+ return persist_<const T, id_oracle> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_oracle> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_oracle> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_oracle> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T>& p)
+ {
+ const P<T>& cr (p);
+ return persist<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ return persist<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const typename object_traits<T>::pointer_type& pobj)
+ {
+ return persist_<T, id_oracle> (pobj);
+ }
+
+ template <typename I>
+ inline void database::
+ persist (I b, I e, bool cont)
+ {
+ persist_<I, id_oracle> (b, e, cont);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ load (const typename object_traits<T>::id_type& id)
+ {
+ return load_<T, id_oracle> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ load (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return load_<T, id_oracle> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ load (T& obj, section& s)
+ {
+ return load_<T, id_oracle> (obj, s);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ find (const typename object_traits<T>::id_type& id)
+ {
+ return find_<T, id_oracle> (id);
+ }
+
+ template <typename T>
+ inline bool database::
+ find (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return find_<T, id_oracle> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T& obj)
+ {
+ reload_<T, id_oracle> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T* p)
+ {
+ reload<T> (*p);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (const P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (const P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T>
+ inline void database::
+ reload (const typename object_traits<T>::pointer_type& pobj)
+ {
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ reload (odb::pointer_traits<pointer_type>::get_ref (pobj));
+ }
+
+ template <typename T>
+ inline void database::
+ update (T& obj)
+ {
+ update_<T, id_oracle> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ update (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_oracle> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_oracle> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_oracle> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (P<T>& p)
+ {
+ const P<T>& cr (p);
+ update<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ update<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const typename object_traits<T>::pointer_type& pobj)
+ {
+ update_<T, id_oracle> (pobj);
+ }
+
+ template <typename I>
+ inline void database::
+ update (I b, I e, bool cont)
+ {
+ update_<I, id_oracle> (b, e, cont);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const T& obj, const section& s)
+ {
+ update_<T, id_oracle> (obj, s);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::id_type& id)
+ {
+ return erase_<T, id_oracle> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T& obj)
+ {
+ return erase_<T, id_oracle> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_oracle> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_oracle> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_oracle> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (P<T>& p)
+ {
+ const P<T>& cr (p);
+ erase<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ erase<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::pointer_type& pobj)
+ {
+ erase_<T, id_oracle> (pobj);
+ }
+
+ template <typename T, typename I>
+ inline void database::
+ erase (I idb, I ide, bool cont)
+ {
+ erase_id_<I, T, id_oracle> (idb, ide, cont);
+ }
+
+ template <typename I>
+ inline void database::
+ erase (I ob, I oe, bool cont)
+ {
+ erase_object_<I, id_oracle> (ob, oe, cont);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query ()
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (oracle::query_base ());
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const char* q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (oracle::query_base (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const std::string& q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (oracle::query_base (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const oracle::query_base& q)
+ {
+ // T is always object_type.
+ //
+ return object_traits_impl<T, id_oracle>::erase_query (*this, q);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return erase_query<T> (oracle::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query ()
+ {
+ return query<T> (oracle::query_base ());
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const char* q)
+ {
+ return query<T> (oracle::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const std::string& q)
+ {
+ return query<T> (oracle::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const oracle::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_<T, id_oracle>::call (*this, q);
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query<T> (oracle::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one ()
+ {
+ return query_one<T> (oracle::query_base ());
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (T& o)
+ {
+ return query_one<T> (oracle::query_base (), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value ()
+ {
+ return query_value<T> (oracle::query_base ());
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const char* q)
+ {
+ return query_one<T> (oracle::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const char* q, T& o)
+ {
+ return query_one<T> (oracle::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const char* q)
+ {
+ return query_value<T> (oracle::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const std::string& q)
+ {
+ return query_one<T> (oracle::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const std::string& q, T& o)
+ {
+ return query_one<T> (oracle::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const std::string& q)
+ {
+ return query_value<T> (oracle::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const oracle::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_one_<T, id_oracle> (q);
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const oracle::query_base& q, T& o)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_one_<T, id_oracle> (q, o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const oracle::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_value_<T, id_oracle> (q);
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query_one<T> (oracle::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const odb::query_base& q, T& o)
+ {
+ // Translate to native query.
+ //
+ return query_one<T> (oracle::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query_value<T> (oracle::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, oracle::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, oracle::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const oracle::query_base& q)
+ {
+ // Throws if not in transaction.
+ //
+ oracle::connection& c (transaction::current ().connection (*this));
+ return c.prepare_query<T> (n, q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return prepare_query<T> (n, oracle::query_base (q));
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/details/.gitignore b/libodb-oracle/odb/oracle/details/.gitignore
new file mode 100644
index 0000000..b298f89
--- /dev/null
+++ b/libodb-oracle/odb/oracle/details/.gitignore
@@ -0,0 +1 @@
+/options.?xx
diff --git a/libodb-oracle/odb/oracle/details/config.hxx b/libodb-oracle/odb/oracle/details/config.hxx
new file mode 100644
index 0000000..a9330a4
--- /dev/null
+++ b/libodb-oracle/odb/oracle/details/config.hxx
@@ -0,0 +1,15 @@
+// file : odb/oracle/details/config.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_DETAILS_CONFIG_HXX
+#define ODB_ORACLE_DETAILS_CONFIG_HXX
+
+// no pre
+
+#ifdef ODB_COMPILER
+# error libodb-oracle header included in odb-compiled header
+#endif
+
+// no post
+
+#endif // ODB_ORACLE_DETAILS_CONFIG_HXX
diff --git a/libodb-oracle/odb/oracle/details/conversion.hxx b/libodb-oracle/odb/oracle/details/conversion.hxx
new file mode 100644
index 0000000..c3c86cf
--- /dev/null
+++ b/libodb-oracle/odb/oracle/details/conversion.hxx
@@ -0,0 +1,58 @@
+// file : odb/oracle/details/conversion.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_DETAILS_CONVERSION_HXX
+#define ODB_ORACLE_DETAILS_CONVERSION_HXX
+
+#include <odb/oracle/traits.hxx>
+
+#include <odb/details/meta/answer.hxx>
+
+namespace odb
+{
+ // @@ Revise this.
+ //
+ namespace details {}
+
+ namespace oracle
+ {
+ namespace details
+ {
+ using namespace odb::details;
+
+ // Detect whether conversion is specified in type_traits.
+ //
+ template <typename T>
+ meta::yes
+ conversion_p_test (typename type_traits<T>::conversion*);
+
+ template <typename T>
+ meta::no
+ conversion_p_test (...);
+
+ template <typename T>
+ struct conversion_p
+ {
+ static const bool value =
+ sizeof (conversion_p_test<T> (0)) == sizeof (meta::yes);
+ };
+
+ template <typename T, bool = conversion_p<T>::value>
+ struct conversion;
+
+ template <typename T>
+ struct conversion<T, true>
+ {
+ static const char* to () {return type_traits<T>::conversion::to ();}
+ };
+
+ template <typename T>
+ struct conversion<T, false>
+ {
+ static const char* to () {return 0;}
+ };
+ }
+ }
+}
+
+#endif // ODB_ORACLE_DETAILS_CONVERSION_HXX
diff --git a/libodb-oracle/odb/oracle/details/date.hxx b/libodb-oracle/odb/oracle/details/date.hxx
new file mode 100644
index 0000000..d6c1acb
--- /dev/null
+++ b/libodb-oracle/odb/oracle/details/date.hxx
@@ -0,0 +1,59 @@
+// file : odb/oracle/details/date.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_DETAILS_DATE_HXX
+#define ODB_ORACLE_DETAILS_DATE_HXX
+
+namespace odb
+{
+ // @@ Revise this.
+ //
+ namespace details
+ {
+ }
+
+ namespace oracle
+ {
+ namespace details
+ {
+ inline void
+ set_date (char* b,
+ short year,
+ unsigned char month,
+ unsigned char day,
+ unsigned char hour,
+ unsigned char minute,
+ unsigned char second)
+ {
+ b[0] = static_cast<char> (year / 100 + 100);
+ b[1] = static_cast<char> (year % 100 + 100);
+ b[2] = static_cast<char> (month);
+ b[3] = static_cast<char> (day);
+ b[4] = static_cast<char> (hour + 1);
+ b[5] = static_cast<char> (minute + 1);
+ b[6] = static_cast<char> (second + 1);
+ }
+
+ inline void
+ get_date (const char* b,
+ short& year,
+ unsigned char& month,
+ unsigned char& day,
+ unsigned char& hour,
+ unsigned char& minute,
+ unsigned char& second)
+ {
+ const unsigned char* ub (reinterpret_cast<const unsigned char*> (b));
+
+ year = 100 * ub[0] + ub[1] - 10100;
+ month = ub[2];
+ day = ub[3];
+ hour = ub[4] - 1;
+ minute = ub[5] - 1;
+ second = ub[6] - 1;
+ }
+ }
+ }
+}
+
+#endif // ODB_ORACLE_DETAILS_DATE_HXX
diff --git a/libodb-oracle/odb/oracle/details/export.hxx b/libodb-oracle/odb/oracle/details/export.hxx
new file mode 100644
index 0000000..788948a
--- /dev/null
+++ b/libodb-oracle/odb/oracle/details/export.hxx
@@ -0,0 +1,50 @@
+// file : odb/oracle/details/export.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_DETAILS_EXPORT_HXX
+#define ODB_ORACLE_DETAILS_EXPORT_HXX
+
+#include <odb/pre.hxx>
+
+// Note: do this check directly instead of including config.hxx.
+//
+#ifdef ODB_COMPILER
+# error libodb-oracle header included in odb-compiled header
+#endif
+
+// Normally we don't export class templates (but do complete specializations),
+// inline functions, and classes with only inline member functions. Exporting
+// classes that inherit from non-exported/imported bases (e.g., std::string)
+// will end up badly. The only known workarounds are to not inherit or to not
+// export. Also, MinGW GCC doesn't like seeing non-exported function being
+// used before their inline definition. The workaround is to reorder code. In
+// the end it's all trial and error.
+
+#if defined(LIBODB_ORACLE_STATIC) // Using static.
+# define LIBODB_ORACLE_EXPORT
+#elif defined(LIBODB_ORACLE_STATIC_BUILD) // Building static.
+# define LIBODB_ORACLE_EXPORT
+#elif defined(LIBODB_ORACLE_SHARED) // Using shared.
+# ifdef _WIN32
+# define LIBODB_ORACLE_EXPORT __declspec(dllimport)
+# else
+# define LIBODB_ORACLE_EXPORT
+# endif
+#elif defined(LIBODB_ORACLE_SHARED_BUILD) // Building shared.
+# ifdef _WIN32
+# define LIBODB_ORACLE_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_ORACLE_EXPORT
+# endif
+#else
+// If none of the above macros are defined, then we assume we are being used
+// by some third-party build system that cannot/doesn't signal the library
+// type. Note that this fallback works for both static and shared but in case
+// of shared will be sub-optimal compared to having dllimport.
+//
+# define LIBODB_ORACLE_EXPORT // Using static or shared.
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_DETAILS_EXPORT_HXX
diff --git a/libodb-oracle/odb/oracle/details/number.cxx b/libodb-oracle/odb/oracle/details/number.cxx
new file mode 100644
index 0000000..aeb2b96
--- /dev/null
+++ b/libodb-oracle/odb/oracle/details/number.cxx
@@ -0,0 +1,269 @@
+// file : odb/oracle/details/number.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cassert>
+
+#include <odb/oracle/details/number.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace oracle
+ {
+ namespace details
+ {
+ // The NUMBER type's binary representation is made up of the following
+ // bit fields, ordered in increasing memory address.
+ //
+ // 000 to 007: The base-100 exponent bits. The most significant bit is
+ // the sign bit of the number, which is set for positive
+ // numbers and cleared for negative numbers. For positive
+ // numbers, the exponent has a bias of 65 added to it.
+ //
+ // 008 to 167: The mantissa bits. Each byte of this field represents a
+ // single base-100 value.
+ //
+ //
+
+ long long
+ number_to_int64 (const char* b, size_t n)
+ {
+ // All bytes in the buffer are interpreted as unsigned.
+ //
+ const unsigned char* ub (reinterpret_cast<const unsigned char*> (b));
+
+ // Zero is represented by zero significant bits and an exponent
+ // set to 128.
+ //
+ if (n == 1)
+ {
+ assert (ub[0] == 128);
+ return 0;
+ }
+
+ long long v (0);
+
+ // Test the sign bit of the exponent.
+ //
+ if ((ub[0] & 0x80) != 0)
+ {
+ // The unbiased exponent of a positive number may be calculated as
+ // ub[0 - 128 - 65. For an integer, this is the order of magnitude
+ // of the number. Calculate the maximum weight, 100 ^ o, where o is
+ // the order of magnitude of the number.
+ //
+ long long w (1);
+
+ for (size_t i (0), o (ub[0] - 193); i < o; ++i)
+ w *= 100;
+
+ // Accumlate the sum of the significant base-100 terms.
+ //
+ for (const unsigned char* m (ub + 1), *e (ub + n); m < e; ++m)
+ {
+ v += (*m - 1) * w;
+ w /= 100;
+ }
+ }
+ else
+ {
+ // The unbiased exponent of a negative number is calculated as
+ // (~ub[0] & 0x7F) - 193. For an integer, this is the order of
+ // magnitude of the number. Calculate the maximum weight, 100 ^ o,
+ // where o is the order of magnitude of the number.
+ //
+ long long w (1);
+
+ for (size_t i (0), o ((~ub[0] & 0x7F) - 65); i < o; ++i)
+ w *= 100;
+
+ // Accumulate the sum of the significant base-100 terms. Note that
+ // negative values will have a terminator byte which is included
+ // in the length. This is ignored.
+ //
+ for (const unsigned char* m (ub + 1), *e (ub + n - 1); m < e; ++m)
+ {
+ v -= (101 - *m) * w;
+ w /= 100;
+ }
+ }
+
+ return v;
+ }
+
+ void
+ int64_to_number (char* b, size_t& n, long long v)
+ {
+ // We assume that b is long enough to contain a long long NUMBER
+ // representation, that being 12 bytes.
+ //
+
+ // All bytes in the buffer are interpreted as unsigned.
+ //
+ unsigned char* ub (reinterpret_cast<unsigned char*> (b));
+
+ if (v == 0)
+ {
+ ub[0] = 128;
+ n = 1;
+
+ return;
+ }
+
+ bool sig (false);
+ unsigned char t[11], *m (t);
+ n = 0;
+
+ if (v < 0)
+ {
+ // Termination marker for negative numbers.
+ //
+ *m++ = 102;
+
+ while (v != 0)
+ {
+ int r (static_cast<int> (v % 100));
+ sig = sig || r != 0;
+
+ if (sig)
+ *m++ = static_cast<unsigned char> (101 + r);
+
+ v /= 100;
+ ++n;
+ }
+
+ // The exponent is one less than the number of base 100 digits. It is
+ // inverted for negative values.
+ //
+ ub[0] = static_cast<unsigned char> (~(n + 192));
+ }
+ else
+ {
+ while (v != 0)
+ {
+ int r (static_cast<int> (v % 100));
+ sig = sig || r != 0;
+
+ if (sig)
+ *m++ = static_cast<unsigned char> (r + 1);
+
+ v /= 100;
+ ++n;
+ }
+
+ // Exponent is one less than the number of base 100 digits.
+ //
+ ub[0] = static_cast<unsigned char> (n + 192);
+ }
+
+ // Set the length.
+ //
+ n = static_cast<unsigned char> (m - t + 1);
+
+ // Set the significant digits in big-endian byte order and the
+ // terminator, if any.
+ //
+ for (size_t i (1); m > t; ++i)
+ ub[i] = *--m;
+ }
+
+ unsigned long long
+ number_to_uint64 (const char* b, size_t n)
+ {
+ // All bytes in the buffer are interpreted as unsigned.
+ //
+ const unsigned char* ub (reinterpret_cast<const unsigned char*> (b));
+
+ // Zero is represented by zero significant bits and an exponent
+ // set to 128.
+ //
+ if (n == 1)
+ {
+ assert (ub[0] == 128);
+ return 0;
+ }
+
+ unsigned long long v (0);
+
+ // Test the sign bit of the exponent.
+ //
+ if ((ub[0] & 0x80) == 0)
+ {
+ assert (false);
+ return 0;
+ }
+
+ // The unbiased exponent of a positive number may be calculated as
+ // ub[0] - 128 - 65. For an integer, this is the order of magnitude
+ // of the number. Calculate the maximum weight, 100 ^ o, where o is
+ // the order of magnitude of the number.
+ //
+ unsigned long long w (1);
+
+ for (size_t i (0), o (ub[0] - 193); i < o; ++i)
+ w *= 100;
+
+ // Accumlate the sum of the significant base-100 terms.
+ //
+ for (const unsigned char* m (ub + 1), *e (ub + n); m < e; ++m)
+ {
+ v += (*m - 1) * w;
+ w /= 100;
+ }
+
+ return v;
+ }
+
+ void
+ uint64_to_number (char* b, size_t& n, unsigned long long v)
+ {
+ // We assume that b is long enough to contain an unsigned long long
+ // NUMBER representation, that being 12 bytes.
+ //
+
+ // All bytes in the buffer are interpreted as unsigned.
+ //
+ unsigned char* ub (reinterpret_cast<unsigned char*> (b));
+
+ if (v == 0)
+ {
+ ub[0] = 128;
+ n = 1;
+
+ return;
+ }
+
+ bool sig (false);
+ unsigned char t[11], *m (t);
+ n = 0;
+
+ while (v != 0)
+ {
+ int r (static_cast<int> (v % 100));
+ sig = sig || r != 0;
+
+ if (sig)
+ *m++ = static_cast<unsigned char> (r + 1);
+
+ v /= 100;
+ ++n;
+ }
+
+ // Exponent is one less than the number of base 100 digits.
+ //
+ ub[0] = static_cast<unsigned char> (n + 192);
+
+ // Set the length.
+ //
+ n = static_cast<unsigned char> (m - t + 1);
+
+ // Set the significant digits in big-endian byte order.
+ //
+ for (size_t i (1); m > t; ++i)
+ ub[i] = *--m;
+ }
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/details/number.hxx b/libodb-oracle/odb/oracle/details/number.hxx
new file mode 100644
index 0000000..8ab6a90
--- /dev/null
+++ b/libodb-oracle/odb/oracle/details/number.hxx
@@ -0,0 +1,44 @@
+// file : odb/oracle/details/number.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_NUMBER_HXX
+#define ODB_ORACLE_NUMBER_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ // @@ Revise this.
+ //
+ namespace details
+ {
+ }
+
+ namespace oracle
+ {
+ namespace details
+ {
+ using namespace odb::details;
+
+ LIBODB_ORACLE_EXPORT long long
+ number_to_int64 (const char* buffer, std::size_t n);
+
+ LIBODB_ORACLE_EXPORT void
+ int64_to_number (char* buffer, std::size_t& n, long long val);
+
+ LIBODB_ORACLE_EXPORT unsigned long long
+ number_to_uint64 (const char* buffer, std::size_t n);
+
+ LIBODB_ORACLE_EXPORT void
+ uint64_to_number (char* buffer, std::size_t& n, unsigned long long val);
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_NUMBER_HXX
diff --git a/libodb-oracle/odb/oracle/details/options.cli b/libodb-oracle/odb/oracle/details/options.cli
new file mode 100644
index 0000000..82be308
--- /dev/null
+++ b/libodb-oracle/odb/oracle/details/options.cli
@@ -0,0 +1,61 @@
+// file : odb/oracle/details/options.cli
+// license : ODB NCUEL; see accompanying LICENSE file
+
+include <string>;
+
+namespace odb
+{
+ namespace oracle
+ {
+ namespace details
+ {
+ class options
+ {
+ std::string --user
+ {
+ "<name>",
+ "Oracle database user."
+ };
+
+ std::string --password
+ {
+ "<str>",
+ "Oracle database password."
+ };
+
+ std::string --database
+ {
+ "<conn-id>",
+ "Oracle connect identifier."
+ };
+
+ std::string --service
+ {
+ "<name>",
+ "Oracle service name."
+ };
+
+ std::string --host
+ {
+ "<str>",
+ "Oracle database host name or address (localhost by default)."
+ };
+
+ unsigned int --port
+ {
+ "<integer>",
+ "Oracle database port number."
+ };
+
+ std::string --options-file
+ {
+ "<file>",
+ "Read additional options from <file>. Each option should appear on a
+ separate line optionally followed by space or equal sign (\cb{=})
+ and an option value. Empty lines and lines starting with \cb{#} are
+ ignored."
+ };
+ };
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.cxx b/libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.cxx
new file mode 100644
index 0000000..69ea3d7
--- /dev/null
+++ b/libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.cxx
@@ -0,0 +1,1125 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <odb/oracle/details/options.hxx>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <utility>
+#include <ostream>
+#include <sstream>
+#include <cstring>
+#include <fstream>
+
+namespace odb
+{
+ namespace oracle
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ // unknown_option
+ //
+ unknown_option::
+ ~unknown_option () throw ()
+ {
+ }
+
+ void unknown_option::
+ print (::std::ostream& os) const
+ {
+ os << "unknown option '" << option ().c_str () << "'";
+ }
+
+ const char* unknown_option::
+ what () const throw ()
+ {
+ return "unknown option";
+ }
+
+ // unknown_argument
+ //
+ unknown_argument::
+ ~unknown_argument () throw ()
+ {
+ }
+
+ void unknown_argument::
+ print (::std::ostream& os) const
+ {
+ os << "unknown argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unknown_argument::
+ what () const throw ()
+ {
+ return "unknown argument";
+ }
+
+ // missing_value
+ //
+ missing_value::
+ ~missing_value () throw ()
+ {
+ }
+
+ void missing_value::
+ print (::std::ostream& os) const
+ {
+ os << "missing value for option '" << option ().c_str () << "'";
+ }
+
+ const char* missing_value::
+ what () const throw ()
+ {
+ return "missing option value";
+ }
+
+ // invalid_value
+ //
+ invalid_value::
+ ~invalid_value () throw ()
+ {
+ }
+
+ void invalid_value::
+ print (::std::ostream& os) const
+ {
+ os << "invalid value '" << value ().c_str () << "' for option '"
+ << option ().c_str () << "'";
+
+ if (!message ().empty ())
+ os << ": " << message ().c_str ();
+ }
+
+ const char* invalid_value::
+ what () const throw ()
+ {
+ return "invalid option value";
+ }
+
+ // eos_reached
+ //
+ void eos_reached::
+ print (::std::ostream& os) const
+ {
+ os << what ();
+ }
+
+ const char* eos_reached::
+ what () const throw ()
+ {
+ return "end of argument stream reached";
+ }
+
+ // file_io_failure
+ //
+ file_io_failure::
+ ~file_io_failure () throw ()
+ {
+ }
+
+ void file_io_failure::
+ print (::std::ostream& os) const
+ {
+ os << "unable to open file '" << file ().c_str () << "' or read failure";
+ }
+
+ const char* file_io_failure::
+ what () const throw ()
+ {
+ return "unable to open file or read failure";
+ }
+
+ // unmatched_quote
+ //
+ unmatched_quote::
+ ~unmatched_quote () throw ()
+ {
+ }
+
+ void unmatched_quote::
+ print (::std::ostream& os) const
+ {
+ os << "unmatched quote in argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unmatched_quote::
+ what () const throw ()
+ {
+ return "unmatched quote";
+ }
+
+ // scanner
+ //
+ scanner::
+ ~scanner ()
+ {
+ }
+
+ // argv_scanner
+ //
+ bool argv_scanner::
+ more ()
+ {
+ return i_ < argc_;
+ }
+
+ const char* argv_scanner::
+ peek ()
+ {
+ if (i_ < argc_)
+ return argv_[i_];
+ else
+ throw eos_reached ();
+ }
+
+ const char* argv_scanner::
+ next ()
+ {
+ if (i_ < argc_)
+ {
+ const char* r (argv_[i_]);
+
+ if (erase_)
+ {
+ for (int i (i_ + 1); i < argc_; ++i)
+ argv_[i - 1] = argv_[i];
+
+ --argc_;
+ argv_[argc_] = 0;
+ }
+ else
+ ++i_;
+
+ ++start_position_;
+ return r;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ void argv_scanner::
+ skip ()
+ {
+ if (i_ < argc_)
+ {
+ ++i_;
+ ++start_position_;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ std::size_t argv_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ // argv_file_scanner
+ //
+ int argv_file_scanner::zero_argc_ = 0;
+ std::string argv_file_scanner::empty_string_;
+
+ bool argv_file_scanner::
+ more ()
+ {
+ if (!args_.empty ())
+ return true;
+
+ while (base::more ())
+ {
+ // See if the next argument is the file option.
+ //
+ const char* a (base::peek ());
+ const option_info* oi = 0;
+ const char* ov = 0;
+
+ if (!skip_)
+ {
+ if ((oi = find (a)) != 0)
+ {
+ base::next ();
+
+ if (!base::more ())
+ throw missing_value (a);
+
+ ov = base::next ();
+ }
+ else if (std::strncmp (a, "-", 1) == 0)
+ {
+ if ((ov = std::strchr (a, '=')) != 0)
+ {
+ std::string o (a, 0, ov - a);
+ if ((oi = find (o.c_str ())) != 0)
+ {
+ base::next ();
+ ++ov;
+ }
+ }
+ }
+ }
+
+ if (oi != 0)
+ {
+ if (oi->search_func != 0)
+ {
+ std::string f (oi->search_func (ov, oi->arg));
+
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ load (ov);
+
+ if (!args_.empty ())
+ return true;
+ }
+ else
+ {
+ if (!skip_)
+ skip_ = (std::strcmp (a, "--") == 0);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ const char* argv_file_scanner::
+ peek ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? base::peek () : args_.front ().value.c_str ();
+ }
+
+ const std::string& argv_file_scanner::
+ peek_file ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? empty_string_ : *args_.front ().file;
+ }
+
+ std::size_t argv_file_scanner::
+ peek_line ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? 0 : args_.front ().line;
+ }
+
+ const char* argv_file_scanner::
+ next ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::next ();
+ else
+ {
+ hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value);
+ args_.pop_front ();
+ ++start_position_;
+ return hold_[i_].c_str ();
+ }
+ }
+
+ void argv_file_scanner::
+ skip ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::skip ();
+ else
+ {
+ args_.pop_front ();
+ ++start_position_;
+ }
+ }
+
+ const argv_file_scanner::option_info* argv_file_scanner::
+ find (const char* a) const
+ {
+ for (std::size_t i (0); i < options_count_; ++i)
+ if (std::strcmp (a, options_[i].option) == 0)
+ return &options_[i];
+
+ return 0;
+ }
+
+ std::size_t argv_file_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ void argv_file_scanner::
+ load (const std::string& file)
+ {
+ using namespace std;
+
+ ifstream is (file.c_str ());
+
+ if (!is.is_open ())
+ throw file_io_failure (file);
+
+ files_.push_back (file);
+
+ arg a;
+ a.file = &*files_.rbegin ();
+
+ for (a.line = 1; !is.eof (); ++a.line)
+ {
+ string line;
+ getline (is, line);
+
+ if (is.fail () && !is.eof ())
+ throw file_io_failure (file);
+
+ string::size_type n (line.size ());
+
+ // Trim the line from leading and trailing whitespaces.
+ //
+ if (n != 0)
+ {
+ const char* f (line.c_str ());
+ const char* l (f + n);
+
+ const char* of (f);
+ while (f < l && (*f == ' ' || *f == '\t' || *f == '\r'))
+ ++f;
+
+ --l;
+
+ const char* ol (l);
+ while (l > f && (*l == ' ' || *l == '\t' || *l == '\r'))
+ --l;
+
+ if (f != of || l != ol)
+ line = f <= l ? string (f, l - f + 1) : string ();
+ }
+
+ // Ignore empty lines, those that start with #.
+ //
+ if (line.empty () || line[0] == '#')
+ continue;
+
+ string::size_type p (string::npos);
+ if (line.compare (0, 1, "-") == 0)
+ {
+ p = line.find (' ');
+
+ string::size_type q (line.find ('='));
+ if (q != string::npos && q < p)
+ p = q;
+ }
+
+ string s1;
+ if (p != string::npos)
+ {
+ s1.assign (line, 0, p);
+
+ // Skip leading whitespaces in the argument.
+ //
+ if (line[p] == '=')
+ ++p;
+ else
+ {
+ n = line.size ();
+ for (++p; p < n; ++p)
+ {
+ char c (line[p]);
+ if (c != ' ' && c != '\t' && c != '\r')
+ break;
+ }
+ }
+ }
+ else if (!skip_)
+ skip_ = (line == "--");
+
+ string s2 (line, p != string::npos ? p : 0);
+
+ // If the string (which is an option value or argument) is
+ // wrapped in quotes, remove them.
+ //
+ n = s2.size ();
+ char cf (s2[0]), cl (s2[n - 1]);
+
+ if (cf == '"' || cf == '\'' || cl == '"' || cl == '\'')
+ {
+ if (n == 1 || cf != cl)
+ throw unmatched_quote (s2);
+
+ s2 = string (s2, 1, n - 2);
+ }
+
+ if (!s1.empty ())
+ {
+ // See if this is another file option.
+ //
+ const option_info* oi;
+ if (!skip_ && (oi = find (s1.c_str ())))
+ {
+ if (s2.empty ())
+ throw missing_value (oi->option);
+
+ if (oi->search_func != 0)
+ {
+ string f (oi->search_func (s2.c_str (), oi->arg));
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ {
+ // If the path of the file being parsed is not simple and the
+ // path of the file that needs to be loaded is relative, then
+ // complete the latter using the former as a base.
+ //
+#ifndef _WIN32
+ string::size_type p (file.find_last_of ('/'));
+ bool c (p != string::npos && s2[0] != '/');
+#else
+ string::size_type p (file.find_last_of ("/\\"));
+ bool c (p != string::npos && s2[1] != ':');
+#endif
+ if (c)
+ s2.insert (0, file, 0, p + 1);
+
+ load (s2);
+ }
+
+ continue;
+ }
+
+ a.value = s1;
+ args_.push_back (a);
+ }
+
+ a.value = s2;
+ args_.push_back (a);
+ }
+ }
+
+ template <typename X>
+ struct parser
+ {
+ static void
+ parse (X& x, bool& xs, scanner& s)
+ {
+ using namespace std;
+
+ const char* o (s.next ());
+ if (s.more ())
+ {
+ string v (s.next ());
+ istringstream is (v);
+ if (!(is >> x && is.peek () == istringstream::traits_type::eof ()))
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<bool>
+ {
+ static void
+ parse (bool& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ const char* v (s.next ());
+
+ if (std::strcmp (v, "1") == 0 ||
+ std::strcmp (v, "true") == 0 ||
+ std::strcmp (v, "TRUE") == 0 ||
+ std::strcmp (v, "True") == 0)
+ x = true;
+ else if (std::strcmp (v, "0") == 0 ||
+ std::strcmp (v, "false") == 0 ||
+ std::strcmp (v, "FALSE") == 0 ||
+ std::strcmp (v, "False") == 0)
+ x = false;
+ else
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<std::string>
+ {
+ static void
+ parse (std::string& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ x = s.next ();
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X>
+ struct parser<std::pair<X, std::size_t> >
+ {
+ static void
+ parse (std::pair<X, std::size_t>& x, bool& xs, scanner& s)
+ {
+ x.second = s.position ();
+ parser<X>::parse (x.first, xs, s);
+ }
+ };
+
+ template <typename X>
+ struct parser<std::vector<X> >
+ {
+ static void
+ parse (std::vector<X>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.push_back (x);
+ xs = true;
+ }
+ };
+
+ template <typename X, typename C>
+ struct parser<std::set<X, C> >
+ {
+ static void
+ parse (std::set<X, C>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.insert (x);
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::map<K, V, C> >
+ {
+ static void
+ parse (std::map<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m[k] = v;
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::multimap<K, V, C> >
+ {
+ static void
+ parse (std::multimap<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m.insert (typename std::multimap<K, V, C>::value_type (k, v));
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X, typename T, T X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, s);
+ }
+
+ template <typename X, bool X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ s.next ();
+ x.*M = true;
+ }
+
+ template <typename X, typename T, T X::*M, bool X::*S>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, x.*S, s);
+ }
+ }
+ }
+ }
+}
+
+#include <map>
+
+namespace odb
+{
+ namespace oracle
+ {
+ namespace details
+ {
+ // options
+ //
+
+ options::
+ options ()
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ service_ (),
+ service_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (),
+ port_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ }
+
+ options::
+ options (int& argc,
+ char** argv,
+ bool erase,
+ ::odb::oracle::details::cli::unknown_mode opt,
+ ::odb::oracle::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ service_ (),
+ service_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (),
+ port_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::oracle::details::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ options::
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ ::odb::oracle::details::cli::unknown_mode opt,
+ ::odb::oracle::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ service_ (),
+ service_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (),
+ port_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::oracle::details::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ options::
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::odb::oracle::details::cli::unknown_mode opt,
+ ::odb::oracle::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ service_ (),
+ service_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (),
+ port_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::oracle::details::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ options::
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::odb::oracle::details::cli::unknown_mode opt,
+ ::odb::oracle::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ service_ (),
+ service_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (),
+ port_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::oracle::details::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ options::
+ options (::odb::oracle::details::cli::scanner& s,
+ ::odb::oracle::details::cli::unknown_mode opt,
+ ::odb::oracle::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ service_ (),
+ service_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (),
+ port_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ _parse (s, opt, arg);
+ }
+
+ ::odb::oracle::details::cli::usage_para options::
+ print_usage (::std::ostream& os, ::odb::oracle::details::cli::usage_para p)
+ {
+ CLI_POTENTIALLY_UNUSED (os);
+
+ if (p != ::odb::oracle::details::cli::usage_para::none)
+ os << ::std::endl;
+
+ os << "--user <name> Oracle database user." << ::std::endl;
+
+ os << std::endl
+ << "--password <str> Oracle database password." << ::std::endl;
+
+ os << std::endl
+ << "--database <conn-id> Oracle connect identifier." << ::std::endl;
+
+ os << std::endl
+ << "--service <name> Oracle service name." << ::std::endl;
+
+ os << std::endl
+ << "--host <str> Oracle database host name or address (localhost by" << ::std::endl
+ << " default)." << ::std::endl;
+
+ os << std::endl
+ << "--port <integer> Oracle database port number." << ::std::endl;
+
+ os << std::endl
+ << "--options-file <file> Read additional options from <file>. Each option should" << ::std::endl
+ << " appear on a separate line optionally followed by space or" << ::std::endl
+ << " equal sign (=) and an option value. Empty lines and lines" << ::std::endl
+ << " starting with # are ignored." << ::std::endl;
+
+ p = ::odb::oracle::details::cli::usage_para::option;
+
+ return p;
+ }
+
+ typedef
+ std::map<std::string, void (*) (options&, ::odb::oracle::details::cli::scanner&)>
+ _cli_options_map;
+
+ static _cli_options_map _cli_options_map_;
+
+ struct _cli_options_map_init
+ {
+ _cli_options_map_init ()
+ {
+ _cli_options_map_["--user"] =
+ &::odb::oracle::details::cli::thunk< options, std::string, &options::user_,
+ &options::user_specified_ >;
+ _cli_options_map_["--password"] =
+ &::odb::oracle::details::cli::thunk< options, std::string, &options::password_,
+ &options::password_specified_ >;
+ _cli_options_map_["--database"] =
+ &::odb::oracle::details::cli::thunk< options, std::string, &options::database_,
+ &options::database_specified_ >;
+ _cli_options_map_["--service"] =
+ &::odb::oracle::details::cli::thunk< options, std::string, &options::service_,
+ &options::service_specified_ >;
+ _cli_options_map_["--host"] =
+ &::odb::oracle::details::cli::thunk< options, std::string, &options::host_,
+ &options::host_specified_ >;
+ _cli_options_map_["--port"] =
+ &::odb::oracle::details::cli::thunk< options, unsigned int, &options::port_,
+ &options::port_specified_ >;
+ _cli_options_map_["--options-file"] =
+ &::odb::oracle::details::cli::thunk< options, std::string, &options::options_file_,
+ &options::options_file_specified_ >;
+ }
+ };
+
+ static _cli_options_map_init _cli_options_map_init_;
+
+ bool options::
+ _parse (const char* o, ::odb::oracle::details::cli::scanner& s)
+ {
+ _cli_options_map::const_iterator i (_cli_options_map_.find (o));
+
+ if (i != _cli_options_map_.end ())
+ {
+ (*(i->second)) (*this, s);
+ return true;
+ }
+
+ return false;
+ }
+
+ bool options::
+ _parse (::odb::oracle::details::cli::scanner& s,
+ ::odb::oracle::details::cli::unknown_mode opt_mode,
+ ::odb::oracle::details::cli::unknown_mode arg_mode)
+ {
+ bool r = false;
+ bool opt = true;
+
+ while (s.more ())
+ {
+ const char* o = s.peek ();
+
+ if (std::strcmp (o, "--") == 0)
+ {
+ opt = false;
+ s.skip ();
+ r = true;
+ continue;
+ }
+
+ if (opt)
+ {
+ if (_parse (o, s))
+ {
+ r = true;
+ continue;
+ }
+
+ if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0')
+ {
+ // Handle combined option values.
+ //
+ std::string co;
+ if (const char* v = std::strchr (o, '='))
+ {
+ co.assign (o, 0, v - o);
+ ++v;
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (co.c_str ()),
+ const_cast<char*> (v)
+ };
+
+ ::odb::oracle::details::cli::argv_scanner ns (0, ac, av);
+
+ if (_parse (co.c_str (), ns))
+ {
+ // Parsed the option but not its value?
+ //
+ if (ns.end () != 2)
+ throw ::odb::oracle::details::cli::invalid_value (co, v);
+
+ s.next ();
+ r = true;
+ continue;
+ }
+ else
+ {
+ // Set the unknown option and fall through.
+ //
+ o = co.c_str ();
+ }
+ }
+
+ switch (opt_mode)
+ {
+ case ::odb::oracle::details::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::odb::oracle::details::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::odb::oracle::details::cli::unknown_mode::fail:
+ {
+ throw ::odb::oracle::details::cli::unknown_option (o);
+ }
+ }
+
+ break;
+ }
+ }
+
+ switch (arg_mode)
+ {
+ case ::odb::oracle::details::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::odb::oracle::details::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::odb::oracle::details::cli::unknown_mode::fail:
+ {
+ throw ::odb::oracle::details::cli::unknown_argument (o);
+ }
+ }
+
+ break;
+ }
+
+ return r;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
diff --git a/libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.hxx b/libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.hxx
new file mode 100644
index 0000000..285c906
--- /dev/null
+++ b/libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.hxx
@@ -0,0 +1,570 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+#ifndef LIBODB_ORACLE_DETAILS_OPTIONS_HXX
+#define LIBODB_ORACLE_DETAILS_OPTIONS_HXX
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <list>
+#include <deque>
+#include <iosfwd>
+#include <string>
+#include <cstddef>
+#include <exception>
+
+#ifndef CLI_POTENTIALLY_UNUSED
+# if defined(_MSC_VER) || defined(__xlC__)
+# define CLI_POTENTIALLY_UNUSED(x) (void*)&x
+# else
+# define CLI_POTENTIALLY_UNUSED(x) (void)x
+# endif
+#endif
+
+namespace odb
+{
+ namespace oracle
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ class usage_para
+ {
+ public:
+ enum value
+ {
+ none,
+ text,
+ option
+ };
+
+ usage_para (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ class unknown_mode
+ {
+ public:
+ enum value
+ {
+ skip,
+ stop,
+ fail
+ };
+
+ unknown_mode (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ // Exceptions.
+ //
+
+ class exception: public std::exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const = 0;
+ };
+
+ ::std::ostream&
+ operator<< (::std::ostream&, const exception&);
+
+ class unknown_option: public exception
+ {
+ public:
+ virtual
+ ~unknown_option () throw ();
+
+ unknown_option (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class unknown_argument: public exception
+ {
+ public:
+ virtual
+ ~unknown_argument () throw ();
+
+ unknown_argument (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ class missing_value: public exception
+ {
+ public:
+ virtual
+ ~missing_value () throw ();
+
+ missing_value (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class invalid_value: public exception
+ {
+ public:
+ virtual
+ ~invalid_value () throw ();
+
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message = std::string ());
+
+ const std::string&
+ option () const;
+
+ const std::string&
+ value () const;
+
+ const std::string&
+ message () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ std::string value_;
+ std::string message_;
+ };
+
+ class eos_reached: public exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class file_io_failure: public exception
+ {
+ public:
+ virtual
+ ~file_io_failure () throw ();
+
+ file_io_failure (const std::string& file);
+
+ const std::string&
+ file () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string file_;
+ };
+
+ class unmatched_quote: public exception
+ {
+ public:
+ virtual
+ ~unmatched_quote () throw ();
+
+ unmatched_quote (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ // Command line argument scanner interface.
+ //
+ // The values returned by next() are guaranteed to be valid
+ // for the two previous arguments up until a call to a third
+ // peek() or next().
+ //
+ // The position() function returns a monotonically-increasing
+ // number which, if stored, can later be used to determine the
+ // relative position of the argument returned by the following
+ // call to next(). Note that if multiple scanners are used to
+ // extract arguments from multiple sources, then the end
+ // position of the previous scanner should be used as the
+ // start position of the next.
+ //
+ class scanner
+ {
+ public:
+ virtual
+ ~scanner ();
+
+ virtual bool
+ more () = 0;
+
+ virtual const char*
+ peek () = 0;
+
+ virtual const char*
+ next () = 0;
+
+ virtual void
+ skip () = 0;
+
+ virtual std::size_t
+ position () = 0;
+ };
+
+ class argv_scanner: public scanner
+ {
+ public:
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ int
+ end () const;
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ protected:
+ std::size_t start_position_;
+ int i_;
+ int& argc_;
+ char** argv_;
+ bool erase_;
+ };
+
+ class argv_file_scanner: public argv_scanner
+ {
+ public:
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t start_position = 0);
+
+ struct option_info
+ {
+ // If search_func is not NULL, it is called, with the arg
+ // value as the second argument, to locate the options file.
+ // If it returns an empty string, then the file is ignored.
+ //
+ const char* option;
+ std::string (*search_func) (const char*, void* arg);
+ void* arg;
+ };
+
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const option_info* options = 0,
+ std::size_t options_count = 0,
+ std::size_t start_position = 0);
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ // Return the file path if the peeked at argument came from a file and
+ // the empty string otherwise. The reference is guaranteed to be valid
+ // till the end of the scanner lifetime.
+ //
+ const std::string&
+ peek_file ();
+
+ // Return the 1-based line number if the peeked at argument came from
+ // a file and zero otherwise.
+ //
+ std::size_t
+ peek_line ();
+
+ private:
+ const option_info*
+ find (const char*) const;
+
+ void
+ load (const std::string& file);
+
+ typedef argv_scanner base;
+
+ const std::string option_;
+ option_info option_info_;
+ const option_info* options_;
+ std::size_t options_count_;
+
+ struct arg
+ {
+ std::string value;
+ const std::string* file;
+ std::size_t line;
+ };
+
+ std::deque<arg> args_;
+ std::list<std::string> files_;
+
+ // Circular buffer of two arguments.
+ //
+ std::string hold_[2];
+ std::size_t i_;
+
+ bool skip_;
+
+ static int zero_argc_;
+ static std::string empty_string_;
+ };
+
+ template <typename X>
+ struct parser;
+ }
+ }
+ }
+}
+
+#include <string>
+
+namespace odb
+{
+ namespace oracle
+ {
+ namespace details
+ {
+ class options
+ {
+ public:
+ options ();
+
+ options (int& argc,
+ char** argv,
+ bool erase = false,
+ ::odb::oracle::details::cli::unknown_mode option = ::odb::oracle::details::cli::unknown_mode::fail,
+ ::odb::oracle::details::cli::unknown_mode argument = ::odb::oracle::details::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ ::odb::oracle::details::cli::unknown_mode option = ::odb::oracle::details::cli::unknown_mode::fail,
+ ::odb::oracle::details::cli::unknown_mode argument = ::odb::oracle::details::cli::unknown_mode::stop);
+
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::odb::oracle::details::cli::unknown_mode option = ::odb::oracle::details::cli::unknown_mode::fail,
+ ::odb::oracle::details::cli::unknown_mode argument = ::odb::oracle::details::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::odb::oracle::details::cli::unknown_mode option = ::odb::oracle::details::cli::unknown_mode::fail,
+ ::odb::oracle::details::cli::unknown_mode argument = ::odb::oracle::details::cli::unknown_mode::stop);
+
+ options (::odb::oracle::details::cli::scanner&,
+ ::odb::oracle::details::cli::unknown_mode option = ::odb::oracle::details::cli::unknown_mode::fail,
+ ::odb::oracle::details::cli::unknown_mode argument = ::odb::oracle::details::cli::unknown_mode::stop);
+
+ // Option accessors.
+ //
+ const std::string&
+ user () const;
+
+ bool
+ user_specified () const;
+
+ const std::string&
+ password () const;
+
+ bool
+ password_specified () const;
+
+ const std::string&
+ database () const;
+
+ bool
+ database_specified () const;
+
+ const std::string&
+ service () const;
+
+ bool
+ service_specified () const;
+
+ const std::string&
+ host () const;
+
+ bool
+ host_specified () const;
+
+ const unsigned int&
+ port () const;
+
+ bool
+ port_specified () const;
+
+ const std::string&
+ options_file () const;
+
+ bool
+ options_file_specified () const;
+
+ // Print usage information.
+ //
+ static ::odb::oracle::details::cli::usage_para
+ print_usage (::std::ostream&,
+ ::odb::oracle::details::cli::usage_para = ::odb::oracle::details::cli::usage_para::none);
+
+ // Implementation details.
+ //
+ protected:
+ bool
+ _parse (const char*, ::odb::oracle::details::cli::scanner&);
+
+ private:
+ bool
+ _parse (::odb::oracle::details::cli::scanner&,
+ ::odb::oracle::details::cli::unknown_mode option,
+ ::odb::oracle::details::cli::unknown_mode argument);
+
+ public:
+ std::string user_;
+ bool user_specified_;
+ std::string password_;
+ bool password_specified_;
+ std::string database_;
+ bool database_specified_;
+ std::string service_;
+ bool service_specified_;
+ std::string host_;
+ bool host_specified_;
+ unsigned int port_;
+ bool port_specified_;
+ std::string options_file_;
+ bool options_file_specified_;
+ };
+ }
+ }
+}
+
+#include <odb/oracle/details/options.ixx>
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
+#endif // LIBODB_ORACLE_DETAILS_OPTIONS_HXX
diff --git a/libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.ixx b/libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.ixx
new file mode 100644
index 0000000..a69d602
--- /dev/null
+++ b/libodb-oracle/odb/oracle/details/pregenerated/odb/oracle/details/options.ixx
@@ -0,0 +1,384 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <cassert>
+
+namespace odb
+{
+ namespace oracle
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ // usage_para
+ //
+ inline usage_para::
+ usage_para (value v)
+ : v_ (v)
+ {
+ }
+
+ // unknown_mode
+ //
+ inline unknown_mode::
+ unknown_mode (value v)
+ : v_ (v)
+ {
+ }
+
+ // exception
+ //
+ inline ::std::ostream&
+ operator<< (::std::ostream& os, const exception& e)
+ {
+ e.print (os);
+ return os;
+ }
+
+ // unknown_option
+ //
+ inline unknown_option::
+ unknown_option (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& unknown_option::
+ option () const
+ {
+ return option_;
+ }
+
+ // unknown_argument
+ //
+ inline unknown_argument::
+ unknown_argument (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unknown_argument::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // missing_value
+ //
+ inline missing_value::
+ missing_value (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& missing_value::
+ option () const
+ {
+ return option_;
+ }
+
+ // invalid_value
+ //
+ inline invalid_value::
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message)
+ : option_ (option),
+ value_ (value),
+ message_ (message)
+ {
+ }
+
+ inline const std::string& invalid_value::
+ option () const
+ {
+ return option_;
+ }
+
+ inline const std::string& invalid_value::
+ value () const
+ {
+ return value_;
+ }
+
+ inline const std::string& invalid_value::
+ message () const
+ {
+ return message_;
+ }
+
+ // file_io_failure
+ //
+ inline file_io_failure::
+ file_io_failure (const std::string& file)
+ : file_ (file)
+ {
+ }
+
+ inline const std::string& file_io_failure::
+ file () const
+ {
+ return file_;
+ }
+
+ // unmatched_quote
+ //
+ inline unmatched_quote::
+ unmatched_quote (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unmatched_quote::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // argv_scanner
+ //
+ inline argv_scanner::
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + 1),
+ i_ (1),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline argv_scanner::
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + static_cast<std::size_t> (start)),
+ i_ (start),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline int argv_scanner::
+ end () const
+ {
+ return i_;
+ }
+
+ // argv_file_scanner
+ //
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+
+ load (file);
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const option_info* options,
+ std::size_t options_count,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ load (file);
+ }
+ }
+ }
+ }
+}
+
+namespace odb
+{
+ namespace oracle
+ {
+ namespace details
+ {
+ // options
+ //
+
+ inline const std::string& options::
+ user () const
+ {
+ return this->user_;
+ }
+
+ inline bool options::
+ user_specified () const
+ {
+ return this->user_specified_;
+ }
+
+ inline const std::string& options::
+ password () const
+ {
+ return this->password_;
+ }
+
+ inline bool options::
+ password_specified () const
+ {
+ return this->password_specified_;
+ }
+
+ inline const std::string& options::
+ database () const
+ {
+ return this->database_;
+ }
+
+ inline bool options::
+ database_specified () const
+ {
+ return this->database_specified_;
+ }
+
+ inline const std::string& options::
+ service () const
+ {
+ return this->service_;
+ }
+
+ inline bool options::
+ service_specified () const
+ {
+ return this->service_specified_;
+ }
+
+ inline const std::string& options::
+ host () const
+ {
+ return this->host_;
+ }
+
+ inline bool options::
+ host_specified () const
+ {
+ return this->host_specified_;
+ }
+
+ inline const unsigned int& options::
+ port () const
+ {
+ return this->port_;
+ }
+
+ inline bool options::
+ port_specified () const
+ {
+ return this->port_specified_;
+ }
+
+ inline const std::string& options::
+ options_file () const
+ {
+ return this->options_file_;
+ }
+
+ inline bool options::
+ options_file_specified () const
+ {
+ return this->options_file_specified_;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
diff --git a/libodb-oracle/odb/oracle/error.cxx b/libodb-oracle/odb/oracle/error.cxx
new file mode 100644
index 0000000..3c9a7df
--- /dev/null
+++ b/libodb-oracle/odb/oracle/error.cxx
@@ -0,0 +1,225 @@
+// file : odb/oracle/error.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <oci.h>
+
+#include <cstring> // std::strlen
+#include <cassert>
+
+#include <odb/details/buffer.hxx>
+
+#include <odb/oracle/error.hxx>
+#include <odb/oracle/exceptions.hxx>
+#include <odb/oracle/connection.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace oracle
+ {
+ static void
+ translate_error (void* h, ub4 htype, sword r, connection* conn,
+ size_t pos, multiple_exceptions* mex)
+ {
+ assert (r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO);
+
+ switch (r)
+ {
+ case OCI_STILL_EXECUTING:
+ {
+ throw database_exception (0, "statement still executing");
+ break;
+ }
+ case OCI_NEED_DATA:
+ case OCI_NO_DATA:
+ {
+ throw database_exception (0, "unhandled OCI_*_DATA condition");
+ break;
+ }
+ case OCI_INVALID_HANDLE:
+ {
+ throw invalid_oci_handle ();
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ sb4 e;
+ char b[512]; // Error message will be truncated if it does not fit.
+
+ if (htype == OCI_HTYPE_ERROR)
+ {
+ // We need to translate certain Oracle error codes to special
+ // exceptions, such as deadlock, timeout, etc. The problem is we can
+ // have multiple records potentially with different error codes. If we
+ // have both, say, a deadlock code and some other code, then we should
+ // probably throw database_exception, which is more severe. To
+ // implement this we are going to pre-scan the records looking for the
+ // codes we are interested in. If in the process we see any other code,
+ // then we stop and go ahead to prepare and throw database_exception.
+ //
+ enum code
+ {
+ code_none,
+ code_deadlock,
+ code_timeout,
+ code_connection_lost
+ };
+
+ code c (code_none);
+
+ for (sb4 i (1);; ++i)
+ {
+ r = OCIErrorGet (h,
+ i,
+ 0,
+ &e,
+ reinterpret_cast<OraText*> (b),
+ 512,
+ htype);
+
+ if (r == OCI_NO_DATA)
+ break;
+
+ code nc;
+
+ if (e == 60 || // Deadlock detected while waiting for resource.
+ e == 104) // Deadlock detected; all public servers blocked.
+ nc = code_deadlock;
+ else if (e == 51 || // Timeout occurred while waiting for a resource.
+ e == 54 || // Resource busy and acquisition timeout expired.
+ e == 2049) // Distributed lock timeout.
+ nc = code_timeout;
+ else if (e == 28 || // Session has been killed.
+ e == 3113 || // End-of-file on communication channel.
+ e == 3135 || // Connection lost contact.
+ e == 3136 || // Inbound connection timed out.
+ e == 3138) // Connection terminated.
+ nc = code_connection_lost;
+ else
+ {
+ c = code_none;
+ break;
+ }
+
+ if (c != code_none && c != nc)
+ {
+ // Several different codes.
+ //
+ c = code_none;
+ break;
+ }
+
+ c = nc;
+ }
+
+ // Check if the connection is lost. If code is connection_lost,
+ // then we know it is gone. If code is deadlock, then the
+ // connection is most likely ok.
+ //
+ if (conn != 0 && (c == code_none || c == code_timeout))
+ {
+ OCIServer* server;
+ r = OCIAttrGet (conn->handle (),
+ OCI_HTYPE_SVCCTX,
+ &server,
+ 0,
+ OCI_ATTR_SERVER,
+ conn->error_handle ());
+
+ if (r != OCI_SUCCESS)
+ throw invalid_oci_handle ();
+
+ ub4 server_status;
+ r = OCIAttrGet (server,
+ OCI_HTYPE_SERVER,
+ &server_status,
+ 0,
+ OCI_ATTR_SERVER_STATUS,
+ conn->error_handle ());
+
+ if (r != OCI_SUCCESS)
+ throw invalid_oci_handle ();
+
+ if (server_status == OCI_SERVER_NOT_CONNECTED)
+ conn->mark_failed ();
+ }
+
+ switch (c)
+ {
+ case code_deadlock:
+ throw deadlock ();
+ case code_timeout:
+ throw timeout ();
+ case code_connection_lost:
+ {
+ if (conn != 0)
+ conn->mark_failed ();
+
+ throw connection_lost ();
+ }
+ case code_none:
+ break;
+ }
+ }
+
+ // Some other error code. Prepare database_exception.
+ //
+ database_exception dbe;
+
+ for (sb4 i (1);; ++i)
+ {
+ r = OCIErrorGet (h,
+ i,
+ 0,
+ &e,
+ reinterpret_cast<OraText*> (b),
+ 512,
+ htype);
+
+ if (r == OCI_NO_DATA)
+ break;
+
+ // Get rid of a trailing newline if there is one.
+ //
+ size_t n (strlen (b));
+ if (n != 0 && b[n - 1] == '\n')
+ b[n - 1] = '\0';
+
+ dbe.append (e, b);
+ }
+
+ if (mex == 0)
+ throw dbe;
+ else
+ // It could be that some of these errors are fatal. I guess we
+ // will just have to learn from experience which ones are. The
+ // client code can always treat specific error codes as fatal.
+ //
+ mex->insert (pos, dbe);
+ }
+
+ void
+ translate_error (OCIError* h, sword r, connection* c,
+ size_t pos, multiple_exceptions* mex)
+ {
+ translate_error (h, OCI_HTYPE_ERROR, r, c, pos, mex);
+ }
+
+ void
+ translate_error (connection& c, sword r)
+ {
+ translate_error (c.error_handle (), OCI_HTYPE_ERROR, r, &c, 0, 0);
+ }
+
+ void
+ translate_error (OCIEnv* h)
+ {
+ translate_error (h, OCI_HTYPE_ENV, OCI_ERROR, 0, 0, 0);
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/error.hxx b/libodb-oracle/odb/oracle/error.hxx
new file mode 100644
index 0000000..50092c8
--- /dev/null
+++ b/libodb-oracle/odb/oracle/error.hxx
@@ -0,0 +1,41 @@
+// file : odb/oracle/error.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_ERROR_HXX
+#define ODB_ORACLE_ERROR_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/oracle/oracle-fwd.hxx>
+#include <odb/oracle/forward.hxx> // connection, multiple_exceptions
+#include <odb/oracle/version.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ // Translate OCI error given an error handle and throw (or return,
+ // in case multiple_exceptions is not NULL) an appropriate exception.
+ //
+ LIBODB_ORACLE_EXPORT void
+ translate_error (OCIError*, sword result, connection* = 0,
+ std::size_t pos = 0, multiple_exceptions* = 0);
+
+ LIBODB_ORACLE_EXPORT void
+ translate_error (connection&, sword result);
+
+ // Translate an OCI error given an environment handle error and throw
+ // an appropriate exception.
+ //
+ LIBODB_ORACLE_EXPORT void
+ translate_error (OCIEnv*);
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_ERROR_HXX
diff --git a/libodb-oracle/odb/oracle/exceptions.cxx b/libodb-oracle/odb/oracle/exceptions.cxx
new file mode 100644
index 0000000..532306e
--- /dev/null
+++ b/libodb-oracle/odb/oracle/exceptions.cxx
@@ -0,0 +1,124 @@
+// file : odb/oracle/exceptions.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/oracle/exceptions.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace oracle
+ {
+ //
+ // database_exception
+ //
+
+ database_exception::record::
+ record (sb4 e, const string& m)
+ : error_ (e), message_ (m)
+ {
+ }
+
+ database_exception::
+ ~database_exception () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ database_exception::
+ database_exception ()
+ {
+ }
+
+ database_exception::
+ database_exception (sb4 e, const string& m)
+ {
+ append (e, m);
+ }
+
+ void database_exception::
+ append (sb4 e, const string& m)
+ {
+ records_.push_back (record (e, m));
+
+ if (!what_.empty ())
+ what_ += '\n';
+
+ ostringstream ostr;
+ ostr << e << ": " << m;
+ what_ += ostr.str ();
+ }
+
+ const char* database_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ database_exception* database_exception::
+ clone () const
+ {
+ return new database_exception (*this);
+ }
+
+ //
+ // lob_comparison
+ //
+
+ const char* lob_comparison::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "comparison of LOB values in queries not supported";
+ }
+
+ lob_comparison* lob_comparison::
+ clone () const
+ {
+ return new lob_comparison (*this);
+ }
+
+ //
+ // cli_exception
+ //
+
+ cli_exception::
+ cli_exception (const string& what)
+ : what_ (what)
+ {
+ }
+
+ cli_exception::
+ ~cli_exception () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ const char* cli_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ cli_exception* cli_exception::
+ clone () const
+ {
+ return new cli_exception (*this);
+ }
+
+ //
+ // invalid_oci_handle
+ //
+
+ const char* invalid_oci_handle::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "invalid oci handle passed or unable to allocate handle";
+ }
+
+ invalid_oci_handle* invalid_oci_handle::
+ clone () const
+ {
+ return new invalid_oci_handle (*this);
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/exceptions.hxx b/libodb-oracle/odb/oracle/exceptions.hxx
new file mode 100644
index 0000000..d21b742
--- /dev/null
+++ b/libodb-oracle/odb/oracle/exceptions.hxx
@@ -0,0 +1,135 @@
+// file : odb/oracle/exceptions.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_EXCEPTIONS_HXX
+#define ODB_ORACLE_EXCEPTIONS_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <vector>
+
+#include <odb/exceptions.hxx>
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx>
+#include <odb/oracle/oracle-fwd.hxx>
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ struct LIBODB_ORACLE_EXPORT database_exception: odb::database_exception
+ {
+ struct record
+ {
+ record (sb4 error, const std::string& message);
+
+ sb4
+ error () const
+ {
+ return error_;
+ }
+
+ const std::string&
+ message () const
+ {
+ return message_;
+ }
+
+ private:
+ sb4 error_;
+ std::string message_;
+ };
+
+ typedef std::vector<record> records;
+
+ typedef records::size_type size_type;
+ typedef records::const_iterator iterator;
+
+ iterator
+ begin () const
+ {
+ return records_.begin ();
+ }
+
+ iterator
+ end () const
+ {
+ return records_.end ();
+ }
+
+ size_type
+ size () const
+ {
+ return records_.size ();
+ }
+
+ public:
+ ~database_exception () ODB_NOTHROW_NOEXCEPT;
+
+ database_exception ();
+ database_exception (sb4 error, const std::string& message);
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual database_exception*
+ clone () const;
+
+ void
+ append (sb4 error, const std::string& message);
+
+ private:
+ records records_;
+ std::string what_;
+ };
+
+ struct LIBODB_ORACLE_EXPORT lob_comparison: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual lob_comparison*
+ clone () const;
+ };
+
+ struct LIBODB_ORACLE_EXPORT cli_exception: odb::exception
+ {
+ cli_exception (const std::string& what);
+ ~cli_exception () ODB_NOTHROW_NOEXCEPT;
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual cli_exception*
+ clone () const;
+
+ private:
+ std::string what_;
+ };
+
+ struct LIBODB_ORACLE_EXPORT invalid_oci_handle: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual invalid_oci_handle*
+ clone () const;
+ };
+
+ namespace core
+ {
+ using oracle::database_exception;
+ using oracle::lob_comparison;
+ using oracle::cli_exception;
+ using oracle::invalid_oci_handle;
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_EXCEPTIONS_HXX
diff --git a/libodb-oracle/odb/oracle/forward.hxx b/libodb-oracle/odb/oracle/forward.hxx
new file mode 100644
index 0000000..ae4d3a0
--- /dev/null
+++ b/libodb-oracle/odb/oracle/forward.hxx
@@ -0,0 +1,92 @@
+// file : odb/oracle/forward.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_FORWARD_HXX
+#define ODB_ORACLE_FORWARD_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ namespace core
+ {
+ using namespace odb::common;
+ }
+
+ //
+ //
+ class database;
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+ class connection_factory;
+ class statement;
+ class transaction;
+ class tracer;
+
+ namespace core
+ {
+ using oracle::database;
+ using oracle::connection;
+ using oracle::connection_ptr;
+ using oracle::transaction;
+ using oracle::statement;
+ }
+
+ // Implementation details.
+ //
+ enum statement_kind
+ {
+ statement_select,
+ statement_insert,
+ statement_update,
+ statement_delete,
+ statement_generic
+ };
+
+ class binding;
+ class select_statement;
+
+ template <typename T>
+ class object_statements;
+
+ template <typename T>
+ class polymorphic_root_object_statements;
+
+ template <typename T>
+ class polymorphic_derived_object_statements;
+
+ template <typename T>
+ class no_id_object_statements;
+
+ template <typename T>
+ class view_statements;
+
+ template <typename T>
+ class container_statements;
+
+ template <typename T>
+ class smart_container_statements;
+
+ template <typename T, typename ST>
+ class section_statements;
+
+ class query_base;
+ }
+
+ namespace details
+ {
+ template <>
+ struct counter_type<oracle::connection>
+ {
+ typedef shared_base counter;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_FORWARD_HXX
diff --git a/libodb-oracle/odb/oracle/no-id-object-result.hxx b/libodb-oracle/odb/oracle/no-id-object-result.hxx
new file mode 100644
index 0000000..93e7e54
--- /dev/null
+++ b/libodb-oracle/odb/oracle/no-id-object-result.hxx
@@ -0,0 +1,84 @@
+// file : odb/oracle/no-id-object-result.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_NO_ID_OBJECT_RESULT_HXX
+#define ODB_ORACLE_NO_ID_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/no-id-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx> // query_base
+#include <odb/oracle/statement.hxx>
+#include <odb/oracle/traits-calls.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <typename T>
+ class no_id_object_result_impl: public odb::no_id_object_result_impl<T>
+ {
+ public:
+ typedef odb::no_id_object_result_impl<T> base_type;
+
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_oracle> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~no_id_object_result_impl ();
+
+ no_id_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type&);
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ typedef oracle::change_callback change_callback_type;
+
+ static void
+ change_callback (void* context);
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ bool use_copy_;
+ typename object_traits::image_type* image_copy_;
+ };
+ }
+}
+
+#include <odb/oracle/no-id-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_NO_ID_OBJECT_RESULT_HXX
diff --git a/libodb-oracle/odb/oracle/no-id-object-result.txx b/libodb-oracle/odb/oracle/no-id-object-result.txx
new file mode 100644
index 0000000..7bac70b
--- /dev/null
+++ b/libodb-oracle/odb/oracle/no-id-object-result.txx
@@ -0,0 +1,149 @@
+// file : odb/oracle/no-id-object-result.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/oracle/no-id-object-statements.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <typename T>
+ no_id_object_result_impl<T>::
+ ~no_id_object_result_impl ()
+ {
+ invalidate ();
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ invalidate ()
+ {
+ change_callback_type& cc (statements_.image ().change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ delete image_copy_;
+ image_copy_ = 0;
+
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ no_id_object_result_impl<T>::
+ no_id_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ use_copy_ (false),
+ image_copy_ (0)
+ {
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ load (object_type& obj)
+ {
+ object_traits::callback (this->db_, obj, callback_event::pre_load);
+
+ tc_.init (obj,
+ use_copy_ ? *image_copy_ : statements_.image (),
+ &this->db_);
+
+ // If we are using a copy, make sure the callback information for
+ // LOB data also comes from the copy.
+ //
+ statement_->stream_result (
+ use_copy_ ? &statements_.image () : 0,
+ use_copy_ ? image_copy_ : 0);
+
+ object_traits::callback (this->db_, obj, callback_event::post_load);
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ typename object_traits::image_type& im (statements_.image ());
+ change_callback_type& cc (im.change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ use_copy_ = false;
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ }
+
+ if (statement_->fetch () == select_statement::no_data)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ else
+ {
+ cc.callback = &change_callback;
+ cc.context = this;
+ }
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t no_id_object_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ change_callback (void* c)
+ {
+ no_id_object_result_impl<T>* r (
+ static_cast<no_id_object_result_impl<T>*> (c));
+
+ typename object_traits::image_type im (r->statements_.image ());
+
+ if (r->image_copy_ == 0)
+ r->image_copy_ = new typename object_traits::image_type (im);
+ else
+ *r->image_copy_ = im;
+
+ im.change_callback_.callback = 0;
+ im.change_callback_.context = 0;
+
+ r->use_copy_ = true;
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/no-id-object-statements.hxx b/libodb-oracle/odb/oracle/no-id-object-statements.hxx
new file mode 100644
index 0000000..2e5a033
--- /dev/null
+++ b/libodb-oracle/odb/oracle/no-id-object-statements.hxx
@@ -0,0 +1,134 @@
+// file : odb/oracle/no-id-object-statements.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_NO_ID_OBJECT_STATEMENTS_HXX
+#define ODB_ORACLE_NO_ID_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx>
+#include <odb/oracle/oracle-types.hxx>
+#include <odb/oracle/binding.hxx>
+#include <odb/oracle/statement.hxx>
+#include <odb/oracle/statements-base.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ //
+ // Implementation for objects without object id.
+ //
+
+ template <typename T>
+ class no_id_object_statements: public statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_oracle> object_traits;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::image_type image_type;
+
+ typedef oracle::insert_statement insert_statement_type;
+
+ public:
+ no_id_object_statements (connection_type&);
+
+ virtual
+ ~no_id_object_statements ();
+
+ // Object image.
+ //
+ image_type&
+ image (std::size_t i = 0)
+ {
+ return image_[i];
+ }
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Select binding (needed for query support).
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ 0));
+
+ return *persist_;
+ }
+
+ public:
+ // select = total
+ // insert = total - inverse; inverse == 0 for object without id
+ //
+ static const std::size_t insert_column_count =
+ object_traits::column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count;
+
+ private:
+ no_id_object_statements (const no_id_object_statements&);
+ no_id_object_statements& operator= (const no_id_object_statements&);
+
+ private:
+ image_type image_[object_traits::batch];
+ sb4 status_[object_traits::batch];
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+ binding select_image_binding_;
+ bind select_image_bind_[select_column_count];
+
+ // Insert binding.
+ //
+ std::size_t insert_image_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[insert_column_count];
+
+ details::shared_ptr<insert_statement_type> persist_;
+ };
+ }
+}
+
+#include <odb/oracle/no-id-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_NO_ID_OBJECT_STATEMENTS_HXX
diff --git a/libodb-oracle/odb/oracle/no-id-object-statements.txx b/libodb-oracle/odb/oracle/no-id-object-statements.txx
new file mode 100644
index 0000000..23d330f
--- /dev/null
+++ b/libodb-oracle/odb/oracle/no-id-object-statements.txx
@@ -0,0 +1,39 @@
+// file : odb/oracle/no-id-object-statements.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <typename T>
+ no_id_object_statements<T>::
+ ~no_id_object_statements ()
+ {
+ }
+
+ template <typename T>
+ no_id_object_statements<T>::
+ no_id_object_statements (connection_type& conn)
+ : statements_base (conn),
+ select_image_binding_ (select_image_bind_, select_column_count),
+ insert_image_binding_ (insert_image_bind_,
+ insert_column_count,
+ object_traits::batch,
+ sizeof (image_type),
+ status_)
+ {
+ image_[0].version = 0; // Only version in the first element used.
+ select_image_version_ = 0;
+ insert_image_version_ = 0;
+
+ // SELECT statements only use the first element (no batches).
+ //
+ select_image_binding_.change_callback = image_[0].change_callback ();
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/oracle-fwd.hxx b/libodb-oracle/odb/oracle/oracle-fwd.hxx
new file mode 100644
index 0000000..cbc107a
--- /dev/null
+++ b/libodb-oracle/odb/oracle/oracle-fwd.hxx
@@ -0,0 +1,35 @@
+// file : odb/oracle/oracle-fwd.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_ORACLE_FWD_HXX
+#define ODB_ORACLE_ORACLE_FWD_HXX
+
+#include <odb/pre.hxx>
+
+// Forward declaration for some of the types defined in oci.h. This
+// allows us to avoid having to include oci.h in public headers.
+//
+typedef signed int sword;
+
+typedef unsigned char ub1;
+typedef signed char sb1;
+typedef signed short sb2;
+typedef unsigned short ub2;
+typedef signed int sb4;
+typedef unsigned int ub4;
+
+typedef struct OCIEnv OCIEnv;
+typedef struct OCISvcCtx OCISvcCtx;
+typedef struct OCIError OCIError;
+typedef struct OCIStmt OCIStmt;
+typedef struct OCIAuthInfo OCIAuthInfo;
+typedef struct OCITrans OCITrans;
+
+typedef struct OCIParam OCIParam;
+typedef struct OCILobLocator OCILobLocator;
+typedef struct OCIDateTime OCIDateTime;
+typedef struct OCIInterval OCIInterval;
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_ORACLE_FWD_HXX
diff --git a/libodb-oracle/odb/oracle/oracle-types.cxx b/libodb-oracle/odb/oracle/oracle-types.cxx
new file mode 100644
index 0000000..1ef531f
--- /dev/null
+++ b/libodb-oracle/odb/oracle/oracle-types.cxx
@@ -0,0 +1,374 @@
+// file : odb/oracle/oracle-types.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <oci.h>
+
+#include <odb/oracle/oracle-types.hxx>
+#include <odb/oracle/exceptions.hxx>
+#include <odb/oracle/error.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ //
+ // lob
+ //
+
+ lob::
+ ~lob ()
+ {
+ if (locator != 0)
+ OCIDescriptorFree (locator, OCI_DTYPE_LOB);
+ }
+
+ lob::
+ lob (const lob& x)
+ : environment (x.environment),
+ error (x.error),
+ locator (0),
+ buffer (x.buffer),
+ position (x.position)
+ {
+ // Watch out for exception safety.
+ //
+ if (x.locator != 0)
+ clone (x);
+ }
+
+ lob& lob::
+ operator= (const lob& x)
+ {
+ // Watch out for exception safety.
+ //
+ if (this != &x)
+ {
+ if (x.locator != 0)
+ clone (x);
+ else
+ {
+ if (locator != 0)
+ {
+ OCIDescriptorFree (locator, OCI_DTYPE_LOB);
+ locator = 0;
+ }
+ }
+
+ environment = x.environment;
+ error = x.error;
+ buffer = x.buffer;
+ position = x.position;
+ }
+
+ return *this;
+ }
+
+ void lob::
+ clone (const lob& x)
+ {
+ // Watch out for exception safety.
+ //
+ sword r;
+ bool alloc (locator == 0);
+
+ if (alloc)
+ {
+ void* d (0);
+ r = OCIDescriptorAlloc (x.environment, &d, OCI_DTYPE_LOB, 0, 0);
+
+ if (r != OCI_SUCCESS)
+ throw invalid_oci_handle ();
+
+ locator = static_cast<OCILobLocator*> (d);
+ }
+
+ r = OCILobAssign (x.environment, x.error, x.locator, &locator);
+
+ if (r != OCI_SUCCESS)
+ {
+ if (alloc)
+ {
+ OCIDescriptorFree (locator, OCI_DTYPE_LOB);
+ locator = 0;
+ }
+
+ translate_error (x.error, r);
+ }
+ }
+
+ //
+ // datetime
+ //
+
+ datetime::
+ ~datetime ()
+ {
+ if (descriptor != 0 && (flags & descriptor_free))
+ OCIDescriptorFree (descriptor, OCI_DTYPE_TIMESTAMP);
+ }
+
+ datetime::
+ datetime (const datetime& x)
+ : descriptor (0), flags (x.flags)
+ {
+ x.get (year_, month_, day_, hour_, minute_, second_, nanosecond_);
+ }
+
+ datetime& datetime::
+ operator= (const datetime& x)
+ {
+ if (this != &x)
+ {
+ if (descriptor != 0 && (flags & descriptor_free))
+ {
+ OCIDescriptorFree (descriptor, OCI_DTYPE_TIMESTAMP);
+ descriptor = 0;
+ }
+
+ flags = x.flags;
+ x.get (year_, month_, day_, hour_, minute_, second_, nanosecond_);
+ }
+
+ return *this;
+ }
+
+ void datetime::
+ get (sb2& y, ub1& m, ub1& d, ub1& h, ub1& mi, ub1& s, ub4& ns) const
+ {
+ if (descriptor != 0)
+ {
+ sword r (OCIDateTimeGetDate (environment,
+ error,
+ descriptor,
+ &y,
+ &m,
+ &d));
+
+ if (r != OCI_SUCCESS)
+ translate_error (error, r);
+
+ r = OCIDateTimeGetTime (environment,
+ error,
+ descriptor,
+ &h,
+ &mi,
+ &s,
+ &ns);
+
+ if (r != OCI_SUCCESS)
+ translate_error (error, r);
+ }
+ else
+ {
+ y = year_;
+ m = month_;
+ d = day_;
+ h = hour_;
+ mi = minute_;
+ s = second_;
+ ns = nanosecond_;
+ }
+ }
+
+ void datetime::
+ set (sb2 y, ub1 m, ub1 d, ub1 h, ub1 minute, ub1 s, ub4 ns)
+ {
+ if (descriptor != 0)
+ {
+ sword r (OCIDateTimeConstruct (environment,
+ error,
+ descriptor,
+ y,
+ m,
+ d,
+ h,
+ minute,
+ s,
+ ns,
+ 0,
+ 0));
+
+ if (r != OCI_SUCCESS)
+ translate_error (error, r);
+ }
+ else
+ {
+ year_ = y;
+ month_ = m;
+ day_ = d;
+ hour_ = h;
+ minute_ = minute;
+ second_ = s;
+ nanosecond_ = ns;
+ }
+ }
+
+ //
+ // interval_ym
+ //
+
+ interval_ym::
+ ~interval_ym ()
+ {
+ if (descriptor != 0 && (flags & descriptor_free))
+ OCIDescriptorFree (descriptor, OCI_DTYPE_INTERVAL_YM);
+ }
+
+ interval_ym::
+ interval_ym (const interval_ym& x)
+ : descriptor (0), flags (x.flags)
+ {
+ x.get (year_, month_);
+ }
+
+ interval_ym& interval_ym::
+ operator= (const interval_ym& x)
+ {
+ if (this != &x)
+ {
+ if (descriptor != 0 && (flags & descriptor_free))
+ {
+ OCIDescriptorFree (descriptor, OCI_DTYPE_INTERVAL_YM);
+ descriptor = 0;
+ }
+
+ flags = x.flags;
+ x.get (year_, month_);
+ }
+
+ return *this;
+ }
+
+ void interval_ym::
+ get (sb4& y, sb4& m) const
+ {
+ if (descriptor != 0)
+ {
+ sword r (OCIIntervalGetYearMonth (environment,
+ error,
+ &y,
+ &m,
+ descriptor));
+
+ if (r != OCI_SUCCESS)
+ translate_error (error, r);
+ }
+ else
+ {
+ y = year_;
+ m = month_;
+ }
+ }
+
+ void interval_ym::
+ set (sb4 y, sb4 m)
+ {
+ if (descriptor != 0)
+ {
+ sword r (OCIIntervalSetYearMonth (environment,
+ error,
+ y,
+ m,
+ descriptor));
+
+ if (r != OCI_SUCCESS)
+ translate_error (error, r);
+ }
+ else
+ {
+ year_ = y;
+ month_ = m;
+ }
+ }
+
+ //
+ // interval_ds
+ //
+
+ interval_ds::
+ ~interval_ds ()
+ {
+ if (descriptor != 0 && (flags & descriptor_free))
+ OCIDescriptorFree (descriptor, OCI_DTYPE_INTERVAL_DS);
+ }
+
+ interval_ds::
+ interval_ds (const interval_ds& x)
+ : descriptor (0), flags (x.flags)
+ {
+ x.get (day_, hour_, minute_, second_, nanosecond_);
+ }
+
+ interval_ds& interval_ds::
+ operator= (const interval_ds& x)
+ {
+ if (this != &x)
+ {
+ if (descriptor != 0 && (flags & descriptor_free))
+ {
+ OCIDescriptorFree (descriptor, OCI_DTYPE_TIMESTAMP);
+ descriptor = 0;
+ }
+
+ flags = x.flags;
+ x.get (day_, hour_, minute_, second_, nanosecond_);
+ }
+
+ return *this;
+ }
+
+ void interval_ds::
+ get (sb4& d, sb4& h, sb4& m, sb4& s, sb4& ns) const
+ {
+ if (descriptor != 0)
+ {
+ sword r (OCIIntervalGetDaySecond (environment,
+ error,
+ &d,
+ &h,
+ &m,
+ &s,
+ &ns,
+ descriptor));
+
+ if (r != OCI_SUCCESS)
+ translate_error (error, r);
+ }
+ else
+ {
+ d = day_;
+ h = hour_;
+ m = minute_;
+ s = second_;
+ ns = nanosecond_;
+ }
+ }
+
+ void interval_ds::
+ set (sb4 d, sb4 h, sb4 m, sb4 s, sb4 ns)
+ {
+ if (descriptor != 0)
+ {
+ sword r (OCIIntervalSetDaySecond (environment,
+ error,
+ d,
+ h,
+ m,
+ s,
+ ns,
+ descriptor));
+
+ if (r != OCI_SUCCESS)
+ translate_error (error, r);
+ }
+ else
+ {
+ day_ = d;
+ hour_ = h;
+ minute_ = m;
+ second_ = s;
+ nanosecond_ = ns;
+ }
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/oracle-types.hxx b/libodb-oracle/odb/oracle/oracle-types.hxx
new file mode 100644
index 0000000..a9553b2
--- /dev/null
+++ b/libodb-oracle/odb/oracle/oracle-types.hxx
@@ -0,0 +1,300 @@
+// file : odb/oracle/oracle-types.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_ORACLE_TYPES_HXX
+#define ODB_ORACLE_ORACLE_TYPES_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/buffer.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx> // binding
+#include <odb/oracle/oracle-fwd.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ enum chunk_position
+ {
+ chunk_one,
+ chunk_first,
+ chunk_next,
+ chunk_last
+ };
+
+ // Callback function signature used to specify LOB parameters that are
+ // passed to the database. If false is returned from the callback,
+ // statement execution is aborted.
+ //
+ typedef bool (*param_callback_type) (
+ const void* context, // [in] The user context.
+ ub4* position_context, // [in] A position context. A callback is free to
+ // use this to track position information. This is
+ // initialized to zero before the callback is
+ // invoked for the first time.
+ const void** buffer, // [out] On return, a pointer to a buffer
+ // containing parameter data.
+ ub4* size, // [out] The parameter data size in bytes.
+ chunk_position*, // [out] The position of the chunk of data in
+ // buffer.
+ void* temp_buffer, // [in] A temporary buffer that may be used if
+ // required. The buffer argument should specify
+ // this buffer on return if it is used.
+ ub4 capacity); // [in] The temporary buffer size in bytes.
+
+ // Callback function signature used to specify LOB values returned from
+ // the database. If false is returned, database_exception is thrown.
+ //
+ typedef bool (*result_callback_type) (
+ void* context, // [in] The user context.
+ ub4* position_context, // [in] A position context. A callback is free to
+ // use this to track position information. This is
+ // initialized to zero before the callback is
+ // invoked for the first time.
+ void* buffer, // [in] A buffer containing the result data.
+ ub4 size, // [in] The result data size in bytes.
+ chunk_position); // [in] The position of this chunk.
+
+ struct lob_callback
+ {
+ union
+ {
+ param_callback_type param;
+ result_callback_type result;
+ } callback;
+
+ union
+ {
+ const void* param;
+ void* result;
+ } context;
+ };
+
+ struct bind
+ {
+ // This enumeration identifies the possible buffer types that can be
+ // bound to a bind instance. In most cases, these map directly to
+ // SQLT_XXX codes, identifying an external OCI type. nstring and nclob
+ // however have no equivalent OCI typecode. These additional identifiers
+ // allow for a consistent interface across all types. Note that these
+ // values are mapped to their corresponding external OCI typecodes (if
+ // any) using their integer values, and should therefore not be
+ // rearranged or explicitly assigned without also adjusting the
+ // sqlt_lookup array in odb/oracle/statement.cxx.
+ //
+ enum buffer_type
+ {
+ integer, // Buffer is an integer type of size specified by size.
+ uinteger, // Buffer is an unsigned integer of size specified by
+ // size.
+ binary_float, // Buffer is a float.
+ binary_double, // Buffer is a double.
+ number, // Buffer is a variable length char array.
+ date, // Buffer is a 7-byte char array.
+ timestamp, // Buffer is a datetime.
+ interval_ym, // Buffer is an interval_ym.
+ interval_ds, // Buffer is an interval_ds.
+ string, // Buffer is a variable length char array.
+ nstring, // Buffer is a variable length char array.
+ raw, // Buffer is a variable length char array.
+ blob, // Bind is a callback.
+ clob, // Bind is a callback.
+ nclob, // Bind is a callback.
+ last // Used as an end of list marker.
+ };
+
+ buffer_type type; // The type stored by buffer.
+ void* buffer; // Data buffer pointer. For LOB type bindings, this is
+ // interpreted as an oracle::lob*.
+ ub2* size; // The number of bytes in buffer.
+ ub4 capacity; // The maximum number of bytes that can be stored in
+ // the buffer. For LOBs, it used to store array skip
+ // size.
+ sb2* indicator; // Pointer to an OCI indicator variable.
+
+ lob_callback* callback;
+ };
+
+ // An instance of this structure specifies the function to invoke and
+ // the context to pass when the object/view image is about to be
+ // modified. This mechanism is used by the query machinery to save the
+ // image between result iteration and dereferencing if something gets
+ // executed between these two operations that would overwrite the
+ // image.
+ //
+ struct change_callback
+ {
+ change_callback (): callback (0), context (0) {};
+
+ void (*callback) (void*);
+ void* context;
+ };
+
+ // The lob structure wraps data required for both parameter and result
+ // LOB type bindings.
+ //
+ struct LIBODB_ORACLE_EXPORT lob
+ {
+ ~lob ();
+ lob (): locator (0), buffer (0), position (0) {}
+
+ lob (const lob&);
+ lob& operator= (const lob&);
+
+ private:
+ void
+ clone (const lob&);
+
+ public:
+ OCIEnv* environment;
+ OCIError* error;
+ OCILobLocator* locator;
+
+ details::buffer* buffer;
+ ub4 position;
+ };
+
+ //
+ // The OCIDateTime and OCIInterval APIs require that an environment and
+ // error handle be passed to any function that manipulates an OCIDateTime
+ // or OCIInterval descriptor. It is however impossible to obtain these
+ // handles at the time a temporal data image is first initialized. The
+ // following structures allow ODB generated code to interact with the OCI
+ // temporal descriptor types indirectly via C++ primitives. The wrapped OCI
+ // descriptor is then set using these primitives at a time when the
+ // required data is available. A symmetric get interface is provided for
+ // consistency.
+ //
+
+ // Descriptor management flags.
+ //
+ const unsigned short descriptor_cache = 0x01;
+ const unsigned short descriptor_free = 0x02;
+
+ struct LIBODB_ORACLE_EXPORT datetime
+ {
+ void
+ get (sb2& year,
+ ub1& month,
+ ub1& day,
+ ub1& hour,
+ ub1& minute,
+ ub1& second,
+ ub4& nanosecond) const;
+
+ void
+ set (sb2 year,
+ ub1 month,
+ ub1 day,
+ ub1 hour,
+ ub1 minute,
+ ub1 second,
+ ub4 nanosecond);
+
+ ~datetime ();
+ datetime (unsigned short f = descriptor_cache | descriptor_free)
+ : descriptor (0), flags (f) {}
+
+ datetime (const datetime&);
+ datetime& operator= (const datetime&);
+
+ // Use the get() and set() functions above unless you know what you
+ // are doing and understand how copying of datetime works.
+ //
+ public:
+ OCIEnv* environment;
+ OCIError* error;
+ OCIDateTime* descriptor;
+
+ unsigned short flags;
+
+ public:
+ sb2 year_;
+ ub1 month_;
+ ub1 day_;
+ ub1 hour_;
+ ub1 minute_;
+ ub1 second_;
+ ub4 nanosecond_;
+ };
+
+ struct LIBODB_ORACLE_EXPORT interval_ym
+ {
+ void
+ get (sb4& year, sb4& month) const;
+
+ void
+ set (sb4 year, sb4 month);
+
+ ~interval_ym ();
+ interval_ym (unsigned short f = descriptor_cache | descriptor_free)
+ : descriptor (0), flags (f) {}
+
+ interval_ym (const interval_ym&);
+ interval_ym& operator= (const interval_ym&);
+
+ // Use the get() and set() functions above unless you know what you
+ // are doing and understand how copying of interval_ym works.
+ //
+ public:
+ OCIEnv* environment;
+ OCIError* error;
+ OCIInterval* descriptor;
+
+ unsigned short flags;
+
+ public:
+ sb4 year_;
+ sb4 month_;
+ };
+
+ struct LIBODB_ORACLE_EXPORT interval_ds
+ {
+ void
+ get (sb4& day,
+ sb4& hour,
+ sb4& minute,
+ sb4& second,
+ sb4& nanosecond) const;
+
+ void
+ set (sb4 day,
+ sb4 hour,
+ sb4 minute,
+ sb4 second,
+ sb4 nanosecond);
+
+ ~interval_ds ();
+ interval_ds (unsigned short f = descriptor_cache | descriptor_free)
+ : descriptor (0), flags (f) {}
+
+ interval_ds (const interval_ds&);
+ interval_ds& operator= (const interval_ds&);
+
+ // Use the get() and set() functions above unless you know what you
+ // are doing and understand how copying of interval_ds works.
+ //
+ public:
+ OCIEnv* environment;
+ OCIError* error;
+ OCIInterval* descriptor;
+
+ unsigned short flags;
+
+ public:
+ sb4 day_;
+ sb4 hour_;
+ sb4 minute_;
+ sb4 second_;
+ sb4 nanosecond_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_ORACLE_TYPES_HXX
diff --git a/libodb-oracle/odb/oracle/polymorphic-object-result.hxx b/libodb-oracle/odb/oracle/polymorphic-object-result.hxx
new file mode 100644
index 0000000..ddb3055
--- /dev/null
+++ b/libodb-oracle/odb/oracle/polymorphic-object-result.hxx
@@ -0,0 +1,98 @@
+// file : odb/oracle/polymorphic-object-result.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_POLYMORPHIC_OBJECT_RESULT_HXX
+#define ODB_ORACLE_POLYMORPHIC_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/polymorphic-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx> // query_base
+#include <odb/oracle/statement.hxx>
+#include <odb/oracle/traits-calls.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <typename T>
+ class polymorphic_object_result_impl:
+ public odb::polymorphic_object_result_impl<T>
+ {
+ public:
+ typedef odb::polymorphic_object_result_impl<T> base_type;
+
+ typedef typename base_type::id_type id_type;
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_oracle> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename base_type::root_type root_type;
+ typedef typename base_type::discriminator_type discriminator_type;
+
+ typedef object_traits_impl<root_type, id_oracle> root_traits;
+
+ typedef typename object_traits::image_type image_type;
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~polymorphic_object_result_impl ();
+
+ polymorphic_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type*, bool fetch);
+
+ virtual id_type
+ load_id ();
+
+ virtual discriminator_type
+ load_discriminator ();
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ typedef oracle::change_callback change_callback_type;
+
+ static void
+ change_callback (void* context);
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ bool use_copy_;
+ image_type* image_copy_;
+ };
+ }
+}
+
+#include <odb/oracle/polymorphic-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_POLYMORPHIC_OBJECT_RESULT_HXX
diff --git a/libodb-oracle/odb/oracle/polymorphic-object-result.txx b/libodb-oracle/odb/oracle/polymorphic-object-result.txx
new file mode 100644
index 0000000..b810b29
--- /dev/null
+++ b/libodb-oracle/odb/oracle/polymorphic-object-result.txx
@@ -0,0 +1,320 @@
+// file : odb/oracle/polymorphic-object-result.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/oracle/polymorphic-object-statements.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <typename T>
+ polymorphic_object_result_impl<T>::
+ ~polymorphic_object_result_impl ()
+ {
+ invalidate ();
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ invalidate ()
+ {
+ change_callback_type& cc (
+ statements_.root_statements ().image ().change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.context = 0;
+ cc.callback = 0;
+ }
+
+ if (image_copy_ != 0)
+ {
+ object_traits::free_image (image_copy_);
+ image_copy_ = 0;
+ }
+
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ polymorphic_object_result_impl<T>::
+ polymorphic_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> st,
+ statements_type& sts,
+ const schema_version_migration* svm)
+ : base_type (sts.connection ()),
+ statement_ (st),
+ statements_ (sts),
+ tc_ (svm),
+ use_copy_ (false),
+ image_copy_ (0)
+ {
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ load (object_type* pobj, bool)
+ {
+ typename statements_type::root_statements_type& rsts (
+ statements_.root_statements ());
+
+ // This is a top-level call so the statements cannot be locked.
+ //
+ assert (!rsts.locked ());
+ typename statements_type::auto_lock l (rsts);
+
+ image_type& i (use_copy_ ? *image_copy_ : statements_.image ());
+ typename root_traits::image_type& ri (
+ use_copy_ ? object_traits::root_image (i) : rsts.image ());
+
+ id_type id (root_traits::id (ri));
+
+ // Determine this object's dynamic type.
+ //
+ typedef typename root_traits::info_type info_type;
+ discriminator_type d (root_traits::discriminator (ri));
+
+ // Use the polymorphic_info() helper to get concrete_info if
+ // object_type is concrete and NULL if it is abstract.
+ //
+ const info_type* spi (polymorphic_info (object_traits::info));
+ const info_type& pi (
+ spi != 0 && spi->discriminator == d
+ ? *spi
+ : root_traits::map->find (d));
+
+ typedef typename root_traits::pointer_type root_pointer_type;
+ typedef typename root_traits::pointer_traits root_pointer_traits;
+
+ typename object_traits::pointer_cache_traits::insert_guard ig;
+
+ if (pobj == 0)
+ {
+ // Need to create a new instance of the dynamic type.
+ //
+ root_pointer_type rp (pi.create ());
+ pointer_type p (
+ root_pointer_traits::template static_pointer_cast<object_type> (rp));
+
+ // Insert it as a root pointer (for non-unique pointers, rp should
+ // still be valid and for unique pointers this is a no-op).
+ //
+ ig.reset (
+ object_traits::pointer_cache_traits::insert (this->db_, id, rp));
+
+ pobj = &pointer_traits::get_ref (p);
+ current (p);
+ }
+ else
+ {
+ // We are loading into an existing instance. If the static and
+ // dynamic types differ, then make sure the instance is at least
+ // of the dynamic type.
+ //
+ if (&pi != &object_traits::info)
+ {
+ const info_type& dpi (root_traits::map->find (typeid (*pobj)));
+
+ if (&dpi != &pi && dpi.derived (pi))
+ throw object_not_persistent (); // @@ type_mismatch ?
+ }
+ }
+
+ callback_event ce (callback_event::pre_load);
+ pi.dispatch (info_type::call_callback, this->db_, pobj, &ce);
+
+ tc_.init (*pobj, i, &this->db_);
+
+ // If we are using a copy, make sure the callback information for
+ // LOB data also comes from the copy.
+ //
+ statement_->stream_result (
+ use_copy_ ? &statements_.image () : 0,
+ use_copy_ ? image_copy_ : 0);
+
+ // Initialize the id image and binding and load the rest of the object
+ // (containers, dynamic part, etc).
+ //
+ typename object_traits::id_image_type& idi (statements_.id_image ());
+ root_traits::init (idi, id);
+
+ binding& idb (statements_.id_image_binding ());
+ if (idi.version != statements_.id_image_version () || idb.version == 0)
+ {
+ object_traits::bind (idb.bind, idi);
+ statements_.id_image_version (idi.version);
+ idb.version++;
+ }
+
+ tc_.load_ (statements_, *pobj, false);
+
+ // Load the dynamic part of the object unless static and dynamic
+ // types are the same.
+ //
+ if (&pi != &object_traits::info)
+ {
+ std::size_t d (object_traits::depth);
+ pi.dispatch (info_type::call_load, this->db_, pobj, &d);
+ };
+
+ rsts.load_delayed (tc_.version ());
+ l.unlock ();
+
+ ce = callback_event::post_load;
+ pi.dispatch (info_type::call_callback, this->db_, pobj, &ce);
+ object_traits::pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+
+ template <typename T>
+ typename polymorphic_object_result_impl<T>::id_type
+ polymorphic_object_result_impl<T>::
+ load_id ()
+ {
+ typename root_traits::image_type& i (
+ use_copy_
+ ? object_traits::root_image (*image_copy_)
+ : statements_.root_statements ().image ());
+
+ return root_traits::id (i);
+ }
+
+ template <typename T>
+ typename polymorphic_object_result_impl<T>::discriminator_type
+ polymorphic_object_result_impl<T>::
+ load_discriminator ()
+ {
+ typename root_traits::image_type& i (
+ use_copy_
+ ? object_traits::root_image (*image_copy_)
+ : statements_.root_statements ().image ());
+
+ return root_traits::discriminator (i);
+ }
+
+ template <typename T, typename R>
+ struct polymorphic_image_rebind
+ {
+ // Derived type version.
+ //
+ typedef object_traits_impl<T, id_oracle> traits;
+
+ static void
+ rebind (typename traits::statements_type& sts,
+ const schema_version_migration* svm)
+ {
+ typename traits::image_type& im (sts.image ());
+
+ if (traits::check_version (sts.select_image_versions (), im))
+ {
+ binding& b (sts.select_image_binding (traits::depth));
+ object_traits_calls<T> tc (svm);
+ tc.bind (b.bind, 0, 0, im, statement_select);
+ traits::update_version (
+ sts.select_image_versions (), im, sts.select_image_bindings ());
+ }
+ }
+ };
+
+ template <typename R>
+ struct polymorphic_image_rebind<R, R>
+ {
+ // Root type version.
+ //
+ typedef object_traits_impl<R, id_oracle> traits;
+
+ static void
+ rebind (typename traits::statements_type& sts,
+ const schema_version_migration* svm)
+ {
+ typename traits::image_type& im (sts.image ());
+
+ if (im.version != sts.select_image_version ())
+ {
+ binding& b (sts.select_image_binding ());
+ object_traits_calls<R> tc (svm);
+ tc.bind (b.bind, im, statement_select);
+ sts.select_image_version (im.version);
+ b.version++;
+ }
+ }
+ };
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ change_callback_type& cc (
+ statements_.root_statements ().image ().change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ use_copy_ = false;
+ polymorphic_image_rebind<object_type, root_type>::rebind (
+ statements_, tc_.version ());
+
+ if (statement_->fetch () == select_statement::no_data)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ else
+ {
+ cc.callback = &change_callback;
+ cc.context = this;
+ }
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t polymorphic_object_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ change_callback (void* c)
+ {
+ polymorphic_object_result_impl<T>* r (
+ static_cast<polymorphic_object_result_impl<T>*> (c));
+ image_type& im (r->statements_.image ());
+
+ if (r->image_copy_ == 0)
+ r->image_copy_ = object_traits::clone_image (im);
+ else
+ object_traits::copy_image (*r->image_copy_, im);
+
+ typename root_traits::image_type& rim (
+ r->statements_.root_statements ().image ());
+
+ rim.change_callback_.callback = 0;
+ rim.change_callback_.context = 0;
+
+ r->use_copy_ = true;
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/polymorphic-object-statements.hxx b/libodb-oracle/odb/oracle/polymorphic-object-statements.hxx
new file mode 100644
index 0000000..fbb8fcf
--- /dev/null
+++ b/libodb-oracle/odb/oracle/polymorphic-object-statements.hxx
@@ -0,0 +1,462 @@
+// file : odb/oracle/polymorphic-object-statements.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_POLYMORPHIC_OBJECT_STATEMENTS_HXX
+#define ODB_ORACLE_POLYMORPHIC_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx>
+#include <odb/oracle/oracle-types.hxx>
+#include <odb/oracle/binding.hxx>
+#include <odb/oracle/statement.hxx>
+#include <odb/oracle/statements-base.hxx>
+#include <odb/oracle/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ //
+ // Implementation for polymorphic objects.
+ //
+
+ template <typename T>
+ class polymorphic_root_object_statements: public object_statements<T>
+ {
+ public:
+ typedef typename object_statements<T>::connection_type connection_type;
+ typedef typename object_statements<T>::object_traits object_traits;
+ typedef typename object_statements<T>::id_image_type id_image_type;
+
+ typedef
+ typename object_traits::discriminator_image_type
+ discriminator_image_type;
+
+ typedef
+ typename object_statements<T>::select_statement_type
+ select_statement_type;
+
+ public:
+ // Interface compatibility with derived_object_statements.
+ //
+ typedef polymorphic_root_object_statements root_statements_type;
+
+ root_statements_type&
+ root_statements ()
+ {
+ return *this;
+ }
+
+ public:
+ // Discriminator binding.
+ //
+ discriminator_image_type&
+ discriminator_image () {return discriminator_image_;}
+
+ std::size_t
+ discriminator_image_version () const
+ {return discriminator_image_version_;}
+
+ void
+ discriminator_image_version (std::size_t v)
+ {discriminator_image_version_ = v;}
+
+ binding&
+ discriminator_image_binding () {return discriminator_image_binding_;}
+
+ // Id binding for discriminator retrieval.
+ //
+ id_image_type&
+ discriminator_id_image () {return discriminator_id_image_;}
+
+ std::size_t
+ discriminator_id_image_version () const
+ {return discriminator_id_image_version_;}
+
+ void
+ discriminator_id_image_version (std::size_t v)
+ {discriminator_id_image_version_ = v;}
+
+ binding&
+ discriminator_id_image_binding ()
+ {return discriminator_id_image_binding_;}
+
+ //
+ //
+ select_statement_type&
+ find_discriminator_statement ()
+ {
+ if (find_discriminator_ == 0)
+ find_discriminator_.reset (
+ new (details::shared) select_statement_type (
+ this->conn_,
+ object_traits::find_discriminator_statement,
+ false, // Doesn't need to be processed.
+ false, // Don't optimize.
+ discriminator_id_image_binding_,
+ discriminator_image_binding_,
+ 0)); // No LOB prefetch (discriminator cannot be LOB).
+
+ return *find_discriminator_;
+ }
+
+ public:
+ polymorphic_root_object_statements (connection_type&);
+
+ virtual
+ ~polymorphic_root_object_statements ();
+
+ // Static "override" (statements type).
+ //
+ void
+ load_delayed (const schema_version_migration* svm)
+ {
+ assert (this->locked ());
+
+ if (!this->delayed_.empty ())
+ this->template load_delayed_<polymorphic_root_object_statements> (
+ svm);
+ }
+
+ public:
+ static const std::size_t id_column_count =
+ object_statements<T>::id_column_count;
+
+ static const std::size_t discriminator_column_count =
+ object_traits::discriminator_column_count;
+
+ static const std::size_t managed_optimistic_column_count =
+ object_traits::managed_optimistic_column_count;
+
+ private:
+ // Discriminator image.
+ //
+ discriminator_image_type discriminator_image_;
+ std::size_t discriminator_image_version_;
+ binding discriminator_image_binding_;
+ bind discriminator_image_bind_[discriminator_column_count +
+ managed_optimistic_column_count];
+
+ // Id image for discriminator retrieval (only used as a parameter).
+ //
+ id_image_type discriminator_id_image_;
+ std::size_t discriminator_id_image_version_;
+ binding discriminator_id_image_binding_;
+ bind discriminator_id_image_bind_[id_column_count];
+
+ details::shared_ptr<select_statement_type> find_discriminator_;
+ };
+
+ template <typename T>
+ class polymorphic_derived_object_statements: public statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_oracle> object_traits;
+ typedef typename object_traits::id_type id_type;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::id_image_type id_image_type;
+ typedef typename object_traits::image_type image_type;
+
+ typedef typename object_traits::root_type root_type;
+ typedef
+ polymorphic_root_object_statements<root_type>
+ root_statements_type;
+
+ typedef typename object_traits::base_type base_type;
+ typedef
+ typename object_traits::base_traits::statements_type
+ base_statements_type;
+
+ typedef
+ typename object_traits::extra_statement_cache_type
+ extra_statement_cache_type;
+
+ typedef oracle::insert_statement insert_statement_type;
+ typedef oracle::select_statement select_statement_type;
+ typedef oracle::update_statement update_statement_type;
+ typedef oracle::delete_statement delete_statement_type;
+
+ typedef typename root_statements_type::auto_lock auto_lock;
+
+ public:
+ polymorphic_derived_object_statements (connection_type&);
+
+ virtual
+ ~polymorphic_derived_object_statements ();
+
+ public:
+ // Delayed loading.
+ //
+ static void
+ delayed_loader (odb::database&,
+ const id_type&,
+ root_type&,
+ const schema_version_migration*);
+
+ public:
+ // Root and immediate base statements.
+ //
+ root_statements_type&
+ root_statements ()
+ {
+ return root_statements_;
+ }
+
+ base_statements_type&
+ base_statements ()
+ {
+ return base_statements_;
+ }
+
+ public:
+ // Object image.
+ //
+ image_type&
+ image ()
+ {
+ return image_;
+ }
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ std::size_t
+ insert_id_binding_version () const { return insert_id_binding_version_;}
+
+ void
+ insert_id_binding_version (std::size_t v) {insert_id_binding_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_binding_version () const { return update_id_binding_version_;}
+
+ void
+ update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t*
+ select_image_versions () { return select_image_versions_;}
+
+ binding*
+ select_image_bindings () {return select_image_bindings_;}
+
+ binding&
+ select_image_binding (std::size_t d)
+ {
+ return select_image_bindings_[object_traits::depth - d];
+ }
+
+ // Object id binding (comes from the root statements).
+ //
+ id_image_type&
+ id_image () {return root_statements_.id_image ();}
+
+ std::size_t
+ id_image_version () const {return root_statements_.id_image_version ();}
+
+ void
+ id_image_version (std::size_t v) {root_statements_.id_image_version (v);}
+
+ binding&
+ id_image_binding () {return root_statements_.id_image_binding ();}
+
+ binding&
+ optimistic_id_image_binding () {
+ return root_statements_.optimistic_id_image_binding ();}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ 0));
+
+ return *persist_;
+ }
+
+ select_statement_type&
+ find_statement (std::size_t d)
+ {
+ std::size_t i (object_traits::depth - d);
+ details::shared_ptr<select_statement_type>& p (find_[i]);
+
+ if (p == 0)
+ p.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ object_traits::find_statements[i],
+ object_traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ root_statements_.id_image_binding (),
+ select_image_bindings_[i],
+ 4096)); // Hardcode a 4kB LOB prefetch size.
+
+ return *p;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ object_traits::update_statement,
+ object_traits::versioned, // Process if versioned.
+ update_image_binding_));
+
+ return *update_;
+ }
+
+ delete_statement_type&
+ erase_statement ()
+ {
+ if (erase_ == 0)
+ erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::erase_statement,
+ root_statements_.id_image_binding ()));
+
+ return *erase_;
+ }
+
+ // Extra (container, section) statement cache.
+ //
+ extra_statement_cache_type&
+ extra_statement_cache ()
+ {
+ return extra_statement_cache_.get (
+ conn_,
+ image_,
+ id_image (),
+ id_image_binding (),
+ &id_image_binding ()); // Note, not id+version.
+ }
+
+ public:
+ // select = total - id - separate_load + base::select
+ // insert = total - inverse
+ // update = total - inverse - id - readonly - separate_update
+ //
+ static const std::size_t id_column_count =
+ object_traits::id_column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count -
+ id_column_count -
+ object_traits::separate_load_column_count +
+ base_statements_type::select_column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count -
+ object_traits::inverse_column_count;
+
+ static const std::size_t update_column_count = insert_column_count -
+ object_traits::id_column_count -
+ object_traits::readonly_column_count -
+ object_traits::separate_update_column_count;
+
+ private:
+ polymorphic_derived_object_statements (
+ const polymorphic_derived_object_statements&);
+
+ polymorphic_derived_object_statements&
+ operator= (const polymorphic_derived_object_statements&);
+
+ private:
+ root_statements_type& root_statements_;
+ base_statements_type& base_statements_;
+
+ extra_statement_cache_ptr<extra_statement_cache_type,
+ image_type,
+ id_image_type> extra_statement_cache_;
+
+ image_type image_;
+
+ // Select binding. Here we are have an array of statements/bindings
+ // one for each depth. In other words, if we have classes root, base,
+ // and derived, then we have the following array of statements:
+ //
+ // [0] d + b + r
+ // [1] d + b
+ // [2] d
+ //
+ // Also, because we have a chain of images bound to these statements,
+ // we have an array of versions, one entry for each base plus one for
+ // our own image.
+ //
+ // A poly-abstract class only needs the first statement and in this
+ // case we have only one entry in the the bindings and statements
+ // arrays (but not versions; we still have a chain of images).
+ //
+ std::size_t select_image_versions_[object_traits::depth];
+ binding select_image_bindings_[
+ object_traits::abstract ? 1 : object_traits::depth];
+ bind select_image_bind_[select_column_count];
+
+ // Insert binding. The id binding is copied from the hierarchy root.
+ //
+ std::size_t insert_image_version_;
+ std::size_t insert_id_binding_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[insert_column_count];
+
+ // Update binding. The id suffix binding is copied from the hierarchy
+ // root.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_binding_version_;
+ binding update_image_binding_;
+ bind update_image_bind_[update_column_count + id_column_count];
+
+ details::shared_ptr<insert_statement_type> persist_;
+ details::shared_ptr<select_statement_type> find_[
+ object_traits::abstract ? 1 : object_traits::depth];
+ details::shared_ptr<update_statement_type> update_;
+ details::shared_ptr<delete_statement_type> erase_;
+ };
+ }
+}
+
+#include <odb/oracle/polymorphic-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_POLYMORPHIC_OBJECT_STATEMENTS_HXX
diff --git a/libodb-oracle/odb/oracle/polymorphic-object-statements.txx b/libodb-oracle/odb/oracle/polymorphic-object-statements.txx
new file mode 100644
index 0000000..9d190e5
--- /dev/null
+++ b/libodb-oracle/odb/oracle/polymorphic-object-statements.txx
@@ -0,0 +1,137 @@
+// file : odb/oracle/polymorphic-object-statements.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/oracle/connection.hxx>
+#include <odb/oracle/transaction.hxx>
+#include <odb/oracle/statement-cache.hxx>
+#include <odb/oracle/traits-calls.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ //
+ // polymorphic_root_object_statements
+ //
+
+ template <typename T>
+ polymorphic_root_object_statements<T>::
+ ~polymorphic_root_object_statements ()
+ {
+ }
+
+ template <typename T>
+ polymorphic_root_object_statements<T>::
+ polymorphic_root_object_statements (connection_type& conn)
+ : object_statements<T> (conn),
+ discriminator_image_binding_ (discriminator_image_bind_,
+ discriminator_column_count +
+ managed_optimistic_column_count),
+ discriminator_id_image_binding_ (discriminator_id_image_bind_,
+ id_column_count)
+ {
+ discriminator_image_.version = 0;
+ discriminator_id_image_.version = 0;
+
+ discriminator_image_version_ = 0;
+ discriminator_id_image_version_ = 0;
+
+ std::memset (
+ discriminator_image_bind_, 0, sizeof (discriminator_image_bind_));
+ std::memset (
+ discriminator_id_image_bind_, 0, sizeof (discriminator_id_image_bind_));
+ }
+
+ //
+ // polymorphic_derived_object_statements
+ //
+
+ template <typename T>
+ polymorphic_derived_object_statements<T>::
+ ~polymorphic_derived_object_statements ()
+ {
+ }
+
+ template <typename T>
+ polymorphic_derived_object_statements<T>::
+ polymorphic_derived_object_statements (connection_type& conn)
+ : statements_base (conn),
+ root_statements_ (conn.statement_cache ().find_object<root_type> ()),
+ base_statements_ (conn.statement_cache ().find_object<base_type> ()),
+ insert_image_binding_ (insert_image_bind_, insert_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count)
+ {
+ image_.base = &base_statements_.image ();
+ image_.version = 0;
+
+ for (std::size_t i (0); i < object_traits::depth; ++i)
+ select_image_versions_[i] = 0;
+
+ for (std::size_t i (0);
+ i < (object_traits::abstract ? 1 : object_traits::depth);
+ ++i)
+ {
+ select_image_bindings_[i].bind = select_image_bind_;
+ select_image_bindings_[i].count = object_traits::find_column_counts[i];
+ select_image_bindings_[i].change_callback = 0;
+ }
+
+ // Statements other than the first one (which goes all the way to
+ // the root) can never override the image because they are used to
+ // load up the dynamic part of the object only after the static
+ // part has been loaded (and triggered the callback if necessary).
+ //
+ select_image_bindings_[0].change_callback =
+ root_statements_.image ().change_callback ();
+
+ insert_image_version_ = 0;
+ insert_id_binding_version_ = 0;
+ update_image_version_ = 0;
+ update_id_binding_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ }
+
+ template <typename T>
+ void polymorphic_derived_object_statements<T>::
+ delayed_loader (odb::database& db,
+ const id_type& id,
+ root_type& robj,
+ const schema_version_migration* svm)
+ {
+ connection_type& conn (transaction::current ().connection (db));
+ polymorphic_derived_object_statements& sts (
+ conn.statement_cache ().find_object<object_type> ());
+ root_statements_type& rsts (sts.root_statements ());
+
+ object_type& obj (static_cast<object_type&> (robj));
+
+ // The same code as in object_statements::load_delayed_().
+ //
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (sts, &id))
+ throw object_not_persistent ();
+
+ object_traits::callback (db, obj, callback_event::pre_load);
+ tc.init (obj, sts.image (), &db);
+ sts.find_[0]->stream_result ();
+ tc.load_ (sts, obj, false); // Load containers, etc.
+
+ rsts.load_delayed (svm);
+
+ {
+ typename root_statements_type::auto_unlock u (rsts);
+ object_traits::callback (db, obj, callback_event::post_load);
+ }
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/prepared-query.cxx b/libodb-oracle/odb/oracle/prepared-query.cxx
new file mode 100644
index 0000000..7fa11be
--- /dev/null
+++ b/libodb-oracle/odb/oracle/prepared-query.cxx
@@ -0,0 +1,15 @@
+// file : odb/oracle/prepared-query.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/oracle/prepared-query.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ prepared_query_impl::
+ ~prepared_query_impl ()
+ {
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/prepared-query.hxx b/libodb-oracle/odb/oracle/prepared-query.hxx
new file mode 100644
index 0000000..c449a3a
--- /dev/null
+++ b/libodb-oracle/odb/oracle/prepared-query.hxx
@@ -0,0 +1,34 @@
+// file : odb/oracle/prepared-query.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_PREPARED_QUERY_HXX
+#define ODB_ORACLE_PREPARED_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/prepared-query.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/query.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ struct LIBODB_ORACLE_EXPORT prepared_query_impl: odb::prepared_query_impl
+ {
+ virtual
+ ~prepared_query_impl ();
+
+ prepared_query_impl (odb::connection& c): odb::prepared_query_impl (c) {}
+
+ oracle::query_base query;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_PREPARED_QUERY_HXX
diff --git a/libodb-oracle/odb/oracle/query-const-expr.cxx b/libodb-oracle/odb/oracle/query-const-expr.cxx
new file mode 100644
index 0000000..5395f1f
--- /dev/null
+++ b/libodb-oracle/odb/oracle/query-const-expr.cxx
@@ -0,0 +1,14 @@
+// file : odb/oracle/query-const-expr.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/oracle/query.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ // Sun CC cannot handle this in query.cxx.
+ //
+ const query_base query_base::true_expr (true);
+ }
+}
diff --git a/libodb-oracle/odb/oracle/query-dynamic.cxx b/libodb-oracle/odb/oracle/query-dynamic.cxx
new file mode 100644
index 0000000..8ee4964
--- /dev/null
+++ b/libodb-oracle/odb/oracle/query-dynamic.cxx
@@ -0,0 +1,163 @@
+// file : odb/oracle/query-dynamic.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+
+#include <odb/oracle/query-dynamic.hxx>
+#include <odb/oracle/exceptions.hxx> // lob_comparison
+
+using namespace std;
+
+namespace odb
+{
+ namespace oracle
+ {
+ static const char* logic_operators[] = {") AND (", ") OR ("};
+ static const char* comp_operators[] = {"=", "!=", "<", ">", "<=", ">="};
+
+ static void
+ translate (query_base& q, const odb::query_base& s, size_t p)
+ {
+ typedef odb::query_base::clause_part part;
+
+ const part& x (s.clause ()[p]);
+
+ switch (x.kind)
+ {
+ case part::kind_column:
+ {
+ const query_column_base* c (
+ static_cast<const query_column_base*> (
+ x.native_info[id_oracle].column));
+
+ q.append (c->table (), c->column ());
+ break;
+ }
+ case part::kind_param_val:
+ case part::kind_param_ref:
+ {
+ const query_column_base& qc (
+ *static_cast<const query_column_base*> (
+ x.native_info[id_oracle].column));
+
+ query_param_factory f (
+ reinterpret_cast<query_param_factory> (
+ x.native_info[id_oracle].param_factory));
+
+ // If factory is NULL, then this is a LOB value.
+ //
+ if (f == 0)
+ throw lob_comparison ();
+
+ const odb::query_param* p (
+ reinterpret_cast<const odb::query_param*> (x.data));
+
+ q.append (f (p->value, qc, x.kind == part::kind_param_ref),
+ qc.conversion ());
+ break;
+ }
+ case part::kind_native:
+ {
+ q.append (s.strings ()[x.data]);
+ break;
+ }
+ case part::kind_true:
+ case part::kind_false:
+ {
+ q.append (x.kind == part::kind_true);
+ break;
+ }
+ case part::op_add:
+ {
+ translate (q, s, x.data);
+ translate (q, s, p - 1);
+ break;
+ }
+ case part::op_and:
+ case part::op_or:
+ {
+ q += "(";
+ translate (q, s, x.data);
+ q += logic_operators[x.kind - part::op_and];
+ translate (q, s, p - 1);
+ q += ")";
+ break;
+ }
+ case part::op_not:
+ {
+ q += "NOT (";
+ translate (q, s, p - 1);
+ q += ")";
+ break;
+ }
+ case part::op_null:
+ case part::op_not_null:
+ {
+ translate (q, s, p - 1);
+ q += (x.kind == part::op_null ? "IS NULL" : "IS NOT NULL");
+ break;
+ }
+ case part::op_in:
+ {
+ if (x.data != 0)
+ {
+ size_t b (p- x.data);
+
+ translate (q, s, b - 1); // column
+ q += "IN (";
+
+ for (size_t i (b); i != p; ++i)
+ {
+ if (i != b)
+ q += ",";
+
+ translate (q, s, i);
+ }
+
+ q += ")";
+ }
+ else
+ q.append (false);
+
+ break;
+ }
+ case part::op_like:
+ {
+ translate (q, s, p - 2); // column
+ q += "LIKE";
+ translate (q, s, p - 1); // pattern
+ break;
+ }
+ case part::op_like_escape:
+ {
+ translate (q, s, p - 3); // column
+ q += "LIKE";
+ translate (q, s, p - 2); // pattern
+ q += "ESCAPE";
+ translate (q, s, p - 1); // escape
+ break;
+ }
+ case part::op_eq:
+ case part::op_ne:
+ case part::op_lt:
+ case part::op_gt:
+ case part::op_le:
+ case part::op_ge:
+ {
+ translate (q, s, x.data);
+ q += comp_operators[x.kind - part::op_eq];
+ translate (q, s, p - 1);
+ break;
+ }
+ }
+ }
+
+ query_base::
+ query_base (const odb::query_base& q)
+ : binding_ (0, 0)
+ {
+ if (!q.empty ())
+ translate (*this, q, q.clause ().size () - 1);
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/query-dynamic.hxx b/libodb-oracle/odb/oracle/query-dynamic.hxx
new file mode 100644
index 0000000..eceac44
--- /dev/null
+++ b/libodb-oracle/odb/oracle/query-dynamic.hxx
@@ -0,0 +1,32 @@
+// file : odb/oracle/query-dynamic.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_QUERY_DYNAMIC_HXX
+#define ODB_ORACLE_QUERY_DYNAMIC_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/query.hxx>
+#include <odb/query-dynamic.hxx>
+
+#include <odb/oracle/query.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ typedef details::shared_ptr<query_param> (*query_param_factory) (
+ const void* val, const query_column_base&, bool by_ref);
+
+ template <typename T, database_type_id ID>
+ details::shared_ptr<query_param>
+ query_param_factory_impl (const void*, const query_column_base&, bool);
+ }
+}
+
+#include <odb/oracle/query-dynamic.ixx>
+#include <odb/oracle/query-dynamic.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_QUERY_DYNAMIC_HXX
diff --git a/libodb-oracle/odb/oracle/query-dynamic.ixx b/libodb-oracle/odb/oracle/query-dynamic.ixx
new file mode 100644
index 0000000..154f5cb
--- /dev/null
+++ b/libodb-oracle/odb/oracle/query-dynamic.ixx
@@ -0,0 +1,72 @@
+// file : odb/oracle/query-dynamic.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace oracle
+ {
+ //
+ //
+ template <typename T, database_type_id ID>
+ inline query_column<T, ID>::
+ query_column (odb::query_column<T>& qc,
+ const char* table,
+ const char* column,
+ const char* conv,
+ unsigned short prec,
+ short scale)
+ : query_column_base (table, column, conv, prec, scale)
+ {
+ native_column_info& ci (qc.native_info[id_oracle]);
+ ci.column = static_cast<query_column_base*> (this);
+
+ // For some reason GCC needs this statically-typed pointer in
+ // order to instantiate the functions.
+ //
+ query_param_factory f (&query_param_factory_impl<T, ID>);
+ ci.param_factory = reinterpret_cast<void*> (f);
+ }
+
+ template <typename T>
+ inline query_column<T, id_blob>::
+ query_column (odb::query_column<T>& qc,
+ const char* table, const char* column, const char*)
+ : lob_query_column (table, column)
+ {
+ native_column_info& ci (qc.native_info[id_oracle]);
+ ci.column = static_cast<query_column_base*> (this);
+
+ // In Oracle LOBs cannot be compared.
+ //
+ ci.param_factory = 0;
+ }
+
+ template <typename T>
+ inline query_column<T, id_clob>::
+ query_column (odb::query_column<T>& qc,
+ const char* table, const char* column, const char*)
+ : lob_query_column (table, column)
+ {
+ native_column_info& ci (qc.native_info[id_oracle]);
+ ci.column = static_cast<query_column_base*> (this);
+
+ // In Oracle LOBs cannot be compared.
+ //
+ ci.param_factory = 0;
+ }
+
+ template <typename T>
+ inline query_column<T, id_nclob>::
+ query_column (odb::query_column<T>& qc,
+ const char* table, const char* column, const char*)
+ : lob_query_column (table, column)
+ {
+ native_column_info& ci (qc.native_info[id_oracle]);
+ ci.column = static_cast<query_column_base*> (this);
+
+ // In Oracle LOBs cannot be compared.
+ //
+ ci.param_factory = 0;
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/query-dynamic.txx b/libodb-oracle/odb/oracle/query-dynamic.txx
new file mode 100644
index 0000000..a19b5c8
--- /dev/null
+++ b/libodb-oracle/odb/oracle/query-dynamic.txx
@@ -0,0 +1,25 @@
+// file : odb/oracle/query-dynamic.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <typename T, database_type_id ID>
+ details::shared_ptr<query_param>
+ query_param_factory_impl (const void* val,
+ const query_column_base& qc,
+ bool by_ref)
+ {
+ const T& v (*static_cast<const T*> (val));
+
+ unsigned short p (qc.prec ());
+ short s (qc.scale ());
+
+ return details::shared_ptr<query_param> (
+ by_ref
+ ? new (details::shared) query_param_impl<T, ID> (ref_bind<T> (v, p, s))
+ : new (details::shared) query_param_impl<T, ID> (val_bind<T> (v, p, s)));
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/query.cxx b/libodb-oracle/odb/oracle/query.cxx
new file mode 100644
index 0000000..890e1db
--- /dev/null
+++ b/libodb-oracle/odb/oracle/query.cxx
@@ -0,0 +1,356 @@
+// file : odb/oracle/query.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+#include <sstream> // std::ostringstream
+
+#include <odb/oracle/query.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace oracle
+ {
+ // query_param
+ //
+ query_param::
+ ~query_param ()
+ {
+ }
+
+ // query_base
+ //
+ query_base::
+ query_base (const query_base& q)
+ : clause_ (q.clause_),
+ parameters_ (q.parameters_),
+ bind_ (q.bind_),
+ binding_ (0, 0)
+ {
+ // Here and below we want to maintain up to date binding info so
+ // that the call to parameters_binding() below is an immutable
+ // operation, provided the query does not have any by-reference
+ // parameters. This way a by-value-only query can be shared
+ // between multiple threads without the need for synchronization.
+ //
+ if (size_t n = bind_.size ())
+ {
+ binding_.bind = &bind_[0];
+ binding_.count = n;
+ binding_.version++;
+ }
+ }
+
+ query_base& query_base::
+ operator= (const query_base& q)
+ {
+ if (this != &q)
+ {
+ clause_ = q.clause_;
+ parameters_ = q.parameters_;
+ bind_ = q.bind_;
+
+ size_t n (bind_.size ());
+ binding_.bind = n != 0 ? &bind_[0] : 0;
+ binding_.count = n;
+ binding_.version++;
+ }
+
+ return *this;
+ }
+
+ query_base& query_base::
+ operator+= (const query_base& q)
+ {
+ clause_.insert (clause_.end (), q.clause_.begin (), q.clause_.end ());
+
+ size_t n (bind_.size ());
+
+ parameters_.insert (
+ parameters_.end (), q.parameters_.begin (), q.parameters_.end ());
+
+ bind_.insert (
+ bind_.end (), q.bind_.begin (), q.bind_.end ());
+
+ if (n != bind_.size ())
+ {
+ binding_.bind = &bind_[0];
+ binding_.count = bind_.size ();
+ binding_.version++;
+ }
+
+ return *this;
+ }
+
+ void query_base::
+ append (const string& q)
+ {
+ if (!clause_.empty () &&
+ clause_.back ().kind == clause_part::kind_native)
+ {
+ string& s (clause_.back ().part);
+
+ char first (!q.empty () ? q[0] : ' ');
+ char last (!s.empty () ? s[s.size () - 1] : ' ');
+
+ // We don't want extra spaces after '(' as well as before ','
+ // and ')'.
+ //
+ if (last != ' ' && last != '\n' && last != '(' &&
+ first != ' ' && first != '\n' && first != ',' && first != ')')
+ s += ' ';
+
+ s += q;
+ }
+ else
+ clause_.push_back (clause_part (clause_part::kind_native, q));
+ }
+
+ void query_base::
+ append (const char* table, const char* column)
+ {
+ string s (table);
+ s += '.';
+ s += column;
+
+ clause_.push_back (clause_part (clause_part::kind_column, s));
+ }
+
+ void query_base::
+ append (details::shared_ptr<query_param> p, const char* conv)
+ {
+ clause_.push_back (clause_part (clause_part::kind_param));
+
+ if (conv != 0)
+ clause_.back ().part = conv;
+
+ parameters_.push_back (p);
+ bind_.push_back (bind ());
+ binding_.bind = &bind_[0];
+ binding_.count = bind_.size ();
+ binding_.version++;
+
+ bind* b (&bind_.back ());
+ memset (b, 0, sizeof (bind));
+ p->bind (b);
+ }
+
+ void query_base::
+ init_parameters () const
+ {
+ bool inc_ver (false);
+
+ for (size_t i (0); i < parameters_.size (); ++i)
+ {
+ query_param& p (*parameters_[i]);
+
+ if (p.reference ())
+ {
+ if (p.init ())
+ {
+ p.bind (&bind_[i]);
+ inc_ver = true;
+ }
+ }
+ }
+
+ if (inc_ver)
+ binding_.version++;
+ }
+
+ static bool
+ check_prefix (const string& s)
+ {
+ string::size_type n;
+
+ // It is easier to compare to upper and lower-case versions
+ // rather than getting involved with the portable case-
+ // insensitive string comparison mess.
+ //
+ if (s.compare (0, (n = 5), "WHERE") == 0 ||
+ s.compare (0, (n = 5), "where") == 0 ||
+ s.compare (0, (n = 6), "SELECT") == 0 ||
+ s.compare (0, (n = 6), "select") == 0 ||
+ s.compare (0, (n = 8), "ORDER BY") == 0 ||
+ s.compare (0, (n = 8), "order by") == 0 ||
+ s.compare (0, (n = 8), "GROUP BY") == 0 ||
+ s.compare (0, (n = 8), "group by") == 0 ||
+ s.compare (0, (n = 6), "HAVING") == 0 ||
+ s.compare (0, (n = 6), "having") == 0 ||
+ s.compare (0, (n = 4), "CALL") == 0 ||
+ s.compare (0, (n = 4), "call") == 0)
+ {
+ // It either has to be an exact match, or there should be
+ // a whitespace following the keyword.
+ //
+ if (s.size () == n || s[n] == ' ' || s[n] == '\n' || s[n] =='\t')
+ return true;
+ }
+
+ return false;
+ }
+
+ void query_base::
+ optimize ()
+ {
+ // Remove a single TRUE literal or one that is followe by one of
+ // the other clauses. This avoids useless WHERE clauses like
+ //
+ // WHERE TRUE GROUP BY foo
+ //
+ clause_type::iterator i (clause_.begin ()), e (clause_.end ());
+
+ if (i != e && i->kind == clause_part::kind_bool && i->bool_part)
+ {
+ clause_type::iterator j (i + 1);
+
+ if (j == e ||
+ (j->kind == clause_part::kind_native && check_prefix (j->part)))
+ clause_.erase (i);
+ }
+ }
+
+ const char* query_base::
+ clause_prefix () const
+ {
+ if (!clause_.empty ())
+ {
+ const clause_part& p (clause_.front ());
+
+ if (p.kind == clause_part::kind_native && check_prefix (p.part))
+ return "";
+
+ return "WHERE ";
+ }
+
+ return "";
+ }
+
+ string query_base::
+ clause () const
+ {
+ string r;
+ size_t param (1);
+
+ for (clause_type::const_iterator i (clause_.begin ()),
+ end (clause_.end ());
+ i != end;
+ ++i)
+ {
+ char last (!r.empty () ? r[r.size () - 1] : ' ');
+
+ switch (i->kind)
+ {
+ case clause_part::kind_column:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ r += i->part;
+ break;
+ }
+ case clause_part::kind_param:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ ostringstream os;
+ os << param++;
+
+ // Add the conversion expression, if any.
+ //
+ string::size_type p (0);
+ if (!i->part.empty ())
+ {
+ p = i->part.find ("(?)");
+ r.append (i->part, 0, p);
+ }
+
+ r += ':';
+ r += os.str ();
+
+ if (!i->part.empty ())
+ r.append (i->part, p + 3, string::npos);
+
+ break;
+ }
+ case clause_part::kind_native:
+ {
+ // We don't want extra spaces after '(' as well as before ','
+ // and ')'.
+ //
+ const string& p (i->part);
+ char first (!p.empty () ? p[0] : ' ');
+
+ if (last != ' ' && last != '\n' && last != '(' &&
+ first != ' ' && first != '\n' && first != ',' && first != ')')
+ r += ' ';
+
+ r += p;
+ break;
+ }
+ case clause_part::kind_bool:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ // Oracle does not have TRUE and FALSE boolean literals (these
+ // are available in PL/SQL). Boolean values seem to only be
+ // created as the result of boolean expressions.
+ //
+ r += i->bool_part ? "1 = 1" : "1 = 0";
+ break;
+ }
+ }
+ }
+
+ return clause_prefix () + r;
+ }
+
+ query_base
+ operator&& (const query_base& x, const query_base& y)
+ {
+ // Optimize cases where one or both sides are constant truth.
+ //
+ bool xt (x.const_true ()), yt (y.const_true ());
+
+ if (xt && yt)
+ return x;
+
+ if (xt)
+ return y;
+
+ if (yt)
+ return x;
+
+ query_base r ("(");
+ r += x;
+ r += ") AND (";
+ r += y;
+ r += ")";
+ return r;
+ }
+
+ query_base
+ operator|| (const query_base& x, const query_base& y)
+ {
+ query_base r ("(");
+ r += x;
+ r += ") OR (";
+ r += y;
+ r += ")";
+ return r;
+ }
+
+ query_base
+ operator! (const query_base& x)
+ {
+ query_base r ("NOT (");
+ r += x;
+ r += ")";
+ return r;
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/query.hxx b/libodb-oracle/odb/oracle/query.hxx
new file mode 100644
index 0000000..a1cbd80
--- /dev/null
+++ b/libodb-oracle/odb/oracle/query.hxx
@@ -0,0 +1,2261 @@
+// file : odb/oracle/query.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_QUERY_HXX
+#define ODB_ORACLE_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx> // odb::query_column
+#include <odb/query.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx>
+#include <odb/oracle/traits.hxx>
+#include <odb/oracle/binding.hxx>
+
+#include <odb/details/buffer.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/oracle/details/export.hxx>
+#include <odb/oracle/details/conversion.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ // For both precision and scale 0 is a valid value. Furthermore,
+ // scale can be negative. To indicate that these values are not
+ // specified, we will use 0xFFF which is out of range for both
+ // precision (0 to 4000) and scale (-84 to 127). Note also that
+ // string size (stored in precision) is always in bytes. If a
+ // national string size is specified as a number of characters
+ // and not bytes, then this will be a conservative estimate (4
+ // bytes per character).
+ //
+ template <typename T>
+ struct val_bind
+ {
+ typedef const T& type;
+
+ explicit
+ val_bind (type v, unsigned short p = 0xFFF, short s = 0xFFF)
+ : val (v), prec (p), scale (s) {}
+
+ type val;
+
+ unsigned short prec;
+ short scale;
+ };
+
+ template <typename T, std::size_t N>
+ struct val_bind<T[N]>
+ {
+ typedef const T* type;
+
+ explicit
+ val_bind (type v, unsigned short p = 0xFFF, short s = 0xFFF)
+ : val (v), prec (p), scale (s) {}
+
+ type val;
+
+ unsigned short prec;
+ short scale;
+ };
+
+ template <typename T>
+ struct ref_bind
+ {
+ typedef const T& type;
+
+ explicit
+ ref_bind (type r, unsigned short p = 0xFFF, short s = 0xFFF)
+ : ref (r), prec (p), scale (s) {}
+
+ const void*
+ ptr () const {return &ref;}
+
+ type ref;
+
+ unsigned short prec;
+ short scale;
+ };
+
+ template <typename T, std::size_t N>
+ struct ref_bind<T[N]>
+ {
+ typedef const T* type;
+
+ explicit
+ ref_bind (type r, unsigned short p = 0xFFF, short s = 0xFFF)
+ : ref (r), prec (p), scale (s) {}
+
+ // Allow implicit conversion from decayed ref_bind's.
+ //
+ ref_bind (ref_bind<T*> r): ref (r.ref), prec (r.prec), scale (r.scale) {}
+ ref_bind (ref_bind<const T*> r)
+ : ref (r.ref), prec (r.prec), scale (r.scale) {}
+
+ const void*
+ ptr () const {return ref;}
+
+ type ref;
+
+ unsigned short prec;
+ short scale;
+ };
+
+ template <typename T, database_type_id ID>
+ struct val_bind_typed: val_bind<T>
+ {
+ explicit
+ val_bind_typed (typename val_bind<T>::type v,
+ unsigned short p = 0xFFF,
+ short s = 0xFFF): val_bind<T> (v, p, s) {}
+ };
+
+ template <typename T, database_type_id ID>
+ struct ref_bind_typed: ref_bind<T>
+ {
+ explicit
+ ref_bind_typed (typename ref_bind<T>::type r,
+ unsigned short p = 0xFFF,
+ short s = 0xFFF): ref_bind<T> (r, p, s) {}
+ };
+
+ struct LIBODB_ORACLE_EXPORT query_param: details::shared_base
+ {
+ typedef oracle::bind bind_type;
+
+ virtual
+ ~query_param ();
+
+ bool
+ reference () const
+ {
+ return value_ != 0;
+ }
+
+ virtual bool
+ init () = 0;
+
+ virtual void
+ bind (bind_type*) = 0;
+
+ protected:
+ query_param (const void* value)
+ : value_ (value)
+ {
+ }
+
+ protected:
+ const void* value_;
+ };
+
+ //
+ //
+ template <typename T, database_type_id ID>
+ struct query_column;
+
+ class LIBODB_ORACLE_EXPORT query_base
+ {
+ public:
+ struct clause_part
+ {
+ enum kind_type
+ {
+ kind_column,
+ kind_param,
+ kind_native,
+ kind_bool
+ };
+
+ clause_part (kind_type k): kind (k), bool_part (false) {}
+ clause_part (kind_type k, const std::string& p)
+ : kind (k), part (p), bool_part (false) {}
+ clause_part (bool p): kind (kind_bool), bool_part (p) {}
+
+ kind_type kind;
+ std::string part; // If kind is param, then part is conversion expr.
+ bool bool_part;
+ };
+
+ query_base ()
+ : binding_ (0, 0)
+ {
+ }
+
+ // True or false literal.
+ //
+ explicit
+ query_base (bool v)
+ : binding_ (0, 0)
+ {
+ append (v);
+ }
+
+ explicit
+ query_base (const char* native)
+ : binding_ (0, 0)
+ {
+ clause_.push_back (clause_part (clause_part::kind_native, native));
+ }
+
+ explicit
+ query_base (const std::string& native)
+ : binding_ (0, 0)
+ {
+ clause_.push_back (clause_part (clause_part::kind_native, native));
+ }
+
+ query_base (const char* table, const char* column)
+ : binding_ (0, 0)
+ {
+ append (table, column);
+ }
+
+ template <typename T>
+ explicit
+ query_base (val_bind<T> v)
+ : binding_ (0, 0)
+ {
+ *this += v;
+ }
+
+ template <typename T, database_type_id ID>
+ explicit
+ query_base (val_bind_typed<T, ID> v)
+ : binding_ (0, 0)
+ {
+ *this += v;
+ }
+
+ template <typename T>
+ explicit
+ query_base (ref_bind<T> r)
+ : binding_ (0, 0)
+ {
+ *this += r;
+ }
+
+ template <typename T, database_type_id ID>
+ explicit
+ query_base (ref_bind_typed<T, ID> r)
+ : binding_ (0, 0)
+ {
+ *this += r;
+ }
+
+ template <database_type_id ID>
+ query_base (const query_column<bool, ID>&);
+
+ // Translate common query representation to Oracle native. Defined
+ // in query-dynamic.cxx
+ //
+ query_base (const odb::query_base&);
+
+ // Copy c-tor and assignment.
+ //
+ query_base (const query_base&);
+
+ query_base&
+ operator= (const query_base&);
+
+ public:
+ std::string
+ clause () const;
+
+ const char*
+ clause_prefix () const;
+
+ // Initialize the by-reference parameters from bound variables.
+ //
+ void
+ init_parameters () const;
+
+ binding&
+ parameters_binding () const;
+
+ public:
+ bool
+ empty () const
+ {
+ return clause_.empty ();
+ }
+
+ static const query_base true_expr;
+
+ bool
+ const_true () const
+ {
+ return clause_.size () == 1 &&
+ clause_.front ().kind == clause_part::kind_bool &&
+ clause_.front ().bool_part;
+ }
+
+ void
+ optimize ();
+
+ public:
+ template <typename T>
+ static val_bind<T>
+ _val (const T& x, unsigned short prec = 0xFFF, short scale = 0xFFF)
+ {
+ return val_bind<T> (x, prec, scale);
+ }
+
+ template <database_type_id ID, typename T>
+ static val_bind_typed<T, ID>
+ _val (const T& x, unsigned short prec = 0xFFF, short scale = 0xFFF)
+ {
+ return val_bind_typed<T, ID> (x, prec, scale);
+ }
+
+ template <typename T>
+ static ref_bind<T>
+ _ref (const T& x, unsigned short prec = 0xFFF, short scale = 0xFFF)
+ {
+ return ref_bind<T> (x, prec, scale);
+ }
+
+ template <database_type_id ID, typename T>
+ static ref_bind_typed<T, ID>
+ _ref (const T& x, unsigned short prec = 0xFFF, short scale = 0xFFF)
+ {
+ return ref_bind_typed<T, ID> (x, prec, scale);
+ }
+
+ // Some compilers (notably VC++), when deducing const T& from const
+ // array do not strip const from the array type. As a result, in the
+ // above signatures we get, for example, T = const char[4] instead
+ // of T = char[4], which is what we want. So to "fix" such compilers,
+ // we will have to provide the following specializations of the above
+ // functions.
+ //
+ template <typename T, std::size_t N>
+ static val_bind<T[N]>
+ _val (const T (&x) [N], unsigned short prec = 0xFFF, short scale = 0xFFF)
+ {
+ return val_bind<T[N]> (x, prec, scale);
+ }
+
+ template <database_type_id ID, typename T, std::size_t N>
+ static val_bind_typed<T[N], ID>
+ _val (const T (&x) [N], unsigned short prec = 0xFFF, short scale = 0xFFF)
+ {
+ return val_bind_typed<T[N], ID> (x, prec, scale);
+ }
+
+ template <typename T, std::size_t N>
+ static ref_bind<T[N]>
+ _ref (const T (&x) [N], unsigned short prec = 0xFFF, short scale = 0xFFF)
+ {
+ return ref_bind<T[N]> (x, prec, scale);
+ }
+
+ template <database_type_id ID, typename T, std::size_t N>
+ static ref_bind_typed<T[N], ID>
+ _ref (const T (&x) [N], unsigned short prec = 0xFFF, short scale = 0xFFF)
+ {
+ return ref_bind_typed<T[N], ID> (x, prec, scale);
+ }
+
+ public:
+ query_base&
+ operator+= (const query_base&);
+
+ query_base&
+ operator+= (const std::string& q)
+ {
+ append (q);
+ return *this;
+ }
+
+ template <typename T>
+ query_base&
+ operator+= (val_bind<T> v)
+ {
+ append<T, type_traits<T>::db_type_id> (
+ v, details::conversion<T>::to ());
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (val_bind_typed<T, ID> v)
+ {
+ // We are not using default type_traits so no default conversion
+ // either.
+ //
+ append<T, ID> (v, 0);
+ return *this;
+ }
+
+ template <typename T>
+ query_base&
+ operator+= (ref_bind<T> r)
+ {
+ append<T, type_traits<T>::db_type_id> (
+ r, details::conversion<T>::to ());
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (ref_bind_typed<T, ID> r)
+ {
+ // We are not using default type_traits so no default conversion
+ // either.
+ //
+ append<T, ID> (r, 0);
+ return *this;
+ }
+
+ // Implementation details.
+ //
+ public:
+ template <typename T, database_type_id ID>
+ void
+ append (val_bind<T>, const char* conv);
+
+ template <typename T, database_type_id ID>
+ void
+ append (ref_bind<T>, const char* conv);
+
+ void
+ append (details::shared_ptr<query_param>, const char* conv);
+
+ void
+ append (bool v)
+ {
+ clause_.push_back (clause_part (v));
+ }
+
+ void
+ append (const std::string& native);
+
+ void
+ append (const char* native) // Clashes with append(bool).
+ {
+ append (std::string (native));
+ }
+
+ void
+ append (const char* table, const char* column);
+
+ private:
+ typedef std::vector<clause_part> clause_type;
+ typedef std::vector<details::shared_ptr<query_param> > parameters_type;
+
+ clause_type clause_;
+ parameters_type parameters_;
+ mutable std::vector<bind> bind_;
+ mutable binding binding_;
+ };
+
+ inline query_base
+ operator+ (const query_base& x, const query_base& y)
+ {
+ query_base r (x);
+ r += y;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, val_bind<T> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (val_bind<T> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, val_bind_typed<T, ID> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (val_bind_typed<T, ID> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, ref_bind<T> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (ref_bind<T> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, ref_bind_typed<T, ID> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (ref_bind_typed<T, ID> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const query_base& q, const std::string& s)
+ {
+ query_base r (q);
+ r += s;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const std::string& s, const query_base& q)
+ {
+ query_base r (s);
+ r += q;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, val_bind<T> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (val_bind<T> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, val_bind_typed<T, ID> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (val_bind_typed<T, ID> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, ref_bind<T> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (ref_bind<T> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, ref_bind_typed<T, ID> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (ref_bind_typed<T, ID> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ LIBODB_ORACLE_EXPORT query_base
+ operator&& (const query_base& x, const query_base& y);
+
+ LIBODB_ORACLE_EXPORT query_base
+ operator|| (const query_base& x, const query_base& y);
+
+ LIBODB_ORACLE_EXPORT query_base
+ operator! (const query_base& x);
+
+ // query_column
+ //
+ struct LIBODB_ORACLE_EXPORT query_column_base
+ {
+ // Note that we keep shallow copies of the table, column, and conversion
+ // expression. The latter can be NULL.
+ //
+ query_column_base (const char* table,
+ const char* column,
+ const char* conv,
+ unsigned short prec,
+ short scale)
+ : table_ (table), column_ (column), conversion_ (conv),
+ prec_ (prec), scale_ (scale)
+ {
+ }
+
+ const char*
+ table () const
+ {
+ return table_;
+ }
+
+ const char*
+ column () const
+ {
+ return column_;
+ }
+
+ // Can be NULL.
+ //
+ const char*
+ conversion () const
+ {
+ return conversion_;
+ }
+
+ unsigned short
+ prec () const
+ {
+ return prec_;
+ }
+
+ short
+ scale () const
+ {
+ return scale_;
+ }
+
+ protected:
+ const char* table_;
+ const char* column_;
+ const char* conversion_;
+
+ unsigned short prec_;
+ short scale_;
+ };
+
+ template <typename T, database_type_id ID>
+ struct query_column: query_column_base
+ {
+ typedef typename decay_traits<T>::type decayed_type;
+
+ // Note that we keep shalow copies of the table, column, and conversion
+ // expression. The latter can be NULL.
+ //
+ query_column (const char* table,
+ const char* column,
+ const char* conv,
+ unsigned short prec = 0xFFF,
+ short scale = 0xFFF)
+ : query_column_base (table, column, conv, prec, scale) {}
+
+ // Implementation is in query-dynamic.ixx.
+ //
+ query_column (odb::query_column<T>&,
+ const char* table,
+ const char* column,
+ const char* conv,
+ unsigned short prec = 0xFFF,
+ short scale = 0xFFF);
+
+ // is_null, is_not_null
+ //
+ public:
+ query_base
+ is_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NULL";
+ return q;
+ }
+
+ query_base
+ is_not_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NOT NULL";
+ return q;
+ }
+
+ // in
+ //
+ public:
+ query_base
+ in (decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type, decayed_type,
+ decayed_type) const;
+
+ template <typename I>
+ query_base
+ in_range (I begin, I end) const;
+
+ // like
+ //
+ public:
+ query_base
+ like (decayed_type pattern) const
+ {
+ return like (val_bind<T> (pattern));
+ }
+
+ query_base
+ like (val_bind<T> pattern) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern) const
+ {
+ return like (val_bind<T> (decayed_type (pattern.val)));
+ }
+
+ query_base
+ like (ref_bind<T> pattern) const;
+
+ query_base
+ like (decayed_type pattern, decayed_type escape) const
+ {
+ return like (val_bind<T> (pattern), escape);
+ }
+
+ query_base
+ like (val_bind<T> pattern, decayed_type escape) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern, decayed_type escape) const
+ {
+ return like (val_bind<T> (decayed_type (pattern.val)), escape);
+ }
+
+ query_base
+ like (ref_bind<T> pattern, decayed_type escape) const;
+
+ // =
+ //
+ public:
+ query_base
+ equal (decayed_type v) const
+ {
+ return equal (val_bind<T> (v));
+ }
+
+ query_base
+ equal (val_bind<T> v) const
+ {
+ v.prec = prec_;
+ v.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ equal (val_bind<T2> v) const
+ {
+ return equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ equal (ref_bind<T> r) const
+ {
+ r.prec = prec_;
+ r.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator== (const query_column& c, decayed_type v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (decayed_type v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, val_bind<T> v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (val_bind<T> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (const query_column& c, val_bind<T2> v)
+ {
+ return c.equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (val_bind<T2> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, ref_bind<T> r)
+ {
+ return c.equal (r);
+ }
+
+ friend query_base
+ operator== (ref_bind<T> r, const query_column& c)
+ {
+ return c.equal (r);
+ }
+
+ // !=
+ //
+ public:
+ query_base
+ unequal (decayed_type v) const
+ {
+ return unequal (val_bind<T> (v));
+ }
+
+ query_base
+ unequal (val_bind<T> v) const
+ {
+ v.prec = prec_;
+ v.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "!=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ unequal (val_bind<T2> v) const
+ {
+ return unequal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ unequal (ref_bind<T> r) const
+ {
+ r.prec = prec_;
+ r.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "!=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator!= (const query_column& c, decayed_type v)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (decayed_type v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (const query_column& c, val_bind<T> v)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (val_bind<T> v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (const query_column& c, val_bind<T2> v)
+ {
+ return c.unequal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (val_bind<T2> v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (const query_column& c, ref_bind<T> r)
+ {
+ return c.unequal (r);
+ }
+
+ friend query_base
+ operator!= (ref_bind<T> r, const query_column& c)
+ {
+ return c.unequal (r);
+ }
+
+ // <
+ //
+ public:
+ query_base
+ less (decayed_type v) const
+ {
+ return less (val_bind<T> (v));
+ }
+
+ query_base
+ less (val_bind<T> v) const
+ {
+ v.prec = prec_;
+ v.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "<";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ less (val_bind<T2> v) const
+ {
+ return less (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ less (ref_bind<T> r) const
+ {
+ r.prec = prec_;
+ r.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "<";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator< (const query_column& c, decayed_type v)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator< (decayed_type v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator< (const query_column& c, val_bind<T> v)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator< (val_bind<T> v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (const query_column& c, val_bind<T2> v)
+ {
+ return c.less (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator< (const query_column& c, ref_bind<T> r)
+ {
+ return c.less (r);
+ }
+
+ friend query_base
+ operator< (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater (r);
+ }
+
+ // >
+ //
+ public:
+ query_base
+ greater (decayed_type v) const
+ {
+ return greater (val_bind<T> (v));
+ }
+
+ query_base
+ greater (val_bind<T> v) const
+ {
+ v.prec = prec_;
+ v.scale = scale_;
+
+ query_base q (table_, column_);
+ q += ">";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ greater (val_bind<T2> v) const
+ {
+ return greater (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ greater (ref_bind<T> r) const
+ {
+ r.prec = prec_;
+ r.scale = scale_;
+
+ query_base q (table_, column_);
+ q += ">";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator> (const query_column& c, decayed_type v)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator> (decayed_type v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator> (const query_column& c, val_bind<T> v)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator> (val_bind<T> v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (val_bind<T2> v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator> (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater (r);
+ }
+
+ friend query_base
+ operator> (ref_bind<T> r, const query_column& c)
+ {
+ return c.less (r);
+ }
+
+ // <=
+ //
+ public:
+ query_base
+ less_equal (decayed_type v) const
+ {
+ return less_equal (val_bind<T> (v));
+ }
+
+ query_base
+ less_equal (val_bind<T> v) const
+ {
+ v.prec = prec_;
+ v.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "<=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ less_equal (val_bind<T2> v) const
+ {
+ return less_equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ less_equal (ref_bind<T> r) const
+ {
+ r.prec = prec_;
+ r.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "<=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator<= (const query_column& c, decayed_type v)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator<= (decayed_type v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator<= (const query_column& c, val_bind<T> v)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator<= (val_bind<T> v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (const query_column& c, val_bind<T2> v)
+ {
+ return c.less_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator<= (const query_column& c, ref_bind<T> r)
+ {
+ return c.less_equal (r);
+ }
+
+ friend query_base
+ operator<= (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater_equal (r);
+ }
+
+ // >=
+ //
+ public:
+ query_base
+ greater_equal (decayed_type v) const
+ {
+ return greater_equal (val_bind<T> (v));
+ }
+
+ query_base
+ greater_equal (val_bind<T> v) const
+ {
+ v.prec = prec_;
+ v.scale = scale_;
+
+ query_base q (table_, column_);
+ q += ">=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ greater_equal (val_bind<T2> v) const
+ {
+ return greater_equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ greater_equal (ref_bind<T> r) const
+ {
+ r.prec = prec_;
+ r.scale = scale_;
+
+ query_base q (table_, column_);
+ q += ">=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator>= (const query_column& c, decayed_type v)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator>= (decayed_type v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator>= (const query_column& c, val_bind<T> v)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator>= (val_bind<T> v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (val_bind<T2> v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator>= (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater_equal (r);
+ }
+
+ friend query_base
+ operator>= (ref_bind<T> r, const query_column& c)
+ {
+ return c.less_equal (r);
+ }
+
+ // Column comparison.
+ //
+ public:
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator== (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () ==
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator!= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () !=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "!=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator< (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "<";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator> (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += ">";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator<= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "<=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator>= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += ">=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+ };
+
+ //
+ // Oracle does not support comparison operations between LOB columns.
+ // query_column therefore only supports the IS NULL and IS NOT NULL
+ // predicates for these types.
+ //
+
+ struct LIBODB_ORACLE_EXPORT lob_query_column: query_column_base
+ {
+ // Note that we keep shallow copies of the table and column names.
+ // There is also no need for conversion expression since the only
+ // valid tests are is IS NULL/IS NOT NULL.
+ //
+ lob_query_column (const char* table, const char* column)
+ : query_column_base (table, column, 0, 0xFFF, 0xFFF)
+ {
+ }
+
+ // is_null, is_not_null
+ //
+ public:
+ query_base
+ is_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NULL";
+ return q;
+ }
+
+ query_base
+ is_not_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NOT NULL";
+ return q;
+ }
+ };
+
+ template <typename T>
+ struct query_column<T, id_blob>: lob_query_column
+ {
+ query_column (const char* table, const char* column, const char*)
+ : lob_query_column (table, column)
+ {
+ }
+
+ // Implementation is in query-dynamic.ixx.
+ //
+ query_column (odb::query_column<T>&,
+ const char* table, const char* column, const char*);
+ };
+
+ template <typename T>
+ struct query_column<T, id_clob>: lob_query_column
+ {
+ query_column (const char* table, const char* column, const char*)
+ : lob_query_column (table, column)
+ {
+ }
+
+ // Implementation is in query-dynamic.ixx.
+ //
+ query_column (odb::query_column<T>&,
+ const char* table, const char* column, const char*);
+ };
+
+ template <typename T>
+ struct query_column<T, id_nclob>: lob_query_column
+ {
+ query_column (const char* table, const char* column, const char*)
+ : lob_query_column (table, column)
+ {
+ }
+
+ // Implementation is in query-dynamic.ixx.
+ //
+ query_column (odb::query_column<T>&,
+ const char* table, const char* column, const char*);
+ };
+
+ // Provide operator+() for using columns to construct native
+ // query fragments (e.g., ORDER BY).
+ //
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_column<T, ID>& c, const std::string& s)
+ {
+ query_base q (c.table (), c.column ());
+ q += s;
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, const query_column<T, ID>& c)
+ {
+ query_base q (s);
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_column<T, ID>& c, const query_base& q)
+ {
+ query_base r (c.table (), c.column ());
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, const query_column<T, ID>& c)
+ {
+ query_base r (q);
+ r.append (c.table (), c.column ());
+ return r;
+ }
+
+ //
+ //
+ template <typename T, database_type_id>
+ struct query_param_impl;
+
+ // id_int32.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_int32>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = image_traits<T, id_int32>::buffer_type;
+ b->buffer = &image_;
+ b->capacity = sizeof (image_);
+ b->size = 0;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_int32>::set_image (image_, is_null, v);
+ }
+
+ private:
+ typename image_traits<T, id_int32>::image_type image_;
+ };
+
+ // id_int64.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_int64>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = image_traits<T, id_int64>::buffer_type;
+ b->buffer = &image_;
+ b->capacity = sizeof (image_);
+ b->size = 0;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_int64>::set_image (image_, is_null, v);
+ }
+
+ private:
+ typename image_traits<T, id_int64>::image_type image_;
+ };
+
+ // id_big_int.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_big_int>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind_type::number;
+ b->buffer = &image_;
+ b->capacity = sizeof (image_);
+ b->size = &size_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0);
+ value_traits<T, id_big_int>::set_image (image_, size, is_null, v);
+ size_ = static_cast<ub2> (size);
+ }
+
+ private:
+ char image_[21];
+ ub2 size_;
+ };
+
+ // id_float.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_float>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind_type::binary_float;
+ b->buffer = &image_;
+ b->capacity = sizeof (image_);
+ b->size = 0;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_float>::set_image (image_, is_null, v);
+ }
+
+ private:
+ float image_;
+ };
+
+ // id_double.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_double>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind_type::binary_double;
+ b->buffer = &image_;
+ b->capacity = sizeof (image_);
+ b->size = 0;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_double>::set_image (image_, is_null, v);
+ }
+
+ private:
+ double image_;
+ };
+
+ // id_big_float.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_big_float>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind_type::number;
+ b->buffer = &image_;
+ b->capacity = sizeof (image_);
+ b->size = &size_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0);
+ value_traits<T, id_big_float>::set_image (image_, size, is_null, v);
+ size_ = static_cast<ub2> (size);
+ }
+
+ private:
+ char image_[21];
+ ub2 size_;
+ };
+
+ // id_date.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_date>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind_type::date;
+ b->buffer = &image_;
+ b->capacity = sizeof (image_);
+ b->size = 0;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_date>::set_image (image_, is_null, v);
+ }
+
+ private:
+ char image_[7];
+ };
+
+ // id_timestamp
+ //
+ template <typename T>
+ struct query_param_impl<T, id_timestamp>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ image_ (descriptor_cache) // Cache, don't free.
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ image_ (0) // Don't cache, don't free.
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind_type::timestamp;
+ b->buffer = &image_;
+ b->capacity = sizeof (OCIDateTime*);
+ b->size = 0;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_timestamp>::set_image (image_, is_null, v);
+ }
+
+ private:
+ datetime image_;
+ };
+
+ // id_interval_ym
+ //
+ template <typename T>
+ struct query_param_impl<T, id_interval_ym>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ image_ (descriptor_cache) // Cache, don't free.
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ image_ (0) // Don't cache, don't free.
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind_type::interval_ym;
+ b->buffer = &image_;
+ b->capacity = sizeof (OCIInterval*);
+ b->size = 0;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_interval_ym>::set_image (image_, is_null, v);
+ }
+
+ private:
+ interval_ym image_;
+ };
+
+ // id_interval_ds
+ //
+ template <typename T>
+ struct query_param_impl<T, id_interval_ds>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ image_ (descriptor_cache) // Cache, don't free.
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ image_ (0) // Don't cache, don't free.
+
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind_type::interval_ds;
+ b->buffer = &image_;
+ b->capacity = sizeof (OCIInterval*);
+ b->size = 0;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_interval_ds>::set_image (image_, is_null, v);
+ }
+
+ private:
+ interval_ds image_;
+ };
+
+ // id_string.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_string>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ // Default to max (4000).
+ buf_ (r.prec != 0xFFF ? r.prec : 4000)
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ // Default to max (4000).
+ buf_ (v.prec != 0xFFF ? v.prec : 4000)
+
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind_type::string;
+ b->buffer = buf_.data ();
+ b->capacity = static_cast<ub4> (buf_.capacity ());
+ b->size = &size_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0);
+ value_traits<T, id_string>::set_image (
+ buf_.data (), buf_.capacity (), size, is_null, v);
+ size_ = static_cast<ub2> (size);
+ }
+
+ private:
+ details::buffer buf_;
+ ub2 size_;
+ };
+
+ // id_nstring
+ //
+ template <typename T>
+ struct query_param_impl<T, id_nstring>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ // Default to max (4000).
+ buf_ (r.prec != 0xFFF ? r.prec : 4000)
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ // Default to max (4000).
+ buf_ (v.prec != 0xFFF ? v.prec : 4000)
+
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind_type::nstring;
+ b->buffer = buf_.data ();
+ b->capacity = static_cast<ub4> (buf_.capacity ());
+ b->size = &size_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0);
+ value_traits<T, id_nstring>::set_image (
+ buf_.data (), buf_.capacity (), size, is_null, v);
+ size_ = static_cast<ub2> (size);
+ }
+
+ private:
+ details::buffer buf_;
+ ub2 size_;
+ };
+
+ // id_raw
+ //
+ template <typename T>
+ struct query_param_impl<T, id_raw>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ // Default to max (2000).
+ buf_ (r.prec != 0xFFF ? r.prec : 2000)
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ // Default to max (2000).
+ buf_ (v.prec != 0xFFF ? v.prec : 2000)
+
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind_type::raw;
+ b->buffer = buf_.data ();
+ b->capacity = static_cast<ub4> (buf_.capacity ());
+ b->size = &size_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0);
+ value_traits<T, id_raw>::set_image (
+ buf_.data (), buf_.capacity (), size, is_null, v);
+ size_ = static_cast<ub2> (size);
+ }
+
+ private:
+ details::buffer buf_;
+ ub2 size_;
+ };
+ }
+}
+
+// odb::oracle::query and odb::query specialization for Oracle.
+//
+namespace odb
+{
+ namespace oracle
+ {
+ template <typename T>
+ class query: public query_base,
+ public query_selector<T, id_oracle>::columns_type
+ {
+ public:
+ // We don't define any typedefs here since they may clash with
+ // column names defined by our base type.
+ //
+
+ query ()
+ {
+ }
+
+ explicit
+ query (bool v)
+ : query_base (v)
+ {
+ }
+
+ explicit
+ query (const char* q)
+ : query_base (q)
+ {
+ }
+
+ explicit
+ query (const std::string& q)
+ : query_base (q)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (val_bind<T2> v)
+ : query_base (v)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (ref_bind<T2> r)
+ : query_base (r)
+ {
+ }
+
+ query (const query_base& q)
+ : query_base (q)
+ {
+ }
+
+ template <database_type_id ID>
+ query (const query_column<bool, ID>& qc)
+ : query_base (qc)
+ {
+ }
+
+ query (const odb::query_base& q)
+ : query_base (q)
+ {
+ }
+ };
+
+ namespace core
+ {
+ using oracle::query;
+ }
+ }
+
+ // Derive odb::query from odb::oracle::query so that it can be
+ // implicitly converted in oracle::database::query() calls.
+ //
+ template <typename T>
+ class query<T, oracle::query_base>: public oracle::query<T>
+ {
+ public:
+ // We don't define any typedefs here since they may clash with
+ // column names defined by our base type.
+ //
+
+ query ()
+ {
+ }
+
+ explicit
+ query (bool v)
+ : oracle::query<T> (v)
+ {
+ }
+
+ explicit
+ query (const char* q)
+ : oracle::query<T> (q)
+ {
+ }
+
+ explicit
+ query (const std::string& q)
+ : oracle::query<T> (q)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (oracle::val_bind<T2> v)
+ : oracle::query<T> (v)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (oracle::ref_bind<T2> r)
+ : oracle::query<T> (r)
+ {
+ }
+
+ query (const oracle::query_base& q)
+ : oracle::query<T> (q)
+ {
+ }
+
+ template <oracle::database_type_id ID>
+ query (const oracle::query_column<bool, ID>& qc)
+ : oracle::query<T> (qc)
+ {
+ }
+ };
+}
+
+#include <odb/oracle/query.ixx>
+#include <odb/oracle/query.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_QUERY_HXX
diff --git a/libodb-oracle/odb/oracle/query.ixx b/libodb-oracle/odb/oracle/query.ixx
new file mode 100644
index 0000000..48caec3
--- /dev/null
+++ b/libodb-oracle/odb/oracle/query.ixx
@@ -0,0 +1,34 @@
+// file : odb/oracle/query.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace oracle
+ {
+ inline binding& query_base::
+ parameters_binding () const
+ {
+ return binding_;
+ }
+
+ template <typename T, database_type_id ID>
+ inline void query_base::
+ append (val_bind<T> v, const char* conv)
+ {
+ append (
+ details::shared_ptr<query_param> (
+ new (details::shared) query_param_impl<T, ID> (v)),
+ conv);
+ }
+
+ template <typename T, database_type_id ID>
+ inline void query_base::
+ append (ref_bind<T> r, const char* conv)
+ {
+ append (
+ details::shared_ptr<query_param> (
+ new (details::shared) query_param_impl<T, ID> (r)),
+ conv);
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/query.txx b/libodb-oracle/odb/oracle/query.txx
new file mode 100644
index 0000000..65f2858
--- /dev/null
+++ b/libodb-oracle/odb/oracle/query.txx
@@ -0,0 +1,169 @@
+// file : odb/oracle/query.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace oracle
+ {
+ //
+ // query_base
+ //
+
+ template <database_type_id ID>
+ query_base::
+ query_base (const query_column<bool, ID>& c)
+ : binding_ (0, 0)
+ {
+ // Cannot use IS TRUE here since database type can be a non-
+ // integral type.
+ //
+ append (c.table (), c.column ());
+ append ("=");
+ append<bool, ID> (val_bind<bool> (true, c.prec (), c.scale ()),
+ c.conversion ());
+ }
+
+ //
+ // query_column
+ //
+
+ // in
+ //
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2, prec_, scale_), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3, prec_, scale_), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3,
+ decayed_type v4) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v4, prec_, scale_), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3, decayed_type v4,
+ decayed_type v5) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v4, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v5, prec_, scale_), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ template <typename I>
+ query_base query_column<T, ID>::
+ in_range (I begin, I end) const
+ {
+ if (begin != end)
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+
+ for (I i (begin); i != end; ++i)
+ {
+ if (i != begin)
+ q += ",";
+
+ q.append<T, ID> (val_bind<T> (*i, prec_, scale_), conversion_);
+ }
+
+ q += ")";
+ return q;
+ }
+ else
+ return query_base (false);
+ }
+
+ // like
+ //
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (val_bind<T> p) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (ref_bind<T> p) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (val_bind<T> p, decayed_type e) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ q += "ESCAPE";
+ q.append<T, ID> (val_bind<T> (e), conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (ref_bind<T> p, decayed_type e) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ q += "ESCAPE";
+ q.append<T, ID> (val_bind<T> (e), conversion_);
+ return q;
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/section-statements.hxx b/libodb-oracle/odb/oracle/section-statements.hxx
new file mode 100644
index 0000000..1819fb1
--- /dev/null
+++ b/libodb-oracle/odb/oracle/section-statements.hxx
@@ -0,0 +1,195 @@
+// file : odb/oracle/section-statements.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_SECTION_STATEMENTS_HXX
+#define ODB_ORACLE_SECTION_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/oracle-types.hxx>
+#include <odb/oracle/binding.hxx>
+#include <odb/oracle/statement.hxx>
+#include <odb/oracle/connection.hxx>
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ class connection;
+
+ // Template argument is the section traits type.
+ //
+ template <typename T, typename ST>
+ class section_statements
+ {
+ public:
+ typedef ST traits;
+
+ typedef typename traits::image_type image_type;
+ typedef typename traits::id_image_type id_image_type;
+
+ typedef oracle::select_statement select_statement_type;
+ typedef oracle::update_statement update_statement_type;
+
+ typedef oracle::connection connection_type;
+
+ section_statements (connection_type&,
+ image_type&, id_image_type&,
+ binding& id, binding& idv);
+
+ connection_type&
+ connection () {return conn_;}
+
+ const schema_version_migration&
+ version_migration (const char* name = "") const
+ {
+ if (svm_ == 0)
+ svm_ = &conn_.database ().schema_version_migration (name);
+
+ return *svm_;
+ }
+
+ image_type&
+ image () {return image_;}
+
+ const binding&
+ id_binding () {return id_binding_;}
+
+ // Id and optimistic concurrency version (if any).
+ //
+ const binding&
+ idv_binding () {return idv_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_binding_version () const { return update_id_binding_version_;}
+
+ void
+ update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ //
+ // Statements.
+ //
+
+ select_statement_type&
+ select_statement ()
+ {
+ if (select_ == 0)
+ select_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ traits::select_statement,
+ traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ id_binding_,
+ select_image_binding_,
+ 4096)); // Hardcode a 4kB LOB prefetch size.
+
+ return *select_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ traits::update_statement,
+ traits::versioned, // Process if versioned.
+ update_image_binding_));
+
+ return *update_;
+ }
+
+ public:
+ static const std::size_t id_column_count = traits::id_column_count;
+ static const std::size_t managed_optimistic_load_column_count =
+ traits::managed_optimistic_load_column_count;
+ static const std::size_t managed_optimistic_update_column_count =
+ traits::managed_optimistic_update_column_count;
+ static const std::size_t select_column_count = traits::load_column_count;
+ static const std::size_t update_column_count =
+ traits::update_column_count;
+
+ private:
+ section_statements (const section_statements&);
+ section_statements& operator= (const section_statements&);
+
+ protected:
+ connection_type& conn_;
+ mutable const schema_version_migration* svm_;
+
+ // These come from object_statements.
+ //
+ image_type& image_;
+ binding& id_binding_;
+ binding& idv_binding_;
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+
+ static const std::size_t select_bind_count =
+ select_column_count != 0 || managed_optimistic_load_column_count != 0
+ ? select_column_count + managed_optimistic_load_column_count
+ : 1;
+
+ binding select_image_binding_;
+ bind select_image_bind_[select_bind_count];
+
+ // Update binding.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_binding_version_;
+
+ static const std::size_t update_bind_count =
+ update_column_count != 0 || managed_optimistic_update_column_count != 0
+ ? update_column_count + id_column_count +
+ managed_optimistic_update_column_count
+ : 1;
+
+ binding update_image_binding_;
+ bind update_image_bind_[update_bind_count];
+
+ details::shared_ptr<select_statement_type> select_;
+ details::shared_ptr<update_statement_type> update_;
+ };
+ }
+}
+
+#include <odb/oracle/section-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_SECTION_STATEMENTS_HXX
diff --git a/libodb-oracle/odb/oracle/section-statements.txx b/libodb-oracle/odb/oracle/section-statements.txx
new file mode 100644
index 0000000..45606d5
--- /dev/null
+++ b/libodb-oracle/odb/oracle/section-statements.txx
@@ -0,0 +1,49 @@
+// file : odb/oracle/section-statements.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <typename T, typename ST>
+ section_statements<T, ST>::
+ section_statements (connection_type& conn,
+ image_type& im, id_image_type&,
+ binding& id, binding& idv)
+ : conn_ (conn),
+ svm_ (0),
+ image_ (im),
+ id_binding_ (id),
+ idv_binding_ (idv),
+ select_image_binding_ (select_image_bind_,
+ select_column_count +
+ managed_optimistic_load_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count +
+ managed_optimistic_update_column_count)
+ {
+ select_image_version_ = 0;
+ update_image_version_ = 0;
+ update_id_binding_version_ = 0;
+
+ // If we are using optimistic concurrency, then the select statement
+ // will override the version member in the object image.
+ //
+ if (managed_optimistic_load_column_count != 0)
+ {
+ // Getting to the root image in polymorphic case is tricky.
+ //
+ typedef object_traits_impl<T, id_oracle> object_traits;
+
+ select_image_binding_.change_callback =
+ root_image<object_traits, object_traits::polymorphic>::get (
+ image_).change_callback ();
+ }
+
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/simple-object-result.hxx b/libodb-oracle/odb/oracle/simple-object-result.hxx
new file mode 100644
index 0000000..df828d2
--- /dev/null
+++ b/libodb-oracle/odb/oracle/simple-object-result.hxx
@@ -0,0 +1,88 @@
+// file : odb/oracle/simple-object-result.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_SIMPLE_OBJECT_RESULT_HXX
+#define ODB_ORACLE_SIMPLE_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/simple-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx> // query_base
+#include <odb/oracle/statement.hxx>
+#include <odb/oracle/traits-calls.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <typename T>
+ class object_result_impl: public odb::object_result_impl<T>
+ {
+ public:
+ typedef odb::object_result_impl<T> base_type;
+
+ typedef typename base_type::id_type id_type;
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_oracle> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~object_result_impl ();
+
+ object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type&, bool fetch);
+
+ virtual id_type
+ load_id ();
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ typedef oracle::change_callback change_callback_type;
+
+ static void
+ change_callback (void* context);
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ bool use_copy_;
+ typename object_traits::image_type* image_copy_;
+ };
+ }
+}
+
+#include <odb/oracle/simple-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_SIMPLE_OBJECT_RESULT_HXX
diff --git a/libodb-oracle/odb/oracle/simple-object-result.txx b/libodb-oracle/odb/oracle/simple-object-result.txx
new file mode 100644
index 0000000..436c5a9
--- /dev/null
+++ b/libodb-oracle/odb/oracle/simple-object-result.txx
@@ -0,0 +1,181 @@
+// file : odb/oracle/simple-object-result.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/oracle/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <typename T>
+ object_result_impl<T>::
+ ~object_result_impl ()
+ {
+ invalidate ();
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ invalidate ()
+ {
+ change_callback_type& cc (statements_.image ().change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ delete image_copy_;
+ image_copy_ = 0;
+
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ object_result_impl<T>::
+ object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ use_copy_ (false),
+ image_copy_ (0)
+ {
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ load (object_type& obj, bool)
+ {
+ // This is a top-level call so the statements cannot be locked.
+ //
+ assert (!statements_.locked ());
+ typename statements_type::auto_lock l (statements_);
+
+ object_traits::callback (this->db_, obj, callback_event::pre_load);
+
+ typename object_traits::image_type& i (
+ use_copy_ ? *image_copy_ : statements_.image ());
+
+ tc_.init (obj, i, &this->db_);
+
+ // If we are using a copy, make sure the callback information for
+ // LOB data also comes from the copy.
+ //
+ statement_->stream_result (
+ use_copy_ ? &statements_.image () : 0,
+ use_copy_ ? image_copy_ : 0);
+
+ // Initialize the id image and binding and load the rest of the object
+ // (containers, etc).
+ //
+ typename object_traits::id_image_type& idi (statements_.id_image ());
+ object_traits::init (idi, object_traits::id (i));
+
+ binding& idb (statements_.id_image_binding ());
+ if (idi.version != statements_.id_image_version () || idb.version == 0)
+ {
+ object_traits::bind (idb.bind, idi);
+ statements_.id_image_version (idi.version);
+ idb.version++;
+ }
+
+ tc_.load_ (statements_, obj, false);
+ statements_.load_delayed (tc_.version ());
+ l.unlock ();
+ object_traits::callback (this->db_, obj, callback_event::post_load);
+ }
+
+ template <typename T>
+ typename object_result_impl<T>::id_type
+ object_result_impl<T>::
+ load_id ()
+ {
+ return object_traits::id (
+ use_copy_ ? *image_copy_ : statements_.image ());
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ typename object_traits::image_type& im (statements_.image ());
+ change_callback_type& cc (im.change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ use_copy_ = false;
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ }
+
+ if (statement_->fetch () == select_statement::no_data)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ else
+ {
+ cc.callback = &change_callback;
+ cc.context = this;
+ }
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t object_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ change_callback (void* c)
+ {
+ object_result_impl<T>* r (static_cast<object_result_impl<T>*> (c));
+ typename object_traits::image_type& im (r->statements_.image ());
+
+ if (r->image_copy_ == 0)
+ r->image_copy_ = new typename object_traits::image_type (im);
+ else
+ *r->image_copy_ = im;
+
+ im.change_callback_.callback = 0;
+ im.change_callback_.context = 0;
+
+ r->use_copy_ = true;
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/simple-object-statements.cxx b/libodb-oracle/odb/oracle/simple-object-statements.cxx
new file mode 100644
index 0000000..330627a
--- /dev/null
+++ b/libodb-oracle/odb/oracle/simple-object-statements.cxx
@@ -0,0 +1,15 @@
+// file : odb/oracle/simple-object-statements.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/oracle/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ object_statements_base::
+ ~object_statements_base ()
+ {
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/simple-object-statements.hxx b/libodb-oracle/odb/oracle/simple-object-statements.hxx
new file mode 100644
index 0000000..98e60b2
--- /dev/null
+++ b/libodb-oracle/odb/oracle/simple-object-statements.hxx
@@ -0,0 +1,594 @@
+// file : odb/oracle/simple-object-statements.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_SIMPLE_OBJECT_STATEMENTS_HXX
+#define ODB_ORACLE_SIMPLE_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cassert>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx>
+#include <odb/oracle/oracle-types.hxx>
+#include <odb/oracle/binding.hxx>
+#include <odb/oracle/statement.hxx>
+#include <odb/oracle/statements-base.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ // The extra_statement_cache class is only defined (and used) in
+ // the generated source file. However, object_statements may be
+ // referenced from another source file in the case of a polymorphic
+ // hierarchy (though in this case the extra statement cache is
+ // not used). As a result, we cannot have a by-value member and
+ // instead will store a pointer and lazily allocate the cache if
+ // and when needed. We will also need to store a pointer to the
+ // deleter function which will be initialized during allocation
+ // (at that point we know that the cache class is defined).
+ //
+ template <typename T, typename I, typename ID>
+ struct extra_statement_cache_ptr
+ {
+ typedef I image_type;
+ typedef ID id_image_type;
+ typedef oracle::connection connection_type;
+
+ extra_statement_cache_ptr (): p_ (0) {}
+ ~extra_statement_cache_ptr ()
+ {
+ if (p_ != 0)
+ (this->*deleter_) (0, 0, 0, 0, 0);
+ }
+
+ T&
+ get (connection_type& c,
+ image_type& im, id_image_type& idim,
+ binding& id, binding* idv)
+ {
+ if (p_ == 0)
+ allocate (&c, &im, &idim, &id, (idv != 0 ? idv : &id));
+
+ return *p_;
+ }
+
+ private:
+ void
+ allocate (connection_type*,
+ image_type*, id_image_type*,
+ binding*, binding*);
+
+ private:
+ T* p_;
+ void (extra_statement_cache_ptr::*deleter_) (
+ connection_type*, image_type*, id_image_type*, binding*, binding*);
+ };
+
+ template <typename T, typename I, typename ID>
+ void extra_statement_cache_ptr<T, I, ID>::
+ allocate (connection_type* c,
+ image_type* im, id_image_type* idim,
+ binding* id, binding* idv)
+ {
+ // To reduce object code size, this function acts as both allocator
+ // and deleter.
+ //
+ if (p_ == 0)
+ {
+ p_ = new T (*c, *im, *idim, *id, *idv);
+ deleter_ = &extra_statement_cache_ptr<T, I, ID>::allocate;
+ }
+ else
+ delete p_;
+ }
+
+ //
+ // Implementation for objects with object id.
+ //
+
+ class LIBODB_ORACLE_EXPORT object_statements_base: public statements_base
+ {
+ // Locking.
+ //
+ public:
+ void
+ lock ()
+ {
+ assert (!locked_);
+ locked_ = true;
+ }
+
+ void
+ unlock ()
+ {
+ assert (locked_);
+ locked_ = false;
+ }
+
+ bool
+ locked () const
+ {
+ return locked_;
+ }
+
+ struct auto_unlock
+ {
+ // Unlocks the statement on construction and re-locks it on
+ // destruction.
+ //
+ auto_unlock (object_statements_base&);
+ ~auto_unlock ();
+
+ private:
+ auto_unlock (const auto_unlock&);
+ auto_unlock& operator= (const auto_unlock&);
+
+ private:
+ object_statements_base& s_;
+ };
+
+ public:
+ virtual
+ ~object_statements_base ();
+
+ protected:
+ object_statements_base (connection_type& conn)
+ : statements_base (conn), locked_ (false)
+ {
+ }
+
+ protected:
+ bool locked_;
+ };
+
+ template <typename T, bool optimistic>
+ struct optimistic_data;
+
+ template <typename T>
+ struct optimistic_data<T, true>
+ {
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_oracle> object_traits;
+
+ optimistic_data (bind*, std::size_t skip, sb4* status);
+
+ binding*
+ id_image_binding () {return &id_image_binding_;}
+
+ // The id + optimistic column binding.
+ //
+ binding id_image_binding_;
+
+ details::shared_ptr<delete_statement> erase_;
+ };
+
+ template <typename T>
+ struct optimistic_data<T, false>
+ {
+ optimistic_data (bind*, std::size_t, sb4*) {}
+
+ binding*
+ id_image_binding () {return 0;}
+ };
+
+ template <typename T>
+ class object_statements: public object_statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_oracle> object_traits;
+ typedef typename object_traits::id_type id_type;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::image_type image_type;
+ typedef typename object_traits::id_image_type id_image_type;
+
+ typedef
+ typename object_traits::pointer_cache_traits
+ pointer_cache_traits;
+
+ typedef
+ typename object_traits::extra_statement_cache_type
+ extra_statement_cache_type;
+
+ typedef oracle::insert_statement insert_statement_type;
+ typedef oracle::select_statement select_statement_type;
+ typedef oracle::update_statement update_statement_type;
+ typedef oracle::delete_statement delete_statement_type;
+
+ // Automatic lock.
+ //
+ struct auto_lock
+ {
+ // Lock the statements unless they are already locked in which
+ // case subsequent calls to locked() will return false.
+ //
+ auto_lock (object_statements&);
+
+ // Unlock the statements if we are holding the lock and clear
+ // the delayed loads. This should only happen in case an
+ // exception is thrown. In normal circumstances, the user
+ // should call unlock() explicitly.
+ //
+ ~auto_lock ();
+
+ // Return true if this auto_lock instance holds the lock.
+ //
+ bool
+ locked () const;
+
+ // Unlock the statements.
+ //
+ void
+ unlock ();
+
+ private:
+ auto_lock (const auto_lock&);
+ auto_lock& operator= (const auto_lock&);
+
+ private:
+ object_statements& s_;
+ bool locked_;
+ };
+
+ public:
+ object_statements (connection_type&);
+
+ virtual
+ ~object_statements ();
+
+ // Delayed loading.
+ //
+ typedef void (*loader_function) (odb::database&,
+ const id_type&,
+ object_type&,
+ const schema_version_migration*);
+
+ void
+ delay_load (const id_type& id,
+ object_type& obj,
+ const typename pointer_cache_traits::position_type& p,
+ loader_function l = 0)
+ {
+ delayed_.push_back (delayed_load (id, obj, p, l));
+ }
+
+ void
+ load_delayed (const schema_version_migration* svm)
+ {
+ assert (locked ());
+
+ if (!delayed_.empty ())
+ load_delayed_<object_statements> (svm);
+ }
+
+ void
+ clear_delayed ()
+ {
+ if (!delayed_.empty ())
+ clear_delayed_ ();
+ }
+
+ // Object image.
+ //
+ image_type&
+ image (std::size_t i = 0) {return images_[i].obj;}
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_image_version () const { return update_id_image_version_;}
+
+ void
+ update_id_image_version (std::size_t v) {update_id_image_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ // Object id image and binding.
+ //
+ id_image_type&
+ id_image (std::size_t i = 0) {return images_[i].id;}
+
+ std::size_t
+ id_image_version () const {return id_image_version_;}
+
+ void
+ id_image_version (std::size_t v) {id_image_version_ = v;}
+
+ binding&
+ id_image_binding () {return id_image_binding_;}
+
+ // Optimistic id + managed column image binding. It points to
+ // the same suffix as id binding and they are always updated
+ // at the same time.
+ //
+ binding&
+ optimistic_id_image_binding () {return od_.id_image_binding_;}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ object_traits::auto_id ? &id_image_binding_ : 0));
+
+ return *persist_;
+ }
+
+ select_statement_type&
+ find_statement ()
+ {
+ if (find_ == 0)
+ find_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ object_traits::find_statement,
+ object_traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ id_image_binding_,
+ select_image_binding_,
+ 4096)); // Hardcode a 4kB LOB prefetch size.
+
+ return *find_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ object_traits::update_statement,
+ true, // Unique (0 or 1).
+ object_traits::versioned, // Process if versioned.
+ update_image_binding_));
+
+ return *update_;
+ }
+
+ delete_statement_type&
+ erase_statement ()
+ {
+ if (erase_ == 0)
+ erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::erase_statement,
+ true, // Unique (0 or 1 affected rows).
+ id_image_binding_));
+
+ return *erase_;
+ }
+
+ delete_statement_type&
+ optimistic_erase_statement ()
+ {
+ if (od_.erase_ == 0)
+ {
+ od_.erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::optimistic_erase_statement,
+ true, // Unique (0 or 1 affected rows).
+ od_.id_image_binding_));
+ }
+
+ return *od_.erase_;
+ }
+
+ // Extra (container, section) statement cache.
+ //
+ extra_statement_cache_type&
+ extra_statement_cache ()
+ {
+ return extra_statement_cache_.get (
+ conn_,
+ images_[0].obj, images_[0].id,
+ id_image_binding_, od_.id_image_binding ());
+ }
+
+ public:
+ // select = total - separate_load
+ // insert = total - inverse - managed_optimistic - auto_id
+ // update = total - inverse - managed_optimistic - id - readonly
+ // - separate_update
+ //
+ static const std::size_t id_column_count =
+ object_traits::id_column_count;
+
+ static const std::size_t managed_optimistic_column_count =
+ object_traits::managed_optimistic_column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count -
+ object_traits::separate_load_column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count -
+ object_traits::inverse_column_count -
+ object_traits::managed_optimistic_column_count -
+ (object_traits::auto_id ? id_column_count : 0);
+
+ static const std::size_t update_column_count =
+ insert_column_count -
+ (object_traits::auto_id ? 0 : id_column_count) -
+ object_traits::readonly_column_count -
+ object_traits::separate_update_column_count;
+
+ private:
+ object_statements (const object_statements&);
+ object_statements& operator= (const object_statements&);
+
+ protected:
+ template <typename STS>
+ void
+ load_delayed_ (const schema_version_migration*);
+
+ void
+ clear_delayed_ ();
+
+ protected:
+ template <typename T1>
+ friend class polymorphic_derived_object_statements;
+
+ extra_statement_cache_ptr<extra_statement_cache_type,
+ image_type,
+ id_image_type> extra_statement_cache_;
+
+ // The UPDATE statement uses both the object and id image. Keep
+ // them next to each other so that the same skip distance can
+ // be used in batch binding.
+ //
+ struct images
+ {
+ image_type obj;
+ id_image_type id;
+ };
+
+ images images_[object_traits::batch];
+ sb4 status_[object_traits::batch];
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+ binding select_image_binding_;
+ bind select_image_bind_[select_column_count];
+
+ // Insert binding.
+ //
+ std::size_t insert_image_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[
+ insert_column_count != 0 ? insert_column_count : 1];
+
+ // Update binding. Note that the id suffix is bound to id_image_
+ // below instead of image_ which makes this binding effectively
+ // bound to two images. As a result, we have to track versions
+ // for both of them. If this object uses optimistic concurrency,
+ // then the binding for the managed column (version, timestamp,
+ // etc) comes after the id and the image for such a column is
+ // stored as part of the id image.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_image_version_;
+ binding update_image_binding_;
+ bind update_image_bind_[update_column_count + id_column_count +
+ managed_optimistic_column_count];
+
+ // Id image binding (only used as a parameter or in RETURNING for
+ // auto ids). Uses the suffix in the update bind.
+ //
+ std::size_t id_image_version_;
+ binding id_image_binding_;
+
+ // Extra data for objects with optimistic concurrency support.
+ //
+ optimistic_data<T, managed_optimistic_column_count != 0> od_;
+
+ details::shared_ptr<insert_statement_type> persist_;
+ details::shared_ptr<select_statement_type> find_;
+ details::shared_ptr<update_statement_type> update_;
+ details::shared_ptr<delete_statement_type> erase_;
+
+ // Delayed loading.
+ //
+ struct delayed_load
+ {
+ typedef typename pointer_cache_traits::position_type position_type;
+
+ delayed_load () {}
+ delayed_load (const id_type& i,
+ object_type& o,
+ const position_type& p,
+ loader_function l)
+ : id (i), obj (&o), pos (p), loader (l)
+ {
+ }
+
+ id_type id;
+ object_type* obj;
+ position_type pos;
+ loader_function loader;
+ };
+
+ typedef std::vector<delayed_load> delayed_loads;
+ delayed_loads delayed_;
+
+ // Delayed vectors swap guard. See the load_delayed_() function for
+ // details.
+ //
+ struct swap_guard
+ {
+ swap_guard (object_statements& os, delayed_loads& dl)
+ : os_ (os), dl_ (dl)
+ {
+ dl_.swap (os_.delayed_);
+ }
+
+ ~swap_guard ()
+ {
+ os_.clear_delayed ();
+ dl_.swap (os_.delayed_);
+ }
+
+ private:
+ object_statements& os_;
+ delayed_loads& dl_;
+ };
+ };
+ }
+}
+
+#include <odb/oracle/simple-object-statements.ixx>
+#include <odb/oracle/simple-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_SIMPLE_OBJECT_STATEMENTS_HXX
diff --git a/libodb-oracle/odb/oracle/simple-object-statements.ixx b/libodb-oracle/odb/oracle/simple-object-statements.ixx
new file mode 100644
index 0000000..4631d69
--- /dev/null
+++ b/libodb-oracle/odb/oracle/simple-object-statements.ixx
@@ -0,0 +1,68 @@
+// file : odb/oracle/simple-object-statements.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace oracle
+ {
+ //
+ // auto_unlock
+ //
+ inline object_statements_base::auto_unlock::
+ auto_unlock (object_statements_base& s)
+ : s_ (s)
+ {
+ s_.unlock ();
+ }
+
+ inline object_statements_base::auto_unlock::
+ ~auto_unlock ()
+ {
+ s_.lock ();
+ }
+
+ //
+ // auto_lock
+ //
+ template <typename T>
+ inline object_statements<T>::auto_lock::
+ auto_lock (object_statements& s)
+ : s_ (s)
+ {
+ if (!s_.locked ())
+ {
+ s_.lock ();
+ locked_ = true;
+ }
+ else
+ locked_ = false;
+ }
+
+ template <typename T>
+ inline object_statements<T>::auto_lock::
+ ~auto_lock ()
+ {
+ if (locked_)
+ {
+ s_.unlock ();
+ s_.clear_delayed ();
+ }
+ }
+
+ template <typename T>
+ inline bool object_statements<T>::auto_lock::
+ locked () const
+ {
+ return locked_;
+ }
+
+ template <typename T>
+ inline void object_statements<T>::auto_lock::
+ unlock ()
+ {
+ assert (locked_);
+ s_.unlock ();
+ locked_ = false;
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/simple-object-statements.txx b/libodb-oracle/odb/oracle/simple-object-statements.txx
new file mode 100644
index 0000000..99de084
--- /dev/null
+++ b/libodb-oracle/odb/oracle/simple-object-statements.txx
@@ -0,0 +1,164 @@
+// file : odb/oracle/simple-object-statements.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/oracle/connection.hxx>
+#include <odb/oracle/traits-calls.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ //
+ // optimistic_data
+ //
+
+ template <typename T>
+ optimistic_data<T, true>::
+ optimistic_data (bind* b, std::size_t skip, sb4* status)
+ : id_image_binding_ (
+ b,
+ object_traits::id_column_count +
+ object_traits::managed_optimistic_column_count,
+ object_traits::batch,
+ skip,
+ status)
+ {
+ }
+
+ //
+ // object_statements
+ //
+
+ template <typename T>
+ object_statements<T>::
+ ~object_statements ()
+ {
+ }
+
+ template <typename T>
+ object_statements<T>::
+ object_statements (connection_type& conn)
+ : object_statements_base (conn),
+ select_image_binding_ (select_image_bind_, select_column_count),
+ insert_image_binding_ (insert_image_bind_,
+ insert_column_count,
+ object_traits::batch,
+ sizeof (images),
+ status_),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count +
+ managed_optimistic_column_count,
+ object_traits::batch,
+ sizeof (images),
+ status_),
+ id_image_binding_ (update_image_bind_ + update_column_count,
+ id_column_count,
+ object_traits::batch,
+ sizeof (images),
+ status_),
+ od_ (update_image_bind_ + update_column_count,
+ sizeof (images),
+ status_)
+ {
+ // Only versions in the first element used.
+ //
+ images_[0].obj.version = 0;
+ images_[0].id.version = 0;
+
+ select_image_version_ = 0;
+ insert_image_version_ = 0;
+ update_image_version_ = 0;
+ update_id_image_version_ = 0;
+ id_image_version_ = 0;
+
+ // SELECT statements only use the first element (no batches).
+ //
+ select_image_binding_.change_callback =
+ images_[0].obj.change_callback ();
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ }
+
+ template <typename T>
+ template <typename STS>
+ void object_statements<T>::
+ load_delayed_ (const schema_version_migration* svm)
+ {
+ database& db (connection ().database ());
+
+ delayed_loads dls;
+ swap_guard sg (*this, dls);
+
+ while (!dls.empty ())
+ {
+ delayed_load l (dls.back ());
+ typename pointer_cache_traits::insert_guard ig (l.pos);
+ dls.pop_back ();
+
+ if (l.loader == 0)
+ {
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (static_cast<STS&> (*this), &l.id))
+ throw object_not_persistent ();
+
+ object_traits::callback (db, *l.obj, callback_event::pre_load);
+
+ // Our calls to init/load below can result in additional delayed
+ // loads being added to the delayed_ vector. We need to process
+ // those before we call the post callback.
+ //
+ tc.init (*l.obj, image (), &db);
+ find_->stream_result ();
+
+ // Load containers, etc.
+ //
+ tc.load_ (static_cast<STS&> (*this), *l.obj, false);
+
+ if (!delayed_.empty ())
+ load_delayed_<STS> (svm);
+
+ // Temporarily unlock the statement for the post_load call so that
+ // it can load objects of this type recursively. This is safe to do
+ // because we have completely loaded the current object. Also the
+ // delayed_ list is clear before the unlock and should be clear on
+ // re-lock (since a callback can only call public API functions
+ // which will make sure all the delayed loads are processed before
+ // returning).
+ //
+ {
+ auto_unlock u (*this);
+ object_traits::callback (db, *l.obj, callback_event::post_load);
+ }
+ }
+ else
+ l.loader (db, l.id, *l.obj, svm);
+
+ pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+ }
+
+ template <typename T>
+ void object_statements<T>::
+ clear_delayed_ ()
+ {
+ // Remove the objects from the session cache.
+ //
+ for (typename delayed_loads::iterator i (delayed_.begin ()),
+ e (delayed_.end ()); i != e; ++i)
+ {
+ pointer_cache_traits::erase (i->pos);
+ }
+
+ delayed_.clear ();
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/statement-cache.hxx b/libodb-oracle/odb/oracle/statement-cache.hxx
new file mode 100644
index 0000000..2dba01f
--- /dev/null
+++ b/libodb-oracle/odb/oracle/statement-cache.hxx
@@ -0,0 +1,59 @@
+// file : odb/oracle/statement-cache.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_STATEMENT_CACHE_HXX
+#define ODB_ORACLE_STATEMENT_CACHE_HXX
+
+#include <odb/pre.hxx>
+
+#include <map>
+#include <typeinfo>
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx>
+#include <odb/oracle/statements-base.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/type-info.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ class LIBODB_ORACLE_EXPORT statement_cache
+ {
+ public:
+ statement_cache (connection& conn)
+ : conn_ (conn),
+ version_seq_ (conn_.database ().schema_version_sequence ()) {}
+
+ template <typename T>
+ typename object_traits_impl<T, id_oracle>::statements_type&
+ find_object ();
+
+ template <typename T>
+ view_statements<T>&
+ find_view ();
+
+ private:
+ typedef std::map<const std::type_info*,
+ details::shared_ptr<statements_base>,
+ details::type_info_comparator> map;
+
+ connection& conn_;
+ unsigned int version_seq_;
+ map map_;
+ };
+ }
+}
+
+#include <odb/oracle/statement-cache.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_STATEMENT_CACHE_HXX
diff --git a/libodb-oracle/odb/oracle/statement-cache.txx b/libodb-oracle/odb/oracle/statement-cache.txx
new file mode 100644
index 0000000..23d9504
--- /dev/null
+++ b/libodb-oracle/odb/oracle/statement-cache.txx
@@ -0,0 +1,60 @@
+// file : odb/oracle/statement-cache.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/oracle/database.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <typename T>
+ typename object_traits_impl<T, id_oracle>::statements_type&
+ statement_cache::
+ find_object ()
+ {
+ typedef
+ typename object_traits_impl<T, id_oracle>::statements_type
+ statements_type;
+
+ // Clear the cache if the database version has changed. This
+ // makes sure we don't re-use statements that correspond to
+ // the old schema.
+ //
+ if (version_seq_ != conn_.database ().schema_version_sequence ())
+ {
+ map_.clear ();
+ version_seq_ = conn_.database ().schema_version_sequence ();
+ }
+
+ map::iterator i (map_.find (&typeid (T)));
+
+ if (i != map_.end ())
+ return static_cast<statements_type&> (*i->second);
+
+ details::shared_ptr<statements_type> p (
+ new (details::shared) statements_type (conn_));
+
+ map_.insert (map::value_type (&typeid (T), p));
+ return *p;
+ }
+
+ template <typename T>
+ view_statements<T>& statement_cache::
+ find_view ()
+ {
+ // We don't cache any statements for views so no need to clear
+ // the cache.
+
+ map::iterator i (map_.find (&typeid (T)));
+
+ if (i != map_.end ())
+ return static_cast<view_statements<T>&> (*i->second);
+
+ details::shared_ptr<view_statements<T> > p (
+ new (details::shared) view_statements<T> (conn_));
+
+ map_.insert (map::value_type (&typeid (T), p));
+ return *p;
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/statement.cxx b/libodb-oracle/odb/oracle/statement.cxx
new file mode 100644
index 0000000..93d8a4a
--- /dev/null
+++ b/libodb-oracle/odb/oracle/statement.cxx
@@ -0,0 +1,2055 @@
+// file : odb/oracle/statement.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <oci.h>
+
+#include <cstring> // std::strlen, std::memset
+#include <cassert>
+
+#include <odb/tracer.hxx>
+#include <odb/exceptions.hxx> // object_not_persistent
+#include <odb/details/unused.hxx>
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/statement.hxx>
+#include <odb/oracle/connection.hxx>
+#include <odb/oracle/auto-descriptor.hxx>
+#include <odb/oracle/error.hxx>
+#include <odb/oracle/exceptions.hxx>
+
+#include <odb/oracle/details/number.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace oracle
+ {
+ // Mapping of bind::buffer_type values for parameter buffers to their
+ // equivalent external OCI typecode identifiers.
+ //
+ static const ub4 param_sqlt_lookup[bind::last] =
+ {
+ SQLT_INT, // bind::integer
+ SQLT_UIN, // bind::uinteger
+ SQLT_BFLOAT, // bind::binary_float
+ SQLT_BDOUBLE, // bind::binary_double
+ SQLT_NUM, // bind::number
+ SQLT_DAT, // bind::date
+ SQLT_TIMESTAMP, // bind::timestamp
+ SQLT_INTERVAL_YM, // bind::interval_ym
+ SQLT_INTERVAL_DS, // bind::interval_ds
+ SQLT_CHR, // bind::string
+ SQLT_CHR, // bind::nstring
+ SQLT_BIN, // bind::raw
+ SQLT_LBI, // bind::blob
+ SQLT_LNG, // bind::clob
+ SQLT_LNG // bind::nclob
+ };
+
+ // Mapping of bind::buffer_type values for result buffers to their
+ // equivalent external OCI typecode identifiers.
+ //
+ static const ub4 result_sqlt_lookup[bind::last] =
+ {
+ SQLT_INT, // bind::integer
+ SQLT_UIN, // bind::uinteger
+ SQLT_BFLOAT, // bind::binary_float
+ SQLT_BDOUBLE, // bind::binary_double
+ SQLT_NUM, // bind::number
+ SQLT_DAT, // bind::date
+ SQLT_TIMESTAMP, // bind::timestamp
+ SQLT_INTERVAL_YM, // bind::interval_ym
+ SQLT_INTERVAL_DS, // bind::interval_ds
+ SQLT_CHR, // bind::string
+ SQLT_CHR, // bind::nstring
+ SQLT_BIN, // bind::raw
+ SQLT_BLOB, // bind::blob
+ SQLT_CLOB, // bind::clob
+ SQLT_CLOB // bind::nclob
+ };
+
+ template <typename T>
+ static inline T*
+ offset (T* base, size_t count, size_t size)
+ {
+ return reinterpret_cast<T*> (
+ reinterpret_cast<char*> (base) + count * size);
+ }
+
+ extern "C" sb4
+ odb_oracle_param_callback_proxy (void* context,
+ OCIBind*,
+ ub4 it, // iteration
+ ub4, // index
+ void** buffer,
+ ub4* size,
+ ub1* piece,
+ void** indicator)
+ {
+ bind& b (*static_cast<bind*> (context));
+
+ // Offset the data based on the current iteration and skip size (stored
+ // in capacity).
+ //
+ sb2* ind (offset (b.indicator, it, b.capacity));
+
+ // Only call the callback if the parameter is not NULL.
+ //
+ if (*ind != -1)
+ {
+ lob* l (static_cast<lob*> (offset (b.buffer, it, b.capacity)));
+ lob_callback* cb (
+ static_cast<lob_callback*> (offset (b.callback, it, b.capacity)));
+
+ chunk_position pos;
+ if (!(*cb->callback.param) (
+ cb->context.param,
+ &l->position,
+ const_cast<const void**> (buffer),
+ size,
+ &pos,
+ l->buffer->data (),
+ static_cast<ub4> (l->buffer->capacity ())))
+ return OCI_ERROR;
+
+ switch (pos)
+ {
+ case chunk_one:
+ {
+ *piece = OCI_ONE_PIECE;
+ break;
+ }
+ case chunk_first:
+ {
+ *piece = OCI_FIRST_PIECE;
+ break;
+ }
+ case chunk_next:
+ {
+ *piece = OCI_NEXT_PIECE;
+ break;
+ }
+ case chunk_last:
+ {
+ *piece = OCI_LAST_PIECE;
+ break;
+ }
+ }
+ }
+ else
+ *piece = OCI_ONE_PIECE;
+
+ *indicator = ind;
+
+ return OCI_CONTINUE;
+ }
+
+ //
+ // statement
+ //
+
+ statement::
+ ~statement ()
+ {
+ if (stmt_ == 0)
+ return;
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->deallocate (conn_, *this);
+ }
+
+ // Unbind (free) parameter descriptors.
+ //
+ for (size_t i (0); i < usize_; ++i)
+ {
+ ub4 t;
+ bind* b (udata_[i].bind);
+
+ switch (udata_[i].type)
+ {
+ case bind::timestamp:
+ {
+ if (b != 0)
+ static_cast<datetime*> (b->buffer)->descriptor = 0;
+
+ t = OCI_DTYPE_TIMESTAMP;
+ break;
+ }
+ case bind::interval_ym:
+ {
+ if (b != 0)
+ static_cast<interval_ym*> (b->buffer)->descriptor = 0;
+
+ t = OCI_DTYPE_INTERVAL_YM;
+ break;
+ }
+ case bind::interval_ds:
+ {
+ if (b != 0)
+ static_cast<interval_ds*> (b->buffer)->descriptor = 0;
+
+ t = OCI_DTYPE_INTERVAL_DS;
+ break;
+ }
+ default:
+ {
+ assert (false);
+ return;
+ }
+ }
+
+ OCIDescriptorFree (udata_[i].value, t);
+ }
+
+ delete[] udata_;
+ }
+
+ statement::
+ statement (connection_type& conn,
+ const string& text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize)
+ : conn_ (conn), udata_ (0), usize_ (0)
+ {
+ init (text.c_str (), text.size (), sk, process, optimize);
+ }
+
+ statement::
+ statement (connection_type& conn,
+ const char* text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize)
+ : conn_ (conn), udata_ (0), usize_ (0)
+ {
+ init (text, strlen (text), sk, process, optimize);
+ }
+
+ void statement::
+ init (const char* text,
+ size_t text_size,
+ statement_kind sk,
+ const binding* proc,
+ bool optimize)
+ {
+ string tmp;
+ if (proc != 0)
+ {
+ switch (sk)
+ {
+ case statement_select:
+ process_select (tmp,
+ text,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ '"', '"',
+ optimize,
+ false); // No AS in JOINs.
+ break;
+ case statement_insert:
+ process_insert (tmp,
+ text,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ ':');
+ break;
+ case statement_update:
+ process_update (tmp,
+ text,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ ':');
+ break;
+ case statement_delete:
+ case statement_generic:
+ assert (false);
+ }
+
+ text = tmp.c_str ();
+ text_size = tmp.size ();
+ }
+
+ // Empty statement.
+ //
+ if (*text == '\0')
+ return;
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ {
+ // Temporarily store the statement text in unbind data so that
+ // text() which may be called by the tracer can access it.
+ //
+ udata_ = reinterpret_cast<unbind*> (const_cast<char*> (text));
+ t->prepare (conn_, *this);
+ udata_ = 0;
+ }
+ }
+
+ OCIError* err (conn_.error_handle ());
+ OCIStmt* handle (0);
+
+ sword r (OCIStmtPrepare2 (conn_.handle (),
+ &handle,
+ err,
+ reinterpret_cast<const OraText*> (text),
+ static_cast<ub4> (text_size),
+ 0,
+ 0,
+ OCI_NTV_SYNTAX,
+ OCI_DEFAULT));
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (conn_, r);
+
+ stmt_.reset (handle, OCI_STRLS_CACHE_DELETE, err);
+ }
+
+ const char* statement::
+ text () const
+ {
+ if (stmt_ == 0)
+ // See init() above for details on what's going on here.
+ //
+ return udata_ != 0 ? reinterpret_cast<const char*> (udata_) : "";
+
+ OCIError* err (conn_.error_handle ());
+
+ OraText* s (0);
+ sword r (OCIAttrGet (stmt_,
+ OCI_HTYPE_STMT,
+ &s,
+ 0,
+ OCI_ATTR_STATEMENT,
+ err));
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+
+ return reinterpret_cast<char*> (s);
+ }
+
+ ub4 statement::
+ bind_param (bind* b, size_t n, size_t batch, size_t skip)
+ {
+ // Figure out how many unbind elements we will need and allocate them.
+ //
+ {
+ size_t un (0);
+
+ for (size_t i (0); i < n; ++i)
+ {
+ if (b[i].buffer == 0) // Skip NULL entries.
+ continue;
+
+ switch (b[i].type)
+ {
+ case bind::timestamp:
+ {
+ datetime* dt (static_cast<datetime*> (b[i].buffer));
+ if (dt->descriptor == 0 && (dt->flags & descriptor_free) == 0)
+ un++;
+ break;
+ }
+ case bind::interval_ym:
+ {
+ interval_ym* iym (static_cast<interval_ym*> (b[i].buffer));
+ if (iym->descriptor == 0 && (iym->flags & descriptor_free) == 0)
+ un++;
+ break;
+ }
+ case bind::interval_ds:
+ {
+ interval_ds* ids (static_cast<interval_ds*> (b[i].buffer));
+ if (ids->descriptor == 0 && (ids->flags & descriptor_free) == 0)
+ un++;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ // Unbind is only used in queries which means there should be no
+ // batches.
+ //
+ assert (un == 0 || batch == 1);
+
+ if (un != 0)
+ udata_ = new unbind[un];
+ }
+
+ bool seen_lob (false);
+ sword r;
+ OCIError* err (conn_.error_handle ());
+ OCIEnv* env (conn_.database ().environment ());
+
+ ub4 i (0);
+ for (bind* end (b + n); b != end; ++b)
+ {
+ if (b->buffer == 0) // Skip NULL entries.
+ continue;
+
+ i++; // Column index is 1-based.
+
+ void* value (0);
+ sb4 capacity;
+ ub2* size (0);
+ bool callback (b->callback != 0);
+
+ switch (b->type)
+ {
+ case bind::timestamp:
+ {
+ for (size_t i (0); i != batch; ++i)
+ {
+ datetime* dt (
+ static_cast<datetime*> (offset (b->buffer, i, skip)));
+
+ void* pd (0); // Pointer to descriptor.
+
+ if (dt->descriptor == 0)
+ {
+ void* d (0);
+ r = OCIDescriptorAlloc (env,
+ &d,
+ OCI_DTYPE_TIMESTAMP,
+ 0,
+ 0);
+
+ if (r != OCI_SUCCESS)
+ throw invalid_oci_handle ();
+
+ if (dt->flags & descriptor_cache)
+ {
+ dt->descriptor = static_cast<OCIDateTime*> (d);
+ dt->environment = env;
+ dt->error = err;
+ }
+
+ // If the datetime instance is not responsible for the
+ // descriptor, then we have to arrange to have it freed
+ // using the unbind machinery.
+ //
+ if ((dt->flags & descriptor_free) == 0)
+ {
+ unbind& u (udata_[usize_++]);
+
+ u.type = bind::timestamp;
+ u.bind = (dt->flags & descriptor_cache) ? b : 0;
+ u.value = d;
+ pd = &u.value;
+ }
+ else
+ pd = &dt->descriptor;
+
+ // Initialize the descriptor from the cached data.
+ //
+ if (b->indicator == 0 || *b->indicator != -1)
+ r = OCIDateTimeConstruct (env,
+ err,
+ static_cast<OCIDateTime*> (d),
+ dt->year_,
+ dt->month_,
+ dt->day_,
+ dt->hour_,
+ dt->minute_,
+ dt->second_,
+ dt->nanosecond_,
+ 0,
+ 0);
+
+ if (r != OCI_SUCCESS)
+ translate_error (err, r);
+ }
+ else
+ pd = &dt->descriptor;
+
+ if (i == 0)
+ value = pd;
+ }
+
+ capacity = static_cast<sb4> (sizeof (OCIDateTime*));
+ break;
+ }
+ case bind::interval_ym:
+ {
+ for (size_t i (0); i != batch; ++i)
+ {
+ interval_ym* iym (
+ static_cast<interval_ym*> (offset (b->buffer, i, skip)));
+
+ void* pd (0); // Pointer to descriptor.
+
+ if (iym->descriptor == 0)
+ {
+ void* d (0);
+ r = OCIDescriptorAlloc (env,
+ &d,
+ OCI_DTYPE_INTERVAL_YM,
+ 0,
+ 0);
+
+ if (r != OCI_SUCCESS)
+ throw invalid_oci_handle ();
+
+ if (iym->flags & descriptor_cache)
+ {
+ iym->descriptor = static_cast<OCIInterval*> (d);
+ iym->environment = env;
+ iym->error = err;
+ }
+
+ // If the interval_ym instance is not responsible for the
+ // descriptor, then we have to arrange to have it freed
+ // using the unbind machinery.
+ //
+ if ((iym->flags & descriptor_free) == 0)
+ {
+ unbind& u (udata_[usize_++]);
+
+ u.type = bind::interval_ym;
+ u.bind = (iym->flags & descriptor_cache) ? b : 0;
+ u.value = d;
+ pd = &u.value;
+ }
+ else
+ pd = &iym->descriptor;
+
+ // Initialize the descriptor from the cached data.
+ //
+ if (b->indicator == 0 || *b->indicator != -1)
+ r = OCIIntervalSetYearMonth (env,
+ err,
+ iym->year_,
+ iym->month_,
+ static_cast<OCIInterval*> (d));
+
+ if (r != OCI_SUCCESS)
+ translate_error (err, r);
+ }
+ else
+ pd = &iym->descriptor;
+
+ if (i == 0)
+ value = pd;
+ }
+
+ capacity = static_cast<sb4> (sizeof (OCIInterval*));
+ break;
+ }
+ case bind::interval_ds:
+ {
+ for (size_t i (0); i != batch; ++i)
+ {
+ interval_ds* ids (
+ static_cast<interval_ds*> (offset (b->buffer, i, skip)));
+
+ void* pd (0); // Pointer to descriptor.
+
+ if (ids->descriptor == 0)
+ {
+ void* d (0);
+ r = OCIDescriptorAlloc (env,
+ &d,
+ OCI_DTYPE_INTERVAL_DS,
+ 0,
+ 0);
+
+ if (r != OCI_SUCCESS)
+ throw invalid_oci_handle ();
+
+ if (ids->flags & descriptor_cache)
+ {
+ ids->descriptor = static_cast<OCIInterval*> (d);
+ ids->environment = env;
+ ids->error = err;
+ }
+
+ // If the interval_ds instance is not responsible for the
+ // descriptor, then we have to arrange to have it freed
+ // using the unbind machinery.
+ //
+ if ((ids->flags & descriptor_free) == 0)
+ {
+ unbind& u (udata_[usize_++]);
+
+ u.type = bind::interval_ds;
+ u.bind = (ids->flags & descriptor_cache) ? b : 0;
+ u.value = d;
+ pd = &u.value;
+ }
+ else
+ pd = &ids->descriptor;
+
+ // Initialize the descriptor from the cached data.
+ //
+ if (b->indicator == 0 || *b->indicator != -1)
+ r = OCIIntervalSetDaySecond (env,
+ err,
+ ids->day_,
+ ids->hour_,
+ ids->minute_,
+ ids->second_,
+ ids->nanosecond_,
+ static_cast<OCIInterval*> (d));
+
+ if (r != OCI_SUCCESS)
+ translate_error (err, r);
+ }
+ else
+ pd = &ids->descriptor;
+
+ if (i == 0)
+ value = pd;
+ }
+
+ capacity = static_cast<sb4> (sizeof (OCIInterval*));
+ break;
+ }
+ case bind::blob:
+ case bind::clob:
+ case bind::nclob:
+ {
+ seen_lob = true;
+
+ lob* l (static_cast<lob*> (b->buffer));
+
+ if (l->buffer == 0)
+ {
+ details::buffer& lob_buffer (conn_.lob_buffer ());
+
+ if (lob_buffer.capacity () == 0)
+ lob_buffer.capacity (4096);
+
+ // Generally, we should not modify the image since that would
+ // break the thread-safety guarantee of the query expression.
+ // However, in Oracle, LOBs cannot be used in queries so we can
+ // make an exception here.
+ //
+ for (size_t i (0); i != batch;)
+ {
+ l->buffer = &lob_buffer;
+ l = static_cast<lob*> (offset (b->buffer, ++i, skip));
+ }
+ }
+
+ assert (callback);
+ value = 0;
+
+ // When binding LOB parameters, the capacity must be greater than
+ // 4000 and less than the maximum LOB length in bytes. If it is
+ // not, OCI returns an error. Other than this, the capacity seems
+ // to be irrelevant to OCI bind behaviour for LOB parameters when
+ // used with callbacks.
+ //
+ capacity = 4096;
+
+ // Store skip in capacity so that the callback can offset the
+ // values based on the iteration number.
+ //
+ b->capacity = static_cast<ub4> (skip);
+
+ break;
+ }
+ default:
+ {
+#if OCI_MAJOR_VERSION < 11 || \
+ (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION < 2)
+ // Assert if a 64 bit integer buffer type is provided and the OCI
+ // version is unable to implicitly convert the NUMBER binary data
+ // to the relevant type.
+ //
+ assert ((b->type != bind::integer &&
+ b->type != bind::uinteger) || b->capacity <= 4);
+#endif
+ value = callback ? 0 : b->buffer;
+ capacity = static_cast<sb4> (b->capacity);
+ size = b->size;
+
+ break;
+ }
+ }
+
+ OCIBind* h (0);
+ r = OCIBindByPos (stmt_,
+ &h,
+ err,
+ i,
+ value,
+ capacity,
+ param_sqlt_lookup[b->type],
+ b->indicator,
+ size,
+ 0,
+ 0,
+ 0,
+ callback ? OCI_DATA_AT_EXEC : OCI_DEFAULT);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+
+ // Set the character set form for national strings.
+ //
+ if (b->type == bind::nstring || b->type == bind::nclob)
+ {
+ ub1 form (SQLCS_NCHAR);
+ r = OCIAttrSet (h,
+ OCI_HTYPE_BIND,
+ &form,
+ 0,
+ OCI_ATTR_CHARSET_FORM,
+ err);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+ }
+
+ if (seen_lob && (b->type == bind::string || b->type == bind::nstring))
+ {
+ // Set the maximum data size for all string types. If this is not set
+ // Oracle server will implicitly calculate this maximum size. If the
+ // calculated size exceeds 4000 bytes (which may occur if a character
+ // set conversion is required) and the string is bound after a LOB
+ // binding, the server will return an ORA-24816 error.
+ //
+ sb4 n (4000);
+ r = OCIAttrSet (h,
+ OCI_HTYPE_BIND,
+ &n,
+ 0,
+ OCI_ATTR_MAXDATA_SIZE,
+ err);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+ }
+
+ if (callback)
+ {
+ r = OCIBindDynamic (
+ h, err, b, &odb_oracle_param_callback_proxy, 0, 0);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+ }
+
+ // Set array information if we have a batch.
+ //
+ if (batch != 1)
+ {
+ ub4 s (static_cast<ub4> (skip));
+
+ r = OCIBindArrayOfStruct (h,
+ err,
+ (value != 0 ? s : 0), // value
+ (b->indicator != 0 ? s : 0), // indicator
+ (size != 0 ? s : 0), // length
+ 0); // return code
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+ }
+ }
+
+ return i;
+ }
+
+ ub4 statement::
+ bind_result (bind* b, size_t c, size_t p)
+ {
+ ODB_POTENTIALLY_UNUSED (p);
+
+ sword r;
+ OCIError* err (conn_.error_handle ());
+ OCIEnv* env (conn_.database ().environment ());
+
+ ub4 i (0);
+ for (bind* end (b + c); b != end; ++b)
+ {
+ if (b->buffer == 0) // Skip NULL entries.
+ continue;
+
+ i++; // Column index is 1-based.
+
+ void* value;
+ sb4 capacity;
+ ub2* size (0);
+
+ switch (b->type)
+ {
+ case bind::timestamp:
+ {
+ datetime* dt (static_cast<datetime*> (b->buffer));
+
+ if (dt->descriptor == 0)
+ {
+ assert ((dt->flags & descriptor_cache) &&
+ (dt->flags & descriptor_free));
+
+ void* d (0);
+ r = OCIDescriptorAlloc (env, &d, OCI_DTYPE_TIMESTAMP, 0, 0);
+
+ if (r != OCI_SUCCESS)
+ throw invalid_oci_handle ();
+
+ dt->descriptor = static_cast<OCIDateTime*> (d);
+ dt->environment = env;
+ dt->error = err;
+ }
+
+ value = &dt->descriptor;
+ capacity = static_cast<sb4> (sizeof (OCIDateTime*));
+
+ break;
+ }
+ case bind::interval_ym:
+ {
+ interval_ym* iym (static_cast<interval_ym*> (b->buffer));
+
+ if (iym->descriptor == 0)
+ {
+ assert ((iym->flags & descriptor_cache) &&
+ (iym->flags & descriptor_free));
+
+ void* d (0);
+ r = OCIDescriptorAlloc (env, &d, OCI_DTYPE_INTERVAL_YM, 0, 0);
+
+ if (r != OCI_SUCCESS)
+ throw invalid_oci_handle ();
+
+ iym->descriptor = static_cast<OCIInterval*> (d);
+ iym->environment = env;
+ iym->error = err;
+ }
+
+ value = &iym->descriptor;
+ capacity = static_cast<sb4> (sizeof (OCIInterval*));
+
+ break;
+ }
+ case bind::interval_ds:
+ {
+ interval_ds* ids (static_cast<interval_ds*> (b->buffer));
+
+ if (ids->descriptor == 0)
+ {
+ assert ((ids->flags & descriptor_cache) &&
+ (ids->flags & descriptor_free));
+
+ void* d (0);
+ r = OCIDescriptorAlloc (env, &d, OCI_DTYPE_INTERVAL_DS, 0, 0);
+
+ if (r != OCI_SUCCESS)
+ throw invalid_oci_handle ();
+
+ ids->descriptor = static_cast<OCIInterval*> (d);
+ ids->environment = env;
+ ids->error = err;
+ }
+
+ value = &ids->descriptor;
+ capacity = static_cast<sb4> (sizeof (OCIInterval*));
+
+ break;
+ }
+ case bind::blob:
+ case bind::clob:
+ case bind::nclob:
+ {
+ lob* l (static_cast<lob*> (b->buffer));
+
+ if (l->locator == 0)
+ {
+ void* d (0);
+ r = OCIDescriptorAlloc (env, &d, OCI_DTYPE_LOB, 0, 0);
+
+ if (r != OCI_SUCCESS)
+ throw invalid_oci_handle ();
+
+ l->locator = static_cast<OCILobLocator*> (d);
+ l->environment = env;
+ l->error = err;
+ }
+
+ value = &l->locator;
+ capacity = static_cast<sb4> (sizeof (OCILobLocator*));
+
+ break;
+ }
+ default:
+ {
+#if OCI_MAJOR_VERSION < 11 || \
+ (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION < 2)
+ // Assert if a 64 bit integer buffer type is provided and the OCI
+ // version is unable to implicitly convert the NUMBER binary data
+ // to the relevant type.
+ //
+ assert ((b->type != bind::integer && b->type != bind::uinteger) ||
+ b->capacity <= 4);
+#endif
+ value = b->buffer;
+ capacity = static_cast<sb4> (b->capacity);
+ size = b->size;
+
+ break;
+ }
+ }
+
+ OCIDefine* h (0);
+ r = OCIDefineByPos (stmt_,
+ &h,
+ err,
+ i,
+ value,
+ capacity,
+ result_sqlt_lookup[b->type],
+ b->indicator,
+ size,
+ 0,
+ OCI_DEFAULT);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+
+ // LOB prefetching is only supported in OCI version 11.1 and greater
+ // and in Oracle server 11.1 and greater. If this code is called
+ // against a pre 11.1 server, the call to OCIAttrSet will return an
+ // error code.
+ //
+#if (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION >= 1) \
+ || OCI_MAJOR_VERSION > 11
+ if (b->type == bind::blob ||
+ b->type == bind::clob ||
+ b->type == bind::nclob)
+ {
+
+ if (p != 0)
+ {
+ ub4 n (static_cast<ub4> (p));
+
+ r = OCIAttrSet (h,
+ OCI_HTYPE_DEFINE,
+ &n,
+ 0,
+ OCI_ATTR_LOBPREFETCH_SIZE,
+ err);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+ }
+ }
+ else
+#endif
+ if (b->type == bind::nstring)
+ {
+ ub1 form (SQLCS_NCHAR);
+
+ r = OCIAttrSet (h,
+ OCI_HTYPE_DEFINE,
+ &form,
+ 0,
+ OCI_ATTR_CHARSET_FORM,
+ err);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+ }
+ }
+
+ return i;
+ }
+
+ void statement::
+ stream_result (bind* b, size_t c, void* obase, void* nbase)
+ {
+ OCIError* err (conn_.error_handle ());
+
+ for (bind* end (b + c); b != end; ++b)
+ {
+ if (b->buffer == 0) // Skip NULL entries.
+ continue;
+
+ // Only stream if the bind specifies a LOB type.
+ //
+ if (b->type == bind::blob ||
+ b->type == bind::clob ||
+ b->type == bind::nclob)
+ {
+ lob* l;
+ sb2* ind;
+ lob_callback* cb;
+
+ if (obase == 0)
+ {
+ l = static_cast<lob*> (b->buffer);
+ ind = b->indicator;
+ cb = b->callback;
+ }
+ else
+ {
+ // Re-base the pointers.
+ //
+ char* ob (static_cast<char*> (obase));
+ char* nb (static_cast<char*> (nbase));
+
+ char* p (static_cast<char*> (b->buffer));
+ assert (ob <= p);
+ l = reinterpret_cast<lob*> (nb + (p - ob));
+
+ if (b->indicator == 0)
+ ind = 0;
+ else
+ {
+ p = reinterpret_cast<char*> (b->indicator);
+ assert (ob <= p);
+ ind = reinterpret_cast<sb2*> (nb + (p - ob));
+ }
+
+ p = reinterpret_cast<char*> (b->callback);
+ assert (ob <= p);
+ cb = reinterpret_cast<lob_callback*> (nb + (p - ob));
+ }
+
+ // Nothing to do if the LOB value is NULL or the result callback
+ // hasn't been provided.
+ //
+ if ((ind != 0 && *ind == -1) || cb->callback.result == 0)
+ continue;
+
+ ub4 position (0); // Position context.
+ ub1 piece (OCI_FIRST_PIECE);
+
+ // Setting the value pointed to by the byte_amt argument to 0 on the
+ // first call to OCILobRead2 instructs OCI to remain in a polling
+ // state until the EOF is reached, at which point OCILobRead2 will
+ // return OCI_SUCCESS.
+ //
+ ub8 read (0);
+ ub1 cs_form (b->type == bind::nclob ? SQLCS_NCHAR : SQLCS_IMPLICIT);
+
+ // Allocate buffer space if necessary.
+ //
+ details::buffer& lob_buffer (conn_.lob_buffer ());
+
+ if (lob_buffer.capacity () == 0)
+ lob_buffer.capacity (4096);
+
+ sword r;
+ do
+ {
+ r = OCILobRead2 (conn_.handle (),
+ err,
+ l->locator,
+ &read,
+ 0,
+ 1,
+ lob_buffer.data (),
+ lob_buffer.capacity (),
+ piece,
+ 0,
+ 0,
+ 0,
+ cs_form);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (conn_, r);
+
+ chunk_position cp;
+
+ if (piece == OCI_FIRST_PIECE)
+ cp = r == OCI_SUCCESS ? chunk_one : chunk_first;
+ else if (r == OCI_NEED_DATA)
+ cp = chunk_next;
+ else
+ cp = chunk_last;
+
+ piece = OCI_NEXT_PIECE;
+
+ // OCI generates and ORA-24343 error when an error code is
+ // returned from a user callback. We simulate this.
+ //
+ if (!(*cb->callback.result) (
+ cb->context.result,
+ &position,
+ lob_buffer.data (),
+ static_cast<ub4> (read),
+ cp))
+ throw database_exception (24343, "user defined callback error");
+
+ } while (r == OCI_NEED_DATA);
+ }
+ }
+ }
+
+ //
+ // bulk_statement
+ //
+
+ bulk_statement::
+ ~bulk_statement () {}
+
+ sword bulk_statement::
+ execute (size_t n, multiple_exceptions* mex, sb4 ignore_code)
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ mex_ = mex;
+
+ OCIError* err (conn_.error_handle ());
+
+ // We use OCI_BATCH_ERRORS for n == 1 in order to get the batch
+ // error reporting even for a single parameter set. This makes
+ // it easier to populate mex since otherwise we would have two
+ // cases to worry about: batch and non-batch (statement fails
+ // as a whole).
+ //
+ sword r (OCIStmtExecute (conn_.handle (),
+ stmt_,
+ err,
+ static_cast<ub4> (n),
+ 0,
+ 0,
+ 0,
+ status_ == 0 ? OCI_DEFAULT : OCI_BATCH_ERRORS));
+
+ // If the statement failed as a whole, assume no parameter sets
+ // were attempted in case of a batch. Otherwise, in the batch
+ // errors mode, all the sets are always attempted (let's hope
+ // this is actually true).
+ //
+ i_ = 0;
+ n_ = (r == OCI_ERROR || r == OCI_INVALID_HANDLE
+ ? (status_ == 0 ? 1 : 0)
+ : n);
+
+ if (mex_ != 0)
+ {
+ mex_->current (i_);
+ mex_->attempted (n_);
+ }
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ {
+ if (mex_ != 0)
+ mex_->fatal (true); // An incomplete batch is always fatal.
+
+ return r;
+ }
+
+ // Initialize the batch status array and extract error information
+ // for failed parameter sets.
+ //
+ if (status_ != 0)
+ {
+ sword r; // Our own return code.
+
+ // Clear the status array.
+ //
+ memset (status_, 0, n * sizeof (status_[0]));
+
+ if (err1_ == 0)
+ {
+ OCIError* e (0);
+ r = OCIHandleAlloc (conn_.database ().environment (),
+ reinterpret_cast<void**> (&e),
+ OCI_HTYPE_ERROR,
+ 0,
+ 0);
+
+ if (r != OCI_SUCCESS)
+ throw invalid_oci_handle ();
+
+ err1_.reset (e);
+ }
+
+ ub4 errors;
+ r = OCIAttrGet (stmt_,
+ OCI_HTYPE_STMT,
+ &errors,
+ 0,
+ OCI_ATTR_NUM_DML_ERRORS,
+ err1_);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err1_, r);
+
+ errors_ = errors;
+
+ if (errors != 0)
+ {
+ auto_handle<OCIError> err2;
+
+ {
+ OCIError* e (0);
+ r = OCIHandleAlloc (conn_.database ().environment (),
+ reinterpret_cast<void**> (&e),
+ OCI_HTYPE_ERROR,
+ 0,
+ 0);
+
+ if (r != OCI_SUCCESS)
+ throw invalid_oci_handle ();
+
+ err2.reset (e);
+ }
+
+ for (ub4 i (0); i != errors; ++i)
+ {
+ {
+ OCIError* tmp (err2);
+ r = OCIParamGet (err, // from
+ OCI_HTYPE_ERROR,
+ err1_, // diagnostics
+ reinterpret_cast<void**> (&tmp), // to
+ i);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err1_, r);
+ }
+
+ ub4 row;
+ r = OCIAttrGet (err2,
+ OCI_HTYPE_ERROR,
+ &row,
+ 0,
+ OCI_ATTR_DML_ROW_OFFSET,
+ err1_);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err1_, r);
+
+ OCIErrorGet (err2, 1, 0, &status_[row], 0, 0, OCI_HTYPE_ERROR);
+
+ if (status_[row] != ignore_code)
+ translate_error (err2, OCI_ERROR, &conn_, row, mex_);
+ }
+ }
+ }
+
+ return r;
+ }
+
+ unsigned long long bulk_statement::
+ affected (bool unique)
+ {
+ unsigned long long rows;
+ {
+ ub4 n (0);
+ OCIError* err (conn_.error_handle ());
+ sword r (OCIAttrGet (stmt_,
+ OCI_HTYPE_STMT,
+ &n,
+ 0,
+ OCI_ATTR_ROW_COUNT,
+ err));
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+
+ rows = static_cast<unsigned long long> (n);
+ }
+
+ if (n_ > 1) // Batch.
+ {
+ if (rows != 0) // Some rows did get affected.
+ {
+ // Subtract the parameter sets that failed since they haven't
+ // affected any rows.
+ //
+ size_t p (n_ - errors_);
+
+ if (p > 1) // True batch.
+ {
+ if (unique) // Each can affect 0 or 1 row.
+ {
+ rows = (p == static_cast<size_t> (rows)
+ ? 1
+ : result_unknown);
+ }
+ else
+ rows = result_unknown;
+ }
+ }
+ }
+
+ return rows;
+ }
+
+ //
+ // generic_statement
+ //
+
+ generic_statement::
+ generic_statement (connection_type& conn, const string& text)
+ : statement (conn,
+ text, statement_generic,
+ 0, false),
+ bound_ (false)
+ {
+ init ();
+ }
+
+ generic_statement::
+ generic_statement (connection_type& conn, const char* text)
+ : statement (conn,
+ text, statement_generic,
+ 0, false),
+ bound_ (false)
+ {
+ init ();
+ }
+
+ void generic_statement::
+ init ()
+ {
+ OCIError* err (conn_.error_handle ());
+
+ sword r (OCIAttrGet (stmt_,
+ OCI_HTYPE_STMT,
+ &stmt_type_,
+ 0,
+ OCI_ATTR_STMT_TYPE,
+ err));
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+ }
+
+ generic_statement::
+ ~generic_statement ()
+ {
+ }
+
+ unsigned long long generic_statement::
+ execute ()
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ sword r (0);
+
+ OCISvcCtx* handle (conn_.handle ());
+ OCIError* err (conn_.error_handle ());
+
+ if (stmt_type_ == OCI_STMT_SELECT)
+ {
+ // Do not prefetch any rows.
+ //
+ r = OCIStmtExecute (handle, stmt_, err, 0, 0, 0, 0, OCI_DEFAULT);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (conn_, r);
+
+ // In order to successfully execute a select statement, OCI/Oracle
+ // requires that there be OCIDefine handles provided for all select
+ // list columns. Since we are not interested in any data returned by
+ // the select statement, all buffer pointers, indicator variable
+ // pointers, and data length pointers are specified as NULL (we still
+ // specify a valid data type identifier; not doing so results in
+ // undefined behavior). This results in truncation errors being
+ // returned for all attempted row fetches. However, cursor behaves
+ // normally allowing us to return the row count for a select
+ // statement. Note also that we only need to do this once.
+ //
+ if (!bound_)
+ {
+ for (ub4 i (1); ; ++i)
+ {
+ auto_descriptor<OCIParam> param;
+ {
+ OCIParam* p (0);
+ r = OCIParamGet (stmt_,
+ OCI_HTYPE_STMT,
+ err,
+ reinterpret_cast<void**> (&p),
+ i);
+
+ if (r == OCI_ERROR) // No more result columns.
+ break;
+
+ param.reset (p);
+ }
+
+ ub2 data_type;
+ r = OCIAttrGet (param,
+ OCI_DTYPE_PARAM,
+ &data_type,
+ 0,
+ OCI_ATTR_DATA_TYPE,
+ err);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+
+ // No need to keep track of the OCIDefine handles - these will
+ // be deallocated with the statement.
+ //
+ OCIDefine* define (0);
+ r = OCIDefineByPos (stmt_,
+ &define,
+ err,
+ i,
+ 0, // NULL value buffer pointer
+ 0, // zero length value buffer
+ data_type,
+ 0, // NULL indicator pointer
+ 0, // NULL length data pointer
+ 0, // NULL column level return code pointer
+ OCI_DEFAULT);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+ }
+
+ bound_ = true;
+ }
+
+ for (;;)
+ {
+ r = OCIStmtFetch2 (stmt_, err, 1, OCI_FETCH_NEXT, 0, OCI_DEFAULT);
+
+ if (r == OCI_NO_DATA)
+ break;
+ else if (r == OCI_ERROR)
+ {
+ sb4 e;
+ r = OCIErrorGet (err, 1, 0, &e, 0, 0, OCI_HTYPE_ERROR);
+
+ // ORA-01406 is returned if there is a truncation error. We expect
+ // and ignore this error.
+ //
+ if (e != 1406)
+ translate_error (conn_, r);
+ }
+ else if (r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+ }
+ }
+ else
+ {
+ // OCIStmtExecute requires a non-zero iters param for DML statements.
+ //
+ r = OCIStmtExecute (handle, stmt_, err, 1, 0, 0, 0, OCI_DEFAULT);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (conn_, r);
+ }
+
+ ub4 row_count (0);
+ r = OCIAttrGet (stmt_,
+ OCI_HTYPE_STMT,
+ &row_count,
+ 0,
+ OCI_ATTR_ROW_COUNT,
+ err);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+
+ return row_count;
+ }
+
+ //
+ // select_statement
+ //
+
+ select_statement::
+ ~select_statement ()
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ bool optimize,
+ binding& param,
+ binding& result,
+ size_t lob_prefetch_size)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ result_ (result),
+ done_ (true)
+ {
+ if (!empty ())
+ {
+ bind_param (param.bind, param.count);
+ result_count_ = bind_result (
+ result.bind, result.count, lob_prefetch_size);
+ }
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ bool optimize,
+ binding& param,
+ binding& result,
+ size_t lob_prefetch_size)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ result_ (result),
+ done_ (true)
+ {
+ if (!empty ())
+ {
+ bind_param (param.bind, param.count);
+ result_count_ = bind_result (
+ result.bind, result.count, lob_prefetch_size);
+ }
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ bool optimize,
+ binding& result,
+ size_t lob_prefetch_size)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ result_ (result),
+ done_ (true)
+ {
+ if (!empty ())
+ {
+ result_count_ = bind_result (
+ result.bind, result.count, lob_prefetch_size);
+ }
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ bool optimize,
+ binding& result,
+ size_t lob_prefetch_size)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ result_ (result),
+ done_ (true)
+ {
+ if (!empty ())
+ {
+ result_count_ = bind_result (
+ result.bind, result.count, lob_prefetch_size);
+ }
+ }
+
+ void select_statement::
+ execute ()
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ OCIError* err (conn_.error_handle ());
+
+ // @@ Retrieve a single row into the already bound output buffers as an
+ // optimization? This will avoid multiple server round-trips in the case
+ // of a single object load.
+ //
+ sword r (OCIStmtExecute (conn_.handle (),
+ stmt_,
+ err,
+ 0,
+ 0,
+ 0,
+ 0,
+ OCI_DEFAULT));
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (conn_, r);
+
+ done_ = r == OCI_NO_DATA;
+
+#ifndef NDEBUG
+ ub4 n (0);
+ r = OCIAttrGet (stmt_, OCI_HTYPE_STMT, &n, 0, OCI_ATTR_PARAM_COUNT, err);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+
+ // Make sure that the number of columns in the result returned by
+ // the database matches the number that we expect. A common cause
+ // of this assertion is a native view with a number of data members
+ // not matching the number of columns in the SELECT-list.
+ //
+ assert (n == result_count_);
+#endif
+ }
+
+ select_statement::result select_statement::
+ fetch ()
+ {
+ if (!done_)
+ {
+ change_callback* cc (result_.change_callback);
+
+ if (cc != 0 && cc->callback != 0)
+ (cc->callback) (cc->context);
+
+ sword r (OCIStmtFetch2 (stmt_,
+ conn_.error_handle (),
+ 1,
+ OCI_FETCH_NEXT,
+ 0,
+ OCI_DEFAULT));
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (conn_, r);
+ else if (r == OCI_NO_DATA)
+ done_ = true;
+ }
+
+ return done_ ? no_data : success;
+ }
+
+ void select_statement::
+ free_result ()
+ {
+ if (!done_)
+ {
+ sword r (OCIStmtFetch2 (stmt_,
+ conn_.error_handle (),
+ 0,
+ OCI_FETCH_NEXT,
+ 0,
+ OCI_DEFAULT));
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (conn_, r);
+
+ done_ = true;
+ }
+ }
+
+ //
+ // insert_statement
+ //
+
+ extern "C" sb4
+ odb_oracle_returning_in (void* context,
+ OCIBind*, // bind
+ ub4 it, // iter
+ ub4, // index
+ void** buffer,
+ ub4* size,
+ ub1* piece,
+ void** indicator)
+ {
+ binding& ret (*static_cast<insert_statement*> (context)->ret_);
+
+ // Offset the data based on the current iteration and skip size.
+ // The id is always first.
+ //
+ *buffer = 0;
+ *size = 0;
+ *piece = OCI_ONE_PIECE;
+
+ sb2* ind (offset (ret.bind[0].indicator, it, ret.skip));
+ *ind = -1;
+ *indicator = ind;
+
+ return OCI_CONTINUE;
+ }
+
+ extern "C" sb4
+ odb_oracle_returning_out (void* context,
+ OCIBind*, // bind
+ ub4 it, // iter
+ ub4, // index
+ void** buffer,
+ ub4** size,
+ ub1* piece,
+ void** indicator,
+ ub2** rcode)
+ {
+ insert_statement& st (*static_cast<insert_statement*> (context));
+ bind& b (st.ret_->bind[0]); // The id is always first.
+ size_t skip (st.ret_->skip);
+
+ // Offset the data based on the current iteration and skip size.
+ //
+ *buffer = offset (b.buffer, it, skip);
+
+ if (b.type == bind::number)
+ {
+ // So the straightforward way to handle this would have been to
+ // set size to the properly offset pointer to b.size, just like
+ // we do for the buffer and indicator. The problem is that in
+ // OCI size is ub2 everywhere except in the *Dynamic() callbacks.
+ // Here it is expected to be ub4 and, as a result, we cannot use
+ // our ub2 size that we use throughout (I know you are tempted
+ // to just cast ub2* to ub4* and forget about this mess, but,
+ // trust me, this won't end up well).
+ //
+ // So what we will do instead is this: have a temporary ub4 buffer
+ // that we return to OCI so that it can store the size for us. But
+ // the callback can be called multiple times (batch operations) so
+ // on each subsequent call we will have to save the size from the
+ // previous call into our ub2 array. We will also have to handle
+ // the last extracted size after OCIStmtExecute() below. Thanks,
+ // Oracle!
+ //
+ if (st.ret_prev_ != 0)
+ *st.ret_prev_ = static_cast<ub2> (st.ret_size_);
+
+ st.ret_prev_ = offset (b.size, it, skip);
+ *size = &st.ret_size_;
+ }
+
+ // For some reason we have to set the out size to the (presumably)
+ // maximum buffer size.
+ //
+ **size = b.capacity;
+
+ *indicator = offset (b.indicator, it, skip);
+ *rcode = 0;
+ *piece = OCI_ONE_PIECE;
+
+ return OCI_CONTINUE;
+ }
+
+ insert_statement::
+ ~insert_statement ()
+ {
+ }
+
+ insert_statement::
+ insert_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ binding& param,
+ binding* returning)
+ : bulk_statement (conn,
+ text, statement_insert,
+ (process ? &param : 0), false,
+ param.batch, param.status),
+ ret_ (returning)
+ {
+ init (param);
+ }
+
+ insert_statement::
+ insert_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ binding& param,
+ binding* returning)
+ : bulk_statement (conn,
+ text, statement_insert,
+ (process ? &param : 0), false,
+ param.batch, param.status),
+ ret_ (returning)
+ {
+ init (param);
+ }
+
+ void insert_statement::
+ init (binding& param)
+ {
+ ub4 param_count (bind_param (param.bind, param.count,
+ param.batch, param.skip));
+ if (ret_ != 0)
+ {
+ OCIError* err (conn_.error_handle ());
+ OCIBind* h (0);
+
+ bind* b (ret_->bind);
+
+#if OCI_MAJOR_VERSION < 11 || \
+ (OCI_MAJOR_VERSION == 11 && OCI_MINOR_VERSION < 2)
+ // Assert if a 64 bit integer buffer type is provided and the OCI
+ // version is unable to implicitly convert the NUMBER binary data
+ // to the relevant type.
+ //
+ assert ((b->type != bind::integer && b->type != bind::uinteger) ||
+ b->capacity <= 4);
+#endif
+ sword r (OCIBindByPos (stmt_,
+ &h,
+ err,
+ param_count + 1,
+ 0,
+ b->capacity,
+ param_sqlt_lookup[b->type],
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ OCI_DATA_AT_EXEC));
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+
+ r = OCIBindDynamic (h,
+ err,
+ this,
+ &odb_oracle_returning_in,
+ this,
+ &odb_oracle_returning_out);
+
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ translate_error (err, r);
+ }
+ }
+
+ size_t insert_statement::
+ execute (size_t n, multiple_exceptions* mex)
+ {
+ OCIError* err (conn_.error_handle ());
+
+ if (ret_ != 0)
+ ret_prev_ = 0;
+
+ // Ignore ORA-00001 error code, see fetch() below for details.
+ //
+ sword r (bulk_statement::execute (n, mex, (ret_ == 0 ? 1 : 0)));
+
+ // Statement failed as a whole, assume no parameter sets were
+ // attempted in case of a batch.
+ //
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ {
+ sb4 e;
+ OCIErrorGet (err, 1, 0, &e, 0, 0, OCI_HTYPE_ERROR);
+ fetch (r, e);
+
+ if (result_) // If fetch() hasn't translated the error.
+ translate_error (err, r, &conn_, 0, mex_); // Can return.
+
+ return n_;
+ }
+
+ // Store the last returned id size (see odb_oracle_returning_out()
+ // for details).
+ //
+ if (ret_ != 0 && ret_prev_ != 0)
+ *ret_prev_ = static_cast<ub2> (ret_size_);
+
+ if (status_ == 0) // Non-batch mode.
+ fetch (OCI_SUCCESS, 0);
+ else
+ {
+ fetch (status_[i_] == 0 ? OCI_SUCCESS : OCI_ERROR, status_[i_]);
+ }
+
+ return n_;
+ }
+
+ //
+ // update_statement
+ //
+
+ update_statement::
+ ~update_statement ()
+ {
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ binding& param)
+ : bulk_statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false,
+ param.batch, param.status),
+ unique_ (false)
+ {
+ assert (param.batch == 1); // Specify unique_hint explicitly.
+
+ if (!empty ())
+ bind_param (param.bind, param.count, param.batch, param.skip);
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const string& text,
+ bool unique,
+ bool process,
+ binding& param)
+ : bulk_statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false,
+ param.batch, param.status),
+ unique_ (unique)
+ {
+ if (!empty ())
+ bind_param (param.bind, param.count, param.batch, param.skip);
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ binding& param)
+ : bulk_statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false,
+ param.batch, param.status),
+ unique_ (false)
+ {
+ assert (param.batch == 1); // Specify unique_hint explicitly.
+
+ if (!empty ())
+ bind_param (param.bind, param.count, param.batch, param.skip);
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const char* text,
+ bool unique,
+ bool process,
+ binding& param)
+ : bulk_statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false,
+ param.batch, param.status),
+ unique_ (unique)
+ {
+ if (!empty ())
+ bind_param (param.bind, param.count, param.batch, param.skip);
+ }
+
+ size_t update_statement::
+ execute (size_t n, multiple_exceptions* mex)
+ {
+ OCIError* err (conn_.error_handle ());
+ sword r (bulk_statement::execute (n, mex));
+
+ // Statement failed as a whole, assume no parameter sets were
+ // attempted in case of a batch.
+ //
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ {
+ translate_error (err, r, &conn_, 0, mex_); // Can return.
+ return n_;
+ }
+
+ // Figure out the affected (matched, not necessarily updated)
+ // row count.
+ //
+ result_ = affected (unique_);
+
+ return n_;
+ }
+
+ //
+ // delete_statement
+ //
+
+ delete_statement::
+ ~delete_statement ()
+ {
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const string& text,
+ binding& param)
+ : bulk_statement (conn,
+ text, statement_delete,
+ 0, false,
+ param.batch, param.status),
+ unique_ (false)
+ {
+ assert (param.batch == 1); // Specify unique_hint explicitly.
+ bind_param (param.bind, param.count, param.batch, param.skip);
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const string& text,
+ bool unique,
+ binding& param)
+ : bulk_statement (conn,
+ text, statement_delete,
+ 0, false,
+ param.batch, param.status),
+ unique_ (unique)
+ {
+ bind_param (param.bind, param.count, param.batch, param.skip);
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const char* text,
+ binding& param)
+ : bulk_statement (conn,
+ text, statement_delete,
+ 0, false,
+ param.batch, param.status),
+ unique_ (false)
+ {
+ assert (param.batch == 1); // Specify unique_hint explicitly.
+ bind_param (param.bind, param.count, param.batch, param.skip);
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const char* text,
+ bool unique,
+ binding& param)
+ : bulk_statement (conn,
+ text, statement_delete,
+ 0, false,
+ param.batch, param.status),
+ unique_ (unique)
+ {
+ bind_param (param.bind, param.count, param.batch, param.skip);
+ }
+
+ size_t delete_statement::
+ execute (size_t n, multiple_exceptions* mex)
+ {
+ sword r (bulk_statement::execute (n, mex));
+ OCIError* err (conn_.error_handle ());
+
+ // Statement failed as a whole, assume no parameter sets were
+ // attempted in case of a batch.
+ //
+ if (r == OCI_ERROR || r == OCI_INVALID_HANDLE)
+ {
+ translate_error (err, r, &conn_, 0, mex_); // Can return.
+ return n_;
+ }
+
+ // Figure out the affected row count.
+ //
+ result_ = affected (unique_);
+
+ return n_;
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/statement.hxx b/libodb-oracle/odb/oracle/statement.hxx
new file mode 100644
index 0000000..d435286
--- /dev/null
+++ b/libodb-oracle/odb/oracle/statement.hxx
@@ -0,0 +1,538 @@
+// file : odb/oracle/statement.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_STATEMENT_HXX
+#define ODB_ORACLE_STATEMENT_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+
+#include <odb/statement.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx>
+#include <odb/oracle/binding.hxx>
+#include <odb/oracle/connection.hxx>
+#include <odb/oracle/oracle-fwd.hxx>
+#include <odb/oracle/auto-handle.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ class LIBODB_ORACLE_EXPORT statement: public odb::statement
+ {
+ public:
+ typedef oracle::connection connection_type;
+
+ virtual
+ ~statement () = 0;
+
+ OCIStmt*
+ handle () const
+ {
+ return stmt_;
+ }
+
+ virtual const char*
+ text () const;
+
+ virtual connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // A statement can be empty. This is used to handle situations
+ // where a SELECT or UPDATE statement ends up not having any
+ // columns after processing. An empty statement cannot be
+ // executed.
+ //
+ bool
+ empty () const
+ {
+ return stmt_ == 0;
+ }
+
+ protected:
+ // We keep two versions to take advantage of std::string COW.
+ //
+ statement (connection_type&,
+ const std::string& text,
+ statement_kind,
+ const binding* process,
+ bool optimize);
+
+ statement (connection_type&,
+ const char* text,
+ statement_kind,
+ const binding* process,
+ bool optimize);
+
+ private:
+ void
+ init (const char* text,
+ std::size_t text_size,
+ statement_kind,
+ const binding* process,
+ bool optimize);
+
+ protected:
+ struct unbind
+ {
+ oracle::bind::buffer_type type; // Bind type.
+ oracle::bind* bind; // Corresponding bind entry.
+ void* value; // Actual value passed to OCIBindByPos.
+ };
+
+ // Bind parameters for this statement. This function must only
+ // be called once. Multiple calls to it will result in memory
+ // leaks due to lost OCIBind resources. Return the actual number
+ // of columns bound.
+ //
+ ub4
+ bind_param (bind*, std::size_t count,
+ size_t batch = 1, std::size_t skip = 0);
+
+ // Bind results for this statement. This function must only be
+ // called once. Multiple calls to it will result in memory leaks
+ // due to lost OCIDefine resources. Return the actual number of
+ // columns bound.
+ //
+ ub4
+ bind_result (bind*,
+ std::size_t count,
+ std::size_t lob_prefetch_size = 0);
+
+ // Stream the result LOBs, calling user callbacks where necessary.
+ // The old_base and new_base arguments can be used to "re-base" the
+ // lob_callback struct pointer (stored in bind::callback), the lob
+ // struct pointer (stored in bind::buffer), and the indicator value
+ // pointer (stored in bind::indicator). This is used by the query
+ // machinery to cause stream_result() to use the callback information
+ // from a copy of the image instead of the bound image.
+ //
+ void
+ stream_result (bind*,
+ std::size_t count,
+ void* old_base = 0,
+ void* new_base = 0);
+
+ protected:
+ connection_type& conn_;
+ auto_handle<OCIStmt> stmt_;
+
+ unbind* udata_;
+ std::size_t usize_;
+ };
+
+ class LIBODB_ORACLE_EXPORT bulk_statement: public statement
+ {
+ public:
+ virtual
+ ~bulk_statement () = 0;
+
+ protected:
+ bulk_statement (connection_type&,
+ const std::string& text,
+ statement_kind,
+ const binding* process,
+ bool optimize,
+ std::size_t batch,
+ sb4* status);
+
+ bulk_statement (connection_type&,
+ const char* text,
+ statement_kind,
+ const binding* process,
+ bool optimize,
+ std::size_t batch,
+ sb4* status);
+
+ // Call OCIStmtExecute() and set up the batch tracking variables (see
+ // below). The ignore_code argument specifies optional error code that
+ // should not be treated as an error.
+ //
+ sword
+ execute (std::size_t n, multiple_exceptions*, sb4 ignore_code = 0);
+
+ static const unsigned long long result_unknown = ~0ULL;
+
+ unsigned long long
+ affected (bool unique);
+
+ protected:
+ auto_handle<OCIError> err1_;
+ sb4* status_; // Parameter sets status array.
+ std::size_t n_; // Actual batch size.
+ std::size_t i_; // Position in result.
+ std::size_t errors_; // Number of parameter sets that failed.
+ multiple_exceptions* mex_;
+ };
+
+ class LIBODB_ORACLE_EXPORT generic_statement: public statement
+ {
+ public:
+ virtual
+ ~generic_statement ();
+
+ generic_statement (connection_type&, const std::string& text);
+ generic_statement (connection_type&, const char* text);
+
+ unsigned long long
+ execute ();
+
+ private:
+ generic_statement (const generic_statement&);
+ generic_statement& operator= (const generic_statement&);
+
+ private:
+ void
+ init ();
+
+ private:
+ ub2 stmt_type_;
+ bool bound_;
+ };
+
+ class LIBODB_ORACLE_EXPORT select_statement: public statement
+ {
+ public:
+ virtual
+ ~select_statement ();
+
+ select_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ binding& param,
+ binding& result,
+ std::size_t lob_prefetch_size = 0);
+
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ bool optimize_text,
+ binding& param,
+ binding& result,
+ std::size_t lob_prefetch_size = 0);
+
+ select_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ binding& result,
+ std::size_t lob_prefetch_size = 0);
+
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ bool optimize_text,
+ binding& result,
+ std::size_t lob_prefetch_size = 0);
+
+ enum result
+ {
+ success,
+ no_data
+ };
+
+ void
+ execute ();
+
+ result
+ fetch ();
+
+ void
+ stream_result (void* old_base = 0, void* new_base = 0)
+ {
+ statement::stream_result (result_.bind,
+ result_.count,
+ old_base,
+ new_base);
+ }
+
+ void
+ free_result ();
+
+ private:
+ select_statement (const select_statement&);
+ select_statement& operator= (const select_statement&);
+
+ private:
+ binding& result_;
+ ub4 result_count_; // Actual number of bound columns.
+ bool done_;
+ };
+
+ struct LIBODB_ORACLE_EXPORT auto_result
+ {
+ explicit auto_result (select_statement& s): s_ (s) {}
+ ~auto_result () {s_.free_result ();}
+
+ private:
+ auto_result (const auto_result&);
+ auto_result& operator= (const auto_result&);
+
+ private:
+ select_statement& s_;
+ };
+
+ class LIBODB_ORACLE_EXPORT insert_statement: public bulk_statement
+ {
+ public:
+ virtual
+ ~insert_statement ();
+
+ insert_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ binding& param,
+ binding* returning);
+
+ insert_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ binding& param,
+ binding* returning);
+
+ // Return the number of parameter sets (out of n) that were attempted.
+ //
+ std::size_t
+ execute (std::size_t n, multiple_exceptions& mex)
+ {
+ return execute (n, &mex);
+ }
+
+ // Return true if successful and false if this row is a duplicate.
+ // All other errors are reported via exceptions.
+ //
+ bool
+ result (std::size_t i)
+ {
+ // Get to the next parameter set if necessary.
+ //
+ if (i != i_)
+ {
+ mex_->current (++i_); // mex cannot be NULL since this is a batch.
+ fetch (status_[i_] == 0 ? 0 /*OCI_SUCCESS*/ : -1 /*OCI_ERROR*/,
+ status_[i_]);
+ }
+
+ return result_;
+ }
+
+ bool
+ execute ()
+ {
+ execute (1, 0);
+ return result (0);
+ }
+
+ private:
+ insert_statement (const insert_statement&);
+ insert_statement& operator= (const insert_statement&);
+
+ private:
+ void
+ init (binding& param);
+
+ std::size_t
+ execute (std::size_t, multiple_exceptions*);
+
+ void
+ fetch (sword r, sb4 code);
+
+ public: // For odb_oracle_returning_*().
+ binding* ret_;
+ ub4 ret_size_; // You don't want to know (see statement.cxx).
+ ub2* ret_prev_;
+
+ private:
+ bool result_;
+ };
+
+ class LIBODB_ORACLE_EXPORT update_statement: public bulk_statement
+ {
+ public:
+ virtual
+ ~update_statement ();
+
+ // OCI does not expose individual affected row counts for batch
+ // operations. Instead, it adds them all up and returns a single
+ // count. This is bad news for us.
+ //
+ // In case of updating by primary key (the affected row count is
+ // either 1 or 0), we can recognize the presumably successful case
+ // where the total affected row count is equal to the batch size
+ // (we can also recognize the "all unsuccessful" case where the
+ // total affected row count is 0). The unique_hint argument in the
+ // constructors below indicates whether this is a "0 or 1" UPDATE
+ // statement.
+ //
+ // In all other situations (provided this is a batch), the result()
+ // function below returns the special result_unknown value.
+ //
+ update_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ binding& param);
+
+ update_statement (connection_type& conn,
+ const std::string& text,
+ bool unique_hint,
+ bool process_text,
+ binding& param);
+
+ update_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ binding& param);
+
+ update_statement (connection_type& conn,
+ const char* text,
+ bool unique_hint,
+ bool process_text,
+ binding& param);
+
+ // Return the number of parameter sets (out of n) that were attempted.
+ //
+ std::size_t
+ execute (std::size_t n, multiple_exceptions& mex)
+ {
+ return execute (n, &mex);
+ }
+
+ // Return the number of rows affected (updated) by the parameter
+ // set. If this is a batch (n > 1 in execute() call above) and it
+ // is impossible to determine the affected row count for each
+ // parameter set, then this function returns result_unknown. All
+ // other errors are reported by throwing exceptions.
+ //
+ using bulk_statement::result_unknown;
+
+ unsigned long long
+ result (std::size_t i)
+ {
+ if (i != i_)
+ mex_->current (++i_); // mex cannot be NULL since this is a batch.
+
+ return result_;
+ }
+
+ unsigned long long
+ execute ()
+ {
+ execute (1, 0);
+ return result (0);
+ }
+
+ private:
+ update_statement (const update_statement&);
+ update_statement& operator= (const update_statement&);
+
+ private:
+ std::size_t
+ execute (std::size_t, multiple_exceptions*);
+
+ private:
+ bool unique_;
+ unsigned long long result_;
+ };
+
+ class LIBODB_ORACLE_EXPORT delete_statement: public bulk_statement
+ {
+ public:
+ virtual
+ ~delete_statement ();
+
+ // OCI does not expose individual affected row counts for batch
+ // operations. Instead, it adds them all up and returns a single
+ // count. This is bad news for us.
+ //
+ // In case of deleting by primary key (the affected row count is
+ // either 1 or 0), we can recognize the presumably successful case
+ // where the total affected row count is equal to the batch size
+ // (we can also recognize the "all unsuccessful" case where the
+ // total affected row count is 0). The unique_hint argument in the
+ // constructors below indicates whether this is a "0 or 1" DELETE
+ // statement.
+ //
+ // In all other situations (provided this is a batch), the result()
+ // function below returns the special result_unknown value.
+ //
+ delete_statement (connection_type& conn,
+ const std::string& text,
+ binding& param);
+
+ delete_statement (connection_type& conn,
+ const std::string& text,
+ bool unique_hint,
+ binding& param);
+
+ delete_statement (connection_type& conn,
+ const char* text,
+ binding& param);
+
+ delete_statement (connection_type& conn,
+ const char* text,
+ bool unique_hint,
+ binding& param);
+
+ // Return the number of parameter sets (out of n) that were attempted.
+ //
+ std::size_t
+ execute (std::size_t n, multiple_exceptions& mex)
+ {
+ return execute (n, &mex);
+ }
+
+ // Return the number of rows affected (deleted) by the parameter
+ // set. If this is a batch (n > 1 in execute() call above) and it
+ // is impossible to determine the affected row count for each
+ // parameter set, then this function returns result_unknown. All
+ // other errors are reported by throwing exceptions.
+ //
+ using bulk_statement::result_unknown;
+
+ unsigned long long
+ result (std::size_t i)
+ {
+ if (i != i_)
+ mex_->current (++i_); // mex cannot be NULL since this is a batch.
+
+ return result_;
+ }
+
+ unsigned long long
+ execute ()
+ {
+ execute (1, 0);
+ return result (0);
+ }
+
+ private:
+ delete_statement (const delete_statement&);
+ delete_statement& operator= (const delete_statement&);
+
+ private:
+ std::size_t
+ execute (std::size_t, multiple_exceptions*);
+
+ private:
+ bool unique_;
+ unsigned long long result_;
+ };
+ }
+}
+
+#include <odb/oracle/statement.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_STATEMENT_HXX
diff --git a/libodb-oracle/odb/oracle/statement.ixx b/libodb-oracle/odb/oracle/statement.ixx
new file mode 100644
index 0000000..ef0fa64
--- /dev/null
+++ b/libodb-oracle/odb/oracle/statement.ixx
@@ -0,0 +1,62 @@
+// file : odb/oracle/statement.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+using namespace std;
+
+namespace odb
+{
+ namespace oracle
+ {
+ // bulk_statement
+ //
+ inline bulk_statement::
+ bulk_statement (connection_type& c,
+ const std::string& text,
+ statement_kind k,
+ const binding* process,
+ bool optimize,
+ std::size_t batch,
+ sb4* status)
+ : statement (c, text, k, process, optimize),
+ status_ (batch == 1 ? 0 : status)
+ {
+ }
+
+ inline bulk_statement::
+ bulk_statement (connection_type& c,
+ const char* text,
+ statement_kind k,
+ const binding* process,
+ bool optimize,
+ std::size_t batch,
+ sb4* status)
+ : statement (c, text, k, process, optimize),
+ status_ (batch == 1 ? 0 : status)
+ {
+ }
+
+ // insert_statement
+ //
+ inline void insert_statement::
+ fetch (sword r, sb4 code)
+ {
+ result_ = true;
+
+ if (r != 0 /*OCI_SUCCESS*/)
+ {
+ // An auto-assigned object id should never cause a duplicate primary
+ // key.
+ //
+ if (ret_ == 0)
+ {
+ // The Oracle error code ORA-00001 indicates unique constraint
+ // violation, which covers more than just a duplicate primary key.
+ // Unfortunately, there is nothing more precise that we can use.
+ //
+ if (code == 1)
+ result_ = false;
+ }
+ }
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/statements-base.cxx b/libodb-oracle/odb/oracle/statements-base.cxx
new file mode 100644
index 0000000..e25d851
--- /dev/null
+++ b/libodb-oracle/odb/oracle/statements-base.cxx
@@ -0,0 +1,15 @@
+// file : odb/oracle/statements-base.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/oracle/statements-base.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ statements_base::
+ ~statements_base ()
+ {
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/statements-base.hxx b/libodb-oracle/odb/oracle/statements-base.hxx
new file mode 100644
index 0000000..61b34ae
--- /dev/null
+++ b/libodb-oracle/odb/oracle/statements-base.hxx
@@ -0,0 +1,63 @@
+// file : odb/oracle/statements-base.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_STATEMENTS_BASE_HXX
+#define ODB_ORACLE_STATEMENTS_BASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/schema-version.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/connection.hxx>
+#include <odb/oracle/database.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ class LIBODB_ORACLE_EXPORT statements_base: public details::shared_base
+ {
+ public:
+ typedef oracle::connection connection_type;
+
+ connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // Schema version. database::schema_version_migration() is thread-
+ // safe which means it is also slow. Cache the result in statements
+ // so we can avoid the mutex lock. This is thread-safe since if the
+ // version is updated, then the statements cache will be expired.
+ //
+ const schema_version_migration&
+ version_migration (const char* name = "") const
+ {
+ if (svm_ == 0)
+ svm_ = &conn_.database ().schema_version_migration (name);
+
+ return *svm_;
+ }
+
+ public:
+ virtual
+ ~statements_base ();
+
+ protected:
+ statements_base (connection_type& conn): conn_ (conn), svm_ (0) {}
+
+ protected:
+ connection_type& conn_;
+ mutable const schema_version_migration* svm_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_STATEMENTS_BASE_HXX
diff --git a/libodb-oracle/odb/oracle/tracer.cxx b/libodb-oracle/odb/oracle/tracer.cxx
new file mode 100644
index 0000000..ca56d05
--- /dev/null
+++ b/libodb-oracle/odb/oracle/tracer.cxx
@@ -0,0 +1,60 @@
+// file : odb/oracle/tracer.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/oracle/tracer.hxx>
+#include <odb/oracle/connection.hxx>
+#include <odb/oracle/statement.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ tracer::
+ ~tracer ()
+ {
+ }
+
+ void tracer::
+ prepare (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ execute (connection& c, const statement& s)
+ {
+ execute (c, s.text ());
+ }
+
+ void tracer::
+ deallocate (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ prepare (odb::connection& c, const odb::statement& s)
+ {
+ prepare (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const odb::statement& s)
+ {
+ execute (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const char* s)
+ {
+ execute (static_cast<connection&> (c), s);
+ }
+
+ void tracer::
+ deallocate (odb::connection& c, const odb::statement& s)
+ {
+ deallocate (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/tracer.hxx b/libodb-oracle/odb/oracle/tracer.hxx
new file mode 100644
index 0000000..0627926
--- /dev/null
+++ b/libodb-oracle/odb/oracle/tracer.hxx
@@ -0,0 +1,61 @@
+// file : odb/oracle/tracer.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_TRACER_HXX
+#define ODB_ORACLE_TRACER_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/tracer.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx>
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ class LIBODB_ORACLE_EXPORT tracer: private odb::tracer
+ {
+ public:
+ virtual
+ ~tracer ();
+
+ 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&);
+
+ private:
+ // Allow these classes to convert oracle::tracer to odb::tracer.
+ //
+ friend class database;
+ friend class connection;
+ friend class transaction;
+
+ virtual void
+ prepare (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const char* statement);
+
+ virtual void
+ deallocate (odb::connection&, const odb::statement&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_TRACER_HXX
diff --git a/libodb-oracle/odb/oracle/traits-calls.hxx b/libodb-oracle/odb/oracle/traits-calls.hxx
new file mode 100644
index 0000000..7b9d6f3
--- /dev/null
+++ b/libodb-oracle/odb/oracle/traits-calls.hxx
@@ -0,0 +1,190 @@
+// file : odb/oracle/traits-calls.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_TRAITS_CALLS_HXX
+#define ODB_ORACLE_TRAITS_CALLS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/oracle/forward.hxx>
+#include <odb/oracle/oracle-types.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ //
+ // object_traits_calls
+ //
+
+ template <typename T,
+ bool versioned = object_traits_impl<T, id_oracle>::versioned>
+ struct object_traits_calls;
+
+ template <typename T>
+ struct object_traits_calls<T, false>
+ {
+ typedef object_traits_impl<T, id_oracle> traits;
+ typedef typename traits::image_type image_type;
+ typedef oracle::bind bind_type;
+
+ object_traits_calls (const schema_version_migration*) {}
+
+ const schema_version_migration*
+ version () const {return 0;}
+
+ static void
+ bind (bind_type* b, image_type& i, statement_kind sk)
+ {
+ traits::bind (b, i, sk);
+ }
+
+ // Poly-derived version.
+ //
+ static void
+ bind (bind_type* b,
+ const bind_type* id, std::size_t id_size,
+ image_type& i,
+ statement_kind sk)
+ {
+ traits::bind (b, id, id_size, i, sk);
+ }
+
+ static void
+ init (T& o, const image_type& i, odb::database* db)
+ {
+ traits::init (o, i, db);
+ }
+
+ static bool
+ find_ (typename traits::statements_type& sts,
+ const typename traits::id_type* id)
+ {
+ return traits::find_ (sts, id);
+ }
+
+ static void
+ load_ (typename traits::statements_type& sts, T& o, bool reload)
+ {
+ return traits::load_ (sts, o, reload);
+ }
+ };
+
+ template <typename T>
+ struct object_traits_calls<T, true>
+ {
+ typedef object_traits_impl<T, id_oracle> traits;
+ typedef typename traits::image_type image_type;
+ typedef oracle::bind bind_type;
+
+ object_traits_calls (const schema_version_migration* svm): svm_ (*svm) {}
+
+ const schema_version_migration*
+ version () const {return &svm_;}
+
+ void
+ bind (bind_type* b, image_type& i, statement_kind sk) const
+ {
+ traits::bind (b, i, sk, svm_);
+ }
+
+ // Poly-derived version.
+ //
+ void
+ bind (bind_type* b,
+ const bind_type* id, std::size_t id_size,
+ image_type& i,
+ statement_kind sk) const
+ {
+ traits::bind (b, id, id_size, i, sk, svm_);
+ }
+
+ void
+ init (T& o, const image_type& i, odb::database* db) const
+ {
+ traits::init (o, i, db, svm_);
+ }
+
+ bool
+ find_ (typename traits::statements_type& sts,
+ const typename traits::id_type* id) const
+ {
+ return traits::find_ (sts, id, svm_);
+ }
+
+ void
+ load_ (typename traits::statements_type& sts, T& o, bool reload) const
+ {
+ return traits::load_ (sts, o, reload, svm_);
+ }
+
+ private:
+ const schema_version_migration& svm_;
+ };
+
+ //
+ // view_traits_calls
+ //
+
+ template <typename T,
+ bool versioned = view_traits_impl<T, id_oracle>::versioned>
+ struct view_traits_calls;
+
+ template <typename T>
+ struct view_traits_calls<T, false>
+ {
+ typedef view_traits_impl<T, id_oracle> traits;
+ typedef typename traits::image_type image_type;
+ typedef oracle::bind bind_type;
+
+ view_traits_calls (const schema_version_migration*) {}
+
+ static void
+ bind (bind_type* b, image_type& i)
+ {
+ traits::bind (b, i);
+ }
+
+ static void
+ init (T& o, const image_type& i, odb::database* db)
+ {
+ traits::init (o, i, db);
+ }
+ };
+
+ template <typename T>
+ struct view_traits_calls<T, true>
+ {
+ typedef view_traits_impl<T, id_oracle> traits;
+ typedef typename traits::image_type image_type;
+ typedef oracle::bind bind_type;
+
+ view_traits_calls (const schema_version_migration* svm): svm_ (*svm) {}
+
+ void
+ bind (bind_type* b, image_type& i) const
+ {
+ traits::bind (b, i, svm_);
+ }
+
+ void
+ init (T& o, const image_type& i, odb::database* db) const
+ {
+ traits::init (o, i, db, svm_);
+ }
+
+ private:
+ const schema_version_migration& svm_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_TRAITS_CALLS_HXX
diff --git a/libodb-oracle/odb/oracle/traits.cxx b/libodb-oracle/odb/oracle/traits.cxx
new file mode 100644
index 0000000..6c7b46e
--- /dev/null
+++ b/libodb-oracle/odb/oracle/traits.cxx
@@ -0,0 +1,201 @@
+// file : odb/oracle/traits.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/oracle/traits.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace oracle
+ {
+ //
+ // c_array_value_traits_base
+ //
+ void c_array_value_traits_base::
+ set_value (char* const& v,
+ const char* b,
+ size_t n,
+ bool is_null,
+ size_t N)
+ {
+ if (!is_null)
+ {
+ n = n < N ? n : N;
+
+ if (n != 0)
+ memcpy (v, b, n);
+ }
+ else
+ n = 0;
+
+ if (n != N) // Append '\0' if there is space.
+ v[n] = '\0';
+ }
+
+ void c_array_value_traits_base::
+ set_image (char* b,
+ size_t c,
+ size_t& n,
+ bool& is_null,
+ const char* v,
+ size_t N)
+ {
+ is_null = false;
+
+ // Figure out the length. We cannot use strlen since it may
+ // not be 0-terminated (strnlen is not standard).
+ //
+ for (n = 0; n != N && v[n] != '\0'; ++n) ;
+
+ if (n > c)
+ n = c;
+
+ if (n != 0)
+ memcpy (b, v, n);
+ }
+
+ //
+ // string_lob_value_traits
+ //
+ bool string_lob_value_traits::
+ result_callback (void* c, ub4*, void* b, ub4 s, chunk_position p)
+ {
+ string& v (*static_cast<string*> (c));
+
+ switch (p)
+ {
+ case chunk_one:
+ case chunk_first:
+ {
+ v.clear ();
+ }
+ // Fall through.
+ case chunk_next:
+ case chunk_last:
+ {
+ v.append (static_cast<char*> (b), s);
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ bool string_lob_value_traits::
+ param_callback (const void* c,
+ ub4*,
+ const void** b,
+ ub4* s,
+ chunk_position* p,
+ void*,
+ ub4)
+ {
+ const string& v (*static_cast<const string*> (c));
+
+ *p = chunk_one;
+ *s = static_cast<ub4> (v.size ());
+ *b = v.c_str ();
+
+ return true;
+ }
+
+ //
+ // default_value_traits<std::vector<char>, id_blob>
+ //
+ // std::vector has to be qualified for Sun CC.
+ //
+ bool default_value_traits<std::vector<char>, id_blob>::
+ result_callback (void* c, ub4*, void* b, ub4 s, chunk_position p)
+ {
+ value_type& v (*static_cast<value_type*> (c));
+
+ switch (p)
+ {
+ case chunk_one:
+ case chunk_first:
+ {
+ v.clear ();
+ }
+ // Fall through.
+ case chunk_next:
+ case chunk_last:
+ {
+ char* cb (static_cast<char*> (b));
+ v.insert (v.end (), cb, cb + s);
+
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ bool default_value_traits<std::vector<char>, id_blob>::
+ param_callback (const void* c,
+ ub4*,
+ const void** b,
+ ub4* s,
+ chunk_position* p,
+ void*,
+ ub4)
+ {
+ const value_type& v (*static_cast<const value_type*> (c));
+
+ *p = chunk_one;
+ *s = static_cast<ub4> (v.size ());
+ *b = v.empty () ? 0 : &v.front ();
+
+ return true;
+ }
+
+ //
+ // default_value_traits<std::vector<unsigned char>, id_blob>
+ //
+ // std::vector has to be qualified for Sun CC.
+ //
+ bool default_value_traits<std::vector<unsigned char>, id_blob>::
+ result_callback (void* c, ub4*, void* b, ub4 s, chunk_position p)
+ {
+ value_type& v (*static_cast<value_type*> (c));
+
+ switch (p)
+ {
+ case chunk_one:
+ case chunk_first:
+ {
+ v.clear ();
+ }
+ // Fall through.
+ case chunk_next:
+ case chunk_last:
+ {
+ unsigned char* cb (static_cast<unsigned char*> (b));
+ v.insert (v.end (), cb, cb + s);
+
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ bool default_value_traits<std::vector<unsigned char>, id_blob>::
+ param_callback (const void* c,
+ ub4*,
+ const void** b,
+ ub4* s,
+ chunk_position* p,
+ void*,
+ ub4)
+ {
+ const value_type& v (*static_cast<const value_type*> (c));
+
+ *p = chunk_one;
+ *s = static_cast<ub4> (v.size ());
+ *b = v.empty () ? 0 : &v.front ();
+
+ return true;
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/traits.hxx b/libodb-oracle/odb/oracle/traits.hxx
new file mode 100644
index 0000000..8a1673c
--- /dev/null
+++ b/libodb-oracle/odb/oracle/traits.hxx
@@ -0,0 +1,1491 @@
+// file : odb/oracle/traits.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_TRAITS_HXX
+#define ODB_ORACLE_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+#include <cstring> // std::memcpy, std::memset, std::strlen
+
+#ifdef ODB_CXX11
+# include <array>
+#endif
+
+#include <odb/traits.hxx>
+#include <odb/wrapper-traits.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/oracle-types.hxx>
+
+#include <odb/details/buffer.hxx>
+#include <odb/details/wrapper-p.hxx>
+
+#include <odb/oracle/details/export.hxx>
+#include <odb/oracle/details/number.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ enum database_type_id
+ {
+ id_int32,
+ id_int64,
+
+ id_big_int,
+
+ id_float,
+ id_double,
+
+ // Both large fixed-point and large floating point NUMBER and FLOAT
+ // values are mapped to this id.
+ //
+ id_big_float,
+
+ id_date,
+ id_timestamp,
+ id_interval_ym,
+ id_interval_ds,
+
+ id_string,
+ id_nstring,
+
+ id_raw,
+
+ id_blob,
+ id_clob,
+ id_nclob
+ };
+
+ //
+ // int_traits
+ //
+
+ // Only mark fundamental unsigned integers as unsigned. In particular,
+ // treat enums as signed since in most cases and on most platforms the
+ // underlying integer type will be signed. On Windows with VC9 and up
+ // and with GCC, the __intN types are simply aliases for the respective
+ // standard integers so the below code will cover them as well. Also
+ // note that the ODB compiler performs a similar test, so if you change
+ // anything below you will probably also need to make a similar change
+ // there.
+ //
+ template <typename T>
+ struct int_traits {static const bool unsign = false;};
+
+ template <>
+ struct int_traits<bool> {static const bool unsign = true;};
+ template <>
+ struct int_traits<unsigned char> {static const bool unsign = true;};
+ template <>
+ struct int_traits<unsigned short> {static const bool unsign = true;};
+ template <>
+ struct int_traits<unsigned int> {static const bool unsign = true;};
+ template <>
+ struct int_traits<unsigned long> {static const bool unsign = true;};
+ template <>
+ struct int_traits<unsigned long long>
+ {
+ static const bool unsign = true;
+ };
+
+ //
+ // image_traits
+ //
+
+ template <typename T, database_type_id>
+ struct image_traits;
+
+ // int32
+ //
+ template <bool unsign>
+ struct int32_image_traits;
+
+ template <>
+ struct int32_image_traits<false>
+ {
+ static const bind::buffer_type buffer_type = bind::integer;
+ typedef int image_type;
+ };
+
+ template <>
+ struct int32_image_traits<true>
+ {
+ static const bind::buffer_type buffer_type = bind::uinteger;
+ typedef unsigned int image_type;
+ };
+
+ template <typename T>
+ struct image_traits<T, id_int32>: int32_image_traits<int_traits<T>::unsign>
+ {
+ };
+
+ // int64
+ //
+ template <bool unsign>
+ struct int64_image_traits;
+
+ template <>
+ struct int64_image_traits<false>
+ {
+ static const bind::buffer_type buffer_type = bind::integer;
+ typedef long long image_type;
+ };
+
+ template <>
+ struct int64_image_traits<true>
+ {
+ static const bind::buffer_type buffer_type = bind::uinteger;
+ typedef unsigned long long image_type;
+ };
+
+ template <typename T>
+ struct image_traits<T, id_int64>: int64_image_traits<int_traits<T>::unsign>
+ {
+ };
+
+ // big_int
+ //
+ template <typename T>
+ struct image_traits<T, id_big_int>
+ {
+ // Image is a buffer containing native OCI NUMBER representation.
+ //
+ typedef char* image_type;
+ };
+
+ template <typename T>
+ struct image_traits<T, id_float> {typedef float image_type;};
+
+ template <typename T>
+ struct image_traits<T, id_double> {typedef double image_type;};
+
+ template <typename T>
+ struct image_traits<T, id_big_float>
+ {
+ // Image is a buffer containing the native OCI NUMBER representation.
+ //
+ typedef char* image_type;
+ };
+
+ template <typename T>
+ struct image_traits<T, id_date>
+ {
+ // Image is a buffer containing the native OCI DATE representation. This
+ // buffer has a fixed length of 7 bytes.
+ //
+ typedef char* image_type;
+ };
+
+ template <typename T>
+ struct image_traits<T, id_timestamp>
+ {
+ typedef datetime image_type;
+ };
+
+ template <typename T>
+ struct image_traits<T, id_interval_ym>
+ {
+ typedef interval_ym image_type;
+ };
+
+ template <typename T>
+ struct image_traits<T, id_interval_ds>
+ {
+ typedef interval_ds image_type;
+ };
+
+ template <typename T>
+ struct image_traits<T, id_string> {typedef char* image_type;};
+
+ template <typename T>
+ struct image_traits<T, id_nstring> {typedef char* image_type;};
+
+ template <typename T>
+ struct image_traits<T, id_raw> {typedef char* image_type;};
+
+ template <typename T>
+ struct image_traits<T, id_blob> {typedef lob_callback image_type;};
+
+ template <typename T>
+ struct image_traits<T, id_clob> {typedef lob_callback image_type;};
+
+ template <typename T>
+ struct image_traits<T, id_nclob> {typedef lob_callback image_type;};
+
+ //
+ // value_traits
+ //
+
+ template <typename W, database_type_id, bool null_handler>
+ struct wrapped_value_traits;
+
+ template <typename T, database_type_id>
+ struct default_value_traits;
+
+ template <typename T, database_type_id, bool w = details::wrapper_p<T>::r>
+ struct select_traits;
+
+ template <typename T, database_type_id ID>
+ struct select_traits<T, ID, false>
+ {
+ typedef default_value_traits<T, ID> type;
+ };
+
+ template <typename W, database_type_id ID>
+ struct select_traits<W, ID, true>
+ {
+ typedef
+ wrapped_value_traits<W, ID, wrapper_traits<W>::null_handler>
+ type;
+ };
+
+ template <typename T, database_type_id ID>
+ class value_traits: public select_traits<T, ID>::type
+ {
+ };
+
+ // The wrapped_value_traits specializations should be able to handle
+ // any value type which means we have to have every possible signature
+ // of the set_value() and set_image() functions.
+ //
+ template <typename W, database_type_id ID>
+ struct wrapped_value_traits<W, ID, false>
+ {
+ typedef wrapper_traits<W> wtraits;
+ typedef typename wtraits::unrestricted_wrapped_type wrapped_type;
+
+ typedef W value_type;
+ typedef wrapped_type query_type;
+ typedef typename image_traits<wrapped_type, ID>::image_type image_type;
+
+ typedef value_traits<wrapped_type, ID> vtraits;
+
+ static void
+ set_value (W& v, const image_type& i, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, const W& v)
+ {
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+
+ // big_int, big_float, string, nstring, raw.
+ //
+ static void
+ set_value (W& v, const char* i, std::size_t n, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, n, is_null);
+ }
+
+ // string, nstring, raw.
+ //
+ static void
+ set_image (char* i,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v));
+ }
+
+ // big_int, big_float.
+ //
+ static void
+ set_image (char* i, std::size_t& n, bool& is_null, const W& v)
+ {
+ vtraits::set_image (i, n, is_null, wtraits::get_ref (v));
+ }
+
+ // blob, clob, nclob.
+ //
+ static void
+ set_value (W& v, result_callback_type& cb, void*& context, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), cb, context, is_null);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const W& v)
+ {
+ vtraits::set_image (cb, context, is_null, wtraits::get_ref (v));
+ }
+ };
+
+ template <typename W, database_type_id ID>
+ struct wrapped_value_traits<W, ID, true>
+ {
+ typedef wrapper_traits<W> wtraits;
+ typedef typename wtraits::unrestricted_wrapped_type wrapped_type;
+
+ typedef W value_type;
+ typedef wrapped_type query_type;
+ typedef typename image_traits<wrapped_type, ID>::image_type image_type;
+
+ typedef value_traits<wrapped_type, ID> vtraits;
+
+ static void
+ set_value (W& v, const image_type& i, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+
+ // big_int, big_float, string, nstring, raw.
+ //
+ static void
+ set_value (W& v, const char* i, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, n, is_null);
+ }
+
+ // string, nstring, raw.
+ //
+ static void
+ set_image (char* i,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v));
+ }
+
+ // big_int, big_float
+ //
+ static void
+ set_image (char* i, std::size_t& n, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, n, is_null, wtraits::get_ref (v));
+ }
+
+ // blob, clob, nclob.
+ //
+ static void
+ set_value (W& v, result_callback_type& cb, void*& context, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), cb, context, is_null);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (cb, context, is_null, wtraits::get_ref (v));
+ }
+ };
+
+ template <typename T, database_type_id ID>
+ struct default_value_traits
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef typename image_traits<T, ID>::image_type image_type;
+
+ static void
+ set_value (T& v, const image_type& i, bool is_null)
+ {
+ if (!is_null)
+ v = T (i);
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, T v)
+ {
+ is_null = false;
+ i = image_type (v);
+ }
+ };
+
+ // id_big_int partial specialization.
+ //
+ template <typename T, bool unsign>
+ struct big_int_value_traits;
+
+ template <typename T>
+ struct big_int_value_traits<T, false>
+ {
+ static void
+ set_value (T& v, const char* b, std::size_t n, bool is_null)
+ {
+ if (!is_null)
+ v = static_cast<T> (details::number_to_int64 (b, n));
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (char* b, std::size_t& n, bool& is_null, T v)
+ {
+ is_null = false;
+ details::int64_to_number (b, n, static_cast<long long> (v));
+ }
+ };
+
+ template <typename T>
+ struct big_int_value_traits<T, true>
+ {
+ static void
+ set_value (T& v, const char* b, std::size_t n, bool is_null)
+ {
+ if (!is_null)
+ v = static_cast<T> (details::number_to_uint64 (b, n));
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (char* b, std::size_t& n, bool& is_null, T v)
+ {
+ is_null = false;
+ details::uint64_to_number (b, n, static_cast<unsigned long long> (v));
+ }
+ };
+
+ template <typename T>
+ struct default_value_traits<T, id_big_int>:
+ big_int_value_traits<T, int_traits<T>::unsign>
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef typename image_traits<T, id_big_int>::image_type image_type;
+ };
+
+ // std::string specialization.
+ //
+ class LIBODB_ORACLE_EXPORT string_value_traits
+ {
+ public:
+ typedef std::string value_type;
+ typedef std::string query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (std::string& v,
+ const char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ v.assign (b, n);
+ else
+ v.erase ();
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const std::string& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > c)
+ n = c;
+
+ if (n != 0)
+ std::memcpy (b, v.c_str (), n);
+ }
+ };
+
+ template <>
+ struct LIBODB_ORACLE_EXPORT default_value_traits<std::string, id_string>:
+ string_value_traits
+ {
+ };
+
+ template <>
+ struct LIBODB_ORACLE_EXPORT default_value_traits<std::string, id_nstring>:
+ string_value_traits
+ {
+ };
+
+ // char*/const char* specialization.
+ //
+ // Specialization for const char* which only supports initialization
+ // of an image from the value but not the other way around. This way
+ // we can pass such values to the queries.
+ //
+ class LIBODB_ORACLE_EXPORT c_string_value_traits
+ {
+ public:
+ typedef const char* value_type;
+ typedef char* image_type;
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ is_null = false;
+ n = std::strlen (v);
+
+ if (n > c)
+ n = c;
+
+ if (n != 0)
+ std::memcpy (b, v, n);
+ }
+ };
+
+ template <>
+ struct LIBODB_ORACLE_EXPORT default_value_traits<char*, id_string>:
+ c_string_value_traits {};
+
+ template <>
+ struct LIBODB_ORACLE_EXPORT default_value_traits<char*, id_nstring>:
+ c_string_value_traits {};
+
+ template <>
+ struct LIBODB_ORACLE_EXPORT default_value_traits<const char*, id_string>:
+ c_string_value_traits {};
+
+ template <>
+ struct LIBODB_ORACLE_EXPORT default_value_traits<const char*, id_nstring>:
+ c_string_value_traits {};
+
+ // char[N] specializations.
+ //
+ struct LIBODB_ORACLE_EXPORT c_array_value_traits_base
+ {
+ static void
+ set_value (char* const& v,
+ const char* b,
+ std::size_t n,
+ bool is_null,
+ std::size_t N);
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const char* v,
+ std::size_t N);
+ };
+
+ template <std::size_t N>
+ struct c_array_value_traits
+ {
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char* const& v,
+ const char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (v, b, n, is_null, N);
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ c_array_value_traits_base::set_image (b, c, n, is_null, v, N);
+ }
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_string>:
+ c_array_value_traits<N> {};
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_nstring>:
+ c_array_value_traits<N> {};
+
+ // std::array<char, N> (string) specialization.
+ //
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct std_array_value_traits
+ {
+ typedef std::array<char, N> value_type;
+ typedef std::array<char, N> query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (v.data (), b, n, is_null, N);
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_array_value_traits_base::set_image (b, c, n, is_null, v.data (), N);
+ }
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_string>:
+ std_array_value_traits<N> {};
+
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_nstring>:
+ std_array_value_traits<N> {};
+#endif
+
+ // char specialization.
+ //
+ struct LIBODB_ORACLE_EXPORT char_value_traits
+ {
+ typedef char value_type;
+ typedef char query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char& v,
+ const char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (&v, b, n, is_null, 1);
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ char v)
+ {
+ c_array_value_traits_base::set_image (b, c, n, is_null, &v, 1);
+ }
+ };
+
+ template <>
+ struct LIBODB_ORACLE_EXPORT default_value_traits<char, id_string>:
+ char_value_traits {};
+
+ template <>
+ struct LIBODB_ORACLE_EXPORT default_value_traits<char, id_nstring>:
+ char_value_traits {};
+
+ // std::vector<char> (buffer) specialization for RAW.
+ //
+ template <>
+ struct LIBODB_ORACLE_EXPORT default_value_traits<std::vector<char>, id_raw>
+ {
+ public:
+ typedef std::vector<char> value_type;
+ typedef std::vector<char> query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (value_type& v, const char* b, std::size_t n, bool is_null)
+ {
+ if (!is_null)
+ v.assign (b, b + n);
+ else
+ v.clear ();
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > c)
+ n = c;
+
+ // std::vector::data() may not be available in older compilers.
+ //
+ if (n != 0)
+ std::memcpy (b, &v.front (), n);
+ }
+ };
+
+ // std::vector<unsigned char> (buffer) specialization for RAW.
+ //
+ template <>
+ struct LIBODB_ORACLE_EXPORT
+ default_value_traits<std::vector<unsigned char>, id_raw>
+ {
+ typedef std::vector<unsigned char> value_type;
+ typedef std::vector<unsigned char> query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (value_type& v, const char* b, std::size_t n, bool is_null)
+ {
+ if (!is_null)
+ {
+ const unsigned char* ub (reinterpret_cast<const unsigned char*> (b));
+ v.assign (ub, ub + n);
+ }
+ else
+ v.clear ();
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > c)
+ n = c;
+
+ // std::vector::data() may not be available in older compilers.
+ //
+ if (n != 0)
+ std::memcpy (b, &v.front (), n);
+ }
+ };
+
+ // char[N] (buffer) specialization for RAW.
+ //
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_raw>
+ {
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef char* image_type;
+
+ static void
+ set_value (char* const& v, const char* b, std::size_t n, bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, b, (n < N ? n : N));
+ else
+ std::memset (v, 0, N);
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ is_null = false;
+ n = N < c ? N : c;
+ std::memcpy (b, v, n);
+ }
+ };
+
+ // unsigned char[N] (buffer) specialization for RAW.
+ //
+ template <std::size_t N>
+ struct default_value_traits<unsigned char[N], id_raw>
+ {
+ typedef unsigned char* value_type;
+ typedef unsigned char query_type[N];
+ typedef char* image_type;
+
+ static void
+ set_value (unsigned char* const& v,
+ const char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, b, (n < N ? n : N));
+ else
+ std::memset (v, 0, N);
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const unsigned char* v)
+ {
+ is_null = false;
+ n = N < c ? N : c;
+ std::memcpy (b, v, n);
+ }
+ };
+
+#ifdef ODB_CXX11
+ // std::array<char, N> (buffer) specialization for RAW.
+ //
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_raw>
+ {
+ public:
+ typedef std::array<char, N> value_type;
+ typedef value_type query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (value_type& v, const char* b, std::size_t n, bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v.data (), b, (n < N ? n : N));
+ else
+ std::memset (v.data (), 0, N);
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = N < c ? N : c;
+ std::memcpy (b, v.data (), n);
+ }
+ };
+
+ // std::array<unsigned char, N> (buffer) specialization for RAW.
+ //
+ template <std::size_t N>
+ struct default_value_traits<std::array<unsigned char, N>, id_raw>
+ {
+ public:
+ typedef std::array<unsigned char, N> value_type;
+ typedef value_type query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (value_type& v, const char* b, std::size_t n, bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v.data (), b, (n < N ? n : N));
+ else
+ std::memset (v.data (), 0, N);
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = N < c ? N : c;
+ std::memcpy (b, v.data (), n);
+ }
+ };
+#endif
+
+ // std::string specialization for LOBs.
+ //
+ class LIBODB_ORACLE_EXPORT string_lob_value_traits
+ {
+ public:
+ typedef std::string value_type;
+ typedef std::string query_type;
+ typedef lob_callback image_type;
+
+ static void
+ set_value (std::string& v,
+ result_callback_type& cb,
+ void*& context,
+ bool is_null)
+ {
+ if (!is_null)
+ {
+ cb = &result_callback;
+ context = &v;
+ }
+ else
+ v.erase ();
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const std::string& v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = &v;
+ }
+
+ static bool
+ result_callback (void* context,
+ ub4* position_context,
+ void* buffer,
+ ub4 size,
+ chunk_position);
+
+ static bool
+ param_callback (const void* context,
+ ub4* position_context,
+ const void** buffer,
+ ub4* size,
+ chunk_position*,
+ void* temp_buffer,
+ ub4 capacity);
+ };
+
+ template <>
+ struct LIBODB_ORACLE_EXPORT default_value_traits<std::string, id_clob>:
+ string_lob_value_traits
+ {
+ };
+
+ template <>
+ struct LIBODB_ORACLE_EXPORT default_value_traits<std::string, id_nclob>:
+ string_lob_value_traits
+ {
+ };
+
+ // std::vector<char> (buffer) specialization for BLOBs.
+ //
+ template <>
+ struct LIBODB_ORACLE_EXPORT default_value_traits<std::vector<char>,
+ id_blob>
+ {
+ public:
+ typedef std::vector<char> value_type;
+ typedef std::vector<char> query_type;
+ typedef lob_callback image_type;
+
+ static void
+ set_value (value_type& v,
+ result_callback_type& cb,
+ void*& context,
+ bool is_null)
+ {
+ if (!is_null)
+ {
+ cb = &result_callback;
+ context = &v;
+ }
+ else
+ v.clear ();
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = &v;
+ }
+
+ static bool
+ result_callback (void* context,
+ ub4* position_context,
+ void* buffer,
+ ub4 size,
+ chunk_position);
+
+ static bool
+ param_callback (const void* context,
+ ub4* position_context,
+ const void** buffer,
+ ub4* size,
+ chunk_position*,
+ void* temp_buffer,
+ ub4 capacity);
+ };
+
+ // std::vector<unsigned char> (buffer) specialization for BLOBs.
+ //
+ template <>
+ struct LIBODB_ORACLE_EXPORT
+ default_value_traits<std::vector<unsigned char>, id_blob>
+ {
+ public:
+ typedef std::vector<unsigned char> value_type;
+ typedef std::vector<unsigned char> query_type;
+ typedef lob_callback image_type;
+
+ static void
+ set_value (value_type& v,
+ result_callback_type& cb,
+ void*& context,
+ bool is_null)
+ {
+ if (!is_null)
+ {
+ cb = &result_callback;
+ context = &v;
+ }
+ else
+ v.clear ();
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = &v;
+ }
+
+ static bool
+ result_callback (void* context,
+ ub4* position_context,
+ void* buffer,
+ ub4 size,
+ chunk_position);
+
+ static bool
+ param_callback (const void* context,
+ ub4* position_context,
+ const void** buffer,
+ ub4* size,
+ chunk_position*,
+ void* temp_buffer,
+ ub4 capacity);
+ };
+
+ // char[N] (buffer) specialization for BLOBs.
+ //
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_blob>
+ {
+ public:
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef lob_callback image_type;
+
+ static void
+ set_value (char* const& v,
+ result_callback_type& cb,
+ void*& context,
+ bool is_null)
+ {
+ if (!is_null)
+ {
+ cb = &result_callback;
+ context = v;
+ }
+ else
+ std::memset (v, 0, N);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const char* v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = v;
+ }
+
+ static bool
+ result_callback (void* context,
+ ub4* position_context,
+ void* buffer,
+ ub4 size,
+ chunk_position);
+
+ static bool
+ param_callback (const void* context,
+ ub4* position_context,
+ const void** buffer,
+ ub4* size,
+ chunk_position*,
+ void* temp_buffer,
+ ub4 capacity);
+ };
+
+ // unsigned char[N] (buffer) specialization for BLOBs.
+ //
+ template <std::size_t N>
+ struct default_value_traits<unsigned char[N], id_blob>
+ {
+ public:
+ typedef unsigned char* value_type;
+ typedef unsigned char query_type[N];
+ typedef lob_callback image_type;
+
+ static void
+ set_value (unsigned char* const& v,
+ result_callback_type& cb,
+ void*& context,
+ bool is_null)
+ {
+ if (!is_null)
+ {
+ cb = &result_callback;
+ context = v;
+ }
+ else
+ std::memset (v, 0, N);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const unsigned char* v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = v;
+ }
+
+ static bool
+ result_callback (void* context,
+ ub4* position_context,
+ void* buffer,
+ ub4 size,
+ chunk_position);
+
+ static bool
+ param_callback (const void* context,
+ ub4* position_context,
+ const void** buffer,
+ ub4* size,
+ chunk_position*,
+ void* temp_buffer,
+ ub4 capacity);
+ };
+
+#ifdef ODB_CXX11
+ // std::array<char, N> (buffer) specialization for BLOBS.
+ //
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_blob>
+ {
+ public:
+ typedef std::array<char, N> value_type;
+ typedef value_type query_type;
+ typedef lob_callback image_type;
+
+ static void
+ set_value (value_type& v,
+ result_callback_type& cb,
+ void*& context,
+ bool is_null)
+ {
+ if (!is_null)
+ {
+ cb = &result_callback;
+ context = v.data ();
+ }
+ else
+ std::memset (v.data (), 0, N);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = v.data ();
+ }
+
+ static bool
+ result_callback (void* context,
+ ub4* position_context,
+ void* buffer,
+ ub4 size,
+ chunk_position);
+
+ static bool
+ param_callback (const void* context,
+ ub4* position_context,
+ const void** buffer,
+ ub4* size,
+ chunk_position*,
+ void* temp_buffer,
+ ub4 capacity);
+ };
+
+ // std::array<unsigned char, N> (buffer) specialization for BLOBS.
+ //
+ template <std::size_t N>
+ struct default_value_traits<std::array<unsigned char, N>, id_blob>
+ {
+ public:
+ typedef std::array<unsigned char, N> value_type;
+ typedef value_type query_type;
+ typedef lob_callback image_type;
+
+ static void
+ set_value (value_type& v,
+ result_callback_type& cb,
+ void*& context,
+ bool is_null)
+ {
+ if (!is_null)
+ {
+ cb = &result_callback;
+ context = v.data ();
+ }
+ else
+ std::memset (v.data (), 0, N);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = v.data ();
+ }
+
+ static bool
+ result_callback (void* context,
+ ub4* position_context,
+ void* buffer,
+ ub4 size,
+ chunk_position);
+
+ static bool
+ param_callback (const void* context,
+ ub4* position_context,
+ const void** buffer,
+ ub4* size,
+ chunk_position*,
+ void* temp_buffer,
+ ub4 capacity);
+ };
+#endif
+
+ //
+ // type_traits
+ //
+
+ template <typename T>
+ struct default_type_traits;
+
+ template <typename T>
+ class type_traits: public default_type_traits<T>
+ {
+ };
+
+ // Integral types.
+ //
+ template <>
+ struct default_type_traits<bool>
+ {
+ static const database_type_id db_type_id = id_int32;
+ };
+
+ template <>
+ struct default_type_traits<signed char>
+ {
+ static const database_type_id db_type_id = id_int32;
+ };
+
+ template <>
+ struct default_type_traits<unsigned char>
+ {
+ static const database_type_id db_type_id = id_int32;
+ };
+
+ template <>
+ struct default_type_traits<short>
+ {
+ static const database_type_id db_type_id = id_int32;
+ };
+
+ template <>
+ struct default_type_traits<unsigned short>
+ {
+ static const database_type_id db_type_id = id_int32;
+ };
+
+ template <>
+ struct default_type_traits<int>
+ {
+ static const database_type_id db_type_id = id_int32;
+ };
+
+ template <>
+ struct default_type_traits<unsigned int>
+ {
+ static const database_type_id db_type_id = id_int32;
+ };
+
+ template <>
+ struct default_type_traits<long>
+ {
+ static const database_type_id db_type_id = id_big_int;
+ };
+
+ template <>
+ struct default_type_traits<unsigned long>
+ {
+ static const database_type_id db_type_id = id_big_int;
+ };
+
+ template <>
+ struct default_type_traits<long long>
+ {
+ static const database_type_id db_type_id = id_big_int;
+ };
+
+ template <>
+ struct default_type_traits<unsigned long long>
+ {
+ static const database_type_id db_type_id = id_big_int;
+ };
+
+ // Float types.
+ //
+ template <>
+ struct default_type_traits<float>
+ {
+ static const database_type_id db_type_id = id_float;
+ };
+
+ template <>
+ struct default_type_traits<double>
+ {
+ static const database_type_id db_type_id = id_double;
+ };
+
+ // String types.
+ //
+ template <>
+ struct default_type_traits<std::string>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ template <>
+ struct default_type_traits<char*>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ template <>
+ struct default_type_traits<const char*>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ template <std::size_t N>
+ struct default_type_traits<char[N]>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<char, N> >
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+#endif
+
+ template <>
+ struct default_type_traits<char>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ // Binary types. Assume RAW since LOBs cannot be compared in
+ // Oracle and this is only used in queries.
+ //
+ template <std::size_t N>
+ struct default_type_traits<unsigned char[N]>
+ {
+ static const database_type_id db_type_id = id_raw;
+ };
+
+ template <>
+ struct default_type_traits<std::vector<char> >
+ {
+ static const database_type_id db_type_id = id_raw;
+ };
+
+ template <>
+ struct default_type_traits<std::vector<unsigned char> >
+ {
+ static const database_type_id db_type_id = id_raw;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<unsigned char, N> >
+ {
+ static const database_type_id db_type_id = id_raw;
+ };
+#endif
+ }
+}
+
+#include <odb/oracle/traits.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_TRAITS_HXX
diff --git a/libodb-oracle/odb/oracle/traits.txx b/libodb-oracle/odb/oracle/traits.txx
new file mode 100644
index 0000000..22d8f9e
--- /dev/null
+++ b/libodb-oracle/odb/oracle/traits.txx
@@ -0,0 +1,130 @@
+// file : odb/oracle/traits.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace oracle
+ {
+ //
+ // default_value_traits<char[N], id_blob>
+ //
+
+ template <std::size_t N>
+ bool default_value_traits<char[N], id_blob>::
+ result_callback (void* c, ub4* position, void* b, ub4 s, chunk_position)
+ {
+ ub4 n (*position + s < N ? s : N - *position);
+ std::memcpy (static_cast<char*> (c) + *position, b, n);
+ *position += n;
+ return true;
+ }
+
+ template <std::size_t N>
+ bool default_value_traits<char[N], id_blob>::
+ param_callback (const void* c,
+ ub4*,
+ const void** b,
+ ub4* s,
+ chunk_position* p,
+ void*,
+ ub4)
+ {
+ *p = chunk_one;
+ *s = static_cast<ub4> (N);
+ *b = c;
+ return true;
+ }
+
+ //
+ // default_value_traits<unsigned char[N], id_blob>
+ //
+
+ template <std::size_t N>
+ bool default_value_traits<unsigned char[N], id_blob>::
+ result_callback (void* c, ub4* position, void* b, ub4 s, chunk_position)
+ {
+ ub4 n (*position + s < N ? s : N - *position);
+ std::memcpy (static_cast<unsigned char*> (c) + *position, b, n);
+ *position += n;
+ return true;
+ }
+
+ template <std::size_t N>
+ bool default_value_traits<unsigned char[N], id_blob>::
+ param_callback (const void* c,
+ ub4*,
+ const void** b,
+ ub4* s,
+ chunk_position* p,
+ void*,
+ ub4)
+ {
+ *p = chunk_one;
+ *s = static_cast<ub4> (N);
+ *b = c;
+ return true;
+ }
+
+#ifdef ODB_CXX11
+ //
+ // default_value_traits<std::array<char, N>, id_blob>
+ //
+
+ template <std::size_t N>
+ bool default_value_traits<std::array<char, N>, id_blob>::
+ result_callback (void* c, ub4* position, void* b, ub4 s, chunk_position)
+ {
+ ub4 n (*position + s < N ? s : N - *position);
+ std::memcpy (static_cast<char*> (c) + *position, b, n);
+ *position += n;
+ return true;
+ }
+
+ template <std::size_t N>
+ bool default_value_traits<std::array<char, N>, id_blob>::
+ param_callback (const void* c,
+ ub4*,
+ const void** b,
+ ub4* s,
+ chunk_position* p,
+ void*,
+ ub4)
+ {
+ *p = chunk_one;
+ *s = static_cast<ub4> (N);
+ *b = c;
+ return true;
+ }
+
+ //
+ // default_value_traits<std::array<unsigned char, N>, id_blob>
+ //
+
+ template <std::size_t N>
+ bool default_value_traits<std::array<unsigned char, N>, id_blob>::
+ result_callback (void* c, ub4* position, void* b, ub4 s, chunk_position)
+ {
+ ub4 n (*position + s < N ? s : N - *position);
+ std::memcpy (static_cast<unsigned char*> (c) + *position, b, n);
+ *position += n;
+ return true;
+ }
+
+ template <std::size_t N>
+ bool default_value_traits<std::array<unsigned char, N>, id_blob>::
+ param_callback (const void* c,
+ ub4*,
+ const void** b,
+ ub4* s,
+ chunk_position* p,
+ void*,
+ ub4)
+ {
+ *p = chunk_one;
+ *s = static_cast<ub4> (N);
+ *b = c;
+ return true;
+ }
+#endif
+ }
+}
diff --git a/libodb-oracle/odb/oracle/transaction-impl.cxx b/libodb-oracle/odb/oracle/transaction-impl.cxx
new file mode 100644
index 0000000..377e409
--- /dev/null
+++ b/libodb-oracle/odb/oracle/transaction-impl.cxx
@@ -0,0 +1,160 @@
+// file : odb/oracle/transaction-impl.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <oci.h>
+
+#include <cassert>
+
+#include <odb/tracer.hxx>
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/connection.hxx>
+#include <odb/oracle/error.hxx>
+#include <odb/oracle/exceptions.hxx>
+#include <odb/oracle/transaction-impl.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ transaction_impl::
+ transaction_impl (database_type& db)
+ : odb::transaction_impl (db)
+ {
+ }
+
+ transaction_impl::
+ transaction_impl (connection_ptr c)
+ : odb::transaction_impl (c->database (), *c), connection_ (c)
+ {
+ }
+
+ transaction_impl::
+ ~transaction_impl ()
+ {
+ }
+
+ void transaction_impl::
+ start ()
+ {
+ database_type& db (static_cast<database_type&> (database_));
+
+ if (connection_ == 0)
+ {
+ connection_ = db.connection ();
+ odb::transaction_impl::connection_ = connection_.get ();
+ }
+
+ OCISvcCtx* h (connection_->handle ());
+ OCIError* err (connection_->error_handle ());
+
+ // Allocate a transaction handle if there is none associated with
+ // the connection.
+ //
+ OCITrans* t (0);
+ sword s (OCIAttrGet (h,
+ OCI_HTYPE_SVCCTX,
+ reinterpret_cast<void*> (&t),
+ 0,
+ OCI_ATTR_TRANS,
+ err));
+
+ if (s == OCI_ERROR || s == OCI_INVALID_HANDLE)
+ translate_error (err, s);
+ else if (t == 0)
+ {
+ auto_handle<OCITrans> auto_t;
+
+ s = OCIHandleAlloc (db.environment (),
+ reinterpret_cast<void**> (&t),
+ OCI_HTYPE_TRANS,
+ 0,
+ 0);
+
+ if (s != OCI_SUCCESS)
+ throw invalid_oci_handle ();
+
+ auto_t.reset (t);
+
+ s = OCIAttrSet (h,
+ OCI_HTYPE_SVCCTX,
+ reinterpret_cast<void*> (t),
+ 0,
+ OCI_ATTR_TRANS,
+ err);
+
+ if (s == OCI_ERROR || s == OCI_INVALID_HANDLE)
+ translate_error (err, s);
+
+ auto_t.release ();
+ }
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "BEGIN");
+ }
+
+ // We never use OCITransDetach so the timeout parameter is
+ // of no consequence.
+ //
+ s = OCITransStart (h,
+ err,
+ 0,
+ OCI_TRANS_NEW);
+
+ if (s == OCI_ERROR || s == OCI_INVALID_HANDLE)
+ translate_error (*connection_, s);
+ }
+
+ void transaction_impl::
+ commit ()
+ {
+ // Invalidate query results.
+ //
+ connection_->invalidate_results ();
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "COMMIT");
+ }
+
+ sword s (OCITransCommit (connection_->handle (),
+ connection_->error_handle (),
+ OCI_DEFAULT));
+
+ if (s == OCI_ERROR || s == OCI_INVALID_HANDLE)
+ translate_error (*connection_, s);
+
+ // Release the connection.
+ //
+ connection_.reset ();
+ }
+
+ void transaction_impl::
+ rollback ()
+ {
+ // Invalidate query results.
+ //
+ connection_->invalidate_results ();
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "ROLLBACK");
+ }
+
+ sword s (OCITransRollback (connection_->handle (),
+ connection_->error_handle (),
+ OCI_DEFAULT));
+
+ if (s == OCI_ERROR || s == OCI_INVALID_HANDLE)
+ translate_error (*connection_, s);
+
+ // Release the connection.
+ //
+ connection_.reset ();
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/transaction-impl.hxx b/libodb-oracle/odb/oracle/transaction-impl.hxx
new file mode 100644
index 0000000..0b7e9bf
--- /dev/null
+++ b/libodb-oracle/odb/oracle/transaction-impl.hxx
@@ -0,0 +1,50 @@
+// file : odb/oracle/transaction-impl.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_TRANSACTION_IMPL_HXX
+#define ODB_ORACLE_TRANSACTION_IMPL_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx>
+#include <odb/oracle/oracle-fwd.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ class LIBODB_ORACLE_EXPORT transaction_impl: public odb::transaction_impl
+ {
+ public:
+ typedef oracle::database database_type;
+ typedef oracle::connection connection_type;
+
+ transaction_impl (database_type&);
+ transaction_impl (connection_ptr);
+
+ virtual
+ ~transaction_impl ();
+
+ virtual void
+ start ();
+
+ virtual void
+ commit ();
+
+ virtual void
+ rollback ();
+
+ private:
+ connection_ptr connection_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_TRANSACTION_IMPL_HXX
diff --git a/libodb-oracle/odb/oracle/transaction.cxx b/libodb-oracle/odb/oracle/transaction.cxx
new file mode 100644
index 0000000..6dff1a0
--- /dev/null
+++ b/libodb-oracle/odb/oracle/transaction.cxx
@@ -0,0 +1,26 @@
+// file : odb/oracle/transaction.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/oracle/transaction.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ transaction& transaction::
+ current ()
+ {
+ // While the impl type can be of the concrete type, the transaction
+ // object can be created as either odb:: or odb::oracle:: type. To
+ // work around that we are going to hard-cast one to the other
+ // relying on the fact that they have the same representation and
+ // no virtual functions. The former is checked in the tests.
+ //
+ odb::transaction& b (odb::transaction::current ());
+ assert (dynamic_cast<transaction_impl*> (&b.implementation ()) != 0);
+ return reinterpret_cast<transaction&> (b);
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/transaction.hxx b/libodb-oracle/odb/oracle/transaction.hxx
new file mode 100644
index 0000000..8d06b2f
--- /dev/null
+++ b/libodb-oracle/odb/oracle/transaction.hxx
@@ -0,0 +1,88 @@
+// file : odb/oracle/transaction.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_TRANSACTION_HXX
+#define ODB_ORACLE_TRANSACTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx>
+#include <odb/oracle/tracer.hxx>
+
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ class transaction_impl;
+
+ class LIBODB_ORACLE_EXPORT transaction: public odb::transaction
+ {
+ public:
+ typedef oracle::database database_type;
+ typedef oracle::connection connection_type;
+
+ explicit
+ transaction (transaction_impl*, bool make_current = true);
+
+ transaction ();
+
+ // Return the database this transaction is on.
+ //
+ database_type&
+ database ();
+
+ // Return the underlying database connection for this transaction.
+ //
+ connection_type&
+ connection ();
+
+ connection_type&
+ connection (odb::database&);
+
+ // Return current transaction or throw if there is no transaction
+ // in effect.
+ //
+ static transaction&
+ current ();
+
+ // Set the current thread's transaction.
+ //
+ static void
+ current (transaction&);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef oracle::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ using odb::transaction::tracer;
+
+ public:
+ transaction_impl&
+ implementation ();
+ };
+ }
+}
+
+#include <odb/oracle/transaction.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_TRANSACTION_HXX
diff --git a/libodb-oracle/odb/oracle/transaction.ixx b/libodb-oracle/odb/oracle/transaction.ixx
new file mode 100644
index 0000000..d75a399
--- /dev/null
+++ b/libodb-oracle/odb/oracle/transaction.ixx
@@ -0,0 +1,57 @@
+// file : odb/oracle/transaction.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/transaction-impl.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ inline transaction::
+ transaction (transaction_impl* impl, bool make_current)
+ : odb::transaction (impl, make_current)
+ {
+ }
+
+ inline transaction::
+ transaction ()
+ : odb::transaction ()
+ {
+ }
+
+ inline transaction_impl& transaction::
+ implementation ()
+ {
+ // We can use static_cast here since we have an instance of
+ // oracle::transaction.
+ //
+ return static_cast<transaction_impl&> (
+ odb::transaction::implementation ());
+ }
+
+ inline transaction::database_type& transaction::
+ database ()
+ {
+ return static_cast<database_type&> (odb::transaction::database ());
+ }
+
+ inline transaction::connection_type& transaction::
+ connection ()
+ {
+ return static_cast<connection_type&> (odb::transaction::connection ());
+ }
+
+ inline transaction::connection_type& transaction::
+ connection (odb::database& db)
+ {
+ return static_cast<connection_type&> (odb::transaction::connection (db));
+ }
+
+ inline void transaction::
+ current (transaction& t)
+ {
+ odb::transaction::current (t);
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/version.hxx b/libodb-oracle/odb/oracle/version.hxx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libodb-oracle/odb/oracle/version.hxx
diff --git a/libodb-oracle/odb/oracle/version.hxx.in b/libodb-oracle/odb/oracle/version.hxx.in
new file mode 100644
index 0000000..c13ce47
--- /dev/null
+++ b/libodb-oracle/odb/oracle/version.hxx.in
@@ -0,0 +1,61 @@
+// file : odb/oracle/version.hxx.in
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef LIBODB_ORACLE_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 LIBODB_ORACLE_VERSION_FULL $libodb_oracle.version.project_number$ULL
+#define LIBODB_ORACLE_VERSION_STR "$libodb_oracle.version.project$"
+#define LIBODB_ORACLE_VERSION_ID "$libodb_oracle.version.project_id$"
+
+#define LIBODB_ORACLE_VERSION_MAJOR $libodb_oracle.version.major$
+#define LIBODB_ORACLE_VERSION_MINOR $libodb_oracle.version.minor$
+#define LIBODB_ORACLE_VERSION_PATCH $libodb_oracle.version.patch$
+
+#define LIBODB_ORACLE_PRE_RELEASE $libodb_oracle.version.pre_release$
+
+#define LIBODB_ORACLE_SNAPSHOT $libodb_oracle.version.snapshot_sn$ULL
+#define LIBODB_ORACLE_SNAPSHOT_ID "$libodb_oracle.version.snapshot_id$"
+
+#include <odb/version.hxx>
+
+$libodb.check(LIBODB_VERSION_FULL, LIBODB_SNAPSHOT)$
+
+
+// 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 LIBODB_ORACLE_VERSION 2049976
+
+#endif // LIBODB_ORACLE_VERSION
diff --git a/libodb-oracle/odb/oracle/view-result.hxx b/libodb-oracle/odb/oracle/view-result.hxx
new file mode 100644
index 0000000..15d1cee
--- /dev/null
+++ b/libodb-oracle/odb/oracle/view-result.hxx
@@ -0,0 +1,84 @@
+// file : odb/oracle/view-result.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_VIEW_RESULT_HXX
+#define ODB_ORACLE_VIEW_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/view-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/forward.hxx> // query_base, view_statements
+#include <odb/oracle/statement.hxx>
+#include <odb/oracle/traits-calls.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <typename T>
+ class view_result_impl: public odb::view_result_impl<T>
+ {
+ public:
+ typedef odb::view_result_impl<T> base_type;
+
+ typedef typename base_type::view_type view_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef view_traits_impl<view_type, id_oracle> view_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef view_statements<view_type> statements_type;
+
+ virtual
+ ~view_result_impl ();
+
+ view_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (view_type&);
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ typedef oracle::change_callback change_callback_type;
+
+ static void
+ change_callback (void* context);
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ view_traits_calls<view_type> tc_;
+ bool use_copy_;
+ typename view_traits::image_type* image_copy_;
+ };
+ }
+}
+
+#include <odb/oracle/view-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_VIEW_RESULT_HXX
diff --git a/libodb-oracle/odb/oracle/view-result.txx b/libodb-oracle/odb/oracle/view-result.txx
new file mode 100644
index 0000000..8ae25aa
--- /dev/null
+++ b/libodb-oracle/odb/oracle/view-result.txx
@@ -0,0 +1,148 @@
+// file : odb/oracle/view-result.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/oracle/view-statements.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <typename T>
+ view_result_impl<T>::
+ ~view_result_impl ()
+ {
+ invalidate ();
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ invalidate ()
+ {
+ change_callback_type& cc (statements_.image ().change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ delete image_copy_;
+ image_copy_ = 0;
+
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ view_result_impl<T>::
+ view_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ use_copy_ (false),
+ image_copy_ (0)
+ {
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ load (view_type& view)
+ {
+ view_traits::callback (this->db_, view, callback_event::pre_load);
+
+ tc_.init (view,
+ use_copy_ ? *image_copy_ : statements_.image (),
+ &this->db_);
+
+ // If we are using a copy, make sure the callback information for
+ // LOB data also comes from the copy.
+ //
+ statement_->stream_result (
+ use_copy_ ? &statements_.image () : 0,
+ use_copy_ ? image_copy_ : 0);
+
+ view_traits::callback (this->db_, view, callback_event::post_load);
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ typename view_traits::image_type& im (statements_.image ());
+ change_callback_type& cc (im.change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ use_copy_ = false;
+
+ if (im.version != statements_.image_version ())
+ {
+ binding& b (statements_.image_binding ());
+ tc_.bind (b.bind, im);
+ statements_.image_version (im.version);
+ b.version++;
+ }
+
+ if (statement_->fetch () == select_statement::no_data)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ else
+ {
+ cc.callback = &change_callback;
+ cc.context = this;
+ }
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t view_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ change_callback (void* c)
+ {
+ view_result_impl<T>* r (static_cast<view_result_impl<T>*> (c));
+
+ typename view_traits::image_type& im (r->statements_.image ());
+
+ if (r->image_copy_ == 0)
+ r->image_copy_ = new typename view_traits::image_type (im);
+ else
+ *r->image_copy_ = im;
+
+ im.change_callback_.callback = 0;
+ im.change_callback_.context = 0;
+
+ r->use_copy_ = true;
+ }
+ }
+}
diff --git a/libodb-oracle/odb/oracle/view-statements.hxx b/libodb-oracle/odb/oracle/view-statements.hxx
new file mode 100644
index 0000000..272352f
--- /dev/null
+++ b/libodb-oracle/odb/oracle/view-statements.hxx
@@ -0,0 +1,81 @@
+// file : odb/oracle/view-statements.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_VIEW_STATEMENTS_HXX
+#define ODB_ORACLE_VIEW_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/oracle/oracle-types.hxx>
+#include <odb/oracle/version.hxx>
+#include <odb/oracle/statement.hxx>
+#include <odb/oracle/statements-base.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <typename T>
+ class view_statements: public statements_base
+ {
+ public:
+ typedef T view_type;
+ typedef view_traits_impl<view_type, id_oracle> view_traits;
+ typedef typename view_traits::pointer_type pointer_type;
+ typedef typename view_traits::image_type image_type;
+
+ public:
+ view_statements (connection_type&);
+
+ virtual
+ ~view_statements ();
+
+ // View image.
+ //
+ image_type&
+ image ()
+ {
+ return image_;
+ }
+
+ std::size_t
+ image_version () const
+ {
+ return image_version_;
+ }
+
+ void
+ image_version (std::size_t v)
+ {
+ image_version_ = v;
+ }
+
+ binding&
+ image_binding ()
+ {
+ return image_binding_;
+ }
+
+ private:
+ view_statements (const view_statements&);
+ view_statements& operator= (const view_statements&);
+
+ private:
+ image_type image_;
+ std::size_t image_version_;
+ binding image_binding_;
+ bind image_bind_[view_traits::column_count];
+ };
+ }
+}
+
+#include <odb/oracle/view-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_VIEW_STATEMENTS_HXX
diff --git a/libodb-oracle/odb/oracle/view-statements.txx b/libodb-oracle/odb/oracle/view-statements.txx
new file mode 100644
index 0000000..3a5d31e
--- /dev/null
+++ b/libodb-oracle/odb/oracle/view-statements.txx
@@ -0,0 +1,30 @@
+// file : odb/oracle/view-statements.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <typename T>
+ view_statements<T>::
+ ~view_statements ()
+ {
+ }
+
+ template <typename T>
+ view_statements<T>::
+ view_statements (connection_type& conn)
+ : statements_base (conn),
+ image_binding_ (image_bind_, view_traits::column_count)
+ {
+ image_.version = 0;
+ image_version_ = 0;
+
+ image_binding_.change_callback = image_.change_callback ();
+
+ std::memset (image_bind_, 0, sizeof (image_bind_));
+ }
+ }
+}
diff --git a/libodb-oracle/tests/.gitignore b/libodb-oracle/tests/.gitignore
new file mode 100644
index 0000000..e54525b
--- /dev/null
+++ b/libodb-oracle/tests/.gitignore
@@ -0,0 +1 @@
+driver
diff --git a/libodb-oracle/tests/basics/buildfile b/libodb-oracle/tests/basics/buildfile
new file mode 100644
index 0000000..e963685
--- /dev/null
+++ b/libodb-oracle/tests/basics/buildfile
@@ -0,0 +1,6 @@
+# file : tests/basics/buildfile
+# license : ODB NCUEL; see accompanying LICENSE file
+
+import libs = libodb-oracle%lib{odb-oracle}
+
+exe{driver}: {hxx cxx}{*} $libs
diff --git a/libodb-oracle/tests/basics/driver.cxx b/libodb-oracle/tests/basics/driver.cxx
new file mode 100644
index 0000000..38ec6d2
--- /dev/null
+++ b/libodb-oracle/tests/basics/driver.cxx
@@ -0,0 +1,37 @@
+// file : tests/basics/driver.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+// Basic test to make sure the library is usable. Functionality testing
+// is done in the odb-tests package.
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/exceptions.hxx>
+#include <odb/oracle/transaction.hxx>
+
+using namespace odb::oracle;
+
+int
+main ()
+{
+ {
+ std::ostringstream os;
+ database::print_usage (os);
+ assert (!os.str ().empty ());
+ }
+
+ // We can't really do much here since that would require a database. We can
+ // create a fake database object as long as we don't expect to get a valid
+ // connection.
+ //
+ database db ("john", "secret", "dummy whammy");
+
+ try
+ {
+ transaction t (db.begin ());
+ assert (false);
+ }
+ catch (const database_exception&) {}
+}
diff --git a/libodb-oracle/tests/build/.gitignore b/libodb-oracle/tests/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-oracle/tests/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-oracle/tests/build/bootstrap.build b/libodb-oracle/tests/build/bootstrap.build
new file mode 100644
index 0000000..895126c
--- /dev/null
+++ b/libodb-oracle/tests/build/bootstrap.build
@@ -0,0 +1,8 @@
+# file : tests/build/bootstrap.build
+# license : ODB NCUEL; see accompanying LICENSE file
+
+project = # Unnamed subproject.
+
+using config
+using dist
+using test
diff --git a/libodb-oracle/tests/build/root.build b/libodb-oracle/tests/build/root.build
new file mode 100644
index 0000000..bbd3781
--- /dev/null
+++ b/libodb-oracle/tests/build/root.build
@@ -0,0 +1,23 @@
+# file : tests/build/root.build
+# license : ODB NCUEL; see accompanying LICENSE file
+
+cxx.std = latest
+
+using cxx
+
+hxx{*}: extension = hxx
+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
+
+# Every exe{} in this subproject is by default a test.
+#
+exe{*}: test = true
+
+# Specify the test target for cross-testing.
+#
+test.target = $cxx.target
diff --git a/libodb-oracle/tests/buildfile b/libodb-oracle/tests/buildfile
new file mode 100644
index 0000000..fd73adc
--- /dev/null
+++ b/libodb-oracle/tests/buildfile
@@ -0,0 +1,4 @@
+# file : tests/buildfile
+# license : ODB NCUEL; see accompanying LICENSE file
+
+./: {*/ -build/}
diff --git a/libodb-pgsql/.gitignore b/libodb-pgsql/.gitignore
new file mode 100644
index 0000000..1c363a0
--- /dev/null
+++ b/libodb-pgsql/.gitignore
@@ -0,0 +1,25 @@
+# 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/libodb-pgsql/GPLv2 b/libodb-pgsql/GPLv2
new file mode 100644
index 0000000..3912109
--- /dev/null
+++ b/libodb-pgsql/GPLv2
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) 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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. 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.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+ 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
+convey 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 2 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, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This 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 Library General
+Public License instead of this License.
diff --git a/libodb-pgsql/INSTALL b/libodb-pgsql/INSTALL
new file mode 100644
index 0000000..32df42c
--- /dev/null
+++ b/libodb-pgsql/INSTALL
@@ -0,0 +1,6 @@
+The easiest way to build this package is with the bpkg package manager:
+
+$ bpkg build libodb-pgsql
+
+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/libodb-pgsql/LICENSE b/libodb-pgsql/LICENSE
new file mode 100644
index 0000000..d96b938
--- /dev/null
+++ b/libodb-pgsql/LICENSE
@@ -0,0 +1,20 @@
+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 2 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, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
diff --git a/libodb-pgsql/NEWS b/libodb-pgsql/NEWS
new file mode 120000
index 0000000..0fae0f8
--- /dev/null
+++ b/libodb-pgsql/NEWS
@@ -0,0 +1 @@
+../NEWS \ No newline at end of file
diff --git a/libodb-pgsql/README b/libodb-pgsql/README
new file mode 100644
index 0000000..c4c3e28
--- /dev/null
+++ b/libodb-pgsql/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 PostgreSQL ODB runtime library. Every
+application that includes code generated for the PostgreSQL database
+will need to link to this library.
+
+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.
+
+Send questions, bug reports, or any other feedback to the
+odb-users@codesynthesis.com mailing list.
diff --git a/libodb-pgsql/build/.gitignore b/libodb-pgsql/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-pgsql/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-pgsql/build/bootstrap.build b/libodb-pgsql/build/bootstrap.build
new file mode 100644
index 0000000..fb6b7df
--- /dev/null
+++ b/libodb-pgsql/build/bootstrap.build
@@ -0,0 +1,10 @@
+# file : build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = libodb-pgsql
+
+using version
+using config
+using dist
+using test
+using install
diff --git a/libodb-pgsql/build/export.build b/libodb-pgsql/build/export.build
new file mode 100644
index 0000000..80aba23
--- /dev/null
+++ b/libodb-pgsql/build/export.build
@@ -0,0 +1,9 @@
+# file : build/export.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+$out_root/
+{
+ include odb/pgsql/
+}
+
+export $out_root/odb/pgsql/lib{odb-pgsql}
diff --git a/libodb-pgsql/build/root.build b/libodb-pgsql/build/root.build
new file mode 100644
index 0000000..329cc45
--- /dev/null
+++ b/libodb-pgsql/build/root.build
@@ -0,0 +1,19 @@
+# file : build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+config [bool] config.libodb_pgsql.develop ?= false
+
+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
diff --git a/libodb-pgsql/buildfile b/libodb-pgsql/buildfile
new file mode 100644
index 0000000..a04e206
--- /dev/null
+++ b/libodb-pgsql/buildfile
@@ -0,0 +1,9 @@
+# file : buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -build/} doc{INSTALL NEWS README} legal{GPLv2 LICENSE} manifest
+
+# Don't install tests or the INSTALL file.
+#
+tests/: install = false
+doc{INSTALL}@./: install = false
diff --git a/libodb-pgsql/manifest b/libodb-pgsql/manifest
new file mode 100644
index 0000000..df62904
--- /dev/null
+++ b/libodb-pgsql/manifest
@@ -0,0 +1,172 @@
+: 1
+name: libodb-pgsql
+version: 2.5.0-b.26.z
+project: odb
+summary: PostgreSQL ODB runtime library
+license: GPL-2.0-only
+license: other: proprietary ; Not free/open source.
+topics: C++, ORM, PostgreSQL, SQL
+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
+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: libpq >= 7.4.0
+depends: libodb == $
+depends: * cli ^1.2.0- ? ($config.libodb_pgsql.develop)
+
+tests: odb-tests == $ \
+ ? (!$defined(config.odb_tests.database)) config.odb_tests.database=pgsql
+
+builds: all
+builds: -wasm
+build-auxiliary: *-postgresql_*
+default-build-config:
+\
+{
+ config.odb_tests.pgsql.user=$getenv(DATABASE_USER)
+ config.odb_tests.pgsql.database=$getenv(DATABASE_NAME)
+ config.odb_tests.pgsql.host=$getenv(DATABASE_HOST)
+ config.odb_tests.pgsql.port=$getenv(DATABASE_PORT)
+}+ odb-tests
+\
+
+# Only build this package configuration where it can be tested via odb-tests
+# package (see its manifest for details).
+#
+multi-builds: all
+multi-builds: -wasm
+multi-builds: -( +windows -gcc )
+multi-builds: &gcc
+multi-builds: &gcc-5+
+multi-builds: -static
+multi-build-config:
+\
+{
+ config.odb_tests.multi_database=true
+
+ config.odb_tests.pgsql.user=$getenv(DATABASE_USER)
+ config.odb_tests.pgsql.database=$getenv(DATABASE_NAME)
+ config.odb_tests.pgsql.host=$getenv(DATABASE_HOST)
+ config.odb_tests.pgsql.port=$getenv(DATABASE_PORT)
+}+ odb-tests
+\
+
+# Complements the default configuration (see odb-tests for background).
+#
+# Note: derives build-auxiliary from default.
+#
+custom-builds: latest
+custom-builds: -wasm
+custom-builds: -static ; Implementation uses plugins and requires -fPIC.
+#custom-build-bot: -- see below.
+custom-build-config:
+\
+{
+ config.odb_tests.pgsql.user=$getenv(DATABASE_USER)
+ config.odb_tests.pgsql.database=$getenv(DATABASE_NAME)
+ config.odb_tests.pgsql.host=$getenv(DATABASE_HOST)
+ config.odb_tests.pgsql.port=$getenv(DATABASE_PORT)
+}+ odb-tests
+\
+
+# Complements the multi configuration (see odb-tests for background).
+#
+# Note: derives build-auxiliary from default.
+#
+custom-multi-builds: latest
+custom-multi-builds: -wasm
+custom-multi-builds: -static ; Implementation uses plugins and requires -fPIC.
+#custom-multi-build-bot: -- see below.
+custom-multi-build-config:
+\
+{
+ config.odb_tests.multi_database=true
+
+ config.odb_tests.pgsql.user=$getenv(DATABASE_USER)
+ config.odb_tests.pgsql.database=$getenv(DATABASE_NAME)
+ config.odb_tests.pgsql.host=$getenv(DATABASE_HOST)
+ config.odb_tests.pgsql.port=$getenv(DATABASE_PORT)
+}+ odb-tests
+\
+
+custom-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuF4YmJmPHY52Q6N+YO0M
+lt/fCovdezleb2tVplyTnvbyAiPdmYCIIjVrsqUn3y46PdFtWEiSdsrCcncoxi6H
+8KelOB/oQ9pNTyEvwGKEH5ZIU7noLZYdXEfoNdvdL/pbY/7uLBZOSekfdQShZtbe
+uOZCM2Mhg2DD76TP/VAwaXuDCnEvxxU/yneUl5ZaBo62AWNrYJuSGAliCOpVpl6X
+X1kbHOvnCx7c9e3LxgaVivPaeZRKYg0OaFt96SBYEZzNPvjA8pMuKuj/vatHaCQ3
+NO9+r3TJ+4dQd7qN6Ju3zUJq9J/ndSh4lPvUalvvhdykecefhcyHwRZOG4xyFMFE
+nJM4sM+aZu6WoKATIKtk7On70inVr0sZJXwJ4Lt4oqaK2VthcSTby3wf2Yv4p5hL
+zNo31cCPmBRYzABcIc6ADYvexVK4uCwaim8xs7RK5Ug2Gv6vUWoRNZW8grIgDwUY
+5pZ4Zk3hW4ii2vehTaVrrmdW6XipIsT+ayiVX7eWuHHNxAeCojXVjOJu9B0ExMlD
+5tHZCs+SNdV5MceexecbptB7fZtRebP120yjLiSnZ5FpaQ1stusr0hSg+VQaX4np
+f5m1W/CcDr53PKWg/ayY9nWMUQaIwH4b69kLM+VTpYSbzu5UQJkmNBNq2EOHgoTv
+9MLA+cE/nNJ/rMI//MZ1+kcCAwEAAQ==
+-----END PUBLIC KEY-----
+\
+
+custom-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuLYZ68rotGDAtWViFHOe
+XEsmZB8BGI+af1ixh9JOy9BE4ohGTfPr1YsjGDzh+PhOVLAtyykOoT/qG7cuGB0T
+gBInoRrgVB2/ZKTMwxeDGb/TA3uykaXxcw7/liTsizHAY+phCNTbke8iER5Y78js
+9GlnTPmNhwFqEj2fwCz+2o08eyZvZ9Vj1fH/bFDCmDmU33JR3crtJlC8wPiF70Ho
+FJzHFdaFQl3MxvEV92HjOsyqozMi6tAVVefN1vapVQeNtjkB0Di18p0/EMugEuGU
+OxktjDHQWNaV8Ao6cCDk6OkJnM3ZNL1no3cV4cuF+/xI8UZzwfPoBnwg/s183Qzu
+pHHKOSHmuO0oVE/XohJhepSw3tb+wf5BwejRhYHikIjqCxJdm9H0QTiqXT82y24K
+yg3gkRMOgqnVxERKKP4ZknLSMQCEKiND/t2zdLJ/lxH9eHZdPHKk3OZZG292j+Bh
+fknxcTKNk1Dmf32Irs5hVrjsoU8eAutbItovzXdBaj//rn/ry/kUlCa1Ov6iLIDJ
+gyxmsDlgKNR/uE9ogmDn0ishJIoCmxeqenRfJkttr9pEsDsUFuB425QGqiSxa1jh
+PCNca3iRtO44wADXaQMTGpvLzBfdfVc8LoFpn+kynN0V1MvxAX4mHRXxw8ERXd3U
+dpHDhOthPLolJQrYKb/YyW8CAwEAAQ==
+-----END PUBLIC KEY-----
+\
+
+custom-multi-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuF4YmJmPHY52Q6N+YO0M
+lt/fCovdezleb2tVplyTnvbyAiPdmYCIIjVrsqUn3y46PdFtWEiSdsrCcncoxi6H
+8KelOB/oQ9pNTyEvwGKEH5ZIU7noLZYdXEfoNdvdL/pbY/7uLBZOSekfdQShZtbe
+uOZCM2Mhg2DD76TP/VAwaXuDCnEvxxU/yneUl5ZaBo62AWNrYJuSGAliCOpVpl6X
+X1kbHOvnCx7c9e3LxgaVivPaeZRKYg0OaFt96SBYEZzNPvjA8pMuKuj/vatHaCQ3
+NO9+r3TJ+4dQd7qN6Ju3zUJq9J/ndSh4lPvUalvvhdykecefhcyHwRZOG4xyFMFE
+nJM4sM+aZu6WoKATIKtk7On70inVr0sZJXwJ4Lt4oqaK2VthcSTby3wf2Yv4p5hL
+zNo31cCPmBRYzABcIc6ADYvexVK4uCwaim8xs7RK5Ug2Gv6vUWoRNZW8grIgDwUY
+5pZ4Zk3hW4ii2vehTaVrrmdW6XipIsT+ayiVX7eWuHHNxAeCojXVjOJu9B0ExMlD
+5tHZCs+SNdV5MceexecbptB7fZtRebP120yjLiSnZ5FpaQ1stusr0hSg+VQaX4np
+f5m1W/CcDr53PKWg/ayY9nWMUQaIwH4b69kLM+VTpYSbzu5UQJkmNBNq2EOHgoTv
+9MLA+cE/nNJ/rMI//MZ1+kcCAwEAAQ==
+-----END PUBLIC KEY-----
+\
+
+custom-multi-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuLYZ68rotGDAtWViFHOe
+XEsmZB8BGI+af1ixh9JOy9BE4ohGTfPr1YsjGDzh+PhOVLAtyykOoT/qG7cuGB0T
+gBInoRrgVB2/ZKTMwxeDGb/TA3uykaXxcw7/liTsizHAY+phCNTbke8iER5Y78js
+9GlnTPmNhwFqEj2fwCz+2o08eyZvZ9Vj1fH/bFDCmDmU33JR3crtJlC8wPiF70Ho
+FJzHFdaFQl3MxvEV92HjOsyqozMi6tAVVefN1vapVQeNtjkB0Di18p0/EMugEuGU
+OxktjDHQWNaV8Ao6cCDk6OkJnM3ZNL1no3cV4cuF+/xI8UZzwfPoBnwg/s183Qzu
+pHHKOSHmuO0oVE/XohJhepSw3tb+wf5BwejRhYHikIjqCxJdm9H0QTiqXT82y24K
+yg3gkRMOgqnVxERKKP4ZknLSMQCEKiND/t2zdLJ/lxH9eHZdPHKk3OZZG292j+Bh
+fknxcTKNk1Dmf32Irs5hVrjsoU8eAutbItovzXdBaj//rn/ry/kUlCa1Ov6iLIDJ
+gyxmsDlgKNR/uE9ogmDn0ishJIoCmxeqenRfJkttr9pEsDsUFuB425QGqiSxa1jh
+PCNca3iRtO44wADXaQMTGpvLzBfdfVc8LoFpn+kynN0V1MvxAX4mHRXxw8ERXd3U
+dpHDhOthPLolJQrYKb/YyW8CAwEAAQ==
+-----END PUBLIC KEY-----
+\
diff --git a/libodb-pgsql/odb/pgsql/auto-handle.cxx b/libodb-pgsql/odb/pgsql/auto-handle.cxx
new file mode 100644
index 0000000..d6c0565
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/auto-handle.cxx
@@ -0,0 +1,24 @@
+// file : odb/pgsql/auto-handle.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <libpq-fe.h>
+
+#include <odb/pgsql/auto-handle.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ void handle_traits<PGconn>::
+ release (PGconn* h)
+ {
+ PQfinish (h);
+ }
+
+ void handle_traits<PGresult>::
+ release (PGresult* h)
+ {
+ PQclear (h);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/auto-handle.hxx b/libodb-pgsql/odb/pgsql/auto-handle.hxx
new file mode 100644
index 0000000..49b396d
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/auto-handle.hxx
@@ -0,0 +1,90 @@
+// file : odb/pgsql/auto-handle.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_AUTO_HANDLE_HXX
+#define ODB_PGSQL_AUTO_HANDLE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/pgsql-fwd.hxx> // PGconn, PGresult
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename H>
+ struct handle_traits;
+
+ template <>
+ struct LIBODB_PGSQL_EXPORT handle_traits<PGconn>
+ {
+ static void
+ release (PGconn*);
+ };
+
+ template <>
+ struct LIBODB_PGSQL_EXPORT handle_traits<PGresult>
+ {
+ static void
+ release (PGresult*);
+ };
+
+ template <typename H>
+ class auto_handle
+ {
+ public:
+ auto_handle (H* h = 0)
+ : h_ (h)
+ {
+ }
+
+ ~auto_handle ()
+ {
+ if (h_ != 0)
+ handle_traits<H>::release (h_);
+ }
+
+ H*
+ get () const
+ {
+ return h_;
+ }
+
+ void
+ reset (H* h = 0)
+ {
+ if (h_ != 0)
+ handle_traits<H>::release (h_);
+
+ h_ = h;
+ }
+
+ H*
+ release ()
+ {
+ H* h (h_);
+ h_ = 0;
+ return h;
+ }
+
+ operator H* () const
+ {
+ return h_;
+ }
+
+ private:
+ auto_handle (const auto_handle&);
+ auto_handle& operator= (const auto_handle&);
+
+ private:
+ H* h_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_AUTO_HANDLE_HXX
diff --git a/libodb-pgsql/odb/pgsql/binding.hxx b/libodb-pgsql/odb/pgsql/binding.hxx
new file mode 100644
index 0000000..1adf144
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/binding.hxx
@@ -0,0 +1,74 @@
+// file : odb/pgsql/binding.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_BINDING_HXX
+#define ODB_PGSQL_BINDING_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/pgsql-types.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class native_binding
+ {
+ public:
+ native_binding (char** v,
+ int* l,
+ int* f,
+ std::size_t n)
+ : values (v), lengths (l), formats (f), count (n)
+ {
+ }
+
+ char** values;
+ int* lengths;
+ int* formats;
+ std::size_t count;
+
+ private:
+ native_binding (const native_binding&);
+ native_binding& operator= (const native_binding&);
+ };
+
+ class binding
+ {
+ public:
+ typedef pgsql::bind bind_type;
+
+ binding ()
+ : bind (0), count (0), version (0),
+ batch (0), skip (0), status (0) {}
+
+ binding (bind_type* b, std::size_t n)
+ : bind (b), count (n), version (0),
+ batch (0), skip (0), status (0) {}
+
+ binding (bind_type* b, std::size_t n,
+ std::size_t bt, std::size_t s, unsigned long long* st)
+ : bind (b), count (n), version (0),
+ batch (bt), skip (s), status (st) {}
+
+ bind_type* bind;
+ std::size_t count;
+ std::size_t version;
+
+ std::size_t batch;
+ std::size_t skip;
+ unsigned long long* status; // Batch status array.
+
+ private:
+ binding (const binding&);
+ binding& operator= (const binding&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_BINDING_HXX
diff --git a/libodb-pgsql/odb/pgsql/buildfile b/libodb-pgsql/odb/pgsql/buildfile
new file mode 100644
index 0000000..57d38f0
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/buildfile
@@ -0,0 +1,132 @@
+# file : odb/pgsql/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+define cli: file
+cli{*}: extension = cli
+
+import int_libs = libodb%lib{odb}
+import imp_libs = libpq%lib{pq}
+
+lib{odb-pgsql}: {hxx ixx txx cxx}{* -version} {hxx}{version} \
+ details/{hxx ixx txx cxx}{* -options} \
+ $imp_libs $int_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.
+#
+cxx.poptions =+ "-I$out_root" "-I$src_root"
+
+obja{*}: cxx.poptions += -DLIBODB_PGSQL_STATIC_BUILD
+objs{*}: cxx.poptions += -DLIBODB_PGSQL_SHARED_BUILD
+
+# Export options.
+#
+lib{odb-pgsql}:
+{
+ cxx.export.poptions = "-I$out_root" "-I$src_root"
+ cxx.export.libs = $int_libs
+}
+
+liba{odb-pgsql}: cxx.export.poptions += -DLIBODB_PGSQL_STATIC
+libs{odb-pgsql}: cxx.export.poptions += -DLIBODB_PGSQL_SHARED
+
+# For pre-releases use the complete version to make sure they cannot be used
+# in place of another pre-release or the final version. See the version module
+# for details on the version.* variable values.
+#
+if $version.pre_release
+ lib{odb-pgsql}: bin.lib.version = @"-$version.project_id"
+else
+ lib{odb-pgsql}: bin.lib.version = @"-$version.major.$version.minor"
+
+develop = $config.libodb_pgsql.develop
+
+## Consumption build ($develop == false).
+#
+
+# Use pregenerated versions in the consumption build.
+#
+lib{odb-pgsql}: details/pregenerated/{hxx ixx cxx}{**}: include = (!$develop)
+
+if! $develop
+ cxx.poptions =+ "-I($src_base/details/pregenerated)" # Note: must come first.
+
+# Don't install pregenerated headers since they are only used internally in
+# the database implementation (also below).
+#
+details/pregenerated/{hxx ixx}{*}: install = false
+
+# Distribute pregenerated versions only in the consumption build.
+#
+details/pregenerated/{hxx ixx cxx}{*}: dist = (!$develop)
+
+#
+##
+
+## Development build ($develop == true).
+#
+
+lib{odb-pgsql}: details/{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.
+#
+<details/{hxx ixx cxx}{options}>: details/cli{options} $cli
+{
+ install = false
+ dist = ($develop ? pregenerated/odb/pgsql/details/ : false)
+
+ # Symlink the generated code in src for convenience of development.
+ #
+ backlink = true
+}
+%
+if $develop
+{{
+ options = --include-with-brackets --include-prefix odb/pgsql/details \
+ --guard-prefix LIBODB_PGSQL_DETAILS --generate-file-scanner \
+ --cli-namespace odb::pgsql::details::cli --long-usage \
+ --generate-specifier --no-combined-flags
+
+ $cli $options -o $out_base/details/ $path($<[0])
+
+ # If the result differs from the pregenerated version, copy it over.
+ #
+ d = [dir_path] $src_base/details/pregenerated/odb/pgsql/details/
+
+ if diff $d/options.hxx $path($>[0]) >- && \
+ diff $d/options.ixx $path($>[1]) >- && \
+ diff $d/options.cxx $path($>[2]) >-
+ exit
+ end
+
+ cp $path($>[0]) $d/options.hxx
+ cp $path($>[1]) $d/options.ixx
+ cp $path($>[2]) $d/options.cxx
+}}
+
+# Install into the odb/pgsql/ subdirectory of, say, /usr/include/
+# recreating subdirectories.
+#
+install_include = [dir_path] include/odb/pgsql/
+
+{hxx ixx txx}{*}:
+{
+ install = $install_include
+ install.subdirs = true
+}
diff --git a/libodb-pgsql/odb/pgsql/connection-factory.cxx b/libodb-pgsql/odb/pgsql/connection-factory.cxx
new file mode 100644
index 0000000..855a9bc
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/connection-factory.cxx
@@ -0,0 +1,159 @@
+// file : odb/pgsql/connection-factory.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/connection-factory.hxx>
+#include <odb/pgsql/exceptions.hxx>
+
+#include <odb/details/lock.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ using namespace details;
+
+ namespace pgsql
+ {
+ // new_connection_factory
+ //
+ connection_ptr new_connection_factory::
+ connect ()
+ {
+ return connection_ptr (new (shared) connection (*this));
+ }
+
+ // connection_pool_factory
+ //
+ connection_pool_factory::pooled_connection_ptr connection_pool_factory::
+ create ()
+ {
+ return pooled_connection_ptr (new (shared) pooled_connection (*this));
+ }
+
+ connection_pool_factory::
+ ~connection_pool_factory ()
+ {
+ // Wait for all the connections currently in use to return to
+ // the pool.
+ //
+ lock l (mutex_);
+ while (in_use_ != 0)
+ {
+ waiters_++;
+ cond_.wait (l);
+ waiters_--;
+ }
+ }
+
+ connection_ptr connection_pool_factory::
+ connect ()
+ {
+ lock l (mutex_);
+
+ while (true)
+ {
+ // See if we have a spare connection.
+ //
+ if (connections_.size () != 0)
+ {
+ shared_ptr<pooled_connection> c (connections_.back ());
+ connections_.pop_back ();
+
+ c->callback_ = &c->cb_;
+ in_use_++;
+ return c;
+ }
+
+ // See if we can create a new one.
+ //
+ if (max_ == 0 || in_use_ < max_)
+ {
+ shared_ptr<pooled_connection> c (create ());
+ c->callback_ = &c->cb_;
+ in_use_++;
+ return c;
+ }
+
+ // Wait until someone releases a connection.
+ //
+ waiters_++;
+ cond_.wait (l);
+ waiters_--;
+ }
+ }
+
+ void connection_pool_factory::
+ database (database_type& db)
+ {
+ bool first (db_ == 0);
+
+ connection_factory::database (db);
+
+ if (!first)
+ return;
+
+ if (min_ > 0)
+ {
+ connections_.reserve (min_);
+
+ for (size_t i (0); i < min_; ++i)
+ connections_.push_back (create ());
+ }
+ }
+
+ bool connection_pool_factory::
+ release (pooled_connection* c)
+ {
+ c->callback_ = 0;
+
+ lock l (mutex_);
+
+ // Determine if we need to keep or free this connection.
+ //
+ bool keep (!c->failed () &&
+ (waiters_ != 0 ||
+ min_ == 0 ||
+ (connections_.size () + in_use_ <= min_)));
+
+ in_use_--;
+
+ if (keep)
+ {
+ connections_.push_back (pooled_connection_ptr (inc_ref (c)));
+ connections_.back ()->recycle ();
+ }
+
+ if (waiters_ != 0)
+ cond_.signal ();
+
+ return !keep;
+ }
+
+ //
+ // connection_pool_factory::pooled_connection
+ //
+
+ connection_pool_factory::pooled_connection::
+ pooled_connection (connection_pool_factory& f)
+ : connection (f)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ connection_pool_factory::pooled_connection::
+ pooled_connection (connection_pool_factory& f, PGconn* handle)
+ : connection (f, handle)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ bool connection_pool_factory::pooled_connection::
+ zero_counter (void* arg)
+ {
+ pooled_connection* c (static_cast<pooled_connection*> (arg));
+ return static_cast<connection_pool_factory&> (c->factory_).release (c);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/connection-factory.hxx b/libodb-pgsql/odb/pgsql/connection-factory.hxx
new file mode 100644
index 0000000..2f300a8
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/connection-factory.hxx
@@ -0,0 +1,134 @@
+// file : odb/pgsql/connection-factory.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_CONNECTION_FACTORY_HXX
+#define ODB_PGSQL_CONNECTION_FACTORY_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cstddef> // std::size_t
+#include <cassert>
+
+#include <odb/details/mutex.hxx>
+#include <odb/details/condition.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/connection.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class LIBODB_PGSQL_EXPORT new_connection_factory: public connection_factory
+ {
+ public:
+ new_connection_factory () {}
+
+ virtual connection_ptr
+ connect ();
+
+ private:
+ new_connection_factory (const new_connection_factory&);
+ new_connection_factory& operator= (const new_connection_factory&);
+ };
+
+ class LIBODB_PGSQL_EXPORT connection_pool_factory:
+ public connection_factory
+ {
+ public:
+ // The max_connections argument specifies the maximum number of
+ // concurrent connections this pool will maintain. If this value
+ // is 0 then the pool will create a new connection every time all
+ // of the existing connections are in use.
+ //
+ // The min_connections argument specifies the minimum number of
+ // connections that should be maintained by the pool. If the
+ // number of connections maintained by the pool exceeds this
+ // number and there are no active waiters for a new connection,
+ // then the pool will release the excess connections. If this
+ // value is 0 then the pool will maintain all the connections
+ // that were ever created.
+ //
+ connection_pool_factory (std::size_t max_connections = 0,
+ std::size_t min_connections = 0)
+ : max_ (max_connections),
+ min_ (min_connections),
+ in_use_ (0),
+ waiters_ (0),
+ cond_ (mutex_)
+ {
+ // max_connections == 0 means unlimited.
+ //
+ assert (max_connections == 0 || max_connections >= min_connections);
+ }
+
+ virtual connection_ptr
+ connect ();
+
+ virtual void
+ database (database_type&);
+
+ virtual
+ ~connection_pool_factory ();
+
+ private:
+ connection_pool_factory (const connection_pool_factory&);
+ connection_pool_factory& operator= (const connection_pool_factory&);
+
+ protected:
+ class LIBODB_PGSQL_EXPORT pooled_connection: public connection
+ {
+ public:
+ pooled_connection (connection_pool_factory&);
+ pooled_connection (connection_pool_factory&, PGconn*);
+
+ private:
+ static bool
+ zero_counter (void*);
+
+ private:
+ friend class connection_pool_factory;
+
+ shared_base::refcount_callback cb_;
+ };
+
+ friend class pooled_connection;
+
+ typedef details::shared_ptr<pooled_connection> pooled_connection_ptr;
+ typedef std::vector<pooled_connection_ptr> connections;
+
+ // This function is called whenever the pool needs to create a new
+ // connection.
+ //
+ virtual pooled_connection_ptr
+ create ();
+
+ protected:
+ // Return true if the connection should be deleted, false otherwise.
+ //
+ bool
+ release (pooled_connection*);
+
+ protected:
+ const std::size_t max_;
+ const std::size_t min_;
+
+ std::size_t in_use_; // Number of connections currently in use.
+ std::size_t waiters_; // Number of threads waiting for a connection.
+
+ connections connections_;
+
+ details::mutex mutex_;
+ details::condition cond_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_CONNECTION_FACTORY_HXX
diff --git a/libodb-pgsql/odb/pgsql/connection.cxx b/libodb-pgsql/odb/pgsql/connection.cxx
new file mode 100644
index 0000000..80a1dd2
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/connection.cxx
@@ -0,0 +1,141 @@
+// file : odb/pgsql/connection.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <new> // std::bad_alloc
+#include <string>
+#include <cstring> // std::strcmp
+#include <cstdlib> // std::atol
+
+#include <libpq-fe.h>
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/transaction.hxx>
+#include <odb/pgsql/error.hxx>
+#include <odb/pgsql/exceptions.hxx>
+#include <odb/pgsql/statement-cache.hxx>
+
+using namespace std;
+
+extern "C" void
+odb_pgsql_process_notice (void*, const char*)
+{
+}
+
+namespace odb
+{
+ namespace pgsql
+ {
+ connection::
+ connection (connection_factory& cf)
+ : odb::connection (cf), failed_ (false)
+ {
+ database_type& db (database ());
+ handle_.reset (PQconnectdb (db.conninfo ().c_str ()));
+
+ if (handle_ == 0)
+ throw bad_alloc ();
+ else if (PQstatus (handle_) == CONNECTION_BAD)
+ throw database_exception (PQerrorMessage (handle_));
+
+ init ();
+ }
+
+ connection::
+ connection (connection_factory& cf, PGconn* handle)
+ : odb::connection (cf), handle_ (handle), failed_ (false)
+ {
+ init ();
+ }
+
+ void connection::
+ init ()
+ {
+ // Establish whether date/time values are represented as
+ // 8-byte integers.
+ //
+ if (strcmp (PQparameterStatus (handle_, "integer_datetimes"), "on") != 0)
+ throw database_exception ("unsupported binary format for PostgreSQL "
+ "date-time SQL types");
+
+ // Suppress server notifications to stdout.
+ //
+ PQsetNoticeProcessor (handle_, &odb_pgsql_process_notice, 0);
+
+ // Create statement cache.
+ //
+ statement_cache_.reset (new statement_cache_type (*this));
+ }
+
+ connection::
+ ~connection ()
+ {
+ // Destroy prepared query statements before freeing the connections.
+ //
+ recycle ();
+ clear_prepared_map ();
+ }
+
+ int connection::
+ server_version () const
+ {
+ return PQserverVersion (handle_);
+ }
+
+ transaction_impl* connection::
+ begin ()
+ {
+ return new transaction_impl (connection_ptr (inc_ref (this)));
+ }
+
+ unsigned long long connection::
+ execute (const char* s, std::size_t n)
+ {
+ // The string may not be '\0'-terminated.
+ //
+ string str (s, n);
+
+ {
+ odb::tracer* t;
+ if ((t = transaction_tracer ()) ||
+ (t = tracer ()) ||
+ (t = database ().tracer ()))
+ t->execute (*this, str.c_str ());
+ }
+
+ auto_handle<PGresult> h (PQexec (handle_, str.c_str ()));
+
+ unsigned long long count (0);
+
+ if (!is_good_result (h))
+ translate_error (*this, h);
+ else if (PGRES_TUPLES_OK == PQresultStatus (h))
+ count = static_cast<unsigned long long> (PQntuples (h));
+ else
+ {
+ const char* s (PQcmdTuples (h));
+
+ if (s[0] != '\0' && s[1] == '\0')
+ count = static_cast<unsigned long long> (s[0] - '0');
+ else
+ count = static_cast<unsigned long long> (atol (s));
+ }
+
+ return count;
+ }
+
+ // connection_factory
+ //
+ connection_factory::
+ ~connection_factory ()
+ {
+ }
+
+ void connection_factory::
+ database (database_type& db)
+ {
+ odb::connection_factory::db_ = &db;
+ db_ = &db;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/connection.hxx b/libodb-pgsql/odb/pgsql/connection.hxx
new file mode 100644
index 0000000..d779273
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/connection.hxx
@@ -0,0 +1,183 @@
+// file : odb/pgsql/connection.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_CONNECTION_HXX
+#define ODB_PGSQL_CONNECTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/connection.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/unique-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/query.hxx>
+#include <odb/pgsql/tracer.hxx>
+#include <odb/pgsql/transaction-impl.hxx>
+#include <odb/pgsql/auto-handle.hxx>
+#include <odb/pgsql/pgsql-fwd.hxx> // PGconn
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class statement_cache;
+ class connection_factory;
+
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+
+ class LIBODB_PGSQL_EXPORT connection: public odb::connection
+ {
+ public:
+ typedef pgsql::statement_cache statement_cache_type;
+ typedef pgsql::database database_type;
+
+ virtual
+ ~connection ();
+
+ connection (connection_factory&);
+ connection (connection_factory&, PGconn* handle);
+
+ database_type&
+ database ();
+
+ public:
+ virtual transaction_impl*
+ begin ();
+
+ public:
+ using odb::connection::execute;
+
+ virtual unsigned long long
+ execute (const char* statement, std::size_t length);
+
+ // Query preparation.
+ //
+ public:
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const pgsql::query_base&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const odb::query_base&);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef pgsql::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ using odb::connection::tracer;
+
+ public:
+ bool
+ failed () const
+ {
+ return failed_;
+ }
+
+ void
+ mark_failed ()
+ {
+ failed_ = true;
+ }
+
+ public:
+ PGconn*
+ handle ()
+ {
+ return handle_;
+ }
+
+ // Server version as returned by PQserverVersion(), for example, 90200
+ // (9.2.0), 90201 (9.2.1), 100000 (10.0), 110001 (11.1).
+ //
+ int
+ server_version () const;
+
+ statement_cache_type&
+ statement_cache ()
+ {
+ return *statement_cache_;
+ }
+
+ private:
+ connection (const connection&);
+ connection& operator= (const connection&);
+
+ private:
+ void
+ init ();
+
+ private:
+ friend class transaction_impl; // invalidate_results()
+
+ private:
+ auto_handle<PGconn> handle_;
+ bool failed_;
+
+ // Keep statement_cache_ after handle_ so that it is destroyed before
+ // the connection is closed.
+ //
+ details::unique_ptr<statement_cache_type> statement_cache_;
+ };
+
+ class LIBODB_PGSQL_EXPORT connection_factory:
+ public odb::connection_factory
+ {
+ public:
+ typedef pgsql::database database_type;
+
+ virtual void
+ database (database_type&);
+
+ database_type&
+ database () {return *db_;}
+
+ virtual connection_ptr
+ connect () = 0;
+
+ virtual
+ ~connection_factory ();
+
+ connection_factory (): db_ (0) {}
+
+ // Needed to break the circular connection_factory-database dependency
+ // (odb::connection_factory has the odb::database member).
+ //
+ protected:
+ database_type* db_;
+ };
+ }
+}
+
+#include <odb/pgsql/connection.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_CONNECTION_HXX
diff --git a/libodb-pgsql/odb/pgsql/connection.ixx b/libodb-pgsql/odb/pgsql/connection.ixx
new file mode 100644
index 0000000..76d558b
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/connection.ixx
@@ -0,0 +1,44 @@
+// file : odb/pgsql/connection.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace pgsql
+ {
+ inline database& connection::
+ database ()
+ {
+ return static_cast<connection_factory&> (factory_).database ();
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const pgsql::query_base& q)
+ {
+ return query_<T, id_pgsql>::call (*this, n, q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return prepare_query<T> (n, pgsql::query_base (q));
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/container-statements.hxx b/libodb-pgsql/odb/pgsql/container-statements.hxx
new file mode 100644
index 0000000..1856bb9
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/container-statements.hxx
@@ -0,0 +1,425 @@
+// file : odb/pgsql/container-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_CONTAINER_STATEMENTS_HXX
+#define ODB_PGSQL_CONTAINER_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/binding.hxx>
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/pgsql-fwd.hxx> // Oid
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class connection;
+
+ // Template argument is the generated abstract container traits type.
+ // That is, it doesn't need to provide column counts and statements.
+ //
+ template <typename T>
+ class container_statements
+ {
+ public:
+ typedef T traits;
+
+ typedef typename traits::data_image_type data_image_type;
+ typedef typename traits::functions_type functions_type;
+
+ typedef pgsql::insert_statement insert_statement_type;
+ typedef pgsql::select_statement select_statement_type;
+ typedef pgsql::delete_statement delete_statement_type;
+
+ typedef pgsql::connection connection_type;
+
+ container_statements (connection_type&,
+ binding& id,
+ native_binding& idn,
+ const Oid* idt);
+
+ connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // Functions.
+ //
+ functions_type&
+ functions ()
+ {
+ return functions_;
+ }
+
+ // Schema version.
+ //
+ const schema_version_migration&
+ version_migration () const {return *svm_;}
+
+ void
+ version_migration (const schema_version_migration& svm) {svm_ = &svm;}
+
+ // Id image binding (external).
+ //
+ const binding&
+ id_binding ()
+ {
+ return id_binding_;
+ }
+
+ // Data image. The image is split into the id (that comes as a
+ // binding) and index/key plus value which are in data_image_type.
+ // The select binding is a subset of the full binding (no id).
+ //
+ data_image_type&
+ data_image ()
+ {
+ return data_image_;
+ }
+
+ bind*
+ data_bind ()
+ {
+ return insert_image_binding_.bind;
+ }
+
+ bool
+ data_binding_test_version () const
+ {
+ return data_id_binding_version_ != id_binding_.version ||
+ data_image_version_ != data_image_.version ||
+ insert_image_binding_.version == 0;
+ }
+
+ void
+ data_binding_update_version ()
+ {
+ data_id_binding_version_ = id_binding_.version;
+ data_image_version_ = data_image_.version;
+ insert_image_binding_.version++;
+ select_image_binding_.version++;
+ }
+
+ bool*
+ select_image_truncated ()
+ {
+ return select_image_truncated_;
+ }
+
+ //
+ // Statements.
+ //
+
+ insert_statement_type&
+ insert_statement ()
+ {
+ if (insert_ == 0)
+ insert_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ insert_name_,
+ insert_text_,
+ versioned_, // Process if versioned.
+ insert_types_,
+ insert_count_,
+ insert_image_binding_,
+ insert_image_native_binding_,
+ 0,
+ false));
+
+ return *insert_;
+ }
+
+ select_statement_type&
+ select_statement ()
+ {
+ if (select_ == 0)
+ select_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ select_name_,
+ select_text_,
+ versioned_, // Process if versioned.
+ false, // Don't optimize.
+ id_types_,
+ id_binding_.count,
+ id_binding_,
+ id_native_binding_,
+ select_image_binding_,
+ false));
+
+ return *select_;
+ }
+
+ delete_statement_type&
+ delete_statement ()
+ {
+ if (delete_ == 0)
+ delete_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ delete_name_,
+ delete_text_,
+ id_types_,
+ id_binding_.count,
+ id_binding_,
+ id_native_binding_,
+ false));
+
+ return *delete_;
+ }
+
+ private:
+ container_statements (const container_statements&);
+ container_statements& operator= (const container_statements&);
+
+ protected:
+ connection_type& conn_;
+ binding& id_binding_;
+ native_binding& id_native_binding_;
+ const Oid* id_types_;
+
+ functions_type functions_;
+
+ data_image_type data_image_;
+ std::size_t data_image_version_;
+ std::size_t data_id_binding_version_;
+
+ binding insert_image_binding_;
+ native_binding insert_image_native_binding_;
+
+ binding select_image_binding_;
+ bool* select_image_truncated_;
+
+ const char* insert_name_;
+ const char* insert_text_;
+ const Oid* insert_types_;
+ std::size_t insert_count_;
+
+ const char* select_name_;
+ const char* select_text_;
+
+ const char* delete_name_;
+ const char* delete_text_;
+
+ bool versioned_;
+ const schema_version_migration* svm_;
+
+ details::shared_ptr<insert_statement_type> insert_;
+ details::shared_ptr<select_statement_type> select_;
+ details::shared_ptr<delete_statement_type> delete_;
+ };
+
+ template <typename T>
+ class smart_container_statements: public container_statements<T>
+ {
+ public:
+ typedef T traits;
+ typedef typename traits::cond_image_type cond_image_type;
+
+ typedef pgsql::update_statement update_statement_type;
+ typedef pgsql::delete_statement delete_statement_type;
+
+ typedef pgsql::connection connection_type;
+
+ smart_container_statements (connection_type&,
+ binding& id,
+ native_binding& idn,
+ const Oid* idt);
+
+ // Condition image. The image is split into the id (that comes as
+ // a binding) and index/key/value which is in cond_image_type.
+ //
+ cond_image_type&
+ cond_image ()
+ {
+ return cond_image_;
+ }
+
+ bind*
+ cond_bind ()
+ {
+ return cond_image_binding_.bind;
+ }
+
+ bool
+ cond_binding_test_version () const
+ {
+ return cond_id_binding_version_ != this->id_binding_.version ||
+ cond_image_version_ != cond_image_.version ||
+ cond_image_binding_.version == 0;
+ }
+
+ void
+ cond_binding_update_version ()
+ {
+ cond_id_binding_version_ = this->id_binding_.version;
+ cond_image_version_ = cond_image_.version;
+ cond_image_binding_.version++;
+ }
+
+ // Update image. The image is split as follows: value comes
+ // from the data image, id comes as binding, and index/key
+ // comes from the condition image.
+ //
+ bind*
+ update_bind ()
+ {
+ return update_image_binding_.bind;
+ }
+
+ bool
+ update_binding_test_version () const
+ {
+ return update_id_binding_version_ != this->id_binding_.version ||
+ update_cond_image_version_ != cond_image_.version ||
+ update_data_image_version_ != this->data_image_.version ||
+ update_image_binding_.version == 0;
+ }
+
+ void
+ update_binding_update_version ()
+ {
+ update_id_binding_version_ = this->id_binding_.version;
+ update_cond_image_version_ = cond_image_.version;
+ update_data_image_version_ = this->data_image_.version;
+ update_image_binding_.version++;
+ }
+
+ //
+ // Statements.
+ //
+
+ delete_statement_type&
+ delete_statement ()
+ {
+ if (this->delete_ == 0)
+ this->delete_.reset (
+ new (details::shared) delete_statement_type (
+ this->conn_,
+ this->delete_name_,
+ this->delete_text_,
+ delete_types_,
+ delete_count_,
+ cond_image_binding_,
+ cond_image_native_binding_,
+ false));
+
+ return *this->delete_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ this->conn_,
+ update_name_,
+ update_text_,
+ this->versioned_, // Process if versioned.
+ update_types_,
+ update_count_,
+ update_image_binding_,
+ update_image_native_binding_,
+ false));
+
+ return *update_;
+ }
+
+ protected:
+ cond_image_type cond_image_;
+ std::size_t cond_image_version_;
+ std::size_t cond_id_binding_version_;
+ binding cond_image_binding_;
+ native_binding cond_image_native_binding_;
+
+ std::size_t update_id_binding_version_;
+ std::size_t update_cond_image_version_;
+ std::size_t update_data_image_version_;
+ binding update_image_binding_;
+ native_binding update_image_native_binding_;
+
+ const char* update_name_;
+ const char* update_text_;
+ const Oid* update_types_;
+ std::size_t update_count_;
+
+ const Oid* delete_types_;
+ std::size_t delete_count_;
+
+ details::shared_ptr<update_statement_type> update_;
+ };
+
+ // Template argument is the generated concrete container traits type.
+ //
+ template <typename T>
+ class container_statements_impl: public T::statements_type
+ {
+ public:
+ typedef T traits;
+ typedef typename T::statements_type base;
+ typedef pgsql::connection connection_type;
+
+ container_statements_impl (connection_type&,
+ binding&,
+ native_binding&,
+ const Oid*);
+ private:
+ container_statements_impl (const container_statements_impl&);
+ container_statements_impl& operator= (const container_statements_impl&);
+
+ private:
+ bind data_image_bind_[traits::data_column_count];
+ char* data_image_values_[traits::data_column_count];
+ int data_image_lengths_[traits::data_column_count];
+ int data_image_formats_[traits::data_column_count];
+
+ bool select_image_truncated_array_[traits::data_column_count -
+ traits::id_column_count];
+ };
+
+ template <typename T>
+ class smart_container_statements_impl: public container_statements_impl<T>
+ {
+ public:
+ typedef T traits;
+ typedef pgsql::connection connection_type;
+
+ smart_container_statements_impl (connection_type&,
+ binding&,
+ native_binding&,
+ const Oid*);
+ private:
+ bind cond_image_bind_[traits::cond_column_count];
+ char* cond_image_values_[traits::cond_column_count];
+ int cond_image_lengths_[traits::cond_column_count];
+ int cond_image_formats_[traits::cond_column_count];
+
+ bind update_image_bind_[traits::value_column_count +
+ traits::cond_column_count];
+ char* update_image_values_[traits::value_column_count +
+ traits::cond_column_count];
+ int update_image_lengths_[traits::value_column_count +
+ traits::cond_column_count];
+ int update_image_formats_[traits::value_column_count +
+ traits::cond_column_count];
+ };
+ }
+}
+
+#include <odb/pgsql/container-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_CONTAINER_STATEMENTS_HXX
diff --git a/libodb-pgsql/odb/pgsql/container-statements.txx b/libodb-pgsql/odb/pgsql/container-statements.txx
new file mode 100644
index 0000000..34eb99f
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/container-statements.txx
@@ -0,0 +1,154 @@
+// file : odb/pgsql/container-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace pgsql
+ {
+ // container_statements
+ //
+ template <typename T>
+ container_statements<T>::
+ container_statements (connection_type& conn,
+ binding& id,
+ native_binding& idn,
+ const Oid* idt)
+ : conn_ (conn),
+ id_binding_ (id),
+ id_native_binding_ (idn),
+ id_types_ (idt),
+ functions_ (this),
+ insert_image_binding_ (0, 0), // Initialized by impl.
+ insert_image_native_binding_ (0, 0, 0, 0), // Initialized by impl.
+ select_image_binding_ (0, 0), // Initialized by impl.
+ svm_ (0)
+ {
+ functions_.insert_ = &traits::insert;
+ functions_.select_ = &traits::select;
+ functions_.delete__ = &traits::delete_;
+
+ data_image_.version = 0;
+ data_image_version_ = 0;
+ data_id_binding_version_ = 0;
+ }
+
+ // smart_container_statements
+ //
+ template <typename T>
+ smart_container_statements<T>::
+ smart_container_statements (connection_type& conn,
+ binding& id,
+ native_binding& idn,
+ const Oid* idt)
+ : container_statements<T> (conn, id, idn, idt),
+ cond_image_binding_ (0, 0), // Initialized by impl.
+ cond_image_native_binding_ (0, 0, 0, 0), // Initialized by impl.
+ update_image_binding_ (0, 0), // Initialized by impl.
+ update_image_native_binding_ (0, 0, 0, 0) // Initialized by impl.
+ {
+ this->functions_.update_ = &traits::update;
+
+ cond_image_.version = 0;
+ cond_image_version_ = 0;
+ cond_id_binding_version_ = 0;
+
+ update_id_binding_version_ = 0;
+ update_cond_image_version_ = 0;
+ update_data_image_version_ = 0;
+ }
+
+ // container_statements_impl
+ //
+ template <typename T>
+ container_statements_impl<T>::
+ container_statements_impl (connection_type& conn,
+ binding& id,
+ native_binding& idn,
+ const Oid* idt)
+ : base (conn, id, idn, idt)
+ {
+ this->select_image_truncated_ = select_image_truncated_array_;
+
+ this->insert_image_binding_.bind = data_image_bind_;
+ this->insert_image_binding_.count = traits::data_column_count;
+
+ this->select_image_binding_.bind = data_image_bind_ +
+ traits::id_column_count;
+ this->select_image_binding_.count = traits::data_column_count -
+ traits::id_column_count;
+
+ this->insert_image_native_binding_.values = data_image_values_;
+ this->insert_image_native_binding_.lengths = data_image_lengths_;
+ this->insert_image_native_binding_.formats = data_image_formats_;
+ this->insert_image_native_binding_.count = traits::data_column_count;
+
+ std::memset (data_image_bind_, 0, sizeof (data_image_bind_));
+ std::memset (select_image_truncated_array_,
+ 0,
+ sizeof (select_image_truncated_array_));
+
+ for (std::size_t i (0);
+ i < traits::data_column_count - traits::id_column_count;
+ ++i)
+ data_image_bind_[i + traits::id_column_count].truncated =
+ select_image_truncated_array_ + i;
+
+ this->insert_name_ = traits::insert_name;
+ this->insert_text_ = traits::insert_statement;
+ this->insert_types_ = traits::insert_types;
+ this->insert_count_ = traits::data_column_count;
+
+ this->select_name_ = traits::select_name;
+ this->select_text_ = traits::select_statement;
+
+ this->delete_name_ = traits::delete_name;
+ this->delete_text_ = traits::delete_statement;
+
+ this->versioned_ = traits::versioned;
+ }
+
+ // smart_container_statements_impl
+ //
+ template <typename T>
+ smart_container_statements_impl<T>::
+ smart_container_statements_impl (connection_type& conn,
+ binding& id,
+ native_binding& idn,
+ const Oid* idt)
+ : container_statements_impl<T> (conn, id, idn, idt)
+ {
+ this->cond_image_binding_.bind = cond_image_bind_;
+ this->cond_image_binding_.count = traits::cond_column_count;
+
+ this->update_image_binding_.bind = update_image_bind_;
+ this->update_image_binding_.count = traits::value_column_count +
+ traits::cond_column_count;
+
+ this->cond_image_native_binding_.values = cond_image_values_;
+ this->cond_image_native_binding_.lengths = cond_image_lengths_;
+ this->cond_image_native_binding_.formats = cond_image_formats_;
+ this->cond_image_native_binding_.count = traits::cond_column_count;
+
+ this->update_image_native_binding_.values = update_image_values_;
+ this->update_image_native_binding_.lengths = update_image_lengths_;
+ this->update_image_native_binding_.formats = update_image_formats_;
+ this->update_image_native_binding_.count = traits::value_column_count +
+ traits::cond_column_count;
+
+ std::memset (cond_image_bind_, 0, sizeof (cond_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+
+ this->update_name_ = traits::update_name;
+ this->update_text_ = traits::update_statement;
+ this->update_types_ = traits::update_types;
+ this->update_count_ = traits::value_column_count +
+ traits::cond_column_count;
+
+ this->delete_types_ = traits::delete_types;
+ this->delete_count_ = traits::cond_column_count;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/database.cxx b/libodb-pgsql/odb/pgsql/database.cxx
new file mode 100644
index 0000000..09bf6f0
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/database.cxx
@@ -0,0 +1,424 @@
+// file : odb/pgsql/database.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // strlen()
+#include <sstream>
+
+#include <odb/pgsql/traits.hxx>
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/connection-factory.hxx>
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/transaction.hxx>
+#include <odb/pgsql/exceptions.hxx>
+
+#include <odb/pgsql/details/options.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace pgsql
+ {
+ using odb::details::transfer_ptr;
+
+ database::
+ database (const string& user,
+ const string& password,
+ const string& db,
+ const string& host,
+ unsigned int port,
+ const string& extra_conninfo,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_pgsql),
+ user_ (user),
+ password_ (password),
+ db_ (db),
+ host_ (host),
+ port_ (port),
+ extra_conninfo_ (extra_conninfo),
+ factory_ (factory.transfer ())
+ {
+ ostringstream ss;
+
+ if (!user.empty ())
+ ss << "user='" << user << "' ";
+
+ if (!password.empty ())
+ ss << "password='" << password << "' ";
+
+ if (!db.empty ())
+ ss << "dbname='" << db << "' ";
+
+ if (!host.empty ())
+ ss << "host='" << host << "' ";
+
+ if (port)
+ ss << "port=" << port << " ";
+
+ // Only the last occurence of keyword/value pair is used by libpq.
+ // extra_conninfo specified options take precedence.
+ //
+ if (!extra_conninfo.empty ())
+ ss << extra_conninfo;
+
+ conninfo_ = ss.str ();
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (const string& user,
+ const string& password,
+ const string& db,
+ const string& host,
+ const string& socket_ext,
+ const string& extra_conninfo,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_pgsql),
+ user_ (user),
+ password_ (password),
+ db_ (db),
+ host_ (host),
+ port_ (0),
+ socket_ext_ (socket_ext),
+ extra_conninfo_ (extra_conninfo),
+ factory_ (factory.transfer ())
+ {
+ ostringstream ss;
+
+ if (!user.empty ())
+ ss << "user='" << user << "' ";
+
+ if (!password.empty ())
+ ss << "password='" << password << "' ";
+
+ if (!db.empty ())
+ ss << "dbname='" << db << "' ";
+
+ if (!host.empty ())
+ ss << "host='" << host << "' ";
+
+ if (!socket_ext.empty ())
+ ss << "port='" << socket_ext << "' ";
+
+ // Only the last occurence of keyword/value pair is used by libpq.
+ // extra_conninfo specified options take precedence.
+ //
+ if (!extra_conninfo.empty ())
+ ss << extra_conninfo;
+
+ conninfo_ = ss.str ();
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (const string& conninfo, transfer_ptr<connection_factory> factory)
+ : odb::database (id_pgsql),
+ port_ (0),
+ conninfo_ (conninfo),
+ factory_ (factory.transfer ())
+ {
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (int& argc,
+ char* argv[],
+ bool erase,
+ const string& extra_conninfo,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_pgsql), port_ (0), factory_ (factory.transfer ())
+ {
+ using namespace details;
+
+ try
+ {
+ cli::argv_file_scanner scan (argc, argv, "--options-file", erase);
+ options ops (scan, cli::unknown_mode::skip, cli::unknown_mode::skip);
+
+ ostringstream oss;
+
+ if (ops.user_specified ())
+ {
+ user_ = ops.user ();
+ oss << "user='" << user_ << "' ";
+ }
+
+ if (ops.password_specified ())
+ {
+ password_ = ops.password ();
+ oss << "password='" << password_ << "' ";
+ }
+
+ if (ops.database_specified ())
+ {
+ db_ = ops.database ();
+ oss << "dbname='" << db_ << "' ";
+ }
+
+ if (ops.host_specified ())
+ {
+ host_ = ops.host ();
+ oss << "host='" << host_ << "' ";
+ }
+
+ if (ops.port_specified ())
+ {
+ istringstream iss (ops.port ());
+
+ if (iss >> port_ && iss.eof ())
+ oss << " port=" << port_ << " ";
+ else
+ {
+ port_ = 0;
+ socket_ext_ = ops.port ();
+ oss << "port='" << socket_ext_ << "' ";
+ }
+ }
+
+ if (!extra_conninfo.empty ())
+ oss << extra_conninfo;
+
+ conninfo_ = oss.str ();
+ }
+ catch (const cli::exception& e)
+ {
+ ostringstream oss;
+ oss << e;
+ throw cli_exception (oss.str ());
+ }
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ void database::
+ print_usage (std::ostream& os)
+ {
+ details::options::print_usage (os);
+ }
+
+ database::
+ ~database ()
+ {
+ }
+
+ transaction_impl* database::
+ begin ()
+ {
+ return new transaction_impl (*this);
+ }
+
+ odb::connection* database::
+ connection_ ()
+ {
+ connection_ptr c (factory_->connect ());
+ return c.release ();
+ }
+
+ const database::schema_version_info& database::
+ load_schema_version (const string& name) const
+ {
+ schema_version_info& svi (schema_version_map_[name]);
+
+ // Quoted table name.
+ //
+ const char* table (
+ !svi.version_table.empty () ? svi.version_table.c_str () :
+ !schema_version_table_.empty () ? schema_version_table_.c_str () :
+ /* */ "\"schema_version\"");
+
+ // Construct the SELECT statement text.
+ //
+ string text ("SELECT \"version\", \"migration\" FROM ");
+ text += table;
+ text += " WHERE \"name\" = $1";
+
+ // Bind parameters and results.
+ //
+ char* pbuf[1] = {const_cast<char*> (name.c_str ())};
+ size_t psize[1] = {name.size ()};
+ bool pnull[1] = {false};
+ bind pbind[1] = {{bind::text,
+ &pbuf[0],
+ &psize[0],
+ psize[0],
+ &pnull[0],
+ 0}};
+ binding param (pbind, 1);
+ param.version++;
+
+ unsigned int param_types[1] = {text_oid};
+
+ char* values[1];
+ int lengths[1];
+ int formats[1];
+ native_binding nparam (values, lengths, formats, 1);
+
+ long long version;
+ bool rnull[2];
+ bind rbind[2] = {{bind::bigint, &version, 0, 0, &rnull[0], 0},
+ {bind::boolean_, &svi.migration, 0, 0, &rnull[1], 0}};
+ binding result (rbind, 2);
+ result.version++;
+
+ // If we are not in transaction, PostgreSQL will start an implicit one
+ // which suits us just fine.
+ //
+ connection_ptr cp;
+ if (!transaction::has_current ())
+ cp = factory_->connect ();
+
+ pgsql::connection& c (
+ cp != 0
+ ? *cp
+ : transaction::current ().connection (const_cast<database&> (*this)));
+
+ // If we are in the user's transaction then things are complicated. When
+ // we try to execute SELECT on a non-existent table, PG "poisons" the
+ // transaction (those "current transaction is aborted, commands ignored
+ // until end of transaction block" messages in the log). Which means all
+ // the user's schema creation statements that are likely to follow will
+ // fail.
+ //
+ // There doesn't seem to be a better way to solve this than to check for
+ // the table's existence. It is relatively easy to do with to_regclass()
+ // in 9.4+ and a real pain in earlier versions. So we are going to do
+ // this for 9.4+ and for older versions the workaround is to "pre-call"
+ // database::schema_version() outside of any transaction.
+ //
+ bool exists (true);
+ if (cp == 0 && c.server_version () >= 90400)
+ {
+ char* pbuf[1] = {const_cast<char*> (table)};
+ size_t psize[1] = {strlen (table)};
+ bool pnull[1] = {false};
+ bind pbind[1] = {{bind::text,
+ &pbuf[0],
+ &psize[0],
+ psize[0],
+ &pnull[0],
+ 0}};
+ binding param (pbind, 1);
+ param.version++;
+
+ unsigned int param_types[1] = {text_oid};
+
+ char* values[1];
+ int lengths[1];
+ int formats[1];
+ native_binding nparam (values, lengths, formats, 1);
+
+ bool rnull[1];
+ bind rbind[1] = {{bind::boolean_, &exists, 0, 0, &rnull[0], 0}};
+ binding result (rbind, 1);
+ result.version++;
+
+ // Note that to_regclass() seems happy to accept a quoted table name.
+ //
+ // Also note that starting 9.6 it requires text type rather than
+ // cstring type.
+ //
+ select_statement st (c,
+ "odb_database_schema_version_exists",
+ c.server_version () >= 90600
+ ? "SELECT to_regclass($1::text) IS NOT NULL"
+ : "SELECT to_regclass($1::cstring) IS NOT NULL",
+ false, // Don't process.
+ false, // Don't optimize.
+ param_types,
+ 1,
+ param,
+ nparam,
+ result,
+ false);
+
+ st.execute ();
+ auto_result ar (st);
+
+ switch (st.fetch ())
+ {
+ case select_statement::success:
+ {
+ assert (st.fetch () == select_statement::no_data);
+ break;
+ }
+ case select_statement::no_data:
+ case select_statement::truncated:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ // Assume no schema until determined otherwise.
+ //
+ svi.version = 0;
+
+ if (exists)
+ {
+ try
+ {
+ select_statement st (c,
+ "odb_database_schema_version_query",
+ text.c_str (),
+ false, // Don't process.
+ false, // Don't optimize.
+ param_types,
+ 1,
+ param,
+ nparam,
+ result,
+ false);
+ st.execute ();
+ auto_result ar (st);
+
+ switch (st.fetch ())
+ {
+ case select_statement::success:
+ {
+ value_traits<unsigned long long, id_bigint>::set_value (
+ svi.version, version, rnull[0]);
+ assert (st.fetch () == select_statement::no_data);
+ break;
+ }
+ case select_statement::no_data:
+ {
+ // No schema.
+ break;
+ }
+ case select_statement::truncated:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+ catch (const database_exception& e)
+ {
+ // Detect the case where there is no version table (the implicit
+ // transaction case).
+ //
+ if (e.sqlstate () != "42P01")
+ throw;
+ }
+ }
+
+ return svi;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/database.hxx b/libodb-pgsql/odb/pgsql/database.hxx
new file mode 100644
index 0000000..950cad1
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/database.hxx
@@ -0,0 +1,527 @@
+// file : odb/pgsql/database.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_DATABASE_HXX
+#define ODB_PGSQL_DATABASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <iosfwd> // std::ostream
+
+#include <odb/database.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+#include <odb/details/unique-ptr.hxx>
+#include <odb/details/transfer-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/query.hxx>
+#include <odb/pgsql/tracer.hxx>
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/connection-factory.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class transaction_impl;
+
+ class LIBODB_PGSQL_EXPORT 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 = "",
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ 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 = "",
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ explicit
+ database (const std::string& conninfo,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Extract the database parameters from the command line. The
+ // following options are recognized:
+ //
+ // --user | --username
+ // --password
+ // --database | --dbname
+ // --host
+ // --port
+ // --options-file
+ //
+ // For more information, see the output of the print_usage() function
+ // below. If erase is true, the above options are removed from the
+ // argv array and the argc count is updated accordingly. This
+ // constructor may throw the cli_exception exception.
+ //
+ database (int& argc,
+ char* argv[],
+ bool erase = false,
+ const std::string& extra_conninfo = "",
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Move-constructible but not move-assignable.
+ //
+ // Note: noexcept is not specified since odb::database(odb::database&&)
+ // can throw.
+ //
+#ifdef ODB_CXX11
+ database (database&&);
+#endif
+
+ static void
+ print_usage (std::ostream&);
+
+ // Object persistence API.
+ //
+ public:
+
+ // Make the object persistent.
+ //
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Bulk persist. Can be a range of references or pointers (including
+ // smart pointers) to objects.
+ //
+ template <typename I>
+ void
+ persist (I begin, I end, bool continue_failed = true);
+
+ // Load an object. Throw object_not_persistent if not found.
+ //
+ 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);
+
+ // Load (or reload, if it is already loaded) a section of an object.
+ //
+ template <typename T>
+ void
+ load (T& object, section&);
+
+ // Reload an object.
+ //
+ template <typename T>
+ void
+ reload (T& object);
+
+ template <typename T>
+ void
+ reload (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ reload (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Loan an object if found. Return NULL/false if not found.
+ //
+ 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);
+
+ // Update the state of a modified objects.
+ //
+ template <typename T>
+ void
+ update (T& object);
+
+ template <typename T>
+ void
+ update (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ update (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Bulk update. Can be a range of references or pointers (including
+ // smart pointers) to objects.
+ //
+ template <typename I>
+ void
+ update (I begin, I end, bool continue_failed = true);
+
+ // Update a section of an object. Throws the section_not_loaded
+ // exception if the section is not loaded. Note also that this
+ // function does not clear the changed flag if it is set.
+ //
+ template <typename T>
+ void
+ update (const T& object, const section&);
+
+ // Make the object transient. Throw object_not_persistent if not
+ // found.
+ //
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::id_type& id);
+
+ template <typename T>
+ void
+ erase (T& object);
+
+ template <typename T>
+ void
+ erase (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Bulk erase.
+ //
+ template <typename T, typename I>
+ void
+ erase (I id_begin, I id_end, bool continue_failed = true);
+
+ // Can be a range of references or pointers (including smart pointers)
+ // to objects.
+ //
+ template <typename I>
+ void
+ erase (I obj_begin, I obj_end, bool continue_failed = true);
+
+ // Erase multiple objects matching a query predicate.
+ //
+ template <typename T>
+ unsigned long long
+ erase_query ();
+
+ template <typename T>
+ unsigned long long
+ erase_query (const char*);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const std::string&);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const pgsql::query_base&);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const odb::query_base&);
+
+ // Query API.
+ //
+ template <typename T>
+ result<T>
+ query ();
+
+ template <typename T>
+ result<T>
+ query (const char*);
+
+ template <typename T>
+ result<T>
+ query (const std::string&);
+
+ template <typename T>
+ result<T>
+ query (const pgsql::query_base&);
+
+ template <typename T>
+ result<T>
+ query (const odb::query_base&);
+
+ // Query one API.
+ //
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one ();
+
+ template <typename T>
+ bool
+ query_one (T& object);
+
+ template <typename T>
+ T
+ query_value ();
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const char*);
+
+ template <typename T>
+ bool
+ query_one (const char*, T& object);
+
+ template <typename T>
+ T
+ query_value (const char*);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const std::string&);
+
+ template <typename T>
+ bool
+ query_one (const std::string&, T& object);
+
+ template <typename T>
+ T
+ query_value (const std::string&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const pgsql::query_base&);
+
+ template <typename T>
+ bool
+ query_one (const pgsql::query_base&, T& object);
+
+ template <typename T>
+ T
+ query_value (const pgsql::query_base&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const odb::query_base&);
+
+ template <typename T>
+ bool
+ query_one (const odb::query_base&, T& object);
+
+ template <typename T>
+ T
+ query_value (const odb::query_base&);
+
+ // Query preparation.
+ //
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const pgsql::query_base&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const odb::query_base&);
+
+ // Transactions.
+ //
+ public:
+ virtual transaction_impl*
+ begin ();
+
+ public:
+ connection_ptr
+ connection ();
+
+ // Database schema version.
+ //
+ protected:
+ virtual const schema_version_info&
+ load_schema_version (const std::string& schema_name) const;
+
+ public:
+ // Database id constant (useful for meta-programming).
+ //
+ static const odb::database_id database_id = id_pgsql;
+
+ public:
+ virtual
+ ~database ();
+
+ protected:
+ virtual odb::connection*
+ connection_ ();
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef pgsql::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::database::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::database::tracer (t);
+ }
+
+ using odb::database::tracer;
+
+ public:
+ const std::string&
+ user () const
+ {
+ return user_;
+ }
+
+ const std::string&
+ password () const
+ {
+ return password_;
+ }
+
+ const std::string&
+ db () const
+ {
+ return db_;
+ }
+
+ const std::string&
+ host () const
+ {
+ return host_;
+ }
+
+ unsigned int
+ port () const
+ {
+ return port_;
+ }
+
+ const std::string&
+ socket_ext () const
+ {
+ return socket_ext_;
+ }
+
+ const std::string&
+ extra_conninfo () const
+ {
+ return extra_conninfo_;
+ }
+
+ const std::string&
+ conninfo () const
+ {
+ return conninfo_;
+ }
+
+ private:
+ // Note: remember to update move ctor if adding any new members.
+ //
+ std::string user_;
+ std::string password_;
+ std::string db_;
+ std::string host_;
+ unsigned int port_;
+ std::string socket_ext_;
+ std::string extra_conninfo_;
+ std::string conninfo_;
+ details::unique_ptr<connection_factory> factory_;
+ };
+ }
+}
+
+#include <odb/pgsql/database.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_DATABASE_HXX
diff --git a/libodb-pgsql/odb/pgsql/database.ixx b/libodb-pgsql/odb/pgsql/database.ixx
new file mode 100644
index 0000000..f04c3e6
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/database.ixx
@@ -0,0 +1,638 @@
+// file : odb/pgsql/database.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <utility> // move()
+
+#include <odb/pgsql/transaction.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+#ifdef ODB_CXX11
+ inline database::
+ database (database&& db) // Has to be inline.
+ : odb::database (std::move (db)),
+ user_ (std::move (db.user_)),
+ password_ (std::move (db.password_)),
+ db_ (std::move (db.db_)),
+ host_ (std::move (db.host_)),
+ port_ (db.port_),
+ socket_ext_ (std::move (db.socket_ext_)),
+ extra_conninfo_ (std::move (db.extra_conninfo_)),
+ conninfo_ (std::move (db.conninfo_)),
+ factory_ (std::move (db.factory_))
+ {
+ factory_->database (*this); // New database instance.
+ }
+#endif
+
+ inline connection_ptr database::
+ connection ()
+ {
+ // Go through the virtual connection_() function instead of
+ // directly to allow overriding.
+ //
+ return connection_ptr (
+ static_cast<pgsql::connection*> (connection_ ()));
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T& obj)
+ {
+ return persist_<T, id_pgsql> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const T& obj)
+ {
+ return persist_<const T, id_pgsql> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T>& p)
+ {
+ const P<T>& cr (p);
+ return persist<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ return persist<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const typename object_traits<T>::pointer_type& pobj)
+ {
+ return persist_<T, id_pgsql> (pobj);
+ }
+
+ template <typename I>
+ inline void database::
+ persist (I b, I e, bool cont)
+ {
+ persist_<I, id_pgsql> (b, e, cont);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ load (const typename object_traits<T>::id_type& id)
+ {
+ return load_<T, id_pgsql> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ load (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return load_<T, id_pgsql> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ load (T& obj, section& s)
+ {
+ return load_<T, id_pgsql> (obj, s);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ find (const typename object_traits<T>::id_type& id)
+ {
+ return find_<T, id_pgsql> (id);
+ }
+
+ template <typename T>
+ inline bool database::
+ find (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return find_<T, id_pgsql> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T& obj)
+ {
+ reload_<T, id_pgsql> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T* p)
+ {
+ reload<T> (*p);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (const P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (const P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T>
+ inline void database::
+ reload (const typename object_traits<T>::pointer_type& pobj)
+ {
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ reload (odb::pointer_traits<pointer_type>::get_ref (pobj));
+ }
+
+ template <typename T>
+ inline void database::
+ update (T& obj)
+ {
+ update_<T, id_pgsql> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ update (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (P<T>& p)
+ {
+ const P<T>& cr (p);
+ update<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ update<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const typename object_traits<T>::pointer_type& pobj)
+ {
+ update_<T, id_pgsql> (pobj);
+ }
+
+ template <typename I>
+ inline void database::
+ update (I b, I e, bool cont)
+ {
+ update_<I, id_pgsql> (b, e, cont);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const T& obj, const section& s)
+ {
+ update_<T, id_pgsql> (obj, s);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::id_type& id)
+ {
+ return erase_<T, id_pgsql> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T& obj)
+ {
+ return erase_<T, id_pgsql> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (P<T>& p)
+ {
+ const P<T>& cr (p);
+ erase<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ erase<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::pointer_type& pobj)
+ {
+ erase_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, typename I>
+ inline void database::
+ erase (I idb, I ide, bool cont)
+ {
+ erase_id_<I, T, id_pgsql> (idb, ide, cont);
+ }
+
+ template <typename I>
+ inline void database::
+ erase (I ob, I oe, bool cont)
+ {
+ erase_object_<I, id_pgsql> (ob, oe, cont);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query ()
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (pgsql::query_base ());
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const char* q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const std::string& q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const pgsql::query_base& q)
+ {
+ // T is always object_type.
+ //
+ return object_traits_impl<T, id_pgsql>::erase_query (*this, q);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return erase_query<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query ()
+ {
+ return query<T> (pgsql::query_base ());
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const char* q)
+ {
+ return query<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const std::string& q)
+ {
+ return query<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const pgsql::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_<T, id_pgsql>::call (*this, q);
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one ()
+ {
+ return query_one<T> (pgsql::query_base ());
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (T& o)
+ {
+ return query_one<T> (pgsql::query_base (), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value ()
+ {
+ return query_value<T> (pgsql::query_base ());
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const char* q)
+ {
+ return query_one<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const char* q, T& o)
+ {
+ return query_one<T> (pgsql::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const char* q)
+ {
+ return query_value<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const std::string& q)
+ {
+ return query_one<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const std::string& q, T& o)
+ {
+ return query_one<T> (pgsql::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const std::string& q)
+ {
+ return query_value<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const pgsql::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_one_<T, id_pgsql> (q);
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const pgsql::query_base& q, T& o)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_one_<T, id_pgsql> (q, o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const pgsql::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_value_<T, id_pgsql> (q);
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query_one<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const odb::query_base& q, T& o)
+ {
+ // Translate to native query.
+ //
+ return query_one<T> (pgsql::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query_value<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const pgsql::query_base& q)
+ {
+ // Throws if not in transaction.
+ //
+ pgsql::connection& c (transaction::current ().connection (*this));
+ return c.prepare_query<T> (n, q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return prepare_query<T> (n, pgsql::query_base (q));
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/details/.gitignore b/libodb-pgsql/odb/pgsql/details/.gitignore
new file mode 100644
index 0000000..b298f89
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/.gitignore
@@ -0,0 +1 @@
+/options.?xx
diff --git a/libodb-pgsql/odb/pgsql/details/config.hxx b/libodb-pgsql/odb/pgsql/details/config.hxx
new file mode 100644
index 0000000..5bed468
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/config.hxx
@@ -0,0 +1,15 @@
+// file : odb/pgsql/details/config.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_DETAILS_CONFIG_HXX
+#define ODB_PGSQL_DETAILS_CONFIG_HXX
+
+// no pre
+
+#ifdef ODB_COMPILER
+# error libodb-pgsql header included in odb-compiled header
+#endif
+
+// no post
+
+#endif // ODB_PGSQL_DETAILS_CONFIG_HXX
diff --git a/libodb-pgsql/odb/pgsql/details/conversion.hxx b/libodb-pgsql/odb/pgsql/details/conversion.hxx
new file mode 100644
index 0000000..b4d7e4f
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/conversion.hxx
@@ -0,0 +1,58 @@
+// file : odb/pgsql/details/conversion.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_DETAILS_CONVERSION_HXX
+#define ODB_PGSQL_DETAILS_CONVERSION_HXX
+
+#include <odb/pgsql/traits.hxx>
+
+#include <odb/details/meta/answer.hxx>
+
+namespace odb
+{
+ // @@ Revise this.
+ //
+ namespace details {}
+
+ namespace pgsql
+ {
+ namespace details
+ {
+ using namespace odb::details;
+
+ // Detect whether conversion is specified in type_traits.
+ //
+ template <typename T>
+ meta::yes
+ conversion_p_test (typename type_traits<T>::conversion*);
+
+ template <typename T>
+ meta::no
+ conversion_p_test (...);
+
+ template <typename T>
+ struct conversion_p
+ {
+ static const bool value =
+ sizeof (conversion_p_test<T> (0)) == sizeof (meta::yes);
+ };
+
+ template <typename T, bool = conversion_p<T>::value>
+ struct conversion;
+
+ template <typename T>
+ struct conversion<T, true>
+ {
+ static const char* to () {return type_traits<T>::conversion::to ();}
+ };
+
+ template <typename T>
+ struct conversion<T, false>
+ {
+ static const char* to () {return 0;}
+ };
+ }
+ }
+}
+
+#endif // ODB_PGSQL_DETAILS_CONVERSION_HXX
diff --git a/libodb-pgsql/odb/pgsql/details/endian-traits.cxx b/libodb-pgsql/odb/pgsql/details/endian-traits.cxx
new file mode 100644
index 0000000..77fdc95
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/endian-traits.cxx
@@ -0,0 +1,30 @@
+// file : odb/pgsql/details/endian-traits.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/details/endian-traits.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ namespace details
+ {
+ namespace
+ {
+ endian_traits::endian
+ infer_host_endian ()
+ {
+ short s (1);
+ char* c (reinterpret_cast<char*> (&s));
+
+ return *c == 0 ?
+ endian_traits::big :
+ endian_traits::little;
+ }
+ }
+
+ const endian_traits::endian endian_traits::host_endian (
+ infer_host_endian ());
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/details/endian-traits.hxx b/libodb-pgsql/odb/pgsql/details/endian-traits.hxx
new file mode 100644
index 0000000..532a4a3
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/endian-traits.hxx
@@ -0,0 +1,155 @@
+// file : odb/pgsql/details/endian-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_DETAILS_ENDIAN_TRAITS_HXX
+#define ODB_PGSQL_DETAILS_ENDIAN_TRAITS_HXX
+
+#include <cstddef> // std::size_t
+#include <algorithm> // std::reverse
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ // @@ Revise this.
+ //
+ namespace details
+ {
+ }
+
+ namespace pgsql
+ {
+ namespace details
+ {
+ using namespace odb::details;
+
+ template <typename T, std::size_t S = sizeof (T)>
+ struct swap_endian;
+
+ template <typename T>
+ struct swap_endian<T, 1>
+ {
+ static T
+ swap (T x)
+ {
+ return x;
+ }
+ };
+
+ template <typename T>
+ struct swap_endian<T, 2>
+ {
+ static T
+ swap (T x)
+ {
+ union u2
+ {
+ T t;
+ char c[2];
+ };
+
+ u2 u;
+ u.t = x;
+
+ char tmp (u.c[0]);
+ u.c[0] = u.c[1];
+ u.c[1] = tmp;
+
+ return u.t;
+ }
+ };
+
+ template <typename T>
+ struct swap_endian<T, 4>
+ {
+ static T
+ swap (T x)
+ {
+ union u4
+ {
+ T t;
+ char c[4];
+ };
+
+ u4 u;
+ u.t = x;
+
+ char tmp (u.c[0]);
+ u.c[0] = u.c[3];
+ u.c[3] = tmp;
+
+ tmp = u.c[1];
+ u.c[1] = u.c[2];
+ u.c[2] = tmp;
+
+ return u.t;
+ }
+ };
+
+ template <typename T>
+ struct swap_endian<T, 8>
+ {
+ static T
+ swap (T x)
+ {
+ union u8
+ {
+ T t;
+ char c[8];
+ };
+
+ u8 u;
+ u.t = x;
+
+ char tmp (u.c[0]);
+ u.c[0] = u.c[7];
+ u.c[7] = tmp;
+
+ tmp = u.c[1];
+ u.c[1] = u.c[6];
+ u.c[6] = tmp;
+
+ tmp = u.c[2];
+ u.c[2] = u.c[5];
+ u.c[5] = tmp;
+
+ tmp = u.c[3];
+ u.c[3] = u.c[4];
+ u.c[4] = tmp;
+
+ return u.t;
+ }
+ };
+
+ class LIBODB_PGSQL_EXPORT endian_traits
+ {
+ public:
+ enum endian
+ {
+ big,
+ little
+ };
+
+ public:
+ static const endian host_endian;
+
+ public:
+ template <typename T>
+ static T
+ hton (T x)
+ {
+ return host_endian == big ? x : swap_endian<T>::swap (x);
+ }
+
+ template <typename T>
+ static T
+ ntoh (T x)
+ {
+ return host_endian == big ? x : swap_endian<T>::swap (x);
+ }
+ };
+ }
+ }
+}
+
+#endif // ODB_PGSQL_DETAILS_ENDIAN_TRAITS_HXX
diff --git a/libodb-pgsql/odb/pgsql/details/export.hxx b/libodb-pgsql/odb/pgsql/details/export.hxx
new file mode 100644
index 0000000..73adc43
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/export.hxx
@@ -0,0 +1,50 @@
+// file : odb/pgsql/details/export.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_DETAILS_EXPORT_HXX
+#define ODB_PGSQL_DETAILS_EXPORT_HXX
+
+#include <odb/pre.hxx>
+
+// Note: do this check directly instead of including config.hxx.
+//
+#ifdef ODB_COMPILER
+# error libodb-pgsql header included in odb-compiled header
+#endif
+
+// Normally we don't export class templates (but do complete specializations),
+// inline functions, and classes with only inline member functions. Exporting
+// classes that inherit from non-exported/imported bases (e.g., std::string)
+// will end up badly. The only known workarounds are to not inherit or to not
+// export. Also, MinGW GCC doesn't like seeing non-exported function being
+// used before their inline definition. The workaround is to reorder code. In
+// the end it's all trial and error.
+
+#if defined(LIBODB_PGSQL_STATIC) // Using static.
+# define LIBODB_PGSQL_EXPORT
+#elif defined(LIBODB_PGSQL_STATIC_BUILD) // Building static.
+# define LIBODB_PGSQL_EXPORT
+#elif defined(LIBODB_PGSQL_SHARED) // Using shared.
+# ifdef _WIN32
+# define LIBODB_PGSQL_EXPORT __declspec(dllimport)
+# else
+# define LIBODB_PGSQL_EXPORT
+# endif
+#elif defined(LIBODB_PGSQL_SHARED_BUILD) // Building shared.
+# ifdef _WIN32
+# define LIBODB_PGSQL_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_PGSQL_EXPORT
+# endif
+#else
+// If none of the above macros are defined, then we assume we are being used
+// by some third-party build system that cannot/doesn't signal the library
+// type. Note that this fallback works for both static and shared but in case
+// of shared will be sub-optimal compared to having dllimport.
+//
+# define LIBODB_PGSQL_EXPORT // Using static or shared.
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_DETAILS_EXPORT_HXX
diff --git a/libodb-pgsql/odb/pgsql/details/options.cli b/libodb-pgsql/odb/pgsql/details/options.cli
new file mode 100644
index 0000000..f568236
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/options.cli
@@ -0,0 +1,56 @@
+// file : odb/pgsql/details/options.cli
+// license : GNU GPL v2; see accompanying LICENSE file
+
+include <string>;
+
+namespace odb
+{
+ namespace pgsql
+ {
+ namespace details
+ {
+ class options
+ {
+ std::string --user | --username
+ {
+ "<name>",
+ "PostgreSQL database user."
+ };
+
+ std::string --password
+ {
+ "<str>",
+ "PostgreSQL database password."
+ };
+
+ std::string --database | --dbname
+ {
+ "<name>",
+ "PostgreSQL database name."
+ };
+
+ std::string --host
+ {
+ "<str>",
+ "PostgreSQL database host name or address (localhost by default)."
+ };
+
+ std::string --port
+ {
+ "<str>",
+ "PostgreSQL database port number or socket file name extension for
+ Unix-domain connections."
+ };
+
+ std::string --options-file
+ {
+ "<file>",
+ "Read additional options from <file>. Each option should appear on a
+ separate line optionally followed by space or equal sign (\cb{=})
+ and an option value. Empty lines and lines starting with \cb{#} are
+ ignored."
+ };
+ };
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.cxx b/libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.cxx
new file mode 100644
index 0000000..a4a5da6
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.cxx
@@ -0,0 +1,1114 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <odb/pgsql/details/options.hxx>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <utility>
+#include <ostream>
+#include <sstream>
+#include <cstring>
+#include <fstream>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ // unknown_option
+ //
+ unknown_option::
+ ~unknown_option () throw ()
+ {
+ }
+
+ void unknown_option::
+ print (::std::ostream& os) const
+ {
+ os << "unknown option '" << option ().c_str () << "'";
+ }
+
+ const char* unknown_option::
+ what () const throw ()
+ {
+ return "unknown option";
+ }
+
+ // unknown_argument
+ //
+ unknown_argument::
+ ~unknown_argument () throw ()
+ {
+ }
+
+ void unknown_argument::
+ print (::std::ostream& os) const
+ {
+ os << "unknown argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unknown_argument::
+ what () const throw ()
+ {
+ return "unknown argument";
+ }
+
+ // missing_value
+ //
+ missing_value::
+ ~missing_value () throw ()
+ {
+ }
+
+ void missing_value::
+ print (::std::ostream& os) const
+ {
+ os << "missing value for option '" << option ().c_str () << "'";
+ }
+
+ const char* missing_value::
+ what () const throw ()
+ {
+ return "missing option value";
+ }
+
+ // invalid_value
+ //
+ invalid_value::
+ ~invalid_value () throw ()
+ {
+ }
+
+ void invalid_value::
+ print (::std::ostream& os) const
+ {
+ os << "invalid value '" << value ().c_str () << "' for option '"
+ << option ().c_str () << "'";
+
+ if (!message ().empty ())
+ os << ": " << message ().c_str ();
+ }
+
+ const char* invalid_value::
+ what () const throw ()
+ {
+ return "invalid option value";
+ }
+
+ // eos_reached
+ //
+ void eos_reached::
+ print (::std::ostream& os) const
+ {
+ os << what ();
+ }
+
+ const char* eos_reached::
+ what () const throw ()
+ {
+ return "end of argument stream reached";
+ }
+
+ // file_io_failure
+ //
+ file_io_failure::
+ ~file_io_failure () throw ()
+ {
+ }
+
+ void file_io_failure::
+ print (::std::ostream& os) const
+ {
+ os << "unable to open file '" << file ().c_str () << "' or read failure";
+ }
+
+ const char* file_io_failure::
+ what () const throw ()
+ {
+ return "unable to open file or read failure";
+ }
+
+ // unmatched_quote
+ //
+ unmatched_quote::
+ ~unmatched_quote () throw ()
+ {
+ }
+
+ void unmatched_quote::
+ print (::std::ostream& os) const
+ {
+ os << "unmatched quote in argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unmatched_quote::
+ what () const throw ()
+ {
+ return "unmatched quote";
+ }
+
+ // scanner
+ //
+ scanner::
+ ~scanner ()
+ {
+ }
+
+ // argv_scanner
+ //
+ bool argv_scanner::
+ more ()
+ {
+ return i_ < argc_;
+ }
+
+ const char* argv_scanner::
+ peek ()
+ {
+ if (i_ < argc_)
+ return argv_[i_];
+ else
+ throw eos_reached ();
+ }
+
+ const char* argv_scanner::
+ next ()
+ {
+ if (i_ < argc_)
+ {
+ const char* r (argv_[i_]);
+
+ if (erase_)
+ {
+ for (int i (i_ + 1); i < argc_; ++i)
+ argv_[i - 1] = argv_[i];
+
+ --argc_;
+ argv_[argc_] = 0;
+ }
+ else
+ ++i_;
+
+ ++start_position_;
+ return r;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ void argv_scanner::
+ skip ()
+ {
+ if (i_ < argc_)
+ {
+ ++i_;
+ ++start_position_;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ std::size_t argv_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ // argv_file_scanner
+ //
+ int argv_file_scanner::zero_argc_ = 0;
+ std::string argv_file_scanner::empty_string_;
+
+ bool argv_file_scanner::
+ more ()
+ {
+ if (!args_.empty ())
+ return true;
+
+ while (base::more ())
+ {
+ // See if the next argument is the file option.
+ //
+ const char* a (base::peek ());
+ const option_info* oi = 0;
+ const char* ov = 0;
+
+ if (!skip_)
+ {
+ if ((oi = find (a)) != 0)
+ {
+ base::next ();
+
+ if (!base::more ())
+ throw missing_value (a);
+
+ ov = base::next ();
+ }
+ else if (std::strncmp (a, "-", 1) == 0)
+ {
+ if ((ov = std::strchr (a, '=')) != 0)
+ {
+ std::string o (a, 0, ov - a);
+ if ((oi = find (o.c_str ())) != 0)
+ {
+ base::next ();
+ ++ov;
+ }
+ }
+ }
+ }
+
+ if (oi != 0)
+ {
+ if (oi->search_func != 0)
+ {
+ std::string f (oi->search_func (ov, oi->arg));
+
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ load (ov);
+
+ if (!args_.empty ())
+ return true;
+ }
+ else
+ {
+ if (!skip_)
+ skip_ = (std::strcmp (a, "--") == 0);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ const char* argv_file_scanner::
+ peek ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? base::peek () : args_.front ().value.c_str ();
+ }
+
+ const std::string& argv_file_scanner::
+ peek_file ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? empty_string_ : *args_.front ().file;
+ }
+
+ std::size_t argv_file_scanner::
+ peek_line ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? 0 : args_.front ().line;
+ }
+
+ const char* argv_file_scanner::
+ next ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::next ();
+ else
+ {
+ hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value);
+ args_.pop_front ();
+ ++start_position_;
+ return hold_[i_].c_str ();
+ }
+ }
+
+ void argv_file_scanner::
+ skip ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::skip ();
+ else
+ {
+ args_.pop_front ();
+ ++start_position_;
+ }
+ }
+
+ const argv_file_scanner::option_info* argv_file_scanner::
+ find (const char* a) const
+ {
+ for (std::size_t i (0); i < options_count_; ++i)
+ if (std::strcmp (a, options_[i].option) == 0)
+ return &options_[i];
+
+ return 0;
+ }
+
+ std::size_t argv_file_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ void argv_file_scanner::
+ load (const std::string& file)
+ {
+ using namespace std;
+
+ ifstream is (file.c_str ());
+
+ if (!is.is_open ())
+ throw file_io_failure (file);
+
+ files_.push_back (file);
+
+ arg a;
+ a.file = &*files_.rbegin ();
+
+ for (a.line = 1; !is.eof (); ++a.line)
+ {
+ string line;
+ getline (is, line);
+
+ if (is.fail () && !is.eof ())
+ throw file_io_failure (file);
+
+ string::size_type n (line.size ());
+
+ // Trim the line from leading and trailing whitespaces.
+ //
+ if (n != 0)
+ {
+ const char* f (line.c_str ());
+ const char* l (f + n);
+
+ const char* of (f);
+ while (f < l && (*f == ' ' || *f == '\t' || *f == '\r'))
+ ++f;
+
+ --l;
+
+ const char* ol (l);
+ while (l > f && (*l == ' ' || *l == '\t' || *l == '\r'))
+ --l;
+
+ if (f != of || l != ol)
+ line = f <= l ? string (f, l - f + 1) : string ();
+ }
+
+ // Ignore empty lines, those that start with #.
+ //
+ if (line.empty () || line[0] == '#')
+ continue;
+
+ string::size_type p (string::npos);
+ if (line.compare (0, 1, "-") == 0)
+ {
+ p = line.find (' ');
+
+ string::size_type q (line.find ('='));
+ if (q != string::npos && q < p)
+ p = q;
+ }
+
+ string s1;
+ if (p != string::npos)
+ {
+ s1.assign (line, 0, p);
+
+ // Skip leading whitespaces in the argument.
+ //
+ if (line[p] == '=')
+ ++p;
+ else
+ {
+ n = line.size ();
+ for (++p; p < n; ++p)
+ {
+ char c (line[p]);
+ if (c != ' ' && c != '\t' && c != '\r')
+ break;
+ }
+ }
+ }
+ else if (!skip_)
+ skip_ = (line == "--");
+
+ string s2 (line, p != string::npos ? p : 0);
+
+ // If the string (which is an option value or argument) is
+ // wrapped in quotes, remove them.
+ //
+ n = s2.size ();
+ char cf (s2[0]), cl (s2[n - 1]);
+
+ if (cf == '"' || cf == '\'' || cl == '"' || cl == '\'')
+ {
+ if (n == 1 || cf != cl)
+ throw unmatched_quote (s2);
+
+ s2 = string (s2, 1, n - 2);
+ }
+
+ if (!s1.empty ())
+ {
+ // See if this is another file option.
+ //
+ const option_info* oi;
+ if (!skip_ && (oi = find (s1.c_str ())))
+ {
+ if (s2.empty ())
+ throw missing_value (oi->option);
+
+ if (oi->search_func != 0)
+ {
+ string f (oi->search_func (s2.c_str (), oi->arg));
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ {
+ // If the path of the file being parsed is not simple and the
+ // path of the file that needs to be loaded is relative, then
+ // complete the latter using the former as a base.
+ //
+#ifndef _WIN32
+ string::size_type p (file.find_last_of ('/'));
+ bool c (p != string::npos && s2[0] != '/');
+#else
+ string::size_type p (file.find_last_of ("/\\"));
+ bool c (p != string::npos && s2[1] != ':');
+#endif
+ if (c)
+ s2.insert (0, file, 0, p + 1);
+
+ load (s2);
+ }
+
+ continue;
+ }
+
+ a.value = s1;
+ args_.push_back (a);
+ }
+
+ a.value = s2;
+ args_.push_back (a);
+ }
+ }
+
+ template <typename X>
+ struct parser
+ {
+ static void
+ parse (X& x, bool& xs, scanner& s)
+ {
+ using namespace std;
+
+ const char* o (s.next ());
+ if (s.more ())
+ {
+ string v (s.next ());
+ istringstream is (v);
+ if (!(is >> x && is.peek () == istringstream::traits_type::eof ()))
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<bool>
+ {
+ static void
+ parse (bool& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ const char* v (s.next ());
+
+ if (std::strcmp (v, "1") == 0 ||
+ std::strcmp (v, "true") == 0 ||
+ std::strcmp (v, "TRUE") == 0 ||
+ std::strcmp (v, "True") == 0)
+ x = true;
+ else if (std::strcmp (v, "0") == 0 ||
+ std::strcmp (v, "false") == 0 ||
+ std::strcmp (v, "FALSE") == 0 ||
+ std::strcmp (v, "False") == 0)
+ x = false;
+ else
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<std::string>
+ {
+ static void
+ parse (std::string& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ x = s.next ();
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X>
+ struct parser<std::pair<X, std::size_t> >
+ {
+ static void
+ parse (std::pair<X, std::size_t>& x, bool& xs, scanner& s)
+ {
+ x.second = s.position ();
+ parser<X>::parse (x.first, xs, s);
+ }
+ };
+
+ template <typename X>
+ struct parser<std::vector<X> >
+ {
+ static void
+ parse (std::vector<X>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.push_back (x);
+ xs = true;
+ }
+ };
+
+ template <typename X, typename C>
+ struct parser<std::set<X, C> >
+ {
+ static void
+ parse (std::set<X, C>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.insert (x);
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::map<K, V, C> >
+ {
+ static void
+ parse (std::map<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m[k] = v;
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::multimap<K, V, C> >
+ {
+ static void
+ parse (std::multimap<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m.insert (typename std::multimap<K, V, C>::value_type (k, v));
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X, typename T, T X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, s);
+ }
+
+ template <typename X, bool X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ s.next ();
+ x.*M = true;
+ }
+
+ template <typename X, typename T, T X::*M, bool X::*S>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, x.*S, s);
+ }
+ }
+ }
+ }
+}
+
+#include <map>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ namespace details
+ {
+ // options
+ //
+
+ options::
+ options ()
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (),
+ port_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ }
+
+ options::
+ options (int& argc,
+ char** argv,
+ bool erase,
+ ::odb::pgsql::details::cli::unknown_mode opt,
+ ::odb::pgsql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (),
+ port_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::pgsql::details::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ options::
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ ::odb::pgsql::details::cli::unknown_mode opt,
+ ::odb::pgsql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (),
+ port_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::pgsql::details::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ options::
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::odb::pgsql::details::cli::unknown_mode opt,
+ ::odb::pgsql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (),
+ port_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::pgsql::details::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ options::
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::odb::pgsql::details::cli::unknown_mode opt,
+ ::odb::pgsql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (),
+ port_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::pgsql::details::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ options::
+ options (::odb::pgsql::details::cli::scanner& s,
+ ::odb::pgsql::details::cli::unknown_mode opt,
+ ::odb::pgsql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (),
+ port_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ _parse (s, opt, arg);
+ }
+
+ ::odb::pgsql::details::cli::usage_para options::
+ print_usage (::std::ostream& os, ::odb::pgsql::details::cli::usage_para p)
+ {
+ CLI_POTENTIALLY_UNUSED (os);
+
+ if (p != ::odb::pgsql::details::cli::usage_para::none)
+ os << ::std::endl;
+
+ os << "--user|--username <name> PostgreSQL database user." << ::std::endl;
+
+ os << std::endl
+ << "--password <str> PostgreSQL database password." << ::std::endl;
+
+ os << std::endl
+ << "--database|--dbname <name> PostgreSQL database name." << ::std::endl;
+
+ os << std::endl
+ << "--host <str> PostgreSQL database host name or address (localhost" << ::std::endl
+ << " by default)." << ::std::endl;
+
+ os << std::endl
+ << "--port <str> PostgreSQL database port number or socket file name" << ::std::endl
+ << " extension for Unix-domain connections." << ::std::endl;
+
+ os << std::endl
+ << "--options-file <file> Read additional options from <file>. Each option" << ::std::endl
+ << " should appear on a separate line optionally followed" << ::std::endl
+ << " by space or equal sign (=) and an option value." << ::std::endl
+ << " Empty lines and lines starting with # are ignored." << ::std::endl;
+
+ p = ::odb::pgsql::details::cli::usage_para::option;
+
+ return p;
+ }
+
+ typedef
+ std::map<std::string, void (*) (options&, ::odb::pgsql::details::cli::scanner&)>
+ _cli_options_map;
+
+ static _cli_options_map _cli_options_map_;
+
+ struct _cli_options_map_init
+ {
+ _cli_options_map_init ()
+ {
+ _cli_options_map_["--user"] =
+ &::odb::pgsql::details::cli::thunk< options, std::string, &options::user_,
+ &options::user_specified_ >;
+ _cli_options_map_["--username"] =
+ &::odb::pgsql::details::cli::thunk< options, std::string, &options::user_,
+ &options::user_specified_ >;
+ _cli_options_map_["--password"] =
+ &::odb::pgsql::details::cli::thunk< options, std::string, &options::password_,
+ &options::password_specified_ >;
+ _cli_options_map_["--database"] =
+ &::odb::pgsql::details::cli::thunk< options, std::string, &options::database_,
+ &options::database_specified_ >;
+ _cli_options_map_["--dbname"] =
+ &::odb::pgsql::details::cli::thunk< options, std::string, &options::database_,
+ &options::database_specified_ >;
+ _cli_options_map_["--host"] =
+ &::odb::pgsql::details::cli::thunk< options, std::string, &options::host_,
+ &options::host_specified_ >;
+ _cli_options_map_["--port"] =
+ &::odb::pgsql::details::cli::thunk< options, std::string, &options::port_,
+ &options::port_specified_ >;
+ _cli_options_map_["--options-file"] =
+ &::odb::pgsql::details::cli::thunk< options, std::string, &options::options_file_,
+ &options::options_file_specified_ >;
+ }
+ };
+
+ static _cli_options_map_init _cli_options_map_init_;
+
+ bool options::
+ _parse (const char* o, ::odb::pgsql::details::cli::scanner& s)
+ {
+ _cli_options_map::const_iterator i (_cli_options_map_.find (o));
+
+ if (i != _cli_options_map_.end ())
+ {
+ (*(i->second)) (*this, s);
+ return true;
+ }
+
+ return false;
+ }
+
+ bool options::
+ _parse (::odb::pgsql::details::cli::scanner& s,
+ ::odb::pgsql::details::cli::unknown_mode opt_mode,
+ ::odb::pgsql::details::cli::unknown_mode arg_mode)
+ {
+ bool r = false;
+ bool opt = true;
+
+ while (s.more ())
+ {
+ const char* o = s.peek ();
+
+ if (std::strcmp (o, "--") == 0)
+ {
+ opt = false;
+ s.skip ();
+ r = true;
+ continue;
+ }
+
+ if (opt)
+ {
+ if (_parse (o, s))
+ {
+ r = true;
+ continue;
+ }
+
+ if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0')
+ {
+ // Handle combined option values.
+ //
+ std::string co;
+ if (const char* v = std::strchr (o, '='))
+ {
+ co.assign (o, 0, v - o);
+ ++v;
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (co.c_str ()),
+ const_cast<char*> (v)
+ };
+
+ ::odb::pgsql::details::cli::argv_scanner ns (0, ac, av);
+
+ if (_parse (co.c_str (), ns))
+ {
+ // Parsed the option but not its value?
+ //
+ if (ns.end () != 2)
+ throw ::odb::pgsql::details::cli::invalid_value (co, v);
+
+ s.next ();
+ r = true;
+ continue;
+ }
+ else
+ {
+ // Set the unknown option and fall through.
+ //
+ o = co.c_str ();
+ }
+ }
+
+ switch (opt_mode)
+ {
+ case ::odb::pgsql::details::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::odb::pgsql::details::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::odb::pgsql::details::cli::unknown_mode::fail:
+ {
+ throw ::odb::pgsql::details::cli::unknown_option (o);
+ }
+ }
+
+ break;
+ }
+ }
+
+ switch (arg_mode)
+ {
+ case ::odb::pgsql::details::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::odb::pgsql::details::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::odb::pgsql::details::cli::unknown_mode::fail:
+ {
+ throw ::odb::pgsql::details::cli::unknown_argument (o);
+ }
+ }
+
+ break;
+ }
+
+ return r;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
diff --git a/libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.hxx b/libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.hxx
new file mode 100644
index 0000000..4d264d4
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.hxx
@@ -0,0 +1,562 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+#ifndef LIBODB_PGSQL_DETAILS_OPTIONS_HXX
+#define LIBODB_PGSQL_DETAILS_OPTIONS_HXX
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <list>
+#include <deque>
+#include <iosfwd>
+#include <string>
+#include <cstddef>
+#include <exception>
+
+#ifndef CLI_POTENTIALLY_UNUSED
+# if defined(_MSC_VER) || defined(__xlC__)
+# define CLI_POTENTIALLY_UNUSED(x) (void*)&x
+# else
+# define CLI_POTENTIALLY_UNUSED(x) (void)x
+# endif
+#endif
+
+namespace odb
+{
+ namespace pgsql
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ class usage_para
+ {
+ public:
+ enum value
+ {
+ none,
+ text,
+ option
+ };
+
+ usage_para (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ class unknown_mode
+ {
+ public:
+ enum value
+ {
+ skip,
+ stop,
+ fail
+ };
+
+ unknown_mode (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ // Exceptions.
+ //
+
+ class exception: public std::exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const = 0;
+ };
+
+ ::std::ostream&
+ operator<< (::std::ostream&, const exception&);
+
+ class unknown_option: public exception
+ {
+ public:
+ virtual
+ ~unknown_option () throw ();
+
+ unknown_option (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class unknown_argument: public exception
+ {
+ public:
+ virtual
+ ~unknown_argument () throw ();
+
+ unknown_argument (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ class missing_value: public exception
+ {
+ public:
+ virtual
+ ~missing_value () throw ();
+
+ missing_value (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class invalid_value: public exception
+ {
+ public:
+ virtual
+ ~invalid_value () throw ();
+
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message = std::string ());
+
+ const std::string&
+ option () const;
+
+ const std::string&
+ value () const;
+
+ const std::string&
+ message () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ std::string value_;
+ std::string message_;
+ };
+
+ class eos_reached: public exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class file_io_failure: public exception
+ {
+ public:
+ virtual
+ ~file_io_failure () throw ();
+
+ file_io_failure (const std::string& file);
+
+ const std::string&
+ file () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string file_;
+ };
+
+ class unmatched_quote: public exception
+ {
+ public:
+ virtual
+ ~unmatched_quote () throw ();
+
+ unmatched_quote (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ // Command line argument scanner interface.
+ //
+ // The values returned by next() are guaranteed to be valid
+ // for the two previous arguments up until a call to a third
+ // peek() or next().
+ //
+ // The position() function returns a monotonically-increasing
+ // number which, if stored, can later be used to determine the
+ // relative position of the argument returned by the following
+ // call to next(). Note that if multiple scanners are used to
+ // extract arguments from multiple sources, then the end
+ // position of the previous scanner should be used as the
+ // start position of the next.
+ //
+ class scanner
+ {
+ public:
+ virtual
+ ~scanner ();
+
+ virtual bool
+ more () = 0;
+
+ virtual const char*
+ peek () = 0;
+
+ virtual const char*
+ next () = 0;
+
+ virtual void
+ skip () = 0;
+
+ virtual std::size_t
+ position () = 0;
+ };
+
+ class argv_scanner: public scanner
+ {
+ public:
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ int
+ end () const;
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ protected:
+ std::size_t start_position_;
+ int i_;
+ int& argc_;
+ char** argv_;
+ bool erase_;
+ };
+
+ class argv_file_scanner: public argv_scanner
+ {
+ public:
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t start_position = 0);
+
+ struct option_info
+ {
+ // If search_func is not NULL, it is called, with the arg
+ // value as the second argument, to locate the options file.
+ // If it returns an empty string, then the file is ignored.
+ //
+ const char* option;
+ std::string (*search_func) (const char*, void* arg);
+ void* arg;
+ };
+
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const option_info* options = 0,
+ std::size_t options_count = 0,
+ std::size_t start_position = 0);
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ // Return the file path if the peeked at argument came from a file and
+ // the empty string otherwise. The reference is guaranteed to be valid
+ // till the end of the scanner lifetime.
+ //
+ const std::string&
+ peek_file ();
+
+ // Return the 1-based line number if the peeked at argument came from
+ // a file and zero otherwise.
+ //
+ std::size_t
+ peek_line ();
+
+ private:
+ const option_info*
+ find (const char*) const;
+
+ void
+ load (const std::string& file);
+
+ typedef argv_scanner base;
+
+ const std::string option_;
+ option_info option_info_;
+ const option_info* options_;
+ std::size_t options_count_;
+
+ struct arg
+ {
+ std::string value;
+ const std::string* file;
+ std::size_t line;
+ };
+
+ std::deque<arg> args_;
+ std::list<std::string> files_;
+
+ // Circular buffer of two arguments.
+ //
+ std::string hold_[2];
+ std::size_t i_;
+
+ bool skip_;
+
+ static int zero_argc_;
+ static std::string empty_string_;
+ };
+
+ template <typename X>
+ struct parser;
+ }
+ }
+ }
+}
+
+#include <string>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ namespace details
+ {
+ class options
+ {
+ public:
+ options ();
+
+ options (int& argc,
+ char** argv,
+ bool erase = false,
+ ::odb::pgsql::details::cli::unknown_mode option = ::odb::pgsql::details::cli::unknown_mode::fail,
+ ::odb::pgsql::details::cli::unknown_mode argument = ::odb::pgsql::details::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ ::odb::pgsql::details::cli::unknown_mode option = ::odb::pgsql::details::cli::unknown_mode::fail,
+ ::odb::pgsql::details::cli::unknown_mode argument = ::odb::pgsql::details::cli::unknown_mode::stop);
+
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::odb::pgsql::details::cli::unknown_mode option = ::odb::pgsql::details::cli::unknown_mode::fail,
+ ::odb::pgsql::details::cli::unknown_mode argument = ::odb::pgsql::details::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::odb::pgsql::details::cli::unknown_mode option = ::odb::pgsql::details::cli::unknown_mode::fail,
+ ::odb::pgsql::details::cli::unknown_mode argument = ::odb::pgsql::details::cli::unknown_mode::stop);
+
+ options (::odb::pgsql::details::cli::scanner&,
+ ::odb::pgsql::details::cli::unknown_mode option = ::odb::pgsql::details::cli::unknown_mode::fail,
+ ::odb::pgsql::details::cli::unknown_mode argument = ::odb::pgsql::details::cli::unknown_mode::stop);
+
+ // Option accessors.
+ //
+ const std::string&
+ user () const;
+
+ bool
+ user_specified () const;
+
+ const std::string&
+ password () const;
+
+ bool
+ password_specified () const;
+
+ const std::string&
+ database () const;
+
+ bool
+ database_specified () const;
+
+ const std::string&
+ host () const;
+
+ bool
+ host_specified () const;
+
+ const std::string&
+ port () const;
+
+ bool
+ port_specified () const;
+
+ const std::string&
+ options_file () const;
+
+ bool
+ options_file_specified () const;
+
+ // Print usage information.
+ //
+ static ::odb::pgsql::details::cli::usage_para
+ print_usage (::std::ostream&,
+ ::odb::pgsql::details::cli::usage_para = ::odb::pgsql::details::cli::usage_para::none);
+
+ // Implementation details.
+ //
+ protected:
+ bool
+ _parse (const char*, ::odb::pgsql::details::cli::scanner&);
+
+ private:
+ bool
+ _parse (::odb::pgsql::details::cli::scanner&,
+ ::odb::pgsql::details::cli::unknown_mode option,
+ ::odb::pgsql::details::cli::unknown_mode argument);
+
+ public:
+ std::string user_;
+ bool user_specified_;
+ std::string password_;
+ bool password_specified_;
+ std::string database_;
+ bool database_specified_;
+ std::string host_;
+ bool host_specified_;
+ std::string port_;
+ bool port_specified_;
+ std::string options_file_;
+ bool options_file_specified_;
+ };
+ }
+ }
+}
+
+#include <odb/pgsql/details/options.ixx>
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
+#endif // LIBODB_PGSQL_DETAILS_OPTIONS_HXX
diff --git a/libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.ixx b/libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.ixx
new file mode 100644
index 0000000..340789e
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.ixx
@@ -0,0 +1,372 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <cassert>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ // usage_para
+ //
+ inline usage_para::
+ usage_para (value v)
+ : v_ (v)
+ {
+ }
+
+ // unknown_mode
+ //
+ inline unknown_mode::
+ unknown_mode (value v)
+ : v_ (v)
+ {
+ }
+
+ // exception
+ //
+ inline ::std::ostream&
+ operator<< (::std::ostream& os, const exception& e)
+ {
+ e.print (os);
+ return os;
+ }
+
+ // unknown_option
+ //
+ inline unknown_option::
+ unknown_option (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& unknown_option::
+ option () const
+ {
+ return option_;
+ }
+
+ // unknown_argument
+ //
+ inline unknown_argument::
+ unknown_argument (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unknown_argument::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // missing_value
+ //
+ inline missing_value::
+ missing_value (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& missing_value::
+ option () const
+ {
+ return option_;
+ }
+
+ // invalid_value
+ //
+ inline invalid_value::
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message)
+ : option_ (option),
+ value_ (value),
+ message_ (message)
+ {
+ }
+
+ inline const std::string& invalid_value::
+ option () const
+ {
+ return option_;
+ }
+
+ inline const std::string& invalid_value::
+ value () const
+ {
+ return value_;
+ }
+
+ inline const std::string& invalid_value::
+ message () const
+ {
+ return message_;
+ }
+
+ // file_io_failure
+ //
+ inline file_io_failure::
+ file_io_failure (const std::string& file)
+ : file_ (file)
+ {
+ }
+
+ inline const std::string& file_io_failure::
+ file () const
+ {
+ return file_;
+ }
+
+ // unmatched_quote
+ //
+ inline unmatched_quote::
+ unmatched_quote (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unmatched_quote::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // argv_scanner
+ //
+ inline argv_scanner::
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + 1),
+ i_ (1),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline argv_scanner::
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + static_cast<std::size_t> (start)),
+ i_ (start),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline int argv_scanner::
+ end () const
+ {
+ return i_;
+ }
+
+ // argv_file_scanner
+ //
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+
+ load (file);
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const option_info* options,
+ std::size_t options_count,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ load (file);
+ }
+ }
+ }
+ }
+}
+
+namespace odb
+{
+ namespace pgsql
+ {
+ namespace details
+ {
+ // options
+ //
+
+ inline const std::string& options::
+ user () const
+ {
+ return this->user_;
+ }
+
+ inline bool options::
+ user_specified () const
+ {
+ return this->user_specified_;
+ }
+
+ inline const std::string& options::
+ password () const
+ {
+ return this->password_;
+ }
+
+ inline bool options::
+ password_specified () const
+ {
+ return this->password_specified_;
+ }
+
+ inline const std::string& options::
+ database () const
+ {
+ return this->database_;
+ }
+
+ inline bool options::
+ database_specified () const
+ {
+ return this->database_specified_;
+ }
+
+ inline const std::string& options::
+ host () const
+ {
+ return this->host_;
+ }
+
+ inline bool options::
+ host_specified () const
+ {
+ return this->host_specified_;
+ }
+
+ inline const std::string& options::
+ port () const
+ {
+ return this->port_;
+ }
+
+ inline bool options::
+ port_specified () const
+ {
+ return this->port_specified_;
+ }
+
+ inline const std::string& options::
+ options_file () const
+ {
+ return this->options_file_;
+ }
+
+ inline bool options::
+ options_file_specified () const
+ {
+ return this->options_file_specified_;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
diff --git a/libodb-pgsql/odb/pgsql/error.cxx b/libodb-pgsql/odb/pgsql/error.cxx
new file mode 100644
index 0000000..ba8451e
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/error.cxx
@@ -0,0 +1,86 @@
+// file : odb/pgsql/error.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <string>
+#include <cassert>
+
+#include <odb/pgsql/error.hxx>
+#include <odb/pgsql/exceptions.hxx>
+#include <odb/pgsql/connection.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace pgsql
+ {
+ void
+ translate_error (connection& c, PGresult* r,
+ size_t pos, multiple_exceptions* mex)
+ {
+ if (!r)
+ {
+ if (PQstatus (c.handle ()) == CONNECTION_BAD)
+ {
+ c.mark_failed ();
+ throw connection_lost ();
+ }
+ else
+ throw bad_alloc ();
+ }
+
+ // Note that we expect the caller to handle PGRES_PIPELINE_ABORTED since
+ // it's not really an error but rather an indication that no attempt was
+ // made to execute this statement.
+ //
+ string ss;
+ switch (PQresultStatus (r))
+ {
+ case PGRES_BAD_RESPONSE:
+ {
+ throw database_exception ("bad server response");
+ }
+ case PGRES_FATAL_ERROR:
+ {
+ const char* s (PQresultErrorField (r, PG_DIAG_SQLSTATE));
+ ss = (s != 0 ? s : "?????");
+
+ // Deadlock detected.
+ //
+ if (ss == "40001" || ss == "40P01")
+ throw deadlock ();
+ else if (PQstatus (c.handle ()) == CONNECTION_BAD)
+ {
+ c.mark_failed ();
+ throw connection_lost ();
+ }
+ break;
+ }
+ default:
+ assert (false);
+ break;
+ }
+
+ string msg;
+ {
+ // Can be NULL in case of PGRES_BAD_RESPONSE.
+ //
+ const char* m (PQresultErrorMessage (r));
+ msg = (m != 0 ? m : "bad server response");
+
+ // Get rid of the trailing newline if there is one.
+ //
+ string::size_type n (msg.size ());
+ if (n != 0 && msg[n - 1] == '\n')
+ msg.resize (n - 1);
+ }
+
+ if (mex == 0)
+ throw database_exception (ss, msg);
+ else
+ // In PosgreSQL all errors are fatal.
+ //
+ mex->insert (pos, database_exception (ss, msg), true);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/error.hxx b/libodb-pgsql/odb/pgsql/error.hxx
new file mode 100644
index 0000000..8d2793d
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/error.hxx
@@ -0,0 +1,41 @@
+// file : odb/pgsql/error.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_ERROR_HXX
+#define ODB_PGSQL_ERROR_HXX
+
+#include <odb/pre.hxx>
+
+#include <libpq-fe.h>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx> // connection, multiple_exceptions
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ // Translate an error condition involving PGresult* and throw (or return,
+ // in case multiple_exceptions is not NULL) an appropriate exception. If
+ // result is NULL, it is assumed that the error was caused due to a bad
+ // connection or a memory allocation error.
+ //
+ LIBODB_PGSQL_EXPORT void
+ translate_error (connection& c, PGresult* r,
+ std::size_t pos = 0, multiple_exceptions* = 0);
+
+ // Return true if PGresult is not NULL and is not in an error state. If
+ // both s and r are non-NULL, the pointed to value will be populated with
+ // the result status. Otherwise, s is ignored.
+ //
+ LIBODB_PGSQL_EXPORT bool
+ is_good_result (PGresult* r, ExecStatusType* s = 0);
+ }
+}
+
+#include <odb/pgsql/error.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_ERROR_HXX
diff --git a/libodb-pgsql/odb/pgsql/error.ixx b/libodb-pgsql/odb/pgsql/error.ixx
new file mode 100644
index 0000000..6a010aa
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/error.ixx
@@ -0,0 +1,31 @@
+// file : odb/pgsql/error.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace pgsql
+ {
+ inline bool
+ is_good_result (PGresult* r, ExecStatusType* s)
+ {
+ if (r != 0)
+ {
+ ExecStatusType status (PQresultStatus (r));
+
+ if (s != 0)
+ *s = status;
+
+ return
+ status != PGRES_BAD_RESPONSE
+ && status != PGRES_NONFATAL_ERROR
+ && status != PGRES_FATAL_ERROR
+#ifdef LIBPQ_HAS_PIPELINING
+ && status != PGRES_PIPELINE_ABORTED
+#endif
+ ;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/exceptions.cxx b/libodb-pgsql/odb/pgsql/exceptions.cxx
new file mode 100644
index 0000000..28e7fc4
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/exceptions.cxx
@@ -0,0 +1,79 @@
+// file : odb/pgsql/exceptions.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/pgsql/exceptions.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ // database_exception
+ //
+
+ database_exception::
+ database_exception (const string& message)
+ : message_ (message), what_ (message)
+ {
+ }
+
+ database_exception::
+ database_exception (const string& sqlstate,
+ const string& message)
+ : sqlstate_ (sqlstate), message_ (message)
+ {
+ if (!sqlstate_.empty ())
+ what_ = sqlstate_ + ": " + message_;
+ else
+ what_ = message_;
+ }
+
+ database_exception::
+ ~database_exception () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ const char* database_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ database_exception* database_exception::
+ clone () const
+ {
+ return new database_exception (*this);
+ }
+
+ //
+ // cli_exception
+ //
+
+ cli_exception::
+ cli_exception (const string& what)
+ : what_ (what)
+ {
+ }
+
+ cli_exception::
+ ~cli_exception () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ const char* cli_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ cli_exception* cli_exception::
+ clone () const
+ {
+ return new cli_exception (*this);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/exceptions.hxx b/libodb-pgsql/odb/pgsql/exceptions.hxx
new file mode 100644
index 0000000..8417c1a
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/exceptions.hxx
@@ -0,0 +1,80 @@
+// file : odb/pgsql/exceptions.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_EXCEPTIONS_HXX
+#define ODB_PGSQL_EXCEPTIONS_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+
+#include <odb/exceptions.hxx>
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ struct LIBODB_PGSQL_EXPORT database_exception: odb::database_exception
+ {
+ database_exception (const std::string& message);
+
+ database_exception (const std::string& sqlstate,
+ const std::string& message);
+
+ ~database_exception () ODB_NOTHROW_NOEXCEPT;
+
+ const std::string&
+ message () const
+ {
+ return message_;
+ }
+
+ const std::string&
+ sqlstate () const
+ {
+ return sqlstate_;
+ }
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual database_exception*
+ clone () const;
+
+ private:
+ std::string sqlstate_;
+ std::string message_;
+ std::string what_;
+ };
+
+ struct LIBODB_PGSQL_EXPORT cli_exception: odb::exception
+ {
+ cli_exception (const std::string& what);
+ ~cli_exception () ODB_NOTHROW_NOEXCEPT;
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual cli_exception*
+ clone () const;
+
+ private:
+ std::string what_;
+ };
+
+ namespace core
+ {
+ using pgsql::database_exception;
+ using pgsql::cli_exception;
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_EXCEPTIONS_HXX
diff --git a/libodb-pgsql/odb/pgsql/forward.hxx b/libodb-pgsql/odb/pgsql/forward.hxx
new file mode 100644
index 0000000..1186b28
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/forward.hxx
@@ -0,0 +1,91 @@
+// file : odb/pgsql/forward.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_FORWARD_HXX
+#define ODB_PGSQL_FORWARD_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ namespace core
+ {
+ using namespace odb::common;
+ }
+
+ //
+ //
+ class database;
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+ class connection_factory;
+ class statement;
+ class transaction;
+ class tracer;
+
+ namespace core
+ {
+ using pgsql::database;
+ using pgsql::connection;
+ using pgsql::connection_ptr;
+ using pgsql::transaction;
+ using pgsql::statement;
+ }
+
+ // Implementation details.
+ //
+ enum statement_kind
+ {
+ statement_select,
+ statement_insert,
+ statement_update,
+ statement_delete
+ };
+
+ class binding;
+ class select_statement;
+
+ template <typename T>
+ class object_statements;
+
+ template <typename T>
+ class polymorphic_root_object_statements;
+
+ template <typename T>
+ class polymorphic_derived_object_statements;
+
+ template <typename T>
+ class no_id_object_statements;
+
+ template <typename T>
+ class view_statements;
+
+ template <typename T>
+ class container_statements;
+
+ template <typename T>
+ class smart_container_statements;
+
+ template <typename T, typename ST>
+ class section_statements;
+
+ class query_base;
+ }
+
+ namespace details
+ {
+ template <>
+ struct counter_type<pgsql::connection>
+ {
+ typedef shared_base counter;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_FORWARD_HXX
diff --git a/libodb-pgsql/odb/pgsql/no-id-object-result.hxx b/libodb-pgsql/odb/pgsql/no-id-object-result.hxx
new file mode 100644
index 0000000..734d4a7
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/no-id-object-result.hxx
@@ -0,0 +1,77 @@
+// file : odb/pgsql/no-id-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_NO_ID_OBJECT_RESULT_HXX
+#define ODB_PGSQL_NO_ID_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/no-id-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx> // query_base
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ class no_id_object_result_impl: public odb::no_id_object_result_impl<T>
+ {
+ public:
+ typedef odb::no_id_object_result_impl<T> base_type;
+
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_pgsql> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~no_id_object_result_impl ();
+
+ no_id_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type&);
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ std::size_t count_;
+ };
+ }
+}
+
+#include <odb/pgsql/no-id-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_NO_ID_OBJECT_RESULT_HXX
diff --git a/libodb-pgsql/odb/pgsql/no-id-object-result.txx b/libodb-pgsql/odb/pgsql/no-id-object-result.txx
new file mode 100644
index 0000000..0bae952
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/no-id-object-result.txx
@@ -0,0 +1,114 @@
+// file : odb/pgsql/no-id-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/callback.hxx>
+
+#include <odb/pgsql/no-id-object-statements.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ no_id_object_result_impl<T>::
+ ~no_id_object_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ no_id_object_result_impl<T>::
+ no_id_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ count_ (0)
+ {
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ load (object_type& obj)
+ {
+ // The image can grow between calls to load() as a result of other
+ // statements execution.
+ //
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ }
+
+ select_statement::result r (statement_->load ());
+
+ if (r == select_statement::truncated)
+ {
+ if (tc_.grow (im, statements_.select_image_truncated ()))
+ im.version++;
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ statement_->reload ();
+ }
+ }
+
+ object_traits::callback (this->db_, obj, callback_event::pre_load);
+ tc_.init (obj, im, &this->db_);
+ object_traits::callback (this->db_, obj, callback_event::post_load);
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (statement_->next ())
+ count_++;
+ else
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t no_id_object_result_impl<T>::
+ size ()
+ {
+ return this->end_ ? count_ : statement_->result_size ();
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/no-id-object-statements.hxx b/libodb-pgsql/odb/pgsql/no-id-object-statements.hxx
new file mode 100644
index 0000000..baa1b2a
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/no-id-object-statements.hxx
@@ -0,0 +1,147 @@
+// file : odb/pgsql/no-id-object-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_NO_ID_OBJECT_STATEMENTS_HXX
+#define ODB_PGSQL_NO_ID_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/pgsql-types.hxx>
+#include <odb/pgsql/binding.hxx>
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/statements-base.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ // Implementation for objects without object id.
+ //
+
+ template <typename T>
+ class no_id_object_statements: public statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_pgsql> object_traits;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::image_type image_type;
+
+ typedef pgsql::insert_statement insert_statement_type;
+
+ public:
+ no_id_object_statements (connection_type&);
+
+ virtual
+ ~no_id_object_statements ();
+
+ // Object image.
+ //
+ image_type&
+ image (std::size_t i = 0)
+ {
+ return image_[i];
+ }
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Select binding (needed for query support).
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement_name,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ object_traits::persist_statement_types,
+ insert_column_count,
+ insert_image_binding_,
+ insert_image_native_binding_,
+ 0,
+ false));
+
+ return *persist_;
+ }
+
+ public:
+ // select = total
+ // insert = total - inverse; inverse == 0 for object without id
+ //
+ static const std::size_t insert_column_count =
+ object_traits::column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count;
+
+ private:
+ no_id_object_statements (const no_id_object_statements&);
+ no_id_object_statements& operator= (const no_id_object_statements&);
+
+ private:
+ image_type image_[object_traits::batch];
+ unsigned long long status_[object_traits::batch];
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+ binding select_image_binding_;
+ bind select_image_bind_[select_column_count];
+ bool select_image_truncated_[select_column_count];
+
+ // Insert binding.
+ //
+ std::size_t insert_image_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[insert_column_count];
+ native_binding insert_image_native_binding_;
+ char* insert_image_values_[insert_column_count];
+ int insert_image_lengths_[insert_column_count];
+ int insert_image_formats_[insert_column_count];
+
+ details::shared_ptr<insert_statement_type> persist_;
+ };
+ }
+}
+
+#include <odb/pgsql/no-id-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_NO_ID_OBJECT_STATEMENTS_HXX
diff --git a/libodb-pgsql/odb/pgsql/no-id-object-statements.txx b/libodb-pgsql/odb/pgsql/no-id-object-statements.txx
new file mode 100644
index 0000000..0c340ab
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/no-id-object-statements.txx
@@ -0,0 +1,50 @@
+// file : odb/pgsql/no-id-object-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ // no_id_object_statements
+ //
+
+ template <typename T>
+ no_id_object_statements<T>::
+ ~no_id_object_statements ()
+ {
+ }
+
+ template <typename T>
+ no_id_object_statements<T>::
+ no_id_object_statements (connection_type& conn)
+ : statements_base (conn),
+ // select
+ select_image_binding_ (select_image_bind_, select_column_count),
+ // insert
+ insert_image_binding_ (insert_image_bind_,
+ insert_column_count,
+ object_traits::batch,
+ sizeof (image_type),
+ status_),
+ insert_image_native_binding_ (insert_image_values_,
+ insert_image_lengths_,
+ insert_image_formats_,
+ insert_column_count)
+ {
+ image_[0].version = 0; // Only version in the first element used.
+ select_image_version_ = 0;
+ insert_image_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+
+ for (std::size_t i (0); i < select_column_count; ++i)
+ select_image_bind_[i].truncated = select_image_truncated_ + i;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/pgsql-fwd.hxx b/libodb-pgsql/odb/pgsql/pgsql-fwd.hxx
new file mode 100644
index 0000000..e60bc37
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/pgsql-fwd.hxx
@@ -0,0 +1,19 @@
+// file : odb/pgsql/pgsql-fwd.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_PGSQL_FWD_HXX
+#define ODB_PGSQL_PGSQL_FWD_HXX
+
+#include <odb/pre.hxx>
+
+// Forward declaration for some of the types defined in libpq-fe.h. This
+// allows us to avoid having to include libpq-fe.h in public headers.
+//
+typedef unsigned int Oid;
+
+typedef struct pg_conn PGconn;
+typedef struct pg_result PGresult;
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_PGSQL_FWD_HXX
diff --git a/libodb-pgsql/odb/pgsql/pgsql-oid.hxx b/libodb-pgsql/odb/pgsql/pgsql-oid.hxx
new file mode 100644
index 0000000..e2ef1af
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/pgsql-oid.hxx
@@ -0,0 +1,47 @@
+// file : odb/pgsql/pgsql-oid.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Generated file of OIDs extracted from the PostgreSQL 8.4.8 source file
+// src/include/catalog/pg_type.h
+//
+
+#ifndef ODB_PGSQL_PGSQL_OID_HXX
+#define ODB_PGSQL_PGSQL_OID_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/pgsql/version.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ enum
+ {
+ bool_oid = 16,
+ int2_oid = 21,
+ int4_oid = 23,
+ int8_oid = 20,
+
+ numeric_oid = 1700,
+
+ float4_oid = 700,
+ float8_oid = 701,
+
+ date_oid = 1082,
+ time_oid = 1083,
+ timestamp_oid = 1114,
+
+ text_oid = 25,
+ bytea_oid = 17,
+ bit_oid = 1560,
+ varbit_oid = 1562,
+
+ uuid_oid = 2950
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_PGSQL_OID_HXX
diff --git a/libodb-pgsql/odb/pgsql/pgsql-types.hxx b/libodb-pgsql/odb/pgsql/pgsql-types.hxx
new file mode 100644
index 0000000..117a41e
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/pgsql-types.hxx
@@ -0,0 +1,59 @@
+// file : odb/pgsql/pgsql-types.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_PGSQL_TYPES_HXX
+#define ODB_PGSQL_PGSQL_TYPES_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/pgsql/pgsql-fwd.hxx> // Oid
+
+namespace odb
+{
+ namespace pgsql
+ {
+ // The libpq result binding. This data structures is roughly modeled
+ // after MYSQL_BIND from MySQL.
+ //
+ // Types that may need to grow are bound as pointers to pointers to char
+ // array (normally in details::buffer) in order to allow simple offsetting
+ // in bulk operation support. Note that if we were to do the same for
+ // capacity, we could get rid of the buffer growth tracking altogether.
+ //
+ struct bind
+ {
+ enum buffer_type
+ {
+ boolean_, // Buffer is a bool; size, capacity, truncated are unused.
+ smallint, // Buffer is short; size, capacity, truncated are unused.
+ integer, // Buffer is int; size, capacity, truncated are unused.
+ bigint, // Buffer is long long; size, capacity, truncated are unused.
+ real, // Buffer is float; size, capacity, truncated are unused.
+ double_, // Buffer is double; size, capacity, truncated are unused.
+ numeric, // Buffer is a pointer to pointer to char array.
+ date, // Buffer is int; size, capacity, truncated are unused.
+ time, // Buffer is long long; size, capacity, truncated are unused.
+ timestamp,// Buffer is long long; size, capacity, truncated are unused.
+ text, // Buffer is a pointer to pointer to char array.
+ bytea, // Buffer is a pointer to pointer to char array.
+ bit, // Buffer is a pointer to char array.
+ varbit, // Buffer is a pointer to pointer to char array.
+ uuid // Buffer is a 16-byte char array; size capacity, truncated
+ // are unused. Note: big-endian, in RFC 4122/4.1.2 order.
+ };
+
+ buffer_type type;
+ void* buffer;
+ std::size_t* size;
+ std::size_t capacity;
+ bool* is_null;
+ bool* truncated;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_PGSQL_TYPES_HXX
diff --git a/libodb-pgsql/odb/pgsql/polymorphic-object-result.hxx b/libodb-pgsql/odb/pgsql/polymorphic-object-result.hxx
new file mode 100644
index 0000000..a1cd108
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/polymorphic-object-result.hxx
@@ -0,0 +1,94 @@
+// file : odb/pgsql/polymorphic-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_POLYMORPHIC_OBJECT_RESULT_HXX
+#define ODB_PGSQL_POLYMORPHIC_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/polymorphic-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx> // query_base
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ class polymorphic_object_result_impl:
+ public odb::polymorphic_object_result_impl<T>
+ {
+ public:
+ typedef odb::polymorphic_object_result_impl<T> base_type;
+
+ typedef typename base_type::id_type id_type;
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_pgsql> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename base_type::root_type root_type;
+ typedef typename base_type::discriminator_type discriminator_type;
+
+ typedef object_traits_impl<root_type, id_pgsql> root_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~polymorphic_object_result_impl ();
+
+ polymorphic_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type*, bool fetch);
+
+ virtual id_type
+ load_id ();
+
+ virtual discriminator_type
+ load_discriminator ();
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ void
+ load_image ();
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ std::size_t count_;
+ };
+ }
+}
+
+#include <odb/pgsql/polymorphic-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_POLYMORPHIC_OBJECT_RESULT_HXX
diff --git a/libodb-pgsql/odb/pgsql/polymorphic-object-result.txx b/libodb-pgsql/odb/pgsql/polymorphic-object-result.txx
new file mode 100644
index 0000000..bad2091
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/polymorphic-object-result.txx
@@ -0,0 +1,287 @@
+// file : odb/pgsql/polymorphic-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // object_not_persistent
+
+#include <odb/pgsql/polymorphic-object-statements.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ polymorphic_object_result_impl<T>::
+ ~polymorphic_object_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ polymorphic_object_result_impl<T>::
+ polymorphic_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> st,
+ statements_type& sts,
+ const schema_version_migration* svm)
+ : base_type (sts.connection ()),
+ statement_ (st),
+ statements_ (sts),
+ tc_ (svm),
+ count_ (0)
+ {
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ load (object_type* pobj, bool fetch)
+ {
+ if (fetch)
+ load_image ();
+
+ typename statements_type::root_statements_type& rsts (
+ statements_.root_statements ());
+
+ // This is a top-level call so the statements cannot be locked.
+ //
+ assert (!rsts.locked ());
+ typename statements_type::auto_lock l (rsts);
+
+ typename object_traits::image_type& i (statements_.image ());
+ typename root_traits::image_type& ri (rsts.image ());
+
+ id_type id (root_traits::id (ri));
+
+ // Determine this object's dynamic type.
+ //
+ typedef typename root_traits::info_type info_type;
+ discriminator_type d (root_traits::discriminator (ri));
+ discriminator_type disc (d);
+
+ // Use the polymorphic_info() helper to get concrete_info if
+ // object_type is concrete and NULL if it is abstract.
+ //
+ const info_type* spi (polymorphic_info (object_traits::info));
+ const info_type& pi (
+ spi != 0 && spi->discriminator == d
+ ? *spi
+ : root_traits::map->find (d));
+
+ typedef typename root_traits::pointer_type root_pointer_type;
+ typedef typename root_traits::pointer_traits root_pointer_traits;
+
+ typename object_traits::pointer_cache_traits::insert_guard ig;
+
+ if (pobj == 0)
+ {
+ // Need to create a new instance of the dynamic type.
+ //
+ root_pointer_type rp (pi.create ());
+ pointer_type p (
+ root_pointer_traits::template static_pointer_cast<object_type> (rp));
+
+ // Insert it as a root pointer (for non-unique pointers, rp should
+ // still be valid and for unique pointers this is a no-op).
+ //
+ ig.reset (
+ object_traits::pointer_cache_traits::insert (this->db_, id, rp));
+
+ pobj = &pointer_traits::get_ref (p);
+ current (p);
+ }
+ else
+ {
+ // We are loading into an existing instance. If the static and
+ // dynamic types differ, then make sure the instance is at least
+ // of the dynamic type.
+ //
+ if (&pi != &object_traits::info)
+ {
+ const info_type& dpi (root_traits::map->find (typeid (*pobj)));
+
+ if (&dpi != &pi && dpi.derived (pi))
+ throw object_not_persistent (); // @@ type_mismatch ?
+ }
+ }
+
+ callback_event ce (callback_event::pre_load);
+ pi.dispatch (info_type::call_callback, this->db_, pobj, &ce);
+
+ tc_.init (*pobj, i, &this->db_);
+
+ // Initialize the id image and binding and load the rest of the object
+ // (containers, dynamic part, etc).
+ //
+ typename object_traits::id_image_type& idi (statements_.id_image ());
+ root_traits::init (idi, id);
+
+ binding& idb (statements_.id_image_binding ());
+ if (idi.version != statements_.id_image_version () || idb.version == 0)
+ {
+ object_traits::bind (idb.bind, idi);
+ statements_.id_image_version (idi.version);
+ idb.version++;
+ }
+
+ tc_.load_ (statements_, *pobj, false);
+
+ // Load the dynamic part of the object unless static and dynamic
+ // types are the same.
+ //
+ if (&pi != &object_traits::info)
+ {
+ std::size_t d (object_traits::depth);
+ pi.dispatch (info_type::call_load, this->db_, pobj, &d);
+ };
+
+ rsts.load_delayed (tc_.version ());
+ l.unlock ();
+
+ ce = callback_event::post_load;
+ pi.dispatch (info_type::call_callback, this->db_, pobj, &ce);
+ object_traits::pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+
+ template <typename T>
+ typename polymorphic_object_result_impl<T>::id_type
+ polymorphic_object_result_impl<T>::
+ load_id ()
+ {
+ load_image ();
+ return root_traits::id (statements_.root_statements ().image ());
+ }
+
+ template <typename T>
+ typename polymorphic_object_result_impl<T>::discriminator_type
+ polymorphic_object_result_impl<T>::
+ load_discriminator ()
+ {
+ load_image ();
+ return root_traits::discriminator (
+ statements_.root_statements ().image ());
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (statement_->next ())
+ count_++;
+ else
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ }
+
+ template <typename T, typename R>
+ struct polymorphic_image_rebind
+ {
+ // Derived type version.
+ //
+ typedef object_traits_impl<T, id_pgsql> traits;
+
+ static bool
+ rebind (typename traits::statements_type& sts,
+ const schema_version_migration* svm)
+ {
+ typename traits::image_type& im (sts.image ());
+
+ if (traits::check_version (sts.select_image_versions (), im))
+ {
+ binding& b (sts.select_image_binding (traits::depth));
+ object_traits_calls<T> tc (svm);
+ tc.bind (b.bind, 0, 0, im, statement_select);
+ traits::update_version (
+ sts.select_image_versions (), im, sts.select_image_bindings ());
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+ template <typename R>
+ struct polymorphic_image_rebind<R, R>
+ {
+ // Root type version.
+ //
+ typedef object_traits_impl<R, id_pgsql> traits;
+
+ static bool
+ rebind (typename traits::statements_type& sts,
+ const schema_version_migration* svm)
+ {
+ typename traits::image_type& im (sts.image ());
+
+ if (im.version != sts.select_image_version ())
+ {
+ binding& b (sts.select_image_binding ());
+ object_traits_calls<R> tc (svm);
+ tc.bind (b.bind, im, statement_select);
+ sts.select_image_version (im.version);
+ b.version++;
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ load_image ()
+ {
+ typedef polymorphic_image_rebind<object_type, root_type> image_rebind;
+
+ // The image can grow between calls to load() as a result of other
+ // statements execution.
+ //
+ image_rebind::rebind (statements_, tc_.version ());
+
+ select_statement::result r (statement_->load ());
+
+ if (r == select_statement::truncated)
+ {
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (tc_.grow (im, statements_.select_image_truncated ()))
+ im.version++;
+
+ if (image_rebind::rebind (statements_, tc_.version ()))
+ statement_->reload ();
+ }
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t polymorphic_object_result_impl<T>::
+ size ()
+ {
+ return this->end_ ? count_ : statement_->result_size ();
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/polymorphic-object-statements.hxx b/libodb-pgsql/odb/pgsql/polymorphic-object-statements.hxx
new file mode 100644
index 0000000..717410a
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/polymorphic-object-statements.hxx
@@ -0,0 +1,513 @@
+// file : odb/pgsql/polymorphic-object-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_POLYMORPHIC_OBJECT_STATEMENTS_HXX
+#define ODB_PGSQL_POLYMORPHIC_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/pgsql-types.hxx>
+#include <odb/pgsql/binding.hxx>
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/statements-base.hxx>
+#include <odb/pgsql/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ // Implementation for polymorphic objects.
+ //
+
+ template <typename T>
+ class polymorphic_root_object_statements: public object_statements<T>
+ {
+ public:
+ typedef typename object_statements<T>::connection_type connection_type;
+ typedef typename object_statements<T>::object_traits object_traits;
+ typedef typename object_statements<T>::id_image_type id_image_type;
+
+ typedef
+ typename object_traits::discriminator_image_type
+ discriminator_image_type;
+
+ typedef
+ typename object_statements<T>::select_statement_type
+ select_statement_type;
+
+ public:
+ // Interface compatibility with derived_object_statements.
+ //
+ typedef polymorphic_root_object_statements root_statements_type;
+
+ root_statements_type&
+ root_statements ()
+ {
+ return *this;
+ }
+
+ public:
+ // Discriminator binding.
+ //
+ discriminator_image_type&
+ discriminator_image () {return discriminator_image_;}
+
+ std::size_t
+ discriminator_image_version () const
+ {return discriminator_image_version_;}
+
+ void
+ discriminator_image_version (std::size_t v)
+ {discriminator_image_version_ = v;}
+
+ binding&
+ discriminator_image_binding () {return discriminator_image_binding_;}
+
+ bool*
+ discriminator_image_truncated () {return discriminator_image_truncated_;}
+
+ // Id binding for discriminator retrieval.
+ //
+ id_image_type&
+ discriminator_id_image () {return discriminator_id_image_;}
+
+ std::size_t
+ discriminator_id_image_version () const
+ {return discriminator_id_image_version_;}
+
+ void
+ discriminator_id_image_version (std::size_t v)
+ {discriminator_id_image_version_ = v;}
+
+ binding&
+ discriminator_id_image_binding ()
+ {return discriminator_id_image_binding_;}
+
+ // Expose id native binding from object_statements.
+ //
+ native_binding&
+ id_image_native_binding () {return this->id_image_native_binding_;}
+
+ //
+ //
+ select_statement_type&
+ find_discriminator_statement ()
+ {
+ if (find_discriminator_ == 0)
+ find_discriminator_.reset (
+ new (details::shared) select_statement_type (
+ this->conn_,
+ object_traits::find_discriminator_statement_name,
+ object_traits::find_discriminator_statement,
+ false, // Doesn't need to be processed.
+ false, // Don't optimize.
+ object_traits::find_statement_types, // The same as find (id).
+ id_column_count,
+ discriminator_id_image_binding_,
+ discriminator_id_image_native_binding_,
+ discriminator_image_binding_,
+ false));
+
+ return *find_discriminator_;
+ }
+
+ public:
+ polymorphic_root_object_statements (connection_type&);
+
+ virtual
+ ~polymorphic_root_object_statements ();
+
+ // Static "override" (statements type).
+ //
+ void
+ load_delayed (const schema_version_migration* svm)
+ {
+ assert (this->locked ());
+
+ if (!this->delayed_.empty ())
+ this->template load_delayed_<polymorphic_root_object_statements> (
+ svm);
+ }
+
+ public:
+ static const std::size_t id_column_count =
+ object_statements<T>::id_column_count;
+
+ static const std::size_t discriminator_column_count =
+ object_traits::discriminator_column_count;
+
+ static const std::size_t managed_optimistic_column_count =
+ object_traits::managed_optimistic_column_count;
+
+ private:
+ // Discriminator image.
+ //
+ discriminator_image_type discriminator_image_;
+ std::size_t discriminator_image_version_;
+ binding discriminator_image_binding_;
+ bind discriminator_image_bind_[discriminator_column_count +
+ managed_optimistic_column_count];
+ bool discriminator_image_truncated_[discriminator_column_count +
+ managed_optimistic_column_count];
+
+ // Id image for discriminator retrieval (only used as a parameter).
+ //
+ id_image_type discriminator_id_image_;
+ std::size_t discriminator_id_image_version_;
+ binding discriminator_id_image_binding_;
+ native_binding discriminator_id_image_native_binding_;
+ bind discriminator_id_image_bind_[id_column_count];
+ char* discriminator_id_image_values_[id_column_count];
+ int discriminator_id_image_lengths_[id_column_count];
+ int discriminator_id_image_formats_[id_column_count];
+
+ details::shared_ptr<select_statement_type> find_discriminator_;
+ };
+
+ template <typename T>
+ class polymorphic_derived_object_statements: public statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_pgsql> object_traits;
+ typedef typename object_traits::id_type id_type;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::id_image_type id_image_type;
+ typedef typename object_traits::image_type image_type;
+
+ typedef typename object_traits::root_type root_type;
+ typedef
+ polymorphic_root_object_statements<root_type>
+ root_statements_type;
+
+ typedef typename object_traits::base_type base_type;
+ typedef
+ typename object_traits::base_traits::statements_type
+ base_statements_type;
+
+ typedef
+ typename object_traits::extra_statement_cache_type
+ extra_statement_cache_type;
+
+ typedef pgsql::insert_statement insert_statement_type;
+ typedef pgsql::select_statement select_statement_type;
+ typedef pgsql::update_statement update_statement_type;
+ typedef pgsql::delete_statement delete_statement_type;
+
+ typedef typename root_statements_type::auto_lock auto_lock;
+
+ public:
+ polymorphic_derived_object_statements (connection_type&);
+
+ virtual
+ ~polymorphic_derived_object_statements ();
+
+ public:
+ // Delayed loading.
+ //
+ static void
+ delayed_loader (odb::database&,
+ const id_type&,
+ root_type&,
+ const schema_version_migration*);
+ public:
+ // Root and immediate base statements.
+ //
+ root_statements_type&
+ root_statements ()
+ {
+ return root_statements_;
+ }
+
+ base_statements_type&
+ base_statements ()
+ {
+ return base_statements_;
+ }
+
+ public:
+ // Object image.
+ //
+ image_type&
+ image ()
+ {
+ return image_;
+ }
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ std::size_t
+ insert_id_binding_version () const { return insert_id_binding_version_;}
+
+ void
+ insert_id_binding_version (std::size_t v) {insert_id_binding_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_binding_version () const { return update_id_binding_version_;}
+
+ void
+ update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t*
+ select_image_versions () { return select_image_versions_;}
+
+ binding*
+ select_image_bindings () {return select_image_bindings_;}
+
+ binding&
+ select_image_binding (std::size_t d)
+ {
+ return select_image_bindings_[object_traits::depth - d];
+ }
+
+ bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Object id binding (comes from the root statements).
+ //
+ id_image_type&
+ id_image () {return root_statements_.id_image ();}
+
+ std::size_t
+ id_image_version () const {return root_statements_.id_image_version ();}
+
+ void
+ id_image_version (std::size_t v) {root_statements_.id_image_version (v);}
+
+ binding&
+ id_image_binding () {return root_statements_.id_image_binding ();}
+
+ binding&
+ optimistic_id_image_binding () {
+ return root_statements_.optimistic_id_image_binding ();}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement_name,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ object_traits::persist_statement_types,
+ insert_column_count,
+ insert_image_binding_,
+ insert_image_native_binding_,
+ 0,
+ false));
+
+ return *persist_;
+ }
+
+ select_statement_type&
+ find_statement (std::size_t d)
+ {
+ std::size_t i (object_traits::depth - d);
+ details::shared_ptr<select_statement_type>& p (find_[i]);
+
+ if (p == 0)
+ p.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ object_traits::find_statement_names[i],
+ object_traits::find_statements[i],
+ object_traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ object_traits::find_statement_types,
+ id_column_count,
+ root_statements_.id_image_binding (),
+ root_statements_.id_image_native_binding (),
+ select_image_bindings_[i],
+ false));
+
+ return *p;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ object_traits::update_statement_name,
+ object_traits::update_statement,
+ object_traits::versioned, // Process if versioned.
+ object_traits::update_statement_types,
+ update_column_count + id_column_count,
+ update_image_binding_,
+ update_image_native_binding_,
+ false));
+
+ return *update_;
+ }
+
+ delete_statement_type&
+ erase_statement ()
+ {
+ if (erase_ == 0)
+ erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::erase_statement_name,
+ object_traits::erase_statement,
+ object_traits::find_statement_types, // The same as find (id).
+ id_column_count,
+ root_statements_.id_image_binding (),
+ root_statements_.id_image_native_binding (),
+ false));
+
+ return *erase_;
+ }
+
+ // Extra (container, section) statement cache.
+ //
+ extra_statement_cache_type&
+ extra_statement_cache ()
+ {
+ return extra_statement_cache_.get (
+ conn_,
+ image_,
+ id_image (),
+ id_image_binding (),
+ &id_image_binding (), // Note, not id+version.
+ root_statements_.id_image_native_binding (),
+ object_traits::find_statement_types);
+ }
+
+ public:
+ // select = total - id - separate_load + base::select
+ // insert = total - inverse
+ // update = total - inverse - id - readonly - separate_update
+ //
+ static const std::size_t id_column_count =
+ object_traits::id_column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count -
+ id_column_count -
+ object_traits::separate_load_column_count +
+ base_statements_type::select_column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count -
+ object_traits::inverse_column_count;
+
+ static const std::size_t update_column_count =
+ insert_column_count -
+ object_traits::id_column_count -
+ object_traits::readonly_column_count -
+ object_traits::separate_update_column_count;
+
+ private:
+ polymorphic_derived_object_statements (
+ const polymorphic_derived_object_statements&);
+
+ polymorphic_derived_object_statements&
+ operator= (const polymorphic_derived_object_statements&);
+
+ private:
+ root_statements_type& root_statements_;
+ base_statements_type& base_statements_;
+
+ extra_statement_cache_ptr<extra_statement_cache_type,
+ image_type,
+ id_image_type> extra_statement_cache_;
+
+ image_type image_;
+
+ // Select binding. Here we are have an array of statements/bindings
+ // one for each depth. In other words, if we have classes root, base,
+ // and derived, then we have the following array of statements:
+ //
+ // [0] d + b + r
+ // [1] d + b
+ // [2] d
+ //
+ // Also, because we have a chain of images bound to these statements,
+ // we have an array of versions, one entry for each base plus one for
+ // our own image.
+ //
+ // A poly-abstract class only needs the first statement and in this
+ // case we have only one entry in the the bindings and statements
+ // arrays (but not versions; we still have a chain of images).
+ //
+ std::size_t select_image_versions_[object_traits::depth];
+ binding select_image_bindings_[
+ object_traits::abstract ? 1 : object_traits::depth];
+ bind select_image_bind_[select_column_count];
+ bool select_image_truncated_[select_column_count];
+
+ // Insert binding. The id binding is copied from the hierarchy root.
+ //
+ std::size_t insert_image_version_;
+ std::size_t insert_id_binding_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[insert_column_count];
+ native_binding insert_image_native_binding_;
+ char* insert_image_values_[insert_column_count];
+ int insert_image_lengths_[insert_column_count];
+ int insert_image_formats_[insert_column_count];
+
+ // Update binding. The id suffix binding is copied from the hierarchy
+ // root.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_binding_version_;
+ binding update_image_binding_;
+ bind update_image_bind_[update_column_count + id_column_count];
+ native_binding update_image_native_binding_;
+ char* update_image_values_[update_column_count + id_column_count];
+ int update_image_lengths_[update_column_count + id_column_count];
+ int update_image_formats_[update_column_count + id_column_count];
+
+ details::shared_ptr<insert_statement_type> persist_;
+ details::shared_ptr<select_statement_type> find_[
+ object_traits::abstract ? 1 : object_traits::depth];
+ details::shared_ptr<update_statement_type> update_;
+ details::shared_ptr<delete_statement_type> erase_;
+ };
+ }
+}
+
+#include <odb/pgsql/polymorphic-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_POLYMORPHIC_OBJECT_STATEMENTS_HXX
diff --git a/libodb-pgsql/odb/pgsql/polymorphic-object-statements.txx b/libodb-pgsql/odb/pgsql/polymorphic-object-statements.txx
new file mode 100644
index 0000000..8472fca
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/polymorphic-object-statements.txx
@@ -0,0 +1,158 @@
+// file : odb/pgsql/polymorphic-object-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/transaction.hxx>
+#include <odb/pgsql/statement-cache.hxx>
+#include <odb/pgsql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ // polymorphic_root_object_statements
+ //
+
+ template <typename T>
+ polymorphic_root_object_statements<T>::
+ ~polymorphic_root_object_statements ()
+ {
+ }
+
+ template <typename T>
+ polymorphic_root_object_statements<T>::
+ polymorphic_root_object_statements (connection_type& conn)
+ : object_statements<T> (conn),
+ discriminator_image_binding_ (discriminator_image_bind_,
+ discriminator_column_count +
+ managed_optimistic_column_count),
+ discriminator_id_image_binding_ (discriminator_id_image_bind_,
+ id_column_count),
+ discriminator_id_image_native_binding_ (
+ discriminator_id_image_values_,
+ discriminator_id_image_lengths_,
+ discriminator_id_image_formats_,
+ id_column_count)
+ {
+ discriminator_image_.version = 0;
+ discriminator_id_image_.version = 0;
+
+ discriminator_image_version_ = 0;
+ discriminator_id_image_version_ = 0;
+
+ std::memset (discriminator_image_bind_,
+ 0,
+ sizeof (discriminator_image_bind_));
+ std::memset (discriminator_id_image_bind_,
+ 0,
+ sizeof (discriminator_id_image_bind_));
+ std::memset (discriminator_image_truncated_,
+ 0,
+ sizeof (discriminator_image_truncated_));
+
+ for (std::size_t i (0);
+ i < discriminator_column_count + managed_optimistic_column_count;
+ ++i)
+ {
+ discriminator_image_bind_[i].truncated =
+ discriminator_image_truncated_ + i;
+ }
+ }
+
+ //
+ // polymorphic_derived_object_statements
+ //
+
+ template <typename T>
+ polymorphic_derived_object_statements<T>::
+ ~polymorphic_derived_object_statements ()
+ {
+ }
+
+ template <typename T>
+ polymorphic_derived_object_statements<T>::
+ polymorphic_derived_object_statements (connection_type& conn)
+ : statements_base (conn),
+ root_statements_ (conn.statement_cache ().find_object<root_type> ()),
+ base_statements_ (conn.statement_cache ().find_object<base_type> ()),
+ insert_image_binding_ (insert_image_bind_, insert_column_count),
+ insert_image_native_binding_ (insert_image_values_,
+ insert_image_lengths_,
+ insert_image_formats_,
+ insert_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count),
+ update_image_native_binding_ (update_image_values_,
+ update_image_lengths_,
+ update_image_formats_,
+ update_column_count + id_column_count)
+ {
+ image_.base = &base_statements_.image ();
+ image_.version = 0;
+
+ for (std::size_t i (0); i < object_traits::depth; ++i)
+ select_image_versions_[i] = 0;
+
+ for (std::size_t i (0);
+ i < (object_traits::abstract ? 1 : object_traits::depth);
+ ++i)
+ {
+ select_image_bindings_[i].bind = select_image_bind_;
+ select_image_bindings_[i].count = object_traits::find_column_counts[i];
+ }
+
+ insert_image_version_ = 0;
+ insert_id_binding_version_ = 0;
+ update_image_version_ = 0;
+ update_id_binding_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+
+ for (std::size_t i (0); i < select_column_count; ++i)
+ select_image_bind_[i].truncated = select_image_truncated_ + i;
+ }
+
+ template <typename T>
+ void polymorphic_derived_object_statements<T>::
+ delayed_loader (odb::database& db,
+ const id_type& id,
+ root_type& robj,
+ const schema_version_migration* svm)
+ {
+ connection_type& conn (transaction::current ().connection (db));
+ polymorphic_derived_object_statements& sts (
+ conn.statement_cache ().find_object<object_type> ());
+ root_statements_type& rsts (sts.root_statements ());
+
+ object_type& obj (static_cast<object_type&> (robj));
+
+ // The same code as in object_statements::load_delayed_().
+ //
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (sts, &id))
+ throw object_not_persistent ();
+
+ object_traits::callback (db, obj, callback_event::pre_load);
+ tc.init (obj, sts.image (), &db);
+ tc.load_ (sts, obj, false); // Load containers, etc.
+
+ rsts.load_delayed (svm);
+
+ {
+ typename root_statements_type::auto_unlock u (rsts);
+ object_traits::callback (db, obj, callback_event::post_load);
+ }
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/prepared-query.cxx b/libodb-pgsql/odb/pgsql/prepared-query.cxx
new file mode 100644
index 0000000..a6d783c
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/prepared-query.cxx
@@ -0,0 +1,15 @@
+// file : odb/pgsql/prepared-query.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/prepared-query.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ prepared_query_impl::
+ ~prepared_query_impl ()
+ {
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/prepared-query.hxx b/libodb-pgsql/odb/pgsql/prepared-query.hxx
new file mode 100644
index 0000000..3622226
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/prepared-query.hxx
@@ -0,0 +1,34 @@
+// file : odb/pgsql/prepared-query.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_PREPARED_QUERY_HXX
+#define ODB_PGSQL_PREPARED_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/prepared-query.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/query.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ struct LIBODB_PGSQL_EXPORT prepared_query_impl: odb::prepared_query_impl
+ {
+ virtual
+ ~prepared_query_impl ();
+
+ prepared_query_impl (odb::connection& c): odb::prepared_query_impl (c) {}
+
+ pgsql::query_base query;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_PREPARED_QUERY_HXX
diff --git a/libodb-pgsql/odb/pgsql/query-const-expr.cxx b/libodb-pgsql/odb/pgsql/query-const-expr.cxx
new file mode 100644
index 0000000..c910620
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/query-const-expr.cxx
@@ -0,0 +1,14 @@
+// file : odb/pgsql/query-const-expr.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/query.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ // Sun CC cannot handle this in query.cxx.
+ //
+ const query_base query_base::true_expr (true);
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/query-dynamic.cxx b/libodb-pgsql/odb/pgsql/query-dynamic.cxx
new file mode 100644
index 0000000..ebd62de
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/query-dynamic.cxx
@@ -0,0 +1,157 @@
+// file : odb/pgsql/query-dynamic.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+
+#include <odb/pgsql/query-dynamic.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace pgsql
+ {
+ static const char* logic_operators[] = {") AND (", ") OR ("};
+ static const char* comp_operators[] = {"=", "!=", "<", ">", "<=", ">="};
+
+ static void
+ translate (query_base& q, const odb::query_base& s, size_t p)
+ {
+ typedef odb::query_base::clause_part part;
+
+ const part& x (s.clause ()[p]);
+
+ switch (x.kind)
+ {
+ case part::kind_column:
+ {
+ const query_column_base* c (
+ static_cast<const query_column_base*> (
+ x.native_info[id_pgsql].column));
+
+ q.append (c->table (), c->column ());
+ break;
+ }
+ case part::kind_param_val:
+ case part::kind_param_ref:
+ {
+ const query_column_base* c (
+ static_cast<const query_column_base*> (
+ x.native_info[id_pgsql].column));
+
+ query_param_factory f (
+ reinterpret_cast<query_param_factory> (
+ x.native_info[id_pgsql].param_factory));
+
+ const odb::query_param* p (
+ reinterpret_cast<const odb::query_param*> (x.data));
+
+ q.append (f (p->value, x.kind == part::kind_param_ref),
+ c->conversion ());
+ break;
+ }
+ case part::kind_native:
+ {
+ q.append (s.strings ()[x.data]);
+ break;
+ }
+ case part::kind_true:
+ case part::kind_false:
+ {
+ q.append (x.kind == part::kind_true);
+ break;
+ }
+ case part::op_add:
+ {
+ translate (q, s, x.data);
+ translate (q, s, p - 1);
+ break;
+ }
+ case part::op_and:
+ case part::op_or:
+ {
+ q += "(";
+ translate (q, s, x.data);
+ q += logic_operators[x.kind - part::op_and];
+ translate (q, s, p - 1);
+ q += ")";
+ break;
+ }
+ case part::op_not:
+ {
+ q += "NOT (";
+ translate (q, s, p - 1);
+ q += ")";
+ break;
+ }
+ case part::op_null:
+ case part::op_not_null:
+ {
+ translate (q, s, p - 1);
+ q += (x.kind == part::op_null ? "IS NULL" : "IS NOT NULL");
+ break;
+ }
+ case part::op_in:
+ {
+ if (x.data != 0)
+ {
+ size_t b (p - x.data);
+
+ translate (q, s, b - 1); // column
+ q += "IN (";
+
+ for (size_t i (b); i != p; ++i)
+ {
+ if (i != b)
+ q += ",";
+
+ translate (q, s, i);
+ }
+
+ q += ")";
+ }
+ else
+ q.append (false);
+
+ break;
+ }
+ case part::op_like:
+ {
+ translate (q, s, p - 2); // column
+ q += "LIKE";
+ translate (q, s, p - 1); // pattern
+ break;
+ }
+ case part::op_like_escape:
+ {
+ translate (q, s, p - 3); // column
+ q += "LIKE";
+ translate (q, s, p - 2); // pattern
+ q += "ESCAPE";
+ translate (q, s, p - 1); // escape
+ break;
+ }
+ case part::op_eq:
+ case part::op_ne:
+ case part::op_lt:
+ case part::op_gt:
+ case part::op_le:
+ case part::op_ge:
+ {
+ translate (q, s, x.data);
+ q += comp_operators[x.kind - part::op_eq];
+ translate (q, s, p - 1);
+ break;
+ }
+ }
+ }
+
+ query_base::
+ query_base (const odb::query_base& q)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ if (!q.empty ())
+ translate (*this, q, q.clause ().size () - 1);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/query-dynamic.hxx b/libodb-pgsql/odb/pgsql/query-dynamic.hxx
new file mode 100644
index 0000000..df87519
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/query-dynamic.hxx
@@ -0,0 +1,32 @@
+// file : odb/pgsql/query-dynamic.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_QUERY_DYNAMIC_HXX
+#define ODB_PGSQL_QUERY_DYNAMIC_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/query.hxx>
+#include <odb/query-dynamic.hxx>
+
+#include <odb/pgsql/query.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ typedef details::shared_ptr<query_param> (*query_param_factory) (
+ const void* val, bool by_ref);
+
+ template <typename T, database_type_id ID>
+ details::shared_ptr<query_param>
+ query_param_factory_impl (const void*, bool);
+ }
+}
+
+#include <odb/pgsql/query-dynamic.ixx>
+#include <odb/pgsql/query-dynamic.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_QUERY_DYNAMIC_HXX
diff --git a/libodb-pgsql/odb/pgsql/query-dynamic.ixx b/libodb-pgsql/odb/pgsql/query-dynamic.ixx
new file mode 100644
index 0000000..016d213
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/query-dynamic.ixx
@@ -0,0 +1,26 @@
+// file : odb/pgsql/query-dynamic.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ //
+ template <typename T, database_type_id ID>
+ inline query_column<T, ID>::
+ query_column (odb::query_column<T>& qc,
+ const char* table, const char* column, const char* conv)
+ : query_column_base (table, column, conv)
+ {
+ native_column_info& ci (qc.native_info[id_pgsql]);
+ ci.column = static_cast<query_column_base*> (this);
+
+ // For some reason GCC needs this statically-typed pointer in
+ // order to instantiate the functions.
+ //
+ query_param_factory f (&query_param_factory_impl<T, ID>);
+ ci.param_factory = reinterpret_cast<void*> (f);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/query-dynamic.txx b/libodb-pgsql/odb/pgsql/query-dynamic.txx
new file mode 100644
index 0000000..7cfaf63
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/query-dynamic.txx
@@ -0,0 +1,20 @@
+// file : odb/pgsql/query-dynamic.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T, database_type_id ID>
+ details::shared_ptr<query_param>
+ query_param_factory_impl (const void* val, bool by_ref)
+ {
+ const T& v (*static_cast<const T*> (val));
+
+ return details::shared_ptr<query_param> (
+ by_ref
+ ? new (details::shared) query_param_impl<T, ID> (ref_bind<T> (v))
+ : new (details::shared) query_param_impl<T, ID> (val_bind<T> (v)));
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/query.cxx b/libodb-pgsql/odb/pgsql/query.cxx
new file mode 100644
index 0000000..b096c97
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/query.cxx
@@ -0,0 +1,444 @@
+// file : odb/pgsql/query.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+#include <cassert>
+#include <sstream>
+
+#include <odb/pgsql/query.hxx>
+#include <odb/pgsql/statement.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace pgsql
+ {
+ // query_param
+ //
+ query_param::
+ ~query_param ()
+ {
+ }
+
+ // query_base
+ //
+ query_base::
+ query_base (const query_base& q)
+ : clause_ (q.clause_),
+ parameters_ (q.parameters_),
+ bind_ (q.bind_),
+ binding_ (0, 0),
+ values_ (q.values_),
+ lengths_ (q.lengths_),
+ formats_ (q.formats_),
+ types_ (q.types_),
+ native_binding_ (0, 0, 0, 0)
+ {
+ // Here and below we want to maintain up to date binding info so
+ // that the call to parameters_binding() below is an immutable
+ // operation, provided the query does not have any by-reference
+ // parameters. This way a by-value-only query can be shared
+ // between multiple threads without the need for synchronization.
+ //
+ if (size_t n = bind_.size ())
+ {
+ binding_.bind = &bind_[0];
+ binding_.count = n;
+ binding_.version++;
+
+ native_binding_.values = &values_[0];
+ native_binding_.lengths = &lengths_[0];
+ native_binding_.formats = &formats_[0];
+ native_binding_.count = n;
+
+ assert (values_.size () == n);
+ assert (lengths_.size () == n);
+ assert (formats_.size () == n);
+ assert (types_.size () == n);
+
+ statement::bind_param (native_binding_, binding_);
+ }
+ }
+
+ query_base& query_base::
+ operator= (const query_base& q)
+ {
+ if (this != &q)
+ {
+ clause_ = q.clause_;
+ parameters_ = q.parameters_;
+ bind_ = q.bind_;
+
+ size_t n (bind_.size ());
+
+ binding_.count = n;
+ binding_.version++;
+
+ values_ = q.values_;
+ lengths_ = q.lengths_;
+ formats_ = q.formats_;
+ types_ = q.types_;
+
+ native_binding_.count = n;
+
+ assert (values_.size () == n);
+ assert (lengths_.size () == n);
+ assert (formats_.size () == n);
+ assert (types_.size () == n);
+
+ if (n != 0)
+ {
+ binding_.bind = &bind_[0];
+
+ native_binding_.values = &values_[0];
+ native_binding_.lengths = &lengths_[0];
+ native_binding_.formats = &formats_[0];
+
+ statement::bind_param (native_binding_, binding_);
+ }
+ }
+
+ return *this;
+ }
+
+ query_base& query_base::
+ operator+= (const query_base& q)
+ {
+ clause_.insert (clause_.end (), q.clause_.begin (), q.clause_.end ());
+
+ size_t n (bind_.size ());
+
+ parameters_.insert (
+ parameters_.end (), q.parameters_.begin (), q.parameters_.end ());
+
+ bind_.insert (
+ bind_.end (), q.bind_.begin (), q.bind_.end ());
+
+ values_.insert (
+ values_.end (), q.values_.begin (), q.values_.end ());
+
+ lengths_.insert (
+ lengths_.end (), q.lengths_.begin (), q.lengths_.end ());
+
+ formats_.insert (
+ formats_.end (), q.formats_.begin (), q.formats_.end ());
+
+ types_.insert (
+ types_.end (), q.types_.begin (), q.types_.end ());
+
+ if (n != bind_.size ())
+ {
+ n = bind_.size ();
+
+ binding_.bind = &bind_[0];
+ binding_.count = n;
+ binding_.version++;
+
+ assert (values_.size () == n);
+ assert (lengths_.size () == n);
+ assert (formats_.size () == n);
+ assert (types_.size () == n);
+
+ native_binding_.values = &values_[0];
+ native_binding_.lengths = &lengths_[0];
+ native_binding_.formats = &formats_[0];
+ native_binding_.count = n;
+
+ statement::bind_param (native_binding_, binding_);
+ }
+
+ return *this;
+ }
+
+ void query_base::
+ append (const string& q)
+ {
+ if (!clause_.empty () &&
+ clause_.back ().kind == clause_part::kind_native)
+ {
+ string& s (clause_.back ().part);
+
+ char first (!q.empty () ? q[0] : ' ');
+ char last (!s.empty () ? s[s.size () - 1] : ' ');
+
+ // We don't want extra spaces after '(' as well as before ','
+ // and ')'.
+ //
+ if (last != ' ' && last != '\n' && last != '(' &&
+ first != ' ' && first != '\n' && first != ',' && first != ')')
+ s += ' ';
+
+ s += q;
+ }
+ else
+ clause_.push_back (clause_part (clause_part::kind_native, q));
+ }
+
+ void query_base::
+ append (const char* table, const char* column)
+ {
+ string s (table);
+ s += '.';
+ s += column;
+
+ clause_.push_back (clause_part (clause_part::kind_column, s));
+ }
+
+ void query_base::
+ append (details::shared_ptr<query_param> p, const char* conv)
+ {
+ clause_.push_back (clause_part (clause_part::kind_param));
+
+ if (conv != 0)
+ clause_.back ().part = conv;
+
+ parameters_.push_back (p);
+ bind_.push_back (bind ());
+ binding_.bind = &bind_[0];
+ binding_.count = bind_.size ();
+ binding_.version++;
+
+ bind* b (&bind_.back ());
+ memset (b, 0, sizeof (bind));
+ p->bind (b);
+
+ values_.push_back (0);
+ lengths_.push_back (0);
+ formats_.push_back (0);
+ native_binding_.values = &values_[0];
+ native_binding_.lengths = &lengths_[0];
+ native_binding_.formats = &formats_[0];
+
+ // native_binding_.count should always equal binding_.count.
+ // At this point, we know that we have added one element to
+ // each array, so there is no need to check.
+ //
+ native_binding_.count = binding_.count;
+
+ types_.push_back (p->oid ());
+
+ statement::bind_param (native_binding_, binding_);
+ }
+
+ void query_base::
+ init_parameters () const
+ {
+ bool ref (false), inc_ver (false);
+
+ for (size_t i (0); i < parameters_.size (); ++i)
+ {
+ query_param& p (*parameters_[i]);
+
+ if (p.reference ())
+ {
+ ref = true;
+
+ if (p.init ())
+ {
+ p.bind (&bind_[i]);
+ inc_ver = true;
+ }
+ }
+ }
+
+ if (ref)
+ {
+ statement::bind_param (native_binding_, binding_);
+
+ if (inc_ver)
+ binding_.version++;
+ }
+ }
+
+ static bool
+ check_prefix (const string& s)
+ {
+ string::size_type n;
+
+ // It is easier to compare to upper and lower-case versions
+ // rather than getting involved with the portable case-
+ // insensitive string comparison mess.
+ //
+ if (s.compare (0, (n = 5), "WHERE") == 0 ||
+ s.compare (0, (n = 5), "where") == 0 ||
+ s.compare (0, (n = 6), "SELECT") == 0 ||
+ s.compare (0, (n = 6), "select") == 0 ||
+ s.compare (0, (n = 8), "ORDER BY") == 0 ||
+ s.compare (0, (n = 8), "order by") == 0 ||
+ s.compare (0, (n = 8), "GROUP BY") == 0 ||
+ s.compare (0, (n = 8), "group by") == 0 ||
+ s.compare (0, (n = 6), "HAVING") == 0 ||
+ s.compare (0, (n = 6), "having") == 0 ||
+ s.compare (0, (n = 4), "WITH") == 0 ||
+ s.compare (0, (n = 4), "with") == 0)
+ {
+ // It either has to be an exact match, or there should be
+ // a whitespace following the keyword.
+ //
+ if (s.size () == n || s[n] == ' ' || s[n] == '\n' || s[n] =='\t')
+ return true;
+ }
+
+ return false;
+ }
+
+ void query_base::
+ optimize ()
+ {
+ // Remove a single TRUE literal or one that is followe by one of
+ // the other clauses. This avoids useless WHERE clauses like
+ //
+ // WHERE TRUE GROUP BY foo
+ //
+ clause_type::iterator i (clause_.begin ()), e (clause_.end ());
+
+ if (i != e && i->kind == clause_part::kind_bool && i->bool_part)
+ {
+ clause_type::iterator j (i + 1);
+
+ if (j == e ||
+ (j->kind == clause_part::kind_native && check_prefix (j->part)))
+ clause_.erase (i);
+ }
+ }
+
+ const char* query_base::
+ clause_prefix () const
+ {
+ if (!clause_.empty ())
+ {
+ const clause_part& p (clause_.front ());
+
+ if (p.kind == clause_part::kind_native && check_prefix (p.part))
+ return "";
+
+ return "WHERE ";
+ }
+
+ return "";
+ }
+
+ string query_base::
+ clause () const
+ {
+ string r;
+ size_t param (1);
+
+ for (clause_type::const_iterator i (clause_.begin ()),
+ end (clause_.end ());
+ i != end;
+ ++i)
+ {
+ char last (!r.empty () ? r[r.size () - 1] : ' ');
+
+ switch (i->kind)
+ {
+ case clause_part::kind_column:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ r += i->part;
+ break;
+ }
+ case clause_part::kind_param:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ ostringstream os;
+ os << param++;
+
+ // Add the conversion expression, if any.
+ //
+ string::size_type p (0);
+ if (!i->part.empty ())
+ {
+ p = i->part.find ("(?)");
+ r.append (i->part, 0, p);
+ }
+
+ r += '$';
+ r += os.str ();
+
+ if (!i->part.empty ())
+ r.append (i->part, p + 3, string::npos);
+
+ break;
+ }
+ case clause_part::kind_native:
+ {
+ // We don't want extra spaces after '(' as well as before ','
+ // and ')'.
+ //
+ const string& p (i->part);
+ char first (!p.empty () ? p[0] : ' ');
+
+ if (last != ' ' && last != '\n' && last != '(' &&
+ first != ' ' && first != '\n' && first != ',' && first != ')')
+ r += ' ';
+
+ r += p;
+ break;
+ }
+ case clause_part::kind_bool:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ r += i->bool_part ? "TRUE" : "FALSE";
+ break;
+ }
+ }
+ }
+
+ return clause_prefix () + r;
+ }
+
+ query_base
+ operator&& (const query_base& x, const query_base& y)
+ {
+ // Optimize cases where one or both sides are constant truth.
+ //
+ bool xt (x.const_true ()), yt (y.const_true ());
+
+ if (xt && yt)
+ return x;
+
+ if (xt)
+ return y;
+
+ if (yt)
+ return x;
+
+ query_base r ("(");
+ r += x;
+ r += ") AND (";
+ r += y;
+ r += ")";
+ return r;
+ }
+
+ query_base
+ operator|| (const query_base& x, const query_base& y)
+ {
+ query_base r ("(");
+ r += x;
+ r += ") OR (";
+ r += y;
+ r += ")";
+ return r;
+ }
+
+ query_base
+ operator! (const query_base& x)
+ {
+ query_base r ("NOT (");
+ r += x;
+ r += ")";
+ return r;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/query.hxx b/libodb-pgsql/odb/pgsql/query.hxx
new file mode 100644
index 0000000..42182d6
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/query.hxx
@@ -0,0 +1,2181 @@
+// file : odb/pgsql/query.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_QUERY_HXX
+#define ODB_PGSQL_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx> // odb::query_column
+#include <odb/query.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/traits.hxx>
+#include <odb/pgsql/binding.hxx>
+#include <odb/pgsql/pgsql-oid.hxx>
+
+#include <odb/details/buffer.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+#include <odb/pgsql/details/conversion.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ struct val_bind
+ {
+ typedef const T& type;
+
+ explicit
+ val_bind (type v): val (v) {}
+
+ type val;
+ };
+
+ template <typename T, std::size_t N>
+ struct val_bind<T[N]>
+ {
+ typedef const T* type;
+
+ explicit
+ val_bind (type v): val (v) {}
+
+ type val;
+ };
+
+ template <typename T>
+ struct ref_bind
+ {
+ typedef const T& type;
+
+ explicit
+ ref_bind (type r): ref (r) {}
+
+ const void*
+ ptr () const {return &ref;}
+
+ type ref;
+ };
+
+ template <typename T, std::size_t N>
+ struct ref_bind<T[N]>
+ {
+ typedef const T* type;
+
+ explicit
+ ref_bind (type r): ref (r) {}
+
+ // Allow implicit conversion from decayed ref_bind's.
+ //
+ ref_bind (ref_bind<T*> r): ref (r.ref) {}
+ ref_bind (ref_bind<const T*> r): ref (r.ref) {}
+
+ const void*
+ ptr () const {return ref;}
+
+ type ref;
+ };
+
+ template <typename T, database_type_id ID>
+ struct val_bind_typed: val_bind<T>
+ {
+ explicit
+ val_bind_typed (typename val_bind<T>::type v): val_bind<T> (v) {}
+ };
+
+ template <typename T, database_type_id ID>
+ struct ref_bind_typed: ref_bind<T>
+ {
+ explicit
+ ref_bind_typed (typename ref_bind<T>::type r): ref_bind<T> (r) {}
+ };
+
+ struct LIBODB_PGSQL_EXPORT query_param: details::shared_base
+ {
+ typedef pgsql::bind bind_type;
+
+ virtual
+ ~query_param ();
+
+ bool
+ reference () const
+ {
+ return value_ != 0;
+ }
+
+ virtual bool
+ init () = 0;
+
+ virtual void
+ bind (bind_type*) = 0;
+
+ virtual unsigned int
+ oid () const = 0;
+
+ protected:
+ query_param (const void* value)
+ : value_ (value)
+ {
+ }
+
+ protected:
+ const void* value_;
+ };
+
+ //
+ //
+ template <typename T, database_type_id ID>
+ struct query_column;
+
+ class LIBODB_PGSQL_EXPORT query_base
+ {
+ public:
+ struct clause_part
+ {
+ enum kind_type
+ {
+ kind_column,
+ kind_param,
+ kind_native,
+ kind_bool
+ };
+
+ clause_part (kind_type k): kind (k), bool_part (false) {}
+ clause_part (kind_type k, const std::string& p)
+ : kind (k), part (p), bool_part (false) {}
+ clause_part (bool p): kind (kind_bool), bool_part (p) {}
+
+ kind_type kind;
+ std::string part; // If kind is param, then part is conversion expr.
+ bool bool_part;
+ };
+
+ query_base ()
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ }
+
+ // True or false literal.
+ //
+ explicit
+ query_base (bool v)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ append (v);
+ }
+
+ explicit
+ query_base (const char* native)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ clause_.push_back (clause_part (clause_part::kind_native, native));
+ }
+
+ explicit
+ query_base (const std::string& native)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ clause_.push_back (clause_part (clause_part::kind_native, native));
+ }
+
+ query_base (const char* table, const char* column)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ append (table, column);
+ }
+
+ template <typename T>
+ explicit
+ query_base (val_bind<T> v)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ *this += v;
+ }
+
+ template <typename T, database_type_id ID>
+ explicit
+ query_base (val_bind_typed<T, ID> v)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ *this += v;
+ }
+
+ template <typename T>
+ explicit
+ query_base (ref_bind<T> r)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ *this += r;
+ }
+
+ template <typename T, database_type_id ID>
+ explicit
+ query_base (ref_bind_typed<T, ID> r)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ *this += r;
+ }
+
+ template <database_type_id ID>
+ query_base (const query_column<bool, ID>&);
+
+ // Translate common query representation to PostgreSQL native. Defined
+ // in query-dynamic.cxx
+ //
+ query_base (const odb::query_base&);
+
+ // Copy c-tor and assignment.
+ //
+ query_base (const query_base&);
+
+ query_base&
+ operator= (const query_base&);
+
+ public:
+ std::string
+ clause () const;
+
+ const char*
+ clause_prefix () const;
+
+ // Initialize the by-reference parameters from bound variables.
+ //
+ void
+ init_parameters () const;
+
+ native_binding&
+ parameters_binding () const;
+
+ const unsigned int*
+ parameter_types () const
+ {
+ return types_.empty () ? 0 : &types_[0];
+ }
+
+ std::size_t
+ parameter_count () const
+ {
+ return parameters_.size ();
+ }
+
+ public:
+ bool
+ empty () const
+ {
+ return clause_.empty ();
+ }
+
+ static const query_base true_expr;
+
+ bool
+ const_true () const
+ {
+ return clause_.size () == 1 &&
+ clause_.front ().kind == clause_part::kind_bool &&
+ clause_.front ().bool_part;
+ }
+
+ void
+ optimize ();
+
+ public:
+ template <typename T>
+ static val_bind<T>
+ _val (const T& x)
+ {
+ return val_bind<T> (x);
+ }
+
+ template <database_type_id ID, typename T>
+ static val_bind_typed<T, ID>
+ _val (const T& x)
+ {
+ return val_bind_typed<T, ID> (x);
+ }
+
+ template <typename T>
+ static ref_bind<T>
+ _ref (const T& x)
+ {
+ return ref_bind<T> (x);
+ }
+
+ template <database_type_id ID, typename T>
+ static ref_bind_typed<T, ID>
+ _ref (const T& x)
+ {
+ return ref_bind_typed<T, ID> (x);
+ }
+
+ // Some compilers (notably VC++), when deducing const T& from const
+ // array do not strip const from the array type. As a result, in the
+ // above signatures we get, for example, T = const char[4] instead
+ // of T = char[4], which is what we want. So to "fix" such compilers,
+ // we will have to provide the following specializations of the above
+ // functions.
+ //
+ template <typename T, std::size_t N>
+ static val_bind<T[N]>
+ _val (const T (&x) [N])
+ {
+ return val_bind<T[N]> (x);
+ }
+
+ template <database_type_id ID, typename T, std::size_t N>
+ static val_bind_typed<T[N], ID>
+ _val (const T (&x) [N])
+ {
+ return val_bind_typed<T[N], ID> (x);
+ }
+
+ template <typename T, std::size_t N>
+ static ref_bind<T[N]>
+ _ref (const T (&x) [N])
+ {
+ return ref_bind<T[N]> (x);
+ }
+
+ template <database_type_id ID, typename T, std::size_t N>
+ static ref_bind_typed<T[N], ID>
+ _ref (const T (&x) [N])
+ {
+ return ref_bind_typed<T[N], ID> (x);
+ }
+
+ public:
+ query_base&
+ operator+= (const query_base&);
+
+ query_base&
+ operator+= (const std::string& q)
+ {
+ append (q);
+ return *this;
+ }
+
+ template <typename T>
+ query_base&
+ operator+= (val_bind<T> v)
+ {
+ append<T, type_traits<T>::db_type_id> (
+ v, details::conversion<T>::to ());
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (val_bind_typed<T, ID> v)
+ {
+ // We are not using default type_traits so no default conversion
+ // either.
+ //
+ append<T, ID> (v, 0);
+ return *this;
+ }
+
+ template <typename T>
+ query_base&
+ operator+= (ref_bind<T> r)
+ {
+ append<T, type_traits<T>::db_type_id> (
+ r, details::conversion<T>::to ());
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (ref_bind_typed<T, ID> r)
+ {
+ // We are not using default type_traits so no default conversion
+ // either.
+ //
+ append<T, ID> (r, 0);
+ return *this;
+ }
+
+ // Implementation details.
+ //
+ public:
+ template <typename T, database_type_id ID>
+ void
+ append (val_bind<T>, const char* conv);
+
+ template <typename T, database_type_id ID>
+ void
+ append (ref_bind<T>, const char* conv);
+
+ void
+ append (details::shared_ptr<query_param>, const char* conv);
+
+ void
+ append (bool v)
+ {
+ clause_.push_back (clause_part (v));
+ }
+
+ void
+ append (const std::string& native);
+
+ void
+ append (const char* native) // Clashes with append(bool).
+ {
+ append (std::string (native));
+ }
+
+ void
+ append (const char* table, const char* column);
+
+ private:
+ typedef std::vector<clause_part> clause_type;
+ typedef std::vector<details::shared_ptr<query_param> > parameters_type;
+
+ clause_type clause_;
+ parameters_type parameters_;
+
+ mutable std::vector<bind> bind_;
+ mutable binding binding_;
+
+ std::vector<char*> values_;
+ std::vector<int> lengths_;
+ std::vector<int> formats_;
+ std::vector<unsigned int> types_;
+ mutable native_binding native_binding_;
+ };
+
+ inline query_base
+ operator+ (const query_base& x, const query_base& y)
+ {
+ query_base r (x);
+ r += y;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, val_bind<T> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (val_bind<T> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, val_bind_typed<T, ID> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (val_bind_typed<T, ID> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, ref_bind<T> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (ref_bind<T> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, ref_bind_typed<T, ID> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (ref_bind_typed<T, ID> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const query_base& q, const std::string& s)
+ {
+ query_base r (q);
+ r += s;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const std::string& s, const query_base& q)
+ {
+ query_base r (s);
+ r += q;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, val_bind<T> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (val_bind<T> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, val_bind_typed<T, ID> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (val_bind_typed<T, ID> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, ref_bind<T> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (ref_bind<T> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, ref_bind_typed<T, ID> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (ref_bind_typed<T, ID> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ LIBODB_PGSQL_EXPORT query_base
+ operator&& (const query_base&, const query_base&);
+
+ LIBODB_PGSQL_EXPORT query_base
+ operator|| (const query_base&, const query_base&);
+
+ LIBODB_PGSQL_EXPORT query_base
+ operator! (const query_base&);
+
+ // query_column
+ //
+ struct query_column_base
+ {
+ // Note that we keep shallow copies of the table, column, and conversion
+ // expression. The latter can be NULL.
+ //
+ query_column_base (const char* table,
+ const char* column,
+ const char* conv)
+ : table_ (table), column_ (column), conversion_ (conv)
+ {
+ }
+
+ const char*
+ table () const
+ {
+ return table_;
+ }
+
+ const char*
+ column () const
+ {
+ return column_;
+ }
+
+ // Can be NULL.
+ //
+ const char*
+ conversion () const
+ {
+ return conversion_;
+ }
+
+ protected:
+ const char* table_;
+ const char* column_;
+ const char* conversion_;
+ };
+
+ template <typename T, database_type_id ID>
+ struct query_column: query_column_base
+ {
+ typedef typename decay_traits<T>::type decayed_type;
+
+ // Note that we keep shallow copies of the table, column, and conversion
+ // expression. The latter can be NULL.
+ //
+ query_column (const char* table, const char* column, const char* conv)
+ : query_column_base (table, column, conv) {}
+
+ // Implementation is in query-dynamic.ixx.
+ //
+ query_column (odb::query_column<T>&,
+ const char* table, const char* column, const char* conv);
+
+ // is_null, is_not_null
+ //
+ public:
+ query_base
+ is_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NULL";
+ return q;
+ }
+
+ query_base
+ is_not_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NOT NULL";
+ return q;
+ }
+
+ // in
+ //
+ public:
+ query_base
+ in (decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type, decayed_type,
+ decayed_type) const;
+
+ template <typename I>
+ query_base
+ in_range (I begin, I end) const;
+
+ // like
+ //
+ public:
+ query_base
+ like (decayed_type pattern) const
+ {
+ return like (val_bind<T> (pattern));
+ }
+
+ query_base
+ like (val_bind<T> pattern) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern) const
+ {
+ return like (val_bind<T> (decayed_type (pattern.val)));
+ }
+
+ query_base
+ like (ref_bind<T> pattern) const;
+
+ query_base
+ like (decayed_type pattern, decayed_type escape) const
+ {
+ return like (val_bind<T> (pattern), escape);
+ }
+
+ query_base
+ like (val_bind<T> pattern, decayed_type escape) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern, decayed_type escape) const
+ {
+ return like (val_bind<T> (decayed_type (pattern.val)), escape);
+ }
+
+ query_base
+ like (ref_bind<T> pattern, decayed_type escape) const;
+
+ // =
+ //
+ public:
+ query_base
+ equal (decayed_type v) const
+ {
+ return equal (val_bind<T> (v));
+ }
+
+ query_base
+ equal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ equal (val_bind<T2> v) const
+ {
+ return equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ equal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator== (const query_column& c, decayed_type v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (decayed_type v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, val_bind<T> v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (val_bind<T> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (const query_column& c, val_bind<T2> v)
+ {
+ return c.equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (val_bind<T2> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, ref_bind<T> r)
+ {
+ return c.equal (r);
+ }
+
+ friend query_base
+ operator== (ref_bind<T> r, const query_column& c)
+ {
+ return c.equal (r);
+ }
+
+ // !=
+ //
+ public:
+ query_base
+ unequal (decayed_type v) const
+ {
+ return unequal (val_bind<T> (v));
+ }
+
+ query_base
+ unequal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "!=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ unequal (val_bind<T2> v) const
+ {
+ return unequal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ unequal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "!=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator!= (const query_column& c, decayed_type v)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (decayed_type v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (const query_column& c, val_bind<T> v)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (val_bind<T> v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (const query_column& c, val_bind<T2> v)
+ {
+ return c.unequal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (val_bind<T2> v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (const query_column& c, ref_bind<T> r)
+ {
+ return c.unequal (r);
+ }
+
+ friend query_base
+ operator!= (ref_bind<T> r, const query_column& c)
+ {
+ return c.unequal (r);
+ }
+
+ // <
+ //
+ public:
+ query_base
+ less (decayed_type v) const
+ {
+ return less (val_bind<T> (v));
+ }
+
+ query_base
+ less (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "<";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ less (val_bind<T2> v) const
+ {
+ return less (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ less (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "<";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator< (const query_column& c, decayed_type v)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator< (decayed_type v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator< (const query_column& c, val_bind<T> v)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator< (val_bind<T> v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (const query_column& c, val_bind<T2> v)
+ {
+ return c.less (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator< (const query_column& c, ref_bind<T> r)
+ {
+ return c.less (r);
+ }
+
+ friend query_base
+ operator< (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater (r);
+ }
+
+ // >
+ //
+ public:
+ query_base
+ greater (decayed_type v) const
+ {
+ return greater (val_bind<T> (v));
+ }
+
+ query_base
+ greater (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += ">";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ greater (val_bind<T2> v) const
+ {
+ return greater (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ greater (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += ">";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator> (const query_column& c, decayed_type v)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator> (decayed_type v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator> (const query_column& c, val_bind<T> v)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator> (val_bind<T> v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (val_bind<T2> v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator> (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater (r);
+ }
+
+ friend query_base
+ operator> (ref_bind<T> r, const query_column& c)
+ {
+ return c.less (r);
+ }
+
+ // <=
+ //
+ public:
+ query_base
+ less_equal (decayed_type v) const
+ {
+ return less_equal (val_bind<T> (v));
+ }
+
+ query_base
+ less_equal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "<=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ less_equal (val_bind<T2> v) const
+ {
+ return less_equal (val_bind<T> (decayed_type (v.val)));;
+ }
+
+ query_base
+ less_equal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "<=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator<= (const query_column& c, decayed_type v)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator<= (decayed_type v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator<= (const query_column& c, val_bind<T> v)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator<= (val_bind<T> v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (const query_column& c, val_bind<T2> v)
+ {
+ return c.less_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator<= (const query_column& c, ref_bind<T> r)
+ {
+ return c.less_equal (r);
+ }
+
+ friend query_base
+ operator<= (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater_equal (r);
+ }
+
+ // >=
+ //
+ public:
+ query_base
+ greater_equal (decayed_type v) const
+ {
+ return greater_equal (val_bind<T> (v));
+ }
+
+ query_base
+ greater_equal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += ">=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ greater_equal (val_bind<T2> v) const
+ {
+ return greater_equal (val_bind<T> (decayed_type (v.val)));;
+ }
+
+ query_base
+ greater_equal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += ">=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator>= (const query_column& c, decayed_type v)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator>= (decayed_type v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator>= (const query_column& c, val_bind<T> v)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator>= (val_bind<T> v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (val_bind<T2> v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator>= (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater_equal (r);
+ }
+
+ friend query_base
+ operator>= (ref_bind<T> r, const query_column& c)
+ {
+ return c.less_equal (r);
+ }
+
+ // Column comparison.
+ //
+ public:
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator== (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () ==
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator!= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () !=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "!=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator< (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "<";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator> (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += ">";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator<= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "<=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator>= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += ">=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+ };
+
+ // Provide operator+() for using columns to construct native
+ // query fragments (e.g., ORDER BY).
+ //
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_column<T, ID>& c, const std::string& s)
+ {
+ query_base q (c.table (), c.column ());
+ q += s;
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, const query_column<T, ID>& c)
+ {
+ query_base q (s);
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_column<T, ID>& c, const query_base& q)
+ {
+ query_base r (c.table (), c.column ());
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, const query_column<T, ID>& c)
+ {
+ query_base r (q);
+ r.append (c.table (), c.column ());
+ return r;
+ }
+
+ //
+ //
+ template <typename T, database_type_id>
+ struct query_param_impl;
+
+ // BOOLEAN
+ //
+ template <typename T>
+ struct query_param_impl<T, id_boolean>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::boolean_;
+ b->buffer = &image_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return bool_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_boolean>::set_image (image_, is_null, v);
+ }
+
+ private:
+ bool image_;
+ };
+
+ // SMALLINT
+ //
+ template <typename T>
+ struct query_param_impl<T, id_smallint>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::smallint;
+ b->buffer = &image_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return int2_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_smallint>::set_image (image_, is_null, v);
+ }
+
+ private:
+ short image_;
+ };
+
+ // INTEGER
+ //
+ template <typename T>
+ struct query_param_impl<T, id_integer>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::integer;
+ b->buffer = &image_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return int4_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_integer>::set_image (image_, is_null, v);
+ }
+
+ private:
+ int image_;
+ };
+
+ // BIGINT
+ //
+ template <typename T>
+ struct query_param_impl<T, id_bigint>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::bigint;
+ b->buffer = &image_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return int8_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_bigint>::set_image (image_, is_null, v);
+ }
+
+ private:
+ long long image_;
+ };
+
+ // REAL
+ //
+ template <typename T>
+ struct query_param_impl<T, id_real>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::real;
+ b->buffer = &image_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return float4_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_real>::set_image (image_, is_null, v);
+ }
+
+ private:
+ float image_;
+ };
+
+ // DOUBLE
+ //
+ template <typename T>
+ struct query_param_impl<T, id_double>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::double_;
+ b->buffer = &image_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return float8_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_double>::set_image (image_, is_null, v);
+ }
+
+ private:
+ double image_;
+ };
+
+ // NUMERIC
+ //
+ template <typename T>
+ struct query_param_impl<T, id_numeric>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::numeric;
+ b->buffer = buffer_.data_ptr ();
+ b->capacity = buffer_.capacity ();
+ b->size = &size_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return numeric_oid;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0), cap (buffer_.capacity ());
+ value_traits<T, id_numeric>::set_image (buffer_, size, is_null, v);
+ size_ = size;
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::buffer buffer_;
+ std::size_t size_;
+ };
+
+ // DATE
+ //
+ template <typename T>
+ struct query_param_impl<T, id_date>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::date;
+ b->buffer = &image_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return date_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_date>::set_image (image_, is_null, v);
+ }
+
+ private:
+ int image_;
+ };
+
+ // TIME
+ //
+ template <typename T>
+ struct query_param_impl<T, id_time>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::time;
+ b->buffer = &image_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return time_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_time>::set_image (image_, is_null, v);
+ }
+
+ private:
+ long long image_;
+ };
+
+ // TIMESTAMP
+ //
+ template <typename T>
+ struct query_param_impl<T, id_timestamp>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::timestamp;
+ b->buffer = &image_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return timestamp_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_timestamp>::set_image (image_, is_null, v);
+ }
+
+ private:
+ long long image_;
+ };
+
+ // STRING
+ //
+ template <typename T>
+ struct query_param_impl<T, id_string>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::text;
+ b->buffer = buffer_.data_ptr ();
+ b->capacity = buffer_.capacity ();
+ b->size = &size_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return text_oid;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0), cap (buffer_.capacity ());
+ value_traits<T, id_string>::set_image (buffer_, size, is_null, v);
+ size_ = size;
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::buffer buffer_;
+ std::size_t size_;
+ };
+
+ // BYTEA
+ //
+ template <typename T>
+ struct query_param_impl<T, id_bytea>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::bytea;
+ b->buffer = buffer_.data_ptr ();
+ b->capacity = buffer_.capacity ();
+ b->size = &size_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return bytea_oid;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0), cap (buffer_.capacity ());
+ value_traits<T, id_bytea>::set_image (buffer_, size, is_null, v);
+ size_ = size;
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::buffer buffer_;
+ std::size_t size_;
+ };
+
+ // BIT
+ //
+ template <typename T>
+ struct query_param_impl<T, id_bit>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::bit;
+ b->buffer = buffer_.data_ptr ();
+ b->capacity = buffer_.capacity ();
+ b->size = &size_;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0), cap (buffer_.capacity ());
+
+ // NOTE: Using a fixed size bit type in queries requires
+ // alternative image buffer type support.
+ //
+ value_traits<T, id_bit>::set_image (buffer_, size, is_null, v);
+ size_ = size;
+
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::ubuffer buffer_;
+ std::size_t size_;
+ };
+
+ // VARBIT
+ //
+ template <typename T>
+ struct query_param_impl<T, id_varbit>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::varbit;
+ b->buffer = buffer_.data_ptr ();
+ b->capacity = buffer_.capacity ();
+ b->size = &size_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return varbit_oid;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0), cap (buffer_.capacity ());
+ value_traits<T, id_varbit>::set_image (buffer_, size, is_null, v);
+ size_ = size;
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::ubuffer buffer_;
+ std::size_t size_;
+ };
+
+ // UUID
+ //
+ template <typename T>
+ struct query_param_impl<T, id_uuid>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::uuid;
+ b->buffer = buffer_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return uuid_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_uuid>::set_image (buffer_, is_null, v);
+ }
+
+ private:
+ unsigned char buffer_[16];
+ };
+ }
+}
+
+// odb::pgsql::query and odb::query specialization for PostgreSQL.
+//
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ class query: public query_base,
+ public query_selector<T, id_pgsql>::columns_type
+ {
+ public:
+ // We don't define any typedefs here since they may clash with
+ // column names defined by our base type.
+ //
+
+ query ()
+ {
+ }
+
+ explicit
+ query (bool v)
+ : query_base (v)
+ {
+ }
+
+ explicit
+ query (const char* q)
+ : query_base (q)
+ {
+ }
+
+ explicit
+ query (const std::string& q)
+ : query_base (q)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (val_bind<T2> v)
+ : query_base (v)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (ref_bind<T2> r)
+ : query_base (r)
+ {
+ }
+
+ query (const query_base& q)
+ : query_base (q)
+ {
+ }
+
+ template <database_type_id ID>
+ query (const query_column<bool, ID>& qc)
+ : query_base (qc)
+ {
+ }
+
+ query (const odb::query_base& q)
+ : query_base (q)
+ {
+ }
+ };
+
+ namespace core
+ {
+ using pgsql::query;
+ }
+ }
+
+ // Derive odb::query from odb::pgsql::query so that it can be
+ // implicitly converted in pgsql::database::query() calls.
+ //
+ template <typename T>
+ class query<T, pgsql::query_base>: public pgsql::query<T>
+ {
+ public:
+ // We don't define any typedefs here since they may clash with
+ // column names defined by our base type.
+ //
+
+ query ()
+ {
+ }
+
+ explicit
+ query (bool v)
+ : pgsql::query<T> (v)
+ {
+ }
+
+ explicit
+ query (const char* q)
+ : pgsql::query<T> (q)
+ {
+ }
+
+ explicit
+ query (const std::string& q)
+ : pgsql::query<T> (q)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (pgsql::val_bind<T2> v)
+ : pgsql::query<T> (v)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (pgsql::ref_bind<T2> r)
+ : pgsql::query<T> (r)
+ {
+ }
+
+ query (const pgsql::query_base& q)
+ : pgsql::query<T> (q)
+ {
+ }
+
+ template <pgsql::database_type_id ID>
+ query (const pgsql::query_column<bool, ID>& qc)
+ : pgsql::query<T> (qc)
+ {
+ }
+ };
+}
+
+#include <odb/pgsql/query.ixx>
+#include <odb/pgsql/query.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_QUERY_HXX
diff --git a/libodb-pgsql/odb/pgsql/query.ixx b/libodb-pgsql/odb/pgsql/query.ixx
new file mode 100644
index 0000000..826b4ab
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/query.ixx
@@ -0,0 +1,34 @@
+// file : odb/pgsql/query.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace pgsql
+ {
+ inline native_binding& query_base::
+ parameters_binding () const
+ {
+ return native_binding_;
+ }
+
+ template <typename T, database_type_id ID>
+ inline void query_base::
+ append (val_bind<T> v, const char* conv)
+ {
+ append (
+ details::shared_ptr<query_param> (
+ new (details::shared) query_param_impl<T, ID> (v)),
+ conv);
+ }
+
+ template <typename T, database_type_id ID>
+ inline void query_base::
+ append (ref_bind<T> r, const char* conv)
+ {
+ append (
+ details::shared_ptr<query_param> (
+ new (details::shared) query_param_impl<T, ID> (r)),
+ conv);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/query.txx b/libodb-pgsql/odb/pgsql/query.txx
new file mode 100644
index 0000000..44dae30
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/query.txx
@@ -0,0 +1,168 @@
+// file : odb/pgsql/query.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ // query_base
+ //
+
+ template <database_type_id ID>
+ query_base::
+ query_base (const query_column<bool, ID>& c)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ // Cannot use IS TRUE here since database type can be a non-
+ // integral type.
+ //
+ append (c.table (), c.column ());
+ append ("=");
+ append<bool, ID> (val_bind<bool> (true), c.conversion ());
+ }
+
+ //
+ // query_column
+ //
+
+ // in
+ //
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3,
+ decayed_type v4) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v4), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3, decayed_type v4,
+ decayed_type v5) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v4), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v5), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ template <typename I>
+ query_base query_column<T, ID>::
+ in_range (I begin, I end) const
+ {
+ if (begin != end)
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+
+ for (I i (begin); i != end; ++i)
+ {
+ if (i != begin)
+ q += ",";
+
+ q.append<T, ID> (val_bind<T> (*i), conversion_);
+ }
+
+ q += ")";
+ return q;
+ }
+ else
+ return query_base (false);
+ }
+
+ // like
+ //
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (val_bind<T> p) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (ref_bind<T> p) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (val_bind<T> p, decayed_type e) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ q += "ESCAPE";
+ q.append<T, ID> (val_bind<T> (e), conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (ref_bind<T> p, decayed_type e) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ q += "ESCAPE";
+ q.append<T, ID> (val_bind<T> (e), conversion_);
+ return q;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/section-statements.hxx b/libodb-pgsql/odb/pgsql/section-statements.hxx
new file mode 100644
index 0000000..e40b282
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/section-statements.hxx
@@ -0,0 +1,220 @@
+// file : odb/pgsql/section-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_SECTION_STATEMENTS_HXX
+#define ODB_PGSQL_SECTION_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/binding.hxx>
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/pgsql-fwd.hxx> // Oid
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class connection;
+
+ // Template argument is the section traits type.
+ //
+ template <typename T, typename ST>
+ class section_statements
+ {
+ public:
+ typedef ST traits;
+
+ typedef typename traits::image_type image_type;
+ typedef typename traits::id_image_type id_image_type;
+
+ typedef pgsql::select_statement select_statement_type;
+ typedef pgsql::update_statement update_statement_type;
+
+ typedef pgsql::connection connection_type;
+
+ section_statements (connection_type&,
+ image_type&,
+ id_image_type&,
+ binding& id,
+ binding& idv,
+ native_binding& idn,
+ const Oid* idt);
+
+ connection_type&
+ connection () {return conn_;}
+
+ const schema_version_migration&
+ version_migration (const char* name = "") const
+ {
+ if (svm_ == 0)
+ svm_ = &conn_.database ().schema_version_migration (name);
+
+ return *svm_;
+ }
+
+ image_type&
+ image () {return image_;}
+
+ const binding&
+ id_binding () {return id_binding_;}
+
+ // Id and optimistic concurrency version (if any).
+ //
+ const binding&
+ idv_binding () {return idv_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_binding_version () const { return update_id_binding_version_;}
+
+ void
+ update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ //
+ // Statements.
+ //
+
+ select_statement_type&
+ select_statement ()
+ {
+ if (select_ == 0)
+ select_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ traits::select_name,
+ traits::select_statement,
+ traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ id_types_,
+ id_column_count,
+ id_binding_,
+ id_native_binding_,
+ select_image_binding_,
+ false));
+
+ return *select_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ traits::update_name,
+ traits::update_statement,
+ traits::versioned, // Process if versioned.
+ traits::update_types,
+ update_column_count + id_column_count +
+ managed_optimistic_update_column_count,
+ update_image_binding_,
+ update_image_native_binding_,
+ false));
+
+ return *update_;
+ }
+
+ public:
+ static const std::size_t id_column_count = traits::id_column_count;
+ static const std::size_t managed_optimistic_load_column_count =
+ traits::managed_optimistic_load_column_count;
+ static const std::size_t managed_optimistic_update_column_count =
+ traits::managed_optimistic_update_column_count;
+ static const std::size_t select_column_count = traits::load_column_count;
+ static const std::size_t update_column_count =
+ traits::update_column_count;
+
+ private:
+ section_statements (const section_statements&);
+ section_statements& operator= (const section_statements&);
+
+ protected:
+ connection_type& conn_;
+ mutable const schema_version_migration* svm_;
+
+ // These come from object_statements.
+ //
+ image_type& image_;
+ binding& id_binding_;
+ binding& idv_binding_;
+ native_binding& id_native_binding_;
+ const Oid* id_types_;
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+
+ static const std::size_t select_bind_count =
+ select_column_count != 0 || managed_optimistic_load_column_count != 0
+ ? select_column_count + managed_optimistic_load_column_count
+ : 1;
+
+ binding select_image_binding_;
+ bind select_image_bind_[select_bind_count];
+ bool select_image_truncated_[select_bind_count];
+
+ // Update binding.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_binding_version_;
+
+ static const std::size_t update_bind_count =
+ update_column_count != 0 || managed_optimistic_update_column_count != 0
+ ? update_column_count + id_column_count +
+ managed_optimistic_update_column_count
+ : 1;
+
+ binding update_image_binding_;
+ bind update_image_bind_[update_bind_count];
+
+ native_binding update_image_native_binding_;
+ char* update_image_values_[update_bind_count];
+ int update_image_lengths_[update_bind_count];
+ int update_image_formats_[update_bind_count];
+
+ details::shared_ptr<select_statement_type> select_;
+ details::shared_ptr<update_statement_type> update_;
+ };
+ }
+}
+
+#include <odb/pgsql/section-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_SECTION_STATEMENTS_HXX
diff --git a/libodb-pgsql/odb/pgsql/section-statements.txx b/libodb-pgsql/odb/pgsql/section-statements.txx
new file mode 100644
index 0000000..55f4093
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/section-statements.txx
@@ -0,0 +1,51 @@
+// file : odb/pgsql/section-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T, typename ST>
+ section_statements<T, ST>::
+ section_statements (connection_type& conn,
+ image_type& im,
+ id_image_type&,
+ binding& id,
+ binding& idv,
+ native_binding& idn,
+ const Oid* idt)
+ : conn_ (conn),
+ svm_ (0),
+ image_ (im),
+ id_binding_ (id),
+ idv_binding_ (idv),
+ id_native_binding_ (idn),
+ id_types_ (idt),
+ select_image_binding_ (select_image_bind_,
+ select_column_count +
+ managed_optimistic_load_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count +
+ managed_optimistic_update_column_count),
+ update_image_native_binding_ (update_image_values_,
+ update_image_lengths_,
+ update_image_formats_,
+ update_column_count + id_column_count +
+ managed_optimistic_update_column_count)
+ {
+ select_image_version_ = 0;
+ update_image_version_ = 0;
+ update_id_binding_version_ = 0;
+
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+
+ for (std::size_t i (0); i < select_bind_count; ++i)
+ select_image_bind_[i].truncated = select_image_truncated_ + i;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/simple-object-result.hxx b/libodb-pgsql/odb/pgsql/simple-object-result.hxx
new file mode 100644
index 0000000..7472cbe
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/simple-object-result.hxx
@@ -0,0 +1,85 @@
+// file : odb/pgsql/simple-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_SIMPLE_OBJECT_RESULT_HXX
+#define ODB_PGSQL_SIMPLE_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/simple-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx> // query_base
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ class object_result_impl: public odb::object_result_impl<T>
+ {
+ public:
+ typedef odb::object_result_impl<T> base_type;
+
+ typedef typename base_type::id_type id_type;
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_pgsql> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~object_result_impl ();
+
+ object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type&, bool fetch);
+
+ virtual id_type
+ load_id ();
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ void
+ load_image ();
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ std::size_t count_;
+ };
+ }
+}
+
+#include <odb/pgsql/simple-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_SIMPLE_OBJECT_RESULT_HXX
diff --git a/libodb-pgsql/odb/pgsql/simple-object-result.txx b/libodb-pgsql/odb/pgsql/simple-object-result.txx
new file mode 100644
index 0000000..c14d64b
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/simple-object-result.txx
@@ -0,0 +1,158 @@
+// file : odb/pgsql/simple-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/callback.hxx>
+
+#include <odb/pgsql/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ object_result_impl<T>::
+ ~object_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ object_result_impl<T>::
+ object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ count_ (0)
+ {
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ load (object_type& obj, bool fetch)
+ {
+ if (fetch)
+ load_image ();
+
+ // This is a top-level call so the statements cannot be locked.
+ //
+ assert (!statements_.locked ());
+ typename statements_type::auto_lock l (statements_);
+
+ object_traits::callback (this->db_, obj, callback_event::pre_load);
+
+ typename object_traits::image_type& i (statements_.image ());
+ tc_.init (obj, i, &this->db_);
+
+ // Initialize the id image and binding and load the rest of the object
+ // (containers, etc).
+ //
+ typename object_traits::id_image_type& idi (statements_.id_image ());
+ object_traits::init (idi, object_traits::id (i));
+
+ binding& idb (statements_.id_image_binding ());
+ if (idi.version != statements_.id_image_version () || idb.version == 0)
+ {
+ object_traits::bind (idb.bind, idi);
+ statements_.id_image_version (idi.version);
+ idb.version++;
+ }
+
+ tc_.load_ (statements_, obj, false);
+ statements_.load_delayed (tc_.version ());
+ l.unlock ();
+ object_traits::callback (this->db_, obj, callback_event::post_load);
+ }
+
+ template <typename T>
+ typename object_result_impl<T>::id_type
+ object_result_impl<T>::
+ load_id ()
+ {
+ load_image ();
+ return object_traits::id (statements_.image ());
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (statement_->next ())
+ count_++;
+ else
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ load_image ()
+ {
+ // The image can grow between calls to load() as a result of other
+ // statements execution.
+ //
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ }
+
+ select_statement::result r (statement_->load ());
+
+ if (r == select_statement::truncated)
+ {
+ if (tc_.grow (im, statements_.select_image_truncated ()))
+ im.version++;
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ statement_->reload ();
+ }
+ }
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t object_result_impl<T>::
+ size ()
+ {
+ return this->end_ ? count_ : statement_->result_size ();
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/simple-object-statements.cxx b/libodb-pgsql/odb/pgsql/simple-object-statements.cxx
new file mode 100644
index 0000000..432f990
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/simple-object-statements.cxx
@@ -0,0 +1,15 @@
+// file : odb/pgsql/simple-object-statements.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ object_statements_base::
+ ~object_statements_base ()
+ {
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/simple-object-statements.hxx b/libodb-pgsql/odb/pgsql/simple-object-statements.hxx
new file mode 100644
index 0000000..086ef5f
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/simple-object-statements.hxx
@@ -0,0 +1,657 @@
+// file : odb/pgsql/simple-object-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_SIMPLE_OBJECT_STATEMENTS_HXX
+#define ODB_PGSQL_SIMPLE_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cassert>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/pgsql-types.hxx>
+#include <odb/pgsql/binding.hxx>
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/statements-base.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ // The extra_statement_cache class is only defined (and used) in
+ // the generated source file. However, object_statements may be
+ // referenced from another source file in the case of a polymorphic
+ // hierarchy (though in this case the extra statement cache is
+ // not used). As a result, we cannot have a by-value member and
+ // instead will store a pointer and lazily allocate the cache if
+ // and when needed. We will also need to store a pointer to the
+ // deleter function which will be initialized during allocation
+ // (at that point we know that the cache class is defined).
+ //
+ template <typename T, typename I, typename ID>
+ struct extra_statement_cache_ptr
+ {
+ typedef I image_type;
+ typedef ID id_image_type;
+ typedef pgsql::connection connection_type;
+
+ extra_statement_cache_ptr (): p_ (0) {}
+ ~extra_statement_cache_ptr ()
+ {
+ if (p_ != 0)
+ (this->*deleter_) (0, 0, 0, 0, 0, 0, 0);
+ }
+
+ T&
+ get (connection_type& c,
+ image_type& im,
+ id_image_type& idim,
+ binding& id,
+ binding* idv,
+ native_binding& idn,
+ const Oid* idt)
+ {
+ if (p_ == 0)
+ allocate (&c, &im, &idim, &id, (idv != 0 ? idv : &id), &idn, idt);
+
+ return *p_;
+ }
+
+ private:
+ void
+ allocate (connection_type*,
+ image_type*, id_image_type*,
+ binding*, binding*, native_binding*, const Oid*);
+
+ private:
+ T* p_;
+ void (extra_statement_cache_ptr::*deleter_) (
+ connection_type*,
+ image_type*, id_image_type*,
+ binding*, binding*, native_binding*, const Oid*);
+ };
+
+ template <typename T, typename I, typename ID>
+ void extra_statement_cache_ptr<T, I, ID>::
+ allocate (connection_type* c,
+ image_type* im, id_image_type* idim,
+ binding* id, binding* idv, native_binding* idn, const Oid* idt)
+ {
+ // To reduce object code size, this function acts as both allocator
+ // and deleter.
+ //
+ if (p_ == 0)
+ {
+ p_ = new T (*c, *im, *idim, *id, *idv, *idn, idt);
+ deleter_ = &extra_statement_cache_ptr<T, I, ID>::allocate;
+ }
+ else
+ delete p_;
+ }
+
+ //
+ // Implementation for objects with object id.
+ //
+
+ class LIBODB_PGSQL_EXPORT object_statements_base: public statements_base
+ {
+ public:
+ // Locking.
+ //
+ void
+ lock ()
+ {
+ assert (!locked_);
+ locked_ = true;
+ }
+
+ void
+ unlock ()
+ {
+ assert (locked_);
+ locked_ = false;
+ }
+
+ bool
+ locked () const
+ {
+ return locked_;
+ }
+
+ struct auto_unlock
+ {
+ // Unlocks the statement on construction and re-locks it on
+ // destruction.
+ //
+ auto_unlock (object_statements_base&);
+ ~auto_unlock ();
+
+ private:
+ auto_unlock (const auto_unlock&);
+ auto_unlock& operator= (const auto_unlock&);
+
+ private:
+ object_statements_base& s_;
+ };
+
+ public:
+ virtual
+ ~object_statements_base ();
+
+ protected:
+ object_statements_base (connection_type& conn)
+ : statements_base (conn), locked_ (false)
+ {
+ }
+
+ protected:
+ bool locked_;
+ };
+
+ template <typename T, bool optimistic>
+ struct optimistic_data;
+
+ template <typename T>
+ struct optimistic_data<T, true>
+ {
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_pgsql> object_traits;
+
+ optimistic_data (bind*, char** nv, int* nl, int* nf,
+ std::size_t skip, unsigned long long* status);
+
+ binding*
+ id_image_binding () {return &id_image_binding_;}
+
+ native_binding*
+ id_image_native_binding () {return &id_image_native_binding_;}
+
+ const Oid*
+ id_image_types ()
+ {return object_traits::optimistic_erase_statement_types;}
+
+ // The id + optimistic column binding.
+ //
+ binding id_image_binding_;
+ native_binding id_image_native_binding_;
+
+ details::shared_ptr<delete_statement> erase_;
+ };
+
+ template <typename T>
+ struct optimistic_data<T, false>
+ {
+ optimistic_data (bind*, char**, int*, int*,
+ std::size_t, unsigned long long*) {}
+
+ binding*
+ id_image_binding () {return 0;}
+
+ native_binding*
+ id_image_native_binding () {return 0;}
+
+ const Oid*
+ id_image_types () {return 0;}
+ };
+
+ template <typename T>
+ class object_statements: public object_statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_pgsql> object_traits;
+ typedef typename object_traits::id_type id_type;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::image_type image_type;
+ typedef typename object_traits::id_image_type id_image_type;
+
+ typedef
+ typename object_traits::pointer_cache_traits
+ pointer_cache_traits;
+
+ typedef
+ typename object_traits::extra_statement_cache_type
+ extra_statement_cache_type;
+
+ typedef pgsql::insert_statement insert_statement_type;
+ typedef pgsql::select_statement select_statement_type;
+ typedef pgsql::update_statement update_statement_type;
+ typedef pgsql::delete_statement delete_statement_type;
+
+ // Automatic lock.
+ //
+ struct auto_lock
+ {
+ // Lock the statements unless they are already locked in which
+ // case subsequent calls to locked() will return false.
+ //
+ auto_lock (object_statements&);
+
+ // Unlock the statemens if we are holding the lock and clear
+ // the delayed loads. This should only happen in case an
+ // exception is thrown. In normal circumstances, the user
+ // should call unlock() explicitly.
+ //
+ ~auto_lock ();
+
+ // Return true if this auto_lock instance holds the lock.
+ //
+ bool
+ locked () const;
+
+ // Unlock the statemens.
+ //
+ void
+ unlock ();
+
+ private:
+ auto_lock (const auto_lock&);
+ auto_lock& operator= (const auto_lock&);
+
+ private:
+ object_statements& s_;
+ bool locked_;
+ };
+
+ public:
+ object_statements (connection_type&);
+
+ virtual
+ ~object_statements ();
+
+ // Delayed loading.
+ //
+ typedef void (*loader_function) (odb::database&,
+ const id_type&,
+ object_type&,
+ const schema_version_migration*);
+ void
+ delay_load (const id_type& id,
+ object_type& obj,
+ const typename pointer_cache_traits::position_type& p,
+ loader_function l = 0)
+ {
+ delayed_.push_back (delayed_load (id, obj, p, l));
+ }
+
+ void
+ load_delayed (const schema_version_migration* svm)
+ {
+ assert (locked ());
+
+ if (!delayed_.empty ())
+ load_delayed_<object_statements> (svm);
+ }
+
+ void
+ clear_delayed ()
+ {
+ if (!delayed_.empty ())
+ clear_delayed_ ();
+ }
+
+ // Object image.
+ //
+ image_type&
+ image (std::size_t i = 0) {return images_[i].obj;}
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_image_version () const { return update_id_image_version_;}
+
+ void
+ update_id_image_version (std::size_t v) {update_id_image_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Object id image and binding.
+ //
+ id_image_type&
+ id_image (std::size_t i = 0) {return images_[i].id;}
+
+ std::size_t
+ id_image_version () const {return id_image_version_;}
+
+ void
+ id_image_version (std::size_t v) {id_image_version_ = v;}
+
+ binding&
+ id_image_binding () {return id_image_binding_;}
+
+ // Optimistic id + managed column image binding. It points to
+ // the same suffix as id binding and they are always updated
+ // at the same time.
+ //
+ binding&
+ optimistic_id_image_binding () {return od_.id_image_binding_;}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement_name,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ object_traits::persist_statement_types,
+ insert_column_count,
+ insert_image_binding_,
+ insert_image_native_binding_,
+ (object_traits::auto_id ? &id_image_binding_ : 0),
+ false));
+
+ return *persist_;
+ }
+
+ select_statement_type&
+ find_statement ()
+ {
+ if (find_ == 0)
+ find_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ object_traits::find_statement_name,
+ object_traits::find_statement,
+ object_traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ object_traits::find_statement_types,
+ id_column_count,
+ id_image_binding_,
+ id_image_native_binding_,
+ select_image_binding_,
+ false));
+
+ return *find_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ object_traits::update_statement_name,
+ object_traits::update_statement,
+ object_traits::versioned, // Process if versioned.
+ object_traits::update_statement_types,
+ update_column_count + id_column_count,
+ update_image_binding_,
+ update_image_native_binding_,
+ false));
+
+ return *update_;
+ }
+
+ delete_statement_type&
+ erase_statement ()
+ {
+ if (erase_ == 0)
+ erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::erase_statement_name,
+ object_traits::erase_statement,
+ object_traits::find_statement_types, // The same as find (id).
+ id_column_count,
+ id_image_binding_,
+ id_image_native_binding_,
+ false));
+
+ return *erase_;
+ }
+
+ delete_statement_type&
+ optimistic_erase_statement ()
+ {
+ if (od_.erase_ == 0)
+ od_.erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::optimistic_erase_statement_name,
+ object_traits::optimistic_erase_statement,
+ object_traits::optimistic_erase_statement_types,
+ id_column_count + managed_optimistic_column_count,
+ od_.id_image_binding_,
+ od_.id_image_native_binding_,
+ false));
+
+ return *od_.erase_;
+ }
+
+ // Extra (container, section) statement cache.
+ //
+ extra_statement_cache_type&
+ extra_statement_cache ()
+ {
+ return extra_statement_cache_.get (
+ conn_,
+ images_[0].obj,
+ images_[0].id,
+ id_image_binding_,
+ od_.id_image_binding (),
+ id_image_native_binding_,
+ object_traits::find_statement_types);
+ }
+
+ public:
+ // select = total - separate_load
+ // insert = total - inverse - managed_optimistic - auto_id
+ // update = total - inverse - managed_optimistic - id - readonly -
+ // separate_update
+ //
+ static const std::size_t select_column_count =
+ object_traits::column_count -
+ object_traits::separate_load_column_count;
+
+ static const std::size_t id_column_count =
+ object_traits::id_column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count -
+ object_traits::inverse_column_count -
+ object_traits::managed_optimistic_column_count -
+ (object_traits::auto_id ? id_column_count : 0);
+
+ static const std::size_t update_column_count =
+ insert_column_count -
+ (object_traits::auto_id ? 0 : id_column_count) -
+ object_traits::readonly_column_count -
+ object_traits::separate_update_column_count;
+
+ static const std::size_t managed_optimistic_column_count =
+ object_traits::managed_optimistic_column_count;
+
+ private:
+ object_statements (const object_statements&);
+ object_statements& operator= (const object_statements&);
+
+ protected:
+ template <typename STS>
+ void
+ load_delayed_ (const schema_version_migration*);
+
+ void
+ clear_delayed_ ();
+
+ protected:
+ template <typename T1>
+ friend class polymorphic_derived_object_statements;
+
+ extra_statement_cache_ptr<extra_statement_cache_type,
+ image_type,
+ id_image_type> extra_statement_cache_;
+
+ // The UPDATE statement uses both the object and id image. Keep them
+ // next to each other so that the same skip distance can be used in
+ // batch binding.
+ //
+ struct images
+ {
+ image_type obj;
+ id_image_type id;
+ };
+
+ images images_[object_traits::batch];
+ unsigned long long status_[object_traits::batch];
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+ binding select_image_binding_;
+ bind select_image_bind_[select_column_count];
+ bool select_image_truncated_[select_column_count];
+
+ // Insert binding.
+ //
+ std::size_t insert_image_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[
+ insert_column_count != 0 ? insert_column_count : 1];
+ native_binding insert_image_native_binding_;
+ char* insert_image_values_[
+ insert_column_count != 0 ? insert_column_count : 1];
+ int insert_image_lengths_[
+ insert_column_count != 0 ? insert_column_count : 1];
+ int insert_image_formats_[
+ insert_column_count != 0 ? insert_column_count : 1];
+
+ // Update binding. Note that the id suffix is bound to id_image_
+ // below instead of image_ which makes this binding effectively
+ // bound to two images. As a result, we have to track versions
+ // for both of them. If this object uses optimistic concurrency,
+ // then the binding for the managed column (version, timestamp,
+ // etc) comes after the id and the image for such a column is
+ // stored as part of the id image.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_image_version_;
+ binding update_image_binding_;
+ bind update_image_bind_[update_column_count + id_column_count +
+ managed_optimistic_column_count];
+ native_binding update_image_native_binding_;
+ char* update_image_values_[update_column_count + id_column_count +
+ managed_optimistic_column_count];
+ int update_image_lengths_[update_column_count + id_column_count +
+ managed_optimistic_column_count];
+ int update_image_formats_[update_column_count + id_column_count +
+ managed_optimistic_column_count];
+
+ // Id image binding (only used as a parameter). Uses the suffix in
+ // the update bind.
+ //
+ std::size_t id_image_version_;
+ binding id_image_binding_;
+ native_binding id_image_native_binding_;
+
+ // Extra data for objects with optimistic concurrency support.
+ //
+ optimistic_data<T, managed_optimistic_column_count != 0> od_;
+
+ details::shared_ptr<insert_statement_type> persist_;
+ details::shared_ptr<select_statement_type> find_;
+ details::shared_ptr<update_statement_type> update_;
+ details::shared_ptr<delete_statement_type> erase_;
+
+ // Delayed loading.
+ //
+ struct delayed_load
+ {
+ typedef typename pointer_cache_traits::position_type position_type;
+
+ delayed_load () {}
+ delayed_load (const id_type& i,
+ object_type& o,
+ const position_type& p,
+ loader_function l)
+ : id (i), obj (&o), pos (p), loader (l)
+ {
+ }
+
+ id_type id;
+ object_type* obj;
+ position_type pos;
+ loader_function loader;
+ };
+
+ typedef std::vector<delayed_load> delayed_loads;
+ delayed_loads delayed_;
+
+ // Delayed vectors swap guard. See the load_delayed_() function for
+ // details.
+ //
+ struct swap_guard
+ {
+ swap_guard (object_statements& os, delayed_loads& dl)
+ : os_ (os), dl_ (dl)
+ {
+ dl_.swap (os_.delayed_);
+ }
+
+ ~swap_guard ()
+ {
+ os_.clear_delayed ();
+ dl_.swap (os_.delayed_);
+ }
+
+ private:
+ object_statements& os_;
+ delayed_loads& dl_;
+ };
+ };
+ }
+}
+
+#include <odb/pgsql/simple-object-statements.ixx>
+#include <odb/pgsql/simple-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_SIMPLE_OBJECT_STATEMENTS_HXX
diff --git a/libodb-pgsql/odb/pgsql/simple-object-statements.ixx b/libodb-pgsql/odb/pgsql/simple-object-statements.ixx
new file mode 100644
index 0000000..fbb2775
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/simple-object-statements.ixx
@@ -0,0 +1,68 @@
+// file : odb/pgsql/simple-object-statements.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ // auto_unlock
+ //
+ inline object_statements_base::auto_unlock::
+ auto_unlock (object_statements_base& s)
+ : s_ (s)
+ {
+ s_.unlock ();
+ }
+
+ inline object_statements_base::auto_unlock::
+ ~auto_unlock ()
+ {
+ s_.lock ();
+ }
+
+ //
+ // auto_lock
+ //
+ template <typename T>
+ inline object_statements<T>::auto_lock::
+ auto_lock (object_statements& s)
+ : s_ (s)
+ {
+ if (!s_.locked ())
+ {
+ s_.lock ();
+ locked_ = true;
+ }
+ else
+ locked_ = false;
+ }
+
+ template <typename T>
+ inline object_statements<T>::auto_lock::
+ ~auto_lock ()
+ {
+ if (locked_)
+ {
+ s_.unlock ();
+ s_.clear_delayed ();
+ }
+ }
+
+ template <typename T>
+ inline bool object_statements<T>::auto_lock::
+ locked () const
+ {
+ return locked_;
+ }
+
+ template <typename T>
+ inline void object_statements<T>::auto_lock::
+ unlock ()
+ {
+ assert (locked_);
+ s_.unlock ();
+ locked_ = false;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/simple-object-statements.txx b/libodb-pgsql/odb/pgsql/simple-object-statements.txx
new file mode 100644
index 0000000..bb47b43
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/simple-object-statements.txx
@@ -0,0 +1,190 @@
+// file : odb/pgsql/simple-object-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ // optimistic_data
+ //
+
+ template <typename T>
+ optimistic_data<T, true>::
+ optimistic_data (bind* b, char** nv, int* nl, int* nf,
+ std::size_t skip, unsigned long long* status)
+ : id_image_binding_ (
+ b,
+ object_traits::id_column_count +
+ object_traits::managed_optimistic_column_count,
+ object_traits::batch,
+ skip,
+ status),
+ id_image_native_binding_ (
+ nv, nl, nf,
+ object_traits::id_column_count +
+ object_traits::managed_optimistic_column_count)
+ {
+ }
+
+ //
+ // object_statements
+ //
+
+ template <typename T>
+ object_statements<T>::
+ ~object_statements ()
+ {
+ }
+
+ template <typename T>
+ object_statements<T>::
+ object_statements (connection_type& conn)
+ : object_statements_base (conn),
+ // select
+ select_image_binding_ (select_image_bind_, select_column_count),
+ // insert
+ insert_image_binding_ (insert_image_bind_,
+ insert_column_count,
+ object_traits::batch,
+ sizeof (images),
+ status_),
+ insert_image_native_binding_ (insert_image_values_,
+ insert_image_lengths_,
+ insert_image_formats_,
+ insert_column_count),
+ // update
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count +
+ managed_optimistic_column_count,
+ object_traits::batch,
+ sizeof (images),
+ status_),
+ update_image_native_binding_ (update_image_values_,
+ update_image_lengths_,
+ update_image_formats_,
+ update_column_count + id_column_count +
+ managed_optimistic_column_count),
+ // id
+ id_image_binding_ (update_image_bind_ + update_column_count,
+ id_column_count,
+ object_traits::batch,
+ sizeof (images),
+ status_),
+ id_image_native_binding_ (
+ update_image_values_ + update_column_count,
+ update_image_lengths_ + update_column_count,
+ update_image_formats_ + update_column_count,
+ id_column_count),
+ // optimistic data
+ od_ (update_image_bind_ + update_column_count,
+ update_image_values_ + update_column_count,
+ update_image_lengths_ + update_column_count,
+ update_image_formats_ + update_column_count,
+ sizeof (images),
+ status_)
+ {
+ // Only versions in the first element used.
+ //
+ images_[0].obj.version = 0;
+ images_[0].id.version = 0;
+
+ select_image_version_ = 0;
+ insert_image_version_ = 0;
+ update_image_version_ = 0;
+ update_id_image_version_ = 0;
+ id_image_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+
+ for (std::size_t i (0); i < select_column_count; ++i)
+ select_image_bind_[i].truncated = select_image_truncated_ + i;
+ }
+
+ template <typename T>
+ template <typename STS>
+ void object_statements<T>::
+ load_delayed_ (const schema_version_migration* svm)
+ {
+ database& db (connection ().database ());
+
+ delayed_loads dls;
+ swap_guard sg (*this, dls);
+
+ while (!dls.empty ())
+ {
+ delayed_load l (dls.back ());
+ typename pointer_cache_traits::insert_guard ig (l.pos);
+ dls.pop_back ();
+
+ if (l.loader == 0)
+ {
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (static_cast<STS&> (*this), &l.id))
+ throw object_not_persistent ();
+
+ object_traits::callback (db, *l.obj, callback_event::pre_load);
+
+ // Our calls to init/load below can result in additional delayed
+ // loads being added to the delayed_ vector. We need to process
+ // those before we call the post callback.
+ //
+ tc.init (*l.obj, image (), &db);
+
+ // Load containers, etc.
+ //
+ tc.load_ (static_cast<STS&> (*this), *l.obj, false);
+
+ if (!delayed_.empty ())
+ load_delayed_<STS> (svm);
+
+ // Temporarily unlock the statement for the post_load call so that
+ // it can load objects of this type recursively. This is safe to do
+ // because we have completely loaded the current object. Also the
+ // delayed_ list is clear before the unlock and should be clear on
+ // re-lock (since a callback can only call public API functions
+ // which will make sure all the delayed loads are processed before
+ // returning).
+ //
+ {
+ auto_unlock u (*this);
+ object_traits::callback (db, *l.obj, callback_event::post_load);
+ }
+ }
+ else
+ l.loader (db, l.id, *l.obj, svm);
+
+ pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+ }
+
+ template <typename T>
+ void object_statements<T>::
+ clear_delayed_ ()
+ {
+ // Remove the objects from the session cache.
+ //
+ for (typename delayed_loads::iterator i (delayed_.begin ()),
+ e (delayed_.end ()); i != e; ++i)
+ {
+ pointer_cache_traits::erase (i->pos);
+ }
+
+ delayed_.clear ();
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/statement-cache.hxx b/libodb-pgsql/odb/pgsql/statement-cache.hxx
new file mode 100644
index 0000000..9417949
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/statement-cache.hxx
@@ -0,0 +1,57 @@
+// file : odb/pgsql/statement-cache.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_STATEMENT_CACHE_HXX
+#define ODB_PGSQL_STATEMENT_CACHE_HXX
+
+#include <odb/pre.hxx>
+
+#include <map>
+#include <typeinfo>
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/type-info.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/statements-base.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class statement_cache
+ {
+ public:
+ statement_cache (connection& conn)
+ : conn_ (conn),
+ version_seq_ (conn_.database ().schema_version_sequence ()) {}
+
+ template <typename T>
+ typename object_traits_impl<T, id_pgsql>::statements_type&
+ find_object ();
+
+ template <typename T>
+ view_statements<T>&
+ find_view ();
+
+ private:
+ typedef std::map<const std::type_info*,
+ details::shared_ptr<statements_base>,
+ details::type_info_comparator> map;
+
+ connection& conn_;
+ unsigned int version_seq_;
+ map map_;
+ };
+ }
+}
+
+#include <odb/pgsql/statement-cache.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_STATEMENT_CACHE_HXX
diff --git a/libodb-pgsql/odb/pgsql/statement-cache.txx b/libodb-pgsql/odb/pgsql/statement-cache.txx
new file mode 100644
index 0000000..488ba2c
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/statement-cache.txx
@@ -0,0 +1,60 @@
+// file : odb/pgsql/statement-cache.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/database.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ typename object_traits_impl<T, id_pgsql>::statements_type&
+ statement_cache::
+ find_object ()
+ {
+ typedef
+ typename object_traits_impl<T, id_pgsql>::statements_type
+ statements_type;
+
+ // Clear the cache if the database version has changed. This
+ // makes sure we don't re-use statements that correspond to
+ // the old schema.
+ //
+ if (version_seq_ != conn_.database ().schema_version_sequence ())
+ {
+ map_.clear ();
+ version_seq_ = conn_.database ().schema_version_sequence ();
+ }
+
+ map::iterator i (map_.find (&typeid (T)));
+
+ if (i != map_.end ())
+ return static_cast<statements_type&> (*i->second);
+
+ details::shared_ptr<statements_type> p (
+ new (details::shared) statements_type (conn_));
+
+ map_.insert (map::value_type (&typeid (T), p));
+ return *p;
+ }
+
+ template <typename T>
+ view_statements<T>& statement_cache::
+ find_view ()
+ {
+ // We don't cache any statements for views so no need to clear
+ // the cache.
+
+ map::iterator i (map_.find (&typeid (T)));
+
+ if (i != map_.end ())
+ return static_cast<view_statements<T>&> (*i->second);
+
+ details::shared_ptr<view_statements<T> > p (
+ new (details::shared) view_statements<T> (conn_));
+
+ map_.insert (map::value_type (&typeid (T), p));
+ return *p;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/statement.cxx b/libodb-pgsql/odb/pgsql/statement.cxx
new file mode 100644
index 0000000..03d58cd
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/statement.cxx
@@ -0,0 +1,1548 @@
+// file : odb/pgsql/statement.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <libpq-fe.h>
+
+#ifdef LIBPQ_HAS_PIPELINING
+# ifndef _WIN32
+# include <errno.h>
+# include <sys/select.h>
+# endif
+#endif
+
+#include <cstring> // strcmp
+#include <utility> // pair
+#include <cassert>
+
+#ifdef ODB_CXX11
+# include <cstdlib> // strtoull
+#else
+# include <sstream> // istringstream
+#endif
+
+#include <odb/tracer.hxx>
+
+#include <odb/details/unused.hxx>
+
+#include <odb/pgsql/pgsql-oid.hxx>
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/exceptions.hxx>
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/transaction.hxx>
+#include <odb/pgsql/auto-handle.hxx>
+#include <odb/pgsql/error.hxx>
+
+#include <odb/pgsql/details/endian-traits.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace pgsql
+ {
+ using namespace details;
+
+ static unsigned long long
+ affected_row_count (PGresult* h)
+ {
+ const char* s (PQcmdTuples (h));
+ unsigned long long count;
+
+ if (s[0] != '\0' && s[1] == '\0')
+ count = static_cast<unsigned long long> (s[0] - '0');
+ else
+ {
+#ifdef ODB_CXX11
+ count = strtoull (s, 0, 10);
+#else
+ istringstream ss (s);
+ ss >> count;
+#endif
+ }
+
+ return count;
+ }
+
+ //
+ // statement
+ //
+
+ statement::
+ ~statement ()
+ {
+ try
+ {
+ deallocate ();
+ }
+ catch (...)
+ {
+ }
+ }
+
+ void statement::
+ deallocate ()
+ {
+ if (!deallocated_)
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->deallocate (conn_, *this);
+ }
+
+ string s ("deallocate \"");
+ s += name_;
+ s += "\"";
+
+ deallocated_ = true;
+ auto_handle<PGresult> h (PQexec (conn_.handle (), s.c_str ()));
+
+ if (!is_good_result (h))
+ {
+ // When we try to execute an invalid statement, PG "poisons" the
+ // transaction (those "current transaction is aborted, commands
+ // ignored until end of transaction block" messages in the log).
+ // This includes prepared statement deallocations (quite a stupid
+ // decision, if you ask me).
+ //
+ // So what can happen in this situation is the deallocation fails
+ // but we ignore it because we are already unwinding the stack
+ // (i.e., the prepared statement execution has failed). Next the
+ // user fixes things (e.g., passes valid parameters) and tries to
+ // re-execute the same query. But since we have failed to deallocate
+ // the statement, we now cannot re-prepare it; the name is already
+ // in use.
+ //
+ // What can we do to fix this? One way would be to postpone the
+ // deallocation until after the transaction is rolled back. This,
+ // however, would require quite an elaborate machinery: connection
+ // will have to store a list of such statements, etc. A much simpler
+ // solution is to mark the connection as failed. While it maybe a
+ // bit less efficient, we assume this is an "exceptional" situation
+ // that doesn't occur often. The only potentially problematic case
+ // is if the user holds the pointer to the connection and runs
+ // multiple transactions on it. But in this case the user should
+ // check if the connection is still good after each failure anyway.
+ //
+ conn_.mark_failed ();
+
+ translate_error (conn_, h);
+ }
+ }
+ }
+
+ statement::
+ statement (connection_type& conn,
+ const string& name,
+ const string& text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize,
+ const Oid* types,
+ size_t types_count)
+ : conn_ (conn),
+ name_copy_ (name), name_ (name_copy_.c_str ()),
+ deallocated_ (false)
+ {
+ if (process == 0)
+ {
+ text_copy_ = text;
+ text_ = text_copy_.c_str ();
+ }
+ else
+ text_ = text.c_str (); // Temporary, see init().
+
+ init (sk, process, optimize, types, types_count);
+
+ //
+ // If any code after this line throws, the statement will be leaked
+ // (on the server) since deallocate() won't be called for it.
+ //
+ }
+
+ statement::
+ statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize,
+ bool copy,
+ const Oid* types,
+ size_t types_count)
+ : conn_ (conn), deallocated_ (false)
+ {
+ if (copy)
+ {
+ name_copy_ = name;
+ name_ = name_copy_.c_str ();
+ }
+ else
+ name_ = name;
+
+ if (process == 0 && copy)
+ {
+ text_copy_ = text;
+ text_ = text_copy_.c_str ();
+ }
+ else
+ text_ = text; // Potentially temporary, see init().
+
+ init (sk, process, optimize, types, types_count);
+
+ //
+ // If any code after this line throws, the statement will be leaked
+ // (on the server) since deallocate() won't be called for it.
+ //
+ }
+
+ void statement::
+ init (statement_kind sk,
+ const binding* proc,
+ bool optimize,
+ const Oid* types,
+ size_t types_count)
+ {
+ if (proc != 0)
+ {
+ switch (sk)
+ {
+ case statement_select:
+ process_select (text_copy_,
+ text_,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ '"', '"',
+ optimize);
+ break;
+ case statement_insert:
+ process_insert (text_copy_,
+ text_,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ '$');
+ break;
+ case statement_update:
+ process_update (text_copy_,
+ text_,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ '$');
+ break;
+ case statement_delete:
+ assert (false);
+ }
+
+ text_ = text_copy_.c_str ();
+ }
+
+ // Empty statement.
+ //
+ if (*text_ == '\0')
+ {
+ deallocated_ = true;
+ return;
+ }
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->prepare (conn_, *this);
+ }
+
+ auto_handle<PGresult> h (
+ PQprepare (conn_.handle (),
+ name_,
+ text_,
+ static_cast<int> (types_count),
+ types));
+
+ if (!is_good_result (h))
+ translate_error (conn_, h);
+
+ //
+ // If any code after this line throws, the statement will be leaked
+ // (on the server) since deallocate() won't be called for it.
+ //
+ }
+
+ const char* statement::
+ text () const
+ {
+ return text_;
+ }
+
+ template <typename T>
+ static inline T*
+ offset (T* base, size_t count, size_t size)
+ {
+ return reinterpret_cast<T*> (
+ reinterpret_cast<char*> (base) + count * size);
+ }
+
+ void statement::
+ bind_param (native_binding& ns, const binding& bs, size_t pos)
+ {
+ assert (ns.count == bs.count);
+
+ for (size_t i (0); i < ns.count; ++i)
+ {
+ const bind& b (bs.bind[i]);
+
+ ns.formats[i] = 1;
+
+ bool* n (b.is_null != 0 ? offset (b.is_null, pos, bs.skip) : 0);
+
+ if ((n != 0 && *n) || b.buffer == 0) // Handle NULL entries.
+ {
+ ns.values[i] = 0;
+ ns.lengths[i] = 0;
+ continue;
+ }
+
+ ns.values[i] = static_cast<char*> (offset (b.buffer, pos, bs.skip));
+
+ size_t l (0);
+
+ switch (b.type)
+ {
+ case bind::boolean_:
+ {
+ l = sizeof (bool);
+ break;
+ }
+ case bind::smallint:
+ {
+ l = sizeof (short);
+ break;
+ }
+ case bind::integer:
+ {
+ l = sizeof (int);
+ break;
+ }
+ case bind::bigint:
+ {
+ l = sizeof (long long);
+ break;
+ }
+ case bind::real:
+ {
+ l = sizeof (float);
+ break;
+ }
+ case bind::double_:
+ {
+ l = sizeof (double);
+ break;
+ }
+ case bind::date:
+ {
+ l = sizeof (int);
+ break;
+ }
+ case bind::time:
+ case bind::timestamp:
+ {
+ l = sizeof (long long);
+ break;
+ }
+ case bind::numeric:
+ case bind::text:
+ case bind::bytea:
+ case bind::varbit:
+ {
+ // In this case b.buffer is a pointer to pointer to buffer so we
+ // need to chase one level.
+ //
+ ns.values[i] = static_cast<char*> (
+ *reinterpret_cast<void**> (ns.values[i]));
+ }
+ // Fall through.
+ case bind::bit:
+ {
+ l = *offset (b.size, pos, bs.skip);
+ break;
+ }
+ case bind::uuid:
+ {
+ // UUID is a 16-byte sequence.
+ //
+ l = 16;
+ break;
+ }
+ }
+
+ ns.lengths[i] = static_cast<int> (l);
+ }
+ }
+
+ bool statement::
+ bind_result (const binding& bs,
+ PGresult* result,
+ size_t row,
+ bool truncated,
+ size_t pos)
+ {
+ bool r (true);
+ int col_count (PQnfields (result));
+
+ int col (0);
+ for (size_t i (0); i != bs.count && col != col_count; ++i)
+ {
+ const bind& b (bs.bind[i]);
+
+ if (b.buffer == 0) // Skip NULL entries.
+ continue;
+
+ int c (col++);
+
+ {
+ bool* t (b.truncated != 0 ? offset (b.truncated, pos, bs.skip) : 0);
+
+ if (truncated && (t == 0 || !*t))
+ continue;
+
+ if (t != 0)
+ *t = false;
+ }
+
+ // Check for NULL unless we are reloading a truncated result.
+ //
+ if (!truncated)
+ {
+ bool* n (offset (b.is_null, pos, bs.skip));
+
+ *n = PQgetisnull (result, static_cast<int> (row), c) == 1;
+
+ if (*n)
+ continue;
+ }
+
+ void* buf (offset (b.buffer, pos, bs.skip));
+
+ const char* v (PQgetvalue (result, static_cast<int> (row), c));
+
+ switch (b.type)
+ {
+ case bind::boolean_:
+ {
+ *static_cast<bool*> (buf) = *reinterpret_cast<const bool*> (v);
+ break;
+ }
+ case bind::smallint:
+ case bind::integer:
+ case bind::bigint:
+ {
+ // If we are dealing with a custom schema, we may have a
+ // difference in the integer widths. Note also that we have
+ // to go to our endianness and back in order for casts to
+ // work properly.
+ //
+ long long i (0);
+
+ switch (PQftype (result, c))
+ {
+ case int2_oid:
+ {
+ i = endian_traits::ntoh (*reinterpret_cast<const short*> (v));
+ break;
+ }
+ case int4_oid:
+ {
+ i = endian_traits::ntoh (*reinterpret_cast<const int*> (v));
+ break;
+ }
+ case int8_oid:
+ {
+ i = endian_traits::ntoh (
+ *reinterpret_cast<const long long*> (v));
+ break;
+ }
+ default:
+ {
+ assert (false); // Column in the database is not an integer.
+ break;
+ }
+ }
+
+ switch (b.type)
+ {
+ case bind::smallint:
+ {
+ *static_cast<short*> (buf) =
+ endian_traits::hton (static_cast<short> (i));
+ break;
+ }
+ case bind::integer:
+ {
+ *static_cast<int*> (buf) =
+ endian_traits::hton (static_cast<int> (i));
+ break;
+ }
+ case bind::bigint:
+ {
+ *static_cast<long long*> (buf) = endian_traits::hton (i);
+ break;
+ }
+ default:
+ break;
+ }
+
+ break;
+ }
+ case bind::real:
+ {
+ *static_cast<float*> (buf) = *reinterpret_cast<const float*> (v);
+ break;
+ }
+ case bind::double_:
+ {
+ *static_cast<double*> (buf) = *reinterpret_cast<const double*> (v);
+ break;
+ }
+ case bind::date:
+ {
+ *static_cast<int*> (buf) = *reinterpret_cast<const int*> (v);
+ break;
+ }
+ case bind::time:
+ case bind::timestamp:
+ {
+ *static_cast<long long*> (buf) =
+ *reinterpret_cast<const long long*> (v);
+ break;
+ }
+ case bind::numeric:
+ case bind::text:
+ case bind::bytea:
+ case bind::bit:
+ case bind::varbit:
+ {
+ // Currently this is neither supported (due to capacity) nor used
+ // in batches.
+ //
+#ifdef LIBPGSQL_EXTRA_CHECKS
+ assert (pos == 0);
+#endif
+
+ *b.size = static_cast<size_t> (
+ PQgetlength (result, static_cast<int> (row), c));
+
+ if (b.capacity < *b.size)
+ {
+ if (b.truncated)
+ *b.truncated = true;
+
+ r = false;
+ continue;
+ }
+
+ // In these cases b.buffer is a pointer to pointer to buffer so we
+ // need to chase one level.
+ //
+ if (b.type != bind::bit)
+ buf = *static_cast<void**> (buf);
+
+ memcpy (buf, v, *b.size);
+ break;
+ }
+ case bind::uuid:
+ {
+ memcpy (buf, v, 16);
+ break;
+ }
+ }
+ }
+
+ // Make sure that the number of columns in the result returned by
+ // the database matches the number that we expect. A common cause
+ // of this assertion is a native view with a number of data members
+ // not matching the number of columns in the SELECT-list.
+ //
+ assert (col == col_count);
+
+ return r;
+ }
+
+#if defined(LIBPQ_HAS_PIPELINING) && !defined(_WIN32)
+
+ // Note that this function always marks the connection as failed.
+ //
+ static void
+ translate_connection_error (connection& conn)
+ {
+ const char* m (PQerrorMessage (conn.handle ()));
+
+ if (PQstatus (conn.handle ()) == CONNECTION_BAD)
+ {
+ conn.mark_failed ();
+ throw connection_lost ();
+ }
+ else
+ {
+ conn.mark_failed ();
+ throw database_exception (m != 0 ? m : "bad connection state");
+ }
+ }
+
+ // A RAII object for PGconn's non-blocking pipeline mode.
+ //
+ struct pipeline
+ {
+ connection& conn;
+ int sock;
+
+ explicit
+ pipeline (connection& c)
+ : conn (c)
+ {
+ PGconn* ch (conn.handle ());
+
+ if ((sock = PQsocket (ch)) == -1 ||
+ PQsetnonblocking (ch, 1) == -1 ||
+ PQenterPipelineMode (ch) == 0)
+ {
+ translate_connection_error (conn);
+ }
+ }
+
+ void
+ close (bool throw_ = true)
+ {
+ if (!conn.failed ())
+ {
+ PGconn* ch (conn.handle ());
+
+ if (PQexitPipelineMode (ch) == 0 ||
+ PQsetnonblocking (ch, 0) == -1)
+ {
+ if (throw_)
+ translate_connection_error (conn);
+ else
+ conn.mark_failed ();
+ }
+ }
+ }
+
+ ~pipeline ()
+ {
+ close (false);
+ }
+
+ pair<bool /* read */, bool /* write */>
+ wait (bool write, bool throw_ = true)
+ {
+ fd_set wds;
+ fd_set rds;
+
+ for (;;)
+ {
+ if (write)
+ {
+ FD_ZERO (&wds);
+ FD_SET (sock, &wds);
+ }
+
+ FD_ZERO (&rds);
+ FD_SET (sock, &rds);
+
+ if (select (sock + 1, &rds, write ? &wds : 0, 0, 0) != -1)
+ break;
+
+ if (errno != EINTR)
+ {
+ if (throw_)
+ translate_connection_error (conn);
+ else
+ {
+ conn.mark_failed ();
+ return pair<bool, bool> (false, false);
+ }
+ }
+ }
+
+ return pair<bool, bool> (FD_ISSET (sock, &rds),
+ write && FD_ISSET (sock, &wds));
+ }
+ };
+
+ // A RAII object for recovering from an error in a pipeline.
+ //
+ // Specifically, it reads and discards results until reaching
+ // PGRES_PIPELINE_SYNC.
+ //
+ struct pipeline_recovery
+ {
+ pipeline_recovery (pipeline& pl, bool wdone, bool sync)
+ : pl_ (&pl), wdone_ (wdone), sync_ (sync)
+ {
+ }
+
+ ~pipeline_recovery ()
+ {
+ if (pl_ != 0 && !pl_->conn.failed ())
+ {
+ PGconn* ch (pl_->conn.handle ());
+
+ // This code runs as part of stack unwinding caused by an exception
+ // so if we encounter an error, we "upgrade" the existing exception
+ // by marking the connection as failed.
+ //
+ // The rest is essentially a special version of execute() below.
+ //
+ // Note that on the first iteration we may still have results from
+ // the previous call to PQconsumeInput() (and these results may
+ // be the entire outstanding sequence, in which case calling wait()
+ // will block indefinitely).
+ //
+ for (bool first (true);; first = false)
+ {
+ if (sync_)
+ {
+ assert (!wdone_);
+
+ if (PQpipelineSync (ch) == 0)
+ break;
+
+ sync_ = false;
+ }
+
+ pair<bool, bool> r (false, false);
+
+ if (!first)
+ {
+ r = pl_->wait (!wdone_);
+ if (!r.first && !r.second)
+ break;
+ }
+
+ if (r.first /* read */ || first)
+ {
+ if (r.first && PQconsumeInput (ch) == 0)
+ break;
+
+ while (PQisBusy (ch) == 0)
+ {
+ auto_handle<PGresult> res (PQgetResult (ch));
+
+ // We should only get NULLs as well as PGRES_PIPELINE_ABORTED
+ // finished with PGRES_PIPELINE_SYNC.
+ //
+ if (res != 0)
+ {
+ ExecStatusType stat (PQresultStatus (res));
+
+ if (stat == PGRES_PIPELINE_SYNC)
+ return;
+
+ assert (stat == PGRES_PIPELINE_ABORTED);
+ }
+ }
+ }
+
+ if (r.second /* write */)
+ {
+ int r (PQflush (ch));
+ if (r == -1)
+ break;
+
+ if (r == 0)
+ wdone_ = true;
+ }
+ }
+
+ pl_->conn.mark_failed ();
+ }
+ }
+
+ void
+ cancel ()
+ {
+ pl_ = 0;
+ }
+
+ private:
+ pipeline* pl_;
+ bool wdone_;
+ bool sync_;
+ };
+
+ size_t statement::
+ execute (const binding& param,
+ native_binding& native_param,
+ size_t n,
+ multiple_exceptions& mex,
+ bool (*process) (size_t, PGresult*, bool, void*),
+ void* data)
+ {
+ size_t i (0); // Parameter set being attempted.
+ mex.current (i);
+
+ PGconn* ch (conn_.handle ());
+
+ pipeline pl (conn_);
+
+ // True if we've written and read everything, respectively.
+ //
+ bool wdone (false), rdone (false);
+
+ for (size_t wn (0), rn (0); !rdone; )
+ {
+ // Note that there is a special version of this code above in
+ // ~pipeline_recovery().
+ //
+ pair<bool, bool> r (pl.wait (!wdone));
+
+ // Note that once we start the pipeline, any call that may throw
+ // without marking the connection as failed should be guarded by
+ // pipeline_recovery.
+
+ // Try to minimize the chance of blocking the server by first
+ // processing the result and then sending more queries.
+ //
+ if (r.first /* read */)
+ {
+ if (PQconsumeInput (ch) == 0)
+ translate_connection_error (conn_);
+
+ // Note that PQisBusy() will return 0 (and subsequent PQgetResult()
+ // -- NULL) if we have consumed all the results for the queries that
+ // we have sent so far. Thus the (wn > rn) condition. Note that for
+ // this to work correctly we have to count the PQpipelineSync() call
+ // below as one of the queries (since it has a separate result).
+ //
+ while (wn > rn && PQisBusy (ch) == 0)
+ {
+ auto_handle<PGresult> res (PQgetResult (ch));
+
+ ExecStatusType stat (PGRES_FATAL_ERROR);
+ bool gr (is_good_result (res, &stat));
+
+ if (stat == PGRES_PIPELINE_SYNC)
+ {
+ assert (wdone && rn == n);
+ rdone = true;
+ break;
+ }
+
+ assert (rn != n);
+ ++rn;
+
+ if (stat != PGRES_PIPELINE_ABORTED)
+ {
+ // translate_error() may throw an exception (e.g., deadlock)
+ // without marking the connection as failed.
+ //
+ {
+ pipeline_recovery plr (pl, wdone, wn < n);
+
+ if (!process (i, res, gr, data))
+ translate_error (conn_, res, i, &mex);
+
+ plr.cancel ();
+ }
+
+ mex.attempted (++i);
+ mex.current (i);
+ }
+ else
+ {
+ // Should we treat PGRES_PIPELINE_ABORTED entries as attempted
+ // or not? While we did issue PQsendQueryPrepared() for them,
+ // the server tells us that it did not attemp to execute them.
+ // So it feels like they should not be treated as attempted.
+ //
+ // Note that for this to fit into out multiple_exceptions model,
+ // such an incomplete batch should be fatal (otherwise we could
+ // end up with unattempted "holes"). This is currently the case
+ // for errors handled by translate_error() but not necessarily
+ // the case for those handled by the process function (e.g.,
+ // duplicate id handled by process_insert_result() below). So in
+ // a somewhat hackish way we assume the error (e.g., duplicate
+ // id) will always be translated to an exception and pre-mark
+ // multiple_exceptions as fatal.
+ //
+ mex.fatal (true);
+ }
+
+ // We get a NULL result after each query result.
+ //
+ {
+ PGresult* end (PQgetResult (ch));
+ ODB_POTENTIALLY_UNUSED (end);
+ assert (end == 0);
+ }
+ }
+ }
+
+ if (r.second /* write */)
+ {
+ // Send queries until we get blocked (write-biased). This feels like
+ // a better overall strategy to keep the server busy compared to
+ // sending one query at a time and then re-checking if there is
+ // anything to read because the results of INSERT/UPDATE/DELETE are
+ // presumably small and quite a few of them can get buffered before
+ // the server gets blocked.
+ //
+ for (;;)
+ {
+ if (wn < n)
+ {
+ bind_param (native_param, param, wn);
+
+ if (PQsendQueryPrepared (ch,
+ name_,
+ static_cast<int> (native_param.count),
+ native_param.values,
+ native_param.lengths,
+ native_param.formats,
+ 1) == 0)
+ translate_connection_error (conn_);
+
+ if (++wn == n)
+ {
+ if (PQpipelineSync (ch) == 0)
+ translate_connection_error (conn_);
+
+ // Count as one of the queries since it has a separate result.
+ //
+ ++wn;
+ }
+ }
+
+ // PQflush() result:
+ //
+ // 0 -- success (queue is now empty)
+ // 1 -- blocked
+ // -1 -- error
+ //
+ int r (PQflush (ch));
+ if (r == -1)
+ translate_connection_error (conn_);
+
+ if (r == 0)
+ {
+ if (wn < n)
+ {
+ // If we continue here, then we are write-biased. And if we
+ // break, then we are read-biased.
+ //
+#ifdef LIBPGSQL_READ_BIASED
+ break;
+#else
+ continue;
+#endif
+ }
+
+ wdone = true;
+ }
+
+ break; // Blocked or done.
+ }
+ }
+ }
+
+ pl.close ();
+ return i;
+ }
+#endif
+
+ //
+ // select_statement
+ //
+
+ select_statement::
+ ~select_statement ()
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ bool process,
+ bool optimize,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ binding& result)
+ : statement (conn,
+ name, text, statement_select,
+ (process ? &result : 0), optimize,
+ types, types_count),
+ param_ (&param),
+ native_param_ (&native_param),
+ result_ (result),
+ row_count_ (0),
+ current_row_ (0)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ bool process,
+ bool optimize,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ binding& result,
+ bool copy)
+ : statement (conn,
+ name, text, statement_select,
+ (process ? &result : 0), optimize, copy,
+ types, types_count),
+ param_ (&param),
+ native_param_ (&native_param),
+ result_ (result),
+ row_count_ (0),
+ current_row_ (0)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ bool process,
+ bool optimize,
+ binding& result)
+ : statement (conn,
+ name, text, statement_select,
+ (process ? &result : 0), optimize,
+ 0, 0),
+ param_ (0),
+ native_param_ (0),
+ result_ (result),
+ row_count_ (0),
+ current_row_ (0)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ bool process,
+ bool optimize,
+ binding& result,
+ bool copy)
+ : statement (conn,
+ name, text, statement_select,
+ (process ? &result : 0), optimize, copy,
+ 0, 0),
+ param_ (0),
+ native_param_ (0),
+ result_ (result),
+ row_count_ (0),
+ current_row_ (0)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ bool process,
+ bool optimize,
+ const Oid* types,
+ std::size_t types_count,
+ native_binding& native_param,
+ binding& result)
+ : statement (conn,
+ name, text, statement_select,
+ (process ? &result : 0), optimize,
+ types, types_count),
+ param_ (0),
+ native_param_ (&native_param),
+ result_ (result),
+ row_count_ (0),
+ current_row_ (0)
+ {
+ }
+
+ void select_statement::
+ execute ()
+ {
+ handle_.reset ();
+
+ if (param_ != 0)
+ bind_param (*native_param_, *param_);
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ bool in (native_param_ != 0);
+
+ handle_.reset (
+ PQexecPrepared (conn_.handle (),
+ name_,
+ in ? static_cast<int> (native_param_->count) : 0,
+ in ? native_param_->values : 0,
+ in ? native_param_->lengths : 0,
+ in ? native_param_->formats : 0,
+ 1));
+
+ if (!is_good_result (handle_))
+ translate_error (conn_, handle_);
+
+ row_count_ = static_cast<size_t> (PQntuples (handle_));
+ current_row_ = 0;
+ }
+
+ void select_statement::
+ free_result ()
+ {
+ handle_.reset ();
+ row_count_ = 0;
+ current_row_ = 0;
+ }
+
+ bool select_statement::
+ next ()
+ {
+ if (current_row_ <= row_count_)
+ ++current_row_;
+
+ return current_row_ <= row_count_;
+ }
+
+ select_statement::result select_statement::
+ load ()
+ {
+ if (current_row_ > row_count_)
+ return no_data;
+
+ assert (current_row_ > 0);
+ return bind_result (result_, handle_, current_row_ - 1)
+ ? success
+ : truncated;
+ }
+
+ void select_statement::
+ reload ()
+ {
+ assert (current_row_ > 0);
+ assert (current_row_ <= row_count_);
+
+ if (!bind_result (result_, handle_, current_row_ - 1, true))
+ assert (false);
+ }
+
+ //
+ // insert_statement
+ //
+
+ insert_statement::
+ ~insert_statement ()
+ {
+ }
+
+ insert_statement::
+ insert_statement (connection_type& conn,
+ const string& name,
+ const string& text,
+ bool process,
+ const Oid* types,
+ size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ binding* returning)
+ : statement (conn,
+ name, text, statement_insert,
+ (process ? &param : 0), false,
+ types, types_count),
+ param_ (param),
+ native_param_ (native_param),
+ returning_ (returning)
+ {
+ if (returning_ != 0)
+ assert (returning_->count == 1);
+ }
+
+ insert_statement::
+ insert_statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ bool process,
+ const Oid* types,
+ size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ binding* returning,
+ bool copy)
+ : statement (conn,
+ name, text, statement_insert,
+ (process ? &param : 0), false, copy,
+ types, types_count),
+ param_ (param),
+ native_param_ (native_param),
+ returning_ (returning)
+ {
+ if (returning_ != 0)
+ assert (returning_->count == 1);
+ }
+
+ bool insert_statement::
+ execute ()
+ {
+ bind_param (native_param_, param_);
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ auto_handle<PGresult> h (
+ PQexecPrepared (conn_.handle (),
+ name_,
+ static_cast<int> (native_param_.count),
+ native_param_.values,
+ native_param_.lengths,
+ native_param_.formats,
+ 1));
+
+ ExecStatusType stat (PGRES_FATAL_ERROR);
+
+ if (!is_good_result (h, &stat))
+ {
+ // An auto-assigned object id should never cause a duplicate
+ // primary key.
+ //
+ if (returning_ == 0 && stat == PGRES_FATAL_ERROR)
+ {
+ const char* ss (PQresultErrorField (h, PG_DIAG_SQLSTATE));
+
+ if (ss != 0 && strcmp (ss, "23505") == 0)
+ return false;
+ }
+
+ translate_error (conn_, h);
+ }
+
+ if (returning_ != 0)
+ bind_result (*returning_, h, 0);
+
+ return true;
+ }
+
+#if defined(LIBPQ_HAS_PIPELINING) && !defined(_WIN32)
+
+ struct insert_data
+ {
+ binding& param;
+ binding* returning;
+ };
+
+ static bool
+ process_insert_result (size_t i, PGresult* r, bool gr, void* data)
+ {
+ insert_data& d (*static_cast<insert_data*> (data));
+
+ unsigned long long& s (d.param.status[i]);
+ s = 1;
+
+ if (gr)
+ {
+ // Note that the result can never be truncated.
+ //
+ if (d.returning != 0)
+ statement::bind_result (*d.returning, r, 0, false, i);
+ }
+ else
+ {
+ // An auto-assigned object id should never cause a duplicate
+ // primary key.
+ //
+ if (d.returning == 0 &&
+ r != 0 && PQresultStatus (r) == PGRES_FATAL_ERROR)
+ {
+ // Note that statement::execute() assumes that this will eventually
+ // be translated to an entry in multiple_exceptions.
+ //
+ const char* ss (PQresultErrorField (r, PG_DIAG_SQLSTATE));
+
+ if (ss != 0 && strcmp (ss, "23505") == 0)
+ s = 0;
+ }
+
+ if (s == 1)
+ return false;
+ }
+
+ return true;
+ }
+
+ size_t insert_statement::
+ execute (size_t n, multiple_exceptions& mex)
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ insert_data d {param_, returning_};
+
+ return statement::execute (
+ param_, native_param_, n, mex, &process_insert_result, &d);
+ }
+#endif
+
+ //
+ // update_statement
+ //
+
+ update_statement::
+ ~update_statement ()
+ {
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const string& name,
+ const string& text,
+ bool process,
+ const Oid* types,
+ size_t types_count,
+ binding& param,
+ native_binding& native_param)
+ : statement (conn,
+ name, text, statement_update,
+ (process ? &param : 0), false,
+ types, types_count),
+ param_ (param),
+ native_param_ (native_param)
+ {
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ bool process,
+ const Oid* types,
+ size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ bool copy)
+ : statement (conn,
+ name, text, statement_update,
+ (process ? &param : 0), false, copy,
+ types, types_count),
+ param_ (param),
+ native_param_ (native_param)
+ {
+ }
+
+ unsigned long long update_statement::
+ execute ()
+ {
+ bind_param (native_param_, param_);
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ auto_handle<PGresult> h (
+ PQexecPrepared (conn_.handle (),
+ name_,
+ static_cast<int> (native_param_.count),
+ native_param_.values,
+ native_param_.lengths,
+ native_param_.formats,
+ 1));
+
+ if (!is_good_result (h))
+ translate_error (conn_, h);
+
+ return affected_row_count (h);
+ }
+
+#if defined(LIBPQ_HAS_PIPELINING) && !defined(_WIN32)
+
+ static bool
+ process_update_result (size_t i, PGresult* r, bool gr, void* data)
+ {
+ binding& param (*static_cast<binding*> (data));
+
+ unsigned long long& s (param.status[i]);
+
+ if (gr)
+ {
+ s = affected_row_count (r);
+ return true;
+ }
+ else
+ {
+ s = update_statement::result_unknown;
+ return false;
+ }
+ }
+
+ size_t update_statement::
+ execute (size_t n, multiple_exceptions& mex)
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ return statement::execute (
+ param_, native_param_, n, mex, &process_update_result, &param_);
+ }
+#endif
+
+ //
+ // delete_statement
+ //
+
+ delete_statement::
+ ~delete_statement ()
+ {
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const string& name,
+ const string& text,
+ const Oid* types,
+ size_t types_count,
+ binding& param,
+ native_binding& native_param)
+ : statement (conn,
+ name, text, statement_delete,
+ 0, false,
+ types, types_count),
+ param_ (&param),
+ native_param_ (native_param)
+ {
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ const Oid* types,
+ size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ bool copy)
+ : statement (conn,
+ name, text, statement_delete,
+ 0, false, copy,
+ types, types_count),
+ param_ (&param),
+ native_param_ (native_param)
+ {
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const string& name,
+ const string& text,
+ const Oid* types,
+ size_t types_count,
+ native_binding& native_param)
+ : statement (conn,
+ name, text, statement_delete,
+ 0, false,
+ types, types_count),
+ param_ (0),
+ native_param_ (native_param)
+ {
+ }
+
+ unsigned long long delete_statement::
+ execute ()
+ {
+ if (param_ != 0)
+ bind_param (native_param_, *param_);
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ auto_handle<PGresult> h (
+ PQexecPrepared (conn_.handle (),
+ name_,
+ static_cast<int> (native_param_.count),
+ native_param_.values,
+ native_param_.lengths,
+ native_param_.formats,
+ 1));
+
+ if (!is_good_result (h))
+ translate_error (conn_, h);
+
+ return affected_row_count (h);
+ }
+
+#if defined(LIBPQ_HAS_PIPELINING) && !defined(_WIN32)
+
+ static bool
+ process_delete_result (size_t i, PGresult* r, bool gr, void* data)
+ {
+ binding& param (*static_cast<binding*> (data));
+
+ unsigned long long& s (param.status[i]);
+
+ if (gr)
+ {
+ s = affected_row_count (r);
+ return true;
+ }
+ else
+ {
+ s = delete_statement::result_unknown;
+ return false;
+ }
+ }
+
+ size_t delete_statement::
+ execute (size_t n, multiple_exceptions& mex)
+ {
+ assert (param_ != 0);
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ return statement::execute (
+ *param_, native_param_, n, mex, &process_delete_result, param_);
+ }
+#endif
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/statement.hxx b/libodb-pgsql/odb/pgsql/statement.hxx
new file mode 100644
index 0000000..139d2d6
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/statement.hxx
@@ -0,0 +1,450 @@
+// file : odb/pgsql/statement.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_STATEMENT_HXX
+#define ODB_PGSQL_STATEMENT_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+
+#include <odb/statement.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/binding.hxx>
+#include <odb/pgsql/pgsql-fwd.hxx> // PGresult
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/auto-handle.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class connection;
+
+ class LIBODB_PGSQL_EXPORT statement: public odb::statement
+ {
+ public:
+ typedef pgsql::connection connection_type;
+
+ virtual
+ ~statement () = 0;
+
+ const char*
+ name () const
+ {
+ return name_;
+ }
+
+ virtual const char*
+ text () const;
+
+ virtual connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // A statement can be empty. This is used to handle situations
+ // where a SELECT or UPDATE statement ends up not having any
+ // columns after processing. An empty statement cannot be
+ // executed.
+ //
+ bool
+ empty () const
+ {
+ return *text_ == '\0';
+ }
+
+ void
+ deallocate ();
+
+ // Adapt an ODB binding to a native PostgreSQL parameter binding. If pos
+ // is not 0, then bind the parameter set at this position in a batch.
+ //
+ static void
+ bind_param (native_binding&, const binding&, std::size_t pos = 0);
+
+ // Populate an ODB binding given a PostgreSQL result. If the truncated
+ // argument is true, then only truncated columns are extracted. Return
+ // true if all the data was extracted successfully and false if one or
+ // more columns were truncated. If pos is not 0, then populate the
+ // parameter set at this position in a batch.
+ //
+ static bool
+ bind_result (const binding&,
+ PGresult*,
+ std::size_t row,
+ bool truncated = false,
+ std::size_t pos = 0);
+
+ protected:
+ // We keep two versions to take advantage of std::string COW.
+ //
+ statement (connection_type&,
+ const std::string& name,
+ const std::string& text,
+ statement_kind,
+ const binding* process,
+ bool optimize,
+ const Oid* types,
+ std::size_t types_count);
+
+ statement (connection_type&,
+ const char* name,
+ const char* text,
+ statement_kind,
+ const binding* process,
+ bool optimize,
+ bool copy_name_text,
+ const Oid* types,
+ std::size_t types_count);
+
+ // Bulk execute implementation.
+ //
+ std::size_t
+ execute (const binding& param,
+ native_binding& native_param,
+ std::size_t n,
+ multiple_exceptions&,
+ bool (*process) (size_t i, PGresult*, bool good, void* data),
+ void* data);
+
+ private:
+ void
+ init (statement_kind,
+ const binding* process,
+ bool optimize,
+ const Oid* types,
+ std::size_t types_count);
+
+ protected:
+ connection_type& conn_;
+
+ std::string name_copy_;
+ const char* name_;
+
+ std::string text_copy_;
+ const char* text_;
+
+ private:
+ bool deallocated_;
+ };
+
+ class LIBODB_PGSQL_EXPORT select_statement: public statement
+ {
+ public:
+ virtual
+ ~select_statement ();
+
+ select_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ binding& result);
+
+ select_statement (connection_type& conn,
+ const char* name,
+ const char* stmt,
+ bool process_text,
+ bool optimize_text,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ binding& result,
+ bool copy_name_text = true);
+
+ select_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ binding& result);
+
+ select_statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ bool process_text,
+ bool optimize_text,
+ binding& result,
+ bool copy_name_text = true);
+
+ select_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ const Oid* types,
+ std::size_t types_count,
+ native_binding& native_param,
+ binding& result);
+
+ // Common select interface expected by the generated code.
+ //
+ public:
+ enum result
+ {
+ success,
+ no_data,
+ truncated
+ };
+
+ void
+ execute ();
+
+ void
+ cache () const
+ {
+ }
+
+ std::size_t
+ result_size () const
+ {
+ return row_count_;
+ }
+
+ // Load next row columns into bound buffers.
+ //
+ result
+ fetch ()
+ {
+ return next () ? load () : no_data;
+ }
+
+ // Reload truncated columns into bound buffers.
+ //
+ void
+ refetch ()
+ {
+ reload ();
+ }
+
+ // Free the result set.
+ //
+ void
+ free_result ();
+
+ // Finer grained control of PostgreSQL-specific interface that
+ // splits fetch() into next() and load().
+ //
+ public:
+ bool
+ next ();
+
+ result
+ load ();
+
+ void
+ reload ();
+
+ private:
+ select_statement (const select_statement&);
+ select_statement& operator= (const select_statement&);
+
+ private:
+ binding* param_;
+ native_binding* native_param_;
+
+ binding& result_;
+
+ auto_handle<PGresult> handle_;
+ std::size_t row_count_;
+ std::size_t current_row_;
+ };
+
+ struct LIBODB_PGSQL_EXPORT auto_result
+ {
+ explicit auto_result (select_statement& s): s_ (s) {}
+ ~auto_result () {s_.free_result ();}
+
+ private:
+ auto_result (const auto_result&);
+ auto_result& operator= (const auto_result&);
+
+ private:
+ select_statement& s_;
+ };
+
+ class LIBODB_PGSQL_EXPORT insert_statement: public statement
+ {
+ public:
+ virtual
+ ~insert_statement ();
+
+ insert_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ bool process_text,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ binding* returning);
+
+ insert_statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ bool process_text,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ binding* returning,
+ bool copy_name_text = true);
+
+ // Return true if successful and false if the row is a duplicate.
+ // All other errors are reported by throwing exceptions.
+ //
+ bool
+ execute ();
+
+ // Return the number of parameter sets (out of n) that were attempted.
+ //
+ std::size_t
+ execute (std::size_t n, multiple_exceptions&);
+
+ // Return true if successful and false if this row is a duplicate.
+ // All other errors are reported via exceptions.
+ //
+ bool
+ result (std::size_t i)
+ {
+ return param_.status[i] != 0;
+ }
+
+ private:
+ insert_statement (const insert_statement&);
+ insert_statement& operator= (const insert_statement&);
+
+ private:
+ binding& param_;
+ native_binding& native_param_;
+ binding* returning_;
+ };
+
+ class LIBODB_PGSQL_EXPORT update_statement: public statement
+ {
+ public:
+ virtual
+ ~update_statement ();
+
+ update_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ bool process_text,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param);
+
+ update_statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ bool process_text,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ bool copy_name_text = true);
+
+ unsigned long long
+ execute ();
+
+ // Return the number of parameter sets (out of n) that were attempted.
+ //
+ std::size_t
+ execute (std::size_t n, multiple_exceptions&);
+
+ // Return the number of rows affected (updated) by the parameter
+ // set. All errors are reported by throwing exceptions.
+ //
+ static const unsigned long long result_unknown = ~0ULL;
+
+ unsigned long long
+ result (std::size_t i)
+ {
+ return param_.status[i];
+ }
+
+ private:
+ update_statement (const update_statement&);
+ update_statement& operator= (const update_statement&);
+
+ private:
+ binding& param_;
+ native_binding& native_param_;
+ };
+
+ class LIBODB_PGSQL_EXPORT delete_statement: public statement
+ {
+ public:
+ virtual
+ ~delete_statement ();
+
+ delete_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param);
+
+ delete_statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ bool copy_name_text = true);
+
+ delete_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ const Oid* types,
+ std::size_t types_count,
+ native_binding& native_param);
+
+ unsigned long long
+ execute ();
+
+ // Return the number of parameter sets (out of n) that were attempted.
+ //
+ std::size_t
+ execute (std::size_t n, multiple_exceptions&);
+
+ // Return the number of rows affected (deleted) by the parameter
+ // set. All errors are reported by throwing exceptions.
+ //
+ static const unsigned long long result_unknown = ~0ULL;
+
+ unsigned long long
+ result (std::size_t i)
+ {
+ return param_->status[i];
+ }
+
+ private:
+ delete_statement (const delete_statement&);
+ delete_statement& operator= (const delete_statement&);
+
+ private:
+ binding* param_;
+ native_binding& native_param_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_STATEMENT_HXX
diff --git a/libodb-pgsql/odb/pgsql/statements-base.cxx b/libodb-pgsql/odb/pgsql/statements-base.cxx
new file mode 100644
index 0000000..0e72555
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/statements-base.cxx
@@ -0,0 +1,15 @@
+// file : odb/pgsql/statements-base.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/statements-base.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ statements_base::
+ ~statements_base ()
+ {
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/statements-base.hxx b/libodb-pgsql/odb/pgsql/statements-base.hxx
new file mode 100644
index 0000000..8b45bdc
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/statements-base.hxx
@@ -0,0 +1,63 @@
+// file : odb/pgsql/statements-base.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_STATEMENTS_BASE_HXX
+#define ODB_PGSQL_STATEMENTS_BASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/schema-version.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/database.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class LIBODB_PGSQL_EXPORT statements_base: public details::shared_base
+ {
+ public:
+ typedef pgsql::connection connection_type;
+
+ connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // Schema version. database::schema_version_migration() is thread-
+ // safe which means it is also slow. Cache the result in statements
+ // so we can avoid the mutex lock. This is thread-safe since if the
+ // version is updated, then the statements cache will be expired.
+ //
+ const schema_version_migration&
+ version_migration (const char* name = "") const
+ {
+ if (svm_ == 0)
+ svm_ = &conn_.database ().schema_version_migration (name);
+
+ return *svm_;
+ }
+
+ public:
+ virtual
+ ~statements_base ();
+
+ protected:
+ statements_base (connection_type& conn): conn_ (conn), svm_ (0) {}
+
+ protected:
+ connection_type& conn_;
+ mutable const schema_version_migration* svm_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_STATEMENTS_BASE_HXX
diff --git a/libodb-pgsql/odb/pgsql/tracer.cxx b/libodb-pgsql/odb/pgsql/tracer.cxx
new file mode 100644
index 0000000..48e0cf8
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/tracer.cxx
@@ -0,0 +1,60 @@
+// file : odb/pgsql/tracer.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/tracer.hxx>
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/statement.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ tracer::
+ ~tracer ()
+ {
+ }
+
+ void tracer::
+ prepare (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ execute (connection& c, const statement& s)
+ {
+ execute (c, s.text ());
+ }
+
+ void tracer::
+ deallocate (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ prepare (odb::connection& c, const odb::statement& s)
+ {
+ prepare (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const odb::statement& s)
+ {
+ execute (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const char* s)
+ {
+ execute (static_cast<connection&> (c), s);
+ }
+
+ void tracer::
+ deallocate (odb::connection& c, const odb::statement& s)
+ {
+ deallocate (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/tracer.hxx b/libodb-pgsql/odb/pgsql/tracer.hxx
new file mode 100644
index 0000000..89cda1c
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/tracer.hxx
@@ -0,0 +1,61 @@
+// file : odb/pgsql/tracer.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_TRACER_HXX
+#define ODB_PGSQL_TRACER_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/tracer.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class LIBODB_PGSQL_EXPORT tracer: private odb::tracer
+ {
+ public:
+ virtual
+ ~tracer ();
+
+ 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&);
+
+ private:
+ // Allow these classes to convert pgsql::tracer to odb::tracer.
+ //
+ friend class database;
+ friend class connection;
+ friend class transaction;
+
+ virtual void
+ prepare (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const char* statement);
+
+ virtual void
+ deallocate (odb::connection&, const odb::statement&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_TRACER_HXX
diff --git a/libodb-pgsql/odb/pgsql/traits-calls.hxx b/libodb-pgsql/odb/pgsql/traits-calls.hxx
new file mode 100644
index 0000000..419c7b2
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/traits-calls.hxx
@@ -0,0 +1,214 @@
+// file : odb/pgsql/traits-calls.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_TRAITS_CALLS_HXX
+#define ODB_PGSQL_TRAITS_CALLS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/pgsql-types.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ // object_traits_calls
+ //
+
+ template <typename T,
+ bool versioned = object_traits_impl<T, id_pgsql>::versioned>
+ struct object_traits_calls;
+
+ template <typename T>
+ struct object_traits_calls<T, false>
+ {
+ typedef object_traits_impl<T, id_pgsql> traits;
+ typedef typename traits::image_type image_type;
+ typedef pgsql::bind bind_type;
+
+ object_traits_calls (const schema_version_migration*) {}
+
+ const schema_version_migration*
+ version () const {return 0;}
+
+ static bool
+ grow (image_type& i, bool* t)
+ {
+ return traits::grow (i, t);
+ }
+
+ static void
+ bind (bind_type* b, image_type& i, statement_kind sk)
+ {
+ traits::bind (b, i, sk);
+ }
+
+ // Poly-derived version.
+ //
+ static void
+ bind (bind_type* b,
+ const bind_type* id, std::size_t id_size,
+ image_type& i,
+ statement_kind sk)
+ {
+ traits::bind (b, id, id_size, i, sk);
+ }
+
+ static void
+ init (T& o, const image_type& i, odb::database* db)
+ {
+ traits::init (o, i, db);
+ }
+
+ static bool
+ find_ (typename traits::statements_type& sts,
+ const typename traits::id_type* id)
+ {
+ return traits::find_ (sts, id);
+ }
+
+ static void
+ load_ (typename traits::statements_type& sts, T& o, bool reload)
+ {
+ return traits::load_ (sts, o, reload);
+ }
+ };
+
+ template <typename T>
+ struct object_traits_calls<T, true>
+ {
+ typedef object_traits_impl<T, id_pgsql> traits;
+ typedef typename traits::image_type image_type;
+ typedef pgsql::bind bind_type;
+
+ object_traits_calls (const schema_version_migration* svm): svm_ (*svm) {}
+
+ const schema_version_migration*
+ version () const {return &svm_;}
+
+ bool
+ grow (image_type& i, bool* t) const
+ {
+ return traits::grow (i, t, svm_);
+ }
+
+ void
+ bind (bind_type* b, image_type& i, statement_kind sk) const
+ {
+ traits::bind (b, i, sk, svm_);
+ }
+
+ // Poly-derived version.
+ //
+ void
+ bind (bind_type* b,
+ const bind_type* id, std::size_t id_size,
+ image_type& i,
+ statement_kind sk) const
+ {
+ traits::bind (b, id, id_size, i, sk, svm_);
+ }
+
+ void
+ init (T& o, const image_type& i, odb::database* db) const
+ {
+ traits::init (o, i, db, svm_);
+ }
+
+ bool
+ find_ (typename traits::statements_type& sts,
+ const typename traits::id_type* id) const
+ {
+ return traits::find_ (sts, id, svm_);
+ }
+
+ void
+ load_ (typename traits::statements_type& sts, T& o, bool reload) const
+ {
+ return traits::load_ (sts, o, reload, svm_);
+ }
+
+ private:
+ const schema_version_migration& svm_;
+ };
+
+ //
+ // view_traits_calls
+ //
+
+ template <typename T,
+ bool versioned = view_traits_impl<T, id_pgsql>::versioned>
+ struct view_traits_calls;
+
+ template <typename T>
+ struct view_traits_calls<T, false>
+ {
+ typedef view_traits_impl<T, id_pgsql> traits;
+ typedef typename traits::image_type image_type;
+ typedef pgsql::bind bind_type;
+
+ view_traits_calls (const schema_version_migration*) {}
+
+ static bool
+ grow (image_type& i, bool* t)
+ {
+ return traits::grow (i, t);
+ }
+
+ static void
+ bind (bind_type* b, image_type& i)
+ {
+ traits::bind (b, i);
+ }
+
+ static void
+ init (T& o, const image_type& i, odb::database* db)
+ {
+ traits::init (o, i, db);
+ }
+ };
+
+ template <typename T>
+ struct view_traits_calls<T, true>
+ {
+ typedef view_traits_impl<T, id_pgsql> traits;
+ typedef typename traits::image_type image_type;
+ typedef pgsql::bind bind_type;
+
+ view_traits_calls (const schema_version_migration* svm): svm_ (*svm) {}
+
+ bool
+ grow (image_type& i, bool* t) const
+ {
+ return traits::grow (i, t, svm_);
+ }
+
+ void
+ bind (bind_type* b, image_type& i) const
+ {
+ traits::bind (b, i, svm_);
+ }
+
+ void
+ init (T& o, const image_type& i, odb::database* db) const
+ {
+ traits::init (o, i, db, svm_);
+ }
+
+ private:
+ const schema_version_migration& svm_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_TRAITS_CALLS_HXX
diff --git a/libodb-pgsql/odb/pgsql/traits.cxx b/libodb-pgsql/odb/pgsql/traits.cxx
new file mode 100644
index 0000000..11a3a67
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/traits.cxx
@@ -0,0 +1,143 @@
+// file : odb/pgsql/traits.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/traits.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace pgsql
+ {
+ using details::buffer;
+
+ //
+ // default_value_traits<std::string, id_string>
+ //
+ void default_value_traits<std::string, id_string>::
+ set_image (buffer& b,
+ size_t& n,
+ bool& is_null,
+ const string& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v.c_str (), n);
+ }
+
+ //
+ // c_string_value_traits
+ //
+ void c_string_value_traits::
+ set_image (buffer& b,
+ size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ is_null = false;
+ n = strlen (v);
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v, n);
+ }
+
+ //
+ // c_array_value_traits_base
+ //
+ void c_array_value_traits_base::
+ set_value (char* const& v,
+ const details::buffer& b,
+ size_t n,
+ bool is_null,
+ size_t N)
+ {
+ if (!is_null)
+ {
+ n = n < N ? n : N;
+
+ if (n != 0)
+ memcpy (v, b.data (), n);
+ }
+ else
+ n = 0;
+
+ if (n != N) // Append '\0' if there is space.
+ v[n] = '\0';
+ }
+
+ void c_array_value_traits_base::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const char* v,
+ size_t N)
+ {
+ is_null = false;
+
+ // Figure out the length. We cannot use strlen since it may
+ // not be 0-terminated (strnlen is not standard).
+ //
+ for (n = 0; n != N && v[n] != '\0'; ++n) ;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v, n);
+ }
+
+ //
+ // default_value_traits<vector<char>, id_bytea>
+ //
+ // std::vector has to be qualified for Sun CC.
+ //
+ void default_value_traits<std::vector<char>, id_bytea>::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ // std::vector::data() may not be available in older compilers.
+ //
+ if (n != 0)
+ memcpy (b.data (), &v.front (), n);
+ }
+
+ //
+ // default_value_traits<vector<unsigned char>, id_bytea>
+ //
+ // std::vector has to be qualified for Sun CC.
+ //
+ void default_value_traits<std::vector<unsigned char>, id_bytea>::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ // std::vector::data() may not be available in older compilers.
+ //
+ if (n != 0)
+ memcpy (b.data (), &v.front (), n);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/traits.hxx b/libodb-pgsql/odb/pgsql/traits.hxx
new file mode 100644
index 0000000..3d87033
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/traits.hxx
@@ -0,0 +1,944 @@
+// file : odb/pgsql/traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_TRAITS_HXX
+#define ODB_PGSQL_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+#include <cstring> // std::memcpy, std::memset, std::strlen
+
+#ifdef ODB_CXX11
+# include <array>
+#endif
+
+#include <odb/traits.hxx>
+#include <odb/wrapper-traits.hxx>
+
+#include <odb/details/buffer.hxx>
+#include <odb/details/wrapper-p.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/pgsql-types.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+#include <odb/pgsql/details/endian-traits.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ enum database_type_id
+ {
+ id_boolean,
+ id_smallint,
+ id_integer,
+ id_bigint,
+
+ id_numeric, // Internal PostgreSQL binary representation.
+
+ id_real,
+ id_double,
+
+ id_date,
+ id_time,
+ id_timestamp,
+
+ id_string,
+ id_bytea,
+ id_bit,
+ id_varbit,
+
+ id_uuid
+ };
+
+ //
+ // image_traits
+ //
+
+ template <database_type_id>
+ struct image_traits;
+
+ template <>
+ struct image_traits<id_boolean> {typedef bool image_type;};
+
+ template <>
+ struct image_traits<id_smallint> {typedef short image_type;};
+
+ template <>
+ struct image_traits<id_integer> {typedef int image_type;};
+
+ template <>
+ struct image_traits<id_bigint> {typedef long long image_type;};
+
+ template <>
+ struct image_traits<id_numeric> {typedef details::buffer image_type;};
+
+ template <>
+ struct image_traits<id_real> {typedef float image_type;};
+
+ template <>
+ struct image_traits<id_double> {typedef double image_type;};
+
+ template <>
+ struct image_traits<id_date> {typedef int image_type;};
+
+ template <>
+ struct image_traits<id_time> {typedef long long image_type;};
+
+ template <>
+ struct image_traits<id_timestamp> {typedef long long image_type;};
+
+ template <>
+ struct image_traits<id_string> {typedef details::buffer image_type;};
+
+ template <>
+ struct image_traits<id_bytea> {typedef details::buffer image_type;};
+
+ template <>
+ struct image_traits<id_bit> {typedef unsigned char* image_type;};
+
+ template <>
+ struct image_traits<id_varbit> {typedef details::ubuffer image_type;};
+
+ // UUID image is a 16-byte sequence.
+ //
+ template <>
+ struct image_traits<id_uuid> {typedef unsigned char* image_type;};
+
+ //
+ // value_traits
+ //
+
+ template <typename W, database_type_id, bool null_handler>
+ struct wrapped_value_traits;
+
+ template <typename T, database_type_id>
+ struct default_value_traits;
+
+ template <typename T, database_type_id, bool w = details::wrapper_p<T>::r>
+ struct select_traits;
+
+ template <typename T, database_type_id ID>
+ struct select_traits<T, ID, false>
+ {
+ typedef default_value_traits<T, ID> type;
+ };
+
+ template <typename W, database_type_id ID>
+ struct select_traits<W, ID, true>
+ {
+ typedef
+ wrapped_value_traits<W, ID, wrapper_traits<W>::null_handler>
+ type;
+ };
+
+ template <typename T, database_type_id ID>
+ class value_traits: public select_traits<T, ID>::type
+ {
+ };
+
+ // The wrapped_value_traits specializations should be able to handle
+ // any value type which means we have to have every possible signature
+ // of the set_value() and set_image() functions.
+ //
+ template <typename W, database_type_id ID>
+ struct wrapped_value_traits<W, ID, false>
+ {
+ typedef wrapper_traits<W> wtraits;
+ typedef typename wtraits::unrestricted_wrapped_type wrapped_type;
+
+ typedef W value_type;
+ typedef wrapped_type query_type;
+ typedef typename image_traits<ID>::image_type image_type;
+
+ typedef value_traits<wrapped_type, ID> vtraits;
+
+ static void
+ set_value (W& v, const image_type& i, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, const W& v)
+ {
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+
+ // String, BYTEA, and NUMERIC.
+ //
+ static void
+ set_value (W& v, const details::buffer& b, std::size_t n, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (details::buffer& b, std::size_t& n, bool& is_null, const W& v)
+ {
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+
+ // BIT.
+ //
+ static void
+ set_value (W& v, const unsigned char* i, std::size_t n, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, n, is_null);
+ }
+
+ static void
+ set_image (unsigned char* i,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v));
+ }
+
+ // VARBIT.
+ //
+ static void
+ set_value (W& v, const details::ubuffer& b, std::size_t n, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (details::ubuffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+
+ // UUID.
+ //
+ static void
+ set_value (W& v, const unsigned char* i, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (unsigned char* i, bool& is_null, const W& v)
+ {
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+ };
+
+ template <typename W, database_type_id ID>
+ struct wrapped_value_traits<W, ID, true>
+ {
+ typedef wrapper_traits<W> wtraits;
+ typedef typename wtraits::unrestricted_wrapped_type wrapped_type;
+
+ typedef W value_type;
+ typedef wrapped_type query_type;
+ typedef typename image_traits<ID>::image_type image_type;
+
+ typedef value_traits<wrapped_type, ID> vtraits;
+
+ static void
+ set_value (W& v, const image_type& i, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+
+ // String, BYTEA, and NUMERIC.
+ //
+ static void
+ set_value (W& v, const details::buffer& b, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (details::buffer& b, std::size_t& n, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+
+ // BIT.
+ //
+ static void
+ set_value (W& v, const unsigned char* i, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, n, is_null);
+ }
+
+ static void
+ set_image (unsigned char* i,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v));
+ }
+
+ // VARBIT.
+ //
+ static void
+ set_value (W& v, const details::ubuffer& b, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (details::ubuffer& b, std::size_t& n, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+
+ // UUID.
+ //
+ static void
+ set_value (W& v, const unsigned char* i, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (unsigned char* i, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+ };
+
+ template <typename T, database_type_id ID>
+ struct default_value_traits
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef typename image_traits<ID>::image_type image_type;
+
+ static void
+ set_value (T& v, const image_type& i, bool is_null)
+ {
+ if (!is_null)
+ v = T (details::endian_traits::ntoh (i));
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, T v)
+ {
+ is_null = false;
+ i = details::endian_traits::hton (image_type (v));
+ }
+ };
+
+ // std::string specialization.
+ //
+ template <>
+ struct LIBODB_PGSQL_EXPORT default_value_traits<std::string, id_string>
+ {
+ typedef std::string value_type;
+ typedef std::string query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (std::string& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ v.assign (b.data (), n);
+ else
+ v.erase ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const std::string&);
+ };
+
+ // char*/const char* specialization
+ //
+ // Specialization for const char* which only supports initialization
+ // of an image from the value but not the other way around. This way
+ // we can pass such values to the queries.
+ //
+ class LIBODB_PGSQL_EXPORT c_string_value_traits
+ {
+ public:
+ typedef const char* value_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const char*);
+ };
+
+ template <>
+ struct default_value_traits<char*, id_string>: c_string_value_traits {};
+
+ template <>
+ struct default_value_traits<const char*, id_string>:
+ c_string_value_traits {};
+
+ // char[N] specializations.
+ //
+ struct LIBODB_PGSQL_EXPORT c_array_value_traits_base
+ {
+ static void
+ set_value (char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null,
+ std::size_t N);
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const char* v,
+ std::size_t N);
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_string>
+ {
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (v, b, n, is_null, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ c_array_value_traits_base::set_image (b, n, is_null, v, N);
+ }
+ };
+
+ // std::array<char, N> (string) specialization.
+ //
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_string>
+ {
+ typedef std::array<char, N> value_type;
+ typedef std::array<char, N> 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)
+ {
+ c_array_value_traits_base::set_value (v.data (), b, n, is_null, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_array_value_traits_base::set_image (b, n, is_null, v.data (), N);
+ }
+ };
+#endif
+
+ // char specialization.
+ //
+ template <>
+ struct default_value_traits<char, id_string>
+ {
+ typedef char value_type;
+ typedef char query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (&v, b, n, is_null, 1);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ char v)
+ {
+ c_array_value_traits_base::set_image (b, n, is_null, &v, 1);
+ }
+ };
+
+ // std::vector<char> (buffer) specialization.
+ //
+ template <>
+ struct LIBODB_PGSQL_EXPORT default_value_traits<
+ std::vector<char>, id_bytea>
+ {
+ public:
+ typedef std::vector<char> value_type;
+ typedef std::vector<char> 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)
+ {
+ if (!is_null)
+ v.assign (b.data (), b.data () + n);
+ else
+ v.clear ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const value_type&);
+ };
+
+ // std::vector<unsigned char> (buffer) specialization.
+ //
+ template <>
+ struct LIBODB_PGSQL_EXPORT default_value_traits<
+ std::vector<unsigned char>, id_bytea>
+ {
+ public:
+ typedef std::vector<unsigned char> value_type;
+ typedef std::vector<unsigned char> 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)
+ {
+ if (!is_null)
+ {
+ const unsigned char* d (
+ reinterpret_cast<const unsigned char*> (b.data ()));
+ v.assign (d, d + n);
+ }
+ else
+ v.clear ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const value_type&);
+ };
+
+ // char[N] (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_bytea>
+ {
+ public:
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, b.data (), (n < N ? n : N));
+ else
+ std::memset (v, 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v, n);
+ }
+ };
+
+ // unsigned char[N] (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<unsigned char[N], id_bytea>
+ {
+ public:
+ typedef unsigned char* value_type;
+ typedef unsigned char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (unsigned char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, b.data (), (n < N ? n : N));
+ else
+ std::memset (v, 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const unsigned char* v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v, n);
+ }
+ };
+
+#ifdef ODB_CXX11
+ // std::array<char, N> (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_bytea>
+ {
+ public:
+ typedef std::array<char, N> 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)
+ {
+ if (!is_null)
+ std::memcpy (v.data (), b.data (), (n < N ? n : N));
+ else
+ std::memset (v.data (), 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v.data (), n);
+ }
+ };
+
+ // std::array<unsigned char, N> (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<std::array<unsigned char, N>, id_bytea>
+ {
+ public:
+ typedef std::array<unsigned char, N> 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)
+ {
+ if (!is_null)
+ std::memcpy (v.data (), b.data (), (n < N ? n : N));
+ else
+ std::memset (v.data (), 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v.data (), n);
+ }
+ };
+#endif
+
+ // char[16] specialization for uuid.
+ //
+ template <>
+ struct default_value_traits<char[16], id_uuid>
+ {
+ typedef char* value_type;
+ typedef char query_type[16];
+ typedef unsigned char* image_type;
+
+ static void
+ set_value (char* const& v, const unsigned char* i, bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, i, 16);
+ else
+ std::memset (v, 0, 16);
+ }
+
+ static void
+ set_image (unsigned char* i, bool& is_null, const char* v)
+ {
+ is_null = false;
+ std::memcpy (i, v, 16);
+ }
+ };
+
+ //
+ // type_traits
+ //
+
+ template <typename T>
+ struct default_type_traits;
+
+ template <typename T>
+ class type_traits: public default_type_traits<T>
+ {
+ };
+
+ // Integral types.
+ //
+ template <>
+ struct default_type_traits<bool>
+ {
+ static const database_type_id db_type_id = id_boolean;
+ };
+
+ template <>
+ struct default_type_traits<signed char>
+ {
+ static const database_type_id db_type_id = id_smallint;
+ };
+
+ template <>
+ struct default_type_traits<unsigned char>
+ {
+ static const database_type_id db_type_id = id_smallint;
+ };
+
+ template <>
+ struct default_type_traits<short>
+ {
+ static const database_type_id db_type_id = id_smallint;
+ };
+
+ template <>
+ struct default_type_traits<unsigned short>
+ {
+ static const database_type_id db_type_id = id_smallint;
+ };
+
+ template <>
+ struct default_type_traits<int>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<unsigned int>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<long>
+ {
+ static const database_type_id db_type_id = id_bigint;
+ };
+
+ template <>
+ struct default_type_traits<unsigned long>
+ {
+ static const database_type_id db_type_id = id_bigint;
+ };
+
+ template <>
+ struct default_type_traits<long long>
+ {
+ static const database_type_id db_type_id = id_bigint;
+ };
+
+ template <>
+ struct default_type_traits<unsigned long long>
+ {
+ static const database_type_id db_type_id = id_bigint;
+ };
+
+ // Float types.
+ //
+ template <>
+ struct default_type_traits<float>
+ {
+ static const database_type_id db_type_id = id_real;
+ };
+
+ template <>
+ struct default_type_traits<double>
+ {
+ static const database_type_id db_type_id = id_double;
+ };
+
+ // String types.
+ //
+ template <>
+ struct default_type_traits<std::string>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ template <>
+ struct default_type_traits<char*>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ template <>
+ struct default_type_traits<const char*>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ template <std::size_t N>
+ struct default_type_traits<char[N]>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<char, N> >
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+#endif
+
+ template <>
+ struct default_type_traits<char>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ // Binary types.
+ //
+ template <std::size_t N>
+ struct default_type_traits<unsigned char[N]>
+ {
+ static const database_type_id db_type_id = id_bytea;
+ };
+
+ template <>
+ struct default_type_traits<std::vector<char> >
+ {
+ static const database_type_id db_type_id = id_bytea;
+ };
+
+ template <>
+ struct default_type_traits<std::vector<unsigned char> >
+ {
+ static const database_type_id db_type_id = id_bytea;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<unsigned char, N> >
+ {
+ static const database_type_id db_type_id = id_bytea;
+ };
+#endif
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_TRAITS_HXX
diff --git a/libodb-pgsql/odb/pgsql/transaction-impl.cxx b/libodb-pgsql/odb/pgsql/transaction-impl.cxx
new file mode 100644
index 0000000..012fe18
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/transaction-impl.cxx
@@ -0,0 +1,107 @@
+// file : odb/pgsql/transaction-impl.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <libpq-fe.h>
+
+#include <odb/tracer.hxx>
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/error.hxx>
+#include <odb/pgsql/exceptions.hxx>
+#include <odb/pgsql/transaction-impl.hxx>
+#include <odb/pgsql/auto-handle.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ transaction_impl::
+ transaction_impl (database_type& db)
+ : odb::transaction_impl (db)
+ {
+ }
+
+ transaction_impl::
+ transaction_impl (connection_ptr c)
+ : odb::transaction_impl (c->database (), *c), connection_ (c)
+ {
+ }
+
+ transaction_impl::
+ ~transaction_impl ()
+ {
+ }
+
+ void transaction_impl::
+ start ()
+ {
+ // Grab a connection if we don't already have one.
+ //
+ if (connection_ == 0)
+ {
+ connection_ = static_cast<database_type&> (database_).connection ();
+ odb::transaction_impl::connection_ = connection_.get ();
+ }
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "BEGIN");
+ }
+
+ auto_handle<PGresult> h (PQexec (connection_->handle (), "begin"));
+
+ if (!h || PGRES_COMMAND_OK != PQresultStatus (h))
+ translate_error (*connection_, h);
+ }
+
+ void transaction_impl::
+ commit ()
+ {
+ // Invalidate query results.
+ //
+ connection_->invalidate_results ();
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "COMMIT");
+ }
+
+ auto_handle<PGresult> h (PQexec (connection_->handle (), "commit"));
+
+ if (!h || PGRES_COMMAND_OK != PQresultStatus (h))
+ translate_error (*connection_, h);
+
+ // Release the connection.
+ //
+ connection_.reset ();
+ }
+
+ void transaction_impl::
+ rollback ()
+ {
+ // Invalidate query results.
+ //
+ connection_->invalidate_results ();
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "ROLLBACK");
+ }
+
+ auto_handle<PGresult> h (PQexec (connection_->handle (), "rollback"));
+
+ if (!h || PGRES_COMMAND_OK != PQresultStatus (h))
+ translate_error (*connection_, h);
+
+ // Release the connection.
+ //
+ connection_.reset ();
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/transaction-impl.hxx b/libodb-pgsql/odb/pgsql/transaction-impl.hxx
new file mode 100644
index 0000000..5c93b0e
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/transaction-impl.hxx
@@ -0,0 +1,49 @@
+// file : odb/pgsql/transaction-impl.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_TRANSACTION_IMPL_HXX
+#define ODB_PGSQL_TRANSACTION_IMPL_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class LIBODB_PGSQL_EXPORT transaction_impl: public odb::transaction_impl
+ {
+ public:
+ typedef pgsql::database database_type;
+ typedef pgsql::connection connection_type;
+
+ transaction_impl (database_type&);
+ transaction_impl (connection_ptr);
+
+ virtual
+ ~transaction_impl ();
+
+ virtual void
+ start ();
+
+ virtual void
+ commit ();
+
+ virtual void
+ rollback ();
+
+ private:
+ connection_ptr connection_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_TRANSACTION_IMPL_HXX
diff --git a/libodb-pgsql/odb/pgsql/transaction.cxx b/libodb-pgsql/odb/pgsql/transaction.cxx
new file mode 100644
index 0000000..3b32d80
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/transaction.cxx
@@ -0,0 +1,26 @@
+// file : odb/pgsql/transaction.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/pgsql/transaction.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ transaction& transaction::
+ current ()
+ {
+ // While the impl type can be of the concrete type, the transaction
+ // object can be created as either odb:: or odb::pgsql:: type. To
+ // work around that we are going to hard-cast one to the other
+ // relying on the fact that they have the same representation and
+ // no virtual functions. The former is checked in the tests.
+ //
+ odb::transaction& b (odb::transaction::current ());
+ assert (dynamic_cast<transaction_impl*> (&b.implementation ()) != 0);
+ return reinterpret_cast<transaction&> (b);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/transaction.hxx b/libodb-pgsql/odb/pgsql/transaction.hxx
new file mode 100644
index 0000000..e83c754
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/transaction.hxx
@@ -0,0 +1,88 @@
+// file : odb/pgsql/transaction.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_TRANSACTION_HXX
+#define ODB_PGSQL_TRANSACTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/tracer.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class transaction_impl;
+
+ class LIBODB_PGSQL_EXPORT transaction: public odb::transaction
+ {
+ public:
+ typedef pgsql::database database_type;
+ typedef pgsql::connection connection_type;
+
+ explicit
+ transaction (transaction_impl*, bool make_current = true);
+
+ transaction ();
+
+ // Return the database this transaction is on.
+ //
+ database_type&
+ database ();
+
+ // Return the underlying database connection for this transaction.
+ //
+ connection_type&
+ connection ();
+
+ connection_type&
+ connection (odb::database&);
+
+ // Return current transaction or throw if there is no transaction
+ // in effect.
+ //
+ static transaction&
+ current ();
+
+ // Set the current thread's transaction.
+ //
+ static void
+ current (transaction&);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef pgsql::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ using odb::transaction::tracer;
+
+ public:
+ transaction_impl&
+ implementation ();
+ };
+ }
+}
+
+#include <odb/pgsql/transaction.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_TRANSACTION_HXX
diff --git a/libodb-pgsql/odb/pgsql/transaction.ixx b/libodb-pgsql/odb/pgsql/transaction.ixx
new file mode 100644
index 0000000..31aa603
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/transaction.ixx
@@ -0,0 +1,57 @@
+// file : odb/pgsql/transaction.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/transaction-impl.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ inline transaction::
+ transaction (transaction_impl* impl, bool make_current)
+ : odb::transaction (impl, make_current)
+ {
+ }
+
+ inline transaction::
+ transaction ()
+ : odb::transaction ()
+ {
+ }
+
+ inline transaction_impl& transaction::
+ implementation ()
+ {
+ // We can use static_cast here since we have an instance of
+ // pgsql::transaction.
+ //
+ return static_cast<transaction_impl&> (
+ odb::transaction::implementation ());
+ }
+
+ inline transaction::database_type& transaction::
+ database ()
+ {
+ return static_cast<database_type&> (odb::transaction::database ());
+ }
+
+ inline transaction::connection_type& transaction::
+ connection ()
+ {
+ return static_cast<connection_type&> (odb::transaction::connection ());
+ }
+
+ inline transaction::connection_type& transaction::
+ connection (odb::database& db)
+ {
+ return static_cast<connection_type&> (odb::transaction::connection (db));
+ }
+
+ inline void transaction::
+ current (transaction& t)
+ {
+ odb::transaction::current (t);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/version.hxx b/libodb-pgsql/odb/pgsql/version.hxx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/version.hxx
diff --git a/libodb-pgsql/odb/pgsql/version.hxx.in b/libodb-pgsql/odb/pgsql/version.hxx.in
new file mode 100644
index 0000000..db8b152
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/version.hxx.in
@@ -0,0 +1,61 @@
+// file : odb/pgsql/version.hxx.in
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef LIBODB_PGSQL_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 LIBODB_PGSQL_VERSION_FULL $libodb_pgsql.version.project_number$ULL
+#define LIBODB_PGSQL_VERSION_STR "$libodb_pgsql.version.project$"
+#define LIBODB_PGSQL_VERSION_ID "$libodb_pgsql.version.project_id$"
+
+#define LIBODB_PGSQL_VERSION_MAJOR $libodb_pgsql.version.major$
+#define LIBODB_PGSQL_VERSION_MINOR $libodb_pgsql.version.minor$
+#define LIBODB_PGSQL_VERSION_PATCH $libodb_pgsql.version.patch$
+
+#define LIBODB_PGSQL_PRE_RELEASE $libodb_pgsql.version.pre_release$
+
+#define LIBODB_PGSQL_SNAPSHOT $libodb_pgsql.version.snapshot_sn$ULL
+#define LIBODB_PGSQL_SNAPSHOT_ID "$libodb_pgsql.version.snapshot_id$"
+
+#include <odb/version.hxx>
+
+$libodb.check(LIBODB_VERSION_FULL, LIBODB_SNAPSHOT)$
+
+
+// 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 LIBODB_PGSQL_VERSION 2049976
+
+#endif // LIBODB_PGSQL_VERSION
diff --git a/libodb-pgsql/odb/pgsql/view-result.hxx b/libodb-pgsql/odb/pgsql/view-result.hxx
new file mode 100644
index 0000000..f9acace
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/view-result.hxx
@@ -0,0 +1,77 @@
+// file : odb/pgsql/view-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_VIEW_RESULT_HXX
+#define ODB_PGSQL_VIEW_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/view-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx> // query_base, view_statements
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ class view_result_impl: public odb::view_result_impl<T>
+ {
+ public:
+ typedef odb::view_result_impl<T> base_type;
+
+ typedef typename base_type::view_type view_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef view_traits_impl<view_type, id_pgsql> view_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef view_statements<view_type> statements_type;
+
+ virtual
+ ~view_result_impl ();
+
+ view_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (view_type&);
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ view_traits_calls<view_type> tc_;
+ std::size_t count_;
+ };
+ }
+}
+
+#include <odb/pgsql/view-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_VIEW_RESULT_HXX
diff --git a/libodb-pgsql/odb/pgsql/view-result.txx b/libodb-pgsql/odb/pgsql/view-result.txx
new file mode 100644
index 0000000..980811a
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/view-result.txx
@@ -0,0 +1,114 @@
+// file : odb/pgsql/view-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/callback.hxx>
+
+#include <odb/pgsql/view-statements.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ view_result_impl<T>::
+ ~view_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ view_result_impl<T>::
+ view_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ count_ (0)
+ {
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ load (view_type& view)
+ {
+ // The image can grow between calls to load() as a result of other
+ // statements execution.
+ //
+ typename view_traits::image_type& im (statements_.image ());
+
+ if (im.version != statements_.image_version ())
+ {
+ binding& b (statements_.image_binding ());
+ tc_.bind (b.bind, im);
+ statements_.image_version (im.version);
+ b.version++;
+ }
+
+ select_statement::result r (statement_->load ());
+
+ if (r == select_statement::truncated)
+ {
+ if (tc_.grow (im, statements_.image_truncated ()))
+ im.version++;
+
+ if (im.version != statements_.image_version ())
+ {
+ binding& b (statements_.image_binding ());
+ tc_.bind (b.bind, im);
+ statements_.image_version (im.version);
+ b.version++;
+ statement_->reload ();
+ }
+ }
+
+ view_traits::callback (this->db_, view, callback_event::pre_load);
+ tc_.init (view, im, &this->db_);
+ view_traits::callback (this->db_, view, callback_event::post_load);
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (statement_->next ())
+ count_++;
+ else
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t view_result_impl<T>::
+ size ()
+ {
+ return this->end_ ? count_ : statement_->result_size ();
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/view-statements.hxx b/libodb-pgsql/odb/pgsql/view-statements.hxx
new file mode 100644
index 0000000..970e610
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/view-statements.hxx
@@ -0,0 +1,88 @@
+// file : odb/pgsql/view-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_VIEW_STATEMENTS_HXX
+#define ODB_PGSQL_VIEW_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/binding.hxx>
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/statements-base.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ class view_statements: public statements_base
+ {
+ public:
+ typedef T view_type;
+ typedef view_traits_impl<view_type, id_pgsql> view_traits;
+ typedef typename view_traits::pointer_type pointer_type;
+ typedef typename view_traits::image_type image_type;
+
+ public:
+ view_statements (connection_type&);
+
+ virtual
+ ~view_statements ();
+
+ // View image.
+ //
+ image_type&
+ image ()
+ {
+ return image_;
+ }
+
+ std::size_t
+ image_version () const
+ {
+ return image_version_;
+ }
+
+ void
+ image_version (std::size_t v)
+ {
+ image_version_ = v;
+ }
+
+ binding&
+ image_binding ()
+ {
+ return image_binding_;
+ }
+
+ bool*
+ image_truncated ()
+ {
+ return image_truncated_;
+ }
+
+ private:
+ view_statements (const view_statements&);
+ view_statements& operator= (const view_statements&);
+
+ private:
+ image_type image_;
+ std::size_t image_version_;
+ binding image_binding_;
+ bind image_bind_[view_traits::column_count];
+ bool image_truncated_[view_traits::column_count];
+ };
+ }
+}
+
+#include <odb/pgsql/view-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_VIEW_STATEMENTS_HXX
diff --git a/libodb-pgsql/odb/pgsql/view-statements.txx b/libodb-pgsql/odb/pgsql/view-statements.txx
new file mode 100644
index 0000000..afa2a49
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/view-statements.txx
@@ -0,0 +1,33 @@
+// file : odb/pgsql/view-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ view_statements<T>::
+ ~view_statements ()
+ {
+ }
+
+ template <typename T>
+ view_statements<T>::
+ view_statements (connection_type& conn)
+ : statements_base (conn),
+ image_binding_ (image_bind_, view_traits::column_count)
+ {
+ image_.version = 0;
+ image_version_ = 0;
+
+ std::memset (image_bind_, 0, sizeof (image_bind_));
+ std::memset (image_truncated_, 0, sizeof (image_truncated_));
+
+ for (std::size_t i (0); i < view_traits::column_count; ++i)
+ image_bind_[i].truncated = image_truncated_ + i;
+ }
+ }
+}
diff --git a/libodb-pgsql/tests/.gitignore b/libodb-pgsql/tests/.gitignore
new file mode 100644
index 0000000..e54525b
--- /dev/null
+++ b/libodb-pgsql/tests/.gitignore
@@ -0,0 +1 @@
+driver
diff --git a/libodb-pgsql/tests/basics/buildfile b/libodb-pgsql/tests/basics/buildfile
new file mode 100644
index 0000000..fccb243
--- /dev/null
+++ b/libodb-pgsql/tests/basics/buildfile
@@ -0,0 +1,6 @@
+# file : tests/basics/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libs = libodb-pgsql%lib{odb-pgsql}
+
+exe{driver}: {hxx cxx}{*} $libs
diff --git a/libodb-pgsql/tests/basics/driver.cxx b/libodb-pgsql/tests/basics/driver.cxx
new file mode 100644
index 0000000..efd1985
--- /dev/null
+++ b/libodb-pgsql/tests/basics/driver.cxx
@@ -0,0 +1,37 @@
+// file : tests/basics/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Basic test to make sure the library is usable. Functionality testing
+// is done in the odb-tests package.
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/exceptions.hxx>
+#include <odb/pgsql/transaction.hxx>
+
+using namespace odb::pgsql;
+
+int
+main ()
+{
+ {
+ std::ostringstream os;
+ database::print_usage (os);
+ assert (!os.str ().empty ());
+ }
+
+ // We can't really do much here since that would require a database. We can
+ // create a fake database object as long as we don't expect to get a valid
+ // connection.
+ //
+ database db ("john", "secret", "dummy whammy");
+
+ try
+ {
+ transaction t (db.begin ());
+ assert (false);
+ }
+ catch (const database_exception&) {}
+}
diff --git a/libodb-pgsql/tests/build/.gitignore b/libodb-pgsql/tests/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-pgsql/tests/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-pgsql/tests/build/bootstrap.build b/libodb-pgsql/tests/build/bootstrap.build
new file mode 100644
index 0000000..6ee38db
--- /dev/null
+++ b/libodb-pgsql/tests/build/bootstrap.build
@@ -0,0 +1,8 @@
+# file : tests/build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = # Unnamed subproject.
+
+using config
+using dist
+using test
diff --git a/libodb-pgsql/tests/build/root.build b/libodb-pgsql/tests/build/root.build
new file mode 100644
index 0000000..6c5a90b
--- /dev/null
+++ b/libodb-pgsql/tests/build/root.build
@@ -0,0 +1,23 @@
+# file : tests/build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+cxx.std = latest
+
+using cxx
+
+hxx{*}: extension = hxx
+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
+
+# Every exe{} in this subproject is by default a test.
+#
+exe{*}: test = true
+
+# Specify the test target for cross-testing.
+#
+test.target = $cxx.target
diff --git a/libodb-pgsql/tests/buildfile b/libodb-pgsql/tests/buildfile
new file mode 100644
index 0000000..57588a4
--- /dev/null
+++ b/libodb-pgsql/tests/buildfile
@@ -0,0 +1,4 @@
+# file : tests/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -build/}
diff --git a/libodb-qt/.gitignore b/libodb-qt/.gitignore
new file mode 100644
index 0000000..1c363a0
--- /dev/null
+++ b/libodb-qt/.gitignore
@@ -0,0 +1,25 @@
+# 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/libodb-qt/GPLv2 b/libodb-qt/GPLv2
new file mode 100644
index 0000000..3912109
--- /dev/null
+++ b/libodb-qt/GPLv2
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) 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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. 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.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+ 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
+convey 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 2 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, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This 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 Library General
+Public License instead of this License.
diff --git a/libodb-qt/INSTALL b/libodb-qt/INSTALL
new file mode 100644
index 0000000..5c67dce
--- /dev/null
+++ b/libodb-qt/INSTALL
@@ -0,0 +1,6 @@
+The easiest way to build this package is with the bpkg package manager:
+
+$ bpkg build libodb-boost
+
+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/libodb-qt/LICENSE b/libodb-qt/LICENSE
new file mode 100644
index 0000000..d96b938
--- /dev/null
+++ b/libodb-qt/LICENSE
@@ -0,0 +1,20 @@
+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 2 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, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
diff --git a/libodb-qt/NEWS b/libodb-qt/NEWS
new file mode 120000
index 0000000..0fae0f8
--- /dev/null
+++ b/libodb-qt/NEWS
@@ -0,0 +1 @@
+../NEWS \ No newline at end of file
diff --git a/libodb-qt/README b/libodb-qt/README
new file mode 100644
index 0000000..cf85909
--- /dev/null
+++ b/libodb-qt/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 Qt profile library. The Qt profile provides
+support for persisting Qt smart pointers, containers, and value types
+with the ODB system.
+
+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.
+
+Send questions, bug reports, or any other feedback to the
+odb-users@codesynthesis.com mailing list.
diff --git a/libodb-qt/build/.gitignore b/libodb-qt/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-qt/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-qt/build/bootstrap.build b/libodb-qt/build/bootstrap.build
new file mode 100644
index 0000000..dfb36a2
--- /dev/null
+++ b/libodb-qt/build/bootstrap.build
@@ -0,0 +1,10 @@
+# file : build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = libodb-qt
+
+using version
+using config
+using dist
+using test
+using install
diff --git a/libodb-qt/build/export.build b/libodb-qt/build/export.build
new file mode 100644
index 0000000..bd72576
--- /dev/null
+++ b/libodb-qt/build/export.build
@@ -0,0 +1,9 @@
+# file : build/export.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+$out_root/
+{
+ include odb/qt/
+}
+
+export $out_root/odb/qt/lib{odb-qt}
diff --git a/libodb-qt/build/root.build b/libodb-qt/build/root.build
new file mode 100644
index 0000000..882047d
--- /dev/null
+++ b/libodb-qt/build/root.build
@@ -0,0 +1,17 @@
+# file : build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+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
diff --git a/libodb-qt/buildfile b/libodb-qt/buildfile
new file mode 100644
index 0000000..8a0d6d9
--- /dev/null
+++ b/libodb-qt/buildfile
@@ -0,0 +1,9 @@
+# file : buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -build/} doc{INSTALL NEWS README} legal{LICENSE} manifest
+
+# Don't install tests or the INSTALL file.
+#
+tests/: install = false
+doc{INSTALL}@./: install = false
diff --git a/libodb-qt/manifest b/libodb-qt/manifest
new file mode 100644
index 0000000..137f93a
--- /dev/null
+++ b/libodb-qt/manifest
@@ -0,0 +1,25 @@
+: 1
+name: libodb-qt
+version: 2.5.0-b.26.z
+project: odb
+summary: Qt ODB profile library
+license: GPL-2.0-only
+license: other: proprietary ; Not free/open source.
+topics: C++, ORM, Qt, SQL
+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
+requires: c++11
+requires: libqt-core ; Requires not yet packaged libqt-core.
+
+# @@ TMP Bump the toolchain version to 0.17.0 after it is released.
+#
+depends: * build2 >= 0.16.0-
+depends: * bpkg >= 0.16.0-
+
+depends: libodb == $
diff --git a/libodb-qt/odb/qt.options b/libodb-qt/odb/qt.options
new file mode 100644
index 0000000..1e31135
--- /dev/null
+++ b/libodb-qt/odb/qt.options
@@ -0,0 +1,7 @@
+# file : odb/qt.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile qt/basic
+--profile qt/containers
+--profile qt/date-time
+--profile qt/smart-ptr
diff --git a/libodb-qt/odb/qt/basic.options b/libodb-qt/odb/qt/basic.options
new file mode 100644
index 0000000..306d949
--- /dev/null
+++ b/libodb-qt/odb/qt/basic.options
@@ -0,0 +1,4 @@
+# file : odb/qt/basic.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile qt/basic/basic
diff --git a/libodb-qt/odb/qt/basic/basic-common.options b/libodb-qt/odb/qt/basic/basic-common.options
new file mode 100644
index 0000000..f704314
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/basic-common.options
@@ -0,0 +1,4 @@
+# file : odb/qt/basic/basic-common.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile qt/version
diff --git a/libodb-qt/odb/qt/basic/basic-mssql.options b/libodb-qt/odb/qt/basic/basic-mssql.options
new file mode 100644
index 0000000..2234df5
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/basic-mssql.options
@@ -0,0 +1,13 @@
+# file : odb/qt/basic/basic-mssql.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile qt/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/qt/basic/mssql/default-mapping.hxx>'
+
+--hxx-prologue '#include <odb/qt/basic/mssql/qstring-traits.hxx>'
+--hxx-prologue '#include <odb/qt/basic/mssql/qbyte-array-traits.hxx>'
+--hxx-prologue '#include <odb/qt/basic/mssql/quuid-traits.hxx>'
diff --git a/libodb-qt/odb/qt/basic/basic-mysql.options b/libodb-qt/odb/qt/basic/basic-mysql.options
new file mode 100644
index 0000000..d049f4e
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/basic-mysql.options
@@ -0,0 +1,13 @@
+# file : odb/qt/basic/basic-mysql.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile qt/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/qt/basic/mysql/default-mapping.hxx>'
+
+--hxx-prologue '#include <odb/qt/basic/mysql/qstring-traits.hxx>'
+--hxx-prologue '#include <odb/qt/basic/mysql/qbyte-array-traits.hxx>'
+--hxx-prologue '#include <odb/qt/basic/mysql/quuid-traits.hxx>'
diff --git a/libodb-qt/odb/qt/basic/basic-oracle.options b/libodb-qt/odb/qt/basic/basic-oracle.options
new file mode 100644
index 0000000..60ef9d0
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/basic-oracle.options
@@ -0,0 +1,13 @@
+# file : odb/qt/basic/basic-oracle.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile qt/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/qt/basic/oracle/default-mapping.hxx>'
+
+--hxx-prologue '#include <odb/qt/basic/oracle/qstring-traits.hxx>'
+--hxx-prologue '#include <odb/qt/basic/oracle/qbyte-array-traits.hxx>'
+--hxx-prologue '#include <odb/qt/basic/oracle/quuid-traits.hxx>'
diff --git a/libodb-qt/odb/qt/basic/basic-pgsql.options b/libodb-qt/odb/qt/basic/basic-pgsql.options
new file mode 100644
index 0000000..6f0dc6e
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/basic-pgsql.options
@@ -0,0 +1,13 @@
+# file : odb/qt/basic/basic-pgsql.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile qt/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/qt/basic/pgsql/default-mapping.hxx>'
+
+--hxx-prologue '#include <odb/qt/basic/pgsql/qstring-traits.hxx>'
+--hxx-prologue '#include <odb/qt/basic/pgsql/qbyte-array-traits.hxx>'
+--hxx-prologue '#include <odb/qt/basic/pgsql/quuid-traits.hxx>'
diff --git a/libodb-qt/odb/qt/basic/basic-sqlite.options b/libodb-qt/odb/qt/basic/basic-sqlite.options
new file mode 100644
index 0000000..c64e37c
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/basic-sqlite.options
@@ -0,0 +1,13 @@
+# file : odb/qt/basic/basic-sqlite.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile qt/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/qt/basic/sqlite/default-mapping.hxx>'
+
+--hxx-prologue '#include <odb/qt/basic/sqlite/qstring-traits.hxx>'
+--hxx-prologue '#include <odb/qt/basic/sqlite/qbyte-array-traits.hxx>'
+--hxx-prologue '#include <odb/qt/basic/sqlite/quuid-traits.hxx>'
diff --git a/libodb-qt/odb/qt/basic/mssql/default-mapping.hxx b/libodb-qt/odb/qt/basic/mssql/default-mapping.hxx
new file mode 100644
index 0000000..19fd43b
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/mssql/default-mapping.hxx
@@ -0,0 +1,29 @@
+// file : odb/qt/basic/mssql/default-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_MSSQL_DEFAULT_MAPPING_HXX
+#define ODB_QT_BASIC_MSSQL_DEFAULT_MAPPING_HXX
+
+#include <QtCore/QString>
+#include <QtCore/QByteArray>
+#include <QtCore/QUuid>
+
+// By default map QString to SQL Server VARCHAR(512) for non-id members
+// and to VARCHAR(256) for id members (the same as the default mapping
+// for std::string). Allow NULL values by default as QString provides
+// a null representation.
+//
+#pragma db value(QString) type("VARCHAR(512)") id_type("VARCHAR(256)") null
+
+// By default map QByteArray to SQL Server VARBINARY(max). Allow NULL
+// values by default as QByteArray provides a null representation.
+//
+#pragma db value(QByteArray) type("VARBINARY(max)") null
+
+// By default map QUuid to SQL Server UNIQUEIDENTIFIER and use NULL to
+// represent null UUIDs. If NULL is disabled (e.g., at the member level),
+// then we store the null UUID (i.e., all bytes are zero).
+//
+#pragma db value(QUuid) type("UNIQUEIDENTIFIER") null
+
+#endif // ODB_QT_BASIC_MSSQL_DEFAULT_MAPPING_HXX
diff --git a/libodb-qt/odb/qt/basic/mssql/qbyte-array-traits.hxx b/libodb-qt/odb/qt/basic/mssql/qbyte-array-traits.hxx
new file mode 100644
index 0000000..8047691
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/mssql/qbyte-array-traits.hxx
@@ -0,0 +1,177 @@
+// file : odb/qt/basic/mssql/qbyte-array-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_MSSQL_QBYTE_ARRAY_TRAITS_HXX
+#define ODB_QT_BASIC_MSSQL_QBYTE_ARRAY_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy
+#include <cstddef> // std::size_t
+#include <cassert>
+
+#include <QtCore/QByteArray>
+
+#include <odb/mssql/traits.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <>
+ struct default_value_traits<QByteArray, id_binary>
+ {
+ typedef QByteArray value_type;
+ typedef QByteArray query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (QByteArray& v, const char* b, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ v = QByteArray ();
+ else
+ {
+ // Note that we cannot use replace() here since a suitable
+ // overload was only added in Qt 4.7.
+ //
+ v.resize (static_cast<int> (n));
+ std::memcpy (v.data (), b, n);
+ }
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const QByteArray& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+ n = static_cast<std::size_t> (v.size ());
+
+ if (n > c)
+ n = c;
+
+ std::memcpy (b, v.constData (), n);
+ }
+ }
+ };
+
+ template <>
+ struct default_value_traits<QByteArray, id_long_binary>
+ {
+ typedef QByteArray value_type;
+ typedef QByteArray query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (QByteArray& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &result_callback;
+ context = &v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const QByteArray& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = &v;
+ }
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t*,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void*,
+ std::size_t)
+ {
+ const QByteArray& v (*static_cast<const QByteArray*> (context));
+
+ *buffer = v.constData ();
+ *size = static_cast<std::size_t> (v.size ());
+ *chunk = chunk_one;
+ }
+
+ static void
+ result_callback (void* context,
+ std::size_t*,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void*,
+ std::size_t)
+ {
+ QByteArray& v (*static_cast<QByteArray*> (context));
+
+ switch (chunk)
+ {
+ case chunk_null:
+ {
+ v = QByteArray ();
+ break;
+ }
+ case chunk_one:
+ {
+ v.clear ();
+ break;
+ }
+ case chunk_first:
+ {
+ // The Native Client ODBC driver seems to always be able to
+ // return the total size. This makes things simple and
+ // efficient.
+ //
+ assert (size_left != 0);
+
+ v.resize (static_cast<int> (size_left));
+ *buffer = v.data ();
+ *size = size_left;
+ break;
+ }
+ case chunk_next:
+ {
+ // We should never get here.
+ //
+ assert (false);
+ break;
+ }
+ case chunk_last:
+ {
+ // Nothing to do here. The array is already of the correct size
+ // and should contain the data.
+ break;
+ }
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QByteArray>
+ {
+ static const database_type_id db_type_id = id_long_binary;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_BASIC_MSSQL_QBYTE_ARRAY_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/basic/mssql/qstring-traits.hxx b/libodb-qt/odb/qt/basic/mssql/qstring-traits.hxx
new file mode 100644
index 0000000..779120e
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/mssql/qstring-traits.hxx
@@ -0,0 +1,372 @@
+// file : odb/qt/basic/mssql/qstring-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_MSSQL_QSTRING_TRAITS_HXX
+#define ODB_QT_BASIC_MSSQL_QSTRING_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy
+#include <cstddef> // std::size_t
+#include <cassert>
+
+#include <QtCore/QString>
+
+#include <odb/mssql/traits.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <>
+ struct default_value_traits<QString, id_string>
+ {
+ typedef QString value_type;
+ typedef QString query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (QString& v,
+ const char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (is_null)
+ v = QString ();
+ else
+ // On Windows the string data is in Windows code page. On Linux
+ // it is always UTF-8.
+ //
+#ifdef _WIN32
+ v = QString::fromLocal8Bit (b, static_cast<int> (n));
+#else
+ v = QString::fromUtf8 (b, static_cast<int> (n));
+#endif
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const QString& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+
+#ifdef _WIN32
+ const QByteArray& a (v.toLocal8Bit ());
+#else
+ const QByteArray& a (v.toUtf8 ());
+#endif
+ n = static_cast<std::size_t> (a.size ());
+
+ if (n > c)
+ n = c;
+
+ std::memcpy (b, a.constData (), n);
+ }
+ }
+ };
+
+ template <>
+ struct default_value_traits<QString, id_nstring>
+ {
+ typedef QString value_type;
+ typedef QString query_type;
+ typedef ucs2_char* image_type;
+
+ static void
+ set_value (QString& v,
+ const ucs2_char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (is_null)
+ v = QString ();
+ else
+ {
+ // Note that we cannot use replace() here since a suitable
+ // overload was only added in Qt 4.7.
+ //
+ v.resize (static_cast<int> (n));
+ std::memcpy (v.data (), b, n * 2);
+ }
+ }
+
+ static void
+ set_image (ucs2_char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const QString& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+ n = static_cast<std::size_t> (v.size ());
+
+ if (n > c)
+ n = c;
+
+ std::memcpy (b, v.constData (), n * 2);
+ }
+ }
+ };
+
+ template <>
+ struct default_value_traits<QString, id_long_string>
+ {
+ typedef QString value_type;
+ typedef QString query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (QString& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &result_callback;
+ context = &v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const QString& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = &v;
+ }
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buf,
+ std::size_t tmp_capacity)
+ {
+ const QString& s (*static_cast<const QString*> (context));
+
+#ifdef _WIN32
+ const QByteArray& v (s.toLocal8Bit ());
+#else
+ const QByteArray& v (s.toUtf8 ());
+#endif
+
+ *size = static_cast<std::size_t> (v.size ());
+
+ if (*position == 0)
+ {
+ if (*size <= tmp_capacity)
+ *chunk = chunk_one;
+ else
+ {
+ *size = tmp_capacity;
+ *chunk = chunk_first;
+ }
+ }
+ else
+ {
+ *size -= *position;
+
+ if (*size <= tmp_capacity)
+ *chunk = chunk_last;
+ else
+ {
+ *size = tmp_capacity;
+ *chunk = chunk_next;
+ }
+ }
+
+ //@@ We might split a multi-byte sequence. Microsoft ODBC driver
+ // doesn't support this.
+ //
+ std::memcpy (tmp_buf, v.constData () + *position, *size);
+ *buffer = tmp_buf;
+ *position += *size;
+ }
+
+ static void
+ result_callback (void* context,
+ std::size_t*,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t,
+ void* tmp_buf,
+ std::size_t tmp_capacity)
+ {
+ QString& v (*static_cast<QString*> (context));
+
+ switch (chunk)
+ {
+ case chunk_null:
+ {
+ v = QString ();
+ break;
+ }
+ case chunk_one:
+ {
+ v.clear ();
+ break;
+ }
+ case chunk_first:
+ {
+ break;
+ }
+ case chunk_next:
+ case chunk_last:
+ {
+ // Append the data from the temporary buffer.
+ //
+#ifdef _WIN32
+ v += QString::fromLocal8Bit (static_cast<char*> (tmp_buf),
+ static_cast<int> (*size));
+#else
+ v += QString::fromUtf8 (static_cast<char*> (tmp_buf),
+ static_cast<int> (*size));
+#endif
+ break;
+ }
+ }
+
+ if (chunk == chunk_first || chunk == chunk_next)
+ {
+ *buffer = tmp_buf;
+ *size = tmp_capacity;
+ }
+ }
+ };
+
+ template <>
+ struct default_value_traits<QString, id_long_nstring>
+ {
+ typedef QString value_type;
+ typedef QString query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (QString& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &result_callback;
+ context = &v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const QString& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = &v;
+ }
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t*,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void*,
+ std::size_t)
+ {
+ const QString& v (*static_cast<const QString*> (context));
+
+ *buffer = v.constData ();
+ *size = static_cast<std::size_t> (v.size ()) * 2;
+ *chunk = chunk_one;
+ }
+
+ static void
+ result_callback (void* context,
+ std::size_t*,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void*,
+ std::size_t)
+ {
+ QString& v (*static_cast<QString*> (context));
+
+ switch (chunk)
+ {
+ case chunk_null:
+ {
+ v = QString ();
+ break;
+ }
+ case chunk_one:
+ {
+ v.clear ();
+ break;
+ }
+ case chunk_first:
+ {
+ // The Native Client ODBC driver seems to always be able to
+ // return the total size. This makes things simple and
+ // efficient.
+ //
+ assert (size_left != 0);
+
+ size_left /= 2; // Convert to characters.
+ size_left++; // One extra for the null terminator.
+
+ v.resize (static_cast<int> (size_left));
+ *buffer = v.data ();
+ *size = size_left * 2; // In bytes.
+ break;
+ }
+ case chunk_next:
+ {
+ // We should never get here.
+ //
+ assert (false);
+ break;
+ }
+ case chunk_last:
+ {
+ // Get rid of the null terminator.
+ //
+ v.resize (static_cast<int> (*size / 2));
+ break;
+ }
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QString>
+ {
+ static const database_type_id db_type_id = id_long_string;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_BASIC_MSSQL_QSTRING_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/basic/mssql/quuid-traits.hxx b/libodb-qt/odb/qt/basic/mssql/quuid-traits.hxx
new file mode 100644
index 0000000..d65b80f
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/mssql/quuid-traits.hxx
@@ -0,0 +1,58 @@
+// file : odb/qt/basic/mssql/uuid-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_MSSQL_UUID_TRAITS_HXX
+#define ODB_QT_BASIC_MSSQL_UUID_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy
+
+#include <QtCore/QUuid>
+
+#include <odb/mssql/traits.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <>
+ class default_value_traits<QUuid, id_uniqueidentifier>
+ {
+ public:
+ typedef QUuid value_type;
+ typedef value_type query_type;
+ typedef uniqueidentifier image_type;
+
+ static void
+ set_value (value_type& v, const uniqueidentifier& i, bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (&v.data1, &i, 16);
+ else
+ v = QUuid ();
+ }
+
+ static void
+ set_image (uniqueidentifier& i, bool& is_null, const value_type& v)
+ {
+ // If we can, store nil as NULL. Otherwise, store it as a value.
+ //
+ is_null = is_null && v.isNull ();
+
+ if (!is_null)
+ std::memcpy (&i, &v.data1, 16);
+ }
+ };
+
+ template <>
+ struct default_type_traits<QUuid>
+ {
+ static const database_type_id db_type_id = id_uniqueidentifier;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_BASIC_MSSQL_UUID_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/basic/mysql/default-mapping.hxx b/libodb-qt/odb/qt/basic/mysql/default-mapping.hxx
new file mode 100644
index 0000000..996895f
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/mysql/default-mapping.hxx
@@ -0,0 +1,28 @@
+// file : odb/qt/basic/mysql/default-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_MYSQL_DEFAULT_MAPPING_HXX
+#define ODB_QT_BASIC_MYSQL_DEFAULT_MAPPING_HXX
+
+#include <QtCore/QString>
+#include <QtCore/QByteArray>
+#include <QtCore/QUuid>
+
+// Map QString to MySQL TEXT for non-id and to VARCHAR(128) for id members.
+// MySQL cannot have primary key of the TEXT type. Allow NULL values by
+// default as QString provides a null representation.
+//
+#pragma db value(QString) type("TEXT") id_type("VARCHAR(128)") null
+
+// Map QByteArray to MySQL BLOB by default. Allow NULL values by default as
+// QByteArray provides a null representation.
+//
+#pragma db value(QByteArray) type("BLOB") null
+
+// By default map QUuid to MySQL BINARY(16) and use NULL to represent null
+// UUIDs. If NULL is disabled (e.g., at the member level), then we store
+// the null UUID (i.e., all bytes are zero).
+//
+#pragma db value(QUuid) type("BINARY(16)") null
+
+#endif // ODB_QT_BASIC_MYSQL_DEFAULT_MAPPING_HXX
diff --git a/libodb-qt/odb/qt/basic/mysql/qbyte-array-traits.hxx b/libodb-qt/odb/qt/basic/mysql/qbyte-array-traits.hxx
new file mode 100644
index 0000000..bfcfc69
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/mysql/qbyte-array-traits.hxx
@@ -0,0 +1,78 @@
+// file : odb/qt/basic/mysql/qbyte-array-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_MYSQL_QBYTE_ARRAY_TRAITS_HXX
+#define ODB_QT_BASIC_MYSQL_QBYTE_ARRAY_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy
+#include <cstddef> // std::size_t
+
+#include <QtCore/QByteArray>
+
+#include <odb/details/buffer.hxx>
+#include <odb/mysql/traits.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <>
+ struct default_value_traits<QByteArray, id_blob>
+ {
+ typedef QByteArray value_type;
+ typedef QByteArray query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (QByteArray& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (is_null)
+ v = QByteArray ();
+ else
+ {
+ // Note that we cannot use replace() here since a suitable
+ // overload was only added in Qt 4.7.
+ //
+ v.resize (static_cast<int> (n));
+ std::memcpy (v.data (), b.data (), n);
+ }
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const QByteArray& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+
+ n = static_cast<std::size_t> (v.size ());
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v.data (), n);
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QByteArray>
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_BASIC_MYSQL_QBYTE_ARRAY_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/basic/mysql/qstring-traits.hxx b/libodb-qt/odb/qt/basic/mysql/qstring-traits.hxx
new file mode 100644
index 0000000..6a5f315
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/mysql/qstring-traits.hxx
@@ -0,0 +1,93 @@
+// file : odb/qt/basic/mysql/qstring-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_MYSQL_QSTRING_TRAITS_HXX
+#define ODB_QT_BASIC_MYSQL_QSTRING_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy
+#include <cstddef> // std::size_t
+
+#include <QtCore/QString>
+
+#include <odb/details/buffer.hxx>
+#include <odb/mysql/traits.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class qstring_value_traits
+ {
+ public:
+ typedef QString value_type;
+ typedef QString query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (QString& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (is_null)
+ v = QString ();
+ else
+ v = QString::fromUtf8 (b.data (), static_cast<int> (n));
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const QString& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+
+ const QByteArray& a (v.toUtf8 ());
+ n = static_cast<std::size_t> (a.size ());
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), a.data (), n);
+ }
+ }
+ };
+
+ template <>
+ struct default_value_traits<QString, id_string>: qstring_value_traits
+ {
+ };
+
+ template <>
+ struct default_value_traits<QString, id_decimal>: qstring_value_traits
+ {
+ };
+
+ template <>
+ struct default_value_traits<QString, id_enum>: qstring_value_traits
+ {
+ };
+
+ template <>
+ struct default_value_traits<QString, id_set>: qstring_value_traits
+ {
+ };
+
+ template <>
+ struct default_type_traits<QString>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_BASIC_MYSQL_QSTRING_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/basic/mysql/quuid-traits.hxx b/libodb-qt/odb/qt/basic/mysql/quuid-traits.hxx
new file mode 100644
index 0000000..c672ee8
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/mysql/quuid-traits.hxx
@@ -0,0 +1,74 @@
+// file : odb/qt/basic/mysql/uuid-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_MYSQL_UUID_TRAITS_HXX
+#define ODB_QT_BASIC_MYSQL_UUID_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy
+#include <cassert>
+
+#include <QtCore/QUuid>
+
+#include <odb/mysql/traits.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <>
+ struct default_value_traits<QUuid, id_blob>
+ {
+ typedef QUuid 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)
+ {
+ if (!is_null)
+ {
+ assert (n == 16);
+ std::memcpy (&v.data1, b.data (), 16);
+ }
+ else
+ v = QUuid ();
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ // If we can, store nil as NULL. Otherwise, store it as a value.
+ //
+ is_null = is_null && v.isNull ();
+
+ if (!is_null)
+ {
+ n = 16;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), &v.data1, n);
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QUuid>
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_BASIC_MYSQL_UUID_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/basic/oracle/default-mapping.hxx b/libodb-qt/odb/qt/basic/oracle/default-mapping.hxx
new file mode 100644
index 0000000..8d72206
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/oracle/default-mapping.hxx
@@ -0,0 +1,27 @@
+// file : odb/qt/basic/oracle/default-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_ORACLE_DEFAULT_MAPPING_HXX
+#define ODB_QT_BASIC_ORACLE_DEFAULT_MAPPING_HXX
+
+#include <QtCore/QString>
+#include <QtCore/QByteArray>
+#include <QtCore/QUuid>
+
+// Map QString to Oracle VARCHAR2 by default. Allow NULL values by default as
+// QString provides a null representation.
+//
+#pragma db value(QString) type("VARCHAR2(512)") null
+
+// Map QByteArray to Oracle BLOB by default. Allow NULL values by default as
+// QByteArray provides a null representation.
+//
+#pragma db value(QByteArray) type("BLOB") null
+
+// By default map QUuid to Oracle RAW(16) and use NULL to represent null
+// UUIDs. If NULL is disabled (e.g., at the member level), then we store
+// the null UUID (i.e., all bytes are zero).
+//
+#pragma db value(QUuid) type("RAW(16)") null
+
+#endif // ODB_QT_BASIC_ORACLE_DEFAULT_MAPPING_HXX
diff --git a/libodb-qt/odb/qt/basic/oracle/qbyte-array-traits.hxx b/libodb-qt/odb/qt/basic/oracle/qbyte-array-traits.hxx
new file mode 100644
index 0000000..6979cc9
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/oracle/qbyte-array-traits.hxx
@@ -0,0 +1,167 @@
+// file : odb/qt/basic/oracle/qbyte-array-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_ORACLE_QBYTE_ARRAY_TRAITS_HXX
+#define ODB_QT_BASIC_ORACLE_QBYTE_ARRAY_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy
+#include <cstddef> // std::size_t
+
+#include <QtCore/QByteArray>
+
+#include <odb/oracle/traits.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <>
+ struct default_value_traits<QByteArray, id_raw>
+ {
+ typedef QByteArray value_type;
+ typedef QByteArray query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (QByteArray& v,
+ const char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (is_null)
+ v = QByteArray ();
+ else
+ {
+ // Note that we cannot use replace() here since a suitable
+ // overload was only added in Qt 4.7.
+ //
+ v.resize (static_cast<int> (n));
+ std::memcpy (v.data (), b, n);
+ }
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const QByteArray& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+
+ n = static_cast<std::size_t> (v.size ());
+
+ if (n > c)
+ n = c;
+
+ std::memcpy (b, v.constData (), n);
+ }
+ }
+ };
+
+ template <>
+ struct default_value_traits<QByteArray, id_blob>
+ {
+ typedef QByteArray value_type;
+ typedef QByteArray query_type;
+ typedef lob_callback image_type;
+
+ static void
+ set_value (QByteArray& v,
+ result_callback_type& cb,
+ void*& context,
+ bool is_null)
+ {
+ if (is_null)
+ v = QByteArray ();
+ else
+ {
+ cb = &result_callback;
+ context = &v;
+ }
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const QByteArray& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = &v;
+ }
+ }
+
+ static bool
+ result_callback (void* context,
+ ub4*,
+ void* b,
+ ub4 s,
+ chunk_position p)
+ {
+ QByteArray& v (*static_cast<QByteArray*> (context));
+
+ switch (p)
+ {
+ case chunk_one:
+ case chunk_first:
+ {
+ v.clear ();
+
+ // Falling through.
+ }
+ case chunk_next:
+ case chunk_last:
+ {
+ v.append (static_cast<char*> (b), static_cast<int> (s));
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ static bool
+ param_callback (const void* context,
+ ub4*,
+ const void** b,
+ ub4* s,
+ chunk_position* p,
+ void*,
+ ub4)
+ {
+ const QByteArray& v (*static_cast<const QByteArray*> (context));
+
+ *p = chunk_one;
+ *s = static_cast<ub4> (v.size ());
+ *b = v.constData ();
+
+ return true;
+ }
+ };
+
+ template <>
+ struct default_type_traits<QByteArray>
+ {
+ // Allow use of QByteArray in query expressions by default by specifying
+ // the default type id as RAW.
+ //
+ static const database_type_id db_type_id = id_raw;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_BASIC_ORACLE_QBYTE_ARRAY_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/basic/oracle/qstring-traits.hxx b/libodb-qt/odb/qt/basic/oracle/qstring-traits.hxx
new file mode 100644
index 0000000..418d30e
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/oracle/qstring-traits.hxx
@@ -0,0 +1,206 @@
+// file : odb/qt/basic/oracle/qstring-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_ORACLE_QSTRING_TRAITS_HXX
+#define ODB_QT_BASIC_ORACLE_QSTRING_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy
+#include <cstddef> // std::size_t
+
+#include <QtCore/QString>
+
+#include <odb/oracle/traits.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ struct qstring_value_traits
+ {
+ public:
+ typedef QString value_type;
+ typedef QString query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (QString& v,
+ const char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (is_null)
+ v = QString ();
+ else
+ v = QString::fromUtf8 (b, static_cast<int> (n));
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const QString& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+
+ const QByteArray& a (v.toUtf8 ());
+
+ n = static_cast<std::size_t> (a.size ());
+
+ if (n > c)
+ n = c;
+
+ std::memcpy (b, a.constData (), n);
+ }
+ }
+ };
+
+ template <>
+ struct default_value_traits <QString, id_string>: qstring_value_traits
+ {
+ };
+
+ template <>
+ struct default_value_traits <QString, id_nstring>: qstring_value_traits
+ {
+ };
+
+ class qstring_lob_value_traits
+ {
+ public:
+ typedef QString value_type;
+ typedef QString query_type;
+ typedef lob_callback image_type;
+
+ static void
+ set_value (QString& v,
+ result_callback_type& cb,
+ void*& context,
+ bool is_null)
+ {
+ if (is_null)
+ v = QString ();
+ else
+ {
+ cb = &result_callback;
+ context = &v;
+ }
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const QString& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = &v;
+ }
+ }
+
+ static bool
+ result_callback (void* context,
+ ub4*,
+ void* b,
+ ub4 s,
+ chunk_position p)
+ {
+ QString& v (*static_cast<QString*> (context));
+
+ switch (p)
+ {
+ case chunk_one:
+ case chunk_first:
+ {
+ v.clear ();
+
+ // Falling through.
+ }
+ case chunk_next:
+ case chunk_last:
+ {
+ v += QString::fromUtf8(static_cast<char*> (b),
+ static_cast<int> (s));
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ static bool
+ param_callback (const void* context,
+ ub4* position_context,
+ const void** b,
+ ub4* s,
+ chunk_position* p,
+ void* temp_buffer,
+ ub4 capacity)
+ {
+ const QByteArray& v (static_cast<const QString*> (context)->toUtf8 ());
+
+ *s = static_cast<ub4> (v.size ());
+
+ if (*position_context == 0)
+ {
+ if (*s <= capacity)
+ *p = chunk_one;
+ else
+ {
+ *s = capacity;
+ *p = chunk_first;
+ }
+ }
+ else
+ {
+ *s -= *position_context;
+
+ if (*s <= capacity)
+ *p = chunk_last;
+ else
+ {
+ *s = capacity;
+ *p = chunk_next;
+ }
+ }
+
+ std::memcpy (temp_buffer, v.constData () + *position_context, *s);
+ *b = temp_buffer;
+ *position_context += *s;
+
+ return true;
+ }
+ };
+
+ template <>
+ struct default_value_traits<QString, id_clob>: qstring_lob_value_traits
+ {
+ };
+
+ template <>
+ struct default_value_traits<QString, id_nclob>: qstring_lob_value_traits
+ {
+ };
+
+ template <>
+ struct default_type_traits<QString>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_BASIC_ORACLE_QSTRING_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/basic/oracle/quuid-traits.hxx b/libodb-qt/odb/qt/basic/oracle/quuid-traits.hxx
new file mode 100644
index 0000000..5f8041e
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/oracle/quuid-traits.hxx
@@ -0,0 +1,70 @@
+// file : odb/qt/basic/oracle/uuid-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_ORACLE_UUID_TRAITS_HXX
+#define ODB_QT_BASIC_ORACLE_UUID_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy
+#include <cassert>
+
+#include <QtCore/QUuid>
+
+#include <odb/oracle/traits.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <>
+ struct default_value_traits<QUuid, id_raw>
+ {
+ public:
+ typedef QUuid value_type;
+ typedef value_type query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (value_type& v, const char* b, std::size_t n, bool is_null)
+ {
+ if (!is_null)
+ {
+ assert (n == 16);
+ std::memcpy (&v.data1, b, 16);
+ }
+ else
+ v = QUuid ();
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ // If we can, store nil as NULL. Otherwise, store it as a value.
+ //
+ is_null = is_null && v.isNull ();
+
+ if (!is_null)
+ {
+ n = 16;
+ assert (c >= n);
+ std::memcpy (b, &v.data1, n);
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QUuid>
+ {
+ static const database_type_id db_type_id = id_raw;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_BASIC_ORACLE_UUID_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/basic/pgsql/default-mapping.hxx b/libodb-qt/odb/qt/basic/pgsql/default-mapping.hxx
new file mode 100644
index 0000000..13dbeb8
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/pgsql/default-mapping.hxx
@@ -0,0 +1,27 @@
+// file : odb/qt/basic/pgsql/default-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_PGSQL_DEFAULT_MAPPING_HXX
+#define ODB_QT_BASIC_PGSQL_DEFAULT_MAPPING_HXX
+
+#include <QtCore/QString>
+#include <QtCore/QByteArray>
+#include <QtCore/QUuid>
+
+// Map QString to PostgreSQL TEXT by default. Allow NULL values by default as
+// QString provides a null representation.
+//
+#pragma db value(QString) type("TEXT") null
+
+// Map QByteArray to PostgreSQL BYTEA by default. Allow NULL values by default
+// as QByteArray provides a null representation.
+//
+#pragma db value(QByteArray) type("BYTEA") null
+
+// By default map QUuid to PostgreSQL UUID and use NULL to represent null
+// UUIDs. If NULL is disabled (e.g., at the member level), then we store
+// the null UUID (i.e., all bytes are zero).
+//
+#pragma db value(QUuid) type("UUID") null
+
+#endif // ODB_QT_BASIC_PGSQL_DEFAULT_MAPPING_HXX
diff --git a/libodb-qt/odb/qt/basic/pgsql/qbyte-array-traits.hxx b/libodb-qt/odb/qt/basic/pgsql/qbyte-array-traits.hxx
new file mode 100644
index 0000000..3c3c496
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/pgsql/qbyte-array-traits.hxx
@@ -0,0 +1,77 @@
+// file : odb/qt/basic/pgsql/qbyte-array-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_PGSQL_QBYTE_ARRAY_TRAITS_HXX
+#define ODB_QT_BASIC_PGSQL_QBYTE_ARRAY_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy
+#include <cstddef> // std::size_t
+
+#include <QtCore/QByteArray>
+
+#include <odb/details/buffer.hxx>
+#include <odb/pgsql/traits.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <>
+ struct default_value_traits<QByteArray, id_bytea>
+ {
+ typedef QByteArray value_type;
+ typedef QByteArray query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (QByteArray& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (is_null)
+ v = QByteArray ();
+ else
+ {
+ // Note that we cannot use replace() here since a suitable
+ // overload was only added in Qt 4.7.
+ //
+ v.resize (static_cast<int> (n));
+ std::memcpy (v.data (), b.data (), n);
+ }
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const QByteArray& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+
+ n = static_cast<std::size_t> (v.size ());
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v.data (), n);
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QByteArray>
+ {
+ static const database_type_id db_type_id = id_bytea;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_BASIC_PGSQL_QBYTE_ARRAY_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/basic/pgsql/qstring-traits.hxx b/libodb-qt/odb/qt/basic/pgsql/qstring-traits.hxx
new file mode 100644
index 0000000..f1ab53c
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/pgsql/qstring-traits.hxx
@@ -0,0 +1,74 @@
+// file : odb/qt/basic/pgsql/qstring-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_PGSQL_QSTRING_TRAITS_HXX
+#define ODB_QT_BASIC_PGSQL_QSTRING_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy
+#include <cstddef> // std::size_t
+
+#include <QtCore/QString>
+
+#include <odb/details/buffer.hxx>
+#include <odb/pgsql/traits.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <>
+ struct default_value_traits <QString, id_string>
+ {
+ public:
+ typedef QString value_type;
+ typedef QString query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (QString& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (is_null)
+ v = QString ();
+ else
+ v = QString::fromUtf8 (b.data (), static_cast<int> (n));
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const QString& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+
+ const QByteArray& a (v.toUtf8 ());
+ n = static_cast<std::size_t> (a.size ());
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), a.data (), n);
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QString>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_BASIC_PGSQL_QSTRING_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/basic/pgsql/quuid-traits.hxx b/libodb-qt/odb/qt/basic/pgsql/quuid-traits.hxx
new file mode 100644
index 0000000..5999b7e
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/pgsql/quuid-traits.hxx
@@ -0,0 +1,65 @@
+// file : odb/qt/basic/pgsql/uuid-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_PGSQL_UUID_TRAITS_HXX
+#define ODB_QT_BASIC_PGSQL_UUID_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy
+
+#include <QtCore/QUuid>
+
+#include <odb/pgsql/traits.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <>
+ class default_value_traits<QUuid, id_uuid>
+ {
+ public:
+ typedef QUuid value_type;
+ typedef value_type query_type;
+ typedef unsigned char* image_type;
+
+ // PostgreSQL binary UUID representation is big-endian in the RFC 4122,
+ // section 4.1.2 order. While Qt provides (since 4.8) to/fromRfc4122(),
+ // they both incur a memory allocation (by QByteArray) which we could
+ // avoid, if we did it ourselves.
+ //
+
+ static void
+ set_value (value_type& v, const unsigned char* i, bool is_null)
+ {
+ if (!is_null)
+ v = QUuid::fromRfc4122 (
+ QByteArray (reinterpret_cast<const char*> (i), 16));
+ else
+ v = QUuid ();
+ }
+
+ static void
+ set_image (unsigned char* i, bool& is_null, const value_type& v)
+ {
+ // If we can, store nil as NULL. Otherwise, store it as a value.
+ //
+ is_null = is_null && v.isNull ();
+
+ if (!is_null)
+ std::memcpy (i, v.toRfc4122 ().constData (), 16);
+ }
+ };
+
+ template <>
+ struct default_type_traits<QUuid>
+ {
+ static const database_type_id db_type_id = id_uuid;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_BASIC_PGSQL_UUID_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/basic/sqlite/default-mapping.hxx b/libodb-qt/odb/qt/basic/sqlite/default-mapping.hxx
new file mode 100644
index 0000000..7d14a6c
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/sqlite/default-mapping.hxx
@@ -0,0 +1,27 @@
+// file : odb/qt/basic/sqlite/default-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_SQLITE_DEFAULT_MAPPING_HXX
+#define ODB_QT_BASIC_SQLITE_DEFAULT_MAPPING_HXX
+
+#include <QtCore/QString>
+#include <QtCore/QByteArray>
+#include <QtCore/QUuid>
+
+// Map QString to SQLite TEXT by default. Allow NULL values by default as
+// QString provides a null representation.
+//
+#pragma db value(QString) type("TEXT") null
+
+// Map QByteArray to SQLite BLOB by default. Allow NULL values by default as
+// QByteArray provides a null representation.
+//
+#pragma db value(QByteArray) type("BLOB") null
+
+// By default map QUuid to SQLite BLOB and use NULL to represent null UUIDs.
+// If NULL is disabled (e.g., at the member level), then we store the null
+// UUID (i.e., all bytes are zero).
+//
+#pragma db value(QUuid) type("BLOB") null
+
+#endif // ODB_QT_BASIC_SQLITE_DEFAULT_MAPPING_HXX
diff --git a/libodb-qt/odb/qt/basic/sqlite/qbyte-array-traits.hxx b/libodb-qt/odb/qt/basic/sqlite/qbyte-array-traits.hxx
new file mode 100644
index 0000000..8a26bb1
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/sqlite/qbyte-array-traits.hxx
@@ -0,0 +1,77 @@
+// file : odb/qt/basic/sqlite/qbyte-array-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_SQLITE_QBYTE_ARRAY_TRAITS_HXX
+#define ODB_QT_BASIC_SQLITE_QBYTE_ARRAY_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy
+#include <cstddef> // std::size_t
+
+#include <QtCore/QByteArray>
+
+#include <odb/details/buffer.hxx>
+#include <odb/sqlite/traits.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <>
+ struct default_value_traits<QByteArray, id_blob>
+ {
+ typedef QByteArray value_type;
+ typedef QByteArray query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (QByteArray& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (is_null)
+ v = QByteArray ();
+ else
+ {
+ // Note that we cannot use replace() here since a suitable
+ // overload was only added in Qt 4.7.
+ //
+ v.resize (static_cast<int> (n));
+ std::memcpy (v.data (), b.data (), n);
+ }
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const QByteArray& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+
+ n = static_cast<std::size_t> (v.size ());
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v.data (), n);
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QByteArray>
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_BASIC_SQLITE_QBYTE_ARRAY_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/basic/sqlite/qstring-traits.hxx b/libodb-qt/odb/qt/basic/sqlite/qstring-traits.hxx
new file mode 100644
index 0000000..30a2136
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/sqlite/qstring-traits.hxx
@@ -0,0 +1,83 @@
+// file : odb/qt/basic/sqlite/qstring-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_SQLITE_QSTRING_TRAITS_HXX
+#define ODB_QT_BASIC_SQLITE_QSTRING_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy
+#include <cstddef> // std::size_t
+
+#include <QtCore/QString>
+
+#include <odb/details/buffer.hxx>
+#include <odb/sqlite/traits.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <>
+ struct image_traits<QString, id_text>
+ {
+ typedef details::buffer image_type;
+
+ // Use UTF-16 binding for QString.
+ //
+ static const bind::buffer_type bind_value = bind::text16;
+ };
+
+ template <>
+ struct default_value_traits <QString, id_text>
+ {
+ public:
+ typedef QString value_type;
+ typedef QString query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (QString& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (is_null)
+ v = QString ();
+ else
+ v.setUtf16 (reinterpret_cast<const ushort*> (b.data ()),
+ static_cast<int> (n / 2)); // In characters.
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const QString& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+ n = static_cast<std::size_t> (v.size ()) * 2; // In bytes.
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v.utf16 (), n);
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QString>
+ {
+ static const database_type_id db_type_id = id_text;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_BASIC_SQLITE_QSTRING_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/basic/sqlite/quuid-traits.hxx b/libodb-qt/odb/qt/basic/sqlite/quuid-traits.hxx
new file mode 100644
index 0000000..20d4a94
--- /dev/null
+++ b/libodb-qt/odb/qt/basic/sqlite/quuid-traits.hxx
@@ -0,0 +1,74 @@
+// file : odb/qt/basic/sqlite/uuid-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_BASIC_SQLITE_UUID_TRAITS_HXX
+#define ODB_QT_BASIC_SQLITE_UUID_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstring> // std::memcpy
+#include <cassert>
+
+#include <QtCore/QUuid>
+
+#include <odb/sqlite/traits.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <>
+ struct default_value_traits<QUuid, id_blob>
+ {
+ typedef QUuid 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)
+ {
+ if (!is_null)
+ {
+ assert (n == 16);
+ std::memcpy (&v.data1, b.data (), 16);
+ }
+ else
+ v = QUuid ();
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ // If we can, store nil as NULL. Otherwise, store it as a value.
+ //
+ is_null = is_null && v.isNull ();
+
+ if (!is_null)
+ {
+ n = 16;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), &v.data1, n);
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QUuid>
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_BASIC_SQLITE_UUID_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/buildfile b/libodb-qt/odb/qt/buildfile
new file mode 100644
index 0000000..7fb051b
--- /dev/null
+++ b/libodb-qt/odb/qt/buildfile
@@ -0,0 +1,65 @@
+# file : odb/qt/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+../
+{
+ define options: file
+ options{*}: extension = options
+
+ # Install into the odb/qt/ subdirectory of, say, /usr/include/
+ # recreating subdirectories.
+ #
+ {hxx ixx txx options}{*}:
+ {
+ install = include/odb/
+ install.subdirs = true
+ }
+
+ qt/
+ {
+ import int_libs = libodb%lib{odb}
+ imp_libs =
+
+ lib{odb-qt}: {hxx ixx txx cxx}{** -version} {hxx}{version} \
+ options{**} ../options{qt} \
+ $imp_libs $int_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.
+ #
+ cxx.poptions =+ "-I$out_root" "-I$src_root"
+
+ obja{*}: cxx.poptions += -DLIBODB_QT_STATIC_BUILD
+ objs{*}: cxx.poptions += -DLIBODB_QT_SHARED_BUILD
+
+ # Export options.
+ #
+ lib{odb-qt}:
+ {
+ cxx.export.poptions = "-I$out_root" "-I$src_root"
+ cxx.export.libs = $int_libs
+ }
+
+ liba{odb-qt}: cxx.export.poptions += -DLIBODB_QT_STATIC
+ libs{odb-qt}: cxx.export.poptions += -DLIBODB_QT_SHARED
+
+ # For pre-releases use the complete version to make sure they cannot be
+ # used in place of another pre-release or the final version. See the
+ # version module for details on the version.* variable values.
+ #
+ if $version.pre_release
+ lib{odb-qt}: bin.lib.version = @"-$version.project_id"
+ else
+ lib{odb-qt}: bin.lib.version = @"-$version.major.$version.minor"
+ }
+}
diff --git a/libodb-qt/odb/qt/containers.options b/libodb-qt/odb/qt/containers.options
new file mode 100644
index 0000000..337b064
--- /dev/null
+++ b/libodb-qt/odb/qt/containers.options
@@ -0,0 +1,18 @@
+# file : odb/qt/containers.options
+# licences : GNU GPL v2; see accompanying LICENSE file
+
+--profile qt/version
+
+--odb-epilogue '#include <odb/qt/containers/qhash-traits.hxx>'
+--odb-epilogue '#include <odb/qt/containers/qlist-traits.hxx>'
+--odb-epilogue '#include <odb/qt/containers/qlinked-list-traits.hxx>'
+--odb-epilogue '#include <odb/qt/containers/qmap-traits.hxx>'
+--odb-epilogue '#include <odb/qt/containers/qset-traits.hxx>'
+--odb-epilogue '#include <odb/qt/containers/qvector-traits.hxx>'
+
+--hxx-prologue '#include <odb/qt/containers/qhash-traits.hxx>'
+--hxx-prologue '#include <odb/qt/containers/qlist-traits.hxx>'
+--hxx-prologue '#include <odb/qt/containers/qlinked-list-traits.hxx>'
+--hxx-prologue '#include <odb/qt/containers/qmap-traits.hxx>'
+--hxx-prologue '#include <odb/qt/containers/qset-traits.hxx>'
+--hxx-prologue '#include <odb/qt/containers/qvector-traits.hxx>'
diff --git a/libodb-qt/odb/qt/containers/list-iterator.hxx b/libodb-qt/odb/qt/containers/list-iterator.hxx
new file mode 100644
index 0000000..67f56fd
--- /dev/null
+++ b/libodb-qt/odb/qt/containers/list-iterator.hxx
@@ -0,0 +1,30 @@
+// file : odb/qt/containers/list-iterator.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_CONTAINERS_LIST_ITERATOR_HXX
+#define ODB_QT_CONTAINERS_LIST_ITERATOR_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QListIterator>
+
+#include <odb/qt/list.hxx>
+
+// Java-style QListIterator-like iterator. You can also use the
+// QListIterator directly (but not QMutableListIterator).
+//
+template <typename T>
+class QOdbListIterator: public QListIterator<T>
+{
+public:
+ QOdbListIterator (const QOdbList<T>& c): QListIterator<T> (c) {}
+ QOdbListIterator& operator=(const QOdbList<T>& c)
+ {
+ static_cast<QListIterator<T>&> (*this) = c;
+ return *this;
+ }
+};
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_CONTAINERS_LIST_ITERATOR_HXX
diff --git a/libodb-qt/odb/qt/containers/list-traits.hxx b/libodb-qt/odb/qt/containers/list-traits.hxx
new file mode 100644
index 0000000..5a3ee38
--- /dev/null
+++ b/libodb-qt/odb/qt/containers/list-traits.hxx
@@ -0,0 +1,107 @@
+// file : odb/qt/containers/list-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_CONTAINERS_LIST_TRAITS_HXX
+#define ODB_QT_CONTAINERS_LIST_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/vector-impl.hxx>
+#include <odb/container-traits.hxx>
+#include <odb/transaction.hxx>
+
+#include <odb/qt/containers/list.hxx>
+
+namespace odb
+{
+ template <typename V>
+ class access::container_traits<QOdbList<V> >
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = true;
+
+ typedef QOdbList<V> container_type;
+
+ typedef V value_type;
+ typedef typename container_type::size_type index_type;
+
+ typedef smart_ordered_functions<index_type, value_type> functions;
+ typedef ordered_functions<index_type, value_type> dumb_functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+
+ // Now that this container is persistent, start tracking changes.
+ //
+ c._start ();
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ // Stop tracking changes.
+ //
+ c._stop ();
+
+ // Load.
+ //
+ c.clear ();
+ while (more)
+ {
+ index_type dummy;
+ c.append (value_type ());
+ more = f.select (dummy, c.modify_back ());
+ }
+
+ // Start tracking changes.
+ //
+ c._start ();
+ }
+
+ static bool
+ changed (const container_type&);
+
+ static void
+ update (const container_type&, const functions&);
+
+ static void
+ erase (const container_type* c, const functions& f)
+ {
+ f.delete_ (0);
+
+ // Stop tracking changes.
+ //
+ if (c != 0)
+ c->_stop ();
+ }
+
+ // Version of load() for dumb functions. Used to support
+ // inverse members of the container type. The implementation
+ // is identical to the smart one except we don't turn off/on
+ // change tracking.
+ //
+ static void
+ load (container_type& c, bool more, const dumb_functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ index_type dummy;
+ c.append (value_type ());
+ more = f.select (dummy, c.modify_back ());
+ }
+ }
+ };
+}
+
+#include <odb/qt/containers/list-traits.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_CONTAINERS_LIST_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/containers/list-traits.txx b/libodb-qt/odb/qt/containers/list-traits.txx
new file mode 100644
index 0000000..105fc2a
--- /dev/null
+++ b/libodb-qt/odb/qt/containers/list-traits.txx
@@ -0,0 +1,101 @@
+// file : odb/qt/containers/list-traits.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ template <typename V>
+ bool access::container_traits<QOdbList<V> >::
+ changed (const container_type& c)
+ {
+ // Because modifications can cancel each other (e.g., push and pop),
+ // it is tricky to keep track of whether there are any changes in
+ // the container. Instead, we are just going to examine each element
+ // just like update().
+ //
+
+ // We should either be tracking or summarily changed.
+ //
+ if (c._tracking ())
+ {
+ const vector_impl& impl (c._impl ());
+
+ for (std::size_t i (0), n (impl.size ()); i < n; ++i)
+ {
+ if (impl.state (i) != vector_impl::state_unchanged)
+ return true;
+ }
+ }
+ else
+ return true;
+
+ return false;
+ }
+
+ template <typename V>
+ void access::container_traits<QOdbList<V> >::
+ update (const container_type& c, const functions& f)
+ {
+ bool u (false); // Updated flag.
+
+ if (c._tracking ())
+ {
+ const vector_impl& impl (c._impl ());
+
+ for (std::size_t i (0), n (impl.size ()); i < n; ++i)
+ {
+ vector_impl::element_state_type s (impl.state (i));
+ index_type ii (static_cast<index_type> (i));
+
+ switch (s)
+ {
+ case vector_impl::state_unchanged:
+ {
+ break;
+ }
+ case vector_impl::state_inserted:
+ {
+ f.insert (ii, c[ii]);
+ u = u || true;
+ break;
+ }
+ case vector_impl::state_updated:
+ {
+ f.update (ii, c[ii]);
+ u = u || true;
+ break;
+ }
+ case vector_impl::state_erased:
+ {
+ f.delete_ (ii); // Delete from i onwards.
+ u = u || true;
+ break;
+ }
+ }
+
+ // We delete all trailing elements in one go.
+ //
+ if (s == vector_impl::state_erased)
+ break;
+ }
+ }
+ else
+ {
+ // Fall back to delete all/insert all.
+ //
+ f.delete_ (0);
+
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+
+ u = true;
+ }
+
+ // Arm the rollback callback and (re)start change tracking.
+ //
+ if (u)
+ {
+ c._arm (transaction::current ());
+ c._start ();
+ }
+ }
+}
diff --git a/libodb-qt/odb/qt/containers/list.hxx b/libodb-qt/odb/qt/containers/list.hxx
new file mode 100644
index 0000000..143deed
--- /dev/null
+++ b/libodb-qt/odb/qt/containers/list.hxx
@@ -0,0 +1,433 @@
+// file : odb/qt/containers/list.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_CONTAINERS_LIST_HXX
+#define ODB_QT_CONTAINERS_LIST_HXX
+
+#include <odb/pre.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <QtCore/QtGlobal> // QT_VERSION
+#include <QtCore/QList>
+
+#ifdef ODB_CXX11
+# include <utility> // std::move
+# if defined(ODB_CXX11_INITIALIZER_LIST) && \
+ defined(Q_COMPILER_INITIALIZER_LISTS)
+# include <initializer_list>
+# endif
+#endif
+
+#include <odb/vector-impl.hxx>
+
+// A QList-like container that keeps track of changes.
+//
+// Note that the style and order of definitions is (mostly) as
+// appears in the qlist.h Qt header (except for some cleanups,
+// such as superfluous inline use).
+//
+template <typename L>
+class QOdbListIteratorImpl;
+
+template <typename T>
+class QOdbList: public odb::vector_base
+{
+public:
+ typedef QList<T> base_list_type;
+ typedef typename base_list_type::iterator base_iterator_type;
+
+ QOdbList() {}
+ QOdbList(const QOdbList &x): vector_base (x), l_ (x.l_) {}
+ // ~QOdbList();
+ QOdbList &operator=(const QOdbList &l);
+
+#if QT_VERSION >= 0x040800
+ void swap(QOdbList &other);
+#endif
+
+#ifdef ODB_CXX11
+ QOdbList(QOdbList &&x) noexcept
+ : vector_base (std::move (x)), l_ (std::move (x.l_)) {}
+
+ // Note: noexcept is not specified since it can throw while reallocating
+ // impl_.
+ //
+ QOdbList &operator=(QOdbList &&other);
+
+#if defined(ODB_CXX11_INITIALIZER_LIST) && \
+ defined(Q_COMPILER_INITIALIZER_LISTS)
+ QOdbList(std::initializer_list<T> il): l_ (il) {}
+#endif
+#endif
+
+ // Implicit conversion.
+ //
+ bool operator==(const QList<T> &x) const {return l_ == x;}
+ bool operator!=(const QList<T> &x) const {return l_ != x;}
+
+ int size() const {return l_.size ();}
+ void detach() {l_.detach ();}
+ void detachShared() {l_.detachShared ();}
+ bool isDetached() const {return l_.isDetached ();}
+ void setSharable(bool sharable) {l_.setSharable (sharable);}
+ // Implicit conversion.
+ bool isSharedWith(const QList<T> &x) const {return l_.isSharedWith (x);}
+ bool isEmpty() const {return l_.isEmpty ();}
+ void clear();
+
+ const T &at(int i) const {return l_.at (i);}
+ const T &operator[](int i) const {return l_[i];}
+ //T &operator[](int i);
+ T &modify (int i);
+
+ void reserve(int size);
+ void append(const T &t);
+ void append(const QList<T> &t); // Implicit conversion.
+ void prepend(const T &t);
+ void insert(int i, const T &t);
+ void replace(int i, const T &t);
+ void removeAt(int i);
+ int removeAll(const T &t);
+ bool removeOne(const T &t);
+ T takeAt(int i);
+ T takeFirst();
+ T takeLast();
+ void move(int from, int to);
+ void swap(int i, int j);
+
+ int indexOf(const T &t, int from = 0) const {return l_.indexOf (t, from);}
+ int lastIndexOf(const T &t, int from = -1) const
+ {return l_.lastIndexOf (t, from);}
+ bool contains(const T &t) const {return l_.contains (t);}
+ int count(const T &t) const {return l_.count (t);}
+
+ typedef QOdbListIteratorImpl<QOdbList> iterator;
+ typedef typename base_list_type::const_iterator const_iterator;
+
+ // stl style
+ iterator begin() {return iterator (this, l_.begin ());}
+ const_iterator begin() const {return l_.begin ();}
+ const_iterator cbegin() const {return l_.cbegin ();}
+ const_iterator constBegin() const {return l_.constBegin ();}
+ iterator end() {return iterator (this, l_.end ());}
+ const_iterator end() const {return l_.end ();}
+ const_iterator cend() const {return l_.cend ();}
+ const_iterator constEnd() const {return l_.constEnd ();}
+
+ // Return QList iterators. The begin() functions mark all
+ // the elements as modified.
+ //
+ base_iterator_type mbegin ();
+ base_iterator_type modifyBegin () {return mbegin ();}
+ base_iterator_type mend () {return l_.end ();}
+ base_iterator_type modifyEnd () {return mend ();}
+
+ iterator insert(iterator before, const T &t);
+ iterator erase(iterator pos);
+ iterator erase(iterator first, iterator last);
+
+ // more Qt
+ typedef iterator Iterator;
+ typedef const_iterator ConstIterator;
+
+ int count() const {return l_.count ();}
+ int length() const {return l_.length ();}
+ //T& first();
+ T& modifyFirst();
+ const T& first() const {return l_.first ();}
+ //T& last();
+ T& modifyLast();
+ const T& last() const {return l_.last ();}
+ void removeFirst();
+ void removeLast();
+ bool startsWith(const T &t) const {return l_.startsWith (t);}
+ bool endsWith(const T &t) const {return l_.endsWith (t);}
+ QList<T> mid(int pos, int length = -1) const {return l_.mid (pos, length);}
+
+ T value(int i) const {return l_.value (i);}
+ T value(int i, const T &defValue) const {return l_.value (i, defValue);}
+
+ // stl compatibility
+ void push_back(const T &t) {append(t);}
+ void push_front(const T &t) {prepend(t);}
+ //T& front();
+ T& modify_front() {return modifyFirst ();}
+ const T& front() const {return l_.front ();}
+ //T& back();
+ T& modify_back() {return modifyLast ();}
+ const T& back() const {return l_.back ();}
+ void pop_front() {removeFirst();}
+ void pop_back() {removeLast();}
+ bool empty() const {return l_.empty ();}
+
+ typedef int size_type;
+ typedef T value_type;
+ typedef value_type *pointer;
+ typedef const value_type *const_pointer;
+ typedef value_type &reference;
+ typedef const value_type &const_reference;
+ typedef typename base_list_type::difference_type difference_type;
+
+ // comfort
+ // Implicit conversion.
+ QOdbList &operator+=(const QList<T> &l) {append (l); return *this;}
+ QOdbList operator+(const QList<T> &l) const
+ {QOdbList r (*this); r.append (l); return r;}
+ QOdbList &operator+=(const T &t) {append (t); return *this;}
+ QOdbList &operator<< (const T &t) {append (t); return *this;}
+ QOdbList &operator<<(const QList<T> &l) {append (l); return *this;}
+
+ QVector<T> toVector() const {return l_.toVector ();}
+ QSet<T> toSet() const {return l_.toSet ();}
+
+ static QOdbList fromVector(const QVector<T> &v)
+ {return base_list_type::fromVector (v);}
+ static QOdbList fromSet(const QSet<T> &s)
+ {return base_list_type::fromSet (s);}
+
+ static QOdbList fromStdList(const std::list<T> &l)
+ {return base_list_type::fromStdList (l);}
+ std::list<T> toStdList() const {return l_.toStdList ();}
+
+ // Interfacing with the base list.
+ //
+ QOdbList (const base_list_type& x): l_ (x) {}
+ QOdbList& operator= (const base_list_type&);
+ operator const base_list_type& () const {return l_;}
+ base_list_type& base () {return l_;}
+ const base_list_type& base () const {return l_;}
+
+#ifdef ODB_CXX11
+ QOdbList (base_list_type&& x): l_ (std::move (x)) {}
+ QOdbList& operator= (base_list_type&&);
+#endif
+
+ // Change tracking (the rest comes from vector_base).
+ //
+public:
+ void _start () const {impl_.start (l_.size ());}
+
+private:
+ base_list_type l_;
+};
+
+template <typename L>
+class QOdbListIteratorImpl
+{
+public:
+ typedef L list_type;
+ typedef typename list_type::base_iterator_type base_iterator_type;
+ typedef typename list_type::const_iterator const_iterator_type;
+
+ typedef typename base_iterator_type::iterator_category iterator_category;
+ typedef typename base_iterator_type::difference_type difference_type;
+ typedef typename base_iterator_type::value_type value_type;
+ typedef typename base_iterator_type::pointer pointer;
+ typedef typename base_iterator_type::reference reference;
+
+ typedef typename list_type::size_type size_type;
+ typedef typename list_type::const_reference const_reference;
+ typedef typename list_type::const_pointer const_pointer;
+
+ QOdbListIteratorImpl (): l_ (0), i_ () {}
+ QOdbListIteratorImpl (list_type* l, const base_iterator_type& i)
+ : l_ (l), i_ (i) {}
+
+#ifndef QT_STRICT_ITERATORS
+ operator const_iterator_type () const {return i_;}
+#endif
+ base_iterator_type base () const {return i_;}
+ list_type* list () const {return l_;}
+
+ // Note: const_{reference,pointer}.
+ //
+ const_reference operator* () const {return *i_;}
+ const_pointer operator-> () const {return i_.operator -> ();}
+ const_reference operator[] (difference_type n) const {return i_[n];}
+
+ // Modifiers.
+ //
+ reference modify () const;
+ reference modify (difference_type n) const;
+
+ QOdbListIteratorImpl& operator++ () {++i_; return *this;}
+ QOdbListIteratorImpl operator++ (int)
+ {return QOdbListIteratorImpl (l_, i_++);}
+ QOdbListIteratorImpl& operator-- () {--i_; return *this;}
+ QOdbListIteratorImpl operator-- (int)
+ {return QOdbListIteratorImpl (l_, i_--);}
+
+ QOdbListIteratorImpl operator+ (int n) const
+ {return QOdbListIteratorImpl (l_, i_ + n);}
+ QOdbListIteratorImpl& operator+= (int n) {i_ += n; return *this;}
+ QOdbListIteratorImpl operator- (int n) const
+ {return QOdbListIteratorImpl (l_, i_ - n);}
+ QOdbListIteratorImpl& operator-= (int n) {i_ -= n; return *this;}
+
+private:
+ list_type* l_;
+ base_iterator_type i_;
+};
+
+// operator==
+//
+template <typename L>
+inline bool
+operator== (const QOdbListIteratorImpl<L>& x, const QOdbListIteratorImpl<L>& y)
+{return x.base () == y.base ();}
+
+#ifndef QT_STRICT_ITERATORS
+template <typename L>
+inline bool
+operator== (const QOdbListIteratorImpl<L>& x,
+ const typename QOdbListIteratorImpl<L>::const_iterator_type& y)
+{return x.base () == y;}
+
+template <typename L>
+inline bool
+operator== (const typename QOdbListIteratorImpl<L>::const_iterator_type& x,
+ const QOdbListIteratorImpl<L>& y)
+{return x == y.base ();}
+#endif
+
+// operator<
+//
+template <typename L>
+inline bool
+operator< (const QOdbListIteratorImpl<L>& x, const QOdbListIteratorImpl<L>& y)
+{return x.base () < y.base ();}
+
+#ifndef QT_STRICT_ITERATORS
+template <typename L>
+inline bool
+operator< (const QOdbListIteratorImpl<L>& x,
+ const typename QOdbListIteratorImpl<L>::const_iterator_type& y)
+{return x.base () < y;}
+
+template <typename L>
+inline bool
+operator< (const typename QOdbListIteratorImpl<L>::const_iterator_type& x,
+ const QOdbListIteratorImpl<L>& y)
+{return x < y.base ();}
+#endif
+
+// operator!=
+//
+template <typename L>
+inline bool
+operator!= (const QOdbListIteratorImpl<L>& x, const QOdbListIteratorImpl<L>& y)
+{return x.base () != y.base ();}
+
+#ifndef QT_STRICT_ITERATORS
+template <typename L>
+inline bool
+operator!= (const QOdbListIteratorImpl<L>& x,
+ const typename QOdbListIteratorImpl<L>::const_iterator_type& y)
+{return x.base () != y;}
+
+template <typename L>
+inline bool
+operator!= (const typename QOdbListIteratorImpl<L>::const_iterator_type& x,
+ const QOdbListIteratorImpl<L>& y)
+{return x != y.base ();}
+#endif
+
+// operator>
+//
+template <typename L>
+inline bool
+operator> (const QOdbListIteratorImpl<L>& x, const QOdbListIteratorImpl<L>& y)
+{return x.base () > y.base ();}
+
+#ifndef QT_STRICT_ITERATORS
+template <typename L>
+inline bool
+operator> (const QOdbListIteratorImpl<L>& x,
+ const typename QOdbListIteratorImpl<L>::const_iterator_type& y)
+{return x.base () > y;}
+
+template <typename L>
+inline bool
+operator> (const typename QOdbListIteratorImpl<L>::const_iterator_type& x,
+ const QOdbListIteratorImpl<L>& y)
+{return x > y.base ();}
+#endif
+
+// operator>=
+//
+template <typename L>
+inline bool
+operator>= (const QOdbListIteratorImpl<L>& x, const QOdbListIteratorImpl<L>& y)
+{return x.base () >= y.base ();}
+
+#ifndef QT_STRICT_ITERATORS
+template <typename L>
+inline bool
+operator>= (const QOdbListIteratorImpl<L>& x,
+ const typename QOdbListIteratorImpl<L>::const_iterator_type& y)
+{return x.base () >= y;}
+
+template <typename L>
+inline bool
+operator>= (const typename QOdbListIteratorImpl<L>::const_iterator_type& x,
+ const QOdbListIteratorImpl<L>& y)
+{return x >= y.base ();}
+#endif
+
+// operator<=
+//
+template <typename L>
+inline bool
+operator<= (const QOdbListIteratorImpl<L>& x, const QOdbListIteratorImpl<L>& y)
+{return x.base () <= y.base ();}
+
+#ifndef QT_STRICT_ITERATORS
+template <typename L>
+inline bool
+operator<= (const QOdbListIteratorImpl<L>& x,
+ const typename QOdbListIteratorImpl<L>::const_iterator_type& y)
+{return x.base () <= y;}
+
+template <typename L>
+inline bool
+operator<= (const typename QOdbListIteratorImpl<L>::const_iterator_type& x,
+ const QOdbListIteratorImpl<L>& y)
+{return x <= y.base ();}
+#endif
+
+// operator-
+//
+template <typename L>
+inline typename QOdbListIteratorImpl<L>::difference_type
+operator-(const QOdbListIteratorImpl<L>& x, const QOdbListIteratorImpl<L>& y)
+{return x.base () - y.base ();}
+
+#ifndef QT_STRICT_ITERATORS
+template <typename L>
+inline typename QOdbListIteratorImpl<L>::difference_type
+operator-(const QOdbListIteratorImpl<L>& x,
+ const typename QOdbListIteratorImpl<L>::const_iterator_type& y)
+{return x.base () - y;}
+
+template <typename L>
+inline typename QOdbListIteratorImpl<L>::difference_type
+operator-(const typename QOdbListIteratorImpl<L>::const_iterator_type& x,
+ const QOdbListIteratorImpl<L>& y)
+{return x - y.base ();}
+#endif
+
+// operator+
+//
+template <typename L>
+inline QOdbListIteratorImpl<L>
+operator+(typename QOdbListIteratorImpl<L>::difference_type n,
+ const QOdbListIteratorImpl<L>& x)
+{return QOdbListIteratorImpl<L> (x.list (), n + x.base ());}
+
+#include <odb/qt/containers/list.ixx>
+
+#include <odb/qt/containers/list-traits.hxx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_CONTAINERS_LIST_HXX
diff --git a/libodb-qt/odb/qt/containers/list.ixx b/libodb-qt/odb/qt/containers/list.ixx
new file mode 100644
index 0000000..d9d37b6
--- /dev/null
+++ b/libodb-qt/odb/qt/containers/list.ixx
@@ -0,0 +1,318 @@
+// file : odb/qt/containers/list.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+//
+// QOdbList
+//
+
+template <typename T>
+inline QOdbList<T>& QOdbList<T>::
+operator= (const QOdbList<T>& x)
+{
+ l_ = x.l_;
+ if (_tracking ())
+ impl_.assign (static_cast<std::size_t> (l_.size ()));
+ return *this;
+}
+
+template <typename T>
+inline QOdbList<T>& QOdbList<T>::
+operator= (const base_list_type& x)
+{
+ l_ = x;
+ if (_tracking ())
+ impl_.assign (static_cast<std::size_t> (l_.size ()));
+ return *this;
+}
+
+#ifdef ODB_CXX11
+template <typename T>
+inline QOdbList<T>& QOdbList<T>::
+operator= (QOdbList&& x)
+{
+ l_ = std::move (x.l_);
+ if (_tracking ())
+ impl_.assign (static_cast<std::size_t> (l_.size ()));
+ return *this;
+}
+
+template <typename T>
+inline QOdbList<T>& QOdbList<T>::
+operator= (base_list_type&& x)
+{
+ l_ = std::move (x);
+ if (_tracking ())
+ impl_.assign (static_cast<std::size_t> (l_.size ()));
+ return *this;
+}
+#endif
+
+#if QT_VERSION >= 0x040800
+template <typename T>
+inline void QOdbList<T>::
+swap (QOdbList<T>& x)
+{
+ l_.swap (x.l_);
+ vector_base::swap (x);
+}
+#endif
+
+template <typename T>
+inline void QOdbList<T>::
+clear()
+{
+ l_.clear ();
+ if (_tracking ())
+ impl_.clear ();
+}
+
+template <typename T>
+inline T& QOdbList<T>::
+modify (int i)
+{
+ T& r (l_[i]);
+ if (_tracking ())
+ impl_.modify (static_cast<std::size_t> (i));
+ return r;
+}
+
+template <typename T>
+inline void QOdbList<T>::
+reserve (int n)
+{
+ l_.reserve (n);
+ if (_tracking ())
+ impl_.reserve (static_cast<std::size_t> (n));
+}
+
+template <typename T>
+inline void QOdbList<T>::
+append (const T& x)
+{
+ l_.append (x);
+ if (_tracking ())
+ impl_.push_back ();
+}
+
+template <typename T>
+inline void QOdbList<T>::
+append (const QList<T>& x)
+{
+ l_.append (x);
+ if (_tracking ())
+ impl_.push_back (static_cast<std::size_t> (x.size ()));
+}
+
+template <typename T>
+inline void QOdbList<T>::
+prepend (const T& x)
+{
+ l_.prepend (x);
+ if (_tracking ())
+ impl_.insert (0);
+}
+
+template <typename T>
+inline void QOdbList<T>::
+insert (int i, const T& x)
+{
+ l_.insert (i, x);
+ if (_tracking ())
+ impl_.insert (static_cast<std::size_t> (i));
+}
+
+template <typename T>
+inline void QOdbList<T>::
+replace (int i, const T& x)
+{
+ l_.insert (i, x);
+ if (_tracking ())
+ impl_.modify (static_cast<std::size_t> (i));
+}
+
+template <typename T>
+inline void QOdbList<T>::
+removeAt (int i)
+{
+ l_.removeAt (i);
+ if (_tracking ())
+ impl_.erase (static_cast<std::size_t> (i));
+}
+
+template <typename T>
+inline int QOdbList<T>::
+removeAll (const T& x)
+{
+ // We have to re-implement this one ourselves since we need to
+ // know the indexes of the removed elements.
+ //
+ int r (0);
+ for (int i (l_.indexOf (x)); i != -1; i = l_.indexOf (x, i))
+ {
+ removeAt (i);
+ r++;
+ }
+ return r;
+}
+
+template <typename T>
+inline bool QOdbList<T>::
+removeOne (const T& x)
+{
+ // We have to re-implement this one ourselves since we need to
+ // know the index of the removed element.
+ //
+ int i (l_.indexOf (x));
+ if (i != -1)
+ removeAt (i);
+ return i != -1;
+}
+
+template <typename T>
+inline T QOdbList<T>::
+takeAt (int i)
+{
+ if (_tracking ())
+ impl_.erase (static_cast<std::size_t> (i));
+ return l_.takeAt (i);
+}
+
+template <typename T>
+inline T QOdbList<T>::
+takeFirst ()
+{
+ if (_tracking ())
+ impl_.erase (0);
+ return l_.takeFirst ();
+}
+
+template <typename T>
+inline T QOdbList<T>::
+takeLast ()
+{
+ if (_tracking ())
+ impl_.pop_back ();
+ return l_.takeLast ();
+}
+
+template <typename T>
+inline void QOdbList<T>::
+move (int from, int to)
+{
+ l_.move (from, to);
+ if (_tracking ())
+ {
+ impl_.erase (static_cast<std::size_t> (from));
+ impl_.insert (static_cast<std::size_t> (to));
+ }
+}
+
+template <typename T>
+inline void QOdbList<T>::
+swap (int i, int j)
+{
+ l_.swap (i, j);
+ if (_tracking ())
+ {
+ impl_.modify (static_cast<std::size_t> (i));
+ impl_.modify (static_cast<std::size_t> (j));
+ }
+}
+
+template <typename T>
+inline typename QOdbList<T>::base_iterator_type QOdbList<T>::
+mbegin ()
+{
+ if (_tracking ())
+ impl_.modify (0, static_cast<std::size_t> (l_.size ()));
+ return l_.begin ();
+}
+
+template <typename T>
+inline typename QOdbList<T>::iterator QOdbList<T>::
+insert (iterator p, const T& x)
+{
+ if (_tracking ())
+ impl_.insert (static_cast<std::size_t> (p.base () - l_.begin ()));
+ return iterator (this, l_.insert (p.base (), x));
+}
+
+template <typename T>
+inline typename QOdbList<T>::iterator QOdbList<T>::
+erase (iterator p)
+{
+ if (_tracking ())
+ impl_.erase (static_cast<std::size_t> (p.base () - l_.begin ()));
+ return iterator (this, l_.erase (p.base ()));
+}
+
+template <typename T>
+inline typename QOdbList<T>::iterator QOdbList<T>::
+erase (iterator f, iterator l)
+{
+ if (_tracking ())
+ impl_.erase (static_cast<std::size_t> (f.base () - l_.begin ()),
+ static_cast<std::size_t> (l - f));
+ return iterator (this, l_.erase (f.base (), l.base ()));
+}
+
+template <typename T>
+inline T& QOdbList<T>::
+modifyFirst ()
+{
+ T& r (l_.first ());
+ if (_tracking ())
+ impl_.modify (0);
+ return r;
+}
+
+template <typename T>
+inline T& QOdbList<T>::
+modifyLast ()
+{
+ T& r (l_.last ());
+ if (_tracking ())
+ impl_.modify (static_cast<std::size_t> (l_.size () - 1));
+ return r;
+}
+
+template <typename T>
+inline void QOdbList<T>::
+removeFirst ()
+{
+ l_.removeFirst ();
+ if (_tracking ())
+ impl_.erase (0);
+}
+
+template <typename T>
+inline void QOdbList<T>::
+removeLast ()
+{
+ l_.removeLast ();
+ if (_tracking ())
+ impl_.pop_back ();
+}
+
+//
+// QOdbListIteratorImpl
+//
+
+template <typename L>
+inline typename QOdbListIteratorImpl<L>::reference QOdbListIteratorImpl<L>::
+modify () const
+{
+ if (l_->_tracking ())
+ l_->_impl ().modify (static_cast<std::size_t> (i_ - l_->base ().begin ()));
+ return *i_;
+}
+
+template <typename L>
+inline typename QOdbListIteratorImpl<L>::reference QOdbListIteratorImpl<L>::
+modify (difference_type n) const
+{
+ if (l_->_tracking ())
+ l_->_impl ().modify (
+ static_cast<std::size_t> (i_ - l_->base ().begin () + n));
+ return i_[n];
+}
diff --git a/libodb-qt/odb/qt/containers/mutable-list-iterator.hxx b/libodb-qt/odb/qt/containers/mutable-list-iterator.hxx
new file mode 100644
index 0000000..271633a
--- /dev/null
+++ b/libodb-qt/odb/qt/containers/mutable-list-iterator.hxx
@@ -0,0 +1,110 @@
+// file : odb/qt/containers/mutable-list-iterator.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_CONTAINERS_MUTABLE_LIST_ITERATOR_HXX
+#define ODB_QT_CONTAINERS_MUTABLE_LIST_ITERATOR_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QMutableListIterator>
+
+#include <odb/qt/list.hxx>
+
+// Java-style QMutableListIterator-like iterator. The implementation
+// is based on what's found in qiterator.h.
+//
+template <typename T>
+class QMutableOdbListIterator
+{
+public:
+ QMutableOdbListIterator(QOdbList<T> &x)
+ : c (&x)
+ {
+ c->setSharable(false);
+ i = c->begin();
+ n = c->end();
+ }
+
+ ~QMutableOdbListIterator() {c->setSharable(true);}
+
+ QMutableOdbListIterator &operator=(QOdbList<T> &x)
+ {
+ c->setSharable(true);
+ c = &x;
+ c->setSharable(false);
+ i = c->begin();
+ n = c->end();
+ return *this;
+ }
+
+ void toFront() {i = c->begin(); n = c->end();}
+ void toBack() {i = c->end(); n = i;}
+ bool hasNext() const {return c->constEnd() != const_iterator(i.base ());}
+ bool hasPrevious() const
+ {
+ return c->constBegin() != const_iterator(i.base ());
+ }
+
+ bool findNext(const T &t)
+ {
+ while (c->constEnd() != const_iterator((n = i).base ()))
+ if (*i++ == t)
+ return true;
+ return false;
+ }
+
+ bool findPrevious(const T &t)
+ {
+ while (c->constBegin() != const_iterator(i.base ()))
+ if (*(n = --i) == t)
+ return true;
+
+ n = c->end();
+ return false;
+ }
+
+ T &next() {n = i++; return n.modify ();}
+ T &peekNext() const {return i.modify ();}
+ T &previous() {n = --i; return n.modify ();}
+ T &peekPrevious() const {iterator p (i); return (--p).modify ();}
+
+ void remove()
+ {
+ if (c->constEnd() != const_iterator(n.base ()))
+ {
+ i = c->erase (n);
+ n = c->end();
+ }
+ }
+
+ void setValue(const T &t) const
+ {
+ if (c->constEnd() != const_iterator(n.base ()))
+ n.modify () = t;
+ }
+
+ T &value()
+ {
+ Q_ASSERT(c->constEnd() != const_iterator(n.base ()));
+ return n.modify ();
+ }
+
+ const T &value() const
+ {
+ Q_ASSERT(c->constEnd() != const_iterator(n.base ()));
+ return *n;
+ }
+
+ void insert(const T &t) {n = i = c->insert(i, t); ++i;}
+
+private:
+ typedef typename QOdbList<T>::iterator iterator;
+ typedef typename QOdbList<T>::const_iterator const_iterator;
+
+ QOdbList<T>* c;
+ iterator i, n;
+};
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_CONTAINERS_MUTABLE_LIST_ITERATOR_HXX
diff --git a/libodb-qt/odb/qt/containers/qhash-traits.hxx b/libodb-qt/odb/qt/containers/qhash-traits.hxx
new file mode 100644
index 0000000..9f42d37
--- /dev/null
+++ b/libodb-qt/odb/qt/containers/qhash-traits.hxx
@@ -0,0 +1,129 @@
+// file : odb/qt/containers/qhash-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_CONTAINER_QHASH_TRAITS_HXX
+#define ODB_QT_CONTAINER_QHASH_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QHash>
+#include <QtCore/QMultiHash>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename Key, typename T>
+ class access::container_traits<QHash<Key, T> >
+ {
+ public:
+ static const container_kind kind = ck_map;
+ static const bool smart = false;
+
+ typedef QHash<Key, T> container_type;
+ typedef Key key_type;
+ typedef T value_type;
+
+ typedef map_functions<key_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i.key (), i.value ());
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ key_type k;
+ value_type v;
+ more = f.select (k, v);
+ c.insert (k, v); // @@ Use std::move in C++11.
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i.key (), i.value ());
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+
+ // @@ QMultiHash guarantees elements to be stored in reverse order of
+ // insertion. The current implementation of the generated code does
+ // not guarantee this.
+ //
+ template <typename Key, typename T>
+ class access::container_traits<QMultiHash<Key, T> >
+ {
+ public:
+ static const container_kind kind = ck_multimap;
+ static const bool smart = false;
+
+ typedef QMultiHash<Key, T> container_type;
+ typedef Key key_type;
+ typedef T value_type;
+
+ typedef map_functions<key_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i.key (), i.value ());
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ key_type k;
+ value_type v;
+ more = f.select (k, v);
+ c.insert (k, v); //@@ Use std::move in C++11.
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i.key (), i.value ());
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_CONTAINER_QHASH_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/containers/qlinked-list-traits.hxx b/libodb-qt/odb/qt/containers/qlinked-list-traits.hxx
new file mode 100644
index 0000000..3bfc9e9
--- /dev/null
+++ b/libodb-qt/odb/qt/containers/qlinked-list-traits.hxx
@@ -0,0 +1,82 @@
+// file : odb/qt/containers/qlinked-list-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_CONTAINER_QLINKED_LIST_TRAITS_HXX
+#define ODB_QT_CONTAINER_QLINKED_LIST_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QtGlobal> // QT_VERSION
+
+// QLinkedList is deprecated since Qt5 5.15 and in Qt6 it has been moved to a
+// separate library.
+//
+#if (QT_VERSION < 0x050F00) || ODB_QT_FORCE_QLINKEDLIST
+
+#include <QtCore/QLinkedList>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename T>
+ class access::container_traits<QLinkedList<T> >
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = false;
+
+ typedef QLinkedList<T> container_type;
+
+ typedef T value_type;
+ typedef typename container_type::size_type index_type;
+
+ typedef ordered_functions<index_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ index_type i (0);
+ for (typename container_type::const_iterator j (c.begin ()),
+ e (c.end ()); j != e; ++j)
+ f.insert (i++, *j);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ index_type dummy;
+ c.append (value_type ());
+ more = f.select (dummy, c.back ());
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ index_type i (0);
+ for (typename container_type::const_iterator j (c.begin ()),
+ e (c.end ()); j != e; ++j)
+ f.insert (i++, *j);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_CONTAINER_QLINKED_LIST_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/containers/qlist-traits.hxx b/libodb-qt/odb/qt/containers/qlist-traits.hxx
new file mode 100644
index 0000000..572d02a
--- /dev/null
+++ b/libodb-qt/odb/qt/containers/qlist-traits.hxx
@@ -0,0 +1,72 @@
+// file : odb/qt/containers/qlist-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_CONTAINER_QLIST_TRAITS_HXX
+#define ODB_QT_CONTAINER_QLIST_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QList>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename T>
+ class access::container_traits<QList<T> >
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = false;
+
+ typedef QList<T> container_type;
+
+ typedef T value_type;
+ typedef typename container_type::size_type index_type;
+
+ typedef ordered_functions<index_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ // Index based access is just as fast as iterator based access for
+ // QList.
+ //
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ index_type dummy;
+ c.append (value_type ());
+ more = f.select (dummy, c.back ());
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_CONTAINER_QLIST_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/containers/qmap-traits.hxx b/libodb-qt/odb/qt/containers/qmap-traits.hxx
new file mode 100644
index 0000000..5f13b29
--- /dev/null
+++ b/libodb-qt/odb/qt/containers/qmap-traits.hxx
@@ -0,0 +1,130 @@
+// file : odb/qt/containers/qmap-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_CONTAINER_QMAP_TRAITS_HXX
+#define ODB_QT_CONTAINER_QMAP_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QMap>
+#include <QtCore/QMultiMap>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename Key, typename T>
+ class access::container_traits<QMap<Key, T> >
+ {
+ public:
+ static const container_kind kind = ck_map;
+ static const bool smart = false;
+
+ typedef QMap<Key, T> container_type;
+
+ typedef Key key_type;
+ typedef T value_type;
+
+ typedef map_functions<key_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i.key (), i.value ());
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ key_type k;
+ value_type v;
+ more = f.select (k, v);
+ c.insert (k, v); //@@ Use std::move in C++11.
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i.key (), i.value ());
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+ // @@ QMultiMap guarantees elements to be stored in reverse order of
+ // insertion. The current implementation of the generated code does
+ // not guarantee this.
+ //
+ template <typename Key, typename T>
+ class access::container_traits<QMultiMap<Key, T> >
+ {
+ public:
+ static const container_kind kind = ck_multimap;
+ static const bool smart = false;
+
+ typedef QMultiMap<Key, T> container_type;
+
+ typedef Key key_type;
+ typedef T value_type;
+
+ typedef map_functions<key_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i.key (), i.value ());
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ key_type k;
+ value_type v;
+ more = f.select (k, v);
+ c.insert (k, v); //@@ Use std::move in C++11.
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i.key (), i.value ());
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_CONTAINER_QMAP_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/containers/qset-traits.hxx b/libodb-qt/odb/qt/containers/qset-traits.hxx
new file mode 100644
index 0000000..fbea8b7
--- /dev/null
+++ b/libodb-qt/odb/qt/containers/qset-traits.hxx
@@ -0,0 +1,69 @@
+// file : odb/qt/containers/qset-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_CONTAINER_QSET_TRAITS_HXX
+#define ODB_QT_CONTAINER_QSET_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QSet>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename T>
+ class access::container_traits<QSet<T> >
+ {
+ public:
+ static const container_kind kind = ck_set;
+ static const bool smart = false;
+
+ typedef QSet<T> container_type;
+ typedef T value_type;
+
+ typedef set_functions<value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ value_type v;
+ more = f.select (v);
+ c.insert (v); //@@ Use std::move in C++11.
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_CONTAINER_QSET_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/containers/qvector-traits.hxx b/libodb-qt/odb/qt/containers/qvector-traits.hxx
new file mode 100644
index 0000000..516475d
--- /dev/null
+++ b/libodb-qt/odb/qt/containers/qvector-traits.hxx
@@ -0,0 +1,82 @@
+// file : odb/qt/containers/qvector-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_CONTAINER_QVECTOR_TRAITS_HXX
+#define ODB_QT_CONTAINER_QVECTOR_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QtGlobal> // QT_VERSION
+
+// In Qt 6 QVector is an alias for QList.
+//
+#if QT_VERSION >= 0x060000
+#include <odb/qt/containers/qlist-traits.hxx>
+#else
+
+#include <QtCore/QVector>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename T>
+ class access::container_traits<QVector<T> >
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = false;
+
+ typedef QVector<T> container_type;
+
+ typedef T value_type;
+ typedef typename container_type::size_type index_type;
+
+ typedef ordered_functions<index_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ // Index based access is just as fast as iterator based access for
+ // QVector.
+ //
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ index_type dummy;
+ c.append (value_type ());
+ more = f.select (dummy, c.back ());
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_CONTAINER_QVECTOR_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/date-time.options b/libodb-qt/odb/qt/date-time.options
new file mode 100644
index 0000000..d199649
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time.options
@@ -0,0 +1,4 @@
+# file : odb/qt/date-time.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile qt/date-time/date-time
diff --git a/libodb-qt/odb/qt/date-time/date-time-common.options b/libodb-qt/odb/qt/date-time/date-time-common.options
new file mode 100644
index 0000000..d813259
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/date-time-common.options
@@ -0,0 +1,4 @@
+# file : odb/qt/date-time/date-time-common.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile qt/version
diff --git a/libodb-qt/odb/qt/date-time/date-time-mssql.options b/libodb-qt/odb/qt/date-time/date-time-mssql.options
new file mode 100644
index 0000000..7e35b4d
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/date-time-mssql.options
@@ -0,0 +1,13 @@
+# file : odb/qt/date-time/date-time-mssql.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile qt/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/qt/date-time/mssql/default-mapping.hxx>'
+
+--hxx-prologue '#include <odb/qt/date-time/mssql/qdate-traits.hxx>'
+--hxx-prologue '#include <odb/qt/date-time/mssql/qtime-traits.hxx>'
+--hxx-prologue '#include <odb/qt/date-time/mssql/qdate-time-traits.hxx>'
diff --git a/libodb-qt/odb/qt/date-time/date-time-mysql.options b/libodb-qt/odb/qt/date-time/date-time-mysql.options
new file mode 100644
index 0000000..fdbb364
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/date-time-mysql.options
@@ -0,0 +1,13 @@
+# file : odb/qt/date-time/date-time-mysql.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile qt/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/qt/date-time/mysql/default-mapping.hxx>'
+
+--hxx-prologue '#include <odb/qt/date-time/mysql/qdate-traits.hxx>'
+--hxx-prologue '#include <odb/qt/date-time/mysql/qtime-traits.hxx>'
+--hxx-prologue '#include <odb/qt/date-time/mysql/qdate-time-traits.hxx>'
diff --git a/libodb-qt/odb/qt/date-time/date-time-oracle.options b/libodb-qt/odb/qt/date-time/date-time-oracle.options
new file mode 100644
index 0000000..c339ce3
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/date-time-oracle.options
@@ -0,0 +1,13 @@
+# file : odb/qt/date-time/date-time-oracle.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile qt/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/qt/date-time/oracle/default-mapping.hxx>'
+
+--hxx-prologue '#include <odb/qt/date-time/oracle/qdate-traits.hxx>'
+--hxx-prologue '#include <odb/qt/date-time/oracle/qtime-traits.hxx>'
+--hxx-prologue '#include <odb/qt/date-time/oracle/qdate-time-traits.hxx>'
diff --git a/libodb-qt/odb/qt/date-time/date-time-pgsql.options b/libodb-qt/odb/qt/date-time/date-time-pgsql.options
new file mode 100644
index 0000000..9d80d48
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/date-time-pgsql.options
@@ -0,0 +1,13 @@
+# file : odb/qt/date-time/date-time-pgsql.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile qt/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/qt/date-time/pgsql/default-mapping.hxx>'
+
+--hxx-prologue '#include <odb/qt/date-time/pgsql/qdate-traits.hxx>'
+--hxx-prologue '#include <odb/qt/date-time/pgsql/qtime-traits.hxx>'
+--hxx-prologue '#include <odb/qt/date-time/pgsql/qdate-time-traits.hxx>'
diff --git a/libodb-qt/odb/qt/date-time/date-time-sqlite.options b/libodb-qt/odb/qt/date-time/date-time-sqlite.options
new file mode 100644
index 0000000..8eb1110
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/date-time-sqlite.options
@@ -0,0 +1,13 @@
+# file : odb/qt/date-time/date-time-sqlite.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile qt/version
+
+# Include the default mapping in prologue instead of epilogue to
+# allow the user to override the default mapping.
+#
+--odb-prologue '#include <odb/qt/date-time/sqlite/default-mapping.hxx>'
+
+--hxx-prologue '#include <odb/qt/date-time/sqlite/qdate-traits.hxx>'
+--hxx-prologue '#include <odb/qt/date-time/sqlite/qtime-traits.hxx>'
+--hxx-prologue '#include <odb/qt/date-time/sqlite/qdate-time-traits.hxx>'
diff --git a/libodb-qt/odb/qt/date-time/exceptions.cxx b/libodb-qt/odb/qt/date-time/exceptions.cxx
new file mode 100644
index 0000000..48f0540
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/exceptions.cxx
@@ -0,0 +1,25 @@
+// file : odb/qt/date-time/exceptions.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/qt/date-time/exceptions.hxx>
+
+namespace odb
+{
+ namespace qt
+ {
+ namespace date_time
+ {
+ const char* value_out_of_range::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "date/time value out of range";
+ }
+
+ value_out_of_range* value_out_of_range::
+ clone () const
+ {
+ return new value_out_of_range (*this);
+ }
+ }
+ }
+}
diff --git a/libodb-qt/odb/qt/date-time/exceptions.hxx b/libodb-qt/odb/qt/date-time/exceptions.hxx
new file mode 100644
index 0000000..7a73be2
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/exceptions.hxx
@@ -0,0 +1,34 @@
+// file : odb/qt/date-time/exceptions.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_EXCEPTIONS_HXX
+#define ODB_QT_DATE_TIME_EXCEPTIONS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+
+#include <odb/qt/exception.hxx>
+#include <odb/qt/details/export.hxx>
+
+namespace odb
+{
+ namespace qt
+ {
+ namespace date_time
+ {
+ struct LIBODB_QT_EXPORT value_out_of_range: exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual value_out_of_range*
+ clone () const;
+ };
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_DATE_TIME_EXCEPTIONS_HXX
diff --git a/libodb-qt/odb/qt/date-time/mssql/default-mapping.hxx b/libodb-qt/odb/qt/date-time/mssql/default-mapping.hxx
new file mode 100644
index 0000000..a8acb6b
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/mssql/default-mapping.hxx
@@ -0,0 +1,31 @@
+// file : odb/qt/date-time/mssql/default-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_MSSQL_DEFAULT_MAPPING_HXX
+#define ODB_QT_DATE_TIME_MSSQL_DEFAULT_MAPPING_HXX
+
+#include <QtCore/QDate>
+#include <QtCore/QTime>
+#include <QtCore/QDateTime>
+
+// By default map QDate to SQL Server DATE (available only since SQL
+// Server 2008). QDate provides a null representation so allow NULL
+// values by default.
+//
+#pragma db value(QDate) type("DATE") null
+
+// By default map QTime to SQL Server TIME(3) (available only since SQL
+// Server 2008). QTime can only represent clock times with a maximum
+// precision of milliseconds. QTime provides a null representation so
+// allow NULL values by default.
+//
+#pragma db value(QTime) type("TIME(3)") null
+
+// By default map QDateTime to SQL Server DATETIME2(3) (available only
+// since SQL Server 2008). QDateTime can only represent clock times with
+// a maximum precision of milliseconds. QDateTime provides a null
+// representation so allow NULL values by default.
+//
+#pragma db value(QDateTime) type("DATETIME2(3)") null
+
+#endif // ODB_QT_DATE_TIME_MSSQL_DEFAULT_MAPPING_HXX
diff --git a/libodb-qt/odb/qt/date-time/mssql/qdate-time-traits.hxx b/libodb-qt/odb/qt/date-time/mssql/qdate-time-traits.hxx
new file mode 100644
index 0000000..5fd8a98
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/mssql/qdate-time-traits.hxx
@@ -0,0 +1,104 @@
+// file : odb/qt/date-time/mssql/qdate-time-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_MSSQL_QDATETIME_TRAITS_HXX
+#define ODB_QT_DATE_TIME_MSSQL_QDATETIME_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QDateTime>
+
+#include <odb/mssql/traits.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <>
+ struct default_value_traits<QDateTime, id_datetime>
+ {
+ typedef QDateTime value_type;
+ typedef QDateTime query_type;
+ typedef datetime image_type;
+
+ static void
+ set_value (QDateTime& v, const datetime& i, bool is_null)
+ {
+ if (is_null)
+ // Default constructor creates a null QDateTime.
+ //
+ v = QDateTime ();
+ else
+ v = QDateTime (QDate (i.year,
+ i.month,
+ i.day),
+ QTime (i.hour,
+ i.minute,
+ i.second,
+ static_cast<int> (i.fraction / 1000000)));
+ }
+
+ static void
+ set_image (datetime& i,
+ unsigned short s,
+ bool& is_null,
+ const QDateTime& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+
+ const QDate& d (v.date ());
+ const QTime& t (v.time ());
+
+ is_null = false;
+ i.year = static_cast<SQLSMALLINT> (d.year ());
+ i.month = static_cast<SQLUSMALLINT> (d.month ());
+ i.day = static_cast<SQLUSMALLINT> (d.day ());
+ i.hour = static_cast<SQLUSMALLINT> (t.hour ());
+ i.minute = static_cast<SQLUSMALLINT> (t.minute ());
+
+ // Scale value 8 indicates we are dealing with SMALLDATETIME
+ // which has the minutes precision.
+ //
+ if (s != 8)
+ {
+ i.second = static_cast<SQLUSMALLINT> (t.second ());
+
+ const unsigned int divider[8] =
+ {
+ 1000000000,
+ 100000000,
+ 10000000,
+ 1000000,
+ 100000,
+ 10000,
+ 1000,
+ 100
+ };
+
+ unsigned int ns (static_cast<unsigned int> (t.msec ()) * 1000000);
+ i.fraction = static_cast<SQLUINTEGER> (ns - ns % divider[s]);
+ }
+ else
+ {
+ i.second = 0;
+ i.fraction = 0;
+ }
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QDateTime>
+ {
+ static const database_type_id db_type_od = id_datetime;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_DATE_TIME_MSSQL_QDATETIME_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/date-time/mssql/qdate-traits.hxx b/libodb-qt/odb/qt/date-time/mssql/qdate-traits.hxx
new file mode 100644
index 0000000..caa5d3c
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/mssql/qdate-traits.hxx
@@ -0,0 +1,63 @@
+// file : odb/qt/date-time/mssql/qdate-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_MSSQL_QDATE_TRAITS_HXX
+#define ODB_QT_DATE_TIME_MSSQL_QDATE_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QDate>
+
+#include <odb/mssql/traits.hxx>
+
+#include <odb/qt/date-time/exceptions.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <>
+ struct default_value_traits<QDate, id_date>
+ {
+ typedef QDate value_type;
+ typedef QDate query_type;
+ typedef date image_type;
+
+ static void
+ set_value (QDate& v, const date& i, bool is_null)
+ {
+ if (is_null)
+ // A null QDate value is equivalent to an invalid QDate value.
+ // Set v to an invalid date to represent null.
+ //
+ v.setDate (0, 0, 0);
+ else
+ v.setDate (i.year, i.month, i.day);
+ }
+
+ static void
+ set_image (date& i, bool& is_null, const QDate& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+ i.year = static_cast<SQLSMALLINT> (v.year ());
+ i.month = static_cast<SQLUSMALLINT> (v.month ());
+ i.day = static_cast<SQLUSMALLINT> (v.day ());
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QDate>
+ {
+ static const database_type_id db_type_id = id_date;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_DATE_TIME_MSSQL_QDATE_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/date-time/mssql/qtime-traits.hxx b/libodb-qt/odb/qt/date-time/mssql/qtime-traits.hxx
new file mode 100644
index 0000000..88fbf41
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/mssql/qtime-traits.hxx
@@ -0,0 +1,80 @@
+// file : odb/qt/date-time/mssql/qtime-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_MSSQL_QTIME_TRAITS_HXX
+#define ODB_QT_DATE_TIME_MSSQL_QTIME_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QTime>
+
+#include <odb/mssql/traits.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <>
+ struct default_value_traits<QTime, id_time>
+ {
+ typedef QTime value_type;
+ typedef QTime query_type;
+ typedef time image_type;
+
+ static void
+ set_value (QTime& v, const time& i, bool is_null)
+ {
+ if (is_null)
+ // A null QTime value is equivalent to an invalid QTime value.
+ // Set v to an invalid time to represent null (hour value of
+ // a valid time must be in the range 0-23).
+ //
+ v.setHMS (24, 0, 0);
+ else
+ v.setHMS (i.hour,
+ i.minute,
+ i.second,
+ static_cast<int> (i.fraction / 1000000));
+ }
+
+ static void
+ set_image (time& i, unsigned short s, bool& is_null, const QTime& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+ i.hour = static_cast<SQLUSMALLINT> (v.hour ());
+ i.minute = static_cast<SQLUSMALLINT> (v.minute ());
+ i.second = static_cast<SQLUSMALLINT> (v.second ());
+
+ const unsigned int divider[8] =
+ {
+ 1000000000,
+ 100000000,
+ 10000000,
+ 1000000,
+ 100000,
+ 10000,
+ 1000,
+ 100
+ };
+
+ unsigned int ns (static_cast<unsigned int> (v.msec ()) * 1000000);
+ i.fraction = static_cast<SQLUINTEGER> (ns - ns % divider[s]);
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QTime>
+ {
+ static const database_type_id db_type_id = id_time;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_DATE_TIME_MSSQL_QTIME_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/date-time/mysql/default-mapping.hxx b/libodb-qt/odb/qt/date-time/mysql/default-mapping.hxx
new file mode 100644
index 0000000..a88c507
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/mysql/default-mapping.hxx
@@ -0,0 +1,26 @@
+// file : odb/qt/date-time/mysql/default-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_MYSQL_DEFAULT_MAPPING_HXX
+#define ODB_QT_DATE_TIME_MYSQL_DEFAULT_MAPPING_HXX
+
+#include <QtCore/QDate>
+#include <QtCore/QTime>
+#include <QtCore/QDateTime>
+
+// Map QDate to MySQL DATE by default. QDate provides a null
+// representation so allow NULL values by default.
+//
+#pragma db value(QDate) type("DATE") null
+
+// Map QTime to MySQL TIME by default. QTime provides a null
+// representation so allow NULL values by default.
+//
+#pragma db value(QTime) type("TIME") null
+
+// Map QDateTime to MySQL DATETIME by default. QDateTime provides a null
+// representation so allow NULL values by default.
+//
+#pragma db value(QDateTime) type("DATETIME") null
+
+#endif // ODB_QT_DATE_TIME_MYSQL_DEFAULT_MAPPING_HXX
diff --git a/libodb-qt/odb/qt/date-time/mysql/qdate-time-traits.hxx b/libodb-qt/odb/qt/date-time/mysql/qdate-time-traits.hxx
new file mode 100644
index 0000000..92279eb
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/mysql/qdate-time-traits.hxx
@@ -0,0 +1,135 @@
+// file : odb/qt/date-time/mysql/qdate-time-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_MYSQL_QDATETIME_TRAITS_HXX
+#define ODB_QT_DATE_TIME_MYSQL_QDATETIME_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QDateTime>
+
+#include <odb/mysql/traits.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <>
+ struct default_value_traits<QDateTime, id_datetime>
+ {
+ typedef QDateTime value_type;
+ typedef QDateTime query_type;
+ typedef MYSQL_TIME image_type;
+
+ static void
+ set_value (QDateTime& v, const MYSQL_TIME& i, bool is_null)
+ {
+ if (is_null)
+ // Default constructor creates a null QDateTime.
+ //
+ v = QDateTime ();
+ else
+ // Since MySQL 5.6.4, the microseconds part is no longer ignored.
+ //
+ v = QDateTime (QDate (static_cast<int> (i.year),
+ static_cast<int> (i.month),
+ static_cast<int> (i.day)),
+ QTime (static_cast<int> (i.hour),
+ static_cast<int> (i.minute),
+ static_cast<int> (i.second),
+ static_cast<int> (i.second_part / 1000)));
+ }
+
+ static void
+ set_image (MYSQL_TIME& i, bool& is_null, const QDateTime& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ if ((v < QDateTime (QDate (1000, 1, 1))) ||
+ (v >= QDateTime (QDate (10000, 1, 1))))
+ throw odb::qt::date_time::value_out_of_range ();
+
+ is_null = false;
+ i.neg = false;
+
+ const QDate& d (v.date ());
+ i.year = static_cast<unsigned int> (d.year ());
+ i.month = static_cast<unsigned int> (d.month ());
+ i.day = static_cast<unsigned int> (d.day ());
+
+ const QTime& t (v.time ());
+ i.hour = static_cast<unsigned int> (t.hour ());
+ i.minute = static_cast<unsigned int> (t.minute ());
+ i.second = static_cast<unsigned int> (t.second ());
+ i.second_part = static_cast<unsigned long> (t.msec ()) * 1000;
+ }
+ }
+ };
+
+ template <>
+ struct default_value_traits<QDateTime, id_timestamp>
+ {
+ typedef QDateTime value_type;
+ typedef QDateTime query_type;
+ typedef MYSQL_TIME image_type;
+
+ static void
+ set_value (QDateTime& v, const MYSQL_TIME& i, bool is_null)
+ {
+ if (is_null)
+ // Default constructor creates a null QDateTime.
+ //
+ v = QDateTime ();
+ else
+ // Since MySQL 5.6.4, the microseconds part is no longer ignored.
+ //
+ v = QDateTime (QDate (static_cast<int> (i.year),
+ static_cast<int> (i.month),
+ static_cast<int> (i.day)),
+ QTime (static_cast<int> (i.hour),
+ static_cast<int> (i.minute),
+ static_cast<int> (i.second),
+ static_cast<int> (i.second_part / 1000)));
+ }
+
+ static void
+ set_image (MYSQL_TIME& i, bool& is_null, const QDateTime& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ if ((v <= QDateTime (QDate (1970, 1, 1))) ||
+ (v > QDateTime (QDate (2038, 1, 19), QTime (3, 14, 7))))
+ throw odb::qt::date_time::value_out_of_range ();
+
+ is_null = false;
+ i.neg = false;
+
+ const QDate& d (v.date ());
+ i.year = static_cast<unsigned int> (d.year ());
+ i.month = static_cast<unsigned int> (d.month ());
+ i.day = static_cast<unsigned int> (d.day ());
+
+ const QTime& t (v.time ());
+ i.hour = static_cast<unsigned int> (t.hour ());
+ i.minute = static_cast<unsigned int> (t.minute ());
+ i.second = static_cast<unsigned int> (t.second ());
+ i.second_part = static_cast<unsigned long> (t.msec ()) * 1000;
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QDateTime>
+ {
+ static const database_type_id db_type_od = id_datetime;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_DATE_TIME_MYSQL_QDATETIME_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/date-time/mysql/qdate-traits.hxx b/libodb-qt/odb/qt/date-time/mysql/qdate-traits.hxx
new file mode 100644
index 0000000..12a00ec
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/mysql/qdate-traits.hxx
@@ -0,0 +1,73 @@
+// file : odb/qt/date-time/mysql/qdate-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_MYSQL_QDATE_TRAITS_HXX
+#define ODB_QT_DATE_TIME_MYSQL_QDATE_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QDate>
+
+#include <odb/mysql/traits.hxx>
+#include <odb/qt/date-time/exceptions.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <>
+ struct default_value_traits<QDate, id_date>
+ {
+ typedef QDate value_type;
+ typedef QDate query_type;
+ typedef MYSQL_TIME image_type;
+
+ static void
+ set_value (QDate& v, const MYSQL_TIME& i, bool is_null)
+ {
+ if (is_null)
+ // A null QDate value is equivalent to an invalid QDate value.
+ // Set v to an invalid date to represent null.
+ //
+ v.setDate (0, 0, 0);
+ else
+ v.setDate (static_cast<int> (i.year),
+ static_cast<int> (i.month),
+ static_cast<int> (i.day));
+ }
+
+ static void
+ set_image (MYSQL_TIME& i, bool& is_null, const QDate& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else if ((v < QDate (1000, 1, 1)) || (v > QDate (9999, 12, 31)))
+ throw odb::qt::date_time::value_out_of_range ();
+ else
+ {
+ is_null = false;
+ i.neg = false;
+
+ i.year = static_cast<unsigned int> (v.year ());
+ i.month = static_cast<unsigned int> (v.month ());
+ i.day = static_cast<unsigned int> (v.day ());
+
+ i.hour = 0;
+ i.minute = 0;
+ i.second = 0;
+ i.second_part = 0;
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QDate>
+ {
+ static const database_type_id db_type_id = id_date;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_DATE_TIME_MYSQL_QDATE_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/date-time/mysql/qtime-traits.hxx b/libodb-qt/odb/qt/date-time/mysql/qtime-traits.hxx
new file mode 100644
index 0000000..c96e13f
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/mysql/qtime-traits.hxx
@@ -0,0 +1,74 @@
+// file : odb/qt/date-time/mysql/qtime-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_MYSQL_QTIME_TRAITS_HXX
+#define ODB_QT_DATE_TIME_MYSQL_QTIME_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QTime>
+
+#include <odb/mysql/traits.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <>
+ struct default_value_traits<QTime, id_time>
+ {
+ typedef QTime value_type;
+ typedef QTime query_type;
+ typedef MYSQL_TIME image_type;
+
+ static void
+ set_value (QTime& v, const MYSQL_TIME& i, bool is_null)
+ {
+ if (is_null)
+ // A null QTime value is equivalent to an invalid QTime value.
+ // Set v to an invalid time to represent null (hour value of
+ // a valid time must be in the range 0-23).
+ //
+ v.setHMS (24, 0, 0);
+ else
+ // Since MySQL 5.6.4, the microseconds part is no longer ignored.
+ //
+ v.setHMS (static_cast<int> (i.hour),
+ static_cast<int> (i.minute),
+ static_cast<int> (i.second),
+ static_cast<int> (i.second_part / 1000));
+ }
+
+ static void
+ set_image (MYSQL_TIME& i, bool& is_null, const QTime& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+ i.neg = false;
+
+ i.year = 0;
+ i.month = 0;
+ i.day = 0;
+
+ i.hour = static_cast<unsigned int> (v.hour ());
+ i.minute = static_cast<unsigned int> (v.minute ());
+ i.second = static_cast<unsigned int> (v.second ());
+ i.second_part = static_cast<unsigned long> (v.msec ()) * 1000;
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QTime>
+ {
+ static const database_type_id db_type_id = id_time;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_DATE_TIME_MYSQL_QTIME_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/date-time/oracle/default-mapping.hxx b/libodb-qt/odb/qt/date-time/oracle/default-mapping.hxx
new file mode 100644
index 0000000..32a733a
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/oracle/default-mapping.hxx
@@ -0,0 +1,29 @@
+// file : odb/qt/date-time/oracle/default-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_ORACLE_DEFAULT_MAPPING_HXX
+#define ODB_QT_DATE_TIME_ORACLE_DEFAULT_MAPPING_HXX
+
+#include <QtCore/QDate>
+#include <QtCore/QTime>
+#include <QtCore/QDateTime>
+
+// Map QDate to Oracle DATE by default. QDate provides a null representation
+// so allow NULL values by default.
+//
+#pragma db value(QDate) type("DATE") null
+
+// Map QTime to Oracle INTERVAL DAY(0) TO SECOND(3) by default. QTime can
+// only represent clock times with a maximum precision of milliseconds.
+// QTime provides a null representation so allow NULL values by default.
+//
+#pragma db value(QTime) type("INTERVAL DAY(0) TO SECOND(3)") null
+
+// Map QDateTime to Oracle TIMESTAMP(3) by default. QDateTime can only
+// represent clock times with a maximum precision of milliseconds.
+// QDateTime provides a null representation so allow NULL values by
+// default.
+//
+#pragma db value(QDateTime) type("TIMESTAMP(3)") null
+
+#endif // ODB_QT_DATE_TIME_ORACLE_DEFAULT_MAPPING_HXX
diff --git a/libodb-qt/odb/qt/date-time/oracle/qdate-time-traits.hxx b/libodb-qt/odb/qt/date-time/oracle/qdate-time-traits.hxx
new file mode 100644
index 0000000..ba90075
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/oracle/qdate-time-traits.hxx
@@ -0,0 +1,136 @@
+// file : odb/qt/date-time/oracle/qdate-time-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_ORACLE_QDATETIME_TRAITS_HXX
+#define ODB_QT_DATE_TIME_ORACLE_QDATETIME_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QDateTime>
+
+#include <odb/oracle/traits.hxx>
+#include <odb/oracle/details/date.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <>
+ struct default_value_traits<QDateTime, id_timestamp>
+ {
+ typedef QDateTime value_type;
+ typedef QDateTime query_type;
+ typedef datetime image_type;
+
+ static void
+ set_value (QDateTime& v, const datetime& i, bool is_null)
+ {
+ if (is_null)
+ // Default constructor creates a null QDateTime.
+ //
+ v = QDateTime ();
+ else
+ {
+ sb2 y (0);
+ ub1 m (0), d (0), h (0), minute (0), s (0);
+ ub4 ns (0);
+ i.get (y, m, d, h, minute, s, ns);
+
+ v = QDateTime (QDate (static_cast<int> (y),
+ static_cast<int> (m),
+ static_cast<int> (d)),
+ QTime (static_cast<int> (h),
+ static_cast<int> (minute),
+ static_cast<int> (s),
+ static_cast<int> (ns / 1000000)));
+ }
+ }
+
+ static void
+ set_image (datetime& i, bool& is_null, const QDateTime& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+
+ const QDate& d (v.date ());
+ const QTime& t (v.time ());
+
+ i.set (static_cast<sb2> (d.year ()),
+ static_cast<ub1> (d.month ()),
+ static_cast<ub1> (d.day ()),
+ static_cast<ub1> (t.hour ()),
+ static_cast<ub1> (t.minute ()),
+ static_cast<ub1> (t.second ()),
+ static_cast<ub4> (t.msec () * 1000000));
+ }
+ }
+ };
+
+ template <>
+ struct default_value_traits<QDateTime, id_date>
+ {
+ typedef QDateTime value_type;
+ typedef QDateTime query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (QDateTime& v, const char* b, bool is_null)
+ {
+ if (is_null)
+ // Default constructor creates a null QDateTime.
+ //
+ v = QDateTime ();
+ else
+ {
+ short y;
+ unsigned char m, d, h, minute, s;
+
+ details::get_date (b, y, m, d, h, minute, s);
+
+ v = QDateTime (QDate (static_cast<int> (y),
+ static_cast<int> (m),
+ static_cast<int> (d)),
+ QTime (static_cast<int> (h),
+ static_cast<int> (minute),
+ static_cast<int> (s),
+ 0));
+ }
+ }
+
+ static void
+ set_image (char* b, bool& is_null, const QDateTime& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+
+ const QDate& d (v.date ());
+ const QTime& t (v.time ());
+
+ details::set_date (b,
+ static_cast<short> (d.year ()),
+ static_cast<unsigned char> (d.month ()),
+ static_cast<unsigned char> (d.day ()),
+ static_cast<unsigned char> (t.hour ()),
+ static_cast<unsigned char> (t.minute ()),
+ static_cast<unsigned char> (t.second ()));
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QDateTime>
+ {
+ static const database_type_id db_type_od = id_timestamp;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_DATE_TIME_ORACLE_QDATETIME_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/date-time/oracle/qdate-traits.hxx b/libodb-qt/odb/qt/date-time/oracle/qdate-traits.hxx
new file mode 100644
index 0000000..f293e2d
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/oracle/qdate-traits.hxx
@@ -0,0 +1,76 @@
+// file : odb/qt/date-time/oracle/qdate-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_ORACLE_QDATE_TRAITS_HXX
+#define ODB_QT_DATE_TIME_ORACLE_QDATE_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QDate>
+
+#include <odb/oracle/traits.hxx>
+
+#include <odb/oracle/details/date.hxx>
+
+#include <odb/qt/date-time/exceptions.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <>
+ struct default_value_traits<QDate, id_date>
+ {
+ typedef QDate value_type;
+ typedef QDate query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (QDate& v, const char* b, bool is_null)
+ {
+ if (is_null)
+ // A null QDate value is equivalent to an invalid QDate value.
+ // Set v to an invalid date to represent null.
+ //
+ v.setDate (0, 0, 0);
+ else
+ {
+ short y (0);
+ unsigned char m (0), d (0), h (0), minute (0), s (0);
+ details::get_date (b, y, m, d, h, minute, s);
+
+ v.setDate (y, m, d);
+ }
+ }
+
+ static void
+ set_image (char* b, bool& is_null, const QDate& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+
+ details::set_date (b,
+ static_cast<short> (v.year ()),
+ static_cast<unsigned char> (v.month ()),
+ static_cast<unsigned char> (v.day ()),
+ 0,
+ 0,
+ 0);
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QDate>
+ {
+ static const database_type_id db_type_id = id_date;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_DATE_TIME_ORACLE_QDATE_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/date-time/oracle/qtime-traits.hxx b/libodb-qt/odb/qt/date-time/oracle/qtime-traits.hxx
new file mode 100644
index 0000000..0d9aff4
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/oracle/qtime-traits.hxx
@@ -0,0 +1,66 @@
+// file : odb/qt/date-time/oracle/qtime-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_ORACLE_QTIME_TRAITS_HXX
+#define ODB_QT_DATE_TIME_ORACLE_QTIME_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QTime>
+
+#include <odb/oracle/traits.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <>
+ struct default_value_traits<QTime, id_interval_ds>
+ {
+ typedef QTime value_type;
+ typedef QTime query_type;
+ typedef interval_ds image_type;
+
+ static void
+ set_value (QTime& v, const interval_ds& i, bool is_null)
+ {
+ if (is_null)
+ // A null QTime value is equivalent to an invalid QTime value.
+ // Set v to an invalid time to represent null (hour value of
+ // a valid time must be in the range 0-23).
+ //
+ v.setHMS (24, 0, 0);
+ else
+ {
+ sb4 d (0), h (0), m (0), s (0), ns (0);
+ i.get (d, h, m, s, ns);
+
+ v.setHMS (h, m, s, ns / 1000000);
+ }
+ }
+
+ static void
+ set_image (interval_ds& i, bool& is_null, const QTime& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+
+ i.set (0, v.hour (), v.minute (), v.second (), v.msec () * 1000000);
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QTime>
+ {
+ static const database_type_id db_type_id = id_interval_ds;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_DATE_TIME_ORACLE_QTIME_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/date-time/pgsql/default-mapping.hxx b/libodb-qt/odb/qt/date-time/pgsql/default-mapping.hxx
new file mode 100644
index 0000000..cd3b1b5
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/pgsql/default-mapping.hxx
@@ -0,0 +1,26 @@
+// file : odb/qt/date-time/pgsql/default-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_PGSQL_DEFAULT_MAPPING_HXX
+#define ODB_QT_DATE_TIME_PGSQL_DEFAULT_MAPPING_HXX
+
+#include <QtCore/QDate>
+#include <QtCore/QTime>
+#include <QtCore/QDateTime>
+
+// Map QDate to PostgreSQL DATE by default. QDate provides a null
+// representation so allow NULL values by default.
+//
+#pragma db value(QDate) type("DATE") null
+
+// Map QTime to PostgreSQL TIME by default. QTime provides a null
+// representation so allow NULL values by default.
+//
+#pragma db value(QTime) type("TIME") null
+
+// Map QDateTime to PostgreSQL TIMESTAMP by default. QDateTime provides a null
+// representation so allow NULL values by default.
+//
+#pragma db value(QDateTime) type("TIMESTAMP") null
+
+#endif // ODB_QT_DATE_TIME_PGSQL_DEFAULT_MAPPING_HXX
diff --git a/libodb-qt/odb/qt/date-time/pgsql/qdate-time-traits.hxx b/libodb-qt/odb/qt/date-time/pgsql/qdate-time-traits.hxx
new file mode 100644
index 0000000..a9fe757
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/pgsql/qdate-time-traits.hxx
@@ -0,0 +1,70 @@
+// file : odb/qt/date-time/pgsql/qdatetime-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_PGSQL_QDATETIME_TRAITS_HXX
+#define ODB_QT_DATE_TIME_PGSQL_QDATETIME_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QDateTime>
+
+#include <odb/pgsql/traits.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ // Implementation of mapping between QDateTime and PostgreSQL TIMESTAMP.
+ // TIMESTAMP values are stored as micro-seconds since the PostgreSQL
+ // epoch 2000-01-01.
+ //
+ template <>
+ struct default_value_traits<QDateTime, id_timestamp>
+ {
+ typedef details::endian_traits endian_traits;
+
+ typedef QDateTime value_type;
+ typedef QDateTime query_type;
+ typedef long long image_type;
+
+ static void
+ set_value (QDateTime& v, long long i, bool is_null)
+ {
+ if (is_null)
+ // Default constructor creates a null QDateTime.
+ //
+ v = QDateTime ();
+ else
+ {
+ const QDateTime pg_epoch (QDate (2000, 1, 1), QTime (0, 0, 0));
+ v = pg_epoch.addMSecs (
+ static_cast <qint64> (endian_traits::ntoh (i) / 1000LL));
+ }
+ }
+
+ static void
+ set_image (long long& i, bool& is_null, const QDateTime& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+ const QDateTime pg_epoch (QDate (2000, 1, 1), QTime (0, 0, 0));
+ i = endian_traits::hton (
+ static_cast<long long> (pg_epoch.msecsTo (v)) * 1000LL);
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QDateTime>
+ {
+ static const database_type_id db_type_id = id_timestamp;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_DATE_TIME_PGSQL_QDATETIME_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/date-time/pgsql/qdate-traits.hxx b/libodb-qt/odb/qt/date-time/pgsql/qdate-traits.hxx
new file mode 100644
index 0000000..b7cc7d4
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/pgsql/qdate-traits.hxx
@@ -0,0 +1,70 @@
+// file : odb/qt/date-time/pgsql/qdate-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_PGSQL_QDATE_TRAITS_HXX
+#define ODB_QT_DATE_TIME_PGSQL_QDATE_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QDate>
+
+#include <odb/pgsql/traits.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ // Implementation of the mapping between QDate and PostgreSQL DATE. The
+ // DATE values are stored as days since the PostgreSQL epoch 2000-01-01.
+ //
+ template <>
+ struct default_value_traits<QDate, id_date>
+ {
+ typedef details::endian_traits endian_traits;
+
+ typedef QDate value_type;
+ typedef QDate query_type;
+ typedef int image_type;
+
+ static void
+ set_value (QDate& v, int i, bool is_null)
+ {
+ if (is_null)
+ // A null QDate value is equivalent to an invalid QDate value.
+ // Set v to an invalid date to represent null.
+ //
+ v.setDate (0, 0, 0);
+ else
+ {
+ const QDate pg_epoch (2000, 1, 1);
+ v = pg_epoch.addDays (endian_traits::ntoh (i));
+ }
+ }
+
+ static void
+ set_image (int& i, bool& is_null, const QDate& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+ const QDate pg_epoch (2000, 1, 1);
+ // In Qt5 daysTo() returns qint64.
+ //
+ i = endian_traits::hton (static_cast<int> (pg_epoch.daysTo (v)));
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QDate>
+ {
+ static const database_type_id db_type_id = id_date;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_DATE_TIME_PGSQL_QDATE_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/date-time/pgsql/qtime-traits.hxx b/libodb-qt/odb/qt/date-time/pgsql/qtime-traits.hxx
new file mode 100644
index 0000000..86a594b
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/pgsql/qtime-traits.hxx
@@ -0,0 +1,73 @@
+// file : odb/qt/date-time/pgsql/qtime-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_PGSQL_QTIME_TRAITS_HXX
+#define ODB_QT_DATE_TIME_PGSQL_QTIME_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QTime>
+
+#include <odb/pgsql/traits.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ // Implementation of the mapping between QTime and PostgreSQL TIME. The
+ // TIME values are stored as micro-seconds since 00:00:00.
+ //
+ template <>
+ struct default_value_traits<QTime, id_time>
+ {
+ typedef details::endian_traits endian_traits;
+
+ typedef QTime value_type;
+ typedef QTime query_type;
+ typedef long long image_type;
+
+ static void
+ set_value (QTime& v, long long i, bool is_null)
+ {
+ if (is_null)
+ // A null QTime value is equivalent to an invalid QTime value.
+ // Set v to an invalid time to represent null (hour value of
+ // a valid time must be in the range 0-23).
+ //
+ v.setHMS (24, 0, 0);
+ else
+ {
+ const QTime base (0, 0, 0);
+
+ v = base.addMSecs (
+ static_cast<int> (endian_traits::ntoh (i) / 1000));
+ }
+ }
+
+ static void
+ set_image (long long& i, bool& is_null, const QTime& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+ const QTime base (0, 0, 0);
+
+ i = endian_traits::hton (
+ static_cast<long long> (base.msecsTo (v)) * 1000);
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QTime>
+ {
+ static const database_type_id db_type_id = id_time;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_DATE_TIME_PGSQL_QTIME_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/date-time/sqlite/default-mapping.hxx b/libodb-qt/odb/qt/date-time/sqlite/default-mapping.hxx
new file mode 100644
index 0000000..a150ca9
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/sqlite/default-mapping.hxx
@@ -0,0 +1,26 @@
+// file : odb/qt/date-time/sqlite/default-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_SQLITE_DEFAULT_MAPPING_HXX
+#define ODB_QT_DATE_TIME_SQLITE_DEFAULT_MAPPING_HXX
+
+#include <QtCore/QDate>
+#include <QtCore/QTime>
+#include <QtCore/QDateTime>
+
+// Map QDate to SQLite TEXT by default. QDate provides a null representation
+// so allow NULL values by default.
+//
+#pragma db value(QDate) type("TEXT") null
+
+// Map QTime to SQLite TEXT by default. QTime provides a null representation
+// so allow NULL values by default.
+//
+#pragma db value(QTime) type("TEXT") null
+
+// Map QDateTime to SQLite TEXT by default. QDateTime provides a null
+// representation so allow NULL values by default.
+//
+#pragma db value(QDateTime) type("TEXT") null
+
+#endif // ODB_QT_DATE_TIME_SQLITE_DEFAULT_MAPPING_HXX
diff --git a/libodb-qt/odb/qt/date-time/sqlite/qdate-time-traits.hxx b/libodb-qt/odb/qt/date-time/sqlite/qdate-time-traits.hxx
new file mode 100644
index 0000000..db561fc
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/sqlite/qdate-time-traits.hxx
@@ -0,0 +1,138 @@
+// file : odb/qt/date-time/sqlite/qdatetime-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_SQLITE_QDATETIME_TRAITS_HXX
+#define ODB_QT_DATE_TIME_SQLITE_QDATETIME_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+#include <cstring> // std::memcpy
+
+#include <QtCore/QtGlobal> // QT_VERSION
+
+#include <QtCore/QDateTime>
+
+#include <odb/details/buffer.hxx>
+#include <odb/sqlite/traits.hxx>
+#include <odb/qt/date-time/exceptions.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <>
+ struct default_value_traits<QDateTime, id_text>
+ {
+ typedef QDateTime value_type;
+ typedef QDateTime query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (QDateTime& v,
+ const details::buffer& i,
+ std::size_t n,
+ bool is_null)
+ {
+ if (is_null)
+ // Default constructor creates a null QDateTime.
+ //
+ v = QDateTime ();
+ else
+ v = QDateTime::fromString (
+ QString::fromLatin1 (i.data (), static_cast<int> (n)),
+ "yyyy-MM-ddTHH:mm:ss.zzz");
+ }
+
+ static void
+ set_image (details::buffer& i,
+ std::size_t& n,
+ bool& is_null,
+ const QDateTime& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+
+ // Cannot use toStdString() here since Qt could have been
+ // configured without the STL compatibility support.
+ //
+ std::string s (
+ v.toString ("yyyy-MM-ddTHH:mm:ss.zzz").toLatin1 ().constData ());
+
+ n = s.size ();
+ if (n > i.capacity ())
+ i.capacity (n);
+
+ std::memcpy (i.data (), s.data (), n);
+ }
+ }
+ };
+
+ // Implementation of mapping between QDateTime and SQLite INTEGER.
+ // The integer value represents UNIX time.
+ //
+ template <>
+ struct default_value_traits<QDateTime, id_integer>
+ {
+ typedef QDateTime value_type;
+ typedef QDateTime query_type;
+ typedef long long image_type;
+
+ static void
+ set_value (QDateTime& v, long long i, bool is_null)
+ {
+ if (is_null)
+ // Default constructor creates a null QDateTime.
+ //
+ v = QDateTime ();
+ else
+ {
+ v.setTimeSpec (Qt::UTC);
+
+ // *Time_t() functions are deprecated in favor of *SecsSinceEpoch().
+ //
+#if QT_VERSION < 0x060000
+ v.setTime_t (static_cast<uint> (i));
+#else
+ v.setSecsSinceEpoch (static_cast<qint64> (i));
+#endif
+ }
+ }
+
+ static void
+ set_image (long long& i, bool& is_null, const QDateTime& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else if (v < QDateTime (QDate (1970, 1, 1),
+ QTime (0, 0, 0),
+ Qt::UTC))
+ throw odb::qt::date_time::value_out_of_range ();
+ else
+ {
+ is_null = false;
+
+#if QT_VERSION < 0x060000
+ i = static_cast<long long> (v.toTime_t ());
+#else
+ i = static_cast<long long> (v.toSecsSinceEpoch ());
+#endif
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QDateTime>
+ {
+ static const database_type_id db_type_id = id_text;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_DATE_TIME_SQLITE_QDATETIME_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/date-time/sqlite/qdate-traits.hxx b/libodb-qt/odb/qt/date-time/sqlite/qdate-traits.hxx
new file mode 100644
index 0000000..52721b7
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/sqlite/qdate-traits.hxx
@@ -0,0 +1,141 @@
+// file : odb/qt/date-time/sqlite/qdate-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_SQLITE_QDATE_TRAITS_HXX
+#define ODB_QT_DATE_TIME_SQLITE_QDATE_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+#include <cstring> // std::memcpy
+
+#include <QtCore/QtGlobal> // QT_VERSION
+
+#include <QtCore/QDate>
+#include <QtCore/QDateTime>
+
+#include <odb/details/buffer.hxx>
+#include <odb/sqlite/traits.hxx>
+#include <odb/qt/date-time/exceptions.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <>
+ struct default_value_traits<QDate, id_text>
+ {
+ typedef QDate value_type;
+ typedef QDate query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (QDate& v,
+ const details::buffer& i,
+ std::size_t n,
+ bool is_null)
+ {
+ if (is_null)
+ // A null QDate value is equivalent to an invalid QDate value.
+ // Set v to an invalid date to represent null.
+ //
+ v.setDate (0, 0, 0);
+ else
+ v = QDate::fromString (
+ QString::fromLatin1 (i.data (), static_cast<int> (n)),
+ "yyyy-MM-dd");
+ }
+
+ static void
+ set_image (details::buffer& i,
+ std::size_t& n,
+ bool& is_null,
+ const QDate& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+
+ // Cannot use toStdString() here since Qt could have been
+ // configured without the STL compatibility support.
+ //
+ std::string s (v.toString ("yyyy-MM-dd").toLatin1 ().constData ());
+
+ n = s.size ();
+ if (n > i.capacity ())
+ i.capacity (n);
+
+ std::memcpy (i.data (), s.data (), n);
+ }
+ }
+ };
+
+ // Implementation of the mapping between QDate and SQLite INTEGER. The
+ // integer value represents UNIX time.
+ //
+ template <>
+ struct default_value_traits<QDate, id_integer>
+ {
+ typedef QDate value_type;
+ typedef QDate query_type;
+ typedef long long image_type;
+
+ static void
+ set_value (QDate& v, long long i, bool is_null)
+ {
+ if (is_null)
+ // A null QDate value is equivalent to an invalid QDate value.
+ // Set v to an invalid date to represent null.
+ //
+ v.setDate (0, 0, 0);
+ else
+ {
+ QDateTime dt;
+ dt.setTimeSpec (Qt::UTC);
+
+ // *Time_t() functions are deprecated in favor of *SecsSinceEpoch().
+ //
+#if QT_VERSION < 0x060000
+ dt.setTime_t (static_cast<uint> (i));
+#else
+ dt.setSecsSinceEpoch (static_cast<qint64> (i));
+#endif
+ v = dt.date ();
+ }
+ }
+
+ static void
+ set_image (long long& i, bool& is_null, const QDate& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else if (v < QDate (1970, 1, 1))
+ throw odb::qt::date_time::value_out_of_range ();
+ else
+ {
+ is_null = false;
+ const QDateTime dt (v, QTime (0, 0, 0), Qt::UTC);
+
+#if QT_VERSION < 0x060000
+ i = static_cast<long long> (dt.toTime_t ());
+#else
+ i = static_cast<long long> (dt.toSecsSinceEpoch ());
+#endif
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QDate>
+ {
+ static const database_type_id db_type_id = id_text;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_DATE_TIME_SQLITE_QDATE_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/date-time/sqlite/qtime-traits.hxx b/libodb-qt/odb/qt/date-time/sqlite/qtime-traits.hxx
new file mode 100644
index 0000000..dd86399
--- /dev/null
+++ b/libodb-qt/odb/qt/date-time/sqlite/qtime-traits.hxx
@@ -0,0 +1,120 @@
+// file : odb/qt/date-time/sqlite/qtime-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DATE_TIME_SQLITE_QTIME_TRAITS_HXX
+#define ODB_QT_DATE_TIME_SQLITE_QTIME_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+#include <cstring> // std::memcpy
+
+#include <QtCore/QTime>
+
+#include <odb/details/buffer.hxx>
+#include <odb/sqlite/traits.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <>
+ struct default_value_traits<QTime, id_text>
+ {
+ typedef QTime value_type;
+ typedef QTime query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (QTime& v,
+ const details::buffer& i,
+ std::size_t n,
+ bool is_null)
+ {
+ if (is_null)
+ // A null QTime value is equivalent to an invalid QTime value.
+ // Set v to an invalid time to represent null (hour value of
+ // a valid time must be in the range 0-23).
+ //
+ v.setHMS (24, 0, 0);
+ else
+ v = QTime::fromString (
+ QString::fromLatin1 (i.data (), static_cast<int> (n)),
+ "HH:mm:ss.zzz");
+ }
+
+ static void
+ set_image (details::buffer& i,
+ std::size_t& n,
+ bool& is_null,
+ const QTime& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+
+ // Cannot use toStdString() here since Qt could have been
+ // configured without the STL compatibility support.
+ //
+ std::string s (
+ v.toString ("HH:mm:ss.zzz").toLatin1 ().constData ());
+
+ n = s.size ();
+ if (n > i.capacity ())
+ i.capacity (n);
+
+ std::memcpy (i.data (), s.data (), n);
+ }
+ }
+ };
+
+ // Implementation of mapping between QTime and SQLite INTEGER. The
+ // integer value represents seconds since midnight.
+ //
+ template <>
+ struct default_value_traits<QTime, id_integer>
+ {
+ typedef QTime value_type;
+ typedef QTime query_type;
+ typedef long long image_type;
+
+ static void
+ set_value (QTime& v, long long i, bool is_null)
+ {
+ if (is_null)
+ // A null QTime value is equivalent to an invalid QTime value.
+ // Set v to an invalid time to represent null (hour value of
+ // a valid time must be in the range 0-23).
+ //
+ v.setHMS (24, 0, 0);
+ else
+ v = QTime (0, 0, 0).addSecs (static_cast<int> (i));
+ }
+
+ static void
+ set_image (long long& i, bool& is_null, const QTime& v)
+ {
+ if (v.isNull ())
+ is_null = true;
+ else
+ {
+ is_null = false;
+ i = static_cast<long long> (QTime (0, 0, 0).secsTo (v));
+ }
+ }
+ };
+
+ template <>
+ struct default_type_traits<QTime>
+ {
+ static const database_type_id db_type_id = id_text;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_DATE_TIME_SQLITE_QTIME_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/details/config.hxx b/libodb-qt/odb/qt/details/config.hxx
new file mode 100644
index 0000000..01a4eb5
--- /dev/null
+++ b/libodb-qt/odb/qt/details/config.hxx
@@ -0,0 +1,32 @@
+// file : odb/qt/details/config.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DETAILS_CONFIG_HXX
+#define ODB_QT_DETAILS_CONFIG_HXX
+
+// no pre
+
+// Qt5 may complain if we are building without -fPIC. Instead of asking the
+// user to pass one of these options to the ODB compiler (which can, BTW, be
+// done with -x -fPIC, for example, if one is not using the Qt profile), we
+// are going to define __PIC__ ourselves just to silence Qt. We also want to
+// try to minimize this to cases where it is actually necessary. To achieve
+// this, we need to include the Qt config file without including <QtGlobal>,
+// which is where the test for PIE is. While newer versions of Qt (from 4.7)
+// have <QtConfig>, to support older versions we will include qconfig.h
+// directly. This file appears to be present in all the versions starting with
+// Qt 4.0.
+//
+#ifdef ODB_COMPILER
+# if defined(__ELF__) && !defined(__PIC__)
+# include <QtCore/qconfig.h> // QT_REDUCE_RELOCATIONS
+# ifdef QT_REDUCE_RELOCATIONS
+# define __PIC__
+# endif
+# endif
+# define LIBODB_QT_STATIC
+#endif
+
+// no post
+
+#endif // ODB_QT_DETAILS_CONFIG_HXX
diff --git a/libodb-qt/odb/qt/details/export.hxx b/libodb-qt/odb/qt/details/export.hxx
new file mode 100644
index 0000000..f38d0ee
--- /dev/null
+++ b/libodb-qt/odb/qt/details/export.hxx
@@ -0,0 +1,46 @@
+// file : odb/qt/details/export.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_DETAILS_EXPORT_HXX
+#define ODB_QT_DETAILS_EXPORT_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/qt/details/config.hxx> // LIBODB_QT_STATIC if ODB_COMPILER
+
+// Normally we don't export class templates (but do complete specializations),
+// inline functions, and classes with only inline member functions. Exporting
+// classes that inherit from non-exported/imported bases (e.g., std::string)
+// will end up badly. The only known workarounds are to not inherit or to not
+// export. Also, MinGW GCC doesn't like seeing non-exported function being
+// used before their inline definition. The workaround is to reorder code. In
+// the end it's all trial and error.
+
+#if defined(LIBODB_QT_STATIC) // Using static.
+# define LIBODB_QT_EXPORT
+#elif defined(LIBODB_QT_STATIC_BUILD) // Building static.
+# define LIBODB_QT_EXPORT
+#elif defined(LIBODB_QT_SHARED) // Using shared.
+# ifdef _WIN32
+# define LIBODB_QT_EXPORT __declspec(dllimport)
+# else
+# define LIBODB_QT_EXPORT
+# endif
+#elif defined(LIBODB_QT_SHARED_BUILD) // Building shared.
+# ifdef _WIN32
+# define LIBODB_QT_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_QT_EXPORT
+# endif
+#else
+// If none of the above macros are defined, then we assume we are being used
+// by some third-party build system that cannot/doesn't signal the library
+// type. Note that this fallback works for both static and shared but in case
+// of shared will be sub-optimal compared to having dllimport.
+//
+# define LIBODB_QT_EXPORT // Using static or shared.
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_DETAILS_EXPORT_HXX
diff --git a/libodb-qt/odb/qt/exception.hxx b/libodb-qt/odb/qt/exception.hxx
new file mode 100644
index 0000000..44646d5
--- /dev/null
+++ b/libodb-qt/odb/qt/exception.hxx
@@ -0,0 +1,28 @@
+// file : odb/qt/exception.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_EXCEPTION_HXX
+#define ODB_QT_EXCEPTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/exceptions.hxx>
+
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+#include <odb/qt/details/export.hxx>
+
+namespace odb
+{
+ namespace qt
+ {
+ struct LIBODB_QT_EXPORT exception: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT = 0;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_EXCEPTION_HXX
diff --git a/libodb-qt/odb/qt/lazy-ptr.hxx b/libodb-qt/odb/qt/lazy-ptr.hxx
new file mode 100644
index 0000000..6e5a3e0
--- /dev/null
+++ b/libodb-qt/odb/qt/lazy-ptr.hxx
@@ -0,0 +1,9 @@
+// file : odb/qt/lazy-ptr.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_LAZY_PTR_HXX
+#define ODB_QT_LAZY_PTR_HXX
+
+#include <odb/qt/smart-ptr/lazy-ptr.hxx>
+
+#endif // ODB_QT_LAZY_PTR_HXX
diff --git a/libodb-qt/odb/qt/list-iterator.hxx b/libodb-qt/odb/qt/list-iterator.hxx
new file mode 100644
index 0000000..bb8b20c
--- /dev/null
+++ b/libodb-qt/odb/qt/list-iterator.hxx
@@ -0,0 +1,9 @@
+// file : odb/qt/list-iterator.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_LIST_ITERATOR_HXX
+#define ODB_QT_LIST_ITERATOR_HXX
+
+#include <odb/qt/containers/list-iterator.hxx>
+
+#endif // ODB_QT_LIST_ITERATOR_HXX
diff --git a/libodb-qt/odb/qt/list.hxx b/libodb-qt/odb/qt/list.hxx
new file mode 100644
index 0000000..7918ef6
--- /dev/null
+++ b/libodb-qt/odb/qt/list.hxx
@@ -0,0 +1,9 @@
+// file : odb/qt/list.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_LIST_HXX
+#define ODB_QT_LIST_HXX
+
+#include <odb/qt/containers/list.hxx>
+
+#endif // ODB_QT_LIST_HXX
diff --git a/libodb-qt/odb/qt/mutable-list-iterator.hxx b/libodb-qt/odb/qt/mutable-list-iterator.hxx
new file mode 100644
index 0000000..045ae99
--- /dev/null
+++ b/libodb-qt/odb/qt/mutable-list-iterator.hxx
@@ -0,0 +1,9 @@
+// file : odb/qt/mutable-list-iterator.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_MUTABLE_LIST_ITERATOR_HXX
+#define ODB_QT_MUTABLE_LIST_ITERATOR_HXX
+
+#include <odb/qt/containers/mutable-list-iterator.hxx>
+
+#endif // ODB_QT_MUTABLE_LIST_ITERATOR_HXX
diff --git a/libodb-qt/odb/qt/smart-ptr.options b/libodb-qt/odb/qt/smart-ptr.options
new file mode 100644
index 0000000..b95b39b
--- /dev/null
+++ b/libodb-qt/odb/qt/smart-ptr.options
@@ -0,0 +1,19 @@
+# file : odb/qt/smart-ptr.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+--profile qt/version
+
+# Make QSharedPointer the default object pointer.
+#
+--hxx-prologue '#include <QtCore/QSharedPointer>'
+--default-pointer QSharedPointer
+
+# Include pointer traits.
+#
+--odb-epilogue '#include <odb/qt/smart-ptr/pointer-traits.hxx>'
+--hxx-prologue '#include <odb/qt/smart-ptr/pointer-traits.hxx>'
+
+# Include wrapper traits.
+#
+--odb-epilogue '#include <odb/qt/smart-ptr/wrapper-traits.hxx>'
+--hxx-prologue '#include <odb/qt/smart-ptr/wrapper-traits.hxx>'
diff --git a/libodb-qt/odb/qt/smart-ptr/lazy-pointer-traits.hxx b/libodb-qt/odb/qt/smart-ptr/lazy-pointer-traits.hxx
new file mode 100644
index 0000000..6c7aa38
--- /dev/null
+++ b/libodb-qt/odb/qt/smart-ptr/lazy-pointer-traits.hxx
@@ -0,0 +1,61 @@
+// file : odb/qt/smart-ptr/lazy-pointer-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_SMART_PTR_LAZY_POINTER_TRAITS_HXX
+#define ODB_QT_SMART_PTR_LAZY_POINTER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/pointer-traits.hxx>
+#include <odb/qt/smart-ptr/lazy-ptr.hxx>
+
+namespace odb
+{
+ template <typename T>
+ class pointer_traits<QLazySharedPointer<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_shared;
+ static const bool lazy = true;
+
+ typedef T element_type;
+ typedef QLazySharedPointer<element_type> pointer_type;
+ typedef QSharedPointer<element_type> eager_pointer_type;
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ template <class O /* = T */>
+ static typename object_traits<O>::id_type
+ object_id (const pointer_type& p)
+ {
+ return p.template objectId<O> ();
+ }
+ };
+
+ template <typename T>
+ class pointer_traits<QLazyWeakPointer<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_weak;
+ static const bool lazy = true;
+
+ typedef T element_type;
+ typedef QLazyWeakPointer<element_type> pointer_type;
+ typedef QLazySharedPointer<element_type> strong_pointer_type;
+ typedef QWeakPointer<element_type> eager_pointer_type;
+
+ static strong_pointer_type
+ lock (const pointer_type& p)
+ {
+ return p.toStrongRef ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_SMART_PTR_LAZY_POINTER_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/smart-ptr/lazy-ptr.hxx b/libodb-qt/odb/qt/smart-ptr/lazy-ptr.hxx
new file mode 100644
index 0000000..865e355
--- /dev/null
+++ b/libodb-qt/odb/qt/smart-ptr/lazy-ptr.hxx
@@ -0,0 +1,442 @@
+// file : odb/qt/smart-ptr/lazy-ptr.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_SMART_PTR_LAZY_PTR_HXX
+#define ODB_QT_SMART_PTR_LAZY_PTR_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QSharedPointer>
+#include <QtCore/QWeakPointer>
+#include <QtCore/QGlobalStatic>
+
+#include <odb/forward.hxx> // odb::database
+#include <odb/traits.hxx>
+#include <odb/lazy-ptr-impl.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+template <class T>
+class QLazyWeakPointer;
+
+//
+// QLazySharedPointer declaration.
+//
+
+template <class T>
+class QLazySharedPointer
+{
+ // The standard QSharedPointer interface.
+ //
+public:
+ // These typedefs are inherited by QSharedPointer indirectly from
+ // QtSharedPointer::Basic<T>
+ //
+ typedef T Type;
+ typedef T element_type;
+ typedef T value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef qptrdiff difference_type;
+
+ QLazySharedPointer ();
+
+ explicit
+ QLazySharedPointer (T*);
+
+ template <class Deleter>
+ QLazySharedPointer (T*, Deleter);
+
+ QLazySharedPointer (const QLazySharedPointer&);
+
+ template <class X>
+ QLazySharedPointer (const QLazySharedPointer<X>&);
+
+ template <class X>
+ QLazySharedPointer (const QLazyWeakPointer<X>&);
+
+ ~QLazySharedPointer ();
+
+ QLazySharedPointer&
+ operator= (const QLazySharedPointer&);
+
+ template <class X>
+ QLazySharedPointer&
+ operator= (const QLazySharedPointer<X>&);
+
+ template <class X>
+ QLazySharedPointer&
+ operator= (const QLazyWeakPointer<X>&);
+
+ typedef QSharedPointer<T> QLazySharedPointer<T>::*unspecified_bool_type;
+ operator unspecified_bool_type () const
+ {
+ return isNull () ? 0 : &QLazySharedPointer::p_;
+ }
+
+ bool
+ operator! () const;
+
+ T&
+ operator* () const;
+
+ T*
+ operator-> () const;
+
+ void
+ swap (QLazySharedPointer&);
+
+ bool
+ isNull () const;
+
+ T*
+ data () const;
+
+ QLazyWeakPointer<T>
+ toWeakRef () const;
+
+ void
+ clear ();
+
+ template <class X>
+ QLazySharedPointer<X>
+ staticCast () const;
+
+ template <class X>
+ QLazySharedPointer<X>
+ dynamicCast () const;
+
+ template <class X>
+ QLazySharedPointer<X>
+ constCast () const;
+
+ // Initialization and assignment from QSharedPointer and QWeakPointer.
+ //
+public:
+ template <class X>
+ QLazySharedPointer (const QSharedPointer<X>&);
+
+ template <class X>
+ explicit
+ QLazySharedPointer (const QWeakPointer<X>&);
+
+ template <class X>
+ QLazySharedPointer&
+ operator= (const QSharedPointer<X>&);
+
+ template <class X>
+ QLazySharedPointer&
+ operator= (const QWeakPointer<X>&);
+
+ // Lazy loading interface.
+ //
+public:
+ typedef odb::database database_type;
+
+ // isNull() 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;
+
+ QSharedPointer<T>
+ load () const;
+
+ // Unload the pointer. For transient objects this function is
+ // equivalent to clear().
+ //
+ 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.
+ //
+ QSharedPointer<T>
+ getEager () const;
+
+ template <class DB, class ID>
+ QLazySharedPointer (DB&, const ID&);
+
+ template <class DB>
+ QLazySharedPointer (DB&, T*);
+
+ template <class DB, class Deleter>
+ QLazySharedPointer (DB&, T*, Deleter);
+
+ template <class DB, class X>
+ QLazySharedPointer (DB&, const QSharedPointer<X>&);
+
+ template <class DB, class X>
+ QLazySharedPointer (DB&, const QWeakPointer<X>&);
+
+#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+ template <class O = T>
+#else
+ template <class O /* = T */>
+#endif
+ typename odb::object_traits<O>::id_type
+ objectId () const;
+
+ database_type&
+ database () const;
+
+ // Helpers.
+ //
+public:
+ template <class X>
+ bool
+ equal (const QLazySharedPointer<X>&) const;
+
+private:
+ template <class X> friend class QLazySharedPointer;
+ template <class X> friend class QLazyWeakPointer;
+
+ // For QLazyWeakPointer::toStrongRef().
+ //
+ QLazySharedPointer (const QSharedPointer<T>& p,
+ const odb::lazy_ptr_impl<T>& i)
+ : p_ (p), i_ (i) {}
+
+private:
+ mutable QSharedPointer<T> p_;
+ mutable odb::lazy_ptr_impl<T> i_;
+};
+
+//
+// QLazySharedPointer related non-members.
+//
+
+template <class T, class X>
+QLazySharedPointer<X>
+qSharedPointerCast (const QLazySharedPointer<T>&);
+
+template <class T, class X>
+QLazySharedPointer<X>
+qSharedPointerConstCast (const QLazySharedPointer<T>&);
+
+template <class T, class X>
+QLazySharedPointer<X>
+qSharedPointerDynamicCast (const QLazySharedPointer<T>&);
+
+template <class T, class X>
+bool
+operator== (const QLazySharedPointer<T>&, const QLazySharedPointer<X>&);
+
+template <class T, class X>
+bool
+operator!= (const QLazySharedPointer<T>&, const QLazySharedPointer<T>&);
+
+//
+// QLazyWeakPointer declaration
+//
+
+template <class T>
+class QLazyWeakPointer
+{
+ // The standard QWeakPointer interface
+ //
+public:
+ typedef T element_type;
+ typedef T value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef qptrdiff difference_type;
+
+ QLazyWeakPointer ();
+
+ QLazyWeakPointer (const QLazyWeakPointer&);
+
+ template <class X>
+ QLazyWeakPointer (const QLazyWeakPointer<X>&);
+
+ template <class X>
+ QLazyWeakPointer (const QLazySharedPointer<X>&);
+
+ ~QLazyWeakPointer ();
+
+ QLazyWeakPointer&
+ operator= (const QLazyWeakPointer&);
+
+ template <class X>
+ QLazyWeakPointer&
+ operator= (const QLazyWeakPointer<X>&);
+
+ template <class X>
+ QLazyWeakPointer&
+ operator= (const QLazySharedPointer<X>&);
+
+ typedef QWeakPointer<T> QLazyWeakPointer<T>::*unspecified_bool_type;
+ operator unspecified_bool_type () const
+ {
+ return isNull () ? 0 : &QLazyWeakPointer<T>::p_;
+ }
+
+ bool
+ operator! () const;
+
+#ifdef QWEAKPOINTER_ENABLE_ARROW
+ T*
+ operator-> () const;
+#endif
+
+ void
+ clear ();
+
+ T*
+ data () const;
+
+ bool
+ isNull () const;
+
+ QLazySharedPointer<T>
+ toStrongRef () const;
+
+ // Initialization/assignment from QSharedPointer and QWeakPointer.
+ //
+public:
+ template <class X>
+ QLazyWeakPointer (const QWeakPointer<X>&);
+
+ template <class X>
+ QLazyWeakPointer (const QSharedPointer<X>&);
+
+ template <class X>
+ QLazyWeakPointer&
+ operator= (const QWeakPointer<X>&);
+
+ template <class X>
+ QLazyWeakPointer&
+ operator= (const QSharedPointer<X>&);
+
+ // Lazy loading interface.
+ //
+public:
+ typedef odb::database database_type;
+
+ // toStrongRef().isNull() loaded()
+ //
+ // true true expired pointer to transient object
+ // false true valid pointer to persistent object
+ // true false expired pointer to persistent object
+ // false false valid pointer to transient object
+ //
+ bool
+ loaded () const;
+
+ // Create a strong reference using toStrongRef() and load.
+ //
+ QSharedPointer<T>
+ load () const;
+
+ // Unload the pointer. For transient objects this function is equivalent
+ // to clear().
+ //
+ 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.
+ //
+ QWeakPointer<T>
+ getEager () const;
+
+ template <class DB, class ID>
+ QLazyWeakPointer (DB&, const ID&);
+
+ template <class DB, class X>
+ QLazyWeakPointer (DB&, const QSharedPointer<X>&);
+
+ template <class DB, class X>
+ QLazyWeakPointer (DB&, const QWeakPointer<X>&);
+
+ // The objectId() function can only be called when the object is persistent,
+ // or: toStrongRef().isNull() XOR loaded() (can use != for XOR).
+ //
+#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+ template <class O = T>
+#else
+ template <class O /* = T */>
+#endif
+ typename odb::object_traits<O>::id_type
+ objectId () const;
+
+ database_type&
+ database () const;
+
+ // Helpers.
+ //
+public:
+ template <class X>
+ bool
+ equal (const QLazyWeakPointer<X>&) const;
+
+ template <class X>
+ bool
+ equal (const QLazySharedPointer<X>&) const;
+
+private:
+ template <class X> friend class QLazySharedPointer;
+ template <class X> friend class QLazyWeakPointer;
+
+ mutable QWeakPointer<T> p_;
+ mutable odb::lazy_ptr_impl<T> i_;
+};
+
+//
+// QLazyWeakPointer related non-members.
+//
+
+template <class T, class X>
+QLazySharedPointer<X>
+qSharedPointerCast (const QLazyWeakPointer<T>&);
+
+template <class T, class X>
+QLazySharedPointer<X>
+qSharedPointerConstCast (const QLazyWeakPointer<T>&);
+
+template <class T, class X>
+QLazySharedPointer<X>
+qSharedPointerDynamicCast (const QLazyWeakPointer<T>&);
+
+template <class T, class X>
+QLazyWeakPointer<X>
+qWeakPointerCast (const QLazyWeakPointer<T>&);
+
+template <class T, class X>
+bool
+operator== (const QLazyWeakPointer<T>&, const QLazyWeakPointer<X>&);
+
+template <class T, class X>
+bool
+operator== (const QLazyWeakPointer<T>&, const QLazySharedPointer<X>&);
+
+template <class T, class X>
+bool
+operator== (const QLazySharedPointer<T>&, const QLazyWeakPointer<X>&);
+
+template <class T, class X>
+bool
+operator!= (const QLazyWeakPointer<T>&, const QLazyWeakPointer<X>&);
+
+template <class T, class X>
+bool
+operator!= (const QLazyWeakPointer<T>&, const QLazySharedPointer<X>&);
+
+template <class T, class X>
+bool
+operator!= (const QLazySharedPointer<T>&, const QLazyWeakPointer<X>&);
+
+#include <odb/qt/smart-ptr/lazy-ptr.ixx>
+#include <odb/qt/smart-ptr/lazy-ptr.txx>
+
+#include <odb/qt/smart-ptr/lazy-pointer-traits.hxx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_SMART_PTR_LAZY_PTR_HXX
diff --git a/libodb-qt/odb/qt/smart-ptr/lazy-ptr.ixx b/libodb-qt/odb/qt/smart-ptr/lazy-ptr.ixx
new file mode 100644
index 0000000..b9e6de8
--- /dev/null
+++ b/libodb-qt/odb/qt/smart-ptr/lazy-ptr.ixx
@@ -0,0 +1,626 @@
+// file : odb/qt/smart-ptr/lazy-ptr.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+//
+// QLazySharedPointer definition.
+//
+
+template <class T>
+inline QLazySharedPointer<T>::
+QLazySharedPointer () {}
+
+template <class T>
+inline QLazySharedPointer<T>::
+QLazySharedPointer (T* p): p_ (p) {}
+
+template <class T>
+template <class Deleter>
+inline QLazySharedPointer<T>::
+QLazySharedPointer (T* p, Deleter d): p_ (p, d) {}
+
+template <class T>
+inline QLazySharedPointer<T>::
+QLazySharedPointer (const QLazySharedPointer& r): p_ (r.p_), i_ (r.i_) {}
+
+template <class T>
+template <class X>
+inline QLazySharedPointer<T>::
+QLazySharedPointer (const QLazySharedPointer<X>& r): p_ (r.p_), i_ (r.i_) {}
+
+template <class T>
+template <class X>
+inline QLazySharedPointer<T>::
+QLazySharedPointer (const QLazyWeakPointer<X>& r): p_ (r.p_), i_ (r.i_) {}
+
+template <class T>
+inline QLazySharedPointer<T>::
+~QLazySharedPointer () {}
+
+template <class T>
+inline QLazySharedPointer<T>& QLazySharedPointer<T>::
+operator= (const QLazySharedPointer& r)
+{
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+}
+
+template <class T>
+template <class X>
+inline QLazySharedPointer<T>& QLazySharedPointer<T>::
+operator= (const QLazySharedPointer<X>& r)
+{
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+}
+
+template <class T>
+template <class X>
+inline QLazySharedPointer<T>& QLazySharedPointer<T>::
+operator= (const QLazyWeakPointer<X>& r)
+{
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+}
+
+template <class T>
+inline bool QLazySharedPointer<T>::
+operator! () const
+{
+ return isNull ();
+}
+
+template <class T>
+inline T& QLazySharedPointer<T>::
+operator* () const
+{
+ return *p_;
+}
+
+template <class T>
+inline T* QLazySharedPointer<T>::
+operator-> () const
+{
+ return p_.operator-> ();
+}
+
+template <class T>
+inline void QLazySharedPointer<T>::
+swap (QLazySharedPointer& x)
+{
+ p_.swap (x.p_);
+ i_.swap (x.i_);
+}
+
+template <class T>
+inline bool QLazySharedPointer<T>::
+isNull () const
+{
+ return !(p_ || i_);
+}
+
+template <class T>
+inline T* QLazySharedPointer<T>::
+data () const
+{
+ return p_.data ();
+}
+
+template <class T>
+inline QLazyWeakPointer<T> QLazySharedPointer<T>::
+toWeakRef () const
+{
+ return QLazyWeakPointer<T> (*this);
+}
+
+template <class T>
+inline void QLazySharedPointer<T>::
+clear ()
+{
+ p_.clear ();
+ i_.reset ();
+}
+
+template <class T>
+template <class X>
+inline QLazySharedPointer<X> QLazySharedPointer<T>::
+staticCast () const
+{
+ QLazySharedPointer c (p_.template staticCast<X> ());
+ c.i_ = i_;
+ return c;
+}
+
+template <class T>
+template <class X>
+inline QLazySharedPointer<X> QLazySharedPointer<T>::
+dynamicCast () const
+{
+ QLazySharedPointer<X> c (p_.template dynamicCast<X> ());
+
+ if (c)
+ c.i_ = i_;
+
+ return c;
+}
+
+template <class T>
+template <class X>
+inline QLazySharedPointer<X> QLazySharedPointer<T>::
+constCast () const
+{
+ QLazySharedPointer<X> c (p_.template constCast<X> ());
+ c.i_ = i_;
+ return c;
+}
+
+template <class T>
+template <class Y>
+inline QLazySharedPointer<T>::
+QLazySharedPointer (const QSharedPointer<Y>& r): p_ (r) {}
+
+template <class T>
+template <class Y>
+inline QLazySharedPointer<T>::
+QLazySharedPointer (const QWeakPointer<Y>& r): p_ (r) {}
+
+template <class T>
+template <class X>
+inline QLazySharedPointer<T>& QLazySharedPointer<T>::
+operator= (const QSharedPointer<X>& r)
+{
+ p_ = r;
+ i_.reset ();
+ return *this;
+}
+
+template <class T>
+template <class X>
+inline QLazySharedPointer<T>& QLazySharedPointer<T>::
+operator= (const QWeakPointer<X>& r)
+{
+ p_ = r;
+ i_.reset ();
+ return *this;
+}
+
+template <class T>
+inline bool QLazySharedPointer<T>::
+loaded () const
+{
+ bool i (i_);
+ return !p_ != i; // !p_ XOR i_
+}
+
+template <class T>
+inline QLazySharedPointer<T> QLazyWeakPointer<T>::
+toStrongRef () const
+{
+ return QLazySharedPointer<T> (p_.toStrongRef (), i_);
+}
+
+template <class T>
+inline QSharedPointer<T> QLazySharedPointer<T>::
+load () const
+{
+ if (!p_ && i_)
+ p_ = i_.template load<T> (true); // Reset id.
+
+ return p_;
+}
+
+template <class T>
+inline void QLazySharedPointer<T>::
+unload () const
+{
+ typedef typename odb::object_traits<T>::object_type object_type;
+
+ if (p_)
+ {
+ if (i_.database () != 0)
+ i_.reset_id (odb::object_traits<object_type>::id (*p_));
+
+ p_.clear ();
+ }
+}
+
+template <class T>
+inline QSharedPointer<T> QLazySharedPointer<T>::
+getEager () const
+{
+ return p_;
+}
+
+template <class T>
+template <class DB, class ID>
+inline QLazySharedPointer<T>::
+QLazySharedPointer (DB& db, const ID& id): i_ (db, id) {}
+
+template <class T>
+template <class DB>
+inline QLazySharedPointer<T>::
+QLazySharedPointer (DB& db, T* p)
+ : p_ (p)
+{
+ if (p_)
+ i_.reset_db (db);
+}
+
+template <class T>
+template <class DB, class Deleter>
+inline QLazySharedPointer<T>::
+QLazySharedPointer (DB& db, T* p, Deleter d)
+ : p_ (p, d)
+{
+ if (p_)
+ i_.reset_db (db);
+}
+
+template <class T>
+template <class DB, class Y>
+inline QLazySharedPointer<T>::
+QLazySharedPointer (DB& db, const QSharedPointer<Y>& r)
+ : p_ (r)
+{
+ if (p_)
+ i_.reset_db (db);
+}
+
+template <class T>
+template <class DB, class Y>
+inline QLazySharedPointer<T>::
+QLazySharedPointer (DB& db, const QWeakPointer<Y>& r)
+ : p_ (r)
+{
+ if (p_)
+ i_.reset_db (db);
+}
+
+template <class T>
+inline typename QLazySharedPointer<T>::database_type& QLazySharedPointer<T>::
+database () const
+{
+ return *i_.database ();
+}
+
+template <class T>
+template <class O>
+inline typename odb::object_traits<O>::id_type QLazySharedPointer<T>::
+objectId () const
+{
+ typedef typename odb::object_traits<T>::object_type object_type;
+
+ return p_
+ ? odb::object_traits<object_type>::id (*p_)
+ : i_.template object_id<O> ();
+}
+
+//
+// QLazySharedPointer related non-member function definitions.
+//
+
+template <class T, class X>
+inline QLazySharedPointer<X>
+qSharedPointerCast (const QLazySharedPointer<T>& r)
+{
+ return r.template staticCast<X> ();
+}
+
+template <class T, class X>
+inline QLazySharedPointer<X>
+qSharedPointerConstCast (const QLazySharedPointer<T>& r)
+{
+ return r.template constCast<X> ();
+}
+
+template <class T, class X>
+inline QLazySharedPointer<X>
+qSharedPointerDynamicCast (const QLazySharedPointer<T>& r)
+{
+ return r.template dynamicCast<X> ();
+}
+
+template <class T, class X>
+inline bool
+operator== (const QLazySharedPointer<T>& a, const QLazySharedPointer<X>& b)
+{
+ return a.equal (b);
+}
+
+template <class T, class X>
+inline bool
+operator!= (const QLazySharedPointer<T>& a, const QLazySharedPointer<X>& b)
+{
+ return !a.equal (b);
+}
+
+//
+// QLazyWeakPointer definition.
+//
+
+template <class T>
+inline QLazyWeakPointer<T>::
+QLazyWeakPointer () {}
+
+
+template <class T>
+inline QLazyWeakPointer<T>::
+QLazyWeakPointer (const QLazyWeakPointer& r): p_ (r.p_), i_ (r.i_) {}
+
+template <class T>
+template <class X>
+inline QLazyWeakPointer<T>::
+QLazyWeakPointer (const QLazyWeakPointer<X>& r): p_ (r.p_), i_ (r.i_) {}
+
+template <class T>
+template <class X>
+inline QLazyWeakPointer<T>::
+QLazyWeakPointer (const QLazySharedPointer<X>& r): p_ (r.p_), i_ (r.i_) {}
+
+template <class T>
+inline QLazyWeakPointer<T>::
+~QLazyWeakPointer () {}
+
+template <class T>
+inline QLazyWeakPointer<T>& QLazyWeakPointer<T>::
+operator= (const QLazyWeakPointer& r)
+{
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+}
+
+template <class T>
+template <class X>
+inline QLazyWeakPointer<T>& QLazyWeakPointer<T>::
+operator= (const QLazyWeakPointer<X>& r)
+{
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+}
+
+template <class T>
+template <class X>
+inline QLazyWeakPointer<T>& QLazyWeakPointer<T>::
+operator= (const QLazySharedPointer<X>& r)
+{
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+}
+
+template <class T>
+inline bool QLazyWeakPointer<T>::
+operator! () const
+{
+ return isNull ();
+}
+
+#ifdef QWEAKPOINTER_ENABLE_ARROW
+template <class T>
+inline T* QLazyWeakPointer<T>::
+operator-> () const
+{
+ return p_.operator-> ();
+}
+#endif
+
+template <class T>
+inline void QLazyWeakPointer<T>::
+clear ()
+{
+ p_.clear ();
+ i_.reset ();
+}
+
+template <class T>
+inline T* QLazyWeakPointer<T>::
+data () const
+{
+ return p_.data ();
+}
+
+template <class T>
+inline bool QLazyWeakPointer<T>::
+isNull () const
+{
+ return !(p_ || i_);
+}
+
+template <class T>
+template <class X>
+inline QLazyWeakPointer<T>::
+QLazyWeakPointer (const QWeakPointer<X>& r): p_ (r) {}
+
+template <class T>
+template <class X>
+inline QLazyWeakPointer<T>::
+QLazyWeakPointer (const QSharedPointer<X>& r): p_ (r) {}
+
+template <class T>
+template <class X>
+inline QLazyWeakPointer<T>& QLazyWeakPointer<T>::
+operator= (const QWeakPointer<X>& r)
+{
+ p_ = r;
+ i_.reset ();
+ return *this;
+}
+
+template <class T>
+template <class X>
+inline QLazyWeakPointer<T>& QLazyWeakPointer<T>::
+operator= (const QSharedPointer<X>& r)
+{
+ p_ = r;
+ i_.reset ();
+ return *this;
+}
+
+template <class T>
+inline bool QLazyWeakPointer<T>::
+loaded () const
+{
+ bool i (i_);
+ return p_.toStrongRef ().isNull () != i; // expired () XOR i_
+}
+
+template <class T>
+inline QSharedPointer<T> QLazyWeakPointer<T>::
+load () const
+{
+ QSharedPointer<T> r (p_.toStrongRef ());
+
+ if (!r && i_)
+ {
+ r = i_.template load<T> (false); // Keep id.
+ p_ = r;
+ }
+
+ return r;
+}
+
+template <class T>
+inline void QLazyWeakPointer<T>::
+unload () const
+{
+ // With weak pointer we always keep i_ up to date.
+ //
+ p_.clear ();
+}
+
+template <class T>
+inline QWeakPointer<T> QLazyWeakPointer<T>::
+getEager () const
+{
+ return p_;
+}
+
+template <class T>
+template <class DB, class ID>
+inline QLazyWeakPointer<T>::
+QLazyWeakPointer (DB& db, const ID& id): i_ (db, id) {}
+
+template <class T>
+template <class DB, class X>
+inline QLazyWeakPointer<T>::
+QLazyWeakPointer (DB& db, const QSharedPointer<X>& r)
+ : p_ (r)
+{
+ typedef typename odb::object_traits<T>::object_type object_type;
+
+ if (r)
+ i_.reset (db, odb::object_traits<object_type>::id (*r));
+}
+
+template <class T>
+template <class DB, class X>
+inline QLazyWeakPointer<T>::
+QLazyWeakPointer (DB& db, const QWeakPointer<X>& r)
+ : p_ (r)
+{
+ typedef typename odb::object_traits<T>::object_type object_type;
+
+ QSharedPointer<T> sp (p_.toStrongRef ());
+
+ if (sp)
+ i_.reset (db, odb::object_traits<object_type>::id (*sp));
+}
+
+template <class T>
+template <class O /* = T */>
+inline typename odb::object_traits<O>::id_type QLazyWeakPointer<T>::
+objectId () const
+{
+ typedef typename odb::object_traits<T>::object_type object_type;
+
+ QSharedPointer<T> sp (p_.toStrongRef ());
+
+ return sp
+ ? odb::object_traits<object_type>::id (*sp)
+ : i_.template object_id<O> ();
+}
+
+template <class T>
+inline typename QLazyWeakPointer<T>::database_type& QLazyWeakPointer<T>::
+database () const
+{
+ return *i_.database ();
+}
+
+//
+// QLazyWeakPointer related non-member functions.
+//
+
+template <class T, class X>
+inline QLazySharedPointer<X>
+qSharedPointerCast (const QLazyWeakPointer<T>& r)
+{
+ return QLazySharedPointer<T> (r).template staticCast<X> ();
+}
+
+template <class T, class X>
+inline QLazySharedPointer<X>
+qSharedPointerConstCast (const QLazyWeakPointer<T>& r)
+{
+ return QLazySharedPointer<T> (r).template constCast<X> ();
+}
+
+template <class T, class X>
+inline QLazySharedPointer<X>
+qSharedPointerDynamicCast (const QLazyWeakPointer<T>& r)
+{
+ return QLazySharedPointer<T> (r).template dynamicCast<X> ();
+}
+
+template <class T, class X>
+inline QLazyWeakPointer<X>
+qWeakPointerCast (const QLazyWeakPointer<T>& r)
+{
+ return QLazySharedPointer<T> (r).template staticCast<X> ().toWeakRef ();
+}
+
+template <class T, class X>
+inline bool
+operator== (const QLazyWeakPointer<T>& t, const QLazyWeakPointer<X>& x)
+{
+ return t.equal (x);
+}
+
+template <class T, class X>
+inline bool
+operator== (const QLazyWeakPointer<T>& t, const QLazySharedPointer<X>& x)
+{
+ return t.equal (x);
+}
+
+template <class T, class X>
+inline bool
+operator== (const QLazySharedPointer<T>& t, const QLazyWeakPointer<X>& x)
+{
+ return x.equal (t);
+}
+
+template <class T, class X>
+inline bool
+operator!= (const QLazyWeakPointer<T>& t, const QLazyWeakPointer<X>& x)
+{
+ return !t.equal (x);
+}
+
+template <class T, class X>
+inline bool
+operator!= (const QLazyWeakPointer<T>& t, const QLazySharedPointer<X>& x)
+{
+ return !t.equal (x);
+}
+
+template <class T, class X>
+inline bool
+operator!= (const QLazySharedPointer<T>& t, const QLazyWeakPointer<X>& x)
+{
+ return !x.equal (t);
+}
diff --git a/libodb-qt/odb/qt/smart-ptr/lazy-ptr.txx b/libodb-qt/odb/qt/smart-ptr/lazy-ptr.txx
new file mode 100644
index 0000000..0ae038a
--- /dev/null
+++ b/libodb-qt/odb/qt/smart-ptr/lazy-ptr.txx
@@ -0,0 +1,74 @@
+// file : odb/qt/smart-ptr/lazy-ptr.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+template <class T>
+template <class X>
+bool QLazySharedPointer<T>::
+equal (const QLazySharedPointer<X>& r) const
+{
+ bool t1 (!p_ == loaded ());
+ bool t2 (!r.p_ == r.loaded ());
+
+ // If both are transient, then compare the underlying pointers.
+ //
+ if (t1 && t2)
+ return p_ == r.p_;
+
+ // If one is transient and the other is persistent, then compare
+ // the underlying pointers but only if they are non NULL. Note
+ // that an unloaded persistent object is always unequal to a
+ // transient object.
+ //
+ if (t1 || t2)
+ return p_ == r.p_ && p_;
+
+ // If both objects are persistent, then we compare databases and
+ // object ids.
+ //
+ typedef typename odb::object_traits<T>::object_type object_type1;
+ typedef typename odb::object_traits<X>::object_type object_type2;
+
+ return i_.database () == r.i_.database () &&
+ objectId<object_type1> () == r.template objectId<object_type2> ();
+}
+
+//
+// QLazyWeakPointer
+//
+
+template <class T>
+template <class X>
+bool QLazyWeakPointer<T>::
+equal (const QLazyWeakPointer<X>& r) const
+{
+ if (isNull () && r.isNull ())
+ return true;
+
+ QLazySharedPointer<T> sp1 (toStrongRef ());
+ QLazySharedPointer<T> sp2 (r.toStrongRef ());
+
+ // If either one has expired, they are not equal.
+ //
+ if (!sp1 || !sp2)
+ return false;
+
+ return sp1.equal (sp2);
+}
+
+template <class T>
+template <class X>
+bool QLazyWeakPointer<T>::
+equal (const QLazySharedPointer<X>& r) const
+{
+ if (isNull () && r.isNull ())
+ return true;
+
+ QLazySharedPointer<T> sp (toStrongRef ());
+
+ // If the weak pointer has expired, they are not equal.
+ //
+ if (!sp)
+ return false;
+
+ return r.equal (sp);
+}
diff --git a/libodb-qt/odb/qt/smart-ptr/pointer-traits.hxx b/libodb-qt/odb/qt/smart-ptr/pointer-traits.hxx
new file mode 100644
index 0000000..f5cbc39
--- /dev/null
+++ b/libodb-qt/odb/qt/smart-ptr/pointer-traits.hxx
@@ -0,0 +1,110 @@
+// file : odb/qt/smart-ptr/pointer-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_SMART_PTR_POINTER_TRAITS_HXX
+#define ODB_QT_SMART_PTR_POINTER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QSharedPointer>
+#include <QtCore/QWeakPointer>
+
+#include <odb/pointer-traits.hxx>
+#include <odb/details/meta/remove-const.hxx>
+
+namespace odb
+{
+ // Specialization for QSharedPointer.
+ //
+ template <typename T>
+ class pointer_traits<QSharedPointer<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_shared;
+ static const bool lazy = false;
+
+ typedef T element_type;
+ typedef QSharedPointer<element_type> pointer_type;
+ typedef QSharedPointer<const element_type> const_pointer_type;
+ typedef typename odb::details::meta::remove_const<element_type>::result
+ unrestricted_element_type;
+ typedef QSharedPointer<unrestricted_element_type>
+ unrestricted_pointer_type;
+ typedef smart_ptr_guard<pointer_type> guard;
+
+ static element_type*
+ get_ptr (const pointer_type& p)
+ {
+ return p.data ();
+ }
+
+ static element_type&
+ get_ref (const pointer_type& p)
+ {
+ return *p;
+ }
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ static unrestricted_pointer_type
+ const_pointer_cast (const pointer_type& p)
+ {
+ return qSharedPointerConstCast<unrestricted_element_type> (p);
+ }
+
+ template <typename T1>
+ static QSharedPointer<T1>
+ static_pointer_cast (const pointer_type& p)
+ {
+ return qSharedPointerCast<T1> (p);
+ }
+
+ template <typename T1>
+ static QSharedPointer<T1>
+ dynamic_pointer_cast (const pointer_type& p)
+ {
+ return qSharedPointerDynamicCast<T1> (p);
+ }
+
+ public:
+ static void*
+ allocate (std::size_t n)
+ {
+ return operator new (n);
+ }
+
+ static void
+ free (void* p)
+ {
+ operator delete (p);
+ }
+ };
+
+ // Specialization for QWeakPointer.
+ //
+ template <typename T>
+ class pointer_traits<QWeakPointer<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_weak;
+ static const bool lazy = false;
+
+ typedef T element_type;
+ typedef QWeakPointer<element_type> pointer_type;
+ typedef QSharedPointer<element_type> strong_pointer_type;
+
+ static strong_pointer_type
+ lock (const pointer_type& p)
+ {
+ return p.toStrongRef ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_SMART_PTR_POINTER_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/smart-ptr/wrapper-traits.hxx b/libodb-qt/odb/qt/smart-ptr/wrapper-traits.hxx
new file mode 100644
index 0000000..dc6cb02
--- /dev/null
+++ b/libodb-qt/odb/qt/smart-ptr/wrapper-traits.hxx
@@ -0,0 +1,64 @@
+// file : odb/qt/smart-ptr/wrapper-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QT_SMART_PTR_WRAPPER_TRAITS_HXX
+#define ODB_QT_SMART_PTR_WRAPPER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <QtCore/QSharedPointer>
+
+#include <odb/wrapper-traits.hxx>
+
+namespace odb
+{
+ // Specialization for QSharedPointer.
+ //
+ template <typename T>
+ class wrapper_traits< QSharedPointer<T> >
+ {
+ public:
+ typedef T wrapped_type;
+ typedef QSharedPointer<T> wrapper_type;
+
+ // T can be const.
+ //
+ typedef
+ typename odb::details::meta::remove_const<T>::result
+ unrestricted_wrapped_type;
+
+ static const bool null_handler = true;
+ static const bool null_default = false;
+
+ static bool
+ get_null (const wrapper_type& p)
+ {
+ return p.isNull ();
+ }
+
+ static void
+ set_null (wrapper_type& p)
+ {
+ p.clear ();
+ }
+
+ static const wrapped_type&
+ get_ref (const wrapper_type& p)
+ {
+ return *p;
+ }
+
+ static wrapped_type&
+ set_ref (wrapper_type& p)
+ {
+ if (p.isNull ())
+ p = wrapper_type (new unrestricted_wrapped_type);
+
+ return const_cast<unrestricted_wrapped_type&> (*p);
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QT_SMART_PTR_WRAPPER_TRAITS_HXX
diff --git a/libodb-qt/odb/qt/version.hxx b/libodb-qt/odb/qt/version.hxx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libodb-qt/odb/qt/version.hxx
diff --git a/libodb-qt/odb/qt/version.hxx.in b/libodb-qt/odb/qt/version.hxx.in
new file mode 100644
index 0000000..c7ce430
--- /dev/null
+++ b/libodb-qt/odb/qt/version.hxx.in
@@ -0,0 +1,75 @@
+// file : odb/qt/version.hxx.in
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef LIBODB_QT_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 LIBODB_QT_VERSION_FULL $libodb_qt.version.project_number$ULL
+#define LIBODB_QT_VERSION_STR "$libodb_qt.version.project$"
+#define LIBODB_QT_VERSION_ID "$libodb_qt.version.project_id$"
+
+#define LIBODB_QT_VERSION_MAJOR $libodb_qt.version.major$
+#define LIBODB_QT_VERSION_MINOR $libodb_qt.version.minor$
+#define LIBODB_QT_VERSION_PATCH $libodb_qt.version.patch$
+
+#define LIBODB_QT_PRE_RELEASE $libodb_qt.version.pre_release$
+
+#define LIBODB_QT_SNAPSHOT $libodb_qt.version.snapshot_sn$ULL
+#define LIBODB_QT_SNAPSHOT_ID "$libodb_qt.version.snapshot_id$"
+
+#include <odb/version.hxx>
+
+$libodb.check(LIBODB_VERSION, LIBODB_SNAPSHOT)$
+
+
+// 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
+//
+
+// ODB Qt interface version: odb interface version plus the Qt interface
+// version.
+//
+// NOTE: also hardcoded in *.options.
+//
+#define ODB_QT_VERSION 2047600
+#define ODB_QT_VERSION_STR "2.5.0-b.26"
+
+// libodb-qt version: odb interface version plus the bugfix version. Note
+// that LIBODB_QT_VERSION is always greater or equal to ODB_QT_VERSION
+// since if the Qt interface virsion is incremented then the bugfix version
+// must be incremented as well.
+//
+#define LIBODB_QT_VERSION 2049976
+
+#endif // LIBODB_QT_VERSION
diff --git a/libodb-qt/odb/qt/version.options b/libodb-qt/odb/qt/version.options
new file mode 100644
index 0000000..0fef537
--- /dev/null
+++ b/libodb-qt/odb/qt/version.options
@@ -0,0 +1,16 @@
+# file : odb/qt/version.options
+# license : GNU GPL v2; see accompanying LICENSE file
+
+# Include the config file first so that it can do its thing before we
+# include any Qt headers.
+#
+--odb-prologue '#include <odb/qt/details/config.hxx>'
+
+# Make sure the options files as seen by the ODB compiler and header
+# files as seen by the C++ compiler have the same Qt interface version.
+#
+--hxx-prologue '#include <odb/qt/version.hxx>'
+
+--hxx-prologue '#if ODB_QT_VERSION != 2047600 // 2.5.0-b.26'
+--hxx-prologue '# error ODB and C++ compilers see different libodb-qt interface versions'
+--hxx-prologue '#endif'
diff --git a/libodb-qt/tests/.gitignore b/libodb-qt/tests/.gitignore
new file mode 100644
index 0000000..e54525b
--- /dev/null
+++ b/libodb-qt/tests/.gitignore
@@ -0,0 +1 @@
+driver
diff --git a/libodb-qt/tests/basics/buildfile b/libodb-qt/tests/basics/buildfile
new file mode 100644
index 0000000..900e57a
--- /dev/null
+++ b/libodb-qt/tests/basics/buildfile
@@ -0,0 +1,6 @@
+# file : tests/basics/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libs = libodb-qt%lib{odb-qt}
+
+exe{driver}: {hxx cxx}{*} $libs
diff --git a/libodb-qt/tests/basics/driver.cxx b/libodb-qt/tests/basics/driver.cxx
new file mode 100644
index 0000000..01e0d55
--- /dev/null
+++ b/libodb-qt/tests/basics/driver.cxx
@@ -0,0 +1,22 @@
+// file : tests/basics/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Basic test to make sure the library is usable. Functionality testing
+// is done in the odb-tests package.
+
+#include <odb/qt/exception.hxx>
+#include <odb/qt/date-time/exceptions.hxx>
+
+using namespace odb;
+
+int
+main ()
+{
+ try
+ {
+ throw qt::date_time::value_out_of_range ();
+ }
+ catch (const qt::exception&)
+ {
+ }
+}
diff --git a/libodb-qt/tests/build/.gitignore b/libodb-qt/tests/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-qt/tests/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-qt/tests/build/bootstrap.build b/libodb-qt/tests/build/bootstrap.build
new file mode 100644
index 0000000..6ee38db
--- /dev/null
+++ b/libodb-qt/tests/build/bootstrap.build
@@ -0,0 +1,8 @@
+# file : tests/build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = # Unnamed subproject.
+
+using config
+using dist
+using test
diff --git a/libodb-qt/tests/build/root.build b/libodb-qt/tests/build/root.build
new file mode 100644
index 0000000..6c5a90b
--- /dev/null
+++ b/libodb-qt/tests/build/root.build
@@ -0,0 +1,23 @@
+# file : tests/build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+cxx.std = latest
+
+using cxx
+
+hxx{*}: extension = hxx
+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
+
+# Every exe{} in this subproject is by default a test.
+#
+exe{*}: test = true
+
+# Specify the test target for cross-testing.
+#
+test.target = $cxx.target
diff --git a/libodb-qt/tests/buildfile b/libodb-qt/tests/buildfile
new file mode 100644
index 0000000..57588a4
--- /dev/null
+++ b/libodb-qt/tests/buildfile
@@ -0,0 +1,4 @@
+# file : tests/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -build/}
diff --git a/libodb-sqlite/.gitignore b/libodb-sqlite/.gitignore
new file mode 100644
index 0000000..1c363a0
--- /dev/null
+++ b/libodb-sqlite/.gitignore
@@ -0,0 +1,25 @@
+# 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/libodb-sqlite/GPLv2 b/libodb-sqlite/GPLv2
new file mode 100644
index 0000000..3912109
--- /dev/null
+++ b/libodb-sqlite/GPLv2
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) 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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. 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.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+ 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
+convey 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 2 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, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This 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 Library General
+Public License instead of this License.
diff --git a/libodb-sqlite/INSTALL b/libodb-sqlite/INSTALL
new file mode 100644
index 0000000..3eb4c33
--- /dev/null
+++ b/libodb-sqlite/INSTALL
@@ -0,0 +1,6 @@
+The easiest way to build this package is with the bpkg package manager:
+
+$ bpkg build libodb-sqlite
+
+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/libodb-sqlite/LICENSE b/libodb-sqlite/LICENSE
new file mode 100644
index 0000000..d96b938
--- /dev/null
+++ b/libodb-sqlite/LICENSE
@@ -0,0 +1,20 @@
+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 2 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, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
diff --git a/libodb-sqlite/NEWS b/libodb-sqlite/NEWS
new file mode 120000
index 0000000..0fae0f8
--- /dev/null
+++ b/libodb-sqlite/NEWS
@@ -0,0 +1 @@
+../NEWS \ No newline at end of file
diff --git a/libodb-sqlite/README b/libodb-sqlite/README
new file mode 100644
index 0000000..ffd4049
--- /dev/null
+++ b/libodb-sqlite/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 SQLite ODB runtime library. Every application
+that includes code generated for the SQLite database will need to link
+to this library.
+
+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.
+
+Send questions, bug reports, or any other feedback to the
+odb-users@codesynthesis.com mailing list.
diff --git a/libodb-sqlite/build/.gitignore b/libodb-sqlite/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-sqlite/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-sqlite/build/bootstrap.build b/libodb-sqlite/build/bootstrap.build
new file mode 100644
index 0000000..bb9c901
--- /dev/null
+++ b/libodb-sqlite/build/bootstrap.build
@@ -0,0 +1,10 @@
+# file : build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = libodb-sqlite
+
+using version
+using config
+using dist
+using test
+using install
diff --git a/libodb-sqlite/build/export.build b/libodb-sqlite/build/export.build
new file mode 100644
index 0000000..8ce77d8
--- /dev/null
+++ b/libodb-sqlite/build/export.build
@@ -0,0 +1,9 @@
+# file : build/export.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+$out_root/
+{
+ include odb/sqlite/
+}
+
+export $out_root/odb/sqlite/lib{odb-sqlite}
diff --git a/libodb-sqlite/build/root.build b/libodb-sqlite/build/root.build
new file mode 100644
index 0000000..a1975db
--- /dev/null
+++ b/libodb-sqlite/build/root.build
@@ -0,0 +1,19 @@
+# file : build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+config [bool] config.libodb_sqlite.develop ?= false
+
+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
diff --git a/libodb-sqlite/buildfile b/libodb-sqlite/buildfile
new file mode 100644
index 0000000..a04e206
--- /dev/null
+++ b/libodb-sqlite/buildfile
@@ -0,0 +1,9 @@
+# file : buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -build/} doc{INSTALL NEWS README} legal{GPLv2 LICENSE} manifest
+
+# Don't install tests or the INSTALL file.
+#
+tests/: install = false
+doc{INSTALL}@./: install = false
diff --git a/libodb-sqlite/manifest b/libodb-sqlite/manifest
new file mode 100644
index 0000000..bcdfe74
--- /dev/null
+++ b/libodb-sqlite/manifest
@@ -0,0 +1,125 @@
+: 1
+name: libodb-sqlite
+version: 2.5.0-b.26.z
+project: odb
+summary: SQLite ODB runtime library
+license: GPL-2.0-only
+license: other: proprietary ; Not free/open source.
+topics: C++, ORM, SQLite, SQL
+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
+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: libsqlite3 ^3.6.18
+depends: libodb == $
+depends: * cli ^1.2.0- ? ($config.libodb_sqlite.develop)
+
+tests: odb-tests == $ \
+ ? (!$defined(config.odb_tests.database)) config.odb_tests.database=sqlite
+
+builds: all
+
+# Only build this package configuration where it can be tested via odb-tests
+# package (see its manifest for details).
+#
+multi-builds: all
+multi-builds: -( +windows -gcc )
+multi-builds: &gcc
+multi-builds: &gcc-5+
+multi-builds: -static
+multi-build-config: { config.odb_tests.multi_database=true }+ odb-tests
+
+# Complements the default configuration (see odb-tests for background).
+#
+custom-builds: latest
+custom-builds: -static ; Implementation uses plugins and requires -fPIC.
+#custom-build-bot: -- see below.
+
+# Complements the multi configuration (see odb-tests for background).
+#
+custom-multi-builds: latest
+custom-multi-builds: -static ; Implementation uses plugins and requires -fPIC.
+#custom-multi-build-bot: -- see below.
+custom-multi-build-config: { config.odb_tests.multi_database=true }+ odb-tests
+
+custom-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuF4YmJmPHY52Q6N+YO0M
+lt/fCovdezleb2tVplyTnvbyAiPdmYCIIjVrsqUn3y46PdFtWEiSdsrCcncoxi6H
+8KelOB/oQ9pNTyEvwGKEH5ZIU7noLZYdXEfoNdvdL/pbY/7uLBZOSekfdQShZtbe
+uOZCM2Mhg2DD76TP/VAwaXuDCnEvxxU/yneUl5ZaBo62AWNrYJuSGAliCOpVpl6X
+X1kbHOvnCx7c9e3LxgaVivPaeZRKYg0OaFt96SBYEZzNPvjA8pMuKuj/vatHaCQ3
+NO9+r3TJ+4dQd7qN6Ju3zUJq9J/ndSh4lPvUalvvhdykecefhcyHwRZOG4xyFMFE
+nJM4sM+aZu6WoKATIKtk7On70inVr0sZJXwJ4Lt4oqaK2VthcSTby3wf2Yv4p5hL
+zNo31cCPmBRYzABcIc6ADYvexVK4uCwaim8xs7RK5Ug2Gv6vUWoRNZW8grIgDwUY
+5pZ4Zk3hW4ii2vehTaVrrmdW6XipIsT+ayiVX7eWuHHNxAeCojXVjOJu9B0ExMlD
+5tHZCs+SNdV5MceexecbptB7fZtRebP120yjLiSnZ5FpaQ1stusr0hSg+VQaX4np
+f5m1W/CcDr53PKWg/ayY9nWMUQaIwH4b69kLM+VTpYSbzu5UQJkmNBNq2EOHgoTv
+9MLA+cE/nNJ/rMI//MZ1+kcCAwEAAQ==
+-----END PUBLIC KEY-----
+\
+
+custom-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuLYZ68rotGDAtWViFHOe
+XEsmZB8BGI+af1ixh9JOy9BE4ohGTfPr1YsjGDzh+PhOVLAtyykOoT/qG7cuGB0T
+gBInoRrgVB2/ZKTMwxeDGb/TA3uykaXxcw7/liTsizHAY+phCNTbke8iER5Y78js
+9GlnTPmNhwFqEj2fwCz+2o08eyZvZ9Vj1fH/bFDCmDmU33JR3crtJlC8wPiF70Ho
+FJzHFdaFQl3MxvEV92HjOsyqozMi6tAVVefN1vapVQeNtjkB0Di18p0/EMugEuGU
+OxktjDHQWNaV8Ao6cCDk6OkJnM3ZNL1no3cV4cuF+/xI8UZzwfPoBnwg/s183Qzu
+pHHKOSHmuO0oVE/XohJhepSw3tb+wf5BwejRhYHikIjqCxJdm9H0QTiqXT82y24K
+yg3gkRMOgqnVxERKKP4ZknLSMQCEKiND/t2zdLJ/lxH9eHZdPHKk3OZZG292j+Bh
+fknxcTKNk1Dmf32Irs5hVrjsoU8eAutbItovzXdBaj//rn/ry/kUlCa1Ov6iLIDJ
+gyxmsDlgKNR/uE9ogmDn0ishJIoCmxeqenRfJkttr9pEsDsUFuB425QGqiSxa1jh
+PCNca3iRtO44wADXaQMTGpvLzBfdfVc8LoFpn+kynN0V1MvxAX4mHRXxw8ERXd3U
+dpHDhOthPLolJQrYKb/YyW8CAwEAAQ==
+-----END PUBLIC KEY-----
+\
+
+custom-multi-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuF4YmJmPHY52Q6N+YO0M
+lt/fCovdezleb2tVplyTnvbyAiPdmYCIIjVrsqUn3y46PdFtWEiSdsrCcncoxi6H
+8KelOB/oQ9pNTyEvwGKEH5ZIU7noLZYdXEfoNdvdL/pbY/7uLBZOSekfdQShZtbe
+uOZCM2Mhg2DD76TP/VAwaXuDCnEvxxU/yneUl5ZaBo62AWNrYJuSGAliCOpVpl6X
+X1kbHOvnCx7c9e3LxgaVivPaeZRKYg0OaFt96SBYEZzNPvjA8pMuKuj/vatHaCQ3
+NO9+r3TJ+4dQd7qN6Ju3zUJq9J/ndSh4lPvUalvvhdykecefhcyHwRZOG4xyFMFE
+nJM4sM+aZu6WoKATIKtk7On70inVr0sZJXwJ4Lt4oqaK2VthcSTby3wf2Yv4p5hL
+zNo31cCPmBRYzABcIc6ADYvexVK4uCwaim8xs7RK5Ug2Gv6vUWoRNZW8grIgDwUY
+5pZ4Zk3hW4ii2vehTaVrrmdW6XipIsT+ayiVX7eWuHHNxAeCojXVjOJu9B0ExMlD
+5tHZCs+SNdV5MceexecbptB7fZtRebP120yjLiSnZ5FpaQ1stusr0hSg+VQaX4np
+f5m1W/CcDr53PKWg/ayY9nWMUQaIwH4b69kLM+VTpYSbzu5UQJkmNBNq2EOHgoTv
+9MLA+cE/nNJ/rMI//MZ1+kcCAwEAAQ==
+-----END PUBLIC KEY-----
+\
+
+custom-multi-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuLYZ68rotGDAtWViFHOe
+XEsmZB8BGI+af1ixh9JOy9BE4ohGTfPr1YsjGDzh+PhOVLAtyykOoT/qG7cuGB0T
+gBInoRrgVB2/ZKTMwxeDGb/TA3uykaXxcw7/liTsizHAY+phCNTbke8iER5Y78js
+9GlnTPmNhwFqEj2fwCz+2o08eyZvZ9Vj1fH/bFDCmDmU33JR3crtJlC8wPiF70Ho
+FJzHFdaFQl3MxvEV92HjOsyqozMi6tAVVefN1vapVQeNtjkB0Di18p0/EMugEuGU
+OxktjDHQWNaV8Ao6cCDk6OkJnM3ZNL1no3cV4cuF+/xI8UZzwfPoBnwg/s183Qzu
+pHHKOSHmuO0oVE/XohJhepSw3tb+wf5BwejRhYHikIjqCxJdm9H0QTiqXT82y24K
+yg3gkRMOgqnVxERKKP4ZknLSMQCEKiND/t2zdLJ/lxH9eHZdPHKk3OZZG292j+Bh
+fknxcTKNk1Dmf32Irs5hVrjsoU8eAutbItovzXdBaj//rn/ry/kUlCa1Ov6iLIDJ
+gyxmsDlgKNR/uE9ogmDn0ishJIoCmxeqenRfJkttr9pEsDsUFuB425QGqiSxa1jh
+PCNca3iRtO44wADXaQMTGpvLzBfdfVc8LoFpn+kynN0V1MvxAX4mHRXxw8ERXd3U
+dpHDhOthPLolJQrYKb/YyW8CAwEAAQ==
+-----END PUBLIC KEY-----
+\
diff --git a/libodb-sqlite/odb/sqlite/auto-handle.hxx b/libodb-sqlite/odb/sqlite/auto-handle.hxx
new file mode 100644
index 0000000..d25e919
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/auto-handle.hxx
@@ -0,0 +1,101 @@
+// file : odb/sqlite/auto-handle.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_AUTO_HANDLE_HXX
+#define ODB_SQLITE_AUTO_HANDLE_HXX
+
+#include <odb/pre.hxx>
+
+#include <cassert>
+#include <sqlite3.h>
+
+#include <odb/sqlite/version.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename H>
+ struct handle_traits;
+
+ template <>
+ struct handle_traits<sqlite3>
+ {
+ static void
+ release (sqlite3* h)
+ {
+ if (sqlite3_close (h) == SQLITE_BUSY)
+ {
+ // Connection has outstanding prepared statements.
+ //
+ assert (false);
+ }
+ }
+ };
+
+ template <>
+ struct handle_traits<sqlite3_stmt>
+ {
+ static void
+ release (sqlite3_stmt* h)
+ {
+ sqlite3_finalize (h);
+ }
+ };
+
+ template <typename H>
+ class auto_handle
+ {
+ public:
+ auto_handle (H* h = 0)
+ : h_ (h)
+ {
+ }
+
+ ~auto_handle ()
+ {
+ if (h_ != 0)
+ handle_traits<H>::release (h_);
+ }
+
+ H*
+ get () const
+ {
+ return h_;
+ }
+
+ void
+ reset (H* h = 0)
+ {
+ if (h_ != 0)
+ handle_traits<H>::release (h_);
+
+ h_ = h;
+ }
+
+ H*
+ release ()
+ {
+ H* h (h_);
+ h_ = 0;
+ return h;
+ }
+
+ operator H* () const
+ {
+ return h_;
+ }
+
+ private:
+ auto_handle (const auto_handle&);
+ auto_handle& operator= (const auto_handle&);
+
+ private:
+ H* h_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_AUTO_HANDLE_HXX
diff --git a/libodb-sqlite/odb/sqlite/binding.hxx b/libodb-sqlite/odb/sqlite/binding.hxx
new file mode 100644
index 0000000..3807130
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/binding.hxx
@@ -0,0 +1,44 @@
+// file : odb/sqlite/binding.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_BINDING_HXX
+#define ODB_SQLITE_BINDING_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+
+#include <odb/sqlite/sqlite-types.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class binding
+ {
+ public:
+ typedef sqlite::bind bind_type;
+
+ binding (): bind (0), count (0), version (0) {}
+
+ binding (bind_type* b, std::size_t n)
+ : bind (b), count (n), version (0)
+ {
+ }
+
+ bind_type* bind;
+ std::size_t count;
+ std::size_t version;
+
+ private:
+ binding (const binding&);
+ binding& operator= (const binding&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_BINDING_HXX
diff --git a/libodb-sqlite/odb/sqlite/blob-stream.hxx b/libodb-sqlite/odb/sqlite/blob-stream.hxx
new file mode 100644
index 0000000..caa7c24
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/blob-stream.hxx
@@ -0,0 +1,31 @@
+// file : odb/sqlite/blob-stream.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_BLOB_STREAM_HXX
+#define ODB_SQLITE_BLOB_STREAM_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/sqlite/blob.hxx>
+#include <odb/sqlite/stream.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class blob_stream: public stream
+ {
+ public:
+ blob_stream (const blob& b, bool rw)
+ : stream (b.db ().c_str (),
+ b.table ().c_str (),
+ b.column ().c_str (),
+ b.rowid (),
+ rw) {}
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_BLOB_STREAM_HXX
diff --git a/libodb-sqlite/odb/sqlite/blob.hxx b/libodb-sqlite/odb/sqlite/blob.hxx
new file mode 100644
index 0000000..eecc13e
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/blob.hxx
@@ -0,0 +1,68 @@
+// file : odb/sqlite/blob.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_BLOB_HXX
+#define ODB_SQLITE_BLOB_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+
+// Carefully allow this header to be included into the ODB compilation.
+//
+#ifndef ODB_COMPILER
+# include <odb/sqlite/forward.hxx>
+#endif
+
+namespace odb
+{
+ namespace sqlite
+ {
+#ifdef ODB_COMPILER
+ #pragma db sqlite:type("BLOB STREAM")
+ class blob
+#else
+ class blob
+#endif
+ {
+ public:
+ // BLOB size to provision for. Set before calling persist() or update().
+ //
+ explicit
+ blob (std::size_t size = 0): size_ (size), rowid_ (0) {}
+
+ std::size_t size () const {return size_;}
+ void size (std::size_t s) {size_ = s;}
+
+ const std::string& db () const {return db_;}
+ const std::string& table () const {return table_;}
+ const std::string& column () const {return column_;}
+ long long rowid () const {return rowid_;}
+
+ void
+ clear ()
+ {
+ size_ = 0;
+ db_.clear ();
+ table_.clear ();
+ column_.clear ();
+ rowid_ = 0;
+ }
+
+ private:
+#ifndef ODB_COMPILER
+ friend struct default_value_traits<blob, id_blob_stream>;
+#endif
+ std::size_t size_;
+ mutable std::string db_;
+ mutable std::string table_;
+ mutable std::string column_;
+ mutable long long rowid_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_BLOB_HXX
diff --git a/libodb-sqlite/odb/sqlite/buildfile b/libodb-sqlite/odb/sqlite/buildfile
new file mode 100644
index 0000000..69d2425
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/buildfile
@@ -0,0 +1,132 @@
+# file : odb/sqlite/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+define cli: file
+cli{*}: extension = cli
+
+import int_libs = libodb%lib{odb}
+import int_libs += libsqlite3%lib{sqlite3}
+
+lib{odb-sqlite}: {hxx ixx txx cxx}{* -version} {hxx}{version} \
+ details/{hxx ixx txx cxx}{* -options} \
+ $int_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.
+#
+cxx.poptions =+ "-I$out_root" "-I$src_root"
+
+obja{*}: cxx.poptions += -DLIBODB_SQLITE_STATIC_BUILD
+objs{*}: cxx.poptions += -DLIBODB_SQLITE_SHARED_BUILD
+
+# Export options.
+#
+lib{odb-sqlite}:
+{
+ cxx.export.poptions = "-I$out_root" "-I$src_root"
+ cxx.export.libs = $int_libs
+}
+
+liba{odb-sqlite}: cxx.export.poptions += -DLIBODB_SQLITE_STATIC
+libs{odb-sqlite}: cxx.export.poptions += -DLIBODB_SQLITE_SHARED
+
+# For pre-releases use the complete version to make sure they cannot be used
+# in place of another pre-release or the final version. See the version module
+# for details on the version.* variable values.
+#
+if $version.pre_release
+ lib{odb-sqlite}: bin.lib.version = @"-$version.project_id"
+else
+ lib{odb-sqlite}: bin.lib.version = @"-$version.major.$version.minor"
+
+develop = $config.libodb_sqlite.develop
+
+## Consumption build ($develop == false).
+#
+
+# Use pregenerated versions in the consumption build.
+#
+lib{odb-sqlite}: details/pregenerated/{hxx ixx cxx}{**}: include = (!$develop)
+
+if! $develop
+ cxx.poptions =+ "-I($src_base/details/pregenerated)" # Note: must come first.
+
+# Don't install pregenerated headers since they are only used internally in
+# the database implementation (also below).
+#
+details/pregenerated/{hxx ixx}{*}: install = false
+
+# Distribute pregenerated versions only in the consumption build.
+#
+details/pregenerated/{hxx ixx cxx}{*}: dist = (!$develop)
+
+#
+##
+
+## Development build ($develop == true).
+#
+
+lib{odb-sqlite}: details/{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.
+#
+<details/{hxx ixx cxx}{options}>: details/cli{options} $cli
+{
+ install = false
+ dist = ($develop ? pregenerated/odb/sqlite/details/ : false)
+
+ # Symlink the generated code in src for convenience of development.
+ #
+ backlink = true
+}
+%
+if $develop
+{{
+ options = --include-with-brackets --include-prefix odb/sqlite/details \
+ --guard-prefix LIBODB_SQLITE_DETAILS --generate-file-scanner \
+ --cli-namespace odb::sqlite::details::cli --long-usage \
+ --no-combined-flags
+
+ $cli $options -o $out_base/details/ $path($<[0])
+
+ # If the result differs from the pregenerated version, copy it over.
+ #
+ d = [dir_path] $src_base/details/pregenerated/odb/sqlite/details/
+
+ if diff $d/options.hxx $path($>[0]) >- && \
+ diff $d/options.ixx $path($>[1]) >- && \
+ diff $d/options.cxx $path($>[2]) >-
+ exit
+ end
+
+ cp $path($>[0]) $d/options.hxx
+ cp $path($>[1]) $d/options.ixx
+ cp $path($>[2]) $d/options.cxx
+}}
+
+# Install into the odb/sqlite/ subdirectory of, say, /usr/include/
+# recreating subdirectories.
+#
+install_include = [dir_path] include/odb/sqlite/
+
+{hxx ixx txx}{*}:
+{
+ install = $install_include
+ install.subdirs = true
+}
diff --git a/libodb-sqlite/odb/sqlite/connection-factory.cxx b/libodb-sqlite/odb/sqlite/connection-factory.cxx
new file mode 100644
index 0000000..794c6dd
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/connection-factory.cxx
@@ -0,0 +1,417 @@
+// file : odb/sqlite/connection-factory.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/details/lock.hxx>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/connection-factory.hxx>
+
+#include <odb/sqlite/details/config.hxx> // LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+
+using namespace std;
+
+namespace odb
+{
+ using namespace details;
+
+ namespace sqlite
+ {
+ //
+ // serial_connection_factory
+ //
+
+ serial_connection_factory::
+ ~serial_connection_factory ()
+ {
+ // We should hold the last reference to the connection.
+ //
+ if (connection_ != 0)
+ assert (connection_.count () == 1);
+ }
+
+ connection_ptr serial_connection_factory::
+ create ()
+ {
+ return connection_ptr (new (shared) connection (*this));
+ }
+
+ connection_ptr serial_connection_factory::
+ connect ()
+ {
+ return connection_;
+ }
+
+ void serial_connection_factory::
+ database (database_type& db)
+ {
+ connection_factory::database (db);
+
+ if (!connection_)
+ connection_ = create ();
+ }
+
+ //
+ // single_connection_factory
+ //
+
+ single_connection_factory::
+ ~single_connection_factory ()
+ {
+ // If the connection is currently in use, wait for it to return to
+ // the factory.
+ //
+ lock l (mutex_);
+ }
+
+ single_connection_factory::single_connection_ptr
+ single_connection_factory::
+ create ()
+ {
+ return single_connection_ptr (new (shared) single_connection (*this));
+ }
+
+ connection_ptr single_connection_factory::
+ connect ()
+ {
+ mutex_.lock ();
+ connection_->callback_ = &connection_->cb_;
+ connection_ptr r (connection_);
+ connection_.reset ();
+ return r;
+ }
+
+ void single_connection_factory::
+ database (database_type& db)
+ {
+ connection_factory::database (db);
+
+ if (!connection_)
+ connection_ = create ();
+ }
+
+ bool single_connection_factory::
+ release (single_connection* c)
+ {
+ c->callback_ = 0;
+ connection_.reset (inc_ref (c));
+ connection_->recycle ();
+ mutex_.unlock ();
+ return false;
+ }
+
+ //
+ // single_connection_factory::single_connection
+ //
+
+ single_connection_factory::single_connection::
+ single_connection (single_connection_factory& f, int extra_flags)
+ : connection (f, extra_flags)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ single_connection_factory::single_connection::
+ single_connection (single_connection_factory& f, sqlite3* handle)
+ : connection (f, handle)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ bool single_connection_factory::single_connection::
+ zero_counter (void* arg)
+ {
+ single_connection* c (static_cast<single_connection*> (arg));
+ return static_cast<single_connection_factory&> (c->factory_).release (c);
+ }
+
+ //
+ // new_connection_factory
+ //
+
+ connection_ptr new_connection_factory::
+ connect ()
+ {
+ return connection_ptr (new (shared) connection (*this, extra_flags_));
+ }
+
+ void new_connection_factory::
+ database (database_type& db)
+ {
+ bool first (db_ == 0);
+
+ connection_factory::database (db);
+
+ if (!first)
+ return;
+
+ // Unless explicitly disabled, enable shared cache.
+ //
+#if SQLITE_VERSION_NUMBER >= 3006018 && defined(LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY)
+ if ((db_->flags () & SQLITE_OPEN_PRIVATECACHE) == 0)
+ extra_flags_ |= SQLITE_OPEN_SHAREDCACHE;
+#endif
+ }
+
+ //
+ // connection_pool_factory
+ //
+
+ connection_pool_factory::pooled_connection_ptr connection_pool_factory::
+ create ()
+ {
+ return pooled_connection_ptr (
+ new (shared) pooled_connection (*this, extra_flags_));
+ }
+
+ connection_pool_factory::
+ ~connection_pool_factory ()
+ {
+ // Wait for all the connections currently in use to return to the pool.
+ //
+ lock l (mutex_);
+ while (in_use_ != 0)
+ {
+ waiters_++;
+ cond_.wait (l);
+ waiters_--;
+ }
+ }
+
+ connection_ptr connection_pool_factory::
+ connect ()
+ {
+ lock l (mutex_);
+
+ while (true)
+ {
+ // See if we have a spare connection.
+ //
+ if (connections_.size () != 0)
+ {
+ shared_ptr<pooled_connection> c (connections_.back ());
+ connections_.pop_back ();
+
+ c->callback_ = &c->cb_;
+ in_use_++;
+ return c;
+ }
+
+ // See if we can create a new one.
+ //
+ if(max_ == 0 || in_use_ < max_)
+ {
+ shared_ptr<pooled_connection> c (create ());
+ c->callback_ = &c->cb_;
+ in_use_++;
+ return c;
+ }
+
+ // Wait until someone releases a connection.
+ //
+ waiters_++;
+ cond_.wait (l);
+ waiters_--;
+ }
+ }
+
+ void connection_pool_factory::
+ database (database_type& db)
+ {
+ bool first (db_ == 0);
+
+ connection_factory::database (db);
+
+ if (!first)
+ return;
+
+ // Unless explicitly disabled, enable shared cache.
+ //
+#if SQLITE_VERSION_NUMBER >= 3006018 && defined(LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY)
+ if ((db_->flags () & SQLITE_OPEN_PRIVATECACHE) == 0)
+ extra_flags_ |= SQLITE_OPEN_SHAREDCACHE;
+#endif
+
+ if (min_ > 0)
+ {
+ connections_.reserve (min_);
+
+ for(size_t i (0); i < min_; ++i)
+ connections_.push_back (create ());
+ }
+ }
+
+ bool connection_pool_factory::
+ release (pooled_connection* c)
+ {
+ c->callback_ = 0;
+
+ lock l (mutex_);
+
+ // Determine if we need to keep or free this connection.
+ //
+ bool keep (waiters_ != 0 ||
+ min_ == 0 ||
+ (connections_.size () + in_use_ <= min_));
+
+ in_use_--;
+
+ if (keep)
+ {
+ connections_.push_back (pooled_connection_ptr (inc_ref (c)));
+ connections_.back ()->recycle ();
+ }
+
+ if (waiters_ != 0)
+ cond_.signal ();
+
+ return !keep;
+ }
+
+ //
+ // connection_pool_factory::pooled_connection
+ //
+
+ connection_pool_factory::pooled_connection::
+ pooled_connection (connection_pool_factory& f, int extra_flags)
+ : connection (f, extra_flags)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ connection_pool_factory::pooled_connection::
+ pooled_connection (connection_pool_factory& f, sqlite3* handle)
+ : connection (f, handle)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ bool connection_pool_factory::pooled_connection::
+ zero_counter (void* arg)
+ {
+ pooled_connection* c (static_cast<pooled_connection*> (arg));
+ return static_cast<connection_pool_factory&> (c->factory_).release (c);
+ }
+
+ //
+ // default_attached_connection_factory
+ //
+
+ void default_attached_connection_factory::
+ detach ()
+ {
+ // Note that this function may be called several times, for example, in
+ // case of detach_database() failure.
+ //
+ if (attached_connection_ != 0)
+ {
+ // We should hold the last reference to the attached connection.
+ //
+ assert (attached_connection_.count () == 1);
+
+ // While it may seem like a good idea to also invalidate query results
+ // and reset active statements, if any such result/statement is still
+ // alive, then there would be bigger problems since it would have a
+ // dangling reference to the connection. In a way, that's the same
+ // reason we don't do it in the connection destructor.
+
+ // Remove ourselves from the active object list of the main
+ // connection.
+ //
+ if (next_ != this) // Might have already been done.
+ list_remove ();
+
+ const string& s (database ().schema ());
+
+ if (s != "main" && s != "temp")
+ main_factory ().detach_database (main_connection_, s);
+
+ // Explicitly free the attached connection so that we don't try to
+ // redo this.
+ //
+ attached_connection_.reset ();
+ }
+ }
+
+ default_attached_connection_factory::
+ ~default_attached_connection_factory ()
+ {
+ if (attached_connection_ != 0)
+ {
+ // This can throw. Ignoring the failure to detach seems like the most
+ // sensible thing to do here.
+ //
+ try{ detach (); } catch (...) {}
+ }
+ }
+
+ connection_ptr default_attached_connection_factory::
+ connect ()
+ {
+ return attached_connection_;
+ }
+
+ void default_attached_connection_factory::
+ database (database_type& db)
+ {
+ attached_connection_factory::database (db);
+
+ if (!attached_connection_)
+ {
+ const string& s (db.schema ());
+
+ if (s != "main" && s != "temp")
+ main_factory ().attach_database (main_connection_, db.name (), s);
+
+ attached_connection_.reset (
+ new (shared) connection (*this,
+ s != "main" ? &translate_statement : 0));
+
+ // Add ourselves to the active object list of the main connection.
+ //
+ list_add ();
+ }
+ }
+
+ void default_attached_connection_factory::
+ clear ()
+ {
+ attached_connection_->clear ();
+ }
+
+ void default_attached_connection_factory::
+ translate_statement (string& r,
+ const char* text,
+ size_t text_size,
+ connection& conn)
+ {
+ r.assign (text, text_size);
+
+ // Things will fall apart if any of the statements we translate use
+ // "main" as a table alias. So we have this crude check even though it
+ // means we cannot use "main" for other aliases (e.g., column).
+ //
+ assert (r.find ("AS \"main\"") == string::npos);
+
+ const string& s (conn.database ().schema ());
+ for (size_t p (0); (p = r.find ("\"main\".", p, 7)) != string::npos; )
+ {
+ // Verify the preceding character.
+ //
+ if (p != 0 && r[p - 1] == '.')
+ {
+ p += 7;
+ continue;
+ }
+
+ r.replace (p + 1, 4, s);
+ p += s.size () + 3;
+ }
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/connection-factory.hxx b/libodb-sqlite/odb/sqlite/connection-factory.hxx
new file mode 100644
index 0000000..b410997
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/connection-factory.hxx
@@ -0,0 +1,273 @@
+// file : odb/sqlite/connection-factory.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_CONNECTION_FACTORY_HXX
+#define ODB_SQLITE_CONNECTION_FACTORY_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cstddef> // std::size_t
+#include <cassert>
+
+#include <odb/details/mutex.hxx>
+#include <odb/details/condition.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // Share a single connection in a guaranteed serial database access.
+ //
+ // For example, a single-threaded application that executes all the
+ // operations via the database instance without messing with multiple
+ // connections/transactions would qualify.
+ //
+ class LIBODB_SQLITE_EXPORT serial_connection_factory:
+ public connection_factory
+ {
+ public:
+ serial_connection_factory () {}
+
+ virtual connection_ptr
+ connect ();
+
+ virtual void
+ database (database_type&);
+
+ virtual
+ ~serial_connection_factory ();
+
+ private:
+ serial_connection_factory (const serial_connection_factory&);
+ serial_connection_factory& operator= (const serial_connection_factory&);
+
+ protected:
+ // This function is called when the factory needs to create the
+ // connection.
+ //
+ virtual connection_ptr
+ create ();
+
+ connection_ptr connection_;
+ };
+
+ // Share a single connection potentially between multiple threads.
+ //
+ class LIBODB_SQLITE_EXPORT single_connection_factory:
+ public connection_factory
+ {
+ public:
+ single_connection_factory () {}
+
+ virtual connection_ptr
+ connect ();
+
+ virtual void
+ database (database_type&);
+
+ virtual
+ ~single_connection_factory ();
+
+ private:
+ single_connection_factory (const single_connection_factory&);
+ single_connection_factory& operator= (const single_connection_factory&);
+
+ protected:
+ class LIBODB_SQLITE_EXPORT single_connection: public connection
+ {
+ public:
+ single_connection (single_connection_factory&, int extra_flags = 0);
+ single_connection (single_connection_factory&, sqlite3*);
+
+ private:
+ static bool
+ zero_counter (void*);
+
+ private:
+ friend class single_connection_factory;
+ shared_base::refcount_callback cb_;
+ };
+
+ friend class single_connection;
+
+ typedef details::shared_ptr<single_connection> single_connection_ptr;
+
+ // This function is called when the factory needs to create the
+ // connection.
+ //
+ virtual single_connection_ptr
+ create ();
+
+ protected:
+ // Return true if the connection should be deleted, false otherwise.
+ //
+ bool
+ release (single_connection*);
+
+ protected:
+ details::mutex mutex_;
+ single_connection_ptr connection_;
+ };
+
+ // Create a new connection every time one is requested.
+ //
+ class LIBODB_SQLITE_EXPORT new_connection_factory:
+ public connection_factory
+ {
+ public:
+ new_connection_factory (): extra_flags_ (0) {}
+
+ virtual connection_ptr
+ connect ();
+
+ virtual void
+ database (database_type&);
+
+ private:
+ new_connection_factory (const new_connection_factory&);
+ new_connection_factory& operator= (const new_connection_factory&);
+
+ private:
+ int extra_flags_;
+ };
+
+ // Pool a number of connections.
+ //
+ class LIBODB_SQLITE_EXPORT connection_pool_factory:
+ public connection_factory
+ {
+ public:
+ // The max_connections argument specifies the maximum number of
+ // concurrent connections this pool will maintain. If this value
+ // is 0 then the pool will create a new connection every time all
+ // of the existing connections are in use.
+ //
+ // The min_connections argument specifies the minimum number of
+ // connections that should be maintained by the pool. If the
+ // number of connections maintained by the pool exceeds this
+ // number and there are no active waiters for a new connection,
+ // then the pool will release the excess connections. If this
+ // value is 0 then the pool will maintain all the connections
+ // that were ever created.
+ //
+ // The ping argument specifies whether to ping the connection to
+ // make sure it is still alive before returning it to the caller.
+ //
+ connection_pool_factory (std::size_t max_connections = 0,
+ std::size_t min_connections = 0)
+ : max_ (max_connections),
+ min_ (min_connections),
+ extra_flags_ (0),
+ in_use_ (0),
+ waiters_ (0),
+ cond_ (mutex_)
+ {
+ // max_connections == 0 means unlimited.
+ //
+ assert (max_connections == 0 || max_connections >= min_connections);
+ }
+
+ virtual connection_ptr
+ connect ();
+
+ virtual void
+ database (database_type&);
+
+ virtual
+ ~connection_pool_factory ();
+
+ private:
+ connection_pool_factory (const connection_pool_factory&);
+ connection_pool_factory& operator= (const connection_pool_factory&);
+
+ protected:
+ class LIBODB_SQLITE_EXPORT pooled_connection: public connection
+ {
+ public:
+ pooled_connection (connection_pool_factory&, int extra_flags = 0);
+ pooled_connection (connection_pool_factory&, sqlite3*);
+
+ private:
+ static bool
+ zero_counter (void*);
+
+ private:
+ friend class connection_pool_factory;
+ shared_base::refcount_callback cb_;
+ };
+
+ friend class pooled_connection;
+
+ typedef details::shared_ptr<pooled_connection> pooled_connection_ptr;
+ typedef std::vector<pooled_connection_ptr> connections;
+
+ // This function is called whenever the pool needs to create a new
+ // connection.
+ //
+ virtual pooled_connection_ptr
+ create ();
+
+ protected:
+ // Return true if the connection should be deleted, false otherwise.
+ //
+ bool
+ release (pooled_connection*);
+
+ protected:
+ const std::size_t max_;
+ const std::size_t min_;
+ int extra_flags_;
+
+ std::size_t in_use_; // Number of connections currently in use.
+ std::size_t waiters_; // Number of threads waiting for a connection.
+
+ connections connections_;
+
+ details::mutex mutex_;
+ details::condition cond_;
+ };
+
+ class LIBODB_SQLITE_EXPORT default_attached_connection_factory:
+ public attached_connection_factory
+ {
+ public:
+ explicit
+ default_attached_connection_factory (const connection_ptr& main)
+ : attached_connection_factory (main) {}
+
+ using attached_connection_factory::database; // Accessor.
+
+ virtual void
+ database (database_type&);
+
+ virtual connection_ptr
+ connect ();
+
+ virtual void
+ detach ();
+
+ // Active object interface.
+ //
+ virtual void
+ clear ();
+
+ virtual
+ ~default_attached_connection_factory ();
+
+ protected:
+ static void
+ translate_statement (std::string&, const char*, std::size_t, connection&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_CONNECTION_FACTORY_HXX
diff --git a/libodb-sqlite/odb/sqlite/connection.cxx b/libodb-sqlite/odb/sqlite/connection.cxx
new file mode 100644
index 0000000..0445163
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/connection.cxx
@@ -0,0 +1,310 @@
+// file : odb/sqlite/connection.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <new> // std::bad_alloc
+#include <string>
+#include <cassert>
+
+#include <odb/details/lock.hxx>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/transaction.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/statement-cache.hxx>
+#include <odb/sqlite/prepared-query.hxx>
+#include <odb/sqlite/error.hxx>
+#include <odb/sqlite/exceptions.hxx> // deadlock
+
+#include <odb/sqlite/details/config.hxx> // LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+
+using namespace std;
+
+extern "C" void
+odb_sqlite_connection_unlock_callback (void**, int);
+
+namespace odb
+{
+ using namespace details;
+
+ namespace sqlite
+ {
+ connection::
+ connection (connection_factory& cf,
+ int extra_flags,
+ statement_translator* st)
+ : odb::connection (cf),
+ statement_translator_ (st),
+ unlock_cond_ (unlock_mutex_),
+ active_objects_ (0)
+ {
+ database_type& db (database ());
+
+ int f (db.flags () | extra_flags);
+ const string& n (db.name ());
+
+ // If we are opening a temporary database, then add the create flag.
+ //
+ if (n.empty () || n == ":memory:")
+ f |= SQLITE_OPEN_CREATE;
+
+ // A connection can only be used by a single thread at a time. So
+ // disable locking in SQLite unless explicitly requested.
+ //
+#if defined(SQLITE_OPEN_NOMUTEX)
+ if ((f & SQLITE_OPEN_FULLMUTEX) == 0)
+ f |= SQLITE_OPEN_NOMUTEX;
+#endif
+
+ sqlite3* h (0);
+
+ // sqlite3_open_v2() was only addedin SQLite 3.5.0.
+ //
+#if SQLITE_VERSION_NUMBER >= 3005000
+ const string& vfs (db.vfs ());
+ int e (
+ sqlite3_open_v2 (
+ n.c_str (), &h, f, (vfs.empty () ? 0 : vfs.c_str ())));
+#else
+ // Readonly opening not supported in SQLite earlier than 3.5.0.
+ //
+ assert ((f & SQLITE_OPEN_READONLY) == 0);
+ int e (sqlite3_open (n.c_str (), &h));
+#endif
+
+ handle_.reset (h);
+
+ if (e != SQLITE_OK)
+ {
+ if (handle_ == 0)
+ throw bad_alloc ();
+
+ translate_error (e, *this);
+ }
+
+ init ();
+ }
+
+ connection::
+ connection (connection_factory& cf,
+ sqlite3* handle,
+ statement_translator* st)
+ : odb::connection (cf),
+ handle_ (handle),
+ statement_translator_ (st),
+ unlock_cond_ (unlock_mutex_),
+ active_objects_ (0)
+ {
+ init ();
+ }
+
+ void connection::
+ init ()
+ {
+ database_type& db (database ());
+
+ // Enable/disable foreign key constraints.
+ //
+ generic_statement st (
+ *this,
+ db.foreign_keys ()
+ ? "PRAGMA foreign_keys=ON"
+ : "PRAGMA foreign_keys=OFF",
+ db.foreign_keys () ? 22 : 23);
+ st.execute ();
+
+ // String lengths include '\0', as per the SQLite manual suggestion.
+ //
+ begin_.reset (new (shared) generic_statement (*this, "BEGIN", 6));
+ commit_.reset (new (shared) generic_statement (*this, "COMMIT", 7));
+ rollback_.reset (new (shared) generic_statement (*this, "ROLLBACK", 9));
+
+ // Create statement cache.
+ //
+ statement_cache_.reset (new statement_cache_type (*this));
+ }
+
+ connection::
+ connection (attached_connection_factory& cf, statement_translator* st)
+ : odb::connection (cf),
+ handle_ (0),
+ statement_translator_ (st),
+ unlock_cond_ (unlock_mutex_),
+ active_objects_ (0)
+ {
+ // Copy some things over from the main connection.
+ //
+ connection& main (*cf.main_connection_);
+
+ tracer_ = main.tracer_;
+
+ // Create statement cache.
+ //
+ statement_cache_.reset (new statement_cache_type (*this));
+ }
+
+ connection::
+ ~connection ()
+ {
+ // Destroy prepared query statements before freeing the connections.
+ //
+ recycle ();
+ clear_prepared_map ();
+ }
+
+ generic_statement& connection::
+ begin_statement ()
+ {
+ return static_cast<generic_statement&> (*begin_);
+ }
+
+ generic_statement& connection::
+ begin_immediate_statement ()
+ {
+ if (!begin_immediate_)
+ begin_immediate_.reset (
+ new (shared) generic_statement (*this, "BEGIN IMMEDIATE", 16));
+
+ return static_cast<generic_statement&> (*begin_immediate_);
+ }
+
+ generic_statement& connection::
+ begin_exclusive_statement ()
+ {
+ if (!begin_exclusive_)
+ begin_exclusive_.reset (
+ new (shared) generic_statement (*this, "BEGIN EXCLUSIVE", 16));
+
+ return static_cast<generic_statement&> (*begin_exclusive_);
+ }
+
+ generic_statement& connection::
+ commit_statement ()
+ {
+ return static_cast<generic_statement&> (*commit_);
+ }
+
+ generic_statement& connection::
+ rollback_statement ()
+ {
+ return static_cast<generic_statement&> (*rollback_);
+ }
+
+ transaction_impl* connection::
+ begin ()
+ {
+ return new transaction_impl (
+ connection_ptr (inc_ref (this)), transaction_impl::deferred);
+ }
+
+ transaction_impl* connection::
+ begin_immediate ()
+ {
+ return new transaction_impl (
+ connection_ptr (inc_ref (this)), transaction_impl::immediate);
+ }
+
+ transaction_impl* connection::
+ begin_exclusive ()
+ {
+ return new transaction_impl (
+ connection_ptr (inc_ref (this)), transaction_impl::exclusive);
+ }
+
+ unsigned long long connection::
+ execute (const char* s, std::size_t n)
+ {
+ generic_statement st (*this, s, n);
+ return st.execute ();
+ }
+
+ inline void
+ connection_unlock_callback (void** args, int n)
+ {
+ for (int i (0); i < n; ++i)
+ {
+ connection* c (static_cast<connection*> (args[i]));
+ details::lock l (c->unlock_mutex_);
+ c->unlocked_ = true;
+ c->unlock_cond_.signal ();
+ }
+ }
+
+ void connection::
+ wait ()
+ {
+#ifdef LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+ unlocked_ = false;
+
+ // unlock_notify() returns SQLITE_OK or SQLITE_LOCKED (deadlock).
+ //
+ int e (sqlite3_unlock_notify (handle (),
+ &odb_sqlite_connection_unlock_callback,
+ this));
+ if (e == SQLITE_LOCKED)
+ throw deadlock ();
+
+ details::lock l (unlock_mutex_);
+
+ while (!unlocked_)
+ unlock_cond_.wait (l);
+#else
+ translate_error (SQLITE_LOCKED, *this);
+#endif
+ }
+
+ void connection::
+ clear ()
+ {
+ invalidate_results ();
+
+ // The current first active_object may remove itself from the list and
+ // make the second object (if any) the new first.
+ //
+ for (active_object** pp (&active_objects_); *pp != 0; )
+ {
+ active_object* p (*pp);
+ p->clear ();
+
+ // Move to the next object if this one decided to stay on the list.
+ //
+ if (*pp == p)
+ pp = &p->next_;
+ }
+ }
+
+ // connection_factory
+ //
+ connection_factory::
+ ~connection_factory ()
+ {
+ }
+
+ void connection_factory::
+ database (database_type& db)
+ {
+ odb::connection_factory::db_ = &db;
+ db_ = &db;
+ }
+
+ void connection_factory::
+ attach_database (const connection_ptr& conn,
+ const std::string& name,
+ const std::string& schema)
+ {
+ conn->execute ("ATTACH DATABASE '" + name + "' AS \"" + schema + '"');
+ }
+
+ void connection_factory::
+ detach_database (const connection_ptr& conn, const std::string& schema)
+ {
+ conn->execute ("DETACH DATABASE \"" + schema + '"');
+ }
+ }
+}
+
+extern "C" void
+odb_sqlite_connection_unlock_callback (void** args, int n)
+{
+ odb::sqlite::connection_unlock_callback (args, n);
+}
diff --git a/libodb-sqlite/odb/sqlite/connection.hxx b/libodb-sqlite/odb/sqlite/connection.hxx
new file mode 100644
index 0000000..dbe4494
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/connection.hxx
@@ -0,0 +1,356 @@
+// file : odb/sqlite/connection.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_CONNECTION_HXX
+#define ODB_SQLITE_CONNECTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <sqlite3.h>
+
+#include <odb/statement.hxx>
+#include <odb/connection.hxx>
+
+#include <odb/details/mutex.hxx>
+#include <odb/details/condition.hxx>
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/unique-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/query.hxx>
+#include <odb/sqlite/tracer.hxx>
+#include <odb/sqlite/transaction-impl.hxx>
+#include <odb/sqlite/auto-handle.hxx>
+
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class statement_cache;
+ class generic_statement;
+ class connection_factory;
+ class attached_connection_factory;
+
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+
+ // SQLite "active object", i.e., an object that needs to be
+ // "cleared" before the transaction can be committed and the
+ // connection released. These form a doubly-linked list.
+ //
+ class LIBODB_SQLITE_EXPORT active_object
+ {
+ public:
+ // This function may remove the object from the list since it may no
+ // longer be "active".
+ //
+ virtual void
+ clear () = 0;
+
+ protected:
+ active_object (connection& c): prev_ (0), next_ (this), conn_ (c) {}
+
+ void
+ list_add ();
+
+ void
+ list_remove ();
+
+ protected:
+ friend class connection;
+
+ // prev_ == 0 means we are the first element.
+ // next_ == 0 means we are the last element.
+ // next_ == this means we are not on the list (prev_ should be 0).
+ //
+ active_object* prev_;
+ active_object* next_;
+
+ connection& conn_;
+ };
+
+ class LIBODB_SQLITE_EXPORT connection: public odb::connection
+ {
+ public:
+ typedef sqlite::statement_cache statement_cache_type;
+ typedef sqlite::database database_type;
+
+ // Translate the database schema in the statement text (used to
+ // implement attached databases). If the result is empty, then no
+ // translation is required and the original text should be used as is.
+ //
+ typedef void (statement_translator) (std::string& result,
+ const char* text,
+ std::size_t text_size,
+ connection&);
+ virtual
+ ~connection ();
+
+ connection (connection_factory&,
+ int extra_flags = 0,
+ statement_translator* = 0);
+
+ connection (connection_factory&,
+ sqlite3* handle,
+ statement_translator* = 0);
+
+ // Create an attached connection (see the attached database constructor
+ // for details).
+ //
+ connection (attached_connection_factory&, statement_translator*);
+
+ database_type&
+ database ();
+
+ // Return the main connection of an attached connection. If this
+ // connection is main, return itself.
+ //
+ connection&
+ main_connection ();
+
+ static connection_ptr
+ main_connection (const connection_ptr&);
+
+ public:
+ virtual transaction_impl*
+ begin ();
+
+ transaction_impl*
+ begin_immediate ();
+
+ transaction_impl*
+ begin_exclusive ();
+
+ public:
+ using odb::connection::execute;
+
+ virtual unsigned long long
+ execute (const char* statement, std::size_t length);
+
+ // Query preparation.
+ //
+ public:
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const sqlite::query_base&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const odb::query_base&);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef sqlite::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ using odb::connection::tracer;
+
+ public:
+ sqlite3*
+ handle ();
+
+ statement_cache_type&
+ statement_cache ()
+ {
+ return *statement_cache_;
+ }
+
+ // Wait for the locks to be released via unlock notification. Can
+ // be called after getting SQLITE_LOCKED_SHAREDCACHE.
+ //
+ void
+ wait ();
+
+ public:
+ // Reset active statements. Also invalidates query results by first
+ // calling invalidate_results().
+ //
+ void
+ clear ();
+
+ public:
+ // Note: only available on main connection.
+ //
+ generic_statement&
+ begin_statement ();
+
+ generic_statement&
+ begin_immediate_statement ();
+
+ generic_statement&
+ begin_exclusive_statement ();
+
+ generic_statement&
+ commit_statement ();
+
+ generic_statement&
+ rollback_statement ();
+
+ protected:
+ friend class attached_connection_factory;
+
+ connection_factory&
+ factory ();
+
+ private:
+ connection (const connection&);
+ connection& operator= (const connection&);
+
+ private:
+ void
+ init ();
+
+ private:
+ // Note that we use NULL handle as an indication of an attached
+ // connection.
+ //
+ auto_handle<sqlite3> handle_;
+
+ statement_translator* statement_translator_;
+
+ // Keep statement_cache_ after handle_ so that it is destroyed before
+ // the connection is closed.
+ //
+ details::unique_ptr<statement_cache_type> statement_cache_;
+
+ // Note: using odb::statement in order to break the connection-statement
+ // dependency cycle.
+ //
+ details::shared_ptr<odb::statement> begin_;
+ details::shared_ptr<odb::statement> begin_immediate_;
+ details::shared_ptr<odb::statement> begin_exclusive_;
+ details::shared_ptr<odb::statement> commit_;
+ details::shared_ptr<odb::statement> rollback_;
+
+ // Unlock notification machinery.
+ //
+ private:
+ bool unlocked_;
+ details::mutex unlock_mutex_;
+ details::condition unlock_cond_;
+
+ friend void
+ connection_unlock_callback (void**, int);
+
+ private:
+ friend class statement; // statement_translator_
+ friend class transaction_impl; // invalidate_results()
+
+ // Linked list of active objects currently associated
+ // with this connection.
+ //
+ private:
+ friend class active_object;
+ active_object* active_objects_;
+ };
+
+ class LIBODB_SQLITE_EXPORT connection_factory:
+ public odb::connection_factory
+ {
+ public:
+ typedef sqlite::database database_type;
+
+ virtual void
+ database (database_type&);
+
+ database_type&
+ database () {return *db_;}
+
+ virtual connection_ptr
+ connect () = 0;
+
+ virtual
+ ~connection_factory ();
+
+ connection_factory (): db_ (0) {}
+
+ // Attach/detach additional databases. Connection is one of the main
+ // connections created by this factory. Note: not called for "main" and
+ // "temp" schemas.
+ //
+ // The default implementations simply execute the ATTACH DATABASE and
+ // DETACH DATABASE SQLite statements.
+ //
+ virtual void
+ attach_database (const connection_ptr&,
+ const std::string& name,
+ const std::string& schema);
+
+ virtual void
+ detach_database (const connection_ptr&, const std::string& schema);
+
+ // Needed to break the circular connection_factory-database dependency
+ // (odb::connection_factory has the odb::database member).
+ //
+ protected:
+ database_type* db_;
+ };
+
+ // The call to database() should cause ATTACH DATABASE (or otherwise make
+ // sure the database is attached). Destruction of the factory should cause
+ // DETACH DATABASE (or otherwise notice that this factory no longer needs
+ // the database attached).
+ //
+ // Note that attached_connection_factory is an active object that
+ // registers itself with the main connection in order to get notified on
+ // transaction finalization.
+ //
+ class LIBODB_SQLITE_EXPORT attached_connection_factory:
+ public connection_factory,
+ public active_object
+ {
+ public:
+ explicit
+ attached_connection_factory (const connection_ptr& main)
+ : active_object (*main), main_connection_ (main) {}
+
+ virtual void
+ detach () = 0;
+
+ protected:
+ friend class database;
+ friend class connection;
+ friend class transaction_impl;
+
+ connection_factory&
+ main_factory ();
+
+ // Note that this essentially establishes a "framework" for all the
+ // attached connection factory implementations: they hold a counted
+ // reference to the main connection and they maintain a single shared
+ // attached connection.
+ //
+ connection_ptr main_connection_;
+ connection_ptr attached_connection_;
+ };
+ }
+}
+
+#include <odb/sqlite/connection.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_CONNECTION_HXX
diff --git a/libodb-sqlite/odb/sqlite/connection.ixx b/libodb-sqlite/odb/sqlite/connection.ixx
new file mode 100644
index 0000000..094fd52
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/connection.ixx
@@ -0,0 +1,108 @@
+// file : odb/sqlite/connection.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // active_object
+ //
+ inline void active_object::
+ list_add ()
+ {
+ next_ = conn_.active_objects_;
+ conn_.active_objects_ = this;
+
+ if (next_ != 0)
+ next_->prev_ = this;
+ }
+
+ inline void active_object::
+ list_remove ()
+ {
+ (prev_ == 0 ? conn_.active_objects_ : prev_->next_) = next_;
+
+ if (next_ != 0)
+ next_->prev_ = prev_;
+
+ prev_ = 0;
+ next_ = this;
+ }
+
+ // connection
+ //
+ inline database& connection::
+ database ()
+ {
+ return static_cast<connection_factory&> (factory_).database ();
+ }
+
+ inline connection& connection::
+ main_connection ()
+ {
+ return handle_ != 0
+ ? *this
+ : *static_cast<attached_connection_factory&> (factory_).main_connection_;
+ }
+
+ inline connection_ptr connection::
+ main_connection (const connection_ptr& c)
+ {
+ return c->handle_ != 0
+ ? c
+ : static_cast<attached_connection_factory&> (c->factory_).main_connection_;
+ }
+
+ inline sqlite3* connection::
+ handle ()
+ {
+ return handle_ != 0
+ ? handle_
+ : static_cast<attached_connection_factory&> (factory_).main_connection_->handle_;
+ }
+
+ inline connection_factory& connection::
+ factory ()
+ {
+ return static_cast<connection_factory&> (factory_);
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const sqlite::query_base& q)
+ {
+ return query_<T, id_sqlite>::call (*this, n, q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return prepare_query<T> (n, sqlite::query_base (q));
+ }
+
+ // attached_connection_factory
+ //
+ inline connection_factory& attached_connection_factory::
+ main_factory ()
+ {
+ return main_connection_->factory ();
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/container-statements.hxx b/libodb-sqlite/odb/sqlite/container-statements.hxx
new file mode 100644
index 0000000..b9cccf0
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/container-statements.hxx
@@ -0,0 +1,355 @@
+// file : odb/sqlite/container-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_CONTAINER_STATEMENTS_HXX
+#define ODB_SQLITE_CONTAINER_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/binding.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class connection;
+
+ // Template argument is the generated abstract container traits type.
+ // That is, it doesn't need to provide column counts and statements.
+ //
+ template <typename T>
+ class container_statements
+ {
+ public:
+ typedef T traits;
+
+ typedef typename traits::data_image_type data_image_type;
+ typedef typename traits::functions_type functions_type;
+
+ typedef sqlite::insert_statement insert_statement_type;
+ typedef sqlite::select_statement select_statement_type;
+ typedef sqlite::delete_statement delete_statement_type;
+
+ typedef sqlite::connection connection_type;
+
+ container_statements (connection_type&, binding& id_binding);
+
+ connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // Functions.
+ //
+ functions_type&
+ functions ()
+ {
+ return functions_;
+ }
+
+ // Schema version.
+ //
+ const schema_version_migration&
+ version_migration () const {return *svm_;}
+
+ void
+ version_migration (const schema_version_migration& svm) {svm_ = &svm;}
+
+ // Id image binding (external).
+ //
+ const binding&
+ id_binding ()
+ {
+ return id_binding_;
+ }
+
+ // Data image. The image is split into the id (that comes as a
+ // binding) and index/key plus value which are in data_image_type.
+ // The select binding is a subset of the full binding (no id).
+ //
+ data_image_type&
+ data_image ()
+ {
+ return data_image_;
+ }
+
+ bind*
+ data_bind ()
+ {
+ return insert_image_binding_.bind;
+ }
+
+ bool
+ data_binding_test_version () const
+ {
+ return data_id_binding_version_ != id_binding_.version ||
+ data_image_version_ != data_image_.version ||
+ insert_image_binding_.version == 0;
+ }
+
+ void
+ data_binding_update_version ()
+ {
+ data_id_binding_version_ = id_binding_.version;
+ data_image_version_ = data_image_.version;
+ insert_image_binding_.version++;
+ select_image_binding_.version++;
+ }
+
+ bool*
+ select_image_truncated ()
+ {
+ return select_image_truncated_;
+ }
+
+ //
+ // Statements.
+ //
+
+ insert_statement_type&
+ insert_statement ()
+ {
+ if (insert_ == 0)
+ insert_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ insert_text_,
+ versioned_, // Process if versioned.
+ insert_image_binding_,
+ 0));
+
+ return *insert_;
+ }
+
+ select_statement_type&
+ select_statement ()
+ {
+ if (select_ == 0)
+ select_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ select_text_,
+ versioned_, // Process if versioned.
+ false, // Don't optimize.
+ id_binding_,
+ select_image_binding_));
+
+ return *select_;
+ }
+
+ delete_statement_type&
+ delete_statement ()
+ {
+ if (delete_ == 0)
+ delete_.reset (
+ new (details::shared) delete_statement_type (
+ conn_, delete_text_, id_binding_));
+
+ return *delete_;
+ }
+
+ private:
+ container_statements (const container_statements&);
+ container_statements& operator= (const container_statements&);
+
+ protected:
+ connection_type& conn_;
+ binding& id_binding_;
+
+ functions_type functions_;
+
+ data_image_type data_image_;
+ std::size_t data_image_version_;
+ std::size_t data_id_binding_version_;
+
+ binding insert_image_binding_;
+
+ binding select_image_binding_;
+ bool* select_image_truncated_;
+
+ const char* insert_text_;
+ const char* select_text_;
+ const char* delete_text_;
+
+ bool versioned_;
+ const schema_version_migration* svm_;
+
+ details::shared_ptr<insert_statement_type> insert_;
+ details::shared_ptr<select_statement_type> select_;
+ details::shared_ptr<delete_statement_type> delete_;
+ };
+
+ template <typename T>
+ class smart_container_statements: public container_statements<T>
+ {
+ public:
+ typedef T traits;
+ typedef typename traits::cond_image_type cond_image_type;
+
+ typedef sqlite::update_statement update_statement_type;
+ typedef sqlite::delete_statement delete_statement_type;
+
+ typedef sqlite::connection connection_type;
+
+ smart_container_statements (connection_type&, binding& id_binding);
+
+ // Condition image. The image is split into the id (that comes as
+ // a binding) and index/key/value which is in cond_image_type.
+ //
+ cond_image_type&
+ cond_image ()
+ {
+ return cond_image_;
+ }
+
+ bind*
+ cond_bind ()
+ {
+ return cond_image_binding_.bind;
+ }
+
+ bool
+ cond_binding_test_version () const
+ {
+ return cond_id_binding_version_ != this->id_binding_.version ||
+ cond_image_version_ != cond_image_.version ||
+ cond_image_binding_.version == 0;
+ }
+
+ void
+ cond_binding_update_version ()
+ {
+ cond_id_binding_version_ = this->id_binding_.version;
+ cond_image_version_ = cond_image_.version;
+ cond_image_binding_.version++;
+ }
+
+ // Update image. The image is split as follows: value comes
+ // from the data image, id comes as binding, and index/key
+ // comes from the condition image.
+ //
+ bind*
+ update_bind ()
+ {
+ return update_image_binding_.bind;
+ }
+
+ bool
+ update_binding_test_version () const
+ {
+ return update_id_binding_version_ != this->id_binding_.version ||
+ update_cond_image_version_ != cond_image_.version ||
+ update_data_image_version_ != this->data_image_.version ||
+ update_image_binding_.version == 0;
+ }
+
+ void
+ update_binding_update_version ()
+ {
+ update_id_binding_version_ = this->id_binding_.version;
+ update_cond_image_version_ = cond_image_.version;
+ update_data_image_version_ = this->data_image_.version;
+ update_image_binding_.version++;
+ }
+
+ //
+ // Statements.
+ //
+
+ delete_statement_type&
+ delete_statement ()
+ {
+ if (this->delete_ == 0)
+ this->delete_.reset (
+ new (details::shared) delete_statement_type (
+ this->conn_,
+ this->delete_text_,
+ this->cond_image_binding_));
+
+ return *this->delete_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ this->conn_,
+ update_text_,
+ this->versioned_, // Process if versioned.
+ update_image_binding_));
+
+ return *update_;
+ }
+
+ protected:
+ cond_image_type cond_image_;
+ std::size_t cond_image_version_;
+ std::size_t cond_id_binding_version_;
+ binding cond_image_binding_;
+
+ std::size_t update_id_binding_version_;
+ std::size_t update_cond_image_version_;
+ std::size_t update_data_image_version_;
+ binding update_image_binding_;
+
+ const char* update_text_;
+
+ details::shared_ptr<update_statement_type> update_;
+ };
+
+ // Template argument is the generated concrete container traits type.
+ //
+ template <typename T>
+ class container_statements_impl: public T::statements_type
+ {
+ public:
+ typedef T traits;
+ typedef typename T::statements_type base;
+ typedef sqlite::connection connection_type;
+
+ container_statements_impl (connection_type&, binding&);
+
+ private:
+ container_statements_impl (const container_statements_impl&);
+ container_statements_impl& operator= (const container_statements_impl&);
+
+ private:
+ bind data_image_bind_[traits::data_column_count];
+ bool select_image_truncated_array_[traits::data_column_count -
+ traits::id_column_count];
+ };
+
+ template <typename T>
+ class smart_container_statements_impl: public container_statements_impl<T>
+ {
+ public:
+ typedef T traits;
+ typedef sqlite::connection connection_type;
+
+ smart_container_statements_impl (connection_type&, binding&);
+
+ private:
+ bind cond_image_bind_[traits::cond_column_count];
+ bind update_image_bind_[traits::value_column_count +
+ traits::cond_column_count];
+ };
+ }
+}
+
+#include <odb/sqlite/container-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_CONTAINER_STATEMENTS_HXX
diff --git a/libodb-sqlite/odb/sqlite/container-statements.txx b/libodb-sqlite/odb/sqlite/container-statements.txx
new file mode 100644
index 0000000..6db91f2
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/container-statements.txx
@@ -0,0 +1,107 @@
+// file : odb/sqlite/container-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // container_statements
+ //
+ template <typename T>
+ container_statements<T>::
+ container_statements (connection_type& conn, binding& id)
+ : conn_ (conn),
+ id_binding_ (id),
+ functions_ (this),
+ insert_image_binding_ (0, 0), // Initialized by impl.
+ select_image_binding_ (0, 0), // Initialized by impl.
+ svm_ (0)
+ {
+ functions_.insert_ = &traits::insert;
+ functions_.select_ = &traits::select;
+ functions_.delete__ = &traits::delete_;
+
+ data_image_.version = 0;
+ data_image_version_ = 0;
+ data_id_binding_version_ = 0;
+ }
+
+ // smart_container_statements
+ //
+ template <typename T>
+ smart_container_statements<T>::
+ smart_container_statements (connection_type& conn, binding& id)
+ : container_statements<T> (conn, id),
+ cond_image_binding_ (0, 0), // Initialized by impl.
+ update_image_binding_ (0, 0) // Initialized by impl.
+ {
+ this->functions_.update_ = &traits::update;
+
+ cond_image_.version = 0;
+ cond_image_version_ = 0;
+ cond_id_binding_version_ = 0;
+
+ update_id_binding_version_ = 0;
+ update_cond_image_version_ = 0;
+ update_data_image_version_ = 0;
+ }
+
+ // container_statements_impl
+ //
+ template <typename T>
+ container_statements_impl<T>::
+ container_statements_impl (connection_type& conn, binding& id)
+ : base (conn, id)
+ {
+ this->select_image_truncated_ = select_image_truncated_array_;
+
+ this->insert_image_binding_.bind = data_image_bind_;
+ this->insert_image_binding_.count = traits::data_column_count;
+
+ this->select_image_binding_.bind = data_image_bind_ +
+ traits::id_column_count;
+ this->select_image_binding_.count = traits::data_column_count -
+ traits::id_column_count;
+
+ std::memset (data_image_bind_, 0, sizeof (data_image_bind_));
+ std::memset (select_image_truncated_array_,
+ 0,
+ sizeof (select_image_truncated_array_));
+
+ for (std::size_t i (0);
+ i < traits::data_column_count - traits::id_column_count;
+ ++i)
+ data_image_bind_[i + traits::id_column_count].truncated =
+ select_image_truncated_array_ + i;
+
+ this->insert_text_ = traits::insert_statement;
+ this->select_text_ = traits::select_statement;
+ this->delete_text_ = traits::delete_statement;
+
+ this->versioned_ = traits::versioned;
+ }
+
+ // smart_container_statements_impl
+ //
+ template <typename T>
+ smart_container_statements_impl<T>::
+ smart_container_statements_impl (connection_type& conn, binding& id)
+ : container_statements_impl<T> (conn, id)
+ {
+ this->cond_image_binding_.bind = cond_image_bind_;
+ this->cond_image_binding_.count = traits::cond_column_count;
+
+ this->update_image_binding_.bind = update_image_bind_;
+ this->update_image_binding_.count = traits::value_column_count +
+ traits::cond_column_count;
+
+ std::memset (cond_image_bind_, 0, sizeof (cond_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+
+ this->update_text_ = traits::update_statement;
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/database.cxx b/libodb-sqlite/odb/sqlite/database.cxx
new file mode 100644
index 0000000..a7cf098
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/database.cxx
@@ -0,0 +1,306 @@
+// file : odb/sqlite/database.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifdef _WIN32
+# include <odb/details/win32/windows.hxx> // WideCharToMultiByte
+#endif
+
+#include <sqlite3.h>
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/connection-factory.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/transaction.hxx>
+#include <odb/sqlite/error.hxx>
+#include <odb/sqlite/exceptions.hxx>
+
+#include <odb/sqlite/details/options.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace sqlite
+ {
+ using odb::details::transfer_ptr;
+
+ database::
+ ~database ()
+ {
+ }
+
+ database::
+ database (const string& name,
+ int flags,
+ bool foreign_keys,
+ const string& vfs,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_sqlite),
+ name_ (name),
+ flags_ (flags),
+ foreign_keys_ (foreign_keys),
+ vfs_ (vfs),
+ factory_ (factory.transfer ())
+ {
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+#ifdef _WIN32
+ database::
+ database (const wstring& name,
+ int flags,
+ bool foreign_keys,
+ const string& vfs,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_sqlite),
+ flags_ (flags),
+ foreign_keys_ (foreign_keys),
+ vfs_ (vfs),
+ factory_ (factory.transfer ())
+ {
+ // Convert UTF-16 name to UTF-8 using the WideCharToMultiByte() Win32
+ // function.
+ //
+ int n (
+ WideCharToMultiByte (
+ CP_UTF8,
+ 0,
+ name.c_str (),
+ static_cast<int> (name.size ()),
+ 0,
+ 0,
+ 0,
+ 0));
+
+ if (n == 0)
+ throw database_exception (
+ SQLITE_CANTOPEN, SQLITE_CANTOPEN, "unable to open database file");
+
+ // This string is not shared so we are going to modify the underlying
+ // buffer directly.
+ //
+ name_.resize (static_cast<string::size_type> (n));
+
+ n = WideCharToMultiByte (
+ CP_UTF8,
+ 0,
+ name.c_str (),
+ static_cast<int> (name.size ()),
+ const_cast<char*> (name_.c_str ()),
+ n,
+ 0,
+ 0);
+
+ if (n == 0)
+ throw database_exception (
+ SQLITE_CANTOPEN, SQLITE_CANTOPEN, "unable to open database file");
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+#endif
+
+ database::
+ database (int& argc,
+ char* argv[],
+ bool erase,
+ int flags,
+ bool foreign_keys,
+ const string& vfs,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_sqlite),
+ flags_ (flags),
+ foreign_keys_ (foreign_keys),
+ vfs_ (vfs),
+ factory_ (factory.transfer ())
+ {
+ using namespace details;
+
+ try
+ {
+ cli::argv_file_scanner scan (argc, argv, "--options-file", erase);
+ options ops (scan, cli::unknown_mode::skip, cli::unknown_mode::skip);
+
+ name_ = ops.database ();
+
+ if (ops.create ())
+ flags_ |= SQLITE_OPEN_CREATE;
+
+ if (ops.read_only ())
+ flags_ = (flags_ & ~SQLITE_OPEN_READWRITE) | SQLITE_OPEN_READONLY;
+ }
+ catch (const cli::exception& e)
+ {
+ ostringstream ostr;
+ ostr << e;
+ throw cli_exception (ostr.str ());
+ }
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (const connection_ptr& conn,
+ const string& name,
+ const string& schema,
+ transfer_ptr<attached_connection_factory> factory)
+ : odb::database (id_sqlite),
+ name_ (name),
+ schema_ (schema),
+ flags_ (0),
+ factory_ (factory.transfer ())
+ {
+ assert (!schema_.empty ());
+
+ // Copy some things over from the connection's database.
+ //
+ database& db (conn->database ());
+
+ tracer_ = db.tracer_;
+ foreign_keys_ = db.foreign_keys_;
+
+ if (!factory_)
+ factory_.reset (new default_attached_connection_factory (
+ connection::main_connection (conn)));
+
+ factory_->database (*this);
+ }
+
+ void database::
+ print_usage (ostream& os)
+ {
+ details::options::print_usage (os);
+ }
+
+ transaction_impl* database::
+ begin ()
+ {
+ return new transaction_impl (*this, transaction_impl::deferred);
+ }
+
+ transaction_impl* database::
+ begin_immediate ()
+ {
+ return new transaction_impl (*this, transaction_impl::immediate);
+ }
+
+ transaction_impl* database::
+ begin_exclusive ()
+ {
+ return new transaction_impl (*this, transaction_impl::exclusive);
+ }
+
+ odb::connection* database::
+ connection_ ()
+ {
+ connection_ptr c (factory_->connect ());
+ return c.release ();
+ }
+
+ const database::schema_version_info& database::
+ load_schema_version (const string& name) const
+ {
+ schema_version_info& svi (schema_version_map_[name]);
+
+ // Construct the SELECT statement text.
+ //
+ string text ("SELECT \"version\", \"migration\" FROM ");
+
+ if (!svi.version_table.empty ())
+ text += svi.version_table; // Already quoted.
+ else if (!schema_version_table_.empty ())
+ text += schema_version_table_; // Already quoted.
+ else
+ text += "\"main\".\"schema_version\"";
+
+ text += " WHERE \"name\" = ?";
+
+ // Bind parameters and results.
+ //
+ size_t psize[1] = {name.size ()};
+ bind pbind[1] = {{bind::text,
+ const_cast<char*> (name.c_str ()),
+ &psize[0],
+ 0, 0, 0}};
+ binding param (pbind, 1);
+ param.version++;
+
+ long long migration;
+ bool rnull[2];
+ bind rbind[2] = {{bind::integer, &svi.version, 0, 0, &rnull[0], 0},
+ {bind::integer, &migration, 0, 0, &rnull[1], 0}};
+ binding result (rbind, 2);
+ result.version++;
+
+ // If we are not in transaction, SQLite will start an implicit one
+ // which suits us just fine.
+ //
+ connection_ptr cp;
+ if (!transaction::has_current ())
+ cp = factory_->connect ();
+
+ sqlite::connection& c (
+ cp != 0
+ ? *cp
+ : transaction::current ().connection (const_cast<database&> (*this)));
+
+ try
+ {
+ select_statement st (c,
+ text,
+ false, // Don't process.
+ false, // Don't optimize.
+ param,
+ result);
+ st.execute ();
+ auto_result ar (st);
+
+ switch (st.fetch ())
+ {
+ case select_statement::success:
+ {
+ svi.migration = migration != 0;
+ assert (st.fetch () == select_statement::no_data);
+ break;
+ }
+ case select_statement::no_data:
+ {
+ svi.version = 0; // No schema.
+ break;
+ }
+ case select_statement::truncated:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+ catch (const database_exception& e)
+ {
+ // Try to detect the case where there is no version table. SQLite
+ // doesn't have an extended error code for this so we have to use
+ // the error text.
+ //
+ if (e.error () == SQLITE_ERROR &&
+ e.message ().compare (0, 14, "no such table:") == 0)
+ svi.version = 0; // No schema.
+ else
+ throw;
+ }
+
+ return svi;
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/database.hxx b/libodb-sqlite/odb/sqlite/database.hxx
new file mode 100644
index 0000000..e1e62cc
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/database.hxx
@@ -0,0 +1,567 @@
+// file : odb/sqlite/database.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_DATABASE_HXX
+#define ODB_SQLITE_DATABASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <sqlite3.h>
+
+#include <string>
+#include <iosfwd> // std::ostream
+
+#include <odb/database.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+#include <odb/details/unique-ptr.hxx>
+#include <odb/details/transfer-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/query.hxx>
+#include <odb/sqlite/tracer.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/connection-factory.hxx>
+#include <odb/sqlite/transaction-impl.hxx>
+
+#include <odb/sqlite/details/export.hxx>
+
+// We use the sqlite3_open_v2() flags in our interface. Define them
+// for SQLite earlier that 3.5.0.
+//
+#if SQLITE_VERSION_NUMBER < 3005000
+# define SQLITE_OPEN_READONLY 0x00000001
+# define SQLITE_OPEN_READWRITE 0x00000002
+# define SQLITE_OPEN_CREATE 0x00000004
+#endif
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class transaction_impl;
+
+ class LIBODB_SQLITE_EXPORT database: public odb::database
+ {
+ public:
+ database (const std::string& name,
+ int flags = SQLITE_OPEN_READWRITE,
+ bool foreign_keys = true,
+ const std::string& vfs = "",
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+#ifdef _WIN32
+ database (const std::wstring& name,
+ int flags = SQLITE_OPEN_READWRITE,
+ bool foreign_keys = true,
+ const std::string& vfs = "",
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+#endif
+
+ // Extract the database parameters from the command line. The
+ // following options are recognized:
+ //
+ // --database
+ // --create
+ // --read-only
+ // --options-file
+ //
+ // For more information, see the output of the print_usage() function
+ // below. If erase is true, the above options are removed from the argv
+ // array and the argc count is updated accordingly. The command line
+ // options override the flags passed as an argument. This constructor
+ // may throw the cli_exception exception.
+ //
+ database (int& argc,
+ char* argv[],
+ bool erase = false,
+ int flags = SQLITE_OPEN_READWRITE,
+ bool foreign_keys = true,
+ const std::string& vfs = "",
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Attach to the specified connection a database with the specified name
+ // as the specified schema. Good understanding of SQLite ATTACH/DETACH
+ // DATABASE statements semantics and ODB connection management is
+ // strongly recommended when using this mechanism.
+ //
+ // The resulting database instance is referred to as an "attached
+ // database" and the connection it returns as an "attached connection"
+ // (which is just a proxy for the main connection). Database operations
+ // executed on the attached database or attached connection are
+ // automatically translated to refer to the specified schema rather than
+ // "main". For uniformity attached databases can also be created for the
+ // pre-attached "main" and "temp" schemas (in this case name can be
+ // anything).
+ //
+ // The automatic translation of the statements relies on their text
+ // having references to top-level database entities (tables, indexes,
+ // etc) qualified with the "main" schema. To achieve this, compile your
+ // headers with `--schema main` and, if using schema migration, with
+ // `--schema-version-table main.schema_version`. You must also not use
+ // "main" as an object/table alias in views of native statements. For
+ // optimal translation performance use 4-character schema names.
+ //
+ // The main connection and attached to it databases and connections are
+ // all meant to be used within the same thread. In particular, the
+ // attached database holds a counted reference to the main connection
+ // which means the connection will not be released until all the
+ // attached to this connection databases are destroyed.
+ //
+ // Note that in this model the attached databases are attached to the
+ // main connection, not to the (main) database, which mimics the
+ // underlying semantics of SQLite. An alternative model would have been
+ // to notionally attach the databases to the main database and under the
+ // hood automatically attach them to each returned connection. While
+ // this may seem like a more convenient model in some cases, it is also
+ // less flexible: the current model allows attaching a different set of
+ // databases to different connections, attaching them on demand as the
+ // transaction progresses, etc. Also, the more convenient model can be
+ // implemented on top of this model by deriving an application-specific
+ // database class and/or providing custom connection factories.
+ //
+ // Note that unless the name is a URI with appropriate mode, it is
+ // opened with the SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE flags. So if
+ // you want just SQLITE_OPEN_READWRITE, then you will need to verify its
+ // existence manually prior to calling this constructor.
+ //
+ // Note that attaching/detaching databases withing a transaction is only
+ // supported since SQLite 3.21.0.
+ //
+ database (const connection_ptr&,
+ const std::string& name,
+ const std::string& schema,
+ details::transfer_ptr<attached_connection_factory> =
+ details::transfer_ptr<attached_connection_factory> ());
+
+ // The database is automatically detached on destruction but a failure
+ // to detach is ignored. To detect such a failure perform explicit
+ // detach. For uniformity detaching a main database is a no-op.
+ //
+ void
+ detach ();
+
+ // Return the main database of an attached database. If this database
+ // is main, return itself.
+ //
+ database&
+ main_database ();
+
+ // Move-constructible but not move-assignable.
+ //
+ // Note: noexcept is not specified since odb::database(odb::database&&)
+ // can throw.
+ //
+#ifdef ODB_CXX11
+ database (database&&);
+#endif
+
+ static void
+ print_usage (std::ostream&);
+
+ public:
+ const std::string&
+ name () const
+ {
+ return name_;
+ }
+
+ // Schema name under which this database was attached or empty for the
+ // main database.
+ //
+ const std::string&
+ schema () const
+ {
+ return schema_;
+ }
+
+ int
+ flags () const
+ {
+ return flags_;
+ }
+
+ bool
+ foreign_keys () const
+ {
+ return foreign_keys_;
+ }
+
+ const std::string&
+ vfs () const
+ {
+ return vfs_;
+ }
+
+ // Object persistence API.
+ //
+ public:
+
+ // Make the object persistent.
+ //
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Load an object. Throw object_not_persistent if not found.
+ //
+ 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);
+
+ // Load (or reload, if it is already loaded) a section of an object.
+ //
+ template <typename T>
+ void
+ load (T& object, section&);
+
+ // Reload an object.
+ //
+ template <typename T>
+ void
+ reload (T& object);
+
+ template <typename T>
+ void
+ reload (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ reload (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Loan an object if found. Return NULL/false if not found.
+ //
+ 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);
+
+ // Update the state of a modified objects.
+ //
+ template <typename T>
+ void
+ update (T& object);
+
+ template <typename T>
+ void
+ update (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ update (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Update a section of an object. Throws the section_not_loaded
+ // exception if the section is not loaded. Note also that this
+ // function does not clear the changed flag if it is set.
+ //
+ template <typename T>
+ void
+ update (const T& object, const section&);
+
+ // Make the object transient. Throw object_not_persistent if not
+ // found.
+ //
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::id_type& id);
+
+ template <typename T>
+ void
+ erase (T& object);
+
+ template <typename T>
+ void
+ erase (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Erase multiple objects matching a query predicate.
+ //
+ template <typename T>
+ unsigned long long
+ erase_query ();
+
+ template <typename T>
+ unsigned long long
+ erase_query (const char*);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const std::string&);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const sqlite::query_base&);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const odb::query_base&);
+
+ // Query API.
+ //
+ template <typename T>
+ result<T>
+ query ();
+
+ template <typename T>
+ result<T>
+ query (const char*);
+
+ template <typename T>
+ result<T>
+ query (const std::string&);
+
+ template <typename T>
+ result<T>
+ query (const sqlite::query_base&);
+
+ template <typename T>
+ result<T>
+ query (const odb::query_base&);
+
+ // Query one API.
+ //
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one ();
+
+ template <typename T>
+ bool
+ query_one (T& object);
+
+ template <typename T>
+ T
+ query_value ();
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const char*);
+
+ template <typename T>
+ bool
+ query_one (const char*, T& object);
+
+ template <typename T>
+ T
+ query_value (const char*);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const std::string&);
+
+ template <typename T>
+ bool
+ query_one (const std::string&, T& object);
+
+ template <typename T>
+ T
+ query_value (const std::string&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const sqlite::query_base&);
+
+ template <typename T>
+ bool
+ query_one (const sqlite::query_base&, T& object);
+
+ template <typename T>
+ T
+ query_value (const sqlite::query_base&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const odb::query_base&);
+
+ template <typename T>
+ bool
+ query_one (const odb::query_base&, T& object);
+
+ template <typename T>
+ T
+ query_value (const odb::query_base&);
+
+ // Query preparation.
+ //
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const sqlite::query_base&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const odb::query_base&);
+
+ // Transactions.
+ //
+ public:
+ virtual transaction_impl*
+ begin ();
+
+ transaction_impl*
+ begin_immediate ();
+
+ transaction_impl*
+ begin_exclusive ();
+
+ public:
+ connection_ptr
+ connection ();
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef sqlite::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::database::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::database::tracer (t);
+ }
+
+ using odb::database::tracer;
+
+ // Database schema version.
+ //
+ protected:
+ virtual const schema_version_info&
+ load_schema_version (const std::string& schema_name) const;
+
+ // Database id constant (useful for meta-programming).
+ //
+ public:
+ static const odb::database_id database_id = id_sqlite;
+
+ public:
+ virtual
+ ~database ();
+
+ protected:
+ virtual odb::connection*
+ connection_ ();
+
+ private:
+ friend class transaction_impl; // factory_
+
+ // Note: remember to update move ctor if adding any new members.
+ //
+ std::string name_;
+ std::string schema_;
+ int flags_;
+ bool foreign_keys_;
+ std::string vfs_;
+
+ // Note: keep last so that all other database members are still valid
+ // during factory's destruction.
+ //
+ details::unique_ptr<connection_factory> factory_;
+ };
+ }
+}
+
+#include <odb/sqlite/database.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_DATABASE_HXX
diff --git a/libodb-sqlite/odb/sqlite/database.ixx b/libodb-sqlite/odb/sqlite/database.ixx
new file mode 100644
index 0000000..e906a39
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/database.ixx
@@ -0,0 +1,622 @@
+// file : odb/sqlite/database.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <utility> // move()
+
+#include <odb/sqlite/transaction.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+#ifdef ODB_CXX11
+ inline database::
+ database (database&& db) // Has to be inline.
+ : odb::database (std::move (db)),
+ name_ (std::move (db.name_)),
+ schema_ (std::move (db.schema_)),
+ flags_ (db.flags_),
+ foreign_keys_ (db.foreign_keys_),
+ vfs_ (std::move (db.vfs_)),
+ factory_ (std::move (db.factory_))
+ {
+ factory_->database (*this); // New database instance.
+ }
+#endif
+
+ inline void database::
+ detach ()
+ {
+ if (!schema_.empty ())
+ static_cast<attached_connection_factory&> (*factory_).detach ();
+ }
+
+ inline database& database::
+ main_database ()
+ {
+ return schema_.empty ()
+ ? *this
+ : static_cast<attached_connection_factory&> (*factory_).main_connection_->database ();
+ }
+
+ inline connection_ptr database::
+ connection ()
+ {
+ // Go through the virtual connection_() function instead of
+ // directly to allow overriding.
+ //
+ return connection_ptr (
+ static_cast<sqlite::connection*> (connection_ ()));
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T& obj)
+ {
+ return persist_<T, id_sqlite> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const T& obj)
+ {
+ return persist_<const T, id_sqlite> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T>& p)
+ {
+ const P<T>& cr (p);
+ return persist<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ return persist<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const typename object_traits<T>::pointer_type& pobj)
+ {
+ return persist_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ load (const typename object_traits<T>::id_type& id)
+ {
+ return load_<T, id_sqlite> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ load (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return load_<T, id_sqlite> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ load (T& obj, section& s)
+ {
+ return load_<T, id_sqlite> (obj, s);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ find (const typename object_traits<T>::id_type& id)
+ {
+ return find_<T, id_sqlite> (id);
+ }
+
+ template <typename T>
+ inline bool database::
+ find (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return find_<T, id_sqlite> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T& obj)
+ {
+ reload_<T, id_sqlite> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T* p)
+ {
+ reload<T> (*p);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (const P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (const P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T>
+ inline void database::
+ reload (const typename object_traits<T>::pointer_type& pobj)
+ {
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ reload (odb::pointer_traits<pointer_type>::get_ref (pobj));
+ }
+
+ template <typename T>
+ inline void database::
+ update (T& obj)
+ {
+ update_<T, id_sqlite> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ update (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (P<T>& p)
+ {
+ const P<T>& cr (p);
+ update<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ update<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const typename object_traits<T>::pointer_type& pobj)
+ {
+ update_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const T& obj, const section& s)
+ {
+ update_<T, id_sqlite> (obj, s);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::id_type& id)
+ {
+ return erase_<T, id_sqlite> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T& obj)
+ {
+ return erase_<T, id_sqlite> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (P<T>& p)
+ {
+ const P<T>& cr (p);
+ erase<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ erase<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::pointer_type& pobj)
+ {
+ erase_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query ()
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (sqlite::query_base ());
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const char* q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const std::string& q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const sqlite::query_base& q)
+ {
+ // T is always object_type.
+ //
+ return object_traits_impl<T, id_sqlite>::erase_query (*this, q);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return erase_query<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query ()
+ {
+ return query<T> (sqlite::query_base ());
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const char* q)
+ {
+ return query<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const std::string& q)
+ {
+ return query<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const sqlite::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_<T, id_sqlite>::call (*this, q);
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one ()
+ {
+ return query_one<T> (sqlite::query_base ());
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (T& o)
+ {
+ return query_one<T> (sqlite::query_base (), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value ()
+ {
+ return query_value<T> (sqlite::query_base ());
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const char* q)
+ {
+ return query_one<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const char* q, T& o)
+ {
+ return query_one<T> (sqlite::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const char* q)
+ {
+ return query_value<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const std::string& q)
+ {
+ return query_one<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const std::string& q, T& o)
+ {
+ return query_one<T> (sqlite::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const std::string& q)
+ {
+ return query_value<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const sqlite::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_one_<T, id_sqlite> (q);
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const sqlite::query_base& q, T& o)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_one_<T, id_sqlite> (q, o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const sqlite::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_value_<T, id_sqlite> (q);
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query_one<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const odb::query_base& q, T& o)
+ {
+ // Translate to native query.
+ //
+ return query_one<T> (sqlite::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query_value<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const sqlite::query_base& q)
+ {
+ // Throws if not in transaction.
+ //
+ sqlite::connection& c (transaction::current ().connection (*this));
+ return c.prepare_query<T> (n, q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return prepare_query<T> (n, sqlite::query_base (q));
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/details/.gitignore b/libodb-sqlite/odb/sqlite/details/.gitignore
new file mode 100644
index 0000000..b298f89
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/.gitignore
@@ -0,0 +1 @@
+/options.?xx
diff --git a/libodb-sqlite/odb/sqlite/details/config.hxx b/libodb-sqlite/odb/sqlite/details/config.hxx
new file mode 100644
index 0000000..98f4bca
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/config.hxx
@@ -0,0 +1,23 @@
+// file : odb/sqlite/details/config.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_DETAILS_CONFIG_HXX
+#define ODB_SQLITE_DETAILS_CONFIG_HXX
+
+// no pre
+
+#ifdef ODB_COMPILER
+# error libodb-sqlite header included in odb-compiled header
+#endif
+
+#include <sqlite3.h>
+
+#if SQLITE_VERSION_NUMBER >= 3006012
+# define LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY 1
+#endif
+
+#define LIBODB_SQLITE_HAVE_COLUMN_METADATA 1
+
+// no post
+
+#endif // ODB_SQLITE_DETAILS_CONFIG_HXX
diff --git a/libodb-sqlite/odb/sqlite/details/conversion.hxx b/libodb-sqlite/odb/sqlite/details/conversion.hxx
new file mode 100644
index 0000000..04843b2
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/conversion.hxx
@@ -0,0 +1,58 @@
+// file : odb/sqlite/details/conversion.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_DETAILS_CONVERSION_HXX
+#define ODB_SQLITE_DETAILS_CONVERSION_HXX
+
+#include <odb/sqlite/traits.hxx>
+
+#include <odb/details/meta/answer.hxx>
+
+namespace odb
+{
+ // @@ Revise this.
+ //
+ namespace details {}
+
+ namespace sqlite
+ {
+ namespace details
+ {
+ using namespace odb::details;
+
+ // Detect whether conversion is specified in type_traits.
+ //
+ template <typename T>
+ meta::yes
+ conversion_p_test (typename type_traits<T>::conversion*);
+
+ template <typename T>
+ meta::no
+ conversion_p_test (...);
+
+ template <typename T>
+ struct conversion_p
+ {
+ static const bool value =
+ sizeof (conversion_p_test<T> (0)) == sizeof (meta::yes);
+ };
+
+ template <typename T, bool = conversion_p<T>::value>
+ struct conversion;
+
+ template <typename T>
+ struct conversion<T, true>
+ {
+ static const char* to () {return type_traits<T>::conversion::to ();}
+ };
+
+ template <typename T>
+ struct conversion<T, false>
+ {
+ static const char* to () {return 0;}
+ };
+ }
+ }
+}
+
+#endif // ODB_SQLITE_DETAILS_CONVERSION_HXX
diff --git a/libodb-sqlite/odb/sqlite/details/export.hxx b/libodb-sqlite/odb/sqlite/details/export.hxx
new file mode 100644
index 0000000..639db2a
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/export.hxx
@@ -0,0 +1,50 @@
+// file : odb/sqlite/details/export.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_DETAILS_EXPORT_HXX
+#define ODB_SQLITE_DETAILS_EXPORT_HXX
+
+#include <odb/pre.hxx>
+
+// Note: do this check directly instead of including config.hxx.
+//
+#ifdef ODB_COMPILER
+# error libodb-sqlite header included in odb-compiled header
+#endif
+
+// Normally we don't export class templates (but do complete specializations),
+// inline functions, and classes with only inline member functions. Exporting
+// classes that inherit from non-exported/imported bases (e.g., std::string)
+// will end up badly. The only known workarounds are to not inherit or to not
+// export. Also, MinGW GCC doesn't like seeing non-exported function being
+// used before their inline definition. The workaround is to reorder code. In
+// the end it's all trial and error.
+
+#if defined(LIBODB_SQLITE_STATIC) // Using static.
+# define LIBODB_SQLITE_EXPORT
+#elif defined(LIBODB_SQLITE_STATIC_BUILD) // Building static.
+# define LIBODB_SQLITE_EXPORT
+#elif defined(LIBODB_SQLITE_SHARED) // Using shared.
+# ifdef _WIN32
+# define LIBODB_SQLITE_EXPORT __declspec(dllimport)
+# else
+# define LIBODB_SQLITE_EXPORT
+# endif
+#elif defined(LIBODB_SQLITE_SHARED_BUILD) // Building shared.
+# ifdef _WIN32
+# define LIBODB_SQLITE_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_SQLITE_EXPORT
+# endif
+#else
+// If none of the above macros are defined, then we assume we are being used
+// by some third-party build system that cannot/doesn't signal the library
+// type. Note that this fallback works for both static and shared but in case
+// of shared will be sub-optimal compared to having dllimport.
+//
+# define LIBODB_SQLITE_EXPORT // Using static or shared.
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_DETAILS_EXPORT_HXX
diff --git a/libodb-sqlite/odb/sqlite/details/options.cli b/libodb-sqlite/odb/sqlite/details/options.cli
new file mode 100644
index 0000000..d1955c3
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/options.cli
@@ -0,0 +1,47 @@
+// file : odb/sqlite/details/options.cli
+// license : GNU GPL v2; see accompanying LICENSE file
+
+include <string>;
+
+namespace odb
+{
+ namespace sqlite
+ {
+ namespace details
+ {
+ class options
+ {
+ std::string --database
+ {
+ "<filename>",
+ "SQLite database file name. If the database file is not specified
+ then a private, temporary on-disk database will be created. Use
+ the \cb{:memory:} special name to create a private, temporary
+ in-memory database."
+ };
+
+ bool --create
+ {
+ "Create the SQLite database if it does not already exist. By default
+ opening the database fails if it does not already exist."
+ };
+
+ bool --read-only
+ {
+ "Open the SQLite database in read-only mode. By default the database
+ is opened for reading and writing if possible, or reading only if
+ the file is write-protected by the operating system."
+ };
+
+ std::string --options-file
+ {
+ "<file>",
+ "Read additional options from <file>. Each option should appear on a
+ separate line optionally followed by space or equal sign (\cb{=})
+ and an option value. Empty lines and lines starting with \cb{#} are
+ ignored."
+ };
+ };
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.cxx b/libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.cxx
new file mode 100644
index 0000000..12f4193
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.cxx
@@ -0,0 +1,1027 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <odb/sqlite/details/options.hxx>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <utility>
+#include <ostream>
+#include <sstream>
+#include <cstring>
+#include <fstream>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ // unknown_option
+ //
+ unknown_option::
+ ~unknown_option () throw ()
+ {
+ }
+
+ void unknown_option::
+ print (::std::ostream& os) const
+ {
+ os << "unknown option '" << option ().c_str () << "'";
+ }
+
+ const char* unknown_option::
+ what () const throw ()
+ {
+ return "unknown option";
+ }
+
+ // unknown_argument
+ //
+ unknown_argument::
+ ~unknown_argument () throw ()
+ {
+ }
+
+ void unknown_argument::
+ print (::std::ostream& os) const
+ {
+ os << "unknown argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unknown_argument::
+ what () const throw ()
+ {
+ return "unknown argument";
+ }
+
+ // missing_value
+ //
+ missing_value::
+ ~missing_value () throw ()
+ {
+ }
+
+ void missing_value::
+ print (::std::ostream& os) const
+ {
+ os << "missing value for option '" << option ().c_str () << "'";
+ }
+
+ const char* missing_value::
+ what () const throw ()
+ {
+ return "missing option value";
+ }
+
+ // invalid_value
+ //
+ invalid_value::
+ ~invalid_value () throw ()
+ {
+ }
+
+ void invalid_value::
+ print (::std::ostream& os) const
+ {
+ os << "invalid value '" << value ().c_str () << "' for option '"
+ << option ().c_str () << "'";
+
+ if (!message ().empty ())
+ os << ": " << message ().c_str ();
+ }
+
+ const char* invalid_value::
+ what () const throw ()
+ {
+ return "invalid option value";
+ }
+
+ // eos_reached
+ //
+ void eos_reached::
+ print (::std::ostream& os) const
+ {
+ os << what ();
+ }
+
+ const char* eos_reached::
+ what () const throw ()
+ {
+ return "end of argument stream reached";
+ }
+
+ // file_io_failure
+ //
+ file_io_failure::
+ ~file_io_failure () throw ()
+ {
+ }
+
+ void file_io_failure::
+ print (::std::ostream& os) const
+ {
+ os << "unable to open file '" << file ().c_str () << "' or read failure";
+ }
+
+ const char* file_io_failure::
+ what () const throw ()
+ {
+ return "unable to open file or read failure";
+ }
+
+ // unmatched_quote
+ //
+ unmatched_quote::
+ ~unmatched_quote () throw ()
+ {
+ }
+
+ void unmatched_quote::
+ print (::std::ostream& os) const
+ {
+ os << "unmatched quote in argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unmatched_quote::
+ what () const throw ()
+ {
+ return "unmatched quote";
+ }
+
+ // scanner
+ //
+ scanner::
+ ~scanner ()
+ {
+ }
+
+ // argv_scanner
+ //
+ bool argv_scanner::
+ more ()
+ {
+ return i_ < argc_;
+ }
+
+ const char* argv_scanner::
+ peek ()
+ {
+ if (i_ < argc_)
+ return argv_[i_];
+ else
+ throw eos_reached ();
+ }
+
+ const char* argv_scanner::
+ next ()
+ {
+ if (i_ < argc_)
+ {
+ const char* r (argv_[i_]);
+
+ if (erase_)
+ {
+ for (int i (i_ + 1); i < argc_; ++i)
+ argv_[i - 1] = argv_[i];
+
+ --argc_;
+ argv_[argc_] = 0;
+ }
+ else
+ ++i_;
+
+ ++start_position_;
+ return r;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ void argv_scanner::
+ skip ()
+ {
+ if (i_ < argc_)
+ {
+ ++i_;
+ ++start_position_;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ std::size_t argv_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ // argv_file_scanner
+ //
+ int argv_file_scanner::zero_argc_ = 0;
+ std::string argv_file_scanner::empty_string_;
+
+ bool argv_file_scanner::
+ more ()
+ {
+ if (!args_.empty ())
+ return true;
+
+ while (base::more ())
+ {
+ // See if the next argument is the file option.
+ //
+ const char* a (base::peek ());
+ const option_info* oi = 0;
+ const char* ov = 0;
+
+ if (!skip_)
+ {
+ if ((oi = find (a)) != 0)
+ {
+ base::next ();
+
+ if (!base::more ())
+ throw missing_value (a);
+
+ ov = base::next ();
+ }
+ else if (std::strncmp (a, "-", 1) == 0)
+ {
+ if ((ov = std::strchr (a, '=')) != 0)
+ {
+ std::string o (a, 0, ov - a);
+ if ((oi = find (o.c_str ())) != 0)
+ {
+ base::next ();
+ ++ov;
+ }
+ }
+ }
+ }
+
+ if (oi != 0)
+ {
+ if (oi->search_func != 0)
+ {
+ std::string f (oi->search_func (ov, oi->arg));
+
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ load (ov);
+
+ if (!args_.empty ())
+ return true;
+ }
+ else
+ {
+ if (!skip_)
+ skip_ = (std::strcmp (a, "--") == 0);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ const char* argv_file_scanner::
+ peek ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? base::peek () : args_.front ().value.c_str ();
+ }
+
+ const std::string& argv_file_scanner::
+ peek_file ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? empty_string_ : *args_.front ().file;
+ }
+
+ std::size_t argv_file_scanner::
+ peek_line ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? 0 : args_.front ().line;
+ }
+
+ const char* argv_file_scanner::
+ next ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::next ();
+ else
+ {
+ hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value);
+ args_.pop_front ();
+ ++start_position_;
+ return hold_[i_].c_str ();
+ }
+ }
+
+ void argv_file_scanner::
+ skip ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::skip ();
+ else
+ {
+ args_.pop_front ();
+ ++start_position_;
+ }
+ }
+
+ const argv_file_scanner::option_info* argv_file_scanner::
+ find (const char* a) const
+ {
+ for (std::size_t i (0); i < options_count_; ++i)
+ if (std::strcmp (a, options_[i].option) == 0)
+ return &options_[i];
+
+ return 0;
+ }
+
+ std::size_t argv_file_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ void argv_file_scanner::
+ load (const std::string& file)
+ {
+ using namespace std;
+
+ ifstream is (file.c_str ());
+
+ if (!is.is_open ())
+ throw file_io_failure (file);
+
+ files_.push_back (file);
+
+ arg a;
+ a.file = &*files_.rbegin ();
+
+ for (a.line = 1; !is.eof (); ++a.line)
+ {
+ string line;
+ getline (is, line);
+
+ if (is.fail () && !is.eof ())
+ throw file_io_failure (file);
+
+ string::size_type n (line.size ());
+
+ // Trim the line from leading and trailing whitespaces.
+ //
+ if (n != 0)
+ {
+ const char* f (line.c_str ());
+ const char* l (f + n);
+
+ const char* of (f);
+ while (f < l && (*f == ' ' || *f == '\t' || *f == '\r'))
+ ++f;
+
+ --l;
+
+ const char* ol (l);
+ while (l > f && (*l == ' ' || *l == '\t' || *l == '\r'))
+ --l;
+
+ if (f != of || l != ol)
+ line = f <= l ? string (f, l - f + 1) : string ();
+ }
+
+ // Ignore empty lines, those that start with #.
+ //
+ if (line.empty () || line[0] == '#')
+ continue;
+
+ string::size_type p (string::npos);
+ if (line.compare (0, 1, "-") == 0)
+ {
+ p = line.find (' ');
+
+ string::size_type q (line.find ('='));
+ if (q != string::npos && q < p)
+ p = q;
+ }
+
+ string s1;
+ if (p != string::npos)
+ {
+ s1.assign (line, 0, p);
+
+ // Skip leading whitespaces in the argument.
+ //
+ if (line[p] == '=')
+ ++p;
+ else
+ {
+ n = line.size ();
+ for (++p; p < n; ++p)
+ {
+ char c (line[p]);
+ if (c != ' ' && c != '\t' && c != '\r')
+ break;
+ }
+ }
+ }
+ else if (!skip_)
+ skip_ = (line == "--");
+
+ string s2 (line, p != string::npos ? p : 0);
+
+ // If the string (which is an option value or argument) is
+ // wrapped in quotes, remove them.
+ //
+ n = s2.size ();
+ char cf (s2[0]), cl (s2[n - 1]);
+
+ if (cf == '"' || cf == '\'' || cl == '"' || cl == '\'')
+ {
+ if (n == 1 || cf != cl)
+ throw unmatched_quote (s2);
+
+ s2 = string (s2, 1, n - 2);
+ }
+
+ if (!s1.empty ())
+ {
+ // See if this is another file option.
+ //
+ const option_info* oi;
+ if (!skip_ && (oi = find (s1.c_str ())))
+ {
+ if (s2.empty ())
+ throw missing_value (oi->option);
+
+ if (oi->search_func != 0)
+ {
+ string f (oi->search_func (s2.c_str (), oi->arg));
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ {
+ // If the path of the file being parsed is not simple and the
+ // path of the file that needs to be loaded is relative, then
+ // complete the latter using the former as a base.
+ //
+#ifndef _WIN32
+ string::size_type p (file.find_last_of ('/'));
+ bool c (p != string::npos && s2[0] != '/');
+#else
+ string::size_type p (file.find_last_of ("/\\"));
+ bool c (p != string::npos && s2[1] != ':');
+#endif
+ if (c)
+ s2.insert (0, file, 0, p + 1);
+
+ load (s2);
+ }
+
+ continue;
+ }
+
+ a.value = s1;
+ args_.push_back (a);
+ }
+
+ a.value = s2;
+ args_.push_back (a);
+ }
+ }
+
+ template <typename X>
+ struct parser
+ {
+ static void
+ parse (X& x, scanner& s)
+ {
+ using namespace std;
+
+ const char* o (s.next ());
+ if (s.more ())
+ {
+ string v (s.next ());
+ istringstream is (v);
+ if (!(is >> x && is.peek () == istringstream::traits_type::eof ()))
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+ }
+ };
+
+ template <>
+ struct parser<bool>
+ {
+ static void
+ parse (bool& x, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ const char* v (s.next ());
+
+ if (std::strcmp (v, "1") == 0 ||
+ std::strcmp (v, "true") == 0 ||
+ std::strcmp (v, "TRUE") == 0 ||
+ std::strcmp (v, "True") == 0)
+ x = true;
+ else if (std::strcmp (v, "0") == 0 ||
+ std::strcmp (v, "false") == 0 ||
+ std::strcmp (v, "FALSE") == 0 ||
+ std::strcmp (v, "False") == 0)
+ x = false;
+ else
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+ }
+ };
+
+ template <>
+ struct parser<std::string>
+ {
+ static void
+ parse (std::string& x, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ x = s.next ();
+ else
+ throw missing_value (o);
+ }
+ };
+
+ template <typename X>
+ struct parser<std::pair<X, std::size_t> >
+ {
+ static void
+ parse (std::pair<X, std::size_t>& x, scanner& s)
+ {
+ x.second = s.position ();
+ parser<X>::parse (x.first, s);
+ }
+ };
+
+ template <typename X>
+ struct parser<std::vector<X> >
+ {
+ static void
+ parse (std::vector<X>& c, scanner& s)
+ {
+ X x;
+ parser<X>::parse (x, s);
+ c.push_back (x);
+ }
+ };
+
+ template <typename X, typename C>
+ struct parser<std::set<X, C> >
+ {
+ static void
+ parse (std::set<X, C>& c, scanner& s)
+ {
+ X x;
+ parser<X>::parse (x, s);
+ c.insert (x);
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::map<K, V, C> >
+ {
+ static void
+ parse (std::map<K, V, C>& m, 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
+ };
+
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, s);
+ }
+
+ m[k] = v;
+ }
+ else
+ throw missing_value (o);
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::multimap<K, V, C> >
+ {
+ static void
+ parse (std::multimap<K, V, C>& m, 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
+ };
+
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, s);
+ }
+
+ m.insert (typename std::multimap<K, V, C>::value_type (k, v));
+ }
+ else
+ throw missing_value (o);
+ }
+ };
+
+ template <typename X, typename T, T X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, s);
+ }
+
+ template <typename X, bool X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ s.next ();
+ x.*M = true;
+ }
+ }
+ }
+ }
+}
+
+#include <map>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ namespace details
+ {
+ // options
+ //
+
+ options::
+ options ()
+ : database_ (),
+ create_ (),
+ read_only_ (),
+ options_file_ ()
+ {
+ }
+
+ options::
+ options (int& argc,
+ char** argv,
+ bool erase,
+ ::odb::sqlite::details::cli::unknown_mode opt,
+ ::odb::sqlite::details::cli::unknown_mode arg)
+ : database_ (),
+ create_ (),
+ read_only_ (),
+ options_file_ ()
+ {
+ ::odb::sqlite::details::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ options::
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ ::odb::sqlite::details::cli::unknown_mode opt,
+ ::odb::sqlite::details::cli::unknown_mode arg)
+ : database_ (),
+ create_ (),
+ read_only_ (),
+ options_file_ ()
+ {
+ ::odb::sqlite::details::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ options::
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::odb::sqlite::details::cli::unknown_mode opt,
+ ::odb::sqlite::details::cli::unknown_mode arg)
+ : database_ (),
+ create_ (),
+ read_only_ (),
+ options_file_ ()
+ {
+ ::odb::sqlite::details::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ options::
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::odb::sqlite::details::cli::unknown_mode opt,
+ ::odb::sqlite::details::cli::unknown_mode arg)
+ : database_ (),
+ create_ (),
+ read_only_ (),
+ options_file_ ()
+ {
+ ::odb::sqlite::details::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ options::
+ options (::odb::sqlite::details::cli::scanner& s,
+ ::odb::sqlite::details::cli::unknown_mode opt,
+ ::odb::sqlite::details::cli::unknown_mode arg)
+ : database_ (),
+ create_ (),
+ read_only_ (),
+ options_file_ ()
+ {
+ _parse (s, opt, arg);
+ }
+
+ ::odb::sqlite::details::cli::usage_para options::
+ print_usage (::std::ostream& os, ::odb::sqlite::details::cli::usage_para p)
+ {
+ CLI_POTENTIALLY_UNUSED (os);
+
+ if (p != ::odb::sqlite::details::cli::usage_para::none)
+ os << ::std::endl;
+
+ os << "--database <filename> SQLite database file name. If the database file is not" << ::std::endl
+ << " specified then a private, temporary on-disk database will" << ::std::endl
+ << " be created. Use the :memory: special name to create a" << ::std::endl
+ << " private, temporary in-memory database." << ::std::endl;
+
+ os << std::endl
+ << "--create Create the SQLite database if it does not already exist." << ::std::endl
+ << " By default opening the database fails if it does not" << ::std::endl
+ << " already exist." << ::std::endl;
+
+ os << std::endl
+ << "--read-only Open the SQLite database in read-only mode. By default" << ::std::endl
+ << " the database is opened for reading and writing if" << ::std::endl
+ << " possible, or reading only if the file is write-protected" << ::std::endl
+ << " by the operating system." << ::std::endl;
+
+ os << std::endl
+ << "--options-file <file> Read additional options from <file>. Each option should" << ::std::endl
+ << " appear on a separate line optionally followed by space or" << ::std::endl
+ << " equal sign (=) and an option value. Empty lines and lines" << ::std::endl
+ << " starting with # are ignored." << ::std::endl;
+
+ p = ::odb::sqlite::details::cli::usage_para::option;
+
+ return p;
+ }
+
+ typedef
+ std::map<std::string, void (*) (options&, ::odb::sqlite::details::cli::scanner&)>
+ _cli_options_map;
+
+ static _cli_options_map _cli_options_map_;
+
+ struct _cli_options_map_init
+ {
+ _cli_options_map_init ()
+ {
+ _cli_options_map_["--database"] =
+ &::odb::sqlite::details::cli::thunk< options, std::string, &options::database_ >;
+ _cli_options_map_["--create"] =
+ &::odb::sqlite::details::cli::thunk< options, &options::create_ >;
+ _cli_options_map_["--read-only"] =
+ &::odb::sqlite::details::cli::thunk< options, &options::read_only_ >;
+ _cli_options_map_["--options-file"] =
+ &::odb::sqlite::details::cli::thunk< options, std::string, &options::options_file_ >;
+ }
+ };
+
+ static _cli_options_map_init _cli_options_map_init_;
+
+ bool options::
+ _parse (const char* o, ::odb::sqlite::details::cli::scanner& s)
+ {
+ _cli_options_map::const_iterator i (_cli_options_map_.find (o));
+
+ if (i != _cli_options_map_.end ())
+ {
+ (*(i->second)) (*this, s);
+ return true;
+ }
+
+ return false;
+ }
+
+ bool options::
+ _parse (::odb::sqlite::details::cli::scanner& s,
+ ::odb::sqlite::details::cli::unknown_mode opt_mode,
+ ::odb::sqlite::details::cli::unknown_mode arg_mode)
+ {
+ bool r = false;
+ bool opt = true;
+
+ while (s.more ())
+ {
+ const char* o = s.peek ();
+
+ if (std::strcmp (o, "--") == 0)
+ {
+ opt = false;
+ s.skip ();
+ r = true;
+ continue;
+ }
+
+ if (opt)
+ {
+ if (_parse (o, s))
+ {
+ r = true;
+ continue;
+ }
+
+ if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0')
+ {
+ // Handle combined option values.
+ //
+ std::string co;
+ if (const char* v = std::strchr (o, '='))
+ {
+ co.assign (o, 0, v - o);
+ ++v;
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (co.c_str ()),
+ const_cast<char*> (v)
+ };
+
+ ::odb::sqlite::details::cli::argv_scanner ns (0, ac, av);
+
+ if (_parse (co.c_str (), ns))
+ {
+ // Parsed the option but not its value?
+ //
+ if (ns.end () != 2)
+ throw ::odb::sqlite::details::cli::invalid_value (co, v);
+
+ s.next ();
+ r = true;
+ continue;
+ }
+ else
+ {
+ // Set the unknown option and fall through.
+ //
+ o = co.c_str ();
+ }
+ }
+
+ switch (opt_mode)
+ {
+ case ::odb::sqlite::details::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::odb::sqlite::details::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::odb::sqlite::details::cli::unknown_mode::fail:
+ {
+ throw ::odb::sqlite::details::cli::unknown_option (o);
+ }
+ }
+
+ break;
+ }
+ }
+
+ switch (arg_mode)
+ {
+ case ::odb::sqlite::details::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::odb::sqlite::details::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::odb::sqlite::details::cli::unknown_mode::fail:
+ {
+ throw ::odb::sqlite::details::cli::unknown_argument (o);
+ }
+ }
+
+ break;
+ }
+
+ return r;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
diff --git a/libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.hxx b/libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.hxx
new file mode 100644
index 0000000..abc4b3f
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.hxx
@@ -0,0 +1,530 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+#ifndef LIBODB_SQLITE_DETAILS_OPTIONS_HXX
+#define LIBODB_SQLITE_DETAILS_OPTIONS_HXX
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <list>
+#include <deque>
+#include <iosfwd>
+#include <string>
+#include <cstddef>
+#include <exception>
+
+#ifndef CLI_POTENTIALLY_UNUSED
+# if defined(_MSC_VER) || defined(__xlC__)
+# define CLI_POTENTIALLY_UNUSED(x) (void*)&x
+# else
+# define CLI_POTENTIALLY_UNUSED(x) (void)x
+# endif
+#endif
+
+namespace odb
+{
+ namespace sqlite
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ class usage_para
+ {
+ public:
+ enum value
+ {
+ none,
+ text,
+ option
+ };
+
+ usage_para (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ class unknown_mode
+ {
+ public:
+ enum value
+ {
+ skip,
+ stop,
+ fail
+ };
+
+ unknown_mode (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ // Exceptions.
+ //
+
+ class exception: public std::exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const = 0;
+ };
+
+ ::std::ostream&
+ operator<< (::std::ostream&, const exception&);
+
+ class unknown_option: public exception
+ {
+ public:
+ virtual
+ ~unknown_option () throw ();
+
+ unknown_option (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class unknown_argument: public exception
+ {
+ public:
+ virtual
+ ~unknown_argument () throw ();
+
+ unknown_argument (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ class missing_value: public exception
+ {
+ public:
+ virtual
+ ~missing_value () throw ();
+
+ missing_value (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class invalid_value: public exception
+ {
+ public:
+ virtual
+ ~invalid_value () throw ();
+
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message = std::string ());
+
+ const std::string&
+ option () const;
+
+ const std::string&
+ value () const;
+
+ const std::string&
+ message () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ std::string value_;
+ std::string message_;
+ };
+
+ class eos_reached: public exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class file_io_failure: public exception
+ {
+ public:
+ virtual
+ ~file_io_failure () throw ();
+
+ file_io_failure (const std::string& file);
+
+ const std::string&
+ file () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string file_;
+ };
+
+ class unmatched_quote: public exception
+ {
+ public:
+ virtual
+ ~unmatched_quote () throw ();
+
+ unmatched_quote (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ // Command line argument scanner interface.
+ //
+ // The values returned by next() are guaranteed to be valid
+ // for the two previous arguments up until a call to a third
+ // peek() or next().
+ //
+ // The position() function returns a monotonically-increasing
+ // number which, if stored, can later be used to determine the
+ // relative position of the argument returned by the following
+ // call to next(). Note that if multiple scanners are used to
+ // extract arguments from multiple sources, then the end
+ // position of the previous scanner should be used as the
+ // start position of the next.
+ //
+ class scanner
+ {
+ public:
+ virtual
+ ~scanner ();
+
+ virtual bool
+ more () = 0;
+
+ virtual const char*
+ peek () = 0;
+
+ virtual const char*
+ next () = 0;
+
+ virtual void
+ skip () = 0;
+
+ virtual std::size_t
+ position () = 0;
+ };
+
+ class argv_scanner: public scanner
+ {
+ public:
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ int
+ end () const;
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ protected:
+ std::size_t start_position_;
+ int i_;
+ int& argc_;
+ char** argv_;
+ bool erase_;
+ };
+
+ class argv_file_scanner: public argv_scanner
+ {
+ public:
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t start_position = 0);
+
+ struct option_info
+ {
+ // If search_func is not NULL, it is called, with the arg
+ // value as the second argument, to locate the options file.
+ // If it returns an empty string, then the file is ignored.
+ //
+ const char* option;
+ std::string (*search_func) (const char*, void* arg);
+ void* arg;
+ };
+
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const option_info* options = 0,
+ std::size_t options_count = 0,
+ std::size_t start_position = 0);
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ // Return the file path if the peeked at argument came from a file and
+ // the empty string otherwise. The reference is guaranteed to be valid
+ // till the end of the scanner lifetime.
+ //
+ const std::string&
+ peek_file ();
+
+ // Return the 1-based line number if the peeked at argument came from
+ // a file and zero otherwise.
+ //
+ std::size_t
+ peek_line ();
+
+ private:
+ const option_info*
+ find (const char*) const;
+
+ void
+ load (const std::string& file);
+
+ typedef argv_scanner base;
+
+ const std::string option_;
+ option_info option_info_;
+ const option_info* options_;
+ std::size_t options_count_;
+
+ struct arg
+ {
+ std::string value;
+ const std::string* file;
+ std::size_t line;
+ };
+
+ std::deque<arg> args_;
+ std::list<std::string> files_;
+
+ // Circular buffer of two arguments.
+ //
+ std::string hold_[2];
+ std::size_t i_;
+
+ bool skip_;
+
+ static int zero_argc_;
+ static std::string empty_string_;
+ };
+
+ template <typename X>
+ struct parser;
+ }
+ }
+ }
+}
+
+#include <string>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ namespace details
+ {
+ class options
+ {
+ public:
+ options ();
+
+ options (int& argc,
+ char** argv,
+ bool erase = false,
+ ::odb::sqlite::details::cli::unknown_mode option = ::odb::sqlite::details::cli::unknown_mode::fail,
+ ::odb::sqlite::details::cli::unknown_mode argument = ::odb::sqlite::details::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ ::odb::sqlite::details::cli::unknown_mode option = ::odb::sqlite::details::cli::unknown_mode::fail,
+ ::odb::sqlite::details::cli::unknown_mode argument = ::odb::sqlite::details::cli::unknown_mode::stop);
+
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::odb::sqlite::details::cli::unknown_mode option = ::odb::sqlite::details::cli::unknown_mode::fail,
+ ::odb::sqlite::details::cli::unknown_mode argument = ::odb::sqlite::details::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::odb::sqlite::details::cli::unknown_mode option = ::odb::sqlite::details::cli::unknown_mode::fail,
+ ::odb::sqlite::details::cli::unknown_mode argument = ::odb::sqlite::details::cli::unknown_mode::stop);
+
+ options (::odb::sqlite::details::cli::scanner&,
+ ::odb::sqlite::details::cli::unknown_mode option = ::odb::sqlite::details::cli::unknown_mode::fail,
+ ::odb::sqlite::details::cli::unknown_mode argument = ::odb::sqlite::details::cli::unknown_mode::stop);
+
+ // Option accessors.
+ //
+ const std::string&
+ database () const;
+
+ const bool&
+ create () const;
+
+ const bool&
+ read_only () const;
+
+ const std::string&
+ options_file () const;
+
+ // Print usage information.
+ //
+ static ::odb::sqlite::details::cli::usage_para
+ print_usage (::std::ostream&,
+ ::odb::sqlite::details::cli::usage_para = ::odb::sqlite::details::cli::usage_para::none);
+
+ // Implementation details.
+ //
+ protected:
+ bool
+ _parse (const char*, ::odb::sqlite::details::cli::scanner&);
+
+ private:
+ bool
+ _parse (::odb::sqlite::details::cli::scanner&,
+ ::odb::sqlite::details::cli::unknown_mode option,
+ ::odb::sqlite::details::cli::unknown_mode argument);
+
+ public:
+ std::string database_;
+ bool create_;
+ bool read_only_;
+ std::string options_file_;
+ };
+ }
+ }
+}
+
+#include <odb/sqlite/details/options.ixx>
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
+#endif // LIBODB_SQLITE_DETAILS_OPTIONS_HXX
diff --git a/libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.ixx b/libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.ixx
new file mode 100644
index 0000000..54092aa
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.ixx
@@ -0,0 +1,324 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <cassert>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ // usage_para
+ //
+ inline usage_para::
+ usage_para (value v)
+ : v_ (v)
+ {
+ }
+
+ // unknown_mode
+ //
+ inline unknown_mode::
+ unknown_mode (value v)
+ : v_ (v)
+ {
+ }
+
+ // exception
+ //
+ inline ::std::ostream&
+ operator<< (::std::ostream& os, const exception& e)
+ {
+ e.print (os);
+ return os;
+ }
+
+ // unknown_option
+ //
+ inline unknown_option::
+ unknown_option (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& unknown_option::
+ option () const
+ {
+ return option_;
+ }
+
+ // unknown_argument
+ //
+ inline unknown_argument::
+ unknown_argument (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unknown_argument::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // missing_value
+ //
+ inline missing_value::
+ missing_value (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& missing_value::
+ option () const
+ {
+ return option_;
+ }
+
+ // invalid_value
+ //
+ inline invalid_value::
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message)
+ : option_ (option),
+ value_ (value),
+ message_ (message)
+ {
+ }
+
+ inline const std::string& invalid_value::
+ option () const
+ {
+ return option_;
+ }
+
+ inline const std::string& invalid_value::
+ value () const
+ {
+ return value_;
+ }
+
+ inline const std::string& invalid_value::
+ message () const
+ {
+ return message_;
+ }
+
+ // file_io_failure
+ //
+ inline file_io_failure::
+ file_io_failure (const std::string& file)
+ : file_ (file)
+ {
+ }
+
+ inline const std::string& file_io_failure::
+ file () const
+ {
+ return file_;
+ }
+
+ // unmatched_quote
+ //
+ inline unmatched_quote::
+ unmatched_quote (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unmatched_quote::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // argv_scanner
+ //
+ inline argv_scanner::
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + 1),
+ i_ (1),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline argv_scanner::
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + static_cast<std::size_t> (start)),
+ i_ (start),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline int argv_scanner::
+ end () const
+ {
+ return i_;
+ }
+
+ // argv_file_scanner
+ //
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+
+ load (file);
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const option_info* options,
+ std::size_t options_count,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ load (file);
+ }
+ }
+ }
+ }
+}
+
+namespace odb
+{
+ namespace sqlite
+ {
+ namespace details
+ {
+ // options
+ //
+
+ inline const std::string& options::
+ database () const
+ {
+ return this->database_;
+ }
+
+ inline const bool& options::
+ create () const
+ {
+ return this->create_;
+ }
+
+ inline const bool& options::
+ read_only () const
+ {
+ return this->read_only_;
+ }
+
+ inline const std::string& options::
+ options_file () const
+ {
+ return this->options_file_;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
diff --git a/libodb-sqlite/odb/sqlite/error.cxx b/libodb-sqlite/odb/sqlite/error.cxx
new file mode 100644
index 0000000..ae6bbe3
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/error.cxx
@@ -0,0 +1,94 @@
+// file : odb/sqlite/error.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <sqlite3.h>
+
+#include <new> // std::bad_alloc
+#include <string>
+
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/exceptions.hxx>
+
+#include <odb/sqlite/details/config.hxx> // LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+
+using namespace std;
+
+namespace odb
+{
+ namespace sqlite
+ {
+ void
+ translate_error (int e, connection& c)
+ {
+ sqlite3* h (c.handle ());
+
+ // Extended error codes are only available in 3.6.5 and later.
+ //
+#if SQLITE_VERSION_NUMBER >= 3006005
+ int ee (sqlite3_extended_errcode (h));
+#else
+ int ee (0);
+#endif
+ string m;
+
+ switch (e)
+ {
+ case SQLITE_NOMEM:
+ {
+ throw bad_alloc ();
+ }
+ case SQLITE_MISUSE:
+ {
+ // In case of SQLITE_MISUSE, error code/message may or may not
+ // be set.
+ //
+ ee = e;
+ m = "SQLite API misuse";
+ break;
+ }
+#ifdef SQLITE_ABORT_ROLLBACK
+ case SQLITE_ABORT:
+ {
+ if (ee == SQLITE_ABORT_ROLLBACK)
+ throw forced_rollback ();
+
+ break;
+ }
+#endif
+ case SQLITE_LOCKED:
+ {
+#ifdef LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+ if (ee != SQLITE_LOCKED_SHAREDCACHE)
+ throw deadlock (); // The DROP TABLE special case.
+#endif
+ // Getting SQLITE_LOCKED_SHAREDCACHE here means we don't have
+ // the unlock notify support. Translate this to timeout.
+ //
+ throw timeout ();
+ }
+ case SQLITE_BUSY:
+ case SQLITE_IOERR:
+ {
+#if SQLITE_VERSION_NUMBER >= 3006005
+ if (e != SQLITE_IOERR || ee == SQLITE_IOERR_BLOCKED)
+ throw timeout ();
+#endif
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (m.empty ())
+ m = sqlite3_errmsg (h);
+
+ // Get rid of a trailing newline if there is one.
+ //
+ string::size_type n (m.size ());
+ if (n != 0 && m[n - 1] == '\n')
+ m.resize (n - 1);
+
+ throw database_exception (e, ee, m);
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/error.hxx b/libodb-sqlite/odb/sqlite/error.hxx
new file mode 100644
index 0000000..4646f85
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/error.hxx
@@ -0,0 +1,27 @@
+// file : odb/sqlite/error.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_ERROR_HXX
+#define ODB_SQLITE_ERROR_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class connection;
+
+ // Translate SQLite error and throw an appropriate exception.
+ //
+ LIBODB_SQLITE_EXPORT void
+ translate_error (int error, connection&);
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_ERROR_HXX
diff --git a/libodb-sqlite/odb/sqlite/exceptions.cxx b/libodb-sqlite/odb/sqlite/exceptions.cxx
new file mode 100644
index 0000000..0621189
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/exceptions.cxx
@@ -0,0 +1,92 @@
+// file : odb/sqlite/exceptions.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/sqlite/exceptions.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ // forced_rollback
+ //
+
+ const char* forced_rollback::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "transaction is forced to rollback";
+ }
+
+ forced_rollback* forced_rollback::
+ clone () const
+ {
+ return new forced_rollback (*this);
+ }
+
+ //
+ // database_exception
+ //
+
+ database_exception::
+ ~database_exception () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ database_exception::
+ database_exception (int e, int ee, const string& m)
+ : error_ (e), extended_error_ (ee), message_ (m)
+ {
+ ostringstream ostr;
+ ostr << error_;
+
+ if (error_ != extended_error_)
+ ostr << " (" << extended_error_ << ")";
+
+ ostr << ": " << message_;
+ what_ = ostr.str ();
+ }
+
+ const char* database_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ database_exception* database_exception::
+ clone () const
+ {
+ return new database_exception (*this);
+ }
+
+ //
+ // cli_exception
+ //
+
+ cli_exception::
+ cli_exception (const std::string& what)
+ : what_ (what)
+ {
+ }
+
+ cli_exception::
+ ~cli_exception () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ const char* cli_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ cli_exception* cli_exception::
+ clone () const
+ {
+ return new cli_exception (*this);
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/exceptions.hxx b/libodb-sqlite/odb/sqlite/exceptions.hxx
new file mode 100644
index 0000000..3adb433
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/exceptions.hxx
@@ -0,0 +1,101 @@
+// file : odb/sqlite/exceptions.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_EXCEPTIONS_HXX
+#define ODB_SQLITE_EXCEPTIONS_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+
+#include <odb/exceptions.hxx>
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // This exception is thrown if SQLite is forcing the current transaction
+ // to rollback. This can happen in SQLite 3.7.11 or later if one of the
+ // connections participating in the shared cache rolls back a transaction.
+ // See the SQLITE_ABORT_ROLLBACK extended error code for detail on this
+ // behavior.
+ //
+ struct LIBODB_SQLITE_EXPORT forced_rollback: recoverable
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual forced_rollback*
+ clone () const;
+ };
+
+ struct LIBODB_SQLITE_EXPORT database_exception: odb::database_exception
+ {
+ database_exception (int error,
+ int extended_error,
+ const std::string& message);
+
+ ~database_exception () ODB_NOTHROW_NOEXCEPT;
+
+ int
+ error () const
+ {
+ return error_;
+ }
+
+ int
+ extended_error () const
+ {
+ return extended_error_;
+ }
+
+ const std::string&
+ message () const
+ {
+ return message_;
+ }
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual database_exception*
+ clone () const;
+
+ private:
+ int error_;
+ int extended_error_;
+ std::string message_;
+ std::string what_;
+ };
+
+ struct LIBODB_SQLITE_EXPORT cli_exception: odb::exception
+ {
+ cli_exception (const std::string& what);
+ ~cli_exception () ODB_NOTHROW_NOEXCEPT;
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual cli_exception*
+ clone () const;
+
+ private:
+ std::string what_;
+ };
+
+ namespace core
+ {
+ using sqlite::database_exception;
+ using sqlite::cli_exception;
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_EXCEPTIONS_HXX
diff --git a/libodb-sqlite/odb/sqlite/forward.hxx b/libodb-sqlite/odb/sqlite/forward.hxx
new file mode 100644
index 0000000..1be05b4
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/forward.hxx
@@ -0,0 +1,112 @@
+// file : odb/sqlite/forward.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_FORWARD_HXX
+#define ODB_SQLITE_FORWARD_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ namespace core
+ {
+ using namespace odb::common;
+ }
+
+ //
+ //
+ class database;
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+ class connection_factory;
+ class statement;
+ class transaction;
+ class tracer;
+
+ namespace core
+ {
+ using sqlite::database;
+ using sqlite::connection;
+ using sqlite::connection_ptr;
+ using sqlite::transaction;
+ using sqlite::statement;
+ }
+
+ // Implementation details.
+ //
+ enum database_type_id
+ {
+ id_integer,
+ id_real,
+ id_text,
+ id_blob,
+ id_text_stream,
+ id_blob_stream
+ };
+
+ template <typename T, database_type_id>
+ struct default_value_traits;
+
+ enum statement_kind
+ {
+ statement_select,
+ statement_insert,
+ statement_update,
+ statement_delete,
+ statement_generic
+ };
+
+ class binding;
+ class select_statement;
+
+ template <typename T>
+ class object_statements;
+
+ template <typename T>
+ class polymorphic_root_object_statements;
+
+ template <typename T>
+ class polymorphic_derived_object_statements;
+
+ template <typename T>
+ class no_id_object_statements;
+
+ template <typename T>
+ class view_statements;
+
+ template <typename T>
+ class container_statements;
+
+ template <typename T>
+ class smart_container_statements;
+
+ template <typename T, typename ST>
+ class section_statements;
+
+ class query_base;
+ class query_params;
+ }
+
+ namespace details
+ {
+ template <>
+ struct counter_type<sqlite::connection>
+ {
+ typedef shared_base counter;
+ };
+
+ template <>
+ struct counter_type<sqlite::query_params>
+ {
+ typedef shared_base counter;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_FORWARD_HXX
diff --git a/libodb-sqlite/odb/sqlite/no-id-object-result.hxx b/libodb-sqlite/odb/sqlite/no-id-object-result.hxx
new file mode 100644
index 0000000..b0edb09
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/no-id-object-result.hxx
@@ -0,0 +1,80 @@
+// file : odb/sqlite/no-id-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_NO_ID_OBJECT_RESULT_HXX
+#define ODB_SQLITE_NO_ID_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/no-id-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx> // query_base, query_params
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/traits-calls.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ class no_id_object_result_impl: public odb::no_id_object_result_impl<T>
+ {
+ public:
+ typedef odb::no_id_object_result_impl<T> base_type;
+
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_sqlite> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~no_id_object_result_impl ();
+
+ no_id_object_result_impl (const query_base&,
+ const details::shared_ptr<select_statement>&,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type&);
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ // We need to hold on to the query parameters because SQLite uses
+ // the parameter buffers to find each next row.
+ //
+ details::shared_ptr<query_params> params_;
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ };
+ }
+}
+
+#include <odb/sqlite/no-id-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_NO_ID_OBJECT_RESULT_HXX
diff --git a/libodb-sqlite/odb/sqlite/no-id-object-result.txx b/libodb-sqlite/odb/sqlite/no-id-object-result.txx
new file mode 100644
index 0000000..bd26afc
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/no-id-object-result.txx
@@ -0,0 +1,114 @@
+// file : odb/sqlite/no-id-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/sqlite/no-id-object-statements.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ no_id_object_result_impl<T>::
+ ~no_id_object_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ params_.reset ();
+ statement_.reset ();
+ }
+
+ template <typename T>
+ no_id_object_result_impl<T>::
+ no_id_object_result_impl (const query_base& q,
+ const details::shared_ptr<select_statement>& s,
+ statements_type& sts,
+ const schema_version_migration* svm)
+ : base_type (sts.connection ()),
+ params_ (q.parameters ()),
+ statement_ (s),
+ statements_ (sts),
+ tc_ (svm)
+ {
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ load (object_type& obj)
+ {
+ // The image can grow between calls to load() as a result of other
+ // statements execution.
+ //
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ }
+
+ select_statement::result r (statement_->load ());
+
+ if (r == select_statement::truncated)
+ {
+ if (tc_.grow (im, statements_.select_image_truncated ()))
+ im.version++;
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ statement_->reload ();
+ }
+ }
+
+ object_traits::callback (this->db_, obj, callback_event::pre_load);
+ tc_.init (obj, im, &this->db_);
+ object_traits::callback (this->db_, obj, callback_event::post_load);
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (!statement_->next ())
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t no_id_object_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/no-id-object-statements.hxx b/libodb-sqlite/odb/sqlite/no-id-object-statements.hxx
new file mode 100644
index 0000000..7a0376a
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/no-id-object-statements.hxx
@@ -0,0 +1,136 @@
+// file : odb/sqlite/no-id-object-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_NO_ID_OBJECT_STATEMENTS_HXX
+#define ODB_SQLITE_NO_ID_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/sqlite-types.hxx>
+#include <odb/sqlite/binding.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/statements-base.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ // Implementation for objects without object id.
+ //
+
+ template <typename T>
+ class no_id_object_statements: public statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_sqlite> object_traits;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::image_type image_type;
+
+ typedef sqlite::insert_statement insert_statement_type;
+
+ public:
+ no_id_object_statements (connection_type&);
+
+ virtual
+ ~no_id_object_statements ();
+
+ // Object image.
+ //
+ image_type&
+ image () {return image_;}
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ {
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ 0));
+ }
+
+ return *persist_;
+ }
+
+ public:
+ // select = total
+ // insert = total - inverse; inverse == 0 for object without id
+ //
+ static const std::size_t insert_column_count =
+ object_traits::column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count;
+
+ private:
+ no_id_object_statements (const no_id_object_statements&);
+ no_id_object_statements& operator= (const no_id_object_statements&);
+
+ private:
+ image_type image_;
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+ binding select_image_binding_;
+ bind select_image_bind_[select_column_count];
+ bool select_image_truncated_[select_column_count];
+
+ // Insert binding.
+ //
+ std::size_t insert_image_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[insert_column_count];
+
+ details::shared_ptr<insert_statement_type> persist_;
+ };
+ }
+}
+
+#include <odb/sqlite/no-id-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_NO_ID_OBJECT_STATEMENTS_HXX
diff --git a/libodb-sqlite/odb/sqlite/no-id-object-statements.txx b/libodb-sqlite/odb/sqlite/no-id-object-statements.txx
new file mode 100644
index 0000000..d0a62b2
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/no-id-object-statements.txx
@@ -0,0 +1,36 @@
+// file : odb/sqlite/no-id-object-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ no_id_object_statements<T>::
+ ~no_id_object_statements ()
+ {
+ }
+
+ template <typename T>
+ no_id_object_statements<T>::
+ no_id_object_statements (connection_type& conn)
+ : statements_base (conn),
+ select_image_binding_ (select_image_bind_, select_column_count),
+ insert_image_binding_ (insert_image_bind_, insert_column_count)
+ {
+ image_.version = 0;
+ select_image_version_ = 0;
+ insert_image_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+
+ for (std::size_t i (0); i < select_column_count; ++i)
+ select_image_bind_[i].truncated = select_image_truncated_ + i;
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/polymorphic-object-result.hxx b/libodb-sqlite/odb/sqlite/polymorphic-object-result.hxx
new file mode 100644
index 0000000..3239471
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/polymorphic-object-result.hxx
@@ -0,0 +1,98 @@
+// file : odb/sqlite/polymorphic-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_POLYMORPHIC_OBJECT_RESULT_HXX
+#define ODB_SQLITE_POLYMORPHIC_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/polymorphic-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx> // query_base, query_params
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/traits-calls.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ class polymorphic_object_result_impl:
+ public odb::polymorphic_object_result_impl<T>
+ {
+ public:
+ typedef odb::polymorphic_object_result_impl<T> base_type;
+
+ typedef typename base_type::id_type id_type;
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_sqlite> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename base_type::root_type root_type;
+ typedef typename base_type::discriminator_type discriminator_type;
+
+ typedef object_traits_impl<root_type, id_sqlite> root_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~polymorphic_object_result_impl ();
+
+ polymorphic_object_result_impl (
+ const query_base&,
+ const details::shared_ptr<select_statement>&,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type*, bool fetch);
+
+ virtual id_type
+ load_id ();
+
+ virtual discriminator_type
+ load_discriminator ();
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ void
+ load_image ();
+
+ private:
+ // We need to hold on to the query parameters because SQLite uses
+ // the parameter buffers to find each next row.
+ //
+ details::shared_ptr<query_params> params_;
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ };
+ }
+}
+
+#include <odb/sqlite/polymorphic-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_POLYMORPHIC_OBJECT_RESULT_HXX
diff --git a/libodb-sqlite/odb/sqlite/polymorphic-object-result.txx b/libodb-sqlite/odb/sqlite/polymorphic-object-result.txx
new file mode 100644
index 0000000..bd22f01
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/polymorphic-object-result.txx
@@ -0,0 +1,287 @@
+// file : odb/sqlite/polymorphic-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/sqlite/polymorphic-object-statements.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ polymorphic_object_result_impl<T>::
+ ~polymorphic_object_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ params_.reset ();
+ statement_.reset ();
+ }
+
+ template <typename T>
+ polymorphic_object_result_impl<T>::
+ polymorphic_object_result_impl (
+ const query_base& q,
+ const details::shared_ptr<select_statement>& s,
+ statements_type& sts,
+ const schema_version_migration* svm)
+ : base_type (sts.connection ()),
+ params_ (q.parameters ()),
+ statement_ (s),
+ statements_ (sts),
+ tc_ (svm)
+ {
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ load (object_type* pobj, bool fetch)
+ {
+ if (fetch)
+ load_image ();
+
+ typename statements_type::root_statements_type& rsts (
+ statements_.root_statements ());
+
+ // This is a top-level call so the statements cannot be locked.
+ //
+ assert (!rsts.locked ());
+ typename statements_type::auto_lock l (rsts);
+
+ typename object_traits::image_type& i (statements_.image ());
+ typename root_traits::image_type& ri (rsts.image ());
+
+ id_type id (root_traits::id (ri));
+
+ // Determine this object's dynamic type.
+ //
+ typedef typename root_traits::info_type info_type;
+ discriminator_type d (root_traits::discriminator (ri));
+ discriminator_type disc (d);
+
+ // Use the polymorphic_info() helper to get concrete_info if
+ // object_type is concrete and NULL if it is abstract.
+ //
+ const info_type* spi (polymorphic_info (object_traits::info));
+ const info_type& pi (
+ spi != 0 && spi->discriminator == d
+ ? *spi
+ : root_traits::map->find (d));
+
+ typedef typename root_traits::pointer_type root_pointer_type;
+ typedef typename root_traits::pointer_traits root_pointer_traits;
+
+ typename object_traits::pointer_cache_traits::insert_guard ig;
+
+ if (pobj == 0)
+ {
+ // Need to create a new instance of the dynamic type.
+ //
+ root_pointer_type rp (pi.create ());
+ pointer_type p (
+ root_pointer_traits::template static_pointer_cast<object_type> (rp));
+
+ // Insert it as a root pointer (for non-unique pointers, rp should
+ // still be valid and for unique pointers this is a no-op).
+ //
+ ig.reset (
+ object_traits::pointer_cache_traits::insert (this->db_, id, rp));
+
+ pobj = &pointer_traits::get_ref (p);
+ current (p);
+ }
+ else
+ {
+ // We are loading into an existing instance. If the static and
+ // dynamic types differ, then make sure the instance is at least
+ // of the dynamic type.
+ //
+ if (&pi != &object_traits::info)
+ {
+ const info_type& dpi (root_traits::map->find (typeid (*pobj)));
+
+ if (&dpi != &pi && dpi.derived (pi))
+ throw object_not_persistent (); // @@ type_mismatch ?
+ }
+ }
+
+ callback_event ce (callback_event::pre_load);
+ pi.dispatch (info_type::call_callback, this->db_, pobj, &ce);
+
+ tc_.init (*pobj, i, &this->db_);
+
+ // Initialize the id image and binding and load the rest of the object
+ // (containers, dynamic part, etc).
+ //
+ typename object_traits::id_image_type& idi (statements_.id_image ());
+ root_traits::init (idi, id);
+
+ binding& idb (statements_.id_image_binding ());
+ if (idi.version != statements_.id_image_version () || idb.version == 0)
+ {
+ object_traits::bind (idb.bind, idi);
+ statements_.id_image_version (idi.version);
+ idb.version++;
+ }
+
+ tc_.load_ (statements_, *pobj, false);
+
+ // Load the dynamic part of the object unless static and dynamic
+ // types are the same.
+ //
+ if (&pi != &object_traits::info)
+ {
+ std::size_t d (object_traits::depth);
+ pi.dispatch (info_type::call_load, this->db_, pobj, &d);
+ };
+
+ rsts.load_delayed (tc_.version ());
+ l.unlock ();
+
+ ce = callback_event::post_load;
+ pi.dispatch (info_type::call_callback, this->db_, pobj, &ce);
+ object_traits::pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+
+ template <typename T>
+ typename polymorphic_object_result_impl<T>::id_type
+ polymorphic_object_result_impl<T>::
+ load_id ()
+ {
+ load_image ();
+ return root_traits::id (statements_.root_statements ().image ());
+ }
+
+ template <typename T>
+ typename polymorphic_object_result_impl<T>::discriminator_type
+ polymorphic_object_result_impl<T>::
+ load_discriminator ()
+ {
+ load_image ();
+ return root_traits::discriminator (
+ statements_.root_statements ().image ());
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (!statement_->next ())
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ }
+
+ template <typename T, typename R>
+ struct polymorphic_image_rebind
+ {
+ // Derived type version.
+ //
+ typedef object_traits_impl<T, id_sqlite> traits;
+
+ static bool
+ rebind (typename traits::statements_type& sts,
+ const schema_version_migration* svm)
+ {
+ typename traits::image_type& im (sts.image ());
+
+ if (traits::check_version (sts.select_image_versions (), im))
+ {
+ binding& b (sts.select_image_binding (traits::depth));
+ object_traits_calls<T> tc (svm);
+ tc.bind (b.bind, 0, 0, im, statement_select);
+ traits::update_version (
+ sts.select_image_versions (), im, sts.select_image_bindings ());
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+ template <typename R>
+ struct polymorphic_image_rebind<R, R>
+ {
+ // Root type version.
+ //
+ typedef object_traits_impl<R, id_sqlite> traits;
+
+ static bool
+ rebind (typename traits::statements_type& sts,
+ const schema_version_migration* svm)
+ {
+ typename traits::image_type& im (sts.image ());
+
+ if (im.version != sts.select_image_version ())
+ {
+ binding& b (sts.select_image_binding ());
+ object_traits_calls<R> tc (svm);
+ tc.bind (b.bind, im, statement_select);
+ sts.select_image_version (im.version);
+ b.version++;
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ load_image ()
+ {
+ typedef polymorphic_image_rebind<object_type, root_type> image_rebind;
+
+ // The image can grow between calls to load() as a result of other
+ // statements execution.
+ //
+ image_rebind::rebind (statements_, tc_.version ());
+
+ select_statement::result r (statement_->load ());
+
+ if (r == select_statement::truncated)
+ {
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (tc_.grow (im, statements_.select_image_truncated ()))
+ im.version++;
+
+ if (image_rebind::rebind (statements_, tc_.version ()))
+ statement_->reload ();
+ }
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t polymorphic_object_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/polymorphic-object-statements.hxx b/libodb-sqlite/odb/sqlite/polymorphic-object-statements.hxx
new file mode 100644
index 0000000..736686b
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/polymorphic-object-statements.hxx
@@ -0,0 +1,479 @@
+// file : odb/sqlite/polymorphic-object-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_POLYMORPHIC_OBJECT_STATEMENTS_HXX
+#define ODB_SQLITE_POLYMORPHIC_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/sqlite-types.hxx>
+#include <odb/sqlite/binding.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/statements-base.hxx>
+#include <odb/sqlite/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ // Implementation for polymorphic objects.
+ //
+
+ template <typename T>
+ class polymorphic_root_object_statements: public object_statements<T>
+ {
+ public:
+ typedef typename object_statements<T>::connection_type connection_type;
+ typedef typename object_statements<T>::object_traits object_traits;
+ typedef typename object_statements<T>::id_image_type id_image_type;
+
+ typedef
+ typename object_traits::discriminator_image_type
+ discriminator_image_type;
+
+ typedef
+ typename object_statements<T>::select_statement_type
+ select_statement_type;
+
+ public:
+ // Interface compatibility with derived_object_statements.
+ //
+ typedef polymorphic_root_object_statements root_statements_type;
+
+ root_statements_type&
+ root_statements ()
+ {
+ return *this;
+ }
+
+ public:
+ // Discriminator binding.
+ //
+ discriminator_image_type&
+ discriminator_image () {return discriminator_image_;}
+
+ std::size_t
+ discriminator_image_version () const
+ {return discriminator_image_version_;}
+
+ void
+ discriminator_image_version (std::size_t v)
+ {discriminator_image_version_ = v;}
+
+ binding&
+ discriminator_image_binding () {return discriminator_image_binding_;}
+
+ bool*
+ discriminator_image_truncated () {return discriminator_image_truncated_;}
+
+ // Id binding for discriminator retrieval.
+ //
+ id_image_type&
+ discriminator_id_image () {return discriminator_id_image_;}
+
+ std::size_t
+ discriminator_id_image_version () const
+ {return discriminator_id_image_version_;}
+
+ void
+ discriminator_id_image_version (std::size_t v)
+ {discriminator_id_image_version_ = v;}
+
+ binding&
+ discriminator_id_image_binding ()
+ {return discriminator_id_image_binding_;}
+
+ //
+ //
+ select_statement_type&
+ find_discriminator_statement ()
+ {
+ if (find_discriminator_ == 0)
+ {
+ find_discriminator_.reset (
+ new (details::shared) select_statement_type (
+ this->conn_,
+ object_traits::find_discriminator_statement,
+ false, // Doesn't need to be processed.
+ false, // Don't optimize.
+ discriminator_id_image_binding_,
+ discriminator_image_binding_));
+ }
+
+ return *find_discriminator_;
+ }
+
+ public:
+ polymorphic_root_object_statements (connection_type&);
+
+ virtual
+ ~polymorphic_root_object_statements ();
+
+ // Static "override" (statements type).
+ //
+ void
+ load_delayed (const schema_version_migration* svm)
+ {
+ assert (this->locked ());
+
+ if (!this->delayed_.empty ())
+ this->template load_delayed_<polymorphic_root_object_statements> (
+ svm);
+ }
+
+ public:
+ static const std::size_t id_column_count =
+ object_statements<T>::id_column_count;
+
+ static const std::size_t discriminator_column_count =
+ object_traits::discriminator_column_count;
+
+ static const std::size_t managed_optimistic_column_count =
+ object_traits::managed_optimistic_column_count;
+
+ private:
+ // Discriminator image.
+ //
+ discriminator_image_type discriminator_image_;
+ std::size_t discriminator_image_version_;
+ binding discriminator_image_binding_;
+ bind discriminator_image_bind_[discriminator_column_count +
+ managed_optimistic_column_count];
+ bool discriminator_image_truncated_[discriminator_column_count +
+ managed_optimistic_column_count];
+
+ // Id image for discriminator retrieval (only used as a parameter).
+ //
+ id_image_type discriminator_id_image_;
+ std::size_t discriminator_id_image_version_;
+ binding discriminator_id_image_binding_;
+ bind discriminator_id_image_bind_[id_column_count];
+
+ details::shared_ptr<select_statement_type> find_discriminator_;
+ };
+
+ template <typename T>
+ class polymorphic_derived_object_statements: public statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_sqlite> object_traits;
+ typedef typename object_traits::id_type id_type;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::id_image_type id_image_type;
+ typedef typename object_traits::image_type image_type;
+
+ typedef typename object_traits::root_type root_type;
+ typedef
+ polymorphic_root_object_statements<root_type>
+ root_statements_type;
+
+ typedef typename object_traits::base_type base_type;
+ typedef
+ typename object_traits::base_traits::statements_type
+ base_statements_type;
+
+ typedef
+ typename object_traits::extra_statement_cache_type
+ extra_statement_cache_type;
+
+ typedef sqlite::insert_statement insert_statement_type;
+ typedef sqlite::select_statement select_statement_type;
+ typedef sqlite::update_statement update_statement_type;
+ typedef sqlite::delete_statement delete_statement_type;
+
+ typedef typename root_statements_type::auto_lock auto_lock;
+
+ public:
+ polymorphic_derived_object_statements (connection_type&);
+
+ virtual
+ ~polymorphic_derived_object_statements ();
+
+ public:
+ // Delayed loading.
+ //
+ static void
+ delayed_loader (odb::database&,
+ const id_type&,
+ root_type&,
+ const schema_version_migration*);
+
+ public:
+ // Root and immediate base statements.
+ //
+ root_statements_type&
+ root_statements ()
+ {
+ return root_statements_;
+ }
+
+ base_statements_type&
+ base_statements ()
+ {
+ return base_statements_;
+ }
+
+ public:
+ // Object image.
+ //
+ image_type&
+ image ()
+ {
+ return image_;
+ }
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ std::size_t
+ insert_id_binding_version () const { return insert_id_binding_version_;}
+
+ void
+ insert_id_binding_version (std::size_t v) {insert_id_binding_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_binding_version () const { return update_id_binding_version_;}
+
+ void
+ update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t*
+ select_image_versions () { return select_image_versions_;}
+
+ binding*
+ select_image_bindings () {return select_image_bindings_;}
+
+ binding&
+ select_image_binding (std::size_t d)
+ {
+ return select_image_bindings_[object_traits::depth - d];
+ }
+
+ bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Object id binding (comes from the root statements).
+ //
+ id_image_type&
+ id_image () {return root_statements_.id_image ();}
+
+ std::size_t
+ id_image_version () const {return root_statements_.id_image_version ();}
+
+ void
+ id_image_version (std::size_t v) {root_statements_.id_image_version (v);}
+
+ binding&
+ id_image_binding () {return root_statements_.id_image_binding ();}
+
+ binding&
+ optimistic_id_image_binding () {
+ return root_statements_.optimistic_id_image_binding ();}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ {
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ 0));
+ }
+
+ return *persist_;
+ }
+
+ select_statement_type&
+ find_statement (std::size_t d)
+ {
+ std::size_t i (object_traits::depth - d);
+ details::shared_ptr<select_statement_type>& p (find_[i]);
+
+ if (p == 0)
+ {
+ p.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ object_traits::find_statements[i],
+ object_traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ root_statements_.id_image_binding (),
+ select_image_bindings_[i]));
+ }
+
+ return *p;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ {
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ object_traits::update_statement,
+ object_traits::versioned, // Process if versioned.
+ update_image_binding_));
+ }
+
+ return *update_;
+ }
+
+ delete_statement_type&
+ erase_statement ()
+ {
+ if (erase_ == 0)
+ {
+ erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::erase_statement,
+ root_statements_.id_image_binding ()));
+ }
+
+ return *erase_;
+ }
+
+ // Extra (container, section) statement cache.
+ //
+ extra_statement_cache_type&
+ extra_statement_cache ()
+ {
+ return extra_statement_cache_.get (
+ conn_,
+ image_,
+ id_image (),
+ id_image_binding (),
+ &id_image_binding ()); // Note, not id+version.
+ }
+
+ public:
+ // select = total - id - separate_load + base::select
+ // insert = total - inverse
+ // update = total - inverse - id - readonly - separate_update
+ //
+ static const std::size_t id_column_count =
+ object_traits::id_column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count -
+ id_column_count -
+ object_traits::separate_load_column_count +
+ base_statements_type::select_column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count -
+ object_traits::inverse_column_count;
+
+ static const std::size_t update_column_count = insert_column_count -
+ object_traits::id_column_count -
+ object_traits::readonly_column_count -
+ object_traits::separate_update_column_count;
+
+ private:
+ polymorphic_derived_object_statements (
+ const polymorphic_derived_object_statements&);
+
+ polymorphic_derived_object_statements&
+ operator= (const polymorphic_derived_object_statements&);
+
+ private:
+ root_statements_type& root_statements_;
+ base_statements_type& base_statements_;
+
+ extra_statement_cache_ptr<extra_statement_cache_type,
+ image_type,
+ id_image_type> extra_statement_cache_;
+
+ image_type image_;
+
+ // Select binding. Here we are have an array of statements/bindings
+ // one for each depth. In other words, if we have classes root, base,
+ // and derived, then we have the following array of statements:
+ //
+ // [0] d + b + r
+ // [1] d + b
+ // [2] d
+ //
+ // Also, because we have a chain of images bound to these statements,
+ // we have an array of versions, one entry for each base plus one for
+ // our own image.
+ //
+ // A poly-abstract class only needs the first statement and in this
+ // case we have only one entry in the the bindings and statements
+ // arrays (but not versions; we still have a chain of images).
+ //
+ std::size_t select_image_versions_[object_traits::depth];
+ binding select_image_bindings_[
+ object_traits::abstract ? 1 : object_traits::depth];
+ bind select_image_bind_[select_column_count];
+ bool select_image_truncated_[select_column_count];
+
+ // Insert binding. The id binding is copied from the hierarchy root.
+ //
+ std::size_t insert_image_version_;
+ std::size_t insert_id_binding_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[insert_column_count];
+
+ // Update binding. The id suffix binding is copied from the hierarchy
+ // root.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_binding_version_;
+ binding update_image_binding_;
+ bind update_image_bind_[update_column_count + id_column_count];
+
+ details::shared_ptr<insert_statement_type> persist_;
+ details::shared_ptr<select_statement_type> find_[
+ object_traits::abstract ? 1 : object_traits::depth];
+ details::shared_ptr<update_statement_type> update_;
+ details::shared_ptr<delete_statement_type> erase_;
+ };
+ }
+}
+
+#include <odb/sqlite/polymorphic-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_POLYMORPHIC_OBJECT_STATEMENTS_HXX
diff --git a/libodb-sqlite/odb/sqlite/polymorphic-object-statements.txx b/libodb-sqlite/odb/sqlite/polymorphic-object-statements.txx
new file mode 100644
index 0000000..6a376d3
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/polymorphic-object-statements.txx
@@ -0,0 +1,145 @@
+// file : odb/sqlite/polymorphic-object-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/transaction.hxx>
+#include <odb/sqlite/statement-cache.hxx>
+#include <odb/sqlite/traits-calls.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ // polymorphic_root_object_statements
+ //
+
+ template <typename T>
+ polymorphic_root_object_statements<T>::
+ ~polymorphic_root_object_statements ()
+ {
+ }
+
+ template <typename T>
+ polymorphic_root_object_statements<T>::
+ polymorphic_root_object_statements (connection_type& conn)
+ : object_statements<T> (conn),
+ discriminator_image_binding_ (discriminator_image_bind_,
+ discriminator_column_count +
+ managed_optimistic_column_count),
+ discriminator_id_image_binding_ (discriminator_id_image_bind_,
+ id_column_count)
+ {
+ discriminator_image_.version = 0;
+ discriminator_id_image_.version = 0;
+
+ discriminator_image_version_ = 0;
+ discriminator_id_image_version_ = 0;
+
+ std::memset (discriminator_image_bind_,
+ 0,
+ sizeof (discriminator_image_bind_));
+ std::memset (discriminator_id_image_bind_,
+ 0,
+ sizeof (discriminator_id_image_bind_));
+ std::memset (discriminator_image_truncated_,
+ 0,
+ sizeof (discriminator_image_truncated_));
+
+ for (std::size_t i (0);
+ i < discriminator_column_count + managed_optimistic_column_count;
+ ++i)
+ {
+ discriminator_image_bind_[i].truncated =
+ discriminator_image_truncated_ + i;
+ }
+ }
+
+ //
+ // polymorphic_derived_object_statements
+ //
+
+ template <typename T>
+ polymorphic_derived_object_statements<T>::
+ ~polymorphic_derived_object_statements ()
+ {
+ }
+
+ template <typename T>
+ polymorphic_derived_object_statements<T>::
+ polymorphic_derived_object_statements (connection_type& conn)
+ : statements_base (conn),
+ root_statements_ (conn.statement_cache ().find_object<root_type> ()),
+ base_statements_ (conn.statement_cache ().find_object<base_type> ()),
+ insert_image_binding_ (insert_image_bind_, insert_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count)
+ {
+ image_.base = &base_statements_.image ();
+ image_.version = 0;
+
+ for (std::size_t i (0); i < object_traits::depth; ++i)
+ select_image_versions_[i] = 0;
+
+ for (std::size_t i (0);
+ i < (object_traits::abstract ? 1 : object_traits::depth);
+ ++i)
+ {
+ select_image_bindings_[i].bind = select_image_bind_;
+ select_image_bindings_[i].count = object_traits::find_column_counts[i];
+ }
+
+ insert_image_version_ = 0;
+ insert_id_binding_version_ = 0;
+ update_image_version_ = 0;
+ update_id_binding_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+
+ for (std::size_t i (0); i < select_column_count; ++i)
+ select_image_bind_[i].truncated = select_image_truncated_ + i;
+ }
+
+ template <typename T>
+ void polymorphic_derived_object_statements<T>::
+ delayed_loader (odb::database& db,
+ const id_type& id,
+ root_type& robj,
+ const schema_version_migration* svm)
+ {
+ connection_type& conn (transaction::current ().connection (db));
+ polymorphic_derived_object_statements& sts (
+ conn.statement_cache ().find_object<object_type> ());
+ root_statements_type& rsts (sts.root_statements ());
+
+ object_type& obj (static_cast<object_type&> (robj));
+
+ // The same code as in object_statements::load_delayed_().
+ //
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (sts, &id))
+ throw object_not_persistent ();
+
+ object_traits::callback (db, obj, callback_event::pre_load);
+ tc.init (obj, sts.image (), &db);
+ tc.load_ (sts, obj, false); // Load containers, etc.
+
+ rsts.load_delayed (svm);
+
+ {
+ typename root_statements_type::auto_unlock u (rsts);
+ object_traits::callback (db, obj, callback_event::post_load);
+ }
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/prepared-query.cxx b/libodb-sqlite/odb/sqlite/prepared-query.cxx
new file mode 100644
index 0000000..79df0f2
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/prepared-query.cxx
@@ -0,0 +1,27 @@
+// file : odb/sqlite/prepared-query.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/sqlite/prepared-query.hxx>
+
+#include <odb/sqlite/connection.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ prepared_query_impl::
+ ~prepared_query_impl ()
+ {
+ }
+
+ bool prepared_query_impl::
+ verify_connection (odb::transaction& t)
+ {
+ // The transaction can be started using the main database of any of the
+ // attached databases. So we verify the main connections match.
+ //
+ return &static_cast<connection&> (t.connection ()).main_connection () ==
+ &static_cast<connection&> (stmt->connection ()).main_connection ();
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/prepared-query.hxx b/libodb-sqlite/odb/sqlite/prepared-query.hxx
new file mode 100644
index 0000000..a8873a5
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/prepared-query.hxx
@@ -0,0 +1,37 @@
+// file : odb/sqlite/prepared-query.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_PREPARED_QUERY_HXX
+#define ODB_SQLITE_PREPARED_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/prepared-query.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/query.hxx>
+
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ struct LIBODB_SQLITE_EXPORT prepared_query_impl: odb::prepared_query_impl
+ {
+ virtual
+ ~prepared_query_impl ();
+
+ prepared_query_impl (odb::connection& c): odb::prepared_query_impl (c) {}
+
+ virtual bool
+ verify_connection (odb::transaction&);
+
+ sqlite::query_base query;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_PREPARED_QUERY_HXX
diff --git a/libodb-sqlite/odb/sqlite/query-const-expr.cxx b/libodb-sqlite/odb/sqlite/query-const-expr.cxx
new file mode 100644
index 0000000..c8eaec7
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/query-const-expr.cxx
@@ -0,0 +1,14 @@
+// file : odb/sqlite/query-const-expr.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/sqlite/query.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // Sun CC cannot handle this in query.cxx.
+ //
+ const query_base query_base::true_expr (true);
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/query-dynamic.cxx b/libodb-sqlite/odb/sqlite/query-dynamic.cxx
new file mode 100644
index 0000000..8089aed
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/query-dynamic.cxx
@@ -0,0 +1,157 @@
+// file : odb/sqlite/query-dynamic.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+
+#include <odb/sqlite/query-dynamic.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace sqlite
+ {
+ static const char* logic_operators[] = {") AND (", ") OR ("};
+ static const char* comp_operators[] = {"=", "!=", "<", ">", "<=", ">="};
+
+ static void
+ translate (query_base& q, const odb::query_base& s, size_t p)
+ {
+ typedef odb::query_base::clause_part part;
+
+ const part& x (s.clause ()[p]);
+
+ switch (x.kind)
+ {
+ case part::kind_column:
+ {
+ const query_column_base* c (
+ static_cast<const query_column_base*> (
+ x.native_info[id_sqlite].column));
+
+ q.append (c->table (), c->column ());
+ break;
+ }
+ case part::kind_param_val:
+ case part::kind_param_ref:
+ {
+ const query_column_base* c (
+ static_cast<const query_column_base*> (
+ x.native_info[id_sqlite].column));
+
+ query_param_factory f (
+ reinterpret_cast<query_param_factory> (
+ x.native_info[id_sqlite].param_factory));
+
+ const odb::query_param* p (
+ reinterpret_cast<const odb::query_param*> (x.data));
+
+ q.append (f (p->value, x.kind == part::kind_param_ref),
+ c->conversion ());
+ break;
+ }
+ case part::kind_native:
+ {
+ q.append (s.strings ()[x.data]);
+ break;
+ }
+ case part::kind_true:
+ case part::kind_false:
+ {
+ q.append (x.kind == part::kind_true);
+ break;
+ }
+ case part::op_add:
+ {
+ translate (q, s, x.data);
+ translate (q, s, p - 1);
+ break;
+ }
+ case part::op_and:
+ case part::op_or:
+ {
+ q += "(";
+ translate (q, s, x.data);
+ q += logic_operators[x.kind - part::op_and];
+ translate (q, s, p - 1);
+ q += ")";
+ break;
+ }
+ case part::op_not:
+ {
+ q += "NOT (";
+ translate (q, s, p - 1);
+ q += ")";
+ break;
+ }
+ case part::op_null:
+ case part::op_not_null:
+ {
+ translate (q, s, p - 1);
+ q += (x.kind == part::op_null ? "IS NULL" : "IS NOT NULL");
+ break;
+ }
+ case part::op_in:
+ {
+ if (x.data != 0)
+ {
+ size_t b (p - x.data);
+
+ translate (q, s, b - 1); // column
+ q += "IN (";
+
+ for (size_t i (b); i != p; ++i)
+ {
+ if (i != b)
+ q += ",";
+
+ translate (q, s, i);
+ }
+
+ q += ")";
+ }
+ else
+ q.append (false);
+
+ break;
+ }
+ case part::op_like:
+ {
+ translate (q, s, p - 2); // column
+ q += "LIKE";
+ translate (q, s, p - 1); // pattern
+ break;
+ }
+ case part::op_like_escape:
+ {
+ translate (q, s, p - 3); // column
+ q += "LIKE";
+ translate (q, s, p - 2); // pattern
+ q += "ESCAPE";
+ translate (q, s, p - 1); // escape
+ break;
+ }
+ case part::op_eq:
+ case part::op_ne:
+ case part::op_lt:
+ case part::op_gt:
+ case part::op_le:
+ case part::op_ge:
+ {
+ translate (q, s, x.data);
+ q += comp_operators[x.kind - part::op_eq];
+ translate (q, s, p - 1);
+ break;
+ }
+ }
+ }
+
+ query_base::
+ query_base (const odb::query_base& q)
+ : parameters_ (new (details::shared) query_params)
+ {
+ if (!q.empty ())
+ translate (*this, q, q.clause ().size () - 1);
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/query-dynamic.hxx b/libodb-sqlite/odb/sqlite/query-dynamic.hxx
new file mode 100644
index 0000000..f720a95
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/query-dynamic.hxx
@@ -0,0 +1,32 @@
+// file : odb/sqlite/query-dynamic.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_QUERY_DYNAMIC_HXX
+#define ODB_SQLITE_QUERY_DYNAMIC_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/query.hxx>
+#include <odb/query-dynamic.hxx>
+
+#include <odb/sqlite/query.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ typedef details::shared_ptr<query_param> (*query_param_factory) (
+ const void* val, bool by_ref);
+
+ template <typename T, database_type_id ID>
+ details::shared_ptr<query_param>
+ query_param_factory_impl (const void*, bool);
+ }
+}
+
+#include <odb/sqlite/query-dynamic.ixx>
+#include <odb/sqlite/query-dynamic.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_QUERY_DYNAMIC_HXX
diff --git a/libodb-sqlite/odb/sqlite/query-dynamic.ixx b/libodb-sqlite/odb/sqlite/query-dynamic.ixx
new file mode 100644
index 0000000..7fafe3e
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/query-dynamic.ixx
@@ -0,0 +1,26 @@
+// file : odb/sqlite/query-dynamic.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ //
+ template <typename T, database_type_id ID>
+ inline query_column<T, ID>::
+ query_column (odb::query_column<T>& qc,
+ const char* table, const char* column, const char* conv)
+ : query_column_base (table, column, conv)
+ {
+ native_column_info& ci (qc.native_info[id_sqlite]);
+ ci.column = static_cast<query_column_base*> (this);
+
+ // For some reason GCC needs this statically-typed pointer in
+ // order to instantiate the functions.
+ //
+ query_param_factory f (&query_param_factory_impl<T, ID>);
+ ci.param_factory = reinterpret_cast<void*> (f);
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/query-dynamic.txx b/libodb-sqlite/odb/sqlite/query-dynamic.txx
new file mode 100644
index 0000000..48b7ec4
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/query-dynamic.txx
@@ -0,0 +1,20 @@
+// file : odb/sqlite/query-dynamic.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T, database_type_id ID>
+ details::shared_ptr<query_param>
+ query_param_factory_impl (const void* val, bool by_ref)
+ {
+ const T& v (*static_cast<const T*> (val));
+
+ return details::shared_ptr<query_param> (
+ by_ref
+ ? new (details::shared) query_param_impl<T, ID> (ref_bind<T> (v))
+ : new (details::shared) query_param_impl<T, ID> (val_bind<T> (v)));
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/query.cxx b/libodb-sqlite/odb/sqlite/query.cxx
new file mode 100644
index 0000000..98eb1cd
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/query.cxx
@@ -0,0 +1,378 @@
+// file : odb/sqlite/query.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+
+#include <odb/sqlite/query.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // query_param
+ //
+
+ query_param::
+ ~query_param ()
+ {
+ }
+
+ // query_params
+ //
+
+ query_params::
+ query_params (const query_params& x)
+ : details::shared_base (x),
+ params_ (x.params_), bind_ (x.bind_), binding_ (0, 0)
+ {
+ // Here and below we want to maintain up to date binding info so
+ // that the call to binding() below is an immutable operation,
+ // provided the query does not have any by-reference parameters.
+ // This way a by-value-only query can be shared between multiple
+ // threads without the need for synchronization.
+ //
+ if (size_t n = bind_.size ())
+ {
+ binding_.bind = &bind_[0];
+ binding_.count = n;
+ binding_.version++;
+ }
+ }
+
+ query_params& query_params::
+ operator= (const query_params& x)
+ {
+ if (this != &x)
+ {
+ params_ = x.params_;
+ bind_ = x.bind_;
+
+ size_t n (bind_.size ());
+ binding_.bind = n != 0 ? &bind_[0] : 0;
+ binding_.count = n;
+ binding_.version++;
+ }
+
+ return *this;
+ }
+
+ query_params& query_params::
+ operator+= (const query_params& x)
+ {
+ size_t n (bind_.size ());
+
+ params_.insert (params_.end (), x.params_.begin (), x.params_.end ());
+ bind_.insert (bind_.end (), x.bind_.begin (), x.bind_.end ());
+
+ if (n != bind_.size ())
+ {
+ binding_.bind = &bind_[0];
+ binding_.count = bind_.size ();
+ binding_.version++;
+ }
+
+ return *this;
+ }
+
+ void query_params::
+ add (details::shared_ptr<query_param> p)
+ {
+ params_.push_back (p);
+ bind_.push_back (sqlite::bind ());
+ binding_.bind = &bind_[0];
+ binding_.count = bind_.size ();
+ binding_.version++;
+
+ sqlite::bind* b (&bind_.back ());
+ memset (b, 0, sizeof (sqlite::bind));
+ p->bind (b);
+ }
+
+ void query_params::
+ init ()
+ {
+ bool inc_ver (false);
+
+ for (size_t i (0); i < params_.size (); ++i)
+ {
+ query_param& p (*params_[i]);
+
+ if (p.reference ())
+ {
+ if (p.init ())
+ {
+ p.bind (&bind_[i]);
+ inc_ver = true;
+ }
+ }
+ }
+
+ if (inc_ver)
+ binding_.version++;
+ }
+
+ // query_base
+ //
+
+ query_base::
+ query_base (const query_base& q)
+ : clause_ (q.clause_),
+ parameters_ (new (details::shared) query_params (*q.parameters_))
+ {
+ }
+
+ query_base& query_base::
+ operator= (const query_base& q)
+ {
+ if (this != &q)
+ {
+ clause_ = q.clause_;
+ *parameters_ = *q.parameters_;
+ }
+
+ return *this;
+ }
+
+ query_base& query_base::
+ operator+= (const query_base& q)
+ {
+ clause_.insert (clause_.end (), q.clause_.begin (), q.clause_.end ());
+ *parameters_ += *q.parameters_;
+ return *this;
+ }
+
+ void query_base::
+ append (const string& q)
+ {
+ if (!clause_.empty () &&
+ clause_.back ().kind == clause_part::kind_native)
+ {
+ string& s (clause_.back ().part);
+
+ char first (!q.empty () ? q[0] : ' ');
+ char last (!s.empty () ? s[s.size () - 1] : ' ');
+
+ // We don't want extra spaces after '(' as well as before ','
+ // and ')'.
+ //
+ if (last != ' ' && last != '\n' && last != '(' &&
+ first != ' ' && first != '\n' && first != ',' && first != ')')
+ s += ' ';
+
+ s += q;
+ }
+ else
+ clause_.push_back (clause_part (clause_part::kind_native, q));
+ }
+
+ void query_base::
+ append (const char* table, const char* column)
+ {
+ string s (table);
+ s += '.';
+ s += column;
+
+ clause_.push_back (clause_part (clause_part::kind_column, s));
+ }
+
+ void query_base::
+ append (details::shared_ptr<query_param> p, const char* conv)
+ {
+ clause_.push_back (clause_part (clause_part::kind_param));
+
+ if (conv != 0)
+ clause_.back ().part = conv;
+
+ parameters_->add (p);
+ }
+
+ static bool
+ check_prefix (const string& s)
+ {
+ string::size_type n;
+
+ // It is easier to compare to upper and lower-case versions
+ // rather than getting involved with the portable case-
+ // insensitive string comparison mess.
+ //
+ if (s.compare (0, (n = 5), "WHERE") == 0 ||
+ s.compare (0, (n = 5), "where") == 0 ||
+ s.compare (0, (n = 6), "SELECT") == 0 ||
+ s.compare (0, (n = 6), "select") == 0 ||
+ s.compare (0, (n = 8), "ORDER BY") == 0 ||
+ s.compare (0, (n = 8), "order by") == 0 ||
+ s.compare (0, (n = 8), "GROUP BY") == 0 ||
+ s.compare (0, (n = 8), "group by") == 0 ||
+ s.compare (0, (n = 6), "HAVING") == 0 ||
+ s.compare (0, (n = 6), "having") == 0 ||
+ s.compare (0, (n = 4), "WITH") == 0 ||
+ s.compare (0, (n = 4), "with") == 0 ||
+ s.compare (0, (n = 6), "PRAGMA") == 0 ||
+ s.compare (0, (n = 6), "pragma") == 0)
+ {
+ // It either has to be an exact match, or there should be
+ // a whitespace following the keyword.
+ //
+ if (s.size () == n || s[n] == ' ' || s[n] == '\n' || s[n] =='\t')
+ return true;
+ }
+
+ return false;
+ }
+
+ void query_base::
+ optimize ()
+ {
+ // Remove a single TRUE literal or one that is followe by one of
+ // the other clauses. This avoids useless WHERE clauses like
+ //
+ // WHERE TRUE GROUP BY foo
+ //
+ clause_type::iterator i (clause_.begin ()), e (clause_.end ());
+
+ if (i != e && i->kind == clause_part::kind_bool && i->bool_part)
+ {
+ clause_type::iterator j (i + 1);
+
+ if (j == e ||
+ (j->kind == clause_part::kind_native && check_prefix (j->part)))
+ clause_.erase (i);
+ }
+ }
+
+ const char* query_base::
+ clause_prefix () const
+ {
+ if (!clause_.empty ())
+ {
+ const clause_part& p (clause_.front ());
+
+ if (p.kind == clause_part::kind_native && check_prefix (p.part))
+ return "";
+
+ return "WHERE ";
+ }
+
+ return "";
+ }
+
+ string query_base::
+ clause () const
+ {
+ string r;
+
+ for (clause_type::const_iterator i (clause_.begin ()),
+ end (clause_.end ());
+ i != end;
+ ++i)
+ {
+ char last (!r.empty () ? r[r.size () - 1] : ' ');
+
+ switch (i->kind)
+ {
+ case clause_part::kind_column:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ r += i->part;
+ break;
+ }
+ case clause_part::kind_param:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ // Add the conversion expression, if any.
+ //
+ string::size_type p (0);
+ if (!i->part.empty ())
+ {
+ p = i->part.find ("(?)");
+ r.append (i->part, 0, p);
+ }
+
+ r += '?';
+
+ if (!i->part.empty ())
+ r.append (i->part, p + 3, string::npos);
+
+ break;
+ }
+ case clause_part::kind_native:
+ {
+ // We don't want extra spaces after '(' as well as before ','
+ // and ')'.
+ //
+ const string& p (i->part);
+ char first (!p.empty () ? p[0] : ' ');
+
+ if (last != ' ' && first != '\n' && last != '(' &&
+ first != ' ' && last != '\n' && first != ',' && first != ')')
+ r += ' ';
+
+ r += p;
+ break;
+ }
+ case clause_part::kind_bool:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ r += i->bool_part ? "1" : "0";
+ break;
+ }
+ }
+ }
+
+ return clause_prefix () + r;
+ }
+
+ query_base
+ operator&& (const query_base& x, const query_base& y)
+ {
+ // Optimize cases where one or both sides are constant truth.
+ //
+ bool xt (x.const_true ()), yt (y.const_true ());
+
+ if (xt && yt)
+ return x;
+
+ if (xt)
+ return y;
+
+ if (yt)
+ return x;
+
+ query_base r ("(");
+ r += x;
+ r += ") AND (";
+ r += y;
+ r += ")";
+ return r;
+ }
+
+ query_base
+ operator|| (const query_base& x, const query_base& y)
+ {
+ query_base r ("(");
+ r += x;
+ r += ") OR (";
+ r += y;
+ r += ")";
+ return r;
+ }
+
+ query_base
+ operator! (const query_base& x)
+ {
+ query_base r ("NOT (");
+ r += x;
+ r += ")";
+ return r;
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/query.hxx b/libodb-sqlite/odb/sqlite/query.hxx
new file mode 100644
index 0000000..c9cbfaa
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/query.hxx
@@ -0,0 +1,1728 @@
+// file : odb/sqlite/query.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_QUERY_HXX
+#define ODB_SQLITE_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx> // odb::query_column
+#include <odb/query.hxx>
+#include <odb/details/buffer.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/traits.hxx>
+#include <odb/sqlite/sqlite-types.hxx>
+#include <odb/sqlite/binding.hxx>
+
+#include <odb/sqlite/details/export.hxx>
+#include <odb/sqlite/details/conversion.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ struct val_bind
+ {
+ typedef const T& type;
+
+ explicit
+ val_bind (type v): val (v) {}
+
+ type val;
+ };
+
+ template <typename T, std::size_t N>
+ struct val_bind<T[N]>
+ {
+ typedef const T* type;
+
+ explicit
+ val_bind (type v): val (v) {}
+
+ type val;
+ };
+
+ template <typename T>
+ struct ref_bind
+ {
+ typedef const T& type;
+
+ explicit
+ ref_bind (type r): ref (r) {}
+
+ const void*
+ ptr () const {return &ref;}
+
+ type ref;
+ };
+
+ template <typename T, std::size_t N>
+ struct ref_bind<T[N]>
+ {
+ typedef const T* type;
+
+ explicit
+ ref_bind (type r): ref (r) {}
+
+ // Allow implicit conversion from decayed ref_bind's.
+ //
+ ref_bind (ref_bind<T*> r): ref (r.ref) {}
+ ref_bind (ref_bind<const T*> r): ref (r.ref) {}
+
+ const void*
+ ptr () const {return ref;}
+
+ type ref;
+ };
+
+ template <typename T, database_type_id ID>
+ struct val_bind_typed: val_bind<T>
+ {
+ explicit
+ val_bind_typed (typename val_bind<T>::type v): val_bind<T> (v) {}
+ };
+
+ template <typename T, database_type_id ID>
+ struct ref_bind_typed: ref_bind<T>
+ {
+ explicit
+ ref_bind_typed (typename ref_bind<T>::type r): ref_bind<T> (r) {}
+ };
+
+ struct LIBODB_SQLITE_EXPORT query_param: details::shared_base
+ {
+ virtual
+ ~query_param ();
+
+ bool
+ reference () const
+ {
+ return value_ != 0;
+ }
+
+ virtual bool
+ init () = 0;
+
+ virtual void
+ bind (sqlite::bind*) = 0;
+
+ protected:
+ query_param (const void* value)
+ : value_ (value)
+ {
+ }
+
+ protected:
+ const void* value_;
+ };
+
+ class query_base;
+
+ class LIBODB_SQLITE_EXPORT query_params: public details::shared_base
+ {
+ public:
+ typedef sqlite::binding binding_type;
+
+ void
+ init ();
+
+ binding_type&
+ binding () {return binding_;}
+
+ private:
+ friend class query_base;
+
+ query_params (): binding_ (0, 0) {}
+ query_params (const query_params&);
+
+ query_params&
+ operator= (const query_params&);
+
+ query_params&
+ operator+= (const query_params&);
+
+ void
+ add (details::shared_ptr<query_param>);
+
+ private:
+ typedef std::vector<details::shared_ptr<query_param> > params;
+
+ params params_;
+ std::vector<sqlite::bind> bind_;
+ binding_type binding_;
+ };
+
+ //
+ //
+ template <typename T, database_type_id ID>
+ struct query_column;
+
+ class LIBODB_SQLITE_EXPORT query_base
+ {
+ public:
+ struct clause_part
+ {
+ enum kind_type
+ {
+ kind_column,
+ kind_param,
+ kind_native,
+ kind_bool
+ };
+
+ clause_part (kind_type k): kind (k), bool_part (false) {}
+ clause_part (kind_type k, const std::string& p)
+ : kind (k), part (p), bool_part (false) {}
+ clause_part (bool p): kind (kind_bool), bool_part (p) {}
+
+ kind_type kind;
+ std::string part; // If kind is param, then part is conversion expr.
+ bool bool_part;
+ };
+
+ query_base ()
+ : parameters_ (new (details::shared) query_params)
+ {
+ }
+
+ // True or false literal.
+ //
+ explicit
+ query_base (bool v)
+ : parameters_ (new (details::shared) query_params)
+ {
+ append (v);
+ }
+
+ explicit
+ query_base (const char* native)
+ : parameters_ (new (details::shared) query_params)
+ {
+ clause_.push_back (clause_part (clause_part::kind_native, native));
+ }
+
+ explicit
+ query_base (const std::string& native)
+ : parameters_ (new (details::shared) query_params)
+ {
+ clause_.push_back (clause_part (clause_part::kind_native, native));
+ }
+
+ query_base (const char* table, const char* column)
+ : parameters_ (new (details::shared) query_params)
+ {
+ append (table, column);
+ }
+
+ template <typename T>
+ explicit
+ query_base (val_bind<T> v)
+ : parameters_ (new (details::shared) query_params)
+ {
+ *this += v;
+ }
+
+ template <typename T, database_type_id ID>
+ explicit
+ query_base (val_bind_typed<T, ID> v)
+ : parameters_ (new (details::shared) query_params)
+ {
+ *this += v;
+ }
+
+ template <typename T>
+ explicit
+ query_base (ref_bind<T> r)
+ : parameters_ (new (details::shared) query_params)
+ {
+ *this += r;
+ }
+
+ template <typename T, database_type_id ID>
+ explicit
+ query_base (ref_bind_typed<T, ID> r)
+ : parameters_ (new (details::shared) query_params)
+ {
+ *this += r;
+ }
+
+ template <database_type_id ID>
+ query_base (const query_column<bool, ID>&);
+
+ // Translate common query representation to SQLite native. Defined
+ // in query-dynamic.cxx
+ //
+ query_base (const odb::query_base&);
+
+ // Copy c-tor and assignment.
+ //
+ query_base (const query_base&);
+
+ query_base&
+ operator= (const query_base&);
+
+ public:
+ std::string
+ clause () const;
+
+ const char*
+ clause_prefix () const;
+
+ // Initialize the by-reference parameters from bound variables.
+ //
+ void
+ init_parameters () const;
+
+ binding&
+ parameters_binding () const;
+
+ const details::shared_ptr<query_params>&
+ parameters () const;
+
+ public:
+ bool
+ empty () const
+ {
+ return clause_.empty ();
+ }
+
+ static const query_base true_expr;
+
+ bool
+ const_true () const
+ {
+ return clause_.size () == 1 &&
+ clause_.front ().kind == clause_part::kind_bool &&
+ clause_.front ().bool_part;
+ }
+
+ void
+ optimize ();
+
+ public:
+ template <typename T>
+ static val_bind<T>
+ _val (const T& x)
+ {
+ return val_bind<T> (x);
+ }
+
+ template <database_type_id ID, typename T>
+ static val_bind_typed<T, ID>
+ _val (const T& x)
+ {
+ return val_bind_typed<T, ID> (x);
+ }
+
+ template <typename T>
+ static ref_bind<T>
+ _ref (const T& x)
+ {
+ return ref_bind<T> (x);
+ }
+
+ template <database_type_id ID, typename T>
+ static ref_bind_typed<T, ID>
+ _ref (const T& x)
+ {
+ return ref_bind_typed<T, ID> (x);
+ }
+
+ // Some compilers (notably VC++), when deducing const T& from const
+ // array do not strip const from the array type. As a result, in the
+ // above signatures we get, for example, T = const char[4] instead
+ // of T = char[4], which is what we want. So to "fix" such compilers,
+ // we will have to provide the following specializations of the above
+ // functions.
+ //
+ template <typename T, std::size_t N>
+ static val_bind<T[N]>
+ _val (const T (&x) [N])
+ {
+ return val_bind<T[N]> (x);
+ }
+
+ template <database_type_id ID, typename T, std::size_t N>
+ static val_bind_typed<T[N], ID>
+ _val (const T (&x) [N])
+ {
+ return val_bind_typed<T[N], ID> (x);
+ }
+
+ template <typename T, std::size_t N>
+ static ref_bind<T[N]>
+ _ref (const T (&x) [N])
+ {
+ return ref_bind<T[N]> (x);
+ }
+
+ template <database_type_id ID, typename T, std::size_t N>
+ static ref_bind_typed<T[N], ID>
+ _ref (const T (&x) [N])
+ {
+ return ref_bind_typed<T[N], ID> (x);
+ }
+
+ public:
+ query_base&
+ operator+= (const query_base&);
+
+ query_base&
+ operator+= (const std::string& q)
+ {
+ append (q);
+ return *this;
+ }
+
+ template <typename T>
+ query_base&
+ operator+= (val_bind<T> v)
+ {
+ append<T, type_traits<T>::db_type_id> (
+ v, details::conversion<T>::to ());
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (val_bind_typed<T, ID> v)
+ {
+ // We are not using default type_traits so no default conversion
+ // either.
+ //
+ append<T, ID> (v, 0);
+ return *this;
+ }
+
+ template <typename T>
+ query_base&
+ operator+= (ref_bind<T> r)
+ {
+ append<T, type_traits<T>::db_type_id> (
+ r, details::conversion<T>::to ());
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (ref_bind_typed<T, ID> r)
+ {
+ // We are not using default type_traits so no default conversion
+ // either.
+ //
+ append<T, ID> (r, 0);
+ return *this;
+ }
+
+ // Implementation details.
+ //
+ public:
+ template <typename T, database_type_id ID>
+ void
+ append (val_bind<T>, const char* conv);
+
+ template <typename T, database_type_id ID>
+ void
+ append (ref_bind<T>, const char* conv);
+
+ void
+ append (details::shared_ptr<query_param>, const char* conv);
+
+ void
+ append (bool v)
+ {
+ clause_.push_back (clause_part (v));
+ }
+
+ void
+ append (const std::string& native);
+
+ void
+ append (const char* native) // Clashes with append(bool).
+ {
+ append (std::string (native));
+ }
+
+ void
+ append (const char* table, const char* column);
+
+ private:
+ typedef std::vector<clause_part> clause_type;
+
+ clause_type clause_;
+ details::shared_ptr<query_params> parameters_;
+ };
+
+ inline query_base
+ operator+ (const query_base& x, const query_base& y)
+ {
+ query_base r (x);
+ r += y;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, val_bind<T> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (val_bind<T> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, val_bind_typed<T, ID> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (val_bind_typed<T, ID> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, ref_bind<T> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (ref_bind<T> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, ref_bind_typed<T, ID> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (ref_bind_typed<T, ID> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const query_base& q, const std::string& s)
+ {
+ query_base r (q);
+ r += s;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const std::string& s, const query_base& q)
+ {
+ query_base r (s);
+ r += q;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, val_bind<T> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (val_bind<T> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, val_bind_typed<T, ID> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (val_bind_typed<T, ID> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, ref_bind<T> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (ref_bind<T> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, ref_bind_typed<T, ID> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (ref_bind_typed<T, ID> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ LIBODB_SQLITE_EXPORT query_base
+ operator&& (const query_base&, const query_base&);
+
+ LIBODB_SQLITE_EXPORT query_base
+ operator|| (const query_base&, const query_base&);
+
+ LIBODB_SQLITE_EXPORT query_base
+ operator! (const query_base&);
+
+ // query_column
+ //
+ struct query_column_base
+ {
+ // Note that we keep shallow copies of the table, column, and conversion
+ // expression. The latter can be NULL.
+ //
+ query_column_base (const char* table,
+ const char* column,
+ const char* conv)
+ : table_ (table), column_ (column), conversion_ (conv)
+ {
+ }
+
+ const char*
+ table () const
+ {
+ return table_;
+ }
+
+ const char*
+ column () const
+ {
+ return column_;
+ }
+
+ // Can be NULL.
+ //
+ const char*
+ conversion () const
+ {
+ return conversion_;
+ }
+
+ protected:
+ const char* table_;
+ const char* column_;
+ const char* conversion_;
+ };
+
+ template <typename T, database_type_id ID>
+ struct query_column: query_column_base
+ {
+ typedef typename decay_traits<T>::type decayed_type;
+
+ // Note that we keep shallow copies of the table, column, and conversion
+ // expression. The latter can be NULL.
+ //
+ query_column (const char* table, const char* column, const char* conv)
+ : query_column_base (table, column, conv) {}
+
+ // Implementation is in query-dynamic.ixx.
+ //
+ query_column (odb::query_column<T>&,
+ const char* table, const char* column, const char* conv);
+
+ // is_null, is_not_null
+ //
+ public:
+ query_base
+ is_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NULL";
+ return q;
+ }
+
+ query_base
+ is_not_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NOT NULL";
+ return q;
+ }
+
+ // in
+ //
+ public:
+ query_base
+ in (decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type, decayed_type,
+ decayed_type) const;
+
+ template <typename I>
+ query_base
+ in_range (I begin, I end) const;
+
+ // like
+ //
+ public:
+ query_base
+ like (decayed_type pattern) const
+ {
+ return like (val_bind<T> (pattern));
+ }
+
+ query_base
+ like (val_bind<T> pattern) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern) const
+ {
+ return like (val_bind<T> (decayed_type (pattern.val)));
+ }
+
+ query_base
+ like (ref_bind<T> pattern) const;
+
+ query_base
+ like (decayed_type pattern, decayed_type escape) const
+ {
+ return like (val_bind<T> (pattern), escape);
+ }
+
+ query_base
+ like (val_bind<T> pattern, decayed_type escape) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern, decayed_type escape) const
+ {
+ return like (val_bind<T> (decayed_type (pattern.val)), escape);
+ }
+
+ query_base
+ like (ref_bind<T> pattern, decayed_type escape) const;
+
+ // =
+ //
+ public:
+ query_base
+ equal (decayed_type v) const
+ {
+ return equal (val_bind<T> (v));
+ }
+
+ query_base
+ equal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ equal (val_bind<T2> v) const
+ {
+ return equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ equal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator== (const query_column& c, decayed_type v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (decayed_type v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, val_bind<T> v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (val_bind<T> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (const query_column& c, val_bind<T2> v)
+ {
+ return c.equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (val_bind<T2> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, ref_bind<T> r)
+ {
+ return c.equal (r);
+ }
+
+ friend query_base
+ operator== (ref_bind<T> r, const query_column& c)
+ {
+ return c.equal (r);
+ }
+
+ // !=
+ //
+ public:
+ query_base
+ unequal (decayed_type v) const
+ {
+ return unequal (val_bind<T> (v));
+ }
+
+ query_base
+ unequal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "!=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ unequal (val_bind<T2> v) const
+ {
+ return unequal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ unequal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "!=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator!= (const query_column& c, decayed_type v)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (decayed_type v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (const query_column& c, val_bind<T> v)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (val_bind<T> v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (const query_column& c, val_bind<T2> v)
+ {
+ return c.unequal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (val_bind<T2> v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (const query_column& c, ref_bind<T> r)
+ {
+ return c.unequal (r);
+ }
+
+ friend query_base
+ operator!= (ref_bind<T> r, const query_column& c)
+ {
+ return c.unequal (r);
+ }
+
+ // <
+ //
+ public:
+ query_base
+ less (decayed_type v) const
+ {
+ return less (val_bind<T> (v));
+ }
+
+ query_base
+ less (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "<";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ less (val_bind<T2> v) const
+ {
+ return less (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ less (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "<";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator< (const query_column& c, decayed_type v)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator< (decayed_type v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator< (const query_column& c, val_bind<T> v)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator< (val_bind<T> v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (const query_column& c, val_bind<T2> v)
+ {
+ return c.less (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator< (const query_column& c, ref_bind<T> r)
+ {
+ return c.less (r);
+ }
+
+ friend query_base
+ operator< (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater (r);
+ }
+
+ // >
+ //
+ public:
+ query_base
+ greater (decayed_type v) const
+ {
+ return greater (val_bind<T> (v));
+ }
+
+ query_base
+ greater (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += ">";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ greater (val_bind<T2> v) const
+ {
+ return greater (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ greater (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += ">";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator> (const query_column& c, decayed_type v)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator> (decayed_type v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator> (const query_column& c, val_bind<T> v)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator> (val_bind<T> v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (val_bind<T2> v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator> (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater (r);
+ }
+
+ friend query_base
+ operator> (ref_bind<T> r, const query_column& c)
+ {
+ return c.less (r);
+ }
+
+ // <=
+ //
+ public:
+ query_base
+ less_equal (decayed_type v) const
+ {
+ return less_equal (val_bind<T> (v));
+ }
+
+ query_base
+ less_equal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "<=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ less_equal (val_bind<T2> v) const
+ {
+ return less_equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ less_equal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "<=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator<= (const query_column& c, decayed_type v)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator<= (decayed_type v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator<= (const query_column& c, val_bind<T> v)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator<= (val_bind<T> v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (const query_column& c, val_bind<T2> v)
+ {
+ return c.less_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator<= (const query_column& c, ref_bind<T> r)
+ {
+ return c.less_equal (r);
+ }
+
+ friend query_base
+ operator<= (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater_equal (r);
+ }
+
+ // >=
+ //
+ public:
+ query_base
+ greater_equal (decayed_type v) const
+ {
+ return greater_equal (val_bind<T> (v));
+ }
+
+ query_base
+ greater_equal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += ">=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ greater_equal (val_bind<T2> v) const
+ {
+ return greater_equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ greater_equal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += ">=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator>= (const query_column& c, decayed_type v)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator>= (decayed_type v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator>= (const query_column& c, val_bind<T> v)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator>= (val_bind<T> v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (val_bind<T2> v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator>= (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater_equal (r);
+ }
+
+ friend query_base
+ operator>= (ref_bind<T> r, const query_column& c)
+ {
+ return c.less_equal (r);
+ }
+
+ // Column comparison.
+ //
+ public:
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator== (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () ==
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator!= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () !=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "!=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator< (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "<";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator> (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += ">";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator<= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "<=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator>= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += ">=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+ };
+
+ // Provide operator+() for using columns to construct native
+ // query fragments (e.g., ORDER BY).
+ //
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_column<T, ID>& c, const std::string& s)
+ {
+ query_base q (c.table (), c.column ());
+ q += s;
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, const query_column<T, ID>& c)
+ {
+ query_base q (s);
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_column<T, ID>& c, const query_base& q)
+ {
+ query_base r (c.table (), c.column ());
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, const query_column<T, ID>& c)
+ {
+ query_base r (q);
+ r.append (c.table (), c.column ());
+ return r;
+ }
+
+ //
+ //
+ template <typename T, database_type_id>
+ struct query_param_impl;
+
+ // INTEGER
+ //
+ template <typename T>
+ struct query_param_impl<T, id_integer>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (sqlite::bind* b)
+ {
+ b->type = sqlite::bind::integer;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_integer>::set_image (image_, is_null, v);
+ }
+
+ private:
+ long long image_;
+ };
+
+ // REAL
+ //
+ template <typename T>
+ struct query_param_impl<T, id_real>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (sqlite::bind* b)
+ {
+ b->type = sqlite::bind::real;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_real>::set_image (image_, is_null, v);
+ }
+
+ private:
+ double image_;
+ };
+
+ // TEXT
+ //
+ template <typename T>
+ struct query_param_impl<T, id_text>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (sqlite::bind* b)
+ {
+ b->type = image_traits<T, id_text>::bind_value;
+ b->buffer = buffer_.data ();
+ b->size = &size_;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t cap (buffer_.capacity ());
+ value_traits<T, id_text>::set_image (buffer_, size_, is_null, v);
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::buffer buffer_;
+ std::size_t size_;
+ };
+
+ // BLOB
+ //
+ template <typename T>
+ struct query_param_impl<T, id_blob>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (sqlite::bind* b)
+ {
+ b->type = sqlite::bind::blob;
+ b->buffer = buffer_.data ();
+ b->size = &size_;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t cap (buffer_.capacity ());
+ value_traits<T, id_blob>::set_image (buffer_, size_, is_null, v);
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::buffer buffer_;
+ std::size_t size_;
+ };
+
+ // TEXT STREAM (reduce to id_text).
+ //
+ template <typename T>
+ struct query_param_impl<T, id_text_stream>: query_param_impl<T, id_text>
+ {
+ query_param_impl (ref_bind<T> r) : query_param_impl<T, id_text> (r) {}
+ query_param_impl (val_bind<T> v) : query_param_impl<T, id_text> (v) {}
+ };
+
+ // BLOB STREAM (reduce to id_blob).
+ //
+ template <typename T>
+ struct query_param_impl<T, id_blob_stream>: query_param_impl<T, id_blob>
+ {
+ query_param_impl (ref_bind<T> r) : query_param_impl<T, id_blob> (r) {}
+ query_param_impl (val_bind<T> v) : query_param_impl<T, id_blob> (v) {}
+ };
+ }
+}
+
+// odb::sqlite::query and odb::query specialization for SQLite.
+//
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ class query: public query_base,
+ public query_selector<T, id_sqlite>::columns_type
+ {
+ public:
+ // We don't define any typedefs here since they may clash with
+ // column names defined by our base type.
+ //
+
+ query ()
+ {
+ }
+
+ explicit
+ query (bool v)
+ : query_base (v)
+ {
+ }
+
+ explicit
+ query (const char* q)
+ : query_base (q)
+ {
+ }
+
+ explicit
+ query (const std::string& q)
+ : query_base (q)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (val_bind<T2> v)
+ : query_base (v)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (ref_bind<T2> r)
+ : query_base (r)
+ {
+ }
+
+ query (const query_base& q)
+ : query_base (q)
+ {
+ }
+
+ template <database_type_id ID>
+ query (const query_column<bool, ID>& qc)
+ : query_base (qc)
+ {
+ }
+
+ query (const odb::query_base& q)
+ : query_base (q)
+ {
+ }
+ };
+
+ namespace core
+ {
+ using sqlite::query;
+ }
+ }
+
+ // Derive odb::query from odb::sqlite::query so that it can be
+ // implicitly converted in sqlite::database::query() calls.
+ //
+ template <typename T>
+ class query<T, sqlite::query_base>: public sqlite::query<T>
+ {
+ public:
+ // We don't define any typedefs here since they may clash with
+ // column names defined by our base type.
+ //
+
+ query ()
+ {
+ }
+
+ explicit
+ query (bool v)
+ : sqlite::query<T> (v)
+ {
+ }
+
+ explicit
+ query (const char* q)
+ : sqlite::query<T> (q)
+ {
+ }
+
+ explicit
+ query (const std::string& q)
+ : sqlite::query<T> (q)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (sqlite::val_bind<T2> v)
+ : sqlite::query<T> (v)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (sqlite::ref_bind<T2> r)
+ : sqlite::query<T> (r)
+ {
+ }
+
+ query (const sqlite::query_base& q)
+ : sqlite::query<T> (q)
+ {
+ }
+
+ template <sqlite::database_type_id ID>
+ query (const sqlite::query_column<bool, ID>& qc)
+ : sqlite::query<T> (qc)
+ {
+ }
+ };
+}
+
+#include <odb/sqlite/query.ixx>
+#include <odb/sqlite/query.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_QUERY_HXX
diff --git a/libodb-sqlite/odb/sqlite/query.ixx b/libodb-sqlite/odb/sqlite/query.ixx
new file mode 100644
index 0000000..00e9b66
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/query.ixx
@@ -0,0 +1,46 @@
+// file : odb/sqlite/query.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace sqlite
+ {
+ inline void query_base::
+ init_parameters () const
+ {
+ return parameters_->init ();
+ }
+
+ inline binding& query_base::
+ parameters_binding () const
+ {
+ return parameters_->binding ();
+ }
+
+ inline const details::shared_ptr<query_params>& query_base::
+ parameters () const
+ {
+ return parameters_;
+ }
+
+ template <typename T, database_type_id ID>
+ inline void query_base::
+ append (val_bind<T> v, const char* conv)
+ {
+ append (
+ details::shared_ptr<query_param> (
+ new (details::shared) query_param_impl<T, ID> (v)),
+ conv);
+ }
+
+ template <typename T, database_type_id ID>
+ inline void query_base::
+ append (ref_bind<T> r, const char* conv)
+ {
+ append (
+ details::shared_ptr<query_param> (
+ new (details::shared) query_param_impl<T, ID> (r)),
+ conv);
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/query.txx b/libodb-sqlite/odb/sqlite/query.txx
new file mode 100644
index 0000000..f381ff0
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/query.txx
@@ -0,0 +1,168 @@
+// file : odb/sqlite/query.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ // query_base
+ //
+
+ template <database_type_id ID>
+ query_base::
+ query_base (const query_column<bool, ID>& c)
+ : parameters_ (new (details::shared) query_params)
+ {
+ // Cannot use IS TRUE here since database type can be a non-
+ // integral type.
+ //
+ append (c.table (), c.column ());
+ append ("=");
+ append<bool, ID> (val_bind<bool> (true), c.conversion ());
+ }
+
+ //
+ // query_column
+ //
+
+ // in
+ //
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3,
+ decayed_type v4) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v4), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3, decayed_type v4,
+ decayed_type v5) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v4), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v5), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ template <typename I>
+ query_base query_column<T, ID>::
+ in_range (I begin, I end) const
+ {
+ if (begin != end)
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+
+ for (I i (begin); i != end; ++i)
+ {
+ if (i != begin)
+ q += ",";
+
+ q.append<T, ID> (val_bind<T> (*i), conversion_);
+ }
+
+ q += ")";
+ return q;
+ }
+ else
+ return query_base (false);
+ }
+
+ // like
+ //
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (val_bind<T> p) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (ref_bind<T> p) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (val_bind<T> p, decayed_type e) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ q += "ESCAPE";
+ q.append<T, ID> (val_bind<T> (e), conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (ref_bind<T> p, decayed_type e) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ q += "ESCAPE";
+ q.append<T, ID> (val_bind<T> (e), conversion_);
+ return q;
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/section-statements.hxx b/libodb-sqlite/odb/sqlite/section-statements.hxx
new file mode 100644
index 0000000..e6a5da6
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/section-statements.hxx
@@ -0,0 +1,198 @@
+// file : odb/sqlite/section-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_SECTION_STATEMENTS_HXX
+#define ODB_SQLITE_SECTION_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/sqlite-types.hxx>
+#include <odb/sqlite/binding.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class connection;
+
+ // Template argument is the section traits type.
+ //
+ template <typename T, typename ST>
+ class section_statements
+ {
+ public:
+ typedef ST traits;
+
+ typedef typename traits::image_type image_type;
+ typedef typename traits::id_image_type id_image_type;
+
+ typedef sqlite::select_statement select_statement_type;
+ typedef sqlite::update_statement update_statement_type;
+
+ typedef sqlite::connection connection_type;
+
+ section_statements (connection_type&,
+ image_type&, id_image_type&,
+ binding& id, binding& idv);
+
+ connection_type&
+ connection () {return conn_;}
+
+ const schema_version_migration&
+ version_migration (const char* name = "") const
+ {
+ if (svm_ == 0)
+ svm_ = &conn_.database ().schema_version_migration (name);
+
+ return *svm_;
+ }
+
+ image_type&
+ image () {return image_;}
+
+ const binding&
+ id_binding () {return id_binding_;}
+
+ // Id and optimistic concurrency version (if any).
+ //
+ const binding&
+ idv_binding () {return idv_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_binding_version () const { return update_id_binding_version_;}
+
+ void
+ update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ //
+ // Statements.
+ //
+
+ select_statement_type&
+ select_statement ()
+ {
+ if (select_ == 0)
+ select_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ traits::select_statement,
+ traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ id_binding_,
+ select_image_binding_));
+
+ return *select_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ traits::update_statement,
+ traits::versioned, // Process if versioned.
+ update_image_binding_));
+
+ return *update_;
+ }
+
+ public:
+ static const std::size_t id_column_count = traits::id_column_count;
+ static const std::size_t managed_optimistic_load_column_count =
+ traits::managed_optimistic_load_column_count;
+ static const std::size_t managed_optimistic_update_column_count =
+ traits::managed_optimistic_update_column_count;
+ static const std::size_t select_column_count = traits::load_column_count;
+ static const std::size_t update_column_count =
+ traits::update_column_count;
+
+ private:
+ section_statements (const section_statements&);
+ section_statements& operator= (const section_statements&);
+
+ protected:
+ connection_type& conn_;
+ mutable const schema_version_migration* svm_;
+
+ // These come from object_statements.
+ //
+ image_type& image_;
+ binding& id_binding_;
+ binding& idv_binding_;
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+
+ static const std::size_t select_bind_count =
+ select_column_count != 0 || managed_optimistic_load_column_count != 0
+ ? select_column_count + managed_optimistic_load_column_count
+ : 1;
+
+ binding select_image_binding_;
+ bind select_image_bind_[select_bind_count];
+ bool select_image_truncated_[select_bind_count];
+
+ // Update binding.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_binding_version_;
+
+ static const std::size_t update_bind_count =
+ update_column_count != 0 || managed_optimistic_update_column_count != 0
+ ? update_column_count + id_column_count +
+ managed_optimistic_update_column_count
+ : 1;
+
+ binding update_image_binding_;
+ bind update_image_bind_[update_bind_count];
+
+ details::shared_ptr<select_statement_type> select_;
+ details::shared_ptr<update_statement_type> update_;
+ };
+ }
+}
+
+#include <odb/sqlite/section-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_SECTION_STATEMENTS_HXX
diff --git a/libodb-sqlite/odb/sqlite/section-statements.txx b/libodb-sqlite/odb/sqlite/section-statements.txx
new file mode 100644
index 0000000..ff588b3
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/section-statements.txx
@@ -0,0 +1,40 @@
+// file : odb/sqlite/section-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T, typename ST>
+ section_statements<T, ST>::
+ section_statements (connection_type& conn,
+ image_type& im, id_image_type&,
+ binding& id, binding& idv)
+ : conn_ (conn),
+ svm_ (0),
+ image_ (im),
+ id_binding_ (id),
+ idv_binding_ (idv),
+ select_image_binding_ (select_image_bind_,
+ select_column_count +
+ managed_optimistic_load_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count +
+ managed_optimistic_update_column_count)
+ {
+ select_image_version_ = 0;
+ update_image_version_ = 0;
+ update_id_binding_version_ = 0;
+
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+
+ for (std::size_t i (0); i < select_bind_count; ++i)
+ select_image_bind_[i].truncated = select_image_truncated_ + i;
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/simple-object-result.hxx b/libodb-sqlite/odb/sqlite/simple-object-result.hxx
new file mode 100644
index 0000000..d68af8e
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/simple-object-result.hxx
@@ -0,0 +1,88 @@
+// file : odb/sqlite/simple-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_SIMPLE_OBJECT_RESULT_HXX
+#define ODB_SQLITE_SIMPLE_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/simple-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx> // query_base, query_params
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/traits-calls.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ class object_result_impl: public odb::object_result_impl<T>
+ {
+ public:
+ typedef odb::object_result_impl<T> base_type;
+
+ typedef typename base_type::id_type id_type;
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_sqlite> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~object_result_impl ();
+
+ object_result_impl (const query_base&,
+ const details::shared_ptr<select_statement>&,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type&, bool fetch);
+
+ virtual id_type
+ load_id ();
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ void
+ load_image ();
+
+ private:
+ // We need to hold on to the query parameters because SQLite uses
+ // the parameter buffers to find each next row.
+ //
+ details::shared_ptr<query_params> params_;
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ };
+ }
+}
+
+#include <odb/sqlite/simple-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_SIMPLE_OBJECT_RESULT_HXX
diff --git a/libodb-sqlite/odb/sqlite/simple-object-result.txx b/libodb-sqlite/odb/sqlite/simple-object-result.txx
new file mode 100644
index 0000000..f27b226
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/simple-object-result.txx
@@ -0,0 +1,158 @@
+// file : odb/sqlite/simple-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/sqlite/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ object_result_impl<T>::
+ ~object_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ params_.reset ();
+ statement_.reset ();
+ }
+
+ template <typename T>
+ object_result_impl<T>::
+ object_result_impl (const query_base& q,
+ const details::shared_ptr<select_statement>& s,
+ statements_type& sts,
+ const schema_version_migration* svm)
+ : base_type (sts.connection ()),
+ params_ (q.parameters ()),
+ statement_ (s),
+ statements_ (sts),
+ tc_ (svm)
+ {
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ load (object_type& obj, bool fetch)
+ {
+ if (fetch)
+ load_image ();
+
+ // This is a top-level call so the statements cannot be locked.
+ //
+ assert (!statements_.locked ());
+ typename statements_type::auto_lock l (statements_);
+
+ object_traits::callback (this->db_, obj, callback_event::pre_load);
+
+ typename object_traits::image_type& i (statements_.image ());
+ tc_.init (obj, i, &this->db_);
+
+ // Initialize the id image and binding and load the rest of the object
+ // (containers, etc).
+ //
+ typename object_traits::id_image_type& idi (statements_.id_image ());
+ object_traits::init (idi, object_traits::id (i));
+
+ binding& idb (statements_.id_image_binding ());
+ if (idi.version != statements_.id_image_version () || idb.version == 0)
+ {
+ object_traits::bind (idb.bind, idi);
+ statements_.id_image_version (idi.version);
+ idb.version++;
+ }
+
+ tc_.load_ (statements_, obj, false);
+ statements_.load_delayed (tc_.version ());
+ l.unlock ();
+ object_traits::callback (this->db_, obj, callback_event::post_load);
+ }
+
+ template <typename T>
+ typename object_result_impl<T>::id_type
+ object_result_impl<T>::
+ load_id ()
+ {
+ load_image ();
+ return object_traits::id (statements_.image ());
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (!statement_->next ())
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ load_image ()
+ {
+ // The image can grow between calls to load() as a result of other
+ // statements execution.
+ //
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ }
+
+ select_statement::result r (statement_->load ());
+
+ if (r == select_statement::truncated)
+ {
+ if (tc_.grow (im, statements_.select_image_truncated ()))
+ im.version++;
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ statement_->reload ();
+ }
+ }
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t object_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/simple-object-statements.cxx b/libodb-sqlite/odb/sqlite/simple-object-statements.cxx
new file mode 100644
index 0000000..1eb07db
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/simple-object-statements.cxx
@@ -0,0 +1,15 @@
+// file : odb/sqlite/simple-object-statements.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/sqlite/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ object_statements_base::
+ ~object_statements_base ()
+ {
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/simple-object-statements.hxx b/libodb-sqlite/odb/sqlite/simple-object-statements.hxx
new file mode 100644
index 0000000..b60fe6c
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/simple-object-statements.hxx
@@ -0,0 +1,591 @@
+// file : odb/sqlite/simple-object-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_SIMPLE_OBJECT_STATEMENTS_HXX
+#define ODB_SQLITE_SIMPLE_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cassert>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/sqlite-types.hxx>
+#include <odb/sqlite/binding.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/statements-base.hxx>
+
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // The extra_statement_cache class is only defined (and used) in
+ // the generated source file. However, object_statements may be
+ // referenced from another source file in the case of a polymorphic
+ // hierarchy (though in this case the extra statement cache is
+ // not used). As a result, we cannot have a by-value member and
+ // instead will store a pointer and lazily allocate the cache if
+ // and when needed. We will also need to store a pointer to the
+ // deleter function which will be initialized during allocation
+ // (at that point we know that the cache class is defined).
+ //
+ template <typename T, typename I, typename ID>
+ struct extra_statement_cache_ptr
+ {
+ typedef I image_type;
+ typedef ID id_image_type;
+ typedef sqlite::connection connection_type;
+
+ extra_statement_cache_ptr (): p_ (0) {}
+ ~extra_statement_cache_ptr ()
+ {
+ if (p_ != 0)
+ (this->*deleter_) (0, 0, 0, 0, 0);
+ }
+
+ T&
+ get (connection_type& c,
+ image_type& im, id_image_type& idim,
+ binding& id, binding* idv)
+ {
+ if (p_ == 0)
+ allocate (&c, &im, &idim, &id, (idv != 0 ? idv : &id));
+
+ return *p_;
+ }
+
+ private:
+ void
+ allocate (connection_type*,
+ image_type*, id_image_type*,
+ binding*, binding*);
+
+ private:
+ T* p_;
+ void (extra_statement_cache_ptr::*deleter_) (
+ connection_type*, image_type*, id_image_type*, binding*, binding*);
+ };
+
+ template <typename T, typename I, typename ID>
+ void extra_statement_cache_ptr<T, I, ID>::
+ allocate (connection_type* c,
+ image_type* im, id_image_type* idim,
+ binding* id, binding* idv)
+ {
+ // To reduce object code size, this function acts as both allocator
+ // and deleter.
+ //
+ if (p_ == 0)
+ {
+ p_ = new T (*c, *im, *idim, *id, *idv);
+ deleter_ = &extra_statement_cache_ptr<T, I, ID>::allocate;
+ }
+ else
+ delete p_;
+ }
+
+ //
+ // Implementation for objects with object id.
+ //
+
+ class LIBODB_SQLITE_EXPORT object_statements_base: public statements_base
+ {
+ public:
+ // Locking.
+ //
+ void
+ lock ()
+ {
+ assert (!locked_);
+ locked_ = true;
+ }
+
+ void
+ unlock ()
+ {
+ assert (locked_);
+ locked_ = false;
+ }
+
+ bool
+ locked () const
+ {
+ return locked_;
+ }
+
+ struct auto_unlock
+ {
+ // Unlocks the statement on construction and re-locks it on
+ // destruction.
+ //
+ auto_unlock (object_statements_base&);
+ ~auto_unlock ();
+
+ private:
+ auto_unlock (const auto_unlock&);
+ auto_unlock& operator= (const auto_unlock&);
+
+ private:
+ object_statements_base& s_;
+ };
+
+ public:
+ virtual
+ ~object_statements_base ();
+
+ protected:
+ object_statements_base (connection_type& conn)
+ : statements_base (conn), locked_ (false)
+ {
+ }
+
+ protected:
+ bool locked_;
+ };
+
+ template <typename T, bool optimistic>
+ struct optimistic_data;
+
+ template <typename T>
+ struct optimistic_data<T, true>
+ {
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_sqlite> object_traits;
+
+ optimistic_data (bind*);
+
+ binding*
+ id_image_binding () {return &id_image_binding_;}
+
+ // The id + optimistic column binding.
+ //
+ binding id_image_binding_;
+
+ details::shared_ptr<delete_statement> erase_;
+ };
+
+ template <typename T>
+ struct optimistic_data<T, false>
+ {
+ optimistic_data (bind*) {}
+
+ binding*
+ id_image_binding () {return 0;}
+ };
+
+ template <typename T>
+ class object_statements: public object_statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_sqlite> object_traits;
+ typedef typename object_traits::id_type id_type;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::image_type image_type;
+ typedef typename object_traits::id_image_type id_image_type;
+
+ typedef
+ typename object_traits::pointer_cache_traits
+ pointer_cache_traits;
+
+ typedef
+ typename object_traits::extra_statement_cache_type
+ extra_statement_cache_type;
+
+ typedef sqlite::insert_statement insert_statement_type;
+ typedef sqlite::select_statement select_statement_type;
+ typedef sqlite::update_statement update_statement_type;
+ typedef sqlite::delete_statement delete_statement_type;
+
+ // Automatic lock.
+ //
+ struct auto_lock
+ {
+ // Lock the statements unless they are already locked in which
+ // case subsequent calls to locked() will return false.
+ //
+ auto_lock (object_statements&);
+
+ // Unlock the statemens if we are holding the lock and clear
+ // the delayed loads. This should only happen in case an
+ // exception is thrown. In normal circumstances, the user
+ // should call unlock() explicitly.
+ //
+ ~auto_lock ();
+
+ // Return true if this auto_lock instance holds the lock.
+ //
+ bool
+ locked () const;
+
+ // Unlock the statemens.
+ //
+ void
+ unlock ();
+
+ private:
+ auto_lock (const auto_lock&);
+ auto_lock& operator= (const auto_lock&);
+
+ private:
+ object_statements& s_;
+ bool locked_;
+ };
+
+
+ public:
+ object_statements (connection_type&);
+
+ virtual
+ ~object_statements ();
+
+ // Delayed loading.
+ //
+ typedef void (*loader_function) (odb::database&,
+ const id_type&,
+ object_type&,
+ const schema_version_migration*);
+
+ void
+ delay_load (const id_type& id,
+ object_type& obj,
+ const typename pointer_cache_traits::position_type& p,
+ loader_function l = 0)
+ {
+ delayed_.push_back (delayed_load (id, obj, p, l));
+ }
+
+ void
+ load_delayed (const schema_version_migration* svm)
+ {
+ assert (locked ());
+
+ if (!delayed_.empty ())
+ load_delayed_<object_statements> (svm);
+ }
+
+ void
+ clear_delayed ()
+ {
+ if (!delayed_.empty ())
+ clear_delayed_ ();
+ }
+
+ // Object image.
+ //
+ image_type&
+ image () {return image_;}
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_image_version () const { return update_id_image_version_;}
+
+ void
+ update_id_image_version (std::size_t v) {update_id_image_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Object id image and binding.
+ //
+ id_image_type&
+ id_image () {return id_image_;}
+
+ std::size_t
+ id_image_version () const {return id_image_version_;}
+
+ void
+ id_image_version (std::size_t v) {id_image_version_ = v;}
+
+ binding&
+ id_image_binding () {return id_image_binding_;}
+
+ // Optimistic id + managed column image binding. It points to
+ // the same suffix as id binding and they are always updated
+ // at the same time.
+ //
+ binding&
+ optimistic_id_image_binding () {return od_.id_image_binding_;}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ {
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ (object_traits::auto_id ? &id_image_binding_ : 0)));
+ }
+
+ return *persist_;
+ }
+
+ select_statement_type&
+ find_statement ()
+ {
+ if (find_ == 0)
+ {
+ find_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ object_traits::find_statement,
+ object_traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ id_image_binding_,
+ select_image_binding_));
+ }
+
+ return *find_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ {
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ object_traits::update_statement,
+ object_traits::versioned, // Process if versioned.
+ update_image_binding_));
+ }
+
+ return *update_;
+ }
+
+ delete_statement_type&
+ erase_statement ()
+ {
+ if (erase_ == 0)
+ {
+ erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::erase_statement,
+ id_image_binding_));
+ }
+
+ return *erase_;
+ }
+
+ delete_statement_type&
+ optimistic_erase_statement ()
+ {
+ if (od_.erase_ == 0)
+ {
+ od_.erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::optimistic_erase_statement,
+ od_.id_image_binding_));
+ }
+
+ return *od_.erase_;
+ }
+
+ // Extra (container, section) statement cache.
+ //
+ extra_statement_cache_type&
+ extra_statement_cache ()
+ {
+ return extra_statement_cache_.get (
+ conn_,
+ image_, id_image_,
+ id_image_binding_, od_.id_image_binding ());
+ }
+
+ public:
+ // select = total - separate_load
+ // insert = total - inverse - managed_optimistic
+ // update = total - inverse - managed_optimistic - id - readonly
+ // - separate_update
+ //
+ static const std::size_t id_column_count =
+ object_traits::id_column_count;
+
+ static const std::size_t managed_optimistic_column_count =
+ object_traits::managed_optimistic_column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count -
+ object_traits::separate_load_column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count -
+ object_traits::inverse_column_count -
+ object_traits::managed_optimistic_column_count;
+
+ static const std::size_t update_column_count =
+ insert_column_count -
+ id_column_count -
+ object_traits::readonly_column_count -
+ object_traits::separate_update_column_count;
+
+ private:
+ object_statements (const object_statements&);
+ object_statements& operator= (const object_statements&);
+
+ protected:
+ template <typename STS>
+ void
+ load_delayed_ (const schema_version_migration*);
+
+ void
+ clear_delayed_ ();
+
+ protected:
+ template <typename T1>
+ friend class polymorphic_derived_object_statements;
+
+ extra_statement_cache_ptr<extra_statement_cache_type,
+ image_type,
+ id_image_type> extra_statement_cache_;
+
+ image_type image_;
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+ binding select_image_binding_;
+ bind select_image_bind_[select_column_count];
+ bool select_image_truncated_[select_column_count];
+
+ // Insert binding.
+ //
+ std::size_t insert_image_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[insert_column_count];
+
+ // Update binding. Note that the id suffix is bound to id_image_
+ // below instead of image_ which makes this binding effectively
+ // bound to two images. As a result, we have to track versions
+ // for both of them. If this object uses optimistic concurrency,
+ // then the binding for the managed column (version, timestamp,
+ // etc) comes after the id and the image for such a column is
+ // stored as part of the id image.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_image_version_;
+ binding update_image_binding_;
+ bind update_image_bind_[update_column_count + id_column_count +
+ managed_optimistic_column_count];
+
+ // Id image binding (only used as a parameter). Uses the suffix in
+ // the update bind.
+ //
+ id_image_type id_image_;
+ std::size_t id_image_version_;
+ binding id_image_binding_;
+
+ // Extra data for objects with optimistic concurrency support.
+ //
+ optimistic_data<T, managed_optimistic_column_count != 0> od_;
+
+ details::shared_ptr<insert_statement_type> persist_;
+ details::shared_ptr<select_statement_type> find_;
+ details::shared_ptr<update_statement_type> update_;
+ details::shared_ptr<delete_statement_type> erase_;
+
+ // Delayed loading.
+ //
+ struct delayed_load
+ {
+ typedef typename pointer_cache_traits::position_type position_type;
+
+ delayed_load () {}
+ delayed_load (const id_type& i,
+ object_type& o,
+ const position_type& p,
+ loader_function l)
+ : id (i), obj (&o), pos (p), loader (l)
+ {
+ }
+
+ id_type id;
+ object_type* obj;
+ position_type pos;
+ loader_function loader;
+ };
+
+ typedef std::vector<delayed_load> delayed_loads;
+ delayed_loads delayed_;
+
+ // Delayed vectors swap guard. See the load_delayed_() function for
+ // details.
+ //
+ struct swap_guard
+ {
+ swap_guard (object_statements& os, delayed_loads& dl)
+ : os_ (os), dl_ (dl)
+ {
+ dl_.swap (os_.delayed_);
+ }
+
+ ~swap_guard ()
+ {
+ os_.clear_delayed ();
+ dl_.swap (os_.delayed_);
+ }
+
+ private:
+ object_statements& os_;
+ delayed_loads& dl_;
+ };
+ };
+ }
+}
+
+#include <odb/sqlite/simple-object-statements.ixx>
+#include <odb/sqlite/simple-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_SIMPLE_OBJECT_STATEMENTS_HXX
diff --git a/libodb-sqlite/odb/sqlite/simple-object-statements.ixx b/libodb-sqlite/odb/sqlite/simple-object-statements.ixx
new file mode 100644
index 0000000..6756c06
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/simple-object-statements.ixx
@@ -0,0 +1,68 @@
+// file : odb/sqlite/simple-object-statements.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ // auto_unlock
+ //
+ inline object_statements_base::auto_unlock::
+ auto_unlock (object_statements_base& s)
+ : s_ (s)
+ {
+ s_.unlock ();
+ }
+
+ inline object_statements_base::auto_unlock::
+ ~auto_unlock ()
+ {
+ s_.lock ();
+ }
+
+ //
+ // auto_lock
+ //
+ template <typename T>
+ inline object_statements<T>::auto_lock::
+ auto_lock (object_statements& s)
+ : s_ (s)
+ {
+ if (!s_.locked ())
+ {
+ s_.lock ();
+ locked_ = true;
+ }
+ else
+ locked_ = false;
+ }
+
+ template <typename T>
+ inline object_statements<T>::auto_lock::
+ ~auto_lock ()
+ {
+ if (locked_)
+ {
+ s_.unlock ();
+ s_.clear_delayed ();
+ }
+ }
+
+ template <typename T>
+ inline bool object_statements<T>::auto_lock::
+ locked () const
+ {
+ return locked_;
+ }
+
+ template <typename T>
+ inline void object_statements<T>::auto_lock::
+ unlock ()
+ {
+ assert (locked_);
+ s_.unlock ();
+ locked_ = false;
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/simple-object-statements.txx b/libodb-sqlite/odb/sqlite/simple-object-statements.txx
new file mode 100644
index 0000000..b80944d
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/simple-object-statements.txx
@@ -0,0 +1,146 @@
+// file : odb/sqlite/simple-object-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/traits-calls.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ // optimistic_data
+ //
+
+ template <typename T>
+ optimistic_data<T, true>::
+ optimistic_data (bind* b)
+ : id_image_binding_ (
+ b,
+ object_traits::id_column_count +
+ object_traits::managed_optimistic_column_count)
+ {
+ }
+
+ //
+ // object_statements
+ //
+
+ template <typename T>
+ object_statements<T>::
+ ~object_statements ()
+ {
+ }
+
+ template <typename T>
+ object_statements<T>::
+ object_statements (connection_type& conn)
+ : object_statements_base (conn),
+ select_image_binding_ (select_image_bind_, select_column_count),
+ insert_image_binding_ (insert_image_bind_, insert_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count +
+ managed_optimistic_column_count),
+ id_image_binding_ (update_image_bind_ + update_column_count,
+ id_column_count),
+ od_ (update_image_bind_ + update_column_count)
+ {
+ image_.version = 0;
+ select_image_version_ = 0;
+ insert_image_version_ = 0;
+ update_image_version_ = 0;
+ update_id_image_version_ = 0;
+
+ id_image_.version = 0;
+ id_image_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+
+ for (std::size_t i (0); i < select_column_count; ++i)
+ select_image_bind_[i].truncated = select_image_truncated_ + i;
+ }
+
+ template <typename T>
+ template <typename STS>
+ void object_statements<T>::
+ load_delayed_ (const schema_version_migration* svm)
+ {
+ database& db (connection ().database ());
+
+ delayed_loads dls;
+ swap_guard sg (*this, dls);
+
+ while (!dls.empty ())
+ {
+ delayed_load l (dls.back ());
+ typename pointer_cache_traits::insert_guard ig (l.pos);
+ dls.pop_back ();
+
+ if (l.loader == 0)
+ {
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (static_cast<STS&> (*this), &l.id))
+ throw object_not_persistent ();
+
+ object_traits::callback (db, *l.obj, callback_event::pre_load);
+
+ // Our calls to init/load below can result in additional delayed
+ // loads being added to the delayed_ vector. We need to process
+ // those before we call the post callback.
+ //
+ tc.init (*l.obj, image (), &db);
+
+ // Load containers, etc.
+ //
+ tc.load_ (static_cast<STS&> (*this), *l.obj, false);
+
+ if (!delayed_.empty ())
+ load_delayed_<STS> (svm);
+
+ // Temporarily unlock the statement for the post_load call so that
+ // it can load objects of this type recursively. This is safe to do
+ // because we have completely loaded the current object. Also the
+ // delayed_ list is clear before the unlock and should be clear on
+ // re-lock (since a callback can only call public API functions
+ // which will make sure all the delayed loads are processed before
+ // returning).
+ //
+ {
+ auto_unlock u (*this);
+ object_traits::callback (db, *l.obj, callback_event::post_load);
+ }
+ }
+ else
+ l.loader (db, l.id, *l.obj, svm);
+
+ pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+ }
+
+ template <typename T>
+ void object_statements<T>::
+ clear_delayed_ ()
+ {
+ // Remove the objects from the session cache.
+ //
+ for (typename delayed_loads::iterator i (delayed_.begin ()),
+ e (delayed_.end ()); i != e; ++i)
+ {
+ pointer_cache_traits::erase (i->pos);
+ }
+
+ delayed_.clear ();
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/sqlite-types.hxx b/libodb-sqlite/odb/sqlite/sqlite-types.hxx
new file mode 100644
index 0000000..b9839bf
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/sqlite-types.hxx
@@ -0,0 +1,57 @@
+// file : odb/sqlite/sqlite-types.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_SQLITE_TYPES_HXX
+#define ODB_SQLITE_SQLITE_TYPES_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // The SQLite parameter/result binding. This data structures is modelled
+ // after MYSQL_BIND from MySQL.
+ //
+ struct bind
+ {
+ enum buffer_type
+ {
+ integer, // Buffer is long long; size, capacity, truncated are unused.
+ real, // Buffer is double; size, capacity, truncated are unused.
+ text, // Buffer is a UTF-8 char array.
+ text16, // Buffer is a UTF-16 2-byte char array (sizes in bytes).
+ blob, // Buffer is a char array.
+ stream // Buffer is stream_buffers. Size specifies the BLOB size
+ // (input only). Capacity and truncated unused.
+ };
+
+ buffer_type type;
+ void* buffer;
+ std::size_t* size;
+ std::size_t capacity;
+ bool* is_null;
+ bool* truncated;
+ };
+
+ // The "out" values should be set in set_image() to point to
+ // variables that will be receiving the data. The "in" values
+ // are used in set_value() and contain the data that needs to
+ // be copied over.
+ //
+ struct stream_buffers
+ {
+ union {std::string* out; const char* in;} db;
+ union {std::string* out; const char* in;} table;
+ union {std::string* out; const char* in;} column;
+ union {long long* out; long long in;} rowid;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_SQLITE_TYPES_HXX
diff --git a/libodb-sqlite/odb/sqlite/statement-cache.hxx b/libodb-sqlite/odb/sqlite/statement-cache.hxx
new file mode 100644
index 0000000..31ca685
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/statement-cache.hxx
@@ -0,0 +1,60 @@
+// file : odb/sqlite/statement-cache.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_STATEMENT_CACHE_HXX
+#define ODB_SQLITE_STATEMENT_CACHE_HXX
+
+#include <odb/pre.hxx>
+
+#include <map>
+#include <typeinfo>
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/type-info.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/statements-base.hxx>
+
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class LIBODB_SQLITE_EXPORT statement_cache
+ {
+ public:
+ statement_cache (connection& conn)
+ : conn_ (conn),
+ version_seq_ (conn_.database ().schema_version_sequence ()) {}
+
+ template <typename T>
+ typename object_traits_impl<T, id_sqlite>::statements_type&
+ find_object ();
+
+ template <typename T>
+ view_statements<T>&
+ find_view ();
+
+ private:
+ typedef std::map<const std::type_info*,
+ details::shared_ptr<statements_base>,
+ details::type_info_comparator> map;
+
+ connection& conn_;
+ unsigned int version_seq_;
+ map map_;
+ };
+ }
+}
+
+#include <odb/sqlite/statement-cache.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_STATEMENT_CACHE_HXX
diff --git a/libodb-sqlite/odb/sqlite/statement-cache.txx b/libodb-sqlite/odb/sqlite/statement-cache.txx
new file mode 100644
index 0000000..c089e32
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/statement-cache.txx
@@ -0,0 +1,60 @@
+// file : odb/sqlite/statement-cache.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/sqlite/database.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ typename object_traits_impl<T, id_sqlite>::statements_type&
+ statement_cache::
+ find_object ()
+ {
+ typedef
+ typename object_traits_impl<T, id_sqlite>::statements_type
+ statements_type;
+
+ // Clear the cache if the database version has changed. This
+ // makes sure we don't re-use statements that correspond to
+ // the old schema.
+ //
+ if (version_seq_ != conn_.database ().schema_version_sequence ())
+ {
+ map_.clear ();
+ version_seq_ = conn_.database ().schema_version_sequence ();
+ }
+
+ map::iterator i (map_.find (&typeid (T)));
+
+ if (i != map_.end ())
+ return static_cast<statements_type&> (*i->second);
+
+ details::shared_ptr<statements_type> p (
+ new (details::shared) statements_type (conn_));
+
+ map_.insert (map::value_type (&typeid (T), p));
+ return *p;
+ }
+
+ template <typename T>
+ view_statements<T>& statement_cache::
+ find_view ()
+ {
+ // We don't cache any statements for views so no need to clear
+ // the cache.
+
+ map::iterator i (map_.find (&typeid (T)));
+
+ if (i != map_.end ())
+ return static_cast<view_statements<T>&> (*i->second);
+
+ details::shared_ptr<view_statements<T> > p (
+ new (details::shared) view_statements<T> (conn_));
+
+ map_.insert (map::value_type (&typeid (T), p));
+ return *p;
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/statement.cxx b/libodb-sqlite/odb/sqlite/statement.cxx
new file mode 100644
index 0000000..b1b0f58
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/statement.cxx
@@ -0,0 +1,988 @@
+// file : odb/sqlite/statement.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/tracer.hxx>
+#include <odb/exceptions.hxx> // object_not_persistent
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/error.hxx>
+
+#include <odb/sqlite/details/config.hxx> // LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+ // LIBODB_SQLITE_HAVE_COLUMN_METADATA
+using namespace std;
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // statement
+ //
+
+ statement::
+ ~statement ()
+ {
+ if (stmt_ != 0)
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->deallocate (conn_, *this);
+ }
+
+ if (next_ != this)
+ list_remove ();
+
+ stmt_.reset ();
+ }
+ }
+
+ void statement::
+ clear ()
+ {
+ reset ();
+ }
+
+ void statement::
+ init (const char* text,
+ std::size_t text_size,
+ statement_kind sk,
+ const binding* proc,
+ bool optimize)
+ {
+ active_ = false;
+
+ string tmp1;
+ if (proc != 0)
+ {
+ switch (sk)
+ {
+ case statement_select:
+ process_select (tmp1,
+ text,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ '"', '"',
+ optimize);
+ break;
+ case statement_insert:
+ process_insert (tmp1,
+ text,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ '?',
+ '$');
+ break;
+ case statement_update:
+ process_update (tmp1,
+ text,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ '?',
+ '$');
+ break;
+ case statement_delete:
+ case statement_generic:
+ assert (false);
+ }
+
+ text = tmp1.c_str ();
+ text_size = tmp1.size ();
+ }
+
+ string tmp2;
+ if (conn_.statement_translator_ != 0)
+ {
+ conn_.statement_translator_ (tmp2, text, text_size, conn_);
+
+ if (!tmp2.empty ())
+ {
+ text = tmp2.c_str ();
+ text_size = tmp2.size ();
+ }
+ }
+
+#if SQLITE_VERSION_NUMBER < 3005003
+ text_.assign (text, text_size);
+#endif
+
+ // Empty statement.
+ //
+ if (*text == '\0')
+ return;
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ {
+ // Temporarily store the statement text in prev_ so that
+ // text() which may be called by the tracer can access it.
+ // Dirty but efficient.
+ //
+#if SQLITE_VERSION_NUMBER >= 3005003
+ prev_ = reinterpret_cast<active_object*> (const_cast<char*> (text));
+#endif
+ t->prepare (conn_, *this);
+#if SQLITE_VERSION_NUMBER >= 3005003
+ prev_ = 0;
+#endif
+ }
+ }
+
+ int e;
+ sqlite3_stmt* stmt (0);
+
+ // sqlite3_prepare_v2() is only available since SQLite 3.3.9
+ // but is buggy until 3.3.11.
+ //
+#if SQLITE_VERSION_NUMBER >= 3003011
+ while ((e = sqlite3_prepare_v2 (conn_.handle (),
+ text,
+ static_cast<int> (text_size),
+ &stmt,
+ 0)) == SQLITE_LOCKED)
+ {
+ conn_.wait ();
+ }
+#else
+ e = sqlite3_prepare (conn_.handle (),
+ text,
+ static_cast<int> (text_size),
+ &stmt,
+ 0);
+#endif
+
+ if (e != SQLITE_OK)
+ translate_error (e, conn_);
+
+ stmt_.reset (stmt);
+ }
+
+ const char* statement::
+ text () const
+ {
+ // sqlite3_sql() is only available since 3.5.3.
+ //
+#if SQLITE_VERSION_NUMBER >= 3005003
+ if (stmt_ == 0)
+ // See init() above for details on what's going on here.
+ //
+ return prev_ != 0 ? reinterpret_cast<const char*> (prev_) : "";
+ else
+ return sqlite3_sql (stmt_);
+#else
+ return text_.c_str ();
+#endif
+ }
+
+ bool statement::
+ bind_param (const bind* p, size_t n)
+ {
+ int e (SQLITE_OK);
+ bool r (false);
+
+ // SQLite parameters are counted from 1.
+ //
+ for (size_t i (0), j (1); e == SQLITE_OK && i < n; ++i)
+ {
+ const bind& b (p[i]);
+
+ if (b.buffer == 0) // Skip NULL entries.
+ continue;
+
+ int c (static_cast<int> (j++));
+
+ if (b.is_null != 0 && *b.is_null)
+ {
+ e = sqlite3_bind_null (stmt_, c);
+ continue;
+ }
+
+ switch (b.type)
+ {
+ case bind::integer:
+ {
+ long long v (*static_cast<long long*> (b.buffer));
+
+ e = sqlite3_bind_int64 (stmt_,
+ c,
+ // Prior to 3.5.0, sqlite3_int64 was called sqlite_int64.
+#if SQLITE_VERSION_NUMBER >= 3005000
+ static_cast<sqlite3_int64> (v)
+#else
+ static_cast<sqlite_int64> (v)
+#endif
+ );
+ break;
+ }
+ case bind::real:
+ {
+ double v (*static_cast<double*> (b.buffer));
+ e = sqlite3_bind_double (stmt_, c, v);
+ break;
+ }
+ case bind::text:
+ {
+ e = sqlite3_bind_text (stmt_,
+ c,
+ static_cast<const char*> (b.buffer),
+ static_cast<int> (*b.size),
+ SQLITE_STATIC);
+ break;
+ }
+ case bind::text16:
+ {
+ e = sqlite3_bind_text16 (stmt_,
+ c,
+ b.buffer,
+ static_cast<int> (*b.size),
+ SQLITE_STATIC);
+ break;
+ }
+ case bind::blob:
+ {
+ e = sqlite3_bind_blob (stmt_,
+ c,
+ b.buffer,
+ static_cast<int> (*b.size),
+ SQLITE_STATIC);
+ break;
+ }
+ case bind::stream:
+ {
+#if SQLITE_VERSION_NUMBER >= 3004000
+ e = sqlite3_bind_zeroblob (stmt_,
+ c,
+ static_cast<int> (*b.size));
+ r = true;
+#else
+ assert (false);
+#endif
+ break;
+ }
+ }
+ }
+
+ if (e != SQLITE_OK)
+ translate_error (e, conn_);
+
+ return r;
+ }
+
+ bool statement::
+ bind_result (const bind* p, size_t count, bool truncated)
+ {
+ bool r (true);
+ int col_count (sqlite3_data_count (stmt_));
+
+ int col (0);
+ for (size_t i (0); i != count && col != col_count; ++i)
+ {
+ const bind& b (p[i]);
+
+ if (b.buffer == 0) // Skip NULL entries.
+ continue;
+
+ int c (col++);
+
+ if (b.type == bind::stream)
+ col++; // Skip ROWID value that follows.
+
+ if (truncated && (b.truncated == 0 || !*b.truncated))
+ continue;
+
+ if (b.truncated != 0)
+ *b.truncated = false;
+
+ // Check for NULL unless we are reloading a truncated result.
+ //
+ if (!truncated)
+ {
+ *b.is_null = sqlite3_column_type (stmt_, c) == SQLITE_NULL;
+
+ if (*b.is_null)
+ continue;
+ }
+
+ switch (b.type)
+ {
+ case bind::integer:
+ {
+ *static_cast<long long*> (b.buffer) =
+ static_cast<long long> (sqlite3_column_int64 (stmt_, c));
+ break;
+ }
+ case bind::real:
+ {
+ *static_cast<double*> (b.buffer) =
+ sqlite3_column_double (stmt_, c);
+ break;
+ }
+ case bind::text:
+ case bind::text16:
+ case bind::blob:
+ {
+ // SQLite documentation recommends that we first call *_text(),
+ // *_text16(), or *_blob() function in order to force the type
+ // conversion, if any.
+ //
+ const void* d;
+
+ if (b.type != bind::text16)
+ {
+ d = b.type == bind::text
+ ? sqlite3_column_text (stmt_, c)
+ : sqlite3_column_blob (stmt_, c);
+ *b.size = static_cast<size_t> (sqlite3_column_bytes (stmt_, c));
+ }
+ else
+ {
+ d = sqlite3_column_text16 (stmt_, c);
+ *b.size = static_cast<size_t> (
+ sqlite3_column_bytes16 (stmt_, c));
+ }
+
+ if (*b.size > b.capacity)
+ {
+ if (b.truncated != 0)
+ *b.truncated = true;
+
+ r = false;
+ continue;
+ }
+
+ memcpy (b.buffer, d, *b.size);
+ break;
+ }
+ case bind::stream:
+ {
+ stream_buffers& sb (*static_cast<stream_buffers*> (b.buffer));
+
+ // SQLite documentation states that these are valid until the
+ // statement is finalized (or reprepared). For our case, we
+ // only need it to stay alive until we call set_value() which
+ // we do while executing the statement (i.e., we don't copy
+ // images for later processing).
+ //
+#ifdef LIBODB_SQLITE_HAVE_COLUMN_METADATA
+ sb.db.in = sqlite3_column_database_name (stmt_, c);
+ sb.table.in = sqlite3_column_table_name (stmt_, c);
+ sb.column.in = sqlite3_column_origin_name (stmt_, c);
+#else
+ assert (false);
+#endif
+
+ // The ROWID comes in the following column.
+ //
+ sb.rowid.in = static_cast<long long> (
+ sqlite3_column_int64 (stmt_, c + 1));
+
+ break;
+ }
+ }
+ }
+
+ // Make sure that the number of columns in the result returned by
+ // the database matches the number that we expect. A common cause
+ // of this assertion is a native view with a number of data members
+ // not matching the number of columns in the SELECT-list.
+ //
+ assert (col == col_count);
+
+ return r;
+ }
+
+ void statement::
+ stream_param (const bind* p, size_t n, const stream_data& d)
+ {
+ // Code similar to bind_param().
+ //
+ for (size_t i (0), j (1); i < n; ++i)
+ {
+ const bind& b (p[i]);
+
+ if (b.buffer == 0) // Skip NULL entries.
+ continue;
+
+ int c (static_cast<int> (j++));
+
+ if ((b.is_null != 0 && *b.is_null) || b.type != bind::stream)
+ continue;
+
+ // Get column name.
+ //
+ const char* col (sqlite3_bind_parameter_name (stmt_, c));
+ assert (col != 0); // Statement doesn't contain column name.
+
+ stream_buffers& sb (*static_cast<stream_buffers*> (b.buffer));
+
+ *sb.db.out = d.db;
+ *sb.table.out = d.table;
+ *sb.column.out = col + 1; // Skip '$'.
+ *sb.rowid.out = d.rowid;
+ }
+ }
+
+ inline void
+ update_hook (void* v, const char* db, const char* table, long long rowid)
+ {
+ statement::stream_data& d (*static_cast<statement::stream_data*> (v));
+ d.db = db;
+ d.table = table;
+ d.rowid = rowid;
+ }
+
+ extern "C" void
+ odb_sqlite_update_hook (void* v,
+ int,
+ const char* db,
+ const char* table,
+#if SQLITE_VERSION_NUMBER >= 3005000
+ sqlite3_int64 rowid
+#else
+ sqlite_int64 rowid
+#endif
+ )
+ {
+ update_hook (v, db, table, static_cast<long long> (rowid));
+ }
+
+ // generic_statement
+ //
+
+ generic_statement::
+ generic_statement (connection_type& conn, const string& text)
+ : statement (conn,
+ text, statement_generic,
+ 0, false),
+ result_set_ (stmt_ ? sqlite3_column_count (stmt_) != 0: false)
+ {
+ }
+
+ generic_statement::
+ generic_statement (connection_type& conn, const char* text)
+ : statement (conn,
+ text, statement_generic,
+ 0, false),
+ result_set_ (stmt_ ? sqlite3_column_count (stmt_) != 0: false)
+ {
+ }
+
+ generic_statement::
+ generic_statement (connection_type& conn,
+ const char* text,
+ size_t text_size)
+ : statement (conn,
+ text, text_size, statement_generic,
+ 0, false),
+ result_set_ (stmt_ ? sqlite3_column_count (stmt_) != 0: false)
+ {
+ }
+
+ unsigned long long generic_statement::
+ execute ()
+ {
+ if (stmt_ == 0) // Empty statement or comment.
+ return 0;
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ unsigned long long r (0);
+
+ int e;
+ sqlite3* h (conn_.handle ());
+
+#ifdef LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+ // Only the first call to sqlite3_step() can return SQLITE_LOCKED.
+ //
+ while ((e = sqlite3_step (stmt_)) == SQLITE_LOCKED)
+ {
+ if (sqlite3_extended_errcode (h) != SQLITE_LOCKED_SHAREDCACHE)
+ break;
+
+ sqlite3_reset (stmt_);
+ conn_.wait ();
+ }
+#else
+ e = sqlite3_step (stmt_);
+#endif
+
+ for (; e == SQLITE_ROW; e = sqlite3_step (stmt_))
+ r++;
+
+ // sqlite3_step() will return a detailed error code only if we used
+ // sqlite3_prepare_v2(). Otherwise, sqlite3_reset() returns the
+ // error.
+ //
+#if SQLITE_VERSION_NUMBER >= 3003011
+ sqlite3_reset (stmt_);
+
+ if (e != SQLITE_DONE)
+ translate_error (e, conn_);
+#else
+ e = sqlite3_reset (stmt_);
+
+ if (e != SQLITE_OK)
+ {
+ // If the schema has changed, try to re-prepare and re-execute the
+ // statement. That's what newer versions of SQLite do automatically.
+ //
+ if (e == SQLITE_SCHEMA)
+ {
+ sqlite3_stmt* stmt (0);
+ e = sqlite3_prepare (h,
+ text_.c_str (),
+ static_cast<int> (text_.size () + 1),
+ &stmt,
+ 0);
+
+ if (e != SQLITE_OK)
+ translate_error (e, conn_);
+
+ stmt_.reset (stmt);
+ return execute (); // Try again by recursively calling ourselves.
+ }
+ else
+ translate_error (e, conn_);
+ }
+#endif
+
+ if (!result_set_)
+ r = static_cast<unsigned long long> (sqlite3_changes (h));
+
+ return r;
+ }
+
+ // select_statement
+ //
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ bool optimize,
+ binding& param,
+ binding& result)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ param_ (&param),
+ result_ (result)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ bool optimize,
+ binding& param,
+ binding& result)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ param_ (&param),
+ result_ (result)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ bool optimize,
+ binding& result)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ param_ (0),
+ result_ (result)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ bool optimize,
+ binding& result)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ param_ (0),
+ result_ (result)
+ {
+ }
+
+ void select_statement::
+ execute ()
+ {
+ if (active ())
+ reset ();
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ done_ = false;
+
+ if (param_ != 0)
+ bind_param (param_->bind, param_->count);
+
+ active (true);
+ }
+
+ void select_statement::
+ free_result ()
+ {
+ reset ();
+ done_ = true;
+ }
+
+ bool select_statement::
+ next ()
+ {
+ if (!done_)
+ {
+ int e;
+
+#ifdef LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+ sqlite3* h (conn_.handle ());
+ while ((e = sqlite3_step (stmt_)) == SQLITE_LOCKED)
+ {
+ if (sqlite3_extended_errcode (h) != SQLITE_LOCKED_SHAREDCACHE)
+ break;
+
+ sqlite3_reset (stmt_);
+ conn_.wait ();
+ }
+#else
+ e = sqlite3_step (stmt_);
+#endif
+
+ if (e != SQLITE_ROW)
+ {
+ done_ = true;
+
+ // sqlite3_step() will return a detailed error code only if we used
+ // sqlite3_prepare_v2(). Otherwise, sqlite3_reset() returns the
+ // error.
+ //
+#if SQLITE_VERSION_NUMBER >= 3003011
+ reset ();
+
+ if (e != SQLITE_DONE)
+#else
+ e = reset ();
+
+ if (e != SQLITE_OK)
+#endif
+ translate_error (e, conn_);
+ }
+ }
+
+ return !done_;
+ }
+
+ select_statement::result select_statement::
+ load ()
+ {
+ if (done_)
+ return no_data;
+
+ return bind_result (result_.bind, result_.count) ? success : truncated;
+ }
+
+ void select_statement::
+ reload ()
+ {
+ assert (!done_);
+
+ if (!bind_result (result_.bind, result_.count, true))
+ assert (false);
+ }
+
+ // insert_statement
+ //
+
+ insert_statement::
+ insert_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ binding& param,
+ binding* returning)
+ : statement (conn,
+ text, statement_insert,
+ (process ? &param : 0), false),
+ param_ (param),
+ returning_ (returning)
+ {
+ }
+
+ insert_statement::
+ insert_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ binding& param,
+ binding* returning)
+ : statement (conn,
+ text, statement_insert,
+ (process ? &param : 0), false),
+ param_ (param),
+ returning_ (returning)
+ {
+ }
+
+ bool insert_statement::
+ execute ()
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ sqlite3* h (conn_.handle ());
+ bool stream (bind_param (param_.bind, param_.count));
+
+ stream_data sd;
+ if (stream)
+ sqlite3_update_hook (h, &odb_sqlite_update_hook, &sd);
+
+ int e;
+
+#ifdef LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+ while ((e = sqlite3_step (stmt_)) == SQLITE_LOCKED)
+ {
+ if (sqlite3_extended_errcode (h) != SQLITE_LOCKED_SHAREDCACHE)
+ break;
+
+ sqlite3_reset (stmt_);
+ conn_.wait ();
+ }
+#else
+ e = sqlite3_step (stmt_);
+#endif
+
+ if (stream)
+ sqlite3_update_hook (h, 0, 0); // Clear the hook.
+
+ // sqlite3_step() will return a detailed error code only if we used
+ // sqlite3_prepare_v2(). Otherwise, sqlite3_reset() returns the
+ // error.
+ //
+#if SQLITE_VERSION_NUMBER >= 3003011
+ sqlite3_reset (stmt_);
+
+ if (e != SQLITE_DONE)
+#else
+ e = sqlite3_reset (stmt_);
+
+ if (e != SQLITE_OK)
+#endif
+ {
+ // SQLITE_CONSTRAINT error code covers more than just a duplicate
+ // primary key. Unfortunately, there is nothing more precise that
+ // we can use (even sqlite3_errmsg() returns generic "constraint
+ // failed"). But an auto-assigned object id should never cause a
+ // duplicate primary key.
+ //
+ if (returning_ == 0 && e == SQLITE_CONSTRAINT)
+ return false;
+ else
+ translate_error (e, conn_);
+ }
+
+ // Stream parameters, if any.
+ //
+ if (stream)
+ stream_param (param_.bind, param_.count, sd);
+
+ if (returning_ != 0)
+ {
+ bind& b (returning_->bind[0]);
+
+ *b.is_null = false;
+ *static_cast<long long*> (b.buffer) =
+ static_cast<long long> (
+ sqlite3_last_insert_rowid (h));
+ }
+
+ return true;
+ }
+
+ // update_statement
+ //
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ binding& param)
+ : statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false),
+ param_ (param)
+ {
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ binding& param)
+ : statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false),
+ param_ (param)
+ {
+ }
+
+ unsigned long long update_statement::
+ execute ()
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ sqlite3* h (conn_.handle ());
+ bool stream (bind_param (param_.bind, param_.count));
+
+ stream_data sd;
+ if (stream)
+ sqlite3_update_hook (h, &odb_sqlite_update_hook, &sd);
+
+ int e;
+
+#ifdef LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+ while ((e = sqlite3_step (stmt_)) == SQLITE_LOCKED)
+ {
+ if (sqlite3_extended_errcode (h) != SQLITE_LOCKED_SHAREDCACHE)
+ break;
+
+ sqlite3_reset (stmt_);
+ conn_.wait ();
+ }
+#else
+ e = sqlite3_step (stmt_);
+#endif
+
+ if (stream)
+ sqlite3_update_hook (h, 0, 0); // Clear the hook.
+
+ // sqlite3_step() will return a detailed error code only if we used
+ // sqlite3_prepare_v2(). Otherwise, sqlite3_reset() returns the
+ // error.
+ //
+#if SQLITE_VERSION_NUMBER >= 3003011
+ sqlite3_reset (stmt_);
+
+ if (e != SQLITE_DONE)
+#else
+ e = sqlite3_reset (stmt_);
+
+ if (e != SQLITE_OK)
+#endif
+ translate_error (e, conn_);
+
+ int r (sqlite3_changes (h));
+
+ // Stream parameters, if any.
+ //
+ if (stream && r != 0)
+ stream_param (param_.bind, param_.count, sd);
+
+ return static_cast<unsigned long long> (r);
+ }
+
+ // delete_statement
+ //
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const string& text,
+ binding& param)
+ : statement (conn,
+ text, statement_delete,
+ 0, false),
+ param_ (param)
+ {
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const char* text,
+ binding& param)
+ : statement (conn,
+ text, statement_delete,
+ 0, false),
+ param_ (param)
+ {
+ }
+
+ unsigned long long delete_statement::
+ execute ()
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ bind_param (param_.bind, param_.count);
+
+ int e;
+ sqlite3* h (conn_.handle ());
+
+#ifdef LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+ while ((e = sqlite3_step (stmt_)) == SQLITE_LOCKED)
+ {
+ if (sqlite3_extended_errcode (h) != SQLITE_LOCKED_SHAREDCACHE)
+ break;
+
+ sqlite3_reset (stmt_);
+ conn_.wait ();
+ }
+#else
+ e = sqlite3_step (stmt_);
+#endif
+
+ // sqlite3_step() will return a detailed error code only if we used
+ // sqlite3_prepare_v2(). Otherwise, sqlite3_reset() returns the
+ // error.
+ //
+#if SQLITE_VERSION_NUMBER >= 3003011
+ sqlite3_reset (stmt_);
+
+ if (e != SQLITE_DONE)
+#else
+ e = sqlite3_reset (stmt_);
+
+ if (e != SQLITE_OK)
+#endif
+ translate_error (e, conn_);
+
+ return static_cast<unsigned long long> (sqlite3_changes (h));
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/statement.hxx b/libodb-sqlite/odb/sqlite/statement.hxx
new file mode 100644
index 0000000..9228103
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/statement.hxx
@@ -0,0 +1,394 @@
+// file : odb/sqlite/statement.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_STATEMENT_HXX
+#define ODB_SQLITE_STATEMENT_HXX
+
+#include <odb/pre.hxx>
+
+#include <sqlite3.h>
+
+#include <string>
+#include <cstddef> // std::size_t
+#include <cstring> // std::strlen, std::memcpy
+#include <cassert>
+
+#include <odb/statement.hxx>
+#include <odb/details/unused.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/binding.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/auto-handle.hxx>
+
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class connection;
+
+ class LIBODB_SQLITE_EXPORT statement: public odb::statement,
+ public active_object
+ {
+ public:
+ typedef sqlite::connection connection_type;
+
+ virtual
+ ~statement () = 0;
+
+ sqlite3_stmt*
+ handle () const
+ {
+ return stmt_;
+ }
+
+ virtual const char*
+ text () const;
+
+ virtual connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // A statement can be empty. This is used to handle situations
+ // where a SELECT or UPDATE statement ends up not having any
+ // columns after processing. An empty statement cannot be
+ // executed.
+ //
+ bool
+ empty () const
+ {
+ return stmt_ == 0;
+ }
+
+ protected:
+ // We keep two versions to take advantage of std::string COW.
+ //
+ statement (connection_type& conn,
+ const std::string& text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize)
+ : active_object (conn)
+ {
+ init (text.c_str (), text.size (), sk, process, optimize);
+ }
+
+ statement (connection_type& conn,
+ const char* text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize)
+ : active_object (conn)
+ {
+ init (text, std::strlen (text), sk, process, optimize);
+ }
+
+ statement (connection_type& conn,
+ const char* text,
+ std::size_t text_size,
+ statement_kind sk,
+ const binding* process,
+ bool optimize)
+ : active_object (conn)
+ {
+ init (text, text_size, sk, process, optimize);
+ }
+
+ protected:
+ // Return true if we bound any stream parameters.
+ //
+ bool
+ bind_param (const bind*, std::size_t count);
+
+ // Extract row columns into the bound buffers. If the truncated
+ // argument is true, then only truncated columns are extracted.
+ // Return true if all the data was extracted successfully and
+ // false if one or more columns were truncated.
+ //
+ bool
+ bind_result (const bind*, std::size_t count, bool truncated = false);
+
+ // Stream (so to speak) parameters.
+ //
+ struct stream_data
+ {
+ std::string db;
+ std::string table;
+ long long rowid;
+ };
+
+ void
+ stream_param (const bind*, std::size_t count, const stream_data&);
+
+ friend void
+ update_hook (void*, const char*, const char*, long long);
+
+ // Active state.
+ //
+ protected:
+ bool
+ active () const
+ {
+ return active_;
+ }
+
+ void
+ active (bool active)
+ {
+ ODB_POTENTIALLY_UNUSED (active);
+ assert (active);
+
+ if (!active_)
+ {
+ list_add ();
+ active_ = true;
+ }
+ }
+
+ int
+ reset ()
+ {
+ int r (SQLITE_OK);
+
+ if (active_)
+ {
+ r = sqlite3_reset (stmt_);
+ list_remove ();
+ active_ = false;
+ }
+
+ return r;
+ }
+
+ // The active_object interface.
+ //
+ virtual void
+ clear ();
+
+ protected:
+ auto_handle<sqlite3_stmt> stmt_;
+
+#if SQLITE_VERSION_NUMBER < 3005003
+ std::string text_;
+#endif
+
+ bool active_;
+
+ private:
+ void
+ init (const char* text,
+ std::size_t text_size,
+ statement_kind,
+ const binding* process,
+ bool optimize);
+ };
+
+ class LIBODB_SQLITE_EXPORT generic_statement: public statement
+ {
+ public:
+ generic_statement (connection_type&, const std::string& text);
+ generic_statement (connection_type&, const char* text);
+ generic_statement (connection_type&,
+ const char* text,
+ std::size_t text_size);
+
+ unsigned long long
+ execute ();
+
+ private:
+ generic_statement (const generic_statement&);
+ generic_statement& operator= (const generic_statement&);
+
+ private:
+ bool result_set_;
+ };
+
+ class LIBODB_SQLITE_EXPORT select_statement: public statement
+ {
+ public:
+ select_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ binding& param,
+ binding& result);
+
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ bool optimize_text,
+ binding& param,
+ binding& result);
+
+ select_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ binding& result);
+
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ bool optimize_text,
+ binding& result);
+
+ // Common select interface expected by the generated code.
+ //
+ public:
+ enum result
+ {
+ success,
+ no_data,
+ truncated
+ };
+
+ void
+ execute ();
+
+ // Load next row columns into bound buffers.
+ //
+ result
+ fetch ()
+ {
+ return next () ? load () : no_data;
+ }
+
+ // Reload truncated columns into bound buffers.
+ //
+ void
+ refetch ()
+ {
+ reload ();
+ }
+
+ // Free the result set.
+ //
+ void
+ free_result ();
+
+ // More fine-grained SQLite-specific interface that splits fetch()
+ // into next() and load().
+ //
+ public:
+ // Return false if there is no more rows. You should call next()
+ // until it returns false or, alternatively, call free_result ().
+ // Otherwise the statement will remain unfinished.
+ //
+ bool
+ next ();
+
+ result
+ load ();
+
+ void
+ reload ();
+
+ private:
+ select_statement (const select_statement&);
+ select_statement& operator= (const select_statement&);
+
+ private:
+ bool done_;
+ binding* param_;
+ binding& result_;
+ };
+
+ struct auto_result
+ {
+ explicit auto_result (select_statement& s): s_ (s) {}
+ ~auto_result () {s_.free_result ();}
+
+ private:
+ auto_result (const auto_result&);
+ auto_result& operator= (const auto_result&);
+
+ private:
+ select_statement& s_;
+ };
+
+ class LIBODB_SQLITE_EXPORT insert_statement: public statement
+ {
+ public:
+ insert_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ binding& param,
+ binding* returning);
+
+ insert_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ binding& param,
+ binding* returning);
+
+ // Return true if successful and false if the row is a duplicate.
+ // All other errors are reported by throwing exceptions.
+ //
+ bool
+ execute ();
+
+ private:
+ insert_statement (const insert_statement&);
+ insert_statement& operator= (const insert_statement&);
+
+ private:
+ binding& param_;
+ binding* returning_;
+ };
+
+ class LIBODB_SQLITE_EXPORT update_statement: public statement
+ {
+ public:
+ update_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ binding& param);
+
+ update_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ binding& param);
+
+ unsigned long long
+ execute ();
+
+ private:
+ update_statement (const update_statement&);
+ update_statement& operator= (const update_statement&);
+
+ private:
+ binding& param_;
+ };
+
+ class LIBODB_SQLITE_EXPORT delete_statement: public statement
+ {
+ public:
+ delete_statement (connection_type& conn,
+ const std::string& text,
+ binding& param);
+
+ delete_statement (connection_type& conn,
+ const char* text,
+ binding& param);
+
+ unsigned long long
+ execute ();
+
+ private:
+ delete_statement (const delete_statement&);
+ delete_statement& operator= (const delete_statement&);
+
+ private:
+ binding& param_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_STATEMENT_HXX
diff --git a/libodb-sqlite/odb/sqlite/statements-base.cxx b/libodb-sqlite/odb/sqlite/statements-base.cxx
new file mode 100644
index 0000000..bde8c55
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/statements-base.cxx
@@ -0,0 +1,15 @@
+// file : odb/sqlite/statements-base.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/sqlite/statements-base.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ statements_base::
+ ~statements_base ()
+ {
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/statements-base.hxx b/libodb-sqlite/odb/sqlite/statements-base.hxx
new file mode 100644
index 0000000..5851d1b
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/statements-base.hxx
@@ -0,0 +1,63 @@
+// file : odb/sqlite/statements-base.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_STATEMENTS_BASE_HXX
+#define ODB_SQLITE_STATEMENTS_BASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/schema-version.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/database.hxx>
+
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class LIBODB_SQLITE_EXPORT statements_base: public details::shared_base
+ {
+ public:
+ typedef sqlite::connection connection_type;
+
+ connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // Schema version. database::schema_version_migration() is thread-
+ // safe which means it is also slow. Cache the result in statements
+ // so we can avoid the mutex lock. This is thread-safe since if the
+ // version is updated, then the statements cache will be expired.
+ //
+ const schema_version_migration&
+ version_migration (const char* name = "") const
+ {
+ if (svm_ == 0)
+ svm_ = &conn_.database ().schema_version_migration (name);
+
+ return *svm_;
+ }
+
+ public:
+ virtual
+ ~statements_base ();
+
+ protected:
+ statements_base (connection_type& conn): conn_ (conn), svm_ (0) {}
+
+ protected:
+ connection_type& conn_;
+ mutable const schema_version_migration* svm_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_STATEMENTS_BASE_HXX
diff --git a/libodb-sqlite/odb/sqlite/stream.cxx b/libodb-sqlite/odb/sqlite/stream.cxx
new file mode 100644
index 0000000..8420ba2
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/stream.cxx
@@ -0,0 +1,120 @@
+// file : odb/sqlite/stream.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <sqlite3.h>
+
+#if SQLITE_VERSION_NUMBER >= 3004000
+
+#include <odb/sqlite/stream.hxx>
+
+#include <stdexcept> // invalid_argument
+
+#include <odb/sqlite/error.hxx>
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/transaction.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace sqlite
+ {
+ stream::
+ stream (const char* db,
+ const char* table,
+ const char* column,
+ long long rowid,
+ bool rw)
+ : active_object (transaction::current ().connection ())
+ {
+ int e (sqlite3_blob_open (conn_.handle (),
+ db,
+ table,
+ column,
+ static_cast<sqlite_int64> (rowid),
+ rw,
+ &h_));
+
+ if (e != SQLITE_OK)
+ translate_error (e, conn_);
+
+ list_add (); // Add ourselves to the active objects list.
+ }
+
+ size_t stream::
+ size () const
+ {
+ return static_cast<size_t> (sqlite3_blob_bytes (h_));
+ }
+
+ void stream::
+ read (void* buf, size_t n, size_t o)
+ {
+ int e (sqlite3_blob_read (
+ h_, buf, static_cast<int> (n), static_cast<int> (o)));
+
+ if (e != SQLITE_OK)
+ {
+ if (e == SQLITE_ERROR)
+ throw invalid_argument ("read past end");
+ else
+ translate_error (e, conn_);
+ }
+ }
+
+ void stream::
+ write (const void* buf, size_t n, size_t o)
+ {
+ int e (sqlite3_blob_write (
+ h_, buf, static_cast<int> (n), static_cast<int> (o)));
+
+ if (e != SQLITE_OK)
+ {
+ if (e == SQLITE_ERROR)
+ throw invalid_argument ("write past end");
+ else
+ translate_error (e, conn_);
+ }
+ }
+
+ void stream::
+ close (bool check)
+ {
+ if (h_ != 0)
+ {
+ list_remove ();
+
+ int e (sqlite3_blob_close (h_));
+ h_ = 0; // No use trying again.
+
+ if (check && e != SQLITE_OK)
+ translate_error (e, conn_);
+ }
+ }
+
+#if SQLITE_VERSION_NUMBER >= 3007004
+ void stream::
+ reopen (long long rowid)
+ {
+ int e (sqlite3_blob_reopen (h_, rowid));
+
+ if (e != SQLITE_OK)
+ // According to the SQLite documentation, the handle is now
+ // considered aborted, which means we still need to close it.
+ //
+ translate_error (e, conn_);
+ }
+#endif
+
+ void stream::
+ clear ()
+ {
+ // This call can be part of the stack unwinding, so don't check
+ // for the errors.
+ //
+ close (false);
+ }
+ }
+}
+
+#endif // SQLITE_VERSION_NUMBER >= 3004000
diff --git a/libodb-sqlite/odb/sqlite/stream.hxx b/libodb-sqlite/odb/sqlite/stream.hxx
new file mode 100644
index 0000000..6ee76cb
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/stream.hxx
@@ -0,0 +1,85 @@
+// file : odb/sqlite/stream.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_STREAM_HXX
+#define ODB_SQLITE_STREAM_HXX
+
+#include <odb/pre.hxx>
+
+#include <sqlite3.h>
+
+#include <cstddef> // std::size_t
+
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // SQLite incremental BLOB/TEXT I/O stream. Available since
+ // SQLite 3.4.0.
+ //
+ class LIBODB_SQLITE_EXPORT stream: public active_object
+ {
+ public:
+ // @@ TODO: db is actually what we now (and SQLite in other places)
+ // call schema (see database::schema(), ATTACH DATABASE). So we
+ // should probably rename this at some point for consistency.
+ //
+ 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 {return h_;}
+
+ // Close without reporting errors, if any.
+ //
+ virtual
+ ~stream () {close (false);}
+
+ // Close, by default with reporting errors, if any.
+ //
+ void
+ close (bool check = true);
+
+ // 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.
+ // Only available since SQLite 3.7.4.
+ //
+#if SQLITE_VERSION_NUMBER >= 3007004
+ void
+ reopen (long long rowid);
+#endif
+
+ protected:
+ // The active_object interface.
+ //
+ virtual void
+ clear ();
+
+ private:
+ sqlite3_blob* h_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_STREAM_HXX
diff --git a/libodb-sqlite/odb/sqlite/text-stream.hxx b/libodb-sqlite/odb/sqlite/text-stream.hxx
new file mode 100644
index 0000000..7a9b467
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/text-stream.hxx
@@ -0,0 +1,31 @@
+// file : odb/sqlite/text-stream.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_TEXT_STREAM_HXX
+#define ODB_SQLITE_TEXT_STREAM_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/sqlite/text.hxx>
+#include <odb/sqlite/stream.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class text_stream: public stream
+ {
+ public:
+ text_stream (const text& b, bool rw)
+ : stream (b.db ().c_str (),
+ b.table ().c_str (),
+ b.column ().c_str (),
+ b.rowid (),
+ rw) {}
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_TEXT_STREAM_HXX
diff --git a/libodb-sqlite/odb/sqlite/text.hxx b/libodb-sqlite/odb/sqlite/text.hxx
new file mode 100644
index 0000000..3f681fb
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/text.hxx
@@ -0,0 +1,69 @@
+// file : odb/sqlite/text.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_TEXT_HXX
+#define ODB_SQLITE_TEXT_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+
+// Carefully allow this header to be included into the ODB compilation.
+//
+#ifndef ODB_COMPILER
+# include <odb/sqlite/forward.hxx>
+# include <odb/sqlite/details/export.hxx>
+#endif
+
+namespace odb
+{
+ namespace sqlite
+ {
+#ifdef ODB_COMPILER
+ #pragma db sqlite:type("TEXT STREAM")
+ class text
+#else
+ class text
+#endif
+ {
+ public:
+ // TEXT size to provision for. Set before calling persist() or update().
+ //
+ explicit
+ text (std::size_t size = 0): size_ (size) {}
+
+ std::size_t size () const {return size_;}
+ void size (std::size_t s) {size_ = s;}
+
+ const std::string& db () const {return db_;}
+ const std::string& table () const {return table_;}
+ const std::string& column () const {return column_;}
+ long long rowid () const {return rowid_;}
+
+ void
+ clear ()
+ {
+ size_ = 0;
+ db_.clear ();
+ table_.clear ();
+ column_.clear ();
+ rowid_ = 0;
+ }
+
+ private:
+#ifndef ODB_COMPILER
+ friend struct default_value_traits<text, id_text_stream>;
+#endif
+ std::size_t size_;
+ mutable std::string db_;
+ mutable std::string table_;
+ mutable std::string column_;
+ mutable long long rowid_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_TEXT_HXX
diff --git a/libodb-sqlite/odb/sqlite/tracer.cxx b/libodb-sqlite/odb/sqlite/tracer.cxx
new file mode 100644
index 0000000..49f6b00
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/tracer.cxx
@@ -0,0 +1,60 @@
+// file : odb/sqlite/tracer.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/sqlite/tracer.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/statement.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ tracer::
+ ~tracer ()
+ {
+ }
+
+ void tracer::
+ prepare (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ execute (connection& c, const statement& s)
+ {
+ execute (c, s.text ());
+ }
+
+ void tracer::
+ deallocate (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ prepare (odb::connection& c, const odb::statement& s)
+ {
+ prepare (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const odb::statement& s)
+ {
+ execute (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const char* s)
+ {
+ execute (static_cast<connection&> (c), s);
+ }
+
+ void tracer::
+ deallocate (odb::connection& c, const odb::statement& s)
+ {
+ deallocate (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/tracer.hxx b/libodb-sqlite/odb/sqlite/tracer.hxx
new file mode 100644
index 0000000..b12573b
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/tracer.hxx
@@ -0,0 +1,61 @@
+// file : odb/sqlite/tracer.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_TRACER_HXX
+#define ODB_SQLITE_TRACER_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/tracer.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class LIBODB_SQLITE_EXPORT tracer: private odb::tracer
+ {
+ public:
+ virtual
+ ~tracer ();
+
+ 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&);
+
+ private:
+ // Allow these classes to convert sqlite::tracer to odb::tracer.
+ //
+ friend class database;
+ friend class connection;
+ friend class transaction;
+
+ virtual void
+ prepare (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const char* statement);
+
+ virtual void
+ deallocate (odb::connection&, const odb::statement&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_TRACER_HXX
diff --git a/libodb-sqlite/odb/sqlite/traits-calls.hxx b/libodb-sqlite/odb/sqlite/traits-calls.hxx
new file mode 100644
index 0000000..9d5b59f
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/traits-calls.hxx
@@ -0,0 +1,214 @@
+// file : odb/sqlite/traits-calls.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_TRAITS_CALLS_HXX
+#define ODB_SQLITE_TRAITS_CALLS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/sqlite-types.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ // object_traits_calls
+ //
+
+ template <typename T,
+ bool versioned = object_traits_impl<T, id_sqlite>::versioned>
+ struct object_traits_calls;
+
+ template <typename T>
+ struct object_traits_calls<T, false>
+ {
+ typedef object_traits_impl<T, id_sqlite> traits;
+ typedef typename traits::image_type image_type;
+ typedef sqlite::bind bind_type;
+
+ object_traits_calls (const schema_version_migration*) {}
+
+ const schema_version_migration*
+ version () const {return 0;}
+
+ static bool
+ grow (image_type& i, bool* t)
+ {
+ return traits::grow (i, t);
+ }
+
+ static void
+ bind (bind_type* b, image_type& i, statement_kind sk)
+ {
+ traits::bind (b, i, sk);
+ }
+
+ // Poly-derived version.
+ //
+ static void
+ bind (bind_type* b,
+ const bind_type* id, std::size_t id_size,
+ image_type& i,
+ statement_kind sk)
+ {
+ traits::bind (b, id, id_size, i, sk);
+ }
+
+ static void
+ init (T& o, const image_type& i, odb::database* db)
+ {
+ traits::init (o, i, db);
+ }
+
+ static bool
+ find_ (typename traits::statements_type& sts,
+ const typename traits::id_type* id)
+ {
+ return traits::find_ (sts, id);
+ }
+
+ static void
+ load_ (typename traits::statements_type& sts, T& o, bool reload)
+ {
+ return traits::load_ (sts, o, reload);
+ }
+ };
+
+ template <typename T>
+ struct object_traits_calls<T, true>
+ {
+ typedef object_traits_impl<T, id_sqlite> traits;
+ typedef typename traits::image_type image_type;
+ typedef sqlite::bind bind_type;
+
+ object_traits_calls (const schema_version_migration* svm): svm_ (*svm) {}
+
+ const schema_version_migration*
+ version () const {return &svm_;}
+
+ bool
+ grow (image_type& i, bool* t) const
+ {
+ return traits::grow (i, t, svm_);
+ }
+
+ void
+ bind (bind_type* b, image_type& i, statement_kind sk) const
+ {
+ traits::bind (b, i, sk, svm_);
+ }
+
+ // Poly-derived version.
+ //
+ void
+ bind (bind_type* b,
+ const bind_type* id, std::size_t id_size,
+ image_type& i,
+ statement_kind sk) const
+ {
+ traits::bind (b, id, id_size, i, sk, svm_);
+ }
+
+ void
+ init (T& o, const image_type& i, odb::database* db) const
+ {
+ traits::init (o, i, db, svm_);
+ }
+
+ bool
+ find_ (typename traits::statements_type& sts,
+ const typename traits::id_type* id) const
+ {
+ return traits::find_ (sts, id, svm_);
+ }
+
+ void
+ load_ (typename traits::statements_type& sts, T& o, bool reload) const
+ {
+ return traits::load_ (sts, o, reload, svm_);
+ }
+
+ private:
+ const schema_version_migration& svm_;
+ };
+
+ //
+ // view_traits_calls
+ //
+
+ template <typename T,
+ bool versioned = view_traits_impl<T, id_sqlite>::versioned>
+ struct view_traits_calls;
+
+ template <typename T>
+ struct view_traits_calls<T, false>
+ {
+ typedef view_traits_impl<T, id_sqlite> traits;
+ typedef typename traits::image_type image_type;
+ typedef sqlite::bind bind_type;
+
+ view_traits_calls (const schema_version_migration*) {}
+
+ static bool
+ grow (image_type& i, bool* t)
+ {
+ return traits::grow (i, t);
+ }
+
+ static void
+ bind (bind_type* b, image_type& i)
+ {
+ traits::bind (b, i);
+ }
+
+ static void
+ init (T& o, const image_type& i, odb::database* db)
+ {
+ traits::init (o, i, db);
+ }
+ };
+
+ template <typename T>
+ struct view_traits_calls<T, true>
+ {
+ typedef view_traits_impl<T, id_sqlite> traits;
+ typedef typename traits::image_type image_type;
+ typedef sqlite::bind bind_type;
+
+ view_traits_calls (const schema_version_migration* svm): svm_ (*svm) {}
+
+ bool
+ grow (image_type& i, bool* t) const
+ {
+ return traits::grow (i, t, svm_);
+ }
+
+ void
+ bind (bind_type* b, image_type& i) const
+ {
+ traits::bind (b, i, svm_);
+ }
+
+ void
+ init (T& o, const image_type& i, odb::database* db) const
+ {
+ traits::init (o, i, db, svm_);
+ }
+
+ private:
+ const schema_version_migration& svm_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_TRAITS_CALLS_HXX
diff --git a/libodb-sqlite/odb/sqlite/traits.cxx b/libodb-sqlite/odb/sqlite/traits.cxx
new file mode 100644
index 0000000..a47455d
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/traits.cxx
@@ -0,0 +1,219 @@
+// file : odb/sqlite/traits.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/sqlite/traits.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace sqlite
+ {
+ using details::buffer;
+
+ //
+ // default_value_traits<std::string>
+ //
+ void default_value_traits<string, id_text>::
+ set_image (buffer& b, size_t& n, bool& is_null, const string& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v.c_str (), n);
+ }
+
+ //
+ // c_string_value_traits
+ //
+ void c_string_value_traits::
+ set_image (buffer& b, size_t& n, bool& is_null, const char* v)
+ {
+ is_null = false;
+ n = strlen (v);
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v, n);
+ }
+
+ //
+ // c_array_value_traits_base
+ //
+ void c_array_value_traits_base::
+ set_value (char* const& v,
+ const details::buffer& b,
+ size_t n,
+ bool is_null,
+ size_t N)
+ {
+ if (!is_null)
+ {
+ n = n < N ? n : N;
+
+ if (n != 0)
+ memcpy (v, b.data (), n);
+ }
+ else
+ n = 0;
+
+ if (n != N) // Append '\0' if there is space.
+ v[n] = '\0';
+ }
+
+ void c_array_value_traits_base::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const char* v,
+ size_t N)
+ {
+ is_null = false;
+
+ // Figure out the length. We cannot use strlen since it may
+ // not be 0-terminated (strnlen is not standard).
+ //
+ for (n = 0; n != N && v[n] != '\0'; ++n) ;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v, n);
+ }
+
+ //
+ // default_value_traits<std::wstring>
+ //
+#ifdef _WIN32
+ void default_value_traits<wstring, id_text>::
+ set_image (buffer& b, size_t& n, bool& is_null, const wstring& v)
+ {
+ is_null = false;
+ n = v.size () * 2;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v.c_str (), n);
+ }
+
+ //
+ // c_wstring_value_traits
+ //
+ void c_wstring_value_traits::
+ set_image (buffer& b, size_t& n, bool& is_null, const wchar_t* v)
+ {
+ is_null = false;
+ n = wcslen (v) * 2;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v, n);
+ }
+
+ //
+ // c_warray_value_traits_base
+ //
+ void c_warray_value_traits_base::
+ set_value (wchar_t* const& v,
+ const details::buffer& b,
+ size_t n,
+ bool is_null,
+ size_t N)
+ {
+ if (!is_null)
+ {
+ n /= 2;
+ n = n < N ? n : N;
+
+ if (n != 0)
+ memcpy (v, b.data (), n * sizeof (wchar_t));
+ }
+ else
+ n = 0;
+
+ if (n != N) // Append '\0' if there is space.
+ v[n] = L'\0';
+ }
+
+ void c_warray_value_traits_base::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const wchar_t* v,
+ size_t N)
+ {
+ is_null = false;
+
+ // Figure out the length. We cannot use wcslen since it may
+ // not be 0-terminated (wcsnlen is not standard).
+ //
+ for (n = 0; n != N && v[n] != L'\0'; ++n) ;
+
+ n *= 2;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v, n);
+ }
+#endif // _WIN32
+
+ //
+ // default_value_traits<vector<char>, id_blob>
+ //
+ // std::vector has to be qualified for Sun CC.
+ //
+ void default_value_traits<std::vector<char>, id_blob>::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ // std::vector::data() may not be available in older compilers.
+ //
+ if (n != 0)
+ memcpy (b.data (), &v.front (), n);
+ }
+
+ //
+ // default_value_traits<vector<unsigned char>, id_blob>
+ //
+ // std::vector has to be qualified for Sun CC.
+ //
+ void default_value_traits<std::vector<unsigned char>, id_blob>::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ // std::vector::data() may not be available in older compilers.
+ //
+ if (n != 0)
+ memcpy (b.data (), &v.front (), n);
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/traits.hxx b/libodb-sqlite/odb/sqlite/traits.hxx
new file mode 100644
index 0000000..a8cf578
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/traits.hxx
@@ -0,0 +1,1099 @@
+// file : odb/sqlite/traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_TRAITS_HXX
+#define ODB_SQLITE_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <string>
+#include <vector>
+#include <limits> // std::numeric_limits
+#include <cstddef> // std::size_t
+#include <cstring> // std::memcpy, std::memset, std::strlen
+
+#ifdef ODB_CXX11
+# include <array>
+#endif
+
+#include <odb/traits.hxx>
+#include <odb/wrapper-traits.hxx>
+
+#include <odb/details/buffer.hxx>
+#include <odb/details/wrapper-p.hxx>
+
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/sqlite-types.hxx>
+#include <odb/sqlite/details/export.hxx>
+
+#include <odb/sqlite/text.hxx>
+#include <odb/sqlite/blob.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ // image_traits
+ //
+
+ template <typename T, database_type_id>
+ struct image_traits;
+
+ template <typename T>
+ struct image_traits<T, id_integer> {typedef long long image_type;};
+
+ template <typename T>
+ struct image_traits<T, id_real> {typedef double image_type;};
+
+ template <typename T>
+ struct image_traits<T, id_text>
+ {
+ typedef details::buffer image_type;
+
+ // By default the text is in UTF-8.
+ //
+ static const bind::buffer_type bind_value = bind::text;
+ };
+
+ template <typename T>
+ struct image_traits<T, id_blob> {typedef details::buffer image_type;};
+
+ template <typename T>
+ struct image_traits<T, id_text_stream>
+ {
+ typedef stream_buffers image_type;
+ };
+
+ template <typename T>
+ struct image_traits<T, id_blob_stream>
+ {
+ typedef stream_buffers image_type;
+ };
+
+ //
+ // value_traits
+ //
+
+ template <typename W, database_type_id, bool null_handler>
+ struct wrapped_value_traits;
+
+ template <typename T, database_type_id>
+ struct default_value_traits;
+
+ template <typename T, database_type_id, bool w = details::wrapper_p<T>::r>
+ struct select_traits;
+
+ template <typename T, database_type_id ID>
+ struct select_traits<T, ID, false>
+ {
+ typedef default_value_traits<T, ID> type;
+ };
+
+ template <typename W, database_type_id ID>
+ struct select_traits<W, ID, true>
+ {
+ typedef
+ wrapped_value_traits<W, ID, wrapper_traits<W>::null_handler>
+ type;
+ };
+
+ template <typename T, database_type_id ID>
+ class value_traits: public select_traits<T, ID>::type
+ {
+ };
+
+ // The wrapped_value_traits specializations should be able to handle
+ // any value type which means we have to have every possible signature
+ // of the set_value() and set_image() functions.
+ //
+ template <typename W, database_type_id ID>
+ struct wrapped_value_traits<W, ID, false>
+ {
+ typedef wrapper_traits<W> wtraits;
+ typedef typename wtraits::unrestricted_wrapped_type wrapped_type;
+
+ typedef W value_type;
+ typedef wrapped_type query_type;
+ typedef typename image_traits<wrapped_type, ID>::image_type image_type;
+
+ typedef value_traits<wrapped_type, ID> vtraits;
+
+ static void
+ set_value (W& v, const image_type& i, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, const W& v)
+ {
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+
+ // TEXT and BLOB.
+ //
+ static void
+ set_value (W& v, const details::buffer& b, std::size_t n, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (details::buffer& b, std::size_t& n, bool& is_null, const W& v)
+ {
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+
+ // TEXT and BLOB STREAM.
+ //
+ static void
+ set_value (W& v, const stream_buffers& b, std::size_t n, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (stream_buffers& b, std::size_t& n, bool& is_null, const W& v)
+ {
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+ };
+
+ template <typename W, database_type_id ID>
+ struct wrapped_value_traits<W, ID, true>
+ {
+ typedef wrapper_traits<W> wtraits;
+ typedef typename wtraits::unrestricted_wrapped_type wrapped_type;
+
+ typedef W value_type;
+ typedef wrapped_type query_type;
+ typedef typename image_traits<wrapped_type, ID>::image_type image_type;
+
+ typedef value_traits<wrapped_type, ID> vtraits;
+
+ static void
+ set_value (W& v, const image_type& i, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+
+ // TEXT and BLOB.
+ //
+ static void
+ set_value (W& v, const details::buffer& b, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (details::buffer& b, std::size_t& n, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+
+ // TEXT and BLOB STREAM.
+ //
+ static void
+ set_value (W& v, const stream_buffers& b, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (stream_buffers& b, std::size_t& n, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+ };
+
+ template <typename T, database_type_id ID>
+ struct default_value_traits
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef typename image_traits<T, ID>::image_type image_type;
+
+ static void
+ set_value (T& v, const image_type& i, bool is_null)
+ {
+ if (!is_null)
+ v = T (i);
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, T v)
+ {
+ is_null = false;
+ i = image_type (v);
+ }
+ };
+
+ // Float & double specialization. SQLite converts NaNs to NULLs so
+ // we convert NULLs to NaNs for consistency.
+ //
+ template <typename T>
+ struct real_value_traits
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef double image_type;
+
+ static void
+ set_value (T& v, double i, bool is_null)
+ {
+ if (!is_null)
+ v = T (i);
+ else
+ v = std::numeric_limits<T>::quiet_NaN ();
+ }
+
+ static void
+ set_image (double& i, bool& is_null, T v)
+ {
+ is_null = false;
+ i = image_type (v);
+ }
+ };
+
+ template <>
+ struct default_value_traits<float, id_real>: real_value_traits<float> {};
+
+ template <>
+ struct default_value_traits<double, id_real>: real_value_traits<double> {};
+
+ // std::string specialization.
+ //
+ template <>
+ struct LIBODB_SQLITE_EXPORT default_value_traits<std::string, id_text>
+ {
+ typedef std::string value_type;
+ typedef std::string query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (std::string& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ v.assign (b.data (), n);
+ else
+ v.erase ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const std::string&);
+ };
+
+ // char*/const char* specialization
+ //
+ // Specialization for const char* which only supports initialization
+ // of an image from the value but not the other way around. This way
+ // we can pass such values to the queries.
+ //
+ struct LIBODB_SQLITE_EXPORT c_string_value_traits
+ {
+ typedef const char* value_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const char*);
+ };
+
+ template <>
+ struct default_value_traits<char*, id_text>: c_string_value_traits {};
+
+ template <>
+ struct default_value_traits<const char*, id_text>:
+ c_string_value_traits {};
+
+ // char[N] specialization.
+ //
+ struct LIBODB_SQLITE_EXPORT c_array_value_traits_base
+ {
+ static void
+ set_value (char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null,
+ std::size_t N);
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const char* v,
+ std::size_t N);
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_text>
+ {
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (v, b, n, is_null, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ c_array_value_traits_base::set_image (b, n, is_null, v, N);
+ }
+ };
+
+ // std::array<char, N> (string) specialization.
+ //
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_text>
+ {
+ typedef std::array<char, N> value_type;
+ typedef std::array<char, N> 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)
+ {
+ c_array_value_traits_base::set_value (v.data (), b, n, is_null, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_array_value_traits_base::set_image (b, n, is_null, v.data (), N);
+ }
+ };
+#endif
+
+ // char specialization.
+ //
+ template <>
+ struct default_value_traits<char, id_text>
+ {
+ typedef char value_type;
+ typedef char query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (&v, b, n, is_null, 1);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ char v)
+ {
+ c_array_value_traits_base::set_image (b, n, is_null, &v, 1);
+ }
+ };
+
+#ifdef _WIN32
+ // std::wstring specialization. Using UTF-16 binding.
+ //
+ struct wstring_image_traits
+ {
+ typedef details::buffer image_type;
+ static const bind::buffer_type bind_value = bind::text16;
+ };
+
+ template <>
+ struct image_traits<std::wstring, id_text>: wstring_image_traits {};
+
+ template <>
+ struct LIBODB_SQLITE_EXPORT default_value_traits<std::wstring, id_text>
+ {
+ typedef std::wstring value_type;
+ typedef std::wstring query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (std::wstring& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ v.assign (reinterpret_cast<const wchar_t*> (b.data ()), n / 2);
+ else
+ v.erase ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const std::wstring&);
+ };
+
+ // wchar_t*/const wchar_t* specialization.
+ //
+ struct LIBODB_SQLITE_EXPORT c_wstring_value_traits
+ {
+ typedef const wchar_t* value_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const wchar_t*);
+ };
+
+ template <>
+ struct image_traits<wchar_t*, id_text>: wstring_image_traits {};
+
+ template <>
+ struct default_value_traits<wchar_t*, id_text>: c_wstring_value_traits {};
+
+ template <>
+ struct image_traits<const wchar_t*, id_text>: wstring_image_traits {};
+
+ template <>
+ struct default_value_traits<const wchar_t*, id_text>:
+ c_wstring_value_traits {};
+
+ // wchar_t[N] specialization.
+ //
+ struct LIBODB_SQLITE_EXPORT c_warray_value_traits_base
+ {
+ static void
+ set_value (wchar_t* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null,
+ std::size_t N);
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const wchar_t* v,
+ std::size_t N);
+ };
+
+ template <std::size_t N>
+ struct image_traits<wchar_t[N], id_text>: wstring_image_traits {};
+
+ template <std::size_t N>
+ struct default_value_traits<wchar_t[N], id_text>
+ {
+ typedef wchar_t* value_type;
+ typedef wchar_t query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (wchar_t* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_warray_value_traits_base::set_value (v, b, n, is_null, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const wchar_t* v)
+ {
+ c_warray_value_traits_base::set_image (b, n, is_null, v, N);
+ }
+ };
+
+ // std::array<wchar_t, N> (string) specialization.
+ //
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct image_traits<std::array<wchar_t, N>, id_text>:
+ wstring_image_traits {};
+
+ template <std::size_t N>
+ struct default_value_traits<std::array<wchar_t, N>, id_text>
+ {
+ typedef std::array<wchar_t, N> value_type;
+ typedef std::array<wchar_t, N> 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)
+ {
+ c_warray_value_traits_base::set_value (v.data (), b, n, is_null, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_warray_value_traits_base::set_image (b, n, is_null, v.data (), N);
+ }
+ };
+#endif
+
+ // wchar_t specialization.
+ //
+ template <>
+ struct image_traits<wchar_t, id_text>: wstring_image_traits {};
+
+ template <>
+ struct default_value_traits<wchar_t, id_text>
+ {
+ typedef wchar_t value_type;
+ typedef wchar_t query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (wchar_t& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_warray_value_traits_base::set_value (&v, b, n, is_null, 1);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ wchar_t v)
+ {
+ c_warray_value_traits_base::set_image (b, n, is_null, &v, 1);
+ }
+ };
+#endif // _WIN32
+
+ // std::vector<char> (buffer) specialization.
+ //
+ template <>
+ struct LIBODB_SQLITE_EXPORT default_value_traits<
+ std::vector<char>, id_blob>
+ {
+ public:
+ typedef std::vector<char> value_type;
+ typedef std::vector<char> 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)
+ {
+ if (!is_null)
+ v.assign (b.data (), b.data () + n);
+ else
+ v.clear ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const value_type&);
+ };
+
+ // std::vector<unsigned char> (buffer) specialization.
+ //
+ template <>
+ struct LIBODB_SQLITE_EXPORT default_value_traits<
+ std::vector<unsigned char>, id_blob>
+ {
+ public:
+ typedef std::vector<unsigned char> value_type;
+ typedef std::vector<unsigned char> 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)
+ {
+ if (!is_null)
+ {
+ const unsigned char* d (
+ reinterpret_cast<const unsigned char*> (b.data ()));
+ v.assign (d, d + n);
+ }
+ else
+ v.clear ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const value_type&);
+ };
+
+ // char[N] (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_blob>
+ {
+ public:
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, b.data (), (n < N ? n : N));
+ else
+ std::memset (v, 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v, n);
+ }
+ };
+
+ // unsigned char[N] (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<unsigned char[N], id_blob>
+ {
+ public:
+ typedef unsigned char* value_type;
+ typedef unsigned char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (unsigned char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, b.data (), (n < N ? n : N));
+ else
+ std::memset (v, 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const unsigned char* v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v, n);
+ }
+ };
+
+#ifdef ODB_CXX11
+ // std::array<char, N> (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_blob>
+ {
+ public:
+ typedef std::array<char, N> 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)
+ {
+ if (!is_null)
+ std::memcpy (v.data (), b.data (), (n < N ? n : N));
+ else
+ std::memset (v.data (), 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v.data (), n);
+ }
+ };
+
+ // std::array<unsigned char, N> (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<std::array<unsigned char, N>, id_blob>
+ {
+ public:
+ typedef std::array<unsigned char, N> 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)
+ {
+ if (!is_null)
+ std::memcpy (v.data (), b.data (), (n < N ? n : N));
+ else
+ std::memset (v.data (), 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v.data (), n);
+ }
+ };
+#endif
+
+ // text (stream) specialization.
+ //
+ template <>
+ struct default_value_traits<text, id_text_stream>
+ {
+ public:
+ typedef text value_type;
+ typedef std::string query_type;
+ typedef stream_buffers image_type;
+
+ static void
+ set_value (text& v, const stream_buffers& b, std::size_t, bool is_null)
+ {
+ if (!is_null)
+ {
+ v.db_ = b.db.in;
+ v.table_ = b.table.in;
+ v.column_ = b.column.in;
+ v.rowid_ = b.rowid.in;
+ }
+ }
+
+ static void
+ set_image (stream_buffers& b,
+ std::size_t& n,
+ bool& is_null,
+ const text& v)
+ {
+ is_null = false;
+ n = v.size_;
+
+ b.db.out = &v.db_;
+ b.table.out = &v.table_;
+ b.column.out = &v.column_;
+ b.rowid.out = &v.rowid_;
+ }
+ };
+
+ // blob (stream) specialization.
+ //
+ template <>
+ struct default_value_traits<blob, id_blob_stream>
+ {
+ public:
+ typedef blob value_type;
+ typedef std::vector<char> query_type;
+ typedef stream_buffers image_type;
+
+ static void
+ set_value (blob& v, const stream_buffers& b, std::size_t, bool is_null)
+ {
+ if (!is_null)
+ {
+ v.db_ = b.db.in;
+ v.table_ = b.table.in;
+ v.column_ = b.column.in;
+ v.rowid_ = b.rowid.in;
+ }
+ }
+
+ static void
+ set_image (stream_buffers& b,
+ std::size_t& n,
+ bool& is_null,
+ const blob& v)
+ {
+ is_null = false;
+ n = v.size_;
+
+ b.db.out = &v.db_;
+ b.table.out = &v.table_;
+ b.column.out = &v.column_;
+ b.rowid.out = &v.rowid_;
+ }
+ };
+
+ //
+ // type_traits
+ //
+
+ template <typename T>
+ struct default_type_traits;
+
+ template <typename T>
+ class type_traits: public default_type_traits<T> {};
+
+ // Integral types.
+ //
+ template <>
+ struct default_type_traits<bool>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<signed char>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<unsigned char>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<short>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<unsigned short>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<int>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<unsigned int>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<long>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<unsigned long>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<long long>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<unsigned long long>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ // Float types.
+ //
+ template <>
+ struct default_type_traits<float>
+ {
+ static const database_type_id db_type_id = id_real;
+ };
+
+ template <>
+ struct default_type_traits<double>
+ {
+ static const database_type_id db_type_id = id_real;
+ };
+
+ // String types.
+ //
+ template <>
+ struct default_type_traits<std::string>
+ {
+ static const database_type_id db_type_id = id_text;
+ };
+
+ template <>
+ struct default_type_traits<char*>
+ {
+ static const database_type_id db_type_id = id_text;
+ };
+
+ template <>
+ struct default_type_traits<const char*>
+ {
+ static const database_type_id db_type_id = id_text;
+ };
+
+ template <std::size_t N>
+ struct default_type_traits<char[N]>
+ {
+ static const database_type_id db_type_id = id_text;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<char, N> >
+ {
+ static const database_type_id db_type_id = id_text;
+ };
+#endif
+
+ template <>
+ struct default_type_traits<char>
+ {
+ static const database_type_id db_type_id = id_text;
+ };
+
+ // Binary types.
+ //
+ template <std::size_t N>
+ struct default_type_traits<unsigned char[N]>
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+
+ template <>
+ struct default_type_traits<std::vector<char> >
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+
+ template <>
+ struct default_type_traits<std::vector<unsigned char> >
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<unsigned char, N> >
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+#endif
+
+ template <>
+ struct default_type_traits<text>
+ {
+ static const database_type_id db_type_id = id_text_stream;
+ };
+
+ template <>
+ struct default_type_traits<blob>
+ {
+ static const database_type_id db_type_id = id_blob_stream;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_TRAITS_HXX
diff --git a/libodb-sqlite/odb/sqlite/transaction-impl.cxx b/libodb-sqlite/odb/sqlite/transaction-impl.cxx
new file mode 100644
index 0000000..6485f7e
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/transaction-impl.cxx
@@ -0,0 +1,172 @@
+// file : odb/sqlite/transaction-impl.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <sqlite3.h>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/transaction-impl.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ transaction_impl::
+ transaction_impl (database_type& db, lock l)
+ : odb::transaction_impl (db), lock_ (l)
+ {
+ }
+
+ transaction_impl::
+ transaction_impl (connection_ptr c, lock l)
+ : odb::transaction_impl (c->database (), *c),
+ connection_ (c),
+ lock_ (l)
+ {
+ }
+
+ transaction_impl::
+ ~transaction_impl ()
+ {
+ }
+
+ void transaction_impl::
+ start ()
+ {
+ // Grab a connection if we don't already have one.
+ //
+ if (connection_ == 0)
+ {
+ connection_ = static_cast<database_type&> (database_).connection ();
+ odb::transaction_impl::connection_ = connection_.get ();
+ }
+
+ connection_type& mc (connection_->main_connection ());
+
+ switch (lock_)
+ {
+ case deferred:
+ {
+ mc.begin_statement ().execute ();
+ break;
+ }
+ case immediate:
+ {
+ mc.begin_immediate_statement ().execute ();
+ break;
+ }
+ case exclusive:
+ {
+ mc.begin_exclusive_statement ().execute ();
+ break;
+ }
+ }
+ }
+
+ // In SQLite, when a commit fails (e.g., because of the deferred
+ // foreign key constraint violation), the transaction may not
+ // be automatically rolled back. So we have to do it ourselves.
+ //
+ struct commit_guard
+ {
+ commit_guard (connection& c): c_ (&c) {}
+ void release () {c_ = 0;}
+
+ ~commit_guard ()
+ {
+ if (c_ != 0 && sqlite3_get_autocommit (c_->handle ()) == 0)
+ {
+ // This is happening while another exception is active.
+ //
+ try
+ {
+ c_->rollback_statement ().execute ();
+ }
+ catch (...) {}
+ }
+ }
+
+ private:
+ connection* c_;
+ };
+
+ void transaction_impl::
+ commit ()
+ {
+ connection_type& mc (connection_->main_connection ());
+
+ // Invalidate query results and reset active statements.
+ //
+ // Active statements will prevent COMMIT from completing (write
+ // statements) or releasing the locks (read statements). Normally, a
+ // statement is automatically reset on completion, however, if an
+ // exception is thrown, that may not happen.
+ //
+ // Note: must be done via the main connection.
+ //
+ mc.clear ();
+
+ {
+ commit_guard cg (mc);
+ mc.commit_statement ().execute ();
+ cg.release ();
+ }
+
+ // Release the connection.
+ //
+ connection_.reset ();
+ }
+
+ void transaction_impl::
+ rollback ()
+ {
+ connection_type& mc (connection_->main_connection ());
+
+ // Invalidate query results and reset active statements (the same
+ // reasoning as in commit()).
+ //
+ // Note: must be done via the main connection.
+ //
+ mc.clear ();
+
+ mc.rollback_statement ().execute ();
+
+ // Release the connection.
+ //
+ connection_.reset ();
+ }
+
+ odb::connection& transaction_impl::
+ connection (odb::database* pdb)
+ {
+ if (pdb == 0)
+ return *connection_;
+
+ // Pick the corresponding connection for main/attached database.
+ //
+ database_type& db (static_cast<database_type&> (*pdb));
+
+ assert (&db.main_database () ==
+ &static_cast<database_type&> (database_).main_database ());
+
+ return db.schema ().empty ()
+ ? connection_->main_connection ()
+ : *static_cast<attached_connection_factory&> (*db.factory_).attached_connection_;
+ }
+
+ // Store transaction tracer in the main connection.
+ //
+ void transaction_impl::
+ tracer (odb::tracer* t)
+ {
+ connection_->main_connection ().transaction_tracer_ = t;
+ }
+
+ odb::tracer* transaction_impl::
+ tracer () const
+ {
+ return connection_->main_connection ().transaction_tracer_;
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/transaction-impl.hxx b/libodb-sqlite/odb/sqlite/transaction-impl.hxx
new file mode 100644
index 0000000..d1a310b
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/transaction-impl.hxx
@@ -0,0 +1,66 @@
+// file : odb/sqlite/transaction-impl.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_TRANSACTION_IMPL_HXX
+#define ODB_SQLITE_TRANSACTION_IMPL_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class LIBODB_SQLITE_EXPORT transaction_impl: public odb::transaction_impl
+ {
+ public:
+ typedef sqlite::database database_type;
+ typedef sqlite::connection connection_type;
+
+ enum lock
+ {
+ deferred,
+ immediate,
+ exclusive
+ };
+
+ transaction_impl (database_type&, lock);
+ transaction_impl (connection_ptr, lock);
+
+ virtual
+ ~transaction_impl ();
+
+ virtual void
+ start ();
+
+ virtual void
+ commit ();
+
+ virtual void
+ rollback ();
+
+ virtual odb::connection&
+ connection (odb::database*);
+
+ virtual void
+ tracer (odb::tracer*);
+
+ virtual odb::tracer*
+ tracer () const;
+
+ private:
+ connection_ptr connection_;
+ lock lock_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_TRANSACTION_IMPL_HXX
diff --git a/libodb-sqlite/odb/sqlite/transaction.cxx b/libodb-sqlite/odb/sqlite/transaction.cxx
new file mode 100644
index 0000000..8b4ab23
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/transaction.cxx
@@ -0,0 +1,26 @@
+// file : odb/sqlite/transaction.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/sqlite/transaction.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ transaction& transaction::
+ current ()
+ {
+ // While the impl type can be of the concrete type, the transaction
+ // object can be created as either odb:: or odb::sqlite:: type. To
+ // work around that we are going to hard-cast one two the other
+ // relying on the fact that they have the same representation and
+ // no virtual functions. The former is checked in the tests.
+ //
+ odb::transaction& b (odb::transaction::current ());
+ assert (dynamic_cast<transaction_impl*> (&b.implementation ()) != 0);
+ return reinterpret_cast<transaction&> (b);
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/transaction.hxx b/libodb-sqlite/odb/sqlite/transaction.hxx
new file mode 100644
index 0000000..5e8c141
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/transaction.hxx
@@ -0,0 +1,87 @@
+// file : odb/sqlite/transaction.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_TRANSACTION_HXX
+#define ODB_SQLITE_TRANSACTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/tracer.hxx>
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class transaction_impl;
+
+ class LIBODB_SQLITE_EXPORT transaction: public odb::transaction
+ {
+ public:
+ typedef sqlite::database database_type;
+ typedef sqlite::connection connection_type;
+
+ explicit
+ transaction (transaction_impl*, bool make_current = true);
+
+ transaction ();
+
+ // Return the database this transaction is on.
+ //
+ database_type&
+ database ();
+
+ // Return the underlying database connection for this transaction.
+ //
+ connection_type&
+ connection ();
+
+ connection_type&
+ connection (odb::database&);
+
+ // Return current transaction or throw if there is no transaction
+ // in effect.
+ //
+ static transaction&
+ current ();
+
+ // Set the current thread's transaction.
+ //
+ static void
+ current (transaction&);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef sqlite::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ using odb::transaction::tracer;
+
+ public:
+ transaction_impl&
+ implementation ();
+ };
+ }
+}
+
+#include <odb/sqlite/transaction.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_TRANSACTION_HXX
diff --git a/libodb-sqlite/odb/sqlite/transaction.ixx b/libodb-sqlite/odb/sqlite/transaction.ixx
new file mode 100644
index 0000000..de4bd3e
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/transaction.ixx
@@ -0,0 +1,57 @@
+// file : odb/sqlite/transaction.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/transaction-impl.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ inline transaction::
+ transaction (transaction_impl* impl, bool make_current)
+ : odb::transaction (impl, make_current)
+ {
+ }
+
+ inline transaction::
+ transaction ()
+ : odb::transaction ()
+ {
+ }
+
+ inline transaction_impl& transaction::
+ implementation ()
+ {
+ // We can use static_cast here since we have an instance of
+ // sqlite::transaction.
+ //
+ return static_cast<transaction_impl&> (
+ odb::transaction::implementation ());
+ }
+
+ inline transaction::database_type& transaction::
+ database ()
+ {
+ return static_cast<database_type&> (odb::transaction::database ());
+ }
+
+ inline transaction::connection_type& transaction::
+ connection ()
+ {
+ return static_cast<connection_type&> (odb::transaction::connection ());
+ }
+
+ inline transaction::connection_type& transaction::
+ connection (odb::database& db)
+ {
+ return static_cast<connection_type&> (odb::transaction::connection (db));
+ }
+
+ inline void transaction::
+ current (transaction& t)
+ {
+ odb::transaction::current (t);
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/version.hxx b/libodb-sqlite/odb/sqlite/version.hxx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/version.hxx
diff --git a/libodb-sqlite/odb/sqlite/version.hxx.in b/libodb-sqlite/odb/sqlite/version.hxx.in
new file mode 100644
index 0000000..c100d57
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/version.hxx.in
@@ -0,0 +1,61 @@
+// file : odb/sqlite/version.hxx.in
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef LIBODB_SQLITE_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 LIBODB_SQLITE_VERSION_FULL $libodb_sqlite.version.project_number$ULL
+#define LIBODB_SQLITE_VERSION_STR "$libodb_sqlite.version.project$"
+#define LIBODB_SQLITE_VERSION_ID "$libodb_sqlite.version.project_id$"
+
+#define LIBODB_SQLITE_VERSION_MAJOR $libodb_sqlite.version.major$
+#define LIBODB_SQLITE_VERSION_MINOR $libodb_sqlite.version.minor$
+#define LIBODB_SQLITE_VERSION_PATCH $libodb_sqlite.version.patch$
+
+#define LIBODB_SQLITE_PRE_RELEASE $libodb_sqlite.version.pre_release$
+
+#define LIBODB_SQLITE_SNAPSHOT $libodb_sqlite.version.snapshot_sn$ULL
+#define LIBODB_SQLITE_SNAPSHOT_ID "$libodb_sqlite.version.snapshot_id$"
+
+#include <odb/version.hxx>
+
+$libodb.check(LIBODB_VERSION_FULL, LIBODB_SNAPSHOT)$
+
+
+// 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 LIBODB_SQLITE_VERSION 2049976
+
+#endif // LIBODB_SQLITE_VERSION
diff --git a/libodb-sqlite/odb/sqlite/view-result.hxx b/libodb-sqlite/odb/sqlite/view-result.hxx
new file mode 100644
index 0000000..ce3d747
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/view-result.hxx
@@ -0,0 +1,80 @@
+// file : odb/sqlite/view-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_VIEW_RESULT_HXX
+#define ODB_SQLITE_VIEW_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/view-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx> // query_base, query_params
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/traits-calls.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ class view_result_impl: public odb::view_result_impl<T>
+ {
+ public:
+ typedef odb::view_result_impl<T> base_type;
+
+ typedef typename base_type::view_type view_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef view_traits_impl<view_type, id_sqlite> view_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef view_statements<view_type> statements_type;
+
+ virtual
+ ~view_result_impl ();
+
+ view_result_impl (const query_base&,
+ const details::shared_ptr<select_statement>&,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (view_type&);
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ // We need to hold on to the query parameters because SQLite uses
+ // the parameter buffers to find each next row.
+ //
+ details::shared_ptr<query_params> params_;
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ view_traits_calls<view_type> tc_;
+ };
+ }
+}
+
+#include <odb/sqlite/view-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_VIEW_RESULT_HXX
diff --git a/libodb-sqlite/odb/sqlite/view-result.txx b/libodb-sqlite/odb/sqlite/view-result.txx
new file mode 100644
index 0000000..60efb81
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/view-result.txx
@@ -0,0 +1,114 @@
+// file : odb/sqlite/view-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/sqlite/view-statements.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ view_result_impl<T>::
+ ~view_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ params_.reset ();
+ statement_.reset ();
+ }
+
+ template <typename T>
+ view_result_impl<T>::
+ view_result_impl (const query_base& q,
+ const details::shared_ptr<select_statement>& s,
+ statements_type& sts,
+ const schema_version_migration* svm)
+ : base_type (sts.connection ()),
+ params_ (q.parameters ()),
+ statement_ (s),
+ statements_ (sts),
+ tc_ (svm)
+ {
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ load (view_type& view)
+ {
+ // The image can grow between calls to load() as a result of other
+ // statements execution.
+ //
+ typename view_traits::image_type& im (statements_.image ());
+
+ if (im.version != statements_.image_version ())
+ {
+ binding& b (statements_.image_binding ());
+ tc_.bind (b.bind, im);
+ statements_.image_version (im.version);
+ b.version++;
+ }
+
+ select_statement::result r (statement_->load ());
+
+ if (r == select_statement::truncated)
+ {
+ if (tc_.grow (im, statements_.image_truncated ()))
+ im.version++;
+
+ if (im.version != statements_.image_version ())
+ {
+ binding& b (statements_.image_binding ());
+ tc_.bind (b.bind, im);
+ statements_.image_version (im.version);
+ b.version++;
+ statement_->reload ();
+ }
+ }
+
+ view_traits::callback (this->db_, view, callback_event::pre_load);
+ tc_.init (view, im, &this->db_);
+ view_traits::callback (this->db_, view, callback_event::post_load);
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (!statement_->next ())
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t view_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/view-statements.hxx b/libodb-sqlite/odb/sqlite/view-statements.hxx
new file mode 100644
index 0000000..0bd79ee
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/view-statements.hxx
@@ -0,0 +1,88 @@
+// file : odb/sqlite/view-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_VIEW_STATEMENTS_HXX
+#define ODB_SQLITE_VIEW_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/binding.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/statements-base.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ class view_statements: public statements_base
+ {
+ public:
+ typedef T view_type;
+ typedef view_traits_impl<view_type, id_sqlite> view_traits;
+ typedef typename view_traits::pointer_type pointer_type;
+ typedef typename view_traits::image_type image_type;
+
+ public:
+ view_statements (connection_type&);
+
+ virtual
+ ~view_statements ();
+
+ // View image.
+ //
+ image_type&
+ image ()
+ {
+ return image_;
+ }
+
+ std::size_t
+ image_version () const
+ {
+ return image_version_;
+ }
+
+ void
+ image_version (std::size_t v)
+ {
+ image_version_ = v;
+ }
+
+ binding&
+ image_binding ()
+ {
+ return image_binding_;
+ }
+
+ bool*
+ image_truncated ()
+ {
+ return image_truncated_;
+ }
+
+ private:
+ view_statements (const view_statements&);
+ view_statements& operator= (const view_statements&);
+
+ private:
+ image_type image_;
+ std::size_t image_version_;
+ binding image_binding_;
+ bind image_bind_[view_traits::column_count];
+ bool image_truncated_[view_traits::column_count];
+ };
+ }
+}
+
+#include <odb/sqlite/view-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_VIEW_STATEMENTS_HXX
diff --git a/libodb-sqlite/odb/sqlite/view-statements.txx b/libodb-sqlite/odb/sqlite/view-statements.txx
new file mode 100644
index 0000000..0531a92
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/view-statements.txx
@@ -0,0 +1,33 @@
+// file : odb/sqlite/view-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ view_statements<T>::
+ ~view_statements ()
+ {
+ }
+
+ template <typename T>
+ view_statements<T>::
+ view_statements (connection_type& conn)
+ : statements_base (conn),
+ image_binding_ (image_bind_, view_traits::column_count)
+ {
+ image_.version = 0;
+ image_version_ = 0;
+
+ std::memset (image_bind_, 0, sizeof (image_bind_));
+ std::memset (image_truncated_, 0, sizeof (image_truncated_));
+
+ for (std::size_t i (0); i < view_traits::column_count; ++i)
+ image_bind_[i].truncated = image_truncated_ + i;
+ }
+ }
+}
diff --git a/libodb-sqlite/tests/.gitignore b/libodb-sqlite/tests/.gitignore
new file mode 100644
index 0000000..e54525b
--- /dev/null
+++ b/libodb-sqlite/tests/.gitignore
@@ -0,0 +1 @@
+driver
diff --git a/libodb-sqlite/tests/basics/buildfile b/libodb-sqlite/tests/basics/buildfile
new file mode 100644
index 0000000..5d671d3
--- /dev/null
+++ b/libodb-sqlite/tests/basics/buildfile
@@ -0,0 +1,6 @@
+# file : tests/basics/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libs = libodb-sqlite%lib{odb-sqlite}
+
+exe{driver}: {hxx cxx}{*} $libs
diff --git a/libodb-sqlite/tests/basics/driver.cxx b/libodb-sqlite/tests/basics/driver.cxx
new file mode 100644
index 0000000..b998574
--- /dev/null
+++ b/libodb-sqlite/tests/basics/driver.cxx
@@ -0,0 +1,52 @@
+// file : tests/basics/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Basic test to make sure the library is usable. Functionality testing
+// is done in the odb-tests package.
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/exceptions.hxx>
+#include <odb/sqlite/transaction.hxx>
+
+using namespace odb::sqlite;
+
+int
+main ()
+{
+ {
+ std::ostringstream os;
+ database::print_usage (os);
+ assert (!os.str ().empty ());
+ }
+
+ database db (":memory:");
+
+ {
+ transaction t (db.begin ());
+ db.execute ("CREATE TABLE test (id INTEGER PRIMARY KEY, str TEXT)");
+ t.commit ();
+ }
+
+ {
+ transaction t (db.begin ());
+ db.execute ("INSERT INTO test VALUES (123, 'abc')");
+ t.commit ();
+ }
+
+ try
+ {
+ transaction t (db.begin ());
+ db.execute ("INSERT INTO test VALUES (123, 'ABC')");
+ assert (false);
+ }
+ catch (const database_exception&) {}
+
+ {
+ transaction t (db.begin ());
+ db.execute ("DROP TABLE test");
+ t.commit ();
+ }
+}
diff --git a/libodb-sqlite/tests/build/.gitignore b/libodb-sqlite/tests/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-sqlite/tests/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-sqlite/tests/build/bootstrap.build b/libodb-sqlite/tests/build/bootstrap.build
new file mode 100644
index 0000000..6ee38db
--- /dev/null
+++ b/libodb-sqlite/tests/build/bootstrap.build
@@ -0,0 +1,8 @@
+# file : tests/build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = # Unnamed subproject.
+
+using config
+using dist
+using test
diff --git a/libodb-sqlite/tests/build/root.build b/libodb-sqlite/tests/build/root.build
new file mode 100644
index 0000000..6c5a90b
--- /dev/null
+++ b/libodb-sqlite/tests/build/root.build
@@ -0,0 +1,23 @@
+# file : tests/build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+cxx.std = latest
+
+using cxx
+
+hxx{*}: extension = hxx
+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
+
+# Every exe{} in this subproject is by default a test.
+#
+exe{*}: test = true
+
+# Specify the test target for cross-testing.
+#
+test.target = $cxx.target
diff --git a/libodb-sqlite/tests/buildfile b/libodb-sqlite/tests/buildfile
new file mode 100644
index 0000000..57588a4
--- /dev/null
+++ b/libodb-sqlite/tests/buildfile
@@ -0,0 +1,4 @@
+# file : tests/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -build/}
diff --git a/libodb/.gitignore b/libodb/.gitignore
new file mode 100644
index 0000000..1c363a0
--- /dev/null
+++ b/libodb/.gitignore
@@ -0,0 +1,25 @@
+# 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/libodb/GPLv2 b/libodb/GPLv2
new file mode 100644
index 0000000..3912109
--- /dev/null
+++ b/libodb/GPLv2
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) 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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. 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.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+ 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
+convey 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 2 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, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This 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 Library General
+Public License instead of this License.
diff --git a/libodb/INSTALL b/libodb/INSTALL
new file mode 100644
index 0000000..46b79ad
--- /dev/null
+++ b/libodb/INSTALL
@@ -0,0 +1,6 @@
+The easiest way to build this package is with the bpkg package manager:
+
+$ bpkg build libodb
+
+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/libodb/LICENSE b/libodb/LICENSE
new file mode 100644
index 0000000..d96b938
--- /dev/null
+++ b/libodb/LICENSE
@@ -0,0 +1,20 @@
+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 2 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, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
diff --git a/libodb/NEWS b/libodb/NEWS
new file mode 120000
index 0000000..0fae0f8
--- /dev/null
+++ b/libodb/NEWS
@@ -0,0 +1 @@
+../NEWS \ No newline at end of file
diff --git a/libodb/README b/libodb/README
new file mode 100644
index 0000000..902e20b
--- /dev/null
+++ b/libodb/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 common ODB runtime library. Every application
+that includes code generated by the ODB compiler will need to link to
+this library.
+
+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.
+
+Send questions, bug reports, or any other feedback to the
+odb-users@codesynthesis.com mailing list.
diff --git a/libodb/build/.gitignore b/libodb/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb/build/bootstrap.build b/libodb/build/bootstrap.build
new file mode 100644
index 0000000..9c8d1a9
--- /dev/null
+++ b/libodb/build/bootstrap.build
@@ -0,0 +1,10 @@
+# file : build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = libodb
+
+using version
+using config
+using dist
+using test
+using install
diff --git a/libodb/build/export.build b/libodb/build/export.build
new file mode 100644
index 0000000..56312c8
--- /dev/null
+++ b/libodb/build/export.build
@@ -0,0 +1,9 @@
+# file : build/export.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+$out_root/
+{
+ include odb/
+}
+
+export $out_root/odb/lib{odb}
diff --git a/libodb/build/root.build b/libodb/build/root.build
new file mode 100644
index 0000000..882047d
--- /dev/null
+++ b/libodb/build/root.build
@@ -0,0 +1,17 @@
+# file : build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+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
diff --git a/libodb/buildfile b/libodb/buildfile
new file mode 100644
index 0000000..a04e206
--- /dev/null
+++ b/libodb/buildfile
@@ -0,0 +1,9 @@
+# file : buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -build/} doc{INSTALL NEWS README} legal{GPLv2 LICENSE} manifest
+
+# Don't install tests or the INSTALL file.
+#
+tests/: install = false
+doc{INSTALL}@./: install = false
diff --git a/libodb/manifest b/libodb/manifest
new file mode 100644
index 0000000..725fc16
--- /dev/null
+++ b/libodb/manifest
@@ -0,0 +1,173 @@
+: 1
+name: libodb
+version: 2.5.0-b.26.z
+project: odb
+summary: Common ODB runtime library
+license: GPL-2.0-only
+license: other: proprietary ; Not free/open source.
+topics: C++, ORM, SQL, 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
+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-
+
+tests: odb-tests == $ ? ($config.odb_tests.libodb_test)
+
+builds: all
+default-build-config:
+\
+{
+ config.odb_tests.libodb_test=true
+ config.odb_tests.database=sqlite
+}+ odb-tests
+\
+
+# Only build this package configuration where it can be tested via odb-tests
+# package and where libodb-* libraries can be built (see their manifests for
+# details).
+#
+multi-builds: all
+multi-builds: -wasm ; Not supported by libodb-{mysql,pgsql}.
+multi-builds: -( +windows -gcc )
+multi-builds: &gcc
+multi-builds: &gcc-5+
+multi-builds: -static
+multi-build-auxiliary-mysql: *-mysql_*
+multi-build-auxiliary-pgsql: *-postgresql_*
+multi-build-config:
+\
+{
+ config.odb_tests.libodb_test=true
+ config.odb_tests.database='mysql sqlite pgsql'
+
+ config.odb_tests.mysql.user=$getenv(MYSQL_DATABASE_USER)
+ config.odb_tests.mysql.database=$getenv(MYSQL_DATABASE_NAME)
+ config.odb_tests.mysql.host=$getenv(MYSQL_DATABASE_HOST)
+ config.odb_tests.mysql.port=$getenv(MYSQL_DATABASE_PORT)
+
+ config.odb_tests.pgsql.user=$getenv(PGSQL_DATABASE_USER)
+ config.odb_tests.pgsql.database=$getenv(PGSQL_DATABASE_NAME)
+ config.odb_tests.pgsql.host=$getenv(PGSQL_DATABASE_HOST)
+ config.odb_tests.pgsql.port=$getenv(PGSQL_DATABASE_PORT)
+}+ odb-tests
+\
+
+# Complements the default configuration (see odb-tests for background).
+#
+custom-builds: latest
+custom-builds: -static ; Implementation uses plugins and requires -fPIC.
+#custom-build-bot: -- see below.
+custom-build-config:
+\
+{
+ config.odb_tests.libodb_test=true
+ config.odb_tests.database=sqlite
+}+ odb-tests
+\
+
+# Complements the multi configuration (see odb-tests for background).
+#
+custom-multi-builds: latest
+custom-multi-builds: -wasm ; Not supported by libodb-{mysql,pgsql}.
+custom-multi-builds: -static ; Implementation uses plugins and requires -fPIC.
+custom-multi-build-auxiliary-mysql: *-mysql_*
+custom-multi-build-auxiliary-pgsql: *-postgresql_*
+#custom-multi-build-bot: -- see below.
+custom-multi-build-config:
+\
+{
+ config.odb_tests.libodb_test=true
+ config.odb_tests.database='mysql sqlite pgsql'
+
+ config.odb_tests.mysql.user=$getenv(MYSQL_DATABASE_USER)
+ config.odb_tests.mysql.database=$getenv(MYSQL_DATABASE_NAME)
+ config.odb_tests.mysql.host=$getenv(MYSQL_DATABASE_HOST)
+ config.odb_tests.mysql.port=$getenv(MYSQL_DATABASE_PORT)
+
+ config.odb_tests.pgsql.user=$getenv(PGSQL_DATABASE_USER)
+ config.odb_tests.pgsql.database=$getenv(PGSQL_DATABASE_NAME)
+ config.odb_tests.pgsql.host=$getenv(PGSQL_DATABASE_HOST)
+ config.odb_tests.pgsql.port=$getenv(PGSQL_DATABASE_PORT)
+}+ odb-tests
+\
+
+custom-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuF4YmJmPHY52Q6N+YO0M
+lt/fCovdezleb2tVplyTnvbyAiPdmYCIIjVrsqUn3y46PdFtWEiSdsrCcncoxi6H
+8KelOB/oQ9pNTyEvwGKEH5ZIU7noLZYdXEfoNdvdL/pbY/7uLBZOSekfdQShZtbe
+uOZCM2Mhg2DD76TP/VAwaXuDCnEvxxU/yneUl5ZaBo62AWNrYJuSGAliCOpVpl6X
+X1kbHOvnCx7c9e3LxgaVivPaeZRKYg0OaFt96SBYEZzNPvjA8pMuKuj/vatHaCQ3
+NO9+r3TJ+4dQd7qN6Ju3zUJq9J/ndSh4lPvUalvvhdykecefhcyHwRZOG4xyFMFE
+nJM4sM+aZu6WoKATIKtk7On70inVr0sZJXwJ4Lt4oqaK2VthcSTby3wf2Yv4p5hL
+zNo31cCPmBRYzABcIc6ADYvexVK4uCwaim8xs7RK5Ug2Gv6vUWoRNZW8grIgDwUY
+5pZ4Zk3hW4ii2vehTaVrrmdW6XipIsT+ayiVX7eWuHHNxAeCojXVjOJu9B0ExMlD
+5tHZCs+SNdV5MceexecbptB7fZtRebP120yjLiSnZ5FpaQ1stusr0hSg+VQaX4np
+f5m1W/CcDr53PKWg/ayY9nWMUQaIwH4b69kLM+VTpYSbzu5UQJkmNBNq2EOHgoTv
+9MLA+cE/nNJ/rMI//MZ1+kcCAwEAAQ==
+-----END PUBLIC KEY-----
+\
+
+custom-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuLYZ68rotGDAtWViFHOe
+XEsmZB8BGI+af1ixh9JOy9BE4ohGTfPr1YsjGDzh+PhOVLAtyykOoT/qG7cuGB0T
+gBInoRrgVB2/ZKTMwxeDGb/TA3uykaXxcw7/liTsizHAY+phCNTbke8iER5Y78js
+9GlnTPmNhwFqEj2fwCz+2o08eyZvZ9Vj1fH/bFDCmDmU33JR3crtJlC8wPiF70Ho
+FJzHFdaFQl3MxvEV92HjOsyqozMi6tAVVefN1vapVQeNtjkB0Di18p0/EMugEuGU
+OxktjDHQWNaV8Ao6cCDk6OkJnM3ZNL1no3cV4cuF+/xI8UZzwfPoBnwg/s183Qzu
+pHHKOSHmuO0oVE/XohJhepSw3tb+wf5BwejRhYHikIjqCxJdm9H0QTiqXT82y24K
+yg3gkRMOgqnVxERKKP4ZknLSMQCEKiND/t2zdLJ/lxH9eHZdPHKk3OZZG292j+Bh
+fknxcTKNk1Dmf32Irs5hVrjsoU8eAutbItovzXdBaj//rn/ry/kUlCa1Ov6iLIDJ
+gyxmsDlgKNR/uE9ogmDn0ishJIoCmxeqenRfJkttr9pEsDsUFuB425QGqiSxa1jh
+PCNca3iRtO44wADXaQMTGpvLzBfdfVc8LoFpn+kynN0V1MvxAX4mHRXxw8ERXd3U
+dpHDhOthPLolJQrYKb/YyW8CAwEAAQ==
+-----END PUBLIC KEY-----
+\
+
+custom-multi-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuF4YmJmPHY52Q6N+YO0M
+lt/fCovdezleb2tVplyTnvbyAiPdmYCIIjVrsqUn3y46PdFtWEiSdsrCcncoxi6H
+8KelOB/oQ9pNTyEvwGKEH5ZIU7noLZYdXEfoNdvdL/pbY/7uLBZOSekfdQShZtbe
+uOZCM2Mhg2DD76TP/VAwaXuDCnEvxxU/yneUl5ZaBo62AWNrYJuSGAliCOpVpl6X
+X1kbHOvnCx7c9e3LxgaVivPaeZRKYg0OaFt96SBYEZzNPvjA8pMuKuj/vatHaCQ3
+NO9+r3TJ+4dQd7qN6Ju3zUJq9J/ndSh4lPvUalvvhdykecefhcyHwRZOG4xyFMFE
+nJM4sM+aZu6WoKATIKtk7On70inVr0sZJXwJ4Lt4oqaK2VthcSTby3wf2Yv4p5hL
+zNo31cCPmBRYzABcIc6ADYvexVK4uCwaim8xs7RK5Ug2Gv6vUWoRNZW8grIgDwUY
+5pZ4Zk3hW4ii2vehTaVrrmdW6XipIsT+ayiVX7eWuHHNxAeCojXVjOJu9B0ExMlD
+5tHZCs+SNdV5MceexecbptB7fZtRebP120yjLiSnZ5FpaQ1stusr0hSg+VQaX4np
+f5m1W/CcDr53PKWg/ayY9nWMUQaIwH4b69kLM+VTpYSbzu5UQJkmNBNq2EOHgoTv
+9MLA+cE/nNJ/rMI//MZ1+kcCAwEAAQ==
+-----END PUBLIC KEY-----
+\
+
+custom-multi-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuLYZ68rotGDAtWViFHOe
+XEsmZB8BGI+af1ixh9JOy9BE4ohGTfPr1YsjGDzh+PhOVLAtyykOoT/qG7cuGB0T
+gBInoRrgVB2/ZKTMwxeDGb/TA3uykaXxcw7/liTsizHAY+phCNTbke8iER5Y78js
+9GlnTPmNhwFqEj2fwCz+2o08eyZvZ9Vj1fH/bFDCmDmU33JR3crtJlC8wPiF70Ho
+FJzHFdaFQl3MxvEV92HjOsyqozMi6tAVVefN1vapVQeNtjkB0Di18p0/EMugEuGU
+OxktjDHQWNaV8Ao6cCDk6OkJnM3ZNL1no3cV4cuF+/xI8UZzwfPoBnwg/s183Qzu
+pHHKOSHmuO0oVE/XohJhepSw3tb+wf5BwejRhYHikIjqCxJdm9H0QTiqXT82y24K
+yg3gkRMOgqnVxERKKP4ZknLSMQCEKiND/t2zdLJ/lxH9eHZdPHKk3OZZG292j+Bh
+fknxcTKNk1Dmf32Irs5hVrjsoU8eAutbItovzXdBaj//rn/ry/kUlCa1Ov6iLIDJ
+gyxmsDlgKNR/uE9ogmDn0ishJIoCmxeqenRfJkttr9pEsDsUFuB425QGqiSxa1jh
+PCNca3iRtO44wADXaQMTGpvLzBfdfVc8LoFpn+kynN0V1MvxAX4mHRXxw8ERXd3U
+dpHDhOthPLolJQrYKb/YyW8CAwEAAQ==
+-----END PUBLIC KEY-----
+\
diff --git a/libodb/odb/buildfile b/libodb/odb/buildfile
new file mode 100644
index 0000000..903893f
--- /dev/null
+++ b/libodb/odb/buildfile
@@ -0,0 +1,67 @@
+# file : odb/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+lib{odb}: {hxx ixx txx cxx}{* -version} \
+ {hxx}{version} \
+ details/{hxx ixx txx}{*} \
+ details/{cxx}{* -condition -lock -mutex} \
+ details/{h}{*} \
+ details/meta/{hxx}{*} \
+details/shared-ptr/{hxx ixx txx cxx}{*} \
+ details/win32/{hxx}{windows}
+
+# 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.
+#
+cxx.poptions =+ "-I$out_root" "-I$src_root"
+
+obja{*}: cxx.poptions += -DLIBODB_STATIC_BUILD
+objs{*}: cxx.poptions += -DLIBODB_SHARED_BUILD
+
+# Export options.
+#
+lib{odb}: cxx.export.poptions = "-I$out_root" "-I$src_root"
+
+liba{odb}: cxx.export.poptions += -DLIBODB_STATIC
+libs{odb}: cxx.export.poptions += -DLIBODB_SHARED
+
+# For pre-releases use the complete version to make sure they cannot be used
+# in place of another pre-release or the final version. See the version module
+# for details on the version.* variable values.
+#
+if $version.pre_release
+ lib{odb}: bin.lib.version = @"-$version.project_id"
+else
+ lib{odb}: bin.lib.version = @"-$version.major.$version.minor"
+
+# Install into the odb/ subdirectory of, say, /usr/include/ recreating
+# subdirectories.
+#
+install_include = [dir_path] include/odb/
+
+{h hxx ixx txx}{*}:
+{
+ install = $install_include
+ install.subdirs = true
+}
+
+if ($cxx.target.class != "windows")
+ details/win32/*: install = false
+
+details/
+{
+ if ($cxx.target.system != 'win32-msvc')
+ h{config-vc}@./: install = false
+ else
+ h{config}@./: install = false
+}
diff --git a/libodb/odb/c-array-traits.hxx b/libodb/odb/c-array-traits.hxx
new file mode 100644
index 0000000..fff7880
--- /dev/null
+++ b/libodb/odb/c-array-traits.hxx
@@ -0,0 +1,103 @@
+// file : odb/c-array-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_C_ARRAY_TRAITS_HXX
+#define ODB_C_ARRAY_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+#include <cassert>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ // Optional mapping of C arrays as containers. Note that this mapping is not
+ // enable by default. To enable, pass the following options to the ODB
+ // compiler:
+ //
+ // --odb-epilogue '#include <odb/c-array-traits.hxx>'
+ // --hxx-prologue '#include <odb/c-array-traits.hxx>'
+ //
+ // Note also that the array types have to be named, for example:
+ //
+ // class object
+ // {
+ // // composite_type values[5]; // Won't work.
+ //
+ // typedef composite_type composite_array[5];
+ // composite_array values;
+ // };
+ //
+ // Finally, this mapping is disabled for the char[N] and wchar_t[N] types
+ // (they are mapped as strings by default).
+ //
+ template <typename V, std::size_t N>
+ class access::container_traits<V[N]>
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = false;
+
+ typedef V container_type[N];
+
+ typedef V value_type;
+ typedef std::size_t index_type;
+
+ typedef ordered_functions<index_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (index_type i (0); i < N; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ index_type i (0);
+
+ for (; more && i < N; ++i)
+ {
+ index_type dummy;
+ more = f.select (dummy, c[i]);
+ }
+
+ assert (!more && i == N);
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (index_type i (0); i < N; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+
+ // Disable for char[N] and wchar_t[N].
+ //
+#ifdef ODB_COMPILER
+ template <std::size_t N>
+ class access::container_traits<char[N]>;
+
+#ifdef _WIN32
+ template <std::size_t N>
+ class access::container_traits<wchar_t[N]>;
+#endif
+#endif
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_C_ARRAY_TRAITS_HXX
diff --git a/libodb/odb/cache-traits.hxx b/libodb/odb/cache-traits.hxx
new file mode 100644
index 0000000..a8cf750
--- /dev/null
+++ b/libodb/odb/cache-traits.hxx
@@ -0,0 +1,182 @@
+// file : odb/cache-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_CACHE_TRAITS_HXX
+#define ODB_CACHE_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/traits.hxx>
+#include <odb/forward.hxx>
+#include <odb/session.hxx>
+#include <odb/pointer-traits.hxx>
+#include <odb/no-op-cache-traits.hxx>
+
+namespace odb
+{
+ // pointer_cache_traits
+ //
+ // Caching traits for objects passed by pointer. P should be the canonical
+ // pointer (non-const).
+ //
+ template <typename P, typename S, pointer_kind pk>
+ struct pointer_cache_traits_impl
+ {
+ typedef P pointer_type;
+ typedef S session_type;
+ typedef odb::pointer_traits<pointer_type> pointer_traits;
+ typedef typename pointer_traits::element_type object_type;
+ typedef typename object_traits<object_type>::id_type id_type;
+ typedef typename session_type::template cache_position<object_type>
+ position_type;
+
+ struct insert_guard
+ {
+ insert_guard () {}
+ insert_guard (const position_type& pos): pos_ (pos) {}
+ ~insert_guard () {session_type::_cache_erase (pos_);}
+
+ const position_type&
+ position () const {return pos_;}
+
+ void
+ release () {pos_ = position_type ();}
+
+ // Note: doesn't call erase() on the old position (assumes empty).
+ //
+ void
+ reset (const position_type& pos) {pos_ = pos;}
+
+ private:
+ position_type pos_;
+ };
+
+ // Cache management.
+ //
+ // We need the insert() overload with explicit id to handle self-
+ // references. In such cases the object is not yet loaded and the
+ // id member does not contain the correct id.
+ //
+ // Qualify the database type to resolve a phony ambiguity in VC 10.
+ //
+ static position_type
+ insert (odb::database& db, const id_type& id, const pointer_type& p)
+ {
+ return session_type::template _cache_insert<object_type> (db, id, p);
+ }
+
+ static position_type
+ insert (odb::database& db, const pointer_type& p)
+ {
+ const id_type& id (
+ object_traits<object_type>::id (
+ pointer_traits::get_ref (p)));
+
+ return session_type::template _cache_insert<object_type> (db, id, p);
+ }
+
+ static pointer_type
+ find (odb::database& db, const id_type& id)
+ {
+ return session_type::template _cache_find<object_type> (db, id);
+ }
+
+ static void
+ erase (const position_type& p)
+ {
+ session_type::template _cache_erase<object_type> (p);
+ }
+
+ // Notifications.
+ //
+ static void
+ persist (const position_type& p)
+ {
+ session_type::template _cache_persist<object_type> (p);
+ }
+
+ static void
+ load (const position_type& p)
+ {
+ session_type::template _cache_load<object_type> (p);
+ }
+
+ static void
+ update (odb::database& db, const object_type& obj)
+ {
+ session_type::template _cache_update<object_type> (db, obj);
+ }
+
+ static void
+ erase (odb::database& db, const id_type& id)
+ {
+ session_type::template _cache_erase<object_type> (db, id);
+ }
+ };
+
+ // Unique pointers don't work with the object cache.
+ //
+ template <typename P, typename S>
+ struct pointer_cache_traits_impl<P, S, pk_unique>:
+ no_op_pointer_cache_traits<P> {};
+
+ template <typename P, typename S>
+ struct pointer_cache_traits:
+ pointer_cache_traits_impl<P, S, pointer_traits<P>::kind> {};
+
+ // reference_cache_traits
+ //
+ // Caching traits for objects passed by reference. T should be the
+ // canonical object type (non-const). Only if the object pointer
+ // kind is raw do we add the object to the session.
+ //
+ template <typename T, typename S, pointer_kind pk>
+ struct reference_cache_traits_impl: no_op_reference_cache_traits<T> {};
+
+ template <typename T, typename S>
+ struct reference_cache_traits_impl<T, S, pk_raw>
+ {
+ typedef T object_type;
+ typedef typename object_traits<object_type>::pointer_type pointer_type;
+ typedef typename object_traits<object_type>::id_type id_type;
+
+ typedef pointer_cache_traits<pointer_type, S> pointer_traits;
+ typedef typename pointer_traits::position_type position_type;
+ typedef typename pointer_traits::insert_guard insert_guard;
+
+ static position_type
+ insert (odb::database& db, const id_type& id, object_type& obj)
+ {
+ pointer_type p (&obj);
+ return pointer_traits::insert (db, id, p);
+ }
+
+ static position_type
+ insert (odb::database& db, object_type& obj)
+ {
+ pointer_type p (&obj);
+ return pointer_traits::insert (db, p);
+ }
+
+ static void
+ persist (const position_type& p)
+ {
+ pointer_traits::persist (p);
+ }
+
+ static void
+ load (const position_type& p)
+ {
+ pointer_traits::load (p);
+ }
+ };
+
+ template <typename T, typename S>
+ struct reference_cache_traits:
+ reference_cache_traits_impl<
+ T, S, pointer_traits<typename object_traits<T>::pointer_type>::kind> {};
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_CACHE_TRAITS_HXX
diff --git a/libodb/odb/callback.hxx b/libodb/odb/callback.hxx
new file mode 100644
index 0000000..aaa066f
--- /dev/null
+++ b/libodb/odb/callback.hxx
@@ -0,0 +1,42 @@
+// file : odb/callback.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_CALLBACK_HXX
+#define ODB_CALLBACK_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx> // odb::core
+
+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): v_ (v) {}
+ operator value () const {return v_;}
+
+ private:
+ value v_;
+ };
+
+ namespace common
+ {
+ using odb::callback_event;
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_CALLBACK_HXX
diff --git a/libodb/odb/connection.cxx b/libodb/odb/connection.cxx
new file mode 100644
index 0000000..29743a2
--- /dev/null
+++ b/libodb/odb/connection.cxx
@@ -0,0 +1,125 @@
+// file : odb/connection.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/database.hxx>
+#include <odb/connection.hxx>
+#include <odb/result.hxx>
+#include <odb/prepared-query.hxx>
+#include <odb/exceptions.hxx> // prepared_*
+
+using namespace std;
+
+namespace odb
+{
+ // connection
+ //
+ connection::
+ ~connection ()
+ {
+ assert (prepared_queries_ == 0);
+ assert (prepared_map_.empty ());
+ }
+
+ void connection::
+ clear_prepared_map ()
+ {
+ for (prepared_map_type::iterator i (prepared_map_.begin ()),
+ e (prepared_map_.end ()); i != e; ++i)
+ {
+ if (i->second.params != 0)
+ i->second.params_deleter (i->second.params);
+ }
+
+ prepared_map_.clear ();
+ }
+
+ void connection::
+ recycle ()
+ {
+ while (prepared_queries_ != 0)
+ {
+ prepared_queries_->stmt.reset ();
+ prepared_queries_->list_remove ();
+ }
+ }
+
+ void connection::
+ invalidate_results ()
+ {
+ while (results_ != 0)
+ {
+ results_->invalidate ();
+ results_->list_remove ();
+ }
+ }
+
+ void connection::
+ cache_query_ (prepared_query_impl* pq,
+ const type_info& ti,
+ void* params,
+ const type_info* params_info,
+ void (*params_deleter) (void*))
+ {
+ pair<prepared_map_type::iterator, bool> r (
+ prepared_map_.insert (
+ prepared_map_type::value_type (pq->name, prepared_entry_type ())));
+
+ if (!r.second)
+ throw prepared_already_cached (pq->name);
+
+ prepared_entry_type& e (r.first->second);
+
+ // Mark this prepared query as cached , get its ref count to 1
+ // (prepared_query instances now reference this impl object),
+ // and remove it from the invalidation list.
+ //
+ pq->cached = true;
+
+ while (pq->_ref_count () > 1)
+ pq->_dec_ref ();
+
+ pq->list_remove ();
+
+ e.prep_query.reset (pq);
+ e.type_info = &ti;
+ e.params = params;
+ e.params_info = params_info;
+ e.params_deleter = params_deleter;
+ }
+
+ prepared_query_impl* connection::
+ lookup_query_ (const char* name,
+ const type_info& ti,
+ void** params,
+ const type_info* params_info) const
+ {
+ prepared_map_type::const_iterator i (prepared_map_.find (name));
+
+ if (i == prepared_map_.end ())
+ {
+ // Use a factory, if there is one.
+ //
+ if (factory_.database ().call_query_factory (
+ name, const_cast<connection&> (*this)))
+ i = prepared_map_.find (name);
+ }
+
+ if (i == prepared_map_.end ())
+ return 0;
+
+ // Make sure the types match.
+ //
+ if (*i->second.type_info != ti)
+ throw prepared_type_mismatch (name);
+
+ if (params != 0)
+ {
+ if (*i->second.params_info != *params_info)
+ throw prepared_type_mismatch (name);
+
+ *params = i->second.params;
+ }
+
+ return i->second.prep_query.get ();
+ }
+}
diff --git a/libodb/odb/connection.hxx b/libodb/odb/connection.hxx
new file mode 100644
index 0000000..8ce4544
--- /dev/null
+++ b/libodb/odb/connection.hxx
@@ -0,0 +1,228 @@
+// file : odb/connection.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_CONNECTION_HXX
+#define ODB_CONNECTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <map>
+#include <string>
+#include <memory> // std::auto_ptr, std::unique_ptr
+#include <cstddef> // std::size_t
+#include <typeinfo>
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+#include <odb/query.hxx>
+#include <odb/prepared-query.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+#include <odb/details/export.hxx>
+#include <odb/details/c-string.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+namespace odb
+{
+ class transaction_impl;
+ class connection_factory;
+
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+
+ class LIBODB_EXPORT connection: public details::shared_base
+ {
+ public:
+ typedef odb::database database_type;
+
+ database_type&
+ database ();
+
+ // Transactions.
+ //
+ public:
+ virtual transaction_impl*
+ begin () = 0;
+
+ // Native database statement execution. Note that unlike the
+ // versions in the database class, these can be executed
+ // without a transaction.
+ //
+ public:
+ unsigned long long
+ execute (const char* statement);
+
+ unsigned long long
+ execute (const std::string& statement);
+
+ virtual unsigned long long
+ execute (const char* statement, std::size_t length) = 0;
+
+ // Query preparation.
+ //
+ public:
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const query<T>&);
+
+ template <typename T>
+ void
+ cache_query (const prepared_query<T>&);
+
+#ifdef ODB_CXX11
+ template <typename T, typename P>
+ void
+ cache_query (const prepared_query<T>&, std::unique_ptr<P> params);
+#else
+ template <typename T, typename P>
+ void
+ cache_query (const prepared_query<T>&, std::auto_ptr<P> params);
+#endif
+
+ 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);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef odb::tracer tracer_type;
+
+ void
+ tracer (tracer_type&);
+
+ void
+ tracer (tracer_type*);
+
+ tracer_type*
+ tracer () const;
+
+ public:
+ // Store the transaction-spacific tracer in the connection. If we
+ // were to store it in the transaction, then in order to check if
+ // it was set, we would need to get the transaction instance using
+ // the current() API. But that requires a TLS lookup, which can be
+ // slow.
+ //
+ tracer_type*
+ transaction_tracer () const;
+
+ public:
+ virtual
+ ~connection ();
+
+ // Recycle the connection to be used by another thread. This call
+ // invalidates uncached prepared queries.
+ //
+ void
+ recycle ();
+
+ protected:
+ connection (connection_factory&);
+
+ template <typename T,
+ database_id DB,
+ class_kind kind = class_traits<T>::kind>
+ struct query_;
+
+ virtual void
+ cache_query_ (prepared_query_impl* pq,
+ const std::type_info& ti,
+ void* params,
+ const std::type_info* params_info,
+ void (*params_deleter) (void*));
+
+ prepared_query_impl*
+ lookup_query_ (const char* name,
+ const std::type_info& ti,
+ void** params, // out
+ const std::type_info* params_info) const;
+
+ template <typename P>
+ static void
+ params_deleter (void*);
+
+ private:
+ connection (const connection&);
+ connection& operator= (const connection&);
+
+ // Prepared query cache.
+ //
+ protected:
+ struct prepared_entry_type
+ {
+ details::shared_ptr<prepared_query_impl> prep_query;
+ const std::type_info* type_info;
+ void* params;
+ const std::type_info* params_info;
+ void (*params_deleter) (void*);
+ };
+
+ typedef
+ std::map<const char*, prepared_entry_type, details::c_string_comparator>
+ prepared_map_type;
+
+ prepared_map_type prepared_map_;
+
+ void
+ clear_prepared_map ();
+
+ protected:
+ connection_factory& factory_;
+ tracer_type* tracer_;
+
+ // Active query result list.
+ //
+ protected:
+ friend class result_impl;
+ result_impl* results_;
+
+ void
+ invalidate_results ();
+
+ // Prepared but uncached query list (cached ones are stored in
+ // prepared_map_).
+ //
+ protected:
+ friend class prepared_query_impl;
+ prepared_query_impl* prepared_queries_;
+
+ // Implementation details.
+ //
+ public:
+ tracer_type* transaction_tracer_;
+ };
+
+ class connection_factory
+ {
+ public:
+ typedef odb::database database_type;
+
+ connection_factory (): db_ (0) {}
+
+ database_type&
+ database () {return *db_;}
+
+ protected:
+ database_type* db_;
+ };
+}
+
+#include <odb/connection.ixx>
+#include <odb/connection.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_CONNECTION_HXX
diff --git a/libodb/odb/connection.ixx b/libodb/odb/connection.ixx
new file mode 100644
index 0000000..d19390a
--- /dev/null
+++ b/libodb/odb/connection.ixx
@@ -0,0 +1,131 @@
+// file : odb/connection.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::string
+#include <cassert>
+
+namespace odb
+{
+ inline connection::
+ connection (connection_factory& f)
+ : factory_ (f),
+ tracer_ (0),
+ results_ (0),
+ prepared_queries_ (0),
+ transaction_tracer_ (0)
+ {
+ }
+
+ inline connection::database_type& connection::
+ database ()
+ {
+ return factory_.database ();
+ }
+
+ inline unsigned long long connection::
+ execute (const char* st)
+ {
+ return execute (st, std::strlen (st));
+ }
+
+ inline unsigned long long connection::
+ execute (const std::string& st)
+ {
+ return execute (st.c_str (), st.size ());
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const query<T>& q)
+ {
+ return query_<T, id_common>::call (*this, n, q);
+ }
+
+ template <typename T>
+ inline void connection::
+ cache_query (const prepared_query<T>& pq)
+ {
+ assert (pq);
+ cache_query_ (pq.impl_, typeid (T), 0, 0, 0);
+ }
+
+#ifdef ODB_CXX11
+ template <typename T, typename P>
+ inline void connection::
+ cache_query (const prepared_query<T>& pq, std::unique_ptr<P> params)
+ {
+ assert (pq);
+ assert (params);
+ cache_query_ (
+ pq.impl_, typeid (T), params.get (), &typeid (P), &params_deleter<P>);
+ params.release ();
+ }
+#else
+ template <typename T, typename P>
+ inline void connection::
+ cache_query (const prepared_query<T>& pq, std::auto_ptr<P> params)
+ {
+ assert (pq);
+ assert (params.get () != 0);
+ cache_query_ (
+ pq.impl_, typeid (T), params.get (), &typeid (P), &params_deleter<P>);
+ params.release ();
+ }
+#endif
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ lookup_query (const char* name)
+ {
+ return prepared_query<T> (lookup_query_ (name, typeid (T), 0, 0));
+ }
+
+ template <typename T, typename P>
+ inline prepared_query<T> connection::
+ lookup_query (const char* name, P*& params)
+ {
+ return prepared_query<T> (
+ lookup_query_ (name,
+ typeid (T),
+ reinterpret_cast<void**> (&params),
+ &typeid (P)));
+ }
+
+ inline void connection::
+ tracer (tracer_type& t)
+ {
+ tracer_ = &t;
+ }
+
+ inline void connection::
+ tracer (tracer_type* t)
+ {
+ tracer_ = t;
+ }
+
+ inline connection::tracer_type* connection::
+ tracer () const
+ {
+ return tracer_;
+ }
+
+ inline connection::tracer_type* connection::
+ transaction_tracer () const
+ {
+ return transaction_tracer_;
+ }
+}
diff --git a/libodb/odb/connection.txx b/libodb/odb/connection.txx
new file mode 100644
index 0000000..a082f14
--- /dev/null
+++ b/libodb/odb/connection.txx
@@ -0,0 +1,44 @@
+// file : odb/connection.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ template <typename T, database_id DB>
+ struct connection::query_<T, DB, class_object>
+ {
+ template <typename Q>
+ static prepared_query<T>
+ call (connection& c, const char* n, const Q& q)
+ {
+ // C++ compiler complaining there is no prepare_query()? Perhaps
+ // you forgot to specify --generate-prepared when compiling your
+ // persistent classes.
+ //
+ return prepared_query<T> (
+ object_traits_impl<T, DB>::prepare_query (c, n, q));
+ }
+ };
+
+ template <typename T, database_id DB>
+ struct connection::query_<T, DB, class_view>
+ {
+ template <typename Q>
+ static prepared_query<T>
+ call (connection& c, const char* n, const Q& q)
+ {
+ // C++ compiler complaining there is no prepare_query()? Perhaps
+ // you forgot to specify --generate-prepared when compiling your
+ // views.
+ //
+ return prepared_query<T> (
+ view_traits_impl<T, DB>::prepare_query (c, n, q));
+ }
+ };
+
+ template <typename P>
+ void connection::
+ params_deleter (void* p)
+ {
+ delete static_cast<P*> (p);
+ }
+}
diff --git a/libodb/odb/container-traits.hxx b/libodb/odb/container-traits.hxx
new file mode 100644
index 0000000..e2f44ce
--- /dev/null
+++ b/libodb/odb/container-traits.hxx
@@ -0,0 +1,219 @@
+// file : odb/container-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_CONTAINER_TRAITS_HXX
+#define ODB_CONTAINER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ // Keep this enum synchronized with the one in odb/odb/context.hxx.
+ //
+ enum container_kind
+ {
+ ck_ordered,
+ ck_set,
+ ck_multiset,
+ ck_map,
+ ck_multimap
+ };
+
+ //
+ // Container API provided by the generated code.
+ //
+
+ // Ordered containers.
+ //
+ template <typename I, typename V>
+ struct ordered_functions
+ {
+ typedef I index_type;
+ typedef V value_type;
+
+ // Return true if the order is preserved in the database. If the
+ // order is not preserved, then the index argument in the functions
+ // below is not used.
+ //
+ bool
+ ordered () const
+ {
+ return ordered_;
+ }
+
+ void
+ insert (I index, const V& value) const
+ {
+ insert_ (index, value, data_);
+ }
+
+ bool
+ select (I& next_index, V& next_value) const
+ {
+ return select_ (next_index, next_value, data_);
+ }
+
+ void
+ delete_ () const
+ {
+ delete__ (data_);
+ }
+
+ // Implementation details.
+ //
+ public:
+ ordered_functions (void* data): data_ (data) {}
+
+ public:
+ void* data_;
+ bool ordered_;
+
+ void (*insert_) (I, const V&, void*);
+ bool (*select_) (I&, V&, void*);
+ void (*delete__) (void*);
+ };
+
+ template <typename I, typename V>
+ struct smart_ordered_functions
+ {
+ typedef I index_type;
+ typedef V value_type;
+
+ void
+ insert (I index, const V& value) const
+ {
+ insert_ (index, value, data_);
+ }
+
+ bool
+ select (I& next_index, V& next_value) const
+ {
+ return select_ (next_index, next_value, data_);
+ }
+
+ void
+ update (I index, const V& value) const
+ {
+ update_ (index, value, data_);
+ }
+
+ // Delete all the elements starting with the specified index. To
+ // delete everything, pass 0.
+ //
+ void
+ delete_ (I start_index) const
+ {
+ delete__ (start_index, data_);
+ }
+
+ // Implementation details.
+ //
+ public:
+ smart_ordered_functions (void* data) : data_ (data) {}
+
+ public:
+ void* data_;
+
+ void (*insert_) (I, const V&, void*);
+ bool (*select_) (I&, V&, void*);
+ void (*update_) (I, const V&, void*);
+ void (*delete__) (I, void*);
+ };
+
+ // Set/multiset containers.
+ //
+ template <typename V>
+ struct set_functions
+ {
+ typedef V value_type;
+
+ void
+ insert (const V& value) const
+ {
+ insert_ (value, data_);
+ }
+
+ bool
+ select (V& next_value) const
+ {
+ return select_ (next_value, data_);
+ }
+
+ void
+ delete_ () const
+ {
+ delete__ (data_);
+ }
+
+ // Implementation details.
+ //
+ public:
+ set_functions (void* data): data_ (data) {}
+
+ public:
+ void* data_;
+
+ void (*insert_) (const V&, void*);
+ bool (*select_) (V&, void*);
+ void (*delete__) (void*);
+ };
+
+ // Map/multimap containers.
+ //
+ template <typename K, typename V>
+ struct map_functions
+ {
+ typedef K key_type;
+ typedef V value_type;
+
+ void
+ insert (const K& key, const V& value) const
+ {
+ insert_ (key, value, data_);
+ }
+
+ bool
+ select (K& next_key, V& next_value) const
+ {
+ return select_ (next_key, next_value, data_);
+ }
+
+ void
+ delete_ () const
+ {
+ delete__ (data_);
+ }
+
+ // Implementation details.
+ //
+ public:
+ map_functions (void* data): data_ (data) {}
+
+ public:
+ void* data_;
+
+ void (*insert_) (const K&, const V&, void*);
+ bool (*select_) (K&, V&, void*);
+ void (*delete__) (void*);
+ };
+}
+
+#include <odb/post.hxx>
+
+#include <odb/std-map-traits.hxx>
+#include <odb/std-set-traits.hxx>
+#include <odb/std-list-traits.hxx>
+#include <odb/std-vector-traits.hxx>
+#include <odb/std-deque-traits.hxx>
+
+#ifdef ODB_CXX11
+# include <odb/std-array-traits.hxx>
+# include <odb/std-forward-list-traits.hxx>
+# include <odb/std-unordered-map-traits.hxx>
+# include <odb/std-unordered-set-traits.hxx>
+#endif
+
+#endif // ODB_CONTAINER_TRAITS_HXX
diff --git a/libodb/odb/core.hxx b/libodb/odb/core.hxx
new file mode 100644
index 0000000..bca295d
--- /dev/null
+++ b/libodb/odb/core.hxx
@@ -0,0 +1,20 @@
+// file : odb/core.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_CORE_HXX
+#define ODB_CORE_HXX
+
+#include <odb/pre.hxx>
+
+#ifdef ODB_COMPILER
+# define PRAGMA_DB_IMPL(x) _Pragma (#x)
+# define PRAGMA_DB(x) PRAGMA_DB_IMPL (db x)
+#else
+# define PRAGMA_DB(x)
+#endif
+
+#include <odb/forward.hxx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_CORE_HXX
diff --git a/libodb/odb/database.cxx b/libodb/odb/database.cxx
new file mode 100644
index 0000000..9e098c7
--- /dev/null
+++ b/libodb/odb/database.cxx
@@ -0,0 +1,83 @@
+// file : odb/database.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/database.hxx>
+
+#include <odb/details/lock.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ using details::lock;
+
+ database::
+ ~database ()
+ {
+ }
+
+ unsigned long long database::
+ execute (const char* st, std::size_t n)
+ {
+ connection_type& c (transaction::current ().connection (*this));
+ return c.execute (st, n);
+ }
+
+ const database::schema_version_migration_type& database::
+ schema_version_migration (const string& name) const
+ {
+ lock l (*mutex_); // Prevents concurrent loading.
+
+ schema_version_map::const_iterator i (schema_version_map_.find (name));
+ return i != schema_version_map_.end () && i->second.version != 0
+ ? i->second
+ : load_schema_version (name);
+ }
+
+ void database::
+ schema_version_migration (const schema_version_migration_type& svm,
+ const string& name)
+ {
+ // Note: no lock, not thread-safe.
+
+ schema_version_info& svi (schema_version_map_[name]);
+ if (svi.version != svm.version || svi.migration != svm.migration)
+ {
+ svi.version = svm.version;
+ svi.migration = svm.migration;
+ schema_version_seq_++;
+ }
+ }
+
+ bool database::
+ call_query_factory (const char* name, connection_type& c) const
+ {
+ query_factory_map::const_iterator i (query_factory_map_.find (name));
+
+ if (i == query_factory_map_.end ())
+ i = query_factory_map_.find (""); // Wildcard factory.
+
+ if (i == query_factory_map_.end ())
+ return false;
+
+ const query_factory_wrapper& fw (i->second);
+ if (fw.std_function == 0)
+ fw.function (name, c);
+ else
+ {
+ typedef void (*caller) (const void*, const char*, connection_type&);
+ fw.cast<caller> () (fw.std_function, name, c);
+ }
+
+ return true;
+ }
+
+ void database::
+ query_factory (const char* name, query_factory_wrapper w)
+ {
+ if (w)
+ query_factory_map_[name] = w; // Destructive copy assignment (move).
+ else
+ query_factory_map_.erase (name);
+ }
+}
diff --git a/libodb/odb/database.hxx b/libodb/odb/database.hxx
new file mode 100644
index 0000000..e18e8ee
--- /dev/null
+++ b/libodb/odb/database.hxx
@@ -0,0 +1,657 @@
+// file : odb/database.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DATABASE_HXX
+#define ODB_DATABASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <map>
+#include <string>
+#include <memory> // std::auto_ptr, std::unique_ptr
+#include <cstddef> // std::size_t
+
+#ifdef ODB_CXX11
+# include <utility> // std::move
+# include <functional> // std::function
+# include <type_traits> // std::enable_if, std::is_convertible
+#endif
+
+#include <odb/traits.hxx>
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/query.hxx>
+#include <odb/prepared-query.hxx>
+#include <odb/result.hxx>
+#include <odb/connection.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/details/export.hxx>
+#include <odb/details/mutex.hxx>
+#include <odb/details/c-string.hxx>
+#include <odb/details/unique-ptr.hxx>
+#include <odb/details/function-wrapper.hxx>
+#include <odb/details/meta/answer.hxx>
+
+namespace odb
+{
+ class transaction_impl;
+
+ class LIBODB_EXPORT database
+ {
+ public:
+ virtual
+ ~database ();
+
+#ifdef ODB_CXX11
+ //database (database&&) = default; // VC 2013
+
+ // Note: noexcept is not specified since *_map_ (std::map) can throw.
+ //
+ database (database&& d)
+ : id_ (d.id_),
+ tracer_ (d.tracer_),
+ query_factory_map_ (std::move (d.query_factory_map_)),
+ mutex_ (std::move (d.mutex_)),
+ schema_version_map_ (std::move (d.schema_version_map_)),
+ schema_version_table_ (std::move (d.schema_version_table_)),
+ schema_version_seq_ (d.schema_version_seq_)
+ {
+ }
+#endif
+
+ private:
+ database (const database&);
+ database& operator= (const database&);
+
+#ifdef ODB_CXX11
+ database& operator= (const database&&);
+#endif
+
+ // Object persistence API.
+ //
+ public:
+ // Make the object persistent.
+ //
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Bulk persist. Can be a range of references or pointers (including
+ // smart pointers) to objects.
+ //
+ template <typename I>
+ void
+ persist (I begin, I end, bool continue_failed = true);
+
+ // Load an object. Throw object_not_persistent if not found.
+ //
+ 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);
+
+ // Load (or reload, if it is already loaded) a section of an object.
+ //
+ template <typename T>
+ void
+ load (T& object, section&);
+
+ // Reload an object.
+ //
+ template <typename T>
+ void
+ reload (T& object);
+
+ template <typename T>
+ void
+ reload (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ reload (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Loan an object if found. Return NULL/false if not found.
+ //
+ 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);
+
+ // Update the state of a modified objects.
+ //
+ template <typename T>
+ void
+ update (T& object);
+
+ template <typename T>
+ void
+ update (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ update (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Bulk update. Can be a range of references or pointers (including
+ // smart pointers) to objects.
+ //
+ template <typename I>
+ void
+ update (I begin, I end, bool continue_failed = true);
+
+ // Update a section of an object. Throws section_not_loaded exception
+ // if section is not loaded. Note also that this function does not
+ // clear the changed flag if it is set.
+ //
+ template <typename T>
+ void
+ update (const T& object, const section&);
+
+ // Make the object transient. Throw object_not_persistent if not
+ // found.
+ //
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::id_type& id);
+
+ template <typename T>
+ void
+ erase (T& object);
+
+ template <typename T>
+ void
+ erase (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Bulk erase.
+ //
+ template <typename T, typename I>
+ void
+ erase (I id_begin, I id_end, bool continue_failed = true);
+
+ // Can be a range of references or pointers (including smart pointers)
+ // to objects.
+ //
+ template <typename I>
+ void
+ erase (I obj_begin, I obj_end, bool continue_failed = true);
+
+ // Erase multiple objects matching a query predicate.
+ //
+ template <typename T>
+ unsigned long long
+ erase_query ();
+
+ template <typename T>
+ unsigned long long
+ erase_query (const char*);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const std::string&);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const odb::query<T>&);
+
+ // Query API.
+ //
+ template <typename T>
+ result<T>
+ query (bool cache = true);
+
+ template <typename T>
+ result<T>
+ query (const char*, bool cache = true);
+
+ template <typename T>
+ result<T>
+ query (const std::string&, bool cache = true);
+
+ template <typename T>
+ result<T>
+ query (const odb::query<T>&, bool cache = true);
+
+ // Query one API.
+ //
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one ();
+
+ template <typename T>
+ bool
+ query_one (T& object);
+
+ template <typename T>
+ T
+ query_value ();
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const char*);
+
+ template <typename T>
+ bool
+ query_one (const char*, T& object);
+
+ template <typename T>
+ T
+ query_value (const char*);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const std::string&);
+
+ template <typename T>
+ bool
+ query_one (const std::string&, T& object);
+
+ template <typename T>
+ T
+ query_value (const std::string&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const odb::query<T>&);
+
+ template <typename T>
+ bool
+ query_one (const odb::query<T>&, T& object);
+
+ template <typename T>
+ T
+ query_value (const odb::query<T>&);
+
+ // Query preparation.
+ //
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const odb::query<T>&);
+
+ template <typename T>
+ void
+ cache_query (const prepared_query<T>&);
+
+#ifdef ODB_CXX11
+ template <typename T, typename P>
+ void
+ cache_query (const prepared_query<T>&, std::unique_ptr<P> params);
+#else
+ template <typename T, typename P>
+ void
+ cache_query (const prepared_query<T>&, std::auto_ptr<P> params);
+#endif
+
+ 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);
+
+ // Prepared query factory.
+ //
+ public:
+ typedef odb::connection connection_type;
+
+ typedef void query_factory_type (const char* name, connection_type&);
+ typedef query_factory_type* query_factory_ptr;
+ typedef details::function_wrapper<
+ query_factory_type> query_factory_wrapper;
+
+#ifndef ODB_CXX11
+ void
+ query_factory (const char* name, query_factory_ptr);
+#else
+ template <typename F>
+ typename std::enable_if<
+ std::is_convertible<
+ F, std::function<query_factory_type>>::value, void>::type
+ query_factory (const char* name, F f)
+ {
+ query_factory (name, query_factory_wrapper (std::move (f)));
+ }
+#endif
+
+ bool
+ call_query_factory (const char* name, connection_type&) const;
+
+ private:
+ void
+ query_factory (const char* name, query_factory_wrapper);
+
+ // Native database statement execution.
+ //
+ public:
+ 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);
+
+ // Transactions.
+ //
+ public:
+ virtual transaction_impl*
+ begin () = 0;
+
+ // Connections.
+ //
+ public:
+ connection_ptr
+ connection ();
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef odb::tracer tracer_type;
+
+ void
+ tracer (tracer_type&);
+
+ void
+ tracer (tracer_type*);
+
+ tracer_type*
+ tracer () const;
+
+ // Database schema version.
+ //
+ public:
+ typedef odb::schema_version schema_version_type;
+ typedef odb::schema_version_migration schema_version_migration_type;
+
+ schema_version_type
+ schema_version (const std::string& schema_name = "") const;
+
+ bool
+ schema_migration (const std::string& schema_name = "") const;
+
+ // Note that there is code that relies on the returned reference
+ // being valid until the version is changed or the database instance
+ // is destroyed.
+ //
+ const schema_version_migration_type&
+ schema_version_migration (const std::string& schema_name = "") const;
+
+ // Set schema version and migration state manually.
+ //
+ // 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.
+ //
+ void
+ schema_version_migration (schema_version_type,
+ bool migration,
+ const std::string& schema_name = "");
+
+ void
+ schema_version_migration (const schema_version_migration_type&,
+ const std::string& schema_name = "");
+
+ // Set default schema version table for all the schema names. The table
+ // name should already be quoted if necessary.
+ //
+ 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& schema_name);
+
+ // Schema version sequence number. It is incremented every time the
+ // schema version or migration flag is changed and can be used to
+ // detect overall version changes. The starting value is 1.
+ //
+ unsigned int
+ schema_version_sequence () const;
+
+ protected:
+ struct schema_version_info: schema_version_migration_type
+ {
+ std::string version_table;
+ };
+
+ virtual const schema_version_info&
+ load_schema_version (const std::string& schema_name) const = 0;
+
+ private:
+ const schema_version_info&
+ schema_version_migration_ (const std::string& schema_name) const;
+
+ // Database id.
+ //
+ public:
+ database_id
+ id () const;
+
+ protected:
+ database (database_id);
+
+ protected:
+ virtual connection_type*
+ connection_ () = 0;
+
+ protected:
+ template <typename T, database_id DB>
+ typename object_traits<T>::id_type
+ persist_ (T&);
+
+ template <typename T, database_id DB>
+ typename object_traits<T>::id_type
+ persist_ (const typename object_traits<T>::pointer_type&);
+
+ template <typename I, database_id DB>
+ void
+ persist_ (I, I, bool);
+
+ template <typename I, typename T, database_id DB>
+ void
+ persist_ (I, I, bool, details::meta::no ptr);
+
+ template <typename I, typename T, database_id DB>
+ void
+ persist_ (I, I, bool, details::meta::yes ptr);
+
+ template <typename T, database_id DB>
+ typename object_traits<T>::pointer_type
+ load_ (const typename object_traits<T>::id_type&);
+
+ template <typename T, database_id DB>
+ void
+ load_ (const typename object_traits<T>::id_type&, T&);
+
+ template <typename T, database_id DB>
+ void
+ load_ (T&, section&);
+
+ template <typename T, database_id DB>
+ void
+ reload_ (T&);
+
+ template <typename T, database_id DB>
+ typename object_traits<T>::pointer_type
+ find_ (const typename object_traits<T>::id_type&);
+
+ template <typename T, database_id DB>
+ bool
+ find_ (const typename object_traits<T>::id_type&, T&);
+
+ template <typename T, database_id DB>
+ void
+ update_ (T&);
+
+ template <typename T, database_id DB>
+ void
+ update_ (const typename object_traits<T>::pointer_type&);
+
+ template <typename I, database_id DB>
+ void
+ update_ (I, I, bool);
+
+ template <typename T, database_id DB>
+ void
+ update_ (const T&, const section&);
+
+ template <typename T, database_id DB>
+ void
+ erase_ (const typename object_traits<T>::id_type&);
+
+ template <typename T, database_id DB>
+ void
+ erase_ (T&);
+
+ template <typename T, database_id DB>
+ void
+ erase_ (const typename object_traits<T>::pointer_type&);
+
+ template <typename I, typename T, database_id DB>
+ void
+ erase_id_ (I, I, bool);
+
+ template <typename I, database_id DB>
+ void
+ erase_object_ (I, I, bool);
+
+ template <typename T, database_id DB, typename Q>
+ typename result<T>::pointer_type
+ query_one_ (const Q&);
+
+ template <typename T, database_id DB, typename Q>
+ bool
+ query_one_ (const Q&, T&);
+
+ template <typename T, database_id DB, typename Q>
+ T
+ query_value_ (const Q&);
+
+ template <typename T,
+ database_id DB,
+ class_kind kind = class_traits<T>::kind>
+ struct query_;
+
+ protected:
+ typedef
+ std::map<const char*, query_factory_wrapper, details::c_string_comparator>
+ query_factory_map;
+
+ typedef std::map<std::string, schema_version_info> schema_version_map;
+
+ database_id id_;
+ tracer_type* tracer_;
+ query_factory_map query_factory_map_;
+
+ details::unique_ptr<details::mutex> mutex_; // Dynamic for move support.
+ mutable schema_version_map schema_version_map_;
+ std::string schema_version_table_;
+ unsigned int schema_version_seq_;
+ };
+}
+
+#include <odb/database.ixx>
+#include <odb/database.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DATABASE_HXX
diff --git a/libodb/odb/database.ixx b/libodb/odb/database.ixx
new file mode 100644
index 0000000..c3cf2e2
--- /dev/null
+++ b/libodb/odb/database.ixx
@@ -0,0 +1,879 @@
+// file : odb/database.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::strlen()
+#include <utility> // std::move
+#include <iterator>
+
+#include <odb/transaction.hxx>
+#include <odb/pointer-traits.hxx>
+
+namespace odb
+{
+ template <typename T>
+ struct object_pointer_traits
+ {
+ typedef details::meta::no result_type;
+ typedef T object_type;
+ static const T& get_ref (const T& x) {return x;}
+ };
+
+ template <typename T>
+ struct object_pointer_traits<T*>
+ {
+ typedef details::meta::yes result_type;
+ typedef T object_type;
+ static const T& get_ref (const T* p) {return *p;}
+ };
+
+ template <typename T>
+ struct object_pointer_traits<T* const>
+ {
+ typedef details::meta::yes result_type;
+ typedef T object_type;
+ static const T& get_ref (const T* p) {return *p;}
+ };
+
+ template <typename T, template <typename> class P>
+ struct object_pointer_traits<P<T> >
+ {
+ typedef details::meta::yes result_type;
+ typedef T object_type;
+ static const T& get_ref (const P<T>& p) {
+ return pointer_traits<P<T> >::get_ref (p);}
+ };
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ struct object_pointer_traits<P<T, A1> >
+ {
+ typedef details::meta::yes result_type;
+ typedef T object_type;
+ static const T& get_ref (const P<T, A1>& p) {
+ return pointer_traits<P<T, A1> >::get_ref (p);}
+ };
+
+ template <typename T, template <typename> class P>
+ struct object_pointer_traits<const P<T> >
+ {
+ typedef details::meta::yes result_type;
+ typedef T object_type;
+ static const T& get_ref (const P<T>& p) {
+ return pointer_traits<P<T> >::get_ref (p);}
+ };
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ struct object_pointer_traits<const P<T, A1> >
+ {
+ typedef details::meta::yes result_type;
+ typedef T object_type;
+ static const T& get_ref (const P<T, A1>& p) {
+ return pointer_traits<P<T, A1> >::get_ref (p);}
+ };
+
+ inline database::
+ database (database_id id)
+ : id_ (id),
+ tracer_ (0),
+ mutex_ (new details::mutex),
+ schema_version_seq_ (1)
+ {
+ }
+
+ inline database_id database::
+ id () const
+ {
+ return id_;
+ }
+
+ inline database::schema_version_type database::
+ schema_version (const std::string& name) const
+ {
+ return schema_version_migration (name).version;
+ }
+
+ inline bool database::
+ schema_migration (const std::string& name) const
+ {
+ return schema_version_migration (name).migration;
+ }
+
+ inline void database::
+ schema_version_migration (schema_version_type v,
+ bool m,
+ const std::string& name)
+ {
+ schema_version_migration (schema_version_migration_type (v, m), name);
+ }
+
+ inline void database::
+ schema_version_table (const std::string& tname)
+ {
+ schema_version_table_ = tname;
+ }
+
+ inline void database::
+ schema_version_table (const std::string& tname, const std::string& sname)
+ {
+ schema_version_map_[sname].version_table = tname;
+ }
+
+ inline unsigned int database::
+ schema_version_sequence () const
+ {
+ return schema_version_seq_;
+ }
+
+ inline connection_ptr database::
+ connection ()
+ {
+ return connection_ptr (connection_ ());
+ }
+
+#ifndef ODB_CXX11
+ inline void database::
+ query_factory (const char* name, query_factory_ptr f)
+ {
+ query_factory (name, query_factory_wrapper (f));
+ }
+#endif
+
+ inline void database::
+ tracer (tracer_type& t)
+ {
+ tracer_ = &t;
+ }
+
+ inline void database::
+ tracer (tracer_type* t)
+ {
+ tracer_ = t;
+ }
+
+ inline database::tracer_type* database::
+ tracer () const
+ {
+ return tracer_;
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T& obj)
+ {
+ return persist_<T, id_common> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const T& obj)
+ {
+ return persist_<const T, id_common> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_common> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_common> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_common> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T>& p)
+ {
+ const P<T>& cr (p);
+ return persist<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ return persist<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const typename object_traits<T>::pointer_type& pobj)
+ {
+ return persist_<T, id_common> (pobj);
+ }
+
+ template <typename I>
+ inline void database::
+ persist (I b, I e, bool cont)
+ {
+ persist_<I, id_common> (b, e, cont);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ load (const typename object_traits<T>::id_type& id)
+ {
+ return load_<T, id_common> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ load (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return load_<T, id_common> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ load (T& obj, section& s)
+ {
+ return load_<T, id_common> (obj, s);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ find (const typename object_traits<T>::id_type& id)
+ {
+ return find_<T, id_common> (id);
+ }
+
+ template <typename T>
+ inline bool database::
+ find (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return find_<T, id_common> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T& obj)
+ {
+ reload_<T, id_common> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T* p)
+ {
+ reload<T> (*p);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (const P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (const P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T>
+ inline void database::
+ reload (const typename object_traits<T>::pointer_type& pobj)
+ {
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ reload (odb::pointer_traits<pointer_type>::get_ref (pobj));
+ }
+
+ template <typename T>
+ inline void database::
+ update (T& obj)
+ {
+ update_<T, id_common> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ update (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_common> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_common> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_common> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (P<T>& p)
+ {
+ const P<T>& cr (p);
+ update<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ update<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const typename object_traits<T>::pointer_type& pobj)
+ {
+ update_<T, id_common> (pobj);
+ }
+
+ template <typename I>
+ inline void database::
+ update (I b, I e, bool cont)
+ {
+ update_<I, id_common> (b, e, cont);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const T& obj, const section& s)
+ {
+ update_<T, id_common> (obj, s);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::id_type& id)
+ {
+ return erase_<T, id_common> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T& obj)
+ {
+ return erase_<T, id_common> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_common> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_common> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_common> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (P<T>& p)
+ {
+ const P<T>& cr (p);
+ erase<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ erase<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::pointer_type& pobj)
+ {
+ erase_<T, id_common> (pobj);
+ }
+
+ template <typename T, typename I>
+ inline void database::
+ erase (I idb, I ide, bool cont)
+ {
+ erase_id_<I, T, id_common> (idb, ide, cont);
+ }
+
+ template <typename I>
+ inline void database::
+ erase (I ob, I oe, bool cont)
+ {
+ erase_object_<I, id_common> (ob, oe, cont);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query ()
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (odb::query<T> ());
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const char* q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (odb::query<T> (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const std::string& q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (odb::query<T> (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const odb::query<T>& q)
+ {
+ // T is always object_type.
+ //
+ return object_traits_impl<T, id_common>::erase_query (*this, q);
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (bool cache)
+ {
+ return query<T> (odb::query<T> (), cache);
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const char* q, bool cache)
+ {
+ return query<T> (odb::query<T> (q), cache);
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const std::string& q, bool cache)
+ {
+ return query<T> (odb::query<T> (q), cache);
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one ()
+ {
+ return query_one<T> (odb::query<T> ());
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (T& o)
+ {
+ return query_one<T> (odb::query<T> (), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value ()
+ {
+ return query_value<T> (odb::query<T> ());
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const char* q)
+ {
+ return query_one<T> (odb::query<T> (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const char* q, T& o)
+ {
+ return query_one<T> (odb::query<T> (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const char* q)
+ {
+ return query_value<T> (odb::query<T> (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const std::string& q)
+ {
+ return query_one<T> (odb::query<T> (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const std::string& q, T& o)
+ {
+ return query_one<T> (odb::query<T> (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const std::string& q)
+ {
+ return query_value<T> (odb::query<T> (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const odb::query<T>& q, T& o)
+ {
+ return query_one_<T, id_common> (q, o);
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const odb::query<T>& q)
+ {
+ return query_one_<T, id_common> (q);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const odb::query<T>& q)
+ {
+ return query_value_<T, id_common> (q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, odb::query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, odb::query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const odb::query<T>& q)
+ {
+ connection_type& c (transaction::current ().connection (*this));
+ return c.prepare_query (n, q);
+ }
+
+ template <typename T>
+ inline void database::
+ cache_query (const prepared_query<T>& pq)
+ {
+ connection_type& c (transaction::current ().connection (*this));
+ c.cache_query (pq);
+ }
+
+#ifdef ODB_CXX11
+ template <typename T, typename P>
+ inline void database::
+ cache_query (const prepared_query<T>& pq, std::unique_ptr<P> params)
+ {
+ connection_type& c (transaction::current ().connection (*this));
+ c.cache_query (pq, std::move (params));
+ }
+#else
+ template <typename T, typename P>
+ inline void database::
+ cache_query (const prepared_query<T>& pq, std::auto_ptr<P> params)
+ {
+ connection_type& c (transaction::current ().connection (*this));
+ c.cache_query (pq, params);
+ }
+#endif
+
+ template <typename T>
+ inline prepared_query<T> database::
+ lookup_query (const char* name)
+ {
+ connection_type& c (transaction::current ().connection (*this));
+ return c.lookup_query<T> (name);
+ }
+
+ template <typename T, typename P>
+ inline prepared_query<T> database::
+ lookup_query (const char* name, P*& params)
+ {
+ connection_type& c (transaction::current ().connection (*this));
+ return c.lookup_query<T, P> (name, params);
+ }
+
+ // Implementations (i.e., the *_() functions).
+ //
+ template <typename I, database_id DB>
+ inline void database::
+ persist_ (I b, I e, bool cont)
+ {
+ // Sun CC with non-standard STL does not have iterator_traits.
+ //
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+ typedef typename std::iterator_traits<I>::value_type value_type;
+#else
+ // Assume iterator is just a pointer.
+ //
+ typedef typename object_pointer_traits<I>::object_type value_type;
+#endif
+
+ typedef object_pointer_traits<value_type> opt;
+
+ persist_<I, typename opt::object_type, id_common> (
+ b, e, cont, typename opt::result_type ());
+ }
+
+ template <typename T, database_id DB>
+ inline typename object_traits<T>::pointer_type database::
+ find_ (const typename object_traits<T>::id_type& id)
+ {
+ // T is always object_type.
+ //
+
+ // Compiler error pointing here? Perhaps the object doesn't have the
+ // default constructor?
+ //
+ return object_traits_impl<T, DB>::find (*this, id);
+ }
+
+ template <typename T, database_id DB>
+ inline bool database::
+ find_ (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ // T is always object_type.
+ //
+ return object_traits_impl<T, DB>::find (*this, id, obj);
+ }
+
+ template <typename T, database_id DB>
+ inline void database::
+ update_ (T& obj)
+ {
+ // T can be const T while object_type will always be T.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+
+ // Compiler error pointing here? Perhaps the object is readonly or
+ // doesn't have an object id? Such objects cannot be updated.
+ //
+ object_traits_impl<object_type, DB>::update (*this, obj);
+ }
+
+ template <typename T, database_id DB>
+ inline void database::
+ update_ (const typename object_traits<T>::pointer_type& pobj)
+ {
+ // T can be const T while object_type will always be T.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ T& obj (pointer_traits<pointer_type>::get_ref (pobj));
+
+ // Compiler error pointing here? Perhaps the object is readonly or
+ // doesn't have an object id? Such objects cannot be updated.
+ //
+ object_traits_impl<object_type, DB>::update (*this, obj);
+ }
+
+ template <typename T, database_id DB>
+ inline void database::
+ erase_ (const typename object_traits<T>::id_type& id)
+ {
+ // T is always object_type.
+ //
+ object_traits_impl<T, DB>::erase (*this, id);
+ }
+
+ template <typename T, database_id DB>
+ inline void database::
+ erase_ (T& obj)
+ {
+ // T can be const T while object_type will always be T.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+
+ object_traits_impl<object_type, DB>::erase (*this, obj);
+ }
+
+ template <typename T, database_id DB>
+ inline void database::
+ erase_ (const typename object_traits<T>::pointer_type& pobj)
+ {
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ erase_<T, DB> (pointer_traits<pointer_type>::get_ref (pobj));
+ }
+
+ template <typename T, database_id DB, typename Q>
+ inline typename result<T>::pointer_type database::
+ query_one_ (const Q& q)
+ {
+ result<T> r (query_<T, DB>::call (*this, q));
+
+ // We still have to cache the result since loading the object
+ // may result in loading of it's related objects and that would
+ // invalidate the result even for just getting the 'end' status.
+ //
+ r.cache ();
+
+ return r.one ();
+ }
+
+ template <typename T, database_id DB, typename Q>
+ inline bool database::
+ query_one_ (const Q& q, T& o)
+ {
+ result<T> r (query_<T, DB>::call (*this, q));
+ r.cache (); // See above.
+ return r.one (o);
+ }
+
+ template <typename T, database_id DB, typename Q>
+ inline T database::
+ query_value_ (const Q& q)
+ {
+ result<T> r (query_<T, DB>::call (*this, q));
+ r.cache (); // See above.
+
+ // Compiler error pointing here? The object must be default-constructible
+ // in order to use the return-by-value API.
+ //
+ T o;
+ r.value (o);
+ return o;
+ }
+
+ // execute()
+ //
+ inline unsigned long long database::
+ execute (const char* statement)
+ {
+ return execute (statement, std::strlen (statement));
+ }
+
+ inline unsigned long long database::
+ execute (const std::string& statement)
+ {
+ return execute (statement.c_str (), statement.size ());
+ }
+}
diff --git a/libodb/odb/database.txx b/libodb/odb/database.txx
new file mode 100644
index 0000000..5659b6f
--- /dev/null
+++ b/libodb/odb/database.txx
@@ -0,0 +1,491 @@
+// file : odb/database.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/section.hxx>
+#include <odb/exceptions.hxx>
+#include <odb/no-op-cache-traits.hxx>
+#include <odb/pointer-traits.hxx>
+
+namespace odb
+{
+ template <typename T>
+ result<T> database::
+ query (const odb::query<T>& q, bool cache)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ result<T> r (query_<T, id_common>::call (*this, q));
+
+ if (cache)
+ r.cache ();
+
+ return r;
+ }
+
+ // Implementations (i.e., the *_() functions).
+ //
+ template <typename T, database_id DB>
+ typename object_traits<T>::id_type database::
+ persist_ (T& obj)
+ {
+ // T can be const T while object_type will always be T.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+ typedef object_traits_impl<object_type, DB> object_traits;
+
+ object_traits::persist (*this, obj);
+
+ typename object_traits::reference_cache_traits::position_type p (
+ object_traits::reference_cache_traits::insert (
+ *this, reference_cache_type<T>::convert (obj)));
+
+ object_traits::reference_cache_traits::persist (p);
+
+ return object_traits::id (obj);
+ }
+
+ template <typename T, database_id DB>
+ typename object_traits<T>::id_type database::
+ persist_ (const typename object_traits<T>::pointer_type& pobj)
+ {
+ // T can be const T while object_type will always be T.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, DB> object_traits;
+
+ T& obj (pointer_traits<pointer_type>::get_ref (pobj));
+ object_traits::persist (*this, obj);
+
+ // Get the canonical object pointer and insert it into object cache.
+ //
+ typename object_traits::pointer_cache_traits::position_type p (
+ object_traits::pointer_cache_traits::insert (
+ *this, pointer_cache_type<pointer_type>::convert (pobj)));
+
+ object_traits::pointer_cache_traits::persist (p);
+
+ return object_traits::id (obj);
+ }
+
+ template <typename T, bool = object_traits<T>::auto_id> struct persist_type;
+ template <typename T> struct persist_type<T, true> {typedef T type;};
+ template <typename T> struct persist_type<T, false> {typedef const T type;};
+
+ template <typename I, typename T, database_id DB>
+ void database::
+ persist_ (I b, I e, bool cont, details::meta::no /*ptr*/)
+ {
+ // T can be const T while object_type will always be T.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+ typedef object_traits_impl<object_type, DB> object_traits;
+
+ multiple_exceptions mex (typeid (object_already_persistent));
+ try
+ {
+ while (b != e && (cont || mex.empty ()))
+ {
+ std::size_t n (0);
+ T* a[object_traits::batch]; // T instead of persist_type for cache.
+
+ for (; b != e && n < object_traits::batch; ++n)
+ {
+ // Compiler error pointing here? Perhaps the passed range is
+ // of const objects?
+ //
+ typename persist_type<object_type>::type* p (&(*b++));
+
+ a[n] = const_cast<T*> (p);
+ }
+
+ // Compiler error pointing here? Perhaps the object or the
+ // database does not support bulk operations?
+ //
+ object_traits::persist (
+ *this,
+ const_cast<typename persist_type<object_type>::type**> (a),
+ n,
+ mex);
+
+ if (mex.fatal ())
+ break;
+
+ for (std::size_t i (0); i < n; ++i)
+ {
+ if (mex[i] != 0) // Don't cache objects that have failed.
+ continue;
+
+ mex.current (i); // Set position in case the below code throws.
+
+ typename object_traits::reference_cache_traits::position_type p (
+ object_traits::reference_cache_traits::insert (
+ *this, reference_cache_type<T>::convert (*a[i])));
+
+ object_traits::reference_cache_traits::persist (p);
+ }
+
+ mex.delta (n);
+ }
+ }
+ catch (const odb::exception& ex)
+ {
+ mex.insert (ex, true);
+ }
+
+ if (!mex.empty ())
+ {
+ mex.prepare ();
+ throw mex;
+ }
+ }
+
+ template <typename P>
+ struct pointer_copy
+ {
+ const P* ref;
+ P copy;
+
+ void assign (const P& p) {ref = &p;}
+ template <typename P1> void assign (const P1& p1)
+ {
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const P& p (p1);
+
+ copy = p;
+ ref = &copy;
+ }
+ };
+
+ template <typename I, typename T, database_id DB>
+ void database::
+ persist_ (I b, I e, bool cont, details::meta::yes /*ptr*/)
+ {
+ // T can be const T while object_type will always be T.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, DB> object_traits;
+
+ multiple_exceptions mex (typeid (object_already_persistent));
+ try
+ {
+ while (b != e && (cont || mex.empty ()))
+ {
+ std::size_t n (0);
+ typename persist_type<object_type>::type* a[object_traits::batch];
+ pointer_copy<pointer_type> p[object_traits::batch];
+
+ for (; b != e && n < object_traits::batch; ++n)
+ {
+ p[n].assign (*b++);
+ a[n] = &pointer_traits<pointer_type>::get_ref (*p[n].ref);
+ }
+
+ // Compiler error pointing here? Perhaps the object or the
+ // database does not support bulk operations?
+ //
+ object_traits::persist (*this, a, n, mex);
+
+ if (mex.fatal ())
+ break;
+
+ for (std::size_t i (0); i < n; ++i)
+ {
+ if (mex[i] != 0) // Don't cache objects that have failed.
+ continue;
+
+ mex.current (i); // Set position in case the below code throws.
+
+ // Get the canonical object pointer and insert it into object cache.
+ //
+ typename object_traits::pointer_cache_traits::position_type pos (
+ object_traits::pointer_cache_traits::insert (
+ *this, pointer_cache_type<pointer_type>::convert (*p[i].ref)));
+
+ object_traits::pointer_cache_traits::persist (pos);
+ }
+
+ mex.delta (n);
+ }
+ }
+ catch (const odb::exception& ex)
+ {
+ mex.insert (ex, true);
+ }
+
+ if (!mex.empty ())
+ {
+ mex.prepare ();
+ throw mex;
+ }
+ }
+
+ template <typename T, database_id DB>
+ typename object_traits<T>::pointer_type database::
+ load_ (const typename object_traits<T>::id_type& id)
+ {
+ // T is always object_type.
+ //
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ pointer_type r (find_<T, DB> (id));
+
+ if (pointer_traits<pointer_type>::null_ptr (r))
+ throw object_not_persistent ();
+
+ return r;
+ }
+
+ template <typename T, database_id DB>
+ void database::
+ load_ (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ if (!find_<T, DB> (id, obj))
+ throw object_not_persistent ();
+ }
+
+ template <typename T, database_id DB>
+ void database::
+ load_ (T& obj, section& s)
+ {
+ connection_type& c (transaction::current ().connection (*this));
+
+ // T is always object_type.
+ //
+ if (object_traits_impl<T, DB>::load (c, obj, s))
+ s.reset (true, false); // Loaded, unchanged.
+ else
+ throw section_not_in_object ();
+ }
+
+ template <typename T, database_id DB>
+ void database::
+ reload_ (T& obj)
+ {
+ // T should be object_type (cannot be const). We also don't need to
+ // check for transaction here; object_traits::reload () does this.
+ //
+ if (!object_traits_impl<T, DB>::reload (*this, obj))
+ throw object_not_persistent ();
+ }
+
+ template <typename I, database_id DB>
+ void database::
+ update_ (I b, I e, bool cont)
+ {
+ // Sun CC with non-standard STL does not have iterator_traits.
+ //
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+ typedef typename std::iterator_traits<I>::value_type value_type;
+#else
+ // Assume iterator is just a pointer.
+ //
+ typedef typename object_pointer_traits<I>::object_type value_type;
+#endif
+
+ // object_pointer_traits<T>::object_type can be const.
+ //
+ typedef object_pointer_traits<value_type> opt;
+
+ typedef
+ typename object_traits<typename opt::object_type>::object_type
+ object_type;
+
+ typedef object_traits_impl<object_type, DB> object_traits;
+
+ multiple_exceptions mex (
+ object_traits::managed_optimistic_column_count == 0
+ ? typeid (object_not_persistent)
+ : typeid (object_changed));
+
+ try
+ {
+ while (b != e && (cont || mex.empty ()))
+ {
+ std::size_t n (0);
+ const object_type* a[object_traits::batch];
+
+ for (; b != e && n < object_traits::batch; ++n)
+ a[n] = &opt::get_ref (*b++);
+
+ // Compiler error pointing here? Perhaps the object or the
+ // database does not support bulk operations?
+ //
+ object_traits::update (*this, a, n, mex);
+
+ if (mex.fatal ())
+ break;
+
+ mex.delta (n);
+ }
+ }
+ catch (const odb::exception& ex)
+ {
+ mex.insert (ex, true);
+ }
+
+ if (!mex.empty ())
+ {
+ mex.prepare ();
+ throw mex;
+ }
+ }
+
+ template <typename T, database_id DB>
+ void database::
+ update_ (const T& obj, const section& s)
+ {
+ if (!s.loaded ())
+ throw section_not_loaded ();
+
+ transaction& t (transaction::current ());
+
+ // T is always object_type.
+ //
+ if (object_traits_impl<T, DB>::update (t.connection (*this), obj, s))
+ {
+ if (s.changed ())
+ s.reset (true, false, &t); // Clear the change flag.
+ }
+ else
+ throw section_not_in_object ();
+ }
+
+ template <typename I, typename T, database_id DB>
+ void database::
+ erase_id_ (I b, I e, bool cont)
+ {
+ // T is explicitly specified by the caller, so assume it is object type.
+ //
+ typedef T object_type;
+ typedef object_traits_impl<object_type, DB> object_traits;
+ typedef typename object_traits::id_type id_type;
+
+ multiple_exceptions mex (typeid (object_not_persistent));
+ try
+ {
+ while (b != e && (cont || mex.empty ()))
+ {
+ std::size_t n (0);
+ const id_type* a[object_traits::batch];
+
+ for (; b != e && n < object_traits::batch; ++n)
+ // Compiler error pointing here? Perhaps the object id type
+ // and the range element type don't match?
+ //
+ a[n] = &(*b++);
+
+ // Compiler error pointing here? Perhaps the object or the
+ // database does not support bulk operations?
+ //
+ object_traits::erase (*this, a, n, mex);
+
+ if (mex.fatal ())
+ break;
+
+ mex.delta (n);
+ }
+ }
+ catch (const odb::exception& ex)
+ {
+ mex.insert (ex, true);
+ }
+
+ if (!mex.empty ())
+ {
+ mex.prepare ();
+ throw mex;
+ }
+ }
+
+ template <typename I, database_id DB>
+ void database::
+ erase_object_ (I b, I e, bool cont)
+ {
+ // Sun CC with non-standard STL does not have iterator_traits.
+ //
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+ typedef typename std::iterator_traits<I>::value_type value_type;
+#else
+ // Assume iterator is just a pointer.
+ //
+ typedef typename object_pointer_traits<I>::object_type value_type;
+#endif
+
+ // object_pointer_traits<T>::object_type can be const.
+ //
+ typedef object_pointer_traits<value_type> opt;
+
+ typedef
+ typename object_traits<typename opt::object_type>::object_type
+ object_type;
+
+ typedef object_traits_impl<object_type, DB> object_traits;
+
+ multiple_exceptions mex (
+ object_traits::managed_optimistic_column_count == 0
+ ? typeid (object_not_persistent)
+ : typeid (object_changed));
+
+ try
+ {
+ while (b != e && (cont || mex.empty ()))
+ {
+ std::size_t n (0);
+ const object_type* a[object_traits::batch];
+
+ for (; b != e && n < object_traits::batch; ++n)
+ a[n] = &opt::get_ref (*b++);
+
+ // Compiler error pointing here? Perhaps the object or the
+ // database does not support bulk operations?
+ //
+ object_traits::erase (*this, a, n, mex);
+
+ if (mex.fatal ())
+ break;
+
+ mex.delta (n);
+ }
+ }
+ catch (const odb::exception& ex)
+ {
+ mex.insert (ex, true);
+ }
+
+ if (!mex.empty ())
+ {
+ mex.prepare ();
+ throw mex;
+ }
+ }
+
+ template <typename T, database_id DB>
+ struct database::query_<T, DB, class_object>
+ {
+ template <typename Q>
+ static result<T>
+ call (database& db, const Q& q)
+ {
+ return object_traits_impl<T, DB>::query (db, q);
+ }
+ };
+
+ template <typename T, database_id DB>
+ struct database::query_<T, DB, class_view>
+ {
+ template <typename Q>
+ static result<T>
+ call (database& db, const Q& q)
+ {
+ return view_traits_impl<T, DB>::query (db, q);
+ }
+ };
+}
diff --git a/libodb/odb/details/buffer.cxx b/libodb/odb/details/buffer.cxx
new file mode 100644
index 0000000..595329e
--- /dev/null
+++ b/libodb/odb/details/buffer.cxx
@@ -0,0 +1,35 @@
+// file : odb/details/buffer.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memcpy
+
+#include <odb/details/buffer.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace details
+ {
+ void basic_buffer_base::
+ capacity (size_t c, size_t data_size)
+ {
+ if (c > capacity_)
+ {
+ size_t n (capacity_ * 2 > c ? capacity_ * 2 : c);
+ void* d (operator new (n));
+
+ if (data_ != 0)
+ {
+ if (data_size != 0)
+ memcpy (d, data_, data_size);
+
+ operator delete (data_);
+ }
+
+ data_ = d;
+ capacity_ = n;
+ }
+ }
+ }
+}
diff --git a/libodb/odb/details/buffer.hxx b/libodb/odb/details/buffer.hxx
new file mode 100644
index 0000000..558be9b
--- /dev/null
+++ b/libodb/odb/details/buffer.hxx
@@ -0,0 +1,92 @@
+// file : odb/details/buffer.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BUFFER_DETAILS_HXX
+#define ODB_BUFFER_DETAILS_HXX
+
+#include <odb/pre.hxx>
+
+#include <new>
+#include <cstddef> // std::size_t
+
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class LIBODB_EXPORT basic_buffer_base
+ {
+ public:
+ ~basic_buffer_base ()
+ {
+ if (data_ != 0)
+ operator delete (data_);
+ }
+
+ basic_buffer_base (std::size_t capacity)
+ : capacity_ (capacity)
+ {
+ data_ = capacity_ == 0 ? 0 : operator new (capacity_);
+ }
+
+ std::size_t
+ capacity () const
+ {
+ return capacity_;
+ }
+
+ void
+ capacity (std::size_t, std::size_t data_size = 0);
+
+ protected:
+ void* data_;
+ std::size_t capacity_;
+ };
+
+ template <typename T>
+ class basic_buffer: public basic_buffer_base
+ {
+ public:
+ basic_buffer (std::size_t capacity = 256)
+ : basic_buffer_base (capacity)
+ {
+ }
+
+ T*
+ data ()
+ {
+ return static_cast<T*> (data_);
+ }
+
+ const T*
+ data () const
+ {
+ return static_cast<T*> (data_);
+ }
+
+ // Note that strictly speaking the return type should be void* const*
+ // but that would make using this function too awkward since we often
+ // store the result as void*.
+ //
+ void**
+ data_ptr ()
+ {
+ return &data_;
+ }
+
+ const void* const*
+ data_ptr () const
+ {
+ return &data_;
+ }
+ };
+
+ typedef basic_buffer<char> buffer;
+ typedef basic_buffer<unsigned char> ubuffer;
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_BUFFER_DETAILS_HXX
diff --git a/libodb/odb/details/c-string.hxx b/libodb/odb/details/c-string.hxx
new file mode 100644
index 0000000..6ab1adc
--- /dev/null
+++ b/libodb/odb/details/c-string.hxx
@@ -0,0 +1,28 @@
+// file : odb/details/c-string.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_C_STRING_HXX
+#define ODB_DETAILS_C_STRING_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstring>
+
+namespace odb
+{
+ namespace details
+ {
+ struct c_string_comparator
+ {
+ bool
+ operator() (const char* x, const char* y) const
+ {
+ return std::strcmp (x, y) < 0;
+ }
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_C_STRING_HXX
diff --git a/libodb/odb/details/condition.cxx b/libodb/odb/details/condition.cxx
new file mode 100644
index 0000000..2c4cbdb
--- /dev/null
+++ b/libodb/odb/details/condition.cxx
@@ -0,0 +1,13 @@
+// file : odb/details/condition.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/condition.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ // This otherwise unnecessary file is here to allow instantiation
+ // of inline functions for exporting.
+ }
+}
diff --git a/libodb/odb/details/condition.hxx b/libodb/odb/details/condition.hxx
new file mode 100644
index 0000000..10b6b4a
--- /dev/null
+++ b/libodb/odb/details/condition.hxx
@@ -0,0 +1,68 @@
+// file : odb/details/condition.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_CONDITION_HXX
+#define ODB_DETAILS_CONDITION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx>
+
+#ifdef ODB_THREADS_NONE
+
+namespace odb
+{
+ namespace details
+ {
+ class mutex;
+ class lock;
+
+ class condition
+ {
+ public:
+ condition (mutex&) {}
+
+ void
+ signal () {}
+
+ void
+ wait (lock&) {}
+
+ private:
+ condition (const condition&);
+ condition& operator= (const condition&);
+ };
+ }
+}
+
+#elif defined(ODB_THREADS_CXX11)
+# include <condition_variable>
+# include <odb/details/mutex.hxx>
+# include <odb/details/lock.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class condition: public std::condition_variable
+ {
+ public:
+ condition (mutex&) {}
+
+ void
+ signal () {notify_one ();}
+ };
+ }
+}
+
+#elif defined(ODB_THREADS_POSIX)
+#include <odb/details/posix/condition.hxx>
+#elif defined(ODB_THREADS_WIN32)
+#include <odb/details/win32/condition.hxx>
+#else
+# error unknown threading model
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_CONDITION_HXX
diff --git a/libodb/odb/details/config-vc.h b/libodb/odb/details/config-vc.h
new file mode 100644
index 0000000..7c4def0
--- /dev/null
+++ b/libodb/odb/details/config-vc.h
@@ -0,0 +1,23 @@
+/* file : odb/details/config-vc.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+/* Configuration file for Windows/VC++ for the build2 build.
+ *
+ * Note that currently we only support ODB_THREADS_NONE and ODB_THREADS_CXX11
+ * but could also support the _WIN32 variant with a bit of effort.
+ *
+ */
+
+#ifndef ODB_DETAILS_CONFIG_VC_H
+#define ODB_DETAILS_CONFIG_VC_H
+
+#ifndef ODB_THREADS_NONE
+# if _MSC_VER >= 1900
+# define ODB_THREADS_CXX11
+# else
+# error Unsupoprted MSVC version (no thread_local)
+# endif
+#endif
+
+#endif /* ODB_DETAILS_CONFIG_VC_H */
diff --git a/libodb/odb/details/config.h b/libodb/odb/details/config.h
new file mode 100644
index 0000000..4ad9a8d
--- /dev/null
+++ b/libodb/odb/details/config.h
@@ -0,0 +1,18 @@
+/* file : odb/details/config.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+/* Static configuration file for build2 build.
+ *
+ * Note that currently we only support ODB_THREADS_NONE and ODB_THREADS_CXX11
+ * but could also support the _POSIX and _WIN32 variants with a bit of effort.
+ */
+
+#ifndef ODB_DETAILS_CONFIG_H
+#define ODB_DETAILS_CONFIG_H
+
+#ifndef ODB_THREADS_NONE
+# define ODB_THREADS_CXX11
+#endif
+
+#endif /* ODB_DETAILS_CONFIG_H */
diff --git a/libodb/odb/details/config.hxx b/libodb/odb/details/config.hxx
new file mode 100644
index 0000000..3168109
--- /dev/null
+++ b/libodb/odb/details/config.hxx
@@ -0,0 +1,74 @@
+// file : odb/details/config.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_CONFIG_HXX
+#define ODB_DETAILS_CONFIG_HXX
+
+// no pre
+
+// C++11 support.
+//
+#ifdef _MSC_VER
+# if _MSC_VER >= 1600 // VC++10 and later have C++11 always enabled.
+# define ODB_CXX11
+# define ODB_CXX11_NULLPTR
+# if _MSC_VER >= 1700
+# define ODB_CXX11_ENUM
+# if _MSC_VER >= 1800
+# define ODB_CXX11_DELETED_FUNCTION
+# define ODB_CXX11_EXPLICIT_CONVERSION_OPERATOR
+# define ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+# define ODB_CXX11_VARIADIC_TEMPLATE
+# define ODB_CXX11_INITIALIZER_LIST
+# if _MSC_VER >= 1900
+# define ODB_CXX11_NOEXCEPT
+# endif
+# endif
+# endif
+# endif
+#else
+# if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L
+# define ODB_CXX11
+# ifdef __clang__ // Pretends to be a really old __GNUC__ on some platforms.
+# define ODB_CXX11_NULLPTR
+# define ODB_CXX11_NOEXCEPT
+# elif defined(__GNUC__)
+# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4
+# define ODB_CXX11_NULLPTR
+# define ODB_CXX11_NOEXCEPT
+# endif
+# else
+# define ODB_CXX11_NULLPTR
+# define ODB_CXX11_NOEXCEPT
+# endif
+# define ODB_CXX11_DELETED_FUNCTION
+# define ODB_CXX11_EXPLICIT_CONVERSION_OPERATOR
+# define ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+# define ODB_CXX11_VARIADIC_TEMPLATE
+# define ODB_CXX11_INITIALIZER_LIST
+# define ODB_CXX11_ENUM // GCC 4.4 (forward -- 4.6), Clang 2.9 (3.1).
+# endif
+#endif
+
+#ifdef ODB_CXX11_NOEXCEPT
+# define ODB_NOTHROW_NOEXCEPT noexcept
+#else
+# define ODB_NOTHROW_NOEXCEPT throw()
+#endif
+
+// Once we drop support for C++98, we can probably get rid of config.h except
+// for the autotools case by fixing ODB_THREADS_CXX11 (and perhaps supporting
+// the ODB_THREADS_NONE case via a "global" (command line) define).
+//
+#ifdef ODB_COMPILER
+# define ODB_THREADS_NONE
+# define LIBODB_STATIC
+#elif defined(_MSC_VER)
+# include <odb/details/config-vc.h>
+#else
+# include <odb/details/config.h>
+#endif
+
+// no post
+
+#endif // ODB_DETAILS_CONFIG_HXX
diff --git a/libodb/odb/details/exception.hxx b/libodb/odb/details/exception.hxx
new file mode 100644
index 0000000..ab838e1
--- /dev/null
+++ b/libodb/odb/details/exception.hxx
@@ -0,0 +1,21 @@
+// file : odb/details/exception.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_EXCEPTION_HXX
+#define ODB_DETAILS_EXCEPTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/exception.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ struct exception: odb::exception {};
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_EXCEPTION_HXX
diff --git a/libodb/odb/details/export.hxx b/libodb/odb/details/export.hxx
new file mode 100644
index 0000000..2ddc104
--- /dev/null
+++ b/libodb/odb/details/export.hxx
@@ -0,0 +1,46 @@
+// file : odb/details/export.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_EXPORT_HXX
+#define ODB_DETAILS_EXPORT_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // LIBODB_STATIC if ODB_COMPILER
+
+// Normally we don't export class templates (but do complete specializations),
+// inline functions, and classes with only inline member functions. Exporting
+// classes that inherit from non-exported/imported bases (e.g., std::string)
+// will end up badly. The only known workarounds are to not inherit or to not
+// export. Also, MinGW GCC doesn't like seeing non-exported function being
+// used before their inline definition. The workaround is to reorder code. In
+// the end it's all trial and error.
+
+#if defined(LIBODB_STATIC) // Using static.
+# define LIBODB_EXPORT
+#elif defined(LIBODB_STATIC_BUILD) // Building static.
+# define LIBODB_EXPORT
+#elif defined(LIBODB_SHARED) // Using shared.
+# ifdef _WIN32
+# define LIBODB_EXPORT __declspec(dllimport)
+# else
+# define LIBODB_EXPORT
+# endif
+#elif defined(LIBODB_SHARED_BUILD) // Building shared.
+# ifdef _WIN32
+# define LIBODB_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_EXPORT
+# endif
+#else
+// If none of the above macros are defined, then we assume we are being used
+// by some third-party build system that cannot/doesn't signal the library
+// type. Note that this fallback works for both static and shared but in case
+// of shared will be sub-optimal compared to having dllimport.
+//
+# define LIBODB_EXPORT // Using static or shared.
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_EXPORT_HXX
diff --git a/libodb/odb/details/function-wrapper.hxx b/libodb/odb/details/function-wrapper.hxx
new file mode 100644
index 0000000..418a625
--- /dev/null
+++ b/libodb/odb/details/function-wrapper.hxx
@@ -0,0 +1,90 @@
+// file : odb/details/function-wrapper.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_FUNCTION_WRAPPER_HXX
+#define ODB_DETAILS_FUNCTION_WRAPPER_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#ifdef ODB_CXX11
+# include <functional> // std::function
+# include <type_traits> // std::enable_if, std::is_convertible
+#endif
+
+namespace odb
+{
+ namespace details
+ {
+ // Low-level 'callable object' wrapper similar to std::function but
+ // that works in both C++98 and 11. In particular, the call site can
+ // be compiled in C++98 and the registration site in C++11 and it
+ // will work.
+ //
+ template <typename F>
+ struct function_wrapper
+ {
+ ~function_wrapper ();
+
+ explicit
+ function_wrapper (F* = 0);
+
+#ifdef ODB_CXX11
+ typedef typename std::function<F> std_function_type;
+
+ // This overload accepts lambdas and std::functions, but when the
+ // argument is convertible to F*, then we disable it in favor of the
+ // other overload (above), which is more efficient.
+ //
+ // Subtlety alert: if you're thinking of changing this to accept a
+ // std::function<F> argument, stop. That creates an overload ambiguity
+ // when the actual parameter is a lambda, which is convertible to either
+ // std::function<F> or F*.
+ //
+ template <typename F1>
+ function_wrapper(F1,
+ typename std::enable_if<
+ !std::is_convertible<F1, F*>::value>::type* = 0);
+#endif
+
+ // Destructive copy construction and assignment (aka move). These
+ // should really only be called by containers when they need to
+ // reallocate the underlying buffer and move the elements.
+ //
+ function_wrapper (const function_wrapper<F>&);
+ function_wrapper&
+ operator= (const function_wrapper<F>&);
+
+ void swap (function_wrapper<F>&);
+
+ // Cleanly cast to an incompatible function type.
+ //
+ template <typename R> R
+ cast () const;
+
+ // Conversion to bool.
+ //
+ public:
+ typedef void (function_wrapper<F>::*bool_convertible) ();
+ void true_value () {}
+
+ operator bool_convertible () const
+ {
+ return function != 0 ? &function_wrapper<F>::true_value : 0;
+ }
+
+ public:
+ F* function;
+ void (*deleter) (const void*);
+ const void* std_function;
+ };
+ }
+}
+
+#include <odb/details/function-wrapper.ixx>
+#include <odb/details/function-wrapper.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_FUNCTION_WRAPPER_HXX
diff --git a/libodb/odb/details/function-wrapper.ixx b/libodb/odb/details/function-wrapper.ixx
new file mode 100644
index 0000000..5b83b96
--- /dev/null
+++ b/libodb/odb/details/function-wrapper.ixx
@@ -0,0 +1,49 @@
+// file : odb/details/function-wrapper.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename F>
+ inline function_wrapper<F>::
+ ~function_wrapper ()
+ {
+ if (deleter != 0)
+ deleter (std_function);
+ }
+
+ template <typename F>
+ inline function_wrapper<F>::
+ function_wrapper (F* f)
+ : function (f), deleter (0), std_function (0)
+ {
+ }
+
+ template <typename F>
+ inline function_wrapper<F>::
+ function_wrapper (const function_wrapper<F>& x)
+ : function (0), deleter (0), std_function (0)
+ {
+ swap (const_cast<function_wrapper<F>&> (x));
+ }
+
+ template <typename F>
+ inline function_wrapper<F>& function_wrapper<F>::
+ operator= (const function_wrapper<F>& x)
+ {
+ swap (const_cast<function_wrapper<F>&> (x));
+ return *this;
+ }
+
+ template <typename F>
+ template <typename R>
+ inline R function_wrapper<F>::
+ cast () const
+ {
+ union { F* f; R r; } r;
+ r.f = function;
+ return r.r;
+ }
+ }
+}
diff --git a/libodb/odb/details/function-wrapper.txx b/libodb/odb/details/function-wrapper.txx
new file mode 100644
index 0000000..db73e8d
--- /dev/null
+++ b/libodb/odb/details/function-wrapper.txx
@@ -0,0 +1,89 @@
+// file : odb/details/function-wrapper.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <utility> // std::swap, std::move
+
+namespace odb
+{
+ namespace details
+ {
+#ifdef ODB_CXX11
+ template <typename F>
+ struct caller_impl;
+
+#ifdef ODB_CXX11_VARIADIC_TEMPLATE
+ template <typename R, typename... A>
+ struct caller_impl<R (A...)>
+ {
+ static R
+ function (const void* f, A... a)
+ {
+ return (*static_cast<const std::function<R (A...)>*> (f)) (a...);
+ }
+ };
+#else
+ template <typename R, typename A1>
+ struct caller_impl<R (A1)>
+ {
+ static R
+ function (const void* f, A1 a1)
+ {
+ return (*static_cast<const std::function<R (A1)>*> (f)) (a1);
+ }
+ };
+
+ template <typename R, typename A1, typename A2>
+ struct caller_impl<R (A1, A2)>
+ {
+ static R
+ function (const void* f, A1 a1, A2 a2)
+ {
+ return (*static_cast<const std::function<R (A1, A2)>*> (f)) (a1, a2);
+ }
+ };
+#endif
+
+ template <typename F>
+ void
+ deleter_impl (const void* f)
+ {
+ delete static_cast<const std::function<F>*> (f);
+ }
+
+ template <typename F>
+ template <typename F1>
+ function_wrapper<F>::
+ function_wrapper (
+ F1 f,
+ typename std::enable_if<!std::is_convertible<F1, F*>::value>::type*)
+ {
+ std_function_type sf (std::move (f));
+
+ if (F* const* const f = sf.template target<F*> ())
+ {
+ function = *f;
+ deleter = 0;
+ std_function = 0;
+ }
+ else
+ {
+ function_wrapper<decltype (caller_impl<F>::function)> fw (
+ &caller_impl<F>::function);
+
+ function = fw.template cast<F*> ();
+ deleter = &deleter_impl<F>;
+ std_function = new std_function_type (std::move (sf));
+ }
+ }
+#endif
+
+ template <typename F>
+ void function_wrapper<F>::
+ swap (function_wrapper<F>& x)
+ {
+ std::swap (function, x.function);
+ std::swap (deleter, x.deleter);
+ std::swap (std_function, x.std_function);
+ }
+ }
+}
diff --git a/libodb/odb/details/lock.cxx b/libodb/odb/details/lock.cxx
new file mode 100644
index 0000000..f474bf5
--- /dev/null
+++ b/libodb/odb/details/lock.cxx
@@ -0,0 +1,13 @@
+// file : odb/details/lock.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/lock.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ // This otherwise unnecessary file is here to allow instantiation
+ // of inline functions for exporting.
+ }
+}
diff --git a/libodb/odb/details/lock.hxx b/libodb/odb/details/lock.hxx
new file mode 100644
index 0000000..0c54f03
--- /dev/null
+++ b/libodb/odb/details/lock.hxx
@@ -0,0 +1,59 @@
+// file : odb/details/lock.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_LOCK_HXX
+#define ODB_DETAILS_LOCK_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/mutex.hxx>
+
+#ifdef ODB_THREADS_CXX11
+# include <mutex>
+namespace odb
+{
+ namespace details
+ {
+ using lock = std::unique_lock<mutex>;
+ }
+}
+#else
+namespace odb
+{
+ namespace details
+ {
+ class lock
+ {
+ public:
+ lock (mutex& m)
+ : mutex_ (&m)
+ {
+ mutex_->lock ();
+ }
+
+ ~lock ()
+ {
+ if (mutex_ != 0)
+ mutex_->unlock ();
+ }
+
+ void
+ unlock ()
+ {
+ if (mutex_ != 0)
+ {
+ mutex_->unlock ();
+ mutex_ = 0;
+ }
+ }
+
+ private:
+ mutex* mutex_;
+ };
+ }
+}
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_LOCK_HXX
diff --git a/libodb/odb/details/meta/answer.hxx b/libodb/odb/details/meta/answer.hxx
new file mode 100644
index 0000000..f15dc43
--- /dev/null
+++ b/libodb/odb/details/meta/answer.hxx
@@ -0,0 +1,30 @@
+// file : odb/details/meta/answer.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_META_ANSWER_HXX
+#define ODB_DETAILS_META_ANSWER_HXX
+
+#include <odb/pre.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ namespace meta
+ {
+ struct yes
+ {
+ char filling;
+ };
+
+ struct no
+ {
+ char filling[2];
+ };
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_META_ANSWER_HXX
diff --git a/libodb/odb/details/meta/class-p.hxx b/libodb/odb/details/meta/class-p.hxx
new file mode 100644
index 0000000..bddb452
--- /dev/null
+++ b/libodb/odb/details/meta/class-p.hxx
@@ -0,0 +1,34 @@
+// file : odb/details/meta/class-p.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_META_CLASS_HXX
+#define ODB_DETAILS_META_CLASS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/meta/answer.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ namespace meta
+ {
+ // g++ cannot have these inside class_p.
+ //
+ template <typename X> no class_p_test (...);
+ template <typename X> yes class_p_test (void (X::*) ());
+
+ template <typename X>
+ struct class_p
+ {
+ static const bool result =
+ sizeof (class_p_test<X> (0)) == sizeof (yes);
+ };
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_META_CLASS_HXX
diff --git a/libodb/odb/details/meta/polymorphic-p.hxx b/libodb/odb/details/meta/polymorphic-p.hxx
new file mode 100644
index 0000000..10fef6a
--- /dev/null
+++ b/libodb/odb/details/meta/polymorphic-p.hxx
@@ -0,0 +1,57 @@
+// file : odb/details/meta/polymorphic-p.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_META_POLYMORPHIC_HXX
+#define ODB_DETAILS_META_POLYMORPHIC_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+#include <odb/details/meta/class-p.hxx>
+#include <odb/details/meta/remove-const-volatile.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ namespace meta
+ {
+ template <typename CVX>
+ struct polymorphic_p
+ {
+ typedef typename remove_const_volatile<CVX>::result X;
+
+ template <typename Y, bool C>
+ struct impl
+ {
+ static const bool result = false;
+ };
+
+ template <typename Y>
+ struct impl<Y, true>
+ {
+ struct t1: Y
+ {
+ t1 ();
+ };
+
+ struct t2: Y
+ {
+ t2 ();
+
+ virtual
+ ~t2 () ODB_NOTHROW_NOEXCEPT;
+ };
+
+ static const bool result = sizeof (t1) == sizeof (t2);
+ };
+
+ static const bool result = impl<X, class_p<X>::result>::result;
+ };
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_META_POLYMORPHIC_HXX
diff --git a/libodb/odb/details/meta/remove-const-volatile.hxx b/libodb/odb/details/meta/remove-const-volatile.hxx
new file mode 100644
index 0000000..910ec35
--- /dev/null
+++ b/libodb/odb/details/meta/remove-const-volatile.hxx
@@ -0,0 +1,31 @@
+// file : odb/details/meta/remove-const-volatile.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_META_REMOVE_CONST_VOLATILE_HXX
+#define ODB_DETAILS_META_REMOVE_CONST_VOLATILE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/meta/remove-const.hxx>
+#include <odb/details/meta/remove-volatile.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ namespace meta
+ {
+ template <typename X>
+ struct remove_const_volatile
+ {
+ typedef
+ typename remove_volatile<typename remove_const<X>::result>::result
+ result;
+ };
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_META_REMOVE_CONST_VOLATILE_HXX
diff --git a/libodb/odb/details/meta/remove-const.hxx b/libodb/odb/details/meta/remove-const.hxx
new file mode 100644
index 0000000..4a92ed3
--- /dev/null
+++ b/libodb/odb/details/meta/remove-const.hxx
@@ -0,0 +1,32 @@
+// file : odb/details/meta/remove-const.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_META_REMOVE_CONST_HXX
+#define ODB_DETAILS_META_REMOVE_CONST_HXX
+
+#include <odb/pre.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ namespace meta
+ {
+ template <typename X>
+ struct remove_const
+ {
+ typedef X result;
+ };
+
+ template <typename X>
+ struct remove_const<const X>
+ {
+ typedef X result;
+ };
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_META_REMOVE_CONST_HXX
diff --git a/libodb/odb/details/meta/remove-pointer.hxx b/libodb/odb/details/meta/remove-pointer.hxx
new file mode 100644
index 0000000..9963fd7
--- /dev/null
+++ b/libodb/odb/details/meta/remove-pointer.hxx
@@ -0,0 +1,32 @@
+// file : odb/details/meta/remove-pointer.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_META_REMOVE_POINTER_HXX
+#define ODB_DETAILS_META_REMOVE_POINTER_HXX
+
+#include <odb/pre.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ namespace meta
+ {
+ template <typename X>
+ struct remove_pointer
+ {
+ typedef X result;
+ };
+
+ template <typename X>
+ struct remove_pointer<X*>
+ {
+ typedef X result;
+ };
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_META_REMOVE_POINTER_HXX
diff --git a/libodb/odb/details/meta/remove-volatile.hxx b/libodb/odb/details/meta/remove-volatile.hxx
new file mode 100644
index 0000000..877e532
--- /dev/null
+++ b/libodb/odb/details/meta/remove-volatile.hxx
@@ -0,0 +1,32 @@
+// file : odb/details/meta/remove-volatile.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_META_REMOVE_VOLATILE_HXX
+#define ODB_DETAILS_META_REMOVE_VOLATILE_HXX
+
+#include <odb/pre.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ namespace meta
+ {
+ template <typename X>
+ struct remove_volatile
+ {
+ typedef X result;
+ };
+
+ template <typename X>
+ struct remove_volatile<volatile X>
+ {
+ typedef X result;
+ };
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_META_REMOVE_VOLATILE_HXX
diff --git a/libodb/odb/details/meta/static-assert.hxx b/libodb/odb/details/meta/static-assert.hxx
new file mode 100644
index 0000000..a2cc81b
--- /dev/null
+++ b/libodb/odb/details/meta/static-assert.hxx
@@ -0,0 +1,32 @@
+// file : odb/details/meta/static-assert.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_META_STATIC_ASSERT_HXX
+#define ODB_DETAILS_META_STATIC_ASSERT_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#ifndef ODB_CXX11
+
+namespace odb
+{
+ namespace details
+ {
+ namespace meta
+ {
+ template <bool>
+ struct static_assert_test;
+
+ template <>
+ struct static_assert_test<true> {};
+ }
+ }
+}
+
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_META_STATIC_ASSERT_HXX
diff --git a/libodb/odb/details/mutex.cxx b/libodb/odb/details/mutex.cxx
new file mode 100644
index 0000000..df367d8
--- /dev/null
+++ b/libodb/odb/details/mutex.cxx
@@ -0,0 +1,13 @@
+// file : odb/details/mutex.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/mutex.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ // This otherwise unnecessary file is here to allow instantiation
+ // of inline functions for exporting.
+ }
+}
diff --git a/libodb/odb/details/mutex.hxx b/libodb/odb/details/mutex.hxx
new file mode 100644
index 0000000..df12013
--- /dev/null
+++ b/libodb/odb/details/mutex.hxx
@@ -0,0 +1,53 @@
+// file : odb/details/mutex.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_MUTEX_HXX
+#define ODB_DETAILS_MUTEX_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx>
+
+#ifdef ODB_THREADS_NONE
+
+namespace odb
+{
+ namespace details
+ {
+ class mutex
+ {
+ public:
+ mutex () {}
+
+ void
+ lock () {}
+
+ void
+ unlock () {}
+
+ private:
+ mutex (const mutex&);
+ mutex& operator= (const mutex&);
+ };
+ }
+}
+#elif defined(ODB_THREADS_CXX11)
+# include <mutex>
+namespace odb
+{
+ namespace details
+ {
+ using std::mutex;
+ }
+}
+#elif defined(ODB_THREADS_POSIX)
+#include <odb/details/posix/mutex.hxx>
+#elif defined(ODB_THREADS_WIN32)
+#include <odb/details/win32/mutex.hxx>
+#else
+# error unknown threading model
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_MUTEX_HXX
diff --git a/libodb/odb/details/posix/condition.hxx b/libodb/odb/details/posix/condition.hxx
new file mode 100644
index 0000000..4f7c42a
--- /dev/null
+++ b/libodb/odb/details/posix/condition.hxx
@@ -0,0 +1,47 @@
+// file : odb/details/posix/condition.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_POSIX_CONDITION_HXX
+#define ODB_DETAILS_POSIX_CONDITION_HXX
+
+#include <odb/pre.hxx>
+
+#include <pthread.h>
+
+#include <odb/details/export.hxx>
+#include <odb/details/posix/mutex.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class lock;
+
+ class LIBODB_EXPORT condition
+ {
+ public:
+ ~condition ();
+ condition (mutex&);
+
+ void
+ signal ();
+
+ void
+ wait (lock&);
+
+ private:
+ condition (const condition&);
+ condition& operator= (const condition&);
+
+ private:
+ mutex& mutex_;
+ pthread_cond_t cond_;
+ };
+ }
+}
+
+#include <odb/details/posix/condition.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_POSIX_CONDITION_HXX
diff --git a/libodb/odb/details/posix/condition.ixx b/libodb/odb/details/posix/condition.ixx
new file mode 100644
index 0000000..9b68d9f
--- /dev/null
+++ b/libodb/odb/details/posix/condition.ixx
@@ -0,0 +1,38 @@
+// file : odb/details/posix/condition.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/posix/exceptions.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ inline condition::
+ ~condition ()
+ {
+ pthread_cond_destroy (&cond_);
+ }
+
+ inline condition::
+ condition (mutex& mutex)
+ : mutex_ (mutex)
+ {
+ if (int e = pthread_cond_init (&cond_, 0))
+ throw posix_exception (e);
+ }
+
+ inline void condition::
+ signal ()
+ {
+ if (int e = pthread_cond_signal (&cond_))
+ throw posix_exception (e);
+ }
+
+ inline void condition::
+ wait (lock&)
+ {
+ if (int e = pthread_cond_wait (&cond_, &mutex_.mutex_))
+ throw posix_exception (e);
+ }
+ }
+}
diff --git a/libodb/odb/details/posix/exceptions.cxx b/libodb/odb/details/posix/exceptions.cxx
new file mode 100644
index 0000000..c346655
--- /dev/null
+++ b/libodb/odb/details/posix/exceptions.cxx
@@ -0,0 +1,22 @@
+// file : odb/details/posix/exceptions.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/posix/exceptions.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ const char* posix_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "POSIX API error";
+ }
+
+ posix_exception* posix_exception::
+ clone () const
+ {
+ return new posix_exception (*this);
+ }
+ }
+}
diff --git a/libodb/odb/details/posix/exceptions.hxx b/libodb/odb/details/posix/exceptions.hxx
new file mode 100644
index 0000000..aff33b6
--- /dev/null
+++ b/libodb/odb/details/posix/exceptions.hxx
@@ -0,0 +1,38 @@
+// file : odb/details/posix/exceptions.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_POSIX_EXCEPTIONS_HXX
+#define ODB_DETAILS_POSIX_EXCEPTIONS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+#include <odb/details/export.hxx>
+#include <odb/details/exception.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ struct LIBODB_EXPORT posix_exception: details::exception
+ {
+ posix_exception (int code) : code_ (code) {}
+
+ int
+ code () const {return code_;}
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual posix_exception*
+ clone () const;
+
+ private:
+ int code_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_POSIX_EXCEPTIONS_HXX
diff --git a/libodb/odb/details/posix/mutex.hxx b/libodb/odb/details/posix/mutex.hxx
new file mode 100644
index 0000000..0cb94db
--- /dev/null
+++ b/libodb/odb/details/posix/mutex.hxx
@@ -0,0 +1,44 @@
+// file : odb/details/posix/mutex.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_POSIX_MUTEX_HXX
+#define ODB_DETAILS_POSIX_MUTEX_HXX
+
+#include <odb/pre.hxx>
+
+#include <pthread.h>
+
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class LIBODB_EXPORT mutex
+ {
+ public:
+ ~mutex ();
+ mutex ();
+
+ void
+ lock ();
+
+ void
+ unlock ();
+
+ private:
+ mutex (const mutex&);
+ mutex& operator= (const mutex&);
+
+ private:
+ friend class condition;
+ pthread_mutex_t mutex_;
+ };
+ }
+}
+
+#include <odb/details/posix/mutex.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_POSIX_MUTEX_HXX
diff --git a/libodb/odb/details/posix/mutex.ixx b/libodb/odb/details/posix/mutex.ixx
new file mode 100644
index 0000000..ee73d09
--- /dev/null
+++ b/libodb/odb/details/posix/mutex.ixx
@@ -0,0 +1,37 @@
+// file : odb/details/posix/mutex.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/posix/exceptions.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ inline mutex::
+ ~mutex ()
+ {
+ pthread_mutex_destroy (&mutex_);
+ }
+
+ inline mutex::
+ mutex ()
+ {
+ if (int e = pthread_mutex_init (&mutex_, 0))
+ throw posix_exception (e);
+ }
+
+ inline void mutex::
+ lock ()
+ {
+ if (int e = pthread_mutex_lock (&mutex_))
+ throw posix_exception (e);
+ }
+
+ inline void mutex::
+ unlock ()
+ {
+ if (int e = pthread_mutex_unlock (&mutex_))
+ throw posix_exception (e);
+ }
+ }
+}
diff --git a/libodb/odb/details/posix/thread.cxx b/libodb/odb/details/posix/thread.cxx
new file mode 100644
index 0000000..045f32a
--- /dev/null
+++ b/libodb/odb/details/posix/thread.cxx
@@ -0,0 +1,44 @@
+// file : odb/details/posix/thread.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/unique-ptr.hxx>
+#include <odb/details/posix/thread.hxx>
+#include <odb/details/posix/exceptions.hxx>
+
+typedef void* (*thread_func) (void*);
+
+struct thread_data
+{
+ thread_func func;
+ void* arg;
+};
+
+extern "C" void*
+odb_thread_thunk (void* arg)
+{
+ thread_data* data (static_cast<thread_data*> (arg));
+ thread_func func = data->func;
+ arg = data->arg;
+ delete data;
+ return func (arg);
+}
+
+namespace odb
+{
+ namespace details
+ {
+ thread::
+ thread (void* (*func) (void*), void* arg)
+ : detached_ (false)
+ {
+ unique_ptr<thread_data> data (new thread_data);
+ data->func = func;
+ data->arg = arg;
+
+ if (int e = pthread_create (&id_, 0, &odb_thread_thunk, data.get ()))
+ throw posix_exception (e);
+
+ data.release (); // Thread thunk will free this.
+ }
+ }
+}
diff --git a/libodb/odb/details/posix/thread.hxx b/libodb/odb/details/posix/thread.hxx
new file mode 100644
index 0000000..f0d29a7
--- /dev/null
+++ b/libodb/odb/details/posix/thread.hxx
@@ -0,0 +1,41 @@
+// file : odb/details/posix/thread.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_POSIX_THREAD_HXX
+#define ODB_DETAILS_POSIX_THREAD_HXX
+
+#include <odb/pre.hxx>
+
+#include <pthread.h>
+
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class LIBODB_EXPORT thread
+ {
+ public:
+ ~thread ();
+ thread (void* (*thread_func) (void*), void* arg = 0);
+
+ void*
+ join ();
+
+ private:
+ thread (const thread&);
+ thread& operator= (const thread&);
+
+ private:
+ bool detached_;
+ pthread_t id_;
+ };
+ }
+}
+
+#include <odb/details/posix/thread.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_POSIX_THREAD_HXX
diff --git a/libodb/odb/details/posix/thread.ixx b/libodb/odb/details/posix/thread.ixx
new file mode 100644
index 0000000..6576101
--- /dev/null
+++ b/libodb/odb/details/posix/thread.ixx
@@ -0,0 +1,29 @@
+// file : odb/details/posix/thread.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/posix/exceptions.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ inline thread::
+ ~thread ()
+ {
+ if (!detached_)
+ pthread_detach (id_);
+ }
+
+ inline void* thread::
+ join ()
+ {
+ void* r;
+
+ if (int e = pthread_join (id_, &r))
+ throw posix_exception (e);
+
+ detached_ = true;
+ return r;
+ }
+ }
+}
diff --git a/libodb/odb/details/posix/tls.hxx b/libodb/odb/details/posix/tls.hxx
new file mode 100644
index 0000000..e868819
--- /dev/null
+++ b/libodb/odb/details/posix/tls.hxx
@@ -0,0 +1,106 @@
+// file : odb/details/posix/tls.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_POSIX_TLS_HXX
+#define ODB_DETAILS_POSIX_TLS_HXX
+
+#include <odb/pre.hxx>
+
+#include <pthread.h>
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename T>
+ class tls
+ {
+ public:
+ tls ();
+
+ T&
+ get () const;
+
+ void
+ free ();
+
+ private:
+ tls (const tls&);
+ tls& operator= (const tls&);
+
+ private:
+ static void
+ key_init ();
+
+ static void
+ destructor (void*);
+
+ private:
+ static int error_;
+ static pthread_once_t once_;
+ static pthread_key_t key_;
+ };
+
+ template <typename T>
+ class tls<T*>
+ {
+ public:
+ tls ();
+
+ T*
+ get () const;
+
+ void
+ set (T* p);
+
+ private:
+ tls (const tls&);
+ tls& operator= (const tls&);
+
+ private:
+ static void
+ key_init ();
+
+ private:
+ static int error_;
+ static pthread_once_t once_;
+ static pthread_key_t key_;
+ };
+
+ template <typename T>
+ inline T&
+ tls_get (const tls<T>& t)
+ {
+ return t.get ();
+ }
+
+ template <typename T>
+ inline void
+ tls_free (tls<T>& t)
+ {
+ t.free ();
+ }
+
+ template <typename T>
+ inline T*
+ tls_get (const tls<T*>& t)
+ {
+ return t.get ();
+ }
+
+ template <typename T, typename T1>
+ inline void
+ tls_set (tls<T*>& t, T1* p1)
+ {
+ T* p (p1);
+ t.set (p);
+ }
+ }
+}
+
+#include <odb/details/posix/tls.ixx>
+#include <odb/details/posix/tls.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_POSIX_TLS_HXX
diff --git a/libodb/odb/details/posix/tls.ixx b/libodb/odb/details/posix/tls.ixx
new file mode 100644
index 0000000..7acc173
--- /dev/null
+++ b/libodb/odb/details/posix/tls.ixx
@@ -0,0 +1,20 @@
+// file : odb/details/posix/tls.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename T>
+ inline tls<T>::
+ tls ()
+ {
+ }
+
+ template <typename T>
+ inline tls<T*>::
+ tls ()
+ {
+ }
+ }
+}
diff --git a/libodb/odb/details/posix/tls.txx b/libodb/odb/details/posix/tls.txx
new file mode 100644
index 0000000..e4c5b8f
--- /dev/null
+++ b/libodb/odb/details/posix/tls.txx
@@ -0,0 +1,121 @@
+// file : odb/details/posix/tls.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/unique-ptr.hxx>
+#include <odb/details/posix/exceptions.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ // tls<T>
+ //
+
+ template <typename T>
+ int tls<T>::error_ = 0;
+
+ template <typename T>
+ pthread_once_t tls<T>::once_ = PTHREAD_ONCE_INIT;
+
+ template <typename T>
+ pthread_key_t tls<T>::key_;
+
+ template <typename T>
+ T& tls<T>::
+ get () const
+ {
+ int e (pthread_once (&once_, key_init));
+
+ if (e != 0 || error_ != 0)
+ throw posix_exception (e ? e : error_);
+
+ if (void* v = pthread_getspecific (key_))
+ return *static_cast<T*> (v);
+
+ unique_ptr<T> p (new T);
+
+ if ((e = pthread_setspecific (key_, p.get ())))
+ throw posix_exception (e);
+
+ T& r (*p);
+ p.release ();
+ return r;
+ }
+
+ template <typename T>
+ void tls<T>::
+ free ()
+ {
+ int e (pthread_once (&once_, key_init));
+
+ if (e != 0 || error_ != 0)
+ throw posix_exception (e ? e : error_);
+
+ if (void* v = pthread_getspecific (key_))
+ {
+ if ((e = pthread_setspecific (key_, 0)))
+ throw posix_exception (e);
+
+ delete static_cast<T*> (v);
+ }
+ }
+
+ template <typename T>
+ void tls<T>::
+ key_init ()
+ {
+ error_ = pthread_key_create (&key_, destructor);
+ }
+
+ template <typename T>
+ void tls<T>::
+ destructor (void* v)
+ {
+ delete static_cast<T*> (v);
+ }
+
+ // tls<T*>
+ //
+
+ template <typename T>
+ int tls<T*>::error_ = 0;
+
+ template <typename T>
+ pthread_once_t tls<T*>::once_ = PTHREAD_ONCE_INIT;
+
+ template <typename T>
+ pthread_key_t tls<T*>::key_;
+
+ template <typename T>
+ T* tls<T*>::
+ get () const
+ {
+ int e (pthread_once (&once_, key_init));
+
+ if (e != 0 || error_ != 0)
+ throw posix_exception (e ? e : error_);
+
+ return static_cast<T*> (pthread_getspecific (key_));
+ }
+
+ template <typename T>
+ void tls<T*>::
+ set (T* p)
+ {
+ int e (pthread_once (&once_, key_init));
+
+ if (e != 0 || error_ != 0)
+ throw posix_exception (e ? e : error_);
+
+ if ((e = pthread_setspecific (key_, p)))
+ throw posix_exception (e);
+ }
+
+ template <typename T>
+ void tls<T*>::
+ key_init ()
+ {
+ error_ = pthread_key_create (&key_, 0);
+ }
+ }
+}
diff --git a/libodb/odb/details/shared-ptr-fwd.hxx b/libodb/odb/details/shared-ptr-fwd.hxx
new file mode 100644
index 0000000..73377b9
--- /dev/null
+++ b/libodb/odb/details/shared-ptr-fwd.hxx
@@ -0,0 +1,24 @@
+// file : odb/details/shared-ptr-fwd.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_SHARED_PTR_FWD_HXX
+#define ODB_DETAILS_SHARED_PTR_FWD_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/shared-ptr/counter-type.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename X>
+ class shared_ptr;
+
+ class shared_base;
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_SHARED_PTR_FWD_HXX
diff --git a/libodb/odb/details/shared-ptr.hxx b/libodb/odb/details/shared-ptr.hxx
new file mode 100644
index 0000000..5a1e842
--- /dev/null
+++ b/libodb/odb/details/shared-ptr.hxx
@@ -0,0 +1,167 @@
+// file : odb/details/shared-ptr.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_SHARED_PTR_HXX
+#define ODB_DETAILS_SHARED_PTR_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/shared-ptr-fwd.hxx>
+#include <odb/details/shared-ptr/base.hxx>
+#include <odb/details/shared-ptr/exception.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename X>
+ class shared_ptr:
+ private bits::counter_ops<typename bits::counter_type<X>::r, X>
+ {
+ typedef bits::counter_ops<typename bits::counter_type<X>::r, X> base;
+
+ public:
+ ~shared_ptr ()
+ {
+ if (x_ != 0)
+ base::dec (x_);
+ }
+
+ explicit
+ shared_ptr (X* x = 0)
+ : base (x), x_ (x)
+ {
+ }
+
+ shared_ptr (const shared_ptr& x)
+ : base (x), x_ (x.x_)
+ {
+ if (x_ != 0)
+ base::inc (x_);
+ }
+
+ template <typename Y>
+ shared_ptr (const shared_ptr<Y>& x)
+ : base (x), x_ (x.x_)
+ {
+ if (x_ != 0)
+ base::inc (x_);
+ }
+
+ shared_ptr&
+ operator= (const shared_ptr& x)
+ {
+ if (x_ != x.x_)
+ {
+ if (x_ != 0)
+ base::dec (x_);
+
+ static_cast<base&> (*this) = x;
+ x_ = x.x_;
+
+ if (x_ != 0)
+ base::inc (x_);
+ }
+
+ return *this;
+ }
+
+ template <typename Y>
+ shared_ptr&
+ operator= (const shared_ptr<Y>& x)
+ {
+ if (x_ != x.x_)
+ {
+ if (x_ != 0)
+ base::dec (x_);
+
+ static_cast<base&> (*this) = x;
+ x_ = x.x_;
+
+ if (x_ != 0)
+ base::inc (x_);
+ }
+
+ return *this;
+ }
+
+ public:
+ X*
+ operator-> () const
+ {
+ return x_;
+ }
+
+ X&
+ operator* () const
+ {
+ return *x_;
+ }
+
+ // Conversion to bool.
+ //
+ typedef void (shared_ptr::*boolean_convertible)();
+ void true_value () {}
+
+ operator boolean_convertible () const
+ {
+ return x_ ? &shared_ptr<X>::true_value : 0;
+ }
+
+ public:
+ X*
+ get () const
+ {
+ return x_;
+ }
+
+ X*
+ release ()
+ {
+ X* r (x_);
+ x_ = 0;
+ return r;
+ }
+
+ void
+ reset (X* x = 0)
+ {
+ if (x_ != 0)
+ base::dec (x_);
+
+ base::reset (x);
+ x_ = x;
+ }
+
+ std::size_t
+ count () const
+ {
+ return x_ != 0 ? base::count (x_) : 0;
+ }
+
+ private:
+ template <typename>
+ friend class shared_ptr;
+
+ X* x_;
+ };
+
+ template <typename X, typename Y>
+ inline bool
+ operator== (const shared_ptr<X>& x, const shared_ptr<Y>& y)
+ {
+ return x.get () == y.get ();
+ }
+
+ template <typename X, typename Y>
+ inline bool
+ operator!= (const shared_ptr<X>& x, const shared_ptr<Y>& y)
+ {
+ return x.get () != y.get ();
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_SHARED_PTR_HXX
diff --git a/libodb/odb/details/shared-ptr/base.cxx b/libodb/odb/details/shared-ptr/base.cxx
new file mode 100644
index 0000000..d937400
--- /dev/null
+++ b/libodb/odb/details/shared-ptr/base.cxx
@@ -0,0 +1,83 @@
+// file : odb/details/shared-ptr/base.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/shared-ptr/base.hxx>
+#include <odb/details/shared-ptr/exception.hxx>
+
+using std::size_t;
+
+namespace odb
+{
+ namespace details
+ {
+ share shared = share (1);
+ share exclusive = share (2);
+
+ const char* not_shared::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "object is not shared";
+ }
+
+ not_shared* not_shared::
+ clone () const
+ {
+ return new not_shared (*this);
+ }
+
+ namespace bits
+ {
+ size_t* locator_common::
+ counter (void* x)
+ {
+ size_t* p (static_cast<size_t*> (x));
+
+ if (*(--p) != 0xDEADBEEF)
+ throw not_shared ();
+
+ return --p;
+ }
+ }
+ }
+}
+
+void*
+#ifdef ODB_CXX11
+operator new (size_t n, odb::details::share s)
+#else
+operator new (size_t n, odb::details::share s) throw (std::bad_alloc)
+#endif
+{
+ if (s == odb::details::shared)
+ {
+ // Here we need to make sure we don't break the alignment of the
+ // returned block. For that we need to know the maximum alignment
+ // of this platform. Twice the pointer size is a good guess for
+ // most platforms.
+ //
+ size_t* p = static_cast<size_t*> (operator new (n + 2 * sizeof (size_t)));
+ *p++ = 1; // Initial count.
+ *p++ = 0xDEADBEEF; // Signature.
+ return p;
+ }
+ else
+ return operator new (n);
+
+}
+
+void
+operator delete (void* p, odb::details::share s) ODB_NOTHROW_NOEXCEPT
+{
+ // This version of operator delete is only called when the c-tor
+ // fails. In this case there is no object and we can just free the
+ // memory.
+ //
+ if (s == odb::details::shared)
+ {
+ size_t* sp = static_cast<size_t*> (p);
+ sp -= 2;
+ operator delete (sp);
+ }
+ else
+ operator delete (p);
+}
diff --git a/libodb/odb/details/shared-ptr/base.hxx b/libodb/odb/details/shared-ptr/base.hxx
new file mode 100644
index 0000000..8cd4c86
--- /dev/null
+++ b/libodb/odb/details/shared-ptr/base.hxx
@@ -0,0 +1,131 @@
+// file : odb/details/shared-ptr/base.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_SHARED_PTR_BASE_HXX
+#define ODB_DETAILS_SHARED_PTR_BASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11, ODB_NOTHROW_NOEXCEPT
+
+#include <new>
+#include <cstddef> // std::size_t
+
+#ifdef ODB_CXX11
+#include <atomic>
+#endif
+
+#include <odb/details/export.hxx>
+#include <odb/details/shared-ptr/counter-type.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ struct share
+ {
+ explicit
+ share (char id);
+
+ bool
+ operator== (share) const;
+
+ private:
+ char id_;
+ };
+
+ extern LIBODB_EXPORT share shared;
+ extern LIBODB_EXPORT share exclusive;
+ }
+}
+
+#ifdef ODB_CXX11
+LIBODB_EXPORT void*
+operator new (std::size_t, odb::details::share);
+#else
+LIBODB_EXPORT void*
+operator new (std::size_t, odb::details::share) throw (std::bad_alloc);
+#endif
+
+LIBODB_EXPORT void
+operator delete (void*, odb::details::share) ODB_NOTHROW_NOEXCEPT;
+
+namespace odb
+{
+ namespace details
+ {
+ class LIBODB_EXPORT shared_base
+ {
+ public:
+ shared_base ();
+ shared_base (const shared_base&);
+ shared_base&
+ operator= (const shared_base&);
+
+ void
+ _inc_ref ();
+
+ bool
+ _dec_ref ();
+
+ std::size_t
+ _ref_count () const;
+
+#ifdef ODB_CXX11
+ void*
+ operator new (std::size_t);
+
+ void*
+ operator new (std::size_t, share);
+#else
+ void*
+ operator new (std::size_t) throw (std::bad_alloc);
+
+ void*
+ operator new (std::size_t, share) throw (std::bad_alloc);
+#endif
+
+ void
+ operator delete (void*, share) ODB_NOTHROW_NOEXCEPT;
+
+ void
+ operator delete (void*) ODB_NOTHROW_NOEXCEPT;
+
+ struct refcount_callback
+ {
+ void* arg;
+
+ // Return true if the object should be deleted, false otherwise.
+ //
+ bool (*zero_counter) (void*);
+ };
+
+ protected:
+#ifdef ODB_CXX11
+ std::atomic<std::size_t> counter_;
+#else
+ std::size_t counter_;
+#endif
+ refcount_callback* callback_;
+ };
+
+ template <typename X>
+ inline X*
+ inc_ref (X*);
+
+ template <typename X>
+ inline void
+ dec_ref (X*);
+
+ template <typename X>
+ inline std::size_t
+ ref_count (const X*);
+ }
+}
+
+#include <odb/details/shared-ptr/base.ixx>
+#include <odb/details/shared-ptr/base.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_SHARED_PTR_BASE_HXX
diff --git a/libodb/odb/details/shared-ptr/base.ixx b/libodb/odb/details/shared-ptr/base.ixx
new file mode 100644
index 0000000..1e2fd4b
--- /dev/null
+++ b/libodb/odb/details/shared-ptr/base.ixx
@@ -0,0 +1,119 @@
+// file : odb/details/shared-ptr/base.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace details
+ {
+ // share
+ //
+
+ inline share::
+ share (char id)
+ : id_ (id)
+ {
+ }
+
+ inline bool share::
+ operator== (share x) const
+ {
+ return id_ == x.id_;
+ }
+
+ // shared_base
+ //
+
+ inline shared_base::
+ shared_base ()
+ : counter_ (1), callback_ (0)
+ {
+ }
+
+ inline shared_base::
+ shared_base (const shared_base&)
+ : counter_ (1), callback_ (0)
+ {
+ }
+
+ inline shared_base& shared_base::
+ operator= (const shared_base&)
+ {
+ return *this;
+ }
+
+ inline void shared_base::
+ _inc_ref ()
+ {
+#ifdef ODB_CXX11
+ counter_.fetch_add (1, std::memory_order_relaxed);
+#else
+ ++counter_;
+#endif
+ }
+
+ inline bool shared_base::
+ _dec_ref ()
+ {
+ // While there are ways to avoid acquire (which is unnecessary except
+ // when the counter drops to zero), for our use-cases we'd rather keep
+ // it simple.
+ //
+ return
+#ifdef ODB_CXX11
+ counter_.fetch_sub (1, std::memory_order_acq_rel) == 1
+#else
+ --counter_ == 0
+#endif
+ ? callback_ == 0 || callback_->zero_counter (callback_->arg)
+ : false;
+ }
+
+ inline std::size_t shared_base::
+ _ref_count () const
+ {
+#ifdef ODB_CXX11
+ return counter_.load (std::memory_order_relaxed);
+#else
+ return counter_;
+#endif
+ }
+
+#ifdef ODB_CXX11
+ inline void* shared_base::
+ operator new (std::size_t n)
+ {
+ return ::operator new (n);
+ }
+
+ inline void* shared_base::
+ operator new (std::size_t n, share)
+ {
+ return ::operator new (n);
+ }
+#else
+ inline void* shared_base::
+ operator new (std::size_t n) throw (std::bad_alloc)
+ {
+ return ::operator new (n);
+ }
+
+ inline void* shared_base::
+ operator new (std::size_t n, share) throw (std::bad_alloc)
+ {
+ return ::operator new (n);
+ }
+#endif
+
+ inline void shared_base::
+ operator delete (void* p, share) ODB_NOTHROW_NOEXCEPT
+ {
+ ::operator delete (p);
+ }
+
+ inline void shared_base::
+ operator delete (void* p) ODB_NOTHROW_NOEXCEPT
+ {
+ ::operator delete (p);
+ }
+ }
+}
diff --git a/libodb/odb/details/shared-ptr/base.txx b/libodb/odb/details/shared-ptr/base.txx
new file mode 100644
index 0000000..77a957d
--- /dev/null
+++ b/libodb/odb/details/shared-ptr/base.txx
@@ -0,0 +1,198 @@
+// file : odb/details/shared-ptr/base.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/meta/answer.hxx>
+#include <odb/details/meta/polymorphic-p.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ namespace bits
+ {
+ // Support for locating the counter in the memory block.
+ //
+ struct LIBODB_EXPORT locator_common
+ {
+ static std::size_t*
+ counter (void*);
+ };
+
+ template <typename X, bool poly = meta::polymorphic_p<X>::result>
+ struct locator;
+
+ template <typename X>
+ struct locator<X, false>: locator_common
+ {
+ static std::size_t*
+ counter (X* x)
+ {
+ return locator_common::counter (x);
+ }
+ };
+
+ template <typename X>
+ struct locator<X, true>: locator_common
+ {
+ static std::size_t*
+ counter (X* x)
+ {
+ return locator_common::counter (dynamic_cast<void*> (x));
+ }
+ };
+
+ template <typename X>
+ std::size_t*
+ counter (const X* p)
+ {
+ return bits::locator<X>::counter (const_cast<X*> (p));
+ }
+
+ // Counter type and operations.
+ //
+ meta::no test (...);
+ meta::yes test (shared_base*);
+
+ template <typename X,
+ std::size_t A = sizeof (bits::test (reinterpret_cast<X*> (0)))>
+ struct counter_type;
+
+ template <typename X>
+ struct counter_type<X, sizeof (meta::no)>
+ {
+ typedef typename details::counter_type<X>::counter r;
+ };
+
+ template <typename X>
+ struct counter_type<X, sizeof (meta::yes)>
+ {
+ typedef shared_base r;
+ };
+
+ template <typename X, typename Y>
+ struct counter_ops;
+
+ template <typename X>
+ struct counter_ops<X, X>
+ {
+ counter_ops (const X* p) : counter_ (p ? bits::counter (p) : 0) {}
+ counter_ops (const counter_ops& x) : counter_ (x.counter_) {}
+
+ template <typename Z>
+ counter_ops (const counter_ops<Z, Z>& x) : counter_ (x.counter_) {}
+
+ counter_ops&
+ operator= (const counter_ops& x)
+ {
+ counter_ = x.counter_;
+ return *this;
+ }
+
+ template <typename Z>
+ counter_ops&
+ operator= (const counter_ops<Z, Z>& x)
+ {
+ counter_ = x.counter_;
+ return *this;
+ }
+
+ void
+ reset (const X* p)
+ {
+ counter_ = p ? bits::counter (p) : 0;
+ }
+
+ void
+ inc (X*)
+ {
+ (*counter_)++;
+ }
+
+ void
+ dec (X* p)
+ {
+ if (--(*counter_) == 0)
+ {
+ p->~X ();
+
+ // Counter is the top of the memory block.
+ //
+ operator delete (counter_);
+ }
+ }
+
+ std::size_t
+ count (const X*) const
+ {
+ return *counter_;
+ }
+
+ std::size_t* counter_;
+ };
+
+ template <typename Y>
+ struct counter_ops<shared_base, Y>
+ {
+ counter_ops (const Y*) {}
+ counter_ops (const counter_ops&) {}
+
+ template <typename Z>
+ counter_ops (const counter_ops<shared_base, Z>&) {}
+
+ counter_ops&
+ operator= (const counter_ops&)
+ {
+ return *this;
+ }
+
+ template <typename Z>
+ counter_ops&
+ operator= (const counter_ops<shared_base, Z>&)
+ {
+ return *this;
+ }
+
+ void
+ reset (const Y*) {}
+
+ void
+ inc (shared_base* p) {p->_inc_ref ();}
+
+ void
+ dec (Y* p)
+ {
+ if (static_cast<shared_base*> (p)->_dec_ref ())
+ delete p;
+ }
+
+ std::size_t
+ count (const shared_base* p) const {return p->_ref_count ();}
+ };
+ }
+
+ template <typename X>
+ inline X*
+ inc_ref (X* p)
+ {
+ bits::counter_ops<typename bits::counter_type<X>::r, X> c (p);
+ c.inc (p);
+ return p;
+ }
+
+ template <typename X>
+ inline void
+ dec_ref (X* p)
+ {
+ bits::counter_ops<typename bits::counter_type<X>::r, X> c (p);
+ c.dec (p);
+ }
+
+ template <typename X>
+ inline std::size_t
+ ref_count (const X* p)
+ {
+ bits::counter_ops<typename bits::counter_type<X>::r, X> c (p);
+ return c.count (p);
+ }
+ }
+}
diff --git a/libodb/odb/details/shared-ptr/counter-type.hxx b/libodb/odb/details/shared-ptr/counter-type.hxx
new file mode 100644
index 0000000..2b6caad
--- /dev/null
+++ b/libodb/odb/details/shared-ptr/counter-type.hxx
@@ -0,0 +1,23 @@
+// file : odb/details/shared-ptr/counter-type.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_SHARED_PTR_COUNTER_TYPE_HXX
+#define ODB_DETAILS_SHARED_PTR_COUNTER_TYPE_HXX
+
+#include <odb/pre.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename X>
+ struct counter_type
+ {
+ typedef X counter;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_SHARED_PTR_COUNTER_TYPE_HXX
diff --git a/libodb/odb/details/shared-ptr/exception.hxx b/libodb/odb/details/shared-ptr/exception.hxx
new file mode 100644
index 0000000..0ed50be
--- /dev/null
+++ b/libodb/odb/details/shared-ptr/exception.hxx
@@ -0,0 +1,31 @@
+// file : odb/details/shared-ptr/exception.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_SHARED_PTR_EXCEPTION_HXX
+#define ODB_DETAILS_SHARED_PTR_EXCEPTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/exception.hxx>
+
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ struct LIBODB_EXPORT not_shared: exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual not_shared*
+ clone () const;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_SHARED_PTR_EXCEPTION_HXX
diff --git a/libodb/odb/details/thread.cxx b/libodb/odb/details/thread.cxx
new file mode 100644
index 0000000..b1fbe42
--- /dev/null
+++ b/libodb/odb/details/thread.cxx
@@ -0,0 +1,22 @@
+// file : odb/details/thread.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/thread.hxx>
+
+// We might be compiled with ODB_THREADS_NONE.
+//
+#ifdef ODB_THREADS_CXX11
+
+namespace odb
+{
+ namespace details
+ {
+ void thread::
+ thunk (void* (*f) (void*), void* a, std::promise<void*> p)
+ {
+ p.set_value (f (a));
+ }
+ }
+}
+
+#endif
diff --git a/libodb/odb/details/thread.hxx b/libodb/odb/details/thread.hxx
new file mode 100644
index 0000000..9095f68
--- /dev/null
+++ b/libodb/odb/details/thread.hxx
@@ -0,0 +1,65 @@
+// file : odb/details/thread.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_THREAD_HXX
+#define ODB_DETAILS_THREAD_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx>
+#include <odb/details/export.hxx>
+
+#ifdef ODB_THREADS_NONE
+# error no thread support available
+#elif defined(ODB_THREADS_CXX11)
+# include <thread>
+# include <future>
+# include <utility> // move()
+
+namespace odb
+{
+ namespace details
+ {
+ class LIBODB_EXPORT thread
+ {
+ public:
+ thread (void* (*thread_func) (void*), void* arg = 0)
+ {
+ std::promise<void*> p;
+ f_ = p.get_future ();
+ t_ = std::thread (thunk, thread_func, arg, std::move (p));
+ }
+
+ void*
+ join ()
+ {
+ f_.wait ();
+ t_.join ();
+ return f_.get ();
+ }
+
+ thread (const thread&) = delete;
+ thread& operator= (const thread&) = delete;
+
+ private:
+ static void
+ thunk (void* (*) (void*), void*, std::promise<void*>);
+
+ private:
+ std::thread t_;
+ std::future<void*> f_;
+ };
+ }
+}
+
+#elif defined(ODB_THREADS_POSIX)
+#include <odb/details/posix/thread.hxx>
+#elif defined(ODB_THREADS_WIN32)
+#include <odb/details/win32/thread.hxx>
+#else
+# error unknown threading model
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_THREAD_HXX
diff --git a/libodb/odb/details/tls.hxx b/libodb/odb/details/tls.hxx
new file mode 100644
index 0000000..de6c344
--- /dev/null
+++ b/libodb/odb/details/tls.hxx
@@ -0,0 +1,168 @@
+// file : odb/details/tls.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_TLS_HXX
+#define ODB_DETAILS_TLS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx>
+
+#ifdef ODB_THREADS_NONE
+
+# define ODB_TLS_POINTER(type) type*
+# define ODB_TLS_OBJECT(type) type
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename T>
+ inline T&
+ tls_get (T& x)
+ {
+ return x;
+ }
+
+ // If early destructions is possible, destroy the object and free
+ // any allocated resources.
+ //
+ template <typename T>
+ inline void
+ tls_free (T&)
+ {
+ }
+
+ template <typename T>
+ inline T*
+ tls_get (T* p)
+ {
+ return p;
+ }
+
+ template <typename T, typename T1>
+ inline void
+ tls_set (T*& rp, T1* p)
+ {
+ rp = p;
+ }
+ }
+}
+
+#elif defined(ODB_THREADS_CXX11)
+
+// Apparently Apple's Clang "temporarily disabled" C++11 thread_local until
+// they can implement a "fast" version, which reportedly happened in XCode 8.
+// So for now we will continue using __thread for this target.
+//
+# if defined(__apple_build_version__) && __apple_build_version__ < 8000000
+# define ODB_TLS_POINTER(type) __thread type*
+# define ODB_TLS_OBJECT(type) thread_local type
+# else
+# define ODB_TLS_POINTER(type) thread_local type*
+# define ODB_TLS_OBJECT(type) thread_local type
+# endif
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename T>
+ inline T&
+ tls_get (T& x)
+ {
+ return x;
+ }
+
+ template <typename T>
+ inline void
+ tls_free (T&)
+ {
+ }
+
+ template <typename T>
+ inline T*
+ tls_get (T* p)
+ {
+ return p;
+ }
+
+ template <typename T, typename T1>
+ inline void
+ tls_set (T*& rp, T1* p)
+ {
+ rp = p;
+ }
+ }
+}
+
+#elif defined(ODB_THREADS_POSIX)
+
+# include <odb/details/posix/tls.hxx>
+
+# ifdef ODB_THREADS_TLS_KEYWORD
+# define ODB_TLS_POINTER(type) __thread type*
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename T>
+ inline T*
+ tls_get (T* p)
+ {
+ return p;
+ }
+
+ template <typename T, typename T1>
+ inline void
+ tls_set (T*& rp, T1* p)
+ {
+ rp = p;
+ }
+ }
+}
+
+# else
+# define ODB_TLS_POINTER(type) tls<type*>
+# endif
+# define ODB_TLS_OBJECT(type) tls<type>
+
+#elif defined(ODB_THREADS_WIN32)
+
+# include <odb/details/win32/tls.hxx>
+
+# ifdef ODB_THREADS_TLS_DECLSPEC
+# define ODB_TLS_POINTER(type) __declspec(thread) type*
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename T>
+ inline T*
+ tls_get (T* p)
+ {
+ return p;
+ }
+
+ template <typename T, typename T1>
+ inline void
+ tls_set (T*& rp, T1* p)
+ {
+ rp = p;
+ }
+ }
+}
+
+# else
+# define ODB_TLS_POINTER(type) tls<type*>
+# endif
+# define ODB_TLS_OBJECT(type) tls<type>
+#else
+# error unknown threading model
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_TLS_HXX
diff --git a/libodb/odb/details/transfer-ptr.hxx b/libodb/odb/details/transfer-ptr.hxx
new file mode 100644
index 0000000..4b63df6
--- /dev/null
+++ b/libodb/odb/details/transfer-ptr.hxx
@@ -0,0 +1,73 @@
+// file : odb/details/transfer-ptr.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_TRANSFER_PTR_HXX
+#define ODB_DETAILS_TRANSFER_PTR_HXX
+
+#include <odb/pre.hxx>
+
+#include <memory> // std::auto_ptr, std::unique_ptr
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename T>
+ class transfer_ptr
+ {
+ public:
+ typedef T element_type;
+
+ transfer_ptr (): p_ (0) {}
+
+#ifndef ODB_CXX11
+ template <typename T1>
+ transfer_ptr (std::auto_ptr<T1> p): p_ (p.release ()) {}
+
+ private:
+ transfer_ptr& operator= (const transfer_ptr&);
+
+ public:
+ // In our usage transfer_ptr is always created implicitly and
+ // never const. So while this is not very clean, it is legal.
+ // Plus it will all go away once we drop C++98 (I can hardly
+ // wait).
+ //
+ transfer_ptr (const transfer_ptr& p)
+ : p_ (const_cast<transfer_ptr&> (p).transfer ()) {}
+#else
+#ifdef ODB_CXX11_NULLPTR
+ transfer_ptr (std::nullptr_t): p_ (0) {}
+#endif
+ template <typename T1>
+ transfer_ptr (std::unique_ptr<T1>&& p): p_ (p.release ()) {}
+
+ private:
+ transfer_ptr (const transfer_ptr&);
+ transfer_ptr& operator= (const transfer_ptr&);
+
+ public:
+ transfer_ptr (transfer_ptr&& p) noexcept: p_ (p.transfer ()) {}
+#endif
+
+ ~transfer_ptr () {delete p_;}
+
+ T*
+ transfer ()
+ {
+ T* r (p_);
+ p_ = 0;
+ return r;
+ }
+
+ private:
+ T* p_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_TRANSFER_PTR_HXX
diff --git a/libodb/odb/details/type-info.hxx b/libodb/odb/details/type-info.hxx
new file mode 100644
index 0000000..fe01699
--- /dev/null
+++ b/libodb/odb/details/type-info.hxx
@@ -0,0 +1,36 @@
+// file : odb/details/type-info.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_TYPE_INFO_HXX
+#define ODB_DETAILS_TYPE_INFO_HXX
+
+#include <odb/pre.hxx>
+
+#include <typeinfo>
+
+namespace odb
+{
+ namespace details
+ {
+ struct type_info_comparator
+ {
+ bool
+ operator() (const std::type_info* x, const std::type_info* y) const
+ {
+ // XL C++ on AIX has buggy type_info::before() in that
+ // it returns true for two different type_info objects
+ // that happened to be for the same type.
+ //
+#if defined(__xlC__) && defined(_AIX)
+ return *x != *y && x->before (*y);
+#else
+ return x->before (*y);
+#endif
+ }
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_TYPE_INFO_HXX
diff --git a/libodb/odb/details/unique-ptr.hxx b/libodb/odb/details/unique-ptr.hxx
new file mode 100644
index 0000000..06b2c76
--- /dev/null
+++ b/libodb/odb/details/unique-ptr.hxx
@@ -0,0 +1,95 @@
+// file : odb/details/unique-ptr.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_UNIQUE_PTR_HXX
+#define ODB_DETAILS_UNIQUE_PTR_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename T>
+ class unique_ptr
+ {
+ public:
+ typedef T element_type;
+
+ explicit unique_ptr (T* p = 0): p_ (p) {}
+ ~unique_ptr () {delete p_;}
+
+#ifdef ODB_CXX11
+ unique_ptr (unique_ptr&& p) noexcept: p_ (p.p_) {p.p_ = 0;}
+ unique_ptr& operator= (unique_ptr&& p) noexcept
+ {
+ if (this != &p)
+ {
+ delete p_;
+ p_ = p.p_;
+ p.p_ = 0;
+ }
+ return *this;
+ }
+#endif
+
+ private:
+ unique_ptr (const unique_ptr&);
+ unique_ptr& operator= (const unique_ptr&);
+
+ public:
+ T*
+ operator-> () const {return p_;}
+
+ T&
+ operator* () const {return *p_;}
+
+ typedef T* unique_ptr::*unspecified_bool_type;
+ operator unspecified_bool_type () const
+ {
+ return p_ != 0 ? &unique_ptr::p_ : 0;
+ }
+
+ T*
+ get () const {return p_;}
+
+ void
+ reset (T* p = 0)
+ {
+ delete p_;
+ p_ = p;
+ }
+
+ T*
+ release ()
+ {
+ T* r (p_);
+ p_ = 0;
+ return r;
+ }
+
+ private:
+ T* p_;
+ };
+
+ template <typename T1, typename T2>
+ inline bool
+ operator== (const unique_ptr<T1>& a, const unique_ptr<T2>& b)
+ {
+ return a.get () == b.get ();
+ }
+
+ template <typename T1, typename T2>
+ inline bool
+ operator!= (const unique_ptr<T1>& a, const unique_ptr<T2>& b)
+ {
+ return a.get () != b.get ();
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_UNIQUE_PTR_HXX
diff --git a/libodb/odb/details/unused.hxx b/libodb/odb/details/unused.hxx
new file mode 100644
index 0000000..8364c44
--- /dev/null
+++ b/libodb/odb/details/unused.hxx
@@ -0,0 +1,21 @@
+// file : odb/details/unused.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_UNUSED_DETAILS_HXX
+#define ODB_UNUSED_DETAILS_HXX
+
+#include <odb/pre.hxx>
+
+// VC++ and xlC don't like the (void)x expression if x is a reference
+// to an incomplete type. On the other hand, GCC warns that (void*)&x
+// doesn't have any effect.
+//
+#if defined(_MSC_VER) || defined(__xlC__)
+# define ODB_POTENTIALLY_UNUSED(x) (void*)&x
+#else
+# define ODB_POTENTIALLY_UNUSED(x) (void)x
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_UNUSED_DETAILS_HXX
diff --git a/libodb/odb/details/win32/condition.cxx b/libodb/odb/details/win32/condition.cxx
new file mode 100644
index 0000000..3a4b605
--- /dev/null
+++ b/libodb/odb/details/win32/condition.cxx
@@ -0,0 +1,54 @@
+// file : odb/details/win32/condition.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/win32/windows.hxx>
+#include <odb/details/win32/condition.hxx>
+#include <odb/details/win32/exceptions.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ void condition::
+ signal ()
+ {
+ mutex_.lock ();
+
+ if (waiters_ > signals_)
+ {
+ if (signals_++ == 0)
+ {
+ if (SetEvent (event_) == 0)
+ throw win32_exception ();
+ }
+ }
+
+ mutex_.unlock ();
+ }
+
+ void condition::
+ wait (lock&)
+ {
+ // When we enter this functions the mutex is locked. When we
+ // return from this function the mutex must be locked.
+ //
+ waiters_++;
+ mutex_.unlock ();
+
+ if (WaitForSingleObject (event_, INFINITE) != 0)
+ throw win32_exception ();
+
+ mutex_.lock ();
+ waiters_--;
+ signals_--;
+
+ if (signals_ > 0)
+ {
+ // Wake up the next thread.
+ //
+ if (SetEvent (event_) == 0)
+ throw win32_exception ();
+ }
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/condition.hxx b/libodb/odb/details/win32/condition.hxx
new file mode 100644
index 0000000..69972a0
--- /dev/null
+++ b/libodb/odb/details/win32/condition.hxx
@@ -0,0 +1,52 @@
+// file : odb/details/win32/condition.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_CONDITION_HXX
+#define ODB_DETAILS_WIN32_CONDITION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/win32/windows.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/details/export.hxx>
+#include <odb/details/win32/mutex.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class lock;
+
+ class LIBODB_EXPORT condition
+ {
+ public:
+ ~condition ();
+ condition (mutex&);
+
+ void
+ signal ();
+
+ void
+ wait (lock&);
+
+ private:
+ condition (const condition&);
+ condition& operator= (const condition&);
+
+ private:
+ mutex& mutex_;
+ HANDLE event_;
+
+ std::size_t waiters_;
+ std::size_t signals_;
+ };
+ }
+}
+
+#include <odb/details/win32/condition.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_CONDITION_HXX
diff --git a/libodb/odb/details/win32/condition.ixx b/libodb/odb/details/win32/condition.ixx
new file mode 100644
index 0000000..37a2bac
--- /dev/null
+++ b/libodb/odb/details/win32/condition.ixx
@@ -0,0 +1,30 @@
+// file : odb/details/win32/condition.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/win32/exceptions.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ inline condition::
+ ~condition ()
+ {
+ CloseHandle (event_);
+ }
+
+ inline condition::
+ condition (mutex& mutex)
+ : mutex_ (mutex), waiters_ (0), signals_ (0)
+ {
+ // Auto-reset event. Releases one waiting thread and automatically
+ // resets the event state. If no threads are waiting the event
+ // remains signalled.
+ //
+ event_ = CreateEvent (0, false, false, 0);
+
+ if (event_ == 0)
+ throw win32_exception ();
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/dll.cxx b/libodb/odb/details/win32/dll.cxx
new file mode 100644
index 0000000..3f674ba
--- /dev/null
+++ b/libodb/odb/details/win32/dll.cxx
@@ -0,0 +1,48 @@
+// file : odb/details/win32/dll.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// If we are building a static library then omit DllMain.
+
+#ifdef LIBODB_SHARED_BUILD
+
+#include <odb/details/win32/windows.hxx>
+#include <odb/details/win32/init.hxx>
+
+using namespace odb::details;
+
+extern "C" BOOL WINAPI
+DllMain (HINSTANCE, DWORD reason, LPVOID reserved)
+{
+ switch (reason)
+ {
+ case DLL_PROCESS_ATTACH:
+ {
+ process_start ();
+ thread_start ();
+ break;
+ }
+
+ case DLL_THREAD_ATTACH:
+ {
+ thread_start ();
+ break;
+ }
+
+ case DLL_THREAD_DETACH:
+ {
+ thread_end ();
+ break;
+ }
+
+ case DLL_PROCESS_DETACH:
+ {
+ thread_end ();
+ process_end (reserved == NULL);
+ break;
+ }
+ }
+
+ return 1;
+}
+
+#endif
diff --git a/libodb/odb/details/win32/exceptions.cxx b/libodb/odb/details/win32/exceptions.cxx
new file mode 100644
index 0000000..3cf11c2
--- /dev/null
+++ b/libodb/odb/details/win32/exceptions.cxx
@@ -0,0 +1,22 @@
+// file : odb/details/win32/exceptions.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/win32/exceptions.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ const char* win32_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "Win32 API error";
+ }
+
+ win32_exception* win32_exception::
+ clone () const
+ {
+ return new win32_exception (*this);
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/exceptions.hxx b/libodb/odb/details/win32/exceptions.hxx
new file mode 100644
index 0000000..b61a447
--- /dev/null
+++ b/libodb/odb/details/win32/exceptions.hxx
@@ -0,0 +1,40 @@
+// file : odb/details/win32/exceptions.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_EXCEPTIONS_HXX
+#define ODB_DETAILS_WIN32_EXCEPTIONS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+#include <odb/details/export.hxx>
+#include <odb/details/exception.hxx>
+#include <odb/details/win32/windows.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ struct LIBODB_EXPORT win32_exception: details::exception
+ {
+ win32_exception () : code_ (GetLastError ()) {}
+ win32_exception (DWORD code) : code_ (code) {}
+
+ DWORD
+ code () const {return code_;}
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual win32_exception*
+ clone () const;
+
+ private:
+ DWORD code_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_EXCEPTIONS_HXX
diff --git a/libodb/odb/details/win32/init.cxx b/libodb/odb/details/win32/init.cxx
new file mode 100644
index 0000000..f6e0f9a
--- /dev/null
+++ b/libodb/odb/details/win32/init.cxx
@@ -0,0 +1,41 @@
+// file : odb/details/win32/init.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/win32/init.hxx>
+#include <odb/details/win32/once-init.hxx>
+#include <odb/details/win32/tls-init.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ void
+ process_start ()
+ {
+ // The order is important.
+ //
+ once_process_start ();
+ tls_process_start ();
+ }
+
+ void
+ process_end (bool safe)
+ {
+ // The order is important.
+ //
+ tls_process_end (safe);
+ once_process_end (safe);
+ }
+
+ void
+ thread_start ()
+ {
+ }
+
+ void
+ thread_end ()
+ {
+ tls_thread_end ();
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/init.hxx b/libodb/odb/details/win32/init.hxx
new file mode 100644
index 0000000..1c15ffd
--- /dev/null
+++ b/libodb/odb/details/win32/init.hxx
@@ -0,0 +1,36 @@
+// file : odb/details/win32/init.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_INIT_HXX
+#define ODB_DETAILS_WIN32_INIT_HXX
+
+#include <odb/pre.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ void
+ process_start ();
+
+ // The safe parameter indicates whether it is safe to free heap objects.
+ // If the process is terminated by a call to ExitProcess(), some threads
+ // might have been killed leaving things in inconsistent state.
+ //
+ void
+ process_end (bool safe = true);
+
+ void
+ thread_start ();
+
+ // This function may be called even for thread for which thread_start()
+ // hasn't been called.
+ //
+ void
+ thread_end ();
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_INIT_HXX
diff --git a/libodb/odb/details/win32/lock.hxx b/libodb/odb/details/win32/lock.hxx
new file mode 100644
index 0000000..2e81ac6
--- /dev/null
+++ b/libodb/odb/details/win32/lock.hxx
@@ -0,0 +1,49 @@
+// file : odb/details/win32/lock.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_LOCK_HXX
+#define ODB_DETAILS_WIN32_LOCK_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/win32/windows.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ // Critical section lock. Not exported; for internal use only.
+ //
+ struct win32_lock
+ {
+ win32_lock (CRITICAL_SECTION& cs)
+ : cs_ (&cs)
+ {
+ EnterCriticalSection (cs_);
+ }
+
+ ~win32_lock ()
+ {
+ if (cs_ != 0)
+ LeaveCriticalSection (cs_);
+ }
+
+ void
+ unlock ()
+ {
+ if (cs_ != 0)
+ {
+ LeaveCriticalSection (cs_);
+ cs_ = 0;
+ }
+ }
+
+ private:
+ CRITICAL_SECTION* cs_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_LOCK_HXX
diff --git a/libodb/odb/details/win32/mutex.hxx b/libodb/odb/details/win32/mutex.hxx
new file mode 100644
index 0000000..b2cd997
--- /dev/null
+++ b/libodb/odb/details/win32/mutex.hxx
@@ -0,0 +1,43 @@
+// file : odb/details/win32/mutex.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_MUTEX_HXX
+#define ODB_DETAILS_WIN32_MUTEX_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/win32/windows.hxx>
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class LIBODB_EXPORT mutex
+ {
+ public:
+ ~mutex ();
+ mutex ();
+
+ void
+ lock ();
+
+ void
+ unlock ();
+
+ private:
+ mutex (const mutex&);
+ mutex& operator= (const mutex&);
+
+ private:
+ friend class condition;
+ CRITICAL_SECTION cs_;
+ };
+ }
+}
+
+#include <odb/details/win32/mutex.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_MUTEX_HXX
diff --git a/libodb/odb/details/win32/mutex.ixx b/libodb/odb/details/win32/mutex.ixx
new file mode 100644
index 0000000..bb06415
--- /dev/null
+++ b/libodb/odb/details/win32/mutex.ixx
@@ -0,0 +1,32 @@
+// file : odb/details/win32/mutex.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace details
+ {
+ inline mutex::
+ ~mutex ()
+ {
+ DeleteCriticalSection (&cs_);
+ }
+
+ inline mutex::
+ mutex ()
+ {
+ InitializeCriticalSection (&cs_);
+ }
+
+ inline void mutex::
+ lock ()
+ {
+ EnterCriticalSection (&cs_);
+ }
+
+ inline void mutex::
+ unlock ()
+ {
+ LeaveCriticalSection (&cs_);
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/once-init.hxx b/libodb/odb/details/win32/once-init.hxx
new file mode 100644
index 0000000..a465c90
--- /dev/null
+++ b/libodb/odb/details/win32/once-init.hxx
@@ -0,0 +1,23 @@
+// file : odb/details/win32/once-init.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_ONCE_INIT_HXX
+#define ODB_DETAILS_WIN32_ONCE_INIT_HXX
+
+#include <odb/pre.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ void
+ once_process_start ();
+
+ void
+ once_process_end (bool safe);
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_ONCE_INIT_HXX
diff --git a/libodb/odb/details/win32/once.cxx b/libodb/odb/details/win32/once.cxx
new file mode 100644
index 0000000..7b98d80
--- /dev/null
+++ b/libodb/odb/details/win32/once.cxx
@@ -0,0 +1,26 @@
+// file : odb/details/win32/once.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/win32/windows.hxx>
+#include <odb/details/win32/once.hxx>
+#include <odb/details/win32/once-init.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ CRITICAL_SECTION win32_once_cs_;
+
+ void
+ once_process_start ()
+ {
+ InitializeCriticalSection (&win32_once_cs_);
+ }
+
+ void
+ once_process_end (bool)
+ {
+ DeleteCriticalSection (&win32_once_cs_);
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/once.hxx b/libodb/odb/details/win32/once.hxx
new file mode 100644
index 0000000..45748b8
--- /dev/null
+++ b/libodb/odb/details/win32/once.hxx
@@ -0,0 +1,50 @@
+// file : odb/details/win32/once.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_ONCE_HXX
+#define ODB_DETAILS_WIN32_ONCE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/win32/windows.hxx>
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class LIBODB_EXPORT once
+ {
+ public:
+ once ();
+
+ void
+ call (void (*func) ());
+
+ private:
+ once (const once&);
+ once& operator= (const once&);
+
+ private:
+ bool called_;
+ };
+
+ // Low-level, POSIX-like API that can be used safely during static
+ // initialization (that is, win32_once() can be called during static
+ // initialization) provided once_process_start() has been called.
+ //
+ typedef unsigned int win32_once_t;
+ const win32_once_t WIN32_ONCE_INIT = 0;
+
+ LIBODB_EXPORT void
+ win32_once (win32_once_t&, void (*func) ());
+
+ extern LIBODB_EXPORT CRITICAL_SECTION win32_once_cs_;
+ }
+}
+
+#include <odb/details/win32/once.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_ONCE_HXX
diff --git a/libodb/odb/details/win32/once.ixx b/libodb/odb/details/win32/once.ixx
new file mode 100644
index 0000000..1638706
--- /dev/null
+++ b/libodb/odb/details/win32/once.ixx
@@ -0,0 +1,42 @@
+// file : odb/details/win32/once.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/win32/lock.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ inline void
+ win32_once (win32_once_t& o, void (*func) ())
+ {
+ win32_lock l (win32_once_cs_);
+
+ if (o == 0)
+ {
+ o = 1;
+ l.unlock ();
+ func ();
+ }
+ }
+
+ inline once::
+ once ()
+ : called_ (false)
+ {
+ }
+
+ inline void once::
+ call (void (*func) ())
+ {
+ win32_lock l (win32_once_cs_);
+
+ if (!called_)
+ {
+ called_ = true;
+ l.unlock ();
+ func ();
+ }
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/thread.cxx b/libodb/odb/details/win32/thread.cxx
new file mode 100644
index 0000000..46720d4
--- /dev/null
+++ b/libodb/odb/details/win32/thread.cxx
@@ -0,0 +1,88 @@
+// file : odb/details/win32/thread.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/win32/windows.hxx>
+#include <process.h> // _beginthreadex, _endthreadex
+
+#include <odb/details/unique-ptr.hxx>
+#include <odb/details/win32/thread.hxx>
+#include <odb/details/win32/exceptions.hxx>
+
+unsigned int __stdcall
+odb_thread_thunk (void* arg)
+{
+ odb::details::thread::thread_thunk (arg);
+ _endthreadex (0);
+ return 0;
+}
+
+namespace odb
+{
+ namespace details
+ {
+ void thread::
+ thread_thunk (void* arg)
+ {
+ data* d (static_cast<data*> (arg));
+ d->ret = d->func (d->arg);
+ d->mutex.lock ();
+ unsigned char count = --d->count;
+ d->mutex.unlock ();
+
+ if (count == 0)
+ delete d;
+ }
+
+ thread::
+ ~thread ()
+ {
+ if (handle_ != 0)
+ {
+ CloseHandle (handle_);
+
+ // Win32 mutex implementation does not throw.
+ //
+ data_->mutex.lock ();
+ unsigned char count = --data_->count;
+ data_->mutex.unlock ();
+
+ if (count == 0)
+ delete data_;
+ }
+ }
+
+ thread::
+ thread (void* (*func) (void*), void* arg)
+ {
+ unique_ptr<data> d (new data);
+ d->func = func;
+ d->arg = arg;
+ d->count = 2; // One for the thread and one for us.
+
+ handle_ = (HANDLE)_beginthreadex (
+ 0, 0, &odb_thread_thunk, d.get (), 0, 0);
+
+ if (handle_ == 0)
+ throw win32_exception ();
+
+ data_ = d.release ();
+ }
+
+ void* thread::
+ join ()
+ {
+ void* r;
+
+ if (WaitForSingleObject (handle_, INFINITE) != 0)
+ throw win32_exception ();
+
+ r = data_->ret;
+
+ CloseHandle (handle_);
+ delete data_;
+ handle_ = 0;
+ data_ = 0;
+ return r;
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/thread.hxx b/libodb/odb/details/win32/thread.hxx
new file mode 100644
index 0000000..a4e1a15
--- /dev/null
+++ b/libodb/odb/details/win32/thread.hxx
@@ -0,0 +1,59 @@
+// file : odb/details/win32/thread.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_THREAD_HXX
+#define ODB_DETAILS_WIN32_THREAD_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/win32/windows.hxx>
+#include <odb/details/export.hxx>
+#include <odb/details/win32/mutex.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class LIBODB_EXPORT thread
+ {
+ public:
+ ~thread ();
+ thread (void* (*thread_func) (void*), void* arg = 0);
+
+ void*
+ join ();
+
+ private:
+ thread (const thread&);
+ thread& operator= (const thread&);
+
+ private:
+ typedef void* (*thread_func) (void*);
+
+ struct data
+ {
+ thread_func func;
+ void* arg;
+ void* ret;
+
+ // Thread-safe reference counter.
+ //
+ details::mutex mutex;
+ unsigned char count;
+ };
+
+
+ public:
+ static void
+ thread_thunk (void*);
+
+ private:
+ HANDLE handle_;
+ data* data_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_THREAD_HXX
diff --git a/libodb/odb/details/win32/tls-init.hxx b/libodb/odb/details/win32/tls-init.hxx
new file mode 100644
index 0000000..0a44a10
--- /dev/null
+++ b/libodb/odb/details/win32/tls-init.hxx
@@ -0,0 +1,26 @@
+// file : odb/details/win32/tls-init.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_TLS_INIT_HXX
+#define ODB_DETAILS_WIN32_TLS_INIT_HXX
+
+#include <odb/pre.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ void
+ tls_process_start ();
+
+ void
+ tls_process_end (bool safe);
+
+ void
+ tls_thread_end ();
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_TLS_INIT_HXX
diff --git a/libodb/odb/details/win32/tls.cxx b/libodb/odb/details/win32/tls.cxx
new file mode 100644
index 0000000..2edc364
--- /dev/null
+++ b/libodb/odb/details/win32/tls.cxx
@@ -0,0 +1,245 @@
+// file : odb/details/win32/tls.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/win32/windows.hxx>
+#include <winerror.h> // ERROR_INVALID_INDEX
+
+#include <new>
+#include <cstddef> // std::size_t
+
+#include <odb/details/win32/lock.hxx>
+#include <odb/details/win32/tls.hxx>
+#include <odb/details/win32/tls-init.hxx>
+#include <odb/details/win32/exceptions.hxx>
+
+#ifdef _MSC_VER
+# pragma warning (disable:4200) // zero-sized array in struct
+#endif
+
+using namespace std;
+
+namespace odb
+{
+ namespace details
+ {
+ typedef void (*dtor_func) (void*);
+
+ struct entry
+ {
+ void* value;
+ dtor_func dtor;
+ };
+
+ struct thread_data
+ {
+ size_t size;
+ size_t capacity;
+ entry entries[0];
+ };
+
+ struct process_data
+ {
+ size_t size;
+ size_t capacity;
+ dtor_func dtors[0];
+ };
+
+ static DWORD index_ = TLS_OUT_OF_INDEXES;
+ static CRITICAL_SECTION cs_;
+ static process_data* proc_data_;
+
+ const size_t init_capacity = 4;
+
+ void
+ tls_process_start ()
+ {
+ index_ = TlsAlloc ();
+
+ if (index_ == TLS_OUT_OF_INDEXES)
+ throw win32_exception ();
+
+ InitializeCriticalSection (&cs_);
+
+ process_data* pd (
+ static_cast<process_data*> (
+ operator new (
+ sizeof (process_data) + sizeof (dtor_func) * init_capacity)));
+
+ pd->size = 0;
+ pd->capacity = init_capacity;
+ memset (pd->dtors, 0, sizeof (dtor_func) * init_capacity);
+
+ proc_data_ = pd;
+ }
+
+ void
+ tls_process_end (bool)
+ {
+ operator delete (proc_data_);
+ DeleteCriticalSection (&cs_);
+
+ if (index_ != TLS_OUT_OF_INDEXES)
+ {
+ if (!TlsFree (index_))
+ throw win32_exception ();
+ }
+ }
+
+ void
+ tls_thread_end ()
+ {
+ if (thread_data* d = static_cast<thread_data*> (TlsGetValue (index_)))
+ {
+ // Call destructors. Implement the pthread semantics in that the
+ // destructors are called until all the values become 0.
+ //
+ for (bool pass (true); pass;)
+ {
+ pass = false;
+
+ for (size_t i (0); i < d->size; ++i)
+ {
+ if (d->entries[i].dtor != 0 && d->entries[i].value != 0)
+ {
+ pass = true;
+ void* tmp (d->entries[i].value);
+ d->entries[i].value = 0;
+ d->entries[i].dtor (tmp);
+ }
+ }
+ }
+
+ operator delete (d);
+ }
+ }
+
+ //
+ // tls_common
+ //
+
+ std::size_t tls_common::
+ _allocate (dtor_func dtor)
+ {
+ win32_lock l (cs_);
+
+ size_t n (proc_data_->size);
+ size_t c (proc_data_->capacity);
+
+ if (n == c)
+ {
+ c *= 2;
+
+ // Try to do "atomic" switch-over so that proc_data_ always points
+ // to memory that can be freed even if this thread is killed in the
+ // middle.
+ //
+ process_data* pd (
+ static_cast<process_data*> (
+ operator new (sizeof (process_data) + sizeof (dtor_func) * c)));
+
+ memcpy (pd->dtors, proc_data_->dtors, n * sizeof (dtor_func));
+ memset (pd->dtors + n, 0, sizeof (dtor_func) * (c - n));
+
+ pd->size = n;
+ pd->capacity = c;
+
+ process_data* old (proc_data_);
+ proc_data_ = pd;
+ operator delete (old);
+ }
+
+ proc_data_->dtors[n] = dtor;
+ return proc_data_->size++;
+ }
+
+ void* tls_common::
+ _get (std::size_t key)
+ {
+ if (thread_data* d = static_cast<thread_data*> (TlsGetValue (index_)))
+ {
+ if (key < d->size)
+ return d->entries[key].value;
+ }
+
+ // Check if this key is valid.
+ //
+ win32_lock l (cs_);
+
+ if (key < proc_data_->size)
+ return 0;
+
+ throw win32_exception (ERROR_INVALID_INDEX);
+ }
+
+ void tls_common::
+ _set (std::size_t key, void* value)
+ {
+ thread_data* d (static_cast<thread_data*> (TlsGetValue (index_)));
+
+ if (d != 0 && key < d->capacity)
+ {
+ if (key >= d->size)
+ {
+ // Check if this key is valid. If so then we need to copy
+ // dtors for new slots.
+ //
+ win32_lock l (cs_);
+
+ size_t n (proc_data_->size);
+
+ if (key >= n)
+ throw win32_exception (ERROR_INVALID_INDEX);
+
+ for (size_t i (d->size); i < n; ++i)
+ d->entries[i].dtor = proc_data_->dtors[i];
+
+ d->size = n;
+ }
+
+ d->entries[key].value = value;
+ }
+ else
+ {
+ // Check if this key is valid. If so then we need to (re)-allocate
+ // our storage.
+ //
+ win32_lock l (cs_);
+
+ size_t n (proc_data_->size);
+
+ if (key >= n)
+ throw win32_exception (ERROR_INVALID_INDEX);
+
+ size_t c (proc_data_->capacity);
+
+ thread_data* nd (
+ static_cast<thread_data*> (
+ operator new (sizeof (thread_data) + sizeof (entry) * c)));
+
+ size_t on (d == 0 ? 0 : d->size);
+
+ // Copy over the data.
+ //
+ if (on != 0)
+ memcpy (nd->entries, d->entries, sizeof (entry) * on);
+
+ // Zero out the rest.
+ //
+ memset (nd->entries + on, 0, sizeof (entry) * (c - on));
+
+ // Assign destructors to new slots [on, n).
+ //
+ for (size_t i (on); i < n; ++i)
+ nd->entries[i].dtor = proc_data_->dtors[i];
+
+ nd->size = n;
+ nd->capacity = c;
+
+ operator delete (d);
+ TlsSetValue (index_, nd);
+
+ nd->entries[key].value = value;
+ }
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/tls.hxx b/libodb/odb/details/win32/tls.hxx
new file mode 100644
index 0000000..2a75cc8
--- /dev/null
+++ b/libodb/odb/details/win32/tls.hxx
@@ -0,0 +1,120 @@
+// file : odb/details/win32/tls.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_TLS_HXX
+#define ODB_DETAILS_WIN32_TLS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/details/export.hxx>
+#include <odb/details/win32/once.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class LIBODB_EXPORT tls_common
+ {
+ public:
+ static std::size_t
+ _allocate (void (*dtor) (void*));
+
+ static void*
+ _get (std::size_t key);
+
+ static void
+ _set (std::size_t key, void* value);
+ };
+
+ template <typename T>
+ class tls: protected tls_common
+ {
+ public:
+ tls ();
+
+ T&
+ get () const;
+
+ void
+ free ();
+
+ private:
+ tls (const tls&);
+ tls& operator= (const tls&);
+
+ private:
+ static void
+ key_init ();
+
+ static void
+ destructor (void*);
+
+ private:
+ static win32_once_t once_;
+ static std::size_t key_;
+ };
+
+ template <typename T>
+ class tls<T*>: protected tls_common
+ {
+ public:
+ tls ();
+
+ T*
+ get () const;
+
+ void
+ set (T* p);
+
+ private:
+ tls (const tls&);
+ tls& operator= (const tls&);
+
+ private:
+ static void
+ key_init ();
+
+ private:
+ static win32_once_t once_;
+ static std::size_t key_;
+ };
+
+ template <typename T>
+ inline T&
+ tls_get (const tls<T>& t)
+ {
+ return t.get ();
+ }
+
+ template <typename T>
+ inline void
+ tls_free (tls<T>& t)
+ {
+ t.free ();
+ }
+
+ template <typename T>
+ inline T*
+ tls_get (const tls<T*>& t)
+ {
+ return t.get ();
+ }
+
+ template <typename T, typename T1>
+ inline void
+ tls_set (tls<T*>& t, T1* p1)
+ {
+ T* p (p1);
+ t.set (p);
+ }
+ }
+}
+
+#include <odb/details/win32/tls.ixx>
+#include <odb/details/win32/tls.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_TLS_HXX
diff --git a/libodb/odb/details/win32/tls.ixx b/libodb/odb/details/win32/tls.ixx
new file mode 100644
index 0000000..fbcc3dd
--- /dev/null
+++ b/libodb/odb/details/win32/tls.ixx
@@ -0,0 +1,20 @@
+// file : odb/details/win32/tls.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename T>
+ inline tls<T>::
+ tls ()
+ {
+ }
+
+ template <typename T>
+ inline tls<T*>::
+ tls ()
+ {
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/tls.txx b/libodb/odb/details/win32/tls.txx
new file mode 100644
index 0000000..96bed4c
--- /dev/null
+++ b/libodb/odb/details/win32/tls.txx
@@ -0,0 +1,94 @@
+// file : odb/details/win32/tls.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/unique-ptr.hxx>
+#include <odb/details/win32/exceptions.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ // tls<T>
+ //
+ template <typename T>
+ win32_once_t tls<T>::once_= WIN32_ONCE_INIT;
+
+ template <typename T>
+ size_t tls<T>::key_;
+
+ template <typename T>
+ T& tls<T>::
+ get () const
+ {
+ win32_once (once_, key_init);
+
+ if (void* v = _get (key_))
+ return *static_cast<T*> (v);
+
+ unique_ptr<T> p (new T);
+ _set (key_, p.get ());
+
+ T& r (*p);
+ p.release ();
+ return r;
+ }
+
+ template <typename T>
+ void tls<T>::
+ free ()
+ {
+ win32_once (once_, key_init);
+
+ if (void* v = _get (key_))
+ {
+ _set (key_, 0);
+ delete static_cast<T*> (v);
+ }
+ }
+
+ template <typename T>
+ void tls<T>::
+ key_init ()
+ {
+ key_ = _allocate (destructor);
+ }
+
+ template <typename T>
+ void tls<T>::
+ destructor (void* v)
+ {
+ delete static_cast<T*> (v);
+ }
+
+ // tls<T*>
+ //
+ template <typename T>
+ win32_once_t tls<T*>::once_ = WIN32_ONCE_INIT;
+
+ template <typename T>
+ size_t tls<T*>::key_;
+
+ template <typename T>
+ T* tls<T*>::
+ get () const
+ {
+ win32_once (once_, key_init);
+ return static_cast<T*> (_get (key_));
+ }
+
+ template <typename T>
+ void tls<T*>::
+ set (T* p)
+ {
+ win32_once (once_, key_init);
+ _set (key_, p);
+ }
+
+ template <typename T>
+ void tls<T*>::
+ key_init ()
+ {
+ key_ = _allocate (0);
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/windows.hxx b/libodb/odb/details/win32/windows.hxx
new file mode 100644
index 0000000..9ff4cb4
--- /dev/null
+++ b/libodb/odb/details/win32/windows.hxx
@@ -0,0 +1,33 @@
+// file : odb/details/win32/windows.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_WINDOWS_HXX
+#define ODB_DETAILS_WIN32_WINDOWS_HXX
+
+#include <odb/pre.hxx>
+
+// Try to include <windows.h> so that it doesn't mess other things up.
+//
+#ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# ifndef NOMINMAX // No min and max macros.
+# define NOMINMAX
+# include <windows.h>
+# undef NOMINMAX
+# else
+# include <windows.h>
+# endif
+# undef WIN32_LEAN_AND_MEAN
+#else
+# ifndef NOMINMAX
+# define NOMINMAX
+# include <windows.h>
+# undef NOMINMAX
+# else
+# include <windows.h>
+# endif
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_WINDOWS_HXX
diff --git a/libodb/odb/details/wrapper-p.hxx b/libodb/odb/details/wrapper-p.hxx
new file mode 100644
index 0000000..8f72b5d
--- /dev/null
+++ b/libodb/odb/details/wrapper-p.hxx
@@ -0,0 +1,38 @@
+// file : odb/details/wrapper-p.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WRAPPER_P_HXX
+#define ODB_DETAILS_WRAPPER_P_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/wrapper-traits.hxx>
+
+#include <odb/details/meta/answer.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ // GCC doesn't like these to be inside wrapper_p.
+ //
+ template <typename T>
+ meta::no
+ wrapper_p_test (...);
+
+ template <typename T>
+ meta::yes
+ wrapper_p_test (typename wrapper_traits<T>::wrapped_type*);
+
+ template <typename T>
+ struct wrapper_p
+ {
+ static const bool r =
+ sizeof (wrapper_p_test<T> (0)) == sizeof (meta::yes);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WRAPPER_P_HXX
diff --git a/libodb/odb/exception.hxx b/libodb/odb/exception.hxx
new file mode 100644
index 0000000..39daf92
--- /dev/null
+++ b/libodb/odb/exception.hxx
@@ -0,0 +1,36 @@
+// file : odb/exception.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_EXCEPTION_HXX
+#define ODB_EXCEPTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <exception>
+
+#include <odb/forward.hxx> // odb::core
+
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+#include <odb/details/export.hxx>
+#include <odb/details/shared-ptr/base.hxx>
+
+namespace odb
+{
+ struct LIBODB_EXPORT exception: std::exception, details::shared_base
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT = 0;
+
+ virtual exception*
+ clone () const = 0;
+ };
+
+ namespace common
+ {
+ using odb::exception;
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_EXCEPTION_HXX
diff --git a/libodb/odb/exceptions.cxx b/libodb/odb/exceptions.cxx
new file mode 100644
index 0000000..bb13b6c
--- /dev/null
+++ b/libodb/odb/exceptions.cxx
@@ -0,0 +1,430 @@
+// file : odb/exceptions.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::strlen
+#include <sstream>
+#include <cassert>
+
+#include <odb/exceptions.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ const char* null_pointer::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "NULL pointer";
+ }
+
+ null_pointer* null_pointer::
+ clone () const
+ {
+ return new null_pointer (*this);
+ }
+
+ const char* already_in_transaction::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "transaction already in progress in this thread";
+ }
+
+ already_in_transaction* already_in_transaction::
+ clone () const
+ {
+ return new already_in_transaction (*this);
+ }
+
+ const char* not_in_transaction::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "operation can only be performed in transaction";
+ }
+
+ not_in_transaction* not_in_transaction::
+ clone () const
+ {
+ return new not_in_transaction (*this);
+ }
+
+ const char* transaction_already_finalized::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "transaction already committed or rolled back";
+ }
+
+ transaction_already_finalized* transaction_already_finalized::
+ clone () const
+ {
+ return new transaction_already_finalized (*this);
+ }
+
+ const char* already_in_session::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "session already in effect in this thread";
+ }
+
+ already_in_session* already_in_session::
+ clone () const
+ {
+ return new already_in_session (*this);
+ }
+
+ const char* not_in_session::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "session not in effect in this thread";
+ }
+
+ not_in_session* not_in_session::
+ clone () const
+ {
+ return new not_in_session (*this);
+ }
+
+ const char* session_required::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "session required to load this object relationship";
+ }
+
+ session_required* session_required::
+ clone () const
+ {
+ return new session_required (*this);
+ }
+
+ const char* deadlock::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "transaction aborted due to deadlock";
+ }
+
+ deadlock* deadlock::
+ clone () const
+ {
+ return new deadlock (*this);
+ }
+
+ const char* connection_lost::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "connection to database lost";
+ }
+
+ connection_lost* connection_lost::
+ clone () const
+ {
+ return new connection_lost (*this);
+ }
+
+ const char* timeout::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "database operation timeout";
+ }
+
+ timeout* timeout::
+ clone () const
+ {
+ return new timeout (*this);
+ }
+
+ const char* object_not_persistent::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "object not persistent";
+ }
+
+ object_not_persistent* object_not_persistent::
+ clone () const
+ {
+ return new object_not_persistent (*this);
+ }
+
+ const char* object_already_persistent::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "object already persistent";
+ }
+
+ object_already_persistent* object_already_persistent::
+ clone () const
+ {
+ return new object_already_persistent (*this);
+ }
+
+ const char* object_changed::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "object changed concurrently";
+ }
+
+ object_changed* object_changed::
+ clone () const
+ {
+ return new object_changed (*this);
+ }
+
+ const char* result_not_cached::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "query result is not cached";
+ }
+
+ result_not_cached* result_not_cached::
+ clone () const
+ {
+ return new result_not_cached (*this);
+ }
+
+ const char* abstract_class::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "database operation on instance of abstract class";
+ }
+
+ abstract_class* abstract_class::
+ clone () const
+ {
+ return new abstract_class (*this);
+ }
+
+ const char* no_type_info::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "no type information";
+ }
+
+ no_type_info* no_type_info::
+ clone () const
+ {
+ return new no_type_info (*this);
+ }
+
+ prepared_already_cached::
+ prepared_already_cached (const char* name)
+ : name_ (name)
+ {
+ what_ = "prepared query '";
+ what_ += name;
+ what_ += "' is already cached";
+ }
+
+ prepared_already_cached::
+ ~prepared_already_cached () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ const char* prepared_already_cached::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ prepared_already_cached* prepared_already_cached::
+ clone () const
+ {
+ return new prepared_already_cached (*this);
+ }
+
+ prepared_type_mismatch::
+ prepared_type_mismatch (const char* name)
+ : name_ (name)
+ {
+ what_ = "type mismatch while looking up prepared query '";
+ what_ += name;
+ what_ += "'";
+ }
+
+ prepared_type_mismatch::
+ ~prepared_type_mismatch () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ const char* prepared_type_mismatch::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ prepared_type_mismatch* prepared_type_mismatch::
+ clone () const
+ {
+ return new prepared_type_mismatch (*this);
+ }
+
+ unknown_schema::
+ unknown_schema (const string& name)
+ : name_ (name)
+ {
+ what_ = "unknown database schema '";
+ what_ += name;
+ what_ += "'";
+ }
+
+ unknown_schema::
+ ~unknown_schema () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ const char* unknown_schema::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ unknown_schema* unknown_schema::
+ clone () const
+ {
+ return new unknown_schema (*this);
+ }
+
+ unknown_schema_version::
+ unknown_schema_version (schema_version v)
+ : version_ (v)
+ {
+ ostringstream os;
+ os << v;
+ what_ = "unknown database schema version ";
+ what_ += os.str ();
+ }
+
+ unknown_schema_version::
+ ~unknown_schema_version () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ const char* unknown_schema_version::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ unknown_schema_version* unknown_schema_version::
+ clone () const
+ {
+ return new unknown_schema_version (*this);
+ }
+
+ const char* section_not_loaded::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "section is not loaded";
+ }
+
+ section_not_loaded* section_not_loaded::
+ clone () const
+ {
+ return new section_not_loaded (*this);
+ }
+
+ const char* section_not_in_object::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "section instance is not part of an object (section was copied?)";
+ }
+
+ section_not_in_object* section_not_in_object::
+ clone () const
+ {
+ return new section_not_in_object (*this);
+ }
+
+ // multiple_exceptions
+ //
+ multiple_exceptions::
+ ~multiple_exceptions () ODB_NOTHROW_NOEXCEPT {}
+
+ void multiple_exceptions::
+ insert (size_t p, bool maybe, const odb::exception& e, bool fatal)
+ {
+ details::shared_ptr<odb::exception> pe;
+
+ if (common_exception_ti_ != typeid (e))
+ pe.reset (e.clone ());
+ else
+ {
+ if (common_exception_ == 0)
+ common_exception_.reset (e.clone ());
+
+ pe = common_exception_;
+ }
+
+ set_.insert (value_type (delta_ + p, maybe, pe));
+ fatal_ = fatal_ || fatal;
+ }
+
+ const multiple_exceptions::value_type* multiple_exceptions::
+ lookup (size_t p) const
+ {
+ p += delta_; // Called while populating multiple_exceptions.
+
+ iterator i (set_.find (value_type (p)));
+ return i == set_.end () ? 0 : &*i;
+ }
+
+ void multiple_exceptions::
+ prepare ()
+ {
+ current_ = 0;
+ delta_ = 0;
+ common_exception_.reset ();
+
+ ostringstream os;
+ os << "multiple exceptions, "
+ << attempted_ << " element" << (attempted_ != 1 ? "s" : "") <<
+ " attempted, "
+ << failed () << " failed"
+ << (fatal_ ? ", fatal" : "") << ":";
+
+ for (iterator i (begin ()); i != end ();)
+ {
+ size_t p (i->position ());
+ const odb::exception& e (i->exception ());
+
+ os << '\n';
+
+ if (!i->maybe ())
+ {
+ os << '[' << p << ']';
+ ++i;
+ }
+ else
+ {
+ // In this case we will normally have a large number of maybe
+ // failures in a row (usually the whole batch). So let's try
+ // to represent them all as a single range.
+ //
+ size_t n (0);
+ for (++i; i != end () && i->maybe (); ++i)
+ {
+ assert (&e == &i->exception ()); // The same common exception.
+ n++;
+ }
+
+ if (n == 0)
+ os << '[' << p << ']';
+ else
+ os << '[' << p << '-' << (p + n) << "] (some)";
+ }
+
+ os << ' ' << e.what ();
+ }
+
+ what_ = os.str ();
+ }
+
+ const char* multiple_exceptions::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ multiple_exceptions* multiple_exceptions::
+ clone () const
+ {
+ return new multiple_exceptions (*this);
+ }
+}
diff --git a/libodb/odb/exceptions.hxx b/libodb/odb/exceptions.hxx
new file mode 100644
index 0000000..d283010
--- /dev/null
+++ b/libodb/odb/exceptions.hxx
@@ -0,0 +1,523 @@
+// file : odb/exceptions.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_EXCEPTIONS_HXX
+#define ODB_EXCEPTIONS_HXX
+
+#include <odb/pre.hxx>
+
+#include <set>
+#include <string>
+#include <cstddef> // std::size_t
+#include <typeinfo>
+
+#include <odb/forward.hxx> // schema_version, odb::core
+#include <odb/exception.hxx>
+
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+#include <odb/details/export.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+namespace odb
+{
+ struct LIBODB_EXPORT null_pointer: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual null_pointer*
+ clone () const;
+ };
+
+ // Transaction exceptions.
+ //
+ struct LIBODB_EXPORT already_in_transaction: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual already_in_transaction*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT not_in_transaction: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual not_in_transaction*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT transaction_already_finalized: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual transaction_already_finalized*
+ clone () const;
+ };
+
+ // Session exceptions.
+ //
+ struct LIBODB_EXPORT already_in_session: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual already_in_session*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT not_in_session: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual not_in_session*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT session_required: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual session_required*
+ clone () const;
+ };
+
+ // Database operations exceptions.
+ //
+ struct LIBODB_EXPORT recoverable: odb::exception
+ {
+ // Abstract.
+ };
+
+ struct LIBODB_EXPORT connection_lost: recoverable
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual connection_lost*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT timeout: recoverable
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual timeout*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT deadlock: recoverable
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual deadlock*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT object_not_persistent: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual object_not_persistent*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT object_already_persistent: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual object_already_persistent*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT object_changed: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual object_changed*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT result_not_cached: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual result_not_cached*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT database_exception: odb::exception
+ {
+ // Abstract.
+ };
+
+ // Polymorphism support exceptions.
+ //
+ struct LIBODB_EXPORT abstract_class: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual abstract_class*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT no_type_info: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual no_type_info*
+ clone () const;
+ };
+
+ // Prepared query support exceptions.
+ //
+ struct LIBODB_EXPORT prepared_already_cached: odb::exception
+ {
+ prepared_already_cached (const char* name);
+ ~prepared_already_cached () ODB_NOTHROW_NOEXCEPT;
+
+ const char*
+ name () const
+ {
+ return name_;
+ }
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual prepared_already_cached*
+ clone () const;
+
+ private:
+ const char* name_;
+ std::string what_;
+ };
+
+ struct LIBODB_EXPORT prepared_type_mismatch: odb::exception
+ {
+ prepared_type_mismatch (const char* name);
+ ~prepared_type_mismatch () ODB_NOTHROW_NOEXCEPT;
+
+ const char*
+ name () const {return name_;}
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual prepared_type_mismatch*
+ clone () const;
+
+ private:
+ const char* name_;
+ std::string what_;
+ };
+
+ // Schema catalog exceptions.
+ //
+ struct LIBODB_EXPORT unknown_schema: odb::exception
+ {
+ unknown_schema (const std::string& name);
+ ~unknown_schema () ODB_NOTHROW_NOEXCEPT;
+
+ const std::string&
+ name () const {return name_;}
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual unknown_schema*
+ clone () const;
+
+ private:
+ std::string name_;
+ std::string what_;
+ };
+
+ struct LIBODB_EXPORT unknown_schema_version: odb::exception
+ {
+ unknown_schema_version (schema_version);
+ ~unknown_schema_version () ODB_NOTHROW_NOEXCEPT;
+
+ schema_version
+ version () const {return version_;}
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual unknown_schema_version*
+ clone () const;
+
+ private:
+ schema_version version_;
+ std::string what_;
+ };
+
+ // Section exceptions.
+ //
+ struct LIBODB_EXPORT section_not_loaded: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual section_not_loaded*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT section_not_in_object: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual section_not_in_object*
+ clone () const;
+ };
+
+ // Bulk operation exceptions.
+ //
+ struct LIBODB_EXPORT multiple_exceptions: odb::exception
+ {
+ struct value_type
+ {
+ std::size_t
+ position () const {return p_;}
+
+ // If true, then this means that some positions in the batch have
+ // triggered the exception but it is not possible, due to the
+ // limitations of the underlying database API, to discern exactly
+ // which ones. As a result, all the positions in the batch are
+ // marked as "maybe failed".
+ //
+ bool
+ maybe () const {return m_;}
+
+ const odb::exception&
+ exception () const {return *e_;}
+
+ // Implementation details.
+ //
+ public:
+ value_type (std::size_t p,
+ bool maybe,
+ details::shared_ptr<odb::exception> e)
+ : m_ (maybe), p_ (p), e_ (e) {}
+
+ value_type (std::size_t p): p_ (p) {} // "Key" for set lookup.
+
+ private:
+ bool m_;
+ std::size_t p_;
+ details::shared_ptr<odb::exception> e_;
+ };
+
+ struct comparator_type
+ {
+ bool
+ operator() (const value_type& x, const value_type& y) const
+ {
+ return x.position () < y.position ();
+ }
+ };
+
+ typedef std::set<value_type, comparator_type> set_type;
+
+ // Iteration.
+ //
+ public:
+ typedef set_type::const_iterator iterator;
+ typedef set_type::const_iterator const_iterator; // For pedantic types.
+
+ iterator
+ begin () const {return set_.begin ();}
+
+ iterator
+ end () const {return set_.end ();}
+
+ // Lookup.
+ //
+ public:
+ // Return NULL if the element at this position has no exception. Note
+ // that the returned value is value_type* and not odb::exception* in
+ // order to provide access to maybe(); see value_type::maybe() for
+ // details.
+ //
+ const value_type*
+ operator[] (std::size_t p) const
+ {
+ return set_.empty () ? 0 : lookup (p);
+ }
+
+ // Severity, failed and attempt counts.
+ //
+ public:
+ // Return the number of elements for which the operation has been
+ // attempted.
+ //
+ std::size_t
+ attempted () const {return attempted_;}
+
+ // Return the number of positions for which the operation has failed.
+ // Note that this count includes the maybe failed positions.
+ //
+ std::size_t
+ failed () const {return set_.size ();}
+
+ // If fatal() returns true, then (some of) the exceptions were fatal.
+ // In this case, even for elements that were processed but did not
+ // cause the exception, no attempts were made to complete the bulk
+ // operation and the transaction must be aborted.
+ //
+ // If fatal() returns false, then the operation on the elements that
+ // don't have an exception has succeeded. The application can try to
+ // correct the errors and re-attempt the operation on the elements
+ // that did cause an exception. In either case, the transaction can
+ // be committed.
+ //
+ bool
+ fatal () const {return fatal_;}
+
+ // Normally you shouldn't need to do this explicitly but you can
+ // "upgrade" an exception to fatal, for example, for specific
+ // database error codes.
+ //
+ void
+ fatal (bool f) {fatal_ = fatal_ || f;}
+
+ // odb::exception interface.
+ //
+ public:
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual multiple_exceptions*
+ clone () const;
+
+ // Direct set access.
+ //
+ public:
+ const set_type&
+ set () const {return set_;}
+
+ // Implementation details.
+ //
+ public:
+ ~multiple_exceptions () ODB_NOTHROW_NOEXCEPT;
+
+ // All instances of the common exception must be equal since we are
+ // going to create and share just one.
+ //
+ multiple_exceptions (const std::type_info& common_exception_ti)
+ : common_exception_ti_ (common_exception_ti),
+ fatal_ (false),
+ delta_ (0),
+ current_ (0) {}
+
+ // Set the attempted count as (delta + n).
+ //
+ void
+ attempted (std::size_t n) {attempted_ = delta_ + n;}
+
+ // Increment the position of the current batch. Also resets the
+ // current position in the batch.
+ //
+ void
+ delta (std::size_t d) {delta_ += d; current_ = 0;}
+
+ // Current position in the batch.
+ //
+ std::size_t
+ current () const {return current_;}
+
+ void
+ current (std::size_t c) {current_ = c;}
+
+ void
+ insert (std::size_t p,
+ bool maybe,
+ const odb::exception& e,
+ bool fatal = false);
+
+ void
+ insert (std::size_t p, const odb::exception& e, bool fatal = false)
+ {
+ insert (p, false, e, fatal);
+ }
+
+ void
+ insert (const odb::exception& e, bool fatal = false)
+ {
+ insert (current_, e, fatal);
+ }
+
+ bool
+ empty () const {return set_.empty ();}
+
+ void
+ prepare ();
+
+ private:
+ const value_type*
+ lookup (std::size_t p) const;
+
+ private:
+ const std::type_info& common_exception_ti_;
+ details::shared_ptr<odb::exception> common_exception_;
+
+ set_type set_;
+ bool fatal_;
+ std::size_t attempted_;
+ std::size_t delta_; // Position of the batch.
+ std::size_t current_; // Position in the batch.
+ std::string what_;
+ };
+
+ namespace common
+ {
+ using odb::null_pointer;
+
+ using odb::already_in_transaction;
+ using odb::not_in_transaction;
+ using odb::transaction_already_finalized;
+
+ using odb::already_in_session;
+ using odb::not_in_session;
+ using odb::session_required;
+
+ using odb::recoverable;
+ using odb::deadlock;
+ using odb::connection_lost;
+ using odb::timeout;
+ using odb::object_not_persistent;
+ using odb::object_already_persistent;
+ using odb::object_changed;
+ using odb::result_not_cached;
+ using odb::database_exception;
+
+ using odb::abstract_class;
+ using odb::no_type_info;
+
+ using odb::unknown_schema;
+ using odb::unknown_schema_version;
+
+ using odb::section_not_loaded;
+ using odb::section_not_in_object;
+
+ using odb::multiple_exceptions;
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_EXCEPTIONS_HXX
diff --git a/libodb/odb/forward.hxx b/libodb/odb/forward.hxx
new file mode 100644
index 0000000..6f1176d
--- /dev/null
+++ b/libodb/odb/forward.hxx
@@ -0,0 +1,178 @@
+// file : odb/forward.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_FORWARD_HXX
+#define ODB_FORWARD_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/details/export.hxx>
+#include <odb/details/shared-ptr-fwd.hxx>
+
+namespace odb
+{
+ // Common and core namespaces. The idea is that you can use the
+ // using directive to get database-independent (common) names or
+ // all core names (core).
+ //
+ namespace common {}
+
+ namespace core
+ {
+ using namespace common;
+ }
+
+ //
+ //
+ typedef unsigned long long schema_version;
+ struct schema_version_migration;
+
+ class database;
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+ class transaction;
+ class statement;
+ class session;
+ class section;
+ template <typename T> class result;
+
+ namespace common
+ {
+ using odb::schema_version;
+ using odb::schema_version_migration;
+ using odb::session;
+ using odb::section;
+ using odb::result;
+ }
+
+ namespace core
+ {
+ using odb::database;
+ using odb::connection;
+ using odb::connection_ptr;
+ using odb::transaction;
+ using odb::statement;
+ }
+
+ // Tracing.
+ //
+ class tracer; // Not in core.
+ extern LIBODB_EXPORT tracer& stderr_tracer;
+ extern LIBODB_EXPORT tracer& stderr_full_tracer;
+
+ namespace common
+ {
+ using odb::stderr_tracer;
+ }
+
+ // Implementation details.
+ //
+
+ // Keep real databases first since their enumerators are used as array
+ // indexes.
+ //
+ enum database_id
+ {
+ id_mysql,
+ id_sqlite,
+ id_pgsql,
+ id_oracle,
+ id_mssql,
+ id_common
+ };
+
+ // Number of real databases (i.e., excluding default) in the database_id
+ // enum.
+ //
+ const std::size_t database_count = id_common;
+
+ // Traits.
+ //
+ class access
+ {
+ public:
+ template <typename T>
+ class object_traits;
+
+ template <typename T, database_id DB>
+ class object_traits_impl;
+
+ template <typename T, typename P>
+ class object_factory;
+
+ template <typename T>
+ class view_traits;
+
+ template <typename T, database_id DB>
+ class view_traits_impl;
+
+ template <typename T, typename P>
+ class view_factory;
+
+ template <typename T, typename P>
+ class pointer_factory;
+
+ template <typename T, database_id DB>
+ class composite_value_traits;
+
+ template <typename C>
+ class container_traits;
+ };
+
+ template <typename T>
+ struct object_traits;
+
+ template <typename T, database_id DB>
+ struct object_traits_impl;
+
+ template <typename T>
+ struct view_traits;
+
+ template <typename T, database_id DB>
+ struct view_traits_impl;
+
+ // Cache traits.
+ //
+ template <typename T> struct no_id_pointer_cache_traits;
+ template <typename T> struct no_op_pointer_cache_traits;
+ template <typename T, typename S> struct pointer_cache_traits;
+ template <typename T> struct no_id_reference_cache_traits;
+ template <typename T> struct no_op_reference_cache_traits;
+ template <typename T, typename S> struct reference_cache_traits;
+
+ //
+ //
+ class query_base;
+
+ template <typename T>
+ struct query_column;
+
+ //
+ //
+ class result_impl;
+ class prepared_query_impl;
+
+ //
+ //
+ struct multiple_exceptions;
+
+ // Polymorphism support.
+ //
+ template <typename R>
+ struct polymorphic_map;
+
+ namespace details
+ {
+ template <>
+ struct counter_type<connection>
+ {
+ typedef shared_base counter;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_FORWARD_HXX
diff --git a/libodb/odb/function-table.hxx b/libodb/odb/function-table.hxx
new file mode 100644
index 0000000..b1a5a94
--- /dev/null
+++ b/libodb/odb/function-table.hxx
@@ -0,0 +1,50 @@
+// file : odb/function-table.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_FUNCTION_TABLE_HXX
+#define ODB_FUNCTION_TABLE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+
+namespace odb
+{
+ template <typename T, database_id DB>
+ struct object_function_table_entry
+ {
+ typedef access::object_traits_impl<T, id_common> common_traits;
+
+ object_function_table_entry (
+ const typename common_traits::function_table_type* t)
+ {
+ common_traits::function_table[DB] = t;
+ }
+
+ ~object_function_table_entry ()
+ {
+ common_traits::function_table[DB] = 0;
+ }
+ };
+
+ template <typename T, database_id DB>
+ struct view_function_table_entry
+ {
+ typedef access::view_traits_impl<T, id_common> common_traits;
+
+ view_function_table_entry (
+ const typename common_traits::function_table_type* t)
+ {
+ common_traits::function_table[DB] = t;
+ }
+
+ ~view_function_table_entry ()
+ {
+ common_traits::function_table[DB] = 0;
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_FUNCTION_TABLE_HXX
diff --git a/libodb/odb/lazy-pointer-traits.hxx b/libodb/odb/lazy-pointer-traits.hxx
new file mode 100644
index 0000000..2a6c8eb
--- /dev/null
+++ b/libodb/odb/lazy-pointer-traits.hxx
@@ -0,0 +1,141 @@
+// file : odb/lazy-pointer-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_LAZY_POINTER_TRAITS_HXX
+#define ODB_LAZY_POINTER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/pointer-traits.hxx>
+#include <odb/lazy-ptr.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ template <typename T>
+ class pointer_traits< lazy_ptr<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_raw;
+ static const bool lazy = true;
+
+ typedef T element_type;
+ typedef lazy_ptr<element_type> pointer_type;
+ typedef element_type* eager_pointer_type;
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ template <class O /* = T */>
+ static typename object_traits<O>::id_type
+ object_id (const pointer_type& p)
+ {
+ return p.template object_id<O> ();
+ }
+ };
+
+#ifndef ODB_CXX11
+ template <typename T>
+ class pointer_traits< lazy_auto_ptr<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_unique;
+ static const bool lazy = true;
+
+ typedef T element_type;
+ typedef lazy_auto_ptr<element_type> pointer_type;
+ typedef std::auto_ptr<element_type> eager_pointer_type;
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ template <class O /* = T */>
+ static typename object_traits<O>::id_type
+ object_id (const pointer_type& p)
+ {
+ return p.template object_id<O> ();
+ }
+ };
+#endif
+
+#ifdef ODB_CXX11
+ template <typename T, typename D>
+ class pointer_traits<lazy_unique_ptr<T, D>>
+ {
+ public:
+ static const pointer_kind kind = pk_unique;
+ static const bool lazy = true;
+
+ typedef T element_type;
+ typedef lazy_unique_ptr<element_type, D> pointer_type;
+ typedef std::unique_ptr<element_type, D> eager_pointer_type;
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ template <class O /* = T */>
+ static typename object_traits<O>::id_type
+ object_id (const pointer_type& p)
+ {
+ return p.template object_id<O> ();
+ }
+ };
+
+ template <typename T>
+ class pointer_traits<lazy_shared_ptr<T>>
+ {
+ public:
+ static const pointer_kind kind = pk_shared;
+ static const bool lazy = true;
+
+ typedef T element_type;
+ typedef lazy_shared_ptr<element_type> pointer_type;
+ typedef std::shared_ptr<element_type> eager_pointer_type;
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ template <class O /* = T */>
+ static typename object_traits<O>::id_type
+ object_id (const pointer_type& p)
+ {
+ return p.template object_id<O> ();
+ }
+ };
+
+ template <typename T>
+ class pointer_traits<lazy_weak_ptr<T>>
+ {
+ public:
+ static const pointer_kind kind = pk_weak;
+ static const bool lazy = true;
+
+ typedef T element_type;
+ typedef lazy_weak_ptr<element_type> pointer_type;
+ typedef lazy_shared_ptr<element_type> strong_pointer_type;
+ typedef std::weak_ptr<element_type> eager_pointer_type;
+
+ static strong_pointer_type
+ lock (const pointer_type& p)
+ {
+ return p.lock ();
+ }
+ };
+#endif // ODB_CXX11
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_LAZY_POINTER_TRAITS_HXX
diff --git a/libodb/odb/lazy-ptr-impl.hxx b/libodb/odb/lazy-ptr-impl.hxx
new file mode 100644
index 0000000..89fe798
--- /dev/null
+++ b/libodb/odb/lazy-ptr-impl.hxx
@@ -0,0 +1,188 @@
+// file : odb/lazy-ptr-impl.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_LAZY_PTR_IMPL_HXX
+#define ODB_LAZY_PTR_IMPL_HXX
+
+#include <odb/pre.hxx>
+
+#include <utility> // std::move
+
+#include <odb/forward.hxx> // odb::database
+#include <odb/traits.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ struct lazy_ptr_impl_ref
+ {
+ void* id_;
+ database* db_;
+ void* loader_;
+ void (*free_) (void*);
+ void* (*copy_) (const void*);
+ };
+
+ class lazy_ptr_base
+ {
+ public:
+ typedef odb::database database_type;
+
+ ~lazy_ptr_base ();
+ lazy_ptr_base ();
+ lazy_ptr_base (const lazy_ptr_base&);
+ lazy_ptr_base (const lazy_ptr_impl_ref&);
+
+ lazy_ptr_base&
+ operator= (const lazy_ptr_base&);
+
+ lazy_ptr_base&
+ operator= (const lazy_ptr_impl_ref&);
+
+ // C++11 support.
+ //
+ public:
+#ifdef ODB_CXX11
+ lazy_ptr_base (lazy_ptr_base&&) noexcept;
+
+ lazy_ptr_base&
+ operator= (lazy_ptr_base&&) noexcept;
+#endif
+
+ public:
+ // Reset both the id and database.
+ //
+ void
+ reset ();
+
+ // Reset the id.
+ //
+ void
+ reset_id ();
+
+ void
+ swap (lazy_ptr_base&);
+
+ database_type*
+ database () const;
+
+ typedef void* lazy_ptr_base::*unspecified_bool_type;
+ operator unspecified_bool_type () const
+ {
+ return db_ != 0 ? &lazy_ptr_base::id_ : 0;
+ }
+
+ operator lazy_ptr_impl_ref ();
+
+ protected:
+ typedef void (*free_func) (void*);
+ typedef void* (*copy_func) (const void*);
+
+ // Makes a copy of id.
+ //
+ void
+ reset_ (database_type*,
+ void* loader,
+ const void* id,
+ free_func, copy_func);
+
+ template <typename T>
+ static void
+ free (void*);
+
+ template <typename T>
+ static void*
+ copy (const void*);
+
+ template <typename T, typename DB>
+ static typename object_traits<T>::pointer_type
+ loader (database_type&, const typename object_traits<T>::id_type&);
+
+ protected:
+ void* id_;
+ database_type* db_;
+ void* loader_;
+
+ private:
+ free_func free_;
+ copy_func copy_;
+ };
+
+ template <typename T>
+ class lazy_ptr_impl: public lazy_ptr_base
+ {
+ public:
+ lazy_ptr_impl ();
+
+ template <typename DB, typename ID>
+ lazy_ptr_impl (DB&, const ID&);
+
+ lazy_ptr_impl (const lazy_ptr_impl&);
+
+ template <typename Y>
+ lazy_ptr_impl (const lazy_ptr_impl<Y>&);
+
+ lazy_ptr_impl (const lazy_ptr_impl_ref&);
+
+ lazy_ptr_impl&
+ operator= (const lazy_ptr_impl&);
+
+ template <typename Y>
+ lazy_ptr_impl&
+ operator= (const lazy_ptr_impl<Y>&);
+
+ lazy_ptr_impl&
+ operator= (const lazy_ptr_impl_ref&);
+
+ // C++11 support.
+ //
+ public:
+#ifdef ODB_CXX11
+ lazy_ptr_impl (lazy_ptr_impl&&) noexcept;
+
+ template <typename Y>
+ lazy_ptr_impl (lazy_ptr_impl<Y>&&);
+
+ lazy_ptr_impl&
+ operator= (lazy_ptr_impl&&) noexcept;
+
+ template <typename Y>
+ lazy_ptr_impl&
+ operator= (lazy_ptr_impl<Y>&&);
+#endif
+
+ public:
+ using lazy_ptr_base::reset;
+ using lazy_ptr_base::reset_id;
+
+ template <typename DB, typename ID>
+ void
+ reset (DB&, const ID&);
+
+ // Reset the id and set the database to the new value.
+ //
+ template <typename DB>
+ void
+ reset_db (DB&);
+
+ template <typename ID>
+ void
+ reset_id (const ID&);
+
+ template <typename O /* = T */>
+ typename object_traits<O>::pointer_type
+ load (bool reset_id);
+
+ template <typename O /* = T */>
+ typename object_traits<O>::id_type
+ object_id () const;
+ };
+}
+
+#include <odb/lazy-ptr-impl.ixx>
+#include <odb/lazy-ptr-impl.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_LAZY_PTR_IMPL_HXX
diff --git a/libodb/odb/lazy-ptr-impl.ixx b/libodb/odb/lazy-ptr-impl.ixx
new file mode 100644
index 0000000..9ab0471
--- /dev/null
+++ b/libodb/odb/lazy-ptr-impl.ixx
@@ -0,0 +1,397 @@
+// file : odb/lazy-ptr-impl.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ //
+ // lazy_ptr_base
+ //
+
+ inline lazy_ptr_base::
+ lazy_ptr_base ()
+ : id_ (0), db_ (0), loader_ (0), free_ (0), copy_ (0)
+ {
+ }
+
+ inline lazy_ptr_base::
+ lazy_ptr_base (const lazy_ptr_base& r)
+ : id_ (0), db_ (r.db_), loader_ (r.loader_),
+ free_ (r.free_), copy_ (r.copy_)
+ {
+ if (r.id_)
+ id_ = copy_ (r.id_);
+ }
+
+ inline lazy_ptr_base::
+ lazy_ptr_base (const lazy_ptr_impl_ref& r)
+ : id_ (r.id_), db_ (r.db_), loader_ (r.loader_),
+ free_ (r.free_), copy_ (r.copy_)
+ {
+ }
+
+#ifdef ODB_CXX11
+ inline lazy_ptr_base::
+ lazy_ptr_base (lazy_ptr_base&& r) noexcept
+ : id_ (r.id_), db_ (r.db_), loader_ (r.loader_),
+ free_ (r.free_), copy_ (r.copy_)
+ {
+ r.id_ = 0;
+ }
+#endif
+
+ inline void lazy_ptr_base::
+ reset_id ()
+ {
+ if (id_)
+ free_ (id_);
+
+ id_ = 0;
+ }
+
+ inline void lazy_ptr_base::
+ reset_ (database_type* db,
+ void* loader,
+ const void* id,
+ free_func free,
+ copy_func copy)
+ {
+ void* idc (id ? copy (id) : 0);
+
+ if (id_)
+ free_ (id_);
+
+ free_ = free;
+ copy_ = copy;
+
+ id_ = idc;
+ db_ = db;
+ loader_ = loader;
+ }
+
+ inline void lazy_ptr_base::
+ reset ()
+ {
+ reset_id ();
+ db_ = 0;
+ loader_ = 0;
+ }
+
+#ifdef ODB_CXX11
+ inline lazy_ptr_base& lazy_ptr_base::
+ operator= (lazy_ptr_base&& r) noexcept
+ {
+ if (id_ != r.id_)
+ {
+ reset_id ();
+ id_ = r.id_;
+ free_ = r.free_;
+ copy_ = r.copy_;
+
+ r.id_ = 0;
+ }
+
+ db_ = r.db_;
+ loader_ = r.loader_;
+ return *this;
+ }
+#endif
+
+ inline lazy_ptr_base& lazy_ptr_base::
+ operator= (const lazy_ptr_base& r)
+ {
+ if (id_ != r.id_)
+ reset_ (r.db_, r.loader_, r.id_, r.free_, r.copy_);
+ else
+ {
+ db_ = r.db_;
+ loader_ = r.loader_;
+ }
+
+ return *this;
+ }
+
+ inline lazy_ptr_base& lazy_ptr_base::
+ operator= (const lazy_ptr_impl_ref& r)
+ {
+ if (id_ != r.id_)
+ {
+ reset_id ();
+ id_ = r.id_;
+ free_ = r.free_;
+ copy_ = r.copy_;
+ }
+
+ db_ = r.db_;
+ loader_ = r.loader_;
+ return *this;
+ }
+
+ inline lazy_ptr_base::
+ ~lazy_ptr_base ()
+ {
+ if (id_)
+ free_ (id_);
+ }
+
+ inline void lazy_ptr_base::
+ swap (lazy_ptr_base& r)
+ {
+ void* id (id_);
+ database_type* db (db_);
+ void* l (loader_);
+ free_func f (free_);
+ copy_func c (copy_);
+
+ id_ = r.id_;
+ db_ = r.db_;
+ loader_ = r.loader_;
+ free_ = r.free_;
+ copy_ = r.copy_;
+
+ r.id_ = id;
+ r.db_ = db;
+ r.loader_ = l;
+ r.free_ = f;
+ r.copy_ = c;
+ }
+
+ inline lazy_ptr_base::database_type* lazy_ptr_base::
+ database () const
+ {
+ return db_;
+ }
+
+ inline lazy_ptr_base::
+ operator lazy_ptr_impl_ref ()
+ {
+ lazy_ptr_impl_ref r;
+ r.id_ = id_;
+ r.db_ = db_;
+ r.loader_ = loader_;
+ r.free_ = free_;
+ r.copy_ = copy_;
+ id_ = 0;
+ db_ = 0;
+ loader_ = 0;
+ return r;
+ }
+
+ //
+ // lazy_ptr_impl
+ //
+
+ template <typename T>
+ inline lazy_ptr_impl<T>::
+ lazy_ptr_impl ()
+ {
+ }
+
+ template <typename T>
+ template <typename DB, typename ID>
+ inline lazy_ptr_impl<T>::
+ lazy_ptr_impl (DB& db, const ID& id)
+ {
+ typedef typename object_traits<T>::id_type id_type;
+ typedef typename object_traits<T>::pointer_type pointer_type;
+ typedef pointer_type (*loader_type) (database_type&, const id_type&);
+
+ // Make sure that ID and T's object id types are the same
+ // (or implicit-convertible). If you get a compile error
+ // pointing here, then you most likely used a wrong object
+ // id argument in the constructor call.
+ //
+ const id_type& r (id);
+
+ // Compiler error pointing here? Perhaps db is not an
+ // odb::<database>::database instance?
+ //
+ database_type& bdb (db);
+
+ // For some reason GCC needs this statically-typed pointer in
+ // order to instantiate the functions.
+ //
+ loader_type ldr (&loader<T, DB>);
+
+ reset_ (&bdb,
+ reinterpret_cast<void*> (ldr),
+ &r,
+ &free<id_type>,
+ &copy<id_type>);
+ }
+
+ template <typename T>
+ inline lazy_ptr_impl<T>::
+ lazy_ptr_impl (const lazy_ptr_impl& r)
+ : lazy_ptr_base (r)
+ {
+ }
+
+ template <typename T>
+ template <typename Y>
+ inline lazy_ptr_impl<T>::
+ lazy_ptr_impl (const lazy_ptr_impl<Y>& r)
+ : lazy_ptr_base (r)
+ {
+ }
+
+ template <typename T>
+ inline lazy_ptr_impl<T>::
+ lazy_ptr_impl (const lazy_ptr_impl_ref& r)
+ : lazy_ptr_base (r)
+ {
+ }
+
+ template <typename T>
+ inline lazy_ptr_impl<T>& lazy_ptr_impl<T>::
+ operator= (const lazy_ptr_impl& r)
+ {
+ lazy_ptr_base& b (*this);
+ b = r;
+ return *this;
+ }
+
+ template <typename T>
+ template <typename Y>
+ inline lazy_ptr_impl<T>& lazy_ptr_impl<T>::
+ operator= (const lazy_ptr_impl<Y>& r)
+ {
+ lazy_ptr_base& b (*this);
+ b = r;
+ return *this;
+ }
+
+ template <typename T>
+ inline lazy_ptr_impl<T>& lazy_ptr_impl<T>::
+ operator= (const lazy_ptr_impl_ref& r)
+ {
+ lazy_ptr_base& b (*this);
+ b = r;
+ return *this;
+ }
+
+#ifdef ODB_CXX11
+ template <typename T>
+ inline lazy_ptr_impl<T>::
+ lazy_ptr_impl (lazy_ptr_impl&& r) noexcept
+ : lazy_ptr_base (std::move (r))
+ {
+ }
+
+ template <typename T>
+ template <typename Y>
+ inline lazy_ptr_impl<T>::
+ lazy_ptr_impl (lazy_ptr_impl<Y>&& r)
+ : lazy_ptr_base (std::move (r))
+ {
+ }
+
+ template <typename T>
+ inline lazy_ptr_impl<T>& lazy_ptr_impl<T>::
+ operator= (lazy_ptr_impl&& r) noexcept
+ {
+ lazy_ptr_base& b (*this);
+ b = std::move (r);
+ return *this;
+ }
+
+ template <typename T>
+ template <typename Y>
+ inline lazy_ptr_impl<T>& lazy_ptr_impl<T>::
+ operator= (lazy_ptr_impl<Y>&& r)
+ {
+ lazy_ptr_base& b (*this);
+ b = std::move (r);
+ return *this;
+ }
+#endif
+
+ template <typename T>
+ template <typename DB, typename ID>
+ inline void lazy_ptr_impl<T>::
+ reset (DB& db, const ID& id)
+ {
+ typedef typename object_traits<T>::id_type id_type;
+ typedef typename object_traits<T>::pointer_type pointer_type;
+ typedef pointer_type (*loader_type) (database_type&, const id_type&);
+
+ // Make sure that ID and T's object id types are the same
+ // (or implicit-convertible). If you get a compile error
+ // pointing here, then you most likely used a wrong object
+ // id argument in the constructor call.
+ //
+ const id_type& r (id);
+
+ // Compiler error pointing here? Perhaps db is not an
+ // odb::<database>::database instance?
+ //
+ database_type& bdb (db);
+
+ // For some reason GCC needs this statically-typed pointer in
+ // order to instantiate the functions.
+ //
+ loader_type ldr (&loader<T, DB>);
+
+ reset_ (&bdb,
+ reinterpret_cast<void*> (ldr),
+ &r,
+ &free<id_type>,
+ &copy<id_type>);
+ }
+
+ template <typename T>
+ template <typename DB>
+ inline void lazy_ptr_impl<T>::
+ reset_db (DB& db)
+ {
+ typedef typename object_traits<T>::id_type id_type;
+ typedef typename object_traits<T>::pointer_type pointer_type;
+ typedef pointer_type (*loader_type) (database_type&, const id_type&);
+
+ reset_id ();
+
+ // Compiler error pointing here? Perhaps db is not an
+ // odb::<database>::database instance?
+ //
+ db_ = &db;
+
+ // For some reason GCC needs this statically-typed pointer in
+ // order to instantiate the functions.
+ //
+ loader_type ldr (&loader<T, DB>);
+ loader_ = reinterpret_cast<void*> (ldr);
+ }
+
+ template <typename T>
+ template <typename ID>
+ inline void lazy_ptr_impl<T>::
+ reset_id (const ID& id)
+ {
+ typedef typename object_traits<T>::id_type id_type;
+
+ // Make sure that ID and T's object id types are the same
+ // (or implicit-convertible). If you get a compile error
+ // pointing here, then you most likely used a wrong object
+ // id argument in the constructor call.
+ //
+ const id_type& r (id);
+
+ reset_ (db_, loader_, &r, &free<id_type>, &copy<id_type>);
+ }
+
+ template <typename T>
+ template <typename O>
+ inline typename object_traits<O>::id_type lazy_ptr_impl<T>::
+ object_id () const
+ {
+ typedef typename object_traits<T>::id_type id_type;
+ const id_type& id (*static_cast<const id_type*> (id_));
+
+ // Make sure that O' and T's object id types are the same
+ // (or implicit-convertible). If you get a compile error
+ // pointing here, then you most likely used a wrong type
+ // as a template argument in the object_id() call.
+ //
+ const typename object_traits<O>::id_type& r (id);
+ return r;
+ }
+}
diff --git a/libodb/odb/lazy-ptr-impl.txx b/libodb/odb/lazy-ptr-impl.txx
new file mode 100644
index 0000000..7aea9c3
--- /dev/null
+++ b/libodb/odb/lazy-ptr-impl.txx
@@ -0,0 +1,60 @@
+// file : odb/lazy-ptr-impl.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ //
+ // lazy_ptr_base
+ //
+
+ template <typename T>
+ void lazy_ptr_base::
+ free (void* p)
+ {
+ delete static_cast<T*> (p);
+ }
+
+ template <typename T>
+ void* lazy_ptr_base::
+ copy (const void* p)
+ {
+ return new T (*static_cast<const T*> (p));
+ }
+
+ template <typename T, typename DB>
+ typename object_traits<T>::pointer_type lazy_ptr_base::
+ loader (database_type& db, const typename object_traits<T>::id_type& id)
+ {
+ // Compiler error pointing here? Perhaps you did not include
+ // <odb/database.hxx>?
+ //
+ return static_cast<DB&> (db).template load<
+ typename object_traits<T>::object_type> (id);
+ }
+
+ //
+ // lazy_ptr_impl
+ //
+
+ template <typename T>
+ template <typename O>
+ inline typename object_traits<O>::pointer_type lazy_ptr_impl<T>::
+ load (bool reset)
+ {
+ typedef typename object_traits<T>::id_type id_type;
+ typedef typename object_traits<T>::pointer_type pointer_type;
+ typedef pointer_type (*loader_type) (database_type&, const id_type&);
+
+ loader_type loader (reinterpret_cast<loader_type> (loader_));
+ const id_type& id (*static_cast<const id_type*> (id_));
+ pointer_type p (loader (*db_, id));
+
+ if (reset)
+ reset_id ();
+
+ // If you get a compile error pointing here, then you most likely
+ // used a wrong type as a template argument in the load() call.
+ //
+ return p;
+ }
+}
diff --git a/libodb/odb/lazy-ptr.hxx b/libodb/odb/lazy-ptr.hxx
new file mode 100644
index 0000000..ab31dfc
--- /dev/null
+++ b/libodb/odb/lazy-ptr.hxx
@@ -0,0 +1,681 @@
+// file : odb/lazy-ptr.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_LAZY_PTR_HXX
+#define ODB_LAZY_PTR_HXX
+
+#include <odb/pre.hxx>
+
+#include <memory> // std::auto_ptr, std::shared_ptr/weak_ptr
+#include <utility> // std::move
+
+#include <odb/forward.hxx> // odb::core, odb::database
+#include <odb/traits.hxx>
+#include <odb/lazy-ptr-impl.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ // Raw pointer lazy version.
+ //
+ template <class T>
+ class lazy_ptr
+ {
+ // Pointer interface.
+ //
+ public:
+ typedef T element_type;
+
+ lazy_ptr ();
+ template <class Y> lazy_ptr (Y*);
+
+ lazy_ptr (const lazy_ptr&);
+ template <class Y> lazy_ptr (const lazy_ptr<Y>&);
+
+ lazy_ptr& operator= (const lazy_ptr&);
+ template <class Y> lazy_ptr& operator= (Y*);
+ template <class Y> lazy_ptr& operator= (const lazy_ptr<Y>&);
+
+ void swap (lazy_ptr&);
+ void reset ();
+ template <class Y> void reset (Y*);
+
+ T& operator* () const;
+ T* operator-> () const;
+ T* get () const;
+
+ typedef T* lazy_ptr::*unspecified_bool_type;
+ operator unspecified_bool_type () const
+ {
+ return (p_ || i_) ? &lazy_ptr::p_ : 0;
+ }
+
+ // Lazy loading interface.
+ //
+ public:
+ typedef odb::database database_type;
+
+ // 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;
+
+ 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.
+ //
+ T* get_eager () const;
+
+ template <class DB, class ID> lazy_ptr (DB&, const ID&);
+ template <class DB, class Y> lazy_ptr (DB&, Y*);
+
+ template <class DB, class ID> void reset (DB&, const ID&);
+ template <class DB, class Y> void reset (DB&, Y*);
+
+#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+ template <class O = T>
+#else
+ template <class O /* = T */>
+#endif
+ typename object_traits<O>::id_type object_id () const;
+
+ database_type& database () const;
+
+ // Helpers.
+ //
+ public:
+ template <class Y> bool equal (const lazy_ptr<Y>&) const;
+
+ private:
+ template <class Y> friend class lazy_ptr;
+
+ mutable T* p_;
+ mutable lazy_ptr_impl<T> i_;
+ };
+
+ // operator< and operator<< are not provided.
+ //
+ template <class T, class Y>
+ bool operator== (const lazy_ptr<T>&, const lazy_ptr<Y>&);
+
+ template <class T, class Y>
+ bool operator!= (const lazy_ptr<T>&, const lazy_ptr<Y>&);
+
+ template <class T> void swap (lazy_ptr<T>&, lazy_ptr<T>&);
+
+ // std::auto_ptr lazy version.
+ //
+#ifndef ODB_CXX11
+ template <class T>
+ struct lazy_auto_ptr_ref
+ {
+ explicit lazy_auto_ptr_ref (T*, const lazy_ptr_impl_ref&);
+
+ T* p_;
+ lazy_ptr_impl_ref i_;
+ };
+
+ template <class T>
+ class lazy_auto_ptr
+ {
+ // Standard auto_ptr interface.
+ //
+ public:
+ typedef T element_type;
+
+ explicit lazy_auto_ptr (T* = 0);
+ lazy_auto_ptr (lazy_auto_ptr&);
+ template <class Y> lazy_auto_ptr (lazy_auto_ptr<Y>&);
+
+ lazy_auto_ptr& operator= (lazy_auto_ptr&);
+ template <class Y> lazy_auto_ptr& operator= (lazy_auto_ptr<Y>&);
+
+ T& operator* () const;
+ T* operator-> () const;
+ T* get () const;
+ T* release ();
+ void reset (T* = 0);
+
+ lazy_auto_ptr (const lazy_auto_ptr_ref<T>&);
+ lazy_auto_ptr& operator= (const lazy_auto_ptr_ref<T>&);
+ template <class Y> operator lazy_auto_ptr_ref<Y> ();
+ template <class Y> operator lazy_auto_ptr<Y> ();
+
+ // Extension: conversion to bool.
+ //
+ public:
+ typedef std::auto_ptr<T> lazy_auto_ptr::*unspecified_bool_type;
+ operator unspecified_bool_type () const
+ {
+ return (p_.get () != 0 || i_) ? &lazy_auto_ptr::p_ : 0;
+ }
+
+ // Initialization/assignment from auto_ptr.
+ //
+ public:
+ template <class Y> lazy_auto_ptr (std::auto_ptr<Y>&);
+ lazy_auto_ptr (std::auto_ptr_ref<T>);
+
+ template <class Y> lazy_auto_ptr& operator= (std::auto_ptr<Y>&);
+ lazy_auto_ptr& operator= (std::auto_ptr_ref<T>);
+
+ // Lazy loading interface.
+ //
+ public:
+ typedef odb::database database_type;
+
+ // 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;
+
+ std::auto_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.
+ //
+ std::auto_ptr<T>& get_eager () const;
+
+ template <class DB, class ID> lazy_auto_ptr (DB&, const ID&);
+ template <class DB> lazy_auto_ptr (DB&, T*);
+ template <class DB, class Y> lazy_auto_ptr (DB&, std::auto_ptr<Y>&);
+
+ template <class DB, class ID> void reset (DB&, const ID&);
+ template <class DB> void reset (DB&, T*);
+ template <class DB, class Y> void reset (DB&, std::auto_ptr<Y>&);
+
+ template <class O /* = T */>
+ typename object_traits<O>::id_type object_id () const;
+
+ database_type& database () const;
+
+ private:
+ template <class Y> friend class lazy_auto_ptr;
+
+ // Note that it is possible to have a situation where p_ is NULL,
+ // i_.id is NULL and i_.db is not NULL. This will happen if the
+ // auto_ptr reference returned by load() is transferred to another
+ // pointer or reset.
+ //
+ mutable std::auto_ptr<T> p_;
+ mutable lazy_ptr_impl<T> i_;
+ };
+#endif
+
+#ifdef ODB_CXX11
+
+ // C++11 std::unique_ptr lazy version.
+ //
+ template <class T, class D = std::default_delete<T>>
+ class lazy_unique_ptr
+ {
+ // Standard lazy_unique_ptr interface.
+ //
+ public:
+ typedef T* pointer; // For now assume it is T*.
+ typedef T element_type;
+ typedef D deleter_type;
+
+ /*constexpr*/ lazy_unique_ptr () /*noexcept*/;
+#ifdef ODB_CXX11_NULLPTR
+ /*constexpr*/ lazy_unique_ptr (std::nullptr_t) /*noexcept*/;
+#endif
+ explicit lazy_unique_ptr (pointer) /*noexcept*/;
+
+ // For now assume D is non-reference.
+ //
+ lazy_unique_ptr (pointer, const deleter_type&) /*noexcept*/;
+ lazy_unique_ptr (pointer, deleter_type&&) /*noexcept*/;
+
+ lazy_unique_ptr (lazy_unique_ptr&&) noexcept;
+ template <class T1, class D1> lazy_unique_ptr (lazy_unique_ptr<T1, D1>&&) /*noexcept*/;
+ //template <class T1> lazy_unique_ptr (std::auto_ptr<T1>&&) /*noexcept*/;
+
+#ifdef ODB_CXX11_NULLPTR
+ lazy_unique_ptr& operator= (std::nullptr_t) /*noexcept*/;
+#endif
+ lazy_unique_ptr& operator= (lazy_unique_ptr&&) noexcept;
+ template <class T1, class D1> lazy_unique_ptr& operator= (lazy_unique_ptr<T1, D1>&&) /*noexcept*/;
+
+ T& operator* () const;
+ pointer operator-> () const /*noexcept*/;
+ pointer get () const /*noexcept*/;
+#ifdef ODB_CXX11_EXPLICIT_CONVERSION_OPERATOR
+ explicit operator bool() const /*noexcept*/;
+#else
+ typedef std::unique_ptr<T, D> lazy_unique_ptr::*unspecified_bool_type;
+ operator unspecified_bool_type () const
+ {
+ return (p_ || i_) ? &lazy_unique_ptr::p_ : 0;
+ }
+#endif
+
+ pointer release () /*noexcept*/;
+ void reset (pointer = pointer ()) /*noexcept*/;
+ void swap (lazy_unique_ptr&) /*noexcept*/;
+
+ deleter_type& get_deleter () /*noexcept*/;
+ const deleter_type& get_deleter () const /*noexcept*/;
+
+#ifdef ODB_CXX11_DELETED_FUNCTION
+ lazy_unique_ptr (const lazy_unique_ptr&) = delete;
+ lazy_unique_ptr& operator= (const lazy_unique_ptr&) = delete;
+#else
+ private:
+ lazy_unique_ptr (const lazy_unique_ptr&);
+ lazy_unique_ptr& operator= (const lazy_unique_ptr&);
+#endif
+
+ // Initialization/assignment from unique_ptr.
+ //
+ public:
+ template <class T1, class D1> lazy_unique_ptr (std::unique_ptr<T1, D1>&&) /*noexcept*/;
+ template <class T1, class D1> lazy_unique_ptr& operator= (std::unique_ptr<T1, D1>&&) /*noexcept*/;
+
+ // Lazy loading interface.
+ //
+ public:
+ typedef odb::database database_type;
+
+ // 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;
+
+ std::unique_ptr<T, D>& 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.
+ //
+ std::unique_ptr<T, D>& get_eager () const;
+
+ template <class DB, class ID> lazy_unique_ptr (DB&, const ID&);
+ template <class DB> lazy_unique_ptr (DB&, pointer);
+ template <class DB> lazy_unique_ptr (DB&, pointer, const deleter_type&);
+ template <class DB> lazy_unique_ptr (DB&, pointer, deleter_type&&);
+ template <class DB, class T1, class D1> lazy_unique_ptr (DB&, std::unique_ptr<T1, D1>&&);
+ //template <class DB, class T1> lazy_unique_ptr (DB&, std::auto_ptr<T1>&&);
+
+ template <class DB, class ID> void reset (DB&, const ID&);
+ template <class DB> void reset (DB&, pointer);
+ template <class DB, class T1, class D1> void reset (DB&, std::unique_ptr<T1, D1>&&);
+ //template <class DB, class T1> void reset (DB&, std::auto_ptr<T1>&&);
+
+#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+ template <class O = T>
+#else
+ template <class O /*= T*/>
+#endif
+ typename object_traits<O>::id_type object_id () const;
+
+ database_type& database () const;
+
+ // Helpers.
+ //
+ public:
+ template <class T1, class D1> bool equal (const lazy_unique_ptr<T1, D1>&) const;
+
+ private:
+ template <class T1, class D1> friend class lazy_unique_ptr;
+
+ // Note that it is possible to have a situation where p_ is NULL,
+ // i_.id is NULL and i_.db is not NULL. This will happen if the
+ // unique_ptr reference returned by load() is transferred to
+ // another pointer or reset.
+ //
+ mutable std::unique_ptr<T, D> p_;
+ mutable lazy_ptr_impl<T> i_;
+ };
+
+ template <class T> void swap (lazy_unique_ptr<T>&, lazy_unique_ptr<T>&) /*noexcept*/;
+
+ // operator< and operator<< are not provided.
+ //
+ template <class T1, class D1, class T2, class D2>
+ bool operator== (const lazy_unique_ptr<T1, D1>&, const lazy_unique_ptr<T2, D2>&);
+
+ template <class T1, class D1, class T2, class D2>
+ bool operator!= (const lazy_unique_ptr<T1, D1>&, const lazy_unique_ptr<T2, D2>&);
+
+#ifdef ODB_CXX11_NULLPTR
+ template <class T, class D>
+ bool operator== (const lazy_unique_ptr<T, D>&, std::nullptr_t) /*noexcept*/;
+
+ template <class T, class D>
+ bool operator== (std::nullptr_t, const lazy_unique_ptr<T, D>&) /*noexcept*/;
+
+ template <class T, class D>
+ bool operator!= (const lazy_unique_ptr<T, D>&, std::nullptr_t) /*noexcept*/;
+
+ template <class T, class D>
+ bool operator!= (std::nullptr_t, const lazy_unique_ptr<T, D>&) /*noexcept*/;
+#endif
+
+ // C++11 std::shared_ptr lazy version.
+ //
+ template <class T>
+ class lazy_weak_ptr;
+
+ template <class T>
+ class lazy_shared_ptr
+ {
+ // The standard shared_ptr interface.
+ //
+ public:
+ typedef T element_type;
+
+ /*constexpr*/ lazy_shared_ptr () /*noexcept*/;
+#ifdef ODB_CXX11_NULLPTR
+ /*constexpr*/ lazy_shared_ptr (std::nullptr_t) /*noexcept*/;
+#endif
+ template <class Y> explicit lazy_shared_ptr (Y*);
+ template <class Y, class D> lazy_shared_ptr (Y*, D);
+ template <class Y, class D, class A> lazy_shared_ptr (Y*, D, A);
+#ifdef ODB_CXX11_NULLPTR
+ template <class D> lazy_shared_ptr (std::nullptr_t, D);
+ template <class D, class A> lazy_shared_ptr (std::nullptr_t, D, A);
+#endif
+ template <class Y> lazy_shared_ptr (const lazy_shared_ptr<Y>&, T*) /*noexcept*/;
+
+ lazy_shared_ptr (const lazy_shared_ptr&) /*noexcept*/;
+ template <class Y> lazy_shared_ptr (const lazy_shared_ptr<Y>&) /*noexcept*/;
+ lazy_shared_ptr (lazy_shared_ptr&&) noexcept;
+ template <class Y> lazy_shared_ptr (lazy_shared_ptr<Y>&&) /*noexcept*/;
+ template <class Y> explicit lazy_shared_ptr (const lazy_weak_ptr<Y>&);
+ //template <class Y> explicit lazy_shared_ptr (std::auto_ptr<Y>&&);
+ template <class Y, class D> lazy_shared_ptr (std::unique_ptr<Y, D>&&);
+
+ ~lazy_shared_ptr ();
+
+ lazy_shared_ptr& operator= (const lazy_shared_ptr&) /*noexcept*/;
+ template <class Y> lazy_shared_ptr& operator= (const lazy_shared_ptr<Y>&) /*noexcept*/;
+ lazy_shared_ptr& operator= (lazy_shared_ptr&&) noexcept;
+ template <class Y> lazy_shared_ptr& operator= (lazy_shared_ptr<Y>&&) /*noexcept*/;
+ //template <class Y> lazy_shared_ptr& operator= (std::auto_ptr<Y>&&);
+ template <class Y, class D> lazy_shared_ptr& operator= (std::unique_ptr<Y, D>&&);
+
+ void swap (lazy_shared_ptr&) /*noexcept*/;
+ void reset () /*noexcept*/;
+ template <class Y> void reset (Y*);
+ template <class Y, class D> void reset (Y*, D);
+ template <class Y, class D, class A> void reset (Y*, D, A);
+
+ T* get () const /*noexcept*/;
+ T& operator* () const /*noexcept*/;
+ T* operator-> () const /*noexcept*/;
+ long use_count () const /*noexcept*/;
+ bool unique () const /*noexcept*/;
+#ifdef ODB_CXX11_EXPLICIT_CONVERSION_OPERATOR
+ explicit operator bool () const /*noexcept*/;
+#else
+ typedef std::shared_ptr<T> lazy_shared_ptr::*unspecified_bool_type;
+ operator unspecified_bool_type () const
+ {
+ return (p_ || i_) ? &lazy_shared_ptr::p_ : 0;
+ }
+#endif
+
+ // owner_before () is not provded.
+
+ // Initialization/assignment from shared_ptr and weak_ptr.
+ //
+ public:
+ template <class Y> lazy_shared_ptr (const std::shared_ptr<Y>&);
+ template <class Y> lazy_shared_ptr (std::shared_ptr<Y>&&);
+ template <class Y> explicit lazy_shared_ptr (const std::weak_ptr<Y>&);
+
+ template <class Y> lazy_shared_ptr& operator= (const std::shared_ptr<Y>&);
+ template <class Y> lazy_shared_ptr& operator= (std::shared_ptr<Y>&&);
+
+ // Lazy loading interface.
+ //
+ public:
+ typedef odb::database database_type;
+
+ // 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;
+
+ std::shared_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.
+ //
+ std::shared_ptr<T> get_eager () const;
+
+ template <class DB, class ID> lazy_shared_ptr (DB&, const ID&);
+ template <class DB, class Y> lazy_shared_ptr (DB&, Y*);
+ template <class DB, class Y, class D> lazy_shared_ptr (DB&, Y*, D);
+ template <class DB, class Y, class D, class A> lazy_shared_ptr (DB&, Y*, D, A);
+ //template <class DB, class Y> lazy_shared_ptr (DB&, std::auto_ptr<Y>&&);
+ template <class DB, class Y> lazy_shared_ptr (DB&, const std::shared_ptr<Y>&);
+ template <class DB, class Y> lazy_shared_ptr (DB&, std::shared_ptr<Y>&&);
+ template <class DB, class Y> lazy_shared_ptr (DB&, const std::weak_ptr<Y>&);
+
+ template <class DB, class ID> void reset (DB&, const ID&);
+ template <class DB, class Y> void reset (DB&, Y*);
+ template <class DB, class Y, class D> void reset (DB&, Y*, D);
+ template <class DB, class Y, class D, class A> void reset (DB&, Y*, D, A);
+ //template <class DB, class Y> void reset (DB&, std::auto_ptr<Y>&&);
+ template <class DB, class Y> void reset (DB&, const std::shared_ptr<Y>&);
+ template <class DB, class Y> void reset (DB&, std::shared_ptr<Y>&&);
+
+#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+ template <class O = T>
+#else
+ template <class O /*= T*/>
+#endif
+ typename object_traits<O>::id_type object_id () const;
+
+ database_type& database () const;
+
+ // Helpers.
+ //
+ public:
+ template <class Y> bool equal (const lazy_shared_ptr<Y>&) const;
+
+ private:
+ template <class Y> friend class lazy_shared_ptr;
+ template <class Y> friend class lazy_weak_ptr;
+
+ // For lazy_weak_ptr::lock().
+ //
+ lazy_shared_ptr (std::shared_ptr<T>&& p, const lazy_ptr_impl<T>& i)
+ : p_ (std::move (p)), i_ (i) {}
+
+ private:
+ mutable std::shared_ptr<T> p_;
+ mutable lazy_ptr_impl<T> i_;
+ };
+
+ template <class T> void swap (lazy_shared_ptr<T>&, lazy_shared_ptr<T>&) /*noexcept*/;
+
+ template <class D, class T>
+ D* get_deleter (const lazy_shared_ptr<T>&) /*noexcept*/;
+
+ // operator< and operator<< are not provided.
+ //
+ template <class T, class Y>
+ bool operator== (const lazy_shared_ptr<T>&, const lazy_shared_ptr<Y>&) /*noexcept*/;
+
+ template <class T, class Y>
+ bool operator!= (const lazy_shared_ptr<T>&, const lazy_shared_ptr<Y>&) /*noexcept*/;
+
+#ifdef ODB_CXX11_NULLPTR
+ template <class T>
+ bool operator== (const lazy_shared_ptr<T>&, std::nullptr_t) /*noexcept*/;
+
+ template <class T>
+ bool operator== (std::nullptr_t, const lazy_shared_ptr<T>&) /*noexcept*/;
+
+ template <class T>
+ bool operator!= (const lazy_shared_ptr<T>&, std::nullptr_t) /*noexcept*/;
+
+ template <class T>
+ bool operator!= (std::nullptr_t, const lazy_shared_ptr<T>&) /*noexcept*/;
+#endif
+
+ // C++11 std::weak_ptr lazy version.
+ //
+ template <class T>
+ class lazy_weak_ptr
+ {
+ // The standard weak_ptr interface.
+ //
+ public:
+ typedef T element_type;
+
+ /*constexpr*/ lazy_weak_ptr () /*noexcept*/;
+ template <class Y> lazy_weak_ptr (const lazy_shared_ptr<Y>&) /*noexcept*/;
+ lazy_weak_ptr (const lazy_weak_ptr&) /*noexcept*/;
+ template <class Y> lazy_weak_ptr (const lazy_weak_ptr<Y>&) /*noexcept*/;
+
+ ~lazy_weak_ptr ();
+
+ lazy_weak_ptr& operator= (const lazy_weak_ptr&) /*noexcept*/;
+ template <class Y> lazy_weak_ptr& operator= (const lazy_weak_ptr<Y>&) /*noexcept*/;
+ template <class Y> lazy_weak_ptr& operator= (const lazy_shared_ptr<Y>&) /*noexcept*/;
+
+ void swap (lazy_weak_ptr<T>&) /*noexcept*/;
+ void reset () /*noexcept*/;
+
+ long use_count () const /*noexcept*/;
+ bool expired () const /*noexcept*/;
+
+ lazy_shared_ptr<T> lock () const /*noexcept*/;
+
+ // owner_before () is not provded.
+
+ // Initialization/assignment from shared_ptr and weak_ptr.
+ //
+ public:
+ template <class Y> lazy_weak_ptr (const std::weak_ptr<Y>&);
+ template <class Y> lazy_weak_ptr (const std::shared_ptr<Y>&);
+
+ template <class Y> lazy_weak_ptr& operator= (const std::weak_ptr<Y>&);
+ template <class Y> lazy_weak_ptr& operator= (const std::shared_ptr<Y>&);
+
+ // Lazy loading interface.
+ //
+ public:
+ typedef odb::database database_type;
+
+ // expired() loaded()
+ //
+ // true true expired pointer to transient object
+ // false true valid pointer to persistent object
+ // true false expired pointer to persistent object
+ // false false valid pointer to transient object
+ //
+ bool loaded () const;
+
+ // Performs both lock and load.
+ //
+ std::shared_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.
+ //
+ std::weak_ptr<T> get_eager () const;
+
+ template <class DB, class ID> lazy_weak_ptr (DB&, const ID&);
+ template <class DB, class Y> lazy_weak_ptr (DB&, const std::shared_ptr<Y>&);
+ template <class DB, class Y> lazy_weak_ptr (DB&, const std::weak_ptr<Y>&);
+
+ template <class DB, class ID> void reset (DB&, const ID&);
+ template <class DB, class Y> void reset (DB&, const std::shared_ptr<Y>&);
+ template <class DB, class Y> void reset (DB&, const std::weak_ptr<Y>&);
+
+ // The object_id() function can only be called when the object is
+ // persistent, or: expired() XOR loaded() (can use != for XOR).
+ //
+#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+ template <class O = T>
+#else
+ template <class O /*= T*/>
+#endif
+ typename object_traits<O>::id_type object_id () const;
+
+ database_type& database () const;
+
+ private:
+ template <class Y> friend class lazy_shared_ptr;
+ template <class Y> friend class lazy_weak_ptr;
+
+ mutable std::weak_ptr<T> p_;
+ mutable lazy_ptr_impl<T> i_;
+ };
+
+ // operator< is not provided.
+ //
+ template <class T> void swap (lazy_weak_ptr<T>&, lazy_weak_ptr<T>&);
+
+#endif // ODB_CXX11
+
+ namespace common
+ {
+ using odb::lazy_ptr;
+
+#ifndef ODB_CXX11
+ using odb::lazy_auto_ptr;
+#endif
+
+#ifdef ODB_CXX11
+ using odb::lazy_unique_ptr;
+ using odb::lazy_shared_ptr;
+ using odb::lazy_weak_ptr;
+#endif
+ }
+}
+
+#include <odb/lazy-ptr.ixx>
+#include <odb/lazy-ptr.txx>
+
+#include <odb/lazy-pointer-traits.hxx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_LAZY_PTR_HXX
diff --git a/libodb/odb/lazy-ptr.ixx b/libodb/odb/lazy-ptr.ixx
new file mode 100644
index 0000000..a2d72f5
--- /dev/null
+++ b/libodb/odb/lazy-ptr.ixx
@@ -0,0 +1,1681 @@
+// file : odb/lazy-ptr.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ //
+ // lazy_ptr
+ //
+
+ template <class T>
+ inline lazy_ptr<T>::
+ lazy_ptr (): p_ (0) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_ptr<T>::
+ lazy_ptr (Y* p): p_ (p) {}
+
+ template <class T>
+ inline lazy_ptr<T>::
+ lazy_ptr (const lazy_ptr& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_ptr<T>::
+ lazy_ptr (const lazy_ptr<Y>& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ inline lazy_ptr<T>& lazy_ptr<T>::
+ operator= (const lazy_ptr& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_ptr<T>& lazy_ptr<T>::
+ operator= (Y* r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_ptr<T>& lazy_ptr<T>::
+ operator= (const lazy_ptr<Y>& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ inline void lazy_ptr<T>::
+ swap (lazy_ptr& b)
+ {
+ T* p (p_);
+ p_ = b.p_;
+ b.p_ = p;
+ i_.swap (b.i_);
+ }
+
+ template <class T>
+ inline void lazy_ptr<T>::
+ reset ()
+ {
+ p_ = 0;
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class Y>
+ inline void lazy_ptr<T>::
+ reset (Y* p)
+ {
+ p_ = p;
+ i_.reset ();
+ }
+
+ template <class T>
+ inline T& lazy_ptr<T>::
+ operator* () const
+ {
+ return *p_;
+ }
+
+ template <class T>
+ inline T* lazy_ptr<T>::
+ operator-> () const
+ {
+ return p_;
+ }
+
+ template <class T>
+ inline T* lazy_ptr<T>::
+ get () const
+ {
+ return p_;
+ }
+
+ template <class T>
+ inline bool lazy_ptr<T>::
+ loaded () const
+ {
+ bool i (i_);
+ return (p_ == 0) != i; // !p_ XOR i
+ }
+
+ template <class T>
+ inline T* lazy_ptr<T>::
+ load () const
+ {
+ if (p_ == 0 && i_)
+ p_ = i_.template load<T> (true); // Reset id.
+
+ return p_;
+ }
+
+ template <class T>
+ inline void lazy_ptr<T>::
+ unload () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ if (p_)
+ {
+ if (i_.database () != 0)
+ i_.reset_id (object_traits<object_type>::id (*p_));
+
+ p_ = 0;
+ }
+ }
+
+ template <class T>
+ inline T* lazy_ptr<T>::
+ get_eager () const
+ {
+ return p_;
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline lazy_ptr<T>::
+ lazy_ptr (DB& db, const ID& id): p_ (0), i_ (db, id) {}
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_ptr<T>::
+ lazy_ptr (DB& db, Y* r)
+ : p_ (r)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline void lazy_ptr<T>::
+ reset (DB& db, const ID& id)
+ {
+ p_ = 0;
+ i_.reset (db, id);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_ptr<T>::
+ reset (DB& db, Y* r)
+ {
+ p_ = r;
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class O>
+ inline typename object_traits<O>::id_type lazy_ptr<T>::
+ object_id () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ return p_
+ ? object_traits<object_type>::id (*p_)
+ : i_.template object_id<O> ();
+ }
+
+ template <class T>
+ inline typename lazy_ptr<T>::database_type& lazy_ptr<T>::
+ database () const
+ {
+ return *i_.database ();
+ }
+
+ template <class T, class Y>
+ inline bool
+ operator== (const lazy_ptr<T>& a, const lazy_ptr<Y>& b)
+ {
+ return a.equal (b);
+ }
+
+ template <class T, class Y>
+ inline bool
+ operator!= (const lazy_ptr<T>& a, const lazy_ptr<Y>& b)
+ {
+ return !a.equal (b);
+ }
+
+ template <class T>
+ inline void
+ swap (lazy_ptr<T>& a, lazy_ptr<T>& b)
+ {
+ a.swap (b);
+ }
+
+ //
+ // lazy_auto_ptr_ref
+ //
+#ifndef ODB_CXX11
+
+ template <class T>
+ inline lazy_auto_ptr_ref<T>::
+ lazy_auto_ptr_ref (T* p, const lazy_ptr_impl_ref& i): p_ (p), i_ (i) {}
+
+ //
+ // lazy_auto_ptr
+ //
+
+ template <class T>
+ inline lazy_auto_ptr<T>::
+ lazy_auto_ptr (T* p): p_ (p) {}
+
+ template <class T>
+ inline lazy_auto_ptr<T>::
+ lazy_auto_ptr (lazy_auto_ptr& r)
+ : p_ (r.p_), i_ (static_cast<lazy_ptr_impl_ref> (r.i_))
+ {
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_auto_ptr<T>::
+ lazy_auto_ptr (lazy_auto_ptr<Y>& r)
+ : p_ (r.p_), i_ (static_cast<lazy_ptr_impl_ref> (r.i_))
+ {
+ }
+
+ template <class T>
+ inline lazy_auto_ptr<T>& lazy_auto_ptr<T>::
+ operator= (lazy_auto_ptr& r)
+ {
+ p_ = r.p_;
+ i_ = static_cast<lazy_ptr_impl_ref> (r.i_);
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_auto_ptr<T>& lazy_auto_ptr<T>::
+ operator= (lazy_auto_ptr<Y>& r)
+ {
+ p_ = r.p_;
+ i_ = static_cast<lazy_ptr_impl_ref> (r.i_);
+ return *this;
+ }
+
+ template <class T>
+ inline T& lazy_auto_ptr<T>::
+ operator* () const
+ {
+ return *p_;
+ }
+
+ template <class T>
+ inline T* lazy_auto_ptr<T>::
+ operator-> () const
+ {
+ return p_.operator-> ();
+ }
+
+ template <class T>
+ inline T* lazy_auto_ptr<T>::
+ get () const
+ {
+ return p_.get ();
+ }
+
+ template <class T>
+ inline T* lazy_auto_ptr<T>::
+ release ()
+ {
+ i_.reset ();
+ return p_.release ();
+ }
+
+ template <class T>
+ inline void lazy_auto_ptr<T>::
+ reset (T* p)
+ {
+ i_.reset ();
+ p_.reset (p);
+ }
+
+ template <class T>
+ inline lazy_auto_ptr<T>::
+ lazy_auto_ptr (const lazy_auto_ptr_ref<T>& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ inline lazy_auto_ptr<T>& lazy_auto_ptr<T>::
+ operator= (const lazy_auto_ptr_ref<T>& r)
+ {
+ if (p_.get () != r.p_)
+ p_.reset (r.p_);
+
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_auto_ptr<T>::
+ operator lazy_auto_ptr_ref<Y> ()
+ {
+ return lazy_auto_ptr_ref<Y> (p_.release (), i_);
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_auto_ptr<T>::
+ operator lazy_auto_ptr<Y> ()
+ {
+ return lazy_auto_ptr<Y> (*this);
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_auto_ptr<T>::
+ lazy_auto_ptr (std::auto_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ inline lazy_auto_ptr<T>::
+ lazy_auto_ptr (std::auto_ptr_ref<T> r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_auto_ptr<T>& lazy_auto_ptr<T>::
+ operator= (std::auto_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ inline lazy_auto_ptr<T>& lazy_auto_ptr<T>::
+ operator= (std::auto_ptr_ref<T> r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ inline bool lazy_auto_ptr<T>::
+ loaded () const
+ {
+ bool i (i_);
+ return (p_.get () == 0) != i; // XOR
+ }
+
+ template <class T>
+ inline std::auto_ptr<T>& lazy_auto_ptr<T>::
+ load () const
+ {
+ if (p_.get () == 0 && i_)
+ {
+ std::auto_ptr<T> tmp (i_.template load<T> (true)); // Reset id.
+ p_ = tmp;
+ }
+
+ return p_;
+ }
+
+ template <class T>
+ inline void lazy_auto_ptr<T>::
+ unload () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ if (p_.get () != 0)
+ {
+ if (i_.database () != 0)
+ i_.reset_id (object_traits<object_type>::id (*p_));
+
+ p_.reset ();
+ }
+ }
+
+ template <class T>
+ inline std::auto_ptr<T>& lazy_auto_ptr<T>::
+ get_eager () const
+ {
+ return p_;
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline lazy_auto_ptr<T>::
+ lazy_auto_ptr (DB& db, const ID& id): i_ (db, id) {}
+
+ template <class T>
+ template <class DB>
+ inline lazy_auto_ptr<T>::
+ lazy_auto_ptr (DB& db, T* p)
+ : p_ (p)
+ {
+ if (p)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_auto_ptr<T>::
+ lazy_auto_ptr (DB& db, std::auto_ptr<Y>& p)
+ : p_ (p)
+ {
+ if (p_.get () != 0)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline void lazy_auto_ptr<T>::
+ reset (DB& db, const ID& id)
+ {
+ p_.reset ();
+ i_.reset (db, id);
+ }
+
+ template <class T>
+ template <class DB>
+ inline void lazy_auto_ptr<T>::
+ reset (DB& db, T* p)
+ {
+ p_.reset (p);
+
+ if (p)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_auto_ptr<T>::
+ reset (DB& db, std::auto_ptr<Y>& p)
+ {
+ p_ = p;
+
+ if (p_.get () != 0)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class O>
+ inline typename object_traits<O>::id_type lazy_auto_ptr<T>::
+ object_id () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ return p_.get () != 0
+ ? object_traits<object_type>::id (*p_)
+ : i_.template object_id<O> ();
+ }
+
+ template <class T>
+ inline typename lazy_auto_ptr<T>::database_type& lazy_auto_ptr<T>::
+ database () const
+ {
+ return *i_.database ();
+ }
+#endif
+
+#ifdef ODB_CXX11
+
+ //
+ // lazy_unique_ptr
+ //
+
+ template <class T, class D>
+ lazy_unique_ptr<T, D>::
+ lazy_unique_ptr () {}
+
+#ifdef ODB_CXX11_NULLPTR
+ template <class T, class D>
+ lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (std::nullptr_t) {}
+#endif
+
+ template <class T, class D>
+ lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (pointer p): p_ (p) {}
+
+ template <class T, class D>
+ lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (pointer p, const deleter_type& d): p_ (p, d) {}
+
+ template <class T, class D>
+ lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (pointer p, deleter_type&& d): p_ (p, std::move (d)) {}
+
+ template <class T, class D>
+ lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (lazy_unique_ptr&& r) noexcept
+ : p_ (std::move (r.p_)), i_ (std::move (r.i_)) {}
+
+ template <class T, class D>
+ template <class T1, class D1>
+ lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (lazy_unique_ptr<T1, D1>&& r)
+ : p_ (std::move (r.p_)), i_ (std::move (r.i_)) {}
+
+ // template <class T, class D>
+ // template <class T1>
+ // lazy_unique_ptr<T, D>::
+ // lazy_unique_ptr (std::auto_ptr<T1>&& r): p_ (std::move (r)) {}
+
+#ifdef ODB_CXX11_NULLPTR
+ template <class T, class D>
+ lazy_unique_ptr<T, D>& lazy_unique_ptr<T, D>::
+ operator= (std::nullptr_t)
+ {
+ reset ();
+ return *this;
+ }
+#endif
+
+ template <class T, class D>
+ lazy_unique_ptr<T, D>& lazy_unique_ptr<T, D>::
+ operator= (lazy_unique_ptr&& r) noexcept
+ {
+ p_ = std::move (r.p_);
+ i_ = std::move (r.i_);
+ return *this;
+ }
+
+ template <class T, class D>
+ template <class T1, class D1>
+ lazy_unique_ptr<T, D>& lazy_unique_ptr<T, D>::
+ operator= (lazy_unique_ptr<T1, D1>&& r)
+ {
+ p_ = std::move (r.p_);
+ i_ = std::move (r.i_);
+ return *this;
+ }
+
+ template <class T, class D>
+ T& lazy_unique_ptr<T, D>::
+ operator* () const
+ {
+ return *p_;
+ }
+
+ template <class T, class D>
+ typename lazy_unique_ptr<T, D>::pointer lazy_unique_ptr<T, D>::
+ operator-> () const
+ {
+ return p_.operator-> ();
+ }
+
+ template <class T, class D>
+ typename lazy_unique_ptr<T, D>::pointer lazy_unique_ptr<T, D>::
+ get () const
+ {
+ return p_.get ();
+ }
+
+#ifdef ODB_CXX11_EXPLICIT_CONVERSION_OPERATOR
+ template <class T, class D>
+ lazy_unique_ptr<T, D>::
+ operator bool() const
+ {
+ return p_ || i_;
+ }
+#endif
+
+ template <class T, class D>
+ typename lazy_unique_ptr<T, D>::pointer lazy_unique_ptr<T, D>::
+ release ()
+ {
+ i_.reset ();
+ return p_.release ();
+ }
+
+ template <class T, class D>
+ void lazy_unique_ptr<T, D>::
+ reset (pointer p)
+ {
+ p_.reset (p);
+ i_.reset ();
+ }
+
+ template <class T, class D>
+ void lazy_unique_ptr<T, D>::
+ swap (lazy_unique_ptr& b)
+ {
+ p_.swap (b.p_);
+ i_.swap (b.i_);
+ }
+
+ template <class T, class D>
+ typename lazy_unique_ptr<T, D>::deleter_type& lazy_unique_ptr<T, D>::
+ get_deleter ()
+ {
+ return p_.get_deleter ();
+ }
+
+ template <class T, class D>
+ const typename lazy_unique_ptr<T, D>::deleter_type& lazy_unique_ptr<T, D>::
+ get_deleter () const
+ {
+ return p_.get_deleter ();
+ }
+
+ template <class T, class D>
+ template <class T1, class D1>
+ inline lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (std::unique_ptr<T1, D1>&& p)
+ : p_ (std::move (p))
+ {
+ }
+
+ template <class T, class D>
+ template <class T1, class D1>
+ inline lazy_unique_ptr<T, D>& lazy_unique_ptr<T, D>::
+ operator= (std::unique_ptr<T1, D1>&& p)
+ {
+ p_ = std::move (p);
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T, class D>
+ inline bool lazy_unique_ptr<T, D>::
+ loaded () const
+ {
+ bool i (i_);
+ return !p_ != i; // !p_ XOR i_
+ }
+
+ template <class T, class D>
+ inline std::unique_ptr<T, D>& lazy_unique_ptr<T, D>::
+ load () const
+ {
+ if (!p_ && i_)
+ p_ = std::unique_ptr<T, D> (i_.template load<T> (true)); // Reset id.
+
+ return p_;
+ }
+
+ template <class T, class D>
+ inline void lazy_unique_ptr<T, D>::
+ unload () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ if (p_)
+ {
+ if (i_.database () != 0)
+ i_.reset_id (object_traits<object_type>::id (*p_));
+
+ p_.reset ();
+ }
+ }
+
+ template <class T, class D>
+ inline std::unique_ptr<T, D>& lazy_unique_ptr<T, D>::
+ get_eager () const
+ {
+ return p_;
+ }
+
+ template <class T, class D>
+ template <class DB, class ID>
+ inline lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (DB& db, const ID& id): i_ (db, id) {}
+
+ template <class T, class D>
+ template <class DB>
+ inline lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (DB& db, T* p)
+ : p_ (p)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T, class D>
+ template <class DB>
+ inline lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (DB& db, T* p, const deleter_type& d)
+ : p_ (p, d)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T, class D>
+ template <class DB>
+ inline lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (DB& db, T* p, deleter_type&& d)
+ : p_ (p, std::move (d))
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T, class D>
+ template <class DB, class T1, class D1>
+ inline lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (DB& db, std::unique_ptr<T1, D1>&& p)
+ : p_ (std::move (p))
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ // template <class T, class D>
+ // template <class DB, class T1>
+ // inline lazy_unique_ptr<T, D>::
+ // lazy_unique_ptr (DB& db, std::auto_ptr<T1>&& p)
+ // : p_ (std::move (p))
+ // {
+ // if (p_)
+ // i_.reset_db (db);
+ // }
+
+ template <class T, class D>
+ template <class DB, class ID>
+ inline void lazy_unique_ptr<T, D>::
+ reset (DB& db, const ID& id)
+ {
+ p_.reset ();
+ i_.reset (db, id);
+ }
+
+ template <class T, class D>
+ template <class DB>
+ inline void lazy_unique_ptr<T, D>::
+ reset (DB& db, T* p)
+ {
+ p_.reset (p);
+
+ if (p)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T, class D>
+ template <class DB, class T1, class D1>
+ inline void lazy_unique_ptr<T, D>::
+ reset (DB& db, std::unique_ptr<T1, D1>&& p)
+ {
+ p_ = std::move (p);
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ // template <class T, class D>
+ // template <class DB, class T1>
+ // inline void lazy_unique_ptr<T, D>::
+ // reset (DB& db, std::auto_ptr<T1>&& p)
+ // {
+ // p_ = std::unique_ptr<T, D> (std::move (p));
+ //
+ // if (p_)
+ // i_.reset_db (db);
+ // else
+ // i_.reset ();
+ // }
+
+ template <class T, class D>
+ template <class O>
+ inline typename object_traits<O>::id_type lazy_unique_ptr<T, D>::
+ object_id () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ return p_
+ ? object_traits<object_type>::id (*p_)
+ : i_.template object_id<O> ();
+ }
+
+ template <class T, class D>
+ inline typename lazy_unique_ptr<T, D>::database_type& lazy_unique_ptr<T, D>::
+ database () const
+ {
+ return *i_.database ();
+ }
+
+ template <class T>
+ inline void
+ swap (lazy_unique_ptr<T>& a, lazy_unique_ptr<T>& b)
+ {
+ a.swap (b);
+ }
+
+ template <class T1, class D1, class T2, class D2>
+ inline bool
+ operator== (const lazy_unique_ptr<T1, D1>& a,
+ const lazy_unique_ptr<T2, D2>& b)
+ {
+ return a.equal (b);
+ }
+
+ template <class T1, class D1, class T2, class D2>
+ inline bool
+ operator!= (const lazy_unique_ptr<T1, D1>& a,
+ const lazy_unique_ptr<T2, D2>& b)
+ {
+ return !a.equal (b);
+ }
+
+#ifdef ODB_CXX11_NULLPTR
+ template <class T, class D>
+ inline bool
+ operator== (const lazy_unique_ptr<T, D>& a, std::nullptr_t)
+ {
+ return !a;
+ }
+
+ template <class T, class D>
+ inline bool
+ operator== (std::nullptr_t, const lazy_unique_ptr<T, D>& b)
+ {
+ return !b;
+ }
+
+ template <class T, class D>
+ inline bool
+ operator!= (const lazy_unique_ptr<T, D>& a, std::nullptr_t)
+ {
+ return bool (a); // Explicit to-bool conversion.
+ }
+
+ template <class T, class D>
+ inline bool
+ operator!= (std::nullptr_t, const lazy_unique_ptr<T, D>& b)
+ {
+ return bool (b); // Explicit to-bool conversion.
+ }
+#endif
+
+ //
+ // lazy_shared_ptr
+ //
+
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr () {}
+
+#ifdef ODB_CXX11_NULLPTR
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (std::nullptr_t) {}
+#endif
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (Y* p): p_ (p) {}
+
+ template <class T>
+ template <class Y, class D>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (Y* p, D d): p_ (p, d) {}
+
+ template <class T>
+ template <class Y, class D, class A>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (Y* p, D d, A a): p_ (p, d, a) {}
+
+#ifdef ODB_CXX11_NULLPTR
+ template <class T>
+ template <class D>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (std::nullptr_t p, D d): p_ (p, d) {}
+
+ template <class T>
+ template <class D, class A>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (std::nullptr_t p, D d, A a): p_ (p, d, a) {}
+#endif
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const lazy_shared_ptr<Y>& r, T* p)
+ // r.p_ has to be loaded
+ : p_ (r.p_, p) {}
+
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const lazy_shared_ptr& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const lazy_shared_ptr<Y>& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (lazy_shared_ptr&& r) noexcept
+ : p_ (std::move (r.p_)), i_ (std::move (r.i_)) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (lazy_shared_ptr<Y>&& r)
+ : p_ (std::move (r.p_)), i_ (std::move (r.i_)) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const lazy_weak_ptr<Y>& r): i_ (r.i_)
+ {
+ // If the pointer has expired but can be re-loaded, then don't throw.
+ //
+ p_ = r.lock ().get_eager ();
+
+ if (!p_ && !i_)
+ throw std::bad_weak_ptr ();
+ }
+
+ // template <class T>
+ // template <class Y>
+ // inline lazy_shared_ptr<T>::
+ // lazy_shared_ptr (std::auto_ptr<Y>&& r): p_ (std::move (r)) {}
+
+ template <class T>
+ template <class Y, class D>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (std::unique_ptr<Y, D>&& r): p_ (std::move (r)) {}
+
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ ~lazy_shared_ptr () {}
+
+ template <class T>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (const lazy_shared_ptr& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (const lazy_shared_ptr<Y>& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (lazy_shared_ptr&& r) noexcept
+ {
+ p_ = std::move (r.p_);
+ i_ = std::move (r.i_);
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (lazy_shared_ptr<Y>&& r)
+ {
+ p_ = std::move (r.p_);
+ i_ = std::move (r.i_);
+ return *this;
+ }
+
+ // template <class T>
+ // template <class Y>
+ // inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ // operator= (std::auto_ptr<Y>&& r)
+ // {
+ // p_ = std::move (r);
+ // i_.reset ();
+ // return *this;
+ // }
+
+ template <class T>
+ template <class Y, class D>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (std::unique_ptr<Y, D>&& r)
+ {
+ p_ = std::move (r);
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ inline void lazy_shared_ptr<T>::
+ swap (lazy_shared_ptr& b)
+ {
+ p_.swap (b.p_);
+ i_.swap (b.i_);
+ }
+
+ template <class T>
+ inline void lazy_shared_ptr<T>::
+ reset ()
+ {
+ p_.reset ();
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (Y* p)
+ {
+ p_.reset (p);
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class Y, class D>
+ inline void lazy_shared_ptr<T>::
+ reset (Y* p, D d)
+ {
+ p_.reset (p, d);
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class Y, class D, class A>
+ inline void lazy_shared_ptr<T>::
+ reset (Y* p, D d, A a)
+ {
+ p_.reset (p, d, a);
+ i_.reset ();
+ }
+
+ template <class T>
+ inline T& lazy_shared_ptr<T>::
+ operator* () const
+ {
+ return *p_;
+ }
+
+ template <class T>
+ inline T* lazy_shared_ptr<T>::
+ operator-> () const
+ {
+ return p_.operator-> ();
+ }
+
+ template <class T>
+ inline T* lazy_shared_ptr<T>::
+ get () const
+ {
+ return p_.get ();
+ }
+
+ template <class T>
+ inline bool lazy_shared_ptr<T>::
+ unique () const
+ {
+ return p_.unique ();
+ }
+
+ template <class T>
+ inline long lazy_shared_ptr<T>::
+ use_count () const
+ {
+ return p_.use_count ();
+ }
+
+#ifdef ODB_CXX11_EXPLICIT_CONVERSION_OPERATOR
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ operator bool () const
+ {
+ return p_ || i_;
+ }
+#endif
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const std::shared_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (std::shared_ptr<Y>&& r): p_ (std::move (r)) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const std::weak_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (const std::shared_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (std::shared_ptr<Y>&& r)
+ {
+ p_ = std::move (r);
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ inline bool lazy_shared_ptr<T>::
+ loaded () const
+ {
+ bool i (i_);
+ return !p_ != i; // !p_ XOR i_
+ }
+
+ template <class T>
+ inline std::shared_ptr<T> lazy_shared_ptr<T>::
+ load () const
+ {
+ if (!p_ && i_)
+ p_ = i_.template load<T> (true); // Reset id.
+
+ return p_;
+ }
+
+ template <class T>
+ inline void lazy_shared_ptr<T>::
+ unload () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ if (p_)
+ {
+ if (i_.database () != 0)
+ i_.reset_id (object_traits<object_type>::id (*p_));
+
+ p_.reset ();
+ }
+ }
+
+ template <class T>
+ inline std::shared_ptr<T> lazy_shared_ptr<T>::
+ get_eager () const
+ {
+ return p_;
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, const ID& id): i_ (db, id) {}
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, Y* p)
+ : p_ (p)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y, class D>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, Y* p, D d)
+ : p_ (p, d)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y, class D, class A>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, Y* p, D d, A a)
+ : p_ (p, d, a)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ // template <class T>
+ // template <class DB, class Y>
+ // inline lazy_shared_ptr<T>::
+ // lazy_shared_ptr (DB& db, std::auto_ptr<Y>&& r)
+ // : p_ (std::move (r))
+ // {
+ // if (p_)
+ // i_.reset_db (db);
+ // }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, const std::shared_ptr<Y>& r)
+ : p_ (r)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, std::shared_ptr<Y>&& r)
+ : p_ (std::move (r))
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, const std::weak_ptr<Y>& r)
+ : p_ (r)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, const ID& id)
+ {
+ p_.reset ();
+ i_.reset (db, id);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, Y* p)
+ {
+ p_.reset (p);
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y, class D>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, Y* p, D d)
+ {
+ p_.reset (p, d);
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y, class D, class A>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, Y* p, D d, A a)
+ {
+ p_.reset (p, d, a);
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ // template <class T>
+ // template <class DB, class Y>
+ // inline void lazy_shared_ptr<T>::
+ // reset (DB& db, std::auto_ptr<Y>&& r)
+ // {
+ // p_ = std::move (r);
+ //
+ // if (p_)
+ // i_.reset_db (db);
+ // else
+ // i_.reset ();
+ // }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, const std::shared_ptr<Y>& r)
+ {
+ p_ = r;
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, std::shared_ptr<Y>&& r)
+ {
+ p_ = std::move (r);
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class O>
+ inline typename object_traits<O>::id_type lazy_shared_ptr<T>::
+ object_id () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ return p_
+ ? object_traits<object_type>::id (*p_)
+ : i_.template object_id<O> ();
+ }
+
+ template <class T>
+ inline typename lazy_shared_ptr<T>::database_type& lazy_shared_ptr<T>::
+ database () const
+ {
+ return *i_.database ();
+ }
+
+ template <class T, class Y>
+ inline bool
+ operator== (const lazy_shared_ptr<T>& a, const lazy_shared_ptr<Y>& b)
+ {
+ return a.equal (b);
+ }
+
+ template <class T, class Y>
+ inline bool
+ operator!= (const lazy_shared_ptr<T>& a, const lazy_shared_ptr<Y>& b)
+ {
+ return !a.equal (b);
+ }
+
+#ifdef ODB_CXX11_NULLPTR
+ template <class T>
+ inline bool
+ operator== (const lazy_shared_ptr<T>& p, std::nullptr_t)
+ {
+ return !p;
+ }
+
+ template <class T>
+ inline bool
+ operator== (std::nullptr_t, const lazy_shared_ptr<T>& p)
+ {
+ return !p;
+ }
+
+ template <class T>
+ inline bool
+ operator!= (const lazy_shared_ptr<T>& p, std::nullptr_t)
+ {
+ return bool (p); // Explicit to-bool conversion.
+ }
+
+ template <class T>
+ inline bool
+ operator!= (std::nullptr_t, const lazy_shared_ptr<T>& p)
+ {
+ return bool (p); // Explicit to-bool conversion.
+ }
+#endif
+
+ template <class T>
+ inline void
+ swap (lazy_shared_ptr<T>& a, lazy_shared_ptr<T>& b)
+ {
+ a.swap (b);
+ }
+
+ template <class D, class T>
+ inline D*
+ get_deleter (const lazy_shared_ptr<T>& p)
+ {
+ return std::get_deleter<D> (p.p_);
+ }
+
+ //
+ // lazy_weak_ptr
+ //
+
+ template <class T>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr () {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const lazy_shared_ptr<Y>& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const lazy_weak_ptr& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const lazy_weak_ptr<Y>& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ inline lazy_weak_ptr<T>::
+ ~lazy_weak_ptr () {}
+
+ template <class T>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const lazy_weak_ptr& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const lazy_weak_ptr<Y>& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const lazy_shared_ptr<Y>& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ inline void lazy_weak_ptr<T>::
+ swap (lazy_weak_ptr<T>& r)
+ {
+ p_.swap (r.p_);
+ i_.swap (r.i_);
+ }
+
+ template <class T>
+ inline void lazy_weak_ptr<T>::
+ reset ()
+ {
+ p_.reset ();
+ i_.reset ();
+ }
+
+ template <class T>
+ inline long lazy_weak_ptr<T>::
+ use_count () const
+ {
+ return p_.use_count ();
+ }
+
+ template <class T>
+ inline bool lazy_weak_ptr<T>::
+ expired () const
+ {
+ return p_.expired ();
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const std::weak_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const std::shared_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const std::weak_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const std::shared_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ inline bool lazy_weak_ptr<T>::
+ loaded () const
+ {
+ bool i (i_);
+ return expired () != i; // expired () XOR i_
+ }
+
+ template <class T>
+ inline lazy_shared_ptr<T> lazy_weak_ptr<T>::
+ lock () const
+ {
+ return lazy_shared_ptr<T> (p_.lock (), i_);
+ }
+
+ template <class T>
+ inline std::shared_ptr<T> lazy_weak_ptr<T>::
+ load () const
+ {
+ std::shared_ptr<T> r (p_.lock ());
+
+ if (!r && i_)
+ {
+ r = i_.template load<T> (false); // Keep id.
+ p_ = r;
+ }
+
+ return r;
+ }
+
+ template <class T>
+ inline void lazy_weak_ptr<T>::
+ unload () const
+ {
+ // With weak pointer we always keep i_ up to date.
+ //
+ p_.reset ();
+ }
+
+ template <class T>
+ inline std::weak_ptr<T> lazy_weak_ptr<T>::
+ get_eager () const
+ {
+ return p_;
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (DB& db, const ID& id): i_ (db, id) {}
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (DB& db, const std::shared_ptr<Y>& r)
+ : p_ (r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ if (r)
+ i_.reset (db, object_traits<object_type>::id (*r));
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (DB& db, const std::weak_ptr<Y>& r)
+ : p_ (r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ std::shared_ptr<T> sp (p_.lock ());
+
+ if (sp)
+ i_.reset (db, object_traits<object_type>::id (*sp));
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline void lazy_weak_ptr<T>::
+ reset (DB& db, const ID& id)
+ {
+ p_.reset ();
+ i_.reset (db, id);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_weak_ptr<T>::
+ reset (DB& db, const std::shared_ptr<Y>& r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ p_ = r;
+
+ if (r)
+ i_.reset (db, object_traits<object_type>::id (*r));
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_weak_ptr<T>::
+ reset (DB& db, const std::weak_ptr<Y>& r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ p_ = r;
+ std::shared_ptr<T> sp (p_.lock ());
+
+ if (sp)
+ i_.reset (db, object_traits<object_type>::id (*sp));
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class O>
+ inline typename object_traits<O>::id_type lazy_weak_ptr<T>::
+ object_id () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ std::shared_ptr<T> sp (p_.lock ());
+ return sp
+ ? object_traits<object_type>::id (*sp)
+ : i_.template object_id<O> ();
+ }
+
+ template <class T>
+ inline typename lazy_weak_ptr<T>::database_type& lazy_weak_ptr<T>::
+ database () const
+ {
+ return *i_.database ();
+ }
+
+ template <class T>
+ inline void
+ swap (lazy_weak_ptr<T>& a, lazy_weak_ptr<T>& b)
+ {
+ a.swap (b);
+ }
+
+#endif // ODB_CXX11
+
+}
diff --git a/libodb/odb/lazy-ptr.txx b/libodb/odb/lazy-ptr.txx
new file mode 100644
index 0000000..17a7405
--- /dev/null
+++ b/libodb/odb/lazy-ptr.txx
@@ -0,0 +1,114 @@
+// file : odb/lazy-ptr.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ //
+ // lazy_ptr
+ //
+
+ template <class T>
+ template <class Y>
+ bool lazy_ptr<T>::
+ equal (const lazy_ptr<Y>& r) const
+ {
+ bool t1 ((p_ == 0) == loaded ());
+ bool t2 ((r.p_ == 0) == r.loaded ());
+
+ // If both are transient, then compare the underlying pointers.
+ //
+ if (t1 && t2)
+ return p_ == r.p_;
+
+ // If one is transient and the other is persistent, then compare
+ // the underlying pointers but only if they are non NULL. Note
+ // that an unloaded persistent object is always unequal to a
+ // transient object.
+ //
+ if (t1 || t2)
+ return p_ == r.p_ && p_ != 0;
+
+ // If both objects are persistent, then we compare databases and
+ // object ids.
+ //
+ typedef typename object_traits<T>::object_type object_type1;
+ typedef typename object_traits<Y>::object_type object_type2;
+
+ return i_.database () == r.i_.database () &&
+ object_id<object_type1> () == r.template object_id<object_type2> ();
+ }
+
+#ifdef ODB_CXX11
+
+ //
+ // lazy_unique_ptr
+ //
+
+ template <class T, class D>
+ template <class T1, class D1>
+ bool lazy_unique_ptr<T, D>::
+ equal (const lazy_unique_ptr<T1, D1>& r) const
+ {
+ bool t1 (!p_ == loaded ());
+ bool t2 (!r.p_ == r.loaded ());
+
+ // If both are transient, then compare the underlying pointers.
+ //
+ if (t1 && t2)
+ return p_ == r.p_;
+
+ // If one is transient and the other is persistent, then compare
+ // the underlying pointers but only if they are non NULL. Note
+ // that an unloaded persistent object is always unequal to a
+ // transient object.
+ //
+ if (t1 || t2)
+ return p_ == r.p_ && p_;
+
+ // If both objects are persistent, then we compare databases and
+ // object ids.
+ //
+ typedef typename object_traits<T>::object_type object_type1;
+ typedef typename object_traits<T1>::object_type object_type2;
+
+ return i_.database () == r.i_.database () &&
+ object_id<object_type1> () == r.template object_id<object_type2> ();
+ }
+
+ //
+ // lazy_shared_ptr
+ //
+
+ template <class T>
+ template <class Y>
+ bool lazy_shared_ptr<T>::
+ equal (const lazy_shared_ptr<Y>& r) const
+ {
+ bool t1 (!p_ == loaded ());
+ bool t2 (!r.p_ == r.loaded ());
+
+ // If both are transient, then compare the underlying pointers.
+ //
+ if (t1 && t2)
+ return p_ == r.p_;
+
+ // If one is transient and the other is persistent, then compare
+ // the underlying pointers but only if they are non NULL. Note
+ // that an unloaded persistent object is always unequal to a
+ // transient object.
+ //
+ if (t1 || t2)
+ return p_ == r.p_ && p_;
+
+ // If both objects are persistent, then we compare databases and
+ // object ids.
+ //
+ typedef typename object_traits<T>::object_type object_type1;
+ typedef typename object_traits<Y>::object_type object_type2;
+
+ return i_.database () == r.i_.database () &&
+ object_id<object_type1> () == r.template object_id<object_type2> ();
+ }
+#endif // ODB_CXX11
+
+}
diff --git a/libodb/odb/nested-container.hxx b/libodb/odb/nested-container.hxx
new file mode 100644
index 0000000..d7e4ec1
--- /dev/null
+++ b/libodb/odb/nested-container.hxx
@@ -0,0 +1,218 @@
+// file : odb/nested-container.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_NESTED_CONTAINER_HXX
+#define ODB_NESTED_CONTAINER_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // size_t
+
+#include <odb/forward.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#ifndef ODB_CXX11
+# error nested container support is only available in C++11
+#endif
+
+namespace odb
+{
+ // Nested container emulation support for ODB.
+ //
+ // In a nutshell, the idea is to represent a nested container, for example,
+ // vector<vector<V>>, as map<nested_key, V> where nested_key is a composite
+ // key consisting of the outer and inner container indexes.
+ //
+ // Note that with this approach the empty trailing entries of the outer
+ // container will not be added on load. It is assumed that the user handles
+ // that on their own, for example, by pre-loading the outer container entry
+ // members if there are any.
+ //
+ // Also note that the outer key in the inner container should strictly
+ // speaking be a foreign key pointing to the key of the outer container. The
+ // only way to achieve this currently is to manually add the constraint via
+ // ALTER TABLE ADD CONSTRAINT. Note, however, that as long as we only modify
+ // these tables via the ODB container interface, not having the foreign key
+ // (and not having ON DELETE CASCADE) should be harmless (since we have a
+ // foreign key pointing to the object id).
+
+ // Map key that is used to emulate 1-level nested container mapping (for
+ // example, vector<vector<V>>). Template parameter IC is a tag that allows
+ // us to distinguish keys for unrelated containers in order to assign column
+ // names, etc. Use the inner container type (for example, vector<V>) for IC.
+ //
+ template <typename IC,
+ typename O = std::size_t,
+ typename I = std::size_t>
+ struct nested_key
+ {
+ using outer_type = O;
+ using inner_type = I;
+
+ outer_type outer;
+ inner_type inner;
+
+ nested_key () = default;
+ nested_key (outer_type o, inner_type i): outer (o), inner (i) {}
+
+ bool
+ operator< (const nested_key& v) const
+ {
+ return outer < v.outer || (outer == v.outer && inner < v.inner);
+ }
+ };
+
+ // Map key that is used to emulate 2-level nested container mapping (for
+ // example, vector<vector<vector<V>>>>). Use the middle container type for
+ // MC (for example, vector<vector<V>>).
+ //
+ template <typename MC,
+ typename O = std::size_t,
+ typename M = std::size_t,
+ typename I = std::size_t>
+ struct nested2_key
+ {
+ using outer_type = O;
+ using middle_type = M;
+ using inner_type = I;
+
+ outer_type outer;
+ middle_type middle;
+ inner_type inner;
+
+ nested2_key () = default;
+ nested2_key (outer_type o, middle_type m, inner_type i)
+ : outer (o), middle (m), inner (i) {}
+
+ bool
+ operator< (const nested2_key& v) const
+ {
+ return outer != v.outer ? outer < v.outer :
+ middle != v.middle ? middle < v.middle :
+ inner < v.inner ;
+ }
+ };
+}
+
+#include <map>
+#include <cstddef> // size_t
+#include <utility> // move(), declval()
+#include <cassert>
+#include <type_traits> // remove_reference
+
+namespace odb
+{
+ template <typename C>
+ struct nested1_type:
+ std::remove_reference<decltype (std::declval<C> ()[0])> {};
+
+ template <typename C>
+ struct nested2_type:
+ std::remove_reference<decltype (std::declval<C> ()[0][0])> {};
+
+ template <typename C>
+ struct nested3_type:
+ std::remove_reference<decltype (std::declval<C> ()[0][0][0])> {};
+
+ // 1-level nesting.
+ //
+ template <typename OC> // For example, OC = vector<vector<V>>.
+ std::map<nested_key<typename nested1_type<OC>::type>,
+ typename nested2_type<OC>::type>
+ nested_get (const OC& oc)
+ {
+ using namespace std;
+
+ using IC = typename nested1_type<OC>::type;
+ using V = typename nested2_type<OC>::type;
+ using K = nested_key<IC>;
+
+ map<K, V> r;
+ for (size_t o (0); o != oc.size (); ++o)
+ {
+ const IC& ic (oc[o]);
+ for (size_t i (0); i != ic.size (); ++i)
+ r.emplace (K (o, i), ic[i]);
+ }
+ return r;
+ }
+
+ template <typename K, typename V, typename OC>
+ void
+ nested_set (OC& oc, std::map<K, V>&& r)
+ {
+ using namespace std;
+
+ for (auto& p: r)
+ {
+ size_t o (p.first.outer);
+ size_t i (p.first.inner);
+ V& v (p.second);
+
+ if (o >= oc.size ())
+ oc.resize (o + 1);
+
+ assert (i == oc[o].size ());
+
+ oc[o].push_back (move (v));
+ }
+ }
+
+ // 2-level nesting.
+ //
+ template <typename OC> // For example, OC = vector<vector<vector<V>>>.
+ std::map<nested2_key<typename nested1_type<OC>::type>,
+ typename nested3_type<OC>::type>
+ nested2_get (const OC& oc)
+ {
+ using namespace std;
+
+ using MC = typename nested1_type<OC>::type;
+ using V = typename nested3_type<OC>::type;
+ using K = nested2_key<MC>;
+
+ map<K, V> r;
+ for (size_t o (0); o != oc.size (); ++o)
+ {
+ const auto& mc (oc[o]);
+ for (size_t m (0); m != mc.size (); ++m)
+ {
+ const auto& ic (mc[m]);
+ for (size_t i (0); i != ic.size (); ++i)
+ r.emplace (K (o, m, i), ic[i]);
+ }
+ }
+ return r;
+ }
+
+ template <typename K, typename V, typename OC>
+ void
+ nested2_set (OC& oc, std::map<K, V>&& r)
+ {
+ using namespace std;
+
+ for (auto& p: r)
+ {
+ size_t o (p.first.outer);
+ size_t m (p.first.middle);
+ size_t i (p.first.inner);
+ V& v (p.second);
+
+ if (o >= oc.size ())
+ oc.resize (o + 1);
+
+ auto& mc (oc[o]);
+
+ if (m >= mc.size ())
+ mc.resize (m + 1);
+
+ assert (i == mc[m].size ());
+
+ mc[m].push_back (move (v));
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_NESTED_CONTAINER_HXX
diff --git a/libodb/odb/no-id-object-result.hxx b/libodb/odb/no-id-object-result.hxx
new file mode 100644
index 0000000..556065b
--- /dev/null
+++ b/libodb/odb/no-id-object-result.hxx
@@ -0,0 +1,182 @@
+// file : odb/no-id-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_NO_ID_OBJECT_RESULT_HXX
+#define ODB_NO_ID_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+#include <utility> // std::move
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+#include <odb/result.hxx>
+#include <odb/object-result.hxx>
+#include <odb/pointer-traits.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ // Implementation for objects without object id (always non-polymorphic).
+ //
+ template <typename T>
+ class no_id_object_result_impl: public result_impl
+ {
+ protected:
+ // In result_impl, T is always non-const and the same as object_type.
+ //
+ typedef T object_type;
+ typedef odb::object_traits<object_type> object_traits;
+
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef odb::pointer_traits<pointer_type> pointer_traits;
+
+ friend class result<T>;
+ friend class result<const T>;
+ friend class result_iterator<T, class_object>;
+ friend class result_iterator<const T, class_object>;
+ friend class object_result_iterator<T, void, false>;
+ friend class object_result_iterator<const T, void, false>;
+
+ protected:
+ no_id_object_result_impl (odb::connection& conn)
+ : result_impl (conn), begin_ (true), end_ (false), current_ ()
+ {
+ }
+
+ // To make this work with all kinds of pointers (raw, std::auto_ptr,
+ // shared), we need to make sure we don't make any copies of the
+ // pointer on the return path.
+ //
+ pointer_type&
+ current ()
+ {
+ if (pointer_traits::null_ptr (current_) && !end_)
+ load ();
+
+ return current_;
+ }
+
+ void
+ release ()
+ {
+ current_ = pointer_type ();
+ guard_.release ();
+ }
+
+ void
+ begin ()
+ {
+ if (begin_)
+ {
+ next ();
+ begin_ = false;
+ }
+ }
+
+ bool
+ end () const
+ {
+ return end_;
+ }
+
+ protected:
+ virtual void
+ load (object_type&) = 0;
+
+ virtual void
+ next () = 0;
+
+ virtual void
+ cache () = 0;
+
+ virtual std::size_t
+ size () = 0;
+
+ protected:
+#ifdef ODB_CXX11
+ void
+ current (pointer_type& p)
+ {
+ current_ = std::move (p);
+ guard_.reset (current_);
+ }
+
+ void
+ current (pointer_type&& p)
+ {
+ current (p);
+ }
+#else
+ void
+ current (pointer_type p)
+ {
+ current_ = p;
+ guard_.reset (current_);
+ }
+#endif
+
+ bool begin_;
+ bool end_;
+
+ private:
+ void
+ load ();
+
+ private:
+ pointer_type current_;
+ typename pointer_traits::guard guard_;
+ };
+
+ template <typename T>
+ class object_result_iterator<T, void, false>
+ {
+ public:
+ // T can be const T while object_type is always non-const.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+
+ typedef no_id_object_result_impl<object_type> result_impl_type;
+
+ public:
+ object_result_iterator (result_impl_type* res)
+ : res_ (res)
+ {
+ }
+
+ public:
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ pointer_type
+ load ()
+ {
+#ifdef ODB_CXX11
+ pointer_type r (std::move (res_->current ()));
+#else
+ pointer_type r (res_->current ());
+#endif
+ res_->release ();
+ return r;
+ }
+
+ void
+ load (object_type& obj)
+ {
+ // Objects without ids are not stored in session cache.
+ //
+ if (!res_->end ())
+ res_->load (obj);
+ }
+
+ protected:
+ result_impl_type* res_;
+ };
+}
+
+#include <odb/no-id-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_NO_ID_OBJECT_RESULT_HXX
diff --git a/libodb/odb/no-id-object-result.txx b/libodb/odb/no-id-object-result.txx
new file mode 100644
index 0000000..886fe4b
--- /dev/null
+++ b/libodb/odb/no-id-object-result.txx
@@ -0,0 +1,21 @@
+// file : odb/no-id-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ //
+ // object_result_impl
+ //
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ load ()
+ {
+ // Objects without ids are not stored in session cache.
+ //
+ pointer_type p (object_traits::create ());
+ object_type& obj (pointer_traits::get_ref (p));
+ current (p);
+ load (obj);
+ }
+}
diff --git a/libodb/odb/no-op-cache-traits.hxx b/libodb/odb/no-op-cache-traits.hxx
new file mode 100644
index 0000000..b6d0518
--- /dev/null
+++ b/libodb/odb/no-op-cache-traits.hxx
@@ -0,0 +1,236 @@
+// file : odb/no-op-cache-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_NO_OP_CACHE_TRAITS_HXX
+#define ODB_NO_OP_CACHE_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/traits.hxx>
+#include <odb/forward.hxx>
+#include <odb/pointer-traits.hxx>
+
+namespace odb
+{
+ // pointer_cache_type
+ //
+ // Used to convert an object pointer to the canonical form (non-const),
+ // suitable for insertion into the cache.
+ //
+ template <typename P,
+ typename E = typename pointer_traits<P>::element_type,
+ typename O = typename object_traits<E>::object_type,
+ pointer_kind pk = pointer_traits<P>::kind>
+ struct pointer_cache_type
+ {
+ typedef typename object_traits<O>::pointer_type pointer_type;
+
+ static pointer_type
+ convert (const P& p)
+ {
+ return pointer_traits<P>::const_pointer_cast (p);
+ }
+ };
+
+ template <typename P, typename T, pointer_kind pk>
+ struct pointer_cache_type<P, T, T, pk>
+ {
+ // If element_type and object_type are the same, then it is already
+ // the canonical pointer.
+ //
+ static const P&
+ convert (const P& p) {return p;}
+ };
+
+ template <typename P, typename E, typename O>
+ struct pointer_cache_type<P, E, O, pk_unique>
+ {
+ // If the pointer is unique, then casting it can transfer ownership.
+ // In this case we return NULL void*, which will be ignored down the
+ // chain.
+ //
+ static void*
+ convert (const P&) {return 0;}
+ };
+
+ template <typename P, typename T>
+ struct pointer_cache_type<P, T, T, pk_unique>
+ {
+ static void*
+ convert (const P&) {return 0;}
+ };
+
+ // reference_cache_type
+ //
+ // Used to convert an object reference to the canonical form (non-const),
+ // suitable for insertion into the cache.
+ //
+ template <typename T,
+ typename O = typename object_traits<T>::object_type>
+ struct reference_cache_type
+ {
+ static O&
+ convert (T& r)
+ {
+ return const_cast<O&> (r);
+ }
+ };
+
+ template <typename T>
+ struct reference_cache_type<T, T>
+ {
+ // If the types are the same, then it is already the canonical form.
+ //
+ static T&
+ convert (T& r) {return r;}
+ };
+
+ // pointer_cache_traits
+ //
+ // Caching traits for objects passed by pointer. P should be the canonical
+ // pointer (non-const).
+ //
+ template <typename P>
+ struct no_op_pointer_cache_traits
+ {
+ typedef P pointer_type;
+ typedef typename pointer_traits<pointer_type>::element_type object_type;
+ typedef typename object_traits<object_type>::id_type id_type;
+ struct position_type {};
+
+ struct insert_guard
+ {
+ insert_guard () {}
+ insert_guard (const position_type&) {}
+
+ position_type
+ position () const {return position_type ();}
+
+ void
+ release () {}
+
+ void
+ reset (const position_type&) {}
+ };
+
+ // Cache management.
+ //
+ static position_type
+ insert (odb::database&, const id_type&, const pointer_type&)
+ {
+ return position_type ();
+ }
+
+ static position_type
+ insert (odb::database&, const pointer_type&) {return position_type ();}
+
+ // Special signature for unique pointers.
+ //
+ static position_type
+ insert (odb::database&, void*) {return position_type ();}
+
+ static pointer_type
+ find (odb::database&, const id_type&) {return pointer_type ();}
+
+ static void
+ erase (const position_type&) {}
+
+ // Notifications.
+ //
+ static void
+ persist (const position_type&) {}
+
+ static void
+ load (const position_type&) {}
+
+ static void
+ update (odb::database&, const object_type&) {}
+
+ static void
+ erase (odb::database&, const id_type&) {}
+ };
+
+ template <typename P>
+ struct no_id_pointer_cache_traits
+ {
+ typedef P pointer_type;
+ struct position_type {};
+
+ static position_type
+ insert (odb::database&, const pointer_type&) {return position_type ();}
+
+ // Special signature for unique pointers.
+ //
+ static position_type
+ insert (odb::database&, void*) {return position_type ();}
+
+ static void
+ persist (const position_type&) {}
+
+ static void
+ load (const position_type&) {}
+ };
+
+ // reference_cache_traits
+ //
+ // Caching traits for objects passed by reference. T should be the
+ // canonical object type (non-const).
+ //
+ template <typename T>
+ struct no_op_reference_cache_traits
+ {
+ typedef T object_type;
+ typedef typename object_traits<object_type>::id_type id_type;
+ struct position_type {};
+
+ struct insert_guard
+ {
+ insert_guard () {}
+ insert_guard (const position_type&) {}
+
+ position_type
+ position () const {return position_type ();}
+
+ void
+ release () {}
+
+ void
+ reset () {}
+ };
+
+ static position_type
+ insert (odb::database&, const id_type&, object_type&)
+ {
+ return position_type ();
+ }
+
+ static position_type
+ insert (odb::database&, object_type&) {return position_type ();}
+
+ static void
+ persist (const position_type&) {}
+
+ static void
+ load (const position_type&) {}
+ };
+
+ template <typename T>
+ struct no_id_reference_cache_traits
+ {
+ typedef T object_type;
+ struct position_type {};
+
+ static position_type
+ insert (odb::database&, object_type&) {return position_type ();}
+
+ static void
+ persist (const position_type&) {}
+
+ static void
+ load (const position_type&) {}
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_NO_OP_CACHE_TRAITS_HXX
diff --git a/libodb/odb/nullable.hxx b/libodb/odb/nullable.hxx
new file mode 100644
index 0000000..faa55c4
--- /dev/null
+++ b/libodb/odb/nullable.hxx
@@ -0,0 +1,492 @@
+// file : odb/nullable.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_NULLABLE_HXX
+#define ODB_NULLABLE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#ifdef ODB_CXX11
+# include <utility> // std::move
+#endif
+
+#include <odb/forward.hxx> // odb::core
+
+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>&);
+
+#ifdef ODB_CXX11
+ nullable (T&&);
+ nullable (nullable&&);
+ template <typename Y> explicit nullable (nullable<Y>&&);
+
+ nullable& operator= (T&&);
+ nullable& operator= (nullable&&);
+ template <typename Y> nullable& operator= (nullable<Y>&&);
+
+ ~nullable ();
+#endif
+
+ void swap (nullable&);
+
+ bool null () const;
+
+ T& get ();
+ const T& get () const;
+
+ T* operator-> ();
+ const T* operator-> () const;
+
+ T& operator* ();
+ const T& operator* () const;
+
+ typedef void (nullable::*bool_convertible) ();
+ operator bool_convertible () const
+ {
+ return null_ ? 0 : &nullable<T>::true_value;
+ }
+
+ void reset ();
+
+ private:
+ void true_value () {};
+
+#ifdef ODB_CXX11
+ struct empty {};
+
+ union
+ {
+ empty empty_;
+ T value_;
+ };
+#else
+ T value_;
+#endif
+
+ bool null_;
+ };
+
+ namespace common
+ {
+ using odb::nullable;
+ }
+
+ template <typename T>
+ inline bool
+ operator== (const nullable<T>& x, const nullable<T>& y)
+ {
+ return x.null () == y.null () && (x.null () || *x == *y);
+ }
+
+ template <typename T>
+ inline bool
+ operator!= (const nullable<T>& x, const nullable<T>& y) {return !(x == y);}
+
+ template <typename T>
+ inline bool
+ operator< (const nullable<T>& x, const nullable<T>& y)
+ {
+ return x.null () > y.null () || (!x.null () && !y.null () && *x < *y);
+ }
+
+ template <typename T>
+ inline bool
+ operator> (const nullable<T>& x, const nullable<T>& y)
+ {
+ return x.null () < y.null () || (!x.null () && !y.null () && *x > *y);
+ }
+
+ template <typename T>
+ inline bool
+ operator<= (const nullable<T>& x, const nullable<T>& y) {return !(x > y);}
+
+ template <typename T>
+ inline bool
+ operator>= (const nullable<T>& x, const nullable<T>& y) {return !(x < y);}
+
+ template <typename T>
+ inline bool nullable<T>::
+ null () const
+ {
+ return null_;
+ }
+
+ template <typename T>
+ inline T& nullable<T>::
+ get ()
+ {
+ return value_;
+ }
+
+ template <typename T>
+ inline const T& nullable<T>::
+ get () const
+ {
+ return value_;
+ }
+
+ template <typename T>
+ inline T* nullable<T>::
+ operator-> ()
+ {
+ return null_ ? 0 : &value_;
+ }
+
+ template <typename T>
+ inline const T* nullable<T>::
+ operator-> () const
+ {
+ return null_ ? 0 : &value_;
+ }
+
+ template <typename T>
+ inline T& nullable<T>::
+ operator* ()
+ {
+ return value_;
+ }
+
+ template <typename T>
+ inline const T& nullable<T>::
+ operator* () const
+ {
+ return value_;
+ }
+
+ template <typename T>
+ inline nullable<T>::
+ nullable ()
+ : null_ (true)
+ {
+ }
+
+ template <typename T>
+ inline nullable<T>::
+ nullable (const T& v)
+ : value_ (v), null_ (false)
+ {
+ }
+
+#ifdef ODB_CXX11
+
+ template <typename T>
+ inline nullable<T>::
+ nullable (T&& v)
+ : value_ (std::move (v)), null_ (false)
+ {
+ }
+
+ template <typename T>
+ inline nullable<T>::
+ nullable (const nullable& y)
+ : null_ (y.null_)
+ {
+ if (!y.null_)
+ new (&value_) T (y.value_);
+ }
+
+ template <typename T>
+ inline nullable<T>::
+ nullable (nullable&& y)
+ : null_ (y.null_)
+ {
+ if (!y.null_)
+ new (&value_) T (std::move (y.value_));
+ }
+
+ template <typename T>
+ template <typename Y>
+ inline nullable<T>::
+ nullable (const nullable<Y>& y)
+ : null_ (y.null_)
+ {
+ if (!y.null_)
+ new (&value_) T (y.value_);
+ }
+
+ template <typename T>
+ template <typename Y>
+ inline nullable<T>::
+ nullable (nullable<Y>&& y)
+ : null_ (y.null_)
+ {
+ if (!y.null_)
+ new (&value_) T (std::move (y.value_));
+ }
+
+ template <typename T>
+ inline nullable<T>& nullable<T>::
+ operator= (const T& v)
+ {
+ if (!null_)
+ value_ = v;
+ else
+ {
+ new (&value_) T (v);
+ null_ = false;
+ }
+
+ return *this;
+ }
+
+ template <typename T>
+ inline nullable<T>& nullable<T>::
+ operator= (T&& v)
+ {
+ if (!null_)
+ value_ = std::move (v);
+ else
+ {
+ new (&value_) T (std::move (v));
+ null_ = false;
+ }
+
+ return *this;
+ }
+
+ template <typename T>
+ inline nullable<T>& nullable<T>::
+ operator= (const nullable& y)
+ {
+ if (this != &y)
+ {
+ if (!y.null_)
+ {
+ if (!null_)
+ value_ = y.value_;
+ else
+ {
+ new (&value_) T (y.value_);
+ null_ = false;
+ }
+ }
+ else if (!null_)
+ {
+ value_.~T ();
+ null_ = true;
+ }
+ }
+
+ return *this;
+ }
+
+ template <typename T>
+ inline nullable<T>& nullable<T>::
+ operator= (nullable&& y)
+ {
+ if (this != &y)
+ {
+ if (!y.null_)
+ {
+ if (!null_)
+ value_ = std::move (y.value_);
+ else
+ {
+ new (&value_) T (std::move (y.value_));
+ null_ = false;
+ }
+ }
+ else if (!null_)
+ {
+ value_.~T ();
+ null_ = true;
+ }
+ }
+
+ return *this;
+ }
+
+ template <typename T>
+ template <typename Y>
+ inline nullable<T>& nullable<T>::
+ operator= (const nullable<Y>& y)
+ {
+ if (!y.null_)
+ {
+ if (!null_)
+ value_ = y.value_;
+ else
+ {
+ new (&value_) T (y.value_);
+ null_ = false;
+ }
+ }
+ else if (!null_)
+ {
+ value_.~T ();
+ null_ = true;
+ }
+
+ return *this;
+ }
+
+ template <typename T>
+ template <typename Y>
+ inline nullable<T>& nullable<T>::
+ operator= (nullable<Y>&& y)
+ {
+ if (!y.null_)
+ {
+ if (!null_)
+ value_ = std::move (y.value_);
+ else
+ {
+ new (&value_) T (std::move (y.value_));
+ null_ = false;
+ }
+ }
+ else if (!null_)
+ {
+ value_.~T ();
+ null_ = true;
+ }
+
+ return *this;
+ }
+
+ template <typename T>
+ inline void nullable<T>::
+ swap (nullable& y)
+ {
+ if (!y.null_)
+ {
+ if (!null_)
+ {
+ T v (std::move (value_));
+ value_ = y.value_;
+ y.value_ = v;
+ }
+ else
+ {
+ new (&value_) T (std::move (y.value_));
+ null_ = false;
+
+ y.value_.~T ();
+ y.null_ = true;
+ }
+ }
+ else
+ {
+ if (!null_)
+ {
+ new (&y.value_) T (std::move (value_));
+ y.null_ = false;
+
+ value_.~T ();
+ null_ = true;
+ }
+ }
+ }
+
+ template <typename T>
+ inline void nullable<T>::
+ reset ()
+ {
+ if (!null_)
+ {
+ value_.~T ();
+ null_ = true;
+ }
+ }
+
+ template <typename T>
+ inline nullable<T>::
+ ~nullable ()
+ {
+ if (!null_)
+ value_.~T ();
+ }
+
+#else // C++98
+
+ template <typename T>
+ inline nullable<T>::
+ nullable (const nullable& y)
+ : value_ (y.value_), null_ (y.null_)
+ {
+ }
+
+ template <typename T>
+ template <typename Y>
+ inline nullable<T>::
+ nullable (const nullable<Y>& y)
+ : value_ (y.value_), null_ (y.null_)
+ {
+ }
+
+ template <typename T>
+ inline nullable<T>& nullable<T>::
+ operator= (const T& v)
+ {
+ value_ = v;
+ null_ = false;
+ return *this;
+ }
+
+ template <typename T>
+ inline nullable<T>& nullable<T>::
+ operator= (const nullable& y)
+ {
+ if (this != &y)
+ {
+ value_ = y.value_;
+ null_ = y.null_;
+ }
+
+ return *this;
+ }
+
+ template <typename T>
+ template <typename Y>
+ inline nullable<T>& nullable<T>::
+ operator= (const nullable<Y>& y)
+ {
+ value_ = y.value_;
+ null_ = y.null_;
+ return *this;
+ }
+
+ template <typename T>
+ inline void nullable<T>::
+ swap (nullable& y)
+ {
+ T v (value_);
+ bool n (null_);
+
+ value_ = y.value_;
+ null_ = y.null_;
+
+ y.value_ = v;
+ y.null_ = n;
+ }
+
+ template <typename T>
+ inline void nullable<T>::
+ reset ()
+ {
+ value_ = T ();
+ null_ = true;
+ }
+
+#endif
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_NULLABLE_HXX
diff --git a/libodb/odb/object-result.hxx b/libodb/odb/object-result.hxx
new file mode 100644
index 0000000..056d518
--- /dev/null
+++ b/libodb/odb/object-result.hxx
@@ -0,0 +1,164 @@
+// file : odb/object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_OBJECT_RESULT_HXX
+#define ODB_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::ptrdiff_t
+#include <iterator> // iterator categories
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+#include <odb/result.hxx>
+#include <odb/pointer-traits.hxx>
+
+namespace odb
+{
+ //
+ // object_result_impl
+ //
+ template <typename T>
+ class object_result_impl;
+
+ template <typename T>
+ class polymorphic_object_result_impl;
+
+ template <typename T>
+ class no_id_object_result_impl;
+
+ //
+ // object_result_impl_selector
+ //
+ template <typename T,
+ typename ID = typename object_traits<T>::id_type,
+ bool polymorphic = object_traits<T>::polymorphic>
+ struct object_result_impl_selector;
+
+ template <typename T, typename ID>
+ struct object_result_impl_selector<T, ID, false>
+ {
+ typedef object_result_impl<T> type;
+ };
+
+ template <typename T, typename ID>
+ struct object_result_impl_selector<T, ID, true>
+ {
+ typedef polymorphic_object_result_impl<T> type;
+ };
+
+ template <typename T>
+ struct object_result_impl_selector<T, void, false>
+ {
+ typedef no_id_object_result_impl<T> type;
+ };
+
+ //
+ // result_iterator
+ //
+
+ template <typename T, typename ID, bool polymorphic>
+ class object_result_iterator;
+
+ template <typename T>
+ class result_iterator<T, class_object>: public object_result_iterator<
+ T,
+ typename object_traits<T>::id_type,
+ object_traits<T>::polymorphic>
+ {
+ public:
+ typedef T value_type;
+ typedef value_type& reference;
+ typedef value_type* pointer;
+ typedef std::ptrdiff_t difference_type;
+ typedef std::input_iterator_tag iterator_category;
+
+ // T can be const T while object_type is always non-const.
+ //
+ typedef
+ object_result_iterator<T,
+ typename object_traits<T>::id_type,
+ object_traits<T>::polymorphic> base_type;
+
+ public:
+ explicit
+ result_iterator (typename base_type::result_impl_type* res = 0)
+ : base_type (res)
+ {
+ }
+
+ // Input iterator requirements.
+ //
+ public:
+ reference
+ operator* () const
+ {
+ return pointer_traits::get_ref (this->res_->current ());
+ }
+
+ // Our value_type is already a pointer so return it instead of
+ // a pointer to it (operator-> will just have to go one deeper
+ // in the latter case).
+ //
+ pointer
+ operator-> () const
+ {
+ return pointer_traits::get_ptr (this->res_->current ());
+ }
+
+ result_iterator&
+ operator++ ()
+ {
+ this->res_->next ();
+ return *this;
+ }
+
+ result_iterator
+ operator++ (int)
+ {
+ // All non-end iterators for a result object move together.
+ //
+ this->res_->next ();
+ return *this;
+ }
+
+ public:
+ bool
+ equal (result_iterator j) const
+ {
+ return (this->res_ ? this->res_->end () : true) ==
+ (j.res_ ? j.res_->end () : true);
+ }
+
+ private:
+ // Use unrestricted pointer traits since that's what is returned by
+ // result_impl.
+ //
+ typedef
+ odb::pointer_traits<
+ typename object_traits<
+ typename base_type::object_type>::pointer_type>
+ pointer_traits;
+ };
+
+ //
+ //
+ template <typename T>
+ class result_base<T, class_object>
+ {
+ public:
+ typedef typename object_traits<T>::pointer_type value_type;
+
+ // T can be const T while object_type is always non-const.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+ typedef
+ typename object_result_impl_selector<object_type>::type
+ result_impl_type;
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_OBJECT_RESULT_HXX
diff --git a/libodb/odb/pointer-traits.hxx b/libodb/odb/pointer-traits.hxx
new file mode 100644
index 0000000..a0baa21
--- /dev/null
+++ b/libodb/odb/pointer-traits.hxx
@@ -0,0 +1,405 @@
+// file : odb/pointer-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_POINTER_TRAITS_HXX
+#define ODB_POINTER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <new> // operators new/delete
+#include <memory> // std::auto_ptr, std::unique_ptr, std::shared_ptr/weak_ptr
+#include <cstddef> // std::size_t
+
+#include <odb/details/config.hxx> // ODB_CXX11
+#include <odb/details/meta/remove-const.hxx>
+
+namespace odb
+{
+ enum pointer_kind
+ {
+ pk_raw, // Raw pointer or equivalent (i.e., unmanaged).
+ pk_unique, // Smart pointer that doesn't support sharing.
+ pk_shared, // Smart pointer that supports sharing.
+ pk_weak // Weak counterpart for shared pointer.
+ };
+
+ template <typename P>
+ class pointer_traits;
+
+ //
+ // Standard pointer guards.
+ //
+
+ // Raw pointer guard.
+ //
+ template <typename P>
+ class raw_ptr_guard
+ {
+ public:
+ ~raw_ptr_guard () {delete p_;}
+ raw_ptr_guard (): p_ (0) {}
+
+ explicit
+ raw_ptr_guard (P p): p_ (p) {}
+
+ void
+ release () {p_ = 0;}
+
+ void
+ reset (P p = 0) {delete p_; p_ = p;}
+
+ private:
+ P p_;
+ };
+
+ // No-op pointer guard for smart pointers.
+ //
+ template <typename P>
+ class smart_ptr_guard
+ {
+ public:
+ smart_ptr_guard () {}
+
+ explicit
+ smart_ptr_guard (const P&) {}
+
+ void
+ release () {}
+
+ void
+ reset () {}
+
+ void
+ reset (const P&) {}
+ };
+
+ // Specialization for raw pointers.
+ //
+ template <typename T>
+ class pointer_traits<T*>
+ {
+ public:
+ static const pointer_kind kind = pk_raw;
+ static const bool lazy = false;
+
+ typedef T element_type;
+ typedef T* pointer_type;
+ typedef const T* const_pointer_type;
+ typedef typename odb::details::meta::remove_const<T>::result*
+ unrestricted_pointer_type;
+ typedef raw_ptr_guard<pointer_type> guard;
+
+ // Return raw pointer to the pointed-to element, including NULL.
+ //
+ static element_type*
+ get_ptr (pointer_type p)
+ {
+ return p;
+ }
+
+ // Return reference to the pointed-to element.
+ //
+ static element_type&
+ get_ref (pointer_type p)
+ {
+ return *p;
+ }
+
+ // Return true if the pointer is NULL.
+ //
+ static bool
+ null_ptr (pointer_type p)
+ {
+ return p == 0;
+ }
+
+ // Casts.
+ //
+ static unrestricted_pointer_type
+ const_pointer_cast (pointer_type p)
+ {
+ return const_cast<unrestricted_pointer_type> (p);
+ }
+
+ template <typename T1>
+ static T1*
+ static_pointer_cast (pointer_type p)
+ {
+ return static_cast<T1*> (p);
+ }
+
+ template <typename T1>
+ static T1*
+ dynamic_pointer_cast (pointer_type p)
+ {
+ return dynamic_cast<T1*> (p);
+ }
+
+ public:
+ // Allocate memory for an element that will be managed by this
+ // pointer.
+ //
+ static void*
+ allocate (std::size_t n)
+ {
+ return operator new (n);
+ }
+
+ // Free memory allocated for an element. This functions is only
+ // called if the constructor of the element being created fails.
+ // Otherwise, the pointer (or guard) is used to delete the object
+ // and free the memory. This behavior is identical to the one
+ // used by operator delete overloading.
+ //
+ static void
+ free (void* p)
+ {
+ operator delete (p);
+ }
+ };
+
+ // Specialization for std::auto_ptr.
+ //
+#ifndef ODB_CXX11
+ template <typename T>
+ class pointer_traits< std::auto_ptr<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_unique;
+ static const bool lazy = false;
+
+ typedef T element_type;
+ typedef std::auto_ptr<element_type> pointer_type;
+ typedef std::auto_ptr<const element_type> const_pointer_type;
+ typedef smart_ptr_guard<pointer_type> guard;
+
+ static element_type*
+ get_ptr (const pointer_type& p)
+ {
+ return p.get ();
+ }
+
+ static element_type&
+ get_ref (const pointer_type& p)
+ {
+ return *p;
+ }
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return p.get () == 0;
+ }
+
+ // const_pointer_cast() is not provided.
+ //
+
+ // Note: transfers ownership.
+ //
+ template <typename T1>
+ static std::auto_ptr<T1>
+ static_pointer_cast (pointer_type& p)
+ {
+ return std::auto_ptr<T1> (static_cast<T1*> (p.release ()));
+ }
+
+ // Note: transfers ownership if successful.
+ //
+ template <typename T1>
+ static std::auto_ptr<T1>
+ dynamic_pointer_cast (pointer_type& p)
+ {
+ T1* p1 (dynamic_cast<T1*> (p.get ()));
+
+ if (p1 != 0)
+ p.release ();
+
+ return std::auto_ptr<T1> (p1);
+ }
+
+ public:
+ static void*
+ allocate (std::size_t n)
+ {
+ return operator new (n);
+ }
+
+ static void
+ free (void* p)
+ {
+ operator delete (p);
+ }
+ };
+#endif
+
+#ifdef ODB_CXX11
+
+ // Specialization for C++11 std::unique_ptr.
+ //
+ template <typename T, template <typename> class D>
+ class pointer_traits<std::unique_ptr<T, D<T>>>
+ {
+ public:
+ static const pointer_kind kind = pk_unique;
+ static const bool lazy = false;
+
+ typedef T element_type;
+ typedef std::unique_ptr<T, D<T>> pointer_type;
+ typedef std::unique_ptr<const T, D<const T>> const_pointer_type;
+ typedef smart_ptr_guard<pointer_type> guard;
+
+ static element_type*
+ get_ptr (const pointer_type& p)
+ {
+ return p.get ();
+ }
+
+ static element_type&
+ get_ref (const pointer_type& p)
+ {
+ return *p;
+ }
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ // const_pointer_cast() is not provided.
+ //
+
+ // Note: transfers ownership.
+ //
+ template <typename T1>
+ static std::unique_ptr<T1, D<T1>>
+ static_pointer_cast (pointer_type& p)
+ {
+ return std::unique_ptr<T1, D<T1>> (static_cast<T1*> (p.release ()));
+ }
+
+ // Note: transfers ownership if successful.
+ //
+ template <typename T1>
+ static std::unique_ptr<T1, D<T1>>
+ dynamic_pointer_cast (pointer_type& p)
+ {
+ T1* p1 (dynamic_cast<T1*> (p.get ()));
+
+ if (p1 != 0)
+ p.release ();
+
+ return std::unique_ptr<T1, D<T1>> (p1);
+ }
+
+ public:
+ static void*
+ allocate (std::size_t n)
+ {
+ return operator new (n);
+ }
+
+ static void
+ free (void* p)
+ {
+ operator delete (p);
+ }
+ };
+
+ // Specialization for C++11 std::shared_ptr.
+ //
+ template <typename T>
+ class pointer_traits<std::shared_ptr<T>>
+ {
+ public:
+ static const pointer_kind kind = pk_shared;
+ static const bool lazy = false;
+
+ typedef T element_type;
+ typedef std::shared_ptr<element_type> pointer_type;
+ typedef std::shared_ptr<const element_type> const_pointer_type;
+ typedef typename odb::details::meta::remove_const<element_type>::result
+ unrestricted_element_type;
+ typedef std::shared_ptr<unrestricted_element_type>
+ unrestricted_pointer_type;
+ typedef smart_ptr_guard<pointer_type> guard;
+
+ static element_type*
+ get_ptr (const pointer_type& p)
+ {
+ return p.get ();
+ }
+
+ static element_type&
+ get_ref (const pointer_type& p)
+ {
+ return *p;
+ }
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ static unrestricted_pointer_type
+ const_pointer_cast (const pointer_type& p)
+ {
+ return std::const_pointer_cast<unrestricted_element_type> (p);
+ }
+
+ template <typename T1>
+ static std::shared_ptr<T1>
+ static_pointer_cast (const pointer_type& p)
+ {
+ return std::static_pointer_cast<T1> (p);
+ }
+
+ template <typename T1>
+ static std::shared_ptr<T1>
+ dynamic_pointer_cast (const pointer_type& p)
+ {
+ return std::dynamic_pointer_cast<T1> (p);
+ }
+
+ public:
+ static void*
+ allocate (std::size_t n)
+ {
+ return operator new (n);
+ }
+
+ static void
+ free (void* p)
+ {
+ operator delete (p);
+ }
+ };
+
+ // Specialization for C++11 std::weak_ptr.
+ //
+ template <typename T>
+ class pointer_traits<std::weak_ptr<T>>
+ {
+ public:
+ static const pointer_kind kind = pk_weak;
+ static const bool lazy = false;
+
+ typedef T element_type;
+ typedef std::weak_ptr<element_type> pointer_type;
+ typedef std::shared_ptr<element_type> strong_pointer_type;
+
+ static strong_pointer_type
+ lock (const pointer_type& p)
+ {
+ return p.lock ();
+ }
+ };
+
+#endif // ODB_CXX11
+
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_POINTER_TRAITS_HXX
diff --git a/libodb/odb/polymorphic-info.hxx b/libodb/odb/polymorphic-info.hxx
new file mode 100644
index 0000000..0f410d5
--- /dev/null
+++ b/libodb/odb/polymorphic-info.hxx
@@ -0,0 +1,188 @@
+// file : odb/polymorphic-info.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_POLYMORPHIC_INFO_HXX
+#define ODB_POLYMORPHIC_INFO_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+#include <typeinfo>
+
+#include <odb/forward.hxx> // database, connection
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+namespace odb
+{
+ template <typename R>
+ struct polymorphic_abstract_info
+ {
+ typedef void (*section_load) (odb::connection&, R&, bool top);
+ typedef void (*section_update) (odb::connection&, const R&);
+
+ struct section_functions
+ {
+ section_load load;
+ section_update update;
+ };
+
+ struct section_list
+ {
+ std::size_t count;
+ const section_functions* functions;
+ };
+
+ public:
+ polymorphic_abstract_info (const std::type_info& t,
+ const polymorphic_abstract_info* b,
+ const section_list* s)
+ : type (t), base (b), sections (s) {}
+
+ bool
+ derived (const polymorphic_abstract_info& b) const
+ {
+ for (const polymorphic_abstract_info* p (base); p != 0; p = p->base)
+ if (&b == p)
+ return true;
+
+ return false;
+ }
+
+ // Find the "most overridden" section functions.
+ //
+ section_load
+ find_section_load (std::size_t index) const
+ {
+ for (const polymorphic_abstract_info* b (this); b != 0; b = b->base)
+ if (b->sections != 0 &&
+ index < b->sections->count &&
+ b->sections->functions[index].load != 0)
+ return b->sections->functions[index].load;
+
+ return 0;
+ }
+
+ section_update
+ find_section_update (std::size_t index) const
+ {
+ for (const polymorphic_abstract_info* b (this); b != 0; b = b->base)
+ if (b->sections != 0 &&
+ index < b->sections->count &&
+ b->sections->functions[index].update != 0)
+ return b->sections->functions[index].update;
+
+ return 0;
+ }
+
+ bool
+ final_section_update (const polymorphic_abstract_info& i,
+ std::size_t index) const
+ {
+ return i.sections != 0 &&
+ index < i.sections->count &&
+ i.sections->functions[index].update != 0 &&
+ i.sections->functions[index].update == find_section_update (index);
+ }
+
+ public:
+ const std::type_info& type;
+ const polymorphic_abstract_info* base;
+
+ // Sections.
+ //
+ // There could be "concrete" (i.e., not overridden) section in an
+ // abstract class. Which means the section table has to be in
+ // abstract_info.
+ //
+ const section_list* sections;
+ };
+
+ template <typename R>
+ struct polymorphic_concrete_info: polymorphic_abstract_info<R>
+ {
+ // Have to use access::object_traits directly because of VC10.
+ //
+ typedef R root_type;
+ typedef access::object_traits<root_type> root_traits;
+ typedef typename root_traits::id_type id_type;
+ typedef typename root_traits::pointer_type pointer_type;
+ typedef typename root_traits::discriminator_type discriminator_type;
+
+ typedef typename polymorphic_abstract_info<R>::section_list section_list;
+
+ enum call_type
+ {
+ call_callback, // arg points to callback event.
+ call_persist, // arg is not used.
+ call_update, // arg is not used.
+ call_find, // arg points to object id.
+ call_reload, // arg is not used.
+ call_load, // arg points to depth.
+ call_erase // arg points to object id.
+ };
+
+ typedef pointer_type (*create_function) ();
+ typedef bool (*dispatch_function) (
+ call_type, odb::database&, const root_type*, const void* arg);
+ typedef void (*delayed_loader_function) (
+ odb::database&,
+ const id_type&,
+ root_type&,
+ const schema_version_migration*);
+
+ public:
+ polymorphic_concrete_info (const std::type_info& t,
+ const polymorphic_abstract_info<R>* b,
+ const section_list* s,
+ const discriminator_type& d,
+ create_function cf,
+ dispatch_function df,
+ delayed_loader_function dlf)
+ : polymorphic_abstract_info<R> (t, b, s),
+ discriminator (d),
+ create (cf), dispatch (df), delayed_loader (dlf)
+ {
+ }
+
+ public:
+ discriminator_type discriminator;
+ create_function create;
+ dispatch_function dispatch;
+ delayed_loader_function delayed_loader;
+ };
+
+ // Register concrete type T in the root's map.
+ //
+ template <typename T, database_id DB>
+ struct polymorphic_entry
+ {
+ typedef T object_type;
+ typedef object_traits_impl<object_type, DB> object_traits;
+ typedef typename object_traits::root_type root_type;
+
+ polymorphic_entry ();
+ ~polymorphic_entry ();
+ };
+
+ // Helper functions that either return the concrete info or NULL
+ // depending on what kind of info we pass (used in query support).
+ //
+ template <typename R>
+ inline const polymorphic_concrete_info<R>*
+ polymorphic_info (const polymorphic_concrete_info<R>& i)
+ {
+ return &i;
+ }
+
+ template <typename R>
+ inline const polymorphic_concrete_info<R>*
+ polymorphic_info (const polymorphic_abstract_info<R>&)
+ {
+ return 0;
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_POLYMORPHIC_INFO_HXX
diff --git a/libodb/odb/polymorphic-map.hxx b/libodb/odb/polymorphic-map.hxx
new file mode 100644
index 0000000..2e61314
--- /dev/null
+++ b/libodb/odb/polymorphic-map.hxx
@@ -0,0 +1,276 @@
+// file : odb/polymorphic-map.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_POLYMORPHIC_MAP_HXX
+#define ODB_POLYMORPHIC_MAP_HXX
+
+#include <odb/pre.hxx>
+
+#include <map>
+#include <utility> // std::move
+#include <cstddef> // std::size_t
+#include <cassert>
+#include <typeinfo>
+
+#include <odb/callback.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+#include <odb/details/type-info.hxx>
+
+#include <odb/polymorphic-info.hxx>
+
+namespace odb
+{
+ template <typename R>
+ struct polymorphic_map
+ {
+ typedef R root_type;
+ typedef polymorphic_concrete_info<root_type> info_type;
+ typedef typename info_type::discriminator_type discriminator_type;
+
+ polymorphic_map (): ref_count_ (1) {}
+
+ const info_type&
+ find (const std::type_info& t) const;
+
+ const info_type&
+ find (const discriminator_type& d) const;
+
+ public:
+ typedef
+ std::map<const std::type_info*,
+ const info_type*,
+ odb::details::type_info_comparator> // VC bug.
+ type_map;
+
+ struct discriminator_comparator
+ {
+ bool
+ operator() (const discriminator_type* x,
+ const discriminator_type* y) const
+ {
+ return *x < *y;
+ }
+ };
+
+ typedef
+ std::map<const discriminator_type*,
+ const info_type*,
+ discriminator_comparator>
+ discriminator_map;
+
+ public:
+ std::size_t ref_count_;
+ type_map type_map_;
+ discriminator_map discriminator_map_;
+ };
+
+ template <typename R, database_id DB>
+ struct polymorphic_entry_impl
+ {
+ typedef R root_type;
+ typedef object_traits_impl<root_type, DB> root_traits;
+ typedef polymorphic_concrete_info<root_type> info_type;
+
+ static void
+ insert (const info_type&);
+
+ static void
+ erase (const info_type&);
+ };
+
+ template <typename T>
+ typename object_traits<typename object_traits<T>::root_type>::pointer_type
+ create_impl ()
+ {
+ typedef object_traits<T> derived_traits;
+ typedef object_traits<typename derived_traits::root_type> root_traits;
+
+ typedef typename derived_traits::pointer_type derived_pointer_type;
+ typedef typename root_traits::pointer_type root_pointer_type;
+
+ derived_pointer_type p (
+ access::object_factory<T, derived_pointer_type>::create ());
+
+ // Implicit downcast.
+ //
+#ifdef ODB_CXX11
+ root_pointer_type r (std::move (p));
+#else
+ root_pointer_type r (p);
+#endif
+ return r;
+ }
+
+ template <typename T, database_id DB, typename R>
+ struct dispatch_load
+ {
+ static void
+ call (database& db, T& obj, std::size_t d)
+ {
+ object_traits_impl<T, DB>::load_ (db, obj, d);
+ }
+ };
+
+ template <typename R, database_id DB>
+ struct dispatch_load<R, DB, R>
+ {
+ static void
+ call (database&, R&, std::size_t)
+ {
+ assert (false);
+ }
+ };
+
+ template <typename T, database_id DB, bool auto_id>
+ struct dispatch_persist
+ {
+ static void
+ call (database& db, const T& obj)
+ {
+ // Top-level call, no dynamic type checking.
+ //
+ object_traits_impl<T, DB>::persist (db, obj, true, false);
+ }
+ };
+
+ template <typename T, database_id DB>
+ struct dispatch_persist<T, DB, true>
+ {
+ static void
+ call (database& db, const T& obj)
+ {
+ // Top-level call, no dynamic type checking.
+ //
+ object_traits_impl<T, DB>::persist (
+ db, const_cast<T&> (obj), true, false);
+ }
+ };
+
+ template <typename T, database_id DB>
+ bool
+ dispatch_impl (
+ typename polymorphic_concrete_info<
+ typename object_traits<T>::root_type>::call_type c,
+ database& db,
+ const typename object_traits<T>::root_type* pobj,
+ const void* arg)
+ {
+ typedef object_traits_impl<T, DB> derived_traits;
+ typedef typename derived_traits::root_type root_type;
+ typedef object_traits_impl<root_type, DB> root_traits;
+ typedef typename root_traits::id_type id_type;
+ typedef polymorphic_concrete_info<root_type> info_type;
+
+ bool r (false);
+
+ switch (c)
+ {
+ case info_type::call_callback:
+ {
+ derived_traits::callback (
+ db,
+ *const_cast<T*> (static_cast<const T*> (pobj)),
+ *static_cast<const callback_event*> (arg));
+ break;
+ }
+ case info_type::call_persist:
+ {
+ dispatch_persist<T, DB, root_traits::auto_id>::call (
+ db,
+ *static_cast<const T*> (pobj));
+ break;
+ }
+ case info_type::call_update:
+ {
+ derived_traits::update (
+ db,
+ *static_cast<const T*> (pobj),
+ true, // Top-level call.
+ false); // No dynamic type checking.
+ break;
+ }
+ case info_type::call_find:
+ {
+ r = derived_traits::find (
+ db,
+ *static_cast<const id_type*> (arg),
+ *const_cast<T*> (static_cast<const T*> (pobj)),
+ false); // No dynamic type checking.
+ break;
+ }
+ case info_type::call_reload:
+ {
+ r = derived_traits::reload (
+ db,
+ *const_cast<T*> (static_cast<const T*> (pobj)),
+ false); // No dynamic type checking.
+ break;
+ }
+ case info_type::call_load:
+ {
+ dispatch_load<T, DB, root_type>::call (
+ db,
+ *const_cast<T*> (static_cast<const T*> (pobj)),
+ *static_cast<const std::size_t*> (arg));
+ break;
+ }
+ case info_type::call_erase:
+ {
+ if (pobj != 0)
+ derived_traits::erase (
+ db,
+ *static_cast<const T*> (pobj),
+ true, // Top-level call.
+ false); // No dynamic type checking.
+ else
+ derived_traits::erase (
+ db,
+ *static_cast<const id_type*> (arg),
+ true, // Top-level call.
+ false); // No dynamic type checking.
+ break;
+ }
+ }
+
+ return r;
+ }
+
+ template <typename T, database_id DB, typename ST>
+ void
+ section_load_impl (odb::connection& conn,
+ typename object_traits<T>::root_type& obj,
+ bool top)
+ {
+ typedef object_traits_impl<T, DB> derived_traits;
+ typedef typename derived_traits::statements_type statements_type;
+ typedef typename statements_type::connection_type connection_type;
+
+ connection_type& c (static_cast<connection_type&> (conn));
+ statements_type& sts (c.statement_cache ().template find_object<T> ());
+
+ ST::load (sts.extra_statement_cache (), static_cast<T&> (obj), top);
+ }
+
+ template <typename T, database_id DB, typename ST>
+ void
+ section_update_impl (odb::connection& conn,
+ const typename object_traits<T>::root_type& obj)
+ {
+ typedef object_traits_impl<T, DB> derived_traits;
+ typedef typename derived_traits::statements_type statements_type;
+ typedef typename statements_type::connection_type connection_type;
+
+ connection_type& c (static_cast<connection_type&> (conn));
+ statements_type& sts (c.statement_cache ().template find_object<T> ());
+
+ ST::update (sts.extra_statement_cache (), static_cast<const T&> (obj));
+ }
+}
+
+#include <odb/polymorphic-map.ixx>
+#include <odb/polymorphic-map.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_POLYMORPHIC_MAP_HXX
diff --git a/libodb/odb/polymorphic-map.ixx b/libodb/odb/polymorphic-map.ixx
new file mode 100644
index 0000000..4e00c46
--- /dev/null
+++ b/libodb/odb/polymorphic-map.ixx
@@ -0,0 +1,19 @@
+// file : odb/polymorphic-map.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ template <typename T, database_id DB>
+ inline polymorphic_entry<T, DB>::
+ polymorphic_entry ()
+ {
+ polymorphic_entry_impl<root_type, DB>::insert (object_traits::info);
+ }
+
+ template <typename T, database_id DB>
+ inline polymorphic_entry<T, DB>::
+ ~polymorphic_entry ()
+ {
+ polymorphic_entry_impl<root_type, DB>::erase (object_traits::info);
+ }
+}
diff --git a/libodb/odb/polymorphic-map.txx b/libodb/odb/polymorphic-map.txx
new file mode 100644
index 0000000..9e0c0f0
--- /dev/null
+++ b/libodb/odb/polymorphic-map.txx
@@ -0,0 +1,75 @@
+// file : odb/polymorphic-map.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/exceptions.hxx> // no_type_info
+
+namespace odb
+{
+ //
+ // polymorphic_map
+ //
+
+ template <typename R>
+ const typename polymorphic_map<R>::info_type& polymorphic_map<R>::
+ find (const std::type_info& t) const
+ {
+ typename type_map::const_iterator i (type_map_.find (&t));
+
+ if (i != type_map_.end ())
+ return *i->second;
+ else
+ throw no_type_info ();
+ }
+
+ template <typename R>
+ const typename polymorphic_map<R>::info_type& polymorphic_map<R>::
+ find (const discriminator_type& d) const
+ {
+ typename discriminator_map::const_iterator i (
+ discriminator_map_.find (&d));
+
+ if (i != discriminator_map_.end ())
+ return *i->second;
+ else
+ throw no_type_info ();
+ }
+
+ //
+ // polymorphic_entry_impl
+ //
+
+ template <typename R, database_id DB>
+ void polymorphic_entry_impl<R, DB>::
+ insert (const info_type& i)
+ {
+ // VC10 cannot grok constructor call syntax here.
+ //
+ polymorphic_map<root_type>*& pm = root_traits::map;
+
+ if (pm == 0)
+ pm = new polymorphic_map<root_type>;
+ else
+ pm->ref_count_++;
+
+ pm->type_map_[&i.type] = &i;
+ pm->discriminator_map_[&i.discriminator] = &i;
+ }
+
+ template <typename R, database_id DB>
+ void polymorphic_entry_impl<R, DB>::
+ erase (const info_type& i)
+ {
+ // VC10 cannot grok constructor call syntax here.
+ //
+ polymorphic_map<root_type>*& pm = root_traits::map;
+
+ pm->discriminator_map_.erase (&i.discriminator);
+ pm->type_map_.erase (&i.type);
+
+ if (--pm->ref_count_ == 0)
+ {
+ delete pm;
+ pm = 0;
+ }
+ }
+}
diff --git a/libodb/odb/polymorphic-object-result.hxx b/libodb/odb/polymorphic-object-result.hxx
new file mode 100644
index 0000000..5498856
--- /dev/null
+++ b/libodb/odb/polymorphic-object-result.hxx
@@ -0,0 +1,224 @@
+// file : odb/polymorphic-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_POLYMORPHIC_OBJECT_RESULT_HXX
+#define ODB_POLYMORPHIC_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+#include <utility> // std::move
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+#include <odb/result.hxx>
+#include <odb/object-result.hxx>
+#include <odb/pointer-traits.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ // Implementation for polymorphic objects with object id.
+ //
+ template <typename T>
+ class polymorphic_object_result_impl: public result_impl
+ {
+ protected:
+ // In result_impl, T is always non-const and the same as object_type.
+ //
+ typedef T object_type;
+ typedef odb::object_traits<object_type> object_traits;
+ typedef typename object_traits::id_type id_type;
+
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef odb::pointer_traits<pointer_type> pointer_traits;
+
+ typedef typename object_traits::root_type root_type;
+ typedef odb::object_traits<root_type> root_traits;
+ typedef typename root_traits::discriminator_type discriminator_type;
+
+ friend class result<T>;
+ friend class result<const T>;
+ friend class result_iterator<T, class_object>;
+ friend class result_iterator<const T, class_object>;
+ friend class object_result_iterator<T, id_type, true>;
+ friend class object_result_iterator<const T, id_type, true>;
+
+ protected:
+ polymorphic_object_result_impl (odb::connection& conn)
+ : result_impl (conn), begin_ (true), end_ (false), current_ ()
+ {
+ }
+
+ // To make this work with all kinds of pointers (raw, std::auto_ptr,
+ // shared), we need to make sure we don't make any copies of the
+ // pointer on the return path.
+ //
+ pointer_type&
+ current ()
+ {
+ if (pointer_traits::null_ptr (current_) && !end_)
+ load ();
+
+ return current_;
+ }
+
+ void
+ release ()
+ {
+ current_ = pointer_type ();
+ guard_.release ();
+ }
+
+ void
+ begin ()
+ {
+ if (begin_)
+ {
+ next ();
+ begin_ = false;
+ }
+ }
+
+ bool
+ end () const
+ {
+ return end_;
+ }
+
+ protected:
+ // The fetch argument is a hint to the implementation. If it is
+ // false then it means load_id() was already called (and presumably
+ // fetched the data into the object image) and the object image
+ // is still valid (so the implementation doesn't need to fetch
+ // the data again).
+ //
+ // The load() signature differs from the non-polymorphic cases in
+ // that we pass a pointer to object instead of a reference. The
+ // object is only passed if the user requests loading into an
+ // existing instance. Otherwise, we pass NULL and load() is
+ // responsible for creating the object of a correct dynamic
+ // type and managing the object cache insertion.
+ //
+ virtual void
+ load (object_type*, bool fetch = true) = 0;
+
+ virtual id_type
+ load_id () = 0;
+
+ virtual discriminator_type
+ load_discriminator () = 0;
+
+ virtual void
+ next () = 0;
+
+ virtual void
+ cache () = 0;
+
+ virtual std::size_t
+ size () = 0;
+
+ protected:
+#ifdef ODB_CXX11
+ void
+ current (pointer_type& p, bool guard = true)
+ {
+ current_ = std::move (p);
+
+ if (guard)
+ guard_.reset (current_);
+ else
+ guard_.reset ();
+ }
+
+ void
+ current (pointer_type&& p, bool guard = true)
+ {
+ current (p, guard);
+ }
+#else
+ void
+ current (pointer_type p, bool guard = true)
+ {
+ current_ = p;
+
+ if (guard)
+ guard_.reset (current_);
+ else
+ guard_.reset ();
+ }
+#endif
+
+ bool begin_;
+ bool end_;
+
+ private:
+ void
+ load ();
+
+ private:
+ pointer_type current_;
+ typename pointer_traits::guard guard_;
+ };
+
+ template <typename T, typename ID>
+ class object_result_iterator<T, ID, true>
+ {
+ public:
+ // T can be const T while object_type is always non-const.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+ typedef typename object_traits<T>::id_type id_type;
+ typedef typename object_traits<T>::root_type root_type;
+ typedef typename object_traits<root_type>::discriminator_type
+ discriminator_type;
+
+ typedef polymorphic_object_result_impl<object_type> result_impl_type;
+
+ public:
+ object_result_iterator (result_impl_type* res)
+ : res_ (res)
+ {
+ }
+
+ public:
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ pointer_type
+ load ()
+ {
+#ifdef ODB_CXX11
+ pointer_type r (std::move (res_->current ()));
+#else
+ pointer_type r (res_->current ());
+#endif
+ res_->release ();
+ return r;
+ }
+
+ void
+ load (object_type&);
+
+ id_type
+ id ()
+ {
+ return res_->load_id ();
+ }
+
+ discriminator_type
+ discriminator ()
+ {
+ return res_->load_discriminator ();
+ }
+
+ protected:
+ result_impl_type* res_;
+ };
+}
+
+#include <odb/polymorphic-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_POLYMORPHIC_OBJECT_RESULT_HXX
diff --git a/libodb/odb/polymorphic-object-result.txx b/libodb/odb/polymorphic-object-result.txx
new file mode 100644
index 0000000..d9252e9
--- /dev/null
+++ b/libodb/odb/polymorphic-object-result.txx
@@ -0,0 +1,70 @@
+// file : odb/polymorphic-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/exceptions.hxx>
+
+namespace odb
+{
+ //
+ // polymorphic_object_result_impl
+ //
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ load ()
+ {
+ typedef typename root_traits::pointer_type root_pointer_type;
+ typedef typename root_traits::pointer_traits root_pointer_traits;
+
+ // First check the session.
+ //
+ const id_type& id (load_id ());
+
+ root_pointer_type rp (
+ object_traits::pointer_cache_traits::find (this->db_, id));
+
+ if (!root_pointer_traits::null_ptr (rp))
+ {
+ // Check if the types match.
+ //
+ pointer_type p (
+ root_pointer_traits::template dynamic_pointer_cast<
+ object_type> (rp));
+
+ if (!pointer_traits::null_ptr (p))
+ current (p, false); // Pointer from cache should not be guarded.
+ else
+ // We have an object in session that has a different type
+ // compared to the one in the database.
+ //
+ throw object_not_persistent (); // @@ type_mismatch?
+ }
+ else
+ // load() is responsible for creating the object of a correct
+ // dynamic type and for object cache insertion.
+ //
+ load (0, false);
+ }
+
+ //
+ // object_result_iterator
+ //
+
+ template <typename T, typename ID>
+ void object_result_iterator<T, ID, true>::
+ load (object_type& obj)
+ {
+ if (res_->end ())
+ return;
+
+ typedef odb::object_traits<object_type> object_traits;
+
+ typename object_traits::reference_cache_traits::position_type p (
+ object_traits::reference_cache_traits::insert (
+ res_->db_, res_->load_id (), obj));
+ typename object_traits::reference_cache_traits::insert_guard ig (p);
+ res_->load (&obj, false);
+ object_traits::reference_cache_traits::load (p);
+ ig.release ();
+ }
+}
diff --git a/libodb/odb/post.hxx b/libodb/odb/post.hxx
new file mode 100644
index 0000000..3cdfd94
--- /dev/null
+++ b/libodb/odb/post.hxx
@@ -0,0 +1,6 @@
+// file : odb/post.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
diff --git a/libodb/odb/pre.hxx b/libodb/odb/pre.hxx
new file mode 100644
index 0000000..088f172
--- /dev/null
+++ b/libodb/odb/pre.hxx
@@ -0,0 +1,23 @@
+// file : odb/pre.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifdef _MSC_VER
+ // Push warning state.
+ //
+# pragma warning (push, 3)
+
+ // Disabled warnings.
+ //
+# pragma warning (disable:4068) // unknown pragma
+# pragma warning (disable:4251) // needs to have DLL-interface
+# pragma warning (disable:4290) // exception specification ignored
+# pragma warning (disable:4355) // passing 'this' to a member
+# pragma warning (disable:4800) // forcing value to bool
+# pragma warning (disable:4231) // non-standard extension (extern template)
+# pragma warning (disable:4275) // "C4251 is essentially noise and can be
+ // silenced" - Stephan T. Lavavej [And C4275
+ // is essentially the same thing.]
+ // Elevated warnings.
+ //
+# pragma warning (2:4239) // standard doesn't allow this conversion
+#endif
diff --git a/libodb/odb/prepared-query.cxx b/libodb/odb/prepared-query.cxx
new file mode 100644
index 0000000..70bcaa1
--- /dev/null
+++ b/libodb/odb/prepared-query.cxx
@@ -0,0 +1,47 @@
+// file : odb/prepared-query.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/connection.hxx>
+#include <odb/transaction.hxx>
+#include <odb/prepared-query.hxx>
+
+namespace odb
+{
+ prepared_query_impl::
+ ~prepared_query_impl ()
+ {
+ if (next_ != this)
+ list_remove ();
+ }
+
+ prepared_query_impl::
+ prepared_query_impl (connection& c)
+ : cached (false), conn (c), prev_ (0), next_ (this)
+ {
+ // Add to the list.
+ //
+ next_ = conn.prepared_queries_;
+ conn.prepared_queries_ = this;
+
+ if (next_ != 0)
+ next_->prev_ = this;
+ }
+
+ bool prepared_query_impl::
+ verify_connection (transaction& t)
+ {
+ return &t.connection () == &stmt->connection ();
+ }
+
+ void prepared_query_impl::
+ list_remove ()
+ {
+ (prev_ == 0 ? conn.prepared_queries_ : prev_->next_) = next_;
+
+ if (next_ != 0)
+ next_->prev_ = prev_;
+
+ prev_ = 0;
+ next_ = this;
+ }
+}
diff --git a/libodb/odb/prepared-query.hxx b/libodb/odb/prepared-query.hxx
new file mode 100644
index 0000000..7cac6da
--- /dev/null
+++ b/libodb/odb/prepared-query.hxx
@@ -0,0 +1,201 @@
+// file : odb/prepared-query.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PREPARED_QUERY_HXX
+#define ODB_PREPARED_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx> // odb::core
+#include <odb/traits.hxx>
+#include <odb/result.hxx>
+#include <odb/statement.hxx>
+
+#include <odb/details/export.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+namespace odb
+{
+ class LIBODB_EXPORT prepared_query_impl: public details::shared_base
+ {
+ public:
+ virtual
+ ~prepared_query_impl ();
+
+ prepared_query_impl (connection&);
+
+ // Verify this prepared query and the specified transaction use the
+ // same connection.
+ //
+ virtual bool
+ verify_connection (transaction&);
+
+ bool cached;
+ connection& conn;
+ const char* name;
+ details::shared_ptr<statement> stmt;
+ details::shared_ptr<result_impl> (*execute) (prepared_query_impl&);
+
+ private:
+ prepared_query_impl (const prepared_query_impl&);
+ prepared_query_impl& operator= (const prepared_query_impl&);
+
+ // Doubly-linked list of results.
+ //
+ // prev_ == 0 means we are the first element.
+ // next_ == 0 means we are the last element.
+ // next_ == this means we are not on the list.
+ //
+ protected:
+ friend class connection;
+
+ void
+ list_remove ();
+
+ prepared_query_impl* prev_;
+ prepared_query_impl* next_;
+ };
+
+ template <typename T>
+ struct prepared_query
+ {
+ // Cached version.
+ //
+ explicit
+ prepared_query (prepared_query_impl* impl = 0): impl_ (impl) {}
+
+ // Uncached version.
+ //
+ explicit
+ prepared_query (const details::shared_ptr<prepared_query_impl>& impl)
+ : impl_ (impl.get ())
+ {
+ impl_->_inc_ref ();
+ }
+
+ result<T>
+ execute (bool cache = true)
+ {
+ typedef
+ typename result_base<T, class_traits<T>::kind>::result_impl_type
+ derived_type;
+
+ details::shared_ptr<result_impl> ri (impl_->execute (*impl_));
+ result<T> r (
+ details::shared_ptr<derived_type> (
+ static_cast<derived_type*> (ri.release ())));
+
+ if (cache)
+ r.cache ();
+
+ return r;
+ }
+
+ typename object_traits<T>::pointer_type
+ execute_one ()
+ {
+ return execute (false).one ();
+ }
+
+ bool
+ execute_one (T& object)
+ {
+ return execute (false).one (object);
+ }
+
+ T
+ execute_value ()
+ {
+ // Compiler error pointing here? The object must be default-
+ // constructible in order to use the return-by-value API.
+ //
+ T o;
+ execute (false).value (o);
+ return o;
+ }
+
+ const char*
+ name () const
+ {
+ return impl_->name;
+ }
+
+ typedef odb::statement statement_type;
+
+ statement_type&
+ statement () const
+ {
+ return *impl_->stmt;
+ }
+
+ typedef prepared_query_impl* prepared_query::*unspecified_bool_type;
+ operator unspecified_bool_type () const
+ {
+ return impl_ ? &prepared_query::impl_ : 0;
+ }
+
+ public:
+ ~prepared_query ()
+ {
+ if (impl_ != 0 && !impl_->cached && impl_->_dec_ref ())
+ delete impl_;
+ }
+
+ prepared_query (const prepared_query& x)
+ : impl_ (x.impl_)
+ {
+ if (!impl_->cached)
+ impl_->_inc_ref ();
+ }
+
+ prepared_query&
+ operator= (const prepared_query& x)
+ {
+ if (impl_ != x.impl_)
+ {
+ if (impl_ != 0 && !impl_->cached && impl_->_dec_ref ())
+ delete impl_;
+
+ impl_ = x.impl_;
+
+ if (!impl_->cached)
+ impl_->_inc_ref ();
+ }
+
+ return *this;
+ }
+
+ private:
+ // Ideally, we would just use shared_ptr to manage the impl object.
+ // However, there is a problem if the prepared query is cached on
+ // the connection and the connection is released early when the
+ // transaction is committed or rolled back. In this case, the
+ // prepared_query object might still be around pointing to impl. If
+ // this connection and the prepared query are then used by another
+ // thread while we release the impl object, then we have a race
+ // condition.
+ //
+ // To work around this problem we will simply "reference" the impl
+ // object without counting if the prepared query is cached. For
+ // transition from pointer to reference, see cache_query_() in
+ // connection.cxx.
+ //
+ // You may also observe that in order to know whether this is a
+ // cached prepared query or not, we have to read the cached data
+ // member in the impl object. This does not cause a race because,
+ // unlike the reference count, this member is immutable once set
+ // to true.
+ //
+ friend class connection;
+ prepared_query_impl* impl_;
+ };
+
+ namespace common
+ {
+ using odb::prepared_query;
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PREPARED_QUERY_HXX
diff --git a/libodb/odb/query-dynamic.cxx b/libodb/odb/query-dynamic.cxx
new file mode 100644
index 0000000..bf9fd9c
--- /dev/null
+++ b/libodb/odb/query-dynamic.cxx
@@ -0,0 +1,200 @@
+// file : odb/query-dynamic.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/query-dynamic.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ // query_param
+ //
+ query_param::
+ ~query_param ()
+ {
+ }
+
+ // query_base
+ //
+ void query_base::
+ clear ()
+ {
+ for (clause_type::iterator i (clause_.begin ()); i != clause_.end (); ++i)
+ {
+ if (i->kind == clause_part::kind_param_val ||
+ i->kind == clause_part::kind_param_ref)
+ {
+ query_param* qp (reinterpret_cast<query_param*> (i->data));
+
+ if (qp != 0 && qp->_dec_ref ())
+ delete qp;
+ }
+ }
+
+ clause_.clear ();
+ strings_.clear ();
+ }
+
+ void query_base::
+ append (const string& native)
+ {
+ strings_.push_back (native);
+ clause_.push_back (clause_part ());
+ clause_.back ().kind = clause_part::kind_native;
+ clause_.back ().data = strings_.size () - 1;
+ }
+
+ void query_base::
+ append (const query_base& x)
+ {
+ size_t i (clause_.size ()), delta (i);
+ size_t n (i + x.clause_.size ());
+ clause_.resize (n);
+
+ for (size_t j (0); i < n; ++i, ++j)
+ {
+ const clause_part& s (x.clause_[j]);
+ clause_part& d (clause_[i]);
+
+ d = s;
+
+ // We need to increment the param references, update pointers
+ // to strings and update argument positions.
+ //
+ switch (s.kind)
+ {
+ case clause_part::kind_param_val:
+ case clause_part::kind_param_ref:
+ {
+ reinterpret_cast<query_param*> (d.data)->_inc_ref ();
+ break;
+ }
+ case clause_part::kind_native:
+ {
+ strings_.push_back (x.strings_[s.data]);
+ d.data = strings_.size () - 1;
+ break;
+ }
+ case clause_part::op_add:
+
+ case clause_part::op_and:
+ case clause_part::op_or:
+
+ case clause_part::op_eq:
+ case clause_part::op_ne:
+ case clause_part::op_lt:
+ case clause_part::op_gt:
+ case clause_part::op_le:
+ case clause_part::op_ge:
+ {
+ d.data += delta;
+ break;
+ }
+ // Do not use default here to remember to handle new op codes.
+ //
+ case clause_part::kind_column:
+ case clause_part::kind_true:
+ case clause_part::kind_false:
+ case clause_part::op_not:
+ case clause_part::op_null:
+ case clause_part::op_not_null:
+ case clause_part::op_in:
+ case clause_part::op_like:
+ case clause_part::op_like_escape:
+ break;
+ }
+ }
+ }
+
+ void query_base::
+ append_ref (const void* ref, const native_column_info* c)
+ {
+ clause_.push_back (clause_part ());
+ clause_part& p (clause_.back ());
+
+ p.kind = clause_part::kind_param_ref;
+ p.data = 0; // In case new below throws.
+ p.native_info = c;
+
+ p.data = reinterpret_cast<std::size_t> (
+ new (details::shared) query_param (ref));
+ }
+
+ query_base& query_base::
+ operator+= (const std::string& native)
+ {
+ if (!native.empty ())
+ {
+ size_t p (clause_.size ());
+ append (native);
+
+ if (p != 0)
+ append (clause_part::op_add, p - 1);
+ }
+
+ return *this;
+ }
+
+ query_base& query_base::
+ operator+= (const query_base& x)
+ {
+ if (!x.empty ())
+ {
+ size_t p (clause_.size ());
+ append (x);
+
+ if (p != 0)
+ append (clause_part::op_add, p - 1);
+ }
+
+ return *this;
+ }
+
+ query_base
+ operator&& (const query_base& x, const query_base& y)
+ {
+ // Optimize cases where one or both sides are constant truth.
+ //
+ bool xt (x.const_true ()), yt (y.const_true ());
+
+ if (xt && yt)
+ return x;
+
+ if (xt || x.empty ())
+ return y;
+
+ if (yt || y.empty ())
+ return x;
+
+ query_base r (x);
+ r.append (y);
+ r.append (query_base::clause_part::op_and, x.clause ().size () - 1);
+ return r;
+ }
+
+ query_base
+ operator|| (const query_base& x, const query_base& y)
+ {
+ if (x.empty ())
+ return y;
+
+ if (y.empty ())
+ return x;
+
+ query_base r (x);
+ r.append (y);
+ r.append (query_base::clause_part::op_or, x.clause ().size () - 1);
+ return r;
+ }
+
+ query_base
+ operator! (const query_base& x)
+ {
+ if (x.empty ())
+ return x;
+
+ query_base r (x);
+ r.append (query_base::clause_part::op_not, 0);
+ return r;
+ }
+}
diff --git a/libodb/odb/query-dynamic.hxx b/libodb/odb/query-dynamic.hxx
new file mode 100644
index 0000000..8c4edae
--- /dev/null
+++ b/libodb/odb/query-dynamic.hxx
@@ -0,0 +1,1069 @@
+// file : odb/query-dynamic.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QUERY_DYNAMIC_HXX
+#define ODB_QUERY_DYNAMIC_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/query.hxx>
+
+#include <odb/details/export.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+namespace odb
+{
+ struct native_column_info;
+
+ template <typename T>
+ struct val_bind
+ {
+ typedef const T& type;
+
+ explicit
+ val_bind (type v): val (v) {}
+
+ type val;
+ };
+
+ // Passing arrays by value in dynamic queries is not supported.
+ // Pass by reference instead.
+ //
+ template <typename T, std::size_t N>
+ struct val_bind<T[N]>;
+
+ template <typename T>
+ struct ref_bind
+ {
+ typedef const T& type;
+
+ explicit
+ ref_bind (type r): ref (r) {}
+
+ const void*
+ ptr () const {return &ref;}
+
+ type ref;
+ };
+
+ template <typename T, std::size_t N>
+ struct ref_bind<T[N]>
+ {
+ typedef const T* type;
+
+ explicit
+ ref_bind (type r): ref (r) {}
+
+ // Allow implicit conversion from decayed ref_bind's.
+ //
+ ref_bind (ref_bind<T*> r): ref (r.ref) {}
+ ref_bind (ref_bind<const T*> r): ref (r.ref) {}
+
+ const void*
+ ptr () const {return ref;}
+
+ type ref;
+ };
+
+ //
+ //
+ struct LIBODB_EXPORT query_param: details::shared_base
+ {
+ virtual ~query_param ();
+ query_param (const void* v): value (v) {}
+
+ const void* value;
+ };
+
+ // For by-value parameters we have to make a copy since the original
+ // can be gone by the time we translate to native query.
+ //
+ template <typename T>
+ struct val_query_param: query_param
+ {
+ val_query_param (const T& v): query_param (&copy), copy (v) {}
+
+ T copy;
+ };
+
+ //
+ //
+ class LIBODB_EXPORT query_base
+ {
+ public:
+ // Internally the query clause is stored in a Reverse Polish Notation-
+ // like representation which also allows us to traverse it as a syntax
+ // tree.
+ //
+ // Let's keep this class POD so that std::vector can do more
+ // efficient copying, etc.
+ //
+ struct clause_part
+ {
+ // Note that the order of enumerators is important (used as indexes).
+ //
+ enum kind_type
+ {
+ kind_column, // native_info points to the native_column_info array.
+
+ kind_param_val, // data points to query_param while native_info points
+ kind_param_ref, // to the native_column_info array.
+
+ kind_native, // data is the index in the strings vector.
+
+ kind_true, // true literal.
+ kind_false, // false literal.
+
+ // Operators.
+ //
+ // For binary operators, data is the index of the last element
+ // belonging to the left hand side sub-expression.
+ //
+ op_add, // + (concatenation of two sub-expressions)
+
+ op_and, // &&
+ op_or, // ||
+ op_not, // !
+
+ op_null, // is_null ()
+ op_not_null, // is_not_null ()
+
+ op_in, // in(), data is the number of arguments
+ op_like, // like(pattern)
+ op_like_escape, // like(pattern, escape)
+
+ op_eq, // ==
+ op_ne, // !=
+ op_lt, // <
+ op_gt, // >
+ op_le, // <=
+ op_ge // >=
+ };
+
+ kind_type kind;
+ std::size_t data;
+ const native_column_info* native_info;
+ };
+
+ public:
+ ~query_base ()
+ {
+ clear ();
+ }
+
+ query_base () {}
+
+ // True or false literal.
+ //
+ explicit
+ query_base (bool v)
+ {
+ append (v ? clause_part::kind_true : clause_part::kind_false, 0);
+ }
+
+ explicit
+ query_base (const char* native)
+ {
+ append (native);
+ }
+
+ explicit
+ query_base (const std::string& native)
+ {
+ append (native);
+ }
+
+ query_base (const query_column<bool>&);
+
+ query_base (const query_base& x)
+ {
+ append (x);
+ }
+
+ query_base&
+ operator= (const query_base& x)
+ {
+ if (this != &x)
+ {
+ clear ();
+ append (x);
+ }
+
+ return *this;
+ }
+
+ public:
+ template <typename T>
+ static val_bind<T>
+ _val (const T& x)
+ {
+ return val_bind<T> (x);
+ }
+
+ template <typename T>
+ static ref_bind<T>
+ _ref (const T& x)
+ {
+ return ref_bind<T> (x);
+ }
+
+ // Some compilers (notably VC++), when deducing const T& from const
+ // array do not strip const from the array type. As a result, in the
+ // above signatures we get, for example, T = const char[4] instead
+ // of T = char[4], which is what we want. So to "fix" such compilers,
+ // we will have to provide the following specialization of the above
+ // _ref() function (we don't need _val() since we don't support passing
+ // arrays by value; see val_bind definition).
+ //
+ template <typename T, std::size_t N>
+ static ref_bind<T[N]>
+ _ref (const T (&x) [N])
+ {
+ return ref_bind<T[N]> (x);
+ }
+
+ public:
+ query_base&
+ operator+= (const query_base&);
+
+ query_base&
+ operator+= (const std::string& native);
+
+ public:
+ bool
+ empty () const
+ {
+ return clause_.empty ();
+ }
+
+ bool
+ const_true () const
+ {
+ return clause_.size () == 1 &&
+ clause_.front ().kind == clause_part::kind_true;
+ }
+
+ // Implementation details.
+ //
+ public:
+ explicit
+ query_base (const native_column_info* c)
+ {
+ append (c);
+ }
+
+ // Native.
+ //
+ void
+ append (const std::string&);
+
+ // Query fragment.
+ //
+ void
+ append (const query_base&);
+
+ // Operator.
+ //
+ void
+ append (clause_part::kind_type k, std::size_t data)
+ {
+ clause_.push_back (clause_part ());
+ clause_.back ().kind = k;
+ clause_.back ().data = data;
+ }
+
+ // Column.
+ //
+ void
+ append (const native_column_info* c)
+ {
+ clause_.push_back (clause_part ());
+ clause_.back ().kind = clause_part::kind_column;
+ clause_.back ().native_info = c;
+ }
+
+ // Parameter.
+ //
+ void
+ append_ref (const void* ref, const native_column_info*);
+
+ template <typename T>
+ void
+ append_val (const T& val, const native_column_info*);
+
+ void
+ clear ();
+
+ public:
+ typedef std::vector<clause_part> clause_type;
+ typedef std::vector<std::string> strings_type;
+
+ const clause_type&
+ clause () const
+ {
+ return clause_;
+ }
+
+ const strings_type&
+ strings () const
+ {
+ return strings_;
+ }
+
+ private:
+ clause_type clause_;
+ strings_type strings_;
+ };
+
+ inline query_base
+ operator+ (const query_base& x, const query_base& y)
+ {
+ query_base r (x);
+ r += y;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const query_base& q, const std::string& s)
+ {
+ query_base r (q);
+ r += s;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const std::string& s, const query_base& q)
+ {
+ query_base r (s);
+ r += q;
+ return r;
+ }
+
+ LIBODB_EXPORT query_base
+ operator&& (const query_base&, const query_base&);
+
+ LIBODB_EXPORT query_base
+ operator|| (const query_base&, const query_base&);
+
+ LIBODB_EXPORT query_base
+ operator! (const query_base&);
+
+ //
+ //
+ struct native_column_info
+ {
+ const void* column;
+ void* param_factory;
+ };
+
+ // This class template has to remain POD since we rely on it being
+ // 0-initialized before any dynamic initialization takes place in
+ // any other translation unit.
+ //
+ template <typename T>
+ struct query_column
+ {
+ // Array of pointers to database-specific columns. It will be
+ // automatically zero-initialized since query_column instances
+ // are always static.
+ //
+ native_column_info native_info[database_count];
+
+ // is_null, is_not_null
+ //
+ public:
+ query_base
+ is_null () const
+ {
+ query_base q (native_info);
+ q.append (query_base::clause_part::op_null, 0);
+ return q;
+ }
+
+ query_base
+ is_not_null () const
+ {
+ query_base q (native_info);
+ q.append (query_base::clause_part::op_not_null, 0);
+ return q;
+ }
+
+ // in
+ //
+ public:
+ query_base
+ in (const T&, const T&) const;
+
+ query_base
+ in (const T&, const T&, const T&) const;
+
+ query_base
+ in (const T&, const T&, const T&, const T&) const;
+
+ query_base
+ in (const T&, const T&, const T&, const T&, const T&) const;
+
+ template <typename I>
+ query_base
+ in_range (I begin, I end) const;
+
+ // like
+ //
+ public:
+ query_base
+ like (const T& pattern) const
+ {
+ return like (val_bind<T> (pattern));
+ }
+
+ query_base
+ like (val_bind<T> pattern) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern) const
+ {
+ return like (val_bind<T> (T (pattern.val)));
+ }
+
+ query_base
+ like (ref_bind<T> pattern) const;
+
+ query_base
+ like (const T& pattern, const T& escape) const
+ {
+ return like (val_bind<T> (pattern), escape);
+ }
+
+ query_base
+ like (val_bind<T> pattern, const T& escape) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern, const T& escape) const
+ {
+ return like (val_bind<T> (T (pattern.val)), escape);
+ }
+
+ query_base
+ like (ref_bind<T> pattern, const T& escape) const;
+
+ // ==
+ //
+ public:
+ query_base
+ equal (val_bind<T> v) const
+ {
+ query_base q (native_info);
+ q.append_val (v.val, native_info);
+ q.append (query_base::clause_part::op_eq, 0);
+ return q;
+ }
+
+ query_base
+ equal (ref_bind<T> r) const
+ {
+ query_base q (native_info);
+ q.append_ref (r.ptr (), native_info);
+ q.append (query_base::clause_part::op_eq, 0);
+ return q;
+ }
+
+ friend query_base
+ operator== (const query_column& c, const T& v)
+ {
+ return c.equal (val_bind<T> (v));
+ }
+
+ friend query_base
+ operator== (const T& v, const query_column& c)
+ {
+ return c.equal (val_bind<T> (v));
+ }
+
+ friend query_base
+ operator== (const query_column& c, val_bind<T> v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (val_bind<T> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (const query_column& c, val_bind<T2> v)
+ {
+ return c.equal (val_bind<T> (T (v.val)));
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (val_bind<T2> v, const query_column& c)
+ {
+ return c.equal (val_bind<T> (T (v.val)));
+ }
+
+ friend query_base
+ operator== (const query_column& c, ref_bind<T> r)
+ {
+ return c.equal (r);
+ }
+
+ friend query_base
+ operator== (ref_bind<T> r, const query_column& c)
+ {
+ return c.equal (r);
+ }
+
+ // !=
+ //
+ public:
+ query_base
+ unequal (val_bind<T> v) const
+ {
+ query_base q (native_info);
+ q.append_val (v.val, native_info);
+ q.append (query_base::clause_part::op_ne, 0);
+ return q;
+ }
+
+ query_base
+ unequal (ref_bind<T> r) const
+ {
+ query_base q (native_info);
+ q.append_ref (r.ptr (), native_info);
+ q.append (query_base::clause_part::op_ne, 0);
+ return q;
+ }
+
+ friend query_base
+ operator!= (const query_column& c, const T& v)
+ {
+ return c.unequal (val_bind<T> (v));
+ }
+
+ friend query_base
+ operator!= (const T& v, const query_column& c)
+ {
+ return c.unequal (val_bind<T> (v));
+ }
+
+ friend query_base
+ operator!= (const query_column& c, val_bind<T> v)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (val_bind<T> v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (const query_column& c, val_bind<T2> v)
+ {
+ return c.unequal (val_bind<T> (T (v.val)));
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (val_bind<T2> v, const query_column& c)
+ {
+ return c.unequal (val_bind<T> (T (v.val)));
+ }
+
+ friend query_base
+ operator!= (const query_column& c, ref_bind<T> r)
+ {
+ return c.unequal (r);
+ }
+
+ friend query_base
+ operator!= (ref_bind<T> r, const query_column& c)
+ {
+ return c.unequal (r);
+ }
+
+ // <
+ //
+ public:
+ query_base
+ less (val_bind<T> v) const
+ {
+ query_base q (native_info);
+ q.append_val (v.val, native_info);
+ q.append (query_base::clause_part::op_lt, 0);
+ return q;
+ }
+
+ query_base
+ less (ref_bind<T> r) const
+ {
+ query_base q (native_info);
+ q.append_ref (r.ptr (), native_info);
+ q.append (query_base::clause_part::op_lt, 0);
+ return q;
+ }
+
+ friend query_base
+ operator< (const query_column& c, const T& v)
+ {
+ return c.less (val_bind<T> (v));
+ }
+
+ friend query_base
+ operator< (const T& v, const query_column& c)
+ {
+ return c.greater (val_bind<T> (v));
+ }
+
+ friend query_base
+ operator< (const query_column& c, val_bind<T> v)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator< (val_bind<T> v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (const query_column& c, val_bind<T2> v)
+ {
+ return c.less (val_bind<T> (T (v.val)));
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater (val_bind<T> (T (v.val)));
+ }
+
+ friend query_base
+ operator< (const query_column& c, ref_bind<T> r)
+ {
+ return c.less (r);
+ }
+
+ friend query_base
+ operator< (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater (r);
+ }
+
+ // >
+ //
+ public:
+ query_base
+ greater (val_bind<T> v) const
+ {
+ query_base q (native_info);
+ q.append_val (v.val, native_info);
+ q.append (query_base::clause_part::op_gt, 0);
+ return q;
+ }
+
+ query_base
+ greater (ref_bind<T> r) const
+ {
+ query_base q (native_info);
+ q.append_ref (r.ptr (), native_info);
+ q.append (query_base::clause_part::op_gt, 0);
+ return q;
+ }
+
+ friend query_base
+ operator> (const query_column& c, const T& v)
+ {
+ return c.greater (val_bind<T> (v));
+ }
+
+ friend query_base
+ operator> (const T& v, const query_column& c)
+ {
+ return c.less (val_bind<T> (v));
+ }
+
+ friend query_base
+ operator> (const query_column& c, val_bind<T> v)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator> (val_bind<T> v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater (val_bind<T> (T (v.val)));
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (val_bind<T2> v, const query_column& c)
+ {
+ return c.less (val_bind<T> (T (v.val)));
+ }
+
+ friend query_base
+ operator> (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater (r);
+ }
+
+ friend query_base
+ operator> (ref_bind<T> r, const query_column& c)
+ {
+ return c.less (r);
+ }
+
+ // <=
+ //
+ public:
+ query_base
+ less_equal (val_bind<T> v) const
+ {
+ query_base q (native_info);
+ q.append_val (v.val, native_info);
+ q.append (query_base::clause_part::op_le, 0);
+ return q;
+ }
+
+ query_base
+ less_equal (ref_bind<T> r) const
+ {
+ query_base q (native_info);
+ q.append_ref (r.ptr (), native_info);
+ q.append (query_base::clause_part::op_le, 0);
+ return q;
+ }
+
+ friend query_base
+ operator<= (const query_column& c, const T& v)
+ {
+ return c.less_equal (val_bind<T> (v));
+ }
+
+ friend query_base
+ operator<= (const T& v, const query_column& c)
+ {
+ return c.greater_equal (val_bind<T> (v));
+ }
+
+ friend query_base
+ operator<= (const query_column& c, val_bind<T> v)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator<= (val_bind<T> v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (const query_column& c, val_bind<T2> v)
+ {
+ return c.less_equal (val_bind<T> (T (v.val)));
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater_equal (val_bind<T> (T (v.val)));
+ }
+
+ friend query_base
+ operator<= (const query_column& c, ref_bind<T> r)
+ {
+ return c.less_equal (r);
+ }
+
+ friend query_base
+ operator<= (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater_equal (r);
+ }
+
+ // >=
+ //
+ public:
+ query_base
+ greater_equal (val_bind<T> v) const
+ {
+ query_base q (native_info);
+ q.append_val (v.val, native_info);
+ q.append (query_base::clause_part::op_ge, 0);
+ return q;
+ }
+
+ query_base
+ greater_equal (ref_bind<T> r) const
+ {
+ query_base q (native_info);
+ q.append_ref (r.ptr (), native_info);
+ q.append (query_base::clause_part::op_ge, 0);
+ return q;
+ }
+
+ friend query_base
+ operator>= (const query_column& c, const T& v)
+ {
+ return c.greater_equal (val_bind<T> (v));
+ }
+
+ friend query_base
+ operator>= (const T& v, const query_column& c)
+ {
+ return c.less_equal (val_bind<T> (v));
+ }
+
+ friend query_base
+ operator>= (const query_column& c, val_bind<T> v)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator>= (val_bind<T> v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater_equal (val_bind<T> (T (v.val)));
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (val_bind<T2> v, const query_column& c)
+ {
+ return c.less_equal (val_bind<T> (T (v.val)));
+ }
+
+ friend query_base
+ operator>= (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater_equal (r);
+ }
+
+ friend query_base
+ operator>= (ref_bind<T> r, const query_column& c)
+ {
+ return c.less_equal (r);
+ }
+
+ // Column comparison.
+ //
+ public:
+ template <typename T2>
+ query_base
+ operator== (const query_column<T2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () ==
+ decay_traits<T2>::instance ()));
+
+ query_base q (native_info);
+ q.append (c.native_info);
+ q.append (query_base::clause_part::op_eq, 0);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ operator!= (const query_column<T2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () !=
+ decay_traits<T2>::instance ()));
+
+ query_base q (native_info);
+ q.append (c.native_info);
+ q.append (query_base::clause_part::op_ne, 0);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ operator< (const query_column<T2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <
+ decay_traits<T2>::instance ()));
+
+ query_base q (native_info);
+ q.append (c.native_info);
+ q.append (query_base::clause_part::op_lt, 0);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ operator> (const query_column<T2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >
+ decay_traits<T2>::instance ()));
+
+ query_base q (native_info);
+ q.append (c.native_info);
+ q.append (query_base::clause_part::op_gt, 0);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ operator<= (const query_column<T2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <=
+ decay_traits<T2>::instance ()));
+
+ query_base q (native_info);
+ q.append (c.native_info);
+ q.append (query_base::clause_part::op_le, 0);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ operator>= (const query_column<T2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >=
+ decay_traits<T2>::instance ()));
+
+ query_base q (native_info);
+ q.append (c.native_info);
+ q.append (query_base::clause_part::op_ge, 0);
+ return q;
+ }
+ };
+
+ // Provide operator+() for using columns to construct native
+ // query fragments (e.g., ORDER BY).
+ //
+ template <typename T>
+ inline query_base
+ operator+ (const query_column<T>& c, const std::string& s)
+ {
+ query_base q (c.native_info);
+ q.append (s);
+ q.append (query_base::clause_part::op_add, 0);
+ return q;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, const query_column<T>& c)
+ {
+ query_base q (s);
+ q.append (c.native_info);
+ q.append (query_base::clause_part::op_add, 0);
+ return q;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_column<T>& c, const query_base& q)
+ {
+ query_base r (c.native_info);
+ r.append (q);
+ r.append (query_base::clause_part::op_add, 0);
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, const query_column<T>& c)
+ {
+ query_base r (q);
+ r.append (c.native_info);
+ r.append (query_base::clause_part::op_add, q.clause ().size () - 1);
+ return r;
+ }
+
+ //
+ //
+ template <typename T>
+ class query<T, query_base>: public query_base,
+ public query_selector<T, id_common>::columns_type
+ {
+ public:
+ // We don't define any typedefs here since they may clash with
+ // column names defined by our base type.
+ //
+
+ query ()
+ {
+ }
+
+ explicit
+ query (bool v)
+ : query_base (v)
+ {
+ }
+
+ explicit
+ query (const char* q)
+ : query_base (q)
+ {
+ }
+
+ explicit
+ query (const std::string& q)
+ : query_base (q)
+ {
+ }
+
+ query (const query_base& q)
+ : query_base (q)
+ {
+ }
+
+ query (const query_column<bool>& qc)
+ : query_base (qc)
+ {
+ }
+ };
+}
+
+#include <odb/query-dynamic.ixx>
+#include <odb/query-dynamic.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_QUERY_DYNAMIC_HXX
diff --git a/libodb/odb/query-dynamic.ixx b/libodb/odb/query-dynamic.ixx
new file mode 100644
index 0000000..8de7b8e
--- /dev/null
+++ b/libodb/odb/query-dynamic.ixx
@@ -0,0 +1,18 @@
+// file : odb/query-dynamic.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ // query_base
+ //
+ inline query_base::
+ query_base (const query_column<bool>& c)
+ {
+ // Some databases provide the IS TRUE operator. However, we cannot
+ // use it since the column type might now be SQL boolean type.
+ //
+ append (c.native_info);
+ append_val (true, c.native_info);
+ append (query_base::clause_part::op_eq, 0);
+ }
+}
diff --git a/libodb/odb/query-dynamic.txx b/libodb/odb/query-dynamic.txx
new file mode 100644
index 0000000..9ea16d2
--- /dev/null
+++ b/libodb/odb/query-dynamic.txx
@@ -0,0 +1,139 @@
+// file : odb/query-dynamic.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ //
+ // query_base
+ //
+
+ template <typename T>
+ void query_base::
+ append_val (const T& val, const native_column_info* c)
+ {
+ clause_.push_back (clause_part ());
+ clause_part& p (clause_.back ());
+
+ p.kind = clause_part::kind_param_val;
+ p.data = 0; // In case new below throws.
+ p.native_info = c;
+
+ query_param* qp (new (details::shared) val_query_param<T> (val));
+ p.data = reinterpret_cast<std::size_t> (qp);
+ }
+
+ //
+ // query_column
+ //
+
+ // in
+ //
+ template <typename T>
+ query_base query_column<T>::
+ in (const T& v1, const T& v2) const
+ {
+ query_base q (native_info);
+ q.append_val (v1, native_info);
+ q.append_val (v2, native_info);
+ q.append (query_base::clause_part::op_in, 2);
+ return q;
+ }
+
+ template <typename T>
+ query_base query_column<T>::
+ in (const T& v1, const T& v2, const T& v3) const
+ {
+ query_base q (native_info);
+ q.append_val (v1, native_info);
+ q.append_val (v2, native_info);
+ q.append_val (v3, native_info);
+ q.append (query_base::clause_part::op_in, 3);
+ return q;
+ }
+
+ template <typename T>
+ query_base query_column<T>::
+ in (const T& v1, const T& v2, const T& v3, const T& v4) const
+ {
+ query_base q (native_info);
+ q.append_val (v1, native_info);
+ q.append_val (v2, native_info);
+ q.append_val (v3, native_info);
+ q.append_val (v4, native_info);
+ q.append (query_base::clause_part::op_in, 4);
+ return q;
+ }
+
+ template <typename T>
+ query_base query_column<T>::
+ in (const T& v1, const T& v2, const T& v3, const T& v4, const T& v5) const
+ {
+ query_base q (native_info);
+ q.append_val (v1, native_info);
+ q.append_val (v2, native_info);
+ q.append_val (v3, native_info);
+ q.append_val (v4, native_info);
+ q.append_val (v5, native_info);
+ q.append (query_base::clause_part::op_in, 5);
+ return q;
+ }
+
+ template <typename T>
+ template <typename I>
+ query_base query_column<T>::
+ in_range (I i, I end) const
+ {
+ query_base q (native_info);
+
+ std::size_t n (0);
+ for (; i != end; ++i, ++n)
+ q.append_val<T> (*i, native_info); // Force implicit conversion.
+
+ q.append (query_base::clause_part::op_in, n);
+ return q;
+ }
+
+ // like
+ //
+ template <typename T>
+ query_base query_column<T>::
+ like (val_bind<T> p) const
+ {
+ query_base q (native_info);
+ q.append_val (p.val, native_info);
+ q.append (query_base::clause_part::op_like, 0);
+ return q;
+ }
+
+ template <typename T>
+ query_base query_column<T>::
+ like (ref_bind<T> p) const
+ {
+ query_base q (native_info);
+ q.append_ref (p.ptr (), native_info);
+ q.append (query_base::clause_part::op_like, 0);
+ return q;
+ }
+
+ template <typename T>
+ query_base query_column<T>::
+ like (val_bind<T> p, const T& e) const
+ {
+ query_base q (native_info);
+ q.append_val (p.val, native_info);
+ q.append_val (e, native_info);
+ q.append (query_base::clause_part::op_like_escape, 0);
+ return q;
+ }
+
+ template <typename T>
+ query_base query_column<T>::
+ like (ref_bind<T> p, const T& e) const
+ {
+ query_base q (native_info);
+ q.append_ref (p.ptr (), native_info);
+ q.append_val (e, native_info);
+ q.append (query_base::clause_part::op_like_escape, 0);
+ return q;
+ }
+}
diff --git a/libodb/odb/query.hxx b/libodb/odb/query.hxx
new file mode 100644
index 0000000..9375738
--- /dev/null
+++ b/libodb/odb/query.hxx
@@ -0,0 +1,119 @@
+// file : odb/query.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QUERY_HXX
+#define ODB_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+namespace odb
+{
+ // Table alias for type T and tag Tag.
+ //
+ // The alias_traits interface consists of two things: the table_name
+ // static variable containing the name and, in case of a derived type
+ // in a polymorphic hierarchy, the base_traits typedef. Note that the
+ // same interface is exposed by object_traits, which is used when
+ // we need straight tables instead of aliases.
+ //
+ //
+ template <typename T, database_id DB, typename Tag>
+ struct alias_traits;
+
+ template <typename T, database_id DB>
+ struct query_columns_base;
+
+ template <typename T, database_id DB, typename A>
+ struct query_columns;
+
+ template <typename T, database_id DB, typename A>
+ struct pointer_query_columns;
+
+ // Object pointer syntax wrapper.
+ //
+ template <typename T>
+ struct query_pointer
+ {
+ query_pointer ()
+ {
+ // For some reason GCC needs this dummy c-tor if we make a static
+ // data member of this type const.
+ }
+
+ T*
+ operator-> () const
+ {
+ return 0; // All members in T are static.
+ }
+ };
+
+ // Query parameter decay traits.
+ //
+ template <typename T>
+ struct decay_traits
+ {
+ typedef const T& type;
+
+ static type
+ instance ();
+ };
+
+ template <typename T, std::size_t N>
+ struct decay_traits<T[N]>
+ {
+ typedef const T* type;
+
+ // Use the pointer comparability as a proxy for data comparability.
+ // Note that it is stricter than using element comparability (i.e.,
+ // one can compare int to char but not int* to char*).
+ //
+ static type
+ instance ();
+ };
+
+ // VC9 cannot handle certain cases of non-type arguments with default
+ // values in template functions (e.g., database::query()). As a result,
+ // we have to use the impl trick below instead of simply having kind
+ // as a second template argument with a default value.
+ //
+ template <typename T, database_id DB, class_kind kind>
+ struct query_selector_impl;
+
+ template <typename T, database_id DB>
+ struct query_selector_impl<T, DB, class_object>
+ {
+ typedef typename object_traits_impl<T, DB>::query_base_type base_type;
+
+ typedef
+ query_columns<T, DB, access::object_traits_impl<T, DB> >
+ columns_type;
+ };
+
+ template <typename T, database_id DB>
+ struct query_selector_impl<T, DB, class_view>
+ {
+ typedef typename view_traits_impl<T, DB>::query_base_type base_type;
+ typedef typename view_traits_impl<T, DB>::query_columns columns_type;
+ };
+
+ template <typename T, database_id DB>
+ struct query_selector: query_selector_impl<T, DB, class_traits<T>::kind>
+ {
+ };
+
+ template <typename T,
+ typename B = typename query_selector<T, id_common>::base_type>
+ class query;
+
+ namespace core
+ {
+ using odb::query;
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QUERY_HXX
diff --git a/libodb/odb/result.cxx b/libodb/odb/result.cxx
new file mode 100644
index 0000000..e9393ca
--- /dev/null
+++ b/libodb/odb/result.cxx
@@ -0,0 +1,40 @@
+// file : odb/result.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/result.hxx>
+#include <odb/connection.hxx>
+
+namespace odb
+{
+ result_impl::
+ ~result_impl ()
+ {
+ if (next_ != this)
+ list_remove ();
+ }
+
+ result_impl::
+ result_impl (connection& c)
+ : db_ (c.database ()), conn_ (c), prev_ (0), next_ (this)
+ {
+ // Add to the list.
+ //
+ next_ = conn_.results_;
+ conn_.results_ = this;
+
+ if (next_ != 0)
+ next_->prev_ = this;
+ }
+
+ void result_impl::
+ list_remove ()
+ {
+ (prev_ == 0 ? conn_.results_ : prev_->next_) = next_;
+
+ if (next_ != 0)
+ next_->prev_ = prev_;
+
+ prev_ = 0;
+ next_ = this;
+ }
+}
diff --git a/libodb/odb/result.hxx b/libodb/odb/result.hxx
new file mode 100644
index 0000000..87a6869
--- /dev/null
+++ b/libodb/odb/result.hxx
@@ -0,0 +1,247 @@
+// file : odb/result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_RESULT_HXX
+#define ODB_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::ptrdiff_t, std::size_t
+
+#include <odb/forward.hxx> // odb::core
+#include <odb/traits.hxx>
+
+#include <odb/details/export.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+namespace odb
+{
+ class LIBODB_EXPORT result_impl: public details::shared_base
+ {
+ public:
+ virtual
+ ~result_impl ();
+
+ virtual void
+ invalidate () = 0;
+
+ protected:
+ result_impl (connection&);
+
+ protected:
+ database& db_;
+ connection& conn_;
+
+ // Doubly-linked list of results.
+ //
+ // prev_ == 0 means we are the first element.
+ // next_ == 0 means we are the last element.
+ // next_ == this means we are not on the list.
+ //
+ protected:
+ friend class connection;
+
+ void
+ list_remove ();
+
+ result_impl* prev_;
+ result_impl* next_;
+ };
+
+ template <typename T, class_kind kind>
+ class result_base;
+
+ template <typename T, class_kind kind = class_traits<T>::kind>
+ class result_iterator;
+
+ // Input iterator requirements.
+ //
+ template <typename T, class_kind kind>
+ inline bool
+ operator== (result_iterator<T, kind> i, result_iterator<T, kind> j)
+ {
+ return i.equal (j);
+ }
+
+ template <typename T, class_kind kind>
+ inline bool
+ operator!= (result_iterator<T, kind> i, result_iterator<T, kind> j)
+ {
+ return !i.equal (j);
+ }
+
+ template <typename T>
+ class result: result_base<T, class_traits<T>::kind>
+ {
+ public:
+ static const class_kind kind = class_traits<T>::kind;
+
+ typedef result_base<T, kind> base;
+
+ typedef typename base::value_type value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+
+ typedef result_iterator<T, kind> iterator;
+
+ typedef std::size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+
+ typedef typename base::result_impl_type result_impl_type;
+
+ public:
+ result ()
+ {
+ }
+
+ explicit
+ result (details::shared_ptr<result_impl_type> impl)
+ : impl_ (impl)
+ {
+ }
+
+ // Copying or assignment of a result instance leads to one instance
+ // being an alias for another. Think of copying a result as copying
+ // a file handle -- the file you access through either of them is
+ // still the same.
+ //
+ public:
+ result (const result& r)
+ : impl_ (r.impl_)
+ {
+ }
+
+ result&
+ operator= (const result& r)
+ {
+ if (impl_ != r.impl_)
+ impl_ = r.impl_;
+
+ return *this;
+ }
+
+ // Conversion from result<T> to result<const T>.
+ //
+ template <typename UT>
+ result (const result<UT>& r)
+ //
+ // If you get a compiler error pointing to the line below saying
+ // that the impl_ member is inaccessible, then you are most likely
+ // trying to perform an illegal result conversion, for example,
+ // from result<const obj> to result<obj>.
+ //
+ : impl_ (r.impl_)
+ {
+ }
+
+ template <typename UT>
+ result&
+ operator= (const result<UT>& r)
+ {
+ // If you get a compiler error pointing to the line below saying
+ // that the impl_ member is inaccessible, then you are most likely
+ // trying to perform an illegal result conversion, for example,
+ // from result<const obj> to result<obj>.
+ //
+ if (impl_ != r.impl_)
+ impl_ = r.impl_;
+
+ return *this;
+ }
+
+ void
+ swap (result& r)
+ {
+ // @@ add swap() to shared_ptr.
+ //
+ details::shared_ptr<result_impl_type> p (impl_);
+ impl_ = r.impl_;
+ r.impl_ = p;
+ }
+
+ public:
+ iterator
+ begin ()
+ {
+ if (impl_)
+ impl_->begin ();
+
+ return iterator (impl_.get ());
+ }
+
+ iterator
+ end ()
+ {
+ return iterator ();
+ }
+
+ // Cache the result instead of fetching the data from the database
+ // one row at a time. This is necessary if you plan on performing
+ // database operations while iterating over the result.
+ //
+ public:
+ void
+ cache ()
+ {
+ if (impl_)
+ impl_->cache ();
+ }
+
+ public:
+ bool
+ empty () const
+ {
+ if (impl_ == 0)
+ return true;
+
+ impl_->begin ();
+ return impl_->end ();
+ }
+
+ // Size is only known in cached results.
+ //
+ size_type
+ size () const
+ {
+ return impl_ ? impl_->size () : 0;
+ }
+
+ // query_one() and query_value() implementation details.
+ //
+ public:
+ typedef typename iterator::pointer_type pointer_type;
+
+ pointer_type
+ one ();
+
+ bool
+ one (T&);
+
+ // We cannot return by value here since result can be instantiated
+ // for an abstract type (polymorphic abstract base) and it seems
+ // the signature must be valid to the point being able to call the
+ // necessary constructors.
+ //
+ void
+ value (T&);
+
+ private:
+ friend class result<const T>;
+
+ details::shared_ptr<result_impl_type> impl_;
+ };
+
+ namespace common
+ {
+ using odb::result;
+ using odb::result_iterator;
+ }
+}
+
+#include <odb/result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_RESULT_HXX
diff --git a/libodb/odb/result.txx b/libodb/odb/result.txx
new file mode 100644
index 0000000..b69d92a
--- /dev/null
+++ b/libodb/odb/result.txx
@@ -0,0 +1,49 @@
+// file : odb/result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+namespace odb
+{
+ template <typename T>
+ typename result<T>::pointer_type result<T>::
+ one ()
+ {
+ iterator i (begin ());
+
+ if (i != end ())
+ {
+ pointer_type o (i.load ());
+ assert (++i == end ()); // More than one element in query_one() result.
+ return o;
+ }
+
+ return pointer_type ();
+ }
+
+ template <typename T>
+ bool result<T>::
+ one (T& o)
+ {
+ iterator i (begin ());
+
+ if (i != end ())
+ {
+ i.load (o);
+ assert (++i == end ()); // More than one element in query_one() result.
+ return true;
+ }
+
+ return false;
+ }
+
+ template <typename T>
+ void result<T>::
+ value (T& o)
+ {
+ iterator i (begin ());
+ assert (i != end ()); // Zero elements in query_value() result.
+ i.load (o);
+ assert (++i == end ()); // More than one element in query_value() result.
+ }
+}
diff --git a/libodb/odb/schema-catalog-impl.hxx b/libodb/odb/schema-catalog-impl.hxx
new file mode 100644
index 0000000..dd729db
--- /dev/null
+++ b/libodb/odb/schema-catalog-impl.hxx
@@ -0,0 +1,54 @@
+// file : odb/schema-catalog-impl.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SCHEMA_CATALOG_IMPL_HXX
+#define ODB_SCHEMA_CATALOG_IMPL_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef>
+
+#include <odb/forward.hxx> // schema_version
+
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ struct schema_catalog_impl;
+
+ // Translation unit initializer.
+ //
+ struct LIBODB_EXPORT schema_catalog_init
+ {
+ static schema_catalog_impl* catalog;
+ static std::size_t count;
+
+ schema_catalog_init ();
+ ~schema_catalog_init ();
+ };
+
+ static const schema_catalog_init schema_catalog_init_;
+
+ // Catalog entry registration.
+ //
+ struct LIBODB_EXPORT schema_catalog_create_entry
+ {
+ schema_catalog_create_entry (
+ database_id,
+ const char* name,
+ bool (*create_function) (database&, unsigned short pass, bool drop));
+ };
+
+ struct LIBODB_EXPORT schema_catalog_migrate_entry
+ {
+ schema_catalog_migrate_entry (
+ database_id,
+ const char* name,
+ schema_version,
+ bool (*migrate_function) (database&, unsigned short pass, bool pre));
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SCHEMA_CATALOG_IMPL_HXX
diff --git a/libodb/odb/schema-catalog.cxx b/libodb/odb/schema-catalog.cxx
new file mode 100644
index 0000000..1bdc112
--- /dev/null
+++ b/libodb/odb/schema-catalog.cxx
@@ -0,0 +1,387 @@
+// file : odb/schema-catalog.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <map>
+#include <vector>
+#include <cassert>
+
+#include <odb/exceptions.hxx>
+#include <odb/schema-catalog.hxx>
+#include <odb/schema-catalog-impl.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ // Schema.
+ //
+ typedef bool (*create_function) (database&, unsigned short pass, bool drop);
+ typedef bool (*migrate_function) (database&, unsigned short pass, bool pre);
+
+ typedef pair<database_id, string> key;
+ typedef vector<create_function> create_functions;
+ typedef vector<migrate_function> migrate_functions;
+ typedef map<schema_version, migrate_functions> version_map;
+
+ struct schema_functions
+ {
+ create_functions create;
+ version_map migrate;
+ };
+ typedef map<key, schema_functions> schema_map;
+
+ // Data. Normally the code would be database-independent, though there
+ // could be database-specific migration steps.
+ //
+ typedef pair<string, schema_version> data_key;
+
+ struct data_function
+ {
+ typedef schema_catalog::data_migration_function_wrapper
+ function_wrapper_type;
+
+ data_function () {}
+ data_function (database_id i, function_wrapper_type m)
+ : id (i), migrate (m) {}
+
+ database_id id;
+ function_wrapper_type migrate;
+ };
+ typedef vector<data_function> data_functions;
+ typedef map<data_key, data_functions> data_map;
+
+ struct schema_catalog_impl
+ {
+ schema_map schema;
+ data_map data;
+ };
+
+ // Static initialization.
+ //
+ schema_catalog_impl* schema_catalog_init::catalog = 0;
+ size_t schema_catalog_init::count = 0;
+
+ struct schema_catalog_init_extra
+ {
+ bool initialized;
+
+ schema_catalog_init_extra (): initialized (false) {}
+ ~schema_catalog_init_extra ()
+ {
+ if (initialized && --schema_catalog_init::count == 0)
+ delete schema_catalog_init::catalog;
+ }
+ };
+
+ static schema_catalog_init_extra schema_catalog_init_extra_;
+
+ bool schema_catalog::
+ exists (database_id id, const string& name)
+ {
+ const schema_catalog_impl& c (*schema_catalog_init::catalog);
+ return c.schema.find (key (id, name)) != c.schema.end ();
+ }
+
+ void schema_catalog::
+ create_schema (database& db, const string& name, bool drop)
+ {
+ const schema_catalog_impl& c (*schema_catalog_init::catalog);
+ schema_map::const_iterator i (c.schema.find (key (db.id (), name)));
+
+ if (i == c.schema.end ())
+ throw unknown_schema (name);
+
+ const create_functions& fs (i->second.create);
+
+ if (drop)
+ drop_schema (db, name);
+
+ // Run the passes until we ran them all or all the functions
+ // return false, which means no more passes necessary.
+ //
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ bool done (true);
+
+ for (create_functions::const_iterator j (fs.begin ()), e (fs.end ());
+ j != e; ++j)
+ {
+ if ((*j) (db, pass, false))
+ done = false;
+ }
+
+ if (done)
+ break;
+ }
+ }
+
+ void schema_catalog::
+ drop_schema (database& db, const string& name)
+ {
+ const schema_catalog_impl& c (*schema_catalog_init::catalog);
+ schema_map::const_iterator i (c.schema.find (key (db.id (), name)));
+
+ if (i == c.schema.end ())
+ throw unknown_schema (name);
+
+ const create_functions& fs (i->second.create);
+
+ // Run the passes until we ran them all or all the functions
+ // return false, which means no more passes necessary.
+ //
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ bool done (true);
+
+ for (create_functions::const_iterator j (fs.begin ()), e (fs.end ());
+ j != e; ++j)
+ {
+ if ((*j) (db, pass, true))
+ done = false;
+ }
+
+ if (done)
+ break;
+ }
+ }
+
+ void schema_catalog::
+ migrate_schema_impl (database& db,
+ schema_version v,
+ const string& name,
+ migrate_mode m)
+ {
+ const schema_catalog_impl& c (*schema_catalog_init::catalog);
+ schema_map::const_iterator i (c.schema.find (key (db.id (), name)));
+
+ if (i == c.schema.end ())
+ throw unknown_schema (name);
+
+ const version_map& vm (i->second.migrate);
+ version_map::const_iterator j (vm.find (v));
+
+ if (j == vm.end ())
+ throw unknown_schema_version (v);
+
+ const migrate_functions& fs (j->second);
+
+ // Run the passes until we ran them all or all the functions
+ // return false, which means no more passes necessary.
+ //
+ for (bool pre (m != migrate_post);; pre = false)
+ {
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ bool done (true);
+
+ for (migrate_functions::const_iterator i (fs.begin ()), e (fs.end ());
+ i != e; ++i)
+ {
+ if ((*i) (db, pass, pre))
+ done = false;
+ }
+
+ if (done)
+ break;
+ }
+
+ if (!pre || m != migrate_both)
+ break;
+ }
+
+ // Update the schema version on the database instance.
+ //
+ db.schema_version_migration (v, m == migrate_pre, name);
+ }
+
+ size_t schema_catalog::
+ migrate_data (database& db, schema_version v, const string& name)
+ {
+ if (v == 0)
+ {
+ if (!db.schema_migration ())
+ return 0;
+
+ v = db.schema_version ();
+ }
+
+ const schema_catalog_impl& c (*schema_catalog_init::catalog);
+ data_map::const_iterator i (c.data.find (data_key (name, v)));
+
+ if (i == c.data.end ())
+ return 0; // No data migration for this schema/version.
+
+ size_t r (0);
+
+ const data_functions& df (i->second);
+ for (data_functions::const_iterator i (df.begin ()), e (df.end ());
+ i != e; ++i)
+ {
+ if (i->id == id_common || i->id == db.id ())
+ {
+ const data_migration_function_wrapper &m = i->migrate;
+
+ if (m.std_function == 0)
+ m.function (db);
+ else
+ {
+ typedef void (*caller) (const void*, database&);
+ m.cast<caller> () (m.std_function, db);
+ }
+ r++;
+ }
+ }
+
+ return r;
+ }
+
+ void schema_catalog::
+ data_migration_function (database_id id,
+ schema_version v,
+ data_migration_function_wrapper f,
+ const string& name)
+ {
+ // This function can be called from a static initializer in which
+ // case the catalog might not have yet been created.
+ //
+ if (schema_catalog_init::count == 0)
+ {
+ schema_catalog_init::catalog = new schema_catalog_impl;
+ ++schema_catalog_init::count;
+ schema_catalog_init_extra_.initialized = true;
+ }
+
+ schema_catalog_impl& c (*schema_catalog_init::catalog);
+ c.data[data_key (name, v)].push_back (data_function (id, f));
+ }
+
+ void schema_catalog::
+ migrate (database& db, schema_version v, const string& name)
+ {
+ schema_version cur (current_version (db, name));
+
+ if (v == 0)
+ v = cur;
+ else if (v > cur)
+ throw unknown_schema_version (v);
+
+ schema_version i (db.schema_version (name));
+
+ if (i > v)
+ throw unknown_schema_version (i); // Database too new.
+
+ // If there is no schema, then "migrate" by creating it.
+ //
+ if (i == 0)
+ {
+ // Schema creation can only "migrate" straight to current.
+ //
+ if (v != cur)
+ throw unknown_schema_version (v);
+
+ create_schema (db, name, false);
+ return;
+ }
+
+ for (i = next_version (db, i, name);
+ i <= v;
+ i = next_version (db, i, name))
+ {
+ migrate_schema_pre (db, i, name);
+ migrate_data (db, i, name);
+ migrate_schema_post (db, i, name);
+ }
+ }
+
+ schema_version schema_catalog::
+ base_version (database_id id, const string& name)
+ {
+ const schema_catalog_impl& c (*schema_catalog_init::catalog);
+ schema_map::const_iterator i (c.schema.find (key (id, name)));
+
+ if (i == c.schema.end ())
+ throw unknown_schema (name);
+
+ const version_map& vm (i->second.migrate);
+ assert (!vm.empty ());
+ return vm.begin ()->first;
+ }
+
+ schema_version schema_catalog::
+ current_version (database_id id, const string& name)
+ {
+ const schema_catalog_impl& c (*schema_catalog_init::catalog);
+ schema_map::const_iterator i (c.schema.find (key (id, name)));
+
+ if (i == c.schema.end ())
+ throw unknown_schema (name);
+
+ const version_map& vm (i->second.migrate);
+ assert (!vm.empty ());
+ return vm.rbegin ()->first;
+ }
+
+ schema_version schema_catalog::
+ next_version (database_id id, schema_version v, const string& name)
+ {
+ const schema_catalog_impl& sc (*schema_catalog_init::catalog);
+ schema_map::const_iterator i (sc.schema.find (key (id, name)));
+
+ if (i == sc.schema.end ())
+ throw unknown_schema (name);
+
+ const version_map& vm (i->second.migrate); // Cannot be empty.
+
+ schema_version b (vm.begin ()->first);
+ schema_version c (vm.rbegin ()->first);
+
+ if (v == 0)
+ return c; // "Migration" to the current via schema creation.
+ else if (v < b)
+ throw unknown_schema_version (v); // Unsupported migration.
+
+ version_map::const_iterator j (vm.upper_bound (v));
+ return j != vm.end () ? j->first : c + 1;
+ }
+
+ // schema_catalog_init
+ //
+ schema_catalog_init::
+ schema_catalog_init ()
+ {
+ if (count == 0)
+ catalog = new schema_catalog_impl;
+
+ ++count;
+ }
+
+ schema_catalog_init::
+ ~schema_catalog_init ()
+ {
+ if (--count == 0)
+ delete catalog;
+ }
+
+ // schema_catalog_create_entry
+ //
+ schema_catalog_create_entry::
+ schema_catalog_create_entry (database_id id,
+ const char* name,
+ create_function cf)
+ {
+ schema_catalog_impl& c (*schema_catalog_init::catalog);
+ c.schema[key(id, name)].create.push_back (cf);
+ }
+
+ // schema_catalog_migrate_entry
+ //
+ schema_catalog_migrate_entry::
+ schema_catalog_migrate_entry (database_id id,
+ const char* name,
+ schema_version v,
+ migrate_function mf)
+ {
+ schema_catalog_impl& c (*schema_catalog_init::catalog);
+ c.schema[key(id, name)].migrate[v].push_back (mf);
+ }
+}
diff --git a/libodb/odb/schema-catalog.hxx b/libodb/odb/schema-catalog.hxx
new file mode 100644
index 0000000..c38cd4f
--- /dev/null
+++ b/libodb/odb/schema-catalog.hxx
@@ -0,0 +1,392 @@
+// file : odb/schema-catalog.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SCHEMA_CATALOG_HXX
+#define ODB_SCHEMA_CATALOG_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <string>
+#include <cstddef> // std::size_t
+
+#ifdef ODB_CXX11
+# include <utility> // std::move
+# include <functional> // std::function
+# include <type_traits> // std::enable_if, std::is_convertible
+#endif
+
+#include <odb/forward.hxx> // schema_version, odb::core
+#include <odb/database.hxx>
+
+#include <odb/details/export.hxx>
+#include <odb/details/unused.hxx>
+#include <odb/details/meta/static-assert.hxx>
+
+namespace odb
+{
+ class LIBODB_EXPORT schema_catalog
+ {
+ public:
+ // Schema creation.
+ //
+ static void
+ create_schema (database&, const std::string& name = "", bool drop = true);
+
+ static void
+ drop_schema (database&, const std::string& name = "");
+
+ // Schema migration.
+ //
+ public:
+ static void
+ migrate_schema_pre (database& db,
+ schema_version v,
+ const std::string& name = "")
+ {
+ migrate_schema_impl (db, v, name, migrate_pre);
+ }
+
+ static void
+ migrate_schema_post (database& db,
+ schema_version v,
+ const std::string& name = "")
+ {
+ migrate_schema_impl (db, v, name, migrate_post);
+ }
+
+ static void
+ migrate_schema (database& db,
+ schema_version v,
+ const std::string& name = "")
+ {
+ migrate_schema_impl (db, v, name, migrate_both);
+ }
+
+ // Data migration.
+ //
+ public:
+ // If version is 0, then use the current database version and also
+ // check whether we are in migration. Returns the number of calls made.
+ //
+ static std::size_t
+ migrate_data (database&,
+ schema_version = 0,
+ const std::string& name = "");
+
+ typedef void data_migration_function_type (database&);
+ typedef data_migration_function_type* data_migration_function_ptr;
+
+ typedef details::function_wrapper<data_migration_function_type>
+ data_migration_function_wrapper;
+
+ // The following three variants of the registration functions make
+ // sure that the version is greater that the base model version.
+ // This helps with identifying and removing data migration function
+ // that are no longer required.
+ //
+ // Data migration functions are called in the order of registration.
+ //
+#ifndef ODB_CXX11
+ template <schema_version v, schema_version base>
+ static void
+ data_migration_function (data_migration_function_ptr f,
+ const std::string& name = "")
+ {
+ data_migration_function<v, base> (id_common, f, name);
+ }
+
+#else
+ template <schema_version v, schema_version base, typename F>
+ static typename std::enable_if<
+ std::is_convertible<
+ F, std::function<data_migration_function_type>>::value, void>::type
+ data_migration_function (F f, const std::string& name = "")
+ {
+ data_migration_function<v, base> (id_common, std::move (f), name);
+ }
+#endif
+
+ // Database-specific data migration.
+ //
+#ifndef ODB_CXX11
+ template <schema_version v, schema_version base>
+ static void
+ data_migration_function (database& db,
+ data_migration_function_ptr f,
+ const std::string& name = "")
+ {
+ data_migration_function<v, base> (db.id (), f, name);
+ }
+#else
+ template <schema_version v, schema_version base, typename F>
+ static typename std::enable_if<
+ std::is_convertible<
+ F, std::function<data_migration_function_type>>::value, void>::type
+ data_migration_function (database& db, F f, const std::string& name = "")
+ {
+ data_migration_function<v, base> (db.id (), std::move (f), name);
+ }
+#endif
+
+#ifndef ODB_CXX11
+ template <schema_version v, schema_version base>
+ static void
+ data_migration_function (database_id id,
+ data_migration_function_ptr f,
+ const std::string& name = "")
+ {
+ // If the data migration version is below the base model version
+ // then it will never be called.
+ //
+
+ // Poor man's static_assert.
+ //
+ typedef details::meta::static_assert_test<(v > base || base == 0)>
+ data_migration_function_is_no_longer_necessary;
+
+ char sa [sizeof (data_migration_function_is_no_longer_necessary)];
+ ODB_POTENTIALLY_UNUSED (sa);
+
+ data_migration_function (id, v, f, name);
+ }
+#else
+ template <schema_version v, schema_version base, typename F>
+ static typename std::enable_if<
+ std::is_convertible<
+ F, std::function<data_migration_function_type>>::value, void>::type
+ data_migration_function (database_id id, F f, const std::string& name = "")
+ {
+ // If the data migration version is below the base model version
+ // then it will never be called.
+ //
+ static_assert (v > base || base == 0,
+ "data migration function is no longer necessary");
+
+ data_migration_function (id, v, std::move (f), name);
+ }
+#endif
+
+ // The same as above but take the version as an argument and do
+ // not check whether it is greater than the base model version.
+ //
+#ifndef ODB_CXX11
+ static void
+ data_migration_function (schema_version v,
+ data_migration_function_ptr f,
+ const std::string& name = "")
+ {
+ data_migration_function (id_common, v, f, name);
+ }
+#else
+ template <typename F>
+ static typename std::enable_if<
+ std::is_convertible<
+ F, std::function<data_migration_function_type>>::value, void>::type
+ data_migration_function (schema_version v,
+ F f,
+ const std::string& name = "")
+ {
+ data_migration_function (id_common, v, std::move (f), name);
+ }
+#endif
+
+#ifndef ODB_CXX11
+ static void
+ data_migration_function (database& db,
+ schema_version v,
+ data_migration_function_ptr f,
+ const std::string& name = "")
+ {
+ data_migration_function (db.id (), v, f, name);
+ }
+#else
+ template <typename F>
+ static typename std::enable_if<
+ std::is_convertible<
+ F, std::function<data_migration_function_type>>::value, void>::type
+ data_migration_function (database& db,
+ schema_version v,
+ F f,
+ const std::string& name = "")
+ {
+ data_migration_function (db.id (), v, std::move (f), name);
+ }
+#endif
+
+#ifndef ODB_CXX11
+ static void
+ data_migration_function (database_id i,
+ schema_version v,
+ data_migration_function_ptr f,
+ const std::string& name = "")
+ {
+ data_migration_function (i,
+ v,
+ data_migration_function_wrapper (f),
+ name);
+ }
+#else
+ template <typename F>
+ static typename std::enable_if<
+ std::is_convertible<
+ F, std::function<data_migration_function_type>>::value, void>::type
+ data_migration_function (database_id i,
+ schema_version v,
+ F f,
+ const std::string& name = "")
+ {
+ data_migration_function (
+ i,
+ v,
+ data_migration_function_wrapper (std::move (f)),
+ name);
+ }
+#endif
+
+ private:
+ static void
+ data_migration_function (database_id,
+ schema_version,
+ data_migration_function_wrapper,
+ const std::string& name);
+
+ // Combined schema and data migration.
+ //
+ public:
+ // Migrate both schema and data to the specified version. If version
+ // is not specified, then migrate to the current model version.
+ //
+ static void
+ migrate (database&, schema_version = 0, const std::string& name = "");
+
+ // Schema version information.
+ //
+ public:
+ // Return the base model version.
+ //
+ static schema_version
+ base_version (const database& db, const std::string& name = "")
+ {
+ return base_version (db.id (), name);
+ }
+
+ static schema_version
+ base_version (database_id, const std::string& name = "");
+
+ // Return the current model version.
+ //
+ static schema_version
+ current_version (const database& db, const std::string& name = "")
+ {
+ return current_version (db.id (), name);
+ }
+
+ static schema_version
+ current_version (database_id, const std::string& name = "");
+
+ // Return current model version + 1 (that is, one past current) if
+ // the passed version is equal to or greater than current. If the
+ // version is not specified, then use the current database version.
+ //
+ static schema_version
+ next_version (const database& db,
+ schema_version v = 0,
+ const std::string& name = "")
+ {
+ return next_version (db.id (), v == 0 ? db.schema_version () : v, name);
+ }
+
+ static schema_version
+ next_version (database_id,
+ schema_version,
+ const std::string& name = "");
+
+ // Schema existence.
+ //
+ public:
+ // Test for presence of a schema with a specific name.
+ //
+ static bool
+ exists (const database& db, const std::string& name = "")
+ {
+ return exists (db.id (), name);
+ }
+
+ static bool
+ exists (database_id, const std::string& name = "");
+
+ private:
+ enum migrate_mode
+ {
+ migrate_pre,
+ migrate_post,
+ migrate_both
+ };
+
+ static void
+ migrate_schema_impl (database&,
+ schema_version,
+ const std::string& name,
+ migrate_mode);
+ };
+
+ // Static data migration function registration.
+ //
+ template <schema_version v, schema_version base>
+ struct data_migration_entry
+ {
+ typedef schema_catalog::data_migration_function_type function_type;
+
+#ifndef ODB_CXX11
+ data_migration_entry (function_type* f, const std::string& name = "")
+ {
+ schema_catalog::data_migration_function<v, base> (f, name);
+ }
+#else
+ template <typename F>
+ data_migration_entry (F f,
+ const std::string& name = "",
+ typename std::enable_if<std::is_convertible<
+ F, std::function<function_type>>::value>
+ ::type* = 0)
+ {
+ schema_catalog::data_migration_function<v, base> (std::move (f), name);
+ }
+#endif
+
+#ifndef ODB_CXX11
+ data_migration_entry (database_id id,
+ function_type *f,
+ const std::string& name = "")
+ {
+ schema_catalog::data_migration_function<v, base> (id, v, f, name);
+ }
+#else
+ template <typename F>
+ data_migration_entry (database_id id,
+ F f,
+ const std::string& name = "",
+ typename std::enable_if<std::is_convertible<
+ F, std::function<function_type>>::value>
+ ::type* = 0)
+ {
+ schema_catalog::data_migration_function<v, base> (id,
+ v,
+ std::move (f),
+ name);
+ }
+#endif
+ };
+
+ namespace common
+ {
+ using odb::schema_catalog;
+ using odb::data_migration_entry;
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SCHEMA_CATALOG_HXX
diff --git a/libodb/odb/schema-version.hxx b/libodb/odb/schema-version.hxx
new file mode 100644
index 0000000..1f047a4
--- /dev/null
+++ b/libodb/odb/schema-version.hxx
@@ -0,0 +1,71 @@
+// file : odb/schema-version.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SCHEMA_VERSION_HXX
+#define ODB_SCHEMA_VERSION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx> // schema_version
+
+namespace odb
+{
+ struct schema_version_migration
+ {
+ schema_version_migration (schema_version v = 0, bool m = false)
+ : version (v), migration (m) {}
+
+ schema_version version;
+ bool migration;
+ };
+
+ // Version ordering is as follows: {1,f} < {2,t} < {2,f} < {3,t}
+ //
+ inline bool
+ operator== (const schema_version_migration& x,
+ const schema_version_migration& y)
+ {
+ return x.version == y.version && x.migration == y.migration;
+ }
+
+ inline bool
+ operator!= (const schema_version_migration& x,
+ const schema_version_migration& y)
+ {
+ return !(x == y);
+ }
+
+ inline bool
+ operator< (const schema_version_migration& x,
+ const schema_version_migration& y)
+ {
+ return x.version < y.version ||
+ (x.version == y.version && x.migration && !y.migration);
+ }
+
+ inline bool
+ operator> (const schema_version_migration& x,
+ const schema_version_migration& y)
+ {
+ return x.version > y.version ||
+ (x.version == y.version && !x.migration && y.migration);
+ }
+
+ inline bool
+ operator<= (const schema_version_migration& x,
+ const schema_version_migration& y)
+ {
+ return !(x > y);
+ }
+
+ inline bool
+ operator>= (const schema_version_migration& x,
+ const schema_version_migration& y)
+ {
+ return !(x < y);
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SCHEMA_VERSION_HXX
diff --git a/libodb/odb/section.cxx b/libodb/odb/section.cxx
new file mode 100644
index 0000000..0fe5211
--- /dev/null
+++ b/libodb/odb/section.cxx
@@ -0,0 +1,27 @@
+// file : odb/section.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/section.hxx>
+
+namespace odb
+{
+ void section::
+ disarm ()
+ {
+ transaction& t (transaction::current ());
+ t.callback_unregister (this);
+ state_.armed = 0;
+ }
+
+ void section::
+ transacion_callback (unsigned short event, void* key, unsigned long long)
+ {
+ section& s (*static_cast<section*> (key));
+
+ if (event == transaction::event_rollback && s.state_.restore)
+ s.state_.changed = 1;
+
+ s.state_.armed = 0;
+ s.state_.restore = 0;
+ }
+}
diff --git a/libodb/odb/section.hxx b/libodb/odb/section.hxx
new file mode 100644
index 0000000..98b98cf
--- /dev/null
+++ b/libodb/odb/section.hxx
@@ -0,0 +1,122 @@
+// file : odb/section.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SECTION_HXX
+#define ODB_SECTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ class LIBODB_EXPORT section
+ {
+ public:
+ // Load state.
+ //
+ bool
+ loaded () const {return state_.loaded;}
+
+ // Mark 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.
+ //
+ void
+ unload ()
+ {
+ state_.loaded = 0;
+ state_.changed = 0;
+ state_.restore = 0;
+ }
+
+ // Mark an unloaded section as loaded. This, for example, can be useful if
+ // you don't want to load the old data before overwriting it with update().
+ //
+ void
+ load ()
+ {
+ state_.loaded = 1;
+ }
+
+ // Change state.
+ //
+ bool
+ changed () const {return state_.changed;}
+
+ // Mark the section as changed.
+ //
+ void
+ change ()
+ {
+ state_.changed = 1;
+ state_.restore = 0;
+ }
+
+ // User data. 4 bits of custom state.
+ //
+ unsigned char
+ user_data () const {return state_.user;}
+
+ void
+ user_data (unsigned char u) {state_.user = u;}
+
+ public:
+ section ()
+ {
+ state_.loaded = 0;
+ state_.changed = 0;
+ state_.armed = 0;
+ state_.restore = 0;
+ }
+
+ ~section ()
+ {
+ if (state_.armed)
+ disarm ();
+ }
+
+ // Implementation details.
+ //
+ public:
+ // Arm the callback and set the restore flag if transaction is not NULL.
+ //
+ void
+ reset (bool l = false, bool c = false, transaction* t = 0) const
+ {
+ state_.loaded = l;
+ state_.changed = c;
+
+ if (t != 0 && !state_.armed)
+ {
+ t->callback_register (&transacion_callback,
+ const_cast<section*> (this));
+ state_.armed = 1;
+ }
+
+ state_.restore = (t != 0);
+ }
+
+ private:
+ void
+ disarm ();
+
+ static void
+ transacion_callback (unsigned short, void* key, unsigned long long);
+
+ private:
+ mutable struct
+ {
+ unsigned char loaded : 1;
+ unsigned char changed : 1;
+ unsigned char armed : 1; // Transaction callback is armed.
+ unsigned char restore: 1; // Restore changed flag on rollback.
+ unsigned char user : 4; // User data.
+ } state_;
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SECTION_HXX
diff --git a/libodb/odb/session.cxx b/libodb/odb/session.cxx
new file mode 100644
index 0000000..bc0e854
--- /dev/null
+++ b/libodb/odb/session.cxx
@@ -0,0 +1,66 @@
+// file : odb/session.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/exceptions.hxx>
+#include <odb/session.hxx>
+
+#include <odb/details/tls.hxx>
+
+namespace odb
+{
+ using namespace details;
+
+ static ODB_TLS_POINTER (session) current_session;
+
+ session::
+ session (bool make_current)
+ {
+ if (make_current)
+ {
+ if (has_current ())
+ throw already_in_session ();
+
+ current_pointer (this);
+ }
+ }
+
+ session::
+ ~session ()
+ {
+ // If we are the current thread's session, reset it.
+ //
+ if (current_pointer () == this)
+ reset_current ();
+ }
+
+ session* session::
+ current_pointer ()
+ {
+ return tls_get (current_session);
+ }
+
+ void session::
+ current_pointer (session* s)
+ {
+ tls_set (current_session, s);
+ }
+
+ session& session::
+ current ()
+ {
+ session* cur (tls_get (current_session));
+
+ if (cur == 0)
+ throw not_in_session ();
+
+ return *cur;
+ }
+
+ //
+ // object_map_base
+ //
+ session::object_map_base::
+ ~object_map_base ()
+ {
+ }
+}
diff --git a/libodb/odb/session.hxx b/libodb/odb/session.hxx
new file mode 100644
index 0000000..a14c42f
--- /dev/null
+++ b/libodb/odb/session.hxx
@@ -0,0 +1,217 @@
+// file : odb/session.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SESSION_HXX
+#define ODB_SESSION_HXX
+
+#include <odb/pre.hxx>
+
+#include <map>
+#include <typeinfo>
+
+#include <odb/traits.hxx>
+#include <odb/forward.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/type-info.hxx>
+
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ class LIBODB_EXPORT session
+ {
+ public:
+ typedef odb::database database_type;
+
+ // If the make_current argument is true, then set the current thread's
+ // session to this session. If another session is already in effect,
+ // throw the already_in_session exception.
+ //
+ session (bool make_current = true);
+
+ // Reset the current thread's session if it is this session.
+ //
+ ~session ();
+
+ // Current session.
+ //
+ public:
+ // Return true if there is a session in effect in the current
+ // thread.
+ //
+ static bool
+ has_current () {return current_pointer () != 0;}
+
+ // Get current thread's session. Throw if no session is in effect.
+ //
+ static session&
+ current ();
+
+ // Set current thread's session.
+ //
+ static void
+ current (session& s) {current_pointer (&s);}
+
+ // Revert to the no session in effect state for the current thread.
+ //
+ static void
+ reset_current () {current_pointer (0);}
+
+ // Pointer versions.
+ //
+ static session*
+ current_pointer ();
+
+ static void
+ current_pointer (session*);
+
+ // Copying or assignment of sessions is not supported.
+ //
+ private:
+ session (const session&);
+ session& operator= (const session&);
+
+ public:
+ struct LIBODB_EXPORT object_map_base: details::shared_base
+ {
+ virtual
+ ~object_map_base ();
+ };
+
+ template <typename T>
+ struct object_map: object_map_base,
+ std::map<typename object_traits<T>::id_type,
+ typename object_traits<T>::pointer_type>
+ {
+ };
+
+ // Object cache.
+ //
+ public:
+ // Position in the cache of the inserted element.
+ //
+ template <typename T>
+ struct cache_position;
+
+ template <typename T>
+ cache_position<T>
+ cache_insert (database_type&,
+ const typename object_traits<T>::id_type&,
+ const typename object_traits<T>::pointer_type&);
+
+ template <typename T>
+ typename object_traits<T>::pointer_type
+ cache_find (database_type&,
+ const typename object_traits<T>::id_type&) const;
+
+ template <typename T>
+ void
+ cache_erase (const cache_position<T>&);
+
+ template <typename T>
+ void
+ cache_erase (database_type&, const typename object_traits<T>::id_type&);
+
+ // Low-level object cache access (iteration, etc).
+ //
+ public:
+ typedef std::map<const std::type_info*,
+ details::shared_ptr<object_map_base>,
+ details::type_info_comparator> type_map;
+
+ typedef std::map<database_type*, type_map> database_map;
+
+ database_map&
+ map () {return db_map_;}
+
+ const database_map&
+ map () const {return db_map_;}
+
+ // Static cache API as expected by the rest of ODB.
+ //
+ public:
+ static bool
+ _has_cache () {return has_current ();}
+
+ // Position in the cache of the inserted element. The requirements
+ // for this class template are: default and copy-constructible as
+ // well as copy-assignable. The default constructor creates an
+ // empty/NULL position.
+ //
+ template <typename T>
+ struct cache_position
+ {
+ typedef object_map<T> map;
+ typedef typename map::iterator iterator;
+
+ cache_position (): map_ (0) {}
+ cache_position (map& m, const iterator& p): map_ (&m), pos_ (p) {}
+
+ cache_position (const cache_position& p)
+ : map_ (p.map_), pos_ (p.pos_) {}
+
+ cache_position&
+ operator= (const cache_position& p)
+ {
+ // It might not be ok to use an uninitialized iterator on the rhs.
+ //
+ if (p.map_ != 0)
+ pos_ = p.pos_;
+ map_ = p.map_;
+ return *this;
+ }
+
+ map* map_;
+ iterator pos_;
+ };
+
+ // The following cache management functions are all static to
+ // allow for a custom notion of a current session. The erase()
+ // function is called to remove the object if the operation
+ // that caused it to be inserted (e.g., load) failed.
+ //
+ template <typename T>
+ static cache_position<T>
+ _cache_insert (database_type&,
+ const typename object_traits<T>::id_type&,
+ const typename object_traits<T>::pointer_type&);
+
+ template <typename T>
+ static typename object_traits<T>::pointer_type
+ _cache_find (database_type&, const typename object_traits<T>::id_type&);
+
+ template <typename T>
+ static void
+ _cache_erase (const cache_position<T>&);
+
+ // Notifications. These are called after per-object callbacks for
+ // post_{persist, load, update, erase} events.
+ //
+ 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 (database_type&, const T&) {}
+
+ template <typename T>
+ static void
+ _cache_erase (database_type&, const typename object_traits<T>::id_type&);
+
+ protected:
+ database_map db_map_;
+ };
+}
+
+#include <odb/session.ixx>
+#include <odb/session.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SESSION_HXX
diff --git a/libodb/odb/session.ixx b/libodb/odb/session.ixx
new file mode 100644
index 0000000..4acdc1b
--- /dev/null
+++ b/libodb/odb/session.ixx
@@ -0,0 +1,60 @@
+// file : odb/session.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/exceptions.hxx>
+
+namespace odb
+{
+ template <typename T>
+ inline void session::
+ cache_erase (const cache_position<T>& p)
+ {
+ // @@ Empty maps are not cleaned up by this version of erase.
+ //
+ if (p.map_ != 0)
+ p.map_->erase (p.pos_);
+ }
+
+ template <typename T>
+ inline typename session::cache_position<T> session::
+ _cache_insert (database_type& db,
+ const typename object_traits<T>::id_type& id,
+ const typename object_traits<T>::pointer_type& obj)
+ {
+ if (session* s = current_pointer ())
+ return s->cache_insert<T> (db, id, obj);
+ else
+ return cache_position<T> ();
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type session::
+ _cache_find (database_type& db, const typename object_traits<T>::id_type& id)
+ {
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ if (const session* s = current_pointer ())
+ return s->cache_find<T> (db, id);
+ else
+ return pointer_type ();
+ }
+
+ template <typename T>
+ inline void session::
+ _cache_erase (const cache_position<T>& p)
+ {
+ // @@ Empty maps are not cleaned up by this version of erase.
+ //
+ if (p.map_ != 0)
+ p.map_->erase (p.pos_);
+ }
+
+ template <typename T>
+ inline void session::
+ _cache_erase (database_type& db,
+ const typename object_traits<T>::id_type& id)
+ {
+ if (session* s = current_pointer ())
+ s->cache_erase<T> (db, id);
+ }
+}
diff --git a/libodb/odb/session.txx b/libodb/odb/session.txx
new file mode 100644
index 0000000..d74fe0f
--- /dev/null
+++ b/libodb/odb/session.txx
@@ -0,0 +1,90 @@
+// file : odb/session.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ template <typename T>
+ typename session::cache_position<T> session::
+ cache_insert (database_type& db,
+ const typename object_traits<T>::id_type& id,
+ const typename object_traits<T>::pointer_type& obj)
+ {
+ type_map& tm (db_map_[&db]);
+ details::shared_ptr<object_map_base>& pom (tm[&typeid (T)]);
+
+ if (!pom)
+ pom.reset (new (details::shared) object_map<T>);
+
+ object_map<T>& om (static_cast<object_map<T>&> (*pom));
+
+ typename object_map<T>::value_type vt (id, obj);
+ std::pair<typename object_map<T>::iterator, bool> r (om.insert (vt));
+
+ // In what situation may we possibly attempt to reinsert the object?
+ // For example, when the user loads the same object in two different
+ // instances (i.e., load into a pre-allocated object). In this case
+ // we should probably update our entries accordingly.
+ //
+ if (!r.second)
+ r.first->second = obj;
+
+ return cache_position<T> (om, r.first);
+ }
+
+ template <typename T>
+ typename object_traits<T>::pointer_type session::
+ cache_find (database_type& db,
+ const typename object_traits<T>::id_type& id) const
+ {
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ database_map::const_iterator di (db_map_.find (&db));
+
+ if (di == db_map_.end ())
+ return pointer_type ();
+
+ const type_map& tm (di->second);
+ type_map::const_iterator ti (tm.find (&typeid (T)));
+
+ if (ti == tm.end ())
+ return pointer_type ();
+
+ const object_map<T>& om (static_cast<const object_map<T>&> (*ti->second));
+ typename object_map<T>::const_iterator oi (om.find (id));
+
+ if (oi == om.end ())
+ return pointer_type ();
+
+ return oi->second;
+ }
+
+ template <typename T>
+ void session::
+ cache_erase (database_type& db, const typename object_traits<T>::id_type& id)
+ {
+ database_map::iterator di (db_map_.find (&db));
+
+ if (di == db_map_.end ())
+ return;
+
+ type_map& tm (di->second);
+ type_map::iterator ti (tm.find (&typeid (T)));
+
+ if (ti == tm.end ())
+ return;
+
+ object_map<T>& om (static_cast<object_map<T>&> (*ti->second));
+ typename object_map<T>::iterator oi (om.find (id));
+
+ if (oi == om.end ())
+ return;
+
+ om.erase (oi);
+
+ if (om.empty ())
+ tm.erase (ti);
+
+ if (tm.empty ())
+ db_map_.erase (di);
+ }
+}
diff --git a/libodb/odb/simple-object-result.hxx b/libodb/odb/simple-object-result.hxx
new file mode 100644
index 0000000..53c0cb2
--- /dev/null
+++ b/libodb/odb/simple-object-result.hxx
@@ -0,0 +1,201 @@
+// file : odb/simple-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SIMPLE_OBJECT_RESULT_HXX
+#define ODB_SIMPLE_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+#include <utility> // std::move
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+#include <odb/result.hxx>
+#include <odb/object-result.hxx>
+#include <odb/pointer-traits.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ // Implementation for non-polymorphic objects with object id.
+ //
+ template <typename T>
+ class object_result_impl: public result_impl
+ {
+ protected:
+ // In result_impl, T is always non-const and the same as object_type.
+ //
+ typedef T object_type;
+ typedef odb::object_traits<object_type> object_traits;
+ typedef typename object_traits::id_type id_type;
+
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef odb::pointer_traits<pointer_type> pointer_traits;
+
+ friend class result<T>;
+ friend class result<const T>;
+ friend class result_iterator<T, class_object>;
+ friend class result_iterator<const T, class_object>;
+ friend class object_result_iterator<T, id_type, false>;
+ friend class object_result_iterator<const T, id_type, false>;
+
+ protected:
+ object_result_impl (odb::connection& conn)
+ : result_impl (conn), begin_ (true), end_ (false), current_ ()
+ {
+ }
+
+ // To make this work with all kinds of pointers (raw, std::auto_ptr,
+ // shared), we need to make sure we don't make any copies of the
+ // pointer on the return path.
+ //
+ pointer_type&
+ current ()
+ {
+ if (pointer_traits::null_ptr (current_) && !end_)
+ load ();
+
+ return current_;
+ }
+
+ void
+ release ()
+ {
+ current_ = pointer_type ();
+ guard_.release ();
+ }
+
+ void
+ begin ()
+ {
+ if (begin_)
+ {
+ next ();
+ begin_ = false;
+ }
+ }
+
+ bool
+ end () const
+ {
+ return end_;
+ }
+
+ protected:
+ // The fetch argument is a hint to the implementation. If it is
+ // false then it means load_id() was already called (and presumably
+ // fetched the data into the object image) and the object image
+ // is still valid (so the implementation doesn't need to fetch
+ // the data again).
+ //
+ virtual void
+ load (object_type&, bool fetch = true) = 0;
+
+ virtual id_type
+ load_id () = 0;
+
+ virtual void
+ next () = 0;
+
+ virtual void
+ cache () = 0;
+
+ virtual std::size_t
+ size () = 0;
+
+ protected:
+#ifdef ODB_CXX11
+ void
+ current (pointer_type& p, bool guard = true)
+ {
+ current_ = std::move (p);
+
+ if (guard)
+ guard_.reset (current_);
+ else
+ guard_.reset ();
+ }
+
+ void
+ current (pointer_type&& p, bool guard = true)
+ {
+ current (p, guard);
+ }
+#else
+ void
+ current (pointer_type p, bool guard = true)
+ {
+ current_ = p;
+
+ if (guard)
+ guard_.reset (current_);
+ else
+ guard_.reset ();
+ }
+#endif
+
+ bool begin_;
+ bool end_;
+
+ private:
+ void
+ load ();
+
+ private:
+ pointer_type current_;
+ typename pointer_traits::guard guard_;
+ };
+
+ template <typename T, typename ID>
+ class object_result_iterator<T, ID, false>
+ {
+ public:
+ // T can be const T while object_type is always non-const.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+ typedef typename object_traits<T>::id_type id_type;
+
+ typedef object_result_impl<object_type> result_impl_type;
+
+ public:
+ object_result_iterator (result_impl_type* res)
+ : res_ (res)
+ {
+ }
+
+ public:
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ pointer_type
+ load ()
+ {
+#ifdef ODB_CXX11
+ pointer_type r (std::move (res_->current ()));
+#else
+ pointer_type r (res_->current ());
+#endif
+ res_->release ();
+ return r;
+ }
+
+ void
+ load (object_type&);
+
+ id_type
+ id ()
+ {
+ return res_->load_id ();
+ }
+
+ protected:
+ result_impl_type* res_;
+ };
+}
+
+#include <odb/simple-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SIMPLE_OBJECT_RESULT_HXX
diff --git a/libodb/odb/simple-object-result.txx b/libodb/odb/simple-object-result.txx
new file mode 100644
index 0000000..00521f8
--- /dev/null
+++ b/libodb/odb/simple-object-result.txx
@@ -0,0 +1,58 @@
+// file : odb/simple-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ //
+ // object_result_impl
+ //
+
+ template <typename T>
+ void object_result_impl<T>::
+ load ()
+ {
+ // First check the session.
+ //
+ const id_type& id (load_id ());
+
+ pointer_type p (object_traits::pointer_cache_traits::find (db_, id));
+
+ if (!pointer_traits::null_ptr (p))
+ current (p, false); // Pointer from cache should not be guarded.
+ else
+ {
+ p = object_traits::create ();
+
+ typename object_traits::pointer_cache_traits::insert_guard ig (
+ object_traits::pointer_cache_traits::insert (db_, id, p));
+
+ object_type& obj (pointer_traits::get_ref (p));
+ current (p);
+ load (obj, false);
+ object_traits::pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+ }
+
+ //
+ // object_result_iterator
+ //
+
+ template <typename T, typename ID>
+ void object_result_iterator<T, ID, false>::
+ load (object_type& obj)
+ {
+ if (res_->end ())
+ return;
+
+ typedef odb::object_traits<object_type> object_traits;
+
+ typename object_traits::reference_cache_traits::position_type p (
+ object_traits::reference_cache_traits::insert (
+ res_->db_, res_->load_id (), obj));
+ typename object_traits::reference_cache_traits::insert_guard ig (p);
+ res_->load (obj, false);
+ object_traits::reference_cache_traits::load (p);
+ ig.release ();
+ }
+}
diff --git a/libodb/odb/statement-processing-common.hxx b/libodb/odb/statement-processing-common.hxx
new file mode 100644
index 0000000..23661fc
--- /dev/null
+++ b/libodb/odb/statement-processing-common.hxx
@@ -0,0 +1,214 @@
+// file : odb/statement-processingc-common.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STATEMENT_PROCESSING_COMMON_HXX
+#define ODB_STATEMENT_PROCESSING_COMMON_HXX
+
+#include <odb/pre.hxx>
+
+//#define LIBODB_DEBUG_STATEMENT_PROCESSING 1
+//#define LIBODB_TRACE_STATEMENT_PROCESSING 1
+
+#include <string>
+#include <cstddef> // std::size_t
+
+namespace odb
+{
+ typedef std::char_traits<char> traits;
+
+ static inline const char*
+ find (const char* b, const char* e, char c)
+ {
+ return traits::find (b, e - b, c);
+ }
+
+ static inline const char*
+ rfind (const char* b, const char* e, char c)
+ {
+ for (--e; b != e; --e)
+ if (*e == c)
+ return e;
+
+ return 0;
+ }
+
+ static inline const char*
+ find (const char* b, const char* e, const char* s, std::size_t n)
+ {
+ for (; b != e; ++b)
+ {
+ if (*b == *s &&
+ static_cast<std::size_t> (e - b) >= n &&
+ traits::compare (b, s, n) == 0)
+ return b;
+ }
+
+ return 0;
+ }
+
+ // Iterate over INSERT column/value list, UPDATE SET expression list,
+ // or SELECT column/join list.
+ //
+ // for (const char* b (columns_begin), *e (begin (b, end));
+ // e != 0;
+ // next (b, e, end))
+ // {
+ // // b points to the beginning of the value (i.e., one past '(').
+ // // e points one past the end of the value (i.e., to ',', ')', or '\n').
+ // }
+ //
+ // // b points one past the last value.
+ //
+ static inline const char*
+ paren_begin (const char*& b, const char* end)
+ {
+ // Note that the list may not end with '\n'.
+
+ b++; // Skip '('.
+ const char* e (find (b, end, '\n'));
+ return (e != 0 ? e : end) - 1; // Skip ',' or ')'.
+ }
+
+ static inline void
+ paren_next (const char*& b, const char*& e, const char* end)
+ {
+ if (*e == ',')
+ {
+ b = e + 2; // Skip past '\n'.
+ e = find (b, end, '\n');
+ e = (e != 0 ? e : end) - 1; // Skip ',' or ')'.
+ }
+ else
+ {
+ b = (e + 1 != end ? e + 2 : end); // Skip past '\n'.
+ e = 0;
+ }
+ }
+
+ static inline const char*
+ comma_begin (const char* b, const char* end)
+ {
+ // Note that the list may not end with '\n'.
+
+ const char* e (find (b, end, '\n'));
+ return e != 0 ? e - (*(e - 1) == ',' ? 1 : 0) : end; // Skip ','.
+ }
+
+ static inline void
+ comma_next (const char*& b, const char*& e, const char* end)
+ {
+ if (*e == ',')
+ {
+ b = e + 2; // Skip past '\n'.
+ e = find (b, end, '\n');
+ e = (e != 0 ? e - (*(e - 1) == ',' ? 1 : 0) : end); // Skip ','.
+ }
+ else
+ {
+ b = (e != end ? e + 1 : end); // Skip past '\n'.
+ e = 0;
+ }
+ }
+
+ // Only allows A-Z and spaces before prefix (e.g., JOIN in LEFT OUTER JOIN).
+ //
+ static inline bool
+ fuzzy_prefix (const char* b,
+ const char* end,
+ const char* prefix,
+ std::size_t prefix_size)
+ {
+ for (; b != end; ++b)
+ {
+ char c (*b);
+
+ if ((c < 'A' || c > 'Z') && c != ' ')
+ break;
+
+ if (c == *prefix &&
+ static_cast<std::size_t> (end - b) > prefix_size &&
+ traits::compare (b, prefix, prefix_size) == 0)
+ return true;
+ }
+
+ return false;
+ }
+
+ static inline const char*
+ newline_begin (const char* b, const char* end)
+ {
+ // Note that the list may not end with '\n'.
+
+ const char* e (find (b, end, '\n'));
+ return e != 0 ? e : end;
+ }
+
+ static inline void
+ newline_next (const char*& b,
+ const char*& e,
+ const char* end,
+ const char* prefix,
+ std::size_t prefix_size,
+ bool prefix_fuzzy = false)
+ {
+ if (e != end)
+ e++; // Skip past '\n'.
+
+ b = e;
+
+ // Do we have another element?
+ //
+ if (static_cast<std::size_t> (end - b) > prefix_size &&
+ (prefix_fuzzy
+ ? fuzzy_prefix (b, end, prefix, prefix_size)
+ : traits::compare (b, prefix, prefix_size) == 0))
+ {
+ e = find (b, end, '\n');
+ if (e == 0)
+ e = end;
+ }
+ else
+ e = 0;
+ }
+
+ // Note that end must point to the beginning of the list.
+ //
+ static inline const char*
+ newline_rbegin (const char* e, const char* end)
+ {
+ const char* b (rfind (end, e - 1, '\n'));
+ return b != 0 ? b + 1 : end; // Skip past '\n'.
+ }
+
+ static inline void
+ newline_rnext (const char*& b, const char*& e, const char* end)
+ {
+ if (b != end)
+ {
+ e = b - 1; // Skip to previous '\n'.
+ b = rfind (end, e - 1, '\n');
+ b = (b != 0 ? b + 1 : end); // Skip past '\n'.
+ }
+ else
+ {
+ e = end - 1; // One before the first element.
+ b = 0;
+ }
+ }
+
+ // Fast path: just remove the "structure".
+ //
+ static inline void
+ process_fast (const char* s, std::string& r)
+ {
+ r = s;
+ for (std::size_t i (r.find ('\n'));
+ i != std::string::npos;
+ i = r.find ('\n', i))
+ r[i++] = ' ';
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STATEMENT_PROCESSING_COMMON_HXX
diff --git a/libodb/odb/statement-processing.cxx b/libodb/odb/statement-processing.cxx
new file mode 100644
index 0000000..708c9ab
--- /dev/null
+++ b/libodb/odb/statement-processing.cxx
@@ -0,0 +1,685 @@
+// file : odb/statement-processing.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/statement-processing-common.hxx>
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+# include <iostream>
+#endif
+
+#include <odb/statement.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ typedef const void* const* bind_type;
+
+ static inline const void*
+ bind_at (size_t i, bind_type bind, size_t bind_skip)
+ {
+ const char* b (reinterpret_cast<const char*> (bind));
+ return *reinterpret_cast<bind_type> (b + i * bind_skip);
+ }
+
+ void statement::
+ process_insert (string& r,
+ const char* s,
+ bind_type bind,
+ size_t bind_size,
+ size_t bind_skip,
+ char param_symbol,
+ char param_symbol2)
+ {
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ assert (bind_size != 0); // Cannot be versioned.
+#endif
+
+ bool fast (true); // Fast case (if all present).
+ for (size_t i (0); i != bind_size && fast; ++i)
+ {
+ if (bind_at (i, bind, bind_skip) == 0)
+ fast = false;
+ }
+
+ // Fast path: just remove the "structure".
+ //
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ if (fast)
+ {
+ process_fast (s, r);
+ return;
+ }
+#endif
+
+ // Scan the statement and store the positions of various parts.
+ //
+ size_t n (traits::length (s));
+ const char* e (s + n);
+
+ // Header.
+ //
+ const char* p (find (s, e, '\n'));
+ assert (p != 0);
+ size_t header_size (p - s);
+ p++;
+
+ // Column list.
+ //
+ const char* columns_begin (0);
+ if (*p == '(')
+ {
+ columns_begin = p;
+
+ // Find the end of the column list.
+ //
+ for (const char* ce (paren_begin (p, e)); ce != 0; paren_next (p, ce, e))
+ ;
+ }
+
+ // OUTPUT
+ //
+ const char* output_begin (0);
+ size_t output_size (0);
+ if (e - p > 7 && traits::compare (p, "OUTPUT ", 7) == 0)
+ {
+ output_begin = p;
+ p += 7;
+ p = find (p, e, '\n');
+ assert (p != 0);
+ output_size = p - output_begin;
+ p++;
+ }
+
+ // VALUES or DEFAULT VALUES
+ //
+ bool empty (true); // DEFAULT VALUES case (if none present).
+ const char* values_begin (0);
+ if (e - p > 7 && traits::compare (p, "VALUES\n", 7) == 0)
+ {
+ p += 7;
+ values_begin = p;
+
+ size_t bi (0);
+ for (const char* ve (paren_begin (p, e)); ve != 0; paren_next (p, ve, e))
+ {
+ // We cannot be empty if we have a non-parameterized value, e.g.,
+ // INSERT ... VALUES(1,?). We also cannot be empty if this value
+ // is present in the bind array.
+ //
+ if ((find (p, ve, param_symbol) == 0 &&
+ (param_symbol2 == '\0' || find (p, ve, param_symbol2) == 0)) ||
+ bind_at (bi++, bind, bind_skip) != 0)
+ empty = false;
+ }
+ }
+ else
+ {
+ // Must be DEFAULT VALUES.
+ //
+ assert (traits::compare (p, "DEFAULT VALUES", 14) == 0);
+ p += 14;
+
+ if (*p == '\n')
+ p++;
+ }
+
+ // Trailer.
+ //
+ const char* trailer_begin (0);
+ size_t trailer_size (0);
+ if (e - p != 0)
+ {
+ trailer_begin = p;
+ trailer_size = e - p;
+ }
+
+ // Empty case.
+ //
+ if (empty)
+ {
+ r.reserve (header_size +
+ (output_size == 0 ? 0 : output_size + 1) +
+ 15 + // For " DEFAULT VALUES"
+ (trailer_size == 0 ? 0 : trailer_size + 1));
+
+ r.assign (s, header_size);
+
+ if (output_size != 0)
+ {
+ r += ' ';
+ r.append (output_begin, output_size);
+ }
+
+ r += " DEFAULT VALUES";
+
+ if (trailer_size != 0)
+ {
+ r += ' ';
+ r.append (trailer_begin, trailer_size);
+ }
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+ if (r.size () != n)
+ cerr << endl
+ << "old: '" << s << "'" << endl << endl
+ << "new: '" << r << "'" << endl << endl;
+#endif
+
+ return;
+ }
+
+ // Assume the same size as the original. It can only shrink, and in
+ // most cases only slightly. So this is a good approximation.
+ //
+ r.reserve (n);
+ r.assign (s, header_size);
+
+ // Column list.
+ //
+ {
+ r += ' ';
+
+ size_t i (0), bi (0);
+
+ for (const char *c (columns_begin), *ce (paren_begin (c, e)),
+ *v (values_begin), *ve (paren_begin (v, e));
+ ce != 0; paren_next (c, ce, e), paren_next (v, ve, e))
+ {
+ // See if the value contains the parameter symbol and, if so,
+ // whether it is present in the bind array.
+ //
+ if ((find (v, ve, param_symbol) != 0 ||
+ (param_symbol2 != '\0' && find (v, ve, param_symbol2) != 0)) &&
+ bind_at (bi++, bind, bind_skip) == 0)
+ continue;
+
+ // Append the column.
+ //
+ if (i++ == 0)
+ r += '(';
+ else
+ r += ", "; // Add the space for consistency with the fast path.
+
+ r.append (c, ce - c);
+ }
+
+ r += ')';
+ }
+
+ // OUTPUT
+ //
+ if (output_size != 0)
+ {
+ r += ' ';
+ r.append (output_begin, output_size);
+ }
+
+ // Value list.
+ //
+ {
+ r += " VALUES ";
+
+ size_t i (0), bi (0);
+
+ for (const char* v (values_begin), *ve (paren_begin (v, e));
+ ve != 0; paren_next (v, ve, e))
+ {
+ // See if the value contains the parameter symbol and, if so,
+ // whether it is present in the bind array.
+ //
+ if ((find (v, ve, param_symbol) != 0 ||
+ (param_symbol2 != '\0' && find (v, ve, param_symbol2) != 0)) &&
+ bind_at (bi++, bind, bind_skip) == 0)
+ continue;
+
+ // Append the value.
+ //
+ if (i++ == 0)
+ r += '(';
+ else
+ r += ", "; // Add the space for consistency with the fast path.
+
+ r.append (v, ve - v);
+ }
+
+ r += ')';
+ }
+
+ // Trailer.
+ //
+ if (trailer_size != 0)
+ {
+ r += ' ';
+ r.append (trailer_begin, trailer_size);
+ }
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+ if (r.size () != n)
+ cerr << endl
+ << "old: '" << s << "'" << endl << endl
+ << "new: '" << r << "'" << endl << endl;
+#endif
+ }
+
+ void statement::
+ process_update (string& r,
+ const char* s,
+ bind_type bind,
+ size_t bind_size,
+ size_t bind_skip,
+ char param_symbol,
+ char param_symbol2)
+ {
+ bool fast (true); // Fast case (if all present).
+ for (size_t i (0); i != bind_size && fast; ++i)
+ {
+ if (bind_at (i, bind, bind_skip) == 0)
+ fast = false;
+ }
+
+ // Fast path: just remove the "structure".
+ //
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ if (fast)
+ {
+ process_fast (s, r);
+ return;
+ }
+#endif
+
+ // Scan the statement and store the positions of various parts.
+ //
+ size_t n (traits::length (s));
+ const char* e (s + n);
+
+ // Header.
+ //
+ const char* p (find (s, e, '\n'));
+ assert (p != 0);
+ size_t header_size (p - s);
+ p++;
+
+ // SET
+ //
+ bool empty (true); // Empty SET case.
+ const char* set_begin (0);
+ if (e - p > 4 && traits::compare (p, "SET\n", 4) == 0)
+ {
+ p += 4;
+ set_begin = p;
+
+ // Scan the SET list.
+ //
+ size_t bi (0);
+ for (const char* pe (comma_begin (p, e)); pe != 0; comma_next (p, pe, e))
+ {
+ if (empty)
+ {
+ // We cannot be empty if we have a non-parameterized set expression,
+ // e.g., UPDATE ... SET ver=ver+1 ... We also cannot be empty if
+ // this expression is present in the bind array.
+ //
+ if ((find (p, pe, param_symbol) == 0 &&
+ (param_symbol2 == '\0' || find (p, pe, param_symbol2) == 0)) ||
+ bind_at (bi++, bind, bind_skip) != 0)
+ empty = false;
+ }
+ }
+ }
+
+ // Empty case.
+ //
+ if (empty)
+ {
+ r.clear ();
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+ if (n != 0)
+ cerr << endl
+ << "old: '" << s << "'" << endl << endl
+ << "new: '" << r << "'" << endl << endl;
+#endif
+
+ return;
+ }
+
+ // Trailer.
+ //
+ const char* trailer_begin (0);
+ size_t trailer_size (0);
+ if (e - p != 0)
+ {
+ trailer_begin = p;
+ trailer_size = e - p;
+ }
+
+ // Assume the same size as the original. It can only shrink, and in
+ // most cases only slightly. So this is a good approximation.
+ //
+ r.reserve (n);
+ r.assign (s, header_size);
+
+ // SET list.
+ //
+ {
+ r += " SET ";
+
+ size_t i (0), bi (0);
+
+ for (const char* p (set_begin), *pe (comma_begin (p, e));
+ pe != 0; comma_next (p, pe, e))
+ {
+ // See if the value contains the parameter symbol and, if so,
+ // whether it is present in the bind array.
+ //
+ if ((find (p, pe, param_symbol) != 0 ||
+ (param_symbol2 != '\0' && find (p, pe, param_symbol2) != 0)) &&
+ bind_at (bi++, bind, bind_skip) == 0)
+ continue;
+
+ // Append the expression.
+ //
+ if (i++ != 0)
+ r += ", "; // Add the space for consistency with the fast path.
+
+ r.append (p, pe - p);
+ }
+ }
+
+ // Trailer.
+ //
+ if (trailer_size != 0)
+ {
+ r += ' ';
+ r.append (trailer_begin, trailer_size);
+ }
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+ if (r.size () != n)
+ cerr << endl
+ << "old: '" << s << "'" << endl << endl
+ << "new: '" << r << "'" << endl << endl;
+#endif
+ }
+
+ void statement::
+ process_select (string& r,
+ const char* s,
+ bind_type bind,
+ size_t bind_size,
+ size_t bind_skip,
+ char quote_open,
+ char quote_close,
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ bool optimize,
+#else
+ bool,
+#endif
+ bool as)
+ {
+ bool empty (true); // Empty case (if none present).
+ bool fast (true); // Fast case (if all present).
+ for (size_t i (0); i != bind_size && (empty || fast); ++i)
+ {
+ if (bind_at (i, bind, bind_skip) != 0)
+ empty = false;
+ else
+ fast = false;
+ }
+
+ // Empty.
+ //
+ if (empty)
+ {
+ r.clear ();
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+ if (*s != '\0')
+ cerr << endl
+ << "old: '" << s << "'" << endl << endl
+ << "new: '" << r << "'" << endl << endl;
+#endif
+ return;
+ }
+
+ // Fast path: just remove the "structure".
+ //
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ if (fast && !optimize)
+ {
+ process_fast (s, r);
+ return;
+ }
+#endif
+
+ // Scan the statement and store the positions of various parts.
+ //
+ size_t n (traits::length (s));
+ const char* e (s + n);
+
+ // Header.
+ //
+ const char* p (find (s, e, '\n'));
+ assert (p != 0);
+ size_t header_size (p - s);
+ p++;
+
+ // Column list.
+ //
+ const char* columns_begin (p);
+ for (const char* ce (comma_begin (p, e)); ce != 0; comma_next (p, ce, e))
+ ;
+
+ // FROM.
+ assert (traits::compare (p, "FROM ", 5) == 0);
+ const char* from_begin (p);
+ p = find (p, e, '\n'); // May not end with '\n'.
+ if (p == 0)
+ p = e;
+ size_t from_size (p - from_begin);
+ if (p != e)
+ p++;
+
+ // JOIN list.
+ //
+ const char* joins_begin (0), *joins_end (0);
+ if (e - p > 5 && fuzzy_prefix (p, e, "JOIN ", 5))
+ {
+ joins_begin = p;
+
+ // Find the end of the JOIN list.
+ //
+ for (const char* je (newline_begin (p, e));
+ je != 0; newline_next (p, je, e, "JOIN ", 5, true))
+ ;
+
+ joins_end = (p != e ? p - 1 : p);
+ }
+
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ if (fast && joins_begin == 0)
+ {
+ // No JOINs to optimize so can still take the fast path.
+ //
+ process_fast (s, r);
+ return;
+ }
+#endif
+
+ // Trailer (WHERE, ORDER BY, etc).
+ //
+ const char* trailer_begin (0);
+ size_t trailer_size (0);
+ if (e - p != 0)
+ {
+ trailer_begin = p;
+ trailer_size = e - p;
+ }
+
+ // Assume the same size as the original. It can only shrink, and in
+ // most cases only slightly. So this is a good approximation.
+ //
+ r.reserve (n);
+ r.assign (s, header_size);
+
+ // Column list.
+ //
+ {
+ r += ' ';
+
+ size_t i (0), bi (0);
+
+ for (const char *c (columns_begin), *ce (comma_begin (c, e));
+ ce != 0; comma_next (c, ce, e))
+ {
+ // See if the column is present in the bind array.
+ //
+ if (bind_at (bi++, bind, bind_skip) == 0)
+ continue;
+
+ // Append the column.
+ //
+ if (i++ != 0)
+ r += ", "; // Add the space for consistency with the fast path.
+
+ r.append (c, ce - c);
+ }
+ }
+
+ // From.
+ //
+ r += ' ';
+ r.append (from_begin, from_size);
+
+ // JOIN list, pass 1.
+ //
+ size_t join_pos (0);
+ if (joins_begin != 0)
+ {
+ // Fill in the JOIN "area" with spaces.
+ //
+ r.resize (r.size () + joins_end - joins_begin + 1, ' ');
+ join_pos = r.size () + 1; // End of the last JOIN.
+ }
+
+ // Trailer.
+ //
+ if (trailer_size != 0)
+ {
+ r += ' ';
+ r.append (trailer_begin, trailer_size);
+ }
+
+ // JOIN list, pass 2.
+ //
+ if (joins_begin != 0)
+ {
+ // Splice the JOINs into the pre-allocated area.
+ //
+ for (const char* je (joins_end), *j (newline_rbegin (je, joins_begin));
+ j != 0; newline_rnext (j, je, joins_begin))
+ {
+ size_t n (je - j);
+
+ // Get the alias or, if none used, the table name.
+ //
+ p = find (j, je, "JOIN ", 5) + 5; // Skip past "JOIN ".
+ const char* table_begin (p);
+ p = find (p, je, ' '); // End of the table name.
+ const char* table_end (p);
+ p++; // Skip space.
+
+ // We may or may not have the AS keyword.
+ //
+ const char* alias_begin (0);
+ size_t alias_size (0);
+
+ if (p != je && // Not the end.
+ (je - p < 4 || traits::compare (p, "ON ", 3) != 0))
+ {
+ // Something other than "ON ", so got to be an alias.
+ //
+ if (as)
+ p += 3;
+
+ alias_begin = p;
+ p = find (p, je, ' '); // There might be no ON (CROSS JOIN).
+ alias_size = (p != 0 ? p : je) - alias_begin;
+ }
+ else
+ {
+ // Just the table.
+ //
+ alias_begin = table_begin;
+ alias_size = table_end - alias_begin;
+ }
+
+ // The alias must be quoted.
+ //
+ assert (*alias_begin == quote_open &&
+ *(alias_begin + alias_size - 1) == quote_close);
+
+ // We now need to see if the alias is used in either the SELECT
+ // list, the WHERE conditions, or the ON condition of any of the
+ // JOINs that we have already processed and decided to keep.
+ //
+ // Instead of re-parsing the whole thing again, we are going to
+ // take a shortcut and simply search for the alias in the statement
+ // we have constructed so far (that's why we have added the
+ // trailer before filling in the JOINs). To make it more robust,
+ // we are going to do a few extra sanity checks, specifically,
+ // that the alias is a top level identifier and is followed by
+ // only a single identifer (column). This will catch cases like
+ // [s].[t].[c] where [s] is also used as an alias or LEFT JOIN [t]
+ // where [t] is also used as an alias in another JOIN.
+ //
+ bool found (false);
+ for (size_t p (r.find (alias_begin, 0, alias_size));
+ p != string::npos;
+ p = r.find (alias_begin, p + alias_size, alias_size))
+ {
+ size_t e (p + alias_size);
+
+ // If we are not a top-level qualifier or not a bottom-level,
+ // then we are done (3 is for at least "[a]").
+ //
+ if ((p != 0 && r[p - 1] == '.') ||
+ (e + 3 >= r.size () || (r[e] != '.' || r[e + 1] != quote_open)))
+ continue;
+
+ // The only way to distinguish the [a].[c] from FROM [a].[c] or
+ // JOIN [a].[c] is by checking the prefix.
+ //
+ if ((p > 5 && r.compare (p - 5, 5, "FROM ") == 0) ||
+ (p > 5 && r.compare (p - 5, 5, "JOIN ") == 0))
+ continue;
+
+ // Check that we are followed by a single identifier.
+ //
+ e = r.find (quote_close, e + 2);
+ if (e == string::npos || (e + 1 != r.size () && r[e + 1] == '.'))
+ continue;
+
+ found = true;
+ break;
+ }
+
+ join_pos -= n + 1; // Extra one for space.
+ if (found)
+ r.replace (join_pos, n, j, n);
+ else
+ r.erase (join_pos - 1, n + 1); // Extra one for space.
+ }
+ }
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+ if (r.size () != n)
+ cerr << endl
+ << "old: '" << s << "'" << endl << endl
+ << "new: '" << r << "'" << endl << endl;
+#endif
+ }
+}
diff --git a/libodb/odb/statement.cxx b/libodb/odb/statement.cxx
new file mode 100644
index 0000000..35e7b77
--- /dev/null
+++ b/libodb/odb/statement.cxx
@@ -0,0 +1,12 @@
+// file : odb/statement.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/statement.hxx>
+
+namespace odb
+{
+ statement::
+ ~statement ()
+ {
+ }
+}
diff --git a/libodb/odb/statement.hxx b/libodb/odb/statement.hxx
new file mode 100644
index 0000000..83513c0
--- /dev/null
+++ b/libodb/odb/statement.hxx
@@ -0,0 +1,108 @@
+// file : odb/statement.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STATEMENT_HXX
+#define ODB_STATEMENT_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx> // connection
+
+#include <odb/details/export.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+namespace odb
+{
+ class LIBODB_EXPORT statement: public details::shared_base
+ {
+ private:
+ statement (const statement&);
+ statement& operator= (const statement&);
+
+ public:
+ typedef odb::connection connection_type;
+
+ virtual const char*
+ text () const = 0;
+
+ virtual connection_type&
+ connection () = 0;
+
+ virtual
+ ~statement () = 0;
+
+ protected:
+ statement () {}
+
+ // Statement processing. Kept public only for testing.
+ //
+ public:
+ // Expected statement structure:
+ //
+ // INSERT INTO table\n
+ // [(a,\n
+ // b)\n]
+ // [OUTPUT ...\n]
+ // [VALUES\n
+ // ($1,\n
+ // $2)[\n]]
+ // [DEFAULT VALUES[\n]]
+ // [RETURNING ...]
+ // [; SELECT ...]
+ //
+ static void
+ process_insert (std::string& result,
+ const char* statement,
+ const void* const* bind, // Array of bind buffer pointers.
+ std::size_t bind_size, // Number of bind elements.
+ std::size_t bind_skip, // Offset to the next bind.
+ char param_symbol, // $, ?, :, etc.
+ char param_symbol2 = '\0');
+
+ // Expected statement structure:
+ //
+ // UPDATE table\n
+ // SET\n
+ // a=$1,\n
+ // b=$2[\n]
+ // [OUTPUT ...]
+ // [WHERE ...]
+ //
+ static void
+ process_update (std::string& result,
+ const char* statement,
+ const void* const* bind, // Array of bind buffer pointers.
+ std::size_t bind_size, // Number of bind elements.
+ std::size_t bind_skip, // Offset to the next bind.
+ char param_symbol, // $, ?, :, etc.
+ char param_symbol2 = '\0');
+
+
+ // Expected statement structure:
+ //
+ // SELECT\n
+ // [schema.]table.a [AS b],\n
+ // alias.b\n
+ // FROM [schema.]table[\n]
+ // [{A-Z }* JOIN [schema.]table [AS alias][ ON ...][\n]]*
+ // [WHERE ...]
+ //
+ static void
+ process_select (std::string& result,
+ const char* statement,
+ const void* const* bind, // Array of bind buffer pointers.
+ std::size_t bind_size, // Number of bind elements.
+ std::size_t bind_skip, // Offset to the next bind.
+ char quote_open, // Identifier opening quote.
+ char quote_close, // Identifier closing quote.
+ bool optimize, // Remove unused JOINs.
+ bool as = true); // JOINs use AS keyword.
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STATEMENT_HXX
diff --git a/libodb/odb/std-array-traits.hxx b/libodb/odb/std-array-traits.hxx
new file mode 100644
index 0000000..1d0f1a1
--- /dev/null
+++ b/libodb/odb/std-array-traits.hxx
@@ -0,0 +1,72 @@
+// file : odb/std-array-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STD_ARRAY_TRAITS_HXX
+#define ODB_STD_ARRAY_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <array>
+#include <cstddef> // std::size_t
+#include <cassert>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename V, std::size_t N>
+ class access::container_traits<std::array<V, N>>
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = false;
+
+ typedef std::array<V, N> container_type;
+
+ typedef V value_type;
+ typedef typename container_type::size_type index_type;
+
+ typedef ordered_functions<index_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (index_type i (0); i < N; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ index_type i (0);
+
+ for (; more && i < N; ++i)
+ {
+ index_type dummy;
+ more = f.select (dummy, c[i]);
+ }
+
+ assert (!more && i == N);
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (index_type i (0); i < N; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STD_ARRAY_TRAITS_HXX
diff --git a/libodb/odb/std-deque-traits.hxx b/libodb/odb/std-deque-traits.hxx
new file mode 100644
index 0000000..351c6db
--- /dev/null
+++ b/libodb/odb/std-deque-traits.hxx
@@ -0,0 +1,69 @@
+// file : odb/std-deque-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STD_DEQUE_TRAITS_HXX
+#define ODB_STD_DEQUE_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <deque>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename V, typename A>
+ class access::container_traits<std::deque<V, A> >
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = false;
+
+ typedef std::deque<V, A> container_type;
+
+ typedef V value_type;
+ typedef typename container_type::size_type index_type;
+
+ typedef ordered_functions<index_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ index_type dummy;
+ c.push_back (value_type ());
+ more = f.select (dummy, c.back ());
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STD_DEQUE_TRAITS_HXX
diff --git a/libodb/odb/std-forward-list-traits.hxx b/libodb/odb/std-forward-list-traits.hxx
new file mode 100644
index 0000000..7978b1c
--- /dev/null
+++ b/libodb/odb/std-forward-list-traits.hxx
@@ -0,0 +1,73 @@
+// file : odb/std-forward-list-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STD_FORWARD_LIST_TRAITS_HXX
+#define ODB_STD_FORWARD_LIST_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <forward_list>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename V, typename A>
+ class access::container_traits<std::forward_list<V, A>>
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = false;
+
+ typedef std::forward_list<V, A> container_type;
+
+ typedef V value_type;
+ typedef typename container_type::size_type index_type;
+
+ typedef ordered_functions<index_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ index_type i (0);
+ for (typename container_type::const_iterator j (c.begin ()),
+ e (c.end ()); j != e; ++j)
+ f.insert (i++, *j);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ for (typename container_type::iterator i (c.before_begin ()); more; )
+ {
+ index_type dummy;
+ i = c.insert_after (i, value_type ());
+ more = f.select (dummy, *i);
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ index_type i (0);
+ for (typename container_type::const_iterator j (c.begin ()),
+ e (c.end ()); j != e; ++j)
+ f.insert (i++, *j);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STD_FORWARD_LIST_TRAITS_HXX
diff --git a/libodb/odb/std-list-traits.hxx b/libodb/odb/std-list-traits.hxx
new file mode 100644
index 0000000..f349079
--- /dev/null
+++ b/libodb/odb/std-list-traits.hxx
@@ -0,0 +1,73 @@
+// file : odb/std-list-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STD_LIST_TRAITS_HXX
+#define ODB_STD_LIST_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <list>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename V, typename A>
+ class access::container_traits<std::list<V, A> >
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = false;
+
+ typedef std::list<V, A> container_type;
+
+ typedef V value_type;
+ typedef typename container_type::size_type index_type;
+
+ typedef ordered_functions<index_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ index_type i (0);
+ for (typename container_type::const_iterator j (c.begin ()),
+ e (c.end ()); j != e; ++j)
+ f.insert (i++, *j);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ index_type dummy;
+ c.push_back (value_type ());
+ more = f.select (dummy, c.back ());
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ index_type i (0);
+ for (typename container_type::const_iterator j (c.begin ()),
+ e (c.end ()); j != e; ++j)
+ f.insert (i++, *j);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STD_LIST_TRAITS_HXX
diff --git a/libodb/odb/std-map-traits.hxx b/libodb/odb/std-map-traits.hxx
new file mode 100644
index 0000000..2b9bf7d
--- /dev/null
+++ b/libodb/odb/std-map-traits.hxx
@@ -0,0 +1,142 @@
+// file : odb/std-map-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STD_MAP_TRAITS_HXX
+#define ODB_STD_MAP_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <map>
+#include <utility> // std::move
+
+#include <odb/container-traits.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ template <typename K, typename V, typename C, typename A>
+ class access::container_traits<std::map<K, V, C, A> >
+ {
+ public:
+ static const container_kind kind = ck_map;
+ static const bool smart = false;
+
+ typedef std::map<K, V, C, A> container_type;
+
+ typedef K key_type;
+ typedef V value_type;
+ typedef typename container_type::value_type pair_type;
+
+ typedef map_functions<key_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ key_type k;
+ value_type v;
+ more = f.select (k, v);
+#ifdef ODB_CXX11
+ c.insert (pair_type (std::move (k), std::move (v)));
+#else
+ c.insert (pair_type (k, v));
+#endif
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+
+ // C++03 does not guarantee insertion order of equal values but C++11
+ // changes that. The current implementation in the generated code does
+ // not guarantee this either.
+ //
+ template <typename K, typename V, typename C, typename A>
+ class access::container_traits<std::multimap<K, V, C, A> >
+ {
+ public:
+ static const container_kind kind = ck_multimap;
+ static const bool smart = false;
+
+ typedef std::multimap<K, V, C, A> container_type;
+
+ typedef K key_type;
+ typedef V value_type;
+ typedef typename container_type::value_type pair_type;
+
+ typedef map_functions<key_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ key_type k;
+ value_type v;
+ more = f.select (k, v);
+#ifdef ODB_CXX11
+ c.insert (pair_type (std::move (k), std::move (v)));
+#else
+ c.insert (pair_type (k, v));
+#endif
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STD_MAP_TRAITS_HXX
diff --git a/libodb/odb/std-set-traits.hxx b/libodb/odb/std-set-traits.hxx
new file mode 100644
index 0000000..45a5dcc
--- /dev/null
+++ b/libodb/odb/std-set-traits.hxx
@@ -0,0 +1,134 @@
+// file : odb/std-set-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STD_SET_TRAITS_HXX
+#define ODB_STD_SET_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <set>
+#include <utility> // std::move
+
+#include <odb/container-traits.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ template <typename V, typename C, typename A>
+ class access::container_traits<std::set<V, C, A> >
+ {
+ public:
+ static const container_kind kind = ck_set;
+ static const bool smart = false;
+
+ typedef std::set<V, C, A> container_type;
+ typedef V value_type;
+
+ typedef set_functions<value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ value_type v;
+ more = f.select (v);
+#ifdef ODB_CXX11
+ c.insert (std::move (v));
+#else
+ c.insert (v);
+#endif
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+
+ // C++03 does not guarantee insertion order of equal values but C++11
+ // changes that. The current implementation in the generated code does
+ // not guarantee this either.
+ //
+ template <typename V, typename C, typename A>
+ class access::container_traits<std::multiset<V, C, A> >
+ {
+ public:
+ static const container_kind kind = ck_multiset;
+ static const bool smart = false;
+
+ typedef std::multiset<V, C, A> container_type;
+ typedef V value_type;
+
+ typedef set_functions<value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ value_type v;
+ more = f.select (v);
+#ifdef ODB_CXX11
+ c.insert (std::move (v));
+#else
+ c.insert (v);
+#endif
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STD_SET_TRAITS_HXX
diff --git a/libodb/odb/std-unordered-map-traits.hxx b/libodb/odb/std-unordered-map-traits.hxx
new file mode 100644
index 0000000..461eb06
--- /dev/null
+++ b/libodb/odb/std-unordered-map-traits.hxx
@@ -0,0 +1,133 @@
+// file : odb/std-unordered-map-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STD_UNORDERED_MAP_TRAITS_HXX
+#define ODB_STD_UNORDERED_MAP_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <utility> // std::move
+#include <unordered_map>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename K, typename V, typename H, typename P, typename A>
+ class access::container_traits<std::unordered_map<K, V, H, P, A>>
+ {
+ public:
+ static const container_kind kind = ck_map;
+ static const bool smart = false;
+
+ typedef std::unordered_map<K, V, H, P, A> container_type;
+
+ typedef K key_type;
+ typedef V value_type;
+ typedef typename container_type::value_type pair_type;
+
+ typedef map_functions<key_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ key_type k;
+ value_type v;
+ more = f.select (k, v);
+ c.insert (pair_type (std::move (k), std::move (v)));
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+
+ // @@ Does multimap preserve insertion order of equal elements? The
+ // current implementation in the generated code does not guarantee
+ // this.
+ //
+ template <typename K, typename V, typename H, typename P, typename A>
+ class access::container_traits<std::unordered_multimap<K, V, H, P, A>>
+ {
+ public:
+ static const container_kind kind = ck_multimap;
+ static const bool smart = false;
+
+ typedef std::unordered_multimap<K, V, H, P, A> container_type;
+
+ typedef K key_type;
+ typedef V value_type;
+ typedef typename container_type::value_type pair_type;
+
+ typedef map_functions<key_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ key_type k;
+ value_type v;
+ more = f.select (k, v);
+ c.insert (pair_type (std::move (k), std::move (v)));
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STD_UNORDERED_MAP_TRAITS_HXX
diff --git a/libodb/odb/std-unordered-set-traits.hxx b/libodb/odb/std-unordered-set-traits.hxx
new file mode 100644
index 0000000..f590665
--- /dev/null
+++ b/libodb/odb/std-unordered-set-traits.hxx
@@ -0,0 +1,125 @@
+// file : odb/std-unordered-set-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STD_UNORDERED_SET_TRAITS_HXX
+#define ODB_STD_UNORDERED_SET_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <utility> // std::move
+#include <unordered_set>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename V, typename H, typename P, typename A>
+ class access::container_traits<std::unordered_set<V, H, P, A>>
+ {
+ public:
+ static const container_kind kind = ck_set;
+ static const bool smart = false;
+
+ typedef std::unordered_set<V, H, P, A> container_type;
+ typedef V value_type;
+
+ typedef set_functions<value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ value_type v;
+ more = f.select (v);
+ c.insert (std::move (v));
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+
+ // @@ Does multiset preserve insertion order of equal elements? The
+ // current implementation in the generated code does not guarantee
+ // this.
+ //
+ template <typename V, typename H, typename P, typename A>
+ class access::container_traits<std::unordered_multiset<V, H, P, A>>
+ {
+ public:
+ static const container_kind kind = ck_multiset;
+ static const bool smart = false;
+
+ typedef std::unordered_multiset<V, H, P, A> container_type;
+ typedef V value_type;
+
+ typedef set_functions<value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ value_type v;
+ more = f.select (v);
+ c.insert (std::move (v));
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STD_UNORDERED_SET_TRAITS_HXX
diff --git a/libodb/odb/std-vector-traits.hxx b/libodb/odb/std-vector-traits.hxx
new file mode 100644
index 0000000..c6bb39e
--- /dev/null
+++ b/libodb/odb/std-vector-traits.hxx
@@ -0,0 +1,123 @@
+// file : odb/std-vector-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STD_VECTOR_TRAITS_HXX
+#define ODB_STD_VECTOR_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename V, typename A>
+ class access::container_traits<std::vector<V, A> >
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = false;
+
+ typedef std::vector<V, A> container_type;
+
+ typedef V value_type;
+ typedef typename container_type::size_type index_type;
+
+ typedef ordered_functions<index_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ index_type dummy;
+ c.push_back (value_type ());
+ more = f.select (dummy, c.back ());
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+
+ // std::vector<bool> is special.
+ //
+ template <typename A>
+ class access::container_traits<std::vector<bool, A> >
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = false;
+
+ typedef std::vector<bool, A> container_type;
+
+ typedef bool value_type;
+ typedef typename container_type::size_type index_type;
+
+ typedef ordered_functions<index_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ index_type dummy;
+ value_type value;
+ more = f.select (dummy, value);
+ c.push_back (value);
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STD_VECTOR_TRAITS_HXX
diff --git a/libodb/odb/tr1/lazy-pointer-traits.hxx b/libodb/odb/tr1/lazy-pointer-traits.hxx
new file mode 100644
index 0000000..7adf957
--- /dev/null
+++ b/libodb/odb/tr1/lazy-pointer-traits.hxx
@@ -0,0 +1,61 @@
+// file : odb/tr1/lazy-pointer-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_TR1_LAZY_POINTER_TRAITS_HXX
+#define ODB_TR1_LAZY_POINTER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/pointer-traits.hxx>
+#include <odb/tr1/lazy-ptr.hxx>
+
+namespace odb
+{
+ template <typename T>
+ class pointer_traits<tr1::lazy_shared_ptr<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_shared;
+ static const bool lazy = true;
+
+ typedef T element_type;
+ typedef tr1::lazy_shared_ptr<element_type> pointer_type;
+ typedef std::tr1::shared_ptr<element_type> eager_pointer_type;
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ template <class O /* = T */>
+ static typename object_traits<O>::id_type
+ object_id (const pointer_type& p)
+ {
+ return p.template object_id<O> ();
+ }
+ };
+
+ template <typename T>
+ class pointer_traits<tr1::lazy_weak_ptr<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_weak;
+ static const bool lazy = true;
+
+ typedef T element_type;
+ typedef tr1::lazy_weak_ptr<element_type> pointer_type;
+ typedef tr1::lazy_shared_ptr<element_type> strong_pointer_type;
+ typedef std::tr1::weak_ptr<element_type> eager_pointer_type;
+
+ static strong_pointer_type
+ lock (const pointer_type& p)
+ {
+ return p.lock ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_TR1_LAZY_POINTER_TRAITS_HXX
diff --git a/libodb/odb/tr1/lazy-ptr.hxx b/libodb/odb/tr1/lazy-ptr.hxx
new file mode 100644
index 0000000..b4946ec
--- /dev/null
+++ b/libodb/odb/tr1/lazy-ptr.hxx
@@ -0,0 +1,267 @@
+// file : odb/tr1/lazy-ptr.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_TR1_LAZY_PTR_HXX
+#define ODB_TR1_LAZY_PTR_HXX
+
+#include <odb/pre.hxx>
+
+//
+// This header assumes that the necessary TR1 header has already
+// been included.
+//
+
+#include <memory> // std::auto_ptr
+
+#include <odb/forward.hxx> // odb::database
+#include <odb/traits.hxx>
+#include <odb/lazy-ptr-impl.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ namespace tr1
+ {
+ template <class T>
+ class lazy_weak_ptr;
+
+ //
+ //
+ template <class T>
+ class lazy_shared_ptr
+ {
+ // The standard shared_ptr interface.
+ //
+ public:
+ typedef T element_type;
+
+ lazy_shared_ptr ();
+ template <class Y> explicit lazy_shared_ptr (Y*);
+ template <class Y, class D> lazy_shared_ptr (Y*, D);
+
+ lazy_shared_ptr (const lazy_shared_ptr&);
+ template <class Y> lazy_shared_ptr (const lazy_shared_ptr<Y>&);
+ template <class Y> explicit lazy_shared_ptr (const lazy_weak_ptr<Y>&);
+ template <class Y> explicit lazy_shared_ptr (std::auto_ptr<Y>&);
+
+ ~lazy_shared_ptr ();
+
+ lazy_shared_ptr& operator= (const lazy_shared_ptr&);
+ template <class Y> lazy_shared_ptr& operator= (const lazy_shared_ptr<Y>&);
+ template <class Y> lazy_shared_ptr& operator= (std::auto_ptr<Y>&);
+
+ void swap (lazy_shared_ptr&);
+ void reset ();
+ template <class Y> void reset (Y*);
+ template <class Y, class D> void reset (Y*, D);
+
+ T& operator* () const;
+ T* operator-> () const;
+ T* get () const;
+
+ bool unique () const;
+ long use_count () const;
+
+ typedef std::tr1::shared_ptr<T> lazy_shared_ptr::*unspecified_bool_type;
+ operator unspecified_bool_type () const
+ {
+ return (p_ || i_) ? &lazy_shared_ptr::p_ : 0;
+ }
+
+ // Initialization/assignment from shared_ptr and weak_ptr.
+ //
+ public:
+ template <class Y> lazy_shared_ptr (const std::tr1::shared_ptr<Y>&);
+ template <class Y> explicit lazy_shared_ptr (const std::tr1::weak_ptr<Y>&);
+
+ template <class Y> lazy_shared_ptr& operator= (const std::tr1::shared_ptr<Y>&);
+
+ // Lazy loading interface.
+ //
+ public:
+ typedef odb::database database_type;
+
+ // 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;
+
+ std::tr1::shared_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.
+ //
+ std::tr1::shared_ptr<T> get_eager () const;
+
+ template <class DB, class ID> lazy_shared_ptr (DB&, const ID&);
+ template <class DB, class Y> lazy_shared_ptr (DB&, Y*);
+ template <class DB, class Y, class D> lazy_shared_ptr (DB&, Y*, D);
+ template <class DB, class Y> lazy_shared_ptr (DB&, std::auto_ptr<Y>&);
+ template <class DB, class Y> lazy_shared_ptr (DB&, const std::tr1::shared_ptr<Y>&);
+ template <class DB, class Y> lazy_shared_ptr (DB&, const std::tr1::weak_ptr<Y>&);
+
+ template <class DB, class ID> void reset (DB&, const ID&);
+ template <class DB, class Y> void reset (DB&, Y*);
+ template <class DB, class Y, class D> void reset (DB&, Y*, D);
+ template <class DB, class Y> void reset (DB&, std::auto_ptr<Y>&);
+ template <class DB, class Y> void reset (DB&, const std::tr1::shared_ptr<Y>&);
+
+#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+ template <class O = T>
+#else
+ template <class O /*= T*/>
+#endif
+ typename object_traits<O>::id_type object_id () const;
+
+ database_type& database () const;
+
+ // Helpers.
+ //
+ public:
+ template <class Y> bool equal (const lazy_shared_ptr<Y>&) const;
+
+ private:
+ template <class Y> friend class lazy_shared_ptr;
+ template <class Y> friend class lazy_weak_ptr;
+
+ // For lazy_weak_ptr::lock().
+ //
+ lazy_shared_ptr (const std::tr1::shared_ptr<T>& p,
+ const lazy_ptr_impl<T>& i)
+ : p_ (p), i_ (i) {}
+
+ private:
+ mutable std::tr1::shared_ptr<T> p_;
+ mutable lazy_ptr_impl<T> i_;
+ };
+
+ // operator< and operator<< are not provided.
+ //
+ template<class T, class Y>
+ bool operator== (const lazy_shared_ptr<T>&, const lazy_shared_ptr<Y>&);
+
+ template<class T, class Y>
+ bool operator!= (const lazy_shared_ptr<T>&, const lazy_shared_ptr<Y>&);
+
+ template<class T> void swap (lazy_shared_ptr<T>&, lazy_shared_ptr<T>&);
+
+ template<class D, class T>
+ D* get_deleter (const lazy_shared_ptr<T>&);
+
+ //
+ //
+ template <class T>
+ class lazy_weak_ptr
+ {
+ // The standard weak_ptr interface.
+ //
+ public:
+ typedef T element_type;
+
+ lazy_weak_ptr ();
+ template <class Y> lazy_weak_ptr (const lazy_shared_ptr<Y>&);
+ lazy_weak_ptr (const lazy_weak_ptr&);
+ template <class Y> lazy_weak_ptr (const lazy_weak_ptr<Y>&);
+
+ ~lazy_weak_ptr ();
+
+ lazy_weak_ptr& operator= (const lazy_weak_ptr&);
+ template <class Y> lazy_weak_ptr& operator= (const lazy_weak_ptr<Y>&);
+ template <class Y> lazy_weak_ptr& operator= (const lazy_shared_ptr<Y>&);
+
+ void swap (lazy_weak_ptr<T>&);
+ void reset ();
+
+ long use_count () const;
+ bool expired () const;
+
+ lazy_shared_ptr<T> lock () const;
+
+ // Initialization/assignment from shared_ptr and weak_ptr.
+ //
+ public:
+ template <class Y> lazy_weak_ptr (const std::tr1::weak_ptr<Y>&);
+ template <class Y> lazy_weak_ptr (const std::tr1::shared_ptr<Y>&);
+
+ template <class Y> lazy_weak_ptr& operator= (const std::tr1::weak_ptr<Y>&);
+ template <class Y> lazy_weak_ptr& operator= (const std::tr1::shared_ptr<Y>&);
+
+ // Lazy loading interface.
+ //
+ public:
+ typedef odb::database database_type;
+
+ // expired() loaded()
+ //
+ // true true expired pointer to transient object
+ // false true valid pointer to persistent object
+ // true false expired pointer to persistent object
+ // false false valid pointer to transient object
+ //
+ bool loaded () const;
+
+ // Performs both lock and load.
+ //
+ std::tr1::shared_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.
+ //
+ std::tr1::weak_ptr<T> get_eager () const;
+
+ template <class DB, class ID> lazy_weak_ptr (DB&, const ID&);
+ template <class DB, class Y> lazy_weak_ptr (DB&, const std::tr1::shared_ptr<Y>&);
+ template <class DB, class Y> lazy_weak_ptr (DB&, const std::tr1::weak_ptr<Y>&);
+
+ template <class DB, class ID> void reset (DB&, const ID&);
+ template <class DB, class Y> void reset (DB&, const std::tr1::shared_ptr<Y>&);
+ template <class DB, class Y> void reset (DB&, const std::tr1::weak_ptr<Y>&);
+
+ // The object_id() function can only be called when the object is
+ // persistent, or: expired() XOR loaded() (can use != for XOR).
+ //
+#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+ template <class O = T>
+#else
+ template <class O /* = T */>
+#endif
+ typename object_traits<O>::id_type object_id () const;
+
+ database_type& database () const;
+
+ private:
+ template <class Y> friend class lazy_shared_ptr;
+ template <class Y> friend class lazy_weak_ptr;
+
+ mutable std::tr1::weak_ptr<T> p_;
+ mutable lazy_ptr_impl<T> i_;
+ };
+
+ // operator< is not provided.
+ //
+ template<class T> void swap (lazy_weak_ptr<T>&, lazy_weak_ptr<T>&);
+ }
+}
+
+#include <odb/tr1/lazy-ptr.ixx>
+#include <odb/tr1/lazy-ptr.txx>
+
+#include <odb/tr1/lazy-pointer-traits.hxx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_TR1_LAZY_PTR_HXX
diff --git a/libodb/odb/tr1/lazy-ptr.ixx b/libodb/odb/tr1/lazy-ptr.ixx
new file mode 100644
index 0000000..dc5000f
--- /dev/null
+++ b/libodb/odb/tr1/lazy-ptr.ixx
@@ -0,0 +1,638 @@
+// file : odb/tr1/lazy-ptr.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace tr1
+ {
+ //
+ // lazy_shared_ptr
+ //
+
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr () {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (Y* p): p_ (p) {}
+
+ template <class T>
+ template <class Y, class D>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (Y* p, D d): p_ (p, d) {}
+
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const lazy_shared_ptr& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const lazy_shared_ptr<Y>& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const lazy_weak_ptr<Y>& r): i_ (r.i_)
+ {
+ // If the pointer has expired but can be re-loaded, then don't throw.
+ //
+ p_ = r.lock ();
+
+ if (!p_ && !i_)
+ throw std::tr1::bad_weak_ptr ();
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (std::auto_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ ~lazy_shared_ptr () {}
+
+ template <class T>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (const lazy_shared_ptr& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (const lazy_shared_ptr<Y>& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (std::auto_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ inline void lazy_shared_ptr<T>::
+ swap (lazy_shared_ptr& b)
+ {
+ p_.swap (b.p_);
+ i_.swap (b.i_);
+ }
+
+ template <class T>
+ inline void lazy_shared_ptr<T>::
+ reset ()
+ {
+ p_.reset ();
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (Y* p)
+ {
+ p_.reset (p);
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class Y, class D>
+ inline void lazy_shared_ptr<T>::
+ reset (Y* p, D d)
+ {
+ p_.reset (p, d);
+ i_.reset ();
+ }
+
+ template <class T>
+ inline T& lazy_shared_ptr<T>::
+ operator* () const
+ {
+ return *p_;
+ }
+
+ template <class T>
+ inline T* lazy_shared_ptr<T>::
+ operator-> () const
+ {
+ return p_.operator-> ();
+ }
+
+ template <class T>
+ inline T* lazy_shared_ptr<T>::
+ get () const
+ {
+ return p_.get ();
+ }
+
+ template <class T>
+ inline bool lazy_shared_ptr<T>::
+ unique () const
+ {
+ return p_.unique ();
+ }
+
+ template <class T>
+ inline long lazy_shared_ptr<T>::
+ use_count () const
+ {
+ return p_.use_count ();
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const std::tr1::shared_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const std::tr1::weak_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (const std::tr1::shared_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ inline bool lazy_shared_ptr<T>::
+ loaded () const
+ {
+ bool i (i_);
+ return !p_ != i; // !p_ XOR i_
+ }
+
+ template <class T>
+ inline std::tr1::shared_ptr<T> lazy_shared_ptr<T>::
+ load () const
+ {
+ if (!p_ && i_)
+ p_ = i_.template load<T> (true); // Reset id.
+
+ return p_;
+ }
+
+ template <class T>
+ inline void lazy_shared_ptr<T>::
+ unload () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ if (p_)
+ {
+ if (i_.database () != 0)
+ i_.reset_id (object_traits<object_type>::id (*p_));
+
+ p_.reset ();
+ }
+ }
+
+ template <class T>
+ inline std::tr1::shared_ptr<T> lazy_shared_ptr<T>::
+ get_eager () const
+ {
+ return p_;
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, const ID& id): i_ (db, id) {}
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, Y* p)
+ : p_ (p)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y, class D>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, Y* p, D d)
+ : p_ (p, d)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, std::auto_ptr<Y>& r)
+ : p_ (r)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, const std::tr1::shared_ptr<Y>& r)
+ : p_ (r)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, const std::tr1::weak_ptr<Y>& r)
+ : p_ (r)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, const ID& id)
+ {
+ p_.reset ();
+ i_.reset (db, id);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, Y* p)
+ {
+ p_.reset (p);
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y, class D>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, Y* p, D d)
+ {
+ p_.reset (p, d);
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, std::auto_ptr<Y>& r)
+ {
+ p_ = r;
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, const std::tr1::shared_ptr<Y>& r)
+ {
+ p_ = r;
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class O>
+ inline typename object_traits<O>::id_type lazy_shared_ptr<T>::
+ object_id () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ return p_
+ ? object_traits<object_type>::id (*p_)
+ : i_.template object_id<O> ();
+ }
+
+ template <class T>
+ inline typename lazy_shared_ptr<T>::database_type& lazy_shared_ptr<T>::
+ database () const
+ {
+ return *i_.database ();
+ }
+
+ template<class T, class Y>
+ inline bool
+ operator== (const lazy_shared_ptr<T>& a, const lazy_shared_ptr<Y>& b)
+ {
+ return a.equal (b);
+ }
+
+ template<class T, class Y>
+ inline bool
+ operator!= (const lazy_shared_ptr<T>& a, const lazy_shared_ptr<Y>& b)
+ {
+ return !a.equal (b);
+ }
+
+ template<class T>
+ inline void
+ swap (lazy_shared_ptr<T>& a, lazy_shared_ptr<T>& b)
+ {
+ a.swap (b);
+ }
+
+ template<class D, class T>
+ inline D*
+ get_deleter (const lazy_shared_ptr<T>& p)
+ {
+ return std::tr1::get_deleter<D> (p.p_);
+ }
+
+
+ //
+ // lazy_weak_ptr
+ //
+
+ template <class T>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr () {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const lazy_shared_ptr<Y>& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const lazy_weak_ptr& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const lazy_weak_ptr<Y>& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ inline lazy_weak_ptr<T>::
+ ~lazy_weak_ptr () {}
+
+ template <class T>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const lazy_weak_ptr& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const lazy_weak_ptr<Y>& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const lazy_shared_ptr<Y>& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ inline void lazy_weak_ptr<T>::
+ swap (lazy_weak_ptr<T>& r)
+ {
+ p_.swap (r.p_);
+ i_.swap (r.i_);
+ }
+
+ template <class T>
+ inline void lazy_weak_ptr<T>::
+ reset ()
+ {
+ p_.reset ();
+ i_.reset ();
+ }
+
+ template <class T>
+ inline long lazy_weak_ptr<T>::
+ use_count () const
+ {
+ return p_.use_count ();
+ }
+
+ template <class T>
+ inline bool lazy_weak_ptr<T>::
+ expired () const
+ {
+ return p_.expired ();
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const std::tr1::weak_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const std::tr1::shared_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const std::tr1::weak_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const std::tr1::shared_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ inline bool lazy_weak_ptr<T>::
+ loaded () const
+ {
+ bool i (i_);
+ return expired () != i; // expired () XOR i_
+ }
+
+ template <class T>
+ inline lazy_shared_ptr<T> lazy_weak_ptr<T>::
+ lock () const
+ {
+ return lazy_shared_ptr<T> (p_.lock (), i_);
+ }
+
+ template <class T>
+ inline std::tr1::shared_ptr<T> lazy_weak_ptr<T>::
+ load () const
+ {
+ std::tr1::shared_ptr<T> r (p_.lock ());
+
+ if (!r && i_)
+ {
+ r = i_.template load<T> (false); // Keep id.
+ p_ = r;
+ }
+
+ return r;
+ }
+
+ template <class T>
+ inline void lazy_weak_ptr<T>::
+ unload () const
+ {
+ // With weak pointer we always keep i_ up to date.
+ //
+ p_.reset ();
+ }
+
+ template <class T>
+ inline std::tr1::weak_ptr<T> lazy_weak_ptr<T>::
+ get_eager () const
+ {
+ return p_;
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (DB& db, const ID& id): i_ (db, id) {}
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (DB& db, const std::tr1::shared_ptr<Y>& r)
+ : p_ (r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ if (r)
+ i_.reset (db, object_traits<object_type>::id (*r));
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (DB& db, const std::tr1::weak_ptr<Y>& r)
+ : p_ (r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ std::tr1::shared_ptr<T> sp (p_.lock ());
+
+ if (sp)
+ i_.reset (db, object_traits<object_type>::id (*sp));
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline void lazy_weak_ptr<T>::
+ reset (DB& db, const ID& id)
+ {
+ p_.reset ();
+ i_.reset (db, id);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_weak_ptr<T>::
+ reset (DB& db, const std::tr1::shared_ptr<Y>& r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ p_ = r;
+
+ if (r)
+ i_.reset (db, object_traits<object_type>::id (*r));
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_weak_ptr<T>::
+ reset (DB& db, const std::tr1::weak_ptr<Y>& r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ p_ = r;
+ std::tr1::shared_ptr<T> sp (p_.lock ());
+
+ if (sp)
+ i_.reset (db, object_traits<object_type>::id (*sp));
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class O>
+ inline typename object_traits<O>::id_type lazy_weak_ptr<T>::
+ object_id () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ std::tr1::shared_ptr<T> sp (p_.lock ());
+ return sp
+ ? object_traits<object_type>::id (*sp)
+ : i_.template object_id<O> ();
+ }
+
+ template <class T>
+ inline typename lazy_weak_ptr<T>::database_type& lazy_weak_ptr<T>::
+ database () const
+ {
+ return *i_.database ();
+ }
+
+ template<class T>
+ inline void
+ swap (lazy_weak_ptr<T>& a, lazy_weak_ptr<T>& b)
+ {
+ a.swap (b);
+ }
+ }
+}
diff --git a/libodb/odb/tr1/lazy-ptr.txx b/libodb/odb/tr1/lazy-ptr.txx
new file mode 100644
index 0000000..7e36cd9
--- /dev/null
+++ b/libodb/odb/tr1/lazy-ptr.txx
@@ -0,0 +1,43 @@
+// file : odb/tr1/lazy-ptr.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace tr1
+ {
+ //
+ // lazy_shared_ptr
+ //
+
+ template <class T>
+ template <class Y>
+ bool lazy_shared_ptr<T>::
+ equal (const lazy_shared_ptr<Y>& r) const
+ {
+ bool t1 (!p_ == loaded ());
+ bool t2 (!r.p_ == r.loaded ());
+
+ // If both are transient, then compare the underlying pointers.
+ //
+ if (t1 && t2)
+ return p_ == r.p_;
+
+ // If one is transient and the other is persistent, then compare
+ // the underlying pointers but only if they are non NULL. Note
+ // that an unloaded persistent object is always unequal to a
+ // transient object.
+ //
+ if (t1 || t2)
+ return p_ == r.p_ && p_;
+
+ // If both objects are persistent, then we compare databases and
+ // object ids.
+ //
+ typedef typename object_traits<T>::object_type object_type1;
+ typedef typename object_traits<Y>::object_type object_type2;
+
+ return i_.database () == r.i_.database () &&
+ object_id<object_type1> () == r.template object_id<object_type2> ();
+ }
+ }
+}
diff --git a/libodb/odb/tr1/memory.hxx b/libodb/odb/tr1/memory.hxx
new file mode 100644
index 0000000..b9e9f45
--- /dev/null
+++ b/libodb/odb/tr1/memory.hxx
@@ -0,0 +1,37 @@
+// file : odb/tr1/memory.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_TR1_MEMORY_HXX
+#define ODB_TR1_MEMORY_HXX
+
+//
+// Try to include TR1 <memory> in a compiler-specific manner. Fall back
+// on the Boost TR1 implementation if the compiler does not support TR1.
+//
+
+#include <cstddef> // __GLIBCXX__, _HAS_TR1
+
+// GNU C++ or Intel C++ using libstd++.
+//
+#if defined (__GNUC__) && __GNUC__ >= 4 && defined (__GLIBCXX__)
+# include <tr1/memory>
+//
+// IBM XL C++.
+//
+#elif defined (__xlC__) && __xlC__ >= 0x0900
+# define __IBMCPP_TR1__
+# include <memory>
+//
+// VC++ or Intel C++ using VC++ standard library.
+//
+#elif defined (_MSC_VER) && \
+ (_MSC_VER == 1500 && defined (_HAS_TR1) || _MSC_VER > 1500)
+# include <memory>
+//
+// Boost fall-back.
+//
+#else
+# include <boost/tr1/memory.hpp>
+#endif
+
+#endif // ODB_TR1_MEMORY_HXX
diff --git a/libodb/odb/tr1/pointer-traits.hxx b/libodb/odb/tr1/pointer-traits.hxx
new file mode 100644
index 0000000..df4f25f
--- /dev/null
+++ b/libodb/odb/tr1/pointer-traits.hxx
@@ -0,0 +1,121 @@
+// file : odb/tr1/pointer-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_TR1_POINTER_TRAITS_HXX
+#define ODB_TR1_POINTER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+// In VC++ std::shared_ptr and std::tr1::shared_ptr is the same class
+// template. One is just a using-declaration for the other.
+//
+#if !(defined(ODB_CXX11) && defined(_MSC_VER))
+
+//
+// This header assumes that the necessary TR1 header has already
+// been included.
+//
+
+#include <odb/pointer-traits.hxx>
+#include <odb/details/meta/remove-const.hxx>
+
+namespace odb
+{
+ // Specialization for std::tr1::shared_ptr.
+ //
+ template <typename T>
+ class pointer_traits<std::tr1::shared_ptr<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_shared;
+ static const bool lazy = false;
+
+ typedef T element_type;
+ typedef std::tr1::shared_ptr<element_type> pointer_type;
+ typedef std::tr1::shared_ptr<const element_type> const_pointer_type;
+ typedef typename odb::details::meta::remove_const<element_type>::result
+ unrestricted_element_type;
+ typedef std::tr1::shared_ptr<unrestricted_element_type>
+ unrestricted_pointer_type;
+ typedef smart_ptr_guard<pointer_type> guard;
+
+ static element_type*
+ get_ptr (const pointer_type& p)
+ {
+ return p.get ();
+ }
+
+ static element_type&
+ get_ref (const pointer_type& p)
+ {
+ return *p;
+ }
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ static unrestricted_pointer_type
+ const_pointer_cast (const pointer_type& p)
+ {
+ return std::tr1::const_pointer_cast<unrestricted_element_type> (p);
+ }
+
+ template <typename T1>
+ static std::tr1::shared_ptr<T1>
+ static_pointer_cast (const pointer_type& p)
+ {
+ return std::tr1::static_pointer_cast<T1> (p);
+ }
+
+ template <typename T1>
+ static std::tr1::shared_ptr<T1>
+ dynamic_pointer_cast (const pointer_type& p)
+ {
+ return std::tr1::dynamic_pointer_cast<T1> (p);
+ }
+
+ public:
+ static void*
+ allocate (std::size_t n)
+ {
+ return operator new (n);
+ }
+
+ static void
+ free (void* p)
+ {
+ operator delete (p);
+ }
+ };
+
+ // Specialization for std::tr1::weak_ptr.
+ //
+ template <typename T>
+ class pointer_traits<std::tr1::weak_ptr<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_weak;
+ static const bool lazy = false;
+
+ typedef T element_type;
+ typedef std::tr1::weak_ptr<element_type> pointer_type;
+ typedef std::tr1::shared_ptr<element_type> strong_pointer_type;
+
+ static strong_pointer_type
+ lock (const pointer_type& p)
+ {
+ return p.lock ();
+ }
+ };
+}
+
+#endif // !(ODB_CXX11 && _MSC_VER)
+
+#include <odb/post.hxx>
+
+#endif // ODB_TR1_POINTER_TRAITS_HXX
diff --git a/libodb/odb/tr1/wrapper-traits.hxx b/libodb/odb/tr1/wrapper-traits.hxx
new file mode 100644
index 0000000..f878ef6
--- /dev/null
+++ b/libodb/odb/tr1/wrapper-traits.hxx
@@ -0,0 +1,76 @@
+// file : odb/tr1/wrapper-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_TR1_WRAPPER_TRAITS_HXX
+#define ODB_TR1_WRAPPER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+// In VC++ std::shared_ptr and std::tr1::shared_ptr is the same class
+// template. One is just a using-declaration for the other.
+//
+#if !(defined(ODB_CXX11) && defined(_MSC_VER))
+
+//
+// This header assumes that the necessary TR1 header has already
+// been included.
+//
+
+#include <odb/wrapper-traits.hxx>
+
+namespace odb
+{
+ // Specialization for std::tr1::shared_ptr.
+ //
+ template <typename T>
+ class wrapper_traits< std::tr1::shared_ptr<T> >
+ {
+ public:
+ typedef T wrapped_type;
+ typedef std::tr1::shared_ptr<T> wrapper_type;
+
+ // T can be const.
+ //
+ typedef
+ typename odb::details::meta::remove_const<T>::result
+ unrestricted_wrapped_type;
+
+ static const bool null_handler = true;
+ static const bool null_default = false;
+
+ static bool
+ get_null (const wrapper_type& p)
+ {
+ return !p;
+ }
+
+ static void
+ set_null (wrapper_type& p)
+ {
+ p.reset ();
+ }
+
+ static const wrapped_type&
+ get_ref (const wrapper_type& p)
+ {
+ return *p;
+ }
+
+ static unrestricted_wrapped_type&
+ set_ref (wrapper_type& p)
+ {
+ if (!p)
+ p.reset (new unrestricted_wrapped_type);
+
+ return const_cast<unrestricted_wrapped_type&> (*p);
+ }
+ };
+}
+
+#endif // !(ODB_CXX11 && _MSC_VER)
+
+#include <odb/post.hxx>
+
+#endif // ODB_TR1_WRAPPER_TRAITS_HXX
diff --git a/libodb/odb/tracer.cxx b/libodb/odb/tracer.cxx
new file mode 100644
index 0000000..1e636a7
--- /dev/null
+++ b/libodb/odb/tracer.cxx
@@ -0,0 +1,95 @@
+// file : odb/tracer.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <iostream>
+
+#include <odb/tracer.hxx>
+#include <odb/statement.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ //
+ // tracer
+ //
+
+ tracer::
+ ~tracer ()
+ {
+ }
+
+ void tracer::
+ prepare (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ execute (connection& c, const statement& s)
+ {
+ execute (c, s.text ());
+ }
+
+ void tracer::
+ deallocate (connection&, const statement&)
+ {
+ }
+
+ //
+ // stderr_tracer
+ //
+
+ class stderr_tracer_type: public tracer
+ {
+ public:
+ stderr_tracer_type (bool full): full_ (full) {}
+
+ virtual void
+ prepare (connection&, const statement&);
+
+ virtual void
+ execute (connection&, const char* statement);
+
+ virtual void
+ deallocate (connection&, const statement&);
+
+ // Override the other version to get rid of a Sun CC warning.
+ //
+ virtual void
+ execute (connection&, const statement&);
+
+ private:
+ bool full_;
+ };
+
+ void stderr_tracer_type::
+ prepare (connection&, const statement& s)
+ {
+ if (full_)
+ cerr << "PREPARE " << s.text () << endl;
+ }
+
+ void stderr_tracer_type::
+ execute (connection&, const char* s)
+ {
+ cerr << s << endl;
+ }
+
+ void stderr_tracer_type::
+ deallocate (connection&, const statement& s)
+ {
+ if (full_)
+ cerr << "DEALLOCATE " << s.text () << endl;
+ }
+
+ void stderr_tracer_type::
+ execute (connection& c, const statement& s)
+ {
+ execute (c, s.text ());
+ }
+
+ static stderr_tracer_type stderr_tracer_ (false);
+ static stderr_tracer_type stderr_full_tracer_ (true);
+ tracer& stderr_tracer = stderr_tracer_;
+ tracer& stderr_full_tracer = stderr_full_tracer_;
+}
diff --git a/libodb/odb/tracer.hxx b/libodb/odb/tracer.hxx
new file mode 100644
index 0000000..11e4e76
--- /dev/null
+++ b/libodb/odb/tracer.hxx
@@ -0,0 +1,36 @@
+// file : odb/tracer.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_TRACER_HXX
+#define ODB_TRACER_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ class LIBODB_EXPORT tracer
+ {
+ public:
+ virtual
+ ~tracer ();
+
+ 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&);
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_TRACER_HXX
diff --git a/libodb/odb/traits.hxx b/libodb/odb/traits.hxx
new file mode 100644
index 0000000..2c6f5d6
--- /dev/null
+++ b/libodb/odb/traits.hxx
@@ -0,0 +1,317 @@
+// file : odb/traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_TRAITS_HXX
+#define ODB_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+#include <odb/pointer-traits.hxx>
+
+namespace odb
+{
+ // Fallback dummy for non-persistent classes. It is necessary to allow
+ // the C++ compiler to instantiate persist(), etc., signatures in class
+ // database when T is a pointer (raw, smart). The overloads that use
+ // these dummy would never actually be selected by the compiler.
+ //
+ template <typename T>
+ class access::object_traits
+ {
+ // If a C++ compiler issues an error pointing to this class and saying
+ // that it is missing some declaration, then you are most likely trying
+ // to perform a database operation on a C++ type that is not a persistent
+ // object. Or you forgot to include the corresponding -odb.hxx file.
+ //
+ public:
+ struct id_type {};
+ typedef T object_type;
+ typedef T* pointer_type;
+
+ static const bool polymorphic = false;
+ };
+
+ template <typename T, typename P>
+ class access::object_factory
+ {
+ public:
+ typedef T object_type;
+ typedef P pointer_type;
+
+ static P
+ create ()
+ {
+ // By default use pointer-specific construction.
+ //
+ return pointer_factory<T, P>::create ();
+ }
+ };
+
+ template <typename T, typename P>
+ class access::view_factory
+ {
+ public:
+ typedef T view_type;
+ typedef P pointer_type;
+
+ static P
+ create ()
+ {
+ // By default use pointer-specific construction.
+ //
+ return pointer_factory<T, P>::create ();
+ }
+ };
+
+ template <typename T, typename P>
+ class access::pointer_factory
+ {
+ public:
+ typedef T value_type;
+ typedef P pointer_type;
+
+
+ // Suppress bogus use-after-free introduced in GCC 12 (GCC bug #105327).
+ //
+#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 12
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wuse-after-free"
+#endif
+
+ static P
+ create ()
+ {
+ void* v (pointer_traits<P>::allocate (sizeof (T)));
+ mem_guard g (v);
+ P p (new (v) T);
+ g.release ();
+ return p;
+ }
+
+#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 12
+#pragma GCC diagnostic pop
+#endif
+
+ private:
+ struct mem_guard
+ {
+ mem_guard (void* p): p_ (p) {}
+ ~mem_guard () {if (p_) pointer_traits<P>::free (p_);}
+ void release () {p_ = 0;}
+ void* p_;
+ };
+ };
+
+ //
+ // class_traits
+ //
+ enum class_kind
+ {
+ class_object,
+ class_view,
+ class_other
+ };
+
+ template <typename T>
+ struct class_traits
+ {
+ static const class_kind kind = class_other;
+ };
+
+ template <typename T>
+ struct class_traits<const T>
+ {
+ static const class_kind kind = class_traits<T>::kind;
+ };
+
+ //
+ // object_traits
+ //
+
+ template <typename T>
+ //
+ // If a C++ compiler issues an error pointing to this struct and
+ // saying that it is incomplete, then you are most likely trying to
+ // perform a database operation on a C++ type that is not a persistent
+ // object. Or you forgot to include the corresponding -odb.hxx file.
+ //
+ struct object_traits:
+ access::object_traits<T>,
+ access::object_factory<T, typename access::object_traits<T>::pointer_type>
+ {
+ typedef
+ odb::pointer_traits<typename access::object_traits<T>::pointer_type>
+ pointer_traits;
+
+ typedef typename access::object_traits<T>::object_type object_type;
+ typedef typename access::object_traits<T>::pointer_type pointer_type;
+ typedef typename pointer_traits::const_pointer_type const_pointer_type;
+ };
+
+ // Specialization for const objects. It only defines the id, object,
+ // pointer, and const_pointer types with pointer and const_pointer
+ // being the same. The idea is to only use this specialization in the
+ // interfaces, with the implementations detecting this situation and
+ // using the non-const object_traits version.
+ //
+ template <typename T>
+ struct object_traits<const T>
+ {
+ private:
+ typedef
+ odb::pointer_traits<typename access::object_traits<T>::pointer_type>
+ pointer_traits;
+
+ public:
+ typedef typename access::object_traits<T>::id_type id_type;
+ typedef typename access::object_traits<T>::object_type object_type;
+ typedef typename pointer_traits::const_pointer_type const_pointer_type;
+ typedef const_pointer_type pointer_type;
+
+ static const bool polymorphic = access::object_traits<T>::polymorphic;
+ };
+
+ // Specialization for section to allow instantiation of all the load()
+ // signature.
+ //
+ template <>
+ struct object_traits<section> {};
+
+ template <typename T, database_id DB>
+ //
+ // If a C++ compiler issues an error pointing to this struct and
+ // saying that it is incomplete, then you are most likely trying to
+ // perform a database operation on a C++ type that is not a persistent
+ // object. Or you forgot to include the corresponding -odb.hxx file.
+ //
+ struct object_traits_impl:
+ access::object_traits_impl<T, DB>,
+ access::object_factory<T, typename access::object_traits<T>::pointer_type>
+ {
+ typedef
+ odb::pointer_traits<typename access::object_traits<T>::pointer_type>
+ pointer_traits;
+
+ typedef typename access::object_traits<T>::object_type object_type;
+ typedef typename access::object_traits<T>::pointer_type pointer_type;
+ typedef typename pointer_traits::const_pointer_type const_pointer_type;
+ };
+
+ //
+ // view_traits
+ //
+
+ template <typename T>
+ //
+ // If a C++ compiler issues an error pointing to this struct and
+ // saying that it is incomplete, then you are most likely trying to
+ // perform a database operation on a C++ type that is not a view
+ // Or you forgot to include the corresponding -odb.hxx file.
+ //
+ struct view_traits:
+ access::view_traits<T>,
+ access::view_factory<T, typename access::view_traits<T>::pointer_type>
+ {
+ typedef
+ odb::pointer_traits<typename access::view_traits<T>::pointer_type>
+ pointer_traits;
+
+ typedef typename access::view_traits<T>::view_type view_type;
+ typedef typename access::view_traits<T>::pointer_type pointer_type;
+ };
+
+ // Specialization for const views. It only defines the view, pointer,
+ // and const_pointer types with pointer and const_pointer being the
+ // same. Similar to objects, the idea is to only use this specialization
+ // in the interfaces, with the implementations detecting this situation
+ // and using the non-const view_traits version.
+ //
+ template <typename T>
+ struct view_traits<const T>
+ {
+ private:
+ typedef
+ odb::pointer_traits<typename access::view_traits<T>::pointer_type>
+ pointer_traits;
+
+ public:
+ typedef typename access::view_traits<T>::view_type view_type;
+ typedef typename pointer_traits::const_pointer_type const_pointer_type;
+ typedef const_pointer_type pointer_type;
+ };
+
+ template <typename T, database_id DB>
+ //
+ // If a C++ compiler issues an error pointing to this struct and
+ // saying that it is incomplete, then you are most likely trying to
+ // perform a database operation on a C++ type that is not a view
+ // Or you forgot to include the corresponding -odb.hxx file.
+ //
+ struct view_traits_impl:
+ access::view_traits_impl<T, DB>,
+ access::view_factory<T, typename access::view_traits<T>::pointer_type>
+ {
+ typedef
+ odb::pointer_traits<typename access::view_traits<T>::pointer_type>
+ pointer_traits;
+
+ typedef typename access::view_traits<T>::view_type view_type;
+ typedef typename access::view_traits<T>::pointer_type pointer_type;
+ };
+
+ //
+ // composite_value_traits
+ //
+
+ template <typename T, database_id DB>
+ struct composite_value_traits: access::composite_value_traits<T, DB>
+ {
+ };
+
+ //
+ // Get root image from a polymorphic image chain.
+ //
+
+ template <typename T, std::size_t d>
+ struct root_image_impl
+ {
+ typedef root_image_impl<typename T::base_traits, d - 1> base_type;
+ typedef typename base_type::image_type image_type;
+
+ static image_type&
+ get (typename T::image_type& i) {return base_type::get (*i.base);}
+ };
+
+ template <typename T>
+ struct root_image_impl<T, 1>
+ {
+ typedef typename T::image_type image_type;
+
+ static image_type&
+ get (image_type& i) {return i;}
+ };
+
+ template <typename T, bool p>
+ struct root_image
+ {
+ typedef root_image_impl<T, T::depth> impl_type;
+ typedef typename impl_type::image_type image_type;
+
+ static image_type&
+ get (typename T::image_type& i) {return impl_type::get (i);}
+ };
+
+ template <typename T>
+ struct root_image<T, false>
+ {
+ typedef typename T::image_type image_type;
+
+ static image_type&
+ get (image_type& i) {return i;}
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_TRAITS_HXX
diff --git a/libodb/odb/transaction.cxx b/libodb/odb/transaction.cxx
new file mode 100644
index 0000000..03810a1
--- /dev/null
+++ b/libodb/odb/transaction.cxx
@@ -0,0 +1,358 @@
+// file : odb/transaction.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/transaction.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/details/tls.hxx>
+#include <odb/details/unused.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ using namespace details;
+
+ //
+ // transaction
+ //
+
+ static ODB_TLS_POINTER (transaction) current_transaction;
+
+ transaction::
+ ~transaction ()
+ {
+ if (!finalized_)
+ try {rollback ();} catch (...) {}
+ }
+
+ void transaction::
+ reset (transaction_impl* impl, bool make_current)
+ {
+ details::unique_ptr<transaction_impl> i (impl);
+
+ if (!finalized_)
+ rollback ();
+
+ impl_.reset (i.release ());
+
+ if (make_current && tls_get (current_transaction) != 0)
+ throw already_in_transaction ();
+
+ impl_->start ();
+ finalized_ = false;
+
+ if (make_current)
+ tls_set (current_transaction, this);
+ }
+
+ bool transaction::
+ has_current ()
+ {
+ return tls_get (current_transaction) != 0;
+ }
+
+ transaction& transaction::
+ current ()
+ {
+ transaction* cur (tls_get (current_transaction));
+
+ if (cur == 0)
+ throw not_in_transaction ();
+
+ return *cur;
+ }
+
+ void transaction::
+ current (transaction& t)
+ {
+ tls_set (current_transaction, &t);
+ }
+
+ void transaction::
+ reset_current ()
+ {
+ transaction* t (0);
+ tls_set (current_transaction, t);
+ }
+
+ struct rollback_guard
+ {
+ rollback_guard (transaction& t): t_ (&t) {}
+ ~rollback_guard ()
+ {if (t_ != 0) t_->callback_call (transaction::event_rollback);}
+ void release () {t_ = 0;}
+ private:
+ transaction* t_;
+ };
+
+ void transaction::
+ commit ()
+ {
+ if (finalized_)
+ throw transaction_already_finalized ();
+
+ finalized_ = true;
+ rollback_guard rg (*this);
+
+ impl_->tracer (0);
+
+ if (tls_get (current_transaction) == this)
+ {
+ transaction* t (0);
+ tls_set (current_transaction, t);
+ }
+
+ impl_->commit ();
+ rg.release ();
+
+ if (callback_count_ != 0)
+ callback_call (event_commit);
+ }
+
+ void transaction::
+ rollback ()
+ {
+ if (finalized_)
+ throw transaction_already_finalized ();
+
+ finalized_ = true;
+ rollback_guard rg (*this);
+
+ impl_->tracer (0);
+
+ if (tls_get (current_transaction) == this)
+ {
+ transaction* t (0);
+ tls_set (current_transaction, t);
+ }
+
+ impl_->rollback ();
+ rg.release ();
+
+ if (callback_count_ != 0)
+ callback_call (event_rollback);
+ }
+
+ void transaction::
+ callback_call (unsigned short event)
+ {
+ size_t stack_count (callback_count_ < stack_callback_count
+ ? callback_count_ : stack_callback_count);
+ size_t dyn_count (callback_count_ - stack_count);
+
+ // We need to be careful with the situation where a callback
+ // throws and we neither call the rest of the callbacks nor
+ // reset their states. To make sure this doesn't happen, we
+ // do a first pass and reset all the states.
+ //
+ for (size_t i (0); i < stack_count; ++i)
+ {
+ callback_data& d (stack_callbacks_[i]);
+ if (d.event != 0 && d.state != 0)
+ *d.state = 0;
+ }
+
+ for (size_t i (0); i < dyn_count; ++i)
+ {
+ callback_data& d (dyn_callbacks_[i]);
+ if (d.event != 0 && d.state != 0)
+ *d.state = 0;
+ }
+
+ // Now do the actual calls.
+ //
+ for (size_t i (0); i < stack_count; ++i)
+ {
+ callback_data& d (stack_callbacks_[i]);
+ if (d.event & event)
+ d.func (event, d.key, d.data);
+ }
+
+ for (size_t i (0); i < dyn_count; ++i)
+ {
+ callback_data& d (dyn_callbacks_[i]);
+ if (d.event & event)
+ d.func (event, d.key, d.data);
+ }
+
+ // Clean things up in case this instance is going to be reused.
+ //
+ if (dyn_count != 0)
+ dyn_callbacks_.clear ();
+
+ free_callback_ = max_callback_count;
+ callback_count_ = 0;
+ }
+
+ void transaction::
+ callback_register (callback_type func,
+ void* key,
+ unsigned short event,
+ unsigned long long data,
+ transaction** state)
+ {
+ callback_data* s;
+
+ // If we have a free slot, use it.
+ //
+ if (free_callback_ != max_callback_count)
+ {
+ s = (free_callback_ < stack_callback_count)
+ ? stack_callbacks_ + free_callback_
+ : &dyn_callbacks_[free_callback_ - stack_callback_count];
+
+ free_callback_ = reinterpret_cast<size_t> (s->key);
+ }
+ // If we have space in the stack, grab that.
+ //
+ else if (callback_count_ < stack_callback_count)
+ {
+ s = stack_callbacks_ + callback_count_;
+ callback_count_++;
+ }
+ // Otherwise use the dynamic storage.
+ //
+ else
+ {
+ dyn_callbacks_.push_back (callback_data ());
+ s = &dyn_callbacks_.back ();
+ callback_count_++;
+ }
+
+ s->func = func;
+ s->key = key;
+ s->event = event;
+ s->data = data;
+ s->state = state;
+ }
+
+ size_t transaction::
+ callback_find (void* key)
+ {
+ if (callback_count_ == 0)
+ return 0;
+
+ size_t stack_count;
+
+ // See if this is the last slot registered. This will be a fast path if,
+ // for example, things are going to be unregistered from destructors.
+ //
+ if (callback_count_ <= stack_callback_count)
+ {
+ if (stack_callbacks_[callback_count_ - 1].key == key)
+ return callback_count_ - 1;
+
+ stack_count = callback_count_;
+ }
+ else
+ {
+ if (dyn_callbacks_.back ().key == key)
+ return callback_count_ - 1;
+
+ stack_count = stack_callback_count;
+ }
+
+ // Otherwise do a linear search.
+ //
+ for (size_t i (0); i < stack_count; ++i)
+ if (stack_callbacks_[i].key == key)
+ return i;
+
+ for (size_t i (0), dyn_count (callback_count_ - stack_count);
+ i < dyn_count; ++i)
+ if (dyn_callbacks_[i].key == key)
+ return i + stack_callback_count;
+
+ return callback_count_;
+ }
+
+ void transaction::
+ callback_unregister (void* key)
+ {
+ size_t i (callback_find (key));
+
+ // It is ok for this function not to find the key.
+ //
+ if (i == callback_count_)
+ return;
+
+ // See if this is the last slot registered.
+ //
+ if (i == callback_count_ - 1)
+ {
+ if (i >= stack_callback_count)
+ dyn_callbacks_.pop_back ();
+
+ callback_count_--;
+ }
+ else
+ {
+ callback_data& d (
+ i < stack_callback_count
+ ? stack_callbacks_[i]
+ : dyn_callbacks_[i - stack_callback_count]);
+
+ // Add to the free list.
+ //
+ d.event = 0;
+ d.key = reinterpret_cast<void*> (free_callback_);
+ free_callback_ = i;
+ }
+ }
+
+ void transaction::
+ callback_update (void* key,
+ unsigned short event,
+ unsigned long long data,
+ transaction** state)
+ {
+ size_t i (callback_find (key));
+
+ // It is ok for this function not to find the key.
+ //
+ if (i == callback_count_)
+ return;
+
+ callback_data& d (
+ i < stack_callback_count
+ ? stack_callbacks_[i]
+ : dyn_callbacks_[i - stack_callback_count]);
+
+ d.event = event;
+ d.data = data;
+ d.state = state;
+ }
+
+ //
+ // transaction_impl
+ //
+
+ transaction_impl::
+ ~transaction_impl ()
+ {
+ }
+
+ connection& transaction_impl::
+ connection (database_type* db)
+ {
+ ODB_POTENTIALLY_UNUSED (db);
+ assert (db == 0 || db == &database_);
+ return *connection_;
+ }
+
+ // The transaction-specific tracer is stored in the connection. See the
+ // connection class for the reason.
+ //
+ void transaction_impl::
+ tracer (tracer_type* t)
+ {
+ connection_->transaction_tracer_ = t;
+ }
+
+ tracer* transaction_impl::
+ tracer () const
+ {
+ return connection_->transaction_tracer_;
+ }
+}
diff --git a/libodb/odb/transaction.hxx b/libodb/odb/transaction.hxx
new file mode 100644
index 0000000..1958df3
--- /dev/null
+++ b/libodb/odb/transaction.hxx
@@ -0,0 +1,278 @@
+// file : odb/transaction.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_TRANSACTION_HXX
+#define ODB_TRANSACTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+
+#include <odb/details/export.hxx>
+#include <odb/details/unique-ptr.hxx>
+
+namespace odb
+{
+ class transaction_impl;
+
+ class LIBODB_EXPORT transaction
+ {
+ public:
+ typedef odb::database database_type;
+ typedef odb::connection connection_type;
+
+ // If the second argument is false, then this transaction is not made
+ // the current transaction of the thread.
+ //
+ explicit
+ transaction (transaction_impl*, bool make_current = true);
+
+ // Create a finalized transaction instance which can later be initialized
+ // with reset().
+ //
+ transaction ();
+
+ // Unless the transaction has already been finalized (explicitly
+ // committed or rolled back), the destructor will roll it back.
+ //
+ ~transaction ();
+
+ // Unless the current transaction has already been finalized (explicitly
+ // committed or rolled back), reset will roll it back. If the second
+ // argument is false, then this transaction is not made the current
+ // transaction of the thread.
+ //
+ void
+ reset (transaction_impl*, bool make_current = true);
+
+ void
+ commit ();
+
+ void
+ rollback ();
+
+ // Return the database this transaction is on.
+ //
+ database_type&
+ database ();
+
+ // Return the connection this transaction is on.
+ //
+ // The second version verifies the connection is to the specified
+ // database. For database implementations that support attaching multiple
+ // databases it may also select the connection corresponding to the
+ // specified database.
+ //
+ connection_type&
+ connection ();
+
+ connection_type&
+ connection (database_type&);
+
+ bool
+ finalized () const {return finalized_;}
+
+ public:
+ // Return true if there is a transaction in effect.
+ //
+ static bool
+ has_current ();
+
+ // Return current transaction or throw if there is no transaction
+ // in effect.
+ //
+ static transaction&
+ current ();
+
+ // Set the current thread's transaction.
+ //
+ static void
+ current (transaction&);
+
+ // Revert to the no transaction in effect state for the current thread.
+ //
+ static void
+ reset_current ();
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef odb::tracer tracer_type;
+
+ void
+ tracer (tracer_type&);
+
+ void
+ tracer (tracer_type*);
+
+ tracer_type*
+ tracer () const;
+
+ // Post-commit/rollback callbacks.
+ //
+ 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);
+
+ // Register a post-commit/rollback callback. The data argument
+ // can be used to store any user data that does not exceed 8
+ // bytes and doesn't require alignment greater than unsigned
+ // long long, such as an old value that needs to be restored
+ // in case of a rollback.
+ //
+ // The state argument can be used to indicate to the caller
+ // that the callback has been unregistered because the
+ // transaction has terminated. In this case the transaction
+ // resets the passed pointer to 0.
+ //
+ // Note that the order in which the callbacks are called is
+ // unspecified.
+ //
+ void
+ callback_register (callback_type,
+ void* key,
+ unsigned short event = event_all,
+ unsigned long long data = 0,
+ transaction** state = 0);
+
+ // Unregister a post-commit/rollback callback. Note that this is a
+ // potentially slow operation. You also don't need to unregister
+ // a callback that has been called or auto-reset using the state
+ // argument passed to register(). This function does nothing if
+ // the key is not found.
+ //
+ void
+ callback_unregister (void* key);
+
+ // Update the event, data, and state values for a callback. Note
+ // that just like unregister(), this is a potentially slow operation.
+ //
+ void
+ callback_update (void* key,
+ unsigned short event,
+ unsigned long long data = 0,
+ transaction** state = 0);
+
+ public:
+ transaction_impl&
+ implementation ();
+
+ // Copying or assignment of transactions is not supported.
+ //
+ private:
+ transaction (const transaction&);
+ transaction& operator= (const transaction&);
+
+ protected:
+ friend struct rollback_guard;
+
+ std::size_t
+ callback_find (void* key);
+
+ void
+ callback_call (unsigned short event);
+
+ protected:
+ bool finalized_;
+ details::unique_ptr<transaction_impl> impl_;
+
+ // Callbacks.
+ //
+ struct callback_data
+ {
+ unsigned short event;
+ callback_type func;
+ void* key;
+ unsigned long long data;
+ transaction** state;
+ };
+
+ // Slots for the first 20 callback are pre-allocated on the stack.
+ // For the rest they are allocated dynamically as needed.
+ //
+ // Note, if you change stack_callback_count, make sure you also
+ // update the common/transaction/callback test accordingly.
+ //
+ static const std::size_t stack_callback_count = 20;
+ static const std::size_t max_callback_count = ~(std::size_t (0));
+
+ callback_data stack_callbacks_[stack_callback_count];
+ std::vector<callback_data> dyn_callbacks_;
+
+ // When a callback is unregistered, the free slot from the stack is
+ // added to the linked list of free slots which is organized by
+ // re-using the key data member to store the slot's index (we cannot
+ // store a pointer because std::vector may move slots on expansion).
+ // The value equal to max_callback_count indicates no free slots are
+ // available.
+ //
+ std::size_t free_callback_;
+
+ // Total number of used slots, both registered and in the free list.
+ //
+ std::size_t callback_count_;
+ };
+
+ class LIBODB_EXPORT transaction_impl
+ {
+ public:
+ typedef odb::tracer tracer_type;
+ typedef odb::database database_type;
+ typedef odb::connection connection_type;
+
+ virtual
+ ~transaction_impl ();
+
+ virtual void
+ start () = 0;
+
+ virtual void
+ commit () = 0;
+
+ virtual void
+ rollback () = 0;
+
+ database_type&
+ database ()
+ {
+ return database_;
+ }
+
+ virtual connection_type&
+ connection (database_type*);
+
+ virtual void
+ tracer (tracer_type*);
+
+ virtual tracer_type*
+ tracer () const;
+
+ protected:
+ transaction_impl (database_type& db)
+ : database_ (db), connection_ (0)
+ {
+ }
+
+ transaction_impl (database_type& db, connection_type& c)
+ : database_ (db), connection_ (&c)
+ {
+ }
+
+ protected:
+ database_type& database_;
+ connection_type* connection_;
+ };
+}
+
+#include <odb/transaction.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_TRANSACTION_HXX
diff --git a/libodb/odb/transaction.ixx b/libodb/odb/transaction.ixx
new file mode 100644
index 0000000..cc1ce5e
--- /dev/null
+++ b/libodb/odb/transaction.ixx
@@ -0,0 +1,68 @@
+// file : odb/transaction.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/connection.hxx>
+
+namespace odb
+{
+ inline transaction::
+ transaction ()
+ : finalized_ (true),
+ impl_ (0),
+ free_callback_ (max_callback_count),
+ callback_count_ (0)
+ {
+ }
+
+ inline transaction::
+ transaction (transaction_impl* impl, bool make_current)
+ : finalized_ (true),
+ impl_ (0),
+ free_callback_ (max_callback_count),
+ callback_count_ (0)
+ {
+ reset (impl, make_current);
+ }
+
+ inline transaction::database_type& transaction::
+ database ()
+ {
+ return impl_->database ();
+ }
+
+ inline transaction::connection_type& transaction::
+ connection ()
+ {
+ return impl_->connection (0);
+ }
+
+ inline transaction::connection_type& transaction::
+ connection (database_type& db)
+ {
+ return impl_->connection (&db);
+ }
+
+ inline transaction_impl& transaction::
+ implementation ()
+ {
+ return *impl_;
+ }
+
+ inline void transaction::
+ tracer (tracer_type& t)
+ {
+ impl_->tracer (&t);
+ }
+
+ inline void transaction::
+ tracer (tracer_type* t)
+ {
+ impl_->tracer (t);
+ }
+
+ inline transaction::tracer_type* transaction::
+ tracer () const
+ {
+ return impl_->tracer ();
+ }
+}
diff --git a/libodb/odb/vector-impl.cxx b/libodb/odb/vector-impl.cxx
new file mode 100644
index 0000000..ca30f8d
--- /dev/null
+++ b/libodb/odb/vector-impl.cxx
@@ -0,0 +1,208 @@
+// file : odb/vector-impl.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/vector-impl.hxx>
+
+#include <cstring> // std::memcpy, std::memset
+#include <algorithm> // std::swap
+
+using namespace std;
+
+namespace odb
+{
+ // vector_impl
+ //
+ const unsigned char vector_impl::mask_[4] = {0x3, 0xC, 0x30, 0xC0};
+ const unsigned char vector_impl::shift_[4] = {0, 2, 4, 6};
+
+ vector_impl::
+ vector_impl (const vector_impl& x)
+ : state_ (x.state_), size_ (0), tail_ (0), capacity_ (0), data_ (0)
+ {
+ // Copy the data over if we are tracking.
+ //
+ if (state_ == state_tracking && x.size_ != 0)
+ {
+ realloc (x.size_ < 1024 ? 1024 : x.size_);
+ memcpy (data_, x.data_, x.size_ / 4 + (x.size_ % 4 == 0 ? 0 : 1));
+ size_ = x.size_;
+ tail_ = x.tail_;
+ }
+ }
+
+ void vector_impl::
+ realloc (size_t n)
+ {
+ // The new capacity can be less or greater than the old one, but
+ // it cannot be less than size.
+ //
+ size_t b (n / 4 + (n % 4 == 0 ? 0 : 1));
+
+ if (b != capacity_ * 4)
+ {
+ unsigned char* d (static_cast<unsigned char*> (operator new (b)));
+
+ if (size_ != 0)
+ memcpy (d, data_, size_ / 4 + (size_ % 4 == 0 ? 0 : 1));
+
+ if (data_ != 0)
+ operator delete (data_);
+
+ data_ = d;
+ capacity_ = b * 4;
+ }
+ }
+
+ void vector_impl::
+ shrink_to_fit ()
+ {
+ if (size_ != capacity_)
+ {
+ if (size_ != 0)
+ realloc (size_);
+ else
+ {
+ operator delete (data_);
+ data_ = 0;
+ capacity_ = 0;
+ }
+ }
+ }
+
+ void vector_impl::
+ start (size_t n)
+ {
+ if (n != 0)
+ {
+ if (capacity_ < n)
+ {
+ size_ = 0;
+ realloc (n < 1024 ? 1024 : n);
+ }
+
+ memset (data_, 0, n / 4 + (n % 4 == 0 ? 0 : 1));
+ }
+
+ state_ = state_tracking;
+ size_ = tail_ = n;
+ }
+
+ void vector_impl::
+ push_back (size_t n)
+ {
+ for (; n != 0; --n)
+ {
+ size_t i (tail_);
+
+ element_state_type s;
+ if (i != size_)
+ // We have an erased element we can reuse.
+ //
+ s = state_updated;
+ else
+ {
+ if (size_ == capacity_)
+ {
+ size_t c (capacity_ == 0 ? 1024 : capacity_ * 2);
+ if (c < size_ + n)
+ c = size_ + n;
+ realloc (c);
+ }
+
+ s = state_inserted;
+ size_++;
+ }
+
+ set (i, s);
+ tail_++;
+ }
+ }
+
+ void vector_impl::
+ pop_back (size_t n)
+ {
+ for (; n != 0; --n)
+ {
+ size_t i (tail_ - 1);
+
+ if (state (i) != state_inserted)
+ set (i, state_erased);
+ else
+ size_--; // tail_ == size_
+
+ tail_--;
+ }
+ }
+
+ void vector_impl::
+ insert (size_t i, size_t n)
+ {
+ for (; i != tail_; ++i)
+ if (state (i) != state_inserted)
+ set (i, state_updated);
+
+ push_back (n);
+ }
+
+ void vector_impl::
+ erase (size_t i, size_t n)
+ {
+ pop_back (n);
+
+ for (; i != tail_; ++i)
+ if (state (i) != state_inserted)
+ set (i, state_updated);
+ }
+
+ void vector_impl::
+ clear ()
+ {
+ // The idea is to drop any inserted elements from the back and
+ // set everything else to erased.
+ //
+ if (tail_ == size_)
+ {
+ while (size_ != 0 && state (size_ - 1) == state_inserted)
+ size_--;
+
+ tail_ = size_;
+ }
+
+ if (tail_ != 0)
+ memset (data_, 0xFF, tail_ / 4 + (tail_ % 4 == 0 ? 0 : 1));
+
+ tail_ = 0;
+ }
+
+ // vector_base
+ //
+ void vector_base::
+ rollback (unsigned short, void* key, unsigned long long)
+ {
+ // Mark as changed.
+ //
+ static_cast<vector_base*> (key)->impl_.change ();
+ }
+
+ void vector_base::
+ swap_tran (vector_base& x)
+ {
+ // If either instance is armed, then we need to update the
+ // callback registration.
+ //
+ transaction* t (x.tran_);
+ if (tran_ != 0)
+ {
+ tran_->callback_unregister (this);
+ x._arm (*tran_);
+ }
+
+ if (t != 0)
+ {
+ t->callback_unregister (&x);
+ _arm (*t);
+ }
+
+ std::swap (tran_, x.tran_);
+ }
+}
diff --git a/libodb/odb/vector-impl.hxx b/libodb/odb/vector-impl.hxx
new file mode 100644
index 0000000..9f2ea7c
--- /dev/null
+++ b/libodb/odb/vector-impl.hxx
@@ -0,0 +1,221 @@
+// file : odb/vector-impl.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_VECTOR_IMPL_HXX
+#define ODB_VECTOR_IMPL_HXX
+
+#include <odb/pre.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <new>
+#include <cstddef> // std::size_t
+
+#include <odb/transaction.hxx>
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ // Change tracking vector implementation details.
+ //
+ class LIBODB_EXPORT vector_impl
+ {
+ public:
+ enum element_state_type
+ {
+ state_unchanged,
+ state_inserted,
+ state_updated,
+ state_erased
+ };
+
+ enum container_state_type
+ {
+ state_tracking,
+ state_not_tracking,
+ state_changed // Container has changed but individual changes
+ // were not tracked.
+ };
+
+ vector_impl ();
+ ~vector_impl ();
+
+ // The copy constructor will copy the state. The idea is that the
+ // copy keeps tracking changes, just like the original.
+ //
+ vector_impl (const vector_impl&);
+
+#ifdef ODB_CXX11
+ vector_impl (vector_impl&&) noexcept;
+#endif
+
+ void
+ swap (vector_impl& x);
+
+ // Allocate enough memory to store the specified number of
+ // elements.
+ //
+ void
+ reserve (std::size_t);
+
+ // Reduce capacity to size.
+ //
+ void
+ shrink_to_fit ();
+
+ // Capacity (each entry takes 2 bits).
+ //
+ std::size_t
+ capacity () const;
+
+ // (Re)start tracking changes for a vector with n elements.
+ //
+ void
+ start (std::size_t);
+
+ // Stop tracking changes.
+ //
+ void
+ stop ();
+
+ // Mark the container as changed without tracking the changes.
+ // This state is useful as a fallback mechnism for situations
+ // where the change information has been discarded (e.g., after
+ // its state has been updated in the database) but the container
+ // should remain changed (e.g., after the transaction is rolled
+ // back).
+ //
+ void
+ change ();
+
+ // Get the state of the container.
+ //
+ container_state_type
+ state () const;
+
+ // Shortcut for state() == state_tracking.
+ //
+ bool
+ tracking () const;
+
+ // Note that the returned size can be greater than the actual,
+ // parallel vector size. In this case the difference is the
+ // erased elements at the back.
+ //
+ std::size_t
+ size () const;
+
+ // Get the change state of the specified element.
+ //
+ element_state_type
+ state (std::size_t) const;
+
+ // Change notifications.
+ //
+ void
+ push_back (std::size_t n = 1);
+
+ void
+ pop_back (std::size_t n = 1);
+
+ void
+ insert (std::size_t, std::size_t n = 1);
+
+ void
+ erase (std::size_t, std::size_t n = 1);
+
+ void
+ modify (std::size_t, std::size_t n = 1);
+
+ void
+ clear ();
+
+ void
+ assign (std::size_t n);
+
+ void
+ resize (std::size_t n);
+
+ private:
+ // Assignment does not make sense (it is changing of the content).
+ //
+ vector_impl& operator= (const vector_impl&);
+
+ private:
+ void
+ realloc (std::size_t);
+
+ void
+ set (std::size_t, element_state_type);
+
+ static const unsigned char mask_[4];
+ static const unsigned char shift_[4];
+
+ container_state_type state_;
+
+ // Size, tail, and capacity are in 2-bit blocks. Size is the number
+ // of elements we have in data. Tail is the position of the first
+ // erased element at the back. If there are no erased elements, then
+ // tail is equal size. Capacity is the number of elements we can
+ // store in data.
+ //
+ std::size_t size_;
+ std::size_t tail_;
+ std::size_t capacity_;
+ unsigned char* data_;
+ };
+
+ // Base class that provides a change tracking interface and
+ // handles the rollback callback. The only function that's
+ // missing is _start() which needs to know the number of
+ // elements currently in the vector.
+ //
+ class LIBODB_EXPORT vector_base
+ {
+ public:
+ void
+ _stop () const;
+
+ bool
+ _tracking () const;
+
+ void
+ _arm (transaction& t) const;
+
+ vector_impl&
+ _impl () const {return impl_;}
+
+ private:
+ // Assignment is changing of the content.
+ //
+ vector_base& operator= (const vector_base&);
+
+ protected:
+ ~vector_base ();
+ vector_base ();
+ vector_base (const vector_base&);
+
+#ifdef ODB_CXX11
+ vector_base (vector_base&&) noexcept;
+#endif
+
+ void
+ swap (vector_base&);
+
+ static void
+ rollback (unsigned short, void* key, unsigned long long);
+
+ private:
+ void
+ swap_tran (vector_base&);
+
+ protected:
+ mutable vector_impl impl_;
+ mutable transaction* tran_;
+ };
+}
+
+#include <odb/vector-impl.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_VECTOR_IMPL_HXX
diff --git a/libodb/odb/vector-impl.ixx b/libodb/odb/vector-impl.ixx
new file mode 100644
index 0000000..21999d5
--- /dev/null
+++ b/libodb/odb/vector-impl.ixx
@@ -0,0 +1,210 @@
+// file : odb/vector-impl.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifdef ODB_CXX11
+# include <utility> // std::swap, std::move
+#else
+# include <algorithm> // std::swap
+#endif
+
+namespace odb
+{
+ // vector_impl
+ //
+ inline vector_impl::
+ vector_impl ()
+ : state_ (state_not_tracking),
+ size_ (0), tail_ (0), capacity_ (0), data_ (0)
+ {
+ }
+
+ inline void vector_impl::
+ swap (vector_impl& x)
+ {
+ std::swap (state_, x.state_);
+ std::swap (size_, x.size_);
+ std::swap (tail_, x.tail_);
+ std::swap (capacity_, x.capacity_);
+ std::swap (data_, x.data_);
+ }
+
+#ifdef ODB_CXX11
+ inline vector_impl::
+ vector_impl (vector_impl&& x) noexcept
+ : state_ (state_not_tracking),
+ size_ (0), tail_ (0), capacity_ (0), data_ (0)
+ {
+ swap (x);
+ }
+#endif
+
+ inline vector_impl::
+ ~vector_impl ()
+ {
+ if (data_ != 0)
+ operator delete (data_);
+ }
+
+ inline void vector_impl::
+ reserve (std::size_t n)
+ {
+ if (n > capacity_)
+ realloc (n);
+ }
+
+ inline void vector_impl::
+ stop ()
+ {
+ state_ = state_not_tracking;
+ size_ = tail_ = 0;
+ }
+
+ inline void vector_impl::
+ change ()
+ {
+ state_ = state_changed;
+ size_ = tail_ = 0;
+ }
+
+ inline vector_impl::container_state_type vector_impl::
+ state () const
+ {
+ return state_;
+ }
+
+ inline bool vector_impl::
+ tracking () const
+ {
+ return state_ == state_tracking;
+ }
+
+ inline std::size_t vector_impl::
+ size () const
+ {
+ return size_;
+ }
+
+ inline std::size_t vector_impl::
+ capacity () const
+ {
+ return capacity_;
+ }
+
+ inline vector_impl::element_state_type vector_impl::
+ state (std::size_t i) const
+ {
+ std::size_t r (i % 4);
+ unsigned char v (data_[i / 4]);
+ return static_cast<element_state_type> ((v & mask_[r]) >> shift_[r]);
+ }
+
+ inline void vector_impl::
+ set (std::size_t i, element_state_type s)
+ {
+ std::size_t r (i % 4);
+ i /= 4;
+ unsigned char v (static_cast<unsigned char> (s));
+ v <<= shift_[r];
+ data_[i] = (data_[i] & ~mask_[r]) | v;
+ }
+
+ inline void vector_impl::
+ modify (std::size_t i, std::size_t n)
+ {
+ for (; n != 0; --n, ++i)
+ if (state (i) != state_inserted)
+ set (i, state_updated);
+ }
+
+ inline void vector_impl::
+ assign (std::size_t n)
+ {
+ if (tail_ != 0)
+ clear ();
+
+ push_back (n);
+ }
+
+ inline void vector_impl::
+ resize (size_t n)
+ {
+ if (n < tail_)
+ pop_back (tail_ - n);
+ else if (n > tail_)
+ push_back (n - tail_);
+ }
+
+ // vector_base
+ //
+ inline vector_base::
+ ~vector_base ()
+ {
+ if (tran_ != 0)
+ tran_->callback_unregister (this);
+ }
+
+ inline vector_base::
+ vector_base (): tran_ (0) {}
+
+ inline void vector_base::
+ _arm (transaction& t) const
+ {
+ tran_ = &t;
+ t.callback_register (&rollback,
+ const_cast<vector_base*> (this),
+ transaction::event_rollback,
+ 0,
+ &tran_);
+ }
+
+ inline vector_base::
+ vector_base (const vector_base& x)
+ : impl_ (x.impl_), tran_ (0)
+ {
+ // If the original is armed, then arm ourselves as well.
+ //
+ if (x.tran_ != 0)
+ _arm (*x.tran_);
+ }
+
+ inline void vector_base::
+ swap (vector_base& x)
+ {
+ impl_.swap (x.impl_);
+
+ if (tran_ != 0 || x.tran_ != 0)
+ swap_tran (x);
+ }
+
+#ifdef ODB_CXX11
+ inline vector_base::
+ vector_base (vector_base&& x) noexcept
+ : impl_ (std::move (x.impl_)), tran_ (0)
+ {
+ if (x.tran_ != 0)
+ {
+ x.tran_->callback_unregister (&x);
+
+ // Note that _arm() can potentially throw bad_alloc while adding a new
+ // callback to the callbacks list of the transaction object. However, we
+ // assume that this will not happen since the new callback should be
+ // saved into an existing slot, freed by the above callback_unregister()
+ // call.
+ //
+ _arm (*x.tran_);
+ }
+ }
+#endif
+
+ inline void vector_base::
+ _stop () const
+ {
+ impl_.stop ();
+ }
+
+ inline bool vector_base::
+ _tracking () const
+ {
+ return impl_.tracking ();
+ }
+}
diff --git a/libodb/odb/vector-traits.hxx b/libodb/odb/vector-traits.hxx
new file mode 100644
index 0000000..5e6cf14
--- /dev/null
+++ b/libodb/odb/vector-traits.hxx
@@ -0,0 +1,106 @@
+// file : odb/vector-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_VECTOR_TRAITS_HXX
+#define ODB_VECTOR_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/vector.hxx>
+#include <odb/vector-impl.hxx>
+#include <odb/container-traits.hxx>
+#include <odb/transaction.hxx>
+
+namespace odb
+{
+ template <typename V, typename A LIBODB_VECTOR_ARG_DECL>
+ class access::container_traits<vector<V, A LIBODB_VECTOR_ARG_USE> >
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = true;
+
+ typedef vector<V, A> container_type;
+
+ typedef V value_type;
+ typedef typename container_type::size_type index_type;
+
+ typedef smart_ordered_functions<index_type, value_type> functions;
+ typedef ordered_functions<index_type, value_type> dumb_functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+
+ // Now that this container is persistent, start tracking changes.
+ //
+ c._start ();
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ // Stop tracking changes.
+ //
+ c._stop ();
+
+ // Load.
+ //
+ c.clear ();
+ while (more)
+ {
+ index_type dummy;
+ c.push_back (value_type ());
+ more = f.select (dummy, c.modify_back ());
+ }
+
+ // Start tracking changes.
+ //
+ c._start ();
+ }
+
+ static bool
+ changed (const container_type&);
+
+ static void
+ update (const container_type&, const functions&);
+
+ static void
+ erase (const container_type* c, const functions& f)
+ {
+ f.delete_ (0);
+
+ // Stop tracking changes.
+ //
+ if (c != 0)
+ c->_stop ();
+ }
+
+ // Version of load() for dumb functions. Used to support
+ // inverse members of the container type. The implementation
+ // is identical to the smart one except we don't turn off/on
+ // change tracking.
+ //
+ static void
+ load (container_type& c, bool more, const dumb_functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ index_type dummy;
+ c.push_back (value_type ());
+ more = f.select (dummy, c.modify_back ());
+ }
+ }
+ };
+}
+
+#include <odb/vector-traits.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_VECTOR_TRAITS_HXX
diff --git a/libodb/odb/vector-traits.txx b/libodb/odb/vector-traits.txx
new file mode 100644
index 0000000..6c33876
--- /dev/null
+++ b/libodb/odb/vector-traits.txx
@@ -0,0 +1,100 @@
+// file : odb/vector-traits.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ template <typename V, typename A LIBODB_VECTOR_ARG_DECL>
+ bool access::container_traits<vector<V, A LIBODB_VECTOR_ARG_USE> >::
+ changed (const container_type& c)
+ {
+ // Because modifications can cancel each other (e.g., push and pop),
+ // it is tricky to keep track of whether there are any changes in
+ // the container. Instead, we are just going to examine each element
+ // just like update().
+ //
+
+ // We should either be tracking or summarily changed.
+ //
+ if (c._tracking ())
+ {
+ const vector_impl& impl (c._impl ());
+
+ for (std::size_t i (0), n (impl.size ()); i < n; ++i)
+ {
+ if (impl.state (i) != vector_impl::state_unchanged)
+ return true;
+ }
+ }
+ else
+ return true;
+
+ return false;
+ }
+
+ template <typename V, typename A LIBODB_VECTOR_ARG_DECL>
+ void access::container_traits<vector<V, A LIBODB_VECTOR_ARG_USE> >::
+ update (const container_type& c, const functions& f)
+ {
+ bool u (false); // Updated flag.
+
+ if (c._tracking ())
+ {
+ const vector_impl& impl (c._impl ());
+
+ for (std::size_t i (0), n (impl.size ()); i < n; ++i)
+ {
+ vector_impl::element_state_type s (impl.state (i));
+
+ switch (s)
+ {
+ case vector_impl::state_unchanged:
+ {
+ break;
+ }
+ case vector_impl::state_inserted:
+ {
+ f.insert (i, c[static_cast<index_type> (i)]);
+ u = u || true;
+ break;
+ }
+ case vector_impl::state_updated:
+ {
+ f.update (i, c[static_cast<index_type> (i)]);
+ u = u || true;
+ break;
+ }
+ case vector_impl::state_erased:
+ {
+ f.delete_ (i); // Delete from i onwards.
+ u = u || true;
+ break;
+ }
+ }
+
+ // We delete all trailing elements in one go.
+ //
+ if (s == vector_impl::state_erased)
+ break;
+ }
+ }
+ else
+ {
+ // Fall back to delete all/insert all.
+ //
+ f.delete_ (0);
+
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+
+ u = true;
+ }
+
+ // Arm the rollback callback and (re)start change tracking.
+ //
+ if (u)
+ {
+ c._arm (transaction::current ());
+ c._start ();
+ }
+ }
+}
diff --git a/libodb/odb/vector.hxx b/libodb/odb/vector.hxx
new file mode 100644
index 0000000..3fe7d8a
--- /dev/null
+++ b/libodb/odb/vector.hxx
@@ -0,0 +1,635 @@
+// file : odb/vector.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_VECTOR_HXX
+#define ODB_VECTOR_HXX
+
+#include <odb/pre.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <vector>
+#include <iterator>
+#include <cstddef> // std::ptrdiff_t
+
+#ifdef ODB_CXX11
+# include <utility> // std::move, std::forward
+# ifdef ODB_CXX11_INITIALIZER_LIST
+# include <initializer_list>
+# endif
+#endif
+
+#include <odb/vector-impl.hxx>
+
+// Because both std::vector and odb::vector are called 'vector' (who
+// cares about namespace qualifications, right?), Sun CC complains
+// with a bogus "Ambiguous partial specialization" error. A really
+// hideous workaround for this bug is to to add a dummy third template
+// argument (with a default value).
+//
+#ifdef __SUNPRO_CC
+# define LIBODB_VECTOR_ARG_DEFAULT ,int = 0
+# define LIBODB_VECTOR_ARG_DECL ,int DUMMY
+# define LIBODB_VECTOR_ARG_USE ,DUMMY
+#else
+# define LIBODB_VECTOR_ARG_DEFAULT
+# define LIBODB_VECTOR_ARG_DECL
+# define LIBODB_VECTOR_ARG_USE
+#endif
+
+namespace odb
+{
+ // An std::vector-like container that keeps track of changes.
+ //
+ // Note that the style and order of definitions is as appears
+ // in the standard.
+ //
+ template <class V, class I>
+ class vector_iterator;
+
+ template <class T, class A = std::allocator<T> LIBODB_VECTOR_ARG_DEFAULT>
+ class vector: public vector_base
+ {
+ public:
+ typedef std::vector<T, A> base_vector_type;
+ typedef typename base_vector_type::iterator base_iterator_type;
+ typedef typename base_vector_type::reverse_iterator
+ base_reverse_iterator_type;
+ // types:
+ //
+ typedef typename base_vector_type::reference reference;
+ typedef typename base_vector_type::const_reference const_reference;
+ typedef vector_iterator<vector, base_iterator_type> iterator;
+ typedef typename base_vector_type::const_iterator const_iterator;
+ typedef typename base_vector_type::size_type size_type;
+ typedef typename base_vector_type::difference_type difference_type;
+ typedef T value_type;
+ typedef A allocator_type;
+ typedef typename base_vector_type::pointer pointer;
+ typedef typename base_vector_type::const_pointer const_pointer;
+ // No non-const reverse iterator support for Sun CC with non-standard STL.
+ //
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+ typedef vector_iterator<vector, base_reverse_iterator_type>
+ reverse_iterator;
+#endif
+ typedef typename base_vector_type::const_reverse_iterator
+ const_reverse_iterator;
+ // construct/copy/destroy:
+ //
+ explicit vector(const A& a = A()): v_ (a) {}
+ explicit vector(size_type n): v_ (n) {} // C++11
+ vector(size_type n, const T& v, const A& a = A()): v_ (n, v, a) {}
+ template <class I>
+ vector(I f, I l, const A& a = A()) : v_ (f, l, a) {}
+ vector(const vector& x): vector_base (x), v_ (x.v_) {}
+ // ~vector() {}
+ vector& operator=(const vector&);
+ template <class I>
+ void assign(I f, I l);
+ void assign(size_type n, const T& u);
+ allocator_type get_allocator() const /*noexcept*/
+ {return v_.get_allocator ();}
+
+#ifdef ODB_CXX11
+ vector(vector&& x) noexcept
+ : vector_base (std::move (x)), v_ (std::move (x.v_)) {}
+
+ vector(const vector& x, const A& a): vector_base (x), v_ (x.v_, a) {}
+ vector(vector&& x, const A& a)
+ : vector_base (std::move (x)), v_ (std::move (x.v_), a) {}
+
+ // Note: noexcept is not specified since it can throw while reallocating
+ // impl_.
+ //
+ vector& operator=(vector&&);
+#ifdef ODB_CXX11_INITIALIZER_LIST
+ vector(std::initializer_list<T> il, const A& a = A()): v_ (il, a) {}
+ vector& operator=(std::initializer_list<T>);
+ void assign(std::initializer_list<T>);
+#endif
+#endif
+
+ // iterators: (all /*noexcept*/)
+ //
+ iterator begin() {return iterator (this, v_.begin ());}
+ iterator end() {return iterator (this, v_.end ());}
+ const_iterator begin() const {return v_.begin ();}
+ const_iterator end() const {return v_.end ();}
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+ reverse_iterator rbegin() {return reverse_iterator (this, v_.rbegin ());}
+ reverse_iterator rend() {return reverse_iterator (this, v_.rend ());}
+#endif
+ const_reverse_iterator rbegin() const {return v_.rbegin ();}
+ const_reverse_iterator rend() const {return v_.rend ();}
+
+ // Return standard vector iterators. The begin() functions mark all
+ // the elements as modified.
+ //
+ base_iterator_type mbegin ();
+ base_iterator_type mend () {return v_.end ();}
+ base_reverse_iterator_type mrbegin ();
+ base_reverse_iterator_type mrend () {return v_.rend ();}
+
+#ifdef ODB_CXX11
+ const_iterator cbegin() const {return v_.cbegin ();}
+ const_iterator cend() const {return v_.cend ();}
+ const_reverse_iterator crbegin() const {return v_.crbegin ();}
+ const_reverse_iterator crend() const {return v_.crend ();}
+#endif
+
+ // capacity:
+ //
+ size_type size() const /*noexcept*/ {return v_.size ();}
+ size_type max_size() const /*noexcept*/ {return v_.max_size ();}
+ void resize(size_type); // C++11
+ void resize(size_type, const T&);
+ size_type capacity() const /*noexcept*/ {return v_.capacity ();}
+ bool empty() const /*noexcept*/ {return v_.empty ();}
+ void reserve(size_type);
+
+#ifdef ODB_CXX11
+ void shrink_to_fit();
+#endif
+
+ // element access:
+ //
+ //reference operator[](size_type n);
+ reference modify(size_type n);
+ const_reference operator[](size_type n) const {return v_[n];}
+ //reference at(size_type n);
+ reference modify_at(size_type n);
+ const_reference at(size_type n) const {return v_.at (n);}
+ //reference front();
+ reference modify_front();
+ const_reference front() const {return v_.front ();}
+ //reference back();
+ reference modify_back();
+ const_reference back() const {return v_.back ();}
+
+ // data access:
+ //
+#ifdef ODB_CXX11
+ //T* data() noexcept;
+ T* modify_data() /*noexcept*/;
+ const T* data() const /*noexcept*/ {return v_.data ();}
+#endif
+
+ // modifiers:
+ //
+ void push_back(const T& x);
+ void pop_back();
+ iterator insert(iterator position, const T& x);
+ void insert(iterator position, size_type n, const T& x);
+ template <class I>
+ void insert(iterator position, I first, I last);
+ iterator erase(iterator position);
+ iterator erase(iterator first, iterator last);
+ void swap(vector&);
+ void clear() /*noexcept*/;
+
+#ifdef ODB_CXX11
+ // In C++11 all modifiers use const_iterator instead of iterator
+ // to represent position. However, some standard libraries (notably
+ // GCC's) still use iterator and so we will do that as well, for now.
+ //
+ void push_back(T&& x);
+ iterator insert(iterator position, T&& x);
+
+#ifdef ODB_CXX11_VARIADIC_TEMPLATE
+ template <class... Args>
+ void emplace_back(Args&&... args);
+ template <class... Args>
+ iterator emplace(iterator position, Args&&... args);
+#endif
+#endif
+
+ // Interfacing with the base vector.
+ //
+ vector (const base_vector_type& x): v_ (x) {}
+ vector& operator= (const base_vector_type&);
+ operator const base_vector_type& () const {return v_;}
+ base_vector_type& base () {return v_;}
+ const base_vector_type& base () const {return v_;}
+
+#ifdef ODB_CXX11
+ vector (base_vector_type&& x): v_ (std::move (x)) {}
+ vector& operator= (base_vector_type&&);
+#endif
+
+ // Change tracking (the rest comes from vector_base).
+ //
+ public:
+ void
+ _start () const {impl_.start (v_.size ());}
+
+ private:
+ base_vector_type v_;
+ };
+
+ namespace core
+ {
+ using odb::vector;
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator==(const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x.base () == y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator==(const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const std::vector<T,A>& y)
+ {return x.base () == y;}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator==(const std::vector<T,A>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x == y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator< (const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x.base () < y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator<(const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const std::vector<T,A>& y)
+ {return x.base () < y;}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator<(const std::vector<T,A>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x < y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator!=(const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x.base () != y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator!=(const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const std::vector<T,A>& y)
+ {return x.base () != y;}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator!=(const std::vector<T,A>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x != y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator> (const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x.base () > y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator>=(const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x.base () >= y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator>=(const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const std::vector<T,A>& y)
+ {return x.base () >= y;}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator>=(const std::vector<T,A>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x >= y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator<=(const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x.base () <= y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator<=(const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const std::vector<T,A>& y)
+ {return x.base () <= y;}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator<=(const std::vector<T,A>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x <= y.base ();}
+
+ template <class V, class I>
+ class vector_iterator
+ {
+ public:
+ typedef V vector_type;
+ typedef I base_iterator_type;
+ typedef typename vector_type::const_iterator const_iterator_type;
+
+ // Sun CC with non-standard STL does not have iterator_traits.
+ //
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+ typedef std::iterator_traits<base_iterator_type> base_iterator_traits;
+
+ typedef typename base_iterator_traits::value_type value_type;
+ typedef typename base_iterator_traits::difference_type difference_type;
+ typedef typename base_iterator_traits::pointer pointer;
+ typedef typename base_iterator_traits::reference reference;
+ typedef typename base_iterator_traits::iterator_category iterator_category;
+#else
+ // Base iterator is just a pointer.
+ //
+ typedef typename vector_type::value_type value_type;
+ typedef typename vector_type::pointer pointer;
+ typedef typename vector_type::reference reference;
+ typedef std::random_access_iterator_tag iterator_category;
+ typedef std::ptrdiff_t difference_type;
+#endif
+
+ typedef typename vector_type::size_type size_type;
+ typedef typename vector_type::const_reference const_reference;
+ typedef typename vector_type::const_pointer const_pointer;
+
+ vector_iterator (): v_ (0), i_ () {}
+ vector_iterator (vector_type* v, const base_iterator_type& i)
+ : v_ (v), i_ (i) {}
+ operator const_iterator_type () const {return i_;}
+ base_iterator_type base () const {return i_;}
+ vector_type* vector () const {return v_;}
+
+ // Note: const_{reference,pointer}.
+ //
+ const_reference operator* () const {return *i_;}
+ const_pointer operator-> () const {return i_.operator -> ();}
+ const_reference operator[] (difference_type n) const {return i_[n];}
+
+ // Modifiers.
+ //
+ // Buggy Sun CC cannot have them out of class.
+ //
+ reference modify () const
+ {
+ if (v_->_tracking ())
+ v_->_impl ().modify (
+ static_cast<size_type> (i_ - v_->base ().begin ()));
+ return *i_;
+ }
+
+ reference modify (difference_type n) const
+ {
+ if (v_->_tracking ())
+ v_->_impl ().modify (
+ static_cast<size_type> (i_ - v_->base ().begin () + n));
+ return i_[n];
+ }
+
+ vector_iterator& operator++ () {++i_; return *this;}
+ vector_iterator operator++ (int) {return vector_iterator (v_, i_++);}
+ vector_iterator& operator-- () {--i_; return *this;}
+ vector_iterator operator-- (int) {return vector_iterator (v_, i_--);}
+
+ vector_iterator operator+ (difference_type n) const
+ {return vector_iterator (v_, i_ + n);}
+ vector_iterator& operator+= (difference_type n) {i_ += n; return *this;}
+ vector_iterator operator- (difference_type n) const
+ {return vector_iterator (v_, i_ - n);}
+ vector_iterator& operator-= (difference_type n) {i_ -= n; return *this;}
+
+ // Implementation details.
+ //
+ public:
+ base_iterator_type _base () const {return i_;} // Same as base ().
+
+ private:
+ vector_type* v_;
+ base_iterator_type i_;
+ };
+
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+ template <class V, class J>
+ class vector_iterator<V, std::reverse_iterator<J> >
+ {
+ public:
+ typedef V vector_type;
+ typedef std::reverse_iterator<J> base_iterator_type;
+ typedef typename vector_type::const_reverse_iterator const_iterator_type;
+ typedef std::iterator_traits<base_iterator_type> base_iterator_traits;
+
+ typedef typename vector_type::iterator iterator_type;
+ typedef typename base_iterator_traits::value_type value_type;
+ typedef typename base_iterator_traits::difference_type difference_type;
+ typedef typename base_iterator_traits::pointer pointer;
+ typedef typename base_iterator_traits::reference reference;
+ typedef typename base_iterator_traits::iterator_category iterator_category;
+
+ typedef typename vector_type::size_type size_type;
+ typedef typename vector_type::const_reference const_reference;
+ typedef typename vector_type::const_pointer const_pointer;
+
+ vector_iterator (): v_ (0), i_ () {}
+ explicit vector_iterator (const iterator_type& i)
+ : v_ (i.vector ()), i_ (i.base ()) {}
+ vector_iterator (vector_type* v, const base_iterator_type& i)
+ : v_ (v), i_ (i) {}
+ operator const_iterator_type () const {return i_;}
+ iterator_type base () const {return iterator_type (v_, i_.base ());}
+ base_iterator_type rbase () const {return i_;}
+ vector_type* vector () const {return v_;}
+
+ // Note: const_{reference,pointer}.
+ //
+ const_reference operator* () const {return *i_;}
+ const_pointer operator-> () const {return i_.operator -> ();}
+ const_reference operator[] (difference_type n) const {return i_[n];}
+
+ // Modifiers.
+ //
+ reference modify () const
+ {
+ if (v_->_tracking ())
+ v_->_impl ().modify (
+ static_cast<size_type> (v_->base ().rend () - i_ - 1));
+ return *i_;
+ }
+
+ reference modify (difference_type n) const
+ {
+ if (v_->_tracking ())
+ // Note: going in the opposite direction.
+ v_->_impl ().modify (
+ static_cast<size_type> (v_->base ().rend () - i_ - 1 - n));
+ return i_[n];
+ }
+
+ vector_iterator& operator++ () {++i_; return *this;}
+ vector_iterator operator++ (int) {return vector_iterator (v_, i_++);}
+ vector_iterator& operator-- () {--i_; return *this;}
+ vector_iterator operator-- (int) {return vector_iterator (v_, i_--);}
+
+ vector_iterator operator+ (difference_type n) const
+ {return vector_iterator (v_, i_ + n);}
+ vector_iterator& operator+= (difference_type n) {i_ += n; return *this;}
+ vector_iterator operator- (difference_type n) const
+ {return vector_iterator (v_, i_ - n);}
+ vector_iterator& operator-= (difference_type n) {i_ -= n; return *this;}
+
+ // Implementation details.
+ //
+ public:
+ base_iterator_type _base () const {return i_;} // Same as rbase().
+
+ private:
+ vector_type* v_;
+ base_iterator_type i_;
+ };
+#endif // _RWSTD_NO_CLASS_PARTIAL_SPEC
+
+ // operator==
+ //
+ template <class V, class I>
+ inline bool
+ operator== (const vector_iterator<V, I>& x, const vector_iterator<V, I>& y)
+ {return x._base () == y._base ();}
+
+ template <class V, class I>
+ inline bool
+ operator== (const vector_iterator<V, I>& x,
+ const typename vector_iterator<V, I>::const_iterator_type& y)
+ {return x._base () == y;}
+
+ template <class V, class I>
+ inline bool
+ operator== (const typename vector_iterator<V, I>::const_iterator_type& x,
+ const vector_iterator<V, I>& y)
+ {return x == y._base ();}
+
+ // operator<
+ //
+ template <class V, class I>
+ inline bool
+ operator< (const vector_iterator<V, I>& x, const vector_iterator<V, I>& y)
+ {return x._base () < y._base ();}
+
+ template <class V, class I>
+ inline bool
+ operator< (const vector_iterator<V, I>& x,
+ const typename vector_iterator<V, I>::const_iterator_type& y)
+ {return x._base () < y;}
+
+ template <class V, class I>
+ inline bool
+ operator< (const typename vector_iterator<V, I>::const_iterator_type& x,
+ const vector_iterator<V, I>& y)
+ {return x < y._base ();}
+
+ // operator!=
+ //
+ template <class V, class I>
+ inline bool
+ operator!= (const vector_iterator<V, I>& x, const vector_iterator<V, I>& y)
+ {return x._base () != y._base ();}
+
+ template <class V, class I>
+ inline bool
+ operator!= (const vector_iterator<V, I>& x,
+ const typename vector_iterator<V, I>::const_iterator_type& y)
+ {return x._base () != y;}
+
+ template <class V, class I>
+ inline bool
+ operator!= (const typename vector_iterator<V, I>::const_iterator_type& x,
+ const vector_iterator<V, I>& y)
+ {return x != y._base ();}
+
+ // operator>
+ //
+ template <class V, class I>
+ inline bool
+ operator> (const vector_iterator<V, I>& x, const vector_iterator<V, I>& y)
+ {return x._base () > y._base ();}
+
+ template <class V, class I>
+ inline bool
+ operator> (const vector_iterator<V, I>& x,
+ const typename vector_iterator<V, I>::const_iterator_type& y)
+ {return x._base () > y;}
+
+ template <class V, class I>
+ inline bool
+ operator> (const typename vector_iterator<V, I>::const_iterator_type& x,
+ const vector_iterator<V, I>& y)
+ {return x > y._base ();}
+
+ // operator>=
+ //
+ template <class V, class I>
+ inline bool
+ operator>= (const vector_iterator<V, I>& x, const vector_iterator<V, I>& y)
+ {return x._base () >= y._base ();}
+
+ template <class V, class I>
+ inline bool
+ operator>= (const vector_iterator<V, I>& x,
+ const typename vector_iterator<V, I>::const_iterator_type& y)
+ {return x._base () >= y;}
+
+ template <class V, class I>
+ inline bool
+ operator>= (const typename vector_iterator<V, I>::const_iterator_type& x,
+ const vector_iterator<V, I>& y)
+ {return x >= y._base ();}
+
+ // operator<=
+ //
+ template <class V, class I>
+ inline bool
+ operator<= (const vector_iterator<V, I>& x, const vector_iterator<V, I>& y)
+ {return x._base () <= y._base ();}
+
+ template <class V, class I>
+ inline bool
+ operator<= (const vector_iterator<V, I>& x,
+ const typename vector_iterator<V, I>::const_iterator_type& y)
+ {return x._base () <= y;}
+
+ template <class V, class I>
+ inline bool
+ operator<= (const typename vector_iterator<V, I>::const_iterator_type& x,
+ const vector_iterator<V, I>& y)
+ {return x <= y._base ();}
+
+ // operator-
+ //
+ template <class V, class I>
+ inline typename vector_iterator<V, I>::difference_type
+ operator-(const vector_iterator<V, I>& x, const vector_iterator<V, I>& y)
+ {return x._base () - y._base ();}
+
+ template <class V, class I>
+ inline typename vector_iterator<V, I>::difference_type
+ operator-(const vector_iterator<V, I>& x,
+ const typename vector_iterator<V, I>::const_iterator_type& y)
+ {return x._base () - y;}
+
+ template <class V, class I>
+ inline typename vector_iterator<V, I>::difference_type
+ operator-(const typename vector_iterator<V, I>::const_iterator_type& x,
+ const vector_iterator<V, I>& y)
+ {return x - y._base ();}
+
+ // operator+
+ //
+ template <class V, class I>
+ inline vector_iterator<V, I>
+ operator+(typename vector_iterator<V, I>::difference_type n,
+ const vector_iterator<V, I>& x)
+ {return vector_iterator<V, I> (x.vector (), n + x._base ());}
+}
+
+namespace std
+{
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void swap(odb::vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ odb::vector<T,A LIBODB_VECTOR_ARG_USE>& y) {x.swap (y);}
+}
+
+#include <odb/vector.ixx>
+
+#include <odb/vector-traits.hxx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_VECTOR_HXX
diff --git a/libodb/odb/vector.ixx b/libodb/odb/vector.ixx
new file mode 100644
index 0000000..230b187
--- /dev/null
+++ b/libodb/odb/vector.ixx
@@ -0,0 +1,359 @@
+// file : odb/vector.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ // construct/copy/destroy:
+ //
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline vector<T, A LIBODB_VECTOR_ARG_USE>&
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ operator= (const vector& x)
+ {
+ v_ = x.v_;
+ if (_tracking ())
+ impl_.assign (v_.size ());
+ return *this;
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ template <class I>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ assign (I f, I l)
+ {
+ v_.assign (f, l);
+ if (_tracking ())
+ impl_.assign (v_.size ());
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ assign (size_type n, const T& u)
+ {
+ v_.assign (n, u);
+ if (_tracking ())
+ impl_.assign (n);
+ }
+
+#ifdef ODB_CXX11
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline vector<T, A LIBODB_VECTOR_ARG_USE>&
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ operator=(vector&& x)
+ {
+ v_ = std::move (x.v_);
+ if (_tracking ())
+ impl_.assign (v_.size ());
+ return *this;
+ }
+
+#ifdef ODB_CXX11_INITIALIZER_LIST
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline vector<T, A LIBODB_VECTOR_ARG_USE>&
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ operator= (std::initializer_list<T> il)
+ {
+ v_ = il;
+ if (_tracking ())
+ impl_.assign (v_.size ());
+ return *this;
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ assign (std::initializer_list<T> il)
+ {
+ v_.assign (il);
+ if (_tracking ())
+ impl_.assign (v_.size ());
+ }
+#endif
+#endif
+
+ // iterators:
+ //
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::base_iterator_type
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ mbegin ()
+ {
+ if (_tracking ())
+ impl_.modify (0, v_.size ());
+ return v_.begin ();
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline
+ typename vector<T, A LIBODB_VECTOR_ARG_USE>::base_reverse_iterator_type
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ mrbegin ()
+ {
+ if (_tracking ())
+ impl_.modify (0, v_.size ());
+ return v_.rbegin ();
+ }
+
+ // capacity:
+ //
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ resize (size_type n)
+ {
+ v_.resize (n);
+ if (_tracking ())
+ impl_.resize (n);
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ resize (size_type n, const T& c)
+ {
+ v_.resize (n, c);
+ if (_tracking ())
+ impl_.resize (n);
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ reserve (size_type n)
+ {
+ v_.reserve (n);
+ if (_tracking ())
+ impl_.reserve (n);
+ }
+
+#ifdef ODB_CXX11
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ shrink_to_fit ()
+ {
+ v_.shrink_to_fit ();
+ impl_.shrink_to_fit ();
+ }
+#endif
+
+ // element access:
+ //
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::reference
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ modify (size_type n)
+ {
+ reference r (v_[n]);
+ if (_tracking ())
+ impl_.modify (n);
+ return r;
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::reference
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ modify_at (size_type n)
+ {
+ reference r (v_.at (n));
+ if (_tracking ())
+ impl_.modify (n);
+ return r;
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::reference
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ modify_front ()
+ {
+ reference r (v_.front ());
+ if (_tracking ())
+ impl_.modify (0);
+ return r;
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::reference
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ modify_back ()
+ {
+ reference r (v_.back ());
+ if (_tracking ())
+ impl_.modify (v_.size () - 1);
+ return r;
+ }
+
+#ifdef ODB_CXX11
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline T* vector<T, A LIBODB_VECTOR_ARG_USE>::
+ modify_data() /*noexcept*/
+ {
+ if (_tracking ())
+ impl_.modify (0, v_.size ());
+ return v_.data ();
+ }
+#endif
+
+ // modifiers:
+ //
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ push_back (const T& x)
+ {
+ v_.push_back (x);
+ if (_tracking ())
+ impl_.push_back ();
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ pop_back ()
+ {
+ v_.pop_back ();
+ if (_tracking ())
+ impl_.pop_back ();
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::iterator
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ insert (iterator p, const T& x)
+ {
+ if (_tracking ())
+ impl_.insert (static_cast<size_type> (p.base () - v_.begin ()));
+ return iterator (this, v_.insert (p.base (), x));
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ insert (iterator p, size_type n, const T& x)
+ {
+ if (_tracking ())
+ impl_.insert (static_cast<size_type> (p.base () - v_.begin ()), n);
+ v_.insert (p.base (), n, x);
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ template <class I>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ insert (iterator p, I f, I l)
+ {
+ size_type i, n;
+ if (_tracking ())
+ {
+ i = static_cast<size_type> (p.base () - v_.begin ());
+ n = v_.size ();
+ }
+
+ v_.insert (p.base (), f, l);
+
+ if (_tracking ())
+ impl_.insert (i, v_.size () - n);
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::iterator
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ erase (iterator p)
+ {
+ if (_tracking ())
+ impl_.erase (static_cast<size_type> (p.base () - v_.begin ()));
+ return iterator (this, v_.erase (p.base ()));
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::iterator
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ erase (iterator f, iterator l)
+ {
+ if (_tracking ())
+ impl_.erase (static_cast<size_type> (f.base () - v_.begin ()),
+ static_cast<size_type> (l - f));
+ return iterator (this, v_.erase (f.base (), l.base ()));
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ swap (vector& x)
+ {
+ v_.swap (x.v_);
+ vector_base::swap (x);
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ clear ()
+ {
+ v_.clear ();
+ if (_tracking ())
+ impl_.clear ();
+ }
+
+#ifdef ODB_CXX11
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ push_back(T&& x)
+ {
+ v_.push_back (std::move (x));
+ if (_tracking ())
+ impl_.push_back ();
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::iterator
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ insert (iterator p, T&& x)
+ {
+ base_iterator_type r (v_.insert (p.base (), std::move (x)));
+ if (_tracking ())
+ impl_.insert (static_cast<size_type> (r - v_.begin ()));
+ return iterator (this, r);
+ }
+
+#ifdef ODB_CXX11_VARIADIC_TEMPLATE
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ template <class... Args>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ emplace_back (Args&&... args)
+ {
+ v_.push_back (std::forward<Args> (args)...);
+ if (_tracking ())
+ impl_.push_back ();
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ template <class... Args>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::iterator
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ emplace (iterator p, Args&&... args)
+ {
+ base_iterator_type r (
+ v_.emplace (p.base (), std::forward<Args> (args)...));
+ if (_tracking ())
+ impl_.insert (static_cast<size_type> (r - v_.begin ()));
+ return iterator (this, r);
+ }
+#endif
+#endif
+
+ // Interfacing with base vector.
+ //
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline vector<T, A LIBODB_VECTOR_ARG_USE>&
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ operator= (const base_vector_type& x)
+ {
+ v_ = x;
+ if (_tracking ())
+ impl_.assign (v_.size ());
+ return *this;
+ }
+
+#ifdef ODB_CXX11
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline vector<T, A LIBODB_VECTOR_ARG_USE>&
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ operator= (base_vector_type&& x)
+ {
+ v_ = std::move (x);
+ if (_tracking ())
+ impl_.assign (v_.size ());
+ return *this;
+ }
+#endif
+}
diff --git a/libodb/odb/version.hxx b/libodb/odb/version.hxx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libodb/odb/version.hxx
diff --git a/libodb/odb/version.hxx.in b/libodb/odb/version.hxx.in
new file mode 100644
index 0000000..178c8cc
--- /dev/null
+++ b/libodb/odb/version.hxx.in
@@ -0,0 +1,65 @@
+// file : odb/version.hxx.in
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef LIBODB_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 LIBODB_VERSION_FULL $libodb.version.project_number$ULL
+#define LIBODB_VERSION_STR "$libodb.version.project$"
+#define LIBODB_VERSION_ID "$libodb.version.project_id$"
+
+#define LIBODB_VERSION_MAJOR $libodb.version.major$
+#define LIBODB_VERSION_MINOR $libodb.version.minor$
+#define LIBODB_VERSION_PATCH $libodb.version.patch$
+
+#define LIBODB_PRE_RELEASE $libodb.version.pre_release$
+
+#define LIBODB_SNAPSHOT $libodb.version.snapshot_sn$ULL
+#define LIBODB_SNAPSHOT_ID "$libodb.version.snapshot_id$"
+
+
+// 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
+//
+
+// ODB interface version: minor, major, and alpha/beta versions.
+//
+#define ODB_VERSION 20476
+#define ODB_VERSION_STR "2.5-b.26"
+
+// libodb version: interface version plus the bugfix version.
+//
+#define LIBODB_VERSION 2049976
+
+#endif // LIBODB_VERSION
diff --git a/libodb/odb/view-image.hxx b/libodb/odb/view-image.hxx
new file mode 100644
index 0000000..51f7cc0
--- /dev/null
+++ b/libodb/odb/view-image.hxx
@@ -0,0 +1,36 @@
+// file : odb/view-image.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_VIEW_IMAGE_HXX
+#define ODB_VIEW_IMAGE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+namespace odb
+{
+ // Helper to create a complete image chain for a polymorphic
+ // object hierarchy.
+ //
+ template <typename D, typename R, database_id DB>
+ struct view_object_image: object_traits_impl<D, DB>::image_type
+ {
+ view_object_image () {this->base = &base_;}
+
+ private:
+ // Data member names in the generated image_type never end with
+ // an underscore, so this name shouldn't clash.
+ //
+ view_object_image<typename object_traits_impl<D, DB>::base_type, R, DB>
+ base_;
+ };
+
+ template <typename R, database_id DB>
+ struct view_object_image<R, R, DB>: object_traits_impl<R, DB>::image_type {};
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_VIEW_IMAGE_HXX
diff --git a/libodb/odb/view-result.hxx b/libodb/odb/view-result.hxx
new file mode 100644
index 0000000..601c3b4
--- /dev/null
+++ b/libodb/odb/view-result.hxx
@@ -0,0 +1,231 @@
+// file : odb/view-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_VIEW_RESULT_HXX
+#define ODB_VIEW_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::ptrdiff_t, std::size_t
+#include <iterator> // iterator categories
+#include <utility> // std::move
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+#include <odb/result.hxx>
+#include <odb/pointer-traits.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ template <typename T>
+ class view_result_impl: public result_impl
+ {
+ protected:
+ friend class result<T>;
+ friend class result<const T>;
+ friend class result_iterator<T, class_view>;
+ friend class result_iterator<const T, class_view>;
+
+ // In result_impl, T is always non-const and the same as view_type.
+ //
+ typedef T view_type;
+ typedef odb::view_traits<view_type> view_traits;
+
+ typedef typename view_traits::pointer_type pointer_type;
+ typedef odb::pointer_traits<pointer_type> pointer_traits;
+
+ view_result_impl (odb::connection& conn)
+ : result_impl (conn), begin_ (true), end_ (false), current_ ()
+ {
+ }
+
+ // To make this work with all kinds of pointers (raw, std::auto_ptr,
+ // shared), we need to make sure we don't make any copies of the
+ // pointer on the return path.
+ //
+ pointer_type&
+ current ();
+
+ void
+ release ()
+ {
+ current_ = pointer_type ();
+ guard_.release ();
+ }
+
+ void
+ begin ()
+ {
+ if (begin_)
+ {
+ next ();
+ begin_ = false;
+ }
+ }
+
+ bool
+ end () const
+ {
+ return end_;
+ }
+
+ protected:
+ virtual void
+ load (view_type&) = 0;
+
+ virtual void
+ next () = 0;
+
+ virtual void
+ cache () = 0;
+
+ virtual std::size_t
+ size () = 0;
+
+ protected:
+#ifdef ODB_CXX11
+ void
+ current (pointer_type& p)
+ {
+ current_ = std::move (p);
+ guard_.reset (current_);
+ }
+
+ void
+ current (pointer_type&& p)
+ {
+ current (p);
+ }
+#else
+ void
+ current (pointer_type p)
+ {
+ current_ = p;
+ guard_.reset (current_);
+ }
+#endif
+
+ bool begin_;
+ bool end_;
+
+ private:
+ pointer_type current_;
+ typename pointer_traits::guard guard_;
+ };
+
+ template <typename T>
+ class result_iterator<T, class_view>
+ {
+ public:
+ typedef T value_type;
+ typedef value_type& reference;
+ typedef value_type* pointer;
+ typedef std::ptrdiff_t difference_type;
+ typedef std::input_iterator_tag iterator_category;
+
+ // T can be const T while view_type is always non-const.
+ //
+ typedef typename view_traits<T>::view_type view_type;
+
+ typedef view_result_impl<view_type> result_impl_type;
+
+ public:
+ explicit
+ result_iterator (result_impl_type* res = 0)
+ : res_ (res)
+ {
+ }
+
+ // Input iterator requirements.
+ //
+ public:
+ reference
+ operator* () const
+ {
+ return pointer_traits::get_ref (res_->current ());
+ }
+
+ // Our value_type is already a pointer so return it instead of
+ // a pointer to it (operator-> will just have to go one deeper
+ // in the latter case).
+ //
+ pointer
+ operator-> () const
+ {
+ return pointer_traits::get_ptr (res_->current ());
+ }
+
+ result_iterator&
+ operator++ ()
+ {
+ res_->next ();
+ return *this;
+ }
+
+ result_iterator
+ operator++ (int)
+ {
+ // All non-end iterators for a result object move together.
+ //
+ res_->next ();
+ return *this;
+ }
+
+ public:
+ typedef typename view_traits<T>::pointer_type pointer_type;
+
+ pointer_type
+ load ()
+ {
+#ifdef ODB_CXX11
+ pointer_type r (std::move (res_->current ()));
+#else
+ pointer_type r (res_->current ());
+#endif
+ res_->release ();
+ return r;
+ }
+
+ void
+ load (view_type&);
+
+ public:
+ bool
+ equal (result_iterator j) const
+ {
+ return (res_ ? res_->end () : true) == (j.res_ ? j.res_->end () : true);
+ }
+
+ private:
+ // Use unrestricted pointer traits since that's what is returned by
+ // result_impl.
+ //
+ typedef
+ odb::pointer_traits<typename view_traits<view_type>::pointer_type>
+ pointer_traits;
+
+ result_impl_type* res_;
+ };
+
+ //
+ //
+ template <typename T>
+ class result_base<T, class_view>
+ {
+ public:
+ typedef typename view_traits<T>::pointer_type value_type;
+
+ // T can be const T while view_type is always non-const.
+ //
+ typedef typename view_traits<T>::view_type view_type;
+ typedef view_result_impl<view_type> result_impl_type;
+ };
+}
+
+#include <odb/view-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_VIEW_RESULT_HXX
diff --git a/libodb/odb/view-result.txx b/libodb/odb/view-result.txx
new file mode 100644
index 0000000..5c62253
--- /dev/null
+++ b/libodb/odb/view-result.txx
@@ -0,0 +1,39 @@
+// file : odb/view-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ //
+ // view_result_impl
+ //
+
+ template <typename T>
+ typename view_result_impl<T>::pointer_type&
+ view_result_impl<T>::
+ current ()
+ {
+ if (pointer_traits::null_ptr (current_) && !end_)
+ {
+ pointer_type p (view_traits::create ());
+ view_type& view (pointer_traits::get_ref (p));
+ current (p);
+ load (view);
+ }
+
+ return current_;
+ }
+
+ //
+ // result_iterator
+ //
+
+ template <typename T>
+ void result_iterator<T, class_view>::
+ load (view_type& view)
+ {
+ if (res_->end ())
+ return;
+
+ res_->load (view);
+ }
+}
diff --git a/libodb/odb/wrapper-traits.hxx b/libodb/odb/wrapper-traits.hxx
new file mode 100644
index 0000000..d31425d
--- /dev/null
+++ b/libodb/odb/wrapper-traits.hxx
@@ -0,0 +1,276 @@
+// file : odb/wrapper-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_WRAPPER_TRAITS_HXX
+#define ODB_WRAPPER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <memory> // std::auto_ptr, std::unique_ptr, std::shared_ptr/weak_ptr
+
+#include <odb/nullable.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+#include <odb/details/meta/remove-const.hxx>
+
+namespace odb
+{
+ template <typename T>
+ class wrapper_traits;
+
+ // Sample specialization for raw pointers. It is not enabled by default
+ // since it makes many assumptions that may not always hold true (such
+ // as that instances are allocated with new and freed with delete).
+ // This makes it too dangerous to be enabled unconditionally. If you
+ // need this functionality, you can copy the below code into your
+ // application. Also consider changing it to only specialize for
+ // specific types instead of for any pointer (it will almost always
+ // do the wrong thing for char*).
+ //
+#if 0
+ template <typename T>
+ class wrapper_traits<T*>
+ {
+ public:
+ typedef T wrapped_type;
+ typedef T* wrapper_type;
+
+ // T can be const.
+ //
+ typedef
+ typename details::meta::remove_const<T>::result
+ unrestricted_wrapped_type;
+
+ static const bool null_handler = true;
+ static const bool null_default = false;
+
+ static bool
+ get_null (const wrapper_type& p)
+ {
+ return p == 0;
+ }
+
+ static void
+ set_null (wrapper_type& p)
+ {
+ delete p;
+ p = 0;
+ }
+
+ static const wrapped_type&
+ get_ref (const wrapper_type& p)
+ {
+ return *p;
+ }
+
+ static unrestricted_wrapped_type&
+ set_ref (wrapper_type& p)
+ {
+ if (p == 0)
+ p = new unrestricted_wrapped_type;
+
+ return const_cast<unrestricted_wrapped_type&> (*p);
+ }
+ };
+#endif
+
+ // Specialization for std::auto_ptr.
+ //
+#ifndef ODB_CXX11
+ template <typename T>
+ class wrapper_traits< std::auto_ptr<T> >
+ {
+ public:
+ // T can be const.
+ //
+ typedef T wrapped_type;
+ typedef std::auto_ptr<T> wrapper_type;
+
+ // T can be const.
+ //
+ typedef
+ typename odb::details::meta::remove_const<T>::result
+ unrestricted_wrapped_type;
+
+ static const bool null_handler = true;
+ static const bool null_default = false;
+
+ static bool
+ get_null (const wrapper_type& p)
+ {
+ return p.get () == 0;
+ }
+
+ static void
+ set_null (wrapper_type& p)
+ {
+ p.reset ();
+ }
+
+ static const wrapped_type&
+ get_ref (const wrapper_type& p)
+ {
+ return *p;
+ }
+
+ static unrestricted_wrapped_type&
+ set_ref (wrapper_type& p)
+ {
+ if (p.get () == 0)
+ p.reset (new unrestricted_wrapped_type ());
+
+ return const_cast<unrestricted_wrapped_type&> (*p);
+ }
+ };
+#endif
+
+#ifdef ODB_CXX11
+
+ // Specialization for C++11 std::unique_ptr.
+ //
+ template <typename T, typename D>
+ class wrapper_traits<std::unique_ptr<T, D>>
+ {
+ public:
+ // T can be const.
+ //
+ typedef T wrapped_type;
+ typedef std::unique_ptr<T, D> wrapper_type;
+
+ // T can be const.
+ //
+ typedef
+ typename odb::details::meta::remove_const<T>::result
+ unrestricted_wrapped_type;
+
+ static const bool null_handler = true;
+ static const bool null_default = false;
+
+ static bool
+ get_null (const wrapper_type& p)
+ {
+ return !p;
+ }
+
+ static void
+ set_null (wrapper_type& p)
+ {
+ p.reset ();
+ }
+
+ static const wrapped_type&
+ get_ref (const wrapper_type& p)
+ {
+ return *p;
+ }
+
+ static unrestricted_wrapped_type&
+ set_ref (wrapper_type& p)
+ {
+ if (!p)
+ p.reset (new unrestricted_wrapped_type ());
+
+ return const_cast<unrestricted_wrapped_type&> (*p);
+ }
+ };
+
+ // Specialization for C++11 std::shared_ptr.
+ //
+ template <typename T>
+ class wrapper_traits<std::shared_ptr<T>>
+ {
+ public:
+ typedef T wrapped_type;
+ typedef std::shared_ptr<T> wrapper_type;
+
+ // T can be const.
+ //
+ typedef
+ typename odb::details::meta::remove_const<T>::result
+ unrestricted_wrapped_type;
+
+ static const bool null_handler = true;
+ static const bool null_default = false;
+
+ static bool
+ get_null (const wrapper_type& p)
+ {
+ return !p;
+ }
+
+ static void
+ set_null (wrapper_type& p)
+ {
+ p.reset ();
+ }
+
+ static const wrapped_type&
+ get_ref (const wrapper_type& p)
+ {
+ return *p;
+ }
+
+ static unrestricted_wrapped_type&
+ set_ref (wrapper_type& p)
+ {
+ if (!p)
+ p.reset (new unrestricted_wrapped_type);
+
+ return const_cast<unrestricted_wrapped_type&> (*p);
+ }
+ };
+
+#endif // ODB_CXX11
+
+ // Specialization for odb::nullable.
+ //
+ template <typename T>
+ class wrapper_traits< nullable<T> >
+ {
+ public:
+ // T can be const.
+ //
+ typedef T wrapped_type;
+ typedef nullable<T> wrapper_type;
+
+ // T can be const.
+ //
+ typedef
+ typename odb::details::meta::remove_const<T>::result
+ unrestricted_wrapped_type;
+
+ static const bool null_handler = true;
+ static const bool null_default = true;
+
+ static bool
+ get_null (const wrapper_type& n)
+ {
+ return n.null ();
+ }
+
+ static void
+ set_null (wrapper_type& n)
+ {
+ n.reset ();
+ }
+
+ static const wrapped_type&
+ get_ref (const wrapper_type& n)
+ {
+ return *n;
+ }
+
+ static unrestricted_wrapped_type&
+ set_ref (wrapper_type& n)
+ {
+ if (n.null ())
+ n = unrestricted_wrapped_type ();
+
+ return const_cast<unrestricted_wrapped_type&> (*n);
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_WRAPPER_TRAITS_HXX
diff --git a/libodb/tests/.gitignore b/libodb/tests/.gitignore
new file mode 100644
index 0000000..e54525b
--- /dev/null
+++ b/libodb/tests/.gitignore
@@ -0,0 +1 @@
+driver
diff --git a/libodb/tests/basics/buildfile b/libodb/tests/basics/buildfile
new file mode 100644
index 0000000..d568216
--- /dev/null
+++ b/libodb/tests/basics/buildfile
@@ -0,0 +1,6 @@
+# file : tests/basics/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libs = libodb%lib{odb}
+
+exe{driver}: {hxx cxx}{*} $libs
diff --git a/libodb/tests/basics/driver.cxx b/libodb/tests/basics/driver.cxx
new file mode 100644
index 0000000..57cec1c
--- /dev/null
+++ b/libodb/tests/basics/driver.cxx
@@ -0,0 +1,29 @@
+// file : tests/basics/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Basic test to make sure the library is usable. Functionality testing
+// is done in the odb-tests package.
+
+#include <cassert>
+
+#include <odb/exceptions.hxx>
+#include <odb/transaction.hxx>
+
+using namespace odb;
+
+int
+main ()
+{
+ // Transaction.
+ //
+ {
+ assert (!transaction::has_current ());
+
+ try
+ {
+ transaction::current ();
+ assert(false);
+ }
+ catch (const not_in_transaction&) {}
+ }
+}
diff --git a/libodb/tests/build/.gitignore b/libodb/tests/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb/tests/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb/tests/build/bootstrap.build b/libodb/tests/build/bootstrap.build
new file mode 100644
index 0000000..6ee38db
--- /dev/null
+++ b/libodb/tests/build/bootstrap.build
@@ -0,0 +1,8 @@
+# file : tests/build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = # Unnamed subproject.
+
+using config
+using dist
+using test
diff --git a/libodb/tests/build/root.build b/libodb/tests/build/root.build
new file mode 100644
index 0000000..6c5a90b
--- /dev/null
+++ b/libodb/tests/build/root.build
@@ -0,0 +1,23 @@
+# file : tests/build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+cxx.std = latest
+
+using cxx
+
+hxx{*}: extension = hxx
+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
+
+# Every exe{} in this subproject is by default a test.
+#
+exe{*}: test = true
+
+# Specify the test target for cross-testing.
+#
+test.target = $cxx.target
diff --git a/libodb/tests/buildfile b/libodb/tests/buildfile
new file mode 100644
index 0000000..57588a4
--- /dev/null
+++ b/libodb/tests/buildfile
@@ -0,0 +1,4 @@
+# file : tests/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -build/}
diff --git a/m4/disable-rpath.m4 b/m4/disable-rpath.m4
deleted file mode 100644
index 388f685..0000000
--- a/m4/disable-rpath.m4
+++ /dev/null
@@ -1,24 +0,0 @@
-dnl file : m4/disable-rpath.m4
-dnl license : GNU GPL v3; see accompanying LICENSE file
-dnl
-AC_DEFUN([DISABLE_RPATH],[
-
-AC_MSG_CHECKING([whether to use rpath])
-AC_ARG_ENABLE(
- [rpath],
- [AC_HELP_STRING([--disable-rpath], [patch libtool to not use rpath])],
- [libtool_rpath="$enable_rpath"],
- [libtool_rpath="yes"])
-AC_MSG_RESULT($libtool_rpath)
-
-# Patch libtool to not use rpath if requested.
-#
-AC_CONFIG_COMMANDS(
- [libtool-rpath-patch],
- [if test "$libtool_use_rpath" = "no"; then
- sed < libtool > libtool-2 's/^hardcode_libdir_flag_spec.*$'/'hardcode_libdir_flag_spec=" -D__LIBTOOL_NO_RPATH__ "/'
- mv libtool-2 libtool
- chmod 755 libtool
- fi],
- [libtool_use_rpath=$libtool_rpath])
-])dnl
diff --git a/m4/gcc-plugin.m4 b/m4/gcc-plugin.m4
deleted file mode 100644
index 20a9c75..0000000
--- a/m4/gcc-plugin.m4
+++ /dev/null
@@ -1,133 +0,0 @@
-dnl file : m4/gcc-plugin.m4
-dnl license : GNU GPL v3; see accompanying LICENSE file
-dnl
-dnl GCC_PLUGIN
-dnl
-AC_DEFUN([GCC_PLUGIN], [
-static_plugin=$enable_static
-
-AC_ARG_WITH(
- [gcc-plugin-dir],
- [AC_HELP_STRING([--with-gcc-plugin-dir=DIR], [install ODB plugin into the GCC plugin directory])],
- [gcc_plugin_dir=$withval],
- [gcc_plugin_dir=test])
-
-if test x"$static_plugin" = xyes; then
- gcc_plugin_dir=no
-else
- if test x"$cross_compiling" = xyes; then
- AC_MSG_CHECKING([whether to install into default GCC plugin dir])
- case $gcc_plugin_dir in
- yes)
- AC_MSG_ERROR([GCC plugin directory must be specified explicitly when cross-compiling])
- ;;
- test)
- # We cannot detect the plugin directory since there is no way to
- # run host GCC. So assume no.
- #
- gcc_plugin_dir=no
- ;;
- no)
- ;;
- *)
- # Add the include/ subdirectory of the plugin dir to CPPFLAGS since
- # the plugin headers are normally installed there.
- #
- CPPFLAGS="$CPPFLAGS -I$gcc_plugin_dir/include"
- ;;
- esac
- else
- if test x"$GXX" != xyes; then
- AC_MSG_ERROR([$CXX is not a GNU C++ compiler])
- fi
-
- AC_MSG_CHECKING([whether $CXX supports plugins])
- dir=`$CXX -print-file-name=plugin 2>/dev/null`
-
- if test x"$dir" = xplugin; then
- AC_MSG_RESULT([no])
- AC_MSG_ERROR([$CXX does not support plugins; reconfigure GCC with --enable-plugin])
- else
- AC_MSG_RESULT([yes])
- fi
-
- CPPFLAGS="$CPPFLAGS -I$dir/include"
-
- AC_MSG_CHECKING([whether to install ODB plugin into default GCC plugin directory])
- case $gcc_plugin_dir in
- yes)
- gcc_plugin_dir=$dir
- ;;
- test)
- # Only install into the GCC plugin dir if both GCC and ODB are
- # installed into the same prefix. Testing whether $libdir or
- # $libexecdir is a prefix of the GCC plugin dir is a good
- # approximation.
- #
-
- # Get the expanded values for libdir and libexecdir.
- #
- if test x$exec_prefix = xNONE; then
- if test x$prefix = xNONE; then
- e_exec_prefix=$ac_default_prefix
- else
- e_exec_prefix=$prefix
- fi
- else
- e_exec_prefix=$exec_prefix
- fi
-
- # On some systems GCC is installed into $prefix/lib even though
- # libdir is $prefix/lib64 and libexecdir is $prefix/libexec. To
- # cover this special case, we will also test $prefix/lib.
- #
- e_libdir=`echo "$libdir" | sed "s?^\\\${exec_prefix}?$e_exec_prefix?"`
- e_libexecdir=`echo "$libexecdir" | sed "s?^\\\${exec_prefix}?$e_exec_prefix?"`
- e_libdir32=$e_exec_prefix/lib
-
- # See if either one of them is a prefix of the plugin dir.
- #
- ld_suffix=`echo "$dir" | sed "s?^$e_libdir/*??"`
- led_suffix=`echo "$dir" | sed "s?^$e_libexecdir/*??"`
- l32d_suffix=`echo "$dir" | sed "s?^$e_libdir32/*??"`
-
- if test x$ld_suffix != x$dir -o x$led_suffix != x$dir -o x$l32d_suffix != x$dir; then
- gcc_plugin_dir=$dir
- else
- gcc_plugin_dir=no
- fi
- ;;
- *)
- ;;
- esac
- fi
-
- if test x"$gcc_plugin_dir" != xno; then
- AC_MSG_RESULT([yes])
- else
- AC_MSG_RESULT([no])
- fi
-fi
-
-AC_MSG_CHECKING([for GCC plugin headers])
-
-CXX_LIBTOOL_LINK_IFELSE([
-AC_LANG_SOURCE([
-#include <bversion.h>
-
-#ifndef BUILDING_GCC_MAJOR
-# error no BUILDING_GCC_MAJOR in bversion.h
-#endif
-
-int main () {}
-])],
-[gcc_plugin_headers=yes],
-[gcc_plugin_headers=no])
-
-if test x"$gcc_plugin_headers" = xyes; then
- AC_MSG_RESULT([yes])
-else
- AC_MSG_RESULT([no])
- AC_MSG_ERROR([GCC plugin headers not found; consider installing GCC plugin development package])
-fi
-])dnl
diff --git a/m4/libcutl.m4 b/m4/libcutl.m4
deleted file mode 100644
index 7275ba2..0000000
--- a/m4/libcutl.m4
+++ /dev/null
@@ -1,81 +0,0 @@
-dnl file : m4/libcutl.m4
-dnl license : MIT; see accompanying LICENSE file
-dnl
-dnl LIBCUTL([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
-dnl
-dnl
-AC_DEFUN([LIBCUTL], [
-libcutl_found=no
-
-AC_ARG_WITH(
- [libcutl],
- [AC_HELP_STRING([--with-libcutl=DIR],[location of libcutl build directory])],
- [libcutl_dir=${withval}],
- [libcutl_dir=])
-
-AC_MSG_CHECKING([for libcutl])
-
-# If libcutl_dir was given, add the necessary preprocessor and linker flags.
-#
-if test x"$libcutl_dir" != x; then
- save_CPPFLAGS="$CPPFLAGS"
- save_LDFLAGS="$LDFLAGS"
-
- AS_SET_CATFILE([abs_libcutl_dir], [$ac_pwd], [$libcutl_dir])
-
- CPPFLAGS="$CPPFLAGS -I$abs_libcutl_dir"
- LDFLAGS="$LDFLAGS -L$abs_libcutl_dir/cutl"
-fi
-
-save_LIBS="$LIBS"
-LIBS="-lcutl $LIBS"
-
-CXX_LIBTOOL_LINK_IFELSE([
-AC_LANG_SOURCE([
-#include <cutl/exception.hxx>
-
-void
-f ()
-{
-}
-
-const char*
-g ()
-{
- try
- {
- f ();
- }
- catch (const cutl::exception& e)
- {
- return e.what ();
- }
- return 0;
-}
-
-int
-main ()
-{
- const char* m (g ());
- return m != 0;
-}
-])],
-[libcutl_found=yes])
-
-if test x"$libcutl_found" = xno; then
- LIBS="$save_LIBS"
-
- if test x"$libcutl_dir" != x; then
- CPPFLAGS="$save_CPPFLAGS"
- LDFLAGS="$save_LDFLAGS"
- fi
-fi
-
-if test x"$libcutl_found" = xyes; then
- AC_MSG_RESULT([yes])
- $1
-else
- AC_MSG_RESULT([no])
- $2
-fi
-])dnl
diff --git a/m4/libtool-link.m4 b/m4/libtool-link.m4
deleted file mode 100644
index 302639f..0000000
--- a/m4/libtool-link.m4
+++ /dev/null
@@ -1,45 +0,0 @@
-dnl file : m4/libtool-link.m4
-dnl license : GNU GPL v2; see accompanying LICENSE file
-dnl
-dnl
-dnl CXX_LIBTOOL_LINK_IFELSE (input, [action-if-true], [action-if-false])
-dnl
-dnl Similar to AC_LINK_IFELSE except it uses libtool to perform the
-dnl linking and it does this using the C++ compiler.
-dnl
-AC_DEFUN([CXX_LIBTOOL_LINK_IFELSE],[
-AC_LANG_SAVE
-AC_LANG(C++)
-
-if test -d .libs; then
- delete_libs_dir=no
-else
- delete_libs_dir=yes
-fi
-
-AC_COMPILE_IFELSE([$1],
-[
- ac_try='./libtool --tag=CXX --mode=link $CXX -no-install $CXXFLAGS $LDFLAGS -o conftest conftest.$OBJEXT $LIBS >&AS_MESSAGE_LOG_FD'
- if _AC_DO_VAR(ac_try); then
- libtool_link_ok=yes
- else
- libtool_link_ok=no
- fi
-],
-[
- libtool_link_ok=no
-])
-
-if test x"$delete_libs_dir" = xyes; then
- rm -rf .libs
-fi
-
-if test x"$libtool_link_ok" = xyes; then
-[$2]
-:
-else
-[$3]
-:
-fi
-
-AC_LANG_RESTORE])dnl
diff --git a/makefile b/makefile
deleted file mode 100644
index 92ba3e6..0000000
--- a/makefile
+++ /dev/null
@@ -1,34 +0,0 @@
-# file : makefile
-# license : GNU GPL v3; see accompanying LICENSE file
-
-include $(dir $(lastword $(MAKEFILE_LIST)))build/bootstrap.make
-
-dirs := odb doc
-
-default := $(out_base)/
-dist := $(out_base)/.dist
-clean := $(out_base)/.clean
-
-$(default): $(addprefix $(out_base)/,$(addsuffix /,$(dirs)))
-
-$(dist): export dirs := $(dirs)
-$(dist): export docs := GPLv3 LICENSE README NEWS version
-$(dist): data_dist := INSTALL
-$(dist): exec_dist := bootstrap
-$(dist): export extra_dist := $(data_dist) $(exec_dist)
-$(dist): export version = $(shell cat $(src_root)/version)
-
-$(dist): $(addprefix $(out_base)/,$(addsuffix /.dist,$(dirs)))
- $(call dist-data,$(docs) $(data_dist))
- $(call dist-exec,$(exec_dist))
- $(call dist-dir,m4)
- $(call meta-automake)
- $(call meta-autoconf)
-
-$(clean): $(addprefix $(out_base)/,$(addsuffix /.clean,$(dirs)))
-
-$(call include,$(bld_root)/dist.make)
-$(call include,$(bld_root)/meta/automake.make)
-$(call include,$(bld_root)/meta/autoconf.make)
-
-$(foreach d,$(dirs),$(call import,$(src_base)/$d/makefile))
diff --git a/manifest b/manifest
deleted file mode 100644
index e289c3f..0000000
--- a/manifest
+++ /dev/null
@@ -1,24 +0,0 @@
-: 1
-name: odb
-version: 2.5.0-b.20.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: c++11
-depends: * build2 >= 0.13.0
-depends: * bpkg >= 0.13.0
-depends: libstudxml ^1.1.0-
-depends: libcutl ^1.11.0-
diff --git a/odb-tests/.gitignore b/odb-tests/.gitignore
new file mode 100644
index 0000000..e3bbf5b
--- /dev/null
+++ b/odb-tests/.gitignore
@@ -0,0 +1,41 @@
+# 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
+
+# Test executables.
+#
+driver
+
+# ODB-generated files.
+#
+test-odb.?xx
+test-odb-*.?xx
+test.sql
+test-*.sql
+
+# Testscript output directories (can be symlinks).
+#
+test
+test-driver
diff --git a/odb-tests/GPLv2 b/odb-tests/GPLv2
new file mode 100644
index 0000000..3912109
--- /dev/null
+++ b/odb-tests/GPLv2
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) 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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. 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.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+ 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
+convey 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 2 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, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This 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 Library General
+Public License instead of this License.
diff --git a/odb-tests/LICENSE b/odb-tests/LICENSE
new file mode 100644
index 0000000..d96b938
--- /dev/null
+++ b/odb-tests/LICENSE
@@ -0,0 +1,20 @@
+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 2 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, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
diff --git a/odb-tests/NEWS b/odb-tests/NEWS
new file mode 120000
index 0000000..0fae0f8
--- /dev/null
+++ b/odb-tests/NEWS
@@ -0,0 +1 @@
+../NEWS \ No newline at end of file
diff --git a/odb-tests/README.md b/odb-tests/README.md
new file mode 100644
index 0000000..5b3662c
--- /dev/null
+++ b/odb-tests/README.md
@@ -0,0 +1,82 @@
+# odb-tests - tests for ODB compiler
+
+This package contains tests for `odb`, object-relational mapping (ORM)
+compiler for C++.
+
+## Setting up PostgreSQL for running tests
+
+1. Install the PostgreSQL server. On Linux this is normally done using
+ distribution packages.
+
+2. In `/etc/postgresql/N/main/pg_hba.conf` add the following line after the
+ `local all postgres ...` line:
+
+ ```
+ local odb_test odb_test trust
+ ```
+
+ If you want to be able to run tests on a different host and access
+ PostgreSQL via TCP/IP, also add the following line (adjusting the IP
+ network to match your setup -- never specify a public IP network here since
+ the access is unauthenticated):
+
+ ```
+ host odb_test odb_test 192.168.0.0/24 trust
+ ```
+
+ You will also need to edit `/etc/postgresql/N/main/postgresql.conf` and
+ change `listen_address` to `*`. You may also need to open port `5432` in
+ your firewall.
+
+ Restart the PostgreSQL server.
+
+3. Add the `odb_test` user and the `odb_test` database.
+
+ First login:
+
+ ```
+ sudo -u postgres psql
+ ```
+
+ Then execute the following statements:
+
+ ```
+ CREATE USER odb_test;
+ CREATE DATABASE odb_test;
+ GRANT ALL PRIVILEGES ON DATABASE odb_test TO odb_test;
+ \c odb_test
+ GRANT ALL PRIVILEGES ON SCHEMA public TO odb_test;
+ ```
+
+## Setting up MySQL for running tests
+
+1. Install the MySQL server. On Linux this is normally done using distribution
+ packages.
+
+2. Setup remote access (optional).
+
+ If you want to be able to run tests on a different host and access MySQL
+ via TCP/IP (never do this on a public server since the access is
+ unauthenticated), then edit `/etc/mysql/.../mysqld.cnf`, the `[mysqld]`
+ section, and change `bind-address` to `0.0.0.0`. You may also need to open
+ port `3306` in your firewall.
+
+ Restart the MySQL server.
+
+3. Add the `odb_test` user and the `odb_test` database.
+
+ First login:
+
+ ```
+ sudo mysql
+ ```
+
+ Then execute the following statements:
+
+ ```
+ CREATE USER odb_test@'%';
+ CREATE USER odb_test@'localhost';
+ CREATE DATABASE odb_test;
+ GRANT ALL PRIVILEGES ON odb_test.* TO odb_test@'%';
+ FLUSH PRIVILEGES;
+ ```
diff --git a/odb-tests/boost/common/multi-index/driver.cxx b/odb-tests/boost/common/multi-index/driver.cxx
new file mode 100644
index 0000000..a077008
--- /dev/null
+++ b/odb-tests/boost/common/multi-index/driver.cxx
@@ -0,0 +1,189 @@
+// file : boost/common/multi-index/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test Boost multi-index container persistence.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ {
+ using namespace odb;
+
+ assert (odb::access::container_traits<int_lst>::kind == ck_ordered);
+ assert (odb::access::container_traits<int_vec>::kind == ck_ordered);
+ assert (odb::access::container_traits<int_set>::kind == ck_set);
+
+ assert (odb::access::container_traits<int_lst_set>::kind == ck_ordered);
+ assert (odb::access::container_traits<comp_set_vec>::kind == ck_ordered);
+ assert (odb::access::container_traits<comp_set_set>::kind == ck_set);
+ }
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ for (unsigned short i (0); i < 2; ++i)
+ {
+ object empty ("empty"), med ("medium"), full ("full");
+
+ //
+ // empty
+ //
+
+ //
+ // med
+ //
+ med.il.push_back (234);
+ med.il.push_back (123);
+
+ med.iv.push_back (234);
+ med.iv.push_back (123);
+
+ med.is.insert (234);
+ med.is.insert (123);
+
+ med.ils.push_back (234);
+ med.ils.push_back (123);
+
+ med.csv.insert (comp (234, "bcd"));
+ med.csv.insert (comp (123, "abc"));
+
+ med.css.insert (comp (234, "bcd"));
+ med.css.insert (comp (123, "abc"));
+
+ //
+ // full
+ //
+ full.il.push_back (2345);
+ full.il.push_back (1234);
+ full.il.push_back (3456);
+
+ full.iv.push_back (2345);
+ full.iv.push_back (1234);
+ full.iv.push_back (3456);
+
+ full.is.insert (2345);
+ full.is.insert (1234);
+ full.is.insert (3456);
+
+ full.ils.push_back (2345);
+ full.ils.push_back (1234);
+ full.ils.push_back (3456);
+
+ full.csv.insert (comp (234, "bcde"));
+ full.csv.insert (comp (123, "abcd"));
+ full.csv.insert (comp (234, "cdef"));
+
+ full.css.insert (comp (234, "bcde"));
+ full.css.insert (comp (123, "abcd"));
+ full.css.insert (comp (234, "cdef"));
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (empty);
+ db->persist (med);
+ db->persist (full);
+ t.commit ();
+ }
+
+ // load & check
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> e (db->load<object> ("empty"));
+ auto_ptr<object> m (db->load<object> ("medium"));
+ auto_ptr<object> f (db->load<object> ("full"));
+ t.commit ();
+
+ assert (empty == *e);
+ assert (med == *m);
+ assert (full == *f);
+ }
+
+ // empty
+ //
+ empty.il.push_back (12);
+ empty.iv.push_back (12);
+ empty.is.insert (12);
+ empty.ils.push_back (12);
+ empty.csv.insert (comp (12, "ab"));
+ empty.css.insert (comp (12, "ab"));
+
+ // med
+ //
+ med.il.clear ();
+ med.iv.clear ();
+ med.is.clear ();
+ med.ils.clear ();
+ med.csv.clear ();
+ med.css.clear ();
+
+ // full
+ //
+ full.il.push_back (4567);
+ full.iv.push_back (4567);
+ full.is.insert (4567);
+ full.ils.push_back (4567);
+ full.csv.insert (comp (4567, "defg"));
+ full.css.insert (comp (4567, "defg"));
+
+ // update
+ //
+ {
+ transaction t (db->begin ());
+ db->update (empty);
+ db->update (med);
+ db->update (full);
+ t.commit ();
+ }
+
+ // load & check
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> e (db->load<object> ("empty"));
+ auto_ptr<object> m (db->load<object> ("medium"));
+ auto_ptr<object> f (db->load<object> ("full"));
+ t.commit ();
+
+ assert (empty == *e);
+ assert (med == *m);
+ assert (full == *f);
+ }
+
+ // erase
+ //
+ if (i == 0)
+ {
+ transaction t (db->begin ());
+ db->erase<object> ("empty");
+ db->erase<object> ("medium");
+ db->erase<object> ("full");
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/boost/common/multi-index/test.hxx b/odb-tests/boost/common/multi-index/test.hxx
new file mode 100644
index 0000000..22b9ea4
--- /dev/null
+++ b/odb-tests/boost/common/multi-index/test.hxx
@@ -0,0 +1,113 @@
+// file : boost/common/multi-index/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/identity.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/multi_index/random_access_index.hpp>
+
+#include <odb/core.hxx>
+
+namespace mi = boost::multi_index;
+
+#pragma db value
+struct comp
+{
+ comp () {}
+ comp (int n, const std::string& s) : num (n), str (s) {}
+
+ #pragma db column("number")
+ int num;
+ std::string str;
+};
+
+inline bool
+operator== (const comp& x, const comp& y)
+{
+ return x.num == y.num && x.str == y.str;
+}
+
+typedef
+mi::multi_index_container<
+ int,
+ mi::indexed_by<mi::sequenced<> >
+> int_lst;
+
+typedef
+mi::multi_index_container<
+ int,
+ mi::indexed_by<mi::random_access<> >
+> int_vec;
+
+typedef
+mi::multi_index_container<
+ int,
+ mi::indexed_by<mi::ordered_unique<mi::identity<int> > >
+> int_set;
+
+typedef
+mi::multi_index_container<
+ int,
+ mi::indexed_by<
+ mi::sequenced<>,
+ mi::ordered_unique<mi::identity<int> >
+ >
+> int_lst_set;
+
+typedef
+mi::multi_index_container<
+ comp,
+ mi::indexed_by<
+ mi::ordered_unique<mi::member<comp, std::string, &comp::str> >,
+ mi::random_access<>
+ >
+> comp_set_vec;
+
+typedef
+mi::multi_index_container<
+ comp,
+ mi::indexed_by<
+ mi::ordered_unique<mi::member<comp, int, &comp::num> >,
+ mi::ordered_unique<mi::member<comp, std::string, &comp::str> >
+ >
+> comp_set_set;
+
+#pragma db object
+struct object
+{
+ object () {}
+ object (const std::string& id): id (id) {}
+
+ #pragma db id
+ std::string id;
+
+ int_lst il;
+ int_lst iv;
+ int_set is;
+
+ int_lst_set ils;
+ comp_set_vec csv;
+ comp_set_set css;
+};
+
+inline bool
+operator== (const object& x, const object& y)
+{
+ return
+ x.id == y.id &&
+
+ x.il == y.il &&
+ x.iv == y.iv &&
+ x.is == y.is &&
+
+ x.ils == y.ils &&
+ x.csv == y.csv &&
+ x.css == y.css;
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/boost/common/optional/driver.cxx b/odb-tests/boost/common/optional/driver.cxx
new file mode 100644
index 0000000..c2e3079
--- /dev/null
+++ b/odb-tests/boost/common/optional/driver.cxx
@@ -0,0 +1,74 @@
+// file : boost/common/optional/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test boost::optional persistence.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ {
+ object o1 (1);
+ object o2 (2);
+ o2.str = "abc";
+
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> o1 (db->load<object> (1));
+ auto_ptr<object> o2 (db->load<object> (2));
+ t.commit ();
+
+ assert (!o1->str);
+ assert (o2->str && *o2->str == "abc");
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+
+ {
+ result r (db->query<object> (query::str.is_null ()));
+ assert (!r.empty ());
+ }
+
+ {
+ result r (db->query<object> (query::str == "abc"));
+ assert (!r.empty ());
+ }
+
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/boost/common/optional/test.hxx b/odb-tests/boost/common/optional/test.hxx
new file mode 100644
index 0000000..65d951e
--- /dev/null
+++ b/odb-tests/boost/common/optional/test.hxx
@@ -0,0 +1,31 @@
+// file : boost/common/optional/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+
+#include <boost/optional.hpp>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+
+ boost::optional<std::string> str;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/boost/common/smart-ptr/driver.cxx b/odb-tests/boost/common/smart-ptr/driver.cxx
new file mode 100644
index 0000000..4bc6366
--- /dev/null
+++ b/odb-tests/boost/common/smart-ptr/driver.cxx
@@ -0,0 +1,202 @@
+// file : boost/common/smart-ptr/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test boost smart pointers.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/session.hxx>
+#include <odb/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+
+using namespace odb::boost;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ using boost::shared_ptr;
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ shared_ptr<cont> c1 (new cont (1));
+ {
+ transaction t (db->begin ());
+ db->persist (c1);
+ t.commit ();
+ }
+
+ // Test comparison operators.
+ //
+ {
+ assert (lazy_shared_ptr<cont> () == lazy_shared_ptr<cont> ());
+ assert (lazy_shared_ptr<cont> () != lazy_shared_ptr<cont> (c1));
+ assert (lazy_shared_ptr<cont> (c1) == lazy_shared_ptr<cont> (c1));
+
+ lazy_shared_ptr<cont> lc1 (*db, 1);
+ assert (lc1 != lazy_shared_ptr<cont> ());
+ assert (lc1 == lazy_shared_ptr<cont> (*db, c1));
+
+ shared_ptr<cont> c2 (new cont (2));
+ assert (lc1 != lazy_shared_ptr<cont> (*db, c2));
+ }
+
+ // Test swap.
+ //
+ {
+ lazy_shared_ptr<cont> lx (*db, 1), ly;
+ assert (lx == lazy_shared_ptr<cont> (*db, c1));
+
+ swap (lx, ly);
+ assert (lx == lazy_shared_ptr<cont> ());
+ assert (ly == lazy_shared_ptr<cont> (*db, c1));
+ }
+
+ // Test assignment from auto_ptr.
+ //
+ {
+ cont* p = new cont (3);
+ auto_ptr<cont> a (p);
+ lazy_shared_ptr<cont> l;
+ l = a;
+
+ assert (l.get() == p);
+ assert (!a.get ());
+ }
+
+ shared_ptr<obj> o1 (new obj (1));
+ shared_ptr<obj> o2 (new obj (2));
+ shared_ptr<obj> o3 (new obj (3));
+ shared_ptr<obj> o4 (new obj (4));
+ shared_ptr<cont> c2 (new cont (2));
+
+ o1->c = c1;
+ o2->c = c1;
+ o3->c = c2;
+ o4->c = c2;
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o3);
+ db->persist (o4);
+ db->persist (c2);
+
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ session s;
+ transaction t (db->begin ());
+
+ shared_ptr<cont> c (db->load<cont> (1));
+ shared_ptr<obj> o (db->load<obj> (1));
+
+ // Ensure that lazy pointers are present but not loaded.
+ //
+ assert (c->o.size () == 2);
+ assert (!c->o[0].loaded ());
+ assert (!c->o[1].loaded ());
+ assert (!o->c.loaded ());
+
+ // Ensure that the correct object IDs were loaded.
+ //
+ assert (c->o[0].object_id<obj> () == 1);
+ assert (c->o[1].object_id<obj> () == 2);
+ assert (o->c.object_id<obj> () == 1);
+
+ // Load the lazy pointer targets ensuring that the loaded
+ // targets correspond to the cached session objects.
+ //
+ shared_ptr<cont> cl (o->c.load ());
+ shared_ptr<obj> ol (c->o[0].load ());
+
+ assert (c->o[0].loaded ());
+ assert (o->c.loaded ());
+
+ assert (cl == c);
+ assert (ol == o);
+
+ t.commit ();
+ }
+
+ // Test lazy weak locking and reloading.
+ //
+ {
+ // No session.
+ //
+ transaction t (db->begin ());
+ shared_ptr<cont> c (db->load<cont> (1));
+
+ // Lock.
+ //
+ assert (!c->o[1].loaded ());
+ lazy_shared_ptr<obj> l (c->o[1].lock ());
+ assert (!l.loaded ());
+ assert (l.object_id<obj> () == c->o[1].object_id<obj> ());
+
+ // Reload.
+ //
+ assert (!c->o[1].loaded ());
+
+ shared_ptr<obj> ol (c->o[1].load ());
+ assert (c->o[1].loaded ());
+
+ ol.reset ();
+ assert (!c->o[1].loaded ());
+
+ ol = c->o[1].load ();
+ assert (c->o[1].loaded ());
+
+ t.commit ();
+ }
+
+ //
+ // Test shared_ptr as a value wrapper.
+ //
+
+ {
+ obj2 o1 (1);
+ obj2 o2 (2);
+ o2.str.reset (new string ("abc"));
+
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ shared_ptr<obj2> o1 (db->load<obj2> (1));
+ shared_ptr<obj2> o2 (db->load<obj2> (2));
+ t.commit ();
+
+ assert (!o1->str);
+ assert (o2->str && *o2->str == "abc");
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/boost/common/smart-ptr/test.hxx b/odb-tests/boost/common/smart-ptr/test.hxx
new file mode 100644
index 0000000..26d7109
--- /dev/null
+++ b/odb-tests/boost/common/smart-ptr/test.hxx
@@ -0,0 +1,82 @@
+// file : boost/common/smart-ptr/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
+#include <odb/core.hxx>
+#include <odb/boost/smart-ptr/lazy-ptr.hxx>
+
+struct obj;
+
+using boost::shared_ptr;
+using odb::boost::lazy_shared_ptr;
+using odb::boost::lazy_weak_ptr;
+
+#pragma db object
+struct cont
+{
+ cont ()
+ {
+ }
+
+ cont (unsigned long id)
+ : id (id)
+ {
+ }
+
+ #pragma db id
+ unsigned long id;
+
+ typedef std::vector<lazy_weak_ptr<obj> > obj_list;
+
+ #pragma db inverse(c) value_not_null
+ obj_list o;
+};
+
+#pragma db object
+struct obj
+{
+ obj ()
+ {
+ }
+
+ obj (unsigned long id)
+ : id (id)
+ {
+ }
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db not_null
+ lazy_shared_ptr<cont> c;
+};
+
+// Test shared_ptr as a value wrapper.
+//
+#pragma db object
+struct obj2
+{
+ obj2 ()
+ {
+ }
+
+ obj2 (unsigned long id)
+ : id (id)
+ {
+ }
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db null
+ shared_ptr<std::string> str;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/boost/common/template/driver.cxx b/odb-tests/boost/common/template/driver.cxx
new file mode 100644
index 0000000..43e28c0
--- /dev/null
+++ b/odb-tests/boost/common/template/driver.cxx
@@ -0,0 +1,39 @@
+// file : boost/common/template/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// PLACE TEST DESCRIPTION HERE
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/boost/common/template/template-vc10.vcxproj b/odb-tests/boost/common/template/template-vc10.vcxproj
new file mode 100644
index 0000000..3c606f9
--- /dev/null
+++ b/odb-tests/boost/common/template/template-vc10.vcxproj
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-__value__(database)-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-__value__(database)-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-__value__(database).lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-__value__(database).lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 --database __value__(database) __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>
+ <ItemGroup>
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx)
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__source_entry__(test-odb.cxx)
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/boost/common/template/template-vc10.vcxproj.filters b/odb-tests/boost/common/template/template-vc10.vcxproj.filters
new file mode 100644
index 0000000..f3ee658
--- /dev/null
+++ b/odb-tests/boost/common/template/template-vc10.vcxproj.filters
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx)
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__source_filter_entry__(test-odb.cxx)
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/boost/common/template/template-vc11.vcxproj b/odb-tests/boost/common/template/template-vc11.vcxproj
new file mode 100644
index 0000000..83d20c5
--- /dev/null
+++ b/odb-tests/boost/common/template/template-vc11.vcxproj
@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-__value__(database)-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-__value__(database)-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-__value__(database).lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-__value__(database).lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 --database __value__(database) __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>
+ <ItemGroup>
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx)
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__source_entry__(test-odb.cxx)
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/boost/common/template/template-vc11.vcxproj.filters b/odb-tests/boost/common/template/template-vc11.vcxproj.filters
new file mode 100644
index 0000000..f3ee658
--- /dev/null
+++ b/odb-tests/boost/common/template/template-vc11.vcxproj.filters
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx)
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__source_filter_entry__(test-odb.cxx)
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/boost/common/template/template-vc12.vcxproj b/odb-tests/boost/common/template/template-vc12.vcxproj
new file mode 100644
index 0000000..fd147f7
--- /dev/null
+++ b/odb-tests/boost/common/template/template-vc12.vcxproj
@@ -0,0 +1,185 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-__value__(database)-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-__value__(database)-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;_SCL_SECURE_NO_WARNINGS;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-__value__(database).lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;_SCL_SECURE_NO_WARNINGS;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-__value__(database).lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 --database __value__(database) __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>
+ <ItemGroup>
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx)
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__source_entry__(test-odb.cxx)
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/boost/common/template/template-vc12.vcxproj.filters b/odb-tests/boost/common/template/template-vc12.vcxproj.filters
new file mode 100644
index 0000000..f3ee658
--- /dev/null
+++ b/odb-tests/boost/common/template/template-vc12.vcxproj.filters
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx)
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__source_filter_entry__(test-odb.cxx)
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/boost/common/template/template-vc8.vcproj b/odb-tests/boost/common/template/template-vc8.vcproj
new file mode 100644
index 0000000..e0ac609
--- /dev/null
+++ b/odb-tests/boost/common/template/template-vc8.vcproj
@@ -0,0 +1,353 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-__value__(database)-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-__value__(database).lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-__value__(database)-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-__value__(database).lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__source_entry__(test-odb.cxx)
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe --database __value__(database) __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1400 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx)
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/boost/common/template/template-vc9.vcproj b/odb-tests/boost/common/template/template-vc9.vcproj
new file mode 100644
index 0000000..b8bf4bc
--- /dev/null
+++ b/odb-tests/boost/common/template/template-vc9.vcproj
@@ -0,0 +1,360 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-__value__(database)-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-__value__(database).lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-__value__(database)-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-__value__(database).lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__source_entry__(test-odb.cxx)
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe --database __value__(database) __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx)
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/boost/common/template/test.hxx b/odb-tests/boost/common/template/test.hxx
new file mode 100644
index 0000000..1d2a5f3
--- /dev/null
+++ b/odb-tests/boost/common/template/test.hxx
@@ -0,0 +1,25 @@
+// file : boost/common/template/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/boost/common/unordered/driver.cxx b/odb-tests/boost/common/unordered/driver.cxx
new file mode 100644
index 0000000..eda5891
--- /dev/null
+++ b/odb-tests/boost/common/unordered/driver.cxx
@@ -0,0 +1,212 @@
+// file : boost/common/unordered/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test Boost unordered containers persistence.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ for (unsigned short i (0); i < 2; ++i)
+ {
+ object empty ("empty"), med ("medium"), full ("full");
+
+ //
+ // empty
+ //
+
+ //
+ // med
+ //
+
+ // set
+ //
+ med.ns.insert (123);
+ med.ns.insert (234);
+
+ med.ss.insert ("aaa");
+ med.ss.insert ("bbbb");
+
+ med.cms.insert (comp (123, "aaa"));
+ med.cms.insert (comp (234, "bbbb"));
+
+ // map
+ //
+ med.nsm[123] = "aaa";
+ med.nsm[234] = "bbbb";
+
+ med.snm["aaa"] = 123;
+ med.snm["bbbb"] = 234;
+
+ med.ncm[123] = comp (123, "aaa");
+ med.ncm[234] = comp (234, "bbbb");
+
+ med.csmm.insert (
+ comp_str_multimap::value_type (comp (123, "aaa"), "aaa"));
+ med.csmm.insert (
+ comp_str_multimap::value_type (comp (234, "bbbb"), "bbbb"));
+
+ //
+ // full
+ //
+
+ // set
+ //
+ full.ns.insert (1234);
+ full.ns.insert (2345);
+ full.ns.insert (3456);
+
+ full.ss.insert ("aaaa");
+ full.ss.insert ("bbbbb");
+ full.ss.insert ("cccccc");
+
+ full.cms.insert (comp (1234, "aaaa"));
+ full.cms.insert (comp (2345, "bbbbb"));
+ full.cms.insert (comp (3456, "cccccc"));
+
+ // map
+ //
+ full.nsm[1234] = "aaaa";
+ full.nsm[2345] = "bbbbb";
+ full.nsm[3456] = "cccccc";
+
+ full.snm["aaaa"] = 1234;
+ full.snm["bbbbb"] = 2345;
+ full.snm["cccccc"] = 3456;
+
+ full.ncm[1234] = comp (1234, "aaaa");
+ full.ncm[2345] = comp (2345, "bbbbb");
+ full.ncm[3456] = comp (3456, "cccccc");
+
+ full.csmm.insert (
+ comp_str_multimap::value_type (comp (1234, "aaaa"), "aaaa"));
+ full.csmm.insert (
+ comp_str_multimap::value_type (comp (2345, "bbbbb"), "bbbbb"));
+ full.csmm.insert (
+ comp_str_multimap::value_type (comp (3456, "cccccc"), "cccccc"));
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (empty);
+ db->persist (med);
+ db->persist (full);
+ t.commit ();
+ }
+
+ // load & check
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> e (db->load<object> ("empty"));
+ auto_ptr<object> m (db->load<object> ("medium"));
+ auto_ptr<object> f (db->load<object> ("full"));
+ t.commit ();
+
+ assert (empty == *e);
+ assert (med == *m);
+ assert (full == *f);
+ }
+
+ // empty
+ //
+ empty.ns.insert (12);
+ empty.ss.insert ("aa");
+ empty.cms.insert (comp (12, "aa"));
+
+ empty.nsm[12] = "aa";
+ empty.snm["aa"] = 12;
+ empty.ncm[12] = comp (12, "aa");
+ empty.csmm.insert (
+ comp_str_multimap::value_type (comp (12, "aa"), "aa"));
+
+ // med
+ //
+ med.ns.clear ();
+ med.ss.clear ();
+ med.cms.clear ();
+
+ med.nsm.clear ();
+ med.snm.clear ();
+ med.ncm.clear ();
+ med.csmm.clear ();
+
+ // full
+ //
+ full.ns.insert (4567);
+ full.ss.insert ("ddddddd");
+ full.cms.insert (comp (4567, "ddddddd"));
+
+ full.nsm[3456] += 'c';
+ full.nsm[4567] = "ddddddd";
+ full.snm["cccccc"]++;
+ full.snm["ddddddd"] = 4567;
+ full.ncm[3456].num++;
+ full.ncm[3456].str += 'c';
+ full.ncm[4567] = comp (4567, "ddddddd");
+ full.csmm.find (comp (3456, "cccccc"))->second += "c";
+ full.csmm.insert (
+ comp_str_multimap::value_type (comp (4567, "ddddddd"), "ddddddd"));
+
+ // update
+ //
+ {
+ transaction t (db->begin ());
+ db->update (empty);
+ db->update (med);
+ db->update (full);
+ t.commit ();
+ }
+
+ // load & check
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> e (db->load<object> ("empty"));
+ auto_ptr<object> m (db->load<object> ("medium"));
+ auto_ptr<object> f (db->load<object> ("full"));
+ t.commit ();
+
+ assert (empty == *e);
+ assert (med == *m);
+ assert (full == *f);
+ }
+
+ // erase
+ //
+ if (i == 0)
+ {
+ transaction t (db->begin ());
+ db->erase<object> ("empty");
+ db->erase<object> ("medium");
+ db->erase<object> ("full");
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/boost/common/unordered/test.hxx b/odb-tests/boost/common/unordered/test.hxx
new file mode 100644
index 0000000..cd77845
--- /dev/null
+++ b/odb-tests/boost/common/unordered/test.hxx
@@ -0,0 +1,113 @@
+// file : boost/common/unordered/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+
+#include <boost/unordered_set.hpp>
+#include <boost/unordered_map.hpp>
+
+#include <odb/core.hxx>
+
+#pragma db value
+struct comp
+{
+ comp () {}
+ comp (int n, const std::string& s) : num (n), str (s) {}
+
+ #pragma db column("number")
+ int num;
+ std::string str;
+};
+
+inline bool
+operator== (const comp& x, const comp& y)
+{
+ return x.num == y.num && x.str == y.str;
+}
+
+inline bool
+operator!= (const comp& x, const comp& y)
+{
+ return !(x == y);
+}
+
+inline bool
+operator< (const comp& x, const comp& y)
+{
+ return x.num != y.num ? x.num < y.num : x.str < y.str;
+}
+
+inline std::size_t
+hash_value (const comp& x)
+{
+ std::size_t seed = 0;
+ boost::hash_combine (seed, x.num);
+ boost::hash_combine (seed, x.str);
+ return seed;
+}
+
+using boost::unordered_set;
+using boost::unordered_multiset;
+
+typedef unordered_set<int> num_set;
+typedef unordered_set<std::string> str_set;
+typedef unordered_multiset<comp> comp_multiset;
+
+using boost::unordered_map;
+using boost::unordered_multimap;
+
+typedef unordered_map<int, std::string> num_str_map;
+typedef unordered_map<std::string, int> str_num_map;
+typedef unordered_map<int, comp> num_comp_map;
+typedef unordered_multimap<comp, std::string> comp_str_multimap;
+
+#pragma db object
+struct object
+{
+ object ()
+ {
+ }
+
+ object (const std::string& id)
+ : id (id)
+ {
+ }
+
+
+ #pragma db id
+ std::string id;
+
+ // set
+ //
+ num_set ns;
+ str_set ss;
+ comp_multiset cms;
+
+ // map
+ //
+ num_str_map nsm;
+ str_num_map snm;
+ num_comp_map ncm;
+ comp_str_multimap csmm;
+};
+
+inline bool
+operator== (const object& x, const object& y)
+{
+ return
+ x.id == y.id &&
+
+ x.ns == y.ns &&
+ x.ss == y.ss &&
+ x.cms == y.cms &&
+
+ x.nsm == y.nsm &&
+ x.snm == y.snm &&
+ x.ncm == y.ncm &&
+ x.csmm == y.csmm;
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/boost/common/uuid/driver.cxx b/odb-tests/boost/common/uuid/driver.cxx
new file mode 100644
index 0000000..aed3390
--- /dev/null
+++ b/odb-tests/boost/common/uuid/driver.cxx
@@ -0,0 +1,69 @@
+// file : boost/common/uuid/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test Boost UUID persistence.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <boost/uuid/uuid_generators.hpp>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace boost::uuids;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ object o (1);
+ o.uuid_ = random_generator() ();
+ o.null_ = nil_uuid ();
+ o.zero_ = nil_uuid ();
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> p (db->load<object> (o.id_));
+ t.commit ();
+
+ assert (*p == o);
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::uuid == o.uuid_));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->id_ == o.id_);
+ assert (++i == r.end ());
+ t.commit ();
+ }
+
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/boost/common/uuid/test.hxx b/odb-tests/boost/common/uuid/test.hxx
new file mode 100644
index 0000000..82ed95b
--- /dev/null
+++ b/odb-tests/boost/common/uuid/test.hxx
@@ -0,0 +1,37 @@
+// file : boost/common/uuid/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <boost/uuid/uuid.hpp>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object () {}
+ object (unsigned long id): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ typedef boost::uuids::uuid uuid;
+
+ uuid uuid_;
+ uuid null_;
+
+ #pragma db not_null
+ uuid zero_;
+
+ bool operator== (const object& x) const
+ {
+ return id_ == x.id_ &&
+ uuid_ == x.uuid_ &&
+ null_ == x.null_ &&
+ zero_ == x.zero_;
+ }
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/boost/mssql/date-time/driver.cxx b/odb-tests/boost/mssql/date-time/driver.cxx
new file mode 100644
index 0000000..e97a276
--- /dev/null
+++ b/odb-tests/boost/mssql/date-time/driver.cxx
@@ -0,0 +1,158 @@
+// file : boost/mssql/date-time/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test boost date/time type persistence. SQL Server version.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/boost/date-time/exceptions.hxx>
+
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+
+using namespace boost::gregorian;
+using namespace boost::posix_time;
+
+using namespace odb::core;
+
+bool
+test_invalid_special_value (object&, auto_ptr<database>&);
+
+bool
+test_out_of_range_value (object&, auto_ptr<database>&);
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ object o;
+
+ // Test all valid date-time mappings.
+ //
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ o.dates.push_back (day_clock::local_day ());
+ o.dates.push_back (date (not_a_date_time));
+ o.dates.push_back (date (max_date_time));
+ o.dates.push_back (date (min_date_time));
+
+ o.times.push_back (second_clock::local_time ());
+ o.times.push_back (not_a_date_time);
+ o.times.push_back (min_date_time);
+ o.times.push_back (ptime (max_date_time));
+#endif
+
+ // In DATETIME fractional seconds are rounded to .000, .003, or .007.
+ //
+ o.times_dt.push_back (ptime (date (2012, 1, 13),
+ time_duration (11, 57, 13, 7000)));
+
+ // SMALLDATETIME doesn't have seconds (always 0).
+ //
+ o.times_sdt.push_back (ptime (date (2012, 1, 13),
+ time_duration (11, 57, 0, 0)));
+
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ o.durations.push_back (time_duration (1, 2, 3, 123456));
+ o.durations.push_back (not_a_date_time);
+#endif
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> ol (db->load<object> (o.id));
+ t.commit ();
+
+ assert (*ol == o);
+ }
+
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ // Test invalid date mappings.
+ //
+ {
+ object sv1, sv2;
+ sv1.dates.push_back (date (neg_infin));
+ sv2.dates.push_back (date (pos_infin));
+
+ transaction t (db->begin ());
+ assert (test_invalid_special_value (sv1, db));
+ assert (test_invalid_special_value (sv2, db));
+ t.commit ();
+ }
+
+ // Test invalid ptime mappings.
+ //
+ {
+ object sv1, sv2;
+ sv1.times.push_back (neg_infin);
+ sv2.times.push_back (pos_infin);
+
+ transaction t (db->begin ());
+ assert (test_invalid_special_value (sv1, db));
+ assert (test_invalid_special_value (sv2, db));
+ t.commit ();
+ }
+
+ // Test invalid time_duration mappings.
+ //
+ {
+ object sv1, sv2, or1;
+ sv1.durations.push_back (pos_infin);
+ sv2.durations.push_back (neg_infin);
+ or1.durations.push_back (time_duration (50, 2, 3, 123456700));
+
+ transaction t (db->begin ());
+ assert (test_invalid_special_value (sv1, db));
+ assert (test_invalid_special_value (sv2, db));
+
+ try
+ {
+ db->persist (or1);
+ assert (false);
+ }
+ catch (const odb::boost::date_time::value_out_of_range&)
+ {
+ }
+
+ t.commit ();
+ }
+#endif
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
+
+bool
+test_invalid_special_value (object& x, auto_ptr<database>& db)
+{
+ try
+ {
+ db->persist (x);
+ return false;
+ }
+ catch (const odb::boost::date_time::special_value&)
+ {
+ }
+
+ return true;
+}
diff --git a/odb-tests/boost/mssql/date-time/test.hxx b/odb-tests/boost/mssql/date-time/test.hxx
new file mode 100644
index 0000000..ed2276b
--- /dev/null
+++ b/odb-tests/boost/mssql/date-time/test.hxx
@@ -0,0 +1,58 @@
+// file : boost/mssql/date-time/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <vector>
+
+#include <boost/date_time/gregorian/gregorian_types.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object ()
+ {
+ }
+
+ #pragma db id auto
+ unsigned long id;
+
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ std::vector<boost::gregorian::date> dates;
+
+ std::vector<boost::posix_time::ptime> times;
+#endif
+
+ #pragma db value_type("DATETIME")
+ std::vector<boost::posix_time::ptime> times_dt;
+
+ #pragma db value_type("SMALLDATETIME")
+ std::vector<boost::posix_time::ptime> times_sdt;
+
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ std::vector<boost::posix_time::time_duration> durations;
+#endif
+
+ bool
+ operator== (const object& x) const
+ {
+ return
+ id == x.id
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ && dates == x.dates
+ && times == x.times
+#endif
+ && times_dt == x.times_dt
+ && times_sdt == x.times_sdt
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ && durations == x.durations
+#endif
+ ;
+ }
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/boost/mssql/template/driver.cxx b/odb-tests/boost/mssql/template/driver.cxx
new file mode 100644
index 0000000..37614c4
--- /dev/null
+++ b/odb-tests/boost/mssql/template/driver.cxx
@@ -0,0 +1,39 @@
+// file : boost/mssql/template/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// PLACE TEST DESCRIPTION HERE
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/boost/mssql/template/template-vc10.vcxproj b/odb-tests/boost/mssql/template/template-vc10.vcxproj
new file mode 100644
index 0000000..dcb625e
--- /dev/null
+++ b/odb-tests/boost/mssql/template/template-vc10.vcxproj
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-mssql-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-mssql-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-mssql.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-mssql.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/boost/mssql/template/template-vc10.vcxproj.filters b/odb-tests/boost/mssql/template/template-vc10.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/boost/mssql/template/template-vc10.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/boost/mssql/template/template-vc11.vcxproj b/odb-tests/boost/mssql/template/template-vc11.vcxproj
new file mode 100644
index 0000000..d5951f1
--- /dev/null
+++ b/odb-tests/boost/mssql/template/template-vc11.vcxproj
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-mssql-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-mssql-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-mssql.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-mssql.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/boost/mssql/template/template-vc11.vcxproj.filters b/odb-tests/boost/mssql/template/template-vc11.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/boost/mssql/template/template-vc11.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/boost/mssql/template/template-vc12.vcxproj b/odb-tests/boost/mssql/template/template-vc12.vcxproj
new file mode 100644
index 0000000..e0cb56f
--- /dev/null
+++ b/odb-tests/boost/mssql/template/template-vc12.vcxproj
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-mssql-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-mssql-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-mssql.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-mssql.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/boost/mssql/template/template-vc12.vcxproj.filters b/odb-tests/boost/mssql/template/template-vc12.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/boost/mssql/template/template-vc12.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/boost/mssql/template/template-vc8.vcproj b/odb-tests/boost/mssql/template/template-vc8.vcproj
new file mode 100644
index 0000000..6d1c40b
--- /dev/null
+++ b/odb-tests/boost/mssql/template/template-vc8.vcproj
@@ -0,0 +1,354 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-mssql-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-mssql.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-mssql-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-mssql.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1400 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/boost/mssql/template/template-vc9.vcproj b/odb-tests/boost/mssql/template/template-vc9.vcproj
new file mode 100644
index 0000000..422fbd7
--- /dev/null
+++ b/odb-tests/boost/mssql/template/template-vc9.vcproj
@@ -0,0 +1,361 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-mssql-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-mssql.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-mssql-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-mssql.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/boost/mssql/template/test.hxx b/odb-tests/boost/mssql/template/test.hxx
new file mode 100644
index 0000000..753498f
--- /dev/null
+++ b/odb-tests/boost/mssql/template/test.hxx
@@ -0,0 +1,25 @@
+// file : boost/mssql/template/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/boost/mysql/date-time/driver.cxx b/odb-tests/boost/mysql/date-time/driver.cxx
new file mode 100644
index 0000000..8959f0c
--- /dev/null
+++ b/odb-tests/boost/mysql/date-time/driver.cxx
@@ -0,0 +1,218 @@
+// file : boost/mysql/date-time/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test boost date/time type persistence. MySQL version.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+
+using namespace boost::gregorian;
+using namespace boost::posix_time;
+
+using namespace odb::core;
+
+bool
+test_invalid_special_value (object&, auto_ptr<database>&);
+
+bool
+test_out_of_range_value (object&, auto_ptr<database>&);
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ mysql_version v;
+ {
+ transaction t (db->begin ());
+ db->query<mysql_version> ().begin ().load (v);
+ t.commit ();
+ }
+
+ // If we are running against MySQL 5.6.4 or later alter the tables
+ // to allow sub-second precision.
+ //
+ bool fs (v.major > 5 ||
+ (v.major == 5 && (v.minor > 6 ||
+ (v.minor == 6 && v.release >= 4))));
+ if (fs)
+ {
+ transaction t (db->begin ());
+
+ db->execute ("ALTER TABLE `boost_mysql_dt_object_durations`" \
+ " MODIFY COLUMN `value` TIME(6)");
+
+ db->execute ("ALTER TABLE `boost_mysql_dt_object_times`" \
+ " MODIFY COLUMN `value` DATETIME(6)");
+
+ db->execute ("ALTER TABLE `boost_mysql_dt_object_timestamps`" \
+ " MODIFY COLUMN `value` TIMESTAMP(6) NULL");
+
+ t.commit ();
+ }
+
+ object o;
+
+ // Test all valid date-time mappings.
+ //
+ o.dates.push_back (day_clock::local_day ());
+ o.dates.push_back (date (not_a_date_time));
+ o.dates.push_back (date (max_date_time));
+ o.dates.push_back (date (min_date_time));
+
+ if (fs)
+ o.times.push_back (microsec_clock::local_time ());
+ else
+ o.times.push_back (second_clock::local_time ());
+ o.times.push_back (not_a_date_time);
+ o.times.push_back (min_date_time);
+
+ // MySQL prior to 5.6.4 does not support fraction seconds. Construct
+ // with zero fractional seconds so that comparison test does not fail
+ // for invalid reasons.
+ //
+ o.times.push_back (
+ ptime (
+ date (max_date_time),
+ time_duration (
+ ptime (max_date_time).time_of_day ().hours (),
+ ptime (max_date_time).time_of_day ().minutes (),
+ ptime (max_date_time).time_of_day ().seconds ())));
+
+ if (fs)
+ o.timestamps.push_back (microsec_clock::local_time ());
+ else
+ o.timestamps.push_back (second_clock::local_time ());
+ o.timestamps.push_back (not_a_date_time);
+
+ o.durations.push_back (time_duration (1, 2, 3));
+ if (fs)
+ o.durations.back () += time_duration (microseconds (123456));
+ o.durations.push_back (time_duration (-1, 2, 3));
+ o.durations.push_back (not_a_date_time);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> ol (db->load<object> (o.id));
+ t.commit ();
+
+ assert (*ol == o);
+ }
+
+ {
+ // Test invalid date mappings.
+ //
+ object sv1, sv2;
+ sv1.dates.push_back (date (neg_infin));
+ sv2.dates.push_back (date (pos_infin));
+
+ transaction t (db->begin ());
+ assert (test_invalid_special_value (sv1, db));
+ assert (test_invalid_special_value (sv2, db));
+ t.commit ();
+ }
+
+
+ {
+ // Test invalid ptime (DATETIME) mappings.
+ //
+ object sv1, sv2;
+ sv1.times.push_back (neg_infin);
+ sv2.times.push_back (pos_infin);
+
+ transaction t (db->begin ());
+ assert (test_invalid_special_value (sv1, db));
+ assert (test_invalid_special_value (sv2, db));
+ t.commit ();
+ }
+
+ {
+ // Test invalid ptime (TIMESTAMP) mappings.
+ //
+ object or1, or2, sv1, sv2;
+ or1.timestamps.push_back (min_date_time);
+ or2.timestamps.push_back (max_date_time);
+ sv1.timestamps.push_back (neg_infin);
+ sv2.timestamps.push_back (pos_infin);
+
+ transaction t (db->begin ());
+ assert (test_out_of_range_value (or1, db));
+ assert (test_out_of_range_value (or2, db));
+ assert (test_invalid_special_value (sv1, db));
+ assert (test_invalid_special_value (sv2, db));
+ t.commit ();
+ }
+
+ {
+ // Test invalid time_duration mappings.
+ //
+ object or1, or2, sv1, sv2;
+ or1.durations.push_back (time_duration (850, 0, 0));
+ or2.durations.push_back (time_duration (-850, 0, 0));
+ sv1.durations.push_back (pos_infin);
+ sv2.durations.push_back (neg_infin);
+
+ transaction t (db->begin ());
+ assert (test_out_of_range_value (or1, db));
+ assert (test_out_of_range_value (or2, db));
+ assert (test_invalid_special_value (sv1, db));
+ assert (test_invalid_special_value (sv2, db));
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
+
+bool
+test_invalid_special_value (object& x, auto_ptr<database>& db)
+{
+ try
+ {
+ db->persist (x);
+ return false;
+ }
+ catch (const odb::boost::date_time::special_value&)
+ {
+ }
+
+ return true;
+}
+
+bool
+test_out_of_range_value (object& x, auto_ptr<database>& db)
+{
+ try
+ {
+ db->persist (x);
+ return false;
+ }
+ catch (const odb::boost::date_time::value_out_of_range&)
+ {
+ }
+
+ return true;
+}
diff --git a/odb-tests/boost/mysql/date-time/test.hxx b/odb-tests/boost/mysql/date-time/test.hxx
new file mode 100644
index 0000000..bf73b09
--- /dev/null
+++ b/odb-tests/boost/mysql/date-time/test.hxx
@@ -0,0 +1,65 @@
+// file : boost/mysql/date-time/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <vector>
+
+#include <boost/date_time/gregorian/gregorian_types.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object ()
+ {
+ }
+
+ bool
+ operator== (const object& x) const
+ {
+ return
+ id == x.id &&
+ dates == x.dates &&
+ times == x.times &&
+ timestamps == x.timestamps &&
+ durations == x.durations;
+ }
+
+ #pragma db id auto
+ unsigned long id;
+
+ std::vector<boost::gregorian::date> dates;
+ std::vector<boost::posix_time::ptime> times;
+
+ // Make timestamp NULL-able to suppress the auto-initialization and
+ // auto-update characteristics of the TIMESTAMP datatype, and to
+ // allow NULL values.
+ //
+ #pragma db value_type ("TIMESTAMP") value_null
+ std::vector<boost::posix_time::ptime> timestamps;
+
+ std::vector<boost::posix_time::time_duration> durations;
+};
+
+// MySQL server version view.
+//
+#pragma db view query( \
+ "SELECT " \
+ "CAST(SUBSTRING_INDEX(@@version, '.', 1) AS UNSIGNED)," \
+ "CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(@@version, '.', 2), '.', -1) AS UNSIGNED)," \
+ "CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(@@version, '-', 1), '.', -1) AS UNSIGNED)," \
+ "@@protocol_version")
+struct mysql_version
+{
+ unsigned int major;
+ unsigned int minor;
+ unsigned int release;
+
+ unsigned int protocol;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/boost/mysql/template/driver.cxx b/odb-tests/boost/mysql/template/driver.cxx
new file mode 100644
index 0000000..1164950
--- /dev/null
+++ b/odb-tests/boost/mysql/template/driver.cxx
@@ -0,0 +1,39 @@
+// file : boost/mysql/template/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// PLACE TEST DESCRIPTION HERE
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/boost/mysql/template/template-vc10.vcxproj b/odb-tests/boost/mysql/template/template-vc10.vcxproj
new file mode 100644
index 0000000..3137abc
--- /dev/null
+++ b/odb-tests/boost/mysql/template/template-vc10.vcxproj
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-mysql-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-mysql-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-mysql.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-mysql.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/boost/mysql/template/template-vc10.vcxproj.filters b/odb-tests/boost/mysql/template/template-vc10.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/boost/mysql/template/template-vc10.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/boost/mysql/template/template-vc11.vcxproj b/odb-tests/boost/mysql/template/template-vc11.vcxproj
new file mode 100644
index 0000000..bc4b5a4
--- /dev/null
+++ b/odb-tests/boost/mysql/template/template-vc11.vcxproj
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-mysql-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-mysql-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-mysql.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-mysql.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/boost/mysql/template/template-vc11.vcxproj.filters b/odb-tests/boost/mysql/template/template-vc11.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/boost/mysql/template/template-vc11.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/boost/mysql/template/template-vc12.vcxproj b/odb-tests/boost/mysql/template/template-vc12.vcxproj
new file mode 100644
index 0000000..e7d70bc
--- /dev/null
+++ b/odb-tests/boost/mysql/template/template-vc12.vcxproj
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-mysql-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-mysql-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-mysql.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-mysql.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/boost/mysql/template/template-vc12.vcxproj.filters b/odb-tests/boost/mysql/template/template-vc12.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/boost/mysql/template/template-vc12.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/boost/mysql/template/template-vc8.vcproj b/odb-tests/boost/mysql/template/template-vc8.vcproj
new file mode 100644
index 0000000..2ef0383
--- /dev/null
+++ b/odb-tests/boost/mysql/template/template-vc8.vcproj
@@ -0,0 +1,354 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-mysql-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-mysql.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-mysql-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-mysql.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1400 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/boost/mysql/template/template-vc9.vcproj b/odb-tests/boost/mysql/template/template-vc9.vcproj
new file mode 100644
index 0000000..b60c7e2
--- /dev/null
+++ b/odb-tests/boost/mysql/template/template-vc9.vcproj
@@ -0,0 +1,361 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-mysql-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-mysql.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-mysql-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-mysql.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/boost/mysql/template/test.hxx b/odb-tests/boost/mysql/template/test.hxx
new file mode 100644
index 0000000..7c97ca2
--- /dev/null
+++ b/odb-tests/boost/mysql/template/test.hxx
@@ -0,0 +1,25 @@
+// file : boost/mysql/template/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/boost/oracle/date-time/driver.cxx b/odb-tests/boost/oracle/date-time/driver.cxx
new file mode 100644
index 0000000..82294c7
--- /dev/null
+++ b/odb-tests/boost/oracle/date-time/driver.cxx
@@ -0,0 +1,136 @@
+// file : boost/oracle/date-time/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test boost date/time type persistence. Oracle version.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/boost/date-time/exceptions.hxx>
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+
+using namespace boost::gregorian;
+using namespace boost::posix_time;
+
+using namespace odb::core;
+
+bool
+test_invalid_special_value (object&, auto_ptr<database>&);
+
+bool
+test_out_of_range_value (object&, auto_ptr<database>&);
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ object o;
+
+ // Test all valid date-time mappings.
+ //
+ o.dates.push_back (day_clock::local_day ());
+ o.dates.push_back (date (not_a_date_time));
+ o.dates.push_back (date (max_date_time));
+ o.dates.push_back (date (min_date_time));
+
+ o.times.push_back (second_clock::local_time ());
+ o.times.push_back (not_a_date_time);
+ o.times.push_back (min_date_time);
+ o.times.push_back (ptime (max_date_time));
+
+ o.times_d.push_back (ptime (date (2012, 6, 27),
+ time_duration (14, 17, 05, 0)));
+ o.times_d.push_back (not_a_date_time);
+
+ o.durations.push_back (time_duration (1, 2, 3, 123456));
+ o.durations.push_back (time_duration (-1, 2, 3));
+ o.durations.push_back (not_a_date_time);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> ol (db->load<object> (o.id));
+ t.commit ();
+
+ assert (*ol == o);
+ }
+
+ // Test invalid date mappings.
+ //
+ {
+ object sv1, sv2;
+ sv1.dates.push_back (date (neg_infin));
+ sv2.dates.push_back (date (pos_infin));
+
+ transaction t (db->begin ());
+ assert (test_invalid_special_value (sv1, db));
+ assert (test_invalid_special_value (sv2, db));
+ t.commit ();
+ }
+
+ // Test invalid ptime mappings.
+ //
+ {
+ object sv1, sv2;
+ sv1.times.push_back (neg_infin);
+ sv2.times.push_back (pos_infin);
+
+ transaction t (db->begin ());
+ assert (test_invalid_special_value (sv1, db));
+ assert (test_invalid_special_value (sv2, db));
+ t.commit ();
+ }
+
+ // Test invalid time_duration mappings.
+ //
+ {
+ object sv1, sv2;
+ sv1.durations.push_back (pos_infin);
+ sv2.durations.push_back (neg_infin);
+
+ transaction t (db->begin ());
+ assert (test_invalid_special_value (sv1, db));
+ assert (test_invalid_special_value (sv2, db));
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
+
+bool
+test_invalid_special_value (object& x, auto_ptr<database>& db)
+{
+ try
+ {
+ db->persist (x);
+ return false;
+ }
+ catch (const odb::boost::date_time::special_value&)
+ {
+ }
+
+ return true;
+}
diff --git a/odb-tests/boost/oracle/date-time/test.hxx b/odb-tests/boost/oracle/date-time/test.hxx
new file mode 100644
index 0000000..5eaab88
--- /dev/null
+++ b/odb-tests/boost/oracle/date-time/test.hxx
@@ -0,0 +1,42 @@
+// file : boost/oracle/date-time/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <vector>
+
+#include <boost/date_time/gregorian/gregorian_types.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object ()
+ {
+ }
+
+ bool
+ operator== (const object& x) const
+ {
+ return
+ id == x.id &&
+ dates == x.dates &&
+ times == x.times &&
+ times_d == x.times_d &&
+ durations == x.durations;
+ }
+
+ #pragma db id auto
+ unsigned long id;
+
+ std::vector<boost::gregorian::date> dates;
+ std::vector<boost::posix_time::ptime> times;
+ #pragma db value_type("DATE")
+ std::vector<boost::posix_time::ptime> times_d;
+ std::vector<boost::posix_time::time_duration> durations;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/boost/oracle/template/driver.cxx b/odb-tests/boost/oracle/template/driver.cxx
new file mode 100644
index 0000000..892b58f
--- /dev/null
+++ b/odb-tests/boost/oracle/template/driver.cxx
@@ -0,0 +1,39 @@
+// file : boost/oracle/template/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// PLACE TEST DESCRIPTION HERE
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/boost/oracle/template/template-vc10.vcxproj b/odb-tests/boost/oracle/template/template-vc10.vcxproj
new file mode 100644
index 0000000..0c93020
--- /dev/null
+++ b/odb-tests/boost/oracle/template/template-vc10.vcxproj
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-oracle-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-oracle-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-oracle.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-oracle.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/boost/oracle/template/template-vc10.vcxproj.filters b/odb-tests/boost/oracle/template/template-vc10.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/boost/oracle/template/template-vc10.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/boost/oracle/template/template-vc11.vcxproj b/odb-tests/boost/oracle/template/template-vc11.vcxproj
new file mode 100644
index 0000000..4df5f5b
--- /dev/null
+++ b/odb-tests/boost/oracle/template/template-vc11.vcxproj
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-oracle-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-oracle-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-oracle.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-oracle.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/boost/oracle/template/template-vc11.vcxproj.filters b/odb-tests/boost/oracle/template/template-vc11.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/boost/oracle/template/template-vc11.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/boost/oracle/template/template-vc12.vcxproj b/odb-tests/boost/oracle/template/template-vc12.vcxproj
new file mode 100644
index 0000000..0b942bc
--- /dev/null
+++ b/odb-tests/boost/oracle/template/template-vc12.vcxproj
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-oracle-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-oracle-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-oracle.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-oracle.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/boost/oracle/template/template-vc12.vcxproj.filters b/odb-tests/boost/oracle/template/template-vc12.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/boost/oracle/template/template-vc12.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/boost/oracle/template/template-vc8.vcproj b/odb-tests/boost/oracle/template/template-vc8.vcproj
new file mode 100644
index 0000000..c13f330
--- /dev/null
+++ b/odb-tests/boost/oracle/template/template-vc8.vcproj
@@ -0,0 +1,354 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-oracle-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-oracle.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-oracle-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-oracle.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1400 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/boost/oracle/template/template-vc9.vcproj b/odb-tests/boost/oracle/template/template-vc9.vcproj
new file mode 100644
index 0000000..a3061ee
--- /dev/null
+++ b/odb-tests/boost/oracle/template/template-vc9.vcproj
@@ -0,0 +1,361 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-oracle-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-oracle.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-oracle-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-oracle.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/boost/oracle/template/test.hxx b/odb-tests/boost/oracle/template/test.hxx
new file mode 100644
index 0000000..141b6cd
--- /dev/null
+++ b/odb-tests/boost/oracle/template/test.hxx
@@ -0,0 +1,25 @@
+// file : boost/oracle/template/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/boost/pgsql/date-time/driver.cxx b/odb-tests/boost/pgsql/date-time/driver.cxx
new file mode 100644
index 0000000..94fcc46
--- /dev/null
+++ b/odb-tests/boost/pgsql/date-time/driver.cxx
@@ -0,0 +1,157 @@
+// file : boost/pgsql/date-time/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test boost date/time type persistence. PostgreSQL version.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/date_time/gregorian/gregorian.hpp>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+
+using namespace boost::gregorian;
+using namespace boost::posix_time;
+
+using namespace odb::core;
+
+bool
+test_invalid_special_value (object&, auto_ptr<database>&);
+
+bool
+test_out_of_range_value (object&, auto_ptr<database>&);
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ object o;
+
+ // Test all valid date-time mappings.
+ //
+ o.dates.push_back (day_clock::local_day ());
+ o.dates.push_back (date (not_a_date_time));
+ o.dates.push_back (date (max_date_time));
+ o.dates.push_back (date (min_date_time));
+
+ o.times.push_back (microsec_clock::local_time ());
+ o.times.push_back (not_a_date_time);
+ o.times.push_back (pos_infin);
+ o.times.push_back (neg_infin);
+ o.times.push_back (ptime (date (max_date_time),
+ time_duration (16, 23, 0, 123456)));
+ o.times.push_back (ptime (date (min_date_time),
+ time_duration (3, 14, 7, 123456)));
+ o.times.push_back (ptime (date (1969, 12, 31), // Before PG epoch.
+ time_duration (23, 59, 59, 123000)));
+
+ o.durations.push_back (time_duration (0, 0, 0));
+ o.durations.push_back (time_duration (12, 3, 4, 123456));
+ o.durations.push_back (time_duration (23, 59, 59, 123456));
+ o.durations.push_back (not_a_date_time);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> ol (db->load<object> (o.id));
+ t.commit ();
+
+ assert (*ol == o);
+ }
+
+ {
+ // Test invalid date mappings.
+ //
+ object sv1, sv2;
+ sv1.dates.push_back (date (neg_infin));
+ sv2.dates.push_back (date (pos_infin));
+
+ transaction t (db->begin ());
+ assert (test_invalid_special_value (sv1, db));
+ assert (test_invalid_special_value (sv2, db));
+ t.commit ();
+ }
+
+ {
+ // Test invalid ptime mappings.
+ //
+ // object sv1, sv2;
+ // sv1.times.push_back (neg_infin);
+ // sv2.times.push_back (pos_infin);
+
+ // transaction t (db->begin ());
+ // assert (test_invalid_special_value (sv1, db));
+ // assert (test_invalid_special_value (sv2, db));
+ // t.commit ();
+ }
+
+ {
+ // Test invalid time_duration mappings.
+ //
+ // object or1, sv1, sv2;
+ // or1.durations.push_back (time_duration (0, 0, -1));
+ // sv1.durations.push_back (pos_infin);
+ // sv2.durations.push_back (neg_infin);
+
+ // transaction t (db->begin ());
+ // assert (test_out_of_range_value (or1, db));
+ // assert (test_invalid_special_value (sv1, db));
+ // assert (test_invalid_special_value (sv2, db));
+ // t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
+
+bool
+test_invalid_special_value (object& x, auto_ptr<database>& db)
+{
+ try
+ {
+ db->persist (x);
+ return false;
+ }
+ catch (const odb::boost::date_time::special_value&)
+ {
+ }
+
+ return true;
+}
+
+bool
+test_out_of_range_value (object& x, auto_ptr<database>& db)
+{
+ try
+ {
+ db->persist (x);
+ return false;
+ }
+ catch (const odb::boost::date_time::value_out_of_range&)
+ {
+ }
+
+ return true;
+}
diff --git a/odb-tests/boost/pgsql/date-time/test.hxx b/odb-tests/boost/pgsql/date-time/test.hxx
new file mode 100644
index 0000000..0b08093
--- /dev/null
+++ b/odb-tests/boost/pgsql/date-time/test.hxx
@@ -0,0 +1,39 @@
+// file : boost/pgsql/date-time/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <vector>
+
+#include <boost/date_time/gregorian/gregorian_types.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object ()
+ {
+ }
+
+ bool
+ operator== (const object& x) const
+ {
+ return
+ id == x.id &&
+ dates == x.dates &&
+ times == x.times &&
+ durations == x.durations;
+ }
+
+ #pragma db id auto
+ unsigned long id;
+
+ std::vector<boost::gregorian::date> dates;
+ std::vector<boost::posix_time::ptime> times;
+ std::vector<boost::posix_time::time_duration> durations;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/boost/pgsql/template/driver.cxx b/odb-tests/boost/pgsql/template/driver.cxx
new file mode 100644
index 0000000..ed2d8a4
--- /dev/null
+++ b/odb-tests/boost/pgsql/template/driver.cxx
@@ -0,0 +1,39 @@
+// file : boost/pgsql/template/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// PLACE TEST DESCRIPTION HERE
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/boost/pgsql/template/template-vc10.vcxproj b/odb-tests/boost/pgsql/template/template-vc10.vcxproj
new file mode 100644
index 0000000..7bc0243
--- /dev/null
+++ b/odb-tests/boost/pgsql/template/template-vc10.vcxproj
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-pgsql-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-pgsql-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-pgsql.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-pgsql.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/boost/pgsql/template/template-vc10.vcxproj.filters b/odb-tests/boost/pgsql/template/template-vc10.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/boost/pgsql/template/template-vc10.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/boost/pgsql/template/template-vc11.vcxproj b/odb-tests/boost/pgsql/template/template-vc11.vcxproj
new file mode 100644
index 0000000..9f8d2f0
--- /dev/null
+++ b/odb-tests/boost/pgsql/template/template-vc11.vcxproj
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-pgsql-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-pgsql-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-pgsql.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-pgsql.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/boost/pgsql/template/template-vc11.vcxproj.filters b/odb-tests/boost/pgsql/template/template-vc11.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/boost/pgsql/template/template-vc11.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/boost/pgsql/template/template-vc12.vcxproj b/odb-tests/boost/pgsql/template/template-vc12.vcxproj
new file mode 100644
index 0000000..acbfc37
--- /dev/null
+++ b/odb-tests/boost/pgsql/template/template-vc12.vcxproj
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-pgsql-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-pgsql-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-pgsql.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-pgsql.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/boost/pgsql/template/template-vc12.vcxproj.filters b/odb-tests/boost/pgsql/template/template-vc12.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/boost/pgsql/template/template-vc12.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/boost/pgsql/template/template-vc8.vcproj b/odb-tests/boost/pgsql/template/template-vc8.vcproj
new file mode 100644
index 0000000..65773c9
--- /dev/null
+++ b/odb-tests/boost/pgsql/template/template-vc8.vcproj
@@ -0,0 +1,354 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-pgsql-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-pgsql.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-pgsql-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-pgsql.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1400 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/boost/pgsql/template/template-vc9.vcproj b/odb-tests/boost/pgsql/template/template-vc9.vcproj
new file mode 100644
index 0000000..db49496
--- /dev/null
+++ b/odb-tests/boost/pgsql/template/template-vc9.vcproj
@@ -0,0 +1,361 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-pgsql-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-pgsql.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-pgsql-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-pgsql.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/boost/pgsql/template/test.hxx b/odb-tests/boost/pgsql/template/test.hxx
new file mode 100644
index 0000000..56eda31
--- /dev/null
+++ b/odb-tests/boost/pgsql/template/test.hxx
@@ -0,0 +1,25 @@
+// file : boost/pgsql/template/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/boost/sqlite/date-time/driver.cxx b/odb-tests/boost/sqlite/date-time/driver.cxx
new file mode 100644
index 0000000..97a7a0c
--- /dev/null
+++ b/odb-tests/boost/sqlite/date-time/driver.cxx
@@ -0,0 +1,208 @@
+// file : boost/sqlite/date-time/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test boost date/time type persistence. SQLite version.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/date_time/gregorian/gregorian.hpp>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+
+using namespace boost::gregorian;
+using namespace boost::posix_time;
+
+using namespace odb::core;
+
+bool
+test_invalid_special_value (object&, auto_ptr<database>&);
+
+bool
+test_out_of_range_value (object&, auto_ptr<database>&);
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ object o;
+
+ // Test all valid date-time mappings.
+ //
+ o.dates.push_back (day_clock::local_day ());
+ o.dates.push_back (date (not_a_date_time));
+ o.dates.push_back (date (max_date_time));
+ o.dates.push_back (date (min_date_time));
+
+ o.times.push_back (second_clock::local_time ());
+ o.times.push_back (not_a_date_time);
+ o.times.push_back (min_date_time);
+ o.times.push_back (max_date_time);
+
+ o.durations.push_back (time_duration (123, 4, 5));
+ o.durations.push_back (not_a_date_time);
+
+ o.u_dates.push_back (day_clock::local_day ());
+ o.u_dates.push_back (date (not_a_date_time));
+
+ // Boost seems to handle 64 bit std::time_t incorrectly.
+ // Insert 32 bit minimum and maximum UNIX time values for now.
+ //
+ // o.u_dates.push_back (date (max_date_time));
+ // o.u_dates.push_back (date (min_date_time));
+ //
+ o.u_dates.push_back (date (2038, 1, 19));
+ o.u_dates.push_back (date (1901, 12, 14));
+
+ o.u_times.push_back (second_clock::local_time ());
+ o.u_times.push_back (not_a_date_time);
+ o.u_times.push_back (ptime (date (1930, 1, 1), time_duration (0, 0, 0)));
+
+ o.s_durations.push_back (time_duration (123, 4, 5));
+ o.s_durations.push_back (time_duration (-12, 3, 4));
+ o.s_durations.push_back (not_a_date_time);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> ol (db->load<object> (o.id));
+ t.commit ();
+
+ assert (*ol == o);
+ }
+
+ {
+ // Test invalid date mappings.
+ //
+ object sv1, sv2;
+ sv1.dates.push_back (date (neg_infin));
+ sv2.dates.push_back (date (pos_infin));
+
+ transaction t (db->begin ());
+ assert (test_invalid_special_value (sv1, db));
+ assert (test_invalid_special_value (sv2, db));
+ t.commit ();
+ }
+
+ {
+ // Test invalid ptime mappings.
+ //
+ object sv1, sv2;
+ sv1.times.push_back (neg_infin);
+ sv2.times.push_back (pos_infin);
+
+ transaction t (db->begin ());
+ assert (test_invalid_special_value (sv1, db));
+ assert (test_invalid_special_value (sv2, db));
+ t.commit ();
+ }
+
+ {
+ // Test invalid time_duration mappings.
+ //
+ object or1, sv1, sv2;
+ or1.durations.push_back (time_duration (0, 0, -1));
+ sv1.durations.push_back (pos_infin);
+ sv2.durations.push_back (neg_infin);
+
+ transaction t (db->begin ());
+ assert (test_out_of_range_value (or1, db));
+ assert (test_invalid_special_value (sv1, db));
+ assert (test_invalid_special_value (sv2, db));
+ t.commit ();
+ }
+
+ {
+ // Test invalid UNIX date mappings.
+ //
+ object sv1, sv2;
+ sv1.u_dates.push_back (date (neg_infin));
+ sv2.u_dates.push_back (date (pos_infin));
+
+ transaction t (db->begin ());
+ assert (test_invalid_special_value (sv1, db));
+ assert (test_invalid_special_value (sv2, db));
+ t.commit ();
+ }
+
+ // Test invalid UNIX times mappings.
+ //
+ {
+ object sv1, sv2;
+ sv1.u_times.push_back (pos_infin);
+ sv2.u_times.push_back (neg_infin);
+
+ transaction t (db->begin ());
+ assert (test_invalid_special_value (sv1, db));
+ assert (test_invalid_special_value (sv2, db));
+ t.commit ();
+ }
+
+ // Test invalid "seconds" duration mappings.
+ //
+ {
+ object sv1, sv2;
+ sv1.s_durations.push_back (pos_infin);
+ sv2.s_durations.push_back (neg_infin);
+
+ transaction t (db->begin ());
+ assert (test_invalid_special_value (sv1, db));
+ assert (test_invalid_special_value (sv2, db));
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
+
+bool
+test_invalid_special_value (object& x, auto_ptr<database>& db)
+{
+ try
+ {
+ db->persist (x);
+ return false;
+ }
+ catch (const odb::boost::date_time::special_value&)
+ {
+ }
+
+ return true;
+}
+
+bool
+test_out_of_range_value (object& x, auto_ptr<database>& db)
+{
+ try
+ {
+ db->persist (x);
+ return false;
+ }
+ catch (const odb::boost::date_time::value_out_of_range&)
+ {
+ }
+
+ return true;
+}
diff --git a/odb-tests/boost/sqlite/date-time/test.hxx b/odb-tests/boost/sqlite/date-time/test.hxx
new file mode 100644
index 0000000..924ce27
--- /dev/null
+++ b/odb-tests/boost/sqlite/date-time/test.hxx
@@ -0,0 +1,57 @@
+// file : boost/sqlite/date-time/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <vector>
+
+#include <boost/date_time/gregorian/gregorian_types.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object ()
+ {
+ }
+
+ bool
+ operator== (const object& x) const
+ {
+ return
+ id == x.id &&
+ dates == x.dates &&
+ times == x.times &&
+ durations == x.durations &&
+ u_dates == x.u_dates &&
+ u_times == x.u_times &&
+ s_durations == x.s_durations;
+ }
+
+ #pragma db id auto
+ unsigned long id;
+
+ std::vector<boost::gregorian::date> dates;
+ std::vector<boost::posix_time::ptime> times;
+ std::vector<boost::posix_time::time_duration> durations;
+
+ // Dates as UNIX time.
+ //
+ #pragma db value_type("INTEGER")
+ std::vector<boost::gregorian::date> u_dates;
+
+ // Times as UNIX time.
+ //
+ #pragma db value_type("INTEGER")
+ std::vector<boost::posix_time::ptime> u_times;
+
+ // Durations as seconds.
+ //
+ #pragma db value_type("INTEGER")
+ std::vector<boost::posix_time::time_duration> s_durations;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/boost/sqlite/template/driver.cxx b/odb-tests/boost/sqlite/template/driver.cxx
new file mode 100644
index 0000000..3e51eef
--- /dev/null
+++ b/odb-tests/boost/sqlite/template/driver.cxx
@@ -0,0 +1,39 @@
+// file : boost/sqlite/template/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// PLACE TEST DESCRIPTION HERE
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/boost/sqlite/template/template-vc10.vcxproj b/odb-tests/boost/sqlite/template/template-vc10.vcxproj
new file mode 100644
index 0000000..f94df08
--- /dev/null
+++ b/odb-tests/boost/sqlite/template/template-vc10.vcxproj
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-sqlite-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-sqlite-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-sqlite.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-sqlite.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/boost/sqlite/template/template-vc10.vcxproj.filters b/odb-tests/boost/sqlite/template/template-vc10.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/boost/sqlite/template/template-vc10.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/boost/sqlite/template/template-vc11.vcxproj b/odb-tests/boost/sqlite/template/template-vc11.vcxproj
new file mode 100644
index 0000000..1c1a279
--- /dev/null
+++ b/odb-tests/boost/sqlite/template/template-vc11.vcxproj
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-sqlite-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-sqlite-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-sqlite.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-sqlite.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/boost/sqlite/template/template-vc11.vcxproj.filters b/odb-tests/boost/sqlite/template/template-vc11.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/boost/sqlite/template/template-vc11.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/boost/sqlite/template/template-vc12.vcxproj b/odb-tests/boost/sqlite/template/template-vc12.vcxproj
new file mode 100644
index 0000000..034b16c
--- /dev/null
+++ b/odb-tests/boost/sqlite/template/template-vc12.vcxproj
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-sqlite-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-sqlite-d.lib;odb-boost-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-sqlite.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;_SCL_SECURE_NO_WARNINGS;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-sqlite.lib;odb-boost.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/boost/sqlite/template/template-vc12.vcxproj.filters b/odb-tests/boost/sqlite/template/template-vc12.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/boost/sqlite/template/template-vc12.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/boost/sqlite/template/template-vc8.vcproj b/odb-tests/boost/sqlite/template/template-vc8.vcproj
new file mode 100644
index 0000000..df92637
--- /dev/null
+++ b/odb-tests/boost/sqlite/template/template-vc8.vcproj
@@ -0,0 +1,354 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-sqlite-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-sqlite.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-sqlite-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-sqlite.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1400 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/boost/sqlite/template/template-vc9.vcproj b/odb-tests/boost/sqlite/template/template-vc9.vcproj
new file mode 100644
index 0000000..32fb152
--- /dev/null
+++ b/odb-tests/boost/sqlite/template/template-vc9.vcproj
@@ -0,0 +1,361 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-sqlite-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-sqlite.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-sqlite-d.lib odb-boost-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-sqlite.lib odb-boost.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/boost/sqlite/template/test.hxx b/odb-tests/boost/sqlite/template/test.hxx
new file mode 100644
index 0000000..b9daed6
--- /dev/null
+++ b/odb-tests/boost/sqlite/template/test.hxx
@@ -0,0 +1,25 @@
+// file : boost/sqlite/template/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#endif // TEST_HXX
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..2fb5824
--- /dev/null
+++ b/odb-tests/build/bootstrap.build
@@ -0,0 +1,9 @@
+# file : build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = odb-tests
+
+using version
+using config
+using dist
+using test
diff --git a/odb-tests/build/export.build b/odb-tests/build/export.build
new file mode 100644
index 0000000..3c073d3
--- /dev/null
+++ b/odb-tests/build/export.build
@@ -0,0 +1,9 @@
+# file : build/export.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+$out_root/
+{
+ include libcommon/
+}
+
+export $out_root/libcommon/$import.target
diff --git a/odb-tests/build/root.build b/odb-tests/build/root.build
new file mode 100644
index 0000000..41f0556
--- /dev/null
+++ b/odb-tests/build/root.build
@@ -0,0 +1,336 @@
+# file : build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+cxx.std = latest
+
+using cxx
+
+hxx{*}: extension = hxx
+ixx{*}: extension = ixx
+txx{*}: extension = txx
+cxx{*}: extension = cxx
+
+define sql: file
+sql{*}: extension = sql
+
+define xml: file
+xml{*}: extension = xml
+
+skeleton = ($build.mode == 'skeleton')
+
+# @@ TMP: remove once no longer used by libodb-<db>.
+#
+config [bool] config.odb_tests.develop ?= false
+
+# List of the identifiers of the databases to compile and run the tests
+# against. The valid identifiers are mysql, sqlite, pgsql, oracle, and mssql.
+#
+# Note: can be specified by the user but is also conditionally reflected by
+# the libodb-* libraries' tests manifest values.
+#
+config [string_set] config.odb_tests.database
+
+databases = [strings] ($defined(config.odb_tests.database) \
+ ? $config.odb_tests.database \
+ : )
+
+assert ($skeleton || $size($databases) > 0) \
+ 'at least one database must be configured via config.odb_tests.database variable'
+
+mysql = false
+sqlite = false
+pgsql = false
+oracle = false
+mssql = false
+
+for db: $databases
+{
+ switch $db
+ {
+ case 'mysql'
+ mysql = true
+
+ case 'sqlite'
+ sqlite = true
+
+ case 'pgsql'
+ pgsql = true
+
+ case 'oracle'
+ oracle = true
+
+ case 'mssql'
+ mssql = true
+
+ default
+ fail "invalid database '$db' specified in config.odb_tests.database value"
+ }
+}
+
+# If true, then build and run the test drivers in the dynamic multi-database
+# mode.
+#
+# Note: do not assign the default value so that if it's not specified by the
+# user, then it won't be saved into config.build. Failed that, if we need to
+# reconfigure a single-database configuration into multi (or vise-versa), then
+# we will have to (remember to) override the saved multi_database value
+# explicitly.
+#
+config [bool, config.report.variable=multi] config.odb_tests.multi_database
+
+multi = ($defined(config.odb_tests.multi_database) \
+ ? $config.odb_tests.multi_database \
+ : $size($databases) > 1)
+
+assert ($skeleton || $multi || $size($databases) == 1) \
+ 'only one database can be configured if config.odb_tests.multi_database value is false'
+
+# If true, then this package is enabled as an external test for libodb library
+# (see libodb's manifest for details).
+#
+# Note that this variable is not used in this package itself.
+#
+config [bool] config.odb_tests.libodb_test ?= false
+
+# Database connections.
+#
+
+# PostgreSQL
+#
+# The database user. Note that the named user must be allowed to connect to
+# the database server without specifying credentials.
+#
+config [string] config.odb_tests.pgsql.user ?= 'odb_test'
+
+# The database name. Note that it WILL BE MODIFIED by the tests.
+#
+config [string] config.odb_tests.pgsql.database ?= 'odb_test'
+
+# The database host or directory of Unix-domain socket. Leaving this variable
+# undefined results in using Unix-domain sockets. Machines without Unix-domain
+# sockets will connect to localhost.
+#
+config [string] config.odb_tests.pgsql.host
+
+# The database port or the socket file name extension for Unix-domain
+# connections.
+#
+# For example, specifying:
+#
+# config.odb_tests.pgsql.host=/var/run/postgresql
+# config.odb_tests.pgsql.port=5433
+#
+# Will result in the /var/run/postgresql/.s.PGSQL.5433 socket being used.
+#
+config [string] config.odb_tests.pgsql.port
+
+# If true, then assume that libodb-pgsql supports the bulk operations.
+#
+# Note: config.odb_tests.pgsql.bulk_default is reflected from manifest.
+#
+config [bool] config.odb_tests.pgsql.bulk_default ?= false
+config [bool] config.odb_tests.pgsql.bulk ?= ($cxx.target.class != 'windows' && \
+ $config.odb_tests.pgsql.bulk_default)
+pgsql_bulk = $config.odb_tests.pgsql.bulk
+
+# MySQL
+#
+# The database user.
+#
+config [string] config.odb_tests.mysql.user ?= 'odb_test'
+
+# The database password.
+#
+config [string] config.odb_tests.mysql.passwd
+
+# The database name. Note that it WILL BE MODIFIED by the tests.
+#
+config [string] config.odb_tests.mysql.database ?= 'odb_test'
+
+# The database host.
+#
+config [string] config.odb_tests.mysql.host
+
+# The database port.
+#
+config [uint64] config.odb_tests.mysql.port
+
+# The database socket path.
+#
+config [path] config.odb_tests.mysql.socket
+
+if! $skeleton
+{
+ if ($cxx.target.system == 'win32-msvc')
+ cxx.poptions += -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS
+
+ switch $cxx.class
+ {
+ case 'gcc'
+ {
+ cxx.coptions += -Wno-unknown-pragmas
+ }
+ case 'msvc'
+ {
+ cxx.coptions += /wd4068 /wd4251 /wd4275 /wd4800
+ }
+ }
+
+ # Import odb that we are testing.
+ #
+ import! [metadata] odb = odb%exe{odb}
+
+ # Import the mysql client for creating the database schemas, etc.
+ #
+ if $mysql
+ {
+ import! mysql_client = mysql-client%exe{mysql}
+ testscript{*}: mysql_client = $mysql_client
+ }
+
+ # Import the psql client for creating the database schemas, etc.
+ #
+ if $pgsql
+ {
+ import! pgsql_client = psql%exe{psql}
+ testscript{*}: pgsql_client = $pgsql_client
+ }
+
+ # Notes:
+ #
+ # - The second prerequisite ($<[1]) is expected to be a metadata library
+ # target which we will use to extract the poptions variable value for
+ # specifying the contained options on the ODB compiler command line.
+ #
+ # - The additional options for the ODB compiler command line must be
+ # specified via the odb_options variable.
+ #
+ # - If the pre/post-migration SQL files are being generated, then the
+ # version numbers the object model is being migrated through must be
+ # specified via the schema_versions variable in the NNN form (001 002,
+ # etc; see the implementation for details).
+ #
+ # Also note that we need ((-.+)?) instead of just (-.+)? because we use this
+ # capture as a back-reference in the pattern.
+ #
+ [rule_name=odb_compile] \
+ <hxx{~'/(.+)-odb((-.+)?)/'} \
+ ixx{~'/\1-odb\2/'} \
+ cxx{~'/\1-odb\2/'}>: hxx{~'/\1/'} libue{~'/.+-meta/'} $odb
+ {{
+ pops = $cxx.lib_poptions($<[1])
+ depdb hash $pops
+
+ hp = $path($>[0])
+ bn = $base($leaf($hp))
+ db = $regex.replace($bn, '.+-odb(-(.+))?', '\2')
+
+ if ($db == '') # *-odb.?xx target group?
+ db = ($multi ? 'common' : $databases[0])
+ end
+
+ # If the external SQL schema file will be generated, then add it as a
+ # dynamic target group member.
+ #
+ # @@ BUILD2 Probably we should add support for --generate-dep ODB compiler
+ # option. Then presumably this will be take care of automatically.
+ #
+ # We assume that the '--generate-schema' and '--schema-format' strings
+ # will never appear as standalone option values.
+ #
+ if ($db != 'common' && $regex.find_match($odb_options, '--generate-schema'))
+ schema_format = ($db == 'sqlite' ? 'embedded' : 'sql')
+
+ for o: $odb_options
+ if ($o == '--schema-format')
+ schema_format = [null] # Indicate that the schema format comes next.
+ elif ($schema_format == [null])
+ schema_format = $o
+ end
+ end
+ else
+ schema_format = ''
+ end
+
+ ts = ($schema_format == 'sql' \
+ ? "$directory($hp)/$regex.replace($bn, '(.+)-odb(-.+)?', '\1\2').sql" \
+ : '')
+
+ # If the object model change log and/or migration SQL files will be
+ # generated, then add them as a dynamic target group member.
+ #
+ changelog = ''
+ init_changelog = false
+ suppress_migration = false
+
+ for o: $odb_options
+ if ($o == '--changelog')
+ changelog = [null] # Indicate that the changelog path comes next.
+ elif ($changelog == [null])
+ changelog = $o
+ elif ($o == '--init-changelog')
+ init_changelog = true
+ elif ($o == '--suppress-migration')
+ suppress_migration = true
+ end
+ end
+
+ if ($changelog != '')
+ if ($init_changelog)
+ ts = ($ts == '' ? "$changelog" : "$ts$\n$changelog")
+ end
+
+ if ($schema_format == 'sql' && !$suppress_migration)
+ n = $base($leaf($path($<[0])))
+
+ # Note that it's not easy to deduce the object model migration files
+ # list. Thus, we expect the version numbers the object model is being
+ # migrated through to be explicitly specified via the schema_versions
+ # variable.
+ #
+ for v: $schema_versions
+ ns = "$out_base/$n-$v-pre.sql$\n$out_base/$n-$v-post.sql"
+ ts = ($ts == '' ? "$ns" : "$ts$\n$ns")
+ end
+ end
+ end
+
+ depdb dyndep --dyn-target --target-what 'generated schema' --format lines \
+ -- echo "$ts"
+
+ $odb --std c++11 \
+ ($multi ? --multi-database dynamic : ) \
+ --database $db \
+ --output-dir $out_base \
+ $odb_options \
+ "-I$src_base" \
+ $pops \
+ $path($<[0])
+ }}
+
+ # Every exe{} in this project is by default a test.
+ #
+ exe{*}: test = true
+
+ # Specify the test target for cross-testing.
+ #
+ test.target = $cxx.target
+}
+
+# The helper targets which can be used as prerequisites of test drivers
+# which require either a specific database client or multiple clients for
+# all the enabled databases.
+#
+alias{mysql-client}: $mysql_client:
+{
+ include = $mysql
+ clean = false
+}
+
+alias{pgsql-client}: $pgsql_client:
+{
+ include = $pgsql
+ clean = false
+}
+
+alias{database-client}: alias{mysql-client pgsql-client}
diff --git a/odb-tests/buildfile b/odb-tests/buildfile
new file mode 100644
index 0000000..82e8fe9
--- /dev/null
+++ b/odb-tests/buildfile
@@ -0,0 +1,12 @@
+# file : buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: libcommon/ common/ doc{README.md} legal{GPLv2 LICENSE} manifest
+
+./: mysql/: include = ($mysql && !$multi)
+./: sqlite/: include = ($sqlite && !$multi)
+./: pgsql/: include = ($pgsql && !$multi)
+
+./: evolution/: include = (!$multi)
+
+./: testscript{*}: include = adhoc
diff --git a/odb-tests/common/access/buildfile b/odb-tests/common/access/buildfile
new file mode 100644
index 0000000..f1264d9
--- /dev/null
+++ b/odb-tests/common/access/buildfile
@@ -0,0 +1,45 @@
+# file : common/access/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix access_ \
+ --generate-schema \
+ --accessor-regex '#(.+)#Get\u\1#' \
+ --modifier-regex '#(.+)#Set\u\1#'
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+# @@ BUILD2: Eventually we should be able to mark it as test.input once
+# this is supported for testscript tests.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/access/driver.cxx b/odb-tests/common/access/driver.cxx
new file mode 100644
index 0000000..b40e73c
--- /dev/null
+++ b/odb-tests/common/access/driver.cxx
@@ -0,0 +1,262 @@
+// file : common/access/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test accessor/modifier expressions.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Test basic accessor/modifier functionality.
+ //
+ {
+ using namespace test1;
+
+ object o (1, 623, 723);
+ o.i1 () = 123;
+ o.i2 (223);
+ o.i3 () = 323;
+ o.i4 () = 423;
+ o.set_i5 (523);
+ o.s1 ("1bc");
+ memcpy (o.b1 (), "123456789012345", 16);
+ o.b2 ("123456789012345");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id ()));
+ t.commit ();
+
+ assert (o == *p);
+ }
+ }
+
+ // Test composite accessor/modifier functionality.
+ //
+ {
+ using namespace test2;
+
+ object o (1);
+
+ o.v1 () = value (1123, 1234);
+ o.v2 (value (2123, 2234));
+ o.v3_i1 (3123);
+ o.v3_i2 (3223);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id ()));
+ t.commit ();
+
+ assert (o == *p);
+ }
+ }
+
+ // Test object pointer accessor/modifier functionality.
+ //
+ {
+ using namespace test3;
+
+ object2 o (1);
+ o.p1 ().reset (new object1 (1));
+ o.p2 (object1_ptr (new object1 (2)));
+
+ {
+ transaction t (db->begin ());
+ const object1_ptr& ptr (o.p1 ());
+ db->persist (ptr);
+ db->persist (o.p2 ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object2> p (db->load<object2> (o.id ()));
+ t.commit ();
+
+ assert (p->p1 ()->id () == o.p1 ()->id () &&
+ p->p2 ()->id () == o.p2 ()->id ());
+ }
+ }
+
+ // Test container accessor/modifier functionality.
+ //
+ {
+ using namespace test4;
+
+ object o (1);
+ o.c1 ().push_back (1123);
+ o.c1 ().push_back (1124);
+ o.c1 ().push_back (1125);
+
+ {
+ std::vector<int> v;
+ v.push_back (2123);
+ v.push_back (2124);
+ v.push_back (2125);
+ o.c2 (v);
+ }
+
+ o.v1 ().c1 ().push_back (1123);
+ o.v1 ().c1 ().push_back (1124);
+ o.v1 ().c1 ().push_back (1125);
+
+ {
+ std::vector<int> v;
+ v.push_back (2123);
+ v.push_back (2124);
+ v.push_back (2125);
+ o.v1 ().c2 (v);
+ }
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id ()));
+ t.commit ();
+
+ assert (o == *p);
+ }
+ }
+
+ // Test id accessor/modifier functionality.
+ //
+ {
+ using namespace test5;
+
+ object1 o1;
+ object2 o2;
+ object3 o3;
+ object4 o4;
+ o4.id (uuid ("\x60\x1D\x17\xF0-\x60\x05-\x47\x23-\x95\x37-"
+ "\xC1\xF8\x94\x41\x2B\xEC"));
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o3);
+ db->persist (o4);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object1> p1 (db->load<object1> (o1.id ()));
+ unique_ptr<object2> p2 (db->load<object2> (o2.id ()));
+ unique_ptr<object3> p3 (db->load<object3> (o3.id_));
+ unique_ptr<object4> p4 (db->load<object4> (o4.id ()));
+ t.commit ();
+ }
+ }
+
+ // Test version accessor/modifier functionality.
+ //
+ {
+ using namespace test6;
+
+ object1 o1 (1);
+ object2 o2;
+ object3 o3 (1);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o3);
+ t.commit ();
+
+ assert (o1.version () == 1);
+ assert (o2.version () == 1);
+ assert (o3.version_ == 1);
+ }
+
+ {
+ transaction t (db->begin ());
+ db->update (o1);
+ db->update (o2);
+ db->update (o3);
+ t.commit ();
+
+ assert (o1.version () == 2);
+ assert (o2.version () == 2);
+ assert (o3.version_ == 2);
+ }
+ }
+
+ // Test basic accessor/modifier functionality.
+ //
+ {
+ using namespace test7;
+
+ object o (1);
+ o.i1 () = 123;
+ o.set_i2 (223);
+ o.setI3 (323);
+ o.seti4 (423);
+ o.i5 () = 523;
+ o.i6 () = 623;
+ o.SetI7 (723);
+ memcpy (o.b1 (), "123456789012345", 16);
+ o.b2 ("123456789012345");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id_));
+ t.commit ();
+
+ assert (o == *p);
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/access/test.hxx b/odb-tests/common/access/test.hxx
new file mode 100644
index 0000000..3a3424d
--- /dev/null
+++ b/odb-tests/common/access/test.hxx
@@ -0,0 +1,592 @@
+// file : common/access/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <vector>
+#include <cstring> // std::memcpy, std::memcmp, std::memset
+#include <memory> // std::unique_ptr
+#include <utility> // std::move
+
+#include <odb/core.hxx>
+
+#ifdef ODB_COMPILER
+# if defined(ODB_DATABASE_MYSQL)
+# define BINARY16_TYPE "BINARY(16)"
+# elif defined(ODB_DATABASE_SQLITE)
+# define BINARY16_TYPE "BLOB"
+# elif defined(ODB_DATABASE_PGSQL)
+# define BINARY16_TYPE "BYTEA"
+# elif defined(ODB_DATABASE_ORACLE)
+# define BINARY16_TYPE "RAW(16)"
+# elif defined(ODB_DATABASE_MSSQL)
+# define BINARY16_TYPE "BINARY(16)"
+# elif defined(ODB_DATABASE_COMMON)
+# define BINARY16_TYPE ""
+# else
+# error unknown database
+# endif
+#endif
+
+// Test basic accessor/modifier functionality.
+//
+#pragma db namespace table("t1_")
+namespace test1
+{
+ #pragma db object
+ struct object
+ {
+ object (): i6_ (0), i7_ (0) {}
+ object (unsigned long id, int i6, int i7): id_ (id), i6_ (i6), i7_ (i7) {}
+
+ public:
+ unsigned long id () const {return id_;}
+ void id (unsigned long id) {id_ = id;}
+ private:
+ #pragma db id access(id)
+ unsigned long id_;
+
+ public:
+ int i1 () const {return i1_;}
+ int& i1 () {return i1_;}
+ private:
+ #pragma db access(i1)
+ int i1_;
+
+ public:
+ int i2 () const {return i2_;}
+ void i2 (int i2) {i2_ = i2;}
+ private:
+ #pragma db access(i2)
+ int i2_;
+
+ // Prefer reference modifier.
+ //
+ public:
+ int i3 () const {return i3_;}
+ int& i3 () {return i3_;}
+ void i3 (int i3);
+ private:
+ #pragma db access(i3)
+ int i3_;
+
+ // Prefer reference modifier (reverse function order).
+ //
+ public:
+ int i4 () const {return i4_;}
+ void i4 (int i4);
+ int& i4 () {return i4_;}
+ private:
+ #pragma db access(i4)
+ int i4_;
+
+ public:
+ int get_i5 () const {return i5_;}
+ void set_i5 (int i5) {i5_ = i5;}
+ private:
+ #pragma db get(get_i5) set(set_i5)
+ int i5_;
+
+ // Const member via reference.
+ //
+ public:
+ const int& i6 () const {return i6_;}
+ private:
+ #pragma db get(i6) set(const_cast<int&> (this.i6 ()))
+ const int i6_;
+
+ // Const member via modifier.
+ //
+ public:
+ int i7 () const {return i7_;}
+ void i7 (int i7) const {const_cast<int&> (i7_) = i7;}
+ private:
+ #pragma db access(i7)
+ const int i7_;
+
+ public:
+ const char* s1 () const {return s1_.c_str ();}
+ void s1 (const char* s1) {s1_ = s1;}
+ //std::string s1 () const {return s1_;}
+ //void s1 (std::string s1) {s1_ = s1;}
+ private:
+ #pragma db get(s1) set(s1((?).c_str ()))
+ //#pragma db access(s1)
+ std::string s1_;
+
+ // Array member via ref.
+ //
+ public:
+ const char* b1 () const {return b1_;}
+ char* b1 () {return b1_;}
+ private:
+ #pragma db type(BINARY16_TYPE) access(b1)
+ char b1_[16];
+
+ // Array member via modifier.
+ //
+ public:
+ const char* b2 () const {return b2_;}
+ void b2 (const char* b2) {std::memcpy (b2_, b2, sizeof (b2_));}
+ private:
+ #pragma db type(BINARY16_TYPE) access(b2)
+ char b2_[16];
+
+ public:
+ bool operator== (const object& o) const
+ {
+ return id_ == o.id_ &&
+ i1_ == o.i1_ &&
+ i2_ == o.i2_ &&
+ i3_ == o.i3_ &&
+ i4_ == o.i4_ &&
+ i5_ == o.i5_ &&
+ i6_ == o.i6_ &&
+ i7_ == o.i7_ &&
+ s1_ == o.s1_ &&
+ std::memcmp (b1_, o.b1_, sizeof (b1_)) == 0 &&
+ std::memcmp (b2_, o.b2_, sizeof (b2_)) == 0;
+ }
+ };
+}
+
+// Test composite accessor/modifier functionality.
+//
+#pragma db namespace table("t2_")
+namespace test2
+{
+ #pragma db value
+ struct value
+ {
+ value () {}
+ value (int i1, int i2): i1_ (i1), i2_ (i2) {}
+
+ bool operator== (const value& v) const
+ {
+ return i1_ == v.i1_ && i2_ == v.i2_;
+ }
+
+ public:
+ int i1 () const {return i1_;}
+ int& i1 () {return i1_;}
+ private:
+ #pragma db access(i1)
+ int i1_;
+
+ public:
+ int i2 () const {return i2_;}
+ void i2 (int i2) {i2_ = i2;}
+ private:
+ #pragma db access(i2)
+ int i2_;
+ };
+
+ #pragma db object
+ struct object
+ {
+ object () {}
+ object (unsigned long id): id_ (id) {}
+
+ bool operator== (const object& o) const
+ {
+ return id_ == o.id_ &&
+ v1_ == o.v1_ &&
+ v2_ == o.v2_ &&
+ v3_ == o.v3_;
+ }
+
+ public:
+ unsigned long id () const {return id_;}
+ void id (unsigned long id) {id_ = id;}
+ private:
+ #pragma db id access(id)
+ unsigned long id_;
+
+ public:
+ const value& v1 () const {return v1_;}
+ value& v1 () {return v1_;}
+ private:
+ #pragma db access(v1)
+ value v1_;
+
+ public:
+ const value& v2 () const {return v2_;}
+ void v2 (const value& v2) {v2_ = v2;}
+ private:
+ #pragma db access(v2)
+ value v2_;
+
+ public:
+ int v3_i1 () const {return v3_.i1 ();}
+ int v3_i2 () const {return v3_.i2 ();}
+ void v3_i1 (int i1) {v3_.i1 () = i1;}
+ void v3_i2 (int i2) {v3_.i2 (i2);}
+ private:
+ #pragma db get(test2::value (this.v3_i1 (), this.v3_i2 ())) \
+ set(this.v3_i1 ((?).i1 ()); this.v3_i2 ((?).i2 ()))
+ value v3_;
+ };
+}
+
+// Test object pointer accessor/modifier functionality.
+//
+#pragma db namespace table("t3_")
+namespace test3
+{
+ struct object1;
+
+ typedef std::unique_ptr<object1> object1_ptr;
+
+ #pragma db object pointer(object1_ptr)
+ struct object1
+ {
+ object1 () {}
+ object1 (unsigned long id): id_ (id) {}
+
+ public:
+ unsigned long id () const {return id_;}
+ void id (unsigned long id) {id_ = id;}
+ private:
+ #pragma db id access(id)
+ unsigned long id_;
+ };
+
+ #pragma db object
+ struct object2
+ {
+ object2 () {}
+ object2 (unsigned long id): id_ (id) {}
+
+ public:
+ unsigned long id () const {return id_;}
+ void id (unsigned long id) {id_ = id;}
+ private:
+ #pragma db id access(id)
+ unsigned long id_;
+
+ public:
+ const object1_ptr& p1 () const {return p1_;}
+ object1_ptr& p1 () {return p1_;}
+ private:
+ #pragma db access(p1)
+ object1_ptr p1_;
+
+ public:
+ const object1_ptr& p2 () const {return p2_;}
+
+ void p2 (object1_ptr p2) {p2_ = std::move (p2);}
+
+ private:
+ #pragma db get(p2) set(p2 (std::move (?)))
+
+ object1_ptr p2_;
+ };
+}
+
+// Test container accessor/modifier functionality.
+//
+#pragma db namespace table("t4_")
+namespace test4
+{
+ #pragma db value
+ struct value
+ {
+ value (): c3_ (3, 999) {}
+ value (int v): c1_ (3, v), c2_ (3, v + 1), c3_ (3, v + 2) {}
+
+ bool operator== (const value& v) const
+ {
+ return c1_ == v.c1_ && c2_ == v.c2_ && c3_ == v.c3_;
+ }
+
+ public:
+ const std::vector<int>& c1 () const {return c1_;}
+ std::vector<int>& c1 () {return c1_;}
+ private:
+ #pragma db access(c1)
+ std::vector<int> c1_;
+
+ public:
+ const std::vector<int>& c2 () const {return c2_;}
+ void c2 (const std::vector<int>& c2) {c2_ = c2;}
+ private:
+ #pragma db access(c2)
+ std::vector<int> c2_;
+
+ public:
+ const std::vector<int> c3_;
+ };
+
+ #pragma db object
+ struct object
+ {
+ object () {}
+ object (unsigned long id): id_ (id), c3_ (3, 3123), v2_ (2123) {}
+
+ bool operator== (const object& o) const
+ {
+ return id_ == o.id_ &&
+ c1_ == o.c1_ &&
+ c2_ == o.c2_ &&
+ c3_ == o.c3_ &&
+ v1_ == o.v1_ &&
+ v2_ == o.v2_;
+ }
+
+ public:
+ unsigned long id () const {return id_;}
+ void id (unsigned long id) {id_ = id;}
+ private:
+ #pragma db id access(id)
+ unsigned long id_;
+
+ public:
+ const std::vector<int>& c1 () const {return c1_;}
+ std::vector<int>& c1 () {return c1_;}
+ private:
+ #pragma db access(c1)
+ std::vector<int> c1_;
+
+ public:
+ const std::vector<int>& c2 () const {return c2_;}
+ void c2 (const std::vector<int>& c2) {c2_ = c2;}
+ private:
+ #pragma db access(c2)
+ std::vector<int> c2_;
+
+ public:
+ const std::vector<int>& c3 () const {return c3_;}
+ private:
+ #pragma db get(c3) set(const_cast<std::vector<int>&> (this.c3 ()))
+ const std::vector<int> c3_;
+
+ public:
+ const value& v1 () const {return v1_;}
+ value& v1 () {return v1_;}
+ private:
+ #pragma db access(v1)
+ value v1_;
+
+ public:
+ const value v2_;
+ };
+}
+
+// Test id accessor/modifier functionality.
+//
+#pragma db namespace table("t5_")
+namespace test5
+{
+ #pragma db object
+ struct object1
+ {
+ object1 (): id_ (0) {}
+
+ public:
+ unsigned long id () const {return id_;}
+ void id (unsigned long id) {id_ = id;}
+ private:
+ #pragma db id auto access(id)
+ unsigned long id_;
+ };
+
+ #pragma db object
+ struct object2
+ {
+ object2 (): id_ (0) {}
+
+ public:
+ unsigned long id () const {return id_;}
+ unsigned long& id () {return id_;}
+ private:
+ #pragma db id auto access(id)
+ unsigned long id_;
+ };
+
+ #pragma db object
+ struct object3
+ {
+ object3 (): id_ (0) {}
+
+ #pragma db id auto
+ const unsigned long id_;
+ };
+
+ #pragma db value
+ struct uuid
+ {
+ uuid () {std::memset (data_, 0, sizeof (data_));}
+ explicit uuid (const char* d) {data (d);}
+
+ public:
+ const char* data () const {return data_;}
+ void data (const char* d) {std::memcpy (data_, d, sizeof (data_));}
+ private:
+ #pragma db type(BINARY16_TYPE) column("") access(data)
+ char data_[16];
+ };
+
+ #pragma db object
+ struct object4
+ {
+ public:
+ const uuid& id () const {return id_;}
+ void id (const uuid& id) {id_ = id;}
+ private:
+ #pragma db id access(id)
+ uuid id_;
+ };
+}
+
+// Test version accessor/modifier functionality.
+//
+#pragma db namespace table("t6_")
+namespace test6
+{
+ #pragma db object optimistic
+ struct object1
+ {
+ object1 (unsigned long id = 0): id_ (id), version_ (0) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ public:
+ unsigned long version () const {return version_;}
+ void version (unsigned long version) {version_ = version;}
+ private:
+ #pragma db version access(version)
+ unsigned long version_;
+ };
+
+ #pragma db object optimistic
+ struct object2
+ {
+ object2 (): version_ (0) {}
+
+ #pragma db id auto
+ unsigned long id_;
+
+ public:
+ unsigned long version () const {return version_;}
+ unsigned long& version () {return version_;}
+ private:
+ #pragma db version access(version)
+ unsigned long version_;
+ };
+
+ #pragma db object optimistic
+ struct object3
+ {
+ object3 (unsigned long id = 0): id_ (id), version_ (0) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db version
+ const unsigned long version_;
+ };
+}
+
+// Test automatic discovery of accessor/modifier functions.
+//
+#pragma db namespace table("t7_")
+namespace test7
+{
+ #pragma db object
+ struct object
+ {
+ object () {}
+ object (unsigned long id): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ public:
+ int i1 () const {return i1_;}
+ int& i1 () {return i1_;}
+ private:
+ int i1_;
+
+ public:
+ const int& get_i2 () const {return i2_;}
+ void set_i2 (int i2) {i2_ = i2;}
+ private:
+ int i2_;
+
+ public:
+ const int& getI3 () const {return i3_;}
+ void setI3 (const int& i3) {i3_ = i3;}
+ private:
+ int i3_;
+
+ public:
+ int geti4 () const {return i4;}
+ int seti4 (int v) {int r (i4); i4 = v; return r;}
+ private:
+ int i4;
+
+ // Prefer reference modifier.
+ //
+ public:
+ int i5 () const {return i5_;}
+ int& i5 () {return i5_;}
+ void i5 (int i5);
+ private:
+ int i5_;
+
+ // Prefer reference modifier (reverse function order).
+ //
+ public:
+ int i6 () const {return i6_;}
+ void i6 (int i6);
+ int& i6 () {return i6_;}
+ private:
+ int i6_;
+
+ // Custom accessor/modifier regex.
+ //
+ public:
+ int GetI7 () const {return i7_;}
+ void SetI7 (int i7) {i7_ = i7;}
+ private:
+ int i7_;
+
+ // Array member via ref.
+ //
+ public:
+ const char* b1 () const {return b1_;}
+ char* b1 () {return b1_;}
+ private:
+ #pragma db type(BINARY16_TYPE)
+ char b1_[16];
+
+ // Array member via modifier.
+ //
+ public:
+ const char* b2 () const {return b2_;}
+ void b2 (const char* b2) {std::memcpy (b2_, b2, sizeof (b2_));}
+ private:
+ #pragma db type(BINARY16_TYPE)
+ char b2_[16];
+
+ public:
+ bool operator== (const object& o) const
+ {
+ return id_ == o.id_ &&
+ i1_ == o.i1_ &&
+ i2_ == o.i2_ &&
+ i3_ == o.i3_ &&
+ i4 == o.i4 &&
+ i5_ == o.i5_ &&
+ i6_ == o.i6_ &&
+ i7_ == o.i7_ &&
+ std::memcmp (b1_, o.b1_, sizeof (b1_)) == 0 &&
+ std::memcmp (b2_, o.b2_, sizeof (b2_)) == 0;
+ }
+ };
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/access/testscript b/odb-tests/common/access/testscript
new file mode 100644
index 0000000..04f0ec0
--- /dev/null
+++ b/odb-tests/common/access/testscript
@@ -0,0 +1,33 @@
+# file : common/access/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/as/buildfile b/odb-tests/common/as/buildfile
new file mode 100644
index 0000000..dcdc961
--- /dev/null
+++ b/odb-tests/common/as/buildfile
@@ -0,0 +1,49 @@
+# file : common/as/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix as_ \
+ --generate-schema \
+ --generate-query \
+ --generate-session
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# @@ BUILD@ Temporarily suppress the following warning:
+#
+# test-odb.cxx(6234): warning C4244: 'argument': conversion from 'id_type::value_type' to 'test5::version_type::value_type', possible loss of data
+#
+if ($cxx.class == 'msvc')
+ cxx.coptions += /wd4244
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/as/driver.cxx b/odb-tests/common/as/driver.cxx
new file mode 100644
index 0000000..578eb23
--- /dev/null
+++ b/odb-tests/common/as/driver.cxx
@@ -0,0 +1,348 @@
+// file : common/as/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test C++ type mapping (#pragma map type as ...).
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/session.hxx>
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Test basic type mapping functionality.
+ //
+ {
+ using namespace test1;
+
+ object o1 (true, green, 123, 234);
+ o1.m[false] = 123;
+ o1.v.push_back (o1.ip);
+ o1.cv.push_back (red);
+ o1.cv.push_back (green);
+
+ object o2 (false, blue, 234, 456);
+ o2.m[true] = 234;
+ o2.v.push_back (o2.ip);
+ o2.cv.push_back (green);
+ o2.cv.push_back (blue);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p1 (db->load<object> (o1.id));
+ unique_ptr<object> p2 (db->load<object> (o2.id));
+ t.commit ();
+
+ assert (*p1 == o1);
+ assert (*p2 == o2);
+ }
+
+ o1.b = false;
+ o1.c = blue;
+ o1.ip.first++;
+ o1.ip.second--;
+ o1.m[false]++;
+ o1.m[true] = 234;
+ o1.v.back () = o1.ip;
+ o1.cv.modify_front () = green;
+ o1.cv.push_back (red);
+
+ o2.b = true;
+ o2.c = red;
+ o2.ip.first--;
+ o2.ip.second++;
+ o2.m[true]--;
+ o2.m[false] = 345;
+ o2.v.push_back (o2.ip);
+ o2.cv.pop_back ();
+
+ {
+ transaction t (db->begin ());
+ db->update (o1);
+ db->update (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p1 (db->load<object> (o1.id));
+ unique_ptr<object> p2 (db->load<object> (o2.id));
+ t.commit ();
+
+ assert (*p1 == o1);
+ assert (*p2 == o2);
+ }
+ }
+
+ // Test wrapped simple type mapping.
+ //
+ {
+ using namespace test2;
+
+ object o1;
+ o1.v.push_back (null_bool ());
+ o1.v.push_back (false);
+
+ object o2;
+ o2.b = true;
+ o2.v.push_back (true);
+ o2.v.push_back (null_bool ());
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p1 (db->load<object> (o1.id));
+ unique_ptr<object> p2 (db->load<object> (o2.id));
+ t.commit ();
+
+ assert (*p1 == o1);
+ assert (*p2 == o2);
+ }
+
+ o1.b = false;
+ o1.v[0] = true;
+ o1.v[1].reset ();
+
+ o2.b.reset ();
+ o2.v.push_back (false);
+
+ {
+ transaction t (db->begin ());
+ db->update (o1);
+ db->update (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p1 (db->load<object> (o1.id));
+ unique_ptr<object> p2 (db->load<object> (o2.id));
+ t.commit ();
+
+ assert (*p1 == o1);
+ assert (*p2 == o2);
+ }
+ }
+
+ // Test wrapped composite type mapping.
+ //
+ {
+ using namespace test3;
+
+ object o1;
+ o1.ip = intp (0, 0); // NULL
+ o1.npv.push_back (o1.np);
+ o1.ipv.push_back (o1.ip);
+
+ object o2;
+ o2.np = intp (123, 234);
+ o1.ip = intp (234, 123);
+ o2.npv.push_back (o2.np);
+ o2.ipv.push_back (o2.ip);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p1 (db->load<object> (o1.id));
+ unique_ptr<object> p2 (db->load<object> (o2.id));
+ t.commit ();
+
+ assert (*p1 == o1);
+ assert (*p2 == o2);
+ }
+
+ o1.np = o1.npv[0] = intp (234, 456);
+ o1.ip = o1.ipv.modify_at (0) = intp (456, 234);
+
+ o2.np.reset ();
+ o2.npv[0].reset ();
+ o2.ip = o2.ipv.modify_at (0) = intp (0, 0); // NULL
+
+ {
+ transaction t (db->begin ());
+ db->update (o1);
+ db->update (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p1 (db->load<object> (o1.id));
+ unique_ptr<object> p2 (db->load<object> (o2.id));
+ t.commit ();
+
+ assert (*p1 == o1);
+ assert (*p2 == o2);
+ }
+ }
+
+ // Test id type mapping.
+ //
+ {
+ using namespace test4;
+
+ object o1 (123);
+ o1.v.push_back (1);
+ o1.v.push_back (2);
+ o1.v.push_back (3);
+
+ object o2 (234);
+ o2.v.push_back (3);
+ o2.v.push_back (2);
+ o2.v.push_back (1);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p1 (db->load<object> (o1.id));
+ unique_ptr<object> p2 (db->load<object> (o2.id));
+ t.commit ();
+
+ assert (*p1 == o1);
+ assert (*p2 == o2);
+ }
+
+ o1.i++;
+ o1.v.pop_back ();
+ o1.v.modify_front ()++;
+
+ o2.i--;
+ o2.v.clear ();
+ o2.v.push_back (4);
+
+ {
+ transaction t (db->begin ());
+ db->update (o1);
+ db->update (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p1 (db->load<object> (o1.id));
+ unique_ptr<object> p2 (db->load<object> (o2.id));
+ t.commit ();
+
+ assert (*p1 == o1);
+ assert (*p2 == o2);
+ }
+ }
+
+ // Test version type mapping.
+ //
+ {
+ using namespace test5;
+
+ object o1 (100, 123);
+ object o2 (200, 234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p1 (db->load<object> (o1.id));
+ unique_ptr<object> p2 (db->load<object> (o2.id));
+
+ assert (*p1 == o1);
+ assert (*p2 == o2);
+
+ p1->i--;
+ p2->i++;
+
+ db->update (*p1);
+ db->update (*p2);
+
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+
+ for (;;)
+ {
+ o1.i++;
+ o2.i--;
+
+ try
+ {
+
+ db->update (o1);
+ db->update (o2);
+ break;
+ }
+ catch (const odb::object_changed&)
+ {
+ db->reload (o1);
+ db->reload (o2);
+ }
+ }
+
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p1 (db->load<object> (o1.id));
+ unique_ptr<object> p2 (db->load<object> (o2.id));
+ t.commit ();
+
+ assert (*p1 == o1);
+ assert (*p2 == o2);
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/as/test.hxx b/odb-tests/common/as/test.hxx
new file mode 100644
index 0000000..963abeb
--- /dev/null
+++ b/odb-tests/common/as/test.hxx
@@ -0,0 +1,270 @@
+// file : common/as/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <map>
+#include <vector>
+#include <string>
+#include <utility> // pair
+
+#include <odb/core.hxx>
+#include <odb/vector.hxx>
+#include <odb/nullable.hxx>
+
+// Test basic type mapping functionality.
+//
+#pragma db namespace table("t1_")
+namespace test1
+{
+ enum color {red, green, blue};
+
+ inline const char*
+ color_to_string (color c)
+ {
+ return c == red ? "RED" : (c == green ? "GREEN" : "BLUE");
+ }
+
+ inline color
+ string_to_color (const std::string& s)
+ {
+ return s == "RED" ? red : (s == "GREEN" ? green : blue);
+ }
+
+ #pragma db map type(color) as(std::string) \
+ to(test1::color_to_string (?)) \
+ from(test1::string_to_color (?))
+
+ typedef std::pair<int, int> intp;
+
+ #pragma db value
+ struct comp
+ {
+ comp () {}
+ comp (int n1_, int n2_): n1 (n1_), n2 (n2_) {}
+
+ int n1;
+ int n2;
+ };
+
+ #pragma db map type(intp) as(comp) \
+ to(test1::comp ((?).first, (?).second)) \
+ from(test1::intp ((?).n1, (?).n2))
+
+ #pragma db object
+ struct object
+ {
+ // Class-scope mapping.
+ //
+ #pragma db map type(bool) as(std::string) \
+ to((?) ? "true" : "false") \
+ from((?) == "true")
+
+ #pragma db id auto
+ unsigned long id;
+
+ bool b;
+ color c;
+ intp ip;
+
+ std::map<bool, int> m;
+ std::vector<intp> v;
+ odb::vector<color> cv;
+
+ object () {}
+ object (bool b_, color c_, int n1, int n2): b (b_), c (c_), ip (n1, n2) {}
+ };
+
+ inline bool
+ operator== (const object& x, const object y)
+ {
+ return
+ x.b == y.b &&
+ x.c == y.c &&
+ x.ip == y.ip &&
+ x.m == y.m &&
+ x.v == y.v &&
+ x.cv == y.cv;
+ }
+}
+
+// Test wrapped simple type mapping.
+//
+#pragma db namespace table("t2_")
+namespace test2
+{
+ #pragma db map type(bool) as(std::string) \
+ to((?) ? "true" : "false") \
+ from((?) == "true")
+
+ typedef odb::nullable<bool> null_bool;
+ typedef odb::nullable<std::string> null_string;
+
+ /*
+ #pragma db map type(null_bool) as(null_string) \
+ to((?) \
+ ? test2::null_string (*(?) ? "true" : "false") \
+ : test2::null_string ()) \
+ from((?) \
+ ? test2::null_bool (*(?) == "true") \
+ : test2::null_bool ())
+ */
+
+ #pragma db map type(null_bool) as(std::string) \
+ to((?) ? (*(?) ? "true" : "false") : "null") \
+ from((?) != "null" \
+ ? test2::null_bool ((?) == "true") \
+ : test2::null_bool ())
+
+ #pragma db object
+ struct object
+ {
+ #pragma db id auto
+ unsigned long id;
+
+ odb::nullable<bool> b;
+ std::vector<odb::nullable<bool> > v;
+ };
+
+ inline bool
+ operator== (const object& x, const object y)
+ {
+ return x.b == y.b && x.v == y.v;
+ }
+}
+
+// Test wrapped simple type mapping.
+//
+#pragma db namespace table("t3_")
+namespace test3
+{
+ typedef std::pair<int, int> intp;
+
+ #pragma db value
+ struct comp
+ {
+ comp () {}
+ comp (int n1_, int n2_): n1 (n1_), n2 (n2_) {}
+
+ int n1;
+ int n2;
+ };
+
+ typedef odb::nullable<intp> null_intp;
+ typedef odb::nullable<comp> null_comp;
+
+ #pragma db map type(null_intp) as(null_comp) \
+ to((?) \
+ ? test3::null_comp (test3::comp ((?)->first, (?)->second)) \
+ : test3::null_comp ()) \
+ from((?) \
+ ? test3::null_intp (test3::intp ((?)->n1, (?)->n2)) \
+ : test3::null_intp ())
+
+ // Map int pair with both members equal 0 to NULL comp.
+ //
+ #pragma db map type(intp) as(null_comp) \
+ to((?).first != 0 || (?).second != 0 \
+ ? test3::null_comp (test3::comp ((?).first, (?).second)) \
+ : test3::null_comp ()) \
+ from((?) \
+ ? test3::intp (test3::intp ((?)->n1, (?)->n2)) \
+ : test3::intp (0, 0))
+
+ #pragma db object
+ struct object
+ {
+ #pragma db id auto
+ unsigned long id;
+
+ odb::nullable<intp> np;
+ intp ip;
+
+ std::vector<odb::nullable<intp> > npv;
+ odb::vector<intp> ipv;
+ };
+
+ inline bool
+ operator== (const object& x, const object y)
+ {
+ return x.np == y.np && x.ip == y.ip && x.npv == y.npv && x.ipv == y.ipv;
+ }
+}
+
+// Test id type mapping.
+//
+struct id_type
+{
+ typedef unsigned long value_type;
+ value_type value;
+
+ id_type (value_type v = 0): value (v) {}
+ operator value_type () const {return value;}
+};
+
+#pragma db map type(id_type) as(id_type::value_type)
+
+#pragma db namespace table("t4_")
+namespace test4
+{
+ #pragma db object
+ struct object
+ {
+ #pragma db id auto
+ id_type id;
+
+ int i;
+ odb::vector<int> v;
+
+ object () {}
+ object (int i_): i (i_) {}
+ };
+
+ inline bool
+ operator== (const object& x, const object y)
+ {
+ return x.id == y.id && x.i == y.i && x.v == y.v;
+ }
+}
+
+// Test version type mapping.
+//
+#pragma db namespace table("t5_")
+namespace test5
+{
+ struct version_type
+ {
+ typedef unsigned short value_type;
+ value_type value;
+
+ version_type (value_type v = 0): value (v) {}
+ operator value_type () const {return value;}
+ version_type& operator++ () {value++; return *this;}
+ };
+
+ #pragma db map type(version_type) as(id_type::value_type)
+
+ #pragma db object optimistic
+ struct object
+ {
+ #pragma db id
+ id_type id;
+
+ #pragma db version
+ version_type v;
+
+ int i;
+
+ object () {}
+ object (id_type id_, int i_): id (id_), i (i_) {}
+ };
+
+ inline bool
+ operator== (const object& x, const object y)
+ {
+ return x.id == y.id && x.v == y.v && x.i == y.i;
+ }
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/as/testscript b/odb-tests/common/as/testscript
new file mode 100644
index 0000000..12d9753
--- /dev/null
+++ b/odb-tests/common/as/testscript
@@ -0,0 +1,33 @@
+# file : common/as/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/blob/buildfile b/odb-tests/common/blob/buildfile
new file mode 100644
index 0000000..cc6d164
--- /dev/null
+++ b/odb-tests/common/blob/buildfile
@@ -0,0 +1,40 @@
+# file : common/blob/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix blob_ \
+ --generate-schema
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/blob/driver.cxx b/odb-tests/common/blob/driver.cxx
new file mode 100644
index 0000000..269f415
--- /dev/null
+++ b/odb-tests/common/blob/driver.cxx
@@ -0,0 +1,76 @@
+// file : common/blob/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test BLOB mapping.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ const char data[] =
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B"
+ "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B"
+ "cccccccccccccccccccccccccccccccccccccccccccccccc"
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B"
+ "dddddddddddddddddddddddddddddddddddddddddddddddd"
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B"
+ "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B"
+ "ffffffffffffffffffffffffffffffffffffffffffffffff";
+
+ const unsigned char* udata = reinterpret_cast<const unsigned char*> (data);
+
+ object o (1);
+ o.vc.assign (data, data + sizeof (data));
+ o.vuc.assign (udata, udata + sizeof (data));
+ memcpy (o.c, data, sizeof (data));
+ memcpy (o.uc, udata, sizeof (data));
+ memcpy (o.a.data (), data, sizeof (data));
+ memcpy (o.ua.data (), udata, sizeof (data));
+ o.cont.push_back (1);
+ o.cont.push_back (2);
+ o.cont.push_back (3);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/blob/test.hxx b/odb-tests/common/blob/test.hxx
new file mode 100644
index 0000000..9602ca2
--- /dev/null
+++ b/odb-tests/common/blob/test.hxx
@@ -0,0 +1,71 @@
+// file : common/blob/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <array>
+#include <vector>
+#include <cstring> // std::memcmp
+
+#include <odb/core.hxx>
+
+#ifdef ODB_COMPILER
+# if defined(ODB_DATABASE_PGSQL)
+# define BLOB_TYPE "BYTEA"
+# elif defined(ODB_DATABASE_MSSQL)
+//# define BLOB_TYPE "VARBINARY(1024)"
+# define BLOB_TYPE "VARBINARY(max)"
+# else
+//# define BLOB_TYPE "RAW(1024)"
+# define BLOB_TYPE "BLOB"
+# endif
+#endif
+
+#pragma db object
+struct object
+{
+ object () {}
+ object (unsigned long id): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db type(BLOB_TYPE)
+ std::vector<char> vc;
+
+ #pragma db type(BLOB_TYPE)
+ std::vector<unsigned char> vuc;
+
+ #pragma db type(BLOB_TYPE)
+ char c[1024];
+
+ #pragma db type(BLOB_TYPE)
+ unsigned char uc[1024];
+
+ #pragma db type(BLOB_TYPE)
+ std::array<char, 1024> a;
+
+ #pragma db type(BLOB_TYPE)
+ std::array<char, 1024> ua;
+
+ // Make sure we can still use std::vector<char> and std::array<char>
+ // as containers.
+ //
+ std::vector<unsigned char> cont;
+};
+
+inline bool
+operator== (const object& x, const object& y)
+{
+ return x.id_ == y.id_
+ && x.vc == y.vc
+ && x.vuc == y.vuc
+ && std::memcmp (x.c, y.c, sizeof (x.c)) == 0
+ && std::memcmp (x.uc, y.uc, sizeof (x.uc)) == 0
+ && x.a == y.a
+ && x.ua == y.ua
+ && x.cont == y.cont;
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/blob/testscript b/odb-tests/common/blob/testscript
new file mode 100644
index 0000000..4fb9955
--- /dev/null
+++ b/odb-tests/common/blob/testscript
@@ -0,0 +1,33 @@
+# file : common/blob/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/buildfile b/odb-tests/common/buildfile
new file mode 100644
index 0000000..cb9c748
--- /dev/null
+++ b/odb-tests/common/buildfile
@@ -0,0 +1,6 @@
+# file : common/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -bulk/}
+
+./: bulk/: include = (!$pgsql || $pgsql_bulk || $size($databases) != 1)
diff --git a/odb-tests/common/bulk/buildfile b/odb-tests/common/bulk/buildfile
new file mode 100644
index 0000000..68e3c8b
--- /dev/null
+++ b/odb-tests/common/bulk/buildfile
@@ -0,0 +1,52 @@
+# file : common/bulk/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert (!$pgsql || $pgsql_bulk || $size($databases) != 1) \
+ "bulk operations are disabled for pgsql which is specified as single database"
+}
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+{
+ if ($db != 'pgsql' || $pgsql_bulk)
+ import libs += libodb-$db%lib{odb-$db}
+}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: \
+ include = ($multi && ($db != 'pgsql' || $pgsql_bulk))
+
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix bulk_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/bulk/driver.cxx b/odb-tests/common/bulk/driver.cxx
new file mode 100644
index 0000000..23b49ad
--- /dev/null
+++ b/odb-tests/common/bulk/driver.cxx
@@ -0,0 +1,1203 @@
+// file : common/bulk/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test bulk database operations.
+//
+
+#include <memory> // std::unique_ptr
+#include <vector>
+#include <iostream>
+#include <iterator>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <odb/details/meta/remove-pointer.hxx>
+
+#include <libcommon/config.hxx> // DATABASE_*
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+// Sun CC with non-standard STL does not have iterator_traits in which
+// case we assume iterator is just a pointer.
+//
+template <typename I,
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+ typename T = typename iterator_traits<I>::value_type
+#else
+ typename T = typename odb::details::meta::remove_pointer<I>::result
+#endif
+ >
+struct element_traits;
+
+template <typename I, typename T>
+struct element_traits
+{
+ typedef T type;
+ typedef T* pointer;
+ typedef std::unique_ptr<T> unique_ptr;
+
+ static T& ref (T& x) {return x;}
+ static T* ptr (T* p) {return p;}
+};
+
+template <typename I, typename T>
+struct element_traits<I, T*>
+{
+ typedef T type;
+ typedef T* pointer;
+ typedef std::unique_ptr<T> unique_ptr;
+
+ static T& ref (T* p) {return *p;}
+ static T* ptr (T* p) {return p;}
+};
+
+template <typename I, typename T>
+struct element_traits<I, std::unique_ptr<T> >
+{
+ typedef T type;
+ typedef std::unique_ptr<T> pointer;
+ typedef std::unique_ptr<T> unique_ptr;
+
+ static T& ref (const unique_ptr& p) {return *p;}
+ static T* ptr (const unique_ptr& p) {return p.get ();}
+};
+
+template <typename I>
+void
+persist (const unique_ptr<database>& db, I b, I e, bool cont = true)
+{
+ typedef element_traits<I> traits;
+ typedef typename traits::type type;
+ typedef typename traits::unique_ptr unique_ptr;
+
+ {
+ transaction t (db->begin ());
+ db->persist (b, e, cont);
+ t.commit ();
+ }
+
+ // Verify we can load the objects via their ids.
+ //
+ {
+ transaction t (db->begin ());
+
+ for (I i (b); i != e; ++i)
+ {
+ type& x (traits::ref (*i));
+ unique_ptr p (db->load<type> (x.id));
+ assert (p->n == x.n && p->s == x.s);
+ }
+
+ t.commit ();
+ }
+}
+
+template <typename I>
+void
+try_persist (const unique_ptr<database>& db, I b, I e, bool cont = true)
+{
+ try
+ {
+ persist (db, b, e, cont);
+ assert (false);
+ }
+ catch (const multiple_exceptions& e)
+ {
+ cout << e.what () << endl << endl;
+ }
+}
+
+template <typename I>
+void
+update (const unique_ptr<database>& db, I b, I e,
+ bool modify = true, bool cont = true)
+{
+ typedef element_traits<I> traits;
+ typedef typename traits::type type;
+ typedef typename traits::unique_ptr unique_ptr;
+
+ if (modify)
+ {
+ for (I i (b); i != e; ++i)
+ {
+ type& x (traits::ref (*i));
+ x.n++;
+ x.s[0]++;
+ }
+ }
+
+ {
+ transaction t (db->begin ());
+ db->update (b, e, cont);
+ t.commit ();
+ }
+
+ // Verify changes.
+ //
+ {
+ transaction t (db->begin ());
+
+ for (I i (b); i != e; ++i)
+ {
+ type& x (traits::ref (*i));
+ unique_ptr p (db->load<type> (x.id));
+ assert (p->n == x.n && p->s == x.s);
+ }
+
+ t.commit ();
+ }
+}
+
+template <typename I>
+void
+try_update (const unique_ptr<database>& db, I b, I e, bool cont = true)
+{
+ try
+ {
+ update (db, b, e, false, cont);
+ assert (false);
+ }
+ catch (const multiple_exceptions& e)
+ {
+ cout << e.what () << endl << endl;
+ }
+}
+
+template <typename I>
+void
+erase (const unique_ptr<database>& db, I b, I e)
+{
+ typedef element_traits<I> traits;
+ typedef typename traits::type type;
+
+ {
+ transaction t (db->begin ());
+ db->erase (b, e);
+ t.commit ();
+ }
+
+ // Verify the objects are gone.
+ //
+ {
+ transaction t (db->begin ());
+
+ for (I i (b); i != e; ++i)
+ {
+ type& x (traits::ref (*i));
+ typename traits::pointer p (db->find<type> (x.id));
+ assert (traits::ptr (p) == 0);
+ }
+
+ t.commit ();
+ }
+}
+
+template <typename T, typename I>
+void
+erase_id (const unique_ptr<database>& db, I b, I e, bool cont = true)
+{
+ typedef element_traits<T*> traits;
+ typedef T type;
+
+ {
+ transaction t (db->begin ());
+ db->erase<T> (b, e, cont);
+ t.commit ();
+ }
+
+ // Verify the objects are gone.
+ //
+ {
+ transaction t (db->begin ());
+
+ for (I i (b); i != e; ++i)
+ assert (traits::ptr (db->find<type> (*i)) == 0);
+
+ t.commit ();
+ }
+}
+
+template <typename T, typename A>
+void
+try_erase (const unique_ptr<database>& db, const A& a, bool cont = true)
+{
+ try
+ {
+ erase_id<T> (db, a, a + sizeof (a) / sizeof (a[0]), cont);
+ assert (false);
+ }
+ catch (const multiple_exceptions& e)
+ {
+ cout << e.what () << endl << endl;
+ }
+}
+
+
+template <typename I>
+void
+test (const unique_ptr<database>& db, I b, I e)
+{
+ persist (db, b, e);
+ update (db, b, e);
+ erase (db, b, e);
+}
+
+template <typename T>
+vector<T>
+fill (std::size_t count)
+{
+ vector<T> r;
+
+ unsigned int n (1);
+ std::string s ("a");
+
+ for (size_t i (0); i != count; ++i)
+ {
+ r.push_back (T (n, s));
+ n++;
+ s[0] = (s[0] == 'z' ? 'a' : s[0] + 1);
+ }
+
+ return r;
+}
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+#if !defined(MULTI_DATABASE) && \
+ (defined(DATABASE_ORACLE) || \
+ defined(DATABASE_MSSQL) || \
+ defined(DATABASE_PGSQL))
+
+ // Test database class API with various forms of containers
+ // and elements (test #6 is a copy).
+ //
+ {
+ using namespace test1;
+
+ {
+ object a[2];
+ a[0] = object (1, "a");
+ a[1] = object (2, "b");
+ test (db, a, a + sizeof (a) / sizeof (a[0]));
+ }
+
+ {
+ vector<object> v;
+ v.push_back (object (1, "a"));
+ v.push_back (object (2, "b"));
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ object o1 (1, "a");
+ object o2 (2, "b");
+ object* a[2] = {&o1, &o2};
+ test (db, a, a + sizeof (a) / sizeof (a[0]));
+ }
+
+ {
+ object o1 (1, "a");
+ object o2 (2, "b");
+ vector<object*> v;
+ v.push_back (&o1);
+ v.push_back (&o2);
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<unique_ptr<unique_object>> v;
+ v.push_back (unique_ptr<unique_object> (new unique_object (1, "a")));
+ v.push_back (unique_ptr<unique_object> (new unique_object (2, "b")));
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v;
+ v.push_back (object (1, "a"));
+ v.push_back (object (2, "b"));
+ persist (db, v.begin (), v.end ());
+
+ unsigned long id[2] = {v[0].id, v[1].id};
+ erase_id<object> (db, id, id + sizeof (id) / sizeof (id[0]));
+ }
+
+ {
+ vector<object> v;
+ v.push_back (object (1, "a"));
+ v.push_back (object (2, "b"));
+ persist (db, v.begin (), v.end ());
+
+ vector<unsigned long> id;
+ id.push_back (v[0].id);
+ id.push_back (v[1].id);
+ erase_id<object> (db, id.begin (), id.end ());
+ }
+ }
+
+ // Test various batch sizes.
+ //
+ {
+ using namespace test1;
+
+ {
+ vector<object> v; // 0
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v (fill<object> (1)); // 1
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v (fill<object> (2)); // batch - 1
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v (fill<object> (3)); // batch
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v (fill<object> (4)); // batch + 1
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v (fill<object> (5)); // 2 * batch - 1
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v (fill<object> (6)); // 2 * batch
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v (fill<object> (100)); // 100
+ test (db, v.begin (), v.end ());
+ }
+ }
+
+ // Test object with manually assigned id.
+ //
+ {
+ using namespace test2;
+
+ {
+ vector<object> v;
+ v.push_back (object ("1", 1, "a"));
+ v.push_back (object ("2", 2, "b"));
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ typedef unique_ptr<unique_object> unique_ptr;
+
+ vector<unique_ptr> v;
+ v.push_back (unique_ptr (new unique_object ("1", 1, "a")));
+ v.push_back (unique_ptr (new unique_object ("2", 2, "b")));
+ test (db, v.begin (), v.end ());
+ }
+
+ // Test const objects.
+ //
+
+ {
+ const object a[1];
+ const object* e (a + sizeof (a) / sizeof (a[0]));
+
+ transaction t (db->begin ());
+ db->persist (a, e);
+ db->erase (a, e);
+ t.commit ();
+ }
+
+ {
+ object o1 ("1", 1, "a");
+ object o2 ("2", 2, "b");
+
+ vector<const object*> v;
+ v.push_back (&o1);
+ v.push_back (&o2);
+
+ transaction t (db->begin ());
+ db->persist (v.begin (), v.end ());
+ db->erase (v.begin (), v.end ());
+ t.commit ();
+ }
+ }
+
+ // Test failure.
+ //
+ {
+ using namespace test3;
+
+ vector<object> v;
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (8, 8));
+ v.push_back (object (9, 9));
+ v.push_back (object (10, 10));
+ v.push_back (object (11, 11));
+
+ persist (db, v.begin (), v.end ());
+
+ // persist
+ //
+ {
+ {
+ vector<object> v; // 1
+ v.push_back (object (6, 6));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // batch
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (8, 8));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // batch + 1
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (8, 8));
+ v.push_back (object (9, 9));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2 x batch - 1
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (8, 8));
+ v.push_back (object (9, 9));
+ v.push_back (object (10, 10));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2 x batch
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (8, 8));
+ v.push_back (object (9, 9));
+ v.push_back (object (10, 10));
+ v.push_back (object (11, 11));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ // Mixture of success and failure.
+ //
+
+ {
+ vector<object> v; // 1
+ v.push_back (object (0, 0));
+ v.push_back (object (6, 6));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 1
+ v.push_back (object (6, 6));
+ v.push_back (object (0, 0));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2
+ v.push_back (object (0, 0));
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2
+ v.push_back (object (6, 6));
+ v.push_back (object (0, 0));
+ v.push_back (object (7, 7));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (0, 0));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // batch
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (0, 0));
+ v.push_back (object (8, 8));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // batch
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (8, 8));
+ v.push_back (object (0, 0));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // mixture
+ v.push_back (object (0, 0));
+ v.push_back (object (6, 6));
+ v.push_back (object (1, 1));
+ v.push_back (object (7, 7));
+ v.push_back (object (2, 2));
+ v.push_back (object (8, 8));
+ v.push_back (object (3, 3));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ // Test stopping after failure.
+ //
+ {
+ vector<object> v; // batch
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ v.push_back (object (6, 6));
+ v.push_back (object (2, 2));
+ v.push_back (object (3, 3));
+ try_persist (db, v.begin (), v.end (), false);
+ }
+ }
+
+ // update
+ //
+ {
+ {
+ vector<object> v; // 1
+ v.push_back (object (0, 0));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // batch
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ v.push_back (object (2, 2));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // batch + 1
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ v.push_back (object (2, 2));
+ v.push_back (object (3, 3));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2 x batch - 1
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ v.push_back (object (2, 2));
+ v.push_back (object (3, 3));
+ v.push_back (object (4, 4));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2 x batch
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ v.push_back (object (2, 2));
+ v.push_back (object (3, 3));
+ v.push_back (object (4, 4));
+ v.push_back (object (5, 5));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ // Mixture of success and failure.
+ //
+
+ {
+ vector<object> v; // 1
+ v.push_back (object (6, 6));
+ v.push_back (object (0, 0));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 1
+ v.push_back (object (0, 0));
+ v.push_back (object (6, 6));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2
+ v.push_back (object (6, 6));
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2
+ v.push_back (object (0, 0));
+ v.push_back (object (6, 6));
+ v.push_back (object (1, 1));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ v.push_back (object (6, 6));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // batch
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ v.push_back (object (6, 6));
+ v.push_back (object (2, 2));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // batch
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ v.push_back (object (2, 2));
+ v.push_back (object (6, 6));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // mixture
+ v.push_back (object (0, 0));
+ v.push_back (object (6, 6));
+ v.push_back (object (2, 2));
+ v.push_back (object (7, 7));
+ v.push_back (object (3, 3));
+ v.push_back (object (8, 8));
+ v.push_back (object (4, 4));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // mixture
+ v.push_back (object (0, 0));
+ v.push_back (object (2, 2));
+ v.push_back (object (3, 3));
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (8, 8));
+ v.push_back (object (4, 4));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ // Test stopping after failure.
+ //
+ {
+ vector<object> v; // batch
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (0, 0));
+ v.push_back (object (8, 8));
+ v.push_back (object (9, 9));
+ try_update (db, v.begin (), v.end (), false);
+ }
+
+ // Test a database exception (unique constraint violation)
+ //
+ try
+ {
+ v[0].n++;
+ v[2].n++;
+
+ update (db, v.begin (), v.begin () + 3, false);
+ assert (false);
+ }
+ catch (const multiple_exceptions& e)
+ {
+#ifndef DATABASE_PGSQL
+ assert (e.attempted () == 3 && e.failed () == 2);
+ assert (e[0] != 0 && e[1] == 0 && e[2] != 0);
+#else
+ // In PosgreSQL no further statements are attempted after the first
+ // failure.
+ //
+ assert (e.attempted () == 1 && e.failed () == 1);
+ assert (e[0] != 0);
+#endif
+ }
+ }
+
+ // erase
+ //
+ {
+ {
+ unsigned long a[] = {0}; // 1
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 1}; // 2
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 1, 2}; // batch
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 1, 2, 3}; // batch + 1
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 1, 2, 3, 4}; // 2 x batch - 1
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 1, 2, 3, 4, 5}; // 2 x batch
+ try_erase<object> (db, a);
+ }
+
+ // Mixture of success and failure.
+ //
+
+ {
+ unsigned long a[] = {6, 0}; // 2
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 6}; // 2
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {6, 0, 1}; // batch
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 6, 1}; // batch
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 1, 6}; // batch
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {6, 0, 1, 2}; // batch + 1
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 6, 1, 2}; // batch + 1
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 1, 6, 2}; // batch + 1
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 1, 2, 6}; // batch + 1
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {6, 0, 7, 1, 8, 2, 9, 3}; // mixture
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 1, 2, 6, 7, 8, 3, 4, 5, 9}; // mixture
+ try_erase<object> (db, a);
+ }
+
+ // Test stopping after failure.
+ //
+ {
+ unsigned long a[] = {6, 7, 0, 8, 9};
+ try_erase<object> (db, a, false);
+ }
+ }
+
+ erase (db, v.begin (), v.end ());
+ }
+
+ // Test a large batch.
+ //
+ {
+ using namespace test4;
+
+ vector<object> v (fill<object> (5000));
+ test (db, v.begin (), v.end ());
+ }
+
+ // Test object without id.
+ //
+ {
+ using namespace test5;
+
+ vector<object> v;
+ v.push_back (object (1, "a"));
+ v.push_back (object (2, "b"));
+
+ {
+ transaction t (db->begin ());
+ db->persist (v.begin (), v.end ());
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+
+ result r (db->query<object> ("ORDER BY" + query::n));
+ result::iterator i (r.begin ());
+
+ assert (i != r.end () && i->n == 1 && i->s == "a");
+ assert (++i != r.end () && i->n == 2 && i->s == "b");
+ assert (++i == r.end ());
+
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase_query<object> ();
+ t.commit ();
+ }
+ }
+
+ // Test API with persistent class template instantiations (copy of
+ // test #1).
+ {
+ using namespace test6;
+
+ // Make sure we can still call the non-bulk API.
+ //
+ {
+ object o (0, "z");
+ transaction t (db->begin ());
+ db->persist (o);
+ db->update<object> (o);
+ db->reload<object> (o);
+ db->erase<object> (o);
+ t.commit ();
+ }
+
+
+ // The rest is a copy of test #1.
+ //
+ {
+ object a[2];
+ a[0] = object (1, "a");
+ a[1] = object (2, "b");
+ test (db, a, a + sizeof (a) / sizeof (a[0]));
+ }
+
+ {
+ vector<object> v;
+ v.push_back (object (1, "a"));
+ v.push_back (object (2, "b"));
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ object o1 (1, "a");
+ object o2 (2, "b");
+ object* a[2] = {&o1, &o2};
+ test (db, a, a + sizeof (a) / sizeof (a[0]));
+ }
+
+ {
+ object o1 (1, "a");
+ object o2 (2, "b");
+ vector<object*> v;
+ v.push_back (&o1);
+ v.push_back (&o2);
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<unique_ptr<unique_object>> v;
+ v.push_back (unique_ptr<unique_object> (new unique_object (1, "a")));
+ v.push_back (unique_ptr<unique_object> (new unique_object (2, "b")));
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v;
+ v.push_back (object (1, "a"));
+ v.push_back (object (2, "b"));
+ persist (db, v.begin (), v.end ());
+
+ unsigned long id[2] = {v[0].id, v[1].id};
+ erase_id<object> (db, id, id + sizeof (id) / sizeof (id[0]));
+ }
+
+ {
+ vector<object> v;
+ v.push_back (object (1, "a"));
+ v.push_back (object (2, "b"));
+ persist (db, v.begin (), v.end ());
+
+ vector<unsigned long> id;
+ id.push_back (v[0].id);
+ id.push_back (v[1].id);
+ erase_id<object> (db, id.begin (), id.end ());
+ }
+ }
+
+ // Test optimistic concurrency.
+ //
+ {
+ using namespace test7;
+
+ std::vector<object> v (fill<object> (4));
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (v.begin (), v.end ());
+ t.commit ();
+
+ assert (v[0].v != 0 &&
+ v[1].v != 0 &&
+ v[2].v != 0 &&
+ v[3].v != 0);
+ }
+
+ // update
+ //
+ {
+ std::vector<object> c (v);
+
+ transaction t (db->begin ());
+ db->update (v.begin (), v.end ());
+ t.commit ();
+
+ assert (v[0].v > c[0].v &&
+ v[1].v > c[1].v &&
+ v[2].v > c[2].v &&
+ v[3].v > c[3].v);
+ }
+
+ {
+ object o2 (v[1]);
+ object o4 (v[3]);
+
+ o2.n++;
+ o4.n++;
+
+ transaction t (db->begin ());
+ db->update (o2);
+ db->update (o4);
+ t.commit ();
+ }
+
+ try
+ {
+ // Some updates may succeed spoiling the version for erase tests.
+ //
+ std::vector<object> c (v);
+
+ transaction t (db->begin ());
+ db->update (c.begin (), c.end ());
+ assert (false);
+ }
+ catch (const multiple_exceptions& e)
+ {
+ cout << e.what () << endl << endl;
+ }
+
+ // erase
+ //
+ try
+ {
+ transaction t (db->begin ());
+ db->erase (v.begin (), v.end ());
+ assert (false);
+ }
+ catch (const multiple_exceptions& e)
+ {
+ cout << e.what () << endl << endl;
+ }
+
+ {
+ transaction t (db->begin ());
+ db->reload (v[1]);
+ db->reload (v[3]);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (v.begin (), v.end ());
+ t.commit ();
+ }
+ }
+
+ // Test SQL Server optimistic concurrency with ROWVERSION.
+ //
+#ifdef DATABASE_MSSQL
+ {
+ using namespace test8;
+
+ std::vector<object> v (fill<object> (4));
+
+ v[0].id = 1;
+ v[1].id = 2;
+ v[2].id = 3;
+ v[3].id = 4;
+
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (v.begin (), v.end ());
+ t.commit ();
+
+ assert (v[0].v != 0 &&
+ v[1].v != 0 &&
+ v[2].v != 0 &&
+ v[3].v != 0);
+
+ //cerr << v[0].v << endl
+ // << v[1].v << endl
+ // << v[2].v << endl
+ // << v[3].v << endl;
+ }
+
+ // update
+ //
+
+ /*
+ {
+ std::vector<object> c (v);
+
+ transaction t (db->begin ());
+ db->update (v.begin (), v.end ());
+ t.commit ();
+
+ assert (v[0].v > c[0].v &&
+ v[1].v > c[1].v &&
+ v[2].v > c[2].v &&
+ v[3].v > c[3].v);
+ }
+ */
+
+ {
+ object o2 (v[1]);
+ object o4 (v[3]);
+
+ o2.n++;
+ o4.n++;
+
+ transaction t (db->begin ());
+ db->update (o2);
+ db->update (o4);
+ t.commit ();
+ }
+
+ /*
+ try
+ {
+ transaction t (db->begin ());
+ db->update (v.begin (), v.end ());
+ assert (false);
+ }
+ catch (const multiple_exceptions& e)
+ {
+ cout << e.what () << endl << endl;
+ }
+ */
+
+ // erase
+ //
+ try
+ {
+ transaction t (db->begin ());
+ db->erase (v.begin (), v.end ());
+ assert (false);
+ }
+ catch (const multiple_exceptions& e)
+ {
+ assert (e.attempted () == 4 && e.failed () == 4);
+ }
+
+ {
+ transaction t (db->begin ());
+ db->reload (v[1]);
+ db->reload (v[3]);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (v.begin (), v.end ());
+ t.commit ();
+ }
+ }
+#endif
+
+#endif
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/bulk/test.hxx b/odb-tests/common/bulk/test.hxx
new file mode 100644
index 0000000..71755f2
--- /dev/null
+++ b/odb-tests/common/bulk/test.hxx
@@ -0,0 +1,211 @@
+// file : common/bulk/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <memory> // std::unique_ptr
+
+#include <odb/core.hxx>
+
+// Test basic functionality.
+//
+#pragma db namespace table("t1_")
+namespace test1
+{
+ #pragma db object bulk(3) session
+ struct object
+ {
+ object (unsigned int n_ = 0, std::string s_ = "")
+ : id (0), n (n_), s (s_) {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ unsigned int n;
+
+ #pragma db oracle:type("CLOB") mssql:type("VARCHAR(max)") // Long data.
+ std::string s;
+ };
+
+ #pragma db object bulk(3) pointer(std::unique_ptr)
+ struct unique_object
+ {
+ unique_object (unsigned int n_ = 0, std::string s_ = "")
+ : id (0), n (n_), s (s_) {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ unsigned int n;
+ std::string s;
+ };
+}
+
+// Test object with manually assigned id.
+//
+#pragma db namespace table("t2_")
+namespace test2
+{
+ #pragma db object bulk(3) session
+ struct object
+ {
+ // Can't use empty id because of Oracle.
+ //
+ object (std::string id_ = "!", unsigned int n_ = 0, std::string s_ = "")
+ : id (id_), n (n_), s (s_) {}
+
+ #pragma db id
+ std::string id;
+
+ unsigned int n;
+ std::string s;
+ };
+
+#pragma db object bulk(3) pointer(std::unique_ptr)
+ struct unique_object
+ {
+ unique_object (std::string id_ = "",
+ unsigned int n_ = 0,
+ std::string s_ = "")
+ : id (id_), n (n_), s (s_) {}
+
+ #pragma db id
+ std::string id;
+
+ unsigned int n;
+ std::string s;
+ };
+}
+
+// Test failure.
+//
+#pragma db namespace table("t3_")
+namespace test3
+{
+ #pragma db object bulk(3)
+ struct object
+ {
+ object (unsigned long id_ = 0, unsigned int n_ = 0)
+ : id (id_), n (n_), s ("abc") {}
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db unique
+ unsigned int n;
+ std::string s;
+ };
+}
+
+// Test a large batch.
+//
+#pragma db namespace table("t4_")
+namespace test4
+{
+ #pragma db object bulk(3000)
+ struct object
+ {
+ object (unsigned int n_ = 0, std::string s_ = "")
+ : id (0), n (n_), s (s_) {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ unsigned int n;
+
+ #pragma db oracle:type("CLOB") mssql:type("VARCHAR(max)") // Long data.
+ std::string s;
+ };
+}
+
+// Test object without id.
+//
+#pragma db namespace table("t5_")
+namespace test5
+{
+ #pragma db object no_id bulk(3)
+ struct object
+ {
+ object (unsigned int n_ = 0, std::string s_ = ""): n (n_), s (s_) {}
+
+ unsigned int n;
+ std::string s;
+ };
+}
+
+// Test API with persistent class template instantiations.
+//
+#pragma db namespace table("t6_")
+namespace test6
+{
+ template <int>
+ struct object_template
+ {
+ object_template (unsigned int n_ = 0, std::string s_ = "")
+ : id (0), n (n_), s (s_) {}
+
+ unsigned long id;
+ unsigned int n;
+ std::string s;
+ };
+
+ typedef object_template<1> object;
+
+ #pragma db object(object) bulk(3)
+ #pragma db member(object::id) id auto
+
+ typedef object_template<3> unique_object;
+
+ #pragma db object(unique_object) bulk(3) pointer(std::unique_ptr)
+ #pragma db member(unique_object::id) id auto
+}
+
+// Test optimistic concurrency.
+//
+#pragma db namespace table("t7_")
+namespace test7
+{
+ #pragma db object optimistic bulk(3)
+ struct object
+ {
+ object (unsigned int n_ = 0, std::string s_ = "")
+ : id (0), v (0), n (n_), s (s_) {}
+
+ #pragma db id auto
+ unsigned long long id;
+
+ #pragma db version
+ unsigned long long v;
+
+ unsigned int n;
+ std::string s;
+ };
+}
+
+// Test SQL Server optimistic concurrency with ROWVERSION.
+//
+#if defined(ODB_DATABASE_MSSQL) || defined(DATABASE_MSSQL)
+#pragma db namespace table("t8_")
+namespace test8
+{
+ #pragma db object optimistic bulk(3)
+ struct object
+ {
+ object (unsigned int n_ = 0, std::string s_ = "")
+ : id (0), v (0), n (n_), s (s_) {}
+
+ #pragma db id
+ unsigned long long id;
+
+ #pragma db version type("ROWVERSION")
+ unsigned long long v;
+
+ unsigned int n;
+ std::string s;
+ };
+}
+#endif
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/bulk/testscript b/odb-tests/common/bulk/testscript
new file mode 100644
index 0000000..e7567c9
--- /dev/null
+++ b/odb-tests/common/bulk/testscript
@@ -0,0 +1,503 @@
+# file : common/bulk/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
++cat <<EOI >=output
+ multiple exceptions, 1 element attempted, 1 failed:
+ [0] object already persistent
+
+ multiple exceptions, 2 elements attempted, 2 failed:
+ [0] object already persistent
+ [1] object already persistent
+
+ multiple exceptions, 3 elements attempted, 3 failed:
+ [0] object already persistent
+ [1] object already persistent
+ [2] object already persistent
+
+ multiple exceptions, 4 elements attempted, 4 failed:
+ [0] object already persistent
+ [1] object already persistent
+ [2] object already persistent
+ [3] object already persistent
+
+ multiple exceptions, 5 elements attempted, 5 failed:
+ [0] object already persistent
+ [1] object already persistent
+ [2] object already persistent
+ [3] object already persistent
+ [4] object already persistent
+
+ multiple exceptions, 6 elements attempted, 6 failed:
+ [0] object already persistent
+ [1] object already persistent
+ [2] object already persistent
+ [3] object already persistent
+ [4] object already persistent
+ [5] object already persistent
+
+ multiple exceptions, 2 elements attempted, 1 failed:
+ [1] object already persistent
+
+ multiple exceptions, 2 elements attempted, 1 failed:
+ [0] object already persistent
+
+ multiple exceptions, 3 elements attempted, 2 failed:
+ [1] object already persistent
+ [2] object already persistent
+
+ multiple exceptions, 3 elements attempted, 2 failed:
+ [0] object already persistent
+ [2] object already persistent
+
+ multiple exceptions, 3 elements attempted, 2 failed:
+ [0] object already persistent
+ [1] object already persistent
+
+ multiple exceptions, 4 elements attempted, 3 failed:
+ [0] object already persistent
+ [1] object already persistent
+ [3] object already persistent
+
+ multiple exceptions, 4 elements attempted, 3 failed:
+ [0] object already persistent
+ [1] object already persistent
+ [2] object already persistent
+
+ multiple exceptions, 7 elements attempted, 3 failed:
+ [1] object already persistent
+ [3] object already persistent
+ [5] object already persistent
+
+ multiple exceptions, 3 elements attempted, 1 failed:
+ [2] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed:
+ [0] object not persistent
+
+ multiple exceptions, 2 elements attempted, 2 failed:
+ [0] object not persistent
+ [1] object not persistent
+
+ multiple exceptions, 3 elements attempted, 3 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+
+ multiple exceptions, 4 elements attempted, 4 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+ [3] object not persistent
+
+ multiple exceptions, 5 elements attempted, 5 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+ [3] object not persistent
+ [4] object not persistent
+
+ multiple exceptions, 6 elements attempted, 6 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+ [3] object not persistent
+ [4] object not persistent
+ [5] object not persistent
+
+ multiple exceptions, 2 elements attempted, 2 failed:
+ [0-1] (some) object not persistent
+
+ multiple exceptions, 2 elements attempted, 2 failed:
+ [0-1] (some) object not persistent
+
+ multiple exceptions, 3 elements attempted, 3 failed:
+ [0-2] (some) object not persistent
+
+ multiple exceptions, 3 elements attempted, 3 failed:
+ [0-2] (some) object not persistent
+
+ multiple exceptions, 3 elements attempted, 3 failed:
+ [0-2] (some) object not persistent
+
+ multiple exceptions, 4 elements attempted, 4 failed:
+ [0-2] (some) object not persistent
+ [3] object not persistent
+
+ multiple exceptions, 4 elements attempted, 3 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+
+ multiple exceptions, 7 elements attempted, 7 failed:
+ [0-5] (some) object not persistent
+ [6] object not persistent
+
+ multiple exceptions, 7 elements attempted, 4 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+ [6] object not persistent
+
+ multiple exceptions, 3 elements attempted, 3 failed:
+ [0-2] (some) object not persistent
+
+ multiple exceptions, 1 element attempted, 1 failed:
+ [0] object not persistent
+
+ multiple exceptions, 2 elements attempted, 2 failed:
+ [0] object not persistent
+ [1] object not persistent
+
+ multiple exceptions, 3 elements attempted, 3 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+
+ multiple exceptions, 4 elements attempted, 4 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+ [3] object not persistent
+
+ multiple exceptions, 5 elements attempted, 5 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+ [3] object not persistent
+ [4] object not persistent
+
+ multiple exceptions, 6 elements attempted, 6 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+ [3] object not persistent
+ [4] object not persistent
+ [5] object not persistent
+
+ multiple exceptions, 2 elements attempted, 2 failed:
+ [0-1] (some) object not persistent
+
+ multiple exceptions, 2 elements attempted, 2 failed:
+ [0-1] (some) object not persistent
+
+ multiple exceptions, 3 elements attempted, 3 failed:
+ [0-2] (some) object not persistent
+
+ multiple exceptions, 3 elements attempted, 3 failed:
+ [0-2] (some) object not persistent
+
+ multiple exceptions, 3 elements attempted, 3 failed:
+ [0-2] (some) object not persistent
+
+ multiple exceptions, 4 elements attempted, 4 failed:
+ [0-2] (some) object not persistent
+ [3] object not persistent
+
+ multiple exceptions, 4 elements attempted, 4 failed:
+ [0-2] (some) object not persistent
+ [3] object not persistent
+
+ multiple exceptions, 4 elements attempted, 4 failed:
+ [0-2] (some) object not persistent
+ [3] object not persistent
+
+ multiple exceptions, 4 elements attempted, 3 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+
+ multiple exceptions, 8 elements attempted, 8 failed:
+ [0-7] (some) object not persistent
+
+ multiple exceptions, 10 elements attempted, 6 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+ [6] object not persistent
+ [7] object not persistent
+ [8] object not persistent
+
+ multiple exceptions, 3 elements attempted, 3 failed:
+ [0-2] (some) object not persistent
+
+ multiple exceptions, 4 elements attempted, 4 failed:
+ [0-2] (some) object changed concurrently
+ [3] object changed concurrently
+
+ multiple exceptions, 4 elements attempted, 4 failed:
+ [0-2] (some) object changed concurrently
+ [3] object changed concurrently
+
+ EOI
+
++cat <<EOI >=pgsql-output
+ multiple exceptions, 1 element attempted, 1 failed:
+ [0] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 2 elements attempted, 1 failed:
+ [1] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 2 elements attempted, 1 failed, fatal:
+ [1] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 2 elements attempted, 1 failed, fatal:
+ [1] object already persistent
+
+ multiple exceptions, 3 elements attempted, 1 failed:
+ [2] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed:
+ [0] object not persistent
+
+ multiple exceptions, 2 elements attempted, 2 failed:
+ [0] object not persistent
+ [1] object not persistent
+
+ multiple exceptions, 3 elements attempted, 3 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+
+ multiple exceptions, 4 elements attempted, 4 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+ [3] object not persistent
+
+ multiple exceptions, 5 elements attempted, 5 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+ [3] object not persistent
+ [4] object not persistent
+
+ multiple exceptions, 6 elements attempted, 6 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+ [3] object not persistent
+ [4] object not persistent
+ [5] object not persistent
+
+ multiple exceptions, 2 elements attempted, 1 failed:
+ [1] object not persistent
+
+ multiple exceptions, 2 elements attempted, 1 failed:
+ [0] object not persistent
+
+ multiple exceptions, 3 elements attempted, 2 failed:
+ [1] object not persistent
+ [2] object not persistent
+
+ multiple exceptions, 3 elements attempted, 2 failed:
+ [0] object not persistent
+ [2] object not persistent
+
+ multiple exceptions, 3 elements attempted, 2 failed:
+ [0] object not persistent
+ [1] object not persistent
+
+ multiple exceptions, 4 elements attempted, 3 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [3] object not persistent
+
+ multiple exceptions, 4 elements attempted, 3 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+
+ multiple exceptions, 7 elements attempted, 4 failed:
+ [0] object not persistent
+ [2] object not persistent
+ [4] object not persistent
+ [6] object not persistent
+
+ multiple exceptions, 7 elements attempted, 4 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+ [6] object not persistent
+
+ multiple exceptions, 3 elements attempted, 1 failed:
+ [2] object not persistent
+
+ multiple exceptions, 1 element attempted, 1 failed:
+ [0] object not persistent
+
+ multiple exceptions, 2 elements attempted, 2 failed:
+ [0] object not persistent
+ [1] object not persistent
+
+ multiple exceptions, 3 elements attempted, 3 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+
+ multiple exceptions, 4 elements attempted, 4 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+ [3] object not persistent
+
+ multiple exceptions, 5 elements attempted, 5 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+ [3] object not persistent
+ [4] object not persistent
+
+ multiple exceptions, 6 elements attempted, 6 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+ [3] object not persistent
+ [4] object not persistent
+ [5] object not persistent
+
+ multiple exceptions, 2 elements attempted, 1 failed:
+ [1] object not persistent
+
+ multiple exceptions, 2 elements attempted, 1 failed:
+ [0] object not persistent
+
+ multiple exceptions, 3 elements attempted, 2 failed:
+ [1] object not persistent
+ [2] object not persistent
+
+ multiple exceptions, 3 elements attempted, 2 failed:
+ [0] object not persistent
+ [2] object not persistent
+
+ multiple exceptions, 3 elements attempted, 2 failed:
+ [0] object not persistent
+ [1] object not persistent
+
+ multiple exceptions, 4 elements attempted, 3 failed:
+ [1] object not persistent
+ [2] object not persistent
+ [3] object not persistent
+
+ multiple exceptions, 4 elements attempted, 3 failed:
+ [0] object not persistent
+ [2] object not persistent
+ [3] object not persistent
+
+ multiple exceptions, 4 elements attempted, 3 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [3] object not persistent
+
+ multiple exceptions, 4 elements attempted, 3 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+
+ multiple exceptions, 8 elements attempted, 4 failed:
+ [1] object not persistent
+ [3] object not persistent
+ [5] object not persistent
+ [7] object not persistent
+
+ multiple exceptions, 10 elements attempted, 6 failed:
+ [0] object not persistent
+ [1] object not persistent
+ [2] object not persistent
+ [6] object not persistent
+ [7] object not persistent
+ [8] object not persistent
+
+ multiple exceptions, 3 elements attempted, 1 failed:
+ [2] object not persistent
+
+ multiple exceptions, 4 elements attempted, 2 failed:
+ [1] object changed concurrently
+ [3] object changed concurrently
+
+ multiple exceptions, 4 elements attempted, 2 failed:
+ [1] object changed concurrently
+ [3] object changed concurrently
+
+ EOI
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if ($pgsql && $pgsql_bulk)
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+
+ # Query the PostgreSQL server version and only run the test if it is 7.4 or
+ # above.
+ #
+ $pgsql_client_cmd --tuples-only -c 'SELECT VERSION()' | \
+ sed -n -e 's/.*PostgreSQL (\d+\.\d+).*/\1/p' | \
+ set version [string];
+
+ if ("$version" == "")
+ exit "unable to obtain PostgreSQL server version"
+ end;
+
+ sed -n -e 's/(.+)\..+/\1/p' <"$version" | set major_version [uint64];
+ sed -n -e 's/.+\.(.+)/\1/p' <"$version" | set minor_version [uint64];
+
+ if (($major_version == 7 && minor_version >= 4) || $major_version > 7)
+ if $multi
+ $* # Noop.
+ else
+ $* >>>../pgsql-output
+ end
+ end
+}
diff --git a/odb-tests/common/callback/buildfile b/odb-tests/common/callback/buildfile
new file mode 100644
index 0000000..2ecc3b8
--- /dev/null
+++ b/odb-tests/common/callback/buildfile
@@ -0,0 +1,41 @@
+# file : common/callback/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix callback_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/callback/driver.cxx b/odb-tests/common/callback/driver.cxx
new file mode 100644
index 0000000..80513c6
--- /dev/null
+++ b/odb-tests/common/callback/driver.cxx
@@ -0,0 +1,184 @@
+// file : common/callback/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test database operation callbacks.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+const char* events[] =
+{
+ "pre_persist",
+ "post_persist",
+ "pre_load",
+ "post_load",
+ "pre_update",
+ "post_update",
+ "pre_erase",
+ "post_erase"
+};
+
+void object::
+db_callback (callback_event e, database& db)
+{
+ cout << " " << events[e] << " " << id_ << endl;
+
+ // Test custom recursive loading.
+ //
+ if (e == callback_event::post_load && ref != 0)
+ {
+ robj = db.load<object> (ref);
+ cout << " " << id_ << ' ' << ref << ' ' << robj->id_ << endl;
+ }
+}
+
+void object::
+db_callback (callback_event e, database&) const
+{
+ cout << " " << events[e] << " " << id_ << " const" << endl;
+}
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Persist.
+ //
+ cout << "persist" << endl;
+ {
+ object o1 (1, 1);
+ object const o2 (2, 2);
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (&o2);
+ t.commit ();
+ }
+ cout << "***" << endl;
+
+ // Load.
+ //
+ cout << "load" << endl;
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (1));
+ object o2;
+ db->load<object> (2, o2);
+ t.commit ();
+ }
+ cout << "***" << endl;
+
+ // Query.
+ //
+ cout << "query" << endl;
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+
+ result r (db->query<object> ((query::id < 3) + "ORDER BY" + query::id));
+
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ if (i->id_ > 3) // Load.
+ break;
+ }
+
+ t.commit ();
+ }
+ cout << "***" << endl;
+
+ // Update.
+ //
+ cout << "update" << endl;
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (1));
+ unique_ptr<object> o2 (db->load<object> (2));
+ o1->data++;
+ o2->data++;
+ db->update (o1.get ());
+ db->update (static_cast<const object&> (*o2));
+ t.commit ();
+ }
+ cout << "***" << endl;
+
+ // Erase.
+ //
+ cout << "erase" << endl;
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (1));
+ unique_ptr<object> o2 (db->load<object> (2));
+ db->erase (static_cast<const object*> (o1.get ()));
+ db->erase (*o2);
+ t.commit ();
+ }
+ cout << "***" << endl;
+
+ // Delayed (recursive) load.
+ //
+ cout << "delayed load" << endl;
+ {
+ {
+ object o1 (1, 1);
+ object o2 (2, 2);
+ object o3 (3, 3);
+ object o4 (4, 4);
+
+ o1.pobj = &o2;
+ o1.ref = 4;
+
+ o2.pobj = &o3;
+ o2.ref = 4;
+
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o3);
+ db->persist (o4);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (1));
+ object* o2 (o1->pobj);
+
+ cout << o1->id_ << ' ' << o1->ref << ' ' << o1->robj->id_ << endl;
+ cout << o2->id_ << ' ' << o2->ref << ' ' << o2->robj->id_ << endl;
+
+ delete o1->robj;
+ delete o2->robj;
+
+ delete o2->pobj;
+ delete o2;
+ t.commit ();
+ }
+ }
+ cout << "***" << endl;
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/callback/test.hxx b/odb-tests/common/callback/test.hxx
new file mode 100644
index 0000000..bd30907
--- /dev/null
+++ b/odb-tests/common/callback/test.hxx
@@ -0,0 +1,43 @@
+// file : common/callback/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+#include <odb/callback.hxx>
+
+#pragma db object callback(db_callback)
+struct object
+{
+ object (unsigned long id, unsigned long d)
+ : id_ (id), data (d), pobj (0), robj (0), ref (0)
+ {
+ }
+
+ object ()
+ : id_ (0), pobj (0), robj (0)
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+
+ unsigned long data;
+
+ object* pobj;
+
+ // Test custom recursive loading.
+ //
+ #pragma db transient
+ object* robj;
+ unsigned long ref; // Unless 0, reference to another object.
+
+ void
+ db_callback (odb::callback_event, odb::database&);
+
+ void
+ db_callback (odb::callback_event, odb::database&) const;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/callback/testscript b/odb-tests/common/callback/testscript
new file mode 100644
index 0000000..c7d03ee
--- /dev/null
+++ b/odb-tests/common/callback/testscript
@@ -0,0 +1,100 @@
+# file : common/callback/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
++cat <<EOI >=output
+ persist
+ pre_persist 1 const
+ post_persist 1 const
+ pre_persist 2 const
+ post_persist 2 const
+ ***
+ load
+ pre_load 0
+ post_load 1
+ pre_load 0
+ post_load 2
+ ***
+ query
+ pre_load 0
+ post_load 1
+ pre_load 0
+ post_load 2
+ ***
+ update
+ pre_load 0
+ post_load 1
+ pre_load 0
+ post_load 2
+ pre_update 1 const
+ post_update 1 const
+ pre_update 2 const
+ post_update 2 const
+ ***
+ erase
+ pre_load 0
+ post_load 1
+ pre_load 0
+ post_load 2
+ pre_erase 1 const
+ post_erase 1 const
+ pre_erase 2 const
+ post_erase 2 const
+ ***
+ delayed load
+ pre_persist 1 const
+ post_persist 1 const
+ pre_persist 2 const
+ post_persist 2 const
+ pre_persist 3 const
+ post_persist 3 const
+ pre_persist 4 const
+ post_persist 4 const
+ pre_load 0
+ pre_load 0
+ pre_load 0
+ post_load 3
+ post_load 2
+ pre_load 0
+ post_load 4
+ 2 4 4
+ post_load 1
+ pre_load 0
+ post_load 4
+ 1 4 4
+ 1 4 4
+ 2 4 4
+ ***
+ EOI
+
+test.redirects += >>>../output
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/changelog/.gitignore b/odb-tests/common/changelog/.gitignore
new file mode 100644
index 0000000..5352a2b
--- /dev/null
+++ b/odb-tests/common/changelog/.gitignore
@@ -0,0 +1,3 @@
+# Generate ODB options file.
+#
+odb.options
diff --git a/odb-tests/common/changelog/add-column-mssql-diff.xml b/odb-tests/common/changelog/add-column-mssql-diff.xml
new file mode 100644
index 0000000..4f9ba09
--- /dev/null
+++ b/odb-tests/common/changelog/add-column-mssql-diff.xml
@@ -0,0 +1,16 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <add-column name="num" type="INT" null="false"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-column-mssql-patch.xml b/odb-tests/common/changelog/add-column-mssql-patch.xml
new file mode 100644
index 0000000..4f396d9
--- /dev/null
+++ b/odb-tests/common/changelog/add-column-mssql-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-column-mysql-diff.xml b/odb-tests/common/changelog/add-column-mysql-diff.xml
new file mode 100644
index 0000000..992306d
--- /dev/null
+++ b/odb-tests/common/changelog/add-column-mysql-diff.xml
@@ -0,0 +1,16 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <add-column name="num" type="INT" null="false"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-column-mysql-patch.xml b/odb-tests/common/changelog/add-column-mysql-patch.xml
new file mode 100644
index 0000000..14f3f01
--- /dev/null
+++ b/odb-tests/common/changelog/add-column-mysql-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-column-oracle-diff.xml b/odb-tests/common/changelog/add-column-oracle-diff.xml
new file mode 100644
index 0000000..fa1dac6
--- /dev/null
+++ b/odb-tests/common/changelog/add-column-oracle-diff.xml
@@ -0,0 +1,16 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <add-column name="num" type="NUMBER(10)" null="false"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <primary-key auto="true" sequence="object_seq">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-column-oracle-patch.xml b/odb-tests/common/changelog/add-column-oracle-patch.xml
new file mode 100644
index 0000000..38fb8d6
--- /dev/null
+++ b/odb-tests/common/changelog/add-column-oracle-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <column name="num" type="NUMBER(10)" null="false"/>
+ <primary-key auto="true" sequence="object_seq">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-column-pgsql-diff.xml b/odb-tests/common/changelog/add-column-pgsql-diff.xml
new file mode 100644
index 0000000..9524d9d
--- /dev/null
+++ b/odb-tests/common/changelog/add-column-pgsql-diff.xml
@@ -0,0 +1,16 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <add-column name="num" type="INTEGER" null="false"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-column-pgsql-patch.xml b/odb-tests/common/changelog/add-column-pgsql-patch.xml
new file mode 100644
index 0000000..7f7d9a0
--- /dev/null
+++ b/odb-tests/common/changelog/add-column-pgsql-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-column-sqlite-diff.xml b/odb-tests/common/changelog/add-column-sqlite-diff.xml
new file mode 100644
index 0000000..b59cc72
--- /dev/null
+++ b/odb-tests/common/changelog/add-column-sqlite-diff.xml
@@ -0,0 +1,16 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <add-column name="num" type="INTEGER" null="false"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-column-sqlite-patch.xml b/odb-tests/common/changelog/add-column-sqlite-patch.xml
new file mode 100644
index 0000000..fbe4428
--- /dev/null
+++ b/odb-tests/common/changelog/add-column-sqlite-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-column.hxx b/odb-tests/common/changelog/add-column.hxx
new file mode 100644
index 0000000..54eab42
--- /dev/null
+++ b/odb-tests/common/changelog/add-column.hxx
@@ -0,0 +1,20 @@
+// file : common/changelog/add-column.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ADD_COLUMN_HXX
+#define ADD_COLUMN_HXX
+
+#pragma db model version(BVER, CVER, open)
+
+#pragma db object
+struct object
+{
+ #pragma db id auto
+ int id;
+
+#if CVER > 1
+ int num;
+#endif
+};
+
+#endif // ADD_COLUMN_HXX
diff --git a/odb-tests/common/changelog/add-foreign-key-diff.xml b/odb-tests/common/changelog/add-foreign-key-diff.xml
new file mode 100644
index 0000000..d4f29ad
--- /dev/null
+++ b/odb-tests/common/changelog/add-foreign-key-diff.xml
@@ -0,0 +1,28 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <add-column name="o1" type="INTEGER" null="true"/>
+ <add-foreign-key name="o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </add-foreign-key>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-foreign-key-mssql-diff.xml b/odb-tests/common/changelog/add-foreign-key-mssql-diff.xml
new file mode 100644
index 0000000..76ebce3
--- /dev/null
+++ b/odb-tests/common/changelog/add-foreign-key-mssql-diff.xml
@@ -0,0 +1,28 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <add-column name="o1" type="INT" null="true"/>
+ <add-foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </add-foreign-key>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-foreign-key-mssql-patch.xml b/odb-tests/common/changelog/add-foreign-key-mssql-patch.xml
new file mode 100644
index 0000000..6d75709
--- /dev/null
+++ b/odb-tests/common/changelog/add-foreign-key-mssql-patch.xml
@@ -0,0 +1,25 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="o1" type="INT" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-foreign-key-mysql-diff.xml b/odb-tests/common/changelog/add-foreign-key-mysql-diff.xml
new file mode 100644
index 0000000..acdfd5b
--- /dev/null
+++ b/odb-tests/common/changelog/add-foreign-key-mysql-diff.xml
@@ -0,0 +1,28 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <add-column name="o1" type="INT" null="true"/>
+ <add-foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </add-foreign-key>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ <table name="object1" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-foreign-key-mysql-patch.xml b/odb-tests/common/changelog/add-foreign-key-mysql-patch.xml
new file mode 100644
index 0000000..adc9081
--- /dev/null
+++ b/odb-tests/common/changelog/add-foreign-key-mysql-patch.xml
@@ -0,0 +1,25 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="o1" type="INT" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object1" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-foreign-key-oracle-diff.xml b/odb-tests/common/changelog/add-foreign-key-oracle-diff.xml
new file mode 100644
index 0000000..6bd8bc3
--- /dev/null
+++ b/odb-tests/common/changelog/add-foreign-key-oracle-diff.xml
@@ -0,0 +1,28 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <add-column name="o1" type="NUMBER(10)" null="true"/>
+ <add-foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </add-foreign-key>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <primary-key auto="true" sequence="object_seq">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-foreign-key-oracle-patch.xml b/odb-tests/common/changelog/add-foreign-key-oracle-patch.xml
new file mode 100644
index 0000000..7aa01ea
--- /dev/null
+++ b/odb-tests/common/changelog/add-foreign-key-oracle-patch.xml
@@ -0,0 +1,25 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <column name="o1" type="NUMBER(10)" null="true"/>
+ <primary-key auto="true" sequence="object_seq">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-foreign-key-pgsql-diff.xml b/odb-tests/common/changelog/add-foreign-key-pgsql-diff.xml
new file mode 100644
index 0000000..793b009
--- /dev/null
+++ b/odb-tests/common/changelog/add-foreign-key-pgsql-diff.xml
@@ -0,0 +1,28 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <add-column name="o1" type="INTEGER" null="true"/>
+ <add-foreign-key name="o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </add-foreign-key>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-foreign-key-pgsql-patch.xml b/odb-tests/common/changelog/add-foreign-key-pgsql-patch.xml
new file mode 100644
index 0000000..a256831
--- /dev/null
+++ b/odb-tests/common/changelog/add-foreign-key-pgsql-patch.xml
@@ -0,0 +1,25 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="o1" type="INTEGER" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-foreign-key-sqlite-diff.xml b/odb-tests/common/changelog/add-foreign-key-sqlite-diff.xml
new file mode 100644
index 0000000..1510470
--- /dev/null
+++ b/odb-tests/common/changelog/add-foreign-key-sqlite-diff.xml
@@ -0,0 +1,28 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <add-column name="o1" type="INTEGER" null="true"/>
+ <add-foreign-key name="o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </add-foreign-key>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-foreign-key-sqlite-patch.xml b/odb-tests/common/changelog/add-foreign-key-sqlite-patch.xml
new file mode 100644
index 0000000..1c2d0ea
--- /dev/null
+++ b/odb-tests/common/changelog/add-foreign-key-sqlite-patch.xml
@@ -0,0 +1,25 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="o1" type="INTEGER" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-foreign-key.hxx b/odb-tests/common/changelog/add-foreign-key.hxx
new file mode 100644
index 0000000..2a43eea
--- /dev/null
+++ b/odb-tests/common/changelog/add-foreign-key.hxx
@@ -0,0 +1,29 @@
+// file : common/changelog/add-foreign-key.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ADD_FOREIGN_KEY_HXX
+#define ADD_FOREIGN_KEY_HXX
+
+#pragma db model version(BVER, CVER, open)
+
+struct object1;
+
+#pragma db object
+struct object
+{
+ #pragma db id auto
+ int id;
+
+#if CVER > 1
+ object1* o1;
+#endif
+};
+
+#pragma db object
+struct object1
+{
+ #pragma db id
+ int id;
+};
+
+#endif // ADD_FOREIGN_KEY_HXX
diff --git a/odb-tests/common/changelog/add-index-mssql-diff.xml b/odb-tests/common/changelog/add-index-mssql-diff.xml
new file mode 100644
index 0000000..58c623f
--- /dev/null
+++ b/odb-tests/common/changelog/add-index-mssql-diff.xml
@@ -0,0 +1,21 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <add-column name="y" type="INT" null="false"/>
+ <add-index name="xy_i" type="UNIQUE">
+ <column name="x"/>
+ <column name="y" options="DESC"/>
+ </add-index>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="x" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-index-mssql-patch.xml b/odb-tests/common/changelog/add-index-mssql-patch.xml
new file mode 100644
index 0000000..2b10e65
--- /dev/null
+++ b/odb-tests/common/changelog/add-index-mssql-patch.xml
@@ -0,0 +1,18 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="x" type="INT" null="false"/>
+ <column name="y" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <index name="xy_i" type="UNIQUE">
+ <column name="x"/>
+ <column name="y" options="DESC"/>
+ </index>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-index-mysql-diff.xml b/odb-tests/common/changelog/add-index-mysql-diff.xml
new file mode 100644
index 0000000..a54a7e3
--- /dev/null
+++ b/odb-tests/common/changelog/add-index-mysql-diff.xml
@@ -0,0 +1,21 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <add-column name="y" type="INT" null="false"/>
+ <add-index name="xy_i" type="UNIQUE">
+ <column name="x"/>
+ <column name="y" options="DESC"/>
+ </add-index>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="x" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-index-mysql-patch.xml b/odb-tests/common/changelog/add-index-mysql-patch.xml
new file mode 100644
index 0000000..a2454b8
--- /dev/null
+++ b/odb-tests/common/changelog/add-index-mysql-patch.xml
@@ -0,0 +1,18 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="x" type="INT" null="false"/>
+ <column name="y" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <index name="xy_i" type="UNIQUE">
+ <column name="x"/>
+ <column name="y" options="DESC"/>
+ </index>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-index-oracle-diff.xml b/odb-tests/common/changelog/add-index-oracle-diff.xml
new file mode 100644
index 0000000..80f8ecc
--- /dev/null
+++ b/odb-tests/common/changelog/add-index-oracle-diff.xml
@@ -0,0 +1,21 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <add-column name="y" type="NUMBER(10)" null="false"/>
+ <add-index name="xy_i" type="UNIQUE">
+ <column name="x"/>
+ <column name="y" options="DESC"/>
+ </add-index>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <column name="x" type="NUMBER(10)" null="false"/>
+ <primary-key auto="true" sequence="object_seq">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-index-oracle-patch.xml b/odb-tests/common/changelog/add-index-oracle-patch.xml
new file mode 100644
index 0000000..a9bafea
--- /dev/null
+++ b/odb-tests/common/changelog/add-index-oracle-patch.xml
@@ -0,0 +1,18 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <column name="x" type="NUMBER(10)" null="false"/>
+ <column name="y" type="NUMBER(10)" null="false"/>
+ <primary-key auto="true" sequence="object_seq">
+ <column name="id"/>
+ </primary-key>
+ <index name="xy_i" type="UNIQUE">
+ <column name="x"/>
+ <column name="y" options="DESC"/>
+ </index>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-index-pgsql-diff.xml b/odb-tests/common/changelog/add-index-pgsql-diff.xml
new file mode 100644
index 0000000..3988643
--- /dev/null
+++ b/odb-tests/common/changelog/add-index-pgsql-diff.xml
@@ -0,0 +1,21 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <add-column name="y" type="INTEGER" null="false"/>
+ <add-index name="xy_i" type="UNIQUE">
+ <column name="x"/>
+ <column name="y" options="DESC"/>
+ </add-index>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="x" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-index-pgsql-patch.xml b/odb-tests/common/changelog/add-index-pgsql-patch.xml
new file mode 100644
index 0000000..e9c564c
--- /dev/null
+++ b/odb-tests/common/changelog/add-index-pgsql-patch.xml
@@ -0,0 +1,18 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="x" type="INTEGER" null="false"/>
+ <column name="y" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <index name="xy_i" type="UNIQUE">
+ <column name="x"/>
+ <column name="y" options="DESC"/>
+ </index>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-index-sqlite-diff.xml b/odb-tests/common/changelog/add-index-sqlite-diff.xml
new file mode 100644
index 0000000..c1f7fdc
--- /dev/null
+++ b/odb-tests/common/changelog/add-index-sqlite-diff.xml
@@ -0,0 +1,21 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <add-column name="y" type="INTEGER" null="false"/>
+ <add-index name="xy_i" type="UNIQUE">
+ <column name="x"/>
+ <column name="y" options="DESC"/>
+ </add-index>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="x" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-index-sqlite-patch.xml b/odb-tests/common/changelog/add-index-sqlite-patch.xml
new file mode 100644
index 0000000..b9512e0
--- /dev/null
+++ b/odb-tests/common/changelog/add-index-sqlite-patch.xml
@@ -0,0 +1,18 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="x" type="INTEGER" null="false"/>
+ <column name="y" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <index name="xy_i" type="UNIQUE">
+ <column name="x"/>
+ <column name="y" options="DESC"/>
+ </index>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-index.hxx b/odb-tests/common/changelog/add-index.hxx
new file mode 100644
index 0000000..645cee2
--- /dev/null
+++ b/odb-tests/common/changelog/add-index.hxx
@@ -0,0 +1,24 @@
+// file : common/changelog/add-index.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ADD_INDEX_HXX
+#define ADD_INDEX_HXX
+
+#pragma db model version(BVER, CVER, open)
+
+#pragma db object
+struct object
+{
+ #pragma db id auto
+ int id;
+
+ int x;
+
+#if CVER > 1
+ int y;
+ #pragma db index ("xy_i") unique member(x) member(y, "DESC")
+#endif
+
+};
+
+#endif // ADD_INDEX_HXX
diff --git a/odb-tests/common/changelog/add-table-mssql-diff.xml b/odb-tests/common/changelog/add-table-mssql-diff.xml
new file mode 100644
index 0000000..8bf0e30
--- /dev/null
+++ b/odb-tests/common/changelog/add-table-mssql-diff.xml
@@ -0,0 +1,45 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1">
+ <changeset version="2">
+ <add-table name="object" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <column name="o1" type="INT" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </add-table>
+ <add-table name="object_nums" kind="container">
+ <column name="object_id" type="INT" null="false"/>
+ <column name="index" type="BIGINT" null="false"/>
+ <column name="value" type="INT" null="false"/>
+ <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="index_i">
+ <column name="index"/>
+ </index>
+ </add-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object1" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-table-mssql-patch.xml b/odb-tests/common/changelog/add-table-mssql-patch.xml
new file mode 100644
index 0000000..9dd41f3
--- /dev/null
+++ b/odb-tests/common/changelog/add-table-mssql-patch.xml
@@ -0,0 +1,44 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object1" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ <table name="object" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <column name="o1" type="INT" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object_nums" kind="container">
+ <column name="object_id" type="INT" null="false"/>
+ <column name="index" type="BIGINT" null="false"/>
+ <column name="value" type="INT" null="false"/>
+ <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-table-mysql-diff.xml b/odb-tests/common/changelog/add-table-mysql-diff.xml
new file mode 100644
index 0000000..57f741b
--- /dev/null
+++ b/odb-tests/common/changelog/add-table-mysql-diff.xml
@@ -0,0 +1,45 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1">
+ <changeset version="2">
+ <add-table name="object" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <column name="o1" type="INT" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </add-table>
+ <add-table name="object_nums" options="ENGINE=InnoDB" kind="container">
+ <column name="object_id" type="INT" null="false"/>
+ <column name="index" type="BIGINT UNSIGNED" null="false"/>
+ <column name="value" type="INT" null="false"/>
+ <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="index_i">
+ <column name="index"/>
+ </index>
+ </add-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object1" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-table-mysql-patch.xml b/odb-tests/common/changelog/add-table-mysql-patch.xml
new file mode 100644
index 0000000..0db1e9f
--- /dev/null
+++ b/odb-tests/common/changelog/add-table-mysql-patch.xml
@@ -0,0 +1,44 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object1" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ <table name="object" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <column name="o1" type="INT" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object_nums" options="ENGINE=InnoDB" kind="container">
+ <column name="object_id" type="INT" null="false"/>
+ <column name="index" type="BIGINT UNSIGNED" null="false"/>
+ <column name="value" type="INT" null="false"/>
+ <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-table-oracle-diff.xml b/odb-tests/common/changelog/add-table-oracle-diff.xml
new file mode 100644
index 0000000..70ec7c6
--- /dev/null
+++ b/odb-tests/common/changelog/add-table-oracle-diff.xml
@@ -0,0 +1,45 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1">
+ <changeset version="2">
+ <add-table name="object" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <column name="num" type="NUMBER(10)" null="false"/>
+ <column name="o1" type="NUMBER(10)" null="true"/>
+ <primary-key auto="true" sequence="object_seq">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </add-table>
+ <add-table name="object_nums" kind="container">
+ <column name="object_id" type="NUMBER(10)" null="false"/>
+ <column name="index" type="NUMBER(20)" null="false"/>
+ <column name="value" type="NUMBER(10)" null="false"/>
+ <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_nums_object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="object_nums_index_i">
+ <column name="index"/>
+ </index>
+ </add-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object1" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <column name="num" type="NUMBER(10)" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-table-oracle-patch.xml b/odb-tests/common/changelog/add-table-oracle-patch.xml
new file mode 100644
index 0000000..969c2e0
--- /dev/null
+++ b/odb-tests/common/changelog/add-table-oracle-patch.xml
@@ -0,0 +1,44 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object1" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <column name="num" type="NUMBER(10)" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ <table name="object" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <column name="num" type="NUMBER(10)" null="false"/>
+ <column name="o1" type="NUMBER(10)" null="true"/>
+ <primary-key auto="true" sequence="object_seq">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object_nums" kind="container">
+ <column name="object_id" type="NUMBER(10)" null="false"/>
+ <column name="index" type="NUMBER(20)" null="false"/>
+ <column name="value" type="NUMBER(10)" null="false"/>
+ <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_nums_object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="object_nums_index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-table-pgsql-diff.xml b/odb-tests/common/changelog/add-table-pgsql-diff.xml
new file mode 100644
index 0000000..9b48062
--- /dev/null
+++ b/odb-tests/common/changelog/add-table-pgsql-diff.xml
@@ -0,0 +1,45 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1">
+ <changeset version="2">
+ <add-table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <column name="o1" type="INTEGER" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </add-table>
+ <add-table name="object_nums" kind="container">
+ <column name="object_id" type="INTEGER" null="false"/>
+ <column name="index" type="BIGINT" null="false"/>
+ <column name="value" type="INTEGER" null="false"/>
+ <foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_nums_object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="object_nums_index_i">
+ <column name="index"/>
+ </index>
+ </add-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object1" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-table-pgsql-patch.xml b/odb-tests/common/changelog/add-table-pgsql-patch.xml
new file mode 100644
index 0000000..b04a933
--- /dev/null
+++ b/odb-tests/common/changelog/add-table-pgsql-patch.xml
@@ -0,0 +1,44 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object1" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <column name="o1" type="INTEGER" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object_nums" kind="container">
+ <column name="object_id" type="INTEGER" null="false"/>
+ <column name="index" type="BIGINT" null="false"/>
+ <column name="value" type="INTEGER" null="false"/>
+ <foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_nums_object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="object_nums_index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-table-sqlite-diff.xml b/odb-tests/common/changelog/add-table-sqlite-diff.xml
new file mode 100644
index 0000000..573bc69
--- /dev/null
+++ b/odb-tests/common/changelog/add-table-sqlite-diff.xml
@@ -0,0 +1,45 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
+ <changeset version="2">
+ <add-table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <column name="o1" type="INTEGER" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </add-table>
+ <add-table name="object_nums" kind="container">
+ <column name="object_id" type="INTEGER" null="false"/>
+ <column name="index" type="INTEGER" null="false"/>
+ <column name="value" type="INTEGER" null="false"/>
+ <foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_nums_object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="object_nums_index_i">
+ <column name="index"/>
+ </index>
+ </add-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object1" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-table-sqlite-patch.xml b/odb-tests/common/changelog/add-table-sqlite-patch.xml
new file mode 100644
index 0000000..3506410
--- /dev/null
+++ b/odb-tests/common/changelog/add-table-sqlite-patch.xml
@@ -0,0 +1,44 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object1" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <column name="o1" type="INTEGER" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object_nums" kind="container">
+ <column name="object_id" type="INTEGER" null="false"/>
+ <column name="index" type="INTEGER" null="false"/>
+ <column name="value" type="INTEGER" null="false"/>
+ <foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_nums_object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="object_nums_index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/add-table.hxx b/odb-tests/common/changelog/add-table.hxx
new file mode 100644
index 0000000..a22e206
--- /dev/null
+++ b/odb-tests/common/changelog/add-table.hxx
@@ -0,0 +1,34 @@
+// file : common/changelog/add-table.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ADD_TABLE_HXX
+#define ADD_TABLE_HXX
+
+#include <vector>
+
+#pragma db model version(BVER, CVER, open)
+
+struct object1;
+
+#if CVER > 1
+#pragma db object
+struct object
+{
+ #pragma db id auto
+ int id;
+ int num;
+
+ std::vector<int> nums;
+ object1* o1;
+};
+#endif
+
+#pragma db object
+struct object1
+{
+ #pragma db id
+ int id;
+ int num;
+};
+
+#endif // ADD_TABLE_HXX
diff --git a/odb-tests/common/changelog/alter-column-mssql-diff.xml b/odb-tests/common/changelog/alter-column-mssql-diff.xml
new file mode 100644
index 0000000..6c1fb6a
--- /dev/null
+++ b/odb-tests/common/changelog/alter-column-mssql-diff.xml
@@ -0,0 +1,17 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <alter-column name="num" null="true"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/alter-column-mssql-patch.xml b/odb-tests/common/changelog/alter-column-mssql-patch.xml
new file mode 100644
index 0000000..15db347
--- /dev/null
+++ b/odb-tests/common/changelog/alter-column-mssql-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/alter-column-mysql-diff.xml b/odb-tests/common/changelog/alter-column-mysql-diff.xml
new file mode 100644
index 0000000..39ad6ef
--- /dev/null
+++ b/odb-tests/common/changelog/alter-column-mysql-diff.xml
@@ -0,0 +1,17 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <alter-column name="num" null="true"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/alter-column-mysql-patch.xml b/odb-tests/common/changelog/alter-column-mysql-patch.xml
new file mode 100644
index 0000000..0131466
--- /dev/null
+++ b/odb-tests/common/changelog/alter-column-mysql-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/alter-column-oracle-diff.xml b/odb-tests/common/changelog/alter-column-oracle-diff.xml
new file mode 100644
index 0000000..d41d333
--- /dev/null
+++ b/odb-tests/common/changelog/alter-column-oracle-diff.xml
@@ -0,0 +1,17 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <alter-column name="num" null="true"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <column name="num" type="NUMBER(10)" null="false"/>
+ <primary-key auto="true" sequence="object_seq">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/alter-column-oracle-patch.xml b/odb-tests/common/changelog/alter-column-oracle-patch.xml
new file mode 100644
index 0000000..0e0794d
--- /dev/null
+++ b/odb-tests/common/changelog/alter-column-oracle-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <column name="num" type="NUMBER(10)" null="true"/>
+ <primary-key auto="true" sequence="object_seq">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/alter-column-pgsql-diff.xml b/odb-tests/common/changelog/alter-column-pgsql-diff.xml
new file mode 100644
index 0000000..fd97fa0
--- /dev/null
+++ b/odb-tests/common/changelog/alter-column-pgsql-diff.xml
@@ -0,0 +1,17 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <alter-column name="num" null="true"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/alter-column-pgsql-patch.xml b/odb-tests/common/changelog/alter-column-pgsql-patch.xml
new file mode 100644
index 0000000..dade1a3
--- /dev/null
+++ b/odb-tests/common/changelog/alter-column-pgsql-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/alter-column-sqlite-diff.xml b/odb-tests/common/changelog/alter-column-sqlite-diff.xml
new file mode 100644
index 0000000..7ecea06
--- /dev/null
+++ b/odb-tests/common/changelog/alter-column-sqlite-diff.xml
@@ -0,0 +1,17 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <alter-column name="num" null="true"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/alter-column-sqlite-patch.xml b/odb-tests/common/changelog/alter-column-sqlite-patch.xml
new file mode 100644
index 0000000..de2762e
--- /dev/null
+++ b/odb-tests/common/changelog/alter-column-sqlite-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/alter-column.hxx b/odb-tests/common/changelog/alter-column.hxx
new file mode 100644
index 0000000..02f091d
--- /dev/null
+++ b/odb-tests/common/changelog/alter-column.hxx
@@ -0,0 +1,21 @@
+// file : common/changelog/alter-column.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ALTER_COLUMN_HXX
+#define ALTER_COLUMN_HXX
+
+#pragma db model version(BVER, CVER, open)
+
+#pragma db object
+struct object
+{
+ #pragma db id auto
+ int id;
+
+#if CVER > 1
+ #pragma db null
+#endif
+ int num;
+};
+
+#endif // ALTER_COLUMN_HXX
diff --git a/odb-tests/common/changelog/buildfile b/odb-tests/common/changelog/buildfile
new file mode 100644
index 0000000..04e0685
--- /dev/null
+++ b/odb-tests/common/changelog/buildfile
@@ -0,0 +1,30 @@
+# file : common/changelog/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+./: file{odb.options} xml{*} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the below ad hoc recipe.
+#
+libue{test-meta}: $libodb
+
+file{odb.options}: libue{test-meta}
+{{
+ pops = $cxx.lib_poptions($<[0])
+ depdb hash $pops
+
+ f = $path($>[0])
+ rm -f $f
+
+ for o: $pops
+ echo $o >+$f
+ end
+}}
+
+# Testscript's run-time prerequisites.
+#
+testscript@./: test = $odb
+
+./: $odb: clean = false
diff --git a/odb-tests/common/changelog/drop-column-mssql-diff.xml b/odb-tests/common/changelog/drop-column-mssql-diff.xml
new file mode 100644
index 0000000..f2bab96
--- /dev/null
+++ b/odb-tests/common/changelog/drop-column-mssql-diff.xml
@@ -0,0 +1,17 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <drop-column name="num"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-column-mssql-patch.xml b/odb-tests/common/changelog/drop-column-mssql-patch.xml
new file mode 100644
index 0000000..32402a3
--- /dev/null
+++ b/odb-tests/common/changelog/drop-column-mssql-patch.xml
@@ -0,0 +1,12 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-column-mysql-diff.xml b/odb-tests/common/changelog/drop-column-mysql-diff.xml
new file mode 100644
index 0000000..2bb321d
--- /dev/null
+++ b/odb-tests/common/changelog/drop-column-mysql-diff.xml
@@ -0,0 +1,17 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <drop-column name="num"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-column-mysql-patch.xml b/odb-tests/common/changelog/drop-column-mysql-patch.xml
new file mode 100644
index 0000000..6572ebe
--- /dev/null
+++ b/odb-tests/common/changelog/drop-column-mysql-patch.xml
@@ -0,0 +1,12 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-column-oracle-diff.xml b/odb-tests/common/changelog/drop-column-oracle-diff.xml
new file mode 100644
index 0000000..e920a12
--- /dev/null
+++ b/odb-tests/common/changelog/drop-column-oracle-diff.xml
@@ -0,0 +1,17 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <drop-column name="num"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <column name="num" type="NUMBER(10)" null="false"/>
+ <primary-key auto="true" sequence="object_seq">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-column-oracle-patch.xml b/odb-tests/common/changelog/drop-column-oracle-patch.xml
new file mode 100644
index 0000000..b113664
--- /dev/null
+++ b/odb-tests/common/changelog/drop-column-oracle-patch.xml
@@ -0,0 +1,12 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <primary-key auto="true" sequence="object_seq">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-column-pgsql-diff.xml b/odb-tests/common/changelog/drop-column-pgsql-diff.xml
new file mode 100644
index 0000000..d2e91ba
--- /dev/null
+++ b/odb-tests/common/changelog/drop-column-pgsql-diff.xml
@@ -0,0 +1,17 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <drop-column name="num"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-column-pgsql-patch.xml b/odb-tests/common/changelog/drop-column-pgsql-patch.xml
new file mode 100644
index 0000000..06cc73d
--- /dev/null
+++ b/odb-tests/common/changelog/drop-column-pgsql-patch.xml
@@ -0,0 +1,12 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-column-sqlite-diff.xml b/odb-tests/common/changelog/drop-column-sqlite-diff.xml
new file mode 100644
index 0000000..1c9d138
--- /dev/null
+++ b/odb-tests/common/changelog/drop-column-sqlite-diff.xml
@@ -0,0 +1,17 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <drop-column name="num"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-column-sqlite-patch.xml b/odb-tests/common/changelog/drop-column-sqlite-patch.xml
new file mode 100644
index 0000000..6887530
--- /dev/null
+++ b/odb-tests/common/changelog/drop-column-sqlite-patch.xml
@@ -0,0 +1,12 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-column.hxx b/odb-tests/common/changelog/drop-column.hxx
new file mode 100644
index 0000000..3de237d
--- /dev/null
+++ b/odb-tests/common/changelog/drop-column.hxx
@@ -0,0 +1,20 @@
+// file : common/changelog/drop-column.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef DROP_COLUMN_HXX
+#define DROP_COLUMN_HXX
+
+#pragma db model version(BVER, CVER, open)
+
+#pragma db object
+struct object
+{
+ #pragma db id auto
+ int id;
+
+#if CVER == 1
+ int num;
+#endif
+};
+
+#endif // DROP_COLUMN_HXX
diff --git a/odb-tests/common/changelog/drop-foreign-key-mssql-diff.xml b/odb-tests/common/changelog/drop-foreign-key-mssql-diff.xml
new file mode 100644
index 0000000..da3ce73
--- /dev/null
+++ b/odb-tests/common/changelog/drop-foreign-key-mssql-diff.xml
@@ -0,0 +1,30 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <drop-foreign-key name="object_o1_fk"/>
+ <drop-column name="o1"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="o1" type="INT" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-foreign-key-mssql-patch.xml b/odb-tests/common/changelog/drop-foreign-key-mssql-patch.xml
new file mode 100644
index 0000000..cd1a372
--- /dev/null
+++ b/odb-tests/common/changelog/drop-foreign-key-mssql-patch.xml
@@ -0,0 +1,18 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-foreign-key-mysql-diff.xml b/odb-tests/common/changelog/drop-foreign-key-mysql-diff.xml
new file mode 100644
index 0000000..aa179f2
--- /dev/null
+++ b/odb-tests/common/changelog/drop-foreign-key-mysql-diff.xml
@@ -0,0 +1,30 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <drop-foreign-key name="object_o1_fk"/>
+ <drop-column name="o1"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="o1" type="INT" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object1" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-foreign-key-mysql-patch.xml b/odb-tests/common/changelog/drop-foreign-key-mysql-patch.xml
new file mode 100644
index 0000000..67f026e
--- /dev/null
+++ b/odb-tests/common/changelog/drop-foreign-key-mysql-patch.xml
@@ -0,0 +1,18 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ <table name="object1" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-foreign-key-oracle-diff.xml b/odb-tests/common/changelog/drop-foreign-key-oracle-diff.xml
new file mode 100644
index 0000000..aa407d3
--- /dev/null
+++ b/odb-tests/common/changelog/drop-foreign-key-oracle-diff.xml
@@ -0,0 +1,30 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <drop-foreign-key name="object_o1_fk"/>
+ <drop-column name="o1"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <column name="o1" type="NUMBER(10)" null="true"/>
+ <primary-key auto="true" sequence="object_seq">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-foreign-key-oracle-patch.xml b/odb-tests/common/changelog/drop-foreign-key-oracle-patch.xml
new file mode 100644
index 0000000..56253f0
--- /dev/null
+++ b/odb-tests/common/changelog/drop-foreign-key-oracle-patch.xml
@@ -0,0 +1,18 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <primary-key auto="true" sequence="object_seq">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-foreign-key-pgsql-diff.xml b/odb-tests/common/changelog/drop-foreign-key-pgsql-diff.xml
new file mode 100644
index 0000000..9a6259a
--- /dev/null
+++ b/odb-tests/common/changelog/drop-foreign-key-pgsql-diff.xml
@@ -0,0 +1,30 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <drop-foreign-key name="o1_fk"/>
+ <drop-column name="o1"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="o1" type="INTEGER" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-foreign-key-pgsql-patch.xml b/odb-tests/common/changelog/drop-foreign-key-pgsql-patch.xml
new file mode 100644
index 0000000..df024b4
--- /dev/null
+++ b/odb-tests/common/changelog/drop-foreign-key-pgsql-patch.xml
@@ -0,0 +1,18 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-foreign-key-sqlite-diff.xml b/odb-tests/common/changelog/drop-foreign-key-sqlite-diff.xml
new file mode 100644
index 0000000..6f9f994
--- /dev/null
+++ b/odb-tests/common/changelog/drop-foreign-key-sqlite-diff.xml
@@ -0,0 +1,30 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <drop-foreign-key name="o1_fk"/>
+ <drop-column name="o1"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="o1" type="INTEGER" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-foreign-key-sqlite-patch.xml b/odb-tests/common/changelog/drop-foreign-key-sqlite-patch.xml
new file mode 100644
index 0000000..6e63218
--- /dev/null
+++ b/odb-tests/common/changelog/drop-foreign-key-sqlite-patch.xml
@@ -0,0 +1,18 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-foreign-key.hxx b/odb-tests/common/changelog/drop-foreign-key.hxx
new file mode 100644
index 0000000..ba3005f
--- /dev/null
+++ b/odb-tests/common/changelog/drop-foreign-key.hxx
@@ -0,0 +1,29 @@
+// file : common/changelog/drop-foreign-key.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef DROP_FOREIGN_KEY_HXX
+#define DROP_FOREIGN_KEY_HXX
+
+#pragma db model version(BVER, CVER, open)
+
+struct object1;
+
+#pragma db object
+struct object
+{
+ #pragma db id auto
+ int id;
+
+#if CVER == 1
+ object1* o1;
+#endif
+};
+
+#pragma db object
+struct object1
+{
+ #pragma db id
+ int id;
+};
+
+#endif // DROP_FOREIGN_KEY_HXX
diff --git a/odb-tests/common/changelog/drop-index-mssql-diff.xml b/odb-tests/common/changelog/drop-index-mssql-diff.xml
new file mode 100644
index 0000000..ac95db5
--- /dev/null
+++ b/odb-tests/common/changelog/drop-index-mssql-diff.xml
@@ -0,0 +1,20 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <drop-index name="num_i"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <index name="num_i">
+ <column name="num"/>
+ </index>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-index-mssql-patch.xml b/odb-tests/common/changelog/drop-index-mssql-patch.xml
new file mode 100644
index 0000000..4f396d9
--- /dev/null
+++ b/odb-tests/common/changelog/drop-index-mssql-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-index-mysql-diff.xml b/odb-tests/common/changelog/drop-index-mysql-diff.xml
new file mode 100644
index 0000000..f8c95ef
--- /dev/null
+++ b/odb-tests/common/changelog/drop-index-mysql-diff.xml
@@ -0,0 +1,20 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <drop-index name="num_i"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <index name="num_i">
+ <column name="num"/>
+ </index>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-index-mysql-patch.xml b/odb-tests/common/changelog/drop-index-mysql-patch.xml
new file mode 100644
index 0000000..14f3f01
--- /dev/null
+++ b/odb-tests/common/changelog/drop-index-mysql-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-index-oracle-diff.xml b/odb-tests/common/changelog/drop-index-oracle-diff.xml
new file mode 100644
index 0000000..d174802
--- /dev/null
+++ b/odb-tests/common/changelog/drop-index-oracle-diff.xml
@@ -0,0 +1,20 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <drop-index name="object_num_i"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <column name="num" type="NUMBER(10)" null="false"/>
+ <primary-key auto="true" sequence="object_seq">
+ <column name="id"/>
+ </primary-key>
+ <index name="object_num_i">
+ <column name="num"/>
+ </index>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-index-oracle-patch.xml b/odb-tests/common/changelog/drop-index-oracle-patch.xml
new file mode 100644
index 0000000..38fb8d6
--- /dev/null
+++ b/odb-tests/common/changelog/drop-index-oracle-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <column name="num" type="NUMBER(10)" null="false"/>
+ <primary-key auto="true" sequence="object_seq">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-index-pgsql-diff.xml b/odb-tests/common/changelog/drop-index-pgsql-diff.xml
new file mode 100644
index 0000000..375a3d8
--- /dev/null
+++ b/odb-tests/common/changelog/drop-index-pgsql-diff.xml
@@ -0,0 +1,20 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <drop-index name="object_num_i"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <index name="object_num_i">
+ <column name="num"/>
+ </index>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-index-pgsql-patch.xml b/odb-tests/common/changelog/drop-index-pgsql-patch.xml
new file mode 100644
index 0000000..7f7d9a0
--- /dev/null
+++ b/odb-tests/common/changelog/drop-index-pgsql-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-index-sqlite-diff.xml b/odb-tests/common/changelog/drop-index-sqlite-diff.xml
new file mode 100644
index 0000000..bf54f9d
--- /dev/null
+++ b/odb-tests/common/changelog/drop-index-sqlite-diff.xml
@@ -0,0 +1,20 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
+ <changeset version="2">
+ <alter-table name="object">
+ <drop-index name="object_num_i"/>
+ </alter-table>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <index name="object_num_i">
+ <column name="num"/>
+ </index>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-index-sqlite-patch.xml b/odb-tests/common/changelog/drop-index-sqlite-patch.xml
new file mode 100644
index 0000000..fbe4428
--- /dev/null
+++ b/odb-tests/common/changelog/drop-index-sqlite-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-index.hxx b/odb-tests/common/changelog/drop-index.hxx
new file mode 100644
index 0000000..08fecba
--- /dev/null
+++ b/odb-tests/common/changelog/drop-index.hxx
@@ -0,0 +1,21 @@
+// file : common/changelog/drop-index.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef DROP_INDEX_HXX
+#define DROP_INDEX_HXX
+
+#pragma db model version(BVER, CVER, open)
+
+#pragma db object
+struct object
+{
+ #pragma db id auto
+ int id;
+
+#if CVER == 1
+ #pragma db index
+#endif
+ int num;
+};
+
+#endif // DROP_INDEX_HXX
diff --git a/odb-tests/common/changelog/drop-table-mssql-diff.xml b/odb-tests/common/changelog/drop-table-mssql-diff.xml
new file mode 100644
index 0000000..399d8bc
--- /dev/null
+++ b/odb-tests/common/changelog/drop-table-mssql-diff.xml
@@ -0,0 +1,47 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1">
+ <changeset version="2">
+ <drop-table name="object"/>
+ <drop-table name="object_nums"/>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <column name="o1" type="INT" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object_nums" kind="container">
+ <column name="object_id" type="INT" null="false"/>
+ <column name="index" type="BIGINT" null="false"/>
+ <column name="value" type="INT" null="false"/>
+ <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-table-mssql-patch.xml b/odb-tests/common/changelog/drop-table-mssql-patch.xml
new file mode 100644
index 0000000..c5dda75
--- /dev/null
+++ b/odb-tests/common/changelog/drop-table-mssql-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object1" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-table-mysql-diff.xml b/odb-tests/common/changelog/drop-table-mysql-diff.xml
new file mode 100644
index 0000000..3ccd553
--- /dev/null
+++ b/odb-tests/common/changelog/drop-table-mysql-diff.xml
@@ -0,0 +1,47 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1">
+ <changeset version="2">
+ <drop-table name="object"/>
+ <drop-table name="object_nums"/>
+ </changeset>
+
+ <model version="1">
+ <table name="object" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <column name="o1" type="INT" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object_nums" options="ENGINE=InnoDB" kind="container">
+ <column name="object_id" type="INT" null="false"/>
+ <column name="index" type="BIGINT UNSIGNED" null="false"/>
+ <column name="value" type="INT" null="false"/>
+ <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ <table name="object1" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-table-mysql-patch.xml b/odb-tests/common/changelog/drop-table-mysql-patch.xml
new file mode 100644
index 0000000..250bd20
--- /dev/null
+++ b/odb-tests/common/changelog/drop-table-mysql-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object1" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-table-oracle-diff.xml b/odb-tests/common/changelog/drop-table-oracle-diff.xml
new file mode 100644
index 0000000..589d64b
--- /dev/null
+++ b/odb-tests/common/changelog/drop-table-oracle-diff.xml
@@ -0,0 +1,47 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1">
+ <changeset version="2">
+ <drop-table name="object"/>
+ <drop-table name="object_nums"/>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <column name="num" type="NUMBER(10)" null="false"/>
+ <column name="o1" type="NUMBER(10)" null="true"/>
+ <primary-key auto="true" sequence="object_seq">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object_nums" kind="container">
+ <column name="object_id" type="NUMBER(10)" null="false"/>
+ <column name="index" type="NUMBER(20)" null="false"/>
+ <column name="value" type="NUMBER(10)" null="false"/>
+ <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_nums_object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="object_nums_index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <column name="num" type="NUMBER(10)" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-table-oracle-patch.xml b/odb-tests/common/changelog/drop-table-oracle-patch.xml
new file mode 100644
index 0000000..5f222dc
--- /dev/null
+++ b/odb-tests/common/changelog/drop-table-oracle-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object1" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <column name="num" type="NUMBER(10)" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-table-pgsql-diff.xml b/odb-tests/common/changelog/drop-table-pgsql-diff.xml
new file mode 100644
index 0000000..168ec46
--- /dev/null
+++ b/odb-tests/common/changelog/drop-table-pgsql-diff.xml
@@ -0,0 +1,47 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1">
+ <changeset version="2">
+ <drop-table name="object"/>
+ <drop-table name="object_nums"/>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <column name="o1" type="INTEGER" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object_nums" kind="container">
+ <column name="object_id" type="INTEGER" null="false"/>
+ <column name="index" type="BIGINT" null="false"/>
+ <column name="value" type="INTEGER" null="false"/>
+ <foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_nums_object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="object_nums_index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-table-pgsql-patch.xml b/odb-tests/common/changelog/drop-table-pgsql-patch.xml
new file mode 100644
index 0000000..ebb7000
--- /dev/null
+++ b/odb-tests/common/changelog/drop-table-pgsql-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object1" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-table-sqlite-diff.xml b/odb-tests/common/changelog/drop-table-sqlite-diff.xml
new file mode 100644
index 0000000..6a258c4
--- /dev/null
+++ b/odb-tests/common/changelog/drop-table-sqlite-diff.xml
@@ -0,0 +1,47 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
+ <changeset version="2">
+ <drop-table name="object"/>
+ <drop-table name="object_nums"/>
+ </changeset>
+
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <column name="o1" type="INTEGER" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="o1_fk" deferrable="DEFERRED">
+ <column name="o1"/>
+ <references table="object1">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="object_nums" kind="container">
+ <column name="object_id" type="INTEGER" null="false"/>
+ <column name="index" type="INTEGER" null="false"/>
+ <column name="value" type="INTEGER" null="false"/>
+ <foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_nums_object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="object_nums_index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-table-sqlite-patch.xml b/odb-tests/common/changelog/drop-table-sqlite-patch.xml
new file mode 100644
index 0000000..45c6f00
--- /dev/null
+++ b/odb-tests/common/changelog/drop-table-sqlite-patch.xml
@@ -0,0 +1,13 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
+ <changeset version="3"/>
+
+ <model version="2">
+ <table name="object1" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key>
+ <column name="id"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/drop-table.hxx b/odb-tests/common/changelog/drop-table.hxx
new file mode 100644
index 0000000..2919e52
--- /dev/null
+++ b/odb-tests/common/changelog/drop-table.hxx
@@ -0,0 +1,34 @@
+// file : common/changelog/drop-table.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef DROP_TABLE_HXX
+#define DROP_TABLE_HXX
+
+#include <vector>
+
+#pragma db model version(BVER, CVER, open)
+
+struct object1;
+
+#if CVER == 1
+#pragma db object
+struct object
+{
+ #pragma db id auto
+ int id;
+ int num;
+
+ std::vector<int> nums;
+ object1* o1;
+};
+#endif
+
+#pragma db object
+struct object1
+{
+ #pragma db id
+ int id;
+ int num;
+};
+
+#endif // DROP_TABLE_HXX
diff --git a/odb-tests/common/changelog/model-mssql-diff.xml b/odb-tests/common/changelog/model-mssql-diff.xml
new file mode 120000
index 0000000..e1f812d
--- /dev/null
+++ b/odb-tests/common/changelog/model-mssql-diff.xml
@@ -0,0 +1 @@
+model-mssql.xml \ No newline at end of file
diff --git a/odb-tests/common/changelog/model-mssql-patch.xml b/odb-tests/common/changelog/model-mssql-patch.xml
new file mode 120000
index 0000000..e1f812d
--- /dev/null
+++ b/odb-tests/common/changelog/model-mssql-patch.xml
@@ -0,0 +1 @@
+model-mssql.xml \ No newline at end of file
diff --git a/odb-tests/common/changelog/model-mssql.xml b/odb-tests/common/changelog/model-mssql.xml
new file mode 100644
index 0000000..509a210
--- /dev/null
+++ b/odb-tests/common/changelog/model-mssql.xml
@@ -0,0 +1,56 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1">
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="true" default="0" options="DUMMY=1"/>
+ <column name="v_x" type="INT" null="false"/>
+ <column name="v_y" type="INT" null="false"/>
+ <column name="o1_x" type="INT" null="true"/>
+ <column name="o1_y" type="INT" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1_x"/>
+ <column name="o1_y"/>
+ <references table="object1">
+ <column name="id_x"/>
+ <column name="id_y"/>
+ </references>
+ </foreign-key>
+ <index name="num_i" type="UNIQUE" method="BTREE" options="DUMMY=1">
+ <column name="num" options="DESC"/>
+ </index>
+ <index name="v_i">
+ <column name="v_x"/>
+ <column name="v_y"/>
+ </index>
+ </table>
+ <table name="object_nums" kind="container">
+ <column name="object_id" type="INT" null="false"/>
+ <column name="index" type="BIGINT" null="false"/>
+ <column name="value" type="INT" null="false"/>
+ <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id_x" type="INT" null="false"/>
+ <column name="id_y" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key>
+ <column name="id_x"/>
+ <column name="id_y"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/model-mysql-diff.xml b/odb-tests/common/changelog/model-mysql-diff.xml
new file mode 120000
index 0000000..9100280
--- /dev/null
+++ b/odb-tests/common/changelog/model-mysql-diff.xml
@@ -0,0 +1 @@
+model-mysql.xml \ No newline at end of file
diff --git a/odb-tests/common/changelog/model-mysql-patch.xml b/odb-tests/common/changelog/model-mysql-patch.xml
new file mode 120000
index 0000000..9100280
--- /dev/null
+++ b/odb-tests/common/changelog/model-mysql-patch.xml
@@ -0,0 +1 @@
+model-mysql.xml \ No newline at end of file
diff --git a/odb-tests/common/changelog/model-mysql.xml b/odb-tests/common/changelog/model-mysql.xml
new file mode 100644
index 0000000..ffbcf8d
--- /dev/null
+++ b/odb-tests/common/changelog/model-mysql.xml
@@ -0,0 +1,56 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1">
+ <model version="1">
+ <table name="object" options="ENGINE=InnoDB" kind="object">
+ <column name="id" type="INT" null="false"/>
+ <column name="num" type="INT" null="true" default="0" options="DUMMY=1"/>
+ <column name="v_x" type="INT" null="false"/>
+ <column name="v_y" type="INT" null="false"/>
+ <column name="o1_x" type="INT" null="true"/>
+ <column name="o1_y" type="INT" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1_x"/>
+ <column name="o1_y"/>
+ <references table="object1">
+ <column name="id_x"/>
+ <column name="id_y"/>
+ </references>
+ </foreign-key>
+ <index name="num_i" type="UNIQUE" method="BTREE" options="DUMMY=1">
+ <column name="num" options="DESC"/>
+ </index>
+ <index name="v_i">
+ <column name="v_x"/>
+ <column name="v_y"/>
+ </index>
+ </table>
+ <table name="object_nums" options="ENGINE=InnoDB" kind="container">
+ <column name="object_id" type="INT" null="false"/>
+ <column name="index" type="BIGINT UNSIGNED" null="false"/>
+ <column name="value" type="INT" null="false"/>
+ <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ <table name="object1" options="ENGINE=InnoDB" kind="object">
+ <column name="id_x" type="INT" null="false"/>
+ <column name="id_y" type="INT" null="false"/>
+ <column name="num" type="INT" null="false"/>
+ <primary-key>
+ <column name="id_x"/>
+ <column name="id_y"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/model-oracle-diff.xml b/odb-tests/common/changelog/model-oracle-diff.xml
new file mode 120000
index 0000000..36e4479
--- /dev/null
+++ b/odb-tests/common/changelog/model-oracle-diff.xml
@@ -0,0 +1 @@
+model-oracle.xml \ No newline at end of file
diff --git a/odb-tests/common/changelog/model-oracle-patch.xml b/odb-tests/common/changelog/model-oracle-patch.xml
new file mode 120000
index 0000000..36e4479
--- /dev/null
+++ b/odb-tests/common/changelog/model-oracle-patch.xml
@@ -0,0 +1 @@
+model-oracle.xml \ No newline at end of file
diff --git a/odb-tests/common/changelog/model-oracle.xml b/odb-tests/common/changelog/model-oracle.xml
new file mode 100644
index 0000000..1824690
--- /dev/null
+++ b/odb-tests/common/changelog/model-oracle.xml
@@ -0,0 +1,56 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1">
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="NUMBER(10)" null="false"/>
+ <column name="num" type="NUMBER(10)" null="true" default="0" options="DUMMY=1"/>
+ <column name="v_x" type="NUMBER(10)" null="false"/>
+ <column name="v_y" type="NUMBER(10)" null="false"/>
+ <column name="o1_x" type="NUMBER(10)" null="true"/>
+ <column name="o1_y" type="NUMBER(10)" null="true"/>
+ <primary-key auto="true" sequence="object_seq">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="object_o1_fk" deferrable="DEFERRED">
+ <column name="o1_x"/>
+ <column name="o1_y"/>
+ <references table="object1">
+ <column name="id_x"/>
+ <column name="id_y"/>
+ </references>
+ </foreign-key>
+ <index name="object_num_i" type="UNIQUE" method="BTREE" options="DUMMY=1">
+ <column name="num" options="DESC"/>
+ </index>
+ <index name="object_v_i">
+ <column name="v_x"/>
+ <column name="v_y"/>
+ </index>
+ </table>
+ <table name="object_nums" kind="container">
+ <column name="object_id" type="NUMBER(10)" null="false"/>
+ <column name="index" type="NUMBER(20)" null="false"/>
+ <column name="value" type="NUMBER(10)" null="false"/>
+ <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_nums_object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="object_nums_index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id_x" type="NUMBER(10)" null="false"/>
+ <column name="id_y" type="NUMBER(10)" null="false"/>
+ <column name="num" type="NUMBER(10)" null="false"/>
+ <primary-key>
+ <column name="id_x"/>
+ <column name="id_y"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/model-pgsql-diff.xml b/odb-tests/common/changelog/model-pgsql-diff.xml
new file mode 120000
index 0000000..b39ce26
--- /dev/null
+++ b/odb-tests/common/changelog/model-pgsql-diff.xml
@@ -0,0 +1 @@
+model-pgsql.xml \ No newline at end of file
diff --git a/odb-tests/common/changelog/model-pgsql-patch.xml b/odb-tests/common/changelog/model-pgsql-patch.xml
new file mode 120000
index 0000000..b39ce26
--- /dev/null
+++ b/odb-tests/common/changelog/model-pgsql-patch.xml
@@ -0,0 +1 @@
+model-pgsql.xml \ No newline at end of file
diff --git a/odb-tests/common/changelog/model-pgsql.xml b/odb-tests/common/changelog/model-pgsql.xml
new file mode 100644
index 0000000..13aee6b
--- /dev/null
+++ b/odb-tests/common/changelog/model-pgsql.xml
@@ -0,0 +1,56 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1">
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="true" default="0" options="DUMMY=1"/>
+ <column name="v_x" type="INTEGER" null="false"/>
+ <column name="v_y" type="INTEGER" null="false"/>
+ <column name="o1_x" type="INTEGER" null="true"/>
+ <column name="o1_y" type="INTEGER" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="o1_fk" deferrable="DEFERRED">
+ <column name="o1_x"/>
+ <column name="o1_y"/>
+ <references table="object1">
+ <column name="id_x"/>
+ <column name="id_y"/>
+ </references>
+ </foreign-key>
+ <index name="object_num_i" type="UNIQUE" method="BTREE" options="DUMMY=1">
+ <column name="num" options="DESC"/>
+ </index>
+ <index name="object_v_i">
+ <column name="v_x"/>
+ <column name="v_y"/>
+ </index>
+ </table>
+ <table name="object_nums" kind="container">
+ <column name="object_id" type="INTEGER" null="false"/>
+ <column name="index" type="BIGINT" null="false"/>
+ <column name="value" type="INTEGER" null="false"/>
+ <foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_nums_object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="object_nums_index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id_x" type="INTEGER" null="false"/>
+ <column name="id_y" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key>
+ <column name="id_x"/>
+ <column name="id_y"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/model-sqlite-diff.xml b/odb-tests/common/changelog/model-sqlite-diff.xml
new file mode 120000
index 0000000..3454f51
--- /dev/null
+++ b/odb-tests/common/changelog/model-sqlite-diff.xml
@@ -0,0 +1 @@
+model-sqlite.xml \ No newline at end of file
diff --git a/odb-tests/common/changelog/model-sqlite-patch.xml b/odb-tests/common/changelog/model-sqlite-patch.xml
new file mode 120000
index 0000000..3454f51
--- /dev/null
+++ b/odb-tests/common/changelog/model-sqlite-patch.xml
@@ -0,0 +1 @@
+model-sqlite.xml \ No newline at end of file
diff --git a/odb-tests/common/changelog/model-sqlite.xml b/odb-tests/common/changelog/model-sqlite.xml
new file mode 100644
index 0000000..d0199ed
--- /dev/null
+++ b/odb-tests/common/changelog/model-sqlite.xml
@@ -0,0 +1,56 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
+ <model version="1">
+ <table name="object" kind="object">
+ <column name="id" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="true" default="0" options="DUMMY=1"/>
+ <column name="v_x" type="INTEGER" null="false"/>
+ <column name="v_y" type="INTEGER" null="false"/>
+ <column name="o1_x" type="INTEGER" null="true"/>
+ <column name="o1_y" type="INTEGER" null="true"/>
+ <primary-key auto="true">
+ <column name="id"/>
+ </primary-key>
+ <foreign-key name="o1_fk" deferrable="DEFERRED">
+ <column name="o1_x"/>
+ <column name="o1_y"/>
+ <references table="object1">
+ <column name="id_x"/>
+ <column name="id_y"/>
+ </references>
+ </foreign-key>
+ <index name="object_num_i" type="UNIQUE" method="BTREE" options="DUMMY=1">
+ <column name="num" options="DESC"/>
+ </index>
+ <index name="object_v_i">
+ <column name="v_x"/>
+ <column name="v_y"/>
+ </index>
+ </table>
+ <table name="object_nums" kind="container">
+ <column name="object_id" type="INTEGER" null="false"/>
+ <column name="index" type="INTEGER" null="false"/>
+ <column name="value" type="INTEGER" null="false"/>
+ <foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="object_id"/>
+ <references table="object">
+ <column name="id"/>
+ </references>
+ </foreign-key>
+ <index name="object_nums_object_id_i">
+ <column name="object_id"/>
+ </index>
+ <index name="object_nums_index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ <table name="object1" kind="object">
+ <column name="id_x" type="INTEGER" null="false"/>
+ <column name="id_y" type="INTEGER" null="false"/>
+ <column name="num" type="INTEGER" null="false"/>
+ <primary-key>
+ <column name="id_x"/>
+ <column name="id_y"/>
+ </primary-key>
+ </table>
+ </model>
+</changelog>
diff --git a/odb-tests/common/changelog/model.hxx b/odb-tests/common/changelog/model.hxx
new file mode 100644
index 0000000..aa8891a
--- /dev/null
+++ b/odb-tests/common/changelog/model.hxx
@@ -0,0 +1,46 @@
+// file : common/changelog/model.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef MODEL_HXX
+#define MODEL_HXX
+
+#include <vector>
+
+#pragma db model version(1, 1, open)
+
+#pragma db value
+struct value
+{
+ int x;
+ int y;
+};
+
+struct object1;
+
+#pragma db object
+struct object
+{
+ #pragma db id auto
+ int id;
+
+ #pragma db null default(0) options("DUMMY=1")
+ int num;
+
+ #pragma db index unique member(num, "DESC") method("BTREE") options("DUMMY=1")
+
+ #pragma db index
+ value v; // Multi-column.
+
+ std::vector<int> nums;
+ object1* o1;
+};
+
+#pragma db object
+struct object1
+{
+ #pragma db id
+ value id; // Multi-column.
+ int num;
+};
+
+#endif // MODEL_HXX
diff --git a/odb-tests/common/changelog/testscript b/odb-tests/common/changelog/testscript
new file mode 100644
index 0000000..9368938
--- /dev/null
+++ b/odb-tests/common/changelog/testscript
@@ -0,0 +1,66 @@
+# file : common/changelog/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+headers = [paths] $path_search($src_base/*.hxx)
+
+odb_options = --generate-schema-only \
+ --schema-format sql \
+ --suppress-migration \
+ --options-file $out_base/odb.options
+
+: mysql
+:
+if $mysql
+{
+ odb_options += --database 'mysql' --changelog-dir $~
+
+ for h: $headers
+ n = $base($leaf($h))
+
+ $* $odb_options -DBVER=1 -DCVER=1 --init-changelog $h &$(n).xml &$(n).sql
+
+ $* $odb_options -DBVER=1 -DCVER=2 $h
+ diff $src_base/$n-mysql-diff.xml $(n).xml
+
+ $* $odb_options -DBVER=2 -DCVER=3 $h
+ diff $src_base/$n-mysql-patch.xml $(n).xml
+ end
+}
+
+: sqlite
+:
+if $sqlite
+{
+ odb_options += --database 'sqlite' --changelog-dir $~
+
+ for h: $headers
+ n = $base($leaf($h))
+
+ $* $odb_options -DBVER=1 -DCVER=1 --init-changelog $h &$(n).xml &$(n).sql
+
+ $* $odb_options -DBVER=1 -DCVER=2 $h
+ diff $src_base/$n-sqlite-diff.xml $(n).xml
+
+ $* $odb_options -DBVER=2 -DCVER=3 $h
+ diff $src_base/$n-sqlite-patch.xml $(n).xml
+ end
+}
+
+: pgsql
+:
+if $pgsql
+{
+ odb_options += --database 'pgsql' --changelog-dir $~
+
+ for h: $headers
+ n = $base($leaf($h))
+
+ $* $odb_options -DBVER=1 -DCVER=1 --init-changelog $h &$(n).xml &$(n).sql
+
+ $* $odb_options -DBVER=1 -DCVER=2 $h
+ diff $src_base/$n-pgsql-diff.xml $(n).xml
+
+ $* $odb_options -DBVER=2 -DCVER=3 $h
+ diff $src_base/$n-pgsql-patch.xml $(n).xml
+ end
+}
diff --git a/odb-tests/common/circular/buildfile b/odb-tests/common/circular/buildfile
new file mode 100644
index 0000000..2e793b9
--- /dev/null
+++ b/odb-tests/common/circular/buildfile
@@ -0,0 +1,9 @@
+# file : common/circular/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -multiple/}
+
+# We cannot support this case in multi-database support since we need to
+# generate extern template involving classes that participate in the cycle.
+#
+./: multiple/: include = (!$multi)
diff --git a/odb-tests/common/circular/multiple/.gitignore b/odb-tests/common/circular/multiple/.gitignore
new file mode 100644
index 0000000..5d39d39
--- /dev/null
+++ b/odb-tests/common/circular/multiple/.gitignore
@@ -0,0 +1,6 @@
+# ODB-generated files.
+#
+test1-odb.?xx
+test1-odb-*.?xx
+test2-odb.?xx
+test2-odb-*.?xx
diff --git a/odb-tests/common/circular/multiple/buildfile b/odb-tests/common/circular/multiple/buildfile
new file mode 100644
index 0000000..51a1191
--- /dev/null
+++ b/odb-tests/common/circular/multiple/buildfile
@@ -0,0 +1,49 @@
+# file : common/circular/multiple/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+hdrs = test1 test2
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+for h: $hdrs
+{
+ exe{driver}: {hxx ixx cxx}{$h-odb}
+
+ <{hxx ixx cxx}{$h-odb}>: hxx{$h} libue{test-meta}
+
+ for db: $databases
+ {
+ exe{driver}: {hxx ixx cxx}{$h-odb-$db}: include = $multi
+ <{hxx ixx cxx}{$h-odb-$db}>: hxx{$h} libue{test-meta}
+ }
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix circular_m_ \
+ --generate-schema \
+ --generate-query \
+ --schema-format embedded
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/circular/multiple/driver.cxx b/odb-tests/common/circular/multiple/driver.cxx
new file mode 100644
index 0000000..4887ac2
--- /dev/null
+++ b/odb-tests/common/circular/multiple/driver.cxx
@@ -0,0 +1,69 @@
+// file : common/circular/multiple/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test cases of circular dependencies between persistent classes, multiple
+// files version.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/connection.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test1.hxx"
+#include "test2.hxx"
+
+#include "test2-odb.hxx"
+#include "test1-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv, false));
+
+ // Create the database schema.
+ //
+ {
+ connection_ptr c (db->connection ());
+
+ // Temporarily disable foreign key constraints for MySQL and SQLite.
+ // For these databases this is the only way to drop circularly-
+ // dependant tables.
+ //
+ if (db->id () == odb::id_mysql)
+ c->execute ("SET FOREIGN_KEY_CHECKS=0");
+ else if (db->id () == odb::id_sqlite)
+ c->execute ("PRAGMA foreign_keys=OFF");
+
+ transaction t (c->begin ());
+ schema_catalog::create_schema (*db);
+ t.commit ();
+
+ if (db->id () == odb::id_mysql)
+ c->execute ("SET FOREIGN_KEY_CHECKS=1");
+ else if (db->id () == odb::id_sqlite)
+ c->execute ("PRAGMA foreign_keys=ON");
+ }
+
+ query<base> bq (query<base>::d->id != 0);
+ query<derived> dq (query<derived>::b->id != 0);
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/circular/multiple/test1.hxx b/odb-tests/common/circular/multiple/test1.hxx
new file mode 100644
index 0000000..36df963
--- /dev/null
+++ b/odb-tests/common/circular/multiple/test1.hxx
@@ -0,0 +1,27 @@
+// file : common/circular/multiple/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct derived;
+
+#pragma db object polymorphic
+struct base
+{
+ virtual ~base () {}
+
+ #pragma db id
+ unsigned long id_;
+
+ derived* d_;
+};
+
+#ifdef ODB_COMPILER
+# include "test2.hxx"
+#endif
+
+#endif // TEST1_HXX
diff --git a/odb-tests/common/circular/multiple/test2.hxx b/odb-tests/common/circular/multiple/test2.hxx
new file mode 100644
index 0000000..49e9ed2
--- /dev/null
+++ b/odb-tests/common/circular/multiple/test2.hxx
@@ -0,0 +1,17 @@
+// file : common/circular/multiple/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+#include <odb/core.hxx>
+
+#include "test1.hxx"
+
+#pragma db object
+struct derived: base
+{
+ base* b_;
+};
+
+#endif // TEST2_HXX
diff --git a/odb-tests/common/circular/multiple/testscript b/odb-tests/common/circular/multiple/testscript
new file mode 100644
index 0000000..6a05dc6
--- /dev/null
+++ b/odb-tests/common/circular/multiple/testscript
@@ -0,0 +1,31 @@
+# file : common/circular/multiple/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $*
+}
diff --git a/odb-tests/common/circular/single/buildfile b/odb-tests/common/circular/single/buildfile
new file mode 100644
index 0000000..740ce91
--- /dev/null
+++ b/odb-tests/common/circular/single/buildfile
@@ -0,0 +1,41 @@
+# file : common/circular/single/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix circular_s_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/circular/single/driver.cxx b/odb-tests/common/circular/single/driver.cxx
new file mode 100644
index 0000000..9bcb135
--- /dev/null
+++ b/odb-tests/common/circular/single/driver.cxx
@@ -0,0 +1,40 @@
+// file : common/circular/single/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test cases of circular dependencies between persistent classes, single
+// file version.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ query<base> bq (query<base>::d->id != 0);
+ query<derived> dq (query<derived>::b->id != 0);
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/circular/single/test.hxx b/odb-tests/common/circular/single/test.hxx
new file mode 100644
index 0000000..7f95dea
--- /dev/null
+++ b/odb-tests/common/circular/single/test.hxx
@@ -0,0 +1,28 @@
+// file : common/circular/single/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+
+struct derived;
+
+#pragma db object polymorphic
+struct base
+{
+ virtual ~base () {}
+
+ #pragma db id
+ unsigned long id_;
+
+ derived* d_;
+};
+
+#pragma db object
+struct derived: base
+{
+ base* b_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/circular/single/testscript b/odb-tests/common/circular/single/testscript
new file mode 100644
index 0000000..b870306
--- /dev/null
+++ b/odb-tests/common/circular/single/testscript
@@ -0,0 +1,33 @@
+# file : common/circular/single/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/composite/buildfile b/odb-tests/common/composite/buildfile
new file mode 100644
index 0000000..0a60638
--- /dev/null
+++ b/odb-tests/common/composite/buildfile
@@ -0,0 +1,41 @@
+# file : common/composite/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix t_comp_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/composite/driver.cxx b/odb-tests/common/composite/driver.cxx
new file mode 100644
index 0000000..06b24b2
--- /dev/null
+++ b/odb-tests/common/composite/driver.cxx
@@ -0,0 +1,229 @@
+// file : common/composite/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test composite value types.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+#undef NDEBUG
+#include <cassert>
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Test basic composite functionality.
+ //
+ for (unsigned short i (0); i < 2; ++i)
+ {
+ using namespace test1;
+
+ person p (1);
+ p.name_.first = "Joe";
+ p.name_.last = "Dirt";
+ p.name_.title = "Mr";
+ p.name_.alias.first = "Anthony";
+ p.name_.alias.last = "Clean";
+ p.name_.nick = "Squeaky";
+ p.name_.flags.nick = true;
+ p.name_.flags.alias = false;
+ p.age_ = 32;
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (p);
+ t.commit ();
+ }
+
+ // load & check
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<person> p1 (db->load<person> (1));
+ t.commit ();
+
+ assert (p == *p1);
+ }
+
+ p.name_.title = "Mrs";
+ p.name_.alias.first = "Anthonia";
+ p.name_.flags.nick = false;
+ p.name_.flags.alias = true;
+
+ // update
+ //
+ {
+ transaction t (db->begin ());
+ db->update (p);
+ t.commit ();
+ }
+
+ // load & check
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<person> p1 (db->load<person> (1));
+ t.commit ();
+
+ assert (p == *p1);
+ }
+
+ typedef odb::query<person> query;
+ typedef odb::result<person> result;
+
+ // query
+ //
+ {
+ transaction t (db->begin ());
+
+ result r (db->query<person> (query::name.first == "Joe"));
+
+ assert (!r.empty ());
+ assert (*r.begin () == p);
+ assert (size (r) == 1);
+
+ t.commit ();
+ }
+
+ // query
+ //
+ {
+ transaction t (db->begin ());
+
+ result r (db->query<person> (query::name.flags.alias));
+
+ assert (!r.empty ());
+ assert (*r.begin () == p);
+ assert (size (r) == 1);
+
+ t.commit ();
+ }
+
+ // erase
+ //
+ if (i == 0)
+ {
+ transaction t (db->begin ());
+ db->erase<person> (1);
+ t.commit ();
+ }
+ }
+
+ // Test composite class template instantiation.
+ //
+ {
+ using namespace test2;
+
+ object o (1);
+
+ o.comp_.num = 123;
+ o.comp_.str = "abc";
+ o.comp_.vec.push_back (int_str_pair (123, "abc"));
+ o.comp_.vec.push_back (int_str_pair (234, "bcd"));
+ o.comp_.vec.push_back (int_str_pair (345, "cde"));
+
+ o.pair_.first = 123;
+ o.pair_.second = "abc";
+
+ o.vec_.push_back (int_str_pair (123, "abc"));
+ o.vec_.push_back (int_str_pair (234, "bcd"));
+ o.vec_.push_back (int_str_pair (345, "cde"));
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // load & check
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+ }
+
+ // Test empty column name.
+ //
+ {
+ using namespace test3;
+
+ object o (1);
+ o.c_.str = "abc";
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // load & check
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+ }
+
+ // Test composite definition inside object.
+ {
+ using namespace test4;
+
+ object o (1);
+ o.str ("abc");
+ o.x (123);
+ o.y (234);
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // load & check
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/composite/test.hxx b/odb-tests/common/composite/test.hxx
new file mode 100644
index 0000000..13b2025
--- /dev/null
+++ b/odb-tests/common/composite/test.hxx
@@ -0,0 +1,250 @@
+// file : common/composite/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <vector>
+#include <utility> // std::pair
+
+#include <odb/core.hxx>
+
+// Test basic composite functionality.
+//
+#pragma db namespace table("t1_")
+namespace test1
+{
+ #pragma db value
+ struct name
+ {
+ std::string first;
+ std::string last;
+ };
+
+ #pragma db value
+ struct name_title
+ {
+ std::string title;
+ };
+
+ #pragma db value
+ struct name_title_ex: name_title
+ {
+ // Test value types without data members.
+ };
+
+ #pragma db value
+ struct name_flags
+ {
+ bool nick;
+ bool alias;
+ };
+
+ #pragma db value
+ struct name_ex: name, name_title_ex
+ {
+ name alias;
+ std::string nick;
+
+ #pragma db column("show_")
+ name_flags flags;
+ };
+
+ #pragma db object
+ struct person
+ {
+ person () {}
+ person (unsigned long id): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db column("")
+ name_ex name_;
+
+ unsigned short age_;
+ };
+
+ inline bool
+ operator== (const person& x, const person& y)
+ {
+ return x.id_ == y.id_ &&
+ x.name_.first == y.name_.first&&
+ x.name_.last == y.name_.last &&
+ x.name_.title == y.name_.title &&
+ x.name_.alias.first == y.name_.alias.first &&
+ x.name_.alias.last == y.name_.alias.last &&
+ x.name_.nick == y.name_.nick &&
+ x.name_.flags.nick == y.name_.flags.nick &&
+ x.name_.flags.alias == y.name_.flags.alias &&
+ x.age_ == y.age_;
+ }
+}
+
+// Test composite class template instantiation.
+//
+#pragma db namespace table("t2_")
+namespace test2
+{
+ template <typename I, typename S>
+ struct comp
+ {
+ I num;
+ S str;
+ std::vector<std::pair<I, S> > vec;
+ };
+
+ template <typename I, typename S>
+ inline bool
+ operator== (const comp<I, S>& x, const comp<I, S>& y)
+ {
+ return x.num == y.num && x.str == y.str && x.vec == y.vec;
+ }
+
+ typedef std::pair<int, std::string> int_str_pair;
+ #pragma db value(int_str_pair)
+
+ // Make sure we use the name that was specified in the pragma.
+ //
+#ifdef ODB_COMPILER
+ typedef comp<int, std::string> int_str_comp1;
+#endif
+
+ typedef comp<int, std::string> int_str_comp;
+ #pragma db value(int_str_comp)
+
+ #pragma db object
+ struct object
+ {
+ object () {}
+ object (unsigned long id): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ comp<int, std::string> comp_;
+ std::pair<int, std::string> pair_;
+ std::vector<int_str_pair> vec_;
+ };
+
+ inline bool
+ operator== (const object& x, const object& y)
+ {
+ return x.id_ == y.id_ &&
+ x.comp_ == y.comp_ &&
+ x.pair_ == y.pair_ &&
+ x.vec_ == y.vec_;
+ }
+}
+
+// Test empty column name.
+//
+#pragma db namespace table("t3_")
+namespace test3
+{
+ #pragma db value
+ struct comp
+ {
+ #pragma db column("")
+ std::string str;
+ };
+
+ #pragma db object
+ struct object
+ {
+ object () {}
+ object (unsigned long id): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ comp c_;
+ };
+
+ inline bool
+ operator== (const object& x, const object& y)
+ {
+ return x.id_ == y.id_ && x.c_.str == y.c_.str;
+ }
+}
+
+// Test composite definition inside object.
+//
+#pragma db namespace table("t4_")
+namespace test4
+{
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ unsigned long id () const {return id_;}
+
+ void str (const std::string& s) {c_.str = s;}
+ const std::string& str () const {return c_.str;}
+
+ void x (int i) {p_.first = i;}
+ int x () const {return p_.first;}
+
+ void y (int i) {p_.second = i;}
+ int y () const {return p_.second;}
+
+ private:
+ friend class odb::access;
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db value
+ struct comp
+ {
+ std::string str;
+ };
+
+ comp c_;
+
+ typedef std::pair<int, int> int_pair;
+ #pragma db value(int_pair)
+
+ int_pair p_;
+ };
+
+ inline bool
+ operator== (const object& x, const object& y)
+ {
+ return x.id () == y.id () && x.str () == y.str () &&
+ x.x () == y.x () && x.y () == y.y ();
+ }
+}
+
+// Test composite name clashes in query columns (compilation test)
+//
+#pragma db namespace table("t5_")
+namespace test5
+{
+ // Class-member conflict.
+ //
+ #pragma db value
+ struct value {int value_;};
+
+ // Class-class conflict.
+ //
+ #pragma db value
+ struct inner {int value;};
+
+ #pragma db value
+ struct outer {inner value;};
+
+ #pragma db object
+ struct object
+ {
+ #pragma db id
+ int id;
+
+ outer value;
+ test5::value v;
+ };
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/composite/testscript b/odb-tests/common/composite/testscript
new file mode 100644
index 0000000..0747507
--- /dev/null
+++ b/odb-tests/common/composite/testscript
@@ -0,0 +1,33 @@
+# file : common/composite/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/const-member/buildfile b/odb-tests/common/const-member/buildfile
new file mode 100644
index 0000000..868f7fd
--- /dev/null
+++ b/odb-tests/common/const-member/buildfile
@@ -0,0 +1,40 @@
+# file : common/const-member/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix constm_ \
+ --generate-schema
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/const-member/driver.cxx b/odb-tests/common/const-member/driver.cxx
new file mode 100644
index 0000000..0c71dfa
--- /dev/null
+++ b/odb-tests/common/const-member/driver.cxx
@@ -0,0 +1,119 @@
+// file : common/const-member/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test const data members. The readonly test tests that const
+// members are automatically treated as read-only.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Const ids.
+ //
+ {
+ const_id o (1);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->load<const_id> (1, o);
+ t.commit ();
+ assert (o.id == 1);
+ }
+ }
+
+ {
+ {
+ const_auto_id o;
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ assert (o.id == 1);
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<const_auto_id> o (db->load<const_auto_id> (1));
+ t.commit ();
+ assert (o->id == 1);
+ }
+ }
+
+ // Container.
+ //
+ {
+ container o (1, 1);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<container> o (db->load<container> (1));
+ t.commit ();
+
+ assert (o->ccom.vec.size () == 1 && o->ccom.vec[0] == 1 &&
+ o->ccom.cvec.size () == 1 && o->ccom.cvec[0] == 1 &&
+ o->cvec.size () == 1 && o->cvec[0] == 1);
+ }
+ }
+
+ // Wrapper.
+ //
+ {
+ wrapper o (1, "abc", 1);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<wrapper> o (db->load<wrapper> (1));
+ t.commit ();
+
+ assert (*o->str == "abc" &&
+ o->com->str == "abc" && o->com->num == 1 &&
+ o->com->vec.size () == 1 && o->com->vec[0] == 1 &&
+ o->vec->size () == 1 && (*o->vec)[0] == 1);
+ }
+ }
+
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/const-member/test.hxx b/odb-tests/common/const-member/test.hxx
new file mode 100644
index 0000000..ab75c55
--- /dev/null
+++ b/odb-tests/common/const-member/test.hxx
@@ -0,0 +1,109 @@
+// file : common/const-member/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <vector>
+#include <string>
+#include <memory> // std::auto_ptr
+
+#include <odb/core.hxx>
+
+// Const ids.
+//
+#pragma db object
+struct const_id
+{
+ const_id (unsigned long i): id (i) {}
+ const_id (): id (0) {}
+
+ #pragma db id
+ const unsigned long id;
+};
+
+#pragma db object
+struct const_auto_id
+{
+ const_auto_id (): id (0) {}
+
+ #pragma db id auto
+ const unsigned long id;
+};
+
+// Container.
+//
+#pragma db value
+struct container_value
+{
+ container_value (unsigned long x)
+ {
+ vec.push_back (x);
+ const_cast<std::vector<unsigned long>&> (cvec).push_back (x);
+ }
+
+ container_value () {}
+
+ std::vector<unsigned long> vec;
+ const std::vector<unsigned long> cvec;
+};
+
+#pragma db object
+struct container
+{
+ container (unsigned long i, unsigned long x)
+ : id (i), ccom (x)
+ {
+ const_cast<std::vector<unsigned long>&> (cvec).push_back (x);
+ }
+
+ container () {}
+
+ #pragma db id
+ unsigned long id;
+
+ const container_value ccom;
+ const std::vector<unsigned long> cvec;
+};
+
+// Wrapper.
+//
+#pragma db value
+struct wrapped_value
+{
+ wrapped_value (const std::string& s, unsigned long n)
+ : str (s), num (n)
+ {
+ vec.push_back (n);
+ }
+
+ wrapped_value () {}
+
+ const std::string str;
+ unsigned long num;
+ std::vector<unsigned long> vec;
+};
+
+#pragma db object
+struct wrapper
+{
+ wrapper (unsigned long i, const std::string& s, unsigned long n)
+ : id (i),
+ str (new std::string (s)),
+ com (new wrapped_value (s, n)),
+ vec (new std::vector<unsigned long>)
+ {
+ const_cast<std::vector<unsigned long>&> (*vec).push_back (n);
+ }
+
+ wrapper () {}
+
+ #pragma db id
+ unsigned long id;
+
+ const std::unique_ptr<const std::string> str;
+ const std::unique_ptr<const wrapped_value> com;
+ const std::unique_ptr<const std::vector<unsigned long>> vec;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/const-member/testscript b/odb-tests/common/const-member/testscript
new file mode 100644
index 0000000..c81d856
--- /dev/null
+++ b/odb-tests/common/const-member/testscript
@@ -0,0 +1,33 @@
+# file : common/const-member/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/const-object/buildfile b/odb-tests/common/const-object/buildfile
new file mode 100644
index 0000000..853c831
--- /dev/null
+++ b/odb-tests/common/const-object/buildfile
@@ -0,0 +1,41 @@
+# file : common/const-object/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix consto_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/const-object/driver.cxx b/odb-tests/common/const-object/driver.cxx
new file mode 100644
index 0000000..7ef48ee
--- /dev/null
+++ b/odb-tests/common/const-object/driver.cxx
@@ -0,0 +1,216 @@
+// file : common/const-object/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test database operations with const objects.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/session.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ aggr a (1);
+ aggr ca_ (2); // o1 and o2 are NULL
+ const aggr& ca (ca_);
+
+ obj1* o1 (new obj1 (1));
+ obj1* co1_ (new obj1 (2));
+ const obj1* co1 (co1_);
+ a.o1 = co1;
+
+ unique_ptr<obj2> o2 (new obj2 (1));
+ obj2* co2_ (new obj2 (2));
+ a.o2.reset (co2_);
+ unique_ptr<const obj2>& co2 (a.o2);
+
+ // persist via references
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (*o1);
+ db->persist (*co1);
+ db->persist (*o2);
+ db->persist (*co2);
+ db->persist (a);
+ db->persist (ca);
+ t.commit ();
+ }
+
+ // persist via pointers
+ //
+ o1->id += 2;
+ co1_->id += 2;
+ o2->id += 2;
+ co2_->id += 2;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (co1);
+ db->persist (o2);
+ db->persist (co2);
+ t.commit ();
+ }
+
+ // load & compare
+ //
+ {
+ transaction t (db->begin ());
+
+ unique_ptr<aggr> a (db->load<aggr> (1));
+ unique_ptr<const aggr> ca (db->load<aggr> (2));
+
+ t.commit ();
+
+ assert (a->o1->id == 2);
+ assert (a->o2->id == 2);
+
+ assert (ca->o1 == 0);
+ assert (ca->o2.get () == 0);
+ }
+
+ // update via references
+ //
+ {
+ transaction t (db->begin ());
+ db->update (*o1);
+ db->update (*co1);
+ db->update (*o2);
+ db->update (*co2);
+ db->update (a);
+ db->update (ca);
+ t.commit ();
+ }
+
+ // update via pointers
+ //
+ {
+ transaction t (db->begin ());
+ db->update (o1);
+ db->update (co1);
+ db->update (o2);
+ db->update (co2);
+ t.commit ();
+ }
+
+ // query
+ //
+ typedef odb::query<obj1> query1;
+ typedef odb::query<obj2> query2;
+
+ typedef odb::result<const obj1> result1;
+ typedef odb::result<const obj2> result2;
+
+ {
+ transaction t (db->begin ());
+ result1 r1 (db->query<obj1> (query1::id < 3));
+ // odb::result<obj1> ur (r1); // error
+ size_t n1 (0);
+
+ for (result1::iterator i (r1.begin ()); i != r1.end (); ++i)
+ {
+ // i->f (); // error
+ i->cf ();
+ // obj1* p (i.load ()); // error
+ const obj1* p (i.load ());
+ obj1 o (0);
+ i.load (o);
+ assert (p->id == o.id);
+ delete p;
+ n1++;
+ }
+
+ assert (n1 == 2);
+
+ result2 r2 (db->query<obj2> (query2::id < 3));
+ size_t n2 (0);
+
+ for (result2::iterator i (r2.begin ()); i != r2.end (); ++i)
+ {
+ // i->f (); // error
+ i->cf ();
+ //unique_ptr<obj2> p (i.load ()); // error
+ unique_ptr<const obj2> p (i.load ());
+ obj2 o (0);
+ i.load (o);
+ assert (p->id == o.id);
+ n2++;
+ }
+
+ assert (n2 == 2);
+
+ t.commit ();
+ }
+
+ // erase via references
+ //
+ {
+ transaction t (db->begin ());
+ db->erase (*o1);
+ db->erase (*co1);
+ db->erase (*o2);
+ db->erase (*co2);
+ db->erase (a);
+ db->erase (ca);
+ t.commit ();
+ }
+
+ // erase via pointers
+ //
+ o1->id -= 2;
+ co1_->id -= 2;
+ o2->id -= 2;
+ co2_->id -= 2;
+
+ {
+ transaction t (db->begin ());
+ db->erase (o1);
+ db->erase (co1);
+ db->erase (o2);
+ db->erase (co2);
+ t.commit ();
+ }
+
+ // Test session and const/non-const object handling
+ //
+ {
+ session s;
+ transaction t (db->begin ());
+
+ obj1 o1 (1);
+ const obj1& co1 (o1);
+ db->persist (co1);
+
+ assert (db->load<obj1> (1) == &o1);
+
+ t.commit ();
+ }
+
+ delete o1;
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/const-object/test.hxx b/odb-tests/common/const-object/test.hxx
new file mode 100644
index 0000000..4e66231
--- /dev/null
+++ b/odb-tests/common/const-object/test.hxx
@@ -0,0 +1,51 @@
+// file : common/const-object/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <memory>
+#include <odb/core.hxx>
+
+#pragma db object pointer (obj1*) session
+struct obj1
+{
+ obj1 () {}
+ obj1 (int i): id (i) {}
+
+ #pragma db id
+ int id;
+
+ void f () {}
+ void cf () const {}
+};
+
+#pragma db object pointer (std::unique_ptr<obj2>)
+struct obj2
+{
+ obj2 () {}
+ obj2 (int i): id (i) {}
+
+ #pragma db id
+ int id;
+
+ void f () {}
+ void cf () const {}
+};
+
+#pragma db object
+struct aggr
+{
+ aggr (int i): id (i), o1 (0) {}
+ aggr (): o1 (0) {}
+ ~aggr () {delete o1;}
+
+ #pragma db id
+ int id;
+
+ const obj1* o1;
+
+ std::unique_ptr<const obj2> o2;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/const-object/testscript b/odb-tests/common/const-object/testscript
new file mode 100644
index 0000000..3885e96
--- /dev/null
+++ b/odb-tests/common/const-object/testscript
@@ -0,0 +1,33 @@
+# file : common/const-object/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/container/basics/buildfile b/odb-tests/common/container/basics/buildfile
new file mode 100644
index 0000000..f83444e
--- /dev/null
+++ b/odb-tests/common/container/basics/buildfile
@@ -0,0 +1,40 @@
+# file : common/container/basics/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix t_cont_bs_ \
+ --generate-schema
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/container/basics/driver.cxx b/odb-tests/common/container/basics/driver.cxx
new file mode 100644
index 0000000..14e1984
--- /dev/null
+++ b/odb-tests/common/container/basics/driver.cxx
@@ -0,0 +1,523 @@
+// file : common/container/basics/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test basic container persistence.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ for (unsigned short i (0); i < 2; ++i)
+ {
+ object empty ("empty"), med ("medium"), full ("full");
+
+ //
+ // empty
+ //
+
+ empty.num = 0;
+ empty.str = "";
+
+ // array
+ //
+ empty.na[0] = 123;
+ empty.na[1] = 234;
+ empty.na[2] = 345;
+
+ empty.sa[0] = "aaa";
+ empty.sa[1] = "bbbb";
+ empty.sa[2] = "ccccc";
+
+ empty.ca[0] = comp (123, "aaa");
+ empty.ca[1] = comp (234, "bbbb");
+ empty.ca[2] = comp (345, "ccccc");
+
+
+ //
+ // med
+ //
+
+ med.num = 999;
+ med.str = "xxx";
+
+ // vector
+ //
+ med.nv.push_back (123);
+ med.nv.push_back (234);
+
+ med.sv.push_back ("aaa");
+ med.sv.push_back ("bbbb");
+
+ med.cv.push_back (comp (123, "aaa"));
+ med.cv.push_back (comp (234, "bbbb"));
+
+ med.uv.push_back (123);
+ med.uv.push_back (234);
+
+ // list
+ //
+ med.sl.push_back ("aaa");
+ med.sl.push_back ("bbbb");
+
+ // deque
+ //
+ med.nd.push_back (123);
+ med.nd.push_back (234);
+
+ // set
+ //
+ med.ns.insert (123);
+ med.ns.insert (234);
+
+ med.ss.insert ("aaa");
+ med.ss.insert ("bbbb");
+
+ med.cs.insert (comp (123, "aaa"));
+ med.cs.insert (comp (234, "bbbb"));
+
+ // map
+ //
+ med.nsm[123] = "aaa";
+ med.nsm[234] = "bbbb";
+
+ med.snm["aaa"] = 123;
+ med.snm["bbbb"] = 234;
+
+ med.ncm[123] = comp (123, "aaa");
+ med.ncm[234] = comp (234, "bbbb");
+
+ med.csm[comp (123, "aaa")] = "aaa";
+ med.csm[comp (234, "bbbb")] = "bbbb";
+
+ // array
+ //
+ med.na[0] = 123;
+ med.na[1] = 234;
+ med.na[2] = 345;
+
+ med.sa[0] = "aaa";
+ med.sa[1] = "bbbb";
+ med.sa[2] = "ccccc";
+
+ med.ca[0] = comp (123, "aaa");
+ med.ca[1] = comp (234, "bbbb");
+ med.ca[2] = comp (345, "ccccc");
+
+ // forward_list
+ //
+ med.nfl.push_front (234);
+ med.nfl.push_front (123);
+
+ med.sfl.push_front ("bbbb");
+ med.sfl.push_front ("aaa");
+
+ med.cfl.push_front (comp (234, "bbbb"));
+ med.cfl.push_front (comp (123, "aaa"));
+
+ // unordered_set
+ //
+ med.nus.insert (123);
+ med.nus.insert (234);
+
+ med.sus.insert ("aaa");
+ med.sus.insert ("bbbb");
+
+ med.cus.insert (comp (123, "aaa"));
+ med.cus.insert (comp (234, "bbbb"));
+
+ // unordered_map
+ //
+ med.nsum[123] = "aaa";
+ med.nsum[234] = "bbbb";
+
+ med.snum["aaa"] = 123;
+ med.snum["bbbb"] = 234;
+
+ med.ncum[123] = comp (123, "aaa");
+ med.ncum[234] = comp (234, "bbbb");
+
+ med.csum[comp (123, "aaa")] = "aaa";
+ med.csum[comp (234, "bbbb")] = "bbbb";
+
+ //
+ // full
+ //
+
+ full.num = 9999;
+ full.str = "xxxx";
+
+ // vector
+ //
+ full.nv.push_back (1234);
+ full.nv.push_back (2345);
+ full.nv.push_back (3456);
+
+ full.sv.push_back ("aaaa");
+ full.sv.push_back ("bbbbb");
+ full.sv.push_back ("cccccc");
+
+ full.cv.push_back (comp (1234, "aaaa"));
+ full.cv.push_back (comp (2345, "bbbbb"));
+ full.cv.push_back (comp (3456, "cccccc"));
+
+ full.uv.push_back (1234);
+ full.uv.push_back (2345);
+ full.uv.push_back (3456);
+
+ // list
+ //
+ full.sl.push_back ("aaaa");
+ full.sl.push_back ("bbbbb");
+ full.sl.push_back ("cccccc");
+
+ // deque
+ //
+ full.nd.push_back (1234);
+ full.nd.push_back (2345);
+ full.nd.push_back (3456);
+
+ // set
+ //
+ full.ns.insert (1234);
+ full.ns.insert (2345);
+ full.ns.insert (3456);
+
+ full.ss.insert ("aaaa");
+ full.ss.insert ("bbbbb");
+ full.ss.insert ("cccccc");
+
+ full.cs.insert (comp (1234, "aaaa"));
+ full.cs.insert (comp (2345, "bbbbb"));
+ full.cs.insert (comp (3456, "cccccc"));
+
+ // map
+ //
+ full.nsm[1234] = "aaaa";
+ full.nsm[2345] = "bbbbb";
+ full.nsm[3456] = "cccccc";
+
+ full.snm["aaaa"] = 1234;
+ full.snm["bbbbb"] = 2345;
+ full.snm["cccccc"] = 3456;
+
+ full.ncm[1234] = comp (1234, "aaaa");
+ full.ncm[2345] = comp (2345, "bbbbb");
+ full.ncm[3456] = comp (3456, "cccccc");
+
+ full.csm[comp (1234, "aaaa")] = "aaaa";
+ full.csm[comp (2345, "bbbbb")] = "bbbbb";
+ full.csm[comp (3456, "cccccc")] = "cccccc";
+
+ // array
+ //
+ full.na[0] = 123;
+ full.na[1] = 234;
+ full.na[2] = 345;
+
+ full.sa[0] = "aaa";
+ full.sa[1] = "bbbb";
+ full.sa[2] = "ccccc";
+
+ full.ca[0] = comp (123, "aaa");
+ full.ca[1] = comp (234, "bbbb");
+ full.ca[2] = comp (345, "ccccc");
+
+ // forward_list
+ //
+ full.nfl.push_front (345);
+ full.nfl.push_front (234);
+ full.nfl.push_front (123);
+
+ full.sfl.push_front ("ccccc");
+ full.sfl.push_front ("bbbb");
+ full.sfl.push_front ("aaa");
+
+ full.cfl.push_front (comp (345, "ccccc"));
+ full.cfl.push_front (comp (234, "bbbb"));
+ full.cfl.push_front (comp (123, "aaa"));
+
+ // unordered_set
+ //
+ full.nus.insert (1234);
+ full.nus.insert (2345);
+ full.nus.insert (3456);
+
+ full.sus.insert ("aaaa");
+ full.sus.insert ("bbbbb");
+ full.sus.insert ("cccccc");
+
+ full.cus.insert (comp (1234, "aaaa"));
+ full.cus.insert (comp (2345, "bbbbb"));
+ full.cus.insert (comp (3456, "cccccc"));
+
+ // unordered_map
+ //
+ full.nsum[1234] = "aaaa";
+ full.nsum[2345] = "bbbbb";
+ full.nsum[3456] = "cccccc";
+
+ full.snum["aaaa"] = 1234;
+ full.snum["bbbbb"] = 2345;
+ full.snum["cccccc"] = 3456;
+
+ full.ncum[1234] = comp (1234, "aaaa");
+ full.ncum[2345] = comp (2345, "bbbbb");
+ full.ncum[3456] = comp (3456, "cccccc");
+
+ full.csum[comp (1234, "aaaa")] = "aaaa";
+ full.csum[comp (2345, "bbbbb")] = "bbbbb";
+ full.csum[comp (3456, "cccccc")] = "cccccc";
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (empty);
+ db->persist (med);
+ db->persist (full);
+ t.commit ();
+ }
+
+ // load & check
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> e (db->load<object> ("empty"));
+ unique_ptr<object> m (db->load<object> ("medium"));
+ unique_ptr<object> f (db->load<object> ("full"));
+ t.commit ();
+
+ assert (empty == *e);
+ assert (med == *m);
+ assert (full == *f);
+ }
+
+ //
+ // empty
+ //
+
+ empty.num = 99;
+ empty.str = "xx";
+
+ empty.nv.push_back (12);
+ empty.sv.push_back ("aa");
+ empty.cv.push_back (comp (12, "aa"));
+ empty.uv.push_back (12);
+ empty.sl.push_back ("aa");
+ empty.nd.push_back (12);
+
+ empty.ns.insert (12);
+ empty.ss.insert ("aa");
+ empty.cs.insert (comp (12, "aa"));
+
+ empty.nsm[12] = "aa";
+ empty.snm["aa"] = 12;
+ empty.ncm[12] = comp (12, "aa");
+ empty.csm[comp (12, "aa")] = "aa";
+
+ empty.nfl.push_front (12);
+ empty.sfl.push_front ("aa");
+ empty.cfl.push_front (comp (12, "aa"));
+
+ empty.nus.insert (12);
+ empty.sus.insert ("aa");
+ empty.cus.insert (comp (12, "aa"));
+
+ empty.nsum[12] = "aa";
+ empty.snum["aa"] = 12;
+ empty.ncum[12] = comp (12, "aa");
+ empty.csum[comp (12, "aa")] = "aa";
+
+ //
+ // med
+ //
+
+ med.num = 0;
+ med.str = "";
+
+ med.nv.clear ();
+ med.sv.clear ();
+ med.cv.clear ();
+ med.uv.clear ();
+
+ med.sl.clear ();
+
+ med.nd.clear ();
+
+ med.ns.clear ();
+ med.ss.clear ();
+ med.cs.clear ();
+
+ med.nsm.clear ();
+ med.snm.clear ();
+ med.ncm.clear ();
+ med.csm.clear ();
+
+ med.nfl.clear ();
+ med.sfl.clear ();
+ med.cfl.clear ();
+
+ med.nus.clear ();
+ med.sus.clear ();
+ med.cus.clear ();
+
+ med.nsum.clear ();
+ med.snum.clear ();
+ med.ncum.clear ();
+ med.csum.clear ();
+
+ //
+ // full
+ //
+
+ full.num++;
+ full.str += "x";
+
+ // vector
+ //
+ full.nv.back ()++;
+ full.nv.push_back (4567);
+
+ full.sv.back () += "c";
+ full.sv.push_back ("ddddddd");
+
+ full.cv.back ().num++;
+ full.cv.back ().str += "c";
+ full.cv.push_back (comp (4567, "ddddddd"));
+
+ full.uv.back ()++;
+ full.uv.push_back (4567);
+
+ // list
+ //
+ full.sl.back () += "c";
+ full.sl.push_back ("ddddddd");
+
+ // deque
+ //
+ full.nd.push_front (456);
+
+ // set
+ //
+ full.ns.insert (4567);
+ full.ss.insert ("ddddddd");
+ full.cs.insert (comp (4567, "ddddddd"));
+
+ // map
+ //
+ full.nsm[3456] += 'c';
+ full.nsm[4567] = "ddddddd";
+
+ full.snm["cccccc"]++;
+ full.snm["ddddddd"] = 4567;
+
+ full.ncm[3456].num++;
+ full.ncm[3456].str += 'c';
+ full.ncm[4567] = comp (4567, "ddddddd");
+
+ full.csm[comp (3456, "cccccc")] += "c";
+ full.csm[comp (4567, "ddddddd")] = "ddddddd";
+
+ // array
+ //
+ full.na[0]++;
+ full.sa[0] += 'a';
+ full.ca[0].num++;
+ full.ca[0].str += 'a';
+
+ // forward_list
+ //
+ full.nfl.front ()++;
+ full.nfl.push_front (4567);
+
+ full.sfl.front () += 'a';
+ full.sfl.push_front ("ddddddd");
+
+ full.cfl.front ().num++;
+ full.cfl.front ().str += 'a';
+ full.cfl.push_front (comp (4567, "ddddddd"));
+
+ // unordered_set
+ //
+ full.nus.insert (4567);
+ full.sus.insert ("ddddddd1"); // 1 is to preserve order in VC++ 10.
+ full.cus.insert (comp (4567, "ddddddd1"));
+
+ // unordered_map
+ //
+ full.nsum[3456] += 'c';
+ full.nsum[4567] = "ddddddd";
+
+ full.snum["cccccc"]++;
+ full.snum["ddddddd1"] = 4567;
+
+ full.ncum[3456].num++;
+ full.ncum[3456].str += 'c';
+ full.ncum[4567] = comp (4567, "ddddddd");
+
+ full.csum[comp (3456, "cccccc")] += "c";
+ full.csum[comp (4567, "ddddddd1")] = "ddddddd";
+
+ // update
+ //
+ {
+ transaction t (db->begin ());
+ db->update (empty);
+ db->update (med);
+ db->update (full);
+ t.commit ();
+ }
+
+ // load & check
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> e (db->load<object> ("empty"));
+ unique_ptr<object> m (db->load<object> ("medium"));
+ unique_ptr<object> f (db->load<object> ("full"));
+ t.commit ();
+
+ assert (empty == *e);
+ assert (med == *m);
+ assert (full == *f);
+ }
+
+ // erase
+ //
+ if (i == 0)
+ {
+ transaction t (db->begin ());
+ db->erase<object> ("empty");
+ db->erase<object> ("medium");
+ db->erase<object> ("full");
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/container/basics/test.hxx b/odb-tests/common/container/basics/test.hxx
new file mode 100644
index 0000000..e8e329e
--- /dev/null
+++ b/odb-tests/common/container/basics/test.hxx
@@ -0,0 +1,245 @@
+// file : common/container/basics/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <map>
+#include <set>
+#include <list>
+#include <vector>
+#include <deque>
+#include <array>
+#include <string>
+#include <forward_list>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <odb/core.hxx>
+
+#pragma db value
+struct comp
+{
+ comp () {}
+ comp (int n, const std::string& s) : num (n), str (s) {}
+
+ #pragma db column("number")
+ int num = 0;
+ std::string str;
+};
+
+inline bool
+operator== (const comp& x, const comp& y)
+{
+ return x.num == y.num && x.str == y.str;
+}
+
+inline bool
+operator!= (const comp& x, const comp& y)
+{
+ return !(x == y);
+}
+
+inline bool
+operator< (const comp& x, const comp& y)
+{
+ return x.num != y.num ? x.num < y.num : x.str < y.str;
+}
+
+typedef std::list<std::string> str_list;
+typedef std::deque<int> num_deque;
+
+typedef std::vector<int> num_vector;
+typedef std::vector<std::string> str_vector;
+
+typedef std::set<int> num_set;
+typedef std::set<std::string> str_set;
+typedef std::set<comp> comp_set;
+
+typedef std::map<int, std::string> num_str_map;
+typedef std::map<std::string, int> str_num_map;
+typedef std::map<int, comp> num_comp_map;
+typedef std::map<comp, std::string> comp_str_map;
+
+struct comp_hash
+{
+ std::size_t
+ operator() (const comp& x) const {return nh (x.num) + sh (x.str);}
+
+ std::hash<int> nh;
+ std::hash<std::string> sh;
+};
+
+typedef std::array<int, 3> num_array;
+typedef std::array<std::string, 3> str_array;
+typedef std::array<comp, 3> comp_array;
+
+typedef std::forward_list<int> num_flist;
+typedef std::forward_list<std::string> str_flist;
+typedef std::forward_list<comp> comp_flist;
+
+typedef std::unordered_set<int> num_uset;
+typedef std::unordered_set<std::string> str_uset;
+typedef std::unordered_set<comp, comp_hash> comp_uset;
+
+typedef std::unordered_map<int, std::string> num_str_umap;
+typedef std::unordered_map<std::string, int> str_num_umap;
+typedef std::unordered_map<int, comp> num_comp_umap;
+typedef std::unordered_map<comp, std::string, comp_hash> comp_str_umap;
+
+#pragma db value
+struct cont_comp1
+{
+ // This composite value does not have any columns.
+ //
+ num_vector sv; // Have the name "conflic" with the one in the object.
+};
+
+#pragma db value
+struct cont_comp2
+{
+ cont_comp2 (): num (777), str ("ggg") {}
+
+ int num;
+ str_list sl;
+ std::string str;
+};
+
+#pragma db object
+struct object
+{
+ object (): nv (comp1_.sv), sl (comp2_.sl) {}
+ object (const std::string& id) : id_ (id), nv (comp1_.sv), sl (comp2_.sl) {}
+
+ #pragma db id
+ std::string id_;
+
+ int num;
+
+ cont_comp1 comp1_;
+ cont_comp2 comp2_;
+
+ // vector
+ //
+ #pragma db transient
+ num_vector& nv;
+
+ #pragma db table("object_strings") id_column ("obj_id")
+ str_vector sv;
+
+ #pragma db value_column("")
+ std::vector<comp> cv;
+
+ #pragma db unordered
+ num_vector uv;
+
+ // list
+ //
+ #pragma db transient
+ str_list& sl;
+
+ // deque
+ //
+ num_deque nd;
+
+ // set
+ //
+ num_set ns;
+ str_set ss;
+ comp_set cs;
+
+ // map
+ //
+ num_str_map nsm;
+ str_num_map snm;
+ num_comp_map ncm;
+ comp_str_map csm;
+
+ // array
+ //
+ num_array na;
+ str_array sa;
+ comp_array ca;
+
+ // forward_list
+ //
+ num_flist nfl;
+ str_flist sfl;
+ comp_flist cfl;
+
+ // unordered_set
+ //
+ num_uset nus;
+ str_uset sus;
+ comp_uset cus;
+
+ // unordered_map
+ //
+ num_str_umap nsum;
+ str_num_umap snum;
+ num_comp_umap ncum;
+ comp_str_umap csum;
+
+ std::string str;
+};
+
+inline bool
+operator== (const object& x, const object& y)
+{
+ if (x.uv.size () != y.uv.size ())
+ return false;
+
+ int xs (0), ys (0);
+
+ for (num_vector::size_type i (0); i < x.uv.size (); ++i)
+ {
+ xs += x.uv[i];
+ ys += y.uv[i];
+ }
+
+ return
+ x.id_ == y.id_ &&
+ x.num == y.num &&
+
+ x.comp2_.num == y.comp2_.num &&
+ x.comp2_.str == y.comp2_.str &&
+
+ x.nv == y.nv &&
+ x.sv == y.sv &&
+ x.cv == y.cv &&
+ xs == ys &&
+
+ x.sl == y.sl &&
+
+ x.nd == y.nd &&
+
+ x.ns == y.ns &&
+ x.ss == y.ss &&
+ x.cs == y.cs &&
+
+ x.nsm == y.nsm &&
+ x.snm == y.snm &&
+ x.ncm == y.ncm &&
+ x.csm == y.csm &&
+
+ x.na == y.na &&
+ x.sa == y.sa &&
+ x.ca == y.ca &&
+
+ x.nfl == y.nfl &&
+ x.sfl == y.sfl &&
+ x.cfl == y.cfl &&
+
+ x.nus == y.nus &&
+ x.sus == y.sus &&
+ x.cus == y.cus &&
+
+ x.nsum == y.nsum &&
+ x.snum == y.snum &&
+ x.ncum == y.ncum &&
+ x.csum == y.csum &&
+
+ x.str == y.str;
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/container/basics/testscript b/odb-tests/common/container/basics/testscript
new file mode 100644
index 0000000..ea99498
--- /dev/null
+++ b/odb-tests/common/container/basics/testscript
@@ -0,0 +1,33 @@
+# file : common/container/basics/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/container/change-tracking/buildfile b/odb-tests/common/container/change-tracking/buildfile
new file mode 100644
index 0000000..1dda818
--- /dev/null
+++ b/odb-tests/common/container/change-tracking/buildfile
@@ -0,0 +1,40 @@
+# file : common/container/change-tracking/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix t_cont_changet_ \
+ --generate-schema
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/container/change-tracking/driver.cxx b/odb-tests/common/container/change-tracking/driver.cxx
new file mode 100644
index 0000000..4894ed9
--- /dev/null
+++ b/odb-tests/common/container/change-tracking/driver.cxx
@@ -0,0 +1,729 @@
+// file : common/container/change-tracking/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test change-tracking containers.
+//
+
+#include <memory> // std::unique_ptr
+#include <utility> // std::move
+#include <iostream>
+
+#include <odb/tracer.hxx>
+#include <odb/session.hxx>
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+struct counting_tracer: odb::tracer
+{
+ void
+ reset (transaction& tr) {u = i = d = s = t = 0; tr.tracer (*this);}
+
+ virtual void
+ execute (odb::connection&, const char* stmt)
+ {
+ string p (stmt, 6);
+ if (p == "UPDATE")
+ u++;
+ else if (p == "INSERT")
+ i++;
+ else if (p == "DELETE")
+ d++;
+ else if (p == "SELECT")
+ s++;
+ t++;
+ }
+
+ size_t u, i, d, s, t;
+};
+
+static counting_tracer tr;
+
+// Compilation test: instantiate all the functions. In C++11 mode only
+// do this if we have a fairly conforming compiler that implements the
+// complete std::vector interface.
+//
+
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+#if defined (__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 7
+struct item {};
+template class odb::vector<item>;
+template class odb::vector_iterator<odb::vector<item>,
+ std::vector<item>::iterator>;
+template class odb::vector_iterator<odb::vector<item>,
+ std::vector<item>::reverse_iterator>;
+#endif
+#endif
+
+void
+f (const std::vector<int>&) {}
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ // Test extended interface.
+ //
+ {
+ typedef odb::vector<int> vector;
+
+ vector ov;
+ std::vector<int> sv;
+ f (ov); // Implicit conversion to std::vector.
+ vector ov1 (sv); // Initialization from std::vector.
+ ov = sv; // Assignement from std::vector.
+
+ // Container comparison.
+ //
+ if (ov != ov1 ||
+ ov != sv ||
+ sv != ov1)
+ ov.clear ();
+
+ // Iterator comparison/conversion.
+ //
+ vector::const_iterator i (ov.begin ());
+ if (i != ov.end ())
+ i = ov.end ();
+
+ // Things are just really borken in Sun CC, no matter which STL
+ // you use.
+ //
+#ifndef __SUNPRO_CC
+ vector::const_reverse_iterator j (ov.rbegin ());
+ if (j != ov.rend ())
+ j = ov.rend ();
+#endif
+ }
+
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Test traits logic.
+ //
+ {
+ object o ("1");
+ o.i = 123;
+ o.s.push_back ("a");
+
+ assert (!o.s._tracking ());
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ assert (o.s._tracking ());
+
+ // load
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> ("1"));
+ assert (p->s._tracking ());
+ t.commit ();
+ }
+
+ // update
+ //
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ assert (o.s._tracking ());
+
+ // erase
+ //
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+
+ assert (!o.s._tracking ());
+ }
+
+ // Test change tracking.
+ //
+ object o ("1");
+ o.i = 123;
+ o.s.push_back ("a");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // push_back/pop_back
+ //
+ {
+ o.s.push_back ("b"); // insert
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 1 && tr.i == 1 && tr.t == 2);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.pop_back ();
+ o.s.push_back ("c"); // update
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 2 && tr.t == 2);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.pop_back ();
+ for (int i (0); i != 1024; ++i)
+ o.s.push_back ("x"); // realloc
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 2 && tr.i == 1023 && tr.t == 1025);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ for (int i (0); i != 1024; ++i)
+ o.s.pop_back (); // delete
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 1 && tr.d == 1 && tr.t == 2);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.push_back ("b");
+ o.s.pop_back (); // no-op
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 1 && tr.t == 1);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ // insert
+ //
+ {
+ o.s.clear ();
+ o.s.push_back ("a");
+ o.s.push_back ("b");
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.insert (o.s.begin (), "a1"); // insert front
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 3 && tr.i == 1 && tr.t == 4);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.insert (o.s.begin () + 1, "a2"); // insert middle
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 3 && tr.i == 1 && tr.t == 4);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.insert (o.s.end (), "b1"); // insert back
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 1 && tr.i == 1 && tr.t == 2);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ // erase
+ //
+ {
+ o.s.clear ();
+ o.s.push_back ("a");
+ o.s.push_back ("b");
+ o.s.push_back ("c");
+ o.s.push_back ("d");
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.erase (o.s.begin ()); // erase front
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 4 && tr.d == 1 && tr.t == 5);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.erase (o.s.begin () + 1); // erase middle
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 2 && tr.d == 1 && tr.t == 3);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.erase (o.s.end () - 1); // erase back
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 1 && tr.d == 1 && tr.t == 2);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ // modify
+ //
+ {
+ o.s.clear ();
+ o.s.push_back ("a");
+ o.s.push_back ("b");
+ o.s.push_back ("c");
+ o.s.push_back ("d");
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.modify (1) += 'b';
+ o.s.modify_at (2) += 'c';
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 3 && tr.t == 3);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.modify_front () += 'a';
+ o.s.modify_back () += 'd';
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 3 && tr.t == 3);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.begin ().modify () += 'a';
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+ o.s.rbegin ().modify () += 'c';
+#endif
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+ assert (tr.u == 3 && tr.t == 3);
+#else
+ assert (tr.u == 2 && tr.t == 2);
+#endif
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+ {
+ (o.s.rbegin () + 1).modify (1) += 'a';
+ (o.s.rbegin () + 1).modify (-1) += 'c';
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 3 && tr.t == 3);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+#endif
+
+ {
+ o.s.mbegin ();
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 5 && tr.t == 5);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ // clear
+ //
+ {
+ o.s.clear ();
+ o.s.push_back ("a");
+ o.s.push_back ("b");
+ o.s.push_back ("c");
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.clear ();
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 1 && tr.d == 1 && tr.t == 2);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ // assign
+ //
+ {
+ o.s.clear ();
+ o.s.push_back ("a");
+ o.s.push_back ("b");
+ o.s.push_back ("c");
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.assign (4, "x");
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 4 && tr.i == 1 && tr.t == 5);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ // resize
+ //
+ {
+ o.s.clear ();
+ o.s.push_back ("a");
+ o.s.push_back ("b");
+ o.s.push_back ("c");
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.pop_back ();
+ o.s.resize (4, "x"); // expand
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 2 && tr.i == 1 && tr.t == 3);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.push_back ("y");
+ o.s.resize (3); // shrink
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 1 && tr.d == 1 && tr.t == 2);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ // Transaction rollback.
+ //
+ {
+ o.s.clear ();
+ o.s.push_back ("a");
+ o.s.push_back ("b");
+ o.s.push_back ("c");
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ {
+ o.s.push_back ("d");
+
+ transaction t (db->begin ());
+ db->update (o);
+ t.rollback ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 1 && tr.i == 4 && tr.d == 1 && tr.t == 6);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 1 && tr.t == 1);
+ t.commit ();
+ }
+ }
+
+ // Armed copy.
+ //
+ {
+ unique_ptr<object> c;
+
+ {
+ o.s.pop_back ();
+
+ transaction t (db->begin ());
+ db->update (o);
+ c.reset (new object (o));
+ t.rollback ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (c);
+ assert (tr.u == 1 && tr.i == 3 && tr.d == 1 && tr.t == 5);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (c);
+ assert (tr.u == 1 && tr.t == 1);
+ t.commit ();
+ }
+ }
+
+ // Armed swap.
+ //
+ {
+ object c (o);
+
+ {
+ o.s.push_back ("d");
+
+ transaction t (db->begin ());
+ db->update (o);
+ assert (o.s._tracking () && !c.s._tracking ());
+ c.s.swap (o.s);
+ assert (!o.s._tracking () && c.s._tracking ());
+ t.rollback ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (c);
+ assert (tr.u == 1 && tr.i == 4 && tr.d == 1 && tr.t == 6);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (c);
+ assert (tr.u == 1 && tr.t == 1);
+ t.commit ();
+ }
+ }
+
+ // Armed move.
+ //
+ {
+ unique_ptr<object> c;
+
+ {
+ o.s.pop_back ();
+
+ transaction t (db->begin ());
+ db->update (o);
+ assert (o.s._tracking ());
+ c.reset (new object (std::move (o)));
+ assert (!o.s._tracking () && c->s._tracking ());
+ t.rollback ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (c);
+ assert (tr.u == 1 && tr.i == 2 && tr.d == 1 && tr.t == 4);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (c);
+ assert (tr.u == 1 && tr.t == 1);
+ t.commit ();
+ }
+ }
+
+ // Test mixing "smart" and "dumb" container (specifically, erase(obj)).
+ //
+ {
+ mix_object o (1);
+ o.ov.assign (3, 123);
+ o.sv.assign (3, 123);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+ }
+
+ // Test using change tracking container as inverse member.
+ //
+ {
+ inv_object1 o1;
+ inv_object2 o2;
+ o1.o2 = &o2;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o2);
+ db->persist (o1);
+ t.commit ();
+ }
+
+ assert (!o2.o1._tracking ());
+
+ {
+ session s;
+ transaction t (db->begin ());
+ unique_ptr<inv_object1> p1 (db->load<inv_object1> (o1.id_));
+ unique_ptr<inv_object2> p2 (db->load<inv_object2> (o2.id_));
+ assert (p2->o1[0] == p1.get ());
+ assert (!p2->o1._tracking ());
+ t.commit ();
+ }
+ }
+
+ // Test read-only values.
+ {
+ ro_object o (1);
+ o.v.push_back (ro_value (1, 1));
+ o.v.push_back (ro_value (2, 2));
+ o.v.push_back (ro_value (3, 3));
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ o.v.erase (o.v.begin ());
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ assert (db->load<ro_object> (1)->v == o.v);
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/container/change-tracking/test.hxx b/odb-tests/common/container/change-tracking/test.hxx
new file mode 100644
index 0000000..8e06f4a
--- /dev/null
+++ b/odb-tests/common/container/change-tracking/test.hxx
@@ -0,0 +1,106 @@
+// file : common/container/change-tracking/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <memory>
+#include <vector>
+#include <utility> // std::move
+
+#include <odb/core.hxx>
+#include <odb/vector.hxx>
+
+#pragma db object pointer(std::unique_ptr)
+struct object
+{
+ object () {}
+ object (const std::string& id): id_ (id) {}
+
+ object (const object& x): id_ (x.id_), i (x.i), s (x.s) {}
+ object (object&& x): id_ (std::move (x.id_)), i (x.i), s (std::move (x.s)) {}
+
+ #pragma db id
+ std::string id_;
+
+ unsigned int i;
+
+ odb::vector<std::string> s;
+
+ inline bool
+ operator== (const object& o) const {return id_ == o.id_ && i == o.i && s == o.s;}
+};
+
+// Test mixing "smart" and "dumb" container (specifically, erase(obj)).
+//
+#pragma db object
+struct mix_object
+{
+ mix_object () {}
+ mix_object (unsigned long id): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ odb::vector<int> ov;
+ std::vector<int> sv;
+};
+
+// Test using change tracking container as inverse member.
+//
+struct inv_object2;
+
+#pragma db object session
+struct inv_object1
+{
+ #pragma db id auto
+ unsigned long id_;
+
+ inv_object2* o2;
+};
+
+#pragma db object session
+struct inv_object2
+{
+ #pragma db id auto
+ unsigned long id_;
+
+ #pragma db inverse(o2)
+ odb::vector<inv_object1*> o1;
+};
+
+// Test read-only values (we still need to include them in the UPDATE
+// statement).
+//
+#pragma db value
+struct ro_value
+{
+ ro_value (int i_ = 0, int j_ = 0): i (i_), j (j_) {}
+
+ #pragma db readonly
+ int i;
+
+ #pragma db readonly
+ int j;
+};
+
+inline bool
+operator== (const ro_value& x, const ro_value& y)
+{
+ return x.i == y.i && x.j == y.j;
+}
+
+#pragma db object
+struct ro_object
+{
+ ro_object () {}
+ ro_object (unsigned long id): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ odb::vector<ro_value> v;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/container/change-tracking/testscript b/odb-tests/common/container/change-tracking/testscript
new file mode 100644
index 0000000..2169869
--- /dev/null
+++ b/odb-tests/common/container/change-tracking/testscript
@@ -0,0 +1,33 @@
+# file : common/container/change-tracking/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/ctor/buildfile b/odb-tests/common/ctor/buildfile
new file mode 100644
index 0000000..a9892bc
--- /dev/null
+++ b/odb-tests/common/ctor/buildfile
@@ -0,0 +1,41 @@
+# file : common/ctor/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix ctor_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/ctor/driver.cxx b/odb-tests/common/ctor/driver.cxx
new file mode 100644
index 0000000..c9b445d
--- /dev/null
+++ b/odb-tests/common/ctor/driver.cxx
@@ -0,0 +1,79 @@
+// file : common/ctor/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test support for persistent objects without default constructors.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ typedef odb::query<person> query;
+ typedef odb::result<person> result;
+
+ unique_ptr<database> db (create_database (argc, argv));
+
+ person p1 ("John", "Doe", 30);
+ person p2 ("Jane", "Doe", 29);
+ person p3 ("Joe", "Dirt", 31);
+
+ {
+ transaction t (db->begin ());
+
+ db->persist (p1);
+ db->persist (p2);
+ db->persist (p3);
+
+ t.commit ();
+ }
+
+ {
+ person p ("", "", 0);
+
+ transaction t (db->begin ());
+
+ db->load (p1.id_, p);
+
+ assert (p.first_ == p1.first_);
+ assert (p.last_ == p1.last_);
+ assert (p.age_ == p1.age_);
+
+ result r (db->query<person> (query::age < 30));
+
+ assert (!r.empty ());
+
+ result::iterator i (r.begin ());
+ i.load (p);
+ assert (p.first_ == "Jane");
+ assert (p.last_ == "Doe");
+ assert (p.age_ == 29);
+
+ assert (size (r) == 1);
+
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/ctor/test.hxx b/odb-tests/common/ctor/test.hxx
new file mode 100644
index 0000000..2a2becd
--- /dev/null
+++ b/odb-tests/common/ctor/test.hxx
@@ -0,0 +1,29 @@
+// file : common/ctor/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct person
+{
+ person (const std::string& first,
+ const std::string& last,
+ unsigned short age)
+ : first_ (first), last_ (last), age_ (age)
+ {
+ }
+
+ #pragma db id auto
+ unsigned long id_;
+
+ std::string first_;
+ std::string last_;
+ unsigned short age_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/ctor/testscript b/odb-tests/common/ctor/testscript
new file mode 100644
index 0000000..8946ddb
--- /dev/null
+++ b/odb-tests/common/ctor/testscript
@@ -0,0 +1,33 @@
+# file : common/ctor/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/default/buildfile b/odb-tests/common/default/buildfile
new file mode 100644
index 0000000..e25bd08
--- /dev/null
+++ b/odb-tests/common/default/buildfile
@@ -0,0 +1,41 @@
+# file : common/default/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix default_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/default/driver.cxx b/odb-tests/common/default/driver.cxx
new file mode 100644
index 0000000..2d3ef01
--- /dev/null
+++ b/odb-tests/common/default/driver.cxx
@@ -0,0 +1,81 @@
+// file : common/default/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test default values.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Insert an object using an ad-hoc SQL statement. This way
+ // we get all the default values.
+ //
+ {
+ transaction t (db->begin ());
+
+ if (db->id () != odb::id_oracle)
+ db->execute ("INSERT INTO default_object (obj_id) VALUES (1)");
+ else
+ db->execute ("INSERT INTO \"default_object\" (\"obj_id\") VALUES (1)");
+
+ t.commit ();
+ }
+
+ // Now load the object and check all the values.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o (db->load<object> (1));
+ t.commit ();
+
+ assert (o->b);
+ assert (o->pi == 1234);
+ assert (o->ni == -1234);
+ assert (o->zi == 0);
+ assert (o->pf == 1.234);
+ assert (o->nf == -1.234);
+ assert (o->zf == 0.0);
+ assert (o->sf == 1.123e+10);
+ assert (o->str == "Someone's string");
+ assert (o->e == green);
+ }
+
+ // Check the NULL default value using a query.
+ //
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::null.is_null ()));
+ assert (!r.empty ());
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/default/test.hxx b/odb-tests/common/default/test.hxx
new file mode 100644
index 0000000..7f35ed4
--- /dev/null
+++ b/odb-tests/common/default/test.hxx
@@ -0,0 +1,67 @@
+// file : common/default/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <odb/core.hxx>
+
+enum color {red, green, blue};
+
+#pragma db value(unsigned long) default(0)
+
+#pragma db object
+struct object
+{
+ #pragma db id
+ unsigned long obj_id;
+
+ // NULL.
+ //
+ #pragma db null default(null)
+ unsigned long null;
+
+ // Boolean.
+ //
+ #pragma db default(true)
+ bool b;
+
+ // Integers.
+ //
+ #pragma db default(1234)
+ unsigned long pi;
+
+ #pragma db default(-1234)
+ long ni;
+
+ // 0 default taken from the type.
+ unsigned long zi;
+
+ // Floats.
+ //
+ #pragma db default(1.234)
+ double pf;
+
+ #pragma db default(-1.234)
+ double nf;
+
+ #pragma db default(0.0)
+ double zf;
+
+ #pragma db default(1.123e+10)
+ double sf;
+
+ // Strings. MySQL doesn't support default values on TEXT
+ // columns, so make the type VARCHAR.
+ //
+ #pragma db type("VARCHAR(64)") default("Someone's string")
+ std::string str;
+
+ // Enums.
+ //
+ #pragma db default(green)
+ color e;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/default/testscript b/odb-tests/common/default/testscript
new file mode 100644
index 0000000..f29cef4
--- /dev/null
+++ b/odb-tests/common/default/testscript
@@ -0,0 +1,33 @@
+# file : common/default/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/definition/.gitignore b/odb-tests/common/definition/.gitignore
new file mode 100644
index 0000000..5838670
--- /dev/null
+++ b/odb-tests/common/definition/.gitignore
@@ -0,0 +1,6 @@
+# ODB-generated files.
+#
+time-mapping-odb.?xx
+time-mapping-odb-*.?xx
+time-mapping.sql
+time-mapping-*.sql
diff --git a/odb-tests/common/definition/buildfile b/odb-tests/common/definition/buildfile
new file mode 100644
index 0000000..83a09ff
--- /dev/null
+++ b/odb-tests/common/definition/buildfile
@@ -0,0 +1,52 @@
+# file : common/definition/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+hdrs = test time-mapping
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+for h: $hdrs
+{
+ exe{driver}: {hxx ixx cxx}{$h-odb}
+
+ <{hxx ixx cxx}{$h-odb}>: hxx{$h} libue{test-meta}
+
+ for db: $databases
+ {
+ exe{driver}: {hxx ixx cxx}{$h-odb-$db}: include = $multi
+ <{hxx ixx cxx}{$h-odb-$db}>: hxx{$h} libue{test-meta}
+ }
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix definition_ \
+ --generate-schema
+
+<{hxx ixx cxx}{time-mapping-odb}>: odb_options =
+
+for db: $databases
+ {hxx ixx cxx}{time-mapping-odb-$db}: odb_options =
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/definition/driver.cxx b/odb-tests/common/definition/driver.cxx
new file mode 100644
index 0000000..223eeaf
--- /dev/null
+++ b/odb-tests/common/definition/driver.cxx
@@ -0,0 +1,56 @@
+// file : common/definition/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test overriding composite value definition point. This is primarily
+// useful to make composite values out of third-party types.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ object o;
+ o.time.tv_sec = 1;
+ o.time.tv_usec = 1000;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ t.commit ();
+
+ assert (p->time.tv_sec == o.time.tv_sec &&
+ p->time.tv_usec == o.time.tv_usec);
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/definition/test.hxx b/odb-tests/common/definition/test.hxx
new file mode 100644
index 0000000..38fc02a
--- /dev/null
+++ b/odb-tests/common/definition/test.hxx
@@ -0,0 +1,26 @@
+// file : common/definition/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#ifdef _WIN32
+# include <winsock2.h> // timeval
+#else
+# include <sys/time.h> // timeval
+#endif
+
+#include <odb/core.hxx>
+
+#include "time-mapping.hxx"
+
+#pragma db object
+struct object
+{
+ #pragma db id auto
+ unsigned long id;
+
+ timeval time;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/definition/testscript b/odb-tests/common/definition/testscript
new file mode 100644
index 0000000..c9dea6d
--- /dev/null
+++ b/odb-tests/common/definition/testscript
@@ -0,0 +1,33 @@
+# file : common/definition/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/definition/time-mapping.hxx b/odb-tests/common/definition/time-mapping.hxx
new file mode 100644
index 0000000..469cfb7
--- /dev/null
+++ b/odb-tests/common/definition/time-mapping.hxx
@@ -0,0 +1,17 @@
+// file : common/definition/time-mapping.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TIME_MAPPING_HXX
+#define TIME_MAPPING_HXX
+
+#ifdef _WIN32
+# include <winsock2.h> // timeval
+#else
+# include <sys/time.h> // timeval
+#endif
+
+#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
diff --git a/odb-tests/common/enum/buildfile b/odb-tests/common/enum/buildfile
new file mode 100644
index 0000000..eb3a29a
--- /dev/null
+++ b/odb-tests/common/enum/buildfile
@@ -0,0 +1,41 @@
+# file : common/enum/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix enum_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/enum/driver.cxx b/odb-tests/common/enum/driver.cxx
new file mode 100644
index 0000000..ed3eb59
--- /dev/null
+++ b/odb-tests/common/enum/driver.cxx
@@ -0,0 +1,84 @@
+// file : common/enum/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test automatic C++ enum mapping.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ unique_ptr<database> db (create_database (argc, argv));
+
+ object o;
+ o.color_ = green;
+ o.taste_ = object::sweet;
+ o.position_ = object::left;
+
+ o.gender_ = object::gender::female;
+ o.scale_ = object::scale::ten;
+ o.yesno_ = object::yesno::yes;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (o.id_));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+
+ {
+ transaction t (db->begin ());
+
+ result r1 (db->query<object> (query::color == blue));
+ result r2 (db->query<object> (query::taste == object::sweet));
+ result r3 (db->query<object> (query::position == object::left));
+
+ assert (r1.empty ());
+ assert (!r2.empty ());
+ assert (!r3.empty ());
+
+ result r4 (db->query<object> (query::gender == object::gender::female));
+ result r5 (db->query<object> (query::scale == object::scale::ten));
+ result r6 (db->query<object> (query::yesno == object::yesno::yes));
+
+ assert (!r4.empty ());
+ assert (!r5.empty ());
+ assert (!r6.empty ());
+
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/enum/test.hxx b/odb-tests/common/enum/test.hxx
new file mode 100644
index 0000000..a279112
--- /dev/null
+++ b/odb-tests/common/enum/test.hxx
@@ -0,0 +1,47 @@
+// file : common/enum/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+
+enum color {red, green, blue};
+
+#pragma db object
+struct object
+{
+ #pragma db id auto
+ unsigned long id_;
+
+ color color_;
+ enum taste {bitter, sweet, sour};
+ taste taste_;
+
+ enum position {left = -1, center = 0, right = 1};
+ position position_;
+
+
+ enum class gender {male, female};
+ enum class scale: unsigned char {one = 1, ten = 10, hundred = 100};
+ enum class yesno: bool {no, yes};
+
+ gender gender_;
+ scale scale_;
+ yesno yesno_;
+};
+
+inline bool
+operator == (const object& x, const object& y)
+{
+ return
+ x.id_ == y.id_
+ && x.color_ == y.color_
+ && x.taste_ == y.taste_
+ && x.position_ == y.position_
+ && x.gender_ == y.gender_
+ && x.scale_ == y.scale_
+ && x.yesno_ == y.yesno_;
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/enum/testscript b/odb-tests/common/enum/testscript
new file mode 100644
index 0000000..d2ca28c
--- /dev/null
+++ b/odb-tests/common/enum/testscript
@@ -0,0 +1,33 @@
+# file : common/enum/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/erase-query/buildfile b/odb-tests/common/erase-query/buildfile
new file mode 100644
index 0000000..d833b6e
--- /dev/null
+++ b/odb-tests/common/erase-query/buildfile
@@ -0,0 +1,41 @@
+# file : common/erase-query/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix erase_query_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/erase-query/driver.cxx b/odb-tests/common/erase-query/driver.cxx
new file mode 100644
index 0000000..6c11957
--- /dev/null
+++ b/odb-tests/common/erase-query/driver.cxx
@@ -0,0 +1,181 @@
+// file : common/erase-query/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test query-based erase.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+void
+persist (database& db)
+{
+ object o1 (1);
+ object o2 (2);
+ object o3 (3);
+ object o4 (4);
+
+ transaction t (db.begin ());
+ db.persist (o1);
+ db.persist (o2);
+ db.persist (o3);
+ db.persist (o4);
+ t.commit ();
+}
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ typedef odb::query<object> query;
+
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // erase_query()
+ //
+ persist (*db);
+
+ {
+ transaction t (db->begin ());
+ assert (db->erase_query<object> () == 4);
+ t.commit ();
+ }
+
+ // erase_query(const char*)
+ //
+ persist (*db);
+
+ {
+ transaction t (db->begin ());
+
+ if (db->id () != odb::id_oracle)
+ assert (db->erase_query<object> (
+ "erase_query_object.id < 3") == 2);
+ else
+ assert (db->erase_query<object> (
+ "\"erase_query_object\".\"id\" < 3") == 2);
+
+ db->erase_query<object> ();
+ t.commit ();
+ }
+
+ // erase_query(query)
+ //
+ persist (*db);
+
+ {
+ transaction t (db->begin ());
+ assert (db->erase_query<object> (query::id == 2 || query::id == 4) == 2);
+ db->erase_query<object> ();
+ t.commit ();
+ }
+
+ // Test predicates involving object pointers (DELETE JOIN).
+ //
+ /*
+ {
+ object o11 (1);
+ object o12 (2);
+ object o13 (3);
+ object2 o2;
+
+ o11.o2 = &o2;
+ o2.num = 123;
+
+ o12.o1 = &o13;
+ o13.num = 123;
+
+ transaction t (db->begin ());
+ db->persist (o2);
+ db->persist (o13);
+ db->persist (o12);
+ db->persist (o11);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ assert (db->erase_query<object> (query::o1::num == 123) == 1);
+ assert (db->erase_query<object> (query::o2::num == 123) == 1);
+ db->erase_query<object> ();
+ t.commit ();
+ }
+ */
+
+ // For now we can only do column-based tests, like is_null().
+ //
+ {
+ object o11 (1);
+ object o12 (2);
+ object o13 (3);
+ object2 o2;
+
+ o12.o2 = &o2;
+
+ transaction t (db->begin ());
+ db->persist (o2);
+ db->persist (o13);
+ db->persist (o12);
+ db->persist (o11);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ assert (db->erase_query<object> (query::o2.is_null ()) == 2);
+ db->erase_query<object> ();
+ t.commit ();
+ }
+
+ // Make sure container data is deleted.
+ //
+ {
+ object o (1);
+ o.v.push_back (1);
+ o.v.push_back (2);
+ o.v.push_back (3);
+
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ assert (db->erase_query<object> () == 1);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+
+ if (db->id () != odb::id_oracle)
+ assert (db->execute ("SELECT * FROM erase_query_object_v "
+ "WHERE object_id = 1") == 0);
+ else
+ assert (db->execute ("SELECT * FROM \"erase_query_object_v\" "
+ "WHERE \"object_id\" = 1") == 0);
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/erase-query/test.hxx b/odb-tests/common/erase-query/test.hxx
new file mode 100644
index 0000000..9e73f12
--- /dev/null
+++ b/odb-tests/common/erase-query/test.hxx
@@ -0,0 +1,46 @@
+// file : common/erase-query/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <vector>
+
+#include <odb/core.hxx>
+
+struct object2;
+
+#pragma db object
+struct object
+{
+ object (unsigned long id)
+ : id_ (id), o1 (0), o2 (0)
+ {
+ }
+
+ object ()
+ : o1 (0), o2 (0)
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+
+ std::vector<int> v;
+
+ int num;
+
+ object* o1;
+ object2* o2;
+};
+
+#pragma db object
+struct object2
+{
+ #pragma db id auto
+ unsigned long id_;
+
+ int num;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/erase-query/testscript b/odb-tests/common/erase-query/testscript
new file mode 100644
index 0000000..90862ab
--- /dev/null
+++ b/odb-tests/common/erase-query/testscript
@@ -0,0 +1,33 @@
+# file : common/erase-query/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/id/auto/buildfile b/odb-tests/common/id/auto/buildfile
new file mode 100644
index 0000000..c340200
--- /dev/null
+++ b/odb-tests/common/id/auto/buildfile
@@ -0,0 +1,40 @@
+# file : common/id/auto/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix t_id_auto_ \
+ --generate-schema
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/id/auto/driver.cxx b/odb-tests/common/id/auto/driver.cxx
new file mode 100644
index 0000000..d294e69
--- /dev/null
+++ b/odb-tests/common/id/auto/driver.cxx
@@ -0,0 +1,96 @@
+// file : common/id/auto/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test automatic id assignment.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // object
+ //
+ {
+ unsigned long id1, id2, id3;
+ {
+ object o1 ("one");
+ object o2 ("two");
+ object o3 ("three");
+
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o3);
+ t.commit ();
+
+ id1 = o1.id_;
+ id2 = o2.id_;
+ id3 = o3.id_;
+
+ assert (id1 != id2);
+ assert (id1 != id3);
+ assert (id2 != id3);
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (id1));
+ unique_ptr<object> o2 (db->load<object> (id2));
+ unique_ptr<object> o3 (db->load<object> (id3));
+ t.commit ();
+
+ assert (o1->id_ == id1 && o1->str_ == "one");
+ assert (o2->id_ == id2 && o2->str_ == "two");
+ assert (o3->id_ == id3 && o3->str_ == "three");
+ }
+ }
+
+ // auto_only
+ //
+ {
+ unsigned short id;
+ {
+ auto_only o;
+
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+
+ id = o.id_;
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<auto_only> o (db->load<auto_only> (id));
+ t.commit ();
+
+ assert (o->id_ == id);
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/id/auto/test.hxx b/odb-tests/common/id/auto/test.hxx
new file mode 100644
index 0000000..233c79f
--- /dev/null
+++ b/odb-tests/common/id/auto/test.hxx
@@ -0,0 +1,40 @@
+// file : common/id/auto/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object (const std::string& str)
+ : id_ (1), str_ (str)
+ {
+ }
+
+ #pragma db auto id
+ unsigned long id_;
+ std::string str_;
+
+private:
+ object ()
+ {
+ }
+
+ friend class odb::access;
+};
+
+// Test the case where the object has just the auto id.
+//
+#pragma db object
+struct auto_only
+{
+ #pragma db auto id pgsql:type("BIGINT")
+ unsigned short id_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/id/auto/testscript b/odb-tests/common/id/auto/testscript
new file mode 100644
index 0000000..bb2a3a4
--- /dev/null
+++ b/odb-tests/common/id/auto/testscript
@@ -0,0 +1,33 @@
+# file : common/id/auto/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/id/composite/buildfile b/odb-tests/common/id/composite/buildfile
new file mode 100644
index 0000000..4bc9f9a
--- /dev/null
+++ b/odb-tests/common/id/composite/buildfile
@@ -0,0 +1,42 @@
+# file : common/id/composite/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix t_id_comp_ \
+ --generate-schema \
+ --generate-query \
+ --generate-session
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/id/composite/driver.cxx b/odb-tests/common/id/composite/driver.cxx
new file mode 100644
index 0000000..3d66101
--- /dev/null
+++ b/odb-tests/common/id/composite/driver.cxx
@@ -0,0 +1,731 @@
+// file : common/id/composite/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test composite object ids.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/session.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Test 1.
+ //
+ {
+ using namespace test1;
+
+ object o1 (scomp ("aaa", "bbb", "ccc"), 123);
+ o1.vec.push_back (scomp ("xxx", "xxx", "xxx"));
+ o1.vec.push_back (scomp ("yyy", "yyy", "yyy"));
+
+ object o2 (scomp ("aaa", "bbb", "ccd"), 234);
+ o2.vec.push_back (scomp ("zzz", "", "zzz"));
+
+ object o3 (scomp ("baa", "bbb", "ccc"), 345);
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o3);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p1 (db->load<object> (o1.id));
+ unique_ptr<object> p2 (db->load<object> (o2.id));
+ unique_ptr<object> p3 (db->load<object> (o3.id));
+ t.commit ();
+
+ assert (*p1 == o1);
+ assert (*p2 == o2);
+ assert (*p3 == o3);
+ }
+
+ // Update.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o1.id));
+ p->num++;
+ db->update (*p);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o1.id));
+ t.commit ();
+
+ assert (p->num == o1.num + 1);
+ }
+
+ // Erase.
+ //
+ {
+ transaction t (db->begin ());
+ db->erase<object> (o1.id);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->find<object> (o1.id));
+ assert (p.get () == 0);
+ t.commit ();
+ }
+ }
+
+ // Test 2.
+ //
+ {
+ using namespace test2;
+
+ object2 o2 (ncomp (2, 0, 1));
+ o2.o1 = new object1 (scomp ("o1", "o2", "aaa"));
+
+ object3 o3 (ncomp (3, 0, 1));
+ o3.o1.push_back (new object1 (scomp ("o1", "o3", "aaa")));
+ o3.o1.push_back (new object1 (scomp ("o1", "o3", "bbb")));
+
+ object4 o4 (ncomp (4, 0, 1));
+ o4.c.o2 = new object2 (ncomp (2, 4, 1));
+ o4.c.o2->o1 = new object1 (scomp ("o1", "o2", "ccc"));
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o2.o1);
+ db->persist (o2);
+ db->persist (o3.o1[0]);
+ db->persist (o3.o1[1]);
+ db->persist (o3);
+ db->persist (o4.c.o2->o1);
+ db->persist (o4.c.o2);
+ db->persist (o4);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object2> p2 (db->load<object2> (o2.id));
+ unique_ptr<object3> p3 (db->load<object3> (o3.id));
+ unique_ptr<object4> p4 (db->load<object4> (o4.id));
+ t.commit ();
+
+ assert (p2->o1->id == o2.o1->id);
+ assert (p3->o1.size () == o3.o1.size ());
+ assert (p3->o1[0]->id == o3.o1[0]->id);
+ assert (p3->o1[1]->id == o3.o1[1]->id);
+ assert (p4->c.o2->id == o4.c.o2->id);
+ assert (p4->c.o2->o1->id == o4.c.o2->o1->id);
+ }
+
+ // Update.
+ //
+ {
+ scomp id2, id3;
+
+ {
+ transaction t (db->begin ());
+
+ unique_ptr<object2> p2 (db->load<object2> (o2.id));
+ delete p2->o1;
+ p2->o1 = new object1 (scomp ("o1", "o2", "bbb"));
+ id2 = db->persist (p2->o1);
+ db->update (*p2);
+
+ unique_ptr<object3> p3 (db->load<object3> (o3.id));
+ delete p3->o1.back ();
+ p3->o1.pop_back ();
+ p3->o1.push_back (new object1 (scomp ("o1", "o3", "ccc")));
+ id3 = db->persist (p3->o1.back ());
+ db->update (*p3);
+
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object2> p2 (db->load<object2> (o2.id));
+ unique_ptr<object3> p3 (db->load<object3> (o3.id));
+ t.commit ();
+
+ assert (p2->o1->id == id2);
+ assert (p3->o1.back ()->id == id3);
+ }
+ }
+
+ // Query.
+ //
+ {
+ {
+ typedef odb::query<object2> query;
+ typedef odb::result<object2> result;
+
+ transaction t (db->begin ());
+
+ {
+ result r (db->query<object2> (query::o1->id.str3 == "bbb"));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->id == o2.id);
+ assert (++i == r.end ());
+ }
+
+ {
+ // As id (dual interface).
+ //
+ result r (db->query<object2> (query::o1.str3 == "bbb"));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->id == o2.id);
+ assert (++i == r.end ());
+ }
+
+ t.commit ();
+ }
+
+ // Second level composite object pointer.
+ //
+ {
+ typedef odb::query<object4> query;
+ typedef odb::result<object4> result;
+
+ transaction t (db->begin ());
+
+ result r (db->query<object4> (query::c.o2->o1.str3 == "ccc"));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->id == o4.id);
+ assert (++i == r.end ());
+
+ t.commit ();
+ }
+ }
+
+ // View.
+ //
+ {
+ transaction t (db->begin ());
+
+ {
+ typedef odb::query<view2> query;
+ typedef odb::result<view2> result;
+
+ result r (db->query<view2> (query::object2::id.num2 == 0));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->num == 1 && i->str == "bbb");
+ assert (++i == r.end ());
+ }
+
+ {
+ typedef odb::query<view3> query;
+ typedef odb::result<view3> result;
+
+ result r (db->query<view3> ((query::object3::id.num2 == 0) +
+ "ORDER BY" + query::object1::id.str3));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->num == 1 && i->str == "aaa");
+ assert (++i != r.end ());
+ assert (i->num == 1 && i->str == "ccc");
+ assert (++i == r.end ());
+ }
+
+ {
+ typedef odb::query<view4> query;
+ typedef odb::result<view4> result;
+
+ result r (db->query<view4> (query::object4::id.num2 == 0));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->num4 == 1 && i->num2 == 1 && i->str == "ccc");
+ assert (++i == r.end ());
+ }
+
+ t.commit ();
+ }
+ }
+
+ // Test 3.
+ //
+ {
+ using namespace test3;
+
+ object2 o2 (ncomp (2, 0, 1));
+ o2.o1 = new object1 (scomp ("o1", "o2", "aaa"));
+ o2.o1->o2 = &o2;
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o2.o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ session s;
+ transaction t (db->begin ());
+ unique_ptr<object2> p2 (db->load<object2> (o2.id));
+ t.commit ();
+
+ assert (p2->o1->o2->id == o2.id);
+ }
+
+ // Query.
+ //
+ {
+ typedef odb::query<object1> query;
+ typedef odb::result<object1> result;
+
+ transaction t (db->begin ());
+
+ {
+ session s;
+
+ result r (db->query<object1> (query::o2->id.num2 == 0));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->id == o2.o1->id);
+
+ i->o2->o1 = 0;
+ delete i->o2;
+
+ assert (++i == r.end ());
+ }
+
+ t.commit ();
+ }
+
+ // View.
+ //
+ {
+ typedef odb::query<view> query;
+ typedef odb::result<view> result;
+
+ transaction t (db->begin ());
+
+ result r (db->query<view> (query::object1::id.str2 == "o2"));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->num == 1 && i->str == "aaa");
+ assert (++i == r.end ());
+
+ t.commit ();
+ }
+ }
+
+ // Test 4.
+ //
+ {
+ using namespace test4;
+
+ object2 o2 (ncomp (2, 0, 1));
+
+ o2.o1.push_back (new object1 (scomp ("o1", "o2", "aaa")));
+ o2.o1.back ()->o2 = &o2;
+
+ o2.o1.push_back (new object1 (scomp ("o1", "o2", "bbb")));
+ o2.o1.back ()->o2 = &o2;
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o2.o1[0]);
+ db->persist (o2.o1[1]);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ session s;
+ transaction t (db->begin ());
+ unique_ptr<object2> p2 (db->load<object2> (o2.id));
+ t.commit ();
+
+ assert (p2->o1.size () == 2);
+ assert (p2->o1[0]->o2->id == o2.id);
+ assert (p2->o1[1]->o2->id == o2.id);
+ }
+
+ // Query.
+ //
+ {
+ typedef odb::query<object1> query;
+ typedef odb::result<object1> result;
+
+ transaction t (db->begin ());
+
+ {
+ session s;
+
+ result r (db->query<object1> (query::o2->id.num2 == 0));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->id == o2.o1[0]->id);
+ i->o2->o1.clear ();
+
+ assert (++i != r.end ());
+ assert (i->id == o2.o1[1]->id);
+
+ i->o2->o1.clear ();
+ delete i->o2;
+
+ assert (++i == r.end ());
+ }
+
+ t.commit ();
+ }
+
+ // View.
+ //
+ {
+ typedef odb::query<view> query;
+ typedef odb::result<view> result;
+
+ transaction t (db->begin ());
+
+ result r (db->query<view> (query::object1::id.str3 == "bbb"));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->num == 1 && i->str == "bbb");
+ assert (++i == r.end ());
+
+ t.commit ();
+ }
+ }
+
+ // Test 5.
+ //
+ {
+ using namespace test5;
+
+ object2 o2 (ncomp (2, 0, 1));
+
+ o2.o1.push_back (new object1 (scomp ("o1", "o2", "aaa")));
+ o2.o1.back ()->o2 = &o2;
+
+ o2.o1.push_back (new object1 (scomp ("o1", "o2", "bbb")));
+ o2.o1.back ()->o2 = &o2;
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o2.o1[0]);
+ db->persist (o2.o1[1]);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ session s;
+ transaction t (db->begin ());
+ unique_ptr<object2> p2 (db->load<object2> (o2.id));
+ t.commit ();
+
+ assert (p2->o1.size () == 2);
+
+ assert (p2->o1[0]->id == o2.o1[0]->id);
+ assert (p2->o1[0]->o2->id == o2.id);
+
+ assert (p2->o1[1]->id == o2.o1[1]->id);
+ assert (p2->o1[1]->o2->id == o2.id);
+ }
+
+ // View.
+ //
+ {
+ typedef odb::query<view> query;
+ typedef odb::result<view> result;
+
+ transaction t (db->begin ());
+
+ result r (db->query<view> ((query::object2::id.num2 == 0) +
+ "ORDER BY" + query::object1::id.str3));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->num == 1 && i->str == "aaa");
+ assert (++i != r.end ());
+ assert (i->num == 1 && i->str == "bbb");
+ assert (++i == r.end ());
+
+ t.commit ();
+ }
+ }
+
+ // Test 6.
+ //
+ {
+ using namespace test6;
+
+ object2 o2 (ncomp (2, 0, 1));
+
+ o2.o1.push_back (new object1 (scomp ("o1", "o2", "aaa")));
+ o2.o1.back ()->o2.push_back (&o2);
+
+ o2.o1.push_back (new object1 (scomp ("o1", "o2", "bbb")));
+ o2.o1.back ()->o2.push_back (&o2);
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o2.o1[0]);
+ db->persist (o2.o1[1]);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ session s;
+ transaction t (db->begin ());
+ unique_ptr<object2> p2 (db->load<object2> (o2.id));
+ t.commit ();
+
+ assert (p2->o1.size () == 2);
+
+ assert (p2->o1[0]->id == o2.o1[0]->id);
+ assert (p2->o1[0]->o2[0]->id == o2.id);
+
+ assert (p2->o1[1]->id == o2.o1[1]->id);
+ assert (p2->o1[1]->o2[0]->id == o2.id);
+ }
+
+ // View.
+ //
+ {
+ typedef odb::query<view> query;
+ typedef odb::result<view> result;
+
+ transaction t (db->begin ());
+
+ result r (db->query<view> ((query::object2::id.num2 == 0) +
+ "ORDER BY" + query::object1::id.str3));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->num == 1 && i->str == "aaa");
+ assert (++i != r.end ());
+ assert (i->num == 1 && i->str == "bbb");
+ assert (++i == r.end ());
+
+ t.commit ();
+ }
+ }
+
+ // Test 7.
+ //
+ {
+ using namespace test7;
+
+ object o (scomp ("aaa", "bbb", "ccc"), 123);
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ t.commit ();
+
+ assert (*p == o);
+ }
+
+ // Update.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ p->num++;
+ db->update (*p);
+
+ try
+ {
+ db->update (o);
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ }
+
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ t.commit ();
+
+ assert (p->num == o.num + 1);
+ }
+
+ // Erase.
+ //
+ {
+ transaction t (db->begin ());
+
+ try
+ {
+ db->update (o);
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ }
+
+ t.commit ();
+ }
+ }
+
+ // Test 8.
+ //
+ {
+ using namespace test8;
+
+ object2 o2a, o2b;
+ object3 o3;
+
+ o2b.o1 = new object1 (scomp ("222", "aaa", "bbb"), 123);
+ o3.o1.push_back (0);
+ o3.o1.push_back (new object1 (scomp ("333", "aaa", "bbb"), 234));
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o2a);
+ db->persist (o2b);
+ db->persist (o2b.o1);
+ db->persist (o3);
+ db->persist (o3.o1[1]);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object2> p2a (db->load<object2> (o2a.id));
+ unique_ptr<object2> p2b (db->load<object2> (o2b.id));
+ unique_ptr<object3> p3 (db->load<object3> (o3.id));
+ t.commit ();
+
+ assert (p2a->o1 == 0);
+ assert (p2b->o1 != 0 && *p2b->o1 == *o2b.o1);
+ assert (p3->o1[0] == 0);
+ assert (p3->o1[1] != 0 && *p3->o1[1] == *o3.o1[1]);
+ }
+
+ // Update.
+ //
+ {
+ object1* o1 (o3.o1[1]);
+
+ o3.o1.clear ();
+ o3.o1.push_back (o2b.o1);
+ o3.o1.push_back (0);
+
+ o2a.o1 = o1;
+ o2b.o1 = 0;
+
+ transaction t (db->begin ());
+ db->update (o2a);
+ db->update (o2b);
+ db->update (o3);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object2> p2a (db->load<object2> (o2a.id));
+ unique_ptr<object2> p2b (db->load<object2> (o2b.id));
+ unique_ptr<object3> p3 (db->load<object3> (o3.id));
+ t.commit ();
+
+ assert (p2a->o1 != 0 && *p2a->o1 == *o2a.o1);
+ assert (p2b->o1 == 0);
+ assert (p3->o1[0] != 0 && *p3->o1[0] == *o3.o1[0]);
+ assert (p3->o1[1] == 0);
+ }
+ }
+
+ // Test 9.
+ {
+ using namespace test9;
+
+ object o (123, "abc");
+ o.v.push_back (123);
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // load & check
+ //
+ {
+ transaction t (db->begin ());
+ result<object> r (db->query<object> ());
+ result<object>::iterator i (r.begin ());
+ assert (i != r.end () && o == *i && ++i == r.end ());
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/id/composite/test.hxx b/odb-tests/common/id/composite/test.hxx
new file mode 100644
index 0000000..70856a6
--- /dev/null
+++ b/odb-tests/common/id/composite/test.hxx
@@ -0,0 +1,519 @@
+// file : common/id/composite/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <vector>
+
+#include <odb/core.hxx>
+
+#pragma db value
+struct scomp
+{
+ scomp () {}
+ scomp (const std::string& s1, const std::string& s2, const std::string& s3)
+ : str1 (s1), str2 (s2), str3 (s3)
+ {
+ }
+
+ std::string str1;
+ std::string str2;
+ std::string str3;
+};
+
+inline bool
+operator== (const scomp& x, const scomp& y)
+{
+ return x.str1 == y.str1 && x.str2 == y.str2 && x.str3 == y.str3;
+}
+
+inline bool
+operator< (const scomp& x, const scomp& y)
+{
+ return x.str1 < y.str1 ||
+ (x.str1 == y.str1 && x.str2 < y.str2) ||
+ (x.str1 == y.str1 && x.str2 == y.str2 && x.str3 < y.str3);
+}
+
+#pragma db value
+struct ncomp
+{
+ ncomp () {}
+ ncomp (unsigned short n1, unsigned short n2, unsigned short n3)
+ : num1 (n1), num2 (n2), num3 (n3)
+ {
+ }
+
+ unsigned short num1;
+ unsigned short num2;
+ unsigned short num3;
+};
+
+inline bool
+operator== (const ncomp& x, const ncomp& y)
+{
+ return x.num1 == y.num1 && x.num2 == y.num2 && x.num3 == y.num3;
+}
+
+inline bool
+operator< (const ncomp& x, const ncomp& y)
+{
+ return x.num1 < y.num1 ||
+ (x.num1 == y.num1 && x.num2 < y.num2) ||
+ (x.num1 == y.num1 && x.num2 == y.num2 && x.num3 < y.num3);
+}
+
+// Test object with composite id, container.
+//
+#pragma db namespace table("t1_")
+namespace test1
+{
+ #pragma db object
+ struct object
+ {
+ object () {}
+ object (const scomp& i, unsigned long n): id (i), num (n) {}
+
+ #pragma db id
+ scomp id;
+
+ unsigned long num;
+ std::vector<scomp> vec;
+ };
+
+ inline bool
+ operator== (const object& x, const object& y)
+ {
+ return x.id == y.id && x.num == y.num && x.vec == y.vec;
+ }
+}
+
+// Test to-one and to-many relationships with composite id as well as
+// queries and views.
+//
+#pragma db namespace table("t2_")
+namespace test2
+{
+ #pragma db object
+ struct object1
+ {
+ object1 () {}
+ object1 (const scomp& i): id (i) {}
+
+ #pragma db id
+ scomp id;
+ };
+
+ #pragma db object
+ struct object2
+ {
+ object2 (): o1 (0) {}
+ object2 (const ncomp& i): id (i), o1 (0) {}
+ ~object2 () {delete o1;}
+
+ #pragma db id
+ ncomp id;
+
+ object1* o1;
+ };
+
+ #pragma db object
+ struct object3
+ {
+ object3 () {}
+ object3 (const ncomp& i): id (i) {}
+
+ ~object3 ()
+ {
+ for (std::vector<object1*>::iterator i (o1.begin ());
+ i != o1.end (); ++i)
+ delete *i;
+ }
+
+ #pragma db id
+ ncomp id;
+
+ std::vector<object1*> o1;
+ };
+
+ // Test second-level query pointer test as well as pointers in
+ // composite types.
+ //
+ #pragma db value
+ struct comp
+ {
+ comp (): o2 (0) {}
+ ~comp () {delete o2;}
+
+ object2* o2;
+ };
+
+ #pragma db object
+ struct object4
+ {
+ object4 () {}
+ object4 (const ncomp& i): id (i) {}
+
+ #pragma db id
+ ncomp id;
+
+ comp c;
+ };
+
+ #pragma db view object(object2) object(object1)
+ struct view2
+ {
+ #pragma db column (object2::id.num3)
+ unsigned short num;
+
+ #pragma db column (object1::id.str3)
+ std::string str;
+ };
+
+ #pragma db view object(object3) object(object1)
+ struct view3
+ {
+ #pragma db column (object3::id.num3)
+ unsigned short num;
+
+ #pragma db column (object1::id.str3)
+ std::string str;
+ };
+
+ #pragma db view object(object4) object(object2) object(object1)
+ struct view4
+ {
+ #pragma db column (object4::id.num3)
+ unsigned short num4;
+
+ #pragma db column (object2::id.num3)
+ unsigned short num2;
+
+ #pragma db column (object1::id.str3)
+ std::string str;
+ };
+}
+
+// Test one-to-one(i) relationship with composite id.
+//
+#pragma db namespace table("t3_")
+namespace test3
+{
+ struct object2;
+
+ #pragma db object
+ struct object1
+ {
+ object1 () {}
+ object1 (const scomp& i): id (i) {}
+
+ #pragma db id
+ scomp id;
+
+ #pragma db inverse(o1)
+ object2* o2;
+ };
+
+ #pragma db object
+ struct object2
+ {
+ object2 (): o1 (0) {}
+ object2 (const ncomp& i): id (i), o1 (0) {}
+ ~object2 () {delete o1;}
+
+ #pragma db id
+ ncomp id;
+
+ object1* o1;
+ };
+
+ #pragma db view object(object2) object(object1)
+ struct view
+ {
+ #pragma db column (object2::id.num3)
+ unsigned short num;
+
+ #pragma db column (object1::id.str3)
+ std::string str;
+ };
+}
+
+// Test many-to-one(i) relationship with composite id.
+//
+#pragma db namespace table("t4_")
+namespace test4
+{
+ struct object2;
+
+ #pragma db object
+ struct object1
+ {
+ object1 () {}
+ object1 (const scomp& i): id (i) {}
+
+ #pragma db id
+ scomp id;
+
+ #pragma db inverse(o1)
+ object2* o2;
+ };
+
+ #pragma db object
+ struct object2
+ {
+ object2 () {}
+ object2 (const ncomp& i): id (i) {}
+
+ ~object2 ()
+ {
+ for (std::vector<object1*>::iterator i (o1.begin ());
+ i != o1.end (); ++i)
+ delete *i;
+ }
+
+ #pragma db id
+ ncomp id;
+
+ std::vector<object1*> o1;
+ };
+
+ #pragma db view object(object2) object(object1)
+ struct view
+ {
+ #pragma db column (object2::id.num3)
+ unsigned short num;
+
+ #pragma db column (object1::id.str3)
+ std::string str;
+ };
+}
+
+// Test one-to-many(i) relationship with composite id.
+//
+#pragma db namespace table("t5_")
+namespace test5
+{
+ struct object2;
+
+ #pragma db object
+ struct object1
+ {
+ object1 () {}
+ object1 (const scomp& i): id (i) {}
+
+ #pragma db id
+ scomp id;
+
+ object2* o2;
+ };
+
+ #pragma db object
+ struct object2
+ {
+ object2 () {}
+ object2 (const ncomp& i): id (i) {}
+
+ ~object2 ()
+ {
+ for (std::vector<object1*>::iterator i (o1.begin ());
+ i != o1.end (); ++i)
+ delete *i;
+ }
+
+ #pragma db id
+ ncomp id;
+
+ #pragma db inverse(o2)
+ std::vector<object1*> o1;
+ };
+
+ #pragma db view object(object2) object(object1)
+ struct view
+ {
+ #pragma db column (object2::id.num3)
+ unsigned short num;
+
+ #pragma db column (object1::id.str3)
+ std::string str;
+ };
+}
+
+// Test many-to-many(i) relationship with composite id.
+//
+#pragma db namespace table("t6_")
+namespace test6
+{
+ struct object2;
+
+ #pragma db object
+ struct object1
+ {
+ object1 () {}
+ object1 (const scomp& i): id (i) {}
+
+ #pragma db id
+ scomp id;
+
+ std::vector<object2*> o2;
+ };
+
+ #pragma db object
+ struct object2
+ {
+ object2 () {}
+ object2 (const ncomp& i): id (i) {}
+
+ ~object2 ()
+ {
+ for (std::vector<object1*>::iterator i (o1.begin ());
+ i != o1.end (); ++i)
+ delete *i;
+ }
+
+ #pragma db id
+ ncomp id;
+
+ #pragma db inverse(o2)
+ std::vector<object1*> o1;
+ };
+
+ #pragma db view object(object2) object(object1)
+ struct view
+ {
+ #pragma db column (object2::id.num3)
+ unsigned short num;
+
+ #pragma db column (object1::id.str3)
+ std::string str;
+ };
+}
+
+// Test object with composite id and version (optimistic concurrency).
+//
+#pragma db namespace table("t7_")
+namespace test7
+{
+ #pragma db object optimistic
+ struct object
+ {
+ object () {}
+ object (const scomp& i, unsigned long n): id (i), num (n) {}
+
+ #pragma db id
+ scomp id;
+
+ #pragma db version
+ unsigned long ver;
+
+ unsigned long num;
+ };
+
+ inline bool
+ operator== (const object& x, const object& y)
+ {
+ return x.id == y.id && x.ver == y.ver && x.num == y.num;
+ }
+}
+
+// Test composite NULL pointers.
+//
+#pragma db namespace table("t8_")
+namespace test8
+{
+ #pragma db object
+ struct object1
+ {
+ object1 () {}
+ object1 (const scomp& i, unsigned long n): id (i), num (n) {}
+
+ #pragma db id
+ scomp id;
+
+ unsigned long num;
+ };
+
+ inline bool
+ operator== (const object1& x, const object1& y)
+ {
+ return x.id == y.id && x.num == y.num;
+ }
+
+ #pragma db object
+ struct object2
+ {
+ object2 (): o1 (0) {}
+ ~object2 () {delete o1;}
+
+ #pragma db id auto
+ unsigned long id;
+
+ object1* o1;
+ };
+
+ #pragma db object
+ struct object3
+ {
+ ~object3 ()
+ {
+ for (std::vector<object1*>::iterator i (o1.begin ());
+ i != o1.end (); ++i)
+ delete *i;
+ }
+
+ #pragma db id auto
+ unsigned long id;
+
+ std::vector<object1*> o1;
+ };
+}
+
+// Test composite id definition inside object.
+//
+#pragma db namespace table("t9_")
+namespace test9
+{
+ #pragma db object
+ struct object
+ {
+ object (unsigned long n = 0, const std::string& s = "")
+ {
+ id_.num = n;
+ id_.str = s;
+ }
+
+ unsigned long num () const {return id_.num;}
+ const std::string& str () const {return id_.str;}
+
+ std::vector<int> v;
+
+ private:
+ friend class odb::access;
+
+ #pragma db value
+ struct comp
+ {
+ unsigned long num;
+ std::string str;
+
+ bool
+ operator< (const comp& x) const
+ {
+ return num < x.num || (num == x.num && str < x.str);
+ }
+ };
+
+ #pragma db id
+ comp id_;
+ };
+
+ inline bool
+ operator== (const object& x, const object& y)
+ {
+ return x.num () == y.num () && x.str () == y.str () && x.v == y.v;
+ }
+}
+
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/id/composite/testscript b/odb-tests/common/id/composite/testscript
new file mode 100644
index 0000000..f87d4bc
--- /dev/null
+++ b/odb-tests/common/id/composite/testscript
@@ -0,0 +1,33 @@
+# file : common/id/composite/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/id/nested/buildfile b/odb-tests/common/id/nested/buildfile
new file mode 100644
index 0000000..777cb65
--- /dev/null
+++ b/odb-tests/common/id/nested/buildfile
@@ -0,0 +1,41 @@
+# file : common/nested/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix t_id_nested_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/id/nested/driver.cxx b/odb-tests/common/id/nested/driver.cxx
new file mode 100644
index 0000000..92a80f6
--- /dev/null
+++ b/odb-tests/common/id/nested/driver.cxx
@@ -0,0 +1,266 @@
+// file : common/id/nested/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test nested ids.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/session.hxx>
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+struct failed {};
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+
+ // Simple nested id.
+ //
+ {
+ using namespace test1;
+
+ object o1 (1, "a", 3);
+ o1.v.push_back (123);
+
+ object o2 (4, "b", 6);
+ o2.v.push_back (234);
+
+ object1 o (new object (10, "abc", 11));
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o.p);
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p1 (db->load<object> (o1.id.y));
+ unique_ptr<object> p2 (db->load<object> (o2.id.y));
+ unique_ptr<object1> p (db->load<object1> (o.id));
+ t.commit ();
+
+ assert (*p1 == o1);
+ assert (*p2 == o2);
+ assert (*p == o);
+ }
+
+ o1.z++;
+ o1.v.pop_back ();
+ o1.v.push_back (234);
+
+ o2.z--;
+ o2.v.back ()++;
+ o2.v.push_back (123);
+
+ delete o.p;
+ o.p = new object (20, "xyz", 11);
+
+ {
+ transaction t (db->begin ());
+ db->update (o1);
+ db->update (o2);
+ db->persist (o.p);
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p1 (db->load<object> (o1.id.y));
+ unique_ptr<object> p2 (db->load<object> (o2.id.y));
+ unique_ptr<object1> p (db->load<object1> (o.id));
+ t.commit ();
+
+ assert (*p1 == o1);
+ assert (*p2 == o2);
+ assert (*p == o);
+ }
+ }
+
+ // Composite nested id.
+ //
+ {
+ using namespace test2;
+
+ object o1 (1, 2, "a", 123);
+ o1.v.push_back (123);
+
+ object o2 (1, 3, "b", 234);
+ o2.v.push_back (234);
+
+ object1 o (new object (2, 2, "abc", 123));
+ o.p->v.push_back (345);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o.p);
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p1 (db->load<object> (o1.id.c));
+ unique_ptr<object> p2 (db->load<object> (o2.id.c));
+ unique_ptr<object1> p (db->load<object1> (o.id));
+ t.commit ();
+
+ assert (*p1 == o1);
+ assert (*p2 == o2);
+ assert (*p == o);
+ }
+
+ o1.z++;
+ o1.v.pop_back ();
+ o1.v.push_back (234);
+
+ o2.z--;
+ o2.v.modify_back ()++;
+ o2.v.push_back (123);
+
+ delete o.p;
+ o.p = new object (2, 3, "xyz", 234);
+
+ {
+ transaction t (db->begin ());
+ db->update (o1);
+ db->update (o2);
+ db->persist (o.p);
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p1 (db->load<object> (o1.id.c));
+ unique_ptr<object> p2 (db->load<object> (o2.id.c));
+ unique_ptr<object1> p (db->load<object1> (o.id));
+ t.commit ();
+
+ assert (*p1 == o1);
+ assert (*p2 == o2);
+ assert (*p == o);
+ }
+ }
+
+ // Custom/by-value access.
+ //
+ {
+ using namespace test3;
+
+ object o1 (1, "a", 3);
+ object o2 (4, "b", 6);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p1 (db->load<object> (o1.id.y));
+ unique_ptr<object> p2 (db->load<object> (o2.id.y));
+ t.commit ();
+
+ assert (*p1 == o1);
+ assert (*p2 == o2);
+ }
+
+ o1.z++;
+ o2.z--;
+
+ {
+ transaction t (db->begin ());
+ db->update (o1);
+ db->update (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p1 (db->load<object> (o1.id.y));
+ unique_ptr<object> p2 (db->load<object> (o2.id.y));
+ t.commit ();
+
+ assert (*p1 == o1);
+ assert (*p2 == o2);
+ }
+ }
+
+ // Polymorphic.
+ //
+ {
+ using namespace test4;
+
+ base o1 (1, "a");
+ object o2 (2, "b", 1);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> p1 (db->load<base> (o1.id.y));
+ unique_ptr<object> p2 (db->load<object> (o2.id.y));
+ t.commit ();
+
+ assert (*p1 == o1);
+ assert (*p2 == o2);
+ }
+
+ o2.z--;
+
+ {
+ transaction t (db->begin ());
+ db->update (o1);
+ db->update (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> p1 (db->load<base> (o1.id.y));
+ unique_ptr<object> p2 (db->load<object> (o2.id.y));
+ t.commit ();
+
+ assert (*p1 == o1);
+ assert (*p2 == o2);
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/id/nested/test.hxx b/odb-tests/common/id/nested/test.hxx
new file mode 100644
index 0000000..06ee6b8
--- /dev/null
+++ b/odb-tests/common/id/nested/test.hxx
@@ -0,0 +1,217 @@
+// file : common/id/nested/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <vector>
+
+#include <odb/core.hxx>
+#include <odb/vector.hxx>
+
+// Simple nested id.
+//
+#pragma db namespace table("t1_")
+namespace test1
+{
+ #pragma db value
+ struct comp
+ {
+ int x;
+ std::string y;
+ };
+
+ #pragma db object
+ struct object
+ {
+ #pragma db id(y)
+ comp id;
+
+ int z;
+ std::vector<int> v;
+
+ object () {}
+ object (int x, std::string y, int z_): z (z_) {id.x = x; id.y = y;}
+ };
+
+ inline bool
+ operator== (object a, object b)
+ {
+ return a.id.x == b.id.x && a.id.y == b.id.y && a.z == b.z && a.v == b.v;
+ }
+
+ #pragma db object
+ struct object1
+ {
+ #pragma db id auto
+ int id;
+
+ object* p;
+
+ object1 (object* p_ = 0): p (p_) {}
+ ~object1 () {delete p;}
+ };
+
+ inline bool
+ operator== (const object1& a, const object1& b)
+ {
+ return a.id == b.id && *a.p == *b.p;
+ }
+}
+
+// Composite nested id.
+//
+#pragma db namespace table("t2_")
+namespace test2
+{
+ #pragma db value
+ struct comp1
+ {
+ int x;
+ int y;
+ };
+
+ #pragma db value
+ struct comp2
+ {
+ comp1 c;
+ std::string s;
+ };
+
+ #pragma db object
+ struct object
+ {
+ #pragma db id(c)
+ comp2 id;
+
+ int z;
+ odb::vector<int> v;
+
+ object () {}
+ object (int x, int y, std::string s, int z_)
+ : z (z_) {id.c.x = x; id.c.y = y; id.s = s;}
+ };
+
+ inline bool
+ operator== (object a, object b)
+ {
+ return a.id.c.x == b.id.c.x && a.id.c.y == b.id.c.y &&
+ a.id.s == b.id.s && a.z == b.z && a.v == b.v;
+ }
+
+ #pragma db object
+ struct object1
+ {
+ #pragma db id auto
+ int id;
+
+ object* p;
+
+ object1 (object* p_ = 0): p (p_) {}
+ ~object1 () {delete p;}
+ };
+
+ inline bool
+ operator== (const object1& a, const object1& b)
+ {
+ return a.id == b.id && *a.p == *b.p;
+ }
+
+ // Multiple levels of nesting, just a compile test.
+ //
+ #pragma db object
+ struct object2
+ {
+ #pragma db id(c.x)
+ comp2 id;
+
+ int z;
+ };
+}
+
+// Custom/by-value access.
+//
+#pragma db namespace table("t3_")
+namespace test3
+{
+ #pragma db value
+ struct comp
+ {
+ int x;
+
+ std::string get_y () const {return y;}
+ void set_y (std::string v) {y = v;}
+
+ #pragma db get(get_y) set(set_y)
+ std::string y;
+ };
+
+ #pragma db object
+ struct object
+ {
+ comp get_id () const {return id;}
+ void set_id (comp v) {id = v;}
+
+ #pragma db id(y) get(get_id) set(set_id)
+ comp id;
+
+ int z;
+
+ object () {}
+ object (int x, std::string y, int z_): z (z_) {id.x = x; id.y = y;}
+ };
+
+ inline bool
+ operator== (object a, object b)
+ {
+ return a.id.x == b.id.x && a.id.y == b.id.y && a.z == b.z;
+ }
+}
+
+// Polymorphic.
+//
+#pragma db namespace table("t4_")
+namespace test4
+{
+ #pragma db value
+ struct comp
+ {
+ int x;
+ std::string y;
+ };
+
+ #pragma db object polymorphic
+ struct base
+ {
+ #pragma db id(y)
+ comp id;
+
+ virtual ~base () {}
+ base () {}
+ base (int x, std::string y) {id.x = x; id.y = y;}
+ };
+
+ inline bool
+ operator== (const base& a, const base& b)
+ {
+ return a.id.x == b.id.x && a.id.y == b.id.y;
+ }
+
+ #pragma db object
+ struct object: base
+ {
+ int z;
+
+ object () {}
+ object (int x, std::string y, int z_): base (x, y), z (z_) {}
+ };
+
+ inline bool
+ operator== (const object& a, const object& b)
+ {
+ return a.id.x == b.id.x && a.id.y == b.id.y && a.z == b.z;
+ }
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/id/nested/testscript b/odb-tests/common/id/nested/testscript
new file mode 100644
index 0000000..89e8d7a
--- /dev/null
+++ b/odb-tests/common/id/nested/testscript
@@ -0,0 +1,33 @@
+# file : common/nested/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/include/.gitignore b/odb-tests/common/include/.gitignore
new file mode 100644
index 0000000..d52f166
--- /dev/null
+++ b/odb-tests/common/include/.gitignore
@@ -0,0 +1,17 @@
+# ODB-generated files.
+#
+obj1-odb.?xx
+obj1-odb-*.?xx
+obj2-odb.?xx
+obj2-odb-*.?xx
+obj3-odb.?xx
+obj3-odb-*.?xx
+
+test1-odb.?xx
+test1-odb-*.?xx
+test2-odb.?xx
+test2-odb-*.?xx
+test3-odb.?xx
+test3-odb-*.?xx
+test4-odb.?xx
+test4-odb-*.?xx
diff --git a/odb-tests/common/include/buildfile b/odb-tests/common/include/buildfile
new file mode 100644
index 0000000..c9ae42e
--- /dev/null
+++ b/odb-tests/common/include/buildfile
@@ -0,0 +1,51 @@
+# file : common/include/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+hdrs = obj1 obj2 obj3 test1 test2 test3 test4
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+for h: $hdrs
+{
+ exe{driver}: {hxx ixx cxx}{$h-odb}
+
+ <{hxx ixx cxx}{$h-odb}>: hxx{$h} libue{test-meta}
+
+ for db: $databases
+ {
+ exe{driver}: {hxx ixx cxx}{$h-odb-$db}: include = $multi
+ <{hxx ixx cxx}{$h-odb-$db}>: hxx{$h} libue{test-meta}
+ }
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details). Also see driver.cxx for the details on
+# the -I options usage.
+#
+odb_options = --table-prefix include_ \
+ "-I$out_base" \
+ "-I$src_base/.." \
+ "-I$src_base/../.."
+
+cxx.poptions =+ "-I$out_base" "-I$src_base" \
+ "-I$out_base/../.." "-I$src_base/../.."
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/include/driver.cxx b/odb-tests/common/include/driver.cxx
new file mode 100644
index 0000000..561746a
--- /dev/null
+++ b/odb-tests/common/include/driver.cxx
@@ -0,0 +1,42 @@
+// file : common/include/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test inclusion of -odb files (compilation test).
+//
+// The setup of this test is as follows: the ODB compiler has two
+// additional include directories in its search path: .. and ../..
+// while the C++ compiler has only ../.. . This way, if a ..-based
+// path is used in the generated code, the C++ compilation will
+// fail.
+//
+
+#include <memory>
+#include <iostream>
+
+#include <odb/exceptions.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test1.hxx"
+#include "test1-odb.hxx"
+
+#include "test2.hxx"
+#include "test2-odb.hxx"
+
+#include "test3.hxx"
+#include "test3-odb.hxx"
+
+#include "test4.hxx"
+#include "test4-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main ()
+{
+}
diff --git a/odb-tests/common/include/obj1.hxx b/odb-tests/common/include/obj1.hxx
new file mode 100644
index 0000000..33ae0d6
--- /dev/null
+++ b/odb-tests/common/include/obj1.hxx
@@ -0,0 +1,25 @@
+// file : common/include/obj1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef OBJ1_HXX
+#define OBJ1_HXX
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object1
+{
+ object1 (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object1 ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#endif // OBJ1_HXX
diff --git a/odb-tests/common/include/obj2.hxx b/odb-tests/common/include/obj2.hxx
new file mode 100644
index 0000000..2f20f58
--- /dev/null
+++ b/odb-tests/common/include/obj2.hxx
@@ -0,0 +1,25 @@
+// file : common/include/obj2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef OBJ2_HXX
+#define OBJ2_HXX
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object2
+{
+ object2 (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object2 ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#endif // OBJ2_HXX
diff --git a/odb-tests/common/include/obj3.hxx b/odb-tests/common/include/obj3.hxx
new file mode 100644
index 0000000..432145b
--- /dev/null
+++ b/odb-tests/common/include/obj3.hxx
@@ -0,0 +1,25 @@
+// file : common/include/obj3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef OBJ3_HXX
+#define OBJ3_HXX
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object3
+{
+ object3 (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object3 ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#endif // OBJ3_HXX
diff --git a/odb-tests/common/include/objs1.hxx b/odb-tests/common/include/objs1.hxx
new file mode 100644
index 0000000..6e949e2
--- /dev/null
+++ b/odb-tests/common/include/objs1.hxx
@@ -0,0 +1,13 @@
+// file : common/include/objs1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef OBJS1_HXX
+#define OBJS1_HXX
+
+#ifdef ODB_COMPILER
+# include <include/obj1.hxx>
+# include <include/obj2.hxx>
+# include <include/obj3.hxx>
+#endif
+
+#endif // OBJS1_HXX
diff --git a/odb-tests/common/include/objs2.hxx b/odb-tests/common/include/objs2.hxx
new file mode 100644
index 0000000..4f8133b
--- /dev/null
+++ b/odb-tests/common/include/objs2.hxx
@@ -0,0 +1,13 @@
+// file : common/include/objs2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef OBJS2_HXX
+#define OBJS2_HXX
+
+#ifdef ODB_COMPILER
+# include "include/obj1.hxx"
+# include "include/obj2.hxx"
+# include "include/obj3.hxx"
+#endif
+
+#endif // OBJS2_HXX
diff --git a/odb-tests/common/include/objs3.hxx b/odb-tests/common/include/objs3.hxx
new file mode 100644
index 0000000..2f7aaff
--- /dev/null
+++ b/odb-tests/common/include/objs3.hxx
@@ -0,0 +1,11 @@
+// file : common/include/objs3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef OBJS3_HXX
+#define OBJS3_HXX
+
+#include "../include/obj1.hxx"
+#include "../include/obj2.hxx"
+#include "../include/obj3.hxx"
+
+#endif // OBJS3_HXX
diff --git a/odb-tests/common/include/objs4.hxx b/odb-tests/common/include/objs4.hxx
new file mode 100644
index 0000000..d766fe6
--- /dev/null
+++ b/odb-tests/common/include/objs4.hxx
@@ -0,0 +1,11 @@
+// file : common/include/objs1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef OBJS4_HXX
+#define OBJS4_HXX
+
+#include <common/include/obj1.hxx>
+#include <common/include/obj2.hxx>
+#include <common/include/obj3.hxx>
+
+#endif // OBJS4_HXX
diff --git a/odb-tests/common/include/test1.hxx b/odb-tests/common/include/test1.hxx
new file mode 100644
index 0000000..1914ddb
--- /dev/null
+++ b/odb-tests/common/include/test1.hxx
@@ -0,0 +1,16 @@
+// file : common/include/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+// Test include directive parsing.
+//
+#include"obj1.hxx"
+
+ # include \
+ <common/include/obj2.hxx>
+
+/*comment*/ # /*comment*/ include /* comment */ "obj3.hxx" // comment
+
+#endif // TEST1_HXX
diff --git a/odb-tests/common/include/test2.hxx b/odb-tests/common/include/test2.hxx
new file mode 100644
index 0000000..6017ac4
--- /dev/null
+++ b/odb-tests/common/include/test2.hxx
@@ -0,0 +1,15 @@
+// file : common/include/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+// Test preference of includes from the main file.
+//
+#include "objs1.hxx"
+
+#include "obj1.hxx"
+#include "obj2.hxx"
+#include "obj3.hxx"
+
+#endif // TEST2_HXX
diff --git a/odb-tests/common/include/test3.hxx b/odb-tests/common/include/test3.hxx
new file mode 100644
index 0000000..e55ecdb
--- /dev/null
+++ b/odb-tests/common/include/test3.hxx
@@ -0,0 +1,12 @@
+// file : common/include/test3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST3_HXX
+#define TEST3_HXX
+
+// Test preference of longer (more qualified) paths.
+//
+#include "objs2.hxx"
+#include "objs3.hxx"
+
+#endif // TEST3_HXX
diff --git a/odb-tests/common/include/test4.hxx b/odb-tests/common/include/test4.hxx
new file mode 100644
index 0000000..b58e679
--- /dev/null
+++ b/odb-tests/common/include/test4.hxx
@@ -0,0 +1,12 @@
+// file : common/include/test3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST3_HXX
+#define TEST3_HXX
+
+// Test preference of <> over "".
+//
+#include "objs2.hxx"
+#include "objs4.hxx"
+
+#endif // TEST3_HXX
diff --git a/odb-tests/common/include/testscript b/odb-tests/common/include/testscript
new file mode 100644
index 0000000..089f7a1
--- /dev/null
+++ b/odb-tests/common/include/testscript
@@ -0,0 +1,31 @@
+# file : common/include/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $* &!odb-test.db
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $*
+}
diff --git a/odb-tests/common/index/buildfile b/odb-tests/common/index/buildfile
new file mode 100644
index 0000000..535bd26
--- /dev/null
+++ b/odb-tests/common/index/buildfile
@@ -0,0 +1,40 @@
+# file : common/index/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix index_ \
+ --generate-schema
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/index/driver.cxx b/odb-tests/common/index/driver.cxx
new file mode 100644
index 0000000..7a22a7c
--- /dev/null
+++ b/odb-tests/common/index/driver.cxx
@@ -0,0 +1,44 @@
+// file : common/index/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test index creation with db pragma index. See also database-specific
+// tests.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ // This is just a schema creation test.
+ //
+ unique_ptr<database> db (create_database (argc, argv));
+
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/index/test.hxx b/odb-tests/common/index/test.hxx
new file mode 100644
index 0000000..f27783f
--- /dev/null
+++ b/odb-tests/common/index/test.hxx
@@ -0,0 +1,142 @@
+// file : common/index/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <vector>
+
+#include <odb/core.hxx>
+
+// Test basic functionality.
+//
+#pragma db namespace table("t1_")
+namespace test1
+{
+ #pragma db object
+ struct object
+ {
+ #pragma db id auto
+ unsigned long id_;
+
+ #pragma db index
+ int i1;
+
+ #pragma db unique
+ int i2;
+
+ #pragma db unique index
+ int i3;
+
+ int i4;
+ #pragma db index unique member(i4)
+
+ int i5;
+ #pragma db index type("UNIQUE") member(i5)
+
+ int i6;
+ #pragma db index("object_i6_index") member(i6)
+
+ int i7;
+ int i8;
+ int i9;
+
+ int i10;
+ #pragma db index member(i10, "ASC")
+ };
+
+ #pragma db index(object) member(i7)
+ #pragma db index(object::"object_i8_index") member(i8)
+}
+
+#pragma db index(test1::object::"object_i9_index") member(i9)
+
+// Test composite indexes.
+//
+#pragma db namespace table("t2_")
+namespace test2
+{
+ #pragma db value
+ struct nested
+ {
+ int x;
+ int y;
+ };
+
+ #pragma db value
+ struct comp
+ {
+ int x;
+ int y;
+ nested n;
+ };
+
+ #pragma db object
+ struct object
+ {
+ #pragma db id auto
+ unsigned long id_;
+
+ int i1a;
+ int i1b;
+ #pragma db index("object_i1_i") member(i1a) member(i1b)
+
+ int i2a;
+ int i2b;
+ #pragma db index("object_i2_i") members(i2a, i2b)
+
+ #pragma db index
+ comp c1;
+
+ #pragma db index column("")
+ comp c2;
+
+ comp c3;
+ #pragma db index member(c3.x)
+ #pragma db index member(c3.y)
+
+ comp c4;
+ #pragma db index("object_c4_i") members(c4.x, c4.y, c4.n.x)
+
+ comp c5;
+ int i5;
+ #pragma db index("object_ci5_i") member(c5) member(i5)
+ };
+}
+
+// Test container indexes.
+//
+#pragma db namespace table("t3_")
+namespace test3
+{
+ #pragma db value
+ struct id
+ {
+ int x;
+ int y;
+ };
+
+ #pragma db value
+ struct comp
+ {
+ int x;
+ std::vector<int> v;
+ };
+
+ #pragma db object
+ struct object
+ {
+ #pragma db id
+ id id_;
+
+ std::vector<int> v;
+ #pragma db index unique member(v.id)
+ #pragma db index("object_v_index_index") member(v.index)
+
+ comp c;
+ #pragma db index("object_c_v_id_index") member(c.v.id)
+ #pragma db index unique member(c.v.index)
+ };
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/index/testscript b/odb-tests/common/index/testscript
new file mode 100644
index 0000000..cbce341
--- /dev/null
+++ b/odb-tests/common/index/testscript
@@ -0,0 +1,33 @@
+# file : common/index/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/inheritance/polymorphism/.gitignore b/odb-tests/common/inheritance/polymorphism/.gitignore
new file mode 100644
index 0000000..f183a6f
--- /dev/null
+++ b/odb-tests/common/inheritance/polymorphism/.gitignore
@@ -0,0 +1,76 @@
+# ODB-generated files.
+#
+test1-odb.?xx
+test1-odb-*.?xx
+test1.sql
+test1-*.sql
+
+test2-odb.?xx
+test2-odb-*.?xx
+test2.sql
+test2-*.sql
+
+test3-odb.?xx
+test3-odb-*.?xx
+test3.sql
+test3-*.sql
+
+test4-odb.?xx
+test4-odb-*.?xx
+test4.sql
+test4-*.sql
+
+test5-odb.?xx
+test5-odb-*.?xx
+test5.sql
+test5-*.sql
+
+test6-odb.?xx
+test6-odb-*.?xx
+test6.sql
+test6-*.sql
+
+test7-odb.?xx
+test7-odb-*.?xx
+test7.sql
+test7-*.sql
+
+test8-odb.?xx
+test8-odb-*.?xx
+test8.sql
+test8-*.sql
+
+test9-odb.?xx
+test9-odb-*.?xx
+test9.sql
+test9-*.sql
+
+test10-odb.?xx
+test10-odb-*.?xx
+test10.sql
+test10-*.sql
+
+test11-odb.?xx
+test11-odb-*.?xx
+test11.sql
+test11-*.sql
+
+test12-odb.?xx
+test12-odb-*.?xx
+test12.sql
+test12-*.sql
+
+test13-odb.?xx
+test13-odb-*.?xx
+test13.sql
+test13-*.sql
+
+test14-odb.?xx
+test14-odb-*.?xx
+test14.sql
+test14-*.sql
+
+test15-odb.?xx
+test15-odb-*.?xx
+test15.sql
+test15-*.sql
diff --git a/odb-tests/common/inheritance/polymorphism/buildfile b/odb-tests/common/inheritance/polymorphism/buildfile
new file mode 100644
index 0000000..40fc978
--- /dev/null
+++ b/odb-tests/common/inheritance/polymorphism/buildfile
@@ -0,0 +1,52 @@
+# file : common/inheritance/polymorphism/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+hdrs = test1 test2 test3 test4 test5 test6 test7 test8 test9 test10 test11 \
+ test12 test13 test14 test15
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+for h: $hdrs
+{
+ exe{driver}: {hxx ixx cxx}{$h-odb}
+
+ <{hxx ixx cxx}{$h-odb}>: hxx{$h} libue{test-meta}
+
+ for db: $databases
+ {
+ exe{driver}: {hxx ixx cxx}{$h-odb-$db}: include = $multi
+ <{hxx ixx cxx}{$h-odb-$db}>: hxx{$h} libue{test-meta}
+ }
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix inhrt_p_ \
+ --generate-schema \
+ --generate-query \
+ --generate-prepared
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
+
+testscript@./: schemas = $hdrs
diff --git a/odb-tests/common/inheritance/polymorphism/driver.cxx b/odb-tests/common/inheritance/polymorphism/driver.cxx
new file mode 100644
index 0000000..12f4666
--- /dev/null
+++ b/odb-tests/common/inheritance/polymorphism/driver.cxx
@@ -0,0 +1,2093 @@
+// file : common/inheritance/polymorphism/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test polymorphic object inheritance.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/session.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test1.hxx"
+#include "test2.hxx"
+#include "test3.hxx"
+#include "test4.hxx"
+#include "test5.hxx"
+#include "test6.hxx"
+#include "test7.hxx"
+#include "test8.hxx"
+#include "test9.hxx"
+#include "test10.hxx"
+#include "test11.hxx"
+#include "test12.hxx"
+#include "test13.hxx"
+#include "test14.hxx"
+#include "test15.hxx"
+
+#include "test1-odb.hxx"
+#include "test2-odb.hxx"
+#include "test3-odb.hxx"
+#include "test4-odb.hxx"
+#include "test5-odb.hxx"
+#include "test6-odb.hxx"
+#include "test7-odb.hxx"
+#include "test8-odb.hxx"
+#include "test9-odb.hxx"
+#include "test10-odb.hxx"
+#include "test11-odb.hxx"
+#include "test12-odb.hxx"
+#include "test13-odb.hxx"
+#include "test14-odb.hxx"
+#include "test15-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+const char* events[] =
+{
+ "pre_persist",
+ "post_persist",
+ "pre_load",
+ "post_load",
+ "pre_update",
+ "post_update",
+ "pre_erase",
+ "post_erase"
+};
+
+namespace test6
+{
+ void base::
+ db_callback (callback_event e, database&)
+ {
+ cout << "base " << events[e] << " " << id << endl;
+ }
+
+ void base::
+ db_callback (callback_event e, database&) const
+ {
+ cout << "base " << events[e] << " " << id << " const" << endl;
+ }
+
+ void derived::
+ db_callback (callback_event e, database&) const
+ {
+ cout << "derived " << events[e] << " " << id << " const" << endl;
+ }
+}
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Test 1: basic polymorphism functionality.
+ //
+ {
+ using namespace test1;
+
+ root r (1, 1);
+ base b (2, 2, "bbb");
+ derived d (3, 3, "ddd");
+
+ r.strs.push_back ("a");
+ r.strs.push_back ("aa");
+ r.strs.push_back ("aaa");
+
+ b.nums.push_back (21);
+ b.nums.push_back (22);
+ b.nums.push_back (23);
+ b.strs.push_back ("b");
+ b.strs.push_back ("bb");
+ b.strs.push_back ("bbb");
+
+ d.nums.push_back (31);
+ d.nums.push_back (32);
+ d.nums.push_back (33);
+ d.strs.push_back ("d");
+ d.strs.push_back ("dd");
+ d.strs.push_back ("ddd");
+
+ {
+ transaction t (db->begin ());
+
+ // Static persist.
+ //
+ db->persist (r);
+ db->persist (b);
+
+ // Dynamic persist.
+ //
+ root& r (d);
+ db->persist (r);
+
+ t.commit ();
+ }
+
+ // Static load.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<root> pr (db->load<root> (r.id));
+ unique_ptr<base> pb (db->load<base> (b.id));
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+ t.commit ();
+
+ assert (*pr == r);
+ assert (*pb == b);
+ assert (*pd == d);
+ }
+
+ // Dynamic load.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<root> pb (db->load<root> (b.id));
+ unique_ptr<root> pd1 (db->load<root> (d.id));
+ unique_ptr<base> pd2 (db->load<base> (d.id));
+ t.commit ();
+
+ assert (*pb == b);
+ assert (*pd1 == d);
+ assert (*pd2 == d);
+ }
+
+ // Invalid load.
+ //
+ {
+ transaction t (db->begin ());
+
+ try
+ {
+ unique_ptr<base> p (db->load<base> (r.id));
+ assert (false);
+ }
+ catch (const object_not_persistent&) {}
+
+ try
+ {
+ unique_ptr<derived> p (db->load<derived> (b.id));
+ assert (false);
+ }
+ catch (const object_not_persistent&) {}
+
+ t.commit ();
+ }
+
+ // Static load into existing instance.
+ //
+ {
+ transaction t (db->begin ());
+ root r1;
+ db->load (r.id, r1);
+ base b1;
+ db->load (b.id, b1);
+ derived d1;
+ db->load (d.id, d1);
+ t.commit ();
+
+ assert (r1 == r);
+ assert (b1 == b);
+ assert (d1 == d);
+ }
+
+ // Dynamic load into existing instance.
+ //
+ {
+ transaction t (db->begin ());
+ base b1;
+ db->load (b.id, static_cast<root&> (b1));
+ derived d1;
+ db->load (d.id, static_cast<base&> (d1));
+ t.commit ();
+
+ assert (b1 == b);
+ assert (d1 == d);
+ }
+
+ // Invalid load into existing instance.
+ //
+ {
+ transaction t (db->begin ());
+
+ try
+ {
+ base b;
+ db->load (r.id, static_cast<root&> (b));
+ assert (false);
+ }
+ catch (const object_not_persistent&) {}
+
+ try
+ {
+ derived d;
+ db->load (b.id, static_cast<base&> (d));
+ assert (false);
+ }
+ catch (const object_not_persistent&) {}
+
+ t.commit ();
+ }
+
+ // Slicing load.
+ //
+ {
+ transaction t (db->begin ());
+ root b1;
+ db->load (b.id, b1);
+ base d1;
+ db->load (d.id, d1);
+ t.commit ();
+
+ assert (b1 == static_cast<root> (b));
+ assert (d1 == static_cast<base> (d));
+ }
+
+ // Static reload.
+ //
+ {
+ transaction t (db->begin ());
+ root r1;
+ r1.id = r.id;
+ db->reload (r1);
+ base b1;
+ b1.id = b.id;
+ db->reload (b1);
+ derived d1;
+ d1.id = d.id;
+ db->reload (d1);
+ t.commit ();
+
+ assert (r1 == r);
+ assert (b1 == b);
+ assert (d1 == d);
+ }
+
+ // Dynamic reload.
+ //
+ {
+ transaction t (db->begin ());
+ base b1;
+ b1.id = b.id;
+ db->reload (static_cast<root&> (b1));
+ derived d1;
+ d1.id = d.id;
+ db->reload (static_cast<base&> (d1));
+ t.commit ();
+
+ assert (b1 == b);
+ assert (d1 == d);
+ }
+
+ // Invalid reload.
+ //
+ {
+ transaction t (db->begin ());
+
+ try
+ {
+ base b;
+ b.id = r.id;
+ db->reload (static_cast<root&> (b));
+ assert (false);
+ }
+ catch (const object_not_persistent&) {}
+
+ try
+ {
+ derived d;
+ d.id = b.id;
+ db->reload (static_cast<base&> (d));
+ assert (false);
+ }
+ catch (const object_not_persistent&) {}
+
+ t.commit ();
+ }
+
+ // Slicing reload.
+ //
+ {
+ transaction t (db->begin ());
+ root b1;
+ b1.id = b.id;
+ db->reload (b1);
+ base d1;
+ d1.id = d.id;
+ db->reload (d1);
+ t.commit ();
+
+ assert (b1 == static_cast<root> (b));
+ assert (d1 == static_cast<base> (d));
+ }
+
+ // Query.
+ //
+ {
+ typedef odb::query<root> root_query;
+ typedef odb::result<root> root_result;
+
+ typedef odb::query<base> base_query;
+ typedef odb::result<base> base_result;
+
+ typedef odb::result<derived> derived_result;
+
+ transaction t (db->begin ());
+
+ // Test loading via root.
+ //
+ {
+ root_result qr (db->query<root> ("ORDER BY" + root_query::id));
+ root_result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e && *i == r);
+ assert (++i != e && *i == b);
+ assert (++i != e && *i == d);
+ assert (++i == e);
+ }
+
+ // Test loading via base.
+ //
+ {
+ base_result qr (db->query<base> ("ORDER BY" + base_query::id));
+ base_result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e && *i == b);
+ assert (++i != e && *i == d);
+ assert (++i == e);
+ }
+
+ // Test loading via derived.
+ //
+ {
+ derived_result qr (db->query<derived> ());
+ derived_result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e && *i == d);
+ assert (++i == e);
+ }
+
+ // Test loading into an existing instance.
+ //
+ {
+ root_result qr (db->query<root> ());
+
+ unsigned short mask (0);
+
+ for (root_result::iterator i (qr.begin ()); i != qr.end (); ++i)
+ {
+ string ds (i.discriminator ());
+
+ if (ds == "test1::root")
+ {
+ root r1;
+ i.load (r1);
+ assert (r1 == r);
+ mask |= 1;
+ }
+ else if (ds == "test1::base")
+ {
+ base b1;
+ i.load (b1);
+ assert (b1 == b);
+ mask |= 2;
+ }
+ else if (ds == "test1::derived")
+ {
+ derived d1;
+ i.load (d1);
+ assert (d1 == d);
+ mask |= 4;
+ }
+ else
+ assert (false);
+ }
+
+ assert (mask == 7);
+ }
+
+ // Test query conditions with columns from multiple tables.
+ //
+ {
+ base_result qr (
+ db->query<base> (
+ base_query::num == 3 && base_query::str == "ddd"));
+
+ base_result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e && *i == d);
+ assert (++i == e);
+ }
+
+ // Test discriminator access.
+ //
+ {
+ base_result qr (db->query<base> (base_query::id == 3));
+ base_result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e && i.discriminator () == "test1::derived");
+ assert (++i == e);
+ }
+
+ // Test loading of an object from the same hierarchy during
+ // query result iteration (tests image copying via change
+ // callbacks in some databases).
+ //
+ {
+ base_result qr (db->query<base> ());
+
+ unsigned short mask (0);
+
+ for (base_result::iterator i (qr.begin ()); i != qr.end (); ++i)
+ {
+ string ds (i.discriminator ());
+
+ if (ds == "test1::base")
+ {
+ unique_ptr<derived> d1 (db->load<derived> (d.id));
+ assert (*d1 == d);
+ assert (*i == b);
+ mask |= 1;
+ }
+ else if (ds == "test1::derived")
+ {
+ unique_ptr<base> b1 (db->load<base> (b.id));
+ assert (*b1 == b);
+ assert (*i == d);
+ mask |= 2;
+ }
+ }
+
+ assert (mask == 3);
+ }
+
+ t.commit ();
+ }
+
+ // Views.
+ //
+ {
+ typedef odb::query<root_view> root_query;
+ typedef odb::result<root_view> root_result;
+
+ typedef odb::query<base_view> base_query;
+ typedef odb::result<base_view> base_result;
+
+ typedef odb::result<derived_view> derived_result;
+
+ transaction t (db->begin ());
+
+ // root
+ //
+ {
+ root_result qr (db->query<root_view> ("ORDER BY" + root_query::id));
+ root_result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e && i->typeid_ == "test1::root" && i->num == r.num);
+ assert (++i != e && i->typeid_ == "test1::base" && i->num == b.num);
+ assert (++i != e && i->typeid_ == "test1::derived" && i->num == d.num);
+ assert (++i == e);
+ }
+
+ // base
+ //
+ {
+ base_result qr (db->query<base_view> ("ORDER BY" + base_query::id));
+ base_result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e &&
+ i->id == b.id && i->num == b.num && i->str == b.str);
+ assert (++i != e &&
+ i->id == d.id && i->num == d.num && i->str == d.str);
+ assert (++i == e);
+ }
+
+ // derived
+ //
+ {
+ derived_result qr (db->query<derived_view> ());
+ derived_result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e &&
+ i->num == d.num && i->str == d.str &&
+ i->dnum == d.dnum && i->dstr == d.dstr);
+ assert (++i == e);
+ }
+
+ t.commit ();
+ }
+
+ // Update.
+ //
+ r.num++;
+ r.strs.push_back ("aaaa");
+
+ b.num++;
+ b.str += "b";
+ b.nums.push_back (24);
+ b.strs.push_back ("bbbb");
+
+ d.num++;
+ d.str += "d";
+ d.dnum++;
+ d.dstr += "d";
+ d.nums.push_back (34);
+ d.strs.push_back ("dddd");
+
+ {
+ transaction t (db->begin ());
+
+ // Static update.
+ //
+ db->update (r);
+ db->update (b);
+
+ // Dynamic update.
+ //
+ root& r (d);
+ db->update (r);
+
+ t.commit ();
+ }
+
+ // Verify update.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<root> pr (db->load<root> (r.id));
+ unique_ptr<base> pb (db->load<base> (b.id));
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+ t.commit ();
+
+ assert (*pr == r);
+ assert (*pb == b);
+ assert (*pd == d);
+ }
+
+ // Invalid erase via id.
+ //
+ {
+ transaction t (db->begin ());
+
+ try
+ {
+ db->erase<base> (r.id);
+ assert (false);
+ }
+ catch (const object_not_persistent&) {}
+
+ try
+ {
+ db->erase<derived> (b.id);
+ assert (false);
+ }
+ catch (const object_not_persistent&) {}
+
+ t.commit ();
+ }
+
+ // Static erase via id.
+ //
+ {
+ transaction t (db->begin ());
+ db->erase<root> (r.id);
+ db->erase<base> (b.id);
+ t.commit ();
+ }
+
+ // Dynamic erase via id.
+ //
+ {
+ transaction t (db->begin ());
+ db->erase<root> (d.id);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->persist (r);
+ db->persist (b);
+ db->persist (d);
+ t.commit ();
+ }
+
+ // Static erase via object.
+ //
+ {
+ transaction t (db->begin ());
+ db->erase (r);
+ db->erase (b);
+ t.commit ();
+ }
+
+ // Dynamic erase via object.
+ //
+ {
+ const root& r (d);
+ transaction t (db->begin ());
+ db->erase (r);
+ t.commit ();
+ }
+ }
+
+ // Test 2: inverse object pointers in polymorhic bases.
+ //
+ {
+ using namespace test2;
+
+ derived d (1, "d", 1);
+ root_pointer rp (&d);
+ base_pointer bp (&d);
+
+ {
+ transaction t (db->begin ());
+ db->persist (rp);
+ db->persist (bp);
+
+ d.rp.reset (*db, &rp);
+ d.bp.reset (*db, &bp);
+
+ db->persist (d);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+ unique_ptr<base> pb (db->load<base> (d.id));
+ unique_ptr<root> pr (db->load<root> (d.id));
+ t.commit ();
+
+ assert (pd->rp.object_id<root_pointer> () == rp.id &&
+ pd->bp.object_id<base_pointer> () == bp.id);
+
+ derived* p (dynamic_cast<derived*> (pb.get ()));
+ assert (p != 0 &&
+ p->rp.object_id<root_pointer> () == rp.id &&
+ p->bp.object_id<base_pointer> () == bp.id);
+
+ p = dynamic_cast<derived*> (pr.get ());
+ assert (p != 0 &&
+ p->rp.object_id<root_pointer> () == rp.id &&
+ p->bp.object_id<base_pointer> () == bp.id);
+ }
+
+ // Query.
+ //
+ {
+ typedef odb::query<base> base_query;
+ typedef odb::result<base> base_result;
+
+ transaction t (db->begin ());
+
+ // Test query conditions with columns in pointed-to objects from
+ // multiple tables.
+ //
+ {
+ base_result qr (
+ db->query<base> (
+ base_query::rp->id == rp.id &&
+ base_query::bp->id == bp.id));
+
+ base_result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e && i.discriminator () == "test2::derived");
+ assert (++i == e);
+ }
+
+ t.commit ();
+ }
+
+ // Views.
+ //
+ {
+ typedef odb::result<root_view> root_result;
+ typedef odb::result<base_view> base_result;
+
+ transaction t (db->begin ());
+
+ // root
+ //
+ {
+ root_result qr (db->query<root_view> ());
+ root_result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e && i->rp_id == rp.id && i->r_id == d.id);
+ assert (++i == e);
+ }
+
+ // base
+ //
+ {
+ base_result qr (db->query<base_view> ());
+ base_result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e &&
+ i->bp_id == bp.id && i->b_id == d.id && i->str == d.str);
+ assert (++i == e);
+ }
+
+ t.commit ();
+ }
+ }
+
+ // Test 3: delayed loading.
+ //
+ {
+ using namespace test3;
+
+ base b1 (21, 21);
+ base b2 (22, 22);
+ base b3 (23, 23);
+
+ derived d1 (31, 31, "d");
+ derived d2 (32, 32, "dd");
+ derived d3 (33, 33, "ddd");
+
+ b1.rptr = new root (1);
+ b2.rptr = new base (2, 2);
+ b3.rptr = new derived (3, 3, "b3");
+
+ d1.rptr = new root (4);
+ d2.rptr = new base (5, 5);
+ d3.rptr = new derived (6, 6, "d3");
+
+ d2.bptr = new base (7, 7);
+ d3.bptr = new derived (8, 8, "d3b");
+
+ {
+ transaction t (db->begin ());
+ db->persist (b1);
+ db->persist (b2);
+ db->persist (b3);
+
+ db->persist (d1);
+ db->persist (d2);
+ db->persist (d3);
+
+ db->persist (b1.rptr);
+ db->persist (b2.rptr);
+ db->persist (b3.rptr);
+
+ db->persist (d1.rptr);
+ db->persist (d2.rptr);
+ db->persist (d3.rptr);
+
+ db->persist (d2.bptr);
+ db->persist (d3.bptr);
+
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+
+ {
+ unique_ptr<base> p1 (db->load<base> (b1.id));
+ unique_ptr<base> p2 (db->load<base> (b2.id));
+ unique_ptr<root> p3 (db->load<root> (b3.id));
+ assert (*p1 == b1);
+ assert (*p2 == b2);
+ assert (*p3 == b3);
+ }
+
+ {
+ unique_ptr<derived> p1 (db->load<derived> (d1.id));
+ unique_ptr<base> p2 (db->load<base> (d2.id));
+ unique_ptr<root> p3 (db->load<root> (d3.id));
+ assert (*p1 == d1);
+ assert (*p2 == d2);
+ assert (*p3 == d3);
+ }
+
+ t.commit ();
+ }
+
+ // Query.
+ //
+ {
+ typedef odb::query<derived> derived_query;
+ typedef odb::result<derived> derived_result;
+
+ transaction t (db->begin ());
+
+ // Test query conditions with columns in pointed-to objects from
+ // multiple tables.
+ //
+ {
+ derived_result qr (
+ db->query<derived> (
+ derived_query::rptr->id == 6 &&
+ derived_query::bptr->id == 8 &&
+ derived_query::bptr->num == 8));
+
+ derived_result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e && *i == d3);
+ assert (++i == e);
+ }
+
+ t.commit ();
+ }
+
+ // Views.
+ //
+ {
+ typedef odb::query<base_view> base_query;
+ typedef odb::result<base_view> base_result;
+
+ typedef odb::query<derived_view> derived_query;
+ typedef odb::result<derived_view> derived_result;
+
+ typedef odb::query<root_view> root_query;
+ typedef odb::result<root_view> root_result;
+
+ transaction t (db->begin ());
+
+ // base
+ //
+ {
+ base_result qr (
+ db->query<base_view> (
+ base_query::base::num == b2.num &&
+ base_query::base::id == b2.id &&
+ base_query::r::id == b2.rptr->id));
+
+ base_result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e &&
+ i->b_id == b2.id &&
+ i->r_id == b2.rptr->id &&
+ i->num == b2.num);
+ assert (++i == e);
+ }
+
+ // derived
+ //
+ {
+ derived_result qr (
+ db->query<derived_view> (
+ derived_query::d::str == d3.str &&
+ derived_query::d::num == d3.num &&
+ derived_query::b::num == d3.bptr->num &&
+ derived_query::d::id == d3.id &&
+ derived_query::b::id == d3.bptr->id &&
+ derived_query::r::id == d3.rptr->id));
+
+ derived_result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e &&
+ i->d_id == d3.id &&
+ i->b_id == d3.bptr->id &&
+ i->r_id == d3.rptr->id &&
+ i->d_num == d3.num &&
+ i->b_num == d3.bptr->num &&
+ i->str == d3.str);
+ assert (++i == e);
+ }
+
+ // root
+ //
+ {
+ root_result qr (
+ db->query<root_view> (
+ root_query::r::id.in (b2.rptr->id, d2.rptr->id)));
+
+ root_result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e &&
+ i->r_id == d2.rptr->id &&
+ i->d_id == d2.id &&
+ i->str == d2.str);
+ assert (++i == e);
+ }
+
+ t.commit ();
+ }
+ }
+
+ // Test 4: views.
+ //
+ {
+ using namespace test4;
+
+ base1 b1 (21, 1);
+
+ root2 r2 (11, 0);
+ base2 b2 (21, 1, "abc");
+
+ {
+ transaction t (db->begin ());
+ db->persist (b1);
+ db->persist (r2);
+ db->persist (b2);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<view1> query;
+ typedef odb::result<view1> result;
+
+ transaction t (db->begin ());
+
+ {
+ result qr (
+ db->query<view1> (
+ query::base1::num == b1.num));
+
+ result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e && i->str == "abc");
+ assert (++i == e);
+ }
+
+ t.commit ();
+ }
+
+ {
+ typedef odb::result<view2> result;
+
+ transaction t (db->begin ());
+
+ {
+ result qr (db->query<view2> ());
+ result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e && i->min_num == 1);
+ assert (++i == e);
+ }
+
+ t.commit ();
+ }
+
+ {
+ typedef odb::result<view3> result;
+
+ transaction t (db->begin ());
+
+ {
+ result qr (db->query<view3> ());
+ result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e && i->str == "abc");
+ assert (++i == e);
+ }
+
+ t.commit ();
+ }
+ }
+
+ // Test 5: polymorphism and optimistic concurrency.
+ //
+ {
+ using namespace test5;
+
+ root r (1, 1);
+ base b (2, 2, "bbb");
+ derived d (3, 3, "ddd");
+
+ r.strs.push_back ("a");
+ r.strs.push_back ("aa");
+ r.strs.push_back ("aaa");
+
+ b.nums.push_back (21);
+ b.nums.push_back (22);
+ b.nums.push_back (23);
+ b.strs.push_back ("b");
+ b.strs.push_back ("bb");
+ b.strs.push_back ("bbb");
+
+ d.nums.push_back (31);
+ d.nums.push_back (32);
+ d.nums.push_back (33);
+ d.strs.push_back ("d");
+ d.strs.push_back ("dd");
+ d.strs.push_back ("ddd");
+
+ {
+ transaction t (db->begin ());
+ db->persist (r);
+ db->persist (b);
+ db->persist (d);
+ t.commit ();
+ }
+
+ // Update.
+ //
+ {
+ transaction t (db->begin ());
+
+ // Root.
+ //
+ {
+ unique_ptr<root> p (db->load<root> (r.id));
+
+ r.num++;
+ r.strs.push_back ("aaaa");
+ db->update (r);
+
+ p->num--;
+ p->strs.pop_back ();
+ try
+ {
+ db->update (p);
+ assert (false);
+ }
+ catch (const odb::object_changed&) {}
+
+ // Make sure the object is intact.
+ //
+ db->reload (p);
+ assert (r == *p);
+ }
+
+ // Base.
+ //
+ {
+ unique_ptr<base> p (db->load<base> (b.id));
+
+ b.num++;
+ b.str += "b";
+ b.strs.push_back ("bbbb");
+ b.nums.push_back (24);
+ db->update (b);
+
+ p->num--;
+ p->str += "B";
+ p->strs.pop_back ();
+ p->nums.pop_back ();
+ try
+ {
+ db->update (p);
+ assert (false);
+ }
+ catch (const odb::object_changed&) {}
+
+ // Make sure the object is intact.
+ //
+ db->reload (p);
+ assert (b == *p);
+ }
+
+ // Derived.
+ //
+ {
+ unique_ptr<root> p (db->load<root> (d.id)); // Via root.
+
+ d.num++;
+ d.str += "d";
+ d.strs.push_back ("dddd");
+ d.nums.push_back (24);
+ d.dnum++;
+ d.dstr += "d";
+ db->update (d);
+
+ derived& d1 (static_cast<derived&> (*p));
+ d1.num--;
+ d1.str += "D";
+ d1.strs.pop_back ();
+ d1.nums.pop_back ();
+ d1.dnum--;
+ d1.dstr += "D";
+ try
+ {
+ db->update (p);
+ assert (false);
+ }
+ catch (const odb::object_changed&) {}
+
+ // Make sure the object is intact.
+ //
+ db->reload (p);
+ assert (d == *p);
+ }
+
+ t.commit ();
+ }
+
+ // Reload.
+ //
+ {
+ transaction t (db->begin ());
+
+ // Make sure reload doesn't modify the object if the versions
+ // match.
+ //
+ derived d1 (d);
+ d1.num++;
+ d1.str += "d";
+ d1.strs.push_back ("dddd");
+ d1.nums.push_back (24);
+ d1.dnum++;
+ d1.dstr += "d";
+ derived d2 (d1);
+
+ db->reload (d1);
+ assert (d1 == d2);
+
+ t.commit ();
+ }
+
+ // Erase.
+ //
+ {
+ transaction t (db->begin ());
+
+ // Root.
+ //
+ {
+ unique_ptr<root> p (db->load<root> (r.id));
+
+ r.num++;
+ r.strs.push_back ("aaaaa");
+ db->update (r);
+
+ try
+ {
+ db->erase (p);
+ assert (false);
+ }
+ catch (const odb::object_changed&) {}
+
+ db->reload (p);
+ db->erase (p);
+ }
+
+ // Base.
+ //
+ {
+ unique_ptr<base> p (db->load<base> (b.id));
+
+ b.num++;
+ b.str += "b";
+ b.strs.push_back ("bbbb");
+ b.nums.push_back (24);
+ db->update (b);
+
+ try
+ {
+ db->erase (p);
+ assert (false);
+ }
+ catch (const odb::object_changed&) {}
+
+ db->reload (p);
+ db->erase (p);
+ }
+
+ // Derived.
+ //
+ {
+ unique_ptr<root> p (db->load<root> (d.id)); // Via root.
+
+ d.num++;
+ d.str += "d";
+ d.strs.push_back ("dddd");
+ d.nums.push_back (24);
+ d.dnum++;
+ d.dstr += "d";
+ db->update (d);
+
+ try
+ {
+ db->erase (p);
+ assert (false);
+ }
+ catch (const odb::object_changed&) {}
+
+ db->reload (p);
+ db->erase (p);
+ }
+
+ // Try to update non-existent object.
+ //
+ {
+ try
+ {
+ db->update (d);
+ assert (false);
+ }
+ catch (const odb::object_changed&) {}
+ }
+
+ // Try to erase non-existent object.
+ //
+ {
+ try
+ {
+ db->erase (d);
+ assert (false);
+ }
+ catch (const odb::object_changed&) {}
+ }
+
+ t.commit ();
+ }
+ }
+
+ // Test 6: polymorphism and callbacks.
+ //
+ {
+ using namespace test6;
+
+ base b (1, 1, "bbb");
+
+ unique_ptr<base> d (new derived (2, 2, "ddd"));
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (b);
+ db->persist (d);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+
+ unique_ptr<base> pb (db->load<base> (b.id));
+ unique_ptr<root> pd (db->load<root> (d->id));
+
+ db->load (b.id, *pb);
+ db->load (d->id, *pd);
+
+ db->reload (*pb);
+ db->reload (*pd);
+
+ t.commit ();
+ }
+
+ // Update.
+ //
+ {
+ b.num++;
+ d->num++;
+
+ transaction t (db->begin ());
+ db->update (b);
+ db->update (d);
+ t.commit ();
+ }
+
+ // Query.
+ //
+ {
+ typedef odb::query<base> query;
+ typedef odb::result<base> result;
+
+ transaction t (db->begin ());
+
+ result r (db->query<base> ("ORDER BY" + query::id));
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ *i;
+
+ t.commit ();
+ }
+
+ // Erase.
+ //
+ {
+ transaction t (db->begin ());
+ db->erase (b);
+ db->erase (d);
+ t.commit ();
+ }
+
+ // Recursive (delayed) loading.
+ //
+ {
+ derived d (3, 3, "dddd");
+ d.ptr.reset (new derived (4, 4, "ddddd"));
+
+ {
+ transaction t (db->begin ());
+ db->persist (d);
+ db->persist (d.ptr);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+
+ unique_ptr<root> p (db->load<root> (d.id));
+ t.commit ();
+ }
+ }
+ }
+
+ // Test 7: polymorphism and object cache (session).
+ //
+ {
+ using namespace test7;
+
+ shared_ptr<root> r (new root (1, 1));
+ shared_ptr<base> b (new base (2, 2, "b"));
+ shared_ptr<root> d (new derived (3, 3, "d"));
+
+ // Persist.
+ //
+ {
+ session s;
+
+ {
+ transaction t (db->begin ());
+ db->persist (r);
+ db->persist (b);
+ db->persist (d);
+ t.commit ();
+ }
+
+ assert (db->load<root> (r->id) == r);
+ assert (db->load<base> (b->id) == b);
+ assert (db->load<root> (d->id) == d);
+ }
+
+ // Load.
+ //
+ {
+ session s;
+
+ transaction t (db->begin ());
+ shared_ptr<root> r1 (db->load<root> (r->id));
+ shared_ptr<base> b1 (db->load<base> (b->id));
+ shared_ptr<derived> d1 (db->load<derived> (d->id));
+ t.commit ();
+
+ assert (db->load<root> (r->id) == r1);
+ assert (db->load<base> (b->id) == b1);
+ assert (db->load<root> (d->id) == d1);
+
+ assert (!db->find<derived> (b->id));
+ }
+
+ // Query.
+ //
+ {
+ typedef odb::query<root> query;
+ typedef odb::result<root> result;
+
+ session s;
+
+ transaction t (db->begin ());
+ shared_ptr<root> r1 (db->load<root> (r->id));
+ shared_ptr<base> b1 (db->load<base> (b->id));
+ shared_ptr<derived> d1 (db->load<derived> (d->id));
+ t.commit ();
+
+ {
+ transaction t (db->begin ());
+
+ result r (db->query<root> ("ORDER BY" + query::id));
+ result::iterator i (r.begin ()), e (r.end ());
+
+ assert (i != e && i.load () == r1);
+ assert (++i != e && i.load () == b1);
+ assert (++i != e && i.load () == d1);
+ assert (++i == e);
+
+ t.commit ();
+ }
+ }
+
+ // Erase.
+ //
+ {
+ session s;
+
+ {
+ transaction t (db->begin ());
+ db->load<root> (r->id);
+ db->load<root> (b->id);
+ db->load<root> (d->id);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (r);
+ db->erase (b);
+ db->erase (d);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ assert (!db->find<root> (r->id));
+ assert (!db->find<base> (b->id));
+ assert (!db->find<root> (d->id));
+ t.commit ();
+ }
+ }
+ }
+
+ // Test 8: polymorphism and abstract bases.
+ //
+ {
+ using namespace test8;
+
+ base b (1, 1, "b");
+ interm i (2, 2, "i", true);
+ derived1 d1 (3, 3, "d1", true);
+ derived2 d2 (4, 4, "d2", false);
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (b);
+ db->persist (static_cast<root&> (d1));
+ db->persist (static_cast<interm&> (d2));
+
+ try
+ {
+ db->persist (i);
+ assert (false);
+ }
+ catch (const odb::abstract_class&) {}
+
+ try
+ {
+ db->persist (static_cast<base&> (i));
+ assert (false);
+ }
+ catch (const odb::no_type_info&) {}
+
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ base vb;
+ interm vi;
+ derived1 vd1;
+ derived2 vd2;
+
+ transaction t (db->begin ());
+
+ // load (id)
+ //
+ unique_ptr<root> pb (db->load<root> (b.id));
+ unique_ptr<interm> pd1 (db->load<interm> (d1.id));
+ unique_ptr<derived2> pd2 (db->load<derived2> (d2.id));
+
+ assert (*pb == b);
+ assert (*pd1 == d1);
+ assert (*pd2 == d2);
+
+ // load (id, obj)
+ //
+ db->load (b.id, static_cast<root&> (vb));
+ db->load (d1.id, static_cast<base&> (vd1));
+ db->load (d2.id, static_cast<interm&> (vd2));
+
+ assert (vb == b);
+ assert (vd1 == d1);
+ assert (vd2 == d2);
+
+ try
+ {
+ db->load (i.id, static_cast<root&> (vi));
+ assert (false);
+ }
+ catch (const odb::no_type_info&) {}
+
+ // reload (obj)
+ //
+ vb.num = 0;
+ vd1.num = 0;
+ vd2.num = 0;
+
+ db->reload (static_cast<root&> (vb));
+ db->reload (static_cast<base&> (vd1));
+ db->reload (static_cast<interm&> (vd2));
+
+ assert (vb == b);
+ assert (vd1 == d1);
+ assert (vd2 == d2);
+
+ try
+ {
+ db->reload (static_cast<root&> (vi));
+ assert (false);
+ }
+ catch (const odb::no_type_info&) {}
+
+ t.commit ();
+ }
+
+ // Update.
+ //
+ {
+ b.num++;
+ b.str += 'b';
+ d1.num++;
+ d1.str += "d1";
+ d2.num++;
+ d2.str += "d1";
+
+ {
+ transaction t (db->begin ());
+ db->update (static_cast<root&> (b));
+ db->update (d1);
+ db->update (static_cast<interm&> (d2));
+
+ try
+ {
+ db->update (i);
+ assert (false);
+ }
+ catch (const odb::abstract_class&) {}
+
+ try
+ {
+ db->update (static_cast<base&> (i));
+ assert (false);
+ }
+ catch (const odb::no_type_info&) {}
+
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+
+ unique_ptr<base> pb (db->load<base> (b.id));
+ unique_ptr<root> pd1 (db->load<root> (d1.id));
+ unique_ptr<base> pd2 (db->load<base> (d2.id));
+
+ t.commit ();
+
+ assert (*pb == b);
+ assert (*pd1 == d1);
+ assert (*pd2 == d2);
+ }
+ }
+
+ // Erase.
+ //
+ {
+ transaction t (db->begin ());
+ db->erase (b);
+ db->erase<interm> (d1.id);
+ db->erase (static_cast<root&> (d2));
+
+ try
+ {
+ db->erase (i);
+ assert (false);
+ }
+ catch (const odb::abstract_class&) {}
+
+ try
+ {
+ db->erase (static_cast<base&> (i));
+ assert (false);
+ }
+ catch (const odb::no_type_info&) {}
+
+ t.commit ();
+ }
+ }
+
+ // Test 9: polymorphism and readonly classes.
+ //
+ {
+ using namespace test9;
+
+ ro_root ro_r (1, 1);
+ rw_base rw_b (2, 2, "b");
+ ro_derived ro_d (3, 3, "d");
+
+ rw_root rw_r (1, 1);
+ ro_base ro_b (2, 2, "b");
+ rw_derived rw_d (3, 3, "d");
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (ro_r);
+ db->persist (rw_b);
+ db->persist (ro_d);
+
+ db->persist (rw_r);
+ db->persist (ro_b);
+ db->persist (rw_d);
+ t.commit ();
+ }
+
+ // Update.
+ //
+ {
+ ro_root ro_r1 (ro_r);
+ rw_base rw_b1 (rw_b);
+ ro_derived ro_d1 (ro_d);
+
+ ro_base ro_b1 (ro_b);
+ rw_derived rw_d1 (rw_d);
+
+ ro_r1.num++;
+ ro_r1.strs.push_back ("b");
+
+ rw_b1.num++;
+ rw_b1.strs.push_back ("b");
+ rw_b1.str += "b";
+ rw_b1.nums.push_back (2);
+ rw_b.str += "b";
+ rw_b.nums.push_back (2);
+
+ ro_d1.num++;
+ ro_d1.strs.push_back ("d");
+ ro_d1.str += "d";
+ ro_d1.nums.push_back (3);
+ ro_d1.dnum++;
+ ro_d1.dstr += "d";
+
+ rw_r.num++;
+ rw_r.strs.push_back ("b");
+
+ ro_b1.num++;
+ ro_b1.strs.push_back ("b");
+ ro_b1.str += "b";
+ ro_b1.nums.push_back (2);
+
+ rw_d1.num++;
+ rw_d1.strs.push_back ("d");
+ rw_d1.str += "d";
+ rw_d1.nums.push_back (3);
+ rw_d1.dnum++;
+ rw_d1.dstr += "d";
+ rw_d.dnum++;
+ rw_d.dstr += "d";
+
+ {
+ // These should be no-ops.
+ //
+ db->update (ro_r1);
+ db->update (static_cast<ro_root&> (ro_d1));
+ db->update (ro_b1);
+
+ transaction t (db->begin ());
+ db->update (static_cast<ro_root&> (rw_b1));
+ db->update (rw_r);
+ db->update (static_cast<ro_base&> (rw_d1));
+ t.commit ();
+ }
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+
+ unique_ptr<ro_root> p_ro_r (db->load<ro_root> (ro_r.id));
+ unique_ptr<ro_root> p_rw_b (db->load<ro_root> (rw_b.id));
+ unique_ptr<ro_root> p_ro_d (db->load<ro_root> (ro_d.id));
+
+ unique_ptr<rw_root> p_rw_r (db->load<rw_root> (rw_r.id));
+ unique_ptr<rw_root> p_ro_b (db->load<rw_root> (ro_b.id));
+ unique_ptr<rw_root> p_rw_d (db->load<rw_root> (rw_d.id));
+
+ t.commit ();
+
+ assert (*p_ro_r == ro_r);
+ assert (*p_rw_b == rw_b);
+ assert (*p_ro_d == ro_d);
+
+ assert (*p_rw_r == rw_r);
+ assert (*p_ro_b == ro_b);
+ assert (*p_rw_d == rw_d);
+ }
+ }
+
+ // Test 10: empty polymorphic classes.
+ //
+ {
+ using namespace test10;
+
+ base b (1, 1);
+ derived d (2, 2);
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (b);
+ db->persist (static_cast<root&> (d));
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+
+ unique_ptr<root> pb (db->load<root> (b.id));
+ unique_ptr<root> pd (db->load<root> (d.id));
+ t.commit ();
+
+ assert (*pb == b);
+ assert (*pd == d);
+ }
+
+ // Update.
+ //
+ {
+ b.num++;
+ d.num++;
+
+ transaction t (db->begin ());
+ db->update (static_cast<root&> (b));
+ db->update (d);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+
+ unique_ptr<root> pb (db->load<root> (b.id));
+ unique_ptr<root> pd (db->load<root> (d.id));
+ t.commit ();
+
+ assert (*pb == b);
+ assert (*pd == d);
+ }
+ }
+
+ // Test 11: reuse and polymorphic inheritance.
+ //
+ {
+ using namespace test11;
+
+ base b (1, 1, "b");
+ derived d (2, 2, "d");
+
+ b.strs.push_back ("b");
+ b.nums.push_back (1);
+
+ d.strs.push_back ("d");
+ d.nums.push_back (1);
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (b);
+ db->persist (static_cast<base&> (d));
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+
+ unique_ptr<base> pb (db->load<base> (b.id));
+ unique_ptr<base> pd (db->load<base> (d.id));
+ t.commit ();
+
+ assert (*pb == b);
+ assert (*pd == d);
+ }
+
+ // Update.
+ //
+ {
+ b.num++;
+ b.str += "b";
+ b.strs.push_back ("bb");
+ b.nums.push_back (2);
+
+ d.num++;
+ d.str += "d";
+ d.strs.push_back ("dd");
+ d.nums.push_back (2);
+ d.dnum++;
+ d.dstr += "d";
+
+ transaction t (db->begin ());
+ db->update (b);
+ db->update (d);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+
+ unique_ptr<base> pb (db->load<base> (b.id));
+ unique_ptr<base> pd (db->load<base> (d.id));
+
+ t.commit ();
+
+ assert (*pb == b);
+ assert (*pd == d);
+ }
+
+ // Query.
+ //
+ {
+ typedef odb::query<base> base_query;
+ typedef odb::result<base> base_result;
+
+ typedef odb::query<derived> derived_query;
+ typedef odb::result<derived> derived_result;
+
+ transaction t (db->begin ());
+
+ {
+ base_result qr (db->query<base> (base_query::num == 2));
+ base_result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e && *i == b);
+ assert (++i == e);
+ }
+
+ {
+ derived_result qr (db->query<derived> (derived_query::num == 3));
+ derived_result::iterator i (qr.begin ()), e (qr.end ());
+
+ assert (i != e && *i == d);
+ assert (++i == e);
+ }
+
+ t.commit ();
+ }
+ }
+
+ // Test 12: polymorphic objects with auto id.
+ //
+ {
+ using namespace test12;
+
+ base b (1);
+ derived d (2);
+
+ unsigned long id1, id2;
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ id1 = db->persist (b);
+ id2 = db->persist (static_cast<root&> (d));
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+
+ unique_ptr<root> pb (db->load<root> (id1));
+ unique_ptr<root> pd (db->load<root> (id2));
+
+ t.commit ();
+
+ assert (*pb == b);
+ assert (*pd == d);
+ }
+ }
+
+ // Test 13: polymorphic derived without any non-container data members
+ // (which results in an empty SELECT statement).
+ //
+ {
+ using namespace test13;
+
+ base b;
+ b.nums.push_back (123);
+ derived d;
+ d.nums.push_back (123);
+ d.strs.push_back ("abc");
+
+ base1 b1;
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (b);
+ db->persist (d);
+ db->persist (b1);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+
+ unique_ptr<root> pbr (db->load<root> (b.id));
+ unique_ptr<root> pdr (db->load<root> (d.id));
+ unique_ptr<base> pdb (db->load<base> (d.id));
+ unique_ptr<root> pb1r (db->load<root> (b1.id));
+ t.commit ();
+
+ base& rb (static_cast<base&> (*pbr));
+ derived& rd1 (static_cast<derived&> (*pdr));
+ derived& rd2 (static_cast<derived&> (*pdb));
+ base1 rb1 (static_cast<base1&> (*pb1r));
+
+ assert (rb.id == b.id && rb.nums == b.nums);
+ assert (rd1.id == d.id && rd1.nums == rd1.nums &&
+ rd1.strs == rd1.strs);
+ assert (rd2.id == d.id && rd2.nums == rd2.nums &&
+ rd2.strs == rd2.strs);
+ assert (rb1.id == b1.id);
+ }
+ }
+
+ // Test 14: inverse pointer in polymorphic base.
+ //
+ {
+ using namespace test14;
+
+ derived d;
+ d.num = 123;
+
+ d.o1 = new object1;
+ d.o2 = new object2;
+ d.o3.push_back (new object3);
+ d.o4.push_back (new object4);
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (d.o1);
+ db->persist (d.o2);
+ db->persist (d.o3[0]);
+ db->persist (d.o4[0]);
+ db->persist (d);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ session s;
+
+ transaction t (db->begin ());
+ object1* p1 (db->load<object1> (d.o1->id));
+ object2* p2 (db->load<object2> (d.o2->id));
+ object3* p3 (db->load<object3> (d.o3[0]->id));
+ object4* p4 (db->load<object4> (d.o4[0]->id));
+ t.commit ();
+
+ assert (p1->d->num == d.num);
+ assert (p2->d[0]->num == d.num);
+ assert (p3->d[0]->num == d.num);
+ assert (p4->d->num == d.num);
+ delete p1->d;
+ }
+
+ // Query.
+ //
+ {
+ typedef odb::query<object1> query;
+ typedef odb::result<object1> result;
+
+ session s;
+ transaction t (db->begin ());
+
+ result r (db->query<object1> (query::d->num == d.num));
+ result::iterator i (r.begin ()), e (r.end ());
+
+ assert (i != e && i->d->num == d.num);
+ delete i.load ()->d;
+ assert (++i == e);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object4> query;
+ typedef odb::result<object4> result;
+
+ session s;
+ transaction t (db->begin ());
+
+ result r (db->query<object4> (query::d->num == d.num));
+ result::iterator i (r.begin ()), e (r.end ());
+
+ assert (i != e && i->d->num == d.num);
+ delete i.load ()->d;
+ assert (++i == e);
+ t.commit ();
+ }
+ }
+
+ // Test 15: LOB/long data and polymorphism.
+ //
+ {
+ using namespace test15;
+
+ const char data[] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B";
+
+ derived d;
+ d.blob.assign (data, data + sizeof (data));
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ base* b (&d);
+ db->persist (b);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+
+ unique_ptr<base> pb (db->load<base> (d.id));
+ t.commit ();
+
+ derived* pd (dynamic_cast<derived*> (pb.get ()));
+ assert (pd != 0 && pd->blob == d.blob);
+ }
+
+ // Query.
+ //
+ {
+ typedef odb::result<base> result;
+
+ transaction t (db->begin ());
+
+ result r (db->query<base> ());
+ result::iterator i (r.begin ()), e (r.end ());
+
+ assert (i != e);
+
+ derived* pd (dynamic_cast<derived*> (&*i));
+ assert (pd != 0 && pd->blob == d.blob);
+
+ assert (++i == e);
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/inheritance/polymorphism/test1.hxx b/odb-tests/common/inheritance/polymorphism/test1.hxx
new file mode 100644
index 0000000..7f598de
--- /dev/null
+++ b/odb-tests/common/inheritance/polymorphism/test1.hxx
@@ -0,0 +1,115 @@
+// file : common/inheritance/polymorphism/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+#include <string>
+#include <vector>
+#include <typeinfo>
+
+#include <odb/core.hxx>
+
+// Test basic polymorphism functionality.
+//
+#pragma db namespace table("t1_")
+namespace test1
+{
+ #pragma db object polymorphic
+ struct root
+ {
+ virtual ~root () {}
+ root () {}
+ root (unsigned long i, unsigned long n): id (i), num (n) {}
+
+ #pragma db id column("object_id")
+ unsigned long id;
+
+ unsigned long num;
+ std::vector<std::string> strs;
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (root))
+ return false;
+
+ return id == r.id && num == r.num && strs == r.strs;
+ }
+ };
+
+ inline bool
+ operator== (const root& x, const root& y) {return x.compare (y);}
+
+ #pragma db object
+ struct base: root
+ {
+ base () {}
+ base (unsigned long i, unsigned long n, const std::string& s)
+ : root (i, n), str (s) {}
+
+ std::string str;
+ std::vector<unsigned long> nums;
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (base))
+ return false;
+
+ const base& b (static_cast<const base&> (r));
+ return root::compare (r, false) && str == b.str && nums == b.nums;
+ }
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ derived () {}
+ derived (unsigned long i, unsigned long n, const std::string& s)
+ : base (i, n, s), dnum (n + 1), dstr (s + 'd') {}
+
+ unsigned long dnum;
+ std::string dstr;
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (derived))
+ return false;
+
+ const derived& d (static_cast<const derived&> (r));
+ return base::compare (r, false) && dnum == d.dnum && dstr == d.dstr;
+ }
+ };
+
+ // Views.
+ //
+ #pragma db view object(root)
+ struct root_view
+ {
+ //#pragma db column(root::typeid_)
+ std::string typeid_; // @@ tmp
+
+ unsigned long num;
+ };
+
+ #pragma db view object(base = b)
+ struct base_view
+ {
+ unsigned long id;
+ unsigned long num;
+ std::string str;
+ };
+
+ #pragma db view object(derived)
+ struct derived_view
+ {
+ unsigned long num;
+ std::string str;
+ unsigned long dnum;
+ std::string dstr;
+ };
+}
+
+#endif // TEST1_HXX
diff --git a/odb-tests/common/inheritance/polymorphism/test10.hxx b/odb-tests/common/inheritance/polymorphism/test10.hxx
new file mode 100644
index 0000000..63673a1
--- /dev/null
+++ b/odb-tests/common/inheritance/polymorphism/test10.hxx
@@ -0,0 +1,78 @@
+// file : common/inheritance/polymorphism/test10.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST10_HXX
+#define TEST10_HXX
+
+#include <typeinfo>
+
+#include <odb/core.hxx>
+
+// Test empty polymorphic classes.
+//
+#pragma db namespace table("t10_")
+namespace test10
+{
+ #pragma db object polymorphic
+ struct root
+ {
+ virtual ~root () = 0; // Auto-abstract.
+ root () {}
+ root (unsigned long i): id (i) {}
+
+ #pragma db id
+ unsigned long id;
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (root))
+ return false;
+
+ return id == r.id;
+ }
+ };
+
+ inline root::
+ ~root () {}
+
+ inline bool
+ operator== (const root& x, const root& y) {return x.compare (y);}
+
+ #pragma db object
+ struct base: root
+ {
+ base () {}
+ base (unsigned long i, unsigned long n): root (i), num (n) {}
+
+ unsigned long num;
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (base))
+ return false;
+
+ const base& b (static_cast<const base&> (r));
+ return root::compare (r, false) && num == b.num;
+ }
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ derived () {}
+ derived (unsigned long i, unsigned long n): base (i, n) {}
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (derived))
+ return false;
+
+ return base::compare (r, false);
+ }
+ };
+}
+
+#endif // TEST10_HXX
diff --git a/odb-tests/common/inheritance/polymorphism/test11.hxx b/odb-tests/common/inheritance/polymorphism/test11.hxx
new file mode 100644
index 0000000..2d38a6c
--- /dev/null
+++ b/odb-tests/common/inheritance/polymorphism/test11.hxx
@@ -0,0 +1,78 @@
+// file : common/inheritance/polymorphism/test11.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST11_HXX
+#define TEST11_HXX
+
+#include <string>
+#include <vector>
+#include <typeinfo>
+
+#include <odb/core.hxx>
+
+// Test mixing reuse and polymorphic inheritance.
+//
+#pragma db namespace table("t11_")
+namespace test11
+{
+ #pragma db object abstract
+ struct root
+ {
+ root () {}
+ root (unsigned long i, unsigned long n): id (i), num (n) {}
+
+ #pragma db id
+ unsigned long id;
+
+ unsigned long num;
+ std::vector<std::string> strs;
+ };
+
+ #pragma db object polymorphic
+ struct base: root
+ {
+ virtual ~base () {}
+ base () {}
+ base (unsigned long i, unsigned long n, const std::string& s)
+ : root (i, n), str (s) {}
+
+ std::string str;
+ std::vector<unsigned long> nums;
+
+ virtual bool
+ compare (const base& b, bool tc = true) const
+ {
+ if (tc && typeid (b) != typeid (base))
+ return false;
+
+ return id == b.id && num == b.num && strs == b.strs &&
+ str == b.str && nums == b.nums;
+ }
+ };
+
+ inline bool
+ operator== (const base& x, const base& y) {return x.compare (y);}
+
+ #pragma db object
+ struct derived: base
+ {
+ derived () {}
+ derived (unsigned long i, unsigned long n, const std::string& s)
+ : base (i, n, s), dnum (n + 1), dstr (s + 'd') {}
+
+ unsigned long dnum;
+ std::string dstr;
+
+ virtual bool
+ compare (const base& b, bool tc = true) const
+ {
+ if (tc && typeid (b) != typeid (derived))
+ return false;
+
+ const derived& d (static_cast<const derived&> (b));
+ return base::compare (b, false) && dnum == d.dnum && dstr == d.dstr;
+ }
+ };
+}
+
+#endif // TEST11_HXX
diff --git a/odb-tests/common/inheritance/polymorphism/test12.hxx b/odb-tests/common/inheritance/polymorphism/test12.hxx
new file mode 100644
index 0000000..85000ac
--- /dev/null
+++ b/odb-tests/common/inheritance/polymorphism/test12.hxx
@@ -0,0 +1,79 @@
+// file : common/inheritance/polymorphism/test12.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST12_HXX
+#define TEST12_HXX
+
+#include <typeinfo>
+
+#include <odb/core.hxx>
+
+// Test polymorphic classes with private auto id.
+//
+#pragma db namespace table("t12_")
+namespace test12
+{
+ #pragma db object polymorphic
+ struct root
+ {
+ virtual ~root () = 0; // Auto-abstract.
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (root))
+ return false;
+
+ return id_ == r.id_;
+ }
+
+ unsigned long id () const {return id_;}
+ void id (unsigned long id) {id_ = id;}
+ private:
+ #pragma db id auto access(id)
+ unsigned long id_;
+ };
+
+ inline root::
+ ~root () {}
+
+ inline bool
+ operator== (const root& x, const root& y) {return x.compare (y);}
+
+ #pragma db object
+ struct base: root
+ {
+ base () {}
+ base (unsigned long n): num (n) {}
+
+ unsigned long num;
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (base))
+ return false;
+
+ const base& b (static_cast<const base&> (r));
+ return root::compare (r, false) && num == b.num;
+ }
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ derived () {}
+ derived (unsigned long n): base (n) {}
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (derived))
+ return false;
+
+ return base::compare (r, false);
+ }
+ };
+}
+
+#endif // TEST12_HXX
diff --git a/odb-tests/common/inheritance/polymorphism/test13.hxx b/odb-tests/common/inheritance/polymorphism/test13.hxx
new file mode 100644
index 0000000..3240a9a
--- /dev/null
+++ b/odb-tests/common/inheritance/polymorphism/test13.hxx
@@ -0,0 +1,46 @@
+// file : common/inheritance/polymorphism/test13.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST13_HXX
+#define TEST13_HXX
+
+#include <string>
+#include <vector>
+
+#include <odb/core.hxx>
+
+// Test polymorphic derived without any non-container data members (which
+// results in an empty SELECT statement).
+//
+#pragma db namespace table("t13_")
+namespace test13
+{
+ #pragma db object polymorphic
+ struct root
+ {
+ virtual ~root () {}
+
+ #pragma db id auto
+ unsigned long id;
+ };
+
+ #pragma db object
+ struct base: root
+ {
+ std::vector<int> nums;
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ std::vector<std::string> strs;
+ };
+
+ #pragma db object
+ struct base1: root
+ {
+ // Nothing.
+ };
+}
+
+#endif // TEST13_HXX
diff --git a/odb-tests/common/inheritance/polymorphism/test14.hxx b/odb-tests/common/inheritance/polymorphism/test14.hxx
new file mode 100644
index 0000000..1050861
--- /dev/null
+++ b/odb-tests/common/inheritance/polymorphism/test14.hxx
@@ -0,0 +1,99 @@
+// file : common/inheritance/polymorphism/test14.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST14_HXX
+#define TEST14_HXX
+
+#include <vector>
+
+#include <odb/core.hxx>
+
+// Test inverse pointer in polymorphic base.
+//
+#pragma db namespace table("t14_")
+namespace test14
+{
+ struct object1;
+ struct object2;
+ struct object3;
+ struct object4;
+
+ #pragma db object polymorphic session
+ struct base
+ {
+ virtual ~base ();
+
+ #pragma db id auto
+ unsigned long id;
+
+ object1* o1;
+ object2* o2;
+ std::vector<object3*> o3;
+ std::vector<object4*> o4;
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ unsigned long num;
+ };
+
+ // one-to-one(i)
+ //
+ #pragma db object session
+ struct object1
+ {
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db inverse(o1)
+ derived* d;
+ };
+
+ // one-to-many(i)
+ //
+ #pragma db object session
+ struct object2
+ {
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db inverse(o2)
+ std::vector<derived*> d;
+ };
+
+ // many-to-many(i)
+ //
+ #pragma db object session
+ struct object3
+ {
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db inverse(o3)
+ std::vector<derived*> d;
+ };
+
+ // many-to-one(i)
+ //
+ #pragma db object session
+ struct object4
+ {
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db inverse(o4)
+ derived* d;
+ };
+
+ inline base::
+ ~base ()
+ {
+ delete o1;
+ delete o2;
+ delete o3[0];
+ delete o4[0];
+ }
+}
+
+#endif // TEST14_HXX
diff --git a/odb-tests/common/inheritance/polymorphism/test15.hxx b/odb-tests/common/inheritance/polymorphism/test15.hxx
new file mode 100644
index 0000000..5799ace
--- /dev/null
+++ b/odb-tests/common/inheritance/polymorphism/test15.hxx
@@ -0,0 +1,44 @@
+// file : common/inheritance/polymorphism/test15.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST15_HXX
+#define TEST15_HXX
+
+#include <vector>
+
+#include <odb/core.hxx>
+
+#ifdef ODB_COMPILER
+# if defined(ODB_DATABASE_PGSQL)
+# define BLOB_TYPE "BYTEA"
+# elif defined(ODB_DATABASE_MSSQL)
+# define BLOB_TYPE "VARBINARY(max)"
+# else
+# define BLOB_TYPE "BLOB"
+# endif
+#endif
+
+
+// Test LOB/long data and polymorphism.
+//
+#pragma db namespace table("t15_")
+namespace test15
+{
+ #pragma db object polymorphic
+ struct base
+ {
+ virtual ~base () {}
+
+ #pragma db id auto
+ unsigned long id;
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ #pragma db type(BLOB_TYPE)
+ std::vector<char> blob;
+ };
+}
+
+#endif // TEST15_HXX
diff --git a/odb-tests/common/inheritance/polymorphism/test2.hxx b/odb-tests/common/inheritance/polymorphism/test2.hxx
new file mode 100644
index 0000000..9890e02
--- /dev/null
+++ b/odb-tests/common/inheritance/polymorphism/test2.hxx
@@ -0,0 +1,105 @@
+// file : common/inheritance/polymorphism/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+#include <string>
+#include <vector>
+
+#include <odb/core.hxx>
+#include <odb/lazy-ptr.hxx>
+
+// Test inverse object pointers in polymorhic bases.
+//
+#pragma db namespace table("t2_")
+namespace test2
+{
+ struct root;
+
+ #pragma db object
+ struct root_pointer
+ {
+ root_pointer (root* r = 0): p (r) {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ root* p;
+ };
+
+ #pragma db object polymorphic
+ struct root
+ {
+ virtual ~root () {}
+ root () {}
+ root (unsigned long i): id (i) {}
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db inverse(p)
+ odb::lazy_ptr<root_pointer> rp;
+ };
+
+ struct base;
+
+ #pragma db object
+ struct base_pointer
+ {
+ base_pointer (base* b = 0) {if (b != 0) vp.push_back (b);}
+
+ #pragma db id auto
+ unsigned long id;
+
+ std::vector<base*> vp;
+ };
+
+ #pragma db object
+ struct base: root
+ {
+ base () {}
+ base (unsigned long i, const std::string& s): root (i), str (s) {}
+
+ std::string str;
+
+ #pragma db inverse(vp)
+ odb::lazy_ptr<base_pointer> bp;
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ derived () {}
+ derived (unsigned long i, const std::string& s, unsigned long n)
+ : base (i, s), num (n) {}
+
+ unsigned long num;
+ };
+
+ // Views.
+ //
+ #pragma db view object(root_pointer = rp) object(root)
+ struct root_view
+ {
+ #pragma db column(rp::id)
+ unsigned long rp_id;
+
+ #pragma db column(root::id)
+ unsigned long r_id;
+ };
+
+ #pragma db view object(base_pointer) object(base = b)
+ struct base_view
+ {
+ #pragma db column(base_pointer::id)
+ unsigned long bp_id;
+
+ #pragma db column(b::id)
+ unsigned long b_id;
+
+ std::string str;
+ };
+}
+
+#endif // TEST2_HXX
diff --git a/odb-tests/common/inheritance/polymorphism/test3.hxx b/odb-tests/common/inheritance/polymorphism/test3.hxx
new file mode 100644
index 0000000..fd68f24
--- /dev/null
+++ b/odb-tests/common/inheritance/polymorphism/test3.hxx
@@ -0,0 +1,146 @@
+// file : common/inheritance/polymorphism/test3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST3_HXX
+#define TEST3_HXX
+
+#include <string>
+#include <typeinfo>
+
+#include <odb/core.hxx>
+
+// Test delayed loading.
+//
+#pragma db namespace table("t3_")
+namespace test3
+{
+ #pragma db object polymorphic
+ struct root
+ {
+ virtual ~root () {}
+ root () {}
+ root (unsigned long i): id (i) {}
+
+ #pragma db id
+ unsigned long id;
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (root))
+ return false;
+
+ return id == r.id;
+ }
+ };
+
+ inline bool
+ operator== (const root& x, const root& y) {return x.compare (y);}
+
+ #pragma db object
+ struct base: root
+ {
+ virtual ~base () {delete rptr;}
+ base (): rptr (0) {}
+ base (unsigned long i, unsigned long n): root (i), num (n), rptr (0) {}
+
+ unsigned long num;
+ root* rptr;
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (base))
+ return false;
+
+ const base& b (static_cast<const base&> (r));
+ return
+ root::compare (r, false) &&
+ num == b.num &&
+ ((rptr == 0 && b.rptr == 0) || rptr->compare (*b.rptr));
+ }
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ virtual ~derived () {delete bptr;}
+ derived (): bptr (0) {}
+ derived (unsigned long i, unsigned long n, const std::string& s)
+ : base (i, n), str (s), bptr (0) {}
+
+ std::string str;
+ base* bptr;
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (derived))
+ return false;
+
+ const derived& d (static_cast<const derived&> (r));
+ return
+ base::compare (r, false) &&
+ str == d.str &&
+ ((bptr == 0 && d.bptr == 0) || bptr->compare (*d.bptr));
+ }
+ };
+
+ // Views.
+ //
+ #pragma db view object(base) object(root = r)
+ struct base_view
+ {
+ #pragma db column(base::id)
+ unsigned long b_id;
+
+ #pragma db column(r::id)
+ unsigned long r_id;
+
+ unsigned long num;
+ };
+
+ #pragma db view \
+ object(derived = d) \
+ object(base = b) \
+ object(root = r: d::rptr)
+ struct derived_view
+ {
+ #pragma db column(d::id)
+ unsigned long d_id;
+
+ #pragma db column(b::id)
+ unsigned long b_id;
+
+ #pragma db column(r::id)
+ unsigned long r_id;
+
+ #pragma db column(d::num)
+ unsigned long d_num;
+
+ #pragma db column(b::num)
+ unsigned long b_num;
+
+ std::string str;
+ };
+
+ // This is an example of a pathological case, where the right-hand-side
+ // of the join condition comes from one of the bases. As a result, we
+ // join the base table first, which means we will get both bases and
+ // derived objects instead of just derived.
+ //
+ //#pragma db view object(root = r) object(derived = d)
+ #pragma db view object(derived = d) object(root = r)
+ struct root_view
+ {
+ #pragma db column(r::id)
+ unsigned long r_id;
+
+ #pragma db column(d::id)
+ unsigned long d_id;
+
+ std::string str;
+ };
+}
+
+#endif // TEST3_HXX
diff --git a/odb-tests/common/inheritance/polymorphism/test4.hxx b/odb-tests/common/inheritance/polymorphism/test4.hxx
new file mode 100644
index 0000000..148c53c
--- /dev/null
+++ b/odb-tests/common/inheritance/polymorphism/test4.hxx
@@ -0,0 +1,84 @@
+// file : common/inheritance/polymorphism/test4.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST4_HXX
+#define TEST4_HXX
+
+#include <string>
+
+#include <odb/core.hxx>
+
+// Test views.
+//
+#pragma db namespace table("t4_")
+namespace test4
+{
+ #pragma db object polymorphic
+ struct root1
+ {
+ virtual ~root1 () {}
+ root1 () {}
+ root1 (unsigned long i): id (i) {}
+
+ #pragma db id
+ unsigned long id;
+ };
+
+ #pragma db object
+ struct base1: root1
+ {
+ base1 () {}
+ base1 (unsigned long i, unsigned long n): root1 (i), num (n) {}
+
+ unsigned long num;
+ };
+
+ #pragma db object polymorphic
+ struct root2
+ {
+ virtual ~root2 () {}
+ root2 () {}
+ root2 (unsigned long i, unsigned long n): id (i), num (n) {}
+
+ #pragma db id
+ unsigned long id;
+
+ unsigned long num;
+ };
+
+ #pragma db object
+ struct base2: root2
+ {
+ base2 () {}
+ base2 (unsigned long i, unsigned long n, const std::string& s)
+ : root2 (i, n), str (s) {}
+
+ std::string str;
+ };
+
+ // Test custom join condition.
+ //
+ #pragma db view object(base2) object(base1: base2::num == base1::num)
+ struct view1
+ {
+ std::string str;
+ };
+
+ #pragma db view object(base2)
+ struct view2
+ {
+ #pragma db column("min(" + base2::num + ")")
+ unsigned long min_num;
+ };
+
+ // Test custom join condition that uses object id. It cannot come
+ // from the base since the base table hasn't been join'ed yet.
+ //
+ #pragma db view object(base1) object(base2: base2::id == base1::id)
+ struct view3
+ {
+ std::string str;
+ };
+}
+
+#endif // TEST4_HXX
diff --git a/odb-tests/common/inheritance/polymorphism/test5.hxx b/odb-tests/common/inheritance/polymorphism/test5.hxx
new file mode 100644
index 0000000..172e7e8
--- /dev/null
+++ b/odb-tests/common/inheritance/polymorphism/test5.hxx
@@ -0,0 +1,92 @@
+// file : common/inheritance/polymorphism/test5.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST5_HXX
+#define TEST5_HXX
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <typeinfo>
+
+#include <odb/core.hxx>
+
+// Test polymorphism and optimistic concurrency.
+//
+#pragma db namespace table("t5_")
+namespace test5
+{
+ #pragma db object polymorphic optimistic pointer(std::unique_ptr)
+ struct root
+ {
+ virtual ~root () {}
+ root () {}
+ root (unsigned long i, unsigned long n): id (i), num (n) {}
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db version
+ unsigned long version;
+
+ unsigned long num;
+ std::vector<std::string> strs;
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (root))
+ return false;
+
+ return id == r.id && version == r.version &&
+ num == r.num && strs == r.strs;
+ }
+ };
+
+ inline bool
+ operator== (const root& x, const root& y) {return x.compare (y);}
+
+ #pragma db object
+ struct base: root
+ {
+ base () {}
+ base (unsigned long i, unsigned long n, const std::string& s)
+ : root (i, n), str (s) {}
+
+ std::string str;
+ std::vector<unsigned long> nums;
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (base))
+ return false;
+
+ const base& b (static_cast<const base&> (r));
+ return root::compare (r, false) && str == b.str && nums == b.nums;
+ }
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ derived () {}
+ derived (unsigned long i, unsigned long n, const std::string& s)
+ : base (i, n, s), dnum (n + 1), dstr (s + 'd') {}
+
+ unsigned long dnum;
+ std::string dstr;
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (derived))
+ return false;
+
+ const derived& d (static_cast<const derived&> (r));
+ return base::compare (r, false) && dnum == d.dnum && dstr == d.dstr;
+ }
+ };
+}
+
+#endif // TEST5_HXX
diff --git a/odb-tests/common/inheritance/polymorphism/test6.hxx b/odb-tests/common/inheritance/polymorphism/test6.hxx
new file mode 100644
index 0000000..b0f9a16
--- /dev/null
+++ b/odb-tests/common/inheritance/polymorphism/test6.hxx
@@ -0,0 +1,64 @@
+// file : common/inheritance/polymorphism/test6.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST6_HXX
+#define TEST6_HXX
+
+#include <string>
+#include <memory>
+
+#include <odb/core.hxx>
+#include <odb/callback.hxx>
+
+// Test polymorphism and callbacks.
+//
+#pragma db namespace table("t6_")
+namespace test6
+{
+ #pragma db object polymorphic pointer(std::unique_ptr)
+ struct root
+ {
+ virtual ~root () {}
+ root (): id (0) {}
+ root (unsigned long i, unsigned long n): id (i), num (n) {}
+
+ #pragma db id
+ unsigned long id;
+
+ unsigned long num;
+ };
+
+ #pragma db object callback(db_callback)
+ struct base: root
+ {
+ base () {}
+ base (unsigned long i, unsigned long n, const std::string& s)
+ : root (i, n), str (s) {}
+
+ std::string str;
+
+ void
+ db_callback (odb::callback_event, odb::database&);
+
+ void
+ db_callback (odb::callback_event, odb::database&) const;
+ };
+
+ #pragma db object callback(db_callback)
+ struct derived: base
+ {
+ derived () {}
+ derived (unsigned long i, unsigned long n, const std::string& s)
+ : base (i, n, s), dnum (n + 1), dstr (s + 'd') {}
+
+ unsigned long dnum;
+ std::string dstr;
+
+ std::unique_ptr<root> ptr;
+
+ void
+ db_callback (odb::callback_event, odb::database&) const;
+ };
+}
+
+#endif // TEST6_HXX
diff --git a/odb-tests/common/inheritance/polymorphism/test7.hxx b/odb-tests/common/inheritance/polymorphism/test7.hxx
new file mode 100644
index 0000000..60da98e
--- /dev/null
+++ b/odb-tests/common/inheritance/polymorphism/test7.hxx
@@ -0,0 +1,54 @@
+// file : common/inheritance/polymorphism/test7.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST7_HXX
+#define TEST7_HXX
+
+#include <string>
+#include <memory>
+
+#include <odb/core.hxx>
+
+// Test polymorphism and object cache (session).
+//
+#pragma db namespace table("t7_")
+namespace test7
+{
+ using std::shared_ptr;
+
+ #pragma db object polymorphic pointer(shared_ptr) session
+ struct root
+ {
+ virtual ~root () {}
+ root (): id (0) {}
+ root (unsigned long i, unsigned long n): id (i), num (n) {}
+
+ #pragma db id
+ unsigned long id;
+
+ unsigned long num;
+ };
+
+ #pragma db object
+ struct base: root
+ {
+ base () {}
+ base (unsigned long i, unsigned long n, const std::string& s)
+ : root (i, n), str (s) {}
+
+ std::string str;
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ derived () {}
+ derived (unsigned long i, unsigned long n, const std::string& s)
+ : base (i, n, s), dnum (n + 1), dstr (s + 'd') {}
+
+ unsigned long dnum;
+ std::string dstr;
+ };
+}
+
+#endif // TEST7_HXX
diff --git a/odb-tests/common/inheritance/polymorphism/test8.hxx b/odb-tests/common/inheritance/polymorphism/test8.hxx
new file mode 100644
index 0000000..84b6688
--- /dev/null
+++ b/odb-tests/common/inheritance/polymorphism/test8.hxx
@@ -0,0 +1,129 @@
+// file : common/inheritance/polymorphism/test8.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST8_HXX
+#define TEST8_HXX
+
+#include <string>
+#include <vector>
+#include <typeinfo>
+
+#include <odb/core.hxx>
+
+// Test polymorphism and abstract bases.
+//
+#pragma db namespace table("t8_")
+namespace test8
+{
+ #pragma db object polymorphic
+ struct root
+ {
+ virtual ~root () = 0; // Auto-abstract.
+ root () {}
+ root (unsigned long i, unsigned long n): id (i), num (n) {}
+
+ #pragma db id
+ unsigned long id;
+
+ unsigned long num;
+ std::vector<std::string> strs;
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (root))
+ return false;
+
+ return id == r.id && num == r.num && strs == r.strs;
+ }
+ };
+
+ inline root::
+ ~root () {}
+
+ inline bool
+ operator== (const root& x, const root& y) {return x.compare (y);}
+
+ #pragma db object
+ struct base: root
+ {
+ base () {}
+ base (unsigned long i, unsigned long n, const std::string& s)
+ : root (i, n), str (s) {}
+
+ std::string str;
+ std::vector<unsigned long> nums;
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (base))
+ return false;
+
+ const base& b (static_cast<const base&> (r));
+ return root::compare (r, false) && str == b.str && nums == b.nums;
+ }
+ };
+
+ #pragma db object abstract
+ struct interm: base
+ {
+ interm () {}
+ interm (unsigned long i, unsigned long n, const std::string& s, bool b)
+ : base (i, n, s), bln (b) {}
+
+ bool bln;
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (interm))
+ return false;
+
+ const interm& i (static_cast<const interm&> (r));
+ return base::compare (r, false) && bln == i.bln;
+ }
+ };
+
+ #pragma db object
+ struct derived1: interm
+ {
+ derived1 () {}
+ derived1 (unsigned long i, unsigned long n, const std::string& s, bool b)
+ : interm (i, n, s, b), dnum (n + 1) {}
+
+ unsigned long dnum;
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (derived1))
+ return false;
+
+ const derived1& d (static_cast<const derived1&> (r));
+ return interm::compare (r, false) && dnum == d.dnum;
+ }
+ };
+
+ #pragma db object
+ struct derived2: interm
+ {
+ derived2 () {}
+ derived2 (unsigned long i, unsigned long n, const std::string& s, bool b)
+ : interm (i, n, s, b), dstr (s + 'd') {}
+
+ std::string dstr;
+
+ virtual bool
+ compare (const root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (derived2))
+ return false;
+
+ const derived2& d (static_cast<const derived2&> (r));
+ return interm::compare (r, false) && dstr == d.dstr;
+ }
+ };
+}
+
+#endif // TEST8_HXX
diff --git a/odb-tests/common/inheritance/polymorphism/test9.hxx b/odb-tests/common/inheritance/polymorphism/test9.hxx
new file mode 100644
index 0000000..cdc97ae
--- /dev/null
+++ b/odb-tests/common/inheritance/polymorphism/test9.hxx
@@ -0,0 +1,161 @@
+// file : common/inheritance/polymorphism/test9.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST9_HXX
+#define TEST9_HXX
+
+#include <string>
+#include <vector>
+#include <typeinfo>
+
+#include <odb/core.hxx>
+
+// Test polymorphism and readonly classes.
+//
+#pragma db namespace table("t9_")
+namespace test9
+{
+ //
+ // ro_root, rw_base, ro_derived
+ //
+ #pragma db object polymorphic readonly
+ struct ro_root
+ {
+ virtual ~ro_root () {}
+ ro_root () {}
+ ro_root (unsigned long i, unsigned long n): id (i), num (n) {}
+
+ #pragma db id
+ unsigned long id;
+
+ unsigned long num;
+ std::vector<std::string> strs;
+
+ virtual bool
+ compare (const ro_root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (ro_root))
+ return false;
+
+ return id == r.id && num == r.num && strs == r.strs;
+ }
+ };
+
+ inline bool
+ operator== (const ro_root& x, const ro_root& y) {return x.compare (y);}
+
+ #pragma db object
+ struct rw_base: ro_root
+ {
+ rw_base () {}
+ rw_base (unsigned long i, unsigned long n, const std::string& s)
+ : ro_root (i, n), str (s) {}
+
+ std::string str;
+ std::vector<unsigned long> nums;
+
+ virtual bool
+ compare (const ro_root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (rw_base))
+ return false;
+
+ const rw_base& b (static_cast<const rw_base&> (r));
+ return ro_root::compare (r, false) && str == b.str && nums == b.nums;
+ }
+ };
+
+ #pragma db object readonly
+ struct ro_derived: rw_base
+ {
+ ro_derived () {}
+ ro_derived (unsigned long i, unsigned long n, const std::string& s)
+ : rw_base (i, n, s), dnum (n + 1), dstr (s + 'd') {}
+
+ unsigned long dnum;
+ std::string dstr;
+
+ virtual bool
+ compare (const ro_root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (ro_derived))
+ return false;
+
+ const ro_derived& d (static_cast<const ro_derived&> (r));
+ return rw_base::compare (r, false) && dnum == d.dnum && dstr == d.dstr;
+ }
+ };
+
+ //
+ // rw_root, ro_base, rw_derived
+ //
+ #pragma db object polymorphic
+ struct rw_root
+ {
+ virtual ~rw_root () {}
+ rw_root () {}
+ rw_root (unsigned long i, unsigned long n): id (i), num (n) {}
+
+ #pragma db id
+ unsigned long id;
+
+ unsigned long num;
+ std::vector<std::string> strs;
+
+ virtual bool
+ compare (const rw_root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (rw_root))
+ return false;
+
+ return id == r.id && num == r.num && strs == r.strs;
+ }
+ };
+
+ inline bool
+ operator== (const rw_root& x, const rw_root& y) {return x.compare (y);}
+
+ #pragma db object readonly
+ struct ro_base: rw_root
+ {
+ ro_base () {}
+ ro_base (unsigned long i, unsigned long n, const std::string& s)
+ : rw_root (i, n), str (s) {}
+
+ std::string str;
+ std::vector<unsigned long> nums;
+
+ virtual bool
+ compare (const rw_root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (ro_base))
+ return false;
+
+ const ro_base& b (static_cast<const ro_base&> (r));
+ return rw_root::compare (r, false) && str == b.str && nums == b.nums;
+ }
+ };
+
+ #pragma db object
+ struct rw_derived: ro_base
+ {
+ rw_derived () {}
+ rw_derived (unsigned long i, unsigned long n, const std::string& s)
+ : ro_base (i, n, s), dnum (n + 1), dstr (s + 'd') {}
+
+ unsigned long dnum;
+ std::string dstr;
+
+ virtual bool
+ compare (const rw_root& r, bool tc = true) const
+ {
+ if (tc && typeid (r) != typeid (rw_derived))
+ return false;
+
+ const rw_derived& d (static_cast<const rw_derived&> (r));
+ return ro_base::compare (r, false) && dnum == d.dnum && dstr == d.dstr;
+ }
+ };
+}
+
+#endif // TEST9_HXX
diff --git a/odb-tests/common/inheritance/polymorphism/testscript b/odb-tests/common/inheritance/polymorphism/testscript
new file mode 100644
index 0000000..89e5726
--- /dev/null
+++ b/odb-tests/common/inheritance/polymorphism/testscript
@@ -0,0 +1,80 @@
+# file : common/inheritance/polymorphism/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
++cat <<EOI >=output
+ base pre_persist 1 const
+ base post_persist 1 const
+ derived pre_persist 2 const
+ derived post_persist 2 const
+ base pre_load 0
+ base post_load 1
+ derived pre_load 0 const
+ derived post_load 2 const
+ base pre_load 1
+ base post_load 1
+ derived pre_load 2 const
+ derived post_load 2 const
+ base pre_load 1
+ base post_load 1
+ derived pre_load 2 const
+ derived post_load 2 const
+ base pre_update 1 const
+ base post_update 1 const
+ derived pre_update 2 const
+ derived post_update 2 const
+ base pre_load 0
+ base post_load 1
+ derived pre_load 0 const
+ derived post_load 2 const
+ base pre_erase 1 const
+ base post_erase 1 const
+ derived pre_erase 2 const
+ derived post_erase 2 const
+ derived pre_persist 3 const
+ derived post_persist 3 const
+ derived pre_persist 4 const
+ derived post_persist 4 const
+ derived pre_load 0 const
+ derived pre_load 0 const
+ derived post_load 4 const
+ derived post_load 3 const
+ EOI
+
+test.redirects += >>>../output
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql-schema.testscript
+
+ for s: $schemas
+ cat $out_base/"$s"($multi ? '-mysql' : '').sql | $create_schema_cmd
+ end;
+
+ $* ($multi ? 'mysql' : ) $mysql_options
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql-schema.testscript
+
+ for s: $schemas
+ $create_schema_cmd -f $out_base/"$s"($multi ? '-pgsql' : '').sql
+ end;
+
+ $* ($multi ? 'pgsql' : ) $pgsql_options
+}
diff --git a/odb-tests/common/inheritance/reuse/buildfile b/odb-tests/common/inheritance/reuse/buildfile
new file mode 100644
index 0000000..b82439a
--- /dev/null
+++ b/odb-tests/common/inheritance/reuse/buildfile
@@ -0,0 +1,41 @@
+# file : common/inheritance/reuse/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix inhrt_r_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/inheritance/reuse/driver.cxx b/odb-tests/common/inheritance/reuse/driver.cxx
new file mode 100644
index 0000000..e6122bb
--- /dev/null
+++ b/odb-tests/common/inheritance/reuse/driver.cxx
@@ -0,0 +1,237 @@
+// file : common/inheritance/reuse/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test reuse object inheritance.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ base b;
+ b.comp_.bools.push_back (true);
+ b.comp_.obools.push_back (true);
+ b.comp_.num = 10;
+ b.comp_.str = "comp bbb";
+ b.comp_.nums.push_back (101);
+ b.comp_.nums.push_back (102);
+ b.comp_.onums.push_back (101);
+ b.comp_.onums.push_back (102);
+ b.num_ = 0;
+ b.str_ = "bbb";
+ b.strs_.push_back ("bbb one");
+ b.strs_.push_back ("bbb two");
+ b.ostrs_.push_back ("bbb one");
+ b.ostrs_.push_back ("bbb two");
+
+ object1 o1;
+ o1.comp_.bools.push_back (false);
+ o1.comp_.obools.push_back (false);
+ o1.comp_.num = 11;
+ o1.comp_.str = "comp o1o1o1";
+ o1.comp_.nums.push_back (111);
+ o1.comp_.nums.push_back (112);
+ o1.comp_.onums.push_back (111);
+ o1.comp_.onums.push_back (112);
+ static_cast<base&> (o1).num_ = 1;
+ o1.num1_ = 21;
+ o1.str_ = "base o1o1o1";
+ o1.strs_.push_back ("base o1o1o1 one");
+ o1.strs_.push_back ("base o1o1o1 two");
+ o1.ostrs_.push_back ("base o1o1o1 one");
+ o1.ostrs_.push_back ("base o1o1o1 two");
+
+ object2 o2;
+ o2.comp_.bools.push_back (true);
+ o2.comp_.bools.push_back (false);
+ o2.comp_.obools.push_back (true);
+ o2.comp_.obools.push_back (false);
+ o2.comp_.num = 12;
+ o2.comp_.str = "comp o2o2o2";
+ o2.comp_.nums.push_back (121);
+ o2.comp_.nums.push_back (122);
+ o2.comp_.onums.push_back (121);
+ o2.comp_.onums.push_back (122);
+ o2.num_ = 2;
+ static_cast<base&> (o2).str_ = "base o2o2o2";
+ o2.str_ = "o2o2o2";
+ o2.strs_.push_back ("base o2o2o2 one");
+ o2.strs_.push_back ("base o2o2o2 two");
+ o2.ostrs_.push_back ("base o2o2o2 one");
+ o2.ostrs_.push_back ("base o2o2o2 two");
+
+ object3 o3;
+ o3.comp_.bools.push_back (false);
+ o3.comp_.bools.push_back (false);
+ o3.comp_.obools.push_back (false);
+ o3.comp_.obools.push_back (false);
+ o3.comp_.num = 13;
+ o3.comp_.str = "comp o3o3o3";
+ o3.comp_.nums.push_back (131);
+ o3.comp_.nums.push_back (132);
+ o3.comp_.onums.push_back (131);
+ o3.comp_.onums.push_back (132);
+ o3.num_ = 3;
+ o3.str_ = "base o3o3o3";
+ o3.strs_.push_back ("base o3o3o3 one");
+ o3.strs_.push_back ("base o3o3o3 two");
+ o3.ostrs_.push_back ("base o3o3o3 one");
+ o3.ostrs_.push_back ("base o3o3o3 two");
+
+ reference r;
+ r.o1_ = &o1;
+
+ empty_object e;
+ e.comp_.bools.push_back (true);
+ e.comp_.bools.push_back (true);
+ e.comp_.obools.push_back (true);
+ e.comp_.obools.push_back (true);
+ e.comp_.num = 14;
+ e.comp_.str = "comp eee";
+ e.comp_.nums.push_back (141);
+ e.comp_.nums.push_back (142);
+ e.comp_.onums.push_back (141);
+ e.comp_.onums.push_back (142);
+ e.num_ = 4;
+ e.str_ = "base eee";
+ e.strs_.push_back ("base eee one");
+ e.strs_.push_back ("base eee two");
+ e.ostrs_.push_back ("base eee one");
+ e.ostrs_.push_back ("base eee two");
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (b);
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o3);
+ db->persist (r);
+ db->persist (e);
+ t.commit ();
+ }
+
+ // load & check
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> lb (db->load<base> (b.id_));
+ unique_ptr<object1> lo1 (db->load<object1> (o1.id_));
+ unique_ptr<object2> lo2 (db->load<object2> (o2.id_));
+ unique_ptr<object3> lo3 (db->load<object3> (o3.id_));
+ unique_ptr<empty_object> le (db->load<empty_object> (e.id_));
+ unique_ptr<reference> lr (db->load<reference> (r.id_));
+ t.commit ();
+
+ assert (b == *lb);
+ assert (o1 == *lo1);
+ assert (o2 == *lo2);
+ assert (o3 == *lo3);
+ assert (lr->o1_->id_ == r.o1_->id_);
+ assert (e == *le);
+
+ delete lr->o1_;
+ }
+
+ // update
+ //
+ {
+ transaction t (db->begin ());
+ db->update (b);
+ db->update (o1);
+ db->update (o2);
+ db->update (o3);
+ db->update (r);
+ db->update (e);
+ t.commit ();
+ }
+
+ // query
+ //
+ {
+ typedef odb::query<base> b_query;
+ typedef odb::query<object1> o1_query;
+ typedef odb::query<object2> o2_query;
+ typedef odb::query<reference> r_query;
+
+ typedef odb::result<reference> r_result;
+
+ transaction t (db->begin ());
+
+ assert (!db->query<base> (b_query::comp.num == 10).empty ());
+ assert (!db->query<object1> (o1_query::num1 == 21).empty ());
+ assert (!db->query<object2> (o2_query::num == 2).empty ());
+
+ // Query condition with hidden members.
+ //
+ assert (
+ !db->query<object2> (o2_query::base::str == "base o2o2o2").empty ());
+
+ // Query condition with referenced composite member in base class.
+ //
+ {
+ r_result r (db->query<reference> (r_query::o1->comp.num == 11));
+ assert (!r.empty ());
+ delete r.begin ()->o1_;
+ }
+
+ t.commit ();
+ }
+
+ // views
+ //
+ {
+ typedef odb::query<object2_view> query;
+ typedef odb::result<object2_view> result;
+
+ transaction t (db->begin ());
+
+ result r (db->query<object2_view> (query::num == o2.num_));
+ result::iterator i (r.begin ());
+ assert (i != r.end () &&
+ i->num == o2.num_ && i->id == o2.id_ && i->str == o2.str_);
+ assert (++i == r.end ());
+
+ t.commit ();
+ }
+
+ // erase
+ //
+ {
+ transaction t (db->begin ());
+ db->erase (b);
+ db->erase (o1);
+ db->erase (o2);
+ db->erase (o3);
+ db->erase (r);
+ db->erase (e);
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/inheritance/reuse/test.hxx b/odb-tests/common/inheritance/reuse/test.hxx
new file mode 100644
index 0000000..48f474f
--- /dev/null
+++ b/odb-tests/common/inheritance/reuse/test.hxx
@@ -0,0 +1,163 @@
+// file : common/inheritance/reuse/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <vector>
+
+#include <odb/core.hxx>
+#include <odb/vector.hxx>
+
+#pragma db value
+struct comp_base
+{
+ std::vector<unsigned char> bools;
+ odb::vector<unsigned char> obools;
+
+ bool
+ operator== (const comp_base& y) const
+ {
+ return bools == y.bools && obools == y.obools;
+ }
+};
+
+#pragma db value
+struct comp: comp_base
+{
+ unsigned int num;
+ std::string str;
+
+ std::vector<unsigned int> nums;
+ odb::vector<unsigned int> onums;
+
+ bool
+ operator== (const comp& y) const
+ {
+ return
+ static_cast<const comp_base&> (*this) == y &&
+ num == y.num &&
+ str == y.str &&
+ nums == y.nums &&
+ onums == y.onums;
+ }
+};
+
+#pragma db object abstract
+struct abstract_base
+{
+ comp comp_;
+
+ unsigned int num_;
+ std::string str_;
+
+ std::vector<std::string> strs_;
+ odb::vector<std::string> ostrs_;
+
+ bool
+ operator== (const abstract_base& y) const
+ {
+ return
+ comp_ == y.comp_ &&
+ num_ == y.num_ &&
+ str_ == y.str_ &&
+ strs_ == y.strs_ &&
+ ostrs_ == y.ostrs_;
+ }
+};
+
+#pragma db object
+struct base: abstract_base
+{
+ #pragma db id auto
+ unsigned long id_;
+
+ bool
+ operator== (const base& y) const
+ {
+ return id_ == y.id_ && static_cast<const abstract_base&> (*this) == y;
+ }
+};
+
+#pragma db object
+struct object1: base
+{
+ unsigned int num1_;
+
+ bool
+ operator== (const object1& y) const
+ {
+ return static_cast<const base&> (*this) == y && num1_ == y.num1_;
+ }
+};
+
+#pragma db object
+struct object2: base
+{
+ #pragma db column("derived_str")
+ std::string str_;
+
+ bool
+ operator== (const object2& y) const
+ {
+ return static_cast<const base&> (*this) == y && str_ == y.str_;
+ }
+};
+
+// Reference to derived object.
+//
+#pragma db object
+struct reference
+{
+ #pragma db id auto
+ unsigned long id_;
+
+ object1* o1_;
+};
+
+// Multiple inheritance.
+//
+#pragma db object abstract
+struct id_base
+{
+ #pragma db id auto
+ unsigned long id_;
+
+ bool
+ operator== (const id_base& y) const
+ {
+ return id_ == y.id_;
+ }
+};
+
+#pragma db object
+struct object3: abstract_base, id_base
+{
+ bool
+ operator== (const object3& y) const
+ {
+ return
+ static_cast<const abstract_base&> (*this) == y &&
+ static_cast<const id_base&> (*this) == y;
+ }
+};
+
+// Empty derived object.
+//
+#pragma db object
+struct empty_object: base
+{
+};
+
+// View based on the derived object.
+//
+#pragma db view object(object2)
+struct object2_view
+{
+ unsigned int num; // from abstract_base
+ unsigned long id; // from base
+ std::string str; // from object2, hides one from abstract_base
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/inheritance/reuse/testscript b/odb-tests/common/inheritance/reuse/testscript
new file mode 100644
index 0000000..995b3f5
--- /dev/null
+++ b/odb-tests/common/inheritance/reuse/testscript
@@ -0,0 +1,33 @@
+# file : common/inheritance/reuse/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/inheritance/transient/buildfile b/odb-tests/common/inheritance/transient/buildfile
new file mode 100644
index 0000000..1961abc
--- /dev/null
+++ b/odb-tests/common/inheritance/transient/buildfile
@@ -0,0 +1,41 @@
+# file : common/inheritance/transient/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix inhrt_t_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/inheritance/transient/driver.cxx b/odb-tests/common/inheritance/transient/driver.cxx
new file mode 100644
index 0000000..1caae6c
--- /dev/null
+++ b/odb-tests/common/inheritance/transient/driver.cxx
@@ -0,0 +1,80 @@
+// file : common/inheritance/transient/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test transient inheritance of objects, composite value types, and views.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ object o;
+ o.num = 1;
+ o.str = "abc";
+ o.strs.push_back ("abc 1");
+ o.strs.push_back ("abc 2");
+ o.c.num = 11;
+ o.c.str = "comp abc";
+ o.c.nums.push_back (111);
+ o.c.nums.push_back (112);
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // load & check
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id_));
+ t.commit ();
+
+ assert (*p == o);
+ }
+
+ // view
+ //
+ {
+ typedef odb::query<view> query;
+ typedef odb::result<view> result;
+
+ transaction t (db->begin ());
+
+ result r (db->query<view> (query::id == o.id_));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->num == o.num && i->str == o.str);
+ assert (++i == r.end ());
+
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/inheritance/transient/test.hxx b/odb-tests/common/inheritance/transient/test.hxx
new file mode 100644
index 0000000..394ee8f
--- /dev/null
+++ b/odb-tests/common/inheritance/transient/test.hxx
@@ -0,0 +1,60 @@
+// file : common/inheritance/transient/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <vector>
+
+#include <odb/core.hxx>
+
+struct object;
+
+struct base
+{
+ int n;
+ std::vector<std::string> v;
+ object* p;
+};
+
+#pragma db value
+struct comp: base
+{
+ unsigned int num;
+ std::string str;
+ std::vector<unsigned int> nums;
+
+ bool
+ operator== (const comp& y) const
+ {
+ return num == y.num && str == y.str && nums == y.nums;
+ }
+};
+
+#pragma db object
+struct object: base
+{
+ #pragma db id auto
+ unsigned int id_;
+
+ unsigned int num;
+ std::string str;
+ std::vector<std::string> strs;
+ comp c;
+
+ bool
+ operator== (const object& y) const
+ {
+ return num == y.num && str == y.str && strs == y.strs && c == y.c;
+ }
+};
+
+#pragma db view object(object)
+struct view: base
+{
+ unsigned int num;
+ std::string str;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/inheritance/transient/testscript b/odb-tests/common/inheritance/transient/testscript
new file mode 100644
index 0000000..bce91de
--- /dev/null
+++ b/odb-tests/common/inheritance/transient/testscript
@@ -0,0 +1,33 @@
+# file : common/inheritance/transient/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/inverse/buildfile b/odb-tests/common/inverse/buildfile
new file mode 100644
index 0000000..63fa1cb
--- /dev/null
+++ b/odb-tests/common/inverse/buildfile
@@ -0,0 +1,42 @@
+# file : common/inverse/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix t_inverse_ \
+ --generate-schema \
+ --generate-query \
+ --generate-session
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/inverse/driver.cxx b/odb-tests/common/inverse/driver.cxx
new file mode 100644
index 0000000..842438e
--- /dev/null
+++ b/odb-tests/common/inverse/driver.cxx
@@ -0,0 +1,502 @@
+// file : common/inverse/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test bidirectional relationships with inverse sides.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/session.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Test raw pointers.
+ //
+ {
+ using namespace test1;
+
+ obj1_ptr o1_1 (new obj1);
+ obj1_ptr o1_2 (new obj1);
+ obj2_ptr o2 (new obj2);
+ obj3_ptr o3_1 (new obj3);
+ obj3_ptr o3_2 (new obj3);
+ obj4_ptr o4_1 (new obj4);
+ obj4_ptr o4_2 (new obj4);
+ obj5_ptr o5_1 (new obj5);
+ obj5_ptr o5_2 (new obj5);
+ obj5_ptr o5_3 (new obj5);
+ obj5_ptr o5_4 (new obj5);
+
+ o1_1->id = "obj1 1";
+ o1_1->o2 = o2;
+ o1_1->o3.insert (o3_1);
+ o1_1->o3.insert (o3_2);
+ o1_1->o4 = o4_1;
+ o1_1->o5.insert (o5_1);
+ o1_1->o5.insert (o5_2);
+
+ o1_2->id = "obj1 2";
+ o1_2->o2 = 0;
+ o1_2->o3.clear ();
+ o1_2->o4 = o4_2;
+ o1_2->o5.insert (o5_3);
+ o1_2->o5.insert (o5_4);
+
+ o2->str = "obj2";
+ o2->o1 = o1_1;
+
+ o3_1->str = "obj3 1";
+ o3_1->o1 = o1_1;
+
+ o3_2->str = "obj3 2";
+ o3_2->o1 = o1_1;
+
+ o4_1->str = "obj4 1";
+ o4_1->o1.insert (o1_1);
+
+ o4_2->str = "obj4 2";
+ o4_2->o1.insert (o1_2);
+
+ o5_1->str = "obj5 1";
+ o5_1->o1.insert (o1_1);
+
+ o5_2->str = "obj5 2";
+ o5_2->o1.insert (o1_1);
+
+ o5_3->str = "obj5 3";
+ o5_3->o1.insert (o1_2);
+
+ o5_4->str = "obj5 4";
+ o5_4->o1.insert (o1_2);
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+
+ // objN come before obj1 to get object id assigned.
+ //
+ db->persist (o5_1);
+ db->persist (o5_2);
+ db->persist (o5_3);
+ db->persist (o5_4);
+ db->persist (o4_1);
+ db->persist (o4_2);
+ db->persist (o3_1);
+ db->persist (o3_2);
+ db->persist (o2);
+ db->persist (o1_1);
+ db->persist (o1_2);
+
+ t.commit ();
+ }
+
+ // load
+ //
+ {
+ session s;
+ transaction t (db->begin ());
+ obj2_ptr x2 (db->load<obj2> (o2->id));
+ obj3_ptr x3_1 (db->load<obj3> (o3_1->id));
+ obj3_ptr x3_2 (db->load<obj3> (o3_2->id));
+ obj4_ptr x4_1 (db->load<obj4> (o4_1->id));
+ obj4_ptr x4_2 (db->load<obj4> (o4_2->id));
+ obj5_ptr x5_1 (db->load<obj5> (o5_1->id));
+ obj5_ptr x5_2 (db->load<obj5> (o5_2->id));
+ obj5_ptr x5_3 (db->load<obj5> (o5_3->id));
+ obj5_ptr x5_4 (db->load<obj5> (o5_4->id));
+ t.commit ();
+
+ assert (x2->str == o2->str);
+ assert (x2->o1->id == o1_1->id);
+ assert (x2->o1->o2 == x2);
+
+ assert (x3_1->str == o3_1->str);
+ assert (x3_2->str == o3_2->str);
+ assert (x3_1->o1 == x3_2->o1);
+ assert (x3_1->o1->id == o1_1->id);
+ assert (x3_1->o1->o3.find (x3_1) != x3_1->o1->o3.end ());
+ assert (x3_1->o1->o3.find (x3_2) != x3_1->o1->o3.end ());
+
+ assert (x4_1->str == o4_1->str);
+ assert (x4_2->str == o4_2->str);
+ assert ((*x4_1->o1.begin ())->id == o1_1->id);
+ assert ((*x4_2->o1.begin ())->id == o1_2->id);
+ assert ((*x4_1->o1.begin ())->o4 == x4_1);
+ assert ((*x4_2->o1.begin ())->o4 == x4_2);
+
+ assert (x5_1->str == o5_1->str);
+ assert (x5_2->str == o5_2->str);
+ assert ((*x5_1->o1.begin ())->id == o1_1->id);
+ assert ((*x5_2->o1.begin ())->id == o1_1->id);
+ assert ((*x5_3->o1.begin ())->id == o1_2->id);
+ assert ((*x5_4->o1.begin ())->id == o1_2->id);
+ assert ((*x5_1->o1.begin ())->o5.find (x5_1) !=
+ (*x5_1->o1.begin ())->o5.end ());
+ assert ((*x5_2->o1.begin ())->o5.find (x5_2) !=
+ (*x5_2->o1.begin ())->o5.end ());
+ assert ((*x5_3->o1.begin ())->o5.find (x5_3) !=
+ (*x5_3->o1.begin ())->o5.end ());
+ assert ((*x5_4->o1.begin ())->o5.find (x5_4) !=
+ (*x5_4->o1.begin ())->o5.end ());
+
+ delete *x4_1->o1.begin ();
+ delete *x4_2->o1.begin ();
+ }
+
+ // query
+ //
+ {
+ // one(i)-to-one
+ //
+ typedef odb::query<obj2> query;
+ typedef odb::result<obj2> result;
+
+ session s;
+ transaction t (db->begin ());
+
+ result r (db->query<obj2> (query::o1->id == "obj1 1"));
+ assert (!r.empty ());
+ assert (r.begin ()->id == o2->id);
+ assert (r.begin ()->o1->id == o1_1->id);
+ assert (size (r) == 1);
+
+ t.commit ();
+ }
+
+ {
+ // one(i)-to-many
+ //
+ typedef odb::query<obj3> query;
+ typedef odb::result<obj3> result;
+
+ session s;
+ transaction t (db->begin ());
+
+ result r (db->query<obj3> (query::o1->id == "obj1 1"));
+ size_t n (0);
+
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ assert (i->id == o3_1->id || i->id == o3_2->id);
+ assert (i->o1->id == o1_1->id);
+ n++;
+ }
+
+ assert (n == 2);
+
+ t.commit ();
+ }
+
+ delete o1_1;
+ delete o1_2;
+ }
+
+ // Test shared_ptr/weak_ptr.
+ //
+ {
+ using namespace test2;
+
+ obj1_ptr o1_1 (new obj1);
+ obj1_ptr o1_2 (new obj1);
+ obj2_ptr o2 (new obj2);
+ obj3_ptr o3_1 (new obj3);
+ obj3_ptr o3_2 (new obj3);
+ obj4_ptr o4 (new obj4);
+ obj5_ptr o5_1 (new obj5);
+ obj5_ptr o5_2 (new obj5);
+
+ o1_1->id = "obj1 1";
+ o1_1->o2 = o2;
+ o1_1->o3.push_back (o3_1);
+ o1_1->o3.push_back (o3_2);
+ o1_1->o4 = o4;
+ o1_1->o5.push_back (o5_1);
+ o1_1->o5.push_back (o5_2);
+
+ o1_2->id = "obj1 2";
+ o1_2->o2 = obj2_ptr ();
+ o1_2->o3.clear ();
+ o1_2->o4 = o4;
+ o1_2->o5.push_back (o5_1);
+
+ o2->str = "obj2";
+ o2->o1 = o1_1;
+
+ o3_1->str = "obj3 1";
+ o3_1->o1 = o1_1;
+
+ o3_2->str = "obj3 3";
+ o3_2->o1 = o1_1;
+
+ o4->str = "obj4";
+ o4->o1.push_back (o1_1);
+ o4->o1.push_back (o1_2);
+
+ o5_1->str = "obj5 1";
+ o5_1->o1.push_back (o1_1);
+ o5_1->o1.push_back (o1_2);
+
+ o5_2->str = "obj5 2";
+ o5_2->o1.push_back (o1_1);
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+
+ // objN come before obj1 to get object id assigned.
+ //
+ db->persist (o5_1);
+ db->persist (o5_2);
+ db->persist (o4);
+ db->persist (o3_1);
+ db->persist (o3_2);
+ db->persist (o2);
+ db->persist (o1_1);
+ db->persist (o1_2);
+
+ t.commit ();
+ }
+
+ // load
+ //
+ {
+ session s;
+ transaction t (db->begin ());
+ obj2_ptr x2 (db->load<obj2> (o2->id));
+ obj3_ptr x3_1 (db->load<obj3> (o3_1->id));
+ obj3_ptr x3_2 (db->load<obj3> (o3_2->id));
+ obj4_ptr x4 (db->load<obj4> (o4->id));
+ obj5_ptr x5_1 (db->load<obj5> (o5_1->id));
+ obj5_ptr x5_2 (db->load<obj5> (o5_2->id));
+ t.commit ();
+
+ assert (x2->str == o2->str);
+ assert (x2->o1.lock ()->id == o1_1->id);
+ assert (x2->o1.lock ()->o2 == x2);
+
+ assert (x3_1->str == o3_1->str);
+ assert (x3_2->str == o3_2->str);
+ assert (x3_1->o1.lock () == x3_2->o1.lock ());
+ assert (x3_1->o1.lock ()->id == o1_1->id);
+ assert (x3_1->o1.lock ()->o3[0] == x3_1);
+ assert (x3_1->o1.lock ()->o3[1] == x3_2);
+
+ {
+ assert (x4->str == o4->str);
+
+ obj1_ptr t1 (x4->o1[0].lock ()), t2 (x4->o1[1].lock ());
+
+ assert (t1->id == o1_1->id || t2->id == o1_1->id);
+ assert (t1->id == o1_2->id || t2->id == o1_2->id);
+ }
+
+ {
+ assert (x5_1->str == o5_1->str);
+ assert (x5_2->str == o5_2->str);
+
+ obj1_ptr t1 (x5_1->o1[0].lock ()), t2 (x5_1->o1[1].lock ()),
+ t3 (x5_2->o1[0].lock ());
+
+ assert (t1->id == o1_1->id || t2->id == o1_1->id);
+ assert (t1->id == o1_2->id || t2->id == o1_2->id);
+ assert (t3->id == o1_1->id);
+ }
+ }
+ }
+
+ // Test inverse based on points_to.
+ //
+ {
+ using namespace test3;
+
+ {
+ obj1 o1 (1, 2);
+ o1.o2 = new obj2;
+
+ {
+ transaction t (db->begin ());
+
+ o1.o2->o1 = db->persist (o1);
+ db->persist (o1.o2);
+
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+
+ unique_ptr<obj1> p (db->load<obj1> (o1.id));
+ assert (p->o2->id == o1.o2->id);
+
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<obj1> query;
+
+ transaction t (db->begin ());
+
+ unique_ptr<obj1> p (db->query_one<obj1> (query::o2->o1.i == o1.id.i &&
+ query::o2->o1.j == o1.id.j));
+ assert (p->o2->id == o1.o2->id);
+
+ t.commit ();
+ }
+ }
+
+ {
+ obj3 o3;
+ o3.o4.push_back (new obj4);
+ o3.o4.push_back (new obj4);
+
+ {
+ transaction t (db->begin ());
+
+ o3.o4[0]->o3 = o3.o4[1]->o3 = db->persist (o3);
+ db->persist (o3.o4[0]);
+ db->persist (o3.o4[1]);
+
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+
+ unique_ptr<obj3> p (db->load<obj3> (o3.id));
+ assert (p->o4[0]->id == o3.o4[0]->id);
+ assert (p->o4[1]->id == o3.o4[1]->id);
+
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<obj3> query;
+
+ transaction t (db->begin ());
+
+ unique_ptr<obj3> p (db->query_one<obj3> (query::id == o3.id));
+ assert (p->o4[0]->id == o3.o4[0]->id);
+ assert (p->o4[1]->id == o3.o4[1]->id);
+
+ t.commit ();
+ }
+ }
+ }
+
+ // Test inverse with nested data members.
+ //
+ {
+ using namespace test4;
+
+ {
+ obj1 o1;
+ o1.o2 = new obj2;
+
+ {
+ transaction t (db->begin ());
+
+ o1.o2->id.i = db->persist (o1);
+ o1.o2->id.j = 123;
+ db->persist (o1.o2);
+
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+
+ unique_ptr<obj1> p (db->load<obj1> (o1.id));
+ assert (p->o2->id.i == o1.o2->id.i && p->o2->id.j == o1.o2->id.j);
+
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<obj1> query;
+
+ transaction t (db->begin ());
+
+ unique_ptr<obj1> p (db->query_one<obj1> (
+ query::o2->id.i == o1.o2->id.i &&
+ query::o2->id.j == o1.o2->id.j));
+ assert (p->o2->id.i == o1.o2->id.i && p->o2->id.j == o1.o2->id.j);
+
+ t.commit ();
+ }
+ }
+
+ {
+ obj3 o3;
+ o3.o4.push_back (new obj4);
+ o3.o4.push_back (new obj4);
+
+ {
+ transaction t (db->begin ());
+
+ o3.o4[0]->id.i = o3.o4[1]->id.i = db->persist (o3);
+ o3.o4[0]->id.j = 123;
+ o3.o4[1]->id.j = 234;
+ db->persist (o3.o4[0]);
+ db->persist (o3.o4[1]);
+
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+
+ unique_ptr<obj3> p (db->load<obj3> (o3.id));
+ assert (p->o4[0]->id.i == o3.o4[0]->id.i &&
+ p->o4[0]->id.j == o3.o4[0]->id.j);
+
+ assert (p->o4[1]->id.i == o3.o4[1]->id.i &&
+ p->o4[1]->id.j == o3.o4[1]->id.j);
+
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<obj3> query;
+
+ transaction t (db->begin ());
+
+ unique_ptr<obj3> p (db->query_one<obj3> (query::id == o3.id));
+
+ assert (p->o4[0]->id.i == o3.o4[0]->id.i &&
+ p->o4[0]->id.j == o3.o4[0]->id.j);
+
+ assert (p->o4[1]->id.i == o3.o4[1]->id.i &&
+ p->o4[1]->id.j == o3.o4[1]->id.j);
+
+ t.commit ();
+ }
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/inverse/test.hxx b/odb-tests/common/inverse/test.hxx
new file mode 100644
index 0000000..a7b8678
--- /dev/null
+++ b/odb-tests/common/inverse/test.hxx
@@ -0,0 +1,391 @@
+// file : common/inverse/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <set>
+#include <vector>
+#include <string>
+#include <memory>
+
+#include <odb/core.hxx>
+
+// Test raw pointers.
+//
+#pragma db namespace table("t1_")
+namespace test1
+{
+ struct obj1;
+ struct obj2;
+ struct obj3;
+ struct obj4;
+ struct obj5;
+
+ typedef obj1* obj1_ptr;
+ typedef obj2* obj2_ptr;
+ typedef obj3* obj3_ptr;
+ typedef obj4* obj4_ptr;
+ typedef obj5* obj5_ptr;
+
+ typedef std::set<obj1_ptr> obj1_ptr_set;
+ typedef std::set<obj3_ptr> obj3_ptr_set;
+ typedef std::set<obj5_ptr> obj5_ptr_set;
+
+ #pragma db object
+ struct obj1
+ {
+ obj1 (): o2 (0), o4 (0) {}
+ ~obj1 ();
+
+ #pragma db id
+ std::string id;
+
+ obj2_ptr o2;
+
+ #pragma db id_column("obj1_id") value_column("obj3_id")
+ obj3_ptr_set o3;
+
+ obj4_ptr o4;
+
+ obj5_ptr_set o5;
+ };
+
+ #pragma db object
+ struct obj2
+ {
+ #pragma db id auto
+ int id;
+
+ // one-to-one
+ //
+ #pragma db inverse(o2)
+ obj1_ptr o1;
+
+ std::string str;
+ };
+
+ #pragma db object
+ struct obj3
+ {
+ std::string str;
+
+ // one(i)-to-many
+ //
+ #pragma db inverse (o3)
+ obj1_ptr o1;
+
+ #pragma db id auto
+ int id;
+ };
+
+ #pragma db object
+ struct obj4
+ {
+ #pragma db id auto
+ int id;
+
+ std::string str;
+
+ // many(i)-to-one
+ //
+ #pragma db inverse (o4)
+ obj1_ptr_set o1;
+ };
+
+ #pragma db object
+ struct obj5
+ {
+ #pragma db id auto
+ int id;
+
+ std::string str;
+
+ // many(i)-to-many
+ //
+ #pragma db inverse (o5)
+ obj1_ptr_set o1;
+ };
+
+ inline obj1::
+ ~obj1 ()
+ {
+ delete o2;
+ for (obj3_ptr_set::iterator i (o3.begin ()); i != o3.end (); ++i)
+ delete *i;
+ delete o4;
+ for (obj5_ptr_set::iterator i (o5.begin ()); i != o5.end (); ++i)
+ delete *i;
+ }
+}
+
+// Test shared_ptr/weak_ptr.
+//
+#pragma db namespace table("t2_")
+namespace test2
+{
+ using std::shared_ptr;
+ using std::weak_ptr;
+
+ struct obj1;
+ struct obj2;
+ struct obj3;
+ struct obj4;
+ struct obj5;
+
+ typedef shared_ptr<obj1> obj1_ptr;
+ typedef shared_ptr<obj2> obj2_ptr;
+ typedef shared_ptr<obj3> obj3_ptr;
+ typedef shared_ptr<obj4> obj4_ptr;
+ typedef shared_ptr<obj5> obj5_ptr;
+
+ typedef weak_ptr<obj1> obj1_wptr;
+
+ typedef std::vector<obj1_wptr> obj1_wptr_vec;
+ typedef std::vector<obj3_ptr> obj3_ptr_vec;
+ typedef std::vector<obj5_ptr> obj5_ptr_vec;
+
+ #pragma db object pointer(obj1_ptr)
+ struct obj1
+ {
+ #pragma db id
+ std::string id;
+
+ obj2_ptr o2;
+
+ #pragma db id_column("obj1_id") value_column("obj3_id")
+ obj3_ptr_vec o3;
+
+ obj4_ptr o4;
+ obj5_ptr_vec o5;
+ };
+
+ #pragma db object pointer(obj2_ptr)
+ struct obj2
+ {
+ #pragma db id auto
+ int id;
+
+ std::string str;
+
+ // one(i)-to-one
+ //
+ #pragma db inverse(o2)
+ obj1_wptr o1;
+ };
+
+ #pragma db object pointer(obj3_ptr)
+ struct obj3
+ {
+ #pragma db id auto
+ int id;
+
+ std::string str;
+
+ // one(i)-to-many
+ //
+ #pragma db inverse (o3)
+ obj1_wptr o1;
+ };
+
+ #pragma db object pointer(obj4_ptr)
+ struct obj4
+ {
+ #pragma db id auto
+ int id;
+
+ std::string str;
+
+ // many(i)-to-one
+ //
+ #pragma db inverse (o4)
+ obj1_wptr_vec o1;
+ };
+
+ #pragma db object pointer(obj5_ptr)
+ struct obj5
+ {
+ #pragma db id auto
+ int id;
+
+ std::string str;
+
+ // many(i)-to-many
+ //
+ #pragma db inverse (o5)
+ obj1_wptr_vec o1;
+ };
+}
+
+// Test inverse based on points_to.
+//
+#pragma db namespace table("t3_")
+namespace test3
+{
+ // Inverse pointer.
+ //
+ #pragma db value
+ struct comp
+ {
+ int i;
+ int j;
+ };
+
+ inline bool
+ operator< (comp x, comp y) {return x.i < y.i || (x.i == y.i && x.j < y.j);}
+
+ struct obj2;
+
+ #pragma db object
+ struct obj1
+ {
+ #pragma db id
+ comp id;
+
+ #pragma db inverse(o1)
+ obj2* o2;
+
+ obj1 (int i = 0, int j = 0): o2 (0) {id.i = i; id.j = j;}
+ ~obj1 ();
+ };
+
+ #pragma db object
+ struct obj2
+ {
+ #pragma db id auto
+ int id;
+
+ #pragma db points_to(obj1)
+ comp o1;
+
+#ifndef ODB_DATABASE_MYSQL
+ #pragma db member(o1) on_delete(cascade)
+#endif
+ };
+
+ inline obj1::
+ ~obj1 () {delete o2;}
+
+ // Inverse container of pointers.
+ //
+ struct obj4;
+
+ #pragma db object
+ struct obj3
+ {
+ #pragma db id auto
+ int id;
+
+ #pragma db inverse(o3)
+ std::vector<obj4*> o4;
+
+ ~obj3 ();
+ };
+
+ #pragma db object
+ struct obj4
+ {
+ #pragma db id auto
+ int id;
+
+ #pragma db points_to(obj3)
+ int o3;
+ };
+
+ inline obj3::
+ ~obj3 ()
+ {
+ for (std::vector<obj4*>::iterator i (o4.begin ()); i != o4.end (); ++i)
+ delete *i;
+ }
+};
+
+// Test inverse with nested data members.
+//
+#pragma db namespace table("t4_")
+namespace test4
+{
+ // Inverse pointer.
+ //
+ struct obj1;
+ struct obj2;
+
+ #pragma db value
+ struct obj2_id
+ {
+ #pragma db points_to(obj1)
+ int i;
+ int j;
+ };
+
+ inline bool
+ operator< (obj2_id x, obj2_id y)
+ {return x.i < y.i || (x.i == y.i && x.j < y.j);}
+
+ #pragma db object
+ struct obj1
+ {
+ #pragma db id auto
+ int id;
+
+ #pragma db inverse(id.i)
+ obj2* o2;
+
+ obj1 (): o2 (0) {}
+ ~obj1 ();
+ };
+
+ #pragma db object
+ struct obj2
+ {
+ #pragma db id
+ obj2_id id;
+ };
+
+ inline obj1::
+ ~obj1 () {delete o2;}
+
+ // Inverse container of pointers.
+ //
+ struct obj3;
+ struct obj4;
+
+ #pragma db value
+ struct obj4_id
+ {
+ #pragma db points_to(obj3)
+ int i;
+ int j;
+ };
+
+ inline bool
+ operator< (obj4_id x, obj4_id y)
+ {return x.i < y.i || (x.i == y.i && x.j < y.j);}
+
+ #pragma db object
+ struct obj3
+ {
+ #pragma db id auto
+ int id;
+
+ #pragma db inverse(id.i)
+ std::vector<obj4*> o4;
+
+ ~obj3 ();
+ };
+
+ #pragma db object
+ struct obj4
+ {
+ #pragma db id
+ obj4_id id;
+ };
+
+ inline obj3::
+ ~obj3 ()
+ {
+ for (std::vector<obj4*>::iterator i (o4.begin ()); i != o4.end (); ++i)
+ delete *i;
+ }
+};
+#endif // TEST_HXX
diff --git a/odb-tests/common/inverse/testscript b/odb-tests/common/inverse/testscript
new file mode 100644
index 0000000..c2a4e3e
--- /dev/null
+++ b/odb-tests/common/inverse/testscript
@@ -0,0 +1,33 @@
+# file : common/inverse/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/lazy-ptr/buildfile b/odb-tests/common/lazy-ptr/buildfile
new file mode 100644
index 0000000..d495d2f
--- /dev/null
+++ b/odb-tests/common/lazy-ptr/buildfile
@@ -0,0 +1,41 @@
+# file : common/lazy-ptr/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix lazy_ptr_ \
+ --generate-schema \
+ --generate-session
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/lazy-ptr/driver.cxx b/odb-tests/common/lazy-ptr/driver.cxx
new file mode 100644
index 0000000..9a3b324
--- /dev/null
+++ b/odb-tests/common/lazy-ptr/driver.cxx
@@ -0,0 +1,360 @@
+// file : common/lazy-ptr/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test lazy object pointers.
+//
+
+#include <memory> // std::unique_ptr
+#include <utility> // std::move
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/session.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+namespace test2
+{
+ cont::cont (unsigned long i)
+ : id (i)
+ {
+ }
+
+ obj_ptr
+ create (unsigned int id)
+ {
+ obj_ptr r (new obj (id));
+ return r;
+ }
+
+ lazy_obj_ptr
+ create (database& db, unsigned int id)
+ {
+ lazy_obj_ptr r (db, id);
+ return r;
+ }
+}
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Raw.
+ //
+ {
+ using namespace test1;
+
+ // persist
+ //
+ obj* o1 (new obj (1));
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ t.commit ();
+ }
+
+ unique_ptr<cont> c1 (new cont (1));
+ unique_ptr<cont> c2 (new cont (2));
+
+ lazy_ptr<obj> lo1 (*db, 1);
+ obj* o2 (new obj (2));
+
+ obj* o3 (new obj (3));
+ obj* o4 (new obj (4));
+
+ c1->o.push_back (lo1);
+ c1->o.push_back (o2);
+ c2->o.push_back (o3);
+ c2->o.push_back (o4);
+
+ // Test pointer comparison.
+ //
+ assert (lazy_ptr<obj> () == lazy_ptr<obj> ());
+ assert (lazy_ptr<obj> (o1) != lazy_ptr<obj> ());
+ assert (lo1 != lazy_ptr<obj> ());
+ assert (lazy_ptr<obj> (o1) == lazy_ptr<obj> (o1));
+ assert (lo1 == lazy_ptr<obj> (*db, o1));
+ assert (lo1 != lazy_ptr<obj> (*db, o2));
+
+ delete o1;
+
+ {
+ transaction t (db->begin ());
+
+ db->persist (o2);
+ db->persist (o3);
+ db->persist (o4);
+
+ db->persist (*c1);
+ db->persist (*c2);
+
+ t.commit ();
+ }
+
+ // load
+ //
+ {
+ session s;
+ transaction t (db->begin ());
+ unique_ptr<cont> c (db->load<cont> (1));
+ obj* o (db->load<obj> (1));
+
+ // Not loaded.
+ //
+ assert (c->o.size () == 2);
+ assert (!c->o[0].loaded ());
+ assert (!c->o[1].loaded ());
+
+ assert (!o->c.loaded ());
+
+ // Correct object ids.
+ //
+ assert (c->o[0].object_id () == o->id);
+ assert (o->c.object_id () == c->id);
+
+ // Load.
+ //
+ cont* cl (o->c.load ());
+ obj* ol (c->o[0].load ());
+
+ assert (cl == c.get ());
+ assert (ol == o);
+
+ // Test unload/reload.
+ //
+ o->c.unload ();
+ assert (!o->c.loaded ());
+ o->c.load ();
+ assert (o->c.loaded ());
+
+ t.commit ();
+ }
+ }
+
+ // std::unique_ptr
+ //
+ {
+ using namespace test2;
+
+ // persist
+ //
+ {
+ obj_ptr o1 (new obj (1));
+ transaction t (db->begin ());
+ db->persist (*o1);
+ t.commit ();
+ }
+
+ cont_ptr c1 (new cont (1));
+ cont_ptr c2 (new cont (2));
+
+ lazy_obj_ptr lo1 = create (*db, 1);
+ lo1 = create (*db, 1);
+
+ c1->o = std::move (lo1);
+ c2->o = create (2);
+
+ {
+ transaction t (db->begin ());
+
+ db->persist (*c2->o);
+
+ db->persist (*c1);
+ db->persist (*c2);
+
+ t.commit ();
+ }
+
+ // load
+ //
+ {
+ session s;
+ transaction t (db->begin ());
+ cont_ptr c (db->load<cont> (1));
+ obj* o (db->load<obj> (1));
+
+ // Not loaded.
+ //
+ assert (!c->o.loaded ());
+ assert (!o->c.loaded ());
+
+ // Correct object ids.
+ //
+ assert (c->o.object_id () == o->id);
+ assert (o->c.object_id () == c->id);
+
+ // Load.
+ //
+ cont* cl (o->c.load ());
+ const obj_ptr& ol (c->o.load ());
+
+ assert (cl == c.get ());
+ assert (ol.get () == o);
+
+ t.commit ();
+ }
+
+ // unload/reload
+ //
+ {
+ // No session.
+ transaction t (db->begin ());
+ cont_ptr c (db->load<cont> (1));
+
+ assert (!c->o.loaded ());
+ c->o.load ();
+ assert (c->o.loaded ());
+ c->o.unload ();
+ assert (!c->o.loaded ());
+ c->o.load ();
+ assert (c->o.loaded ());
+
+ t.commit ();
+ }
+ }
+
+ // Shared pointer from C++11 or TR1.
+ //
+ {
+ using namespace test3;
+
+ // persist
+ //
+ shared_ptr<cont> c1 (new cont (1));
+
+ {
+ transaction t (db->begin ());
+ db->persist (c1);
+ t.commit ();
+ }
+
+ lazy_shared_ptr<cont> lc1 (*db, 1);
+ shared_ptr<cont> c2 (new cont (2));
+
+ shared_ptr<obj> o1 (new obj (1));
+ shared_ptr<obj> o2 (new obj (2));
+
+ shared_ptr<obj> o3 (new obj (3));
+ shared_ptr<obj> o4 (new obj (4));
+
+ o1->c = lc1;
+ o2->c = lc1;
+ o3->c = c2;
+ o4->c = c2;
+
+ // Test pointer comparison.
+ //
+ assert (lazy_shared_ptr<cont> () == lazy_shared_ptr<cont> ());
+ assert (lazy_shared_ptr<cont> (c1) != lazy_shared_ptr<cont> ());
+ assert (lc1 != lazy_shared_ptr<cont> ());
+ assert (lazy_shared_ptr<cont> (c1) == lazy_shared_ptr<cont> (c1));
+ assert (lc1 == lazy_shared_ptr<cont> (*db, c1));
+ assert (lc1 != lazy_shared_ptr<cont> (*db, c2));
+
+ // Test move constructors.
+ //
+ {
+ lazy_shared_ptr<cont> tmp (*db, 1);
+ lazy_shared_ptr<cont> l (std::move (tmp));
+ assert (lc1 == l);
+ }
+
+ {
+ shared_ptr<cont> tmp (c1);
+ lazy_shared_ptr<cont> l (*db, std::move (tmp));
+ assert (lc1 == l);
+ }
+
+ {
+ transaction t (db->begin ());
+
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o3);
+ db->persist (o4);
+
+ db->persist (c2);
+
+ t.commit ();
+ }
+
+ // load
+ //
+ {
+ session s;
+ transaction t (db->begin ());
+ shared_ptr<cont> c (db->load<cont> (1));
+ shared_ptr<obj> o (db->load<obj> (1));
+
+ // Not loaded.
+ //
+ assert (c->o.size () == 2);
+ assert (!c->o[0].loaded ());
+ assert (!c->o[1].loaded ());
+
+ assert (!o->c.loaded ());
+
+ // Correct object ids.
+ //
+ assert (c->o[0].object_id () == o->id);
+ assert (o->c.object_id () == c->id);
+
+ // Load.
+ //
+ shared_ptr<cont> cl (o->c.load ());
+ shared_ptr<obj> ol (c->o[0].load ());
+
+ assert (cl == c);
+ assert (ol == o);
+
+ t.commit ();
+ }
+
+ // Test lazy weak locking and reloading.
+ //
+ {
+ // No session.
+ transaction t (db->begin ());
+ shared_ptr<cont> c (db->load<cont> (1));
+
+ // Lock.
+ //
+ assert (!c->o[1].loaded ());
+ lazy_shared_ptr<obj> l (c->o[1].lock ());
+ assert (!l.loaded ());
+ assert (l.object_id () == c->o[1].object_id ());
+
+ // Reload.
+ //
+ assert (!c->o[1].loaded ());
+ shared_ptr<obj> ol (c->o[1].load ());
+ assert (c->o[1].loaded ());
+ ol.reset ();
+ assert (!c->o[1].loaded ());
+ ol = c->o[1].load ();
+ assert (c->o[1].loaded ());
+
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/lazy-ptr/test.hxx b/odb-tests/common/lazy-ptr/test.hxx
new file mode 100644
index 0000000..f946029
--- /dev/null
+++ b/odb-tests/common/lazy-ptr/test.hxx
@@ -0,0 +1,147 @@
+// file : common/lazy-ptr/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <vector>
+#include <string>
+#include <memory>
+
+#include <odb/core.hxx>
+#include <odb/lazy-ptr.hxx>
+
+// Raw pointer.
+//
+#pragma db namespace table("t1_")
+namespace test1
+{
+ using odb::lazy_ptr;
+ class obj;
+
+ #pragma db object
+ class cont
+ {
+ public:
+ cont () {}
+ cont (unsigned long i): id (i) {}
+ ~cont ();
+
+ #pragma db id
+ unsigned long id;
+
+ typedef std::vector<lazy_ptr<obj> > obj_list;
+
+ #pragma db value_not_null
+ obj_list o;
+ };
+
+ #pragma db object
+ class obj
+ {
+ public:
+ obj () {}
+ obj (unsigned long i): id (i) {}
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db inverse(o) not_null
+ lazy_ptr<cont> c; // weak
+ };
+
+ inline cont::
+ ~cont ()
+ {
+ for (obj_list::iterator i (o.begin ()); i != o.end (); ++i)
+ if (obj* p = i->get ())
+ delete p;
+ }
+}
+
+// std::auto_ptr/std::unique_ptr
+//
+#pragma db namespace table("t2_")
+namespace test2
+{
+ using odb::lazy_ptr;
+
+ class obj;
+ class cont;
+
+ typedef std::unique_ptr<obj> obj_ptr;
+ typedef std::unique_ptr<cont> cont_ptr;
+ typedef odb::lazy_unique_ptr<obj> lazy_obj_ptr;
+
+ #pragma db object
+ class cont
+ {
+ public:
+ cont () = default;
+ cont (unsigned long id);
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db not_null
+ lazy_obj_ptr o;
+ };
+
+ #pragma db object
+ class obj
+ {
+ public:
+ obj () = default;
+ obj (unsigned long i): id (i) {}
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db inverse(o) not_null
+ lazy_ptr<cont> c; // weak
+ };
+}
+
+// shared_ptr
+//
+#pragma db namespace table("t3_")
+namespace test3
+{
+ using std::shared_ptr;
+ using odb::lazy_shared_ptr;
+ using odb::lazy_weak_ptr;
+
+ class obj;
+
+ #pragma db object pointer(shared_ptr<cont>)
+ class cont
+ {
+ public:
+ cont () {}
+ cont (unsigned long i): id (i) {}
+
+ #pragma db id
+ unsigned long id;
+
+ typedef std::vector<lazy_weak_ptr<obj> > obj_list;
+
+ #pragma db inverse(c) value_not_null
+ obj_list o;
+ };
+
+ #pragma db object pointer(shared_ptr<obj>)
+ class obj
+ {
+ public:
+ obj () {}
+ obj (unsigned long i): id (i) {}
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db not_null
+ lazy_shared_ptr<cont> c;
+ };
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/lazy-ptr/testscript b/odb-tests/common/lazy-ptr/testscript
new file mode 100644
index 0000000..736fa4c
--- /dev/null
+++ b/odb-tests/common/lazy-ptr/testscript
@@ -0,0 +1,33 @@
+# file : common/lazy-ptr/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/lifecycle/buildfile b/odb-tests/common/lifecycle/buildfile
new file mode 100644
index 0000000..b5b2b00
--- /dev/null
+++ b/odb-tests/common/lifecycle/buildfile
@@ -0,0 +1,40 @@
+# file : common/lifecycle/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix lifecycle_ \
+ --generate-schema
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/lifecycle/driver.cxx b/odb-tests/common/lifecycle/driver.cxx
new file mode 100644
index 0000000..a01d5bd
--- /dev/null
+++ b/odb-tests/common/lifecycle/driver.cxx
@@ -0,0 +1,248 @@
+// file : common/lifecycle/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test object state transistions.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Database operation out of transaction.
+ //
+ try
+ {
+ object o (1);
+ db->persist (o);
+ assert (false);
+ }
+ catch (const not_in_transaction&)
+ {
+ }
+
+ // Transient.
+ //
+ try
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o (db->load<object> (1));
+ assert (false);
+ t.commit ();
+ }
+ catch (const object_not_persistent&)
+ {
+ }
+
+ // Persistent.
+ //
+ {
+ object o (1);
+ o.str_ = "value 1";
+
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+
+ try
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ assert (false);
+ t.commit ();
+ }
+ catch (const object_already_persistent&)
+ {
+ }
+ }
+
+ // Find.
+ //
+ {
+ transaction t (db->begin ());
+
+ unique_ptr<object> o1 (db->find<object> (1));
+ assert (o1.get () != 0 && o1->str_ == "value 1");
+
+ unique_ptr<object> o2 (db->find<object> (2));
+ assert (o2.get () == 0);
+
+ t.commit ();
+ }
+
+ // Find (into existing).
+ //
+ {
+ object o;
+
+ transaction t (db->begin ());
+
+ assert (db->find (1, o));
+ assert (o.str_ == "value 1");
+
+ assert (!db->find (2, o));
+
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o (db->load<object> (1));
+ assert (o->str_ == "value 1");
+ t.commit ();
+
+ try
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o (db->load<object> (2));
+ assert (false);
+ t.commit ();
+ }
+ catch (const object_not_persistent&)
+ {
+ }
+ }
+
+ // Load (into existing).
+ //
+ {
+ object o;
+
+ transaction t (db->begin ());
+ db->load (1, o);
+ assert (o.str_ == "value 1");
+ t.commit ();
+
+ try
+ {
+ transaction t (db->begin ());
+ db->load (2, o);
+ assert (false);
+ t.commit ();
+ }
+ catch (const object_not_persistent&)
+ {
+ }
+ }
+
+ // Reload.
+ //
+ {
+ object o;
+
+ transaction t (db->begin ());
+ db->load (1, o);
+ o.str_ = "junk";
+ db->reload (o);
+ assert (o.str_ == "value 1");
+ t.commit ();
+
+ try
+ {
+ transaction t (db->begin ());
+ o.id_ = 2;
+ db->reload (o);
+ assert (false);
+ t.commit ();
+ }
+ catch (const object_not_persistent&)
+ {
+ }
+ }
+
+ // Modified.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o (db->load<object> (1));
+ o->str_ = "value 2";
+ db->update (*o);
+ t.commit ();
+
+ try
+ {
+ transaction t (db->begin ());
+ o->id_ = 2;
+ db->update (*o);
+ assert (false);
+ t.commit ();
+ }
+ catch (const object_not_persistent&)
+ {
+ }
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o (db->load<object> (1));
+ assert (o->str_ == "value 2");
+ t.commit ();
+ }
+
+ // Update of unmodified object.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o (db->load<object> (1));
+ db->update (*o);
+ t.commit ();
+ }
+
+ // Transient.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o (db->load<object> (1));
+ db->erase (*o);
+ t.commit ();
+
+ try
+ {
+ transaction t (db->begin ());
+ db->erase<object> (1);
+ assert (false);
+ t.commit ();
+ }
+ catch (const object_not_persistent&)
+ {
+ }
+ }
+
+ try
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o (db->load<object> (1));
+ assert (false);
+ t.commit ();
+ }
+ catch (const object_not_persistent&)
+ {
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/lifecycle/test.hxx b/odb-tests/common/lifecycle/test.hxx
new file mode 100644
index 0000000..8d260d2
--- /dev/null
+++ b/odb-tests/common/lifecycle/test.hxx
@@ -0,0 +1,27 @@
+// file : common/lifecycle/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+ std::string str_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/lifecycle/testscript b/odb-tests/common/lifecycle/testscript
new file mode 100644
index 0000000..0337bba
--- /dev/null
+++ b/odb-tests/common/lifecycle/testscript
@@ -0,0 +1,33 @@
+# file : common/lifecycle/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/no-id/buildfile b/odb-tests/common/no-id/buildfile
new file mode 100644
index 0000000..1a64401
--- /dev/null
+++ b/odb-tests/common/no-id/buildfile
@@ -0,0 +1,41 @@
+# file : common/no-id/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix no_id_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/no-id/driver.cxx b/odb-tests/common/no-id/driver.cxx
new file mode 100644
index 0000000..eee69a5
--- /dev/null
+++ b/odb-tests/common/no-id/driver.cxx
@@ -0,0 +1,102 @@
+// file : common/no-id/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test persistent classes without id.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ object o1 (1, "aaa");
+ object o2 (2, "bbb");
+ object o3 (3, "ccc");
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o2); // Ok, since there is no id.
+ db->persist (o3);
+ t.commit ();
+ }
+
+ // Compile errors.
+ //
+ {
+ //db->load<object> (1);
+ //db->find<object> (1);
+ //db->update (o1);
+ //db->erase<object> (1);
+ }
+
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ // Query.
+ //
+ {
+ transaction t (db->begin ());
+
+ {
+ result r (db->query<object> ());
+ assert (size (r) == 4);
+ }
+
+ {
+ result r (db->query<object> (query::str == "aaa"));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->num == 1 && i->str == "aaa");
+ object o;
+ i.load (o);
+ //i.id (); // Compile-time error.
+ assert (o.num == 1 && o.str == "aaa");
+ assert (++i == r.end ());
+ }
+
+ {
+ result r (db->query<object> (query::num < 3));
+ assert (size (r) == 3);
+ }
+
+ t.commit ();
+ }
+
+ // Erase (query).
+ //
+ {
+ transaction t (db->begin ());
+ assert (db->erase_query<object> (query::num == 2) == 2);
+ assert (db->erase_query<object> () == 2);
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/no-id/test.hxx b/odb-tests/common/no-id/test.hxx
new file mode 100644
index 0000000..c5b5c65
--- /dev/null
+++ b/odb-tests/common/no-id/test.hxx
@@ -0,0 +1,21 @@
+// file : common/no-id/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+
+#include <odb/core.hxx>
+
+#pragma db object no_id
+struct object
+{
+ object () {}
+ object (unsigned long n, const std::string& s): num (n), str (s) {}
+
+ unsigned long num;
+ std::string str;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/no-id/testscript b/odb-tests/common/no-id/testscript
new file mode 100644
index 0000000..5ec57ee
--- /dev/null
+++ b/odb-tests/common/no-id/testscript
@@ -0,0 +1,33 @@
+# file : common/no-id/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/object/buildfile b/odb-tests/common/object/buildfile
new file mode 100644
index 0000000..cb56aee
--- /dev/null
+++ b/odb-tests/common/object/buildfile
@@ -0,0 +1,41 @@
+# file : common/object/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix object_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/object/driver.cxx b/odb-tests/common/object/driver.cxx
new file mode 100644
index 0000000..1c29417
--- /dev/null
+++ b/odb-tests/common/object/driver.cxx
@@ -0,0 +1,84 @@
+// file : common/object/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test persistent classes.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Test persistent class template instantiation.
+ //
+ {
+ using namespace test1;
+
+ pair_object po;
+ po.second = "abc";
+
+ derived d;
+ d.x = "abc";
+ d.n = 123;
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+ db->persist<pair_object> (po);
+ db->persist (d);
+ t.commit ();
+ }
+
+ // load & check
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<pair_object> po1 (db->load<pair_object> (po.first));
+ unique_ptr<derived> d1 (db->load<derived> (d.id));
+ t.commit ();
+
+ assert (po == *po1);
+
+ assert (d.x == d1->x);
+ assert (d.n == d1->n);
+ }
+
+ // Test the API confusion.
+ //
+ {
+ transaction t (db->begin ());
+ db->update<pair_object> (po);
+ db->reload<pair_object> (po);
+ db->erase<pair_object> (po);
+
+ db->query<pair_object> ();
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/object/test.hxx b/odb-tests/common/object/test.hxx
new file mode 100644
index 0000000..87bac91
--- /dev/null
+++ b/odb-tests/common/object/test.hxx
@@ -0,0 +1,49 @@
+// file : common/object/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <utility> // std::pair
+
+#include <odb/core.hxx>
+
+// Test persistent class template instantiation.
+//
+#pragma db namespace table("t1_")
+namespace test1
+{
+ typedef std::pair<unsigned long, std::string> pair_object;
+ #pragma db object(pair_object)
+ #pragma db member(pair_object::first) id auto
+
+ #pragma db object abstract
+ struct base_data
+ {
+ #pragma db id auto
+ unsigned long id;
+ };
+
+ template <typename T>
+ struct base: base_data
+ {
+ T x;
+ };
+
+ typedef base<std::string> base_derived;
+ #pragma db object(base_derived) abstract
+
+ #pragma db object
+ struct derived: base_derived
+ {
+ int n;
+ };
+
+ // Test instantiation in order to "see" id, etc.
+ //
+ typedef base<int> int_base;
+ #pragma db object(int_base)
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/object/testscript b/odb-tests/common/object/testscript
new file mode 100644
index 0000000..6982409
--- /dev/null
+++ b/odb-tests/common/object/testscript
@@ -0,0 +1,33 @@
+# file : common/object/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/optimistic/buildfile b/odb-tests/common/optimistic/buildfile
new file mode 100644
index 0000000..06af705
--- /dev/null
+++ b/odb-tests/common/optimistic/buildfile
@@ -0,0 +1,41 @@
+# file : common/optimistic/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix t_optimistic_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/optimistic/driver.cxx b/odb-tests/common/optimistic/driver.cxx
new file mode 100644
index 0000000..6dfec6e
--- /dev/null
+++ b/odb-tests/common/optimistic/driver.cxx
@@ -0,0 +1,300 @@
+// file : common/optimistic/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test optimistic concurrency support.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+unsigned long
+version (const unique_ptr<database>& db, unsigned long id)
+{
+ typedef odb::query<object_version> query;
+ typedef odb::result<object_version> result;
+
+ result r (db->query<object_version> (query::id == id));
+ return r.empty () ? 0 : r.begin ()->ver;
+}
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ object o (1);
+ o.num = 123;
+ o.str = "abc";
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // Verify initial version in the instance and database.
+ //
+ assert (o.ver == 1);
+ {
+ transaction t (db->begin ());
+ assert (version (db, 1) == 1);
+ t.commit ();
+ }
+
+ object c (o);
+ o.num++;
+ o.str += 'd';
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ // Verify updated version in the instance and database.
+ //
+ assert (o.ver == 2);
+ {
+ transaction t (db->begin ());
+ assert (version (db, 1) == 2);
+ t.commit ();
+ }
+
+ // Verify the data has been updated.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o1->ver == 2 && o1->num == 124 && o1->str == "abcd");
+ }
+
+ // Try to update using outdated object.
+ //
+ c.num--;
+ c.str += 'z';
+
+ {
+ transaction t (db->begin ());
+
+ try
+ {
+ db->update (c);
+ assert (false);
+ }
+ catch (const object_changed&) {}
+
+ // Verify the data hasn't changed.
+ //
+ unique_ptr<object> o1 (db->load<object> (1));
+ assert (o1->ver == 2 && o1->num == 124 && o1->str == "abcd");
+
+ // Reload the object.
+ //
+ db->reload (c);
+ assert (c.ver == 2 && c.num == 124);
+
+ // Check that we don't reload an object that is up-to-date.
+ //
+ c.num--;
+ db->reload (c);
+ assert (c.ver == 2 && c.num == 123);
+
+ t.commit ();
+ }
+
+ // Try to delete using an outdated object.
+ //
+ {
+ transaction t (db->begin ());
+
+ try
+ {
+ db->update (o);
+ db->erase (c);
+ assert (false);
+ }
+ catch (const object_changed&) {}
+
+ t.commit ();
+ }
+
+ // Try to delete using an up-to-date object.
+ //
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+
+ // Try to update deleted object.
+ //
+ {
+ transaction t (db->begin ());
+
+ try
+ {
+ db->update (o);
+ assert (false);
+ }
+ catch (const object_not_persistent&)
+ {
+ assert (false);
+ }
+ catch (const object_changed&) {}
+
+ t.commit ();
+ }
+
+ // Optimistic delete of objects with container requires
+ // extra logic. Test it here.
+ //
+ {
+ container o ("abc");
+ o.nums.push_back (1);
+ o.nums.push_back (2);
+ o.nums.push_back (3);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ container c (o);
+ o.nums.pop_back ();
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ // Try to delete using an outdated object.
+ //
+ {
+ transaction t (db->begin ());
+
+ try
+ {
+ db->erase (c);
+ assert (false);
+ }
+ catch (const object_changed&) {}
+
+ // Verify the container data hasn't changed.
+ //
+ unique_ptr<container> o1 (db->load<container> ("abc"));
+ assert (o1->nums.size () == 2 && o1->nums[0] == 1 && o1->nums[1] == 2);
+
+ t.commit ();
+ }
+
+ // Try to delete using an up-to-date object.
+ //
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ // Test optimistic class inheritance. This is a shortened version
+ // of the object test.
+ //
+ {
+ derived o;
+ o.num = 123;
+ o.str = "abc";
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ derived c (o);
+ o.num++;
+ o.str += 'd';
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ // Try to update using outdated object.
+ //
+ c.num--;
+ c.str += 'z';
+
+ {
+ transaction t (db->begin ());
+
+ try
+ {
+ db->update (c);
+ assert (false);
+ }
+ catch (const object_changed&) {}
+
+ // Reload the object.
+ //
+ db->reload (c);
+ assert (c.ver == 2 && c.num == 124);
+
+ t.commit ();
+ }
+
+ // Try to delete using an outdated object.
+ //
+ {
+ transaction t (db->begin ());
+
+ try
+ {
+ db->update (o);
+ db->erase (c);
+ assert (false);
+ }
+ catch (const object_changed&) {}
+
+ t.commit ();
+ }
+
+ // Try to delete using an up-to-date object.
+ //
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/optimistic/test.hxx b/odb-tests/common/optimistic/test.hxx
new file mode 100644
index 0000000..fcefa3d
--- /dev/null
+++ b/odb-tests/common/optimistic/test.hxx
@@ -0,0 +1,76 @@
+// file : common/optimistic/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <vector>
+
+#include <odb/core.hxx>
+
+#pragma db object optimistic
+struct object
+{
+ object (): ver (123) {}
+ object (unsigned long id): id_ (id), ver (123) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db version
+ unsigned long ver;
+
+ unsigned int num;
+ std::string str;
+};
+
+#pragma db view object(object)
+struct object_version
+{
+ unsigned long ver;
+};
+
+// Optimistic class with a container.
+//
+#pragma db object optimistic
+struct container
+{
+ container (): ver (123) {}
+ container (const std::string& id): id_ (id), ver (123) {}
+
+ #pragma db id
+ std::string id_;
+
+ #pragma db version
+ unsigned long ver;
+
+ std::vector<unsigned int> nums;
+};
+
+// Optimistic class inheritance.
+//
+#pragma db object abstract optimistic
+struct base
+{
+ base (): ver (123) {}
+
+ #pragma db id auto
+ unsigned long id_;
+
+ #pragma db version
+ const unsigned long ver;
+
+ std::string str;
+
+ #pragma db readonly
+ std::string ro;
+};
+
+#pragma db object
+struct derived: base
+{
+ unsigned int num;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/optimistic/testscript b/odb-tests/common/optimistic/testscript
new file mode 100644
index 0000000..9ebafb2
--- /dev/null
+++ b/odb-tests/common/optimistic/testscript
@@ -0,0 +1,33 @@
+# file : common/optimistic/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/pragma/buildfile b/odb-tests/common/pragma/buildfile
new file mode 100644
index 0000000..a2eeaa0
--- /dev/null
+++ b/odb-tests/common/pragma/buildfile
@@ -0,0 +1,39 @@
+# file : common/pragma/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix pragma_
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/pragma/driver.cxx b/odb-tests/common/pragma/driver.cxx
new file mode 100644
index 0000000..a9cc6e0
--- /dev/null
+++ b/odb-tests/common/pragma/driver.cxx
@@ -0,0 +1,27 @@
+// file : common/pragma/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test #pragma db parsing.
+//
+
+#include <memory>
+#include <iostream>
+
+#include <odb/exceptions.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main ()
+{
+}
diff --git a/odb-tests/common/pragma/test.hxx b/odb-tests/common/pragma/test.hxx
new file mode 100644
index 0000000..6877e73
--- /dev/null
+++ b/odb-tests/common/pragma/test.hxx
@@ -0,0 +1,40 @@
+// file : common/template/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+
+#pragma db value(bool) type ("INTEGER")
+
+struct x {int i;};
+#pragma db value (x)
+
+namespace N
+{
+ #pragma db object
+ struct object1
+ {
+ object1 () {}
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db member type ("INTEGER")
+ bool b_;
+ };
+
+ struct object2
+ {
+ object2 () {}
+
+ unsigned long id_;
+ };
+
+ #pragma db object (object2)
+}
+
+PRAGMA_DB (member (N::object2::id_) id auto);
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/pragma/testscript b/odb-tests/common/pragma/testscript
new file mode 100644
index 0000000..089f7a1
--- /dev/null
+++ b/odb-tests/common/pragma/testscript
@@ -0,0 +1,31 @@
+# file : common/include/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $* &!odb-test.db
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $*
+}
diff --git a/odb-tests/common/prepared/buildfile b/odb-tests/common/prepared/buildfile
new file mode 100644
index 0000000..4006a4f
--- /dev/null
+++ b/odb-tests/common/prepared/buildfile
@@ -0,0 +1,43 @@
+# file : common/prepared/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix prepared_ \
+ --generate-schema \
+ --generate-query \
+ --generate-prepared \
+ --omit-unprepared
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/prepared/driver.cxx b/odb-tests/common/prepared/driver.cxx
new file mode 100644
index 0000000..44df651
--- /dev/null
+++ b/odb-tests/common/prepared/driver.cxx
@@ -0,0 +1,444 @@
+// file : common/prepared/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test prepared query functionality.
+//
+
+#include <memory> // std::unique_ptr
+#include <utility> // std::move
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+struct params
+{
+ unsigned short age;
+ std::string name;
+};
+
+static void
+query_factory (const char* name, connection& c)
+{
+ typedef odb::query<person> query;
+
+ unique_ptr<params> p (new params);
+ prepared_query<person> pq (
+ c.prepare_query<person> (
+ name,
+ query::age > query::_ref (p->age) &&
+ query::name != query::_ref (p->name)));
+ c.cache_query (pq, move (p));
+}
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ {
+ person p1 ("John First", 91);
+ person p2 ("John Second", 81);
+ person p3 ("John Third", 71);
+ person p4 ("John Fourth", 61);
+ person p5 ("John Fifth", 51);
+
+ transaction t (db->begin ());
+ db->persist (p1);
+ db->persist (p2);
+ db->persist (p3);
+ db->persist (p4);
+ db->persist (p5);
+ t.commit ();
+ }
+
+ typedef odb::query<person> query;
+ typedef odb::prepared_query<person> prep_query;
+ typedef odb::result<person> result;
+
+ // Uncached query in the same transaction.
+ //
+ {
+ transaction t (db->begin ());
+
+ unsigned short age (90);
+ prep_query pq (
+ db->prepare_query<person> (
+ "person-age-query",
+ query::age > query::_ref (age)));
+
+ for (unsigned short i (1); i < 6; ++i, age -= 10)
+ {
+ result r (pq.execute ());
+ assert (size (r) == i);
+ }
+
+ age = 90;
+ result r (pq.execute ());
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->name_ == "John First" && i->age_ == 91);
+ assert (++i == r.end ());
+
+ t.commit ();
+ }
+
+ // Uncached query in multiple transaction.
+ //
+ {
+ connection_ptr c (db->connection ());
+
+ unsigned short age (90);
+ prep_query pq (
+ c->prepare_query<person> (
+ "person-age-query",
+ query::age > query::_ref (age)));
+
+ for (unsigned short i (1); i < 6; ++i, age -= 10)
+ {
+ transaction t (c->begin ());
+
+ result r (pq.execute ());
+ assert (size (r) == i);
+
+ t.commit ();
+ }
+
+ transaction t (c->begin ());
+
+ age = 90;
+ result r (pq.execute ());
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->name_ == "John First" && i->age_ == 91);
+ assert (++i == r.end ());
+
+ t.commit ();
+ }
+
+ // Cached query without parameters.
+ //
+ {
+ for (unsigned short i (1); i < 6; ++i)
+ {
+ transaction t (db->begin ());
+
+ prep_query pq (db->lookup_query<person> ("person-val-age-query"));
+
+ if (!pq)
+ {
+ assert (i == 1);
+ pq = db->prepare_query<person> (
+ "person-val-age-query",
+ query::age > 90);
+ db->cache_query (pq);
+ }
+ else if (i == 2)
+ {
+ try
+ {
+ db->cache_query (pq);
+ assert (false);
+ }
+ catch (const odb::prepared_already_cached&)
+ {
+ }
+ }
+
+ result r (pq.execute ());
+ assert (size (r) == 1);
+
+ t.commit ();
+ }
+ }
+
+ // Cached query with parameters.
+ //
+ {
+ for (unsigned short i (1); i < 6; ++i)
+ {
+ transaction t (db->begin ());
+
+ unsigned short* age;
+ prep_query pq (db->lookup_query<person> ("person-ref-age-query", age));
+
+ if (!pq)
+ {
+ assert (i == 1);
+
+ unique_ptr<unsigned short> p (new unsigned short);
+ age = p.get ();
+ pq = db->prepare_query<person> (
+ "person-ref-age-query",
+ query::age > query::_ref (*age));
+
+ db->cache_query (pq, move (p));
+ }
+ else if (i == 2)
+ {
+ // Object type mismatch.
+ //
+ try
+ {
+ db->lookup_query<int> ("person-ref-age-query", age);
+ assert (false);
+ }
+ catch (const odb::prepared_type_mismatch&)
+ {
+ }
+
+ // Parameters type mismatch.
+ //
+ try
+ {
+ int* age;
+ db->lookup_query<person> ("person-ref-age-query", age);
+ assert (false);
+ }
+ catch (const odb::prepared_type_mismatch&)
+ {
+ }
+ }
+
+ *age = 100 - i * 10;
+ result r (pq.execute ());
+ assert (size (r) == i);
+
+ t.commit ();
+ }
+ }
+
+ // Cached query with factory.
+ //
+ {
+ db->query_factory ("person-params-query", &query_factory);
+
+ for (unsigned int i (1); i < 6; ++i)
+ {
+ transaction t (db->begin ());
+
+ params* p;
+ prep_query pq (db->lookup_query<person> ("person-params-query", p));
+ assert (pq);
+
+ p->age = 100 - i * 10;
+ p->name = "John First";
+ result r (pq.execute ());
+ assert (size (r) == i - 1);
+
+ t.commit ();
+ }
+
+ db->query_factory ("person-params-query",
+ database::query_factory_ptr ());
+ }
+
+ // Cached query with wildcard factory.
+ //
+ {
+ db->query_factory ("", &query_factory);
+
+ for (unsigned int i (1); i < 6; ++i)
+ {
+ transaction t (db->begin ());
+
+ params* p;
+ prep_query pq (db->lookup_query<person> ("person-params-query-1", p));
+ assert (pq);
+
+ p->age = 100 - i * 10;
+ p->name = "John First";
+ result r (pq.execute ());
+ assert (size (r) == i - 1);
+
+ t.commit ();
+ }
+
+ db->query_factory ("", database::query_factory_ptr ());
+ }
+
+ // Cached query with lambda factory.
+ //
+ {
+ db->query_factory (
+ "person-params-query-2",
+ [] (const char* name, connection& c)
+ {
+ typedef odb::query<person> query;
+
+ unique_ptr<params> p (new params);
+ prepared_query<person> pq (
+ c.prepare_query<person> (
+ name,
+ query::age > query::_ref (p->age) &&
+ query::name != query::_ref (p->name)));
+ c.cache_query (pq, move (p));
+ });
+
+ for (unsigned int i (1); i < 6; ++i)
+ {
+ transaction t (db->begin ());
+
+ params* p;
+ prep_query pq (db->lookup_query<person> ("person-params-query-2", p));
+ assert (pq);
+
+ p->age = 100 - i * 10;
+ p->name = "John First";
+ result r (pq.execute ());
+ assert (size (r) == i - 1);
+
+ t.commit ();
+ }
+
+ db->query_factory ("person-params-query-2",
+ database::query_factory_ptr ());
+ }
+
+ // Cached query with lambda factory using closure. Forces nonoptimized
+ // representation of std::function.
+ //
+ {
+ const std::string person_name ("John First");
+
+ db->query_factory (
+ "person-params-query-3",
+ [person_name] (const char* name, connection& c)
+ {
+ typedef odb::query<person> query;
+
+ prepared_query<person> pq (
+ c.prepare_query<person> (
+ name,
+ query::age > 50 && query::name != person_name));
+ c.cache_query (pq);
+ });
+
+ {
+ transaction t (db->begin ());
+
+ prep_query pq (db->lookup_query<person> ("person-params-query-3"));
+ assert (pq);
+
+ result r (pq.execute ());
+ assert (size (r) == 4);
+
+ t.commit ();
+ }
+
+ db->query_factory ("person-params-query-3", nullptr);
+ }
+
+ // View prepared query.
+ //
+ {
+ typedef odb::query<person_view> query;
+ typedef odb::prepared_query<person_view> prep_query;
+ typedef odb::result<person_view> result;
+
+ transaction t (db->begin ());
+
+ unsigned short age (90);
+ prep_query pq (
+ db->prepare_query<person_view> (
+ "person-view-age-query",
+ query::age > query::_ref (age)));
+
+ for (unsigned short i (1); i < 6; ++i, age -= 10)
+ {
+ result r (pq.execute ());
+ assert (size (r) == i);
+ }
+
+ age = 90;
+ result r (pq.execute ());
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->name == "John First" && i->age == 91);
+ assert (++i == r.end ());
+
+ t.commit ();
+ }
+
+ // By-ref parameter image growth.
+ //
+ {
+ transaction t (db->begin ());
+
+ string name;
+ prep_query pq (
+ db->prepare_query<person> (
+ "person-name-query",
+ query::name != query::_ref (name)));
+
+ {
+ name = "John First";
+ result r (pq.execute ());
+ assert (size (r) == 4);
+ }
+
+ {
+ name.assign (2048, 'x');
+ result r (pq.execute ());
+ assert (size (r) == 5);
+ }
+
+ t.commit ();
+ }
+
+ // Test execute_one() and execute_value().
+ //
+ {
+ transaction t (db->begin ());
+
+ person p ("John Doe", 23);
+ db->persist (p);
+
+ prep_query pq1 (
+ db->prepare_query<person> ("query-1", query::id == p.id_));
+ prep_query pq0 (
+ db->prepare_query<person> ("query-0", query::id == p.id_ + 1));
+
+ {
+ unique_ptr<person> p (pq1.execute_one ());
+ assert (p.get () != 0 && p->name_ == "John Doe");
+ }
+
+ {
+ unique_ptr<person> p (pq0.execute_one ());
+ assert (p.get () == 0);
+ }
+
+ {
+ person p;
+ assert (pq1.execute_one (p) && p.name_ == "John Doe");
+ }
+
+ {
+ person p ("", 0);
+ assert (!pq0.execute_one (p) &&
+ p.id_ == 0 && p.name_.empty () && p.age_ == 0);
+ }
+
+ {
+ person p (pq1.execute_value ());
+ assert (p.name_ == "John Doe");
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/prepared/test.hxx b/odb-tests/common/prepared/test.hxx
new file mode 100644
index 0000000..db16e67
--- /dev/null
+++ b/odb-tests/common/prepared/test.hxx
@@ -0,0 +1,32 @@
+// file : common/prepared/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct person
+{
+ person (): id_ (0) {}
+ person (const std::string& name, unsigned short age)
+ : id_ (0), name_ (name), age_ (age) {}
+
+ #pragma db id auto
+ unsigned long id_;
+
+ std::string name_;
+ unsigned short age_;
+};
+
+#pragma db view object(person)
+struct person_view
+{
+ std::string name;
+ unsigned short age;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/prepared/testscript b/odb-tests/common/prepared/testscript
new file mode 100644
index 0000000..3530c5b
--- /dev/null
+++ b/odb-tests/common/prepared/testscript
@@ -0,0 +1,33 @@
+# file : common/prepared/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/query/array/buildfile b/odb-tests/common/query/array/buildfile
new file mode 100644
index 0000000..3beb6d0
--- /dev/null
+++ b/odb-tests/common/query/array/buildfile
@@ -0,0 +1,43 @@
+# file : common/query/array/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix t_query_array_ \
+ --generate-schema \
+ --generate-query \
+ --generate-prepared \
+ --sql-name-case oracle:upper
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/query/array/driver.cxx b/odb-tests/common/query/array/driver.cxx
new file mode 100644
index 0000000..43d91f2
--- /dev/null
+++ b/odb-tests/common/query/array/driver.cxx
@@ -0,0 +1,200 @@
+// file : common/query/array/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test query support for C arrays.
+//
+
+#include <string>
+#include <memory> // std::unique_ptr
+#include <cstring> // std::memcpy
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/config.hxx> // DATABASE_*
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+#ifndef MULTI_DATABASE
+# if defined(DATABASE_MYSQL)
+const odb::mysql::database_type_id bt = odb::mysql::id_blob;
+# elif defined(DATABASE_SQLITE)
+const odb::sqlite::database_type_id bt = odb::sqlite::id_blob;
+# elif defined(DATABASE_PGSQL)
+const odb::pgsql::database_type_id bt = odb::pgsql::id_bytea;
+# elif defined(DATABASE_ORACLE)
+const odb::oracle::database_type_id bt = odb::oracle::id_raw;
+# elif defined(DATABASE_MSSQL)
+const odb::mssql::database_type_id bt = odb::mssql::id_binary;
+# else
+# error unknown database
+# endif
+#endif
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ typedef odb::query<object> query;
+
+ const char buf[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6};
+
+ //
+ //
+ {
+ object o1 (1, "abc", buf);
+ object o2 (2, "bcd", buf);
+ object o3 (3, "cde", buf);
+
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o3);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+
+ // string
+ //
+#ifndef MULTI_DATABASE
+ assert (size (db->query<object> (query::s == "abc")) == 1);
+ assert (size (db->query<object> (query::s == query::_val ("bcd"))) == 1);
+ assert (size (db->query<object> ("s = " + query::_val ("bcd"))) == 1);
+ assert (size (db->query<object> ("s = " + query::_ref ("bcd"))) == 1);
+#endif
+
+ {
+ char a[] = "bcd";
+ char* ra = a;
+#ifndef MULTI_DATABASE
+ assert (size (db->query<object> (query::s == a)) == 1);
+ assert (size (db->query<object> (query::s == query::_val (a))) == 1);
+#endif
+ assert (size (db->query<object> (query::s == query::_ref (ra))) == 1);
+#ifndef MULTI_DATABASE
+ assert (size (db->query<object> ("s = " + query::_val (a))) == 1);
+ assert (size (db->query<object> ("s = " + query::_ref (a))) == 1);
+#endif
+ }
+
+ {
+ const char a[] = "bcd";
+ const char* ra = a;
+#ifndef MULTI_DATABASE
+ assert (size (db->query<object> (query::s == a)) == 1);
+ assert (size (db->query<object> (query::s == query::_val (a))) == 1);
+#endif
+ assert (size (db->query<object> (query::s == query::_ref (ra))) == 1);
+#ifndef MULTI_DATABASE
+ assert (size (db->query<object> ("s = " + query::_val (a))) == 1);
+ assert (size (db->query<object> ("s = " + query::_ref (a))) == 1);
+#endif
+ }
+
+ {
+ const char* p = "cde";
+#ifndef MULTI_DATABASE
+ assert (size (db->query<object> (query::s == p)) == 1);
+ assert (size (db->query<object> (query::s == query::_val (p))) == 1);
+#endif
+ assert (size (db->query<object> (query::s == query::_ref (p))) == 1);
+#ifndef MULTI_DATABASE
+ assert (size (db->query<object> ("s = " + query::_val (p))) == 1);
+ assert (size (db->query<object> ("s = " + query::_ref (p))) == 1);
+#endif
+ }
+
+ {
+ char a[] = "cde";
+ char* p = a;
+#ifndef MULTI_DATABASE
+ assert (size (db->query<object> (query::s == p)) == 1);
+ assert (size (db->query<object> (query::s == query::_val (p))) == 1);
+#endif
+ assert (size (db->query<object> (query::s == query::_ref (p))) == 1);
+#ifndef MULTI_DATABASE
+ assert (size (db->query<object> ("s = " + query::_val (p))) == 1);
+ assert (size (db->query<object> ("s = " + query::_ref (p))) == 1);
+#endif
+ }
+
+#ifndef MULTI_DATABASE
+ string s ("abc");
+ //assert (size (db->query<object> (query::s == s)) == 1);
+ assert (size (db->query<object> (query::s == s.c_str ())) == 1);
+ //assert (size (db->query<object> (query::s == query::_val (s))) == 1);
+ assert (size (db->query<object> (query::s == query::_val (s.c_str ()))) == 1);
+
+ assert (size (db->query<object> ("s = " + query::_val (s))) == 1);
+ assert (size (db->query<object> ("s = " + query::_ref (s))) == 1);
+#endif
+
+ assert (size (db->query<object> (query::s == query::s1)) == 3);
+
+ // std::array
+ //
+ array<char, 17> a;
+ memcpy (a.data (), "abc", 4); // VC++ strcpy deprecation.
+
+#ifndef MULTI_DATABASE
+ assert (size (db->query<object> (query::a == a)) == 1);
+ assert (size (db->query<object> (query::a == query::_val (a))) == 1);
+#endif
+ assert (size (db->query<object> (query::a == query::_ref (a))) == 1);
+#ifndef MULTI_DATABASE
+ assert (size (db->query<object> ("a = " + query::_val (a))) == 1);
+ assert (size (db->query<object> ("a = " + query::_ref (a))) == 1);
+#endif
+
+ // char
+ //
+ assert (size (db->query<object> (query::c == 'a')) == 1);
+
+ char c ('b');
+ assert (size (db->query<object> (query::c == query::_val (c))) == 1);
+ assert (size (db->query<object> (query::c == query::_ref (c))) == 1);
+
+#ifndef MULTI_DATABASE
+ assert (size (db->query<object> ("c = " + query::_val ('c'))) == 1);
+ assert (size (db->query<object> ("c = " + query::_ref (c))) == 1);
+#endif
+
+ assert (size (db->query<object> (query::c == query::c1)) == 3);
+
+ // buffer
+ //
+#ifndef MULTI_DATABASE
+ assert (size (db->query<object> (query::b == buf)) == 3);
+ assert (size (db->query<object> (query::b == query::_val (buf))) == 3);
+#endif
+
+ assert (size (db->query<object> (query::b == query::_ref (buf))) == 3);
+
+#ifndef MULTI_DATABASE
+ assert (size (db->query<object> ("b = " + query::_val<bt> (buf))) == 3);
+ assert (size (db->query<object> ("b = " + query::_ref<bt> (buf))) == 3);
+#endif
+
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/query/array/test.hxx b/odb-tests/common/query/array/test.hxx
new file mode 100644
index 0000000..f0d5f3b
--- /dev/null
+++ b/odb-tests/common/query/array/test.hxx
@@ -0,0 +1,70 @@
+// file : common/query/array/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <array>
+#include <cstring> // std::memcpy, std::strlen
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object () {}
+ object (unsigned long id, const char* s, const char* b)
+ : id_ (id)
+ {
+ std::memcpy (s_, s, std::strlen (s) + 1); // VC++ strncpy deprecation.
+ std::memcpy (s1_, s, std::strlen (s) + 1);
+ std::memcpy (a_.data (), s, std::strlen (s) + 1);
+ c_ = c1_ = *s;
+ std::memcpy (b_, b, sizeof (b_));
+ }
+
+ #pragma db id
+ unsigned long id_;
+
+ char s_[17];
+ char s1_[17];
+
+#ifdef ODB_COMPILER
+# if defined(ODB_DATABASE_MYSQL) || \
+ defined(ODB_DATABASE_PGSQL) || \
+ defined(ODB_DATABASE_ORACLE) || \
+ defined(ODB_DATABASE_MSSQL)
+# pragma db type("VARCHAR(16)")
+# elif defined(ODB_DATABASE_SQLITE)
+# pragma db type("TEXT")
+# elif defined(ODB_DATABASE_COMMON)
+# pragma db type("DYMMU") // Necessary to make it a value.
+# else
+# error unknown database
+# endif
+#endif
+ std::array<char, 17> a_;
+
+ char c_;
+ char c1_;
+
+#ifdef ODB_COMPILER
+# if defined(ODB_DATABASE_MYSQL)
+# pragma db type("BINARY(16)")
+# elif defined(ODB_DATABASE_SQLITE)
+# pragma db type("BLOB")
+# elif defined(ODB_DATABASE_PGSQL)
+# pragma db type("BYTEA")
+# elif defined(ODB_DATABASE_ORACLE)
+# pragma db type("RAW(16)")
+# elif defined(ODB_DATABASE_MSSQL)
+# pragma db type("BINARY(16)")
+# elif defined(ODB_DATABASE_COMMON)
+# else
+# error unknown database
+# endif
+#endif
+ char b_[16];
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/query/array/testscript b/odb-tests/common/query/array/testscript
new file mode 100644
index 0000000..631ae24
--- /dev/null
+++ b/odb-tests/common/query/array/testscript
@@ -0,0 +1,33 @@
+# file : common/query/array/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/query/basics/buildfile b/odb-tests/common/query/basics/buildfile
new file mode 100644
index 0000000..e38e6fe
--- /dev/null
+++ b/odb-tests/common/query/basics/buildfile
@@ -0,0 +1,42 @@
+# file : common/query/basics/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix t_query_basics_ \
+ --generate-schema \
+ --generate-query \
+ --generate-prepared
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/query/basics/driver.cxx b/odb-tests/common/query/basics/driver.cxx
new file mode 100644
index 0000000..eb7e2a7
--- /dev/null
+++ b/odb-tests/common/query/basics/driver.cxx
@@ -0,0 +1,668 @@
+// file : common/query/basics/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test basic query support.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/config.hxx> // DATABASE_XXX
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+void
+print (result<person>& r)
+{
+ for (result<person>::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ unique_ptr<person> o (i.load ());
+ cout << *o << endl;
+ }
+ cout << endl;
+}
+
+const char* names[] = { "John", "Jane", "Joe" };
+const char** names_end = names + sizeof (names)/sizeof (names[0]);
+
+const char* key_data[] = { "\x23\x03\x15", "\x13\x13\x54", "\x08\x62\x35" };
+
+int
+main (int argc, char* argv[])
+{
+ buffer
+ key1 (key_data[0], key_data[0] + 3),
+ key2 (key_data[1], key_data[1] + 3),
+ key3 (key_data[2], key_data[2] + 3);
+
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+ odb::database_id db_id (db->id ());
+
+ typedef odb::query<person> query;
+ typedef odb::result<person> result;
+
+ //
+ //
+ {
+ person p1 (1, "John", "Doe", 30, true, key1);
+ person p2 (2, "Jane", "Doe", 29, true, key2);
+ person p3 (3, "Joe", "Dirt", 31, false, key3);
+ p3.middle_name_.reset (new string ("Squeaky"));
+ person p4 (4, "Johansen", "Johansen", 32, false);
+ p4.middle_name_.reset (new string ("J"));
+
+ transaction t (db->begin ());
+ db->persist (p1);
+ db->persist (p2);
+ db->persist (p3);
+ db->persist (p4);
+ t.commit ();
+ }
+
+ //
+ // Native queries.
+ //
+
+ // Compilation tests.
+ //
+#ifndef MULTI_DATABASE
+ if (false)
+ {
+ string name;
+ unsigned short age;
+
+ db->query<person> ("age = " + query::_ref (age));
+ db->query<person> ("age = " + query::_val (age));
+
+ query age_q (query::_ref (age) + " = age");
+ query name_q ("first = " + query::_val (name));
+ query q (age_q + "AND" + name_q);
+
+ db->query<person> (q);
+ db->query<person> (age_q + "OR" +
+ name_q + "OR" +
+ "age < " + query::_ref (age));
+
+ query q1 (query::_val (name));
+ q1 += " = first";
+ }
+#endif
+
+ // Select-all query.
+ //
+ cout << "test 001" << endl;
+ {
+ transaction t (db->begin ());
+ result r (db->query<person> ());
+
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ person p;
+ i.load (p);
+ cout << p << endl;
+ }
+
+ t.commit ();
+ }
+
+ // Select-all query with order by.
+ //
+ cout << "test 002" << endl;
+ {
+ transaction t (db->begin ());
+ result r (db->query<person> ("ORDER BY" + query::age));
+
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ person& p (*i);
+
+ cout << p.first_name_;
+
+ if (i->middle_name_.get () != 0)
+ cout << ' ' << *i->middle_name_;
+
+ cout << ' ' << i->last_name_ << ' ' << i->age_ <<
+ (i->married_ ? " married" : " single") << endl;
+ }
+ cout << endl;
+
+ t.commit ();
+ }
+
+ // String query.
+ //
+ cout << "test 003" << endl;
+ {
+ transaction t (db->begin ());
+
+ result r;
+ if (db_id != odb::id_oracle)
+ r = db->query<person> ("age >= 30 AND last = 'Doe'");
+ else
+ r = db->query<person> ("\"age\" >= 30 AND \"last\" = 'Doe'");
+
+ print (r);
+ t.commit ();
+ }
+
+ // Value binding.
+ //
+ cout << "test 004" << endl;
+ {
+ transaction t (db->begin ());
+
+ const char* name = "Doe";
+
+#if defined(MULTI_DATABASE)
+ result r (
+ db->query<person> (
+ query::age >= query::_val (30) &&
+ query::last_name == query::_val (name)));
+
+#elif defined(DATABASE_ORACLE)
+ result r (
+ db->query<person> (
+ "\"age\" >= " + query::_val (30) + "AND" +
+ "\"last\" = " + query::_val (name)));
+#else
+ result r (
+ db->query<person> (
+ "age >= " + query::_val (30) + "AND" +
+ "last = " + query::_val (name)));
+#endif
+
+ print (r);
+ t.commit ();
+ }
+
+ // Reference binding.
+ //
+ cout << "test 005" << endl;
+ {
+ transaction t (db->begin ());
+
+ string name;
+ unsigned short age (0);
+
+#if defined(MULTI_DATABASE)
+ query q (query::age >= query::_ref (age) &&
+ query::last_name == query::_ref (name));
+#elif defined(DATABASE_ORACLE)
+ query q ("\"age\" >= " + query::_ref (age) + "AND" +
+ "\"last\" = " + query::_ref (name));
+#else
+ query q ("age >= " + query::_ref (age) + "AND" +
+ "last = " + query::_ref (name));
+#endif
+
+ name = "Doe";
+ age = 30;
+ result r (db->query<person> (q));
+ print (r);
+
+ name = "Dirt";
+ age = 31;
+ r = db->query<person> (q);
+ print (r);
+
+ t.commit ();
+ }
+
+ //
+ // Language-embedded queries.
+ //
+
+ // Compilation tests.
+ //
+ if (false)
+ {
+ string name;
+ unsigned short age;
+
+ // Column operators.
+ //
+ query q1 (query::married);
+ db->query<person> (query::married);
+ db->query<person> (query::married == true);
+
+ //db->query<person> (query::age);
+
+ db->query<person> (query::age == 30);
+ db->query<person> (query::age == age);
+ db->query<person> (query::age == query::_val (30));
+ db->query<person> (query::age == query::_val (age));
+ db->query<person> (query::age == query::_ref (age));
+ //db->query<person> (query::age == "123");
+ //db->query<person> ("123" == query::age);
+ //db->query<person> (query::age == query::_val ("123"));
+ //db->query<person> (query::age == query::_ref (name));
+ db->query<person> (query::last_name == "Doe");
+ db->query<person> (query::last_name == name);
+#ifndef MULTI_DATABASE
+ db->query<person> (query::last_name == query::_val ("Doe"));
+#endif
+ db->query<person> (query::last_name == query::_val (name));
+ db->query<person> (query::last_name == query::_ref (name));
+ //db->query<person> (query::last_name == 30);
+ //db->query<person> (query::last_name == query::_val (30));
+ //db->query<person> (query::last_name == query::_ref (age));
+
+ db->query<person> (query::last_name.is_null ());
+ db->query<person> (query::last_name.is_not_null ());
+
+ db->query<person> (query::first_name == query::last_name);
+
+ db->query<person> (query::first_name.in ("John", "Jane"));
+ db->query<person> (query::first_name.in_range (names, names_end));
+
+ db->query<person> (query::first_name.like ("J%"));
+ db->query<person> (query::first_name.like ("J%!%", "!"));
+
+ // Query operators.
+ //
+ db->query<person> (query::age == 30 && query::last_name == "Doe");
+ db->query<person> (query::age == 30 || query::last_name == "Doe");
+ db->query<person> (!(query::age == 30 || query::last_name == "Doe"));
+ db->query<person> ((query::last_name == "Doe") + "ORDER BY age");
+ }
+
+ // Test is_null/is_not_null.
+ //
+ cout << "test 006" << endl;
+ {
+ transaction t (db->begin ());
+ result r (db->query<person> (query::middle_name.is_null ()));
+ print (r);
+ r = db->query<person> (query::middle_name.is_not_null ());
+ print (r);
+ t.commit ();
+ }
+
+ // Test boolean columns.
+ //
+ cout << "test 007" << endl;
+ {
+ transaction t (db->begin ());
+ result r (db->query<person> (query::married));
+ print (r);
+ r = db->query<person> (!query::married);
+ print (r);
+ t.commit ();
+ }
+
+ // Test implicit by-value, explicit by-value, and by-reference.
+ //
+ cout << "test 008" << endl;
+ {
+ string name ("Dirt");
+
+ transaction t (db->begin ());
+ result r (db->query<person> (query::last_name == "Doe"));
+ print (r);
+ r = db->query<person> (query::last_name == query::_val (name));
+ print (r);
+ query q (query::last_name == query::_ref (name));
+ name = "Doe";
+ r = db->query<person> (q);
+ print (r);
+ t.commit ();
+ }
+
+ // Test column operators (==, !=, <, >, <=, >=).
+ //
+ cout << "test 009" << endl;
+ {
+ transaction t (db->begin ());
+
+ // ==
+ //
+ result r (db->query<person> (query::last_name == "Doe"));
+ print (r);
+
+ // !=
+ //
+ r = db->query<person> (query::last_name != "Doe");
+ print (r);
+
+ // <
+ //
+ r = db->query<person> (query::age < 31);
+ print (r);
+
+ // >
+ //
+ r = db->query<person> (query::age > 30);
+ print (r);
+
+ // <=
+ //
+ r = db->query<person> (query::age <= 30);
+ print (r);
+
+ // >=
+ //
+ r = db->query<person> (query::age >= 31);
+ print (r);
+
+ t.commit ();
+ }
+
+ // Test query operators (&&, ||, (), !, +).
+ //
+ cout << "test 010" << endl;
+ {
+ transaction t (db->begin ());
+
+ // &&
+ //
+ result r (db->query<person> (
+ query::last_name == "Doe" && query::age == 29));
+ print (r);
+
+ // ||
+ //
+ r = db->query<person> (query::last_name == "Doe" || query::age == 31);
+ print (r);
+
+ // ()
+ //
+ r = db->query<person> (
+ (query::last_name != "Doe" || query::age == 29) && query::married);
+ print (r);
+
+ // !=
+ //
+ r = db->query<person> (!(query::last_name == "Doe"));
+ print (r);
+
+ // +
+ //
+ r = db->query<person> ((query::last_name == "Doe") +
+ "ORDER BY" + query::age);
+ print (r);
+
+ t.commit ();
+ }
+
+ // Test in/in_range.
+ //
+ cout << "test 011" << endl;
+ {
+ transaction t (db->begin ());
+
+ result r (db->query<person> (query::first_name.in ("John", "Jane")));
+ print (r);
+
+ r = db->query<person> (query::first_name.in_range (names, names_end));
+ print (r);
+
+ // Empty range.
+ //
+ r = db->query<person> (query::last_name == "Doe" &&
+ query::first_name.in_range (names, names));
+ assert (r.empty ());
+
+ t.commit ();
+ }
+
+ // Test column-to-column comparison.
+ //
+ cout << "test 012" << endl;
+ {
+ transaction t (db->begin ());
+ result r (db->query<person> (query::first_name == query::last_name));
+ print (r);
+ t.commit ();
+ }
+
+ // Test value_traits::value_type != value_traits::query_type.
+ //
+ cout << "test 013" << endl;
+ {
+ transaction t (db->begin ());
+ result r (db->query<person> (query::middle_name == "Squeaky"));
+ print (r);
+ t.commit ();
+ }
+
+ // Test that loading of the same object type during iteration does
+ // not invalidate the result.
+ //
+ cout << "test 014" << endl;
+ {
+ transaction t (db->begin ());
+ result r (db->query<person> (query::last_name == "Doe"));
+
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ ++i;
+ assert (i != r.end ());
+
+ {
+ unique_ptr<person> joe (db->load<person> (3));
+ }
+
+ {
+ person p (5, "Peter", "Peterson", 70, false, key3);
+ db->persist (p);
+ db->erase (p);
+ }
+
+ // SQL Server does not support re-loading of an object with long data
+ // from a query result.
+ //
+ if (db_id != odb::id_mssql)
+ assert (i->last_name_ == "Doe"); // Actual load.
+
+ // Overwrite object image again.
+ //
+ unique_ptr<person> joe (db->load<person> (3));
+ person p;
+ i.load (p);
+ assert (p.last_name_ == "Doe");
+
+ t.commit ();
+ }
+
+ // Test uncached result.
+ //
+ cout << "test 015" << endl;
+ {
+ transaction t (db->begin ());
+ result r (db->query<person> (query::last_name == "Doe", false));
+ print (r);
+ t.commit ();
+ }
+
+ // Test BLOB column operations.
+ //
+ cout << "test 016" << endl;
+ {
+ transaction t (db->begin ());
+
+ result r;
+ result::iterator i;
+
+ // ==
+ //
+
+ // Oracle does not support LOB comparisons.
+ //
+#if defined(MULTI_DATABASE) || !defined(DATABASE_ORACLE)
+ if (db_id != odb::id_oracle)
+ {
+ r = db->query<person> (query::public_key == key2);
+
+ i = r.begin ();
+ assert (i != r.end ());
+
+ assert (*i->public_key_ == key2);
+ assert (++i == r.end ());
+ }
+#endif
+
+ // is_null
+ //
+ r = db->query<person> (query::public_key.is_null ());
+
+ i = r.begin ();
+ assert (i != r.end ());
+
+ assert (i->first_name_ == "Johansen" && i->last_name_ == "Johansen");
+ assert (++i == r.end ());
+
+ // is_not_null
+ //
+ r = db->query<person> (query::public_key.is_not_null ());
+
+ i = r.begin ();
+ assert (i != r.end ());
+
+ assert (i->first_name_ == "John" && i->last_name_ == "Doe");
+ assert (++i != r.end ());
+
+ assert (i->first_name_ == "Jane" && i->last_name_ == "Doe");
+ assert (++i != r.end ());
+
+ assert (i->first_name_ == "Joe" && i->last_name_ == "Dirt");
+ assert (++i == r.end ());
+
+ t.commit ();
+ }
+
+ // Test iterator::id().
+ //
+ cout << "test 017" << endl;
+ {
+ transaction t (db->begin ());
+ result r (db->query<person> (query::last_name == "Dirt"));
+
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i.id () == 3);
+
+ t.commit ();
+ }
+
+ // Test empty result set.
+ //
+ cout << "test 018" << endl;
+ {
+ {
+ transaction t (db->begin ());
+ result r (db->query<person> (query::last_name == "None"));
+ assert (r.empty ());
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ result r (db->query<person> (query::last_name == "None"));
+ assert (r.begin () == r.end ());
+ t.commit ();
+ }
+ }
+
+ // Test size() validity at the beginning/middle/end of result set.
+ //
+ cout << "test 019" << endl;
+ if (db_id != odb::id_sqlite &&
+ db_id != odb::id_oracle &&
+ db_id != odb::id_mssql)
+ {
+ {
+ transaction t (db->begin ());
+ result r (db->query<person> ());
+ assert (r.size () == 4);
+ result::iterator i (r.begin ());
+ assert (r.size () == 4);
+ ++i;
+ ++i;
+ ++i;
+ assert (r.size () == 4);
+ ++i;
+ assert (r.size () == 4);
+ }
+
+ {
+ transaction t (db->begin ());
+ result r (db->query<person> (false));
+ result::iterator i (r.begin ());
+ ++i;
+ ++i;
+ r.cache (); // Cache in the middle.
+ assert (r.size () == 4);
+ ++i;
+ assert (r.size () == 4);
+ ++i;
+ assert (r.size () == 4);
+ }
+
+ {
+ transaction t (db->begin ());
+ result r (db->query<person> (false));
+ result::iterator i (r.begin ());
+ ++i;
+ ++i;
+ ++i;
+ r.cache (); // Cache at the end.
+ assert (r.size () == 4);
+ }
+
+ {
+ transaction t (db->begin ());
+ result r (db->query<person> (query::last_name == "None"));
+ assert (r.size () == 0);
+ for (result::iterator i (r.begin ()); i != r.end (); ++i) ;
+ assert (r.size () == 0);
+ }
+ }
+
+ // Test like.
+ //
+ cout << "test 020" << endl;
+ {
+ transaction t (db->begin ());
+
+ result r (db->query<person> (query::first_name.like ("Jo%")));
+ print (r);
+
+ r = db->query<person> (!query::first_name.like ("Jo%"));
+ print (r);
+
+ r = db->query<person> (query::first_name.like ("Jo!%", "!"));
+ print (r);
+
+ // In Oracle one can only escape special characters (% and _).
+ //
+ string v;
+ if (db_id != odb::id_oracle)
+ v = "!Ja%";
+ else
+ v = "Ja%";
+
+ r = db->query<person> (query::first_name.like (query::_ref (v), "!"));
+ print (r);
+
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/query/basics/test.hxx b/odb-tests/common/query/basics/test.hxx
new file mode 100644
index 0000000..239f6d6
--- /dev/null
+++ b/odb-tests/common/query/basics/test.hxx
@@ -0,0 +1,117 @@
+// file : common/query/basics/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <iostream>
+
+#include <odb/core.hxx>
+#include <odb/nullable.hxx>
+
+#ifdef ODB_COMPILER
+# if defined(ODB_DATABASE_PGSQL)
+# define BLOB_TYPE "BYTEA"
+# elif defined(ODB_DATABASE_MSSQL)
+# define BLOB_TYPE "VARBINARY(max)"
+# else
+# define BLOB_TYPE "BLOB"
+# endif
+#endif
+
+typedef std::vector<char> buffer;
+typedef odb::nullable<buffer> nullable_buffer;
+
+#pragma db object
+struct person
+{
+ person (unsigned long id,
+ const std::string& fn,
+ const std::string& ln,
+ unsigned short age,
+ bool married,
+ const nullable_buffer& public_key = nullable_buffer ())
+ : id_ (id),
+ first_name_ (fn),
+ last_name_ (ln),
+ age_ (age),
+ married_ (married),
+ public_key_ (public_key)
+ {
+ }
+
+ person ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db column ("first")
+ std::string first_name_;
+
+ #pragma db column ("middle") null
+ std::unique_ptr<std::string> middle_name_;
+
+ #pragma db column ("last")
+ std::string last_name_;
+
+ unsigned short age_;
+ bool married_;
+
+ #pragma db column ("key") type(BLOB_TYPE) null
+ nullable_buffer public_key_;
+};
+
+inline std::ostream&
+operator<< (std::ostream& os, const person& p)
+{
+ os << p.first_name_;
+
+ if (p.middle_name_.get () != 0)
+ os << ' ' << *p.middle_name_;
+
+ os << ' ' << p.last_name_ << ' ' << p.age_ <<
+ (p.married_ ? " married" : " single");
+
+ return os;
+}
+
+// Test member name conflicts (compilation-only test).
+//
+#pragma db namespace table("t2_")
+namespace test2
+{
+ #pragma db object
+ struct object
+ {
+ #pragma db id
+ int id;
+ };
+
+ #pragma db value
+ struct value
+ {
+ object* m_object;
+ };
+
+ #pragma db value
+ struct bar
+ {
+ value m_value;
+ };
+
+ #pragma db object
+ struct foo
+ {
+ #pragma db id
+ int id;
+
+ bar m_value;
+ };
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/query/basics/testscript b/odb-tests/common/query/basics/testscript
new file mode 100644
index 0000000..9086b66
--- /dev/null
+++ b/odb-tests/common/query/basics/testscript
@@ -0,0 +1,150 @@
+# file : common/query/basics/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
++cat <<EOI >=output
+ test 001
+ John Doe 30 married
+ Jane Doe 29 married
+ Joe Squeaky Dirt 31 single
+ Johansen J Johansen 32 single
+ test 002
+ Jane Doe 29 married
+ John Doe 30 married
+ Joe Squeaky Dirt 31 single
+ Johansen J Johansen 32 single
+
+ test 003
+ John Doe 30 married
+
+ test 004
+ John Doe 30 married
+
+ test 005
+ John Doe 30 married
+
+ Joe Squeaky Dirt 31 single
+
+ test 006
+ John Doe 30 married
+ Jane Doe 29 married
+
+ Joe Squeaky Dirt 31 single
+ Johansen J Johansen 32 single
+
+ test 007
+ John Doe 30 married
+ Jane Doe 29 married
+
+ Joe Squeaky Dirt 31 single
+ Johansen J Johansen 32 single
+
+ test 008
+ John Doe 30 married
+ Jane Doe 29 married
+
+ Joe Squeaky Dirt 31 single
+
+ John Doe 30 married
+ Jane Doe 29 married
+
+ test 009
+ John Doe 30 married
+ Jane Doe 29 married
+
+ Joe Squeaky Dirt 31 single
+ Johansen J Johansen 32 single
+
+ John Doe 30 married
+ Jane Doe 29 married
+
+ Joe Squeaky Dirt 31 single
+ Johansen J Johansen 32 single
+
+ John Doe 30 married
+ Jane Doe 29 married
+
+ Joe Squeaky Dirt 31 single
+ Johansen J Johansen 32 single
+
+ test 010
+ Jane Doe 29 married
+
+ John Doe 30 married
+ Jane Doe 29 married
+ Joe Squeaky Dirt 31 single
+
+ Jane Doe 29 married
+
+ Joe Squeaky Dirt 31 single
+ Johansen J Johansen 32 single
+
+ Jane Doe 29 married
+ John Doe 30 married
+
+ test 011
+ John Doe 30 married
+ Jane Doe 29 married
+
+ John Doe 30 married
+ Jane Doe 29 married
+ Joe Squeaky Dirt 31 single
+
+ test 012
+ Johansen J Johansen 32 single
+
+ test 013
+ Joe Squeaky Dirt 31 single
+
+ test 014
+ test 015
+ John Doe 30 married
+ Jane Doe 29 married
+
+ test 016
+ test 017
+ test 018
+ test 019
+ test 020
+ John Doe 30 married
+ Joe Squeaky Dirt 31 single
+ Johansen J Johansen 32 single
+
+ Jane Doe 29 married
+
+
+ Jane Doe 29 married
+
+ EOI
+
+test.redirects += >>>../output
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/query/one/buildfile b/odb-tests/common/query/one/buildfile
new file mode 100644
index 0000000..76a36b0
--- /dev/null
+++ b/odb-tests/common/query/one/buildfile
@@ -0,0 +1,42 @@
+# file : common/query/one/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix t_query_one_ \
+ --generate-schema \
+ --generate-query \
+ --generate-prepared
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/query/one/driver.cxx b/odb-tests/common/query/one/driver.cxx
new file mode 100644
index 0000000..4c3dcdc
--- /dev/null
+++ b/odb-tests/common/query/one/driver.cxx
@@ -0,0 +1,205 @@
+// file : common/query/one/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test query one support.
+//
+// We assume that other tests in common/query/ exercise a variety of
+// different kinds of queries. Here we are concerned with what is
+// specific to query_one() and query_value().
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+ odb::database_id db_id (db->id ());
+
+ transaction t (db->begin ());
+
+ // query_one()
+ //
+ {
+ unique_ptr<object> o (db->query_one<object> ());
+ assert (o.get () == 0);
+ }
+
+ {
+ object o (4);
+ assert (!db->query_one<object> (o) && o.id_ == 4 && o.str_.empty ());
+ }
+
+ /*
+ {
+ object o (db->query_value<object> ());
+ assert (false);
+ }
+ */
+
+ object o (1);
+ o.str_ = "value 1";
+ db->persist (o);
+
+ {
+ unique_ptr<object> o (db->query_one<object> ());
+ assert (o.get () != 0 && o->str_ == "value 1");
+ }
+
+ {
+ object o;
+ assert (db->query_one<object> (o) && o.str_ == "value 1");
+ }
+
+ {
+ object o (db->query_value<object> ());
+ assert (o.str_ == "value 1");
+ }
+
+ // query_one(const char*)
+ //
+ const char* q1_c (db_id == odb::id_oracle ? "\"id\" = 1" : "id = 1");
+ const char* q0_c (db_id == odb::id_oracle ? "\"id\" = 2" : "id = 2");
+
+ {
+ unique_ptr<object> o (db->query_one<object> (q1_c));
+ assert (o.get () != 0 && o->str_ == "value 1");
+ }
+
+ {
+ unique_ptr<object> o (db->query_one<object> (q0_c));
+ assert (o.get () == 0);
+ }
+
+ {
+ object o;
+ assert (db->query_one<object> (q1_c, o) && o.str_ == "value 1");
+ }
+
+ {
+ object o (4);
+ assert (!db->query_one<object> (q0_c, o) &&
+ o.id_ == 4 && o.str_.empty ());
+ }
+
+ {
+ object o (db->query_value<object> (q1_c));
+ assert (o.str_ == "value 1");
+ }
+
+ // query_one(std::string)
+ //
+ string q1_s (q1_c);
+ string q0_s (q0_c);
+
+ {
+ unique_ptr<object> o (db->query_one<object> (q1_s));
+ assert (o.get () != 0 && o->str_ == "value 1");
+ }
+
+ {
+ unique_ptr<object> o (db->query_one<object> (q0_s));
+ assert (o.get () == 0);
+ }
+
+ {
+ object o;
+ assert (db->query_one<object> (q1_s, o) && o.str_ == "value 1");
+ }
+
+ {
+ object o (4);
+ assert (!db->query_one<object> (q0_s, o) &&
+ o.id_ == 4 && o.str_.empty ());
+ }
+
+ {
+ object o (db->query_value<object> (q1_s));
+ assert (o.str_ == "value 1");
+ }
+
+ // query_one(odb::query)
+ //
+ typedef odb::query<object> query;
+
+ query q1 (query::id == 1);
+ query q0 (query::id == 2);
+
+ {
+ unique_ptr<object> o (db->query_one<object> (q1));
+ assert (o.get () != 0 && o->str_ == "value 1");
+ }
+
+ {
+ unique_ptr<object> o (db->query_one<object> (q0));
+ assert (o.get () == 0);
+ }
+
+ {
+ object o;
+ assert (db->query_one<object> (q1, o) && o.str_ == "value 1");
+ }
+
+ {
+ object o (4);
+ assert (!db->query_one<object> (q0, o) && o.id_ == 4 && o.str_.empty ());
+ }
+
+ {
+ object o (db->query_value<object> (q1));
+ assert (o.str_ == "value 1");
+ }
+
+ // Assertion on more than one element.
+ //
+ {
+ object o (2);
+ o.str_ = "value 2";
+ db->persist (o);
+ }
+
+ /*
+ {
+ unique_ptr<object> o (db->query_one<object> ());
+ assert (false);
+ }
+ */
+
+ /*
+ {
+ object o;
+ db->query_one<object> (o);
+ assert (false);
+ }
+ */
+
+ /*
+ {
+ object o (db->query_value<object> ());
+ assert (false);
+ }
+ */
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/query/one/test.hxx b/odb-tests/common/query/one/test.hxx
new file mode 100644
index 0000000..3008063
--- /dev/null
+++ b/odb-tests/common/query/one/test.hxx
@@ -0,0 +1,26 @@
+// file : common/query/one/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+
+#pragma db object
+struct object
+{
+ object (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+ std::string str_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/query/one/testscript b/odb-tests/common/query/one/testscript
new file mode 100644
index 0000000..963a206
--- /dev/null
+++ b/odb-tests/common/query/one/testscript
@@ -0,0 +1,33 @@
+# file : common/query/one/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/readonly/buildfile b/odb-tests/common/readonly/buildfile
new file mode 100644
index 0000000..2d83cf1
--- /dev/null
+++ b/odb-tests/common/readonly/buildfile
@@ -0,0 +1,40 @@
+# file : common/readonly/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix readonly_ \
+ --generate-schema
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/readonly/driver.cxx b/odb-tests/common/readonly/driver.cxx
new file mode 100644
index 0000000..b207627
--- /dev/null
+++ b/odb-tests/common/readonly/driver.cxx
@@ -0,0 +1,324 @@
+// file : common/readonly/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test readonly members/objects. Also test that const members are
+// treated as readonly. For other const member tests, see the const-
+// member test.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/config.hxx> // DATABASE_*
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Simple.
+ //
+ {
+ simple o (1, 1);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ o.ro++;
+ const_cast<unsigned long&> (o.co)++;
+ o.rw++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->load<simple> (1, o);
+ t.commit ();
+ }
+
+ assert (o.ro == 1 && o.co == 1 && o.rw == 2);
+ }
+
+ // Pointer.
+ //
+ {
+ pointer p (1, new pointer (2));
+ unique_ptr<pointer> p1 (new pointer (3));
+
+ {
+ transaction t (db->begin ());
+ db->persist (p);
+ db->persist (p.ro);
+ db->persist (*p1);
+ t.commit ();
+ }
+
+ delete p.ro;
+ p.ro = p1.release ();
+ const_cast<pointer*&> (p.co) = p.ro;
+ p.rw = p.ro;
+
+ {
+ transaction t (db->begin ());
+ db->update (p);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<pointer> p (db->load<pointer> (1));
+ t.commit ();
+
+ assert (p->ro->id == 2 && p->co->id == 2 && p->rw->id == 3);
+ }
+ }
+
+ // Composite.
+ //
+ {
+ composite o (1, 1);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ o.ro.v++;
+ o.ro.ro++;
+ const_cast<unsigned long&> (o.ro.co)++;
+ o.ro.rw++;
+
+ value& co (const_cast<value&> (o.co));
+ co.v++;
+ co.ro++;
+ const_cast<unsigned long&> (co.co)++;
+ co.rw++;
+
+ o.rw.v++;
+ o.rw.ro++;
+ const_cast<unsigned long&> (o.rw.co)++;
+ o.rw.rw++;
+
+ o.v.v++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->load<composite> (1, o);
+ t.commit ();
+ }
+
+ assert (o.ro.v == 1 &&
+ o.ro.ro == 1 &&
+ o.ro.co == 1 &&
+ o.ro.rw == 1 &&
+
+ o.co.v == 1 &&
+ o.co.ro == 1 &&
+ o.co.co == 1 &&
+ o.co.rw == 1 &&
+
+ o.rw.v == 1 &&
+ o.rw.ro == 1 &&
+ o.rw.co == 1 &&
+ o.rw.rw == 2 &&
+
+ o.v.v == 1);
+ }
+
+ // Container.
+ //
+ {
+ typedef vector<unsigned long> ulongs;
+
+ container o (1);
+
+ o.ro.push_back (1);
+ o.ro.push_back (2);
+
+ ulongs& co (const_cast<ulongs&> (o.co));
+ co.push_back (1);
+ co.push_back (2);
+
+ o.rw.push_back (1);
+ o.rw.push_back (2);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ o.ro[0]++;
+ o.ro.pop_back ();
+
+ co[0]++;
+ co.pop_back ();
+
+ o.rw[0]++;
+ o.rw.pop_back ();
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->load<container> (1, o);
+ t.commit ();
+ }
+
+ assert (o.ro.size () == 2 && o.ro[0] == 1 && o.ro[1] == 2 &&
+ o.co.size () == 2 && o.co[0] == 1 && o.co[1] == 2 &&
+ o.rw.size () == 1 && o.rw[0] == 2);
+ }
+
+ // Readonly object.
+ //
+ {
+#ifndef MULTI_DATABASE
+ typedef odb::object_traits_impl<simple_object, odb::id_common> so_traits;
+ typedef odb::object_traits_impl<ro_object, odb::id_common> ro_traits;
+ typedef odb::object_traits_impl<rw_object, odb::id_common> rw_traits;
+
+ assert (so_traits::column_count ==
+ so_traits::id_column_count + so_traits::readonly_column_count);
+
+ assert (ro_traits::column_count ==
+ ro_traits::id_column_count + ro_traits::readonly_column_count);
+
+ assert (rw_traits::column_count !=
+ rw_traits::id_column_count + rw_traits::readonly_column_count);
+#endif
+
+ simple_object so (1, 1);
+ ro_object ro_o (1, 1);
+ rw_object rw_o (1, 1);
+
+ ro_o.cr.push_back (1);
+ ro_o.cr.push_back (2);
+
+ rw_o.cr.push_back (1);
+ rw_o.cr.push_back (2);
+
+ {
+ transaction t (db->begin ());
+ db->persist (so);
+ db->persist (ro_o);
+ db->persist (rw_o);
+ t.commit ();
+ }
+
+ rw_o.sv++;
+ rw_o.rw_sv++;
+
+ {
+ transaction t (db->begin ());
+ //db->update (so); // Compile error.
+ //db->update (ro_o); // Compile error.
+ db->update (rw_o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->load (1, so);
+ db->load (1, ro_o);
+ db->load (1, rw_o);
+ t.commit ();
+ }
+
+ assert (rw_o.sv == 1 && rw_o.rw_sv == 2);
+ }
+
+ // Readonly object.
+ //
+ {
+ wrapper o (1, 1);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ *o.pl = 2;
+ *o.cpl = 2;
+ o.pcl.reset (new unsigned long (2));
+ const_cast<unsigned long&> (*o.cpcl) = 2;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->load<wrapper> (1, o);
+ t.commit ();
+ }
+
+ assert (*o.pl == 2 && *o.cpl == 2 && *o.pcl == 2 && *o.cpcl == 1);
+ }
+
+ // Readonly object with auto id.
+ //
+ {
+ ro_auto o1 (1);
+ ro_auto o2 (2);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<ro_auto> p1 (db->load<ro_auto> (o1.id));
+ unique_ptr<ro_auto> p2 (db->load<ro_auto> (o2.id));
+ t.commit ();
+
+ assert (p1->num == o1.num);
+ assert (p2->num == o2.num);
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/readonly/test.hxx b/odb-tests/common/readonly/test.hxx
new file mode 100644
index 0000000..45797b6
--- /dev/null
+++ b/odb-tests/common/readonly/test.hxx
@@ -0,0 +1,225 @@
+// file : common/readonly/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <vector>
+#include <memory> // std::unique_ptr
+
+#include <odb/core.hxx>
+
+// Simple readonly object.
+//
+#pragma db object
+struct simple
+{
+ simple (unsigned long i, unsigned long x)
+ : id (i), ro (x), co (x), rw (x)
+ {
+ }
+
+ simple (): co (0) {}
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db readonly
+ unsigned long ro;
+
+ const unsigned long co;
+
+ unsigned long rw;
+};
+
+// Readonly pointer.
+//
+#pragma db object
+struct pointer
+{
+ pointer (unsigned long i, pointer* p = 0): id (i), ro (p), co (p), rw (p) {}
+ pointer (): ro (0), co (0), rw (0) {}
+
+ ~pointer ()
+ {
+ delete ro;
+
+ if (co != ro)
+ delete co;
+
+ if (rw != ro && rw != co)
+ delete rw;
+ }
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db readonly
+ pointer* ro;
+
+ pointer* const co;
+
+ pointer* rw;
+};
+
+// Composite readonly value as well as simple readonly value inside
+// a composite.
+//
+#pragma db value readonly
+struct ro_value
+{
+ ro_value () {}
+ ro_value (unsigned long x): v (x) {}
+
+ unsigned long v;
+};
+
+#pragma db value
+struct value: ro_value
+{
+ value (): co (0) {}
+ value (unsigned long x): ro_value (x), ro (x), co (x), rw (x) {}
+
+ #pragma db readonly
+ unsigned long ro;
+
+ const unsigned long co;
+
+ unsigned long rw;
+};
+
+#pragma db object
+struct composite
+{
+ composite (unsigned long i, unsigned long x)
+ : id (i), ro (x), co (x), rw (x), v (x)
+ {
+ }
+
+ composite () {}
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db readonly
+ value ro;
+
+ const value co;
+
+ value rw;
+ ro_value v;
+};
+
+// Readonly container.
+//
+#pragma db object
+struct container
+{
+ container (unsigned long i): id (i) {}
+ container () {}
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db readonly
+ std::vector<unsigned long> ro;
+
+ const std::vector<unsigned long> co;
+
+ std::vector<unsigned long> rw;
+};
+
+// Readonly object.
+//
+#pragma db object readonly
+struct simple_object
+{
+ simple_object (unsigned long i, unsigned long x): id (i), sv (x) {}
+ simple_object () {}
+
+ #pragma db id
+ unsigned long id;
+
+ unsigned long sv;
+};
+
+#pragma db object
+struct object
+{
+ object (unsigned long i, unsigned long x): id (i), sv (x) {}
+ object () {}
+
+ #pragma db id
+ unsigned long id;
+
+ unsigned long sv;
+};
+
+#pragma db object readonly
+struct ro_object: object
+{
+ ro_object (unsigned long i, unsigned long x)
+ : object (i, x), cv (x)
+ {
+ }
+
+ ro_object () {}
+
+ value cv;
+ std::vector<unsigned long> cr;
+};
+
+#pragma db object
+struct rw_object: ro_object
+{
+ rw_object (unsigned long i, unsigned long x)
+ : ro_object (i, x), rw_sv (x)
+ {
+ }
+
+ rw_object () {}
+
+ unsigned long rw_sv;
+};
+
+// Readonly wrappers. Here we make sure that only const wrappers with
+// const wrapped types are automatically treated as readonly.
+//
+#pragma db object
+struct wrapper
+{
+ wrapper (unsigned long i, unsigned long x)
+ : id (i),
+ pl (new unsigned long (x)),
+ cpl (new unsigned long (x)),
+ pcl (new unsigned long (x)),
+ cpcl (new unsigned long (x))
+ {
+ }
+
+ wrapper () {}
+
+ #pragma db id
+ unsigned long id;
+
+ std::unique_ptr<unsigned long> pl;
+ const std::unique_ptr<unsigned long> cpl;
+ std::unique_ptr<const unsigned long> pcl;
+ const std::unique_ptr<const unsigned long> cpcl;
+};
+
+// Readonly object with auto id.
+//
+#pragma db object readonly
+struct ro_auto
+{
+ ro_auto (unsigned long n): num (n) {}
+ ro_auto () {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ unsigned long num;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/readonly/testscript b/odb-tests/common/readonly/testscript
new file mode 100644
index 0000000..c798201
--- /dev/null
+++ b/odb-tests/common/readonly/testscript
@@ -0,0 +1,33 @@
+# file : common/readonly/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/relationship/basics/buildfile b/odb-tests/common/relationship/basics/buildfile
new file mode 100644
index 0000000..d7bbb7e
--- /dev/null
+++ b/odb-tests/common/relationship/basics/buildfile
@@ -0,0 +1,41 @@
+# file : common/relationship/basics/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix t_rel_basics_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/relationship/basics/driver.cxx b/odb-tests/common/relationship/basics/driver.cxx
new file mode 100644
index 0000000..e27c127
--- /dev/null
+++ b/odb-tests/common/relationship/basics/driver.cxx
@@ -0,0 +1,150 @@
+// file : common/relationship/basics/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test object relationships.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ aggr a ("aggr");
+ a.o1 = new obj1 ("o1", "obj1");
+ a.o2.reset (new obj2 ("obj2"));
+
+ a.v2.push_back (obj2_ptr (new obj2 ("v1 obj2 1")));
+ a.v2.push_back (0);
+ a.v2.push_back (obj2_ptr (new obj2 ("v1 obj2 2")));
+
+ a.o3.reset (new obj3 ("obj3"));
+
+ a.c.num = 123;
+ a.c.o3.reset (new obj3 ("c"));
+
+ a.cv.push_back (comp (234, obj3_ptr (new obj3 ("cv 0"))));
+ a.cv.push_back (comp (235, obj3_ptr ()));
+ a.cv.push_back (comp (236, obj3_ptr (new obj3 ("cv 2"))));
+
+ a.v1.push_back (new obj1 ("v1 0", "v1 0"));
+ a.v1.push_back (0);
+ a.v1.push_back (new obj1 ("v1 2", "v1 2"));
+
+ // Set cannot contain NULL pointers.
+ //
+ a.s1.insert (new obj1 ("s1 0", "s1 0"));
+ a.s1.insert (new obj1 ("s1 2", "s1 2"));
+
+ a.m1[0] = new obj1 ("m1 0", "m1 0");
+ a.m1[1] = 0;
+ a.m1[2] = new obj1 ("m1 2", "m1 2");
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (a.o1);
+ db->persist (a.o2);
+
+ for (obj2_vec::iterator i (a.v2.begin ()); i != a.v2.end (); ++i)
+ if (*i)
+ db->persist (*i);
+
+ db->persist (a.o3);
+
+ db->persist (a.c.o3);
+
+ for (comp_vec::iterator i (a.cv.begin ()); i != a.cv.end (); ++i)
+ if (i->o3)
+ db->persist (i->o3);
+
+ for (obj1_vec::iterator i (a.v1.begin ()); i != a.v1.end (); ++i)
+ if (*i)
+ db->persist (*i);
+
+ for (obj1_set::iterator i (a.s1.begin ()); i != a.s1.end (); ++i)
+ if (*i)
+ db->persist (*i);
+
+ for (obj1_map::iterator i (a.m1.begin ()); i != a.m1.end (); ++i)
+ if (i->second)
+ db->persist (i->second);
+
+ db->persist (a);
+ t.commit ();
+ }
+
+ // load & compare
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<aggr> a1 (db->load<aggr> (a.id));
+ t.commit ();
+
+ assert (*a1 == a);
+ }
+
+ // query
+ //
+ typedef odb::query<aggr> query;
+ typedef odb::result<aggr> result;
+
+ {
+ transaction t (db->begin ());
+
+ result r (db->query<aggr> (query::o1->str == "obj1"));
+ assert (!r.empty ());
+ assert (r.begin ()->o1->id == a.o1->id);
+ assert (size (r) == 1);
+
+ t.commit ();
+ }
+
+ // Test NULL pointer.
+ //
+ delete a.o1;
+ a.o1 = 0;
+ a.o2.reset ();
+ a.o3.reset ();
+
+ {
+ transaction t (db->begin ());
+ db->update (a);
+ t.commit ();
+ }
+
+ // load & compare
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<aggr> a1 (db->load<aggr> (a.id));
+ t.commit ();
+
+ assert (*a1 == a);
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/relationship/basics/test.hxx b/odb-tests/common/relationship/basics/test.hxx
new file mode 100644
index 0000000..e2d3936
--- /dev/null
+++ b/odb-tests/common/relationship/basics/test.hxx
@@ -0,0 +1,260 @@
+// file : common/relationship/basics/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <set>
+#include <map>
+#include <vector>
+#include <string>
+#include <memory>
+
+#include <odb/core.hxx>
+
+// Raw pointer.
+//
+#pragma db object pointer(obj1*)
+struct obj1
+{
+ obj1 () {}
+ obj1 (const std::string& i, const std::string& s): id (i), str (s) {}
+
+ #pragma db id
+ std::string id;
+ std::string str;
+};
+
+inline bool
+operator== (const obj1& x, const obj1& y)
+{
+ return x.id == y.id && x.str == y.str;
+}
+
+// vector
+//
+typedef std::vector<obj1*> obj1_vec;
+
+inline bool
+operator== (const obj1_vec& x, const obj1_vec& y)
+{
+ if (x.size () != y.size ())
+ return false;
+
+ for (obj1_vec::size_type i (0); i < x.size (); ++i)
+ if (!(x[i] ? (y[i] && *x[i] == *y[i]) : !y[i]))
+ return false;
+
+ return true;
+}
+
+// set
+//
+struct obj1_cmp
+{
+ bool
+ operator() (obj1* x, obj1* y) const
+ {
+ return (!x || !y) ? x < y : x->id < y->id;
+ }
+};
+
+typedef std::set<obj1*, obj1_cmp> obj1_set;
+
+inline bool
+operator== (const obj1_set& x, const obj1_set& y)
+{
+ if (x.size () != y.size ())
+ return false;
+
+ for (obj1_set::const_iterator i (x.begin ()); i != x.end (); ++i)
+ {
+ obj1_set::const_iterator j (y.find (*i));
+
+ if (j == y.end ())
+ return false;
+
+ obj1* x (*i);
+ obj1* y (*j);
+
+ if (!(x ? (y && *x == *y) : !y))
+ return false;
+ }
+
+ return true;
+}
+
+// map
+//
+typedef std::map<int, obj1*> obj1_map;
+
+inline bool
+operator== (const obj1_map& x, const obj1_map& y)
+{
+ if (x.size () != y.size ())
+ return false;
+
+ for (obj1_map::const_iterator i (x.begin ()); i != x.end (); ++i)
+ {
+ obj1_map::const_iterator j (y.find (i->first));
+
+ if (j == y.end ())
+ return false;
+
+ obj1* x (i->second);
+ obj1* y (j->second);
+
+ if (!(x ? (y && *x == *y) : !y))
+ return false;
+ }
+
+ return true;
+}
+
+// auto_ptr/unique_ptr
+//
+struct obj2;
+
+typedef std::unique_ptr<obj2> obj2_ptr;
+
+#pragma db object pointer(obj2_ptr)
+struct obj2
+{
+ obj2 () {}
+ obj2 (const std::string& s): str (s) {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ std::string str;
+};
+
+inline bool
+operator== (const obj2& x, const obj2& y)
+{
+ return x.id == y.id && x.str == y.str;
+}
+
+typedef std::vector<obj2_ptr> obj2_vec;
+
+inline bool
+operator== (const obj2_vec& x, const obj2_vec& y)
+{
+ if (x.size () != y.size ())
+ return false;
+
+ for (obj2_vec::size_type i (0); i < x.size (); ++i)
+ if (!(x[i] ? (y[i] && *x[i] == *y[i]) : !y[i]))
+ return false;
+
+ return true;
+}
+
+// shared_ptr
+//
+struct obj3;
+
+typedef std::shared_ptr<obj3> obj3_ptr;
+
+#pragma db object pointer(obj3_ptr)
+struct obj3
+{
+ obj3 () {}
+ obj3 (const std::string& s): str (s) {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ std::string str;
+};
+
+inline bool
+operator== (const obj3& x, const obj3& y)
+{
+ return x.id == y.id && x.str == y.str;
+}
+
+// composite
+//
+#pragma db value
+struct comp
+{
+ comp () = default;
+ comp (int n, obj3_ptr o): num (n), o3 (o) {}
+
+ int num;
+ obj3_ptr o3;
+};
+
+inline bool
+operator== (const comp& x, const comp& y)
+{
+ return x.num == y.num &&
+ (x.o3 ? (y.o3 && *x.o3 == *y.o3) : !y.o3);
+}
+
+typedef std::vector<comp> comp_vec;
+
+//
+//
+#pragma db object
+struct aggr
+{
+ aggr (): o1 (0) {}
+ aggr (const std::string& s): o1 (0), str (s) {}
+
+ ~aggr ()
+ {
+ delete o1;
+
+ for (obj1_vec::iterator i (v1.begin ()); i != v1.end (); ++i)
+ delete *i;
+
+ for (obj1_set::iterator i (s1.begin ()); i != s1.end (); ++i)
+ delete *i;
+
+ for (obj1_map::iterator i (m1.begin ()); i != m1.end (); ++i)
+ delete i->second;
+ }
+
+ #pragma db id auto
+ unsigned long id;
+
+ obj1* o1;
+
+ obj2_ptr o2; // std::auto_ptr or std::unique_ptr
+ obj2_vec v2;
+
+ obj3_ptr o3;
+ comp c;
+ comp_vec cv;
+
+ obj1_vec v1;
+ obj1_set s1;
+ obj1_map m1;
+
+ std::string str;
+
+private:
+ aggr (const aggr&);
+ aggr& operator= (const aggr&);
+};
+
+inline bool
+operator== (const aggr& x, const aggr& y)
+{
+ return
+ x.id == y.id &&
+ (x.o1 ? (y.o1 && *x.o1 == *y.o1) : !y.o1) &&
+ (x.o2.get () ? (y.o2.get () && *x.o2 == *y.o2) : !y.o2.get ()) &&
+ x.v2 == y.v2 &&
+ (x.o3.get () ? (y.o3.get () && *x.o3 == *y.o3) : !y.o3.get ()) &&
+ x.c == y.c &&
+ x.cv == y.cv &&
+ x.v1 == y.v1 &&
+ x.s1 == y.s1 &&
+ x.m1 == y.m1 &&
+ x.str == y.str;
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/relationship/basics/testscript b/odb-tests/common/relationship/basics/testscript
new file mode 100644
index 0000000..b498bfa
--- /dev/null
+++ b/odb-tests/common/relationship/basics/testscript
@@ -0,0 +1,33 @@
+# file : common/relationship/basics/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/relationship/on-delete/buildfile b/odb-tests/common/relationship/on-delete/buildfile
new file mode 100644
index 0000000..965a2eb
--- /dev/null
+++ b/odb-tests/common/relationship/on-delete/buildfile
@@ -0,0 +1,43 @@
+# file : common/relationship/on-delete/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix t_rel_on_d_ \
+ --generate-schema \
+ --fkeys-deferrable-mode mysql:not_deferrable \
+ --fkeys-deferrable-mode mssql:not_deferrable
+
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/relationship/on-delete/driver.cxx b/odb-tests/common/relationship/on-delete/driver.cxx
new file mode 100644
index 0000000..eec57cf
--- /dev/null
+++ b/odb-tests/common/relationship/on-delete/driver.cxx
@@ -0,0 +1,82 @@
+// file : common/relationship/on-delete/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test ON DELETE functionality.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ object o;
+
+ cascade c;
+ c.p = &o;
+
+ cascade_cont cc;
+ cc.p.push_back (&o);
+
+ set_null n;
+ n.p = &o;
+
+ set_null_cont nc;
+ nc.p.push_back (&o);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ db->persist (c);
+ db->persist (cc);
+ db->persist (n);
+ db->persist (nc);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ assert (db->find<cascade> (c.id) == 0);
+
+ unique_ptr<cascade_cont> pcc (db->load<cascade_cont> (cc.id));
+ assert (pcc->p.empty ());
+
+ unique_ptr<set_null> pn (db->load<set_null> (n.id));
+ assert (pn->p == 0);
+
+ unique_ptr<set_null_cont> pnc (db->load<set_null_cont> (nc.id));
+ assert (pnc->p.size () == 1 && pnc->p[0] == 0);
+
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/relationship/on-delete/test.hxx b/odb-tests/common/relationship/on-delete/test.hxx
new file mode 100644
index 0000000..841acd9
--- /dev/null
+++ b/odb-tests/common/relationship/on-delete/test.hxx
@@ -0,0 +1,58 @@
+// file : common/relationship/on-delete/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <vector>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ #pragma db id auto
+ unsigned long id;
+};
+
+#pragma db object
+struct cascade
+{
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db on_delete(cascade)
+ object* p;
+};
+
+#pragma db object
+struct cascade_cont
+{
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db on_delete(cascade)
+ std::vector<object*> p;
+};
+
+#pragma db object
+struct set_null
+{
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db on_delete(set_null)
+ object* p;
+};
+
+#pragma db object
+struct set_null_cont
+{
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db on_delete(set_null)
+ std::vector<object*> p;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/relationship/on-delete/testscript b/odb-tests/common/relationship/on-delete/testscript
new file mode 100644
index 0000000..8288ebc
--- /dev/null
+++ b/odb-tests/common/relationship/on-delete/testscript
@@ -0,0 +1,33 @@
+# file : common/relationship/on-delete/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/relationship/query/buildfile b/odb-tests/common/relationship/query/buildfile
new file mode 100644
index 0000000..b70edc4
--- /dev/null
+++ b/odb-tests/common/relationship/query/buildfile
@@ -0,0 +1,42 @@
+# file : common/relationship/query/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix t_rel_query_ \
+ --generate-schema \
+ --generate-query \
+ --generate-session
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/relationship/query/driver.cxx b/odb-tests/common/relationship/query/driver.cxx
new file mode 100644
index 0000000..20d5370
--- /dev/null
+++ b/odb-tests/common/relationship/query/driver.cxx
@@ -0,0 +1,168 @@
+// file : common/relationship-query/query/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test relationship queries.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/session.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ //
+ //
+ {
+ shared_ptr<country> ca (new country ("CA", "Canada"));
+ shared_ptr<country> za (new country ("ZA", "South Africa"));
+ shared_ptr<country> us (new country ("US", "United States"));
+ shared_ptr<country> se (new country ("SE", "Sweden"));
+
+ shared_ptr<employer> st (new employer ("Simple Tech, Inc", ca));
+ shared_ptr<employer> ct (new employer ("Complex Tech, Inc", us));
+
+ // person
+ //
+ shared_ptr<person> p1 (
+ new person (1, "John", "Doe", 30, ca, true, za));
+
+ shared_ptr<person> p2 (
+ new person (2, "Jane", "Doe", 29, za, false, us));
+ p2->husband = p1;
+
+ shared_ptr<person> p3 (
+ new person (3, "Joe", "Dirt", 31, us, true, us));
+
+ shared_ptr<person> p4 (
+ new person (4, "Johan", "Johansen", 32, se, false, ca));
+
+ // employee
+ //
+ shared_ptr<employee> e1 (
+ new employee (1, "John", "Doe", 30, ca, true, za, st));
+
+ shared_ptr<employee> e2 (
+ new employee (2, "Jane", "Doe", 29, za, false, us, ct));
+ e2->husband = p1;
+
+ shared_ptr<employee> e3 (
+ new employee (3, "Joe", "Dirt", 31, us, true, us, st));
+
+ shared_ptr<employee> e4 (
+ new employee (4, "Johan", "Johansen", 32, se, false, ca, ct));
+
+ transaction t (db->begin ());
+ db->persist (ca);
+ db->persist (za);
+ db->persist (us);
+ db->persist (se);
+
+ db->persist (st);
+ db->persist (ct);
+
+ db->persist (p1);
+ db->persist (p2);
+ db->persist (p3);
+ db->persist (p4);
+
+ db->persist (e1);
+ db->persist (e2);
+ db->persist (e3);
+ db->persist (e4);
+ t.commit ();
+ }
+
+ typedef odb::query<person> p_query;
+ typedef odb::result<person> p_result;
+
+ typedef odb::query<employee> e_query;
+ typedef odb::result<employee> e_result;
+
+ // Make sure we have an independent JOIN for each relationship.
+ //
+ {
+ session s;
+ transaction t (db->begin ());
+
+ p_result pr (db->query<person> (
+ p_query::residence.location->code == "ZA"));
+ assert (size (pr) == 1);
+
+ e_result er (db->query<employee> (
+ e_query::residence.location->code == "ZA"));
+ assert (size (er) == 1);
+
+ t.commit ();
+ }
+
+ // Test Self-JOIN.
+ //
+ {
+ session s;
+ transaction t (db->begin ());
+
+ p_result pr (db->query<person> (p_query::husband->last_name == "Doe"));
+ assert (size (pr) == 1);
+
+ e_result er (db->query<employee> (e_query::husband->last_name == "Doe"));
+ assert (size (er) == 1);
+
+ t.commit ();
+ }
+
+ // Test query conditions from both base and derived.
+ //
+ {
+ session s;
+ transaction t (db->begin ());
+
+ e_result r (
+ db->query<employee> (
+ e_query::employed_by->name == "Simple Tech, Inc" &&
+ e_query::nationality->code == "US"));
+
+ assert (size (r) == 1);
+
+ t.commit ();
+ }
+
+ // Test second-level pointers.
+ //
+ {
+ session s;
+ transaction t (db->begin ());
+
+ p_result r (
+ db->query<person> (
+ p_query::husband->residence.location == "CA"));
+
+ assert (size (r) == 1);
+
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/relationship/query/test.hxx b/odb-tests/common/relationship/query/test.hxx
new file mode 100644
index 0000000..c6e2d6d
--- /dev/null
+++ b/odb-tests/common/relationship/query/test.hxx
@@ -0,0 +1,140 @@
+// file : common/relationship-query/query/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <memory>
+
+#include <odb/core.hxx>
+
+using std::shared_ptr;
+
+struct country;
+
+#pragma db value
+struct residence_info
+{
+ residence_info (bool p, shared_ptr<country> l)
+ : permanent (p), location (l)
+ {
+ }
+
+ residence_info ()
+ {
+ }
+
+ bool permanent;
+
+ #pragma db not_null
+ shared_ptr<country> location;
+};
+
+#pragma db object pointer(shared_ptr)
+struct person
+{
+ person (unsigned long i,
+ const std::string& fn,
+ const std::string& ln,
+ unsigned short a,
+ shared_ptr<country> r,
+ bool p,
+ shared_ptr<country> n)
+ : id (i),
+ first_name (fn),
+ last_name (ln),
+ age (a),
+ residence (p, r),
+ nationality (n)
+ {
+ }
+
+ person ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db column ("first")
+ std::string first_name;
+
+ #pragma db column ("last")
+ std::string last_name;
+
+ unsigned short age;
+
+ residence_info residence;
+
+ #pragma db not_null
+ shared_ptr<country> nationality;
+
+ shared_ptr<person> husband; // Self-join.
+};
+
+struct employer;
+
+#pragma db object pointer(shared_ptr)
+struct employee: person
+{
+ employee (unsigned long i,
+ const std::string& fn,
+ const std::string& ln,
+ unsigned short a,
+ shared_ptr<country> r,
+ bool p,
+ shared_ptr<country> n,
+ shared_ptr<employer> e)
+ : person (i, fn, ln, a, r, p, n),
+ employed_by (e)
+ {
+ }
+
+ employee ()
+ {
+ }
+
+ shared_ptr<employer> employed_by;
+};
+
+#pragma db object pointer(shared_ptr)
+struct employer
+{
+ employer (const std::string& n, shared_ptr<country> nat)
+ : name (n), nationality (nat)
+ {
+ }
+
+ employer ()
+ {
+ }
+
+ #pragma db id
+ std::string name;
+
+ // The same member name and type as in person (test JOIN alias).
+ //
+ #pragma db not_null
+ shared_ptr<country> nationality;
+};
+
+#pragma db object pointer(shared_ptr)
+struct country
+{
+ country (const std::string& c, const std::string& n)
+ : code (c), name (n)
+ {
+ }
+
+ country ()
+ {
+ }
+
+ #pragma db id
+ std::string code; // ISO 2-letter country code.
+
+ std::string name;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/relationship/query/testscript b/odb-tests/common/relationship/query/testscript
new file mode 100644
index 0000000..d5ad419
--- /dev/null
+++ b/odb-tests/common/relationship/query/testscript
@@ -0,0 +1,33 @@
+# file : common/relationship/query/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/schema/embedded/basics/buildfile b/odb-tests/common/schema/embedded/basics/buildfile
new file mode 100644
index 0000000..0cfe85e
--- /dev/null
+++ b/odb-tests/common/schema/embedded/basics/buildfile
@@ -0,0 +1,42 @@
+# file : common/schema/embedded/basics/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix schema_embd_bscs_ \
+ --generate-schema \
+ --schema-format embedded \
+ --schema-name test
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/schema/embedded/basics/driver.cxx b/odb-tests/common/schema/embedded/basics/driver.cxx
new file mode 100644
index 0000000..7ba2bce
--- /dev/null
+++ b/odb-tests/common/schema/embedded/basics/driver.cxx
@@ -0,0 +1,59 @@
+// file : common/schema/embedded/basics/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test basic embedded schema functionality.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv, false));
+
+ // Create the database schema.
+ //
+ {
+ connection_ptr c (db->connection ());
+
+ // Temporarily disable foreign key constraints for SQLite.
+ //
+ if (db->id () == odb::id_sqlite)
+ c->execute ("PRAGMA foreign_keys=OFF");
+
+ assert (schema_catalog::exists (*db, "test"));
+ assert (!schema_catalog::exists (*db, "test1"));
+ assert (!schema_catalog::exists (*db, ""));
+
+ transaction t (c->begin ());
+ schema_catalog::create_schema (*db, "test");
+ t.commit ();
+
+ if (db->id () == odb::id_sqlite)
+ c->execute ("PRAGMA foreign_keys=ON");
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/schema/embedded/basics/test.hxx b/odb-tests/common/schema/embedded/basics/test.hxx
new file mode 100644
index 0000000..43331f9
--- /dev/null
+++ b/odb-tests/common/schema/embedded/basics/test.hxx
@@ -0,0 +1,23 @@
+// file : common/schema/embedded/basics/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <vector>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ #pragma db auto id
+ unsigned long id;
+
+ std::string str;
+
+ std::vector<int> nums;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/schema/embedded/basics/testscript b/odb-tests/common/schema/embedded/basics/testscript
new file mode 100644
index 0000000..24448c0
--- /dev/null
+++ b/odb-tests/common/schema/embedded/basics/testscript
@@ -0,0 +1,31 @@
+# file : common/schema/embedded/basics/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../../mysql.testscript
+
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../../pgsql.testscript
+
+ $*
+}
diff --git a/odb-tests/common/schema/embedded/order/.gitignore b/odb-tests/common/schema/embedded/order/.gitignore
new file mode 100644
index 0000000..5d39d39
--- /dev/null
+++ b/odb-tests/common/schema/embedded/order/.gitignore
@@ -0,0 +1,6 @@
+# ODB-generated files.
+#
+test1-odb.?xx
+test1-odb-*.?xx
+test2-odb.?xx
+test2-odb-*.?xx
diff --git a/odb-tests/common/schema/embedded/order/buildfile b/odb-tests/common/schema/embedded/order/buildfile
new file mode 100644
index 0000000..b2fac0b
--- /dev/null
+++ b/odb-tests/common/schema/embedded/order/buildfile
@@ -0,0 +1,48 @@
+# file : common/schema/embedded/order/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+hdrs = test1 test2
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+for h: $hdrs
+{
+ exe{driver}: {hxx ixx cxx}{$h-odb}
+
+ <{hxx ixx cxx}{$h-odb}>: hxx{$h} libue{test-meta}
+
+ for db: $databases
+ {
+ exe{driver}: {hxx ixx cxx}{$h-odb-$db}: include = $multi
+ <{hxx ixx cxx}{$h-odb-$db}>: hxx{$h} libue{test-meta}
+ }
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix schema_embd_ordr_ \
+ --generate-schema \
+ --schema-format embedded
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/schema/embedded/order/driver.cxx b/odb-tests/common/schema/embedded/order/driver.cxx
new file mode 100644
index 0000000..fde5e96
--- /dev/null
+++ b/odb-tests/common/schema/embedded/order/driver.cxx
@@ -0,0 +1,65 @@
+// file : common/schema/embedded/order/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test statement execution order in embedded schemas.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test1.hxx"
+#include "test2.hxx"
+
+#include "test1-odb.hxx"
+#include "test2-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv, false));
+ odb::database_id db_id (db->id ());
+
+ // Create the database schema.
+ //
+ {
+ connection_ptr c (db->connection ());
+
+ // Temporarily disable foreign key constraints for MySQL and SQLite.
+ // For MySQL we can actually create the tables in any order. It is
+ // dropping them that's the problem (there is no IF EXISTS).
+ //
+ if (db_id == odb::id_mysql)
+ c->execute ("SET FOREIGN_KEY_CHECKS=0");
+ else if (db_id == odb::id_sqlite)
+ c->execute ("PRAGMA foreign_keys=OFF");
+
+ transaction t (c->begin ());
+ schema_catalog::create_schema (*db);
+ t.commit ();
+
+ if (db_id == odb::id_mysql)
+ c->execute ("SET FOREIGN_KEY_CHECKS=1");
+ else if (db_id == odb::id_sqlite)
+ c->execute ("PRAGMA foreign_keys=ON");
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/schema/embedded/order/test1.hxx b/odb-tests/common/schema/embedded/order/test1.hxx
new file mode 100644
index 0000000..b35074b
--- /dev/null
+++ b/odb-tests/common/schema/embedded/order/test1.hxx
@@ -0,0 +1,23 @@
+// file : common/schema/embedded/order/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+#include <string>
+
+#include <odb/core.hxx>
+
+#pragma db object polymorphic
+struct base
+{
+ virtual
+ ~base () {}
+
+ #pragma db auto id
+ unsigned long id;
+
+ std::string str;
+};
+
+#endif // TEST1_HXX
diff --git a/odb-tests/common/schema/embedded/order/test2.hxx b/odb-tests/common/schema/embedded/order/test2.hxx
new file mode 100644
index 0000000..b4e6d20
--- /dev/null
+++ b/odb-tests/common/schema/embedded/order/test2.hxx
@@ -0,0 +1,17 @@
+// file : common/schema/embedded/order/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+#include <odb/core.hxx>
+
+#include "test1.hxx"
+
+#pragma db object
+struct derived: base
+{
+ int num;
+};
+
+#endif // TEST2_HXX
diff --git a/odb-tests/common/schema/embedded/order/testscript b/odb-tests/common/schema/embedded/order/testscript
new file mode 100644
index 0000000..6dfe58c
--- /dev/null
+++ b/odb-tests/common/schema/embedded/order/testscript
@@ -0,0 +1,31 @@
+# file : common/schema/embedded/order/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../../mysql.testscript
+
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../../pgsql.testscript
+
+ $*
+}
diff --git a/odb-tests/common/schema/namespace/buildfile b/odb-tests/common/schema/namespace/buildfile
new file mode 100644
index 0000000..fa496d7
--- /dev/null
+++ b/odb-tests/common/schema/namespace/buildfile
@@ -0,0 +1,41 @@
+# file : common/schema/namespace/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix schema_ns_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/schema/namespace/driver.cxx b/odb-tests/common/schema/namespace/driver.cxx
new file mode 100644
index 0000000..25515f2
--- /dev/null
+++ b/odb-tests/common/schema/namespace/driver.cxx
@@ -0,0 +1,113 @@
+// file : common/schema/namespace/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test database schemas (aka database namespaces).
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Test database schema (aka database namespace).
+ //
+ using ns::object2;
+
+ object2 o2;
+ o2.id = "aaa";
+ o2.nums.push_back (1);
+ o2.nums.push_back (2);
+ o2.nums.push_back (3);
+ o2.obj1 = new object1;
+ o2.obj1->str = "aaa";
+
+ {
+ transaction t (db->begin ());
+ db->persist (o2.obj1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object2> p2 (db->load<object2> ("aaa"));
+ t.commit ();
+
+ assert (o2 == *p2);
+ }
+
+ {
+ typedef odb::query<object2> query;
+ typedef odb::result<object2> result;
+
+ transaction t (db->begin ());
+
+ {
+ result r (db->query<object2> (query::id == "aaa"));
+ assert (size (r) == 1);
+ }
+
+ {
+ result r (db->query<object2> (query::obj1->str == "aaa"));
+ assert (size (r) == 1);
+ }
+
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object_view> query;
+ typedef odb::result<object_view> result;
+
+ transaction t (db->begin ());
+
+ result r (db->query<object_view> (query::object2::id == "aaa"));
+
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->id2 == "aaa" && i->str == "aaa");
+ assert (++i == r.end ());
+
+ t.commit ();
+ }
+
+ {
+ typedef odb::result<table_view> result;
+
+ transaction t (db->begin ());
+
+ result r (db->query<table_view> ());
+
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->str == "aaa");
+ assert (++i == r.end ());
+
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/schema/namespace/test.hxx b/odb-tests/common/schema/namespace/test.hxx
new file mode 100644
index 0000000..0b1844a
--- /dev/null
+++ b/odb-tests/common/schema/namespace/test.hxx
@@ -0,0 +1,158 @@
+// file : common/schema/namespace/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <vector>
+
+#include <odb/core.hxx>
+
+// Table names.
+//
+#pragma db object table("TABLE_EXPLICIT")
+struct table_explicit
+{
+ #pragma db id
+ unsigned long id_;
+};
+
+#pragma db object
+struct table_implicit
+{
+ #pragma db id
+ unsigned long id_;
+};
+
+// Column names.
+//
+#pragma db object
+struct column
+{
+ #pragma db id
+ int m1;
+
+ #pragma db column("foo")
+ int m2;
+
+ int m_m3;
+ int _m4;
+ int m5_;
+ int m_;
+ int m__;
+};
+
+// Column types.
+//
+#pragma db object
+struct type
+{
+ #pragma db id
+ std::string id;
+
+ // Test default C++ to DB type mapping.
+ //
+ bool b;
+ char c;
+ signed char sc;
+ unsigned char uc;
+ short s;
+ unsigned short us;
+ int i;
+ unsigned int ui;
+ long l;
+ unsigned long ul;
+ long long ll;
+ unsigned long long ull;
+ float f;
+ double d;
+ std::string str;
+
+ #pragma db type("INTEGER")
+ bool m1;
+
+ #pragma db transient
+ char* m2;
+};
+
+// Test database schema (aka database namespace).
+//
+#ifdef ODB_COMPILER
+#if defined (ODB_DATABASE_MYSQL)
+//# define DB_SCHEMA "odb_test"
+# define DB_SCHEMA ""
+#elif defined (ODB_DATABASE_SQLITE)
+# define DB_SCHEMA "main"
+#elif defined (ODB_DATABASE_PGSQL)
+# define DB_SCHEMA "public"
+#elif defined (ODB_DATABASE_ORACLE)
+//# define DB_SCHEMA "ODB_TEST"
+# define DB_SCHEMA ""
+#elif defined(ODB_DATABASE_MSSQL)
+# define DB_SCHEMA "dbo"
+#elif defined(ODB_DATABASE_COMMON)
+# define DB_SCHEMA "dummy"
+#else
+# error unknown database
+#endif
+#endif
+
+namespace ns {typedef int my_int;} // Original.
+
+#pragma db object table(DB_SCHEMA."object_1")
+struct object1
+{
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db column("str")
+ std::string str;
+};
+
+inline bool
+operator== (const object1& x, const object1& y)
+{
+ return x.id == y.id && x.str == y.str;
+}
+
+#pragma db namespace schema(DB_SCHEMA)
+namespace ns // Extension.
+{
+ #pragma db object
+ struct object2
+ {
+ object2 (): obj1 (0) {}
+ ~object2 () {delete obj1;}
+
+ #pragma db id
+ std::string id;
+
+ std::vector<unsigned int> nums;
+ object1* obj1;
+ };
+
+ inline bool
+ operator== (const object2& x, const object2& y)
+ {
+ return x.id == y.id && x.nums == y.nums && *x.obj1 == *y.obj1;
+ }
+}
+
+#pragma db view object(object1) object(ns::object2)
+struct object_view
+{
+ #pragma db column(ns::object2::id)
+ std::string id2;
+
+ std::string str;
+};
+
+#pragma db view table(DB_SCHEMA."schema_ns_object_1")
+struct table_view
+{
+ #pragma db column(DB_SCHEMA."schema_ns_object_1"."str")
+ std::string str;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/schema/namespace/testscript b/odb-tests/common/schema/namespace/testscript
new file mode 100644
index 0000000..0fabe6e
--- /dev/null
+++ b/odb-tests/common/schema/namespace/testscript
@@ -0,0 +1,33 @@
+# file : common/schema/namespace/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/section/basics/buildfile b/odb-tests/common/section/basics/buildfile
new file mode 100644
index 0000000..5ccdd6b
--- /dev/null
+++ b/odb-tests/common/section/basics/buildfile
@@ -0,0 +1,41 @@
+# file : common/section/basics/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix t_section_b_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/section/basics/driver.cxx b/odb-tests/common/section/basics/driver.cxx
new file mode 100644
index 0000000..53783a3
--- /dev/null
+++ b/odb-tests/common/section/basics/driver.cxx
@@ -0,0 +1,1735 @@
+// file : common/section/basics/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test object section basics.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/session.hxx>
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+struct failed {};
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Test lazy-loaded, always updating section.
+ //
+ {
+ using namespace test1;
+
+ object o (123, "abc");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+
+ assert (o.s.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ assert (!p->s.loaded ());
+ assert (p->n == o.n &&
+ p->sn != o.sn && p->ss != o.ss && p->sv != o.sv);
+
+ db->load (*p, p->s);
+
+ assert (p->s.loaded ());
+ assert (p->n == o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv == o.sv);
+
+ t.commit ();
+ }
+
+ // Update.
+ //
+ o.n++;
+ o.sn++;
+ o.ss += 'd';
+ o.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ assert (p->n == o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv == o.sv);
+ t.commit ();
+ }
+
+ // We can also update just the section.
+ //
+ o.n++;
+ o.sn++;
+ o.ss += 'd';
+ o.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o, o.s);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ assert (p->n != o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv == o.sv);
+ t.commit ();
+ }
+
+ // Test updating unloaded section.
+ //
+ o.n++;
+ o.sn++;
+ o.ss += 'd';
+ o.sv[0]++;
+ o.s.unload ();
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ assert (p->n == o.n &&
+ p->sn != o.sn && p->ss != o.ss && p->sv != o.sv);
+ t.commit ();
+ }
+
+ // Test reloading of loaded/unloaded sections.
+ //
+ o.n++;
+ o.sn++;
+ o.ss += 'd';
+ o.sv[0]++;
+ o.s.unload ();
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ db->reload (o);
+ assert (p->n == o.n &&
+ p->sn != o.sn && p->ss != o.ss && p->sv != o.sv);
+ db->load (o, o.s);
+ t.commit ();
+ }
+
+ o.n++;
+ o.sn++;
+ o.ss += 'd';
+ o.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ db->reload (o);
+ assert (p->n == o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv == o.sv);
+ t.commit ();
+ }
+
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ // Make sure we can access section members in queries.
+ //
+ {
+ transaction t (db->begin ());
+
+ result r (db->query<object> (query::ss == o.ss));
+ result::iterator i (r.begin ());
+
+ assert (i != r.end () && !i->s.loaded ());
+
+ db->load (*i, i->s);
+ assert (i->n == o.n &&
+ i->sn == o.sn && i->ss == o.ss && i->sv == o.sv);
+
+ assert (++i == r.end ());
+
+ t.commit ();
+ }
+
+ // Make sure we can load/update sections without messing up the
+ // loaded object's image.
+ //
+ {
+ transaction t (db->begin ());
+
+ result r (db->query<object> (query::ss == o.ss));
+ result::iterator i (r.begin ());
+
+ assert (i != r.end ());
+
+ object o1;
+ i.load (o1);
+ db->load (o1, o1.s);
+ assert (o1.n == o.n &&
+ o1.sn == o.sn && o1.ss == o.ss && o1.sv == o.sv);
+
+ o.sn++;
+ o.ss += 'd';
+ o.sv[0]++;
+ db->update (o, o.s);
+
+ object o2;
+ i.load (o2);
+ db->load (o2, o2.s);
+ assert (o2.n == o1.n &&
+ o2.sn == o.sn && o2.ss == o.ss && o2.sv == o.sv);
+
+ assert (++i == r.end ());
+
+ t.commit ();
+ }
+ }
+
+ // Test lazy-loaded, change-updated section.
+ //
+ {
+ using namespace test2;
+
+ object o (123, "abc");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+
+ assert (o.s.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ assert (!p->s.loaded ());
+ assert (p->n == o.n &&
+ p->sn != o.sn && p->ss != o.ss && p->sv != o.sv);
+
+ db->load (*p, p->s);
+
+ assert (p->s.loaded ());
+ assert (p->n == o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv == o.sv);
+
+ t.commit ();
+ }
+
+ // Update but don't mark as changed.
+ //
+ o.n++;
+ o.sn++;
+ o.ss += 'd';
+ o.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ assert (p->n == o.n &&
+ p->sn != o.sn && p->ss != o.ss && p->sv != o.sv);
+ t.commit ();
+ }
+
+ // Mark as changed.
+ //
+ o.s.change ();
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ assert (!o.s.changed ());
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ assert (p->n == o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv == o.sv);
+ t.commit ();
+ }
+
+ // We can also update just the section manually.
+ //
+ o.n++;
+ o.sn++;
+ o.ss += 'd';
+ o.sv[0]++;
+ o.s.change ();
+
+ {
+ transaction t (db->begin ());
+ db->update (o, o.s);
+ assert (!o.s.changed ());
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ assert (p->n != o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv == o.sv);
+ t.commit ();
+ }
+ }
+
+ // Test lazy-loaded, manually-updated section.
+ //
+ {
+ using namespace test3;
+
+ object o (123, "abc");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+
+ assert (o.s.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ assert (!p->s.loaded ());
+ assert (p->n == o.n &&
+ p->sn != o.sn && p->ss != o.ss && p->sv != o.sv);
+
+ db->load (*p, p->s);
+
+ assert (p->s.loaded ());
+ assert (p->n == o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv == o.sv);
+
+ t.commit ();
+ }
+
+ // Update the object only.
+ //
+ o.n++;
+ o.sn++;
+ o.ss += 'd';
+ o.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ assert (p->n == o.n &&
+ p->sn != o.sn && p->ss != o.ss && p->sv != o.sv);
+ t.commit ();
+ }
+
+ // Update both the object and section.
+ //
+ o.n++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ db->update (o, o.s);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ assert (p->n == o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv == o.sv);
+ t.commit ();
+ }
+
+ // We can also update just the section.
+ //
+ o.n++;
+ o.sn++;
+ o.ss += 'd';
+ o.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o, o.s);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ assert (p->n != o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv == o.sv);
+ t.commit ();
+ }
+
+ // Test detection of unloaded section update.
+ //
+ try
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->update (*p, p->s);
+ assert (false);
+ }
+ catch (const section_not_loaded&)
+ {
+ }
+ }
+
+ // Test eager-loaded, change-updated section.
+ //
+ {
+ using namespace test4;
+
+ object o (123, "abc");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+
+ assert (o.s.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ assert (p->s.loaded ());
+ assert (p->n == o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv == o.sv);
+
+ t.commit ();
+ }
+
+ // Update but don't mark as changed.
+ //
+ o.n++;
+ o.sn++;
+ o.ss += 'd';
+ o.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ assert (p->n == o.n &&
+ p->sn != o.sn && p->ss != o.ss && p->sv != o.sv);
+ t.commit ();
+ }
+
+ // Mark as changed.
+ //
+ o.s.change ();
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ assert (!o.s.changed ());
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ assert (p->n == o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv == o.sv);
+ t.commit ();
+ }
+ }
+
+ // Test eager-loaded, manually-updated section.
+ //
+ {
+ using namespace test5;
+
+ object o (123, "abc");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+
+ assert (o.s.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ assert (p->s.loaded ());
+ assert (p->n == o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv == o.sv);
+
+ t.commit ();
+ }
+
+ // Update the object only.
+ //
+ o.n++;
+ o.sn++;
+ o.ss += 'd';
+ o.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ assert (p->n == o.n &&
+ p->sn != o.sn && p->ss != o.ss && p->sv != o.sv);
+ t.commit ();
+ }
+
+ // Update both the object and section.
+ //
+ o.n++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ db->update (o, o.s);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ assert (p->n == o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv == o.sv);
+ t.commit ();
+ }
+
+ // We can also update just the section.
+ //
+ o.n++;
+ o.sn++;
+ o.ss += 'd';
+ o.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o, o.s);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ assert (p->n != o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv == o.sv);
+ t.commit ();
+ }
+ }
+
+ // Test value-only and container-only section. Also multiple sections
+ // in an object.
+ //
+ {
+ using namespace test6;
+
+ object o (123, "abc");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+
+ assert (o.s1.loaded ());
+ assert (o.s2.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ assert (!p->s1.loaded ());
+ assert (!p->s2.loaded ());
+ assert (p->n == o.n &&
+ p->sn != o.sn && p->ss != o.ss && p->sv != o.sv);
+
+ db->load (*p, p->s1);
+ assert (p->s1.loaded ());
+ assert (p->n == o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv != o.sv);
+
+ db->load (*p, p->s2);
+ assert (p->s2.loaded ());
+ assert (p->n == o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv == o.sv);
+
+ t.commit ();
+ }
+
+ // Update.
+ //
+ o.n++;
+ o.sn++;
+ o.ss += 'd';
+ o.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s1);
+ db->load (*p, p->s2);
+ assert (p->n == o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv == o.sv);
+ t.commit ();
+ }
+
+ // We can also update just the section.
+ //
+ o.n++;
+ o.sn++;
+ o.ss += 'd';
+ o.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o, o.s1);
+ db->update (o, o.s2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s1);
+ db->load (*p, p->s2);
+ assert (p->n != o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv == o.sv);
+ t.commit ();
+ }
+ }
+
+ // Test value-only and container-only section. Also multiple sections
+ // in an object.
+ //
+ {
+ using namespace test7;
+
+ object o (123, "abc", true);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+
+ assert (o.s1.loaded ());
+ assert (o.s2.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ assert (!p->s1.loaded ());
+ assert (!p->s2.loaded ());
+ assert (p->sn1 != o.sn1 && p->ss1 != o.ss1 &&
+ p->sn2 != o.sn2 && p->ss2 != o.ss2 && p->sb2 != o.sb2);
+
+ db->load (*p, p->s1);
+ db->load (*p, p->s2);
+ assert (p->s1.loaded ());
+ assert (p->s2.loaded ());
+ assert (p->sn1 == o.sn1 && p->ss1 == o.ss1 &&
+ p->sn2 == o.sn2 && p->ss2 == o.ss2 && p->sb2 == o.sb2);
+
+
+ t.commit ();
+ }
+
+ // Update.
+ //
+ o.sn1++;
+ o.sn2++;
+ o.ss1 += 'd';
+ o.ss2 += 'd';
+ o.sb2 = !o.sb2;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s1);
+ db->load (*p, p->s2);
+ assert (p->sn1 == o.sn1 && p->ss1 == o.ss1 &&
+ p->sn2 == o.sn2 && p->ss2 == o.ss2 && p->sb2 == o.sb2);
+ t.commit ();
+ }
+
+ // Manual update of just the section.
+ //
+ o.sn1++;
+ o.sn2++;
+ o.ss1 += 'd';
+ o.ss2 += 'd';
+ o.sb2 = !o.sb2;
+
+ {
+ transaction t (db->begin ());
+ db->update (o, o.s2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s1);
+ db->load (*p, p->s2);
+ assert (p->sn1 != o.sn1 && p->ss1 != o.ss1 &&
+ p->sn2 == o.sn2 && p->ss2 == o.ss2 && p->sb2 == o.sb2);
+ t.commit ();
+ }
+ }
+
+ // Test readonly and inverse section members.
+ //
+ {
+ using namespace test8;
+
+ object1 o1 (new object (123, "abc"));
+ object& o (*o1.p);
+ o.sp = &o1;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ db->persist (o1);
+ t.commit ();
+
+ assert (o.s.loaded ());
+ }
+
+ {
+ session s;
+
+ transaction t (db->begin ());
+ unique_ptr<object1> p1 (db->load<object1> (o1.id));
+ object* p (p1->p);
+
+ assert (!p->s.loaded ());
+ assert (p->n == o.n &&
+ p->sn != o.sn && p->ss != o.ss && p->sp == 0);
+
+ db->load (*p, p->s);
+
+ assert (p->s.loaded ());
+ assert (p->n == o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sp->id == o.sp->id);
+
+ t.commit ();
+ }
+
+ // Update.
+ //
+ o.n++;
+ o.sn++;
+ o.ss += 'd';
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ session s;
+
+ transaction t (db->begin ());
+ unique_ptr<object1> p1 (db->load<object1> (o1.id));
+ object* p (p1->p);
+
+ db->load (*p, p->s);
+ assert (p->n == o.n &&
+ p->sn != o.sn && p->ss == o.ss && p->sp->id == o.sp->id);
+
+ t.commit ();
+ }
+ }
+
+ // Test object without any columns to load or update.
+ //
+ {
+ using namespace test9;
+
+ object o (123, "abc");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+
+ assert (o.s.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ assert (!p->s.loaded ());
+ assert (o.id == o.id &&
+ p->sn != o.sn && p->ss != o.ss);
+
+ db->load (*p, p->s);
+
+ assert (p->s.loaded ());
+ assert (o.id == o.id &&
+ p->sn == o.sn && p->ss == o.ss);
+
+ t.commit ();
+ }
+
+ // Update object.
+ //
+ o.sn++;
+ o.ss += 'd';
+
+ {
+ transaction t (db->begin ());
+ db->update (o); // No-op.
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ assert (o.id == o.id &&
+ p->sn != o.sn && p->ss != o.ss);
+ t.commit ();
+ }
+
+ // Update section.
+ //
+ {
+ transaction t (db->begin ());
+ db->update (o, o.s);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ assert (o.id == o.id &&
+ p->sn == o.sn && p->ss == o.ss);
+ t.commit ();
+ }
+ }
+
+ // Test section without any columns or containers to update.
+ //
+ {
+ using namespace test10;
+
+ object o (123);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+
+ assert (o.s.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ assert (!p->s.loaded ());
+ assert (p->n == o.n && p->sn != o.sn);
+
+ db->load (*p, p->s);
+
+ assert (p->s.loaded ());
+ assert (p->n == o.n && p->sn == o.sn);
+
+ t.commit ();
+ }
+
+ // Update.
+ //
+ o.n++;
+ o.sn++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ //db->update (o, o.s); // Error.
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ assert (p->n == o.n && p->sn != o.sn);
+ t.commit ();
+ }
+ }
+
+ // Test section with composite member.
+ //
+ {
+ using namespace test11;
+
+ object o (123, "abc");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+
+ assert (o.s.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ assert (!p->s.loaded ());
+ assert (p->n == o.n &&
+ p->sn != o.sn && p->sc.s != o.sc.s && p->sc.v != o.sc.v);
+
+ db->load (*p, p->s);
+
+ assert (p->s.loaded ());
+ assert (p->n == o.n &&
+ p->sn == o.sn && p->sc.s == o.sc.s && p->sc.v == o.sc.v);
+
+ t.commit ();
+ }
+
+ // Update.
+ //
+ o.n++;
+ o.sn++;
+ o.sc.s += 'd';
+ o.sc.v[0]++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ assert (p->n == o.n &&
+ p->sn == o.sn && p->sc.s == o.sc.s && p->sc.v == o.sc.v);
+ t.commit ();
+ }
+ }
+
+ // Test change state restoration on transaction rollback.
+ //
+ {
+ using namespace test12;
+
+ object o (123, "abc");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+
+ assert (o.s.loaded ());
+ }
+
+ // Update.
+ //
+ o.n++;
+ o.sn++;
+ o.ss += 'd';
+ o.s.change ();
+
+ try
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ assert (!o.s.changed ());
+ throw failed ();
+ }
+ catch (const failed&)
+ {
+ assert (o.s.changed ());
+ }
+
+ // Retry. Also test the object destruction before transaction
+ // termination case.
+ //
+ {
+ transaction t (db->begin ());
+ {
+ object c (o);
+ db->update (c);
+ assert (!c.s.changed ());
+ }
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ assert (p->n == o.n && p->sn == o.sn && p->ss == o.ss);
+ t.commit ();
+ }
+ }
+
+ // Test section accessor/modifier.
+ //
+ {
+ using namespace test13;
+
+ object o (123, "abc");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+
+ assert (o.s ().loaded ());
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ assert (!p->s ().loaded ());
+ assert (p->n == o.n && p->sn != o.sn && p->ss != o.ss);
+
+ db->load (*p, p->rw_s ());
+
+ assert (p->s ().loaded ());
+ assert (p->n == o.n && p->sn == o.sn && p->ss == o.ss);
+
+ t.commit ();
+ }
+
+ // Update.
+ //
+ o.n++;
+ o.sn++;
+ o.ss += 'd';
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ db->update (o, o.s ());
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->rw_s ());
+ assert (p->n == o.n && p->sn == o.sn && p->ss == o.ss);
+ t.commit ();
+ }
+
+ // Test detection of section copy.
+ //
+ try
+ {
+ transaction t (db->begin ());
+ section c (o.s ());
+ db->update (o, c);
+ assert (false);
+ }
+ catch (const section_not_in_object&)
+ {
+ }
+ }
+
+ // Test LOB in section streaming, column re-ordering.
+ //
+ {
+ using namespace test14;
+
+ object o (1, 123, "\x01\x02\x03\x04\x05");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+
+ assert (o.s.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ assert (!p->s.loaded ());
+ assert (p->n == o.n && p->sn != o.sn && p->sb != o.sb);
+
+ db->load (*p, p->s);
+
+ assert (p->s.loaded ());
+ assert (p->n == o.n && p->sn == o.sn && p->sb == o.sb);
+
+ t.commit ();
+ }
+
+ // Update.
+ //
+ o.n++;
+ o.sn++;
+ o.sb.push_back ('\x06');
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ assert (p->n == o.n && p->sn == o.sn && p->sb == o.sb);
+ t.commit ();
+ }
+
+ // We can also update just the section.
+ //
+ o.n++;
+ o.sn++;
+ o.sb.push_back ('\x07');
+
+ {
+ transaction t (db->begin ());
+ db->update (o, o.s);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ assert (p->n != o.n && p->sn == o.sn && p->sb == o.sb);
+ t.commit ();
+ }
+ }
+
+ // Test sections and optimistic concurrency.
+ //
+ {
+ using namespace test15;
+
+ object o (123, "abc");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+
+ assert (o.s.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ assert (!p->s.loaded ());
+ assert (p->n == o.n &&
+ p->sn != o.sn && p->ss != o.ss && p->sv != o.sv);
+
+ db->load (*p, p->s);
+
+ assert (p->s.loaded ());
+ assert (p->n == o.n &&
+ p->sn == o.sn && p->ss == o.ss && p->sv == o.sv);
+
+ t.commit ();
+ }
+
+ // Update object.
+ //
+ object o1 (o);
+ o1.n++;
+ o1.sn++;
+ o1.ss += 'd';
+ o1.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ db->update (o1);
+ assert (o.v != o1.v);
+
+ try
+ {
+ db->load (*p, p->s);
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (*p);
+ assert (!p->s.loaded ());
+ db->load (*p, p->s);
+
+ assert (p->n == o1.n &&
+ p->sn == o1.sn && p->ss == o1.ss && p->sv == o1.sv);
+ }
+
+ db->reload (o);
+ assert (o.v == o1.v);
+ assert (o.n == o1.n &&
+ o.sn == o1.sn && o.ss == o1.ss && o.sv == o1.sv);
+ t.commit ();
+ }
+
+ // Update section.
+ //
+ o1.sn++;
+ o1.ss += 'd';
+ o1.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ db->update (o1, o1.s);
+ assert (o.v != o1.v);
+
+ // Double-check object version was updated.
+ //
+ {
+ unique_ptr<object> p1 (db->load<object> (o.id));
+ assert (o1.v == p1->v);
+ }
+
+ try
+ {
+ db->load (*p, p->s);
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (*p);
+ assert (!p->s.loaded ());
+ db->load (*p, p->s);
+
+ assert (p->n == o1.n &&
+ p->sn == o1.sn && p->ss == o1.ss && p->sv == o1.sv);
+ }
+
+ db->reload (o);
+ assert (o.v == o1.v);
+ assert (o.n == o1.n &&
+ o.sn == o1.sn && o.ss == o1.ss && o.sv == o1.sv);
+ t.commit ();
+ }
+
+ // Update changed section.
+ //
+ o1.sn++;
+ o1.ss += 'd';
+ o1.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o1, o1.s);
+
+ try
+ {
+ db->update (o, o.s);
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (o);
+ db->update (o, o.s);
+ }
+
+ t.commit ();
+ }
+ }
+
+ // Test container-only sections and optimistic concurrency.
+ //
+ {
+ using namespace test16;
+
+ object o (123);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+
+ assert (o.s.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ assert (!p->s.loaded ());
+ assert (p->sv != o.sv);
+
+ db->load (*p, p->s);
+
+ assert (p->s.loaded ());
+ assert (p->sv == o.sv);
+
+ t.commit ();
+ }
+
+ // Update object.
+ //
+ object o1 (o);
+ o1.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ db->update (o1);
+ assert (o.v != o1.v);
+
+ try
+ {
+ db->load (*p, p->s);
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (*p);
+ assert (!p->s.loaded ());
+ db->load (*p, p->s);
+
+ assert (p->sv == o1.sv);
+ }
+
+ db->reload (o);
+ assert (o.v == o1.v);
+ assert (o.sv == o1.sv);
+ t.commit ();
+ }
+
+ // Update section.
+ //
+ o1.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ db->update (o1, o1.s);
+ assert (o.v != o1.v);
+
+ // Double-check object version was updated.
+ //
+ {
+ unique_ptr<object> p1 (db->load<object> (o.id));
+ assert (o1.v == p1->v);
+ }
+
+ try
+ {
+ db->load (*p, p->s);
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (*p);
+ assert (!p->s.loaded ());
+ db->load (*p, p->s);
+
+ assert (p->sv == o1.sv);
+ }
+
+ db->reload (o);
+ assert (o.v == o1.v);
+ assert (o.sv == o1.sv);
+ t.commit ();
+ }
+
+ // Update changed section.
+ //
+ o1.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o1, o1.s);
+
+ try
+ {
+ db->update (o, o.s);
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (o);
+ db->update (o, o.s);
+ }
+
+ t.commit ();
+ }
+ }
+
+ // Test reuse-inheritance, sections, and optimistic concurrency.
+ //
+ {
+ using namespace test17;
+
+ object o (123);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+
+ assert (o.s1.loaded ());
+ assert (o.s2.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ assert (!p->s1.loaded ());
+ assert (!p->s2.loaded ());
+ assert (p->s1n != o.s1n && p->s2v != o.s2v);
+
+ db->load (*p, p->s1);
+ db->load (*p, p->s2);
+
+ assert (p->s1.loaded ());
+ assert (p->s2.loaded ());
+ assert (p->s1n == o.s1n && p->s2v == o.s2v);
+
+ t.commit ();
+ }
+
+ object o1 (o);
+
+ // Update object.
+ //
+ for (unsigned short s (1); s < 3; ++s)
+ {
+ o1.s1n++;
+ o1.s2v[0]++;
+
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ db->update (o1);
+ assert (o.v != o1.v);
+
+ try
+ {
+ switch (s)
+ {
+ case 1: db->load (*p, p->s1); break;
+ case 2: db->load (*p, p->s2); break;
+ default: break;
+ }
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (*p);
+
+ assert (!p->s1.loaded ());
+ assert (!p->s2.loaded ());
+
+ db->load (*p, p->s1);
+ db->load (*p, p->s2);
+
+ assert (p->s1n == o1.s1n && p->s2v == o1.s2v);
+ }
+
+ db->reload (o);
+ assert (o.v == o1.v);
+ assert (o.s1n == o1.s1n && o.s2v == o1.s2v);
+ t.commit ();
+ }
+
+ // Update section.
+ //
+ for (unsigned short s (1); s < 3; ++s)
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ switch (s)
+ {
+ case 1:
+ o1.s1n++;
+ db->update (o1, o1.s1);
+ assert (o.v != o1.v);
+ break;
+ case 2:
+ o1.s2v[0]++;
+ db->update (o1, o1.s2);
+ assert (o.v != o1.v);
+ break;
+ default: break;
+ }
+
+ try
+ {
+ switch (s)
+ {
+ case 1: db->load (*p, p->s1); break;
+ case 2: db->load (*p, p->s2); break;
+ default: break;
+ }
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (*p);
+
+ assert (!p->s1.loaded ());
+ assert (!p->s2.loaded ());
+
+ db->load (*p, p->s1);
+ db->load (*p, p->s2);
+
+ assert (p->s1n == o1.s1n && p->s2v == o1.s2v);
+ }
+
+ db->reload (o);
+ assert (o.v == o1.v);
+ assert (o.s1n == o1.s1n && o.s2v == o1.s2v);
+ t.commit ();
+ }
+
+ // Update changed section.
+ //
+ for (unsigned short s (1); s < 3; ++s)
+ {
+ transaction t (db->begin ());
+
+ switch (s)
+ {
+ case 1:
+ o1.s1n++;
+ db->update (o1, o1.s1);
+ break;
+ case 2:
+ o1.s2v[0]++;
+ db->update (o1, o1.s2);
+ break;
+ default: break;
+ }
+
+ try
+ {
+ switch (s)
+ {
+ case 1: db->update (o, o.s1); break;
+ case 2: db->update (o, o.s2); break;
+ default: break;
+ }
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (o);
+
+ switch (s)
+ {
+ case 1: db->update (o, o.s1); break;
+ case 2: db->update (o, o.s2); break;
+ default: break;
+ }
+ }
+
+ db->reload (o1);
+
+ t.commit ();
+ }
+ }
+
+ // Test change-updated section and change-tracking container.
+ //
+ {
+ using namespace test18;
+
+ object o (123);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+
+ assert (o.s.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ assert (!p->s.loaded ());
+ assert (p->sn != o.sn && p->sv != o.sv);
+
+ db->load (*p, p->s);
+
+ assert (p->s.loaded ());
+ assert (p->sn == o.sn && p->sv == o.sv);
+
+ t.commit ();
+ }
+
+ // Update but don't mark as changed.
+ //
+ o.sn++;
+ o.sv.modify (0)++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o); // Automatically marked as changed.
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ assert (p->sn == o.sn && p->sv == o.sv);
+ t.commit ();
+ }
+
+ // Test updating just the section manually.
+ //
+ o.sn++;
+ o.sv.modify (0)++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o, o.s);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+ db->load (*p, p->s);
+ assert (p->sn == o.sn && p->sv == o.sv);
+ t.commit ();
+ }
+ }
+
+ // Regression: BLOB in a value type used as a map value that is in a
+ // section.
+ //
+ {
+ using namespace test19;
+
+ object o;
+ o.m[1].b.assign (560, 'x'); // Size greater than the default image.
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ // Hold "old" connection to force a new set of statements/image
+ // buffers.
+ //
+ connection_ptr c (db->connection ());
+
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ db->load (*p, p->s);
+ assert (p->m[1].b == o.m[1].b);
+
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/section/basics/test.hxx b/odb-tests/common/section/basics/test.hxx
new file mode 100644
index 0000000..702ef8b
--- /dev/null
+++ b/odb-tests/common/section/basics/test.hxx
@@ -0,0 +1,628 @@
+// file : common/section/basics/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <vector>
+
+#include <odb/core.hxx>
+#include <odb/section.hxx>
+#include <odb/vector.hxx>
+
+#ifdef ODB_COMPILER
+# if defined(ODB_DATABASE_PGSQL)
+# define BLOB_TYPE "BYTEA"
+# elif defined(ODB_DATABASE_MSSQL)
+# define BLOB_TYPE "VARBINARY(max)"
+# else
+# define BLOB_TYPE "BLOB"
+# endif
+#endif
+
+// Test lazy-loaded, always-updated section.
+//
+#pragma db namespace table("t1_")
+namespace test1
+{
+ #pragma db object
+ struct object
+ {
+ object (int n_ = 999, const std::string& s_ = "xxx")
+ : sn (n_), n (n_), ss (s_) {sv.push_back (n_);}
+
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db load(lazy)
+ odb::section s;
+
+ #pragma db section(s)
+ int sn;
+
+ int n;
+
+ #pragma db section(s)
+ std::string ss;
+
+ #pragma db section(s)
+ std::vector<int> sv;
+ };
+}
+
+// Test lazy-loaded, change-updated section.
+//
+#pragma db namespace table("t2_")
+namespace test2
+{
+ #pragma db object
+ struct object
+ {
+ object (int n_ = 999, const std::string& s_ = "xxx")
+ : sn (n_), n (n_), ss (s_) {sv.push_back (n_);}
+
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db load(lazy) update(change)
+ odb::section s;
+
+ #pragma db section(s)
+ int sn;
+
+ int n;
+
+ #pragma db section(s)
+ std::string ss;
+
+ #pragma db section(s)
+ std::vector<int> sv;
+ };
+}
+
+// Test lazy-loaded, manually-updated section.
+//
+#pragma db namespace table("t3_")
+namespace test3
+{
+ #pragma db object
+ struct object
+ {
+ object (int n_ = 999, const std::string& s_ = "xxx")
+ : sn (n_), n (n_), ss (s_) {sv.push_back (n_);}
+
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db load(lazy) update(manual)
+ odb::section s;
+
+ #pragma db section(s)
+ int sn;
+
+ int n;
+
+ #pragma db section(s)
+ std::string ss;
+
+ #pragma db section(s)
+ std::vector<int> sv;
+ };
+}
+
+// Test eager-loaded, change-updated section.
+//
+#pragma db namespace table("t4_")
+namespace test4
+{
+ #pragma db object
+ struct object
+ {
+ object (int n_ = 999, const std::string& s_ = "xxx")
+ : sn (n_), n (n_), ss (s_) {sv.push_back (n_);}
+
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db section(s)
+ int sn;
+
+ #pragma db update(change)
+ odb::section s;
+
+ int n;
+
+ #pragma db section(s)
+ std::string ss;
+
+ #pragma db section(s)
+ std::vector<int> sv;
+ };
+}
+
+// Test eager-loaded, manually-updated section.
+//
+#pragma db namespace table("t5_")
+namespace test5
+{
+ #pragma db object
+ struct object
+ {
+ object (int n_ = 999, const std::string& s_ = "xxx")
+ : sn (n_), n (n_), ss (s_) {sv.push_back (n_);}
+
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db section(s)
+ int sn;
+
+ #pragma db update(manual)
+ odb::section s;
+
+ int n;
+
+ #pragma db section(s)
+ std::string ss;
+
+ #pragma db section(s)
+ std::vector<int> sv;
+ };
+}
+
+// Test value-only and container-only section. Also multiple sections
+// in an object.
+//
+#pragma db namespace table("t6_")
+namespace test6
+{
+ #pragma db object
+ struct object
+ {
+ object (int n_ = 999, const std::string& s_ = "xxx")
+ : n (n_), sn (n_), ss (s_) {sv.push_back (n_);}
+
+ #pragma db id auto
+ unsigned long id;
+
+ int n;
+
+ #pragma db load(lazy)
+ odb::section s1;
+
+ #pragma db load(lazy)
+ odb::section s2;
+
+ #pragma db section(s1)
+ int sn;
+
+ #pragma db section(s1)
+ std::string ss;
+
+ #pragma db section(s2)
+ std::vector<int> sv;
+ };
+}
+
+// Test sections and reuse inheritance.
+//
+#pragma db namespace table("t7_")
+namespace test7
+{
+ #pragma db object abstract
+ struct base
+ {
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db load(lazy)
+ odb::section s1; // Empty section.
+
+ #pragma db load(lazy)
+ odb::section s2;
+
+ #pragma db section(s2)
+ int sn2;
+ };
+
+ #pragma db object abstract
+ struct interm: base
+ {
+ // Section s1 is still empty.
+
+ #pragma db section(s2)
+ std::string ss2;
+ };
+
+ #pragma db object
+ struct derived: interm
+ {
+ #pragma db section(s1)
+ int sn1;
+ };
+
+ #pragma db object
+ struct object: derived
+ {
+ object (int n = 999, const std::string& s = "xxx", bool b = false)
+ {
+ sn1 = sn2 = n;
+ ss1 = ss2 = s;
+ sb2 = b;
+ }
+
+ #pragma db section(s1)
+ std::string ss1;
+
+ #pragma db section(s2)
+ bool sb2;
+ };
+}
+
+// Test readonly and inverse section members.
+//
+#pragma db namespace table("t8_")
+namespace test8
+{
+ struct object1;
+
+ #pragma db object session
+ struct object
+ {
+ object (int n_ = 999, const std::string& s_ = "xxx", object1* p_ = 0)
+ : n (n_), sn (n_), ss (s_), sp (p_) {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ int n;
+
+ #pragma db load(lazy)
+ odb::section s;
+
+ #pragma db section(s) readonly
+ int sn;
+
+ #pragma db section(s)
+ std::string ss;
+
+ #pragma db inverse(p) section(s)
+ object1* sp;
+ };
+
+ #pragma db object session
+ struct object1
+ {
+ object1 (object* p_ = 0): p (p_) {}
+ ~object1 () {delete p;}
+
+ #pragma db id auto
+ unsigned long id;
+
+ object* p;
+ };
+}
+
+// Test object without any columns to load or update.
+//
+#pragma db namespace table("t9_")
+namespace test9
+{
+ #pragma db object
+ struct object
+ {
+ object (int n_ = 999, const std::string& s_ = "xxx"): sn (n_), ss (s_) {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db load(lazy) update(manual)
+ odb::section s;
+
+ #pragma db section(s)
+ int sn;
+
+ #pragma db section(s)
+ std::string ss;
+ };
+}
+
+// Test section without any columns or containers to update.
+//
+#pragma db namespace table("t10_")
+namespace test10
+{
+ #pragma db object
+ struct object
+ {
+ object (int n_ = 999): n (n_), sn (n_) {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ int n;
+
+ #pragma db load(lazy)
+ odb::section s;
+
+ #pragma db section(s) readonly
+ int sn;
+ };
+}
+
+// Test section with composite member.
+//
+#pragma db namespace table("t11_")
+namespace test11
+{
+ #pragma db value
+ struct comp
+ {
+ comp (int n_, const std::string& s_): s (s_) {v.push_back (n_);}
+
+ std::string s;
+ std::vector<int> v;
+ };
+
+ #pragma db object
+ struct object
+ {
+ object (int n_ = 999, const std::string& s_ = "xxx")
+ : n (n_), sn (n_), sc (n_, s_) {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ int n;
+
+ #pragma db load(lazy)
+ odb::section s;
+
+ #pragma db section(s)
+ int sn;
+
+ #pragma db section(s)
+ comp sc;
+ };
+}
+
+// Test change state restoration on transaction rollback.
+//
+#pragma db namespace table("t12_")
+namespace test12
+{
+ #pragma db object
+ struct object
+ {
+ object (int n_ = 999, const std::string& s_ = "xxx")
+ : sn (n_), n (n_), ss (s_) {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db load(lazy) update(change)
+ odb::section s;
+
+ #pragma db section(s)
+ int sn;
+
+ int n;
+
+ #pragma db section(s)
+ std::string ss;
+ };
+}
+
+// Test section accessor/modifier.
+//
+#pragma db namespace table("t13_")
+namespace test13
+{
+ #pragma db object
+ struct object
+ {
+ object (int n_ = 999, const std::string& s_ = "xxx")
+ : n (n_), sn (n_), ss (s_) {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ int n;
+
+ #pragma db section(s_)
+ int sn;
+
+ #pragma db section(s_)
+ std::string ss;
+
+ public:
+ const odb::section&
+ s () const {return s_;}
+
+ odb::section&
+ rw_s () {return s_;}
+
+ private:
+ #pragma db load(lazy) update(manual)
+ odb::section s_;
+ };
+}
+
+// Test LOB in section streaming, column re-ordering.
+//
+#pragma db namespace table("t14_")
+namespace test14
+{
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id_ = 0, int n_ = 999, const std::string& s_ = "xxx")
+ : id (id_), sb (s_.begin (), s_.end ()), sn (n_), n (n_) {}
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db load(lazy)
+ odb::section s;
+
+ #pragma db section(s) type(BLOB_TYPE)
+ std::vector<char> sb; // Comes before sn.
+
+ #pragma db section(s)
+ int sn;
+
+ int n;
+ };
+}
+
+// Test sections and optimistic concurrency.
+//
+#pragma db namespace table("t15_")
+namespace test15
+{
+ #pragma db object optimistic
+ struct object
+ {
+ object (int n_ = 999, const std::string& s_ = "xxx")
+ : sn (n_), n (n_), ss (s_) {sv.push_back (n_);}
+
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db version mssql:type("ROWVERSION")
+ unsigned long long v;
+
+ #pragma db load(lazy)
+ odb::section s;
+
+ #pragma db section(s)
+ int sn;
+
+ int n;
+
+ #pragma db section(s)
+ std::string ss;
+
+ #pragma db section(s)
+ std::vector<int> sv;
+ };
+}
+
+// Test container-only sections and optimistic concurrency.
+//
+#pragma db namespace table("t16_")
+namespace test16
+{
+ #pragma db object optimistic
+ struct object
+ {
+ object (int n = 999) {sv.push_back (n);}
+
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db version // mssql:type("ROWVERSION")
+ unsigned long long v;
+
+ #pragma db load(lazy)
+ odb::section s;
+
+ #pragma db section(s)
+ std::vector<int> sv;
+ };
+}
+
+// Test reuse-inheritance, sections, and optimistic concurrency.
+//
+#pragma db namespace table("t17_")
+namespace test17
+{
+ #pragma db object optimistic sectionable abstract
+ struct root
+ {
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db version
+ unsigned long long v;
+ };
+
+ #pragma db object
+ struct base: root
+ {
+ };
+
+ #pragma db object
+ struct object: base
+ {
+ object (int n = 999): s1n (n) {s2v.push_back (n);}
+
+ #pragma db load(lazy)
+ odb::section s1;
+
+ #pragma db section(s1)
+ int s1n;
+
+ #pragma db load(lazy)
+ odb::section s2;
+
+ #pragma db section(s2)
+ std::vector<int> s2v;
+ };
+}
+
+// Test change-updated section and change-tracking container.
+//
+#pragma db namespace table("t18_")
+namespace test18
+{
+ #pragma db object
+ struct object
+ {
+ object (int n = 999): sn (n) {sv.push_back (n);}
+
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db load(lazy) update(change)
+ odb::section s;
+
+ #pragma db section(s)
+ int sn;
+
+ #pragma db section(s)
+ odb::vector<int> sv;
+ };
+}
+
+// Regression: BLOB in a value type used as a map value that is in a section.
+//
+#include <map>
+#include <vector>
+
+#pragma db namespace table("t19_")
+namespace test19
+{
+ #pragma db value
+ struct value
+ {
+ #pragma db type(BLOB_TYPE)
+ std::vector<char> b;
+ };
+
+ #pragma db object
+ struct object
+ {
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db load(lazy) update(always)
+ odb::section s;
+
+ #pragma db section(s)
+ std::map<int, value> m;
+ };
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/section/basics/testscript b/odb-tests/common/section/basics/testscript
new file mode 100644
index 0000000..c0c6617
--- /dev/null
+++ b/odb-tests/common/section/basics/testscript
@@ -0,0 +1,33 @@
+# file : common/section/basics/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/section/polymorphism/buildfile b/odb-tests/common/section/polymorphism/buildfile
new file mode 100644
index 0000000..b9a7514
--- /dev/null
+++ b/odb-tests/common/section/polymorphism/buildfile
@@ -0,0 +1,41 @@
+# file : common/section/polymorphism/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix t_section_p_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/section/polymorphism/driver.cxx b/odb-tests/common/section/polymorphism/driver.cxx
new file mode 100644
index 0000000..bdde74f
--- /dev/null
+++ b/odb-tests/common/section/polymorphism/driver.cxx
@@ -0,0 +1,1807 @@
+// file : common/section/polymorphism/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test sections in polymorphic objects.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/session.hxx>
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+struct failed {};
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Test basic polymorphic section functionality.
+ //
+ {
+ using namespace test1;
+
+ base b (123, "abc");
+ derived d (234, "bcd", true);
+
+ {
+ transaction t (db->begin ());
+ db->persist (b);
+ db->persist (d);
+ t.commit ();
+
+ assert (b.rs1.loaded ());
+ assert (b.rs2.loaded ());
+ assert (b.rs3.loaded ());
+ assert (b.rs4.loaded ());
+ assert (b.bs1.loaded ());
+
+ assert (d.rs1.loaded ());
+ assert (d.rs2.loaded ());
+ assert (d.rs3.loaded ());
+ assert (d.rs4.loaded ());
+ assert (d.bs1.loaded ());
+ assert (d.ds1.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> pb (db->load<base> (b.id));
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ assert (!pb->rs1.loaded ());
+ assert (!pb->rs2.loaded ());
+ assert (!pb->rs3.loaded ());
+ assert (!pb->rs4.loaded ());
+ assert (!pb->bs1.loaded ());
+
+ assert (!pd->rs1.loaded ());
+ assert (!pd->rs2.loaded ());
+ assert (!pd->rs3.loaded ());
+ assert (!pd->rs4.loaded ());
+ assert (!pd->bs1.loaded ());
+ assert (!pd->ds1.loaded ());
+
+ assert (pb->rs1n != b.rs1n && pb->rs1s != b.rs1s &&
+ pb->rs2n != b.rs2n && pb->rs2v != b.rs2v &&
+ pb->rs3v != b.rs3v &&
+ pb->rs4n != b.rs4n &&
+ pb->bs1n != b.bs1n);
+
+ assert (pd->rs1n != d.rs1n && pd->rs1s != d.rs1s &&
+ pd->rs1b != d.rs1b && pd->rs1v != d.rs1v &&
+ pd->rs2n != d.rs2n && pd->rs2v != d.rs2v &&
+ pd->rs3v != d.rs3v && pd->rs3n != d.rs3n &&
+ pd->rs4n != d.rs4n && pd->rs4s != d.rs4s &&
+ pd->bs1n != d.bs1n && pd->bs1s != d.bs1s &&
+ pd->ds1n != d.ds1n);
+
+ db->load (*pb, pb->rs1);
+ db->load (*pb, pb->rs2);
+ db->load (*pb, pb->rs3);
+ db->load (*pb, pb->rs4);
+ db->load (*pb, pb->bs1);
+
+ root* pr (pd.get ());
+ db->load (*pr, pr->rs1); // Via base.
+ db->load (*pd, pd->rs2);
+ db->load (*pr, pr->rs3); // Via base.
+ db->load (*pd, pd->rs4);
+ db->load (*pd, pd->bs1);
+ db->load (*pd, pd->ds1);
+
+ try
+ {
+ db->load (*pr, pd->bs1); // Object-section association is static.
+ assert (false);
+ }
+ catch (const section_not_in_object&) {}
+
+ assert (pb->rs1.loaded ());
+ assert (pb->rs2.loaded ());
+ assert (pb->rs3.loaded ());
+ assert (pb->rs4.loaded ());
+ assert (pb->bs1.loaded ());
+
+ assert (pd->rs1.loaded ());
+ assert (pd->rs2.loaded ());
+ assert (pd->rs3.loaded ());
+ assert (pd->rs4.loaded ());
+ assert (pd->bs1.loaded ());
+ assert (pd->ds1.loaded ());
+
+ assert (pb->rs1n == b.rs1n && pb->rs1s == b.rs1s &&
+ pb->rs2n == b.rs2n && pb->rs2v == b.rs2v &&
+ pb->rs3v == b.rs3v &&
+ pb->rs4n == b.rs4n &&
+ pb->bs1n == b.bs1n);
+
+ assert (pd->rs1n == d.rs1n && pd->rs1s == d.rs1s &&
+ pd->rs1b == d.rs1b && pd->rs1v == d.rs1v &&
+ pd->rs2n == d.rs2n && pd->rs2v == d.rs2v &&
+ pd->rs3v == d.rs3v && pd->rs3n == d.rs3n &&
+ pd->rs4n == d.rs4n && pd->rs4s == d.rs4s &&
+ pd->bs1n == d.bs1n && pd->bs1s == d.bs1s &&
+ pd->ds1n == d.ds1n);
+ t.commit ();
+ }
+
+ // Update object.
+ //
+ b.rs1n++;
+ b.rs1s += 'd';
+ b.rs1.change ();
+ b.rs2n++;
+ b.rs2v[0]++;
+ b.rs3v[0]++;
+ b.rs4n++;
+ b.bs1n++;
+
+ d.rs1n++;
+ d.rs1s += 'e';
+ d.rs1b = !d.rs1b;
+ d.rs1v[0]++;
+ d.rs1.change ();
+ d.rs2n++;
+ d.rs2v[0]++;
+ d.rs3v[0]++;
+ d.rs3n++;
+ d.rs4n++;
+ d.rs4s += 'e';
+ d.bs1n++;
+ d.bs1s += 'e';
+ d.ds1n++;
+
+ {
+ transaction t (db->begin ());
+ db->update (b);
+ db->update (d);
+ t.commit ();
+
+ assert (!b.rs1.changed ());
+ assert (!d.rs1.changed ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> pb (db->load<base> (b.id));
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ db->load (*pb, pb->rs1);
+ db->load (*pb, pb->rs2);
+ db->load (*pb, pb->rs3);
+ db->load (*pb, pb->rs4);
+ db->load (*pb, pb->bs1);
+
+ db->load (*pd, pd->rs1);
+ db->load (*pd, pd->rs2);
+ db->load (*pd, pd->rs3);
+ db->load (*pd, pd->rs4);
+ db->load (*pd, pd->bs1);
+ db->load (*pd, pd->ds1);
+
+ assert (pb->rs1n == b.rs1n && pb->rs1s == b.rs1s &&
+ pb->rs2n == b.rs2n && pb->rs2v == b.rs2v &&
+ pb->rs3v == b.rs3v &&
+ pb->rs4n == b.rs4n &&
+ pb->bs1n == b.bs1n);
+
+ assert (pd->rs1n == d.rs1n && pd->rs1s == d.rs1s &&
+ pd->rs1b == d.rs1b && pd->rs1v == d.rs1v &&
+ pd->rs2n == d.rs2n && pd->rs2v == d.rs2v &&
+ pd->rs3v == d.rs3v && pd->rs3n == d.rs3n &&
+ pd->rs4n == d.rs4n && pd->rs4s == d.rs4s &&
+ pd->bs1n == d.bs1n && pd->bs1s == d.bs1s &&
+ pd->ds1n == d.ds1n);
+ t.commit ();
+ }
+
+ // Update section.
+ //
+ b.rs1n++;
+ b.rs1s += 'd';
+ b.rs2n++;
+ b.rs2v[0]++;
+ b.rs3v[0]++;
+ b.rs4n++;
+ b.bs1n++;
+
+ d.rs1n++;
+ d.rs1s += 'e';
+ d.rs1b = !d.rs1b;
+ d.rs1v[0]++;
+ d.rs2n++;
+ d.rs2v[0]++;
+ d.rs3v[0]++;
+ d.rs3n++;
+ d.rs4n++;
+ d.rs4s += 'e';
+ d.bs1n++;
+ d.bs1s += 'e';
+ d.ds1n++;
+
+ {
+ transaction t (db->begin ());
+ db->update (b, b.rs1);
+ db->update (b, b.rs2);
+ db->update (b, b.rs3);
+ db->update (b, b.rs4);
+ db->update (b, b.bs1);
+
+ db->update (d, d.rs1);
+ db->update (d, d.rs2);
+ db->update (d, d.rs3);
+ db->update (d, d.rs4);
+ db->update (d, d.bs1);
+ db->update (d, d.ds1);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> pb (db->load<base> (b.id));
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ db->load (*pb, pb->rs1);
+ db->load (*pb, pb->rs2);
+ db->load (*pb, pb->rs3);
+ db->load (*pb, pb->rs4);
+ db->load (*pb, pb->bs1);
+
+ db->load (*pd, pd->rs1);
+ db->load (*pd, pd->rs2);
+ db->load (*pd, pd->rs3);
+ db->load (*pd, pd->rs4);
+ db->load (*pd, pd->bs1);
+ db->load (*pd, pd->ds1);
+
+ assert (pb->rs1n == b.rs1n && pb->rs1s == b.rs1s &&
+ pb->rs2n == b.rs2n && pb->rs2v == b.rs2v &&
+ pb->rs3v == b.rs3v &&
+ pb->rs4n == b.rs4n &&
+ pb->bs1n == b.bs1n);
+
+ assert (pd->rs1n == d.rs1n && pd->rs1s == d.rs1s &&
+ pd->rs1b == d.rs1b && pd->rs1v == d.rs1v &&
+ pd->rs2n == d.rs2n && pd->rs2v == d.rs2v &&
+ pd->rs3v == d.rs3v && pd->rs3n == d.rs3n &&
+ pd->rs4n == d.rs4n && pd->rs4s == d.rs4s &&
+ pd->bs1n == d.bs1n && pd->bs1s == d.bs1s &&
+ pd->ds1n == d.ds1n);
+ t.commit ();
+ }
+
+ // Reload.
+ //
+ b.rs1n++;
+ b.rs1s += 'd';
+ b.rs1.change ();
+ b.rs2n++;
+ b.rs2v[0]++;
+ b.rs3v[0]++;
+ b.rs4n++;
+ b.bs1n++;
+
+ d.rs1n++;
+ d.rs1s += 'e';
+ d.rs1b = !d.rs1b;
+ d.rs1v[0]++;
+ d.rs1.change ();
+ d.rs2n++;
+ d.rs2v[0]++;
+ d.rs3v[0]++;
+ d.rs3n++;
+ d.rs4n++;
+ d.rs4s += 'e';
+ d.bs1n++;
+ d.bs1s += 'e';
+ d.ds1n++;
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> pb (db->load<base> (b.id));
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ db->load (*pb, pb->rs1);
+ db->load (*pb, pb->rs2);
+ db->load (*pb, pb->rs3);
+ db->load (*pb, pb->rs4);
+ db->load (*pb, pb->bs1);
+
+ db->load (*pd, pd->rs1);
+ db->load (*pd, pd->rs2);
+ db->load (*pd, pd->rs3);
+ db->load (*pd, pd->rs4);
+ db->load (*pd, pd->bs1);
+ db->load (*pd, pd->ds1);
+
+ db->update (b);
+ db->update (d);
+
+ db->reload (*pb);
+ db->reload (*pd);
+
+ assert (pb->rs1n == b.rs1n && pb->rs1s == b.rs1s &&
+ pb->rs2n == b.rs2n && pb->rs2v == b.rs2v &&
+ pb->rs3v == b.rs3v &&
+ pb->rs4n == b.rs4n &&
+ pb->bs1n == b.bs1n);
+
+ assert (pd->rs1n == d.rs1n && pd->rs1s == d.rs1s &&
+ pd->rs1b == d.rs1b && pd->rs1v == d.rs1v &&
+ pd->rs2n == d.rs2n && pd->rs2v == d.rs2v &&
+ pd->rs3v == d.rs3v && pd->rs3n == d.rs3n &&
+ pd->rs4n == d.rs4n && pd->rs4s == d.rs4s &&
+ pd->bs1n == d.bs1n && pd->bs1s == d.bs1s &&
+ pd->ds1n == d.ds1n);
+
+ t.commit ();
+ }
+ }
+
+ // Test empty section and override "gap".
+ //
+ {
+ using namespace test2;
+
+ derived d (234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (d);
+ t.commit ();
+
+ assert (d.s.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ assert (!pd->s.loaded ());
+ assert (pd->sn != d.sn && pd->sv != d.sv);
+
+ root* pr (pd.get ());
+ db->load (*pr, pr->s); // Via root.
+
+ assert (pd->s.loaded ());
+ assert (pd->sn == d.sn && pd->sv == d.sv);
+ t.commit ();
+ }
+
+ // Update object.
+ //
+ d.sn++;
+ d.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ root* pr (&d);
+ db->update (pr); // Via root.
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ base* pb (pd.get ());
+ db->load (*pb, pb->s); // Via base.
+
+ assert (pd->sn == d.sn && pd->sv == d.sv);
+ t.commit ();
+ }
+
+ // Update section.
+ //
+ d.sn++;
+ d.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ root* pr (&d);
+ db->update (*pr, pr->s); // Via root.
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ db->load (*pd, pd->s);
+
+ assert (pd->sn == d.sn && pd->sv == d.sv);
+ t.commit ();
+ }
+
+ // Reload.
+ //
+ d.sn++;
+ d.sv[0]++;
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ db->load (*pd, pd->s);
+
+ db->update (d);
+
+ root* pr (pd.get ());
+ db->reload (*pr);
+
+ assert (pd->sn == d.sn && pd->sv == d.sv);
+ t.commit ();
+ }
+ }
+
+ // Test value-only/container-only base/override combinations.
+ //
+ {
+ using namespace test3;
+
+ root r (123);
+ base b (234);
+ derived d (345, "abc");
+
+ {
+ transaction t (db->begin ());
+ db->persist (r);
+ db->persist (b);
+ db->persist (d);
+ t.commit ();
+
+ assert (r.s1.loaded ());
+ assert (r.s2.loaded ());
+ assert (r.s3.loaded ());
+ assert (r.s4.loaded ());
+
+ assert (b.s1.loaded ());
+ assert (b.s2.loaded ());
+ assert (b.s3.loaded ());
+ assert (b.s4.loaded ());
+
+ assert (d.s1.loaded ());
+ assert (d.s2.loaded ());
+ assert (d.s3.loaded ());
+ assert (d.s4.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<root> pr (db->load<root> (r.id));
+ unique_ptr<base> pb (db->load<base> (b.id));
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ assert (!pr->s1.loaded ());
+ assert (!pr->s2.loaded ());
+ assert (!pr->s3.loaded ());
+ assert (!pr->s4.loaded ());
+
+ assert (!pb->s1.loaded ());
+ assert (!pb->s2.loaded ());
+ assert (!pb->s3.loaded ());
+ assert (!pb->s4.loaded ());
+
+ assert (!pd->s1.loaded ());
+ assert (!pd->s2.loaded ());
+ assert (!pd->s3.loaded ());
+ assert (!pd->s4.loaded ());
+
+ assert (pr->s1n != r.s1n &&
+ pr->s2n != r.s2n &&
+ pr->s3v != r.s3v &&
+ pr->s4nv != r.s4nv);
+
+ assert (pb->s1n != b.s1n &&
+ pb->s2n != b.s2n &&
+ pb->s3v != b.s3v &&
+ pb->s4nv != b.s4nv);
+
+ assert (pd->s1n != d.s1n && pd->s1s != d.s1s &&
+ pd->s2n != d.s2n && pd->s2v != d.s2v &&
+ pd->s3v != d.s3v && pd->s3n != d.s3n &&
+ pd->s4nv != d.s4nv && pd->s4sv != d.s4sv);
+
+ db->load (*pr, pr->s1);
+ db->load (*pr, pr->s2);
+ db->load (*pr, pr->s3);
+ db->load (*pr, pr->s4);
+
+ db->load (*pb, pb->s1);
+ db->load (*pb, pb->s2);
+ db->load (*pb, pb->s3);
+ db->load (*pb, pb->s4);
+
+ root* pdr (pd.get ());
+ db->load (*pdr, pdr->s1);
+ db->load (*pdr, pdr->s2);
+ db->load (*pdr, pdr->s3);
+ db->load (*pdr, pdr->s4);
+
+ assert (pr->s1.loaded ());
+ assert (pr->s2.loaded ());
+ assert (pr->s3.loaded ());
+ assert (pr->s4.loaded ());
+
+ assert (pb->s1.loaded ());
+ assert (pb->s2.loaded ());
+ assert (pb->s3.loaded ());
+ assert (pb->s4.loaded ());
+
+ assert (pd->s1.loaded ());
+ assert (pd->s2.loaded ());
+ assert (pd->s3.loaded ());
+ assert (pd->s4.loaded ());
+
+ assert (pr->s1n == r.s1n &&
+ pr->s2n == r.s2n &&
+ pr->s3v == r.s3v &&
+ pr->s4nv == r.s4nv);
+
+ assert (pb->s1n == b.s1n &&
+ pb->s2n == b.s2n &&
+ pb->s3v == b.s3v &&
+ pb->s4nv == b.s4nv);
+
+ assert (pd->s1n == d.s1n && pd->s1s == d.s1s &&
+ pd->s2n == d.s2n && pd->s2v == d.s2v &&
+ pd->s3v == d.s3v && pd->s3n == d.s3n &&
+ pd->s4nv == d.s4nv && pd->s4sv == d.s4sv);
+ t.commit ();
+ }
+
+ // Update object.
+ //
+ r.s1n++;
+ r.s2n++;
+ r.s3v[0]++;
+ r.s4nv[0]++;
+
+ b.s1n++;
+ b.s2n++;
+ b.s3v[0]++;
+ b.s4nv[0]++;
+
+ d.s1n++;
+ d.s1s += 'd';
+ d.s2n++;
+ d.s2v[0]++;
+ d.s3v[0]++;
+ d.s3n++;
+ d.s4nv[0]++;
+ d.s4sv[0] += 'd';
+
+ {
+ transaction t (db->begin ());
+ db->update (r);
+ db->update (b);
+ db->update (d);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<root> pr (db->load<root> (r.id));
+ unique_ptr<base> pb (db->load<base> (b.id));
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ db->load (*pr, pr->s1);
+ db->load (*pr, pr->s2);
+ db->load (*pr, pr->s3);
+ db->load (*pr, pr->s4);
+
+ db->load (*pb, pb->s1);
+ db->load (*pb, pb->s2);
+ db->load (*pb, pb->s3);
+ db->load (*pb, pb->s4);
+
+ db->load (*pd, pd->s1);
+ db->load (*pd, pd->s2);
+ db->load (*pd, pd->s3);
+ db->load (*pd, pd->s4);
+
+ assert (pr->s1n == r.s1n &&
+ pr->s2n == r.s2n &&
+ pr->s3v == r.s3v &&
+ pr->s4nv == r.s4nv);
+
+ assert (pb->s1n == b.s1n &&
+ pb->s2n == b.s2n &&
+ pb->s3v == b.s3v &&
+ pb->s4nv == b.s4nv);
+
+ assert (pd->s1n == d.s1n && pd->s1s == d.s1s &&
+ pd->s2n == d.s2n && pd->s2v == d.s2v &&
+ pd->s3v == d.s3v && pd->s3n == d.s3n &&
+ pd->s4nv == d.s4nv && pd->s4sv == d.s4sv);
+ t.commit ();
+ }
+
+ // Update section.
+ //
+ r.s1n++;
+ r.s2n++;
+ r.s3v[0]++;
+ r.s4nv[0]++;
+
+ b.s1n++;
+ b.s2n++;
+ b.s3v[0]++;
+ b.s4nv[0]++;
+
+ d.s1n++;
+ d.s1s += 'd';
+ d.s2n++;
+ d.s2v[0]++;
+ d.s3v[0]++;
+ d.s3n++;
+ d.s4nv[0]++;
+ d.s4sv[0] += 'd';
+
+ {
+ transaction t (db->begin ());
+ db->update (r, r.s1);
+ db->update (r, r.s2);
+ db->update (r, r.s3);
+ db->update (r, r.s4);
+
+ db->update (b, b.s1);
+ db->update (b, b.s2);
+ db->update (b, b.s3);
+ db->update (b, b.s4);
+
+ root& rr (d);
+ db->update (rr, rr.s1);
+ db->update (rr, rr.s2);
+ db->update (rr, rr.s3);
+ db->update (rr, rr.s4);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<root> pr (db->load<root> (r.id));
+ unique_ptr<base> pb (db->load<base> (b.id));
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ db->load (*pr, pr->s1);
+ db->load (*pr, pr->s2);
+ db->load (*pr, pr->s3);
+ db->load (*pr, pr->s4);
+
+ db->load (*pb, pb->s1);
+ db->load (*pb, pb->s2);
+ db->load (*pb, pb->s3);
+ db->load (*pb, pb->s4);
+
+ db->load (*pd, pd->s1);
+ db->load (*pd, pd->s2);
+ db->load (*pd, pd->s3);
+ db->load (*pd, pd->s4);
+
+ assert (pr->s1n == r.s1n &&
+ pr->s2n == r.s2n &&
+ pr->s3v == r.s3v &&
+ pr->s4nv == r.s4nv);
+
+ assert (pb->s1n == b.s1n &&
+ pb->s2n == b.s2n &&
+ pb->s3v == b.s3v &&
+ pb->s4nv == b.s4nv);
+
+ assert (pd->s1n == d.s1n && pd->s1s == d.s1s &&
+ pd->s2n == d.s2n && pd->s2v == d.s2v &&
+ pd->s3v == d.s3v && pd->s3n == d.s3n &&
+ pd->s4nv == d.s4nv && pd->s4sv == d.s4sv);
+ t.commit ();
+ }
+
+ // Reload.
+ //
+ r.s1n++;
+ r.s2n++;
+ r.s3v[0]++;
+ r.s4nv[0]++;
+
+ b.s1n++;
+ b.s2n++;
+ b.s3v[0]++;
+ b.s4nv[0]++;
+
+ d.s1n++;
+ d.s1s += 'd';
+ d.s2n++;
+ d.s2v[0]++;
+ d.s3v[0]++;
+ d.s3n++;
+ d.s4nv[0]++;
+ d.s4sv[0] += 'd';
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<root> pr (db->load<root> (r.id));
+ unique_ptr<base> pb (db->load<base> (b.id));
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ db->load (*pr, pr->s1);
+ db->load (*pr, pr->s2);
+ db->load (*pr, pr->s3);
+ db->load (*pr, pr->s4);
+
+ db->load (*pb, pb->s1);
+ db->load (*pb, pb->s2);
+ db->load (*pb, pb->s3);
+ db->load (*pb, pb->s4);
+
+ db->load (*pd, pd->s1);
+ db->load (*pd, pd->s2);
+ db->load (*pd, pd->s3);
+ db->load (*pd, pd->s4);
+
+ db->update (r);
+ db->update (b);
+ db->update (d);
+
+ db->reload (*pr);
+ db->reload (*pb);
+ db->reload (*pd);
+
+ assert (pr->s1n == r.s1n &&
+ pr->s2n == r.s2n &&
+ pr->s3v == r.s3v &&
+ pr->s4nv == r.s4nv);
+
+ assert (pb->s1n == b.s1n &&
+ pb->s2n == b.s2n &&
+ pb->s3v == b.s3v &&
+ pb->s4nv == b.s4nv);
+
+ assert (pd->s1n == d.s1n && pd->s1s == d.s1s &&
+ pd->s2n == d.s2n && pd->s2v == d.s2v &&
+ pd->s3v == d.s3v && pd->s3n == d.s3n &&
+ pd->s4nv == d.s4nv && pd->s4sv == d.s4sv);
+ t.commit ();
+ }
+ }
+
+ // Test basic polymorphic optimistic section functionality.
+ //
+ {
+ using namespace test4;
+
+ base b (123, "abc");
+ derived d (234, "bcd", true);
+
+ {
+ transaction t (db->begin ());
+ db->persist (b);
+ db->persist (d);
+ t.commit ();
+
+ assert (b.rs1.loaded ());
+ assert (b.rs2.loaded ());
+ assert (b.rs3.loaded ());
+ assert (b.rs4.loaded ());
+ assert (b.bs1.loaded ());
+
+ assert (d.rs1.loaded ());
+ assert (d.rs2.loaded ());
+ assert (d.rs3.loaded ());
+ assert (d.rs4.loaded ());
+ assert (d.bs1.loaded ());
+ assert (d.ds1.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> pb (db->load<base> (b.id));
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ assert (!pb->rs1.loaded ());
+ assert (!pb->rs2.loaded ());
+ assert (!pb->rs3.loaded ());
+ assert (!pb->rs4.loaded ());
+ assert (!pb->bs1.loaded ());
+
+ assert (!pd->rs1.loaded ());
+ assert (!pd->rs2.loaded ());
+ assert (!pd->rs3.loaded ());
+ assert (!pd->rs4.loaded ());
+ assert (!pd->bs1.loaded ());
+ assert (!pd->ds1.loaded ());
+
+ assert (pb->rs1n != b.rs1n && pb->rs1s != b.rs1s &&
+ pb->rs2n != b.rs2n &&
+ pb->rs3n != b.rs3n &&
+ pb->rs4n != b.rs4n && pb->rs4s != b.rs4s);
+
+ assert (pd->rs1n != d.rs1n && pd->rs1s != d.rs1s &&
+ pd->rs1b != d.rs1b && pd->rs1v != d.rs1v &&
+ pd->rs2n != d.rs2n &&
+ pd->rs3n != d.rs3n && pd->rs3s != d.rs3s &&
+ pd->rs4n != d.rs4n && pd->rs4s != d.rs4s &&
+ pd->rs4v != d.rs4v &&
+ pd->bs1n != d.bs1n &&
+ pd->ds1v != d.ds1v);
+
+ db->load (*pb, pb->rs1);
+ db->load (*pb, pb->rs2);
+ db->load (*pb, pb->rs3);
+ db->load (*pb, pb->rs4);
+ db->load (*pb, pb->bs1); // No-op.
+
+ root* pr (pd.get ());
+ db->load (*pr, pr->rs1); // Via base.
+ db->load (*pd, pd->rs2);
+ db->load (*pd, pd->rs3);
+ db->load (*pr, pr->rs4); // Via base.
+ db->load (*pd, pd->bs1);
+ db->load (*pd, pd->ds1);
+
+ assert (pb->rs1.loaded ());
+ assert (pb->rs2.loaded ());
+ assert (pb->rs3.loaded ());
+ assert (pb->rs4.loaded ());
+ assert (pb->bs1.loaded ());
+
+ assert (pd->rs1.loaded ());
+ assert (pd->rs2.loaded ());
+ assert (pd->rs3.loaded ());
+ assert (pd->rs4.loaded ());
+ assert (pd->bs1.loaded ());
+ assert (pd->ds1.loaded ());
+
+ assert (pb->rs1n == b.rs1n && pb->rs1s == b.rs1s &&
+ pb->rs2n == b.rs2n &&
+ pb->rs3n == b.rs3n &&
+ pb->rs4n == b.rs4n && pb->rs4s == b.rs4s);
+
+ assert (pd->rs1n == d.rs1n && pd->rs1s == d.rs1s &&
+ pd->rs1b == d.rs1b && pd->rs1v == d.rs1v &&
+ pd->rs2n == d.rs2n &&
+ pd->rs3n == d.rs3n && pd->rs3s == d.rs3s &&
+ pd->rs4n == d.rs4n && pd->rs4s == d.rs4s &&
+ pd->rs4v == d.rs4v &&
+ pd->bs1n == d.bs1n &&
+ pd->ds1v == d.ds1v);
+ t.commit ();
+ }
+
+ base b1 (b);
+ derived d1 (d);
+
+ // Update object.
+ //
+ for (unsigned short s (1); s < 7; ++s)
+ {
+ b1.rs1n++;
+ b1.rs1s += 'd';
+ b1.rs1.change ();
+ b1.rs4s += 'd';
+
+ d1.rs1n++;
+ d1.rs1s += 'e';
+ d1.rs1b = !d.rs1b;
+ d1.rs1v[0]++;
+ d1.rs1.change ();
+ d1.rs4s += 'e';
+ d1.rs4v[0]++;
+ d1.bs1n++;
+ d1.ds1v[0]++;
+
+ transaction t (db->begin ());
+ unique_ptr<base> pb (db->load<base> (b.id));
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ db->update (b1);
+ db->update (d1);
+
+ assert (!b1.rs1.changed ());
+ assert (!d1.rs1.changed ());
+
+ assert (b.v != b1.v);
+ assert (d.v != d1.v);
+
+ try
+ {
+ bool a (false);
+ switch (s)
+ {
+ case 1: db->load (*pb, pb->rs1); break;
+ case 2: db->load (*pb, pb->rs2); break;
+ case 3: db->load (*pb, pb->rs3); break;
+ case 4: db->load (*pb, pb->rs4); break;
+ case 5:
+ case 6: a = true; break; // No-op.
+ default: break;
+ }
+ assert (a);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (*pb);
+
+ assert (!pb->rs1.loaded ());
+ assert (!pb->rs2.loaded ());
+ assert (!pb->rs3.loaded ());
+ assert (!pb->rs3.loaded ());
+ assert (!pb->bs1.loaded ());
+
+ db->load (*pb, pb->rs1);
+ db->load (*pb, pb->rs2);
+ db->load (*pb, pb->rs3);
+ db->load (*pb, pb->rs4);
+ db->load (*pb, pb->bs1); // No-op.
+
+ assert (pb->rs1n == b1.rs1n && pb->rs1s == b1.rs1s &&
+ pb->rs2n == b1.rs2n &&
+ pb->rs3n == b1.rs3n &&
+ pb->rs4n == b1.rs4n && pb->rs4s == b1.rs4s);
+ }
+
+ try
+ {
+ switch (s)
+ {
+ case 1: db->load (*pd, pd->rs1); break;
+ case 2: db->load (*pd, pd->rs2); break;
+ case 3: db->load (*pd, pd->rs3); break;
+ case 4: db->load (*pd, pd->rs4); break;
+ case 5: db->load (*pd, pd->bs1); break;
+ case 6: db->load (*pd, pd->ds1); break;
+ default: break;
+ }
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (*pd);
+
+ assert (!pd->rs1.loaded ());
+ assert (!pd->rs2.loaded ());
+ assert (!pd->rs3.loaded ());
+ assert (!pd->rs4.loaded ());
+ assert (!pd->bs1.loaded ());
+ assert (!pd->ds1.loaded ());
+
+ db->load (*pd, pd->rs1);
+ db->load (*pd, pd->rs2);
+ db->load (*pd, pd->rs3);
+ db->load (*pd, pd->rs4);
+ db->load (*pd, pd->bs1);
+ db->load (*pd, pd->ds1);
+
+ assert (pd->rs1n == d1.rs1n && pd->rs1s == d1.rs1s &&
+ pd->rs1b == d1.rs1b && pd->rs1v == d1.rs1v &&
+ pd->rs2n == d1.rs2n &&
+ pd->rs3n == d1.rs3n && pd->rs3s == d1.rs3s &&
+ pd->rs4n == d1.rs4n && pd->rs4s == d1.rs4s &&
+ pd->rs4v == d1.rs4v &&
+ pd->bs1n == d1.bs1n &&
+ pd->ds1v == d1.ds1v);
+ }
+
+ db->reload (b);
+ db->reload (d);
+
+ assert (b.v == b1.v);
+ assert (d.v == d1.v);
+
+ assert (b.rs1n == b1.rs1n && b.rs1s == b1.rs1s &&
+ b.rs2n == b1.rs2n &&
+ b.rs3n == b1.rs3n &&
+ b.rs4n == b1.rs4n && b.rs4s == b1.rs4s);
+
+ assert (d.rs1n == d1.rs1n && d.rs1s == d1.rs1s &&
+ d.rs1b == d1.rs1b && d.rs1v == d1.rs1v &&
+ d.rs2n == d1.rs2n &&
+ d.rs3n == d1.rs3n && d.rs3s == d1.rs3s &&
+ d.rs4n == d1.rs4n && d.rs4s == d1.rs4s &&
+ d.rs4v == d1.rs4v &&
+ d.bs1n == d1.bs1n &&
+ d.ds1v == d1.ds1v);
+
+ t.commit ();
+ }
+
+ // Update section.
+ //
+ for (unsigned short s (1); s < 7; ++s)
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> pb (db->load<base> (b.id));
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ switch (s)
+ {
+ case 1:
+ b1.rs1n++;
+ b1.rs1s += 'd';
+
+ d1.rs1n++;
+ d1.rs1s += 'e';
+ d1.rs1b = !d.rs1b;
+ d1.rs1v[0]++;
+
+ db->update (b1, b1.rs1);
+ db->update (d1, d1.rs1);
+
+ assert (b.v != b1.v);
+ assert (d.v != d1.v);
+ break;
+ case 2:
+ db->update (b1, b1.rs2); // No-op.
+ db->update (d1, d1.rs2); // No-op.
+
+ assert (b.v == b1.v);
+ assert (d.v == d1.v);
+ continue; // Object hasn't changed.
+ case 3:
+ db->update (b1, b1.rs3); // No-op.
+ db->update (d1, d1.rs3); // No-op.
+
+ assert (b.v == b1.v);
+ assert (d.v == d1.v);
+ continue; // Object hasn't changed.
+ case 4:
+ b1.rs4s += 'd';
+
+ d1.rs4s += 'e';
+ d1.rs4v[0]++;
+
+ db->update (b1, b1.rs4);
+ db->update (d1, d1.rs4);
+
+ assert (b.v != b1.v);
+ assert (d.v != d1.v);
+ break;
+ case 5:
+ d1.bs1n++;
+
+ db->update (b1, b1.bs1); // No-op.
+ db->update (d1, d1.bs1);
+
+ assert (b.v == b1.v);
+ assert (d.v != d1.v);
+ break;
+ case 6:
+ d1.ds1v[0]++;
+
+ db->update (d1, d1.ds1);
+
+ assert (d.v != d1.v);
+ break;
+ default: break;
+ }
+
+ try
+ {
+ bool a (false);
+ switch (s)
+ {
+ case 1: db->load (*pb, pb->rs1); break;
+ case 4: db->load (*pb, pb->rs4); break;
+ case 5:
+ case 6: a = true; break; // No-op.
+ default: break;
+ }
+ assert (a);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (*pb);
+
+ assert (!pb->rs1.loaded ());
+ assert (!pb->rs2.loaded ());
+ assert (!pb->rs3.loaded ());
+ assert (!pb->rs4.loaded ());
+ assert (!pb->bs1.loaded ());
+
+ db->load (*pb, pb->rs1);
+ db->load (*pb, pb->rs2);
+ db->load (*pb, pb->rs3);
+ db->load (*pb, pb->rs4);
+ db->load (*pb, pb->bs1); // No-op.
+
+ assert (pb->rs1n == b1.rs1n && pb->rs1s == b1.rs1s &&
+ pb->rs2n == b1.rs2n &&
+ pb->rs3n == b1.rs3n &&
+ pb->rs4n == b1.rs4n && pb->rs4s == b1.rs4s);
+ }
+
+ try
+ {
+ switch (s)
+ {
+ case 1: db->load (*pd, pd->rs1); break;
+ case 4: db->load (*pd, pd->rs4); break;
+ case 5: db->load (*pd, pd->bs1); break;
+ case 6: db->load (*pd, pd->ds1); break;
+ default: break;
+ }
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (*pd);
+
+ assert (!pd->rs1.loaded ());
+ assert (!pd->rs2.loaded ());
+ assert (!pd->rs3.loaded ());
+ assert (!pd->rs4.loaded ());
+ assert (!pd->bs1.loaded ());
+ assert (!pd->ds1.loaded ());
+
+ db->load (*pd, pd->rs1);
+ db->load (*pd, pd->rs2);
+ db->load (*pd, pd->rs3);
+ db->load (*pd, pd->rs4);
+ db->load (*pd, pd->bs1);
+ db->load (*pd, pd->ds1);
+
+ assert (pd->rs1n == d1.rs1n && pd->rs1s == d1.rs1s &&
+ pd->rs1b == d1.rs1b && pd->rs1v == d1.rs1v &&
+ pd->rs2n == d1.rs2n &&
+ pd->rs3n == d1.rs3n && pd->rs3s == d1.rs3s &&
+ pd->rs4n == d1.rs4n && pd->rs4s == d1.rs4s &&
+ pd->rs4v == d1.rs4v &&
+ pd->bs1n == d1.bs1n &&
+ pd->ds1v == d1.ds1v);
+ }
+
+ db->reload (b);
+ db->reload (d);
+
+ assert (b.v == b1.v);
+ assert (d.v == d1.v);
+
+ assert (b.rs1n == b1.rs1n && b.rs1s == b1.rs1s &&
+ b.rs2n == b1.rs2n &&
+ b.rs3n == b1.rs3n &&
+ b.rs4n == b1.rs4n && b.rs4s == b1.rs4s);
+
+ assert (d.rs1n == d1.rs1n && d.rs1s == d1.rs1s &&
+ d.rs1b == d1.rs1b && d.rs1v == d1.rs1v &&
+ d.rs2n == d1.rs2n &&
+ d.rs3n == d1.rs3n && d.rs3s == d1.rs3s &&
+ d.rs4n == d1.rs4n && d.rs4s == d1.rs4s &&
+ d.rs4v == d1.rs4v &&
+ d.bs1n == d1.bs1n &&
+ d.ds1v == d1.ds1v);
+
+ t.commit ();
+ }
+
+ // Update changed section.
+ //
+ for (unsigned short s (1); s < 7; ++s)
+ {
+ if (s == 2 || s == 3) // Readonly sections.
+ continue;
+
+ transaction t (db->begin ());
+
+ switch (s)
+ {
+ case 1:
+ b1.rs1n++;
+ b1.rs1s += 'd';
+
+ d1.rs1n++;
+ d1.rs1s += 'e';
+ d1.rs1b = !d.rs1b;
+ d1.rs1v[0]++;
+
+ db->update (b1, b1.rs1);
+ db->update (d1, d1.rs1);
+ break;
+ case 4:
+ b1.rs4s += 'd';
+
+ d1.rs4s += 'e';
+ d1.rs4v[0]++;
+
+ db->update (b1, b1.rs4);
+ db->update (d1, d1.rs4);
+ break;
+ case 5:
+ d1.bs1n++;
+
+ db->update (b1, b1.bs1); // No-op.
+ db->update (d1, d1.bs1);
+ break;
+ case 6:
+ d1.ds1v[0]++;
+
+ db->update (d1, d1.bs1);
+ break;
+ default: break;
+ }
+
+ try
+ {
+ bool a (false);
+ switch (s)
+ {
+ case 1: db->update (b, b.rs1); break;
+ case 4: db->update (b, b.rs4); break;
+ case 5:
+ case 6: a = true; break; // No-op.
+ default: break;
+ }
+ assert (a);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (b);
+
+ switch (s)
+ {
+ case 1: db->update (b, b.rs1); break;
+ case 4: db->update (b, b.rs4); break;
+ default: break;
+ }
+ }
+
+ try
+ {
+ switch (s)
+ {
+ case 1: db->update (d, d.rs1); break;
+ case 4: db->update (d, d.rs4); break;
+ case 5: db->update (d, d.bs1); break;
+ case 6: db->update (d, d.ds1); break;
+ default: break;
+ }
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (d);
+
+ switch (s)
+ {
+ case 1: db->update (d, d.rs1); break;
+ case 4: db->update (d, d.rs4); break;
+ case 5: db->update (d, d.bs1); break;
+ case 6: db->update (d, d.ds1); break;
+ default: break;
+ }
+ }
+
+ db->reload (b1);
+ db->reload (d1);
+
+ t.commit ();
+ }
+ }
+
+ // Test polymorphic optimistic readonly/empty to readwrite section
+ // override.
+ //
+ {
+ using namespace test5;
+
+ base b;
+ derived d (123);
+
+ {
+ transaction t (db->begin ());
+ db->persist (b);
+ db->persist (d);
+ t.commit ();
+
+ assert (b.s.loaded ());
+ assert (d.s.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> pb (db->load<base> (b.id));
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ assert (!pb->s.loaded ());
+ assert (!pd->s.loaded ());
+ assert (pd->sn != d.sn);
+
+ db->load (*pb, pb->s); // No-op.
+ db->load (*pd, pd->s);
+
+ assert (pb->s.loaded ());
+ assert (pd->s.loaded ());
+
+ assert (pd->sn == d.sn);
+
+ t.commit ();
+ }
+
+ // Update object.
+ //
+ base b1 (b);
+ derived d1 (d);
+ d1.sn++;
+ d1.s.change ();
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ db->update (d1);
+
+ assert (!d1.s.changed ());
+ assert (d.v != d1.v);
+
+ try
+ {
+ db->load (*pd, pd->s);
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (*pd);
+ assert (!pd->s.loaded ());
+ db->load (*pd, pd->s);
+ assert (pd->sn == d1.sn);
+ }
+
+ db->reload (d);
+ assert (d.v == d1.v);
+ assert (d.sn == d1.sn);
+ t.commit ();
+ }
+
+ // Update section.
+ //
+ d1.sn++;
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ db->update (b1, b1.s); // No-op.
+ db->update (d1, d1.s);
+
+ assert (b.v == b1.v);
+ assert (d.v != d1.v);
+
+ try
+ {
+ db->load (*pd, pd->s);
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (*pd);
+ assert (!pd->s.loaded ());
+ db->load (*pd, pd->s);
+
+ assert (pd->sn == d1.sn);
+ }
+
+ db->reload (d);
+ assert (d.v == d1.v);
+ assert (d.sn == d1.sn);
+ t.commit ();
+ }
+
+ // Update changed section.
+ //
+ d1.sn++;
+
+ {
+ transaction t (db->begin ());
+ db->update (d1, d1.s);
+
+ try
+ {
+ db->update (d, d.s);
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (d);
+ db->update (d, d.s);
+ }
+
+ t.commit ();
+ }
+ }
+
+ // Test polymorphic optimistic readonly/empty to readwrite section
+ // override, eager-loaded case.
+ //
+ {
+ using namespace test6;
+
+ derived d (123);
+
+ {
+ transaction t (db->begin ());
+ db->persist (d);
+ t.commit ();
+
+ assert (d.s.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ assert (pd->s.loaded ());
+ assert (pd->sn == d.sn);
+
+ t.commit ();
+ }
+
+ // Update object.
+ //
+ derived d1 (d);
+ d1.sn++;
+ d1.s.change ();
+
+ {
+ transaction t (db->begin ());
+
+ db->update (d1);
+
+ assert (!d1.s.changed ());
+ assert (d.v != d1.v);
+
+ db->reload (d);
+ assert (d.v == d1.v);
+ assert (d.sn == d1.sn);
+ t.commit ();
+ }
+
+ // Update section.
+ //
+ d1.sn++;
+
+ {
+ transaction t (db->begin ());
+
+ db->update (d1, d1.s);
+ assert (d.v != d1.v);
+
+ db->reload (d);
+ assert (d.v == d1.v);
+ assert (d.sn == d1.sn);
+ t.commit ();
+ }
+
+ // Update changed section.
+ //
+ d1.sn++;
+
+ {
+ transaction t (db->begin ());
+ db->update (d1, d1.s);
+
+ try
+ {
+ db->update (d, d.s);
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (d);
+ db->update (d, d.s);
+ }
+
+ t.commit ();
+ }
+ }
+
+ // Test polymorphic optimistic section added in derived.
+ //
+ {
+ using namespace test7;
+
+ base b;
+ derived d (123);
+
+ {
+ transaction t (db->begin ());
+ db->persist (b);
+ db->persist (d);
+ t.commit ();
+
+ assert (b.s.loaded ());
+ assert (d.s.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> pb (db->load<base> (b.id));
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ assert (!pb->s.loaded ());
+ assert (!pd->s.loaded ());
+ assert (pd->sn != d.sn);
+
+ db->load (*pb, pb->s); // No-op.
+ db->load (*pd, pd->s);
+
+ assert (pb->s.loaded ());
+ assert (pd->s.loaded ());
+
+ assert (pd->sn == d.sn);
+
+ t.commit ();
+ }
+
+ // Update object.
+ //
+ base b1 (b);
+ derived d1 (d);
+ d1.sn++;
+ d1.s.change ();
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ db->update (d1);
+
+ assert (!d1.s.changed ());
+ assert (d.v != d1.v);
+
+ try
+ {
+ db->load (*pd, pd->s);
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (*pd);
+ assert (!pd->s.loaded ());
+ db->load (*pd, pd->s);
+ assert (pd->sn == d1.sn);
+ }
+
+ db->reload (d);
+ assert (d.v == d1.v);
+ assert (d.sn == d1.sn);
+ t.commit ();
+ }
+
+ // Update section.
+ //
+ d1.sn++;
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ db->update (b1, b1.s); // No-op.
+ db->update (d1, d1.s);
+
+ assert (b.v == b1.v);
+ assert (d.v != d1.v);
+
+ try
+ {
+ db->load (*pd, pd->s);
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (*pd);
+ assert (!pd->s.loaded ());
+ db->load (*pd, pd->s);
+
+ assert (pd->sn == d1.sn);
+ }
+
+ db->reload (d);
+ assert (d.v == d1.v);
+ assert (d.sn == d1.sn);
+ t.commit ();
+ }
+
+ // Update changed section.
+ //
+ d1.sn++;
+
+ {
+ transaction t (db->begin ());
+ db->update (d1, d1.s);
+
+ try
+ {
+ db->update (d, d.s);
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (d);
+ db->update (d, d.s);
+ }
+
+ t.commit ();
+ }
+ }
+
+ // Test reuse/polymorphic inheritance and optimistic mix.
+ //
+ {
+ using namespace test8;
+
+ derived d (123);
+
+ {
+ transaction t (db->begin ());
+ db->persist (d);
+ t.commit ();
+
+ assert (d.s.loaded ());
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ assert (!pd->s.loaded ());
+ assert (pd->sn != d.sn);
+
+ db->load (*pd, pd->s);
+
+ assert (pd->s.loaded ());
+ assert (pd->sn == d.sn);
+
+ t.commit ();
+ }
+
+ // Update object.
+ //
+ derived d1 (d);
+ d1.sn++;
+ d1.s.change ();
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ db->update (d1);
+
+ assert (!d1.s.changed ());
+ assert (d.v != d1.v);
+
+ try
+ {
+ db->load (*pd, pd->s);
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (*pd);
+ assert (!pd->s.loaded ());
+ db->load (*pd, pd->s);
+ assert (pd->sn == d1.sn);
+ }
+
+ db->reload (d);
+ assert (d.v == d1.v);
+ assert (d.sn == d1.sn);
+ t.commit ();
+ }
+
+ // Update section.
+ //
+ d1.sn++;
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<derived> pd (db->load<derived> (d.id));
+
+ db->update (d1, d1.s);
+ assert (d.v != d1.v);
+
+ try
+ {
+ db->load (*pd, pd->s);
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (*pd);
+ assert (!pd->s.loaded ());
+ db->load (*pd, pd->s);
+
+ assert (pd->sn == d1.sn);
+ }
+
+ db->reload (d);
+ assert (d.v == d1.v);
+ assert (d.sn == d1.sn);
+ t.commit ();
+ }
+
+ // Update changed section.
+ //
+ d1.sn++;
+
+ {
+ transaction t (db->begin ());
+ db->update (d1, d1.s);
+
+ try
+ {
+ db->update (d, d.s);
+ assert (false);
+ }
+ catch (const object_changed&)
+ {
+ db->reload (d);
+ db->update (d, d.s);
+ }
+
+ t.commit ();
+ }
+ }
+
+ // Test reuse/polymorphic inheritance and optimistic mix.
+ //
+ {
+ using namespace test9;
+ using std::shared_ptr;
+
+ unsigned long id;
+
+ {
+ container c (123);
+
+ c.e1.push_back (shared_ptr<element> (new element (11)));
+ c.e1.push_back (shared_ptr<element> (new element (12)));
+
+ c.e2.push_back (shared_ptr<element> (new element (21)));
+ c.e2.push_back (shared_ptr<element> (new element (22)));
+
+ transaction t (db->begin ());
+
+ db->persist (c.e1[0]);
+ db->persist (c.e1[1]);
+ db->persist (c.e2[0]);
+ db->persist (c.e2[1]);
+
+ id = db->persist (c);
+
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+
+ shared_ptr<container> c (db->load<container> (id));
+
+ assert (c->n == 123);
+ db->load (*c, c->s);
+ assert (c->e1.size () == 2 && c->e1[0]->n == 11 && c->e1[1]->n == 12);
+ assert (c->e2.size () == 2 && c->e2[0]->n == 21 && c->e2[1]->n == 22);
+
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/section/polymorphism/test.hxx b/odb-tests/common/section/polymorphism/test.hxx
new file mode 100644
index 0000000..6d524bd
--- /dev/null
+++ b/odb-tests/common/section/polymorphism/test.hxx
@@ -0,0 +1,542 @@
+// file : common/section/polymorphism/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <vector>
+#include <memory>
+
+#include <odb/core.hxx>
+#include <odb/section.hxx>
+
+// Test basic polymorphic section functionality.
+//
+#pragma db namespace table("t1_")
+namespace test1
+{
+ #pragma db object polymorphic abstract
+ struct root
+ {
+ root (int n): rs1n (n), rs2n (n), rs4n (n) {rs2v.push_back (n);}
+ virtual ~root () {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ // rs1: override in base and derived
+ //
+ #pragma db load(lazy) update(change)
+ odb::section rs1;
+
+ #pragma db section(rs1)
+ int rs1n;
+
+ // rs2: no override
+ //
+ #pragma db load(lazy)
+ odb::section rs2;
+
+ #pragma db section(rs2)
+ int rs2n;
+
+ #pragma db section(rs2)
+ std::vector<int> rs2v;
+
+ // rs3: empty
+ //
+ #pragma db load(lazy)
+ odb::section rs3;
+
+ // rs4: override "gap"
+ //
+ #pragma db load(lazy)
+ odb::section rs4;
+
+ #pragma db section(rs4)
+ int rs4n;
+ };
+
+ #pragma db object
+ struct base: root
+ {
+ base (int n = 999, const std::string& s = "xxx")
+ : root (n), rs1s (s), bs1n (n) {rs3v.push_back (n);}
+
+ // rs1
+ //
+ #pragma db section(rs1)
+ std::string rs1s;
+
+ // rs3
+ //
+ #pragma db section(rs3)
+ std::vector<int> rs3v;
+
+ // bs1: override in derived
+ //
+ #pragma db load(lazy)
+ odb::section bs1;
+
+ #pragma db section(bs1)
+ int bs1n;
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ derived (int n = 999, const std::string& s = "xxx", bool b = false)
+ : base (n, s), rs1b (b), rs3n (n), rs4s (s), bs1s (s), ds1n (n)
+ {rs1v.push_back (n);}
+
+ // rs1
+ //
+ #pragma db section(rs1)
+ bool rs1b;
+
+ #pragma db section(rs1)
+ std::vector<int> rs1v;
+
+ // rs3
+ //
+ #pragma db section(rs3)
+ int rs3n;
+
+ // rs4
+ //
+ #pragma db section(rs4)
+ std::string rs4s;
+
+ // bs1
+ //
+ #pragma db section(bs1)
+ std::string bs1s;
+
+ // ds1: no override
+ //
+ #pragma db load(lazy)
+ odb::section ds1;
+
+ #pragma db section(ds1)
+ int ds1n;
+ };
+}
+
+// Test empty section and override "gap".
+//
+#pragma db namespace table("t2_")
+namespace test2
+{
+ #pragma db object polymorphic abstract
+ struct root
+ {
+ virtual ~root () {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db load(lazy)
+ odb::section s;
+ };
+
+ #pragma db object abstract
+ struct base: root
+ {
+ // The "gap".
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ derived (int n = 999): sn (n) {sv.push_back (n);}
+
+ #pragma db section(s)
+ int sn;
+
+ #pragma db section(s)
+ std::vector<int> sv;
+ };
+}
+
+// Test value-only/container-only base/override combinations.
+//
+#pragma db namespace table("t3_")
+namespace test3
+{
+ #pragma db object polymorphic
+ struct root
+ {
+ root (int n = 999)
+ : s1n (n), s2n (n) {s3v.push_back (n); s4nv.push_back (n);}
+ virtual ~root () {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ // value/value
+ //
+ #pragma db load(lazy)
+ odb::section s1;
+
+ #pragma db section(s1)
+ int s1n;
+
+ // value/container
+ //
+ #pragma db load(lazy)
+ odb::section s2;
+
+ #pragma db section(s2)
+ int s2n;
+
+ // container/value
+ //
+ #pragma db load(lazy)
+ odb::section s3;
+
+ #pragma db section(s3)
+ std::vector<int> s3v;
+
+ // container/container
+ //
+ #pragma db load(lazy)
+ odb::section s4;
+
+ #pragma db section(s4)
+ std::vector<int> s4nv;
+ };
+
+ #pragma db object
+ struct base: root
+ {
+ base (int n = 999): root (n) {}
+
+ // The "gap".
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ derived (int n = 999, const std::string& s = "xxx")
+ : base (n), s1s (s), s3n (n) {s2v.push_back (n); s4sv.push_back (s);}
+
+ #pragma db section(s1)
+ std::string s1s;
+
+ #pragma db section(s2)
+ std::vector<int> s2v;
+
+ #pragma db section(s3)
+ int s3n;
+
+ #pragma db section(s4)
+ std::vector<std::string> s4sv;
+ };
+}
+
+// Test basic polymorphic optimistic section functionality.
+//
+#pragma db namespace table("t4_")
+namespace test4
+{
+ #pragma db object polymorphic optimistic abstract sectionable
+ struct root
+ {
+ root (int n): rs1n (n), rs2n (n), rs3n (n), rs4n (n) {}
+ virtual ~root () {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db version
+ unsigned long long v;
+
+ // rs1: readwrite, override
+ //
+ #pragma db load(lazy) update(change)
+ odb::section rs1;
+
+ #pragma db section(rs1)
+ int rs1n;
+
+ // rs2: readonly, no override
+ //
+ #pragma db load(lazy)
+ odb::section rs2;
+
+ #pragma db section(rs2)
+ const int rs2n;
+
+ // rs3: readonly, readonly override
+ //
+ #pragma db load(lazy)
+ odb::section rs3;
+
+ #pragma db section(rs3)
+ const int rs3n;
+
+ // rs4: readonly, readwrite override
+ //
+ #pragma db load(lazy)
+ odb::section rs4;
+
+ #pragma db section(rs4)
+ const int rs4n;
+ };
+
+ #pragma db object
+ struct base: root
+ {
+ base (int n = 999, const std::string& s = "xxx")
+ : root (n), rs1s (s), rs4s (s) {}
+
+ // rs1
+ //
+ #pragma db section(rs1)
+ std::string rs1s;
+
+ // rs4
+ //
+ #pragma db section(rs4)
+ std::string rs4s;
+
+ // bs2: empty, readwrite override
+ //
+ #pragma db load(lazy)
+ odb::section bs1;
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ derived (int n = 999, const std::string& s = "xxx", bool b = false)
+ : base (n, s), rs1b (b), rs3s (s), bs1n (n)
+ {
+ rs1v.push_back (n);
+ rs4v.push_back (n);
+ ds1v.push_back (n);
+ }
+
+ // rs1
+ //
+ #pragma db section(rs1)
+ bool rs1b;
+
+ #pragma db section(rs1)
+ std::vector<int> rs1v;
+
+ // rs3
+ //
+ #pragma db section(rs3)
+ const std::string rs3s;
+
+ // rs4
+ //
+ #pragma db section(rs4)
+ std::vector<int> rs4v;
+
+ // bs1
+ //
+ #pragma db section(bs1)
+ int bs1n;
+
+ // ds1: readwrite
+ //
+ #pragma db load(lazy)
+ odb::section ds1;
+
+ #pragma db section(ds1)
+ std::vector<int> ds1v;
+ };
+}
+
+// Test polymorphic optimistic readonly/empty to readwrite section override.
+//
+#pragma db namespace table("t5_")
+namespace test5
+{
+ #pragma db object polymorphic optimistic abstract
+ struct root
+ {
+ virtual ~root () {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db version
+ unsigned long long v;
+
+ #pragma db load(lazy) update(change)
+ odb::section s;
+ };
+
+ #pragma db object
+ struct base: root
+ {
+ // The "gap".
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ derived (int n = 999): sn (n) {}
+
+ #pragma db section(s)
+ int sn;
+ };
+}
+
+// Test polymorphic optimistic readonly/empty to readwrite section override,
+// eager-loaded case.
+//
+#pragma db namespace table("t6_")
+namespace test6
+{
+ #pragma db object polymorphic optimistic abstract
+ struct root
+ {
+ virtual ~root () {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db version
+ unsigned long long v;
+
+ #pragma db update(change)
+ odb::section s;
+ };
+
+ #pragma db object abstract
+ struct base: root
+ {
+ // The "gap".
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ derived (int n = 999): sn (n) {}
+
+ #pragma db section(s)
+ int sn;
+ };
+}
+
+// Test polymorphic optimistic section added in derived.
+//
+#pragma db namespace table("t7_")
+namespace test7
+{
+ #pragma db object polymorphic optimistic sectionable
+ struct root
+ {
+ virtual ~root () {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db version
+ unsigned long long v;
+ };
+
+ #pragma db object
+ struct base: root
+ {
+ #pragma db load(lazy) update(change)
+ odb::section s;
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ derived (int n = 999): sn (n) {}
+
+ #pragma db section(s)
+ int sn;
+ };
+}
+
+// Test reuse/polymorphic inheritance and optimistic mix.
+//
+#pragma db namespace table("t8_")
+namespace test8
+{
+ #pragma db object optimistic sectionable abstract
+ struct root
+ {
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db version
+ unsigned long long v;
+ };
+
+ #pragma db object polymorphic sectionable
+ struct base: root
+ {
+ virtual ~base () {}
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ derived (int n = 999): sn (n) {}
+
+ #pragma db load(lazy) update(change)
+ odb::section s;
+
+ #pragma db section(s)
+ int sn;
+ };
+}
+
+// Test id overwrite regression.
+//
+// The key here is the setup: the object that contains the containers in a
+// section and the pointers to objects stored in those containers. And these
+// objects derive polymorphically from the same base (and thus shared the id
+// bindind).
+//
+#pragma db namespace table("t9_")
+namespace test9
+{
+ #pragma db object polymorphic pointer(std::shared_ptr)
+ struct base
+ {
+ virtual ~base () {}
+
+ #pragma db id auto
+ unsigned long id;
+ };
+
+ #pragma db object
+ struct element: base
+ {
+ element (int n_ = 0): n (n_) {}
+
+ int n;
+ };
+
+ typedef std::vector<std::shared_ptr<element>> elements;
+
+ #pragma db object
+ struct container: base
+ {
+ container (int n_ = 0): n (n_) {}
+
+ int n;
+
+ #pragma db load(lazy) update(always)
+ odb::section s;
+
+ #pragma db section(s)
+ elements e1;
+
+ #pragma db section(s)
+ elements e2;
+ };
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/section/polymorphism/testscript b/odb-tests/common/section/polymorphism/testscript
new file mode 100644
index 0000000..f2cd536
--- /dev/null
+++ b/odb-tests/common/section/polymorphism/testscript
@@ -0,0 +1,33 @@
+# file : common/section/polymorphism/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/session/cache/buildfile b/odb-tests/common/session/cache/buildfile
new file mode 100644
index 0000000..6d5b0bc
--- /dev/null
+++ b/odb-tests/common/session/cache/buildfile
@@ -0,0 +1,41 @@
+# file : common/session/cache/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix session_cache_ \
+ --generate-schema \
+ --generate-session
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/session/cache/driver.cxx b/odb-tests/common/session/cache/driver.cxx
new file mode 100644
index 0000000..4b4ea12
--- /dev/null
+++ b/odb-tests/common/session/cache/driver.cxx
@@ -0,0 +1,83 @@
+// file : common/session/cache/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test session object cache.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/session.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Test the session_required exception.
+ //
+ {
+ using namespace test1;
+
+ shared_ptr<obj1> o1a (new obj1 (1));
+ shared_ptr<obj1> o1b (new obj1 (2));
+ shared_ptr<obj2> o2 (new obj2 (1));
+
+ o1a->o2 = o2;
+ o1b->o2 = o2;
+
+ o2->o1.push_back (o1a);
+ o2->o1.push_back (o1b);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1a);
+ db->persist (o1b);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+
+ try
+ {
+ shared_ptr<obj1> o1 (db->load<obj1> (1));
+ assert (false);
+ }
+ catch (const session_required&)
+ {
+ }
+
+ t.commit ();
+ }
+
+ {
+ session s;
+ transaction t (db->begin ());
+ shared_ptr<obj1> o1 (db->load<obj1> (1));
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/session/cache/test.hxx b/odb-tests/common/session/cache/test.hxx
new file mode 100644
index 0000000..d2b1b2b
--- /dev/null
+++ b/odb-tests/common/session/cache/test.hxx
@@ -0,0 +1,50 @@
+// file : common/session/cache/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <memory>
+#include <vector>
+
+#include <odb/core.hxx>
+
+// Test the session_required exception.
+//
+#pragma db namespace table("t1_")
+namespace test1
+{
+ using std::shared_ptr;
+ using std::weak_ptr;
+
+ #pragma db namespace(test1) pointer(shared_ptr)
+
+ struct obj2;
+
+ #pragma db object
+ struct obj1
+ {
+ obj1 () {}
+ obj1 (unsigned long id): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ shared_ptr<obj2> o2;
+ };
+
+ #pragma db object
+ struct obj2
+ {
+ obj2 () {}
+ obj2 (unsigned long id): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db inverse (o2)
+ std::vector< weak_ptr<obj1> > o1;
+ };
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/session/cache/testscript b/odb-tests/common/session/cache/testscript
new file mode 100644
index 0000000..6d013eb
--- /dev/null
+++ b/odb-tests/common/session/cache/testscript
@@ -0,0 +1,33 @@
+# file : common/session/cache/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/session/custom/buildfile b/odb-tests/common/session/custom/buildfile
new file mode 100644
index 0000000..1b64de1
--- /dev/null
+++ b/odb-tests/common/session/custom/buildfile
@@ -0,0 +1,43 @@
+# file : common/session/custom/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx txx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix session_custom_ \
+ --generate-schema \
+ --generate-session \
+ --session-type ::session \
+ --hxx-prologue '#include "session.hxx"'
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/session/custom/driver.cxx b/odb-tests/common/session/custom/driver.cxx
new file mode 100644
index 0000000..3056fd6
--- /dev/null
+++ b/odb-tests/common/session/custom/driver.cxx
@@ -0,0 +1,231 @@
+// file : common/session/custom/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test custom session (C++11 only).
+//
+
+#include <memory>
+#include <cstddef> // std::size_t
+#include <iostream>
+
+#include <odb/tracer.hxx>
+#include <odb/database.hxx>
+#include <odb/session.hxx>
+#include <odb/transaction.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11_*
+
+#include <libcommon/common.hxx>
+
+#include "session.hxx"
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+
+using odb::database;
+using odb::transaction;
+
+struct counting_tracer: odb::tracer
+{
+ virtual void
+ execute (odb::connection&, const char*) {count++;}
+ size_t count;
+};
+
+static counting_tracer tracer;
+
+struct failed {};
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Simple Tech Ltd.
+ //
+ {
+ shared_ptr<employer> er (new employer ("Simple Tech Ltd", "ST"));
+
+ shared_ptr<employee> john (new employee ("John", "Doe", er));
+ shared_ptr<employee> jane (new employee ("Jane", "Doe", er));
+
+ transaction t (db->begin ());
+
+ db->persist (er);
+ db->persist (john);
+ db->persist (jane);
+
+ t.commit ();
+ }
+
+ // Complex Systems Inc.
+ //
+ {
+ shared_ptr<employer> er (new employer ("Complex Systems Inc", "CS"));
+
+ shared_ptr<employee> john (new employee ("John", "Smith", er));
+ shared_ptr<employee> jane (new employee ("Jane", "Smith", er));
+
+ transaction t (db->begin ());
+
+ db->persist (er);
+ db->persist (john);
+ db->persist (jane);
+
+ t.commit ();
+ }
+
+ {
+ session s;
+ shared_ptr<employer> st, cs;
+ shared_ptr<employee> ste, cse;
+
+ {
+ transaction t (db->begin ());
+
+ st = db->load<employer> ("Simple Tech Ltd");
+#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+ ste = db->load<employee> (st->employees ()[0].object_id ());
+#else
+ ste = db->load<employee> (st->employees ()[0].object_id<employee> ());
+#endif
+
+ // Test object cache.
+ //
+ shared_ptr<employee> e (st->employees ()[0].load ());
+ assert (ste->employer () == st);
+ assert (ste == e);
+
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+
+ cs = db->load<employer> ("Complex Systems Inc");
+#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+ cse = db->load<employee> (cs->employees ()[0].object_id ());
+#else
+ cse = db->load<employee> (cs->employees ()[0].object_id<employee> ());
+#endif
+ cs->employees ()[0].load ();
+
+ t.commit ();
+ }
+
+ cs->symbol ("CSI");
+
+ // Swap employees.
+ //
+ ste->employer (cs);
+ cse->employer (st);
+ st->employees ()[0] = cse;
+ cs->employees ()[0] = ste;
+
+ {
+ transaction t (db->begin ());
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db);
+ assert (tracer.count == 3);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db);
+ assert (tracer.count == 0);
+ t.commit ();
+ }
+
+ cs->symbol ("COMP");
+ st->symbol ("SMPL");
+
+ {
+ transaction t (db->begin ());
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db);
+ assert (tracer.count == 2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db);
+ assert (tracer.count == 0);
+ t.commit ();
+ }
+
+ // Explicit update.
+ //
+ cs->symbol ("CS");
+ st->symbol ("ST");
+
+ {
+ transaction t (db->begin ());
+ db->update (cs);
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db);
+ assert (tracer.count == 1);
+ t.commit ();
+ }
+
+ // Rollback after update.
+ //
+ cs->symbol ("CSI");
+
+ try
+ {
+ transaction t (db->begin ());
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db);
+ assert (tracer.count == 1);
+ throw failed ();
+ t.commit ();
+ }
+ catch (const failed&)
+ {
+ transaction t (db->begin ());
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db);
+ assert (tracer.count == 1);
+ t.commit ();
+ }
+ }
+
+ // Test session destruction before transaction is commited.
+ //
+ {
+ transaction t (db->begin ());
+ {
+ session s;
+ shared_ptr<employer> st (db->load<employer> ("Simple Tech Ltd"));
+ st->symbol ("STL");
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db);
+ assert (tracer.count == 1);
+ }
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/session/custom/session.cxx b/odb-tests/common/session/custom/session.cxx
new file mode 100644
index 0000000..1a08c79
--- /dev/null
+++ b/odb-tests/common/session/custom/session.cxx
@@ -0,0 +1,57 @@
+// file : common/session/custom/session.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include "session.hxx"
+
+session* session::current;
+
+session::
+session ()
+ : tran_ (0)
+{
+ assert (current == 0);
+ current = this;
+}
+
+session::
+~session ()
+{
+ // Unregister from transaction.
+ //
+ if (tran_ != 0)
+ tran_->callback_unregister (this);
+
+ assert (current == this);
+ current = 0;
+}
+
+void session::
+flush (odb::database& db)
+{
+ bool flushed (false);
+
+ for (type_map::iterator i (map_.begin ()), e (map_.end ()); i != e; ++i)
+ {
+ bool r (i->second->flush (db));
+ flushed = flushed || r;
+ }
+
+ // If we flushed anything, then register the post-commit/rollback callback.
+ //
+ if (flushed)
+ {
+ tran_ = &odb::transaction::current ();
+ tran_->callback_register (
+ &mark, this, odb::transaction::event_all, 0, &tran_);
+ }
+}
+
+void session::
+mark (unsigned short event, void* key, unsigned long long)
+{
+ session& s (*static_cast<session*> (key));
+ for (type_map::iterator i (s.map_.begin ()), e (s.map_.end ()); i != e; ++i)
+ i->second->mark (event);
+}
diff --git a/odb-tests/common/session/custom/session.hxx b/odb-tests/common/session/custom/session.hxx
new file mode 100644
index 0000000..2d2f597
--- /dev/null
+++ b/odb-tests/common/session/custom/session.hxx
@@ -0,0 +1,191 @@
+// file : common/session/custom/session.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef SESSION_HXX
+#define SESSION_HXX
+
+#include <map>
+#include <memory>
+#include <typeinfo>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <odb/traits.hxx> // odb::object_traits
+#include <odb/details/type-info.hxx> // odb::details::type_info_comparator
+
+// This custom session implementation assumes we are working with
+// one database at a time.
+//
+class session
+{
+public:
+ session ();
+ ~session ();
+
+private:
+ session (const session&);
+ session& operator= (const session&);
+
+ // Session for the current thread. This can be implemented in pretty
+ // much any way that makes sense to the application. It can be a global
+ // session as we have here. In multi-threaded applications we could use
+ // TLS instead.
+ //
+public:
+ static session* current;
+
+ // Change tracking interface.
+ //
+public:
+ // Call flush() within a transaction to apply the changes to the
+ // database.
+ //
+ void
+ flush (odb::database&);
+
+private:
+ struct object_map_base
+ {
+ virtual
+ ~object_map_base () {}
+
+ // Return true if we flushed anything.
+ //
+ virtual bool
+ flush (odb::database&) = 0;
+
+ virtual void
+ mark (unsigned short event) = 0;
+ };
+
+ enum object_state
+ {
+ tracking, // Tracking any modifications by storing the original copy.
+ changed, // Known to be changed.
+ flushed // Flushed but not yet committed/rolled back.
+ };
+
+ template <typename T>
+ struct object_data
+ {
+ typedef typename odb::object_traits<T>::pointer_type pointer_type;
+
+ explicit
+ object_data (pointer_type o): obj (o), state (tracking) {}
+
+ pointer_type obj;
+ pointer_type orig;
+ object_state state;
+ };
+
+ template <typename T>
+ struct object_map: object_map_base,
+ std::map<typename odb::object_traits<T>::id_type,
+ object_data<T> >
+ {
+ virtual bool
+ flush (odb::database&);
+
+ virtual void
+ mark (unsigned short event);
+ };
+
+ // Object cache interface.
+ //
+public:
+ static bool
+ _has_cache () {return current != 0;}
+
+ template <typename T>
+ struct cache_position
+ {
+ typedef object_map<T> map;
+ typedef typename map::iterator iterator;
+
+ cache_position (): map_ (0) {}
+ cache_position (map& m, const iterator& p): map_ (&m), pos_ (p) {}
+
+ cache_position (const cache_position& p)
+ : map_ (p.map_)
+ {
+ // It might not be ok to use an uninitialized iterator.
+ //
+ if (p.map_ != 0)
+ pos_ = p.pos_;
+ }
+
+ cache_position&
+ operator= (const cache_position& p)
+ {
+ // It might not be ok to use an uninitialized iterator on the rhs.
+ //
+ if (p.map_ != 0)
+ pos_ = p.pos_;
+ map_ = p.map_;
+ return *this;
+ }
+
+ map* map_;
+ iterator pos_;
+ };
+
+ // Cache management.
+ //
+ 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>& p)
+ {
+ if (p.map_ != 0)
+ p.map_->erase (p.pos_);
+ }
+
+ // Notifications.
+ //
+ template <typename T>
+ static void
+ _cache_persist (const cache_position<T>& p)
+ {
+ _cache_load (p);
+ }
+
+ template <typename T>
+ static void
+ _cache_load (const cache_position<T>&);
+
+ template <typename T>
+ static void
+ _cache_update (odb::database&, const T&);
+
+ template <typename T>
+ static void
+ _cache_erase (odb::database&,
+ const typename odb::object_traits<T>::id_type&);
+
+private:
+ // Post-commit/rollback callback.
+ //
+ static void
+ mark (unsigned short event, void* key, unsigned long long);
+
+private:
+ typedef std::map<const std::type_info*,
+ std::shared_ptr<object_map_base>,
+ odb::details::type_info_comparator> type_map;
+ type_map map_;
+ odb::transaction* tran_;
+};
+
+#include "session.txx"
+
+#endif // SESSION_HXX
diff --git a/odb-tests/common/session/custom/session.txx b/odb-tests/common/session/custom/session.txx
new file mode 100644
index 0000000..65ab933
--- /dev/null
+++ b/odb-tests/common/session/custom/session.txx
@@ -0,0 +1,159 @@
+// file : common/session/custom/session.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+template <typename T>
+typename session::cache_position<T> session::
+_cache_insert (odb::database&,
+ const typename odb::object_traits<T>::id_type& id,
+ const typename odb::object_traits<T>::pointer_type& obj)
+{
+ if (current == 0)
+ return cache_position<T> (); // No session, return empty position.
+
+ std::shared_ptr<object_map_base>& pm (current->map_[&typeid (T)]);
+
+ if (!pm)
+ pm.reset (new object_map<T>);
+
+ object_map<T>& m (static_cast<object_map<T>&> (*pm));
+
+ typename object_map<T>::value_type vt (id, object_data<T> (obj));
+ std::pair<typename object_map<T>::iterator, bool> r (m.insert (vt));
+
+ // We shall never try to re-insert the same object into the cache.
+ //
+ assert (r.second);
+
+ return cache_position<T> (m, r.first);
+}
+
+template <typename T>
+typename odb::object_traits<T>::pointer_type session::
+_cache_find (odb::database&, const typename odb::object_traits<T>::id_type& id)
+{
+ typedef typename odb::object_traits<T>::pointer_type pointer_type;
+
+ if (current == 0)
+ return pointer_type (); // No session, return NULL pointer.
+
+ type_map::const_iterator ti (current->map_.find (&typeid (T)));
+
+ if (ti == current->map_.end ())
+ return pointer_type ();
+
+ const object_map<T>& m (static_cast<const object_map<T>&> (*ti->second));
+ typename object_map<T>::const_iterator oi (m.find (id));
+
+ if (oi == m.end ())
+ return pointer_type ();
+
+ return oi->second.obj;
+}
+
+template <typename T>
+void session::
+_cache_load (const cache_position<T>& p)
+{
+ typedef typename odb::object_traits<T>::pointer_type pointer_type;
+
+ if (p.map_ == 0)
+ return; // Empty position.
+
+ // Make a copy for change tracking. If our object model had a
+ // polymorphic hierarchy, then we would have had to use a
+ // virtual function-based mechanism (e.g., clone()) instead of
+ // the copy constructor since for a polymorphic hierarchy all
+ // the derived objects are stored as pointers to the root object.
+ //
+ p.pos_->second.orig = pointer_type (new T (*p.pos_->second.obj));
+}
+
+template <typename T>
+void session::
+_cache_update (odb::database&, const T& obj)
+{
+ typedef odb::object_traits<T> object_traits;
+ typedef typename object_traits::pointer_type pointer_type;
+
+ if (current == 0)
+ return; // No session.
+
+ // User explicitly updated the object by calling database::update().
+ // Change the state to flushed and reset the original copy (we are
+ // still tracking changes after the update).
+ //
+ type_map::iterator ti (current->map_.find (&typeid (T)));
+
+ if (ti == current->map_.end ())
+ return; // This object is not in the session.
+
+ object_map<T>& m (static_cast<object_map<T>&> (*ti->second));
+ typename object_map<T>::iterator oi (m.find (object_traits::id (obj)));
+
+ if (oi == m.end ())
+ return; // This object is not in the session.
+
+ object_data<T>& d (oi->second);
+ d.orig = pointer_type (new T (*d.obj));
+ d.state = flushed;
+}
+
+template <typename T>
+void session::
+_cache_erase (odb::database&,
+ const typename odb::object_traits<T>::id_type& id)
+{
+ if (current == 0)
+ return; // No session.
+
+ type_map::iterator ti (current->map_.find (&typeid (T)));
+
+ if (ti == current->map_.end ())
+ return;
+
+ object_map<T>& m (static_cast<object_map<T>&> (*ti->second));
+ typename object_map<T>::iterator oi (m.find (id));
+
+ if (oi == m.end ())
+ return;
+
+ m.erase (oi);
+
+ if (m.empty ())
+ current->map_.erase (ti);
+}
+
+template <typename T>
+bool session::object_map<T>::
+flush (odb::database& db)
+{
+ bool r (false);
+ for (typename object_map<T>::iterator i (this->begin ()), e (this->end ());
+ i != e; ++i)
+ {
+ object_data<T>& d (i->second);
+
+ if (d.state == changed || d.obj->changed (*d.orig))
+ db.update (d.obj); // State changed by the update() notification.
+
+ r = r || d.state == flushed;
+ }
+
+ return r;
+}
+
+template <typename T>
+void session::object_map<T>::
+mark (unsigned short event)
+{
+ for (typename object_map<T>::iterator i (this->begin ()), e (this->end ());
+ i != e; ++i)
+ {
+ object_data<T>& d (i->second);
+
+ if (d.state == flushed)
+ d.state = event == odb::transaction::event_commit ? tracking : changed;
+ }
+}
diff --git a/odb-tests/common/session/custom/test.hxx b/odb-tests/common/session/custom/test.hxx
new file mode 100644
index 0000000..3f2703f
--- /dev/null
+++ b/odb-tests/common/session/custom/test.hxx
@@ -0,0 +1,118 @@
+// file : common/session/custom/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <memory>
+#include <vector>
+
+#include <odb/core.hxx>
+#include <odb/lazy-ptr.hxx>
+
+class employee;
+
+#pragma db object pointer(std::shared_ptr) session
+class employer
+{
+public:
+ employer (const std::string& name, const std::string& symbol)
+ : name_ (name), symbol_ (symbol) {}
+
+ const std::string&
+ name () const {return name_;}
+
+ const std::string&
+ symbol () const {return symbol_;}
+
+ void
+ symbol (const std::string& symbol) {symbol_ = symbol;}
+
+ // Employees of this employer.
+ //
+ typedef std::vector<odb::lazy_weak_ptr<employee>> employees_type;
+
+ const employees_type&
+ employees () const {return employees_;}
+
+ employees_type&
+ employees () {return employees_;}
+
+ // Change tracking.
+ //
+public:
+ bool
+ changed (const employer& orig) const
+ {
+ // Note that we don't need to track object ids, inverse pointers, nor
+ // readonly/const data members.
+ //
+ return symbol_ != orig.symbol_;
+ }
+
+private:
+ friend class odb::access;
+ employer () {}
+
+ #pragma db id
+ std::string name_;
+
+ std::string symbol_;
+
+ #pragma db value_not_null inverse(employer_)
+ employees_type employees_;
+};
+
+#pragma db object pointer(std::shared_ptr) session
+class employee
+{
+public:
+ typedef ::employer employer_type;
+
+ employee (const std::string& first,
+ const std::string& last,
+ std::shared_ptr<employer_type> employer)
+ : first_ (first), last_ (last), employer_ (employer) {}
+
+ // Name.
+ //
+ const std::string&
+ first () const {return first_;}
+
+ const std::string&
+ last () const {return last_;}
+
+ // Employer.
+ //
+ std::shared_ptr<employer_type>
+ employer () const {return employer_;}
+
+ void
+ employer (std::shared_ptr<employer_type> e) {employer_ = e;}
+
+ // Change tracking.
+ //
+public:
+ bool
+ changed (const employee& orig) const
+ {
+ return first_ != orig.first_ || last_ != orig.last_ ||
+ employer_ != orig.employer_;
+ }
+
+private:
+ friend class odb::access;
+ employee () {}
+
+ #pragma db id auto
+ unsigned long id_;
+
+ std::string first_;
+ std::string last_;
+
+ #pragma db not_null
+ std::shared_ptr<employer_type> employer_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/session/custom/testscript b/odb-tests/common/session/custom/testscript
new file mode 100644
index 0000000..39c281d
--- /dev/null
+++ b/odb-tests/common/session/custom/testscript
@@ -0,0 +1,33 @@
+# file : common/session/custom/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/statement/processing/buildfile b/odb-tests/common/statement/processing/buildfile
new file mode 100644
index 0000000..97124e8
--- /dev/null
+++ b/odb-tests/common/statement/processing/buildfile
@@ -0,0 +1,8 @@
+# file : common/statement/processing/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libs = libodb%lib{odb}
+
+exe{driver}: {hxx cxx}{*} $libs testscript
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
diff --git a/odb-tests/common/statement/processing/driver.cxx b/odb-tests/common/statement/processing/driver.cxx
new file mode 100644
index 0000000..2d00107
--- /dev/null
+++ b/odb-tests/common/statement/processing/driver.cxx
@@ -0,0 +1,619 @@
+// file : common/statement/processing/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test internal statement processing machinery.
+//
+
+#include <string>
+#include <iostream>
+
+#include <odb/statement.hxx>
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+
+static bool
+insert (const char* stmt,
+ const char* expected,
+ const void* const* bind,
+ size_t bind_size)
+{
+ string r;
+ odb::statement::process_insert (
+ r, stmt, bind, bind_size, sizeof (void*), '$');
+ return r == expected;
+}
+
+static bool
+update (const char* stmt,
+ const char* expected,
+ const void* const* bind,
+ size_t bind_size)
+{
+ string r;
+ odb::statement::process_update (
+ r, stmt, bind, bind_size, sizeof (void*), '$');
+ return r == expected;
+}
+
+static bool
+select (const char* stmt,
+ const char* expected,
+ const void* const* bind,
+ size_t bind_size)
+{
+ string r;
+ odb::statement::process_select (
+ r, stmt, bind, bind_size, sizeof (void*), '[', ']', true);
+ return r == expected;
+}
+
+int
+main (int, char* argv[])
+{
+ //
+ // INSERT
+ //
+
+ // Fast path.
+ //
+ {
+ void* b[] = {argv, argv};
+ assert (insert ("INSERT INTO [foo]\n"
+ "([a],\n[b])\n"
+ "VALUES\n"
+ "(DEFAULT,\n$1)",
+ "INSERT INTO [foo] ([a], [b]) VALUES (DEFAULT, $1)",
+ b, 2));
+ }
+
+ // Empty via statement.
+ //
+ /* LIBODB_DEBUG_STATEMENT_PROCESSING
+ {
+ assert (insert ("INSERT INTO [foo]\n"
+ "DEFAULT VALUES",
+ "INSERT INTO [foo] DEFAULT VALUES",
+ 0, 0));
+ }
+ */
+
+ // Empty via bind.
+ //
+ {
+ void* b[] = {0};
+ assert (insert ("INSERT INTO [foo]\n"
+ "([a])\n"
+ "VALUES\n"
+ "($1)",
+ "INSERT INTO [foo] DEFAULT VALUES",
+ b, 1));
+ }
+
+ // Empty with OUTPUT.
+ //
+ {
+ void* b[] = {0, 0};
+ assert (insert ("INSERT INTO [foo]\n"
+ "([a],\n[b])\n"
+ "OUTPUT INSERTED.[id]\n"
+ "VALUES\n"
+ "($1,\n$2)",
+ "INSERT INTO [foo] OUTPUT INSERTED.[id] DEFAULT VALUES",
+ b, 2));
+ }
+
+ // Empty with RETURNING.
+ //
+ {
+ void* b[] = {0, 0, 0};
+ assert (insert ("INSERT INTO [foo]\n"
+ "([a],\n[b],\n[c])\n"
+ "VALUES\n"
+ "($1,\n$1,\n$2)\n"
+ "RETURNING [id]",
+ "INSERT INTO [foo] DEFAULT VALUES RETURNING [id]",
+ b, 3));
+ }
+
+ // Empty via bind, but not values.
+ //
+ {
+ void* b[] = {0};
+ assert (insert ("INSERT INTO [foo]\n"
+ "([a],\n[b])\n"
+ "VALUES\n"
+ "(1,\n$1)",
+ "INSERT INTO [foo] ([a]) VALUES (1)",
+ b, 1));
+ }
+
+ // Empty via bind, but not values.
+ //
+ {
+ void* b[] = {0};
+ assert (insert ("INSERT INTO [foo]\n"
+ "([a],\n[b],\n[c])\n"
+ "VALUES\n"
+ "(1,\n$1,\nDEFAULT)",
+ "INSERT INTO [foo] ([a], [c]) VALUES (1, DEFAULT)",
+ b, 1));
+ }
+
+ // First not present.
+ //
+ {
+ void* b[] = {0, argv, argv};
+ assert (insert ("INSERT INTO [foo]\n"
+ "([a],\n[b],\n[c])\n"
+ "VALUES\n"
+ "($1,\n$2,\n$3)",
+ "INSERT INTO [foo] ([b], [c]) VALUES ($2, $3)",
+ b, 3));
+ }
+
+ // Last not present.
+ //
+ {
+ void* b[] = {argv, argv, 0};
+ assert (insert ("INSERT INTO [foo]\n"
+ "([a],\n[b],\n[c])\n"
+ "VALUES\n"
+ "($1,\n$2,\n$3)",
+ "INSERT INTO [foo] ([a], [b]) VALUES ($1, $2)",
+ b, 3));
+ }
+
+ // Middle not present.
+ //
+ {
+ void* b[] = {argv, 0, argv};
+ assert (insert ("INSERT INTO [foo]\n"
+ "([a],\n[b],\n[c])\n"
+ "VALUES\n"
+ "($1,\n$2,\n$3)",
+ "INSERT INTO [foo] ([a], [c]) VALUES ($1, $3)",
+ b, 3));
+ }
+
+ // Multiple not present.
+ //
+ {
+ void* b[] = {0, argv, 0, argv, 0};
+ assert (insert ("INSERT INTO [foo]\n"
+ "([a],\n[b],\n[c],\n[d],\n[e])\n"
+ "VALUES\n"
+ "($1,\n$2,\n$3,\n$4,\n$5)",
+ "INSERT INTO [foo] ([b], [d]) VALUES ($2, $4)",
+ b, 5));
+ }
+
+ // Not present and OUTPUT.
+ //
+ {
+ void* b[] = {argv, 0, argv};
+ assert (insert ("INSERT INTO [foo]\n"
+ "([a],\n[b],\n[c])\n"
+ "OUTPUT INSERTED.[id]\n"
+ "VALUES\n"
+ "($1,\n$2,\n$3)",
+ "INSERT INTO [foo] ([a], [c]) OUTPUT INSERTED.[id] "
+ "VALUES ($1, $3)",
+ b, 3));
+ }
+
+ // Not present and RETURNING.
+ //
+ {
+ void* b[] = {argv, 0, argv};
+ assert (insert ("INSERT INTO [foo]\n"
+ "([a],\n[b],\n[c])\n"
+ "VALUES\n"
+ "($1,\n$2,\n$3)\n"
+ "RETURNING [id]",
+ "INSERT INTO [foo] ([a], [c]) VALUES ($1, $3) "
+ "RETURNING [id]",
+ b, 3));
+ }
+
+ // Value expressions.
+ //
+ {
+ void* b[] = {argv, argv, argv};
+ assert (insert ("INSERT INTO [foo]\n"
+ "([a],\n[b],\n[c])\n"
+ "VALUES\n"
+ "($1,\nCAST($2, TEXT),\n$3)",
+ "INSERT INTO [foo] ([a], [b], [c]) "
+ "VALUES ($1, CAST($2, TEXT), $3)",
+ b, 3));
+ }
+
+ //
+ // UPDATE
+ //
+
+ // Fast path.
+ //
+ {
+ void* b[] = {argv, argv};
+ assert (update ("UPDATE [foo]\n"
+ "SET\n"
+ "ver=ver+1,\n[a]=$1\n"
+ "WHERE [id]=$2",
+ "UPDATE [foo] SET ver=ver+1, [a]=$1 WHERE [id]=$2",
+ b, 2));
+ }
+
+ // Empty via bind.
+ //
+ {
+ void* b[] = {0, argv};
+ assert (update ("UPDATE [foo]\n"
+ "SET\n"
+ "[a]=$1\n"
+ "WHERE [id]=$2",
+ "",
+ b, 2));
+ }
+
+ // Empty via bind, but not values.
+ //
+ {
+ void* b[] = {0, argv};
+ assert (update ("UPDATE [foo]\n"
+ "SET\n"
+ "ver=ver+1,\n[a]=$1\n"
+ "WHERE [id]=$2",
+ "UPDATE [foo] SET ver=ver+1 WHERE [id]=$2",
+ b, 2));
+ }
+
+ // First not present.
+ //
+ {
+ void* b[] = {0, argv, argv, argv};
+ assert (update ("UPDATE [foo]\n"
+ "SET\n"
+ "[a]=$1,\n"
+ "[b]=$2,\n"
+ "[c]=$3\n"
+ "WHERE [id]=$4",
+ "UPDATE [foo] SET [b]=$2, [c]=$3 WHERE [id]=$4",
+ b, 4));
+ }
+
+ // Last not present.
+ //
+ {
+ void* b[] = {argv, argv, 0, argv};
+ assert (update ("UPDATE [foo]\n"
+ "SET\n"
+ "[a]=$1,\n"
+ "[b]=$2,\n"
+ "[c]=$3\n"
+ "WHERE [id]=$4",
+ "UPDATE [foo] SET [a]=$1, [b]=$2 WHERE [id]=$4",
+ b, 4));
+ }
+
+ // Middle not present.
+ //
+ {
+ void* b[] = {argv, 0, argv, argv};
+ assert (update ("UPDATE [foo]\n"
+ "SET\n"
+ "[a]=$1,\n"
+ "[b]=$2,\n"
+ "[c]=$3\n"
+ "WHERE [id]=$4",
+ "UPDATE [foo] SET [a]=$1, [c]=$3 WHERE [id]=$4",
+ b, 4));
+ }
+
+ // Multiple not present.
+ //
+ {
+ void* b[] = {0, argv, 0, argv, argv};
+ assert (update ("UPDATE [foo]\n"
+ "SET\n"
+ "[a]=$1,\n"
+ "[b]=$2,\n"
+ "[c]=$3,\n"
+ "[d]=$4\n"
+ "WHERE [id]=$5",
+ "UPDATE [foo] SET [b]=$2, [d]=$4 WHERE [id]=$5",
+ b, 5));
+ }
+
+ // Not present and OUTPUT.
+ //
+ {
+ void* b[] = {argv, 0, argv, argv};
+ assert (update ("UPDATE [foo]\n"
+ "SET\n"
+ "[a]=$1,\n"
+ "[b]=$2,\n"
+ "[c]=$3\n"
+ "OUTPUT INSERTED.[ver] "
+ "WHERE [id]=$4",
+ "UPDATE [foo] SET [a]=$1, [c]=$3 OUTPUT INSERTED.[ver] "
+ "WHERE [id]=$4",
+ b, 4));
+ }
+
+ // Value expressions.
+ //
+ {
+ void* b[] = {argv, argv, argv, argv};
+ assert (update ("UPDATE [foo]\n"
+ "SET\n"
+ "[a]=$1,\n"
+ "[b]=CAST($2, TEXT),\n"
+ "[c]=$3\n"
+ "WHERE [id]=$4",
+ "UPDATE [foo] SET [a]=$1, [b]=CAST($2, TEXT), [c]=$3 "
+ "WHERE [id]=$4",
+ b, 4));
+ }
+
+ // No OUTPUT/WHERE clause.
+ //
+ {
+ void* b[] = {argv, 0, argv};
+ assert (update ("UPDATE [foo]\n"
+ "SET\n"
+ "[a]=$1,\n"
+ "[b]=$2,\n"
+ "[c]=$3",
+ "UPDATE [foo] SET [a]=$1, [c]=$3",
+ b, 4));
+ }
+
+ //
+ // SELECT
+ //
+
+ // Empty.
+ //
+ {
+ void* b[] = {0, 0, 0};
+ assert (select ("SELECT\n"
+ "[a].[x],\n"
+ "[t].[y],\n"
+ "[t].[z]\n"
+ "FROM [t]\n"
+ "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n"
+ "WHERE [t].[id]=$1",
+ "",
+ b, 3));
+ }
+
+ // Fast path.
+ //
+ {
+ void* b[] = {argv, argv};
+ assert (select ("SELECT\n"
+ "[s].[t].[x],\n"
+ "[a].[y]\n"
+ "FROM [s].[t]\n"
+ "LEFT JOIN [t1] AS [a] ON [a].[id]=[s].[t].[id]\n"
+ "WHERE [s].[t].[id]=$1",
+ "SELECT [s].[t].[x], [a].[y] FROM [s].[t] "
+ "LEFT JOIN [t1] AS [a] ON [a].[id]=[s].[t].[id] "
+ "WHERE [s].[t].[id]=$1",
+ b, 2));
+ }
+
+ // First not present.
+ //
+ {
+ void* b[] = {0, argv, argv};
+ assert (select ("SELECT\n"
+ "[a].[x],\n"
+ "[t].[y],\n"
+ "[t].[z]\n"
+ "FROM [t]\n"
+ "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n"
+ "WHERE [t].[id]=$1",
+ "SELECT [t].[y], [t].[z] FROM [t] WHERE [t].[id]=$1",
+ b, 3));
+ }
+
+ // Last not present.
+ //
+ {
+ void* b[] = {argv, argv, 0};
+ assert (select ("SELECT\n"
+ "[t].[x],\n"
+ "[t].[y],\n"
+ "[a].[z]\n"
+ "FROM [t]\n"
+ "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n"
+ "WHERE [t].[id]=$1",
+ "SELECT [t].[x], [t].[y] FROM [t] WHERE [t].[id]=$1",
+ b, 3));
+ }
+
+ // Middle not present.
+ //
+ {
+ void* b[] = {argv, 0, argv};
+ assert (select ("SELECT\n"
+ "[t].[x],\n"
+ "[a].[y],\n"
+ "[t].[z]\n"
+ "FROM [t]\n"
+ "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n"
+ "WHERE [t].[id]=$1",
+ "SELECT [t].[x], [t].[z] FROM [t] WHERE [t].[id]=$1",
+ b, 3));
+ }
+
+ // Multiple not present.
+ //
+ {
+ void* b[] = {0, argv, 0, argv};
+ assert (select ("SELECT\n"
+ "[a1].[w],\n"
+ "[t].[x],\n"
+ "[a2].[y],\n"
+ "[a3].[z]\n"
+ "FROM [t]\n"
+ "LEFT JOIN [t1] AS [a1] ON [a1].[id]=[t].[id]\n"
+ "LEFT JOIN [t2] AS [a2] ON [a2].[id]=[t].[id]\n"
+ "LEFT JOIN [t3] AS [a3] ON [a3].[id]=[t].[id]\n"
+ "WHERE [t].[id]=$1",
+ "SELECT [t].[x], [a3].[z] FROM [t] "
+ "LEFT JOIN [t3] AS [a3] ON [a3].[id]=[t].[id] "
+ "WHERE [t].[id]=$1",
+ b, 4));
+ }
+
+ // Column expression.
+ //
+ {
+ void* b[] = {argv, argv, 0};
+ assert (select ("SELECT\n"
+ "[t].[x],\n"
+ "CAST([a].[y], TEXT),\n"
+ "[t].[z]\n"
+ "FROM [t]\n"
+ "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n"
+ "WHERE [t].[id]=$1",
+ "SELECT [t].[x], CAST([a].[y], TEXT) FROM [t] "
+ "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id] "
+ "WHERE [t].[id]=$1",
+ b, 3));
+ }
+
+ // No WHERE.
+ //
+ {
+ void* b[] = {argv, 0, argv};
+ assert (select ("SELECT\n"
+ "[t].[x],\n"
+ "[t].[y],\n"
+ "[t].[z]\n"
+ "FROM [t]",
+ "SELECT [t].[x], [t].[z] FROM [t]",
+ b, 3));
+ }
+
+ // JOIN without WHERE.
+ //
+ {
+ void* b[] = {argv, 0, argv};
+ assert (select ("SELECT\n"
+ "[t].[x],\n"
+ "[a].[y],\n"
+ "[t].[z]\n"
+ "FROM [t]\n"
+ "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]",
+ "SELECT [t].[x], [t].[z] FROM [t]",
+ b, 3));
+ }
+
+ // JOIN presence because of WHERE.
+ //
+ {
+ void* b[] = {argv, 0, argv};
+ assert (select ("SELECT\n"
+ "[t].[x],\n"
+ "[a].[y],\n"
+ "[t].[z]\n"
+ "FROM [t]\n"
+ "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n"
+ "WHERE [t].[id]=$1 AND [a].[id]=$2",
+ "SELECT [t].[x], [t].[z] FROM [t] "
+ "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id] "
+ "WHERE [t].[id]=$1 AND [a].[id]=$2",
+ b, 3));
+ }
+
+
+ // JOIN presence because of dependent JOIN.
+ //
+ {
+ void* b[] = {argv, argv, argv};
+ assert (select ("SELECT\n"
+ "[t].[x],\n"
+ "[a_b].[y],\n"
+ "[t].[z]\n"
+ "FROM [t]\n"
+ "LEFT JOIN [d] AS [a_d] ON [a_d].[id]=[t].[id]\n"
+ "LEFT JOIN [b] AS [a_b] ON [a_b].[id]=[a_d].[id]\n"
+ "WHERE [t].[id]=$1",
+ "SELECT [t].[x], [a_b].[y], [t].[z] FROM [t] "
+ "LEFT JOIN [d] AS [a_d] ON [a_d].[id]=[t].[id] "
+ "LEFT JOIN [b] AS [a_b] ON [a_b].[id]=[a_d].[id] "
+ "WHERE [t].[id]=$1",
+ b, 3));
+ }
+
+ // JOIN without alias and with schema.
+ //
+ {
+ void* b[] = {argv, argv, argv};
+ assert (select ("SELECT\n"
+ "[t].[x],\n"
+ "[s].[t1].[y],\n"
+ "[t2].[z]\n"
+ "FROM [t]\n"
+ "LEFT JOIN [s].[t1] ON [s].[t1].[id]=[t].[id]\n"
+ "LEFT JOIN [t2] ON [t2].[id]=[t].[id]\n"
+ "WHERE [t].[id]=$1",
+ "SELECT [t].[x], [s].[t1].[y], [t2].[z] FROM [t] "
+ "LEFT JOIN [s].[t1] ON [s].[t1].[id]=[t].[id] "
+ "LEFT JOIN [t2] ON [t2].[id]=[t].[id] "
+ "WHERE [t].[id]=$1",
+ b, 3));
+ }
+
+ // JOIN alias top-level qualifer test.
+ //
+ {
+ void* b[] = {argv, 0};
+ assert (select ("SELECT\n"
+ "[s].[a].[x],\n"
+ "[a].[y]\n"
+ "FROM [s].[a]\n"
+ "LEFT JOIN [t1] AS [a] ON [a].[id]=[s].[a].[id]\n"
+ "WHERE [s].[a].[id]=$1",
+ "SELECT [s].[a].[x] FROM [s].[a] WHERE [s].[a].[id]=$1",
+ b, 2));
+ }
+
+ // JOIN alias bottom-level qualifer test (FROM case).
+ //
+ {
+ void* b[] = {argv, 0};
+ assert (select ("SELECT\n"
+ "[a].[t].[x],\n"
+ "[a].[y]\n"
+ "FROM [a].[t]\n"
+ "LEFT JOIN [t1] AS [a] ON [a].[id]=[a].[t].[id]\n"
+ "WHERE [a].[t].[id]=$1",
+ "SELECT [a].[t].[x] FROM [a].[t] WHERE [a].[t].[id]=$1",
+ b, 2));
+ }
+
+ // JOIN alias bottom-level qualifer test (LEFT JOIN case).
+ //
+ {
+ void* b[] = {0, argv};
+ assert (select ("SELECT\n"
+ "[a].[y],\n"
+ "[a].[t2].[x]\n"
+ "FROM [t]\n"
+ "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n"
+ "LEFT JOIN [a].[t2] ON [a].[t2].[id]=[t].[id]\n"
+ "WHERE [t].[id]=$1",
+ "SELECT [a].[t2].[x] FROM [t] "
+ "LEFT JOIN [a].[t2] ON [a].[t2].[id]=[t].[id] "
+ "WHERE [t].[id]=$1",
+ b, 2));
+ }
+}
diff --git a/odb-tests/common/statement/processing/testscript b/odb-tests/common/statement/processing/testscript
new file mode 100644
index 0000000..2460dc6
--- /dev/null
+++ b/odb-tests/common/statement/processing/testscript
@@ -0,0 +1,6 @@
+# file : common/statement/processing/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+: basics
+:
+$*
diff --git a/odb-tests/common/threads/buildfile b/odb-tests/common/threads/buildfile
new file mode 100644
index 0000000..53b98ec
--- /dev/null
+++ b/odb-tests/common/threads/buildfile
@@ -0,0 +1,49 @@
+# file : common/threads/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix threads_ \
+ --generate-schema \
+ --generate-query \
+ --generate-prepared
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# While we don't call any pthread_*() functions, this appears to be needed for
+# some std::thread implementations (like libstdc++). Note that
+# odb::details::thread inlines some std::thread API calls.
+#
+if ($cxx.target.class != 'windows')
+ cxx.libs += -pthread
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/threads/driver.cxx b/odb-tests/common/threads/driver.cxx
new file mode 100644
index 0000000..1add011
--- /dev/null
+++ b/odb-tests/common/threads/driver.cxx
@@ -0,0 +1,236 @@
+// file : common/threads/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test operations in a multi-threaded environment.
+//
+
+#include <vector>
+#include <memory> // std::unique_ptr
+#include <cstddef> // std::size_t
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/thread.hxx>
+
+#include <libcommon/config.hxx> // DATABASE_*
+#include <libcommon/common.hxx>
+
+#if defined(DATABASE_SQLITE)
+# include <odb/sqlite/database.hxx>
+#endif
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+namespace details = odb::details;
+
+const unsigned long thread_count = 24;
+const unsigned long iteration_count = 30;
+const unsigned long sub_iteration_count = 40;
+
+struct task
+{
+ task (database& db, unsigned long n)
+ : db_ (db), n_ (n)
+ {
+ }
+
+ void*
+ execute ()
+ {
+ try
+ {
+ for (unsigned long i (0); i < iteration_count; ++i)
+ {
+ unsigned long id ((n_ * iteration_count + i) * 3);
+
+ object o1 (id, "first object");
+ object o2 (id + 1, "second object");
+ object o3 (id + 2, "third object");
+
+ // The following transactions may lead to deadlocks.
+ //
+ while (true)
+ {
+ try
+ {
+ transaction t (db_.begin ());
+
+ db_.persist (o1);
+ db_.persist (o2);
+ db_.persist (o3);
+ t.commit ();
+ break;
+ }
+ catch (const deadlock&) {}
+ }
+
+ while (true)
+ {
+ try
+ {
+#if !defined(DATABASE_SQLITE)
+ transaction t (db_.begin ());
+#else
+ // SQLite has a peculiar table locking mode (shared cache)
+ // which can lead to any of the transactions in this test
+ // deadlocking even though they shouldn't from the user's
+ // perspective. One way to work around this problem is to
+ // start a "write" transaction as such right away.
+ //
+ transaction t;
+
+ if (db_.id () != odb::id_sqlite)
+ t.reset (db_.begin ());
+ else
+ {
+ t.reset (
+ static_cast<odb::sqlite::database&> (db_).begin_immediate ());
+ }
+#endif
+ unique_ptr<object> o (db_.load<object> (id));
+ assert (o->str_ == "first object");
+ o->str_ = "another value";
+ db_.update (*o);
+ t.commit ();
+ break;
+ }
+ catch (const deadlock&) {}
+ }
+
+ for (unsigned long j (0); j < sub_iteration_count; ++j)
+ {
+ typedef odb::query<object> query;
+ typedef odb::prepared_query<object> prep_query;
+ typedef odb::result<object> result;
+
+ while (true)
+ {
+ try
+ {
+ transaction t (db_.begin ());
+
+ prep_query pq (db_.lookup_query<object> ("object-query"));
+
+ if (!pq)
+ {
+ pq = db_.prepare_query<object> (
+ "object-query", query::str == "another value");
+ db_.cache_query (pq);
+ }
+
+ result r (pq.execute (false));
+
+ bool found (false);
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ if (i->id_ == id)
+ {
+ found = true;
+ break;
+ }
+ }
+ assert (found);
+ t.commit ();
+ break;
+ }
+ catch (const deadlock&) {}
+ }
+ }
+
+ while (true)
+ {
+ try
+ {
+ transaction t (db_.begin ());
+ db_.erase<object> (id);
+ t.commit ();
+ break;
+ }
+ catch (const deadlock&) {}
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return reinterpret_cast<void*> (1);
+ }
+
+ return 0;
+ }
+
+ static void*
+ execute (void* arg)
+ {
+ return static_cast<task*> (arg)->execute ();
+ }
+
+ database& db_;
+ unsigned long n_;
+};
+
+bool
+test (int argc, char* argv[], size_t max_connections)
+{
+ unique_ptr<database> db (create_database (argc, argv, true, max_connections));
+
+ vector<details::shared_ptr<details::thread> > threads;
+ vector<details::shared_ptr<task> > tasks;
+
+ for (unsigned long i (0); i < thread_count; ++i)
+ {
+ details::shared_ptr<task> t (new (details::shared) task (*db, i));
+ tasks.push_back (t);
+
+ threads.push_back (
+ details::shared_ptr<details::thread> (
+ new (details::shared) details::thread (&task::execute, t.get ())));
+ }
+
+ bool r (true);
+
+ for (unsigned long i (0); i < thread_count; ++i)
+ if (threads[i]->join () != 0)
+ r = false;
+
+ {
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> ());
+
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ db->erase<object> (i->id_);
+
+ t.commit ();
+ }
+
+ return r;
+}
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ if (!(test (argc, argv, 0) &&
+ test (argc, argv, thread_count - 1) &&
+ test (argc, argv, thread_count / 2) &&
+ test (argc, argv, thread_count / 4)))
+ return 1;
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/threads/test.hxx b/odb-tests/common/threads/test.hxx
new file mode 100644
index 0000000..2ed6e67
--- /dev/null
+++ b/odb-tests/common/threads/test.hxx
@@ -0,0 +1,29 @@
+// file : common/template/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object (unsigned long id, const std::string& str)
+ : id_ (id), str_ (str)
+ {
+ }
+
+ object ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string str_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/threads/testscript b/odb-tests/common/threads/testscript
new file mode 100644
index 0000000..87e03e0
--- /dev/null
+++ b/odb-tests/common/threads/testscript
@@ -0,0 +1,50 @@
+# file : common/threads/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ # Note: this is quite slow:
+ #
+ # $ time ./driver --database ~/odb-test.db
+ # real 3m5.593s
+ # user 1m1.244s
+ # sys 0m26.793s
+ #
+ # $ time ./driver --database /tmp/odb-test.db
+ # real 0m13.909s
+ # user 0m16.724s
+ # sys 0m4.874s
+ #
+ # $ time ./driver --database "file::memory:"
+ # real 0m12.406s
+ # user 0m15.694s
+ # sys 0m4.207s
+ #
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/transaction/basics/buildfile b/odb-tests/common/transaction/basics/buildfile
new file mode 100644
index 0000000..f412235
--- /dev/null
+++ b/odb-tests/common/transaction/basics/buildfile
@@ -0,0 +1,13 @@
+# file : common/transaction/basics/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libs = libodb%lib{odb}
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{*} $libs testscript
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/transaction/basics/driver.cxx b/odb-tests/common/transaction/basics/driver.cxx
new file mode 100644
index 0000000..1833555
--- /dev/null
+++ b/odb-tests/common/transaction/basics/driver.cxx
@@ -0,0 +1,151 @@
+// file : common/transaction/basics/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test basic transaction operations.
+//
+
+#include <string>
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/tracer.hxx>
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/statement.hxx>
+#include <odb/exceptions.hxx>
+
+#include <libcommon/common.hxx>
+#include <libcommon/concrete.hxx>
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+struct transaction_tracer: odb::tracer
+{
+ virtual void
+ execute (connection&, const char* s)
+ {
+ string str (s);
+
+ if (str == "BEGIN")
+ cout << "begin transaction" << endl;
+ else if (str == "COMMIT")
+ cout << "commit transaction" << endl;
+ else if (str == "ROLLBACK")
+ cout << "rollback transaction" << endl;
+ }
+
+ // Override the other version to get rid of a Sun CC warning.
+ //
+ virtual void
+ execute (connection& c, const statement& s)
+ {
+ execute (c, s.text ());
+ }
+};
+
+int
+main (int argc, char* argv[])
+{
+ {
+ transaction_tracer tracer;
+ unique_ptr<database> db (create_database (argc, argv, false));
+ db->tracer (tracer);
+
+ assert (!transaction::has_current ());
+
+ // Current and db accessors.
+ //
+ cout << "test 001" << endl;
+ {
+ transaction t (db->begin ());
+ assert (&t.database () == db.get ());
+ assert (transaction::has_current ());
+ assert (&transaction::current () == &t);
+
+ transaction::reset_current ();
+ assert (!transaction::has_current ());
+
+ transaction t2 (db->begin (), false);
+ assert (!transaction::has_current ());
+
+ transaction::current (t2);
+ assert (&transaction::current () == &t2);
+ }
+
+ // Commit.
+ //
+ cout << "test 002" << endl;
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ }
+
+ // Rollback.
+ //
+ cout << "test 003" << endl;
+ {
+ transaction t (db->begin ());
+ t.rollback ();
+ }
+
+ // Auto rollback.
+ //
+ cout << "test 004" << endl;
+ {
+ transaction t (db->begin ());
+ }
+
+ // Nested transaction.
+ //
+ cout << "test 005" << endl;
+ {
+ transaction t (db->begin ());
+
+ try
+ {
+ transaction n (db->begin ());
+ }
+ catch (const already_in_transaction&)
+ {
+ cout << "already_in_transaction" << endl;
+ }
+ }
+
+ // Concrete transaction type.
+ //
+ cout << "test 006" << endl;
+ {
+ assert (sizeof (odb_db::transaction) == sizeof (transaction));
+
+ odb_db::transaction t (static_cast<odb_db::database&> (*db).begin ());
+ odb_db::transaction& r (odb_db::transaction::current ());
+ assert (&t == &r);
+ }
+
+ // Transaction restart.
+ //
+ cout << "test 007" << endl;
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ t.reset (db->begin ());
+ t.commit ();
+ }
+ }
+
+ // Test early connection release.
+ //
+ {
+ unique_ptr<database> db (create_database (argc, argv, false, 1));
+ transaction t1 (db->begin ());
+ t1.commit ();
+ transaction t2 (db->begin ());
+ t2.rollback ();
+ transaction t3 (db->begin ());
+ t3.commit ();
+ }
+}
diff --git a/odb-tests/common/transaction/basics/testscript b/odb-tests/common/transaction/basics/testscript
new file mode 100644
index 0000000..94c58b6
--- /dev/null
+++ b/odb-tests/common/transaction/basics/testscript
@@ -0,0 +1,62 @@
+# file : common/transaction/basics/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
++cat <<EOI >=output
+ test 001
+ begin transaction
+ begin transaction
+ rollback transaction
+ rollback transaction
+ test 002
+ begin transaction
+ commit transaction
+ test 003
+ begin transaction
+ rollback transaction
+ test 004
+ begin transaction
+ rollback transaction
+ test 005
+ begin transaction
+ already_in_transaction
+ rollback transaction
+ test 006
+ begin transaction
+ rollback transaction
+ test 007
+ begin transaction
+ commit transaction
+ begin transaction
+ commit transaction
+ EOI
+
+test.redirects += >>>../output
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $*
+}
diff --git a/odb-tests/common/transaction/callback/buildfile b/odb-tests/common/transaction/callback/buildfile
new file mode 100644
index 0000000..78b1b03
--- /dev/null
+++ b/odb-tests/common/transaction/callback/buildfile
@@ -0,0 +1,13 @@
+# file : common/transaction/callback/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libs = libodb%lib{odb}
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{*} $libs testscript
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/transaction/callback/driver.cxx b/odb-tests/common/transaction/callback/driver.cxx
new file mode 100644
index 0000000..d0af993
--- /dev/null
+++ b/odb-tests/common/transaction/callback/driver.cxx
@@ -0,0 +1,231 @@
+// file : common/transaction/callback/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test transaction callbacks.
+//
+
+#include <memory> // std::unique_ptr
+#include <cstddef> // std::size_t
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+struct callback
+{
+ callback (unsigned short v): v_ (v), t_ (0) {}
+ callback (unsigned short v, transaction& t): v_ (v), t_ (0) {register_ (t);}
+ ~callback () {if (t_ != 0) unregister ();}
+
+ void
+ register_ (transaction& t)
+ {
+ t_ = &t;
+ t.callback_register (&func, this, transaction::event_all, v_, &t_);
+ }
+
+ void
+ unregister ()
+ {
+ cout << " unregister callback " << v_ << endl;
+ t_->callback_unregister (this);
+ t_ = 0;
+ }
+
+ void
+ update (unsigned short v)
+ {
+ v_ = v;
+ t_->callback_update (this, transaction::event_all, v_, &t_);
+ }
+
+private:
+ static void
+ func (unsigned short event, void* key, unsigned long long data)
+ {
+ callback& c (*static_cast<callback*> (key));
+
+ const char* en;
+ switch (event)
+ {
+ case transaction::event_commit:
+ en = "commit";
+ break;
+ case transaction::event_rollback:
+ en = "rollback";
+ break;
+ default:
+ en = "unknown";
+ }
+
+ cout << " callback " << c.v_ << " " << en << endl;
+
+ assert (data == c.v_);
+ assert (c.t_ == 0);
+ }
+
+ unsigned short v_;
+ transaction* t_;
+};
+
+struct failed {};
+
+static void
+throw_func (unsigned short, void*, unsigned long long)
+{
+ throw failed ();
+}
+
+static void
+dummy_func (unsigned short, void* key, unsigned long long data)
+{
+ assert (reinterpret_cast<unsigned long long> (key) == data);
+}
+
+static void
+fill (transaction& t)
+{
+ // 20 is from odb/transaction.hxx.
+ //
+ for (size_t i (0); i < 20; ++i)
+ t.callback_register (&dummy_func,
+ reinterpret_cast<void*> (i),
+ transaction::event_all,
+ i);
+}
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv, false));
+
+ // We want to test both stack and dynamic slots.
+ //
+ for (unsigned short i (1); i < 3; ++i)
+ {
+ // Test basic logic.
+ //
+ cout << "test " << i << "/001" << endl;
+
+ // Commit callback.
+ //
+ {
+ transaction t (db->begin ());
+ if (i == 2) fill (t);
+ callback c1 (1, t);
+ t.commit ();
+ }
+
+ // Rollback callback.
+ //
+ {
+ transaction t (db->begin ());
+ if (i == 2) fill (t);
+ callback c1 (1, t);
+ t.rollback ();
+ }
+
+ // Rollback via exception callback.
+ //
+ {
+ callback c1 (1);
+
+ try
+ {
+ transaction t (db->begin ());
+ if (i == 2) fill (t);
+ c1.register_ (t);
+ throw failed ();
+ }
+ catch (const failed&)
+ {
+ }
+ }
+
+ // Unregister callback at the end.
+ //
+ {
+ transaction t (db->begin ());
+ if (i == 2) fill (t);
+ callback c1 (1, t);
+ c1.unregister ();
+ t.callback_unregister (&c1); // Test unregistering non-registered key.
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ if (i == 2) fill (t);
+ callback c1 (1, t);
+ c1.unregister ();
+ callback c2 (2, t);
+ t.commit ();
+ }
+
+ // Unregister callback in the middle.
+ //
+ cout << "test " << i << "/002" << endl;
+ {
+ transaction t (db->begin ());
+ if (i == 2) fill (t);
+ callback c1 (1, t);
+ callback c2 (2, t);
+ callback c3 (3, t);
+ c2.unregister ();
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ if (i == 2) fill (t);
+ callback c1 (1, t);
+ callback c2 (2, t);
+ callback c3 (3, t);
+ c2.unregister ();
+ callback c4 (4, t); // Using the free slot.
+ t.commit ();
+ }
+
+ // Test a callback in the middle that throws.
+ //
+ cout << "test " << i << "/003" << endl;
+ try
+ {
+ transaction t (db->begin ());
+ if (i == 2) fill (t);
+ callback c1 (1, t);
+ t.callback_register (&throw_func, 0);
+ callback c2 (2, t);
+ t.commit ();
+ }
+ catch (const failed&)
+ {
+ }
+
+ // Test callback_update().
+ //
+ {
+ transaction t (db->begin ());
+ if (i == 2) fill (t);
+ callback c (1, t);
+ c.update (2);
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/transaction/callback/testscript b/odb-tests/common/transaction/callback/testscript
new file mode 100644
index 0000000..7229ecd
--- /dev/null
+++ b/odb-tests/common/transaction/callback/testscript
@@ -0,0 +1,72 @@
+# file : common/transaction/callback/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
++cat <<EOI >=output
+ test 1/001
+ callback 1 commit
+ callback 1 rollback
+ callback 1 rollback
+ unregister callback 1
+ unregister callback 1
+ callback 2 commit
+ test 1/002
+ unregister callback 2
+ callback 1 commit
+ callback 3 commit
+ unregister callback 2
+ callback 1 commit
+ callback 4 commit
+ callback 3 commit
+ test 1/003
+ callback 1 commit
+ callback 2 commit
+ test 2/001
+ callback 1 commit
+ callback 1 rollback
+ callback 1 rollback
+ unregister callback 1
+ unregister callback 1
+ callback 2 commit
+ test 2/002
+ unregister callback 2
+ callback 1 commit
+ callback 3 commit
+ unregister callback 2
+ callback 1 commit
+ callback 4 commit
+ callback 3 commit
+ test 2/003
+ callback 1 commit
+ callback 2 commit
+ EOI
+
+test.redirects += >>>../output
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $*
+}
diff --git a/odb-tests/common/types/buildfile b/odb-tests/common/types/buildfile
new file mode 100644
index 0000000..95fe5b6
--- /dev/null
+++ b/odb-tests/common/types/buildfile
@@ -0,0 +1,39 @@
+# file : common/types/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix types_
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/types/driver.cxx b/odb-tests/common/types/driver.cxx
new file mode 100644
index 0000000..bdc66b8
--- /dev/null
+++ b/odb-tests/common/types/driver.cxx
@@ -0,0 +1,37 @@
+// file : common/types/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test C++ type handling (anonymous types, aliasing).
+//
+
+#include <memory>
+#include <iostream>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+template <typename T1, typename T2>
+struct same_p
+{
+ static const bool result = false;
+};
+
+template <typename T>
+struct same_p<T, T>
+{
+ static const bool result = true;
+};
+
+int
+main ()
+{
+ assert ((same_p<odb::object_traits<object2>::id_type, int>::result));
+}
diff --git a/odb-tests/common/types/test.hxx b/odb-tests/common/types/test.hxx
new file mode 100644
index 0000000..a99b499
--- /dev/null
+++ b/odb-tests/common/types/test.hxx
@@ -0,0 +1,55 @@
+// file : common/types/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#ifdef ODB_COMPILER
+typedef int int_t;
+typedef short num_t;
+#else
+typedef int num_t;
+#endif
+
+typedef num_t num_type;
+
+#pragma db object
+struct object1
+{
+ typedef int int_type;
+
+ #pragma db id
+ int_type id_;
+};
+
+#pragma db object
+struct object2
+{
+ #pragma db id
+ num_type num_;
+};
+
+// Template-id with "inner" name (compilation test).
+//
+template <typename X>
+struct num_wrap
+{
+#ifdef ODB_COMPILER
+ typedef num_wrap this_type; // Name that we should not use.
+#endif
+
+ num_wrap () {}
+ num_wrap (X v): v_ (v) {}
+ operator X () const {return v_;}
+
+ X v_;
+};
+
+#pragma db object
+struct object3
+{
+ #pragma db id type("INTEGER")
+ num_wrap<long long> num_; // Use long long to avoid warnings.
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/types/testscript b/odb-tests/common/types/testscript
new file mode 100644
index 0000000..159972b
--- /dev/null
+++ b/odb-tests/common/types/testscript
@@ -0,0 +1,6 @@
+# file : common/types/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+: basic
+:
+$*
diff --git a/odb-tests/common/view/basics/buildfile b/odb-tests/common/view/basics/buildfile
new file mode 100644
index 0000000..2cccab2
--- /dev/null
+++ b/odb-tests/common/view/basics/buildfile
@@ -0,0 +1,45 @@
+# file : common/view/basics/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix t_view_b_ \
+ --generate-schema \
+ --generate-query \
+ --generate-prepared
+
+if $multi
+ odb_options += -DMULTI_DATABASE
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/view/basics/driver.cxx b/odb-tests/common/view/basics/driver.cxx
new file mode 100644
index 0000000..419b29d
--- /dev/null
+++ b/odb-tests/common/view/basics/driver.cxx
@@ -0,0 +1,838 @@
+// file : common/view/basics/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test view basics.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+#include <libcommon/config.hxx> // DATABASE_XXX
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+template <typename V>
+void
+view1_check (odb::result<V>& r)
+{
+ typedef odb::result<V> result;
+
+ typename result::iterator i (r.begin ());
+
+ assert (i != r.end ());
+ assert (i->first == "Jane" && i->last == "Doe" && i->age == 29);
+
+ assert (++i != r.end ());
+ V v;
+ i.load (v);
+ assert (v.first == "John" && v.last == "Doe" && v.age == 30);
+
+ assert (++i == r.end ());
+}
+
+template <typename V>
+void
+view2_test (const unique_ptr<database>& db)
+{
+ typedef odb::query<V> query;
+ typedef odb::result<V> result;
+ typedef typename result::iterator iterator;
+
+ transaction t (db->begin ());
+
+ {
+ result r (db->query<V> ());
+ iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->count == 4);
+ }
+
+ {
+ result r;
+ if (db->id () != odb::id_oracle)
+ r = db->query<V> ("age < 31");
+ else
+ r = db->query<V> ("\"age\" < 31");
+
+ iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->count == 2);
+ }
+
+ {
+ result r (db->query<V> (query::age < 31));
+ iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->count == 2);
+ }
+
+ {
+ unique_ptr<V> v (db->query_one<V> ());
+ assert (v->count == 4);
+ }
+
+ {
+ unique_ptr<V> v;
+ if (db->id () != odb::id_oracle)
+ v.reset (db->query_one<V> ("age < 31"));
+ else
+ v.reset (db->query_one<V> ("\"age\" < 31"));
+ assert (v->count == 2);
+ }
+
+ {
+ unique_ptr<V> v (db->query_one<V> (query::age < 31));
+ assert (v->count == 2);
+ }
+
+ t.commit ();
+}
+
+template <typename V>
+void
+view4_test (const unique_ptr<database>& db)
+{
+ typedef odb::query<V> query;
+ typedef odb::result<V> result;
+ typedef typename result::iterator iterator;
+
+ transaction t (db->begin ());
+
+ {
+ result r;
+ if (db->id () != odb::id_oracle)
+ r = db->query<V> ((query::person::age > 30) + "ORDER BY age");
+ else
+ r = db->query<V> ((query::person::age > 30) + "ORDER BY \"age\"");
+
+ iterator i (r.begin ());
+
+ assert (i != r.end ());
+ assert (i->first_name == "Joe" && i->last_name == "Dirt" &&
+ i->name == "United States");
+
+ assert (++i != r.end ());
+ assert (i->first_name == "Johan" && i->last_name == "Johansen" &&
+ i->name == "Sweden");
+
+ assert (++i == r.end ());
+ }
+
+ {
+ result r (db->query<V> (
+ (query::person::age > 30) +
+ "ORDER BY " + query::person::age));
+
+ iterator i (r.begin ());
+
+ assert (i != r.end ());
+ assert (i->first_name == "Joe" && i->last_name == "Dirt" &&
+ i->name == "United States");
+
+ assert (++i != r.end ());
+ assert (i->first_name == "Johan" && i->last_name == "Johansen" &&
+ i->name == "Sweden");
+
+ assert (++i == r.end ());
+ }
+
+ {
+ result r (db->query<V> (query::residence::code == "US"));
+
+ iterator i (r.begin ());
+
+ assert (i != r.end ());
+ assert (i->first_name == "Joe" && i->last_name == "Dirt" &&
+ i->name == "United States");
+
+ assert (++i == r.end ());
+ }
+
+ t.commit ();
+}
+
+template <typename V>
+void
+view6_test (const unique_ptr<database>& db, const odb::query<V>& q)
+{
+ typedef odb::result<V> result;
+ typedef typename result::iterator iterator;
+
+ transaction t (db->begin ());
+
+ {
+ result r (db->query<V> (q));
+
+ iterator i (r.begin ());
+
+ assert (i != r.end ());
+ assert (i->first_name == "John" && i->last_name == "Doe" &&
+ i->employer == "Simple Tech, Inc");
+
+ assert (++i != r.end ());
+ assert (i->first_name == "Joe" && i->last_name == "Dirt" &&
+ i->employer == "Simple Tech, Inc");
+
+ assert (++i == r.end ());
+ }
+
+ t.commit ();
+}
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ //
+ //
+ {
+ country ca ("CA", "Canada");
+ country za ("ZA", "South Africa");
+ country us ("US", "United States");
+ country se ("SE", "Sweden");
+
+ person p1 (1, "John", "Doe", 30, male, measures (60, 160), &ca, &ca);
+ person p2 (2, "Jane", "Doe", 29, female, measures (70, 170), &za, &us);
+ person p3 (3, "Joe", "Dirt", 31, male, measures (80, 180), &us, &za);
+ person p4 (4, "Johan", "Johansen", 32, male, measures (90, 190), &se,
+ &se);
+
+ p2.husband = &p1;
+
+ employer st ("Simple Tech, Inc");
+ employer ct ("Complex Tech, Inc");
+
+ p2.previous_employer = st.name;
+ p3.previous_employer = ct.name;
+
+ st.employees.push_back (&p1);
+ st.employees.push_back (&p3);
+ st.head_count = 2;
+
+ ct.employees.push_back (&p2);
+ ct.employees.push_back (&p4);
+ ct.head_count = 2;
+
+ transaction t (db->begin ());
+ db->persist (ca);
+ db->persist (za);
+ db->persist (us);
+ db->persist (se);
+
+ db->persist (p1);
+ db->persist (p2);
+ db->persist (p3);
+ db->persist (p4);
+
+ db->persist (st);
+ db->persist (ct);
+ t.commit ();
+ }
+
+ // view1
+ //
+ {
+ typedef odb::result<view1> result;
+
+ {
+ transaction t (db->begin ());
+
+ {
+ result r (db->query<view1> ());
+ assert (size (r) == 4);
+ }
+
+ {
+ result r;
+ if (db->id () != odb::id_oracle)
+ r = db->query<view1> ("ORDER BY age");
+ else
+ r = db->query<view1> ("ORDER BY \"age\"");
+
+ assert (size (r) == 4);
+ }
+
+ {
+ result r;
+ if (db->id () != odb::id_oracle)
+ r = db->query<view1> ("age < 31 ORDER BY age");
+ else
+ r = db->query<view1> ("\"age\" < 31 ORDER BY \"age\"");
+
+ view1_check (r);
+ }
+
+ t.commit ();
+ }
+ }
+
+ // view1a
+ //
+ {
+ typedef odb::result<view1a> result;
+
+ {
+ transaction t (db->begin ());
+
+ result r (db->query<view1a> ());
+ view1_check (r);
+
+ t.commit ();
+ }
+ }
+
+ // view1b
+ //
+ {
+ typedef odb::result<view1b> result;
+
+ {
+ transaction t (db->begin ());
+
+ result r (db->query<view1b> ());
+ view1_check (r);
+
+ t.commit ();
+ }
+
+ // No native parameter support in dynamic multi-database mode.
+ //
+#ifndef MULTI_DATABASE
+ {
+ typedef odb::query<view1b> query;
+
+ transaction t (db->begin ());
+
+#ifndef DATABASE_ORACLE
+ result r (db->query<view1b> ("first = " + query::_val ("Jane")));
+#else
+ result r (db->query<view1b> ("\"first\" = " + query::_val ("Jane")));
+#endif
+
+ result::iterator i (r.begin ());
+
+ assert (i != r.end ());
+ assert (i->first == "Jane" && i->last == "Doe");
+ assert (++i == r.end ());
+
+ t.commit ();
+ }
+#endif
+ }
+
+ // view1c
+ //
+ {
+ typedef odb::result<view1c> result;
+
+ {
+ transaction t (db->begin ());
+
+ result r;
+ if (db->id () != odb::id_oracle)
+ r = db->query<view1c> ("SELECT first, last, age "
+ "FROM t_view_b_person "
+ "WHERE age < 31 ORDER BY age");
+ else
+ r = db->query<view1c> ("SELECT \"first\", \"last\", \"age\" "
+ "FROM \"t_view_b_person\" "
+ "WHERE \"age\" < 31 ORDER BY \"age\"");
+ view1_check (r);
+
+ t.commit ();
+ }
+ }
+
+ // view1d
+ //
+ {
+ typedef odb::result<view1d> result;
+
+ {
+ transaction t (db->begin ());
+
+ {
+ result r;
+ if (db->id () != odb::id_oracle)
+ r = db->query<view1d> ("age < 31 ORDER BY age");
+ else
+ r = db->query<view1d> ("\"age\" < 31 ORDER BY \"age\"");
+
+ view1_check (r);
+ }
+
+ t.commit ();
+ }
+ }
+
+ // view2
+ //
+ view2_test<view2> (db);
+ view2_test<view2a> (db);
+ view2_test<view2b> (db);
+ view2_test<view2c> (db);
+
+ // view3
+ //
+ {
+ typedef odb::result<const view3> result; // Test const result.
+
+ {
+ transaction t (db->begin ());
+
+ {
+ result r (db->query<view3> ());
+
+ size_t count (0);
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ if (i->last_name == "Doe")
+ assert (i->count == 2);
+ else if (i->last_name == "Dirt" ||
+ i->last_name == "Johansen")
+ assert (i->count == 1);
+ else
+ assert (false);
+
+ count++;
+ }
+
+ assert (count == 3);
+ }
+
+ t.commit ();
+ }
+ }
+
+ // view3a
+ //
+ {
+ typedef odb::query<view3a> query;
+ typedef odb::result<view3a> result;
+
+ {
+ transaction t (db->begin ());
+
+ {
+ result r (db->query<view3a> (query::last_name == "Doe"));
+ result::iterator i (r.begin ());
+
+ assert (i != r.end ());
+ assert (i->last_name == "Doe" && i->count == 2);
+ assert (++i == r.end ());
+ }
+
+ t.commit ();
+ }
+ }
+
+ // view4
+ //
+ view4_test<view4> (db);
+ view4_test<view4a> (db);
+
+ // view5
+ //
+ {
+ typedef odb::query<view5> query;
+ typedef odb::result<view5> result;
+
+ {
+ transaction t (db->begin ());
+
+ {
+ result r (
+ db->query<view5> (
+ query::residence::name == query::nationality::name));
+
+ result::iterator i (r.begin ());
+
+ assert (i != r.end ());
+ assert (i->first_name == "John" && i->last_name == "Doe" &&
+ i->rname == "Canada" && i->rname == "Canada");
+
+ assert (++i != r.end ());
+ assert (i->first_name == "Johan" && i->last_name == "Johansen" &&
+ i->rname == "Sweden" && i->rname == "Sweden");
+
+ assert (++i == r.end ());
+ }
+
+ t.commit ();
+ }
+ }
+
+ // view6
+ //
+ view6_test<view6> (
+ db, odb::query<view6>::employer::name == "Simple Tech, Inc");
+
+ view6_test<view6a> (
+ db, odb::query<view6a>::employer::name == "Simple Tech, Inc");
+
+ view6_test<view6b> (
+ db, odb::query<view6b>::employer::name == "Simple Tech, Inc");
+
+ // No native parameter support in dynamic multi-database mode.
+ //
+#ifndef MULTI_DATABASE
+ view6_test<view6c> (
+#ifndef DATABASE_ORACLE
+ db, "e.name = " + odb::query<view6c>::_val ("Simple Tech, Inc"));
+#else
+ db, "\"e\".\"name\" = " + odb::query<view6c>::_val ("Simple Tech, Inc"));
+#endif
+#endif
+
+ // view7
+ //
+ {
+ typedef odb::query<view7> query;
+ typedef odb::result<view7> result;
+
+ {
+ transaction t (db->begin ());
+
+ {
+ result r (db->query<view7> (query::person::last_name == "Doe"));
+
+ result::iterator i (r.begin ());
+
+ assert (i != r.end ());
+ assert (i->first_name == "Jane" && i->last_name == "Doe" &&
+ !i->head_count.null () && *i->head_count == 2);
+
+ assert (++i != r.end ());
+ assert (i->first_name == "John" && i->last_name == "Doe" &&
+ i->head_count.null ());
+
+ assert (++i == r.end ());
+ }
+
+ t.commit ();
+ }
+ }
+
+ // view8
+ //
+ {
+ typedef odb::result<view8> result;
+
+ {
+ transaction t (db->begin ());
+
+ {
+ result r (db->query<view8> ());
+
+ result::iterator i (r.begin ());
+
+ assert (i != r.end ());
+ assert (i->wife_name == "Jane" && i->husb_name == "John");
+ assert (++i == r.end ());
+ }
+
+ t.commit ();
+ }
+ }
+
+ // view9
+ //
+ {
+ typedef odb::query<view9> query;
+ typedef odb::result<view9> result;
+
+ {
+ transaction t (db->begin ());
+
+ {
+ // Test case-insensitive clause prefix detection.
+ //
+ result r (db->query<view9> ("where" + (query::gender == female)));
+
+ result::iterator i (r.begin ());
+
+ assert (i != r.end ());
+ assert (i->first_name == "Jane" && i->last_name == "Doe" &&
+ i->gender == female);
+ assert (++i == r.end ());
+ }
+
+ t.commit ();
+ }
+ }
+
+ // view10
+ //
+ {
+ typedef odb::query<view10> query;
+ typedef odb::result<view10> result;
+
+ {
+ transaction t (db->begin ());
+
+ {
+ result r (db->query<view10> (
+ query::measures.weight > 60 &&
+ query::measures.hight < 190));
+
+ result::iterator i (r.begin ());
+
+ assert (i != r.end ());
+ assert (i->last_name == "Doe" &&
+ i->measures.weight == 70 && i->measures.hight == 170);
+
+ assert (++i != r.end ());
+ assert (i->last_name == "Dirt" &&
+ i->measures.weight == 80 && i->measures.hight == 180);
+
+ assert (++i == r.end ());
+ }
+
+ t.commit ();
+ }
+ }
+
+ // view11
+ //
+ {
+ typedef odb::result<view11> result;
+
+ {
+ transaction t (db->begin ());
+
+ {
+ result r (db->query<view11> ());
+
+ result::iterator i (r.begin ());
+
+ assert (i != r.end ());
+ assert (i->last_name == "Doe" && i->hight == 170);
+
+ assert (++i != r.end ());
+ assert (i->last_name == "Dirt" && i->hight == 180);
+
+ assert (++i == r.end ());
+ }
+
+ t.commit ();
+ }
+ }
+
+ // view12
+ //
+ {
+ typedef odb::query<view12> query;
+ typedef odb::result<view12> result;
+
+ {
+ transaction t (db->begin ());
+
+ {
+ result r (db->query<view12> (query::last_name == "Dirt"));
+
+ result::iterator i (r.begin ());
+
+ assert (i != r.end ());
+ assert (i->residence == "US");
+ assert (++i == r.end ());
+ }
+
+ t.commit ();
+ }
+ }
+
+ // view13
+ //
+ {
+ typedef odb::query<view13> query;
+ typedef odb::result<view13> result;
+
+ {
+ transaction t (db->begin ());
+
+ {
+ result r (db->query<view13> (
+ (query::person::age < 32) +
+ "ORDER BY" + query::employer::name));
+
+ assert (size (r) == 2);
+ }
+
+ t.commit ();
+ }
+ }
+
+ // view14
+ //
+ {
+ transaction t (db->begin ());
+ assert (size (db->query<view14> ()) == 2);
+ t.commit ();
+ }
+
+ // Test join types.
+ //
+ {
+ using namespace test2;
+
+ {
+ obj1 o11 (1, 1);
+ obj1 o12 (2, 2);
+
+ obj2 o21 (1, 1);
+ obj2 o22 (2, 1);
+ obj2 o23 (3, 3);
+
+ transaction t (db->begin ());
+ db->persist (o11);
+ db->persist (o12);
+ db->persist (o21);
+ db->persist (o22);
+ db->persist (o23);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<vleft> query;
+ typedef odb::result<vleft> result;
+
+ transaction t (db->begin ());
+ result r (db->query<vleft> (
+ "ORDER BY" + query::o1::id1 + "," + query::o2::id2));
+ result::iterator i (r.begin ());
+ assert ( i != r.end () && i->id1 == 1 && *i->id2 == 1);
+ assert (++i != r.end () && i->id1 == 1 && *i->id2 == 2);
+ assert (++i != r.end () && i->id1 == 2 && i->id2.null ());
+ assert (++i == r.end ());
+ t.commit ();
+ }
+
+#if !defined(DATABASE_SQLITE) && !defined(MULTI_DATABASE)
+ {
+ typedef odb::query<vright> query;
+ typedef odb::result<vright> result;
+
+ transaction t (db->begin ());
+ result r (db->query<vright> (
+ "ORDER BY" + query::o1::id1 + "," + query::o2::id2));
+ result::iterator i (r.begin ());
+ assert ( i != r.end () && i->id1 == 1 && *i->id2 == 1);
+ assert (++i != r.end () && i->id1 == 1 && *i->id2 == 2);
+ assert (++i != r.end () && i->id1 == 2 && i->id2.null ());
+ assert (++i == r.end ());
+ t.commit ();
+ }
+#endif
+
+#if !defined(DATABASE_SQLITE) && \
+ !defined(DATABASE_MYSQL) && \
+ !defined(MULTI_DATABASE)
+ {
+ typedef odb::query<vfull> query;
+ typedef odb::result<vfull> result;
+
+ transaction t (db->begin ());
+ result r (db->query<vfull> (
+ "ORDER BY" + query::o1::id1 + "," + query::o2::id2));
+ result::iterator i (r.begin ());
+
+ // SQL Server orders NULL values first. Got to be different.
+ //
+#ifdef DATABASE_MSSQL
+ assert ( i != r.end () && i->id1.null () && *i->id2 == 3);
+ assert (++i != r.end () && *i->id1 == 1 && *i->id2 == 1);
+ assert (++i != r.end () && *i->id1 == 1 && *i->id2 == 2);
+ assert (++i != r.end () && *i->id1 == 2 && i->id2.null ());
+#else
+ assert ( i != r.end () && *i->id1 == 1 && *i->id2 == 1);
+ assert (++i != r.end () && *i->id1 == 1 && *i->id2 == 2);
+ assert (++i != r.end () && *i->id1 == 2 && i->id2.null ());
+ assert (++i != r.end () && i->id1.null () && *i->id2 == 3);
+#endif
+ assert (++i == r.end ());
+ t.commit ();
+ }
+#endif
+
+ {
+ typedef odb::query<vinner> query;
+ typedef odb::result<vinner> result;
+
+ transaction t (db->begin ());
+ result r (db->query<vinner> (
+ "ORDER BY" + query::o1::id1 + "," + query::o2::id2));
+ result::iterator i (r.begin ());
+ assert ( i != r.end () && i->id1 == 1 && i->id2 == 1);
+ assert (++i != r.end () && i->id1 == 1 && i->id2 == 2);
+ assert (++i == r.end ());
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<vcross> query;
+ typedef odb::result<vcross> result;
+
+ transaction t (db->begin ());
+ result r (db->query<vcross> (
+ "ORDER BY" + query::o1::id1 + "," + query::o2::id2));
+ result::iterator i (r.begin ());
+ assert ( i != r.end () && i->id1 == 1 && i->id2 == 1);
+ assert (++i != r.end () && i->id1 == 1 && i->id2 == 2);
+ assert (++i != r.end () && i->id1 == 1 && i->id2 == 3);
+ assert (++i != r.end () && i->id1 == 2 && i->id2 == 1);
+ assert (++i != r.end () && i->id1 == 2 && i->id2 == 2);
+ assert (++i != r.end () && i->id1 == 2 && i->id2 == 3);
+ assert (++i == r.end ());
+ t.commit ();
+ }
+
+ // Inner JOIN via relationship/container.
+ //
+ {
+ obj3 o31 (1, 1);
+ obj3 o32 (2, 2);
+
+ obj4 o41 (1, 1);
+ obj4 o42 (2, 2);
+ o42.o3.push_back (&o32);
+
+ transaction t (db->begin ());
+ db->persist (o31);
+ db->persist (o32);
+ db->persist (o41);
+ db->persist (o42);
+ t.commit ();
+ }
+
+ {
+ typedef odb::result<vrel> result;
+
+ transaction t (db->begin ());
+ result r (db->query<vrel> ());
+ result::iterator i (r.begin ());
+ assert ( i != r.end () && i->id4 == 2);
+ assert (++i == r.end ());
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/view/basics/test.hxx b/odb-tests/common/view/basics/test.hxx
new file mode 100644
index 0000000..25c502b
--- /dev/null
+++ b/odb-tests/common/view/basics/test.hxx
@@ -0,0 +1,616 @@
+// file : common/view/basics/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+
+#include <odb/core.hxx>
+#include <odb/nullable.hxx>
+
+struct employer;
+
+#pragma db object
+struct country
+{
+ country (const std::string& c, const std::string& n)
+ : code (c), name (n)
+ {
+ }
+
+ country ()
+ {
+ }
+
+ #pragma db id
+ std::string code; // ISO 2-letter country code.
+
+ std::string name;
+};
+
+enum gender_type {male, female};
+
+#pragma db value
+struct measures
+{
+ measures (unsigned short w, unsigned short h) : weight (w), hight (h) {}
+ measures () {}
+
+ unsigned short weight;
+ unsigned short hight;
+};
+
+#pragma db object
+struct person
+{
+ typedef ::measures measures_type;
+
+ person (unsigned long i,
+ const std::string& fn,
+ const std::string& ln,
+ unsigned short a,
+ gender_type g,
+ const measures_type m,
+ country* r,
+ country* n)
+ : id (i),
+ first_name_ (fn),
+ last_name_ (ln),
+ age (a),
+ gender (g),
+ measures (m),
+ residence (r),
+ nationality (n),
+ husband (0)
+ {
+ }
+
+ person ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db column("first")
+ std::string first_name_;
+
+ #pragma db column("last")
+ std::string last_name_;
+
+ unsigned short age;
+
+ // #pragma db type("INT") - in MySQL test type pragma copying
+ gender_type gender;
+
+ measures_type measures;
+
+ #pragma db not_null
+ country* residence;
+
+ #pragma db not_null
+ country* nationality;
+
+ #pragma db inverse(employees)
+ employer* employed_by;
+
+ // A non-pointer relationship.
+ //
+ odb::nullable<std::string> previous_employer;
+
+ person* husband; // Self-reference.
+};
+
+#pragma db object
+struct employer
+{
+ employer (const std::string& n)
+ : name (n)
+ {
+ }
+
+ employer ()
+ {
+ }
+
+ #pragma db id
+ std::string name;
+ unsigned int head_count;
+ std::vector<person*> employees;
+};
+
+//
+// General view with no associated objects.
+//
+
+// Complete suffix query template.
+//
+#ifndef ODB_DATABASE_ORACLE
+# pragma db view query("SELECT first, last, age FROM t_view_b_person")
+#else
+# pragma db view query("SELECT \"first\", \"last\", \"age\" " \
+ "FROM \"t_view_b_person\"")
+#endif
+struct view1
+{
+ std::string first;
+ std::string last;
+ unsigned short age;
+};
+
+// Complete query.
+//
+#ifndef ODB_DATABASE_ORACLE
+# pragma db view query("SELECT first, last, age " \
+ "FROM t_view_b_person " \
+ "WHERE age < 31 ORDER BY age")
+#else
+# pragma db view query("SELECT \"first\", \"last\", \"age\" " \
+ "FROM \"t_view_b_person\" " \
+ "WHERE \"age\" < 31 ORDER BY \"age\"")
+#endif
+struct view1a
+{
+ std::string first;
+ std::string last;
+ unsigned short age;
+};
+
+// Complete placeholder query template.
+//
+#ifndef ODB_DATABASE_ORACLE
+# pragma db view query("SELECT first, last, age " \
+ "FROM t_view_b_person " \
+ "WHERE age < 31 AND (?) ORDER BY age")
+#else
+# pragma db view query("SELECT \"first\", \"last\", \"age\" " \
+ "FROM \"t_view_b_person\" " \
+ "WHERE \"age\" < 31 AND (?) ORDER BY \"age\"")
+#endif
+struct view1b
+{
+ std::string first;
+ std::string last;
+ unsigned short age;
+};
+
+// Runtime query.
+//
+#pragma db view //query()
+struct view1c
+{
+ std::string first;
+ std::string last;
+ unsigned short age;
+};
+
+// Assembled SELECT and FROM-lists.
+//
+#pragma db view table("t_view_b_person")
+struct view1d
+{
+ #pragma db column("first")
+ std::string first;
+
+ #pragma db column("last")
+ std::string last;
+
+ #pragma db column("age")
+ unsigned short age;
+};
+
+//
+// Count view plus associated object.
+//
+
+// Complete suffix query.
+//
+#ifndef ODB_DATABASE_ORACLE
+# pragma db view object(person) \
+ query("SELECT count(id) FROM t_view_b_person")
+#else
+# pragma db view object(person) \
+ query("SELECT count(\"id\") FROM \"t_view_b_person\"")
+#endif
+struct view2
+{
+ std::size_t count;
+};
+
+// Generated query, literal column.
+//
+#pragma db view object(person)
+struct view2a
+{
+#ifndef ODB_DATABASE_ORACLE
+ #pragma db column("count(id)")
+#else
+ #pragma db column("count(\"id\")")
+#endif
+ std::size_t count;
+};
+
+// Generated query, qualified literal column.
+//
+#pragma db view object(person)
+struct view2b
+{
+#ifndef ODB_DATABASE_ORACLE
+ #pragma db column("count(t_view_b_person.id)")
+#else
+ #pragma db column("count(\"t_view_b_person\".\"id\")")
+#endif
+ std::size_t count;
+};
+
+// Generated query, expression column.
+//
+#pragma db view object(person)
+struct view2c
+{
+ #pragma db column("count(" + person::id + ")")
+ std::size_t count;
+};
+
+//
+// Aggregate view plus associated object with a custom alias.
+//
+
+// Complete suffix query.
+//
+#ifndef ODB_DATABASE_ORACLE
+# pragma db view object(person = test) \
+ query("SELECT last, count(last) " \
+ "FROM t_view_b_person " \
+ "GROUP BY last")
+#else
+# pragma db view object(person = test) \
+ query("SELECT \"last\", count(\"last\") " \
+ "FROM \"t_view_b_person\" " \
+ "GROUP BY \"last\"")
+#endif
+struct view3
+{
+ std::string last_name;
+ std::size_t count;
+};
+
+// Generated query with integrated query condition and placeholder.
+//
+#pragma db view object(person = test) \
+ query((?) + "GROUP BY" + test::last_name_)
+struct view3a
+{
+ // Automatically resolved to test::last_name_.
+ //
+ std::string last_name;
+
+ #pragma db column("count(" + test::last_name_ + ")")
+ std::size_t count;
+};
+
+//
+// JOIN view plus associated objects, some with custom aliases.
+//
+
+// Complete suffix query.
+//
+#ifndef ODB_DATABASE_ORACLE
+# pragma db view object(person) object(country = residence) \
+ query("SELECT first, last, residence.name " \
+ "FROM t_view_b_person " \
+ "LEFT JOIN t_view_b_country AS residence " \
+ "ON t_view_b_person.residence = residence.code")
+#else
+# pragma db view object(person) object(country = residence) \
+ query("SELECT \"first\", \"last\", \"residence\".\"name\" " \
+ "FROM \"t_view_b_person\" " \
+ "LEFT JOIN \"t_view_b_country\" \"residence\" " \
+ "ON \"t_view_b_person\".\"residence\" = \"residence\".\"code\"")
+#endif
+struct view4
+{
+ std::string first_name;
+ std::string last_name;
+ std::string name;
+};
+
+// Generated query.
+//
+#pragma db view object(person) \
+ object(country = residence: person::residence)
+struct view4a
+{
+ std::string first_name;
+ std::string last_name;
+ std::string name;
+};
+
+//
+// JOIN the same object twice.
+//
+#pragma db view object(person) \
+ object(country = residence: person::residence) \
+ object(country = nationality: person::nationality) \
+ query((?) + "ORDER BY" + person::age)
+struct view5
+{
+ std::string first_name;
+ std::string last_name;
+
+ #pragma db column(residence::name)
+ std::string rname;
+
+ #pragma db column(nationality::name)
+ std::string nname;
+};
+
+//
+// JOIN via one(i)-to-many relationship.
+//
+
+// Automatic relationship discovery.
+//
+#pragma db view object(person) object(employer)
+struct view6
+{
+ std::string first_name;
+ std::string last_name;
+
+ #pragma db column(::employer::name)
+ std::string employer;
+};
+
+// Manual relationship specification, left side.
+//
+#pragma db view object(person) object(employer: person::employed_by)
+struct view6a
+{
+ std::string first_name;
+ std::string last_name;
+
+ #pragma db column(::employer::name)
+ std::string employer;
+};
+
+// Manual relationship specification, right side.
+//
+#pragma db view object(person) object(employer: employer::employees)
+struct view6b
+{
+ std::string first_name;
+ std::string last_name;
+
+ #pragma db column(::employer::name)
+ std::string employer;
+};
+
+// The same using tables.
+//
+#if defined(ODB_DATABASE_ORACLE)
+#pragma db view table("t_view_b_person" = "p") \
+ table("t_view_b_employer_employees" = "ee": "\"ee\".\"value\" = \"p\".\"id\"")\
+ table("t_view_b_employer" = "e": "\"ee\".\"object_id\" = \"e\".\"name\"")
+#elif defined(ODB_DATABASE_MSSQL)
+#pragma db view table("t_view_b_person" = "p") \
+ table("t_view_b_employer_employees" = "ee": "ee.value = p.id") \
+ table("t_view_b_employer" = "e": "[ee].[object_id] = e.name")
+#elif defined(ODB_DATABASE_MYSQL)
+#pragma db view table("t_view_b_person" = "p") \
+ table("t_view_b_employer_employees" = "ee": "ee.value = p.id") \
+ table("t_view_b_employer" = "e": "`ee`.`object_id` = e.name")
+#else
+#pragma db view table("t_view_b_person" = "p") \
+ table("t_view_b_employer_employees" = "ee": "ee.value = p.id") \
+ table("t_view_b_employer" = "e": "\"ee\".\"object_id\" = e.name")
+#endif
+struct view6c
+{
+ #pragma db column("p.first")
+ std::string first_name;
+
+ #pragma db column("p.last")
+ std::string last_name;
+
+ #pragma db column("e"."name")
+ std::string employer;
+};
+
+//
+// JOIN via a custom condition.
+//
+#pragma db view object(person) \
+ object(employer: person::previous_employer == employer::name)\
+ query((?) + "ORDER BY" + person::age)
+struct view7
+{
+ std::string first_name;
+ std::string last_name;
+
+ odb::nullable<unsigned int> head_count;
+};
+
+//
+// Self-JOIN.
+//
+#pragma db view object(person = wife) object(person = husb) \
+ query (wife::husband.is_not_null ())
+struct view8
+{
+ #pragma db column(wife::first_name_)
+ std::string wife_name;
+
+ #pragma db column(husb::first_name_)
+ std::string husb_name;
+};
+
+//
+// Enum mapping.
+//
+#pragma db view object(person)
+struct view9
+{
+ std::string first_name;
+ std::string last_name;
+ gender_type gender;
+};
+
+//
+// Composite in view.
+//
+#pragma db view object(person) query((?) + "ORDER BY" + person::age)
+struct view10
+{
+ std::string last_name;
+ ::measures measures;
+};
+
+//
+// Composite in object.
+//
+#pragma db view object(person) \
+ query((person::measures.weight > 60 && person::measures.hight < 190 && (?)) \
+ + "ORDER BY" + person::age)
+struct view11
+{
+ std::string last_name;
+
+ #pragma db column(person::measures.hight)
+ unsigned short hight;
+};
+
+//
+// Extract object pointer as object id.
+//
+#pragma db view object(person)
+struct view12
+{
+ std::string residence;
+};
+
+//
+// Test 'distinct' result modifier.
+//
+#pragma db view object(employer) object(person) query(distinct)
+struct view13
+{
+ std::string name;
+};
+
+//
+// Test 'for_update' result modifier.
+//
+#pragma db view object(employer) query((?), for_update)
+struct view14
+{
+ std::string name;
+};
+
+// Test join types.
+//
+#pragma db namespace table("t2_")
+namespace test2
+{
+ #pragma db object
+ struct obj1
+ {
+ obj1 (int id = 0, int n_ = 0): id1 (id), n (n_) {}
+
+ #pragma db id
+ int id1;
+
+ int n;
+ };
+
+ #pragma db object no_id
+ struct obj2
+ {
+ obj2 (int id = 0, int n_ = 0): id2 (id), n (n_) {}
+
+ #pragma db id
+ int id2;
+
+ int n;
+ };
+
+ #pragma db view object(obj1 = o1) object(obj2 = o2 left: o1::n == o2::n)
+ struct vleft
+ {
+ int id1;
+ odb::nullable<int> id2;
+ };
+
+#if !defined(ODB_DATABASE_SQLITE) && !defined(MULTI_DATABASE)
+
+ #pragma db view object(obj2 = o2) object(obj1 = o1 right: o2::n == o1::n)
+ struct vright
+ {
+ int id1;
+ odb::nullable<int> id2;
+ };
+
+#endif
+
+#if !defined(ODB_DATABASE_MYSQL) && \
+ !defined(ODB_DATABASE_SQLITE) && \
+ !defined(MULTI_DATABASE)
+
+ #pragma db view object(obj1 = o1) object(obj2 = o2 full: o1::n == o2::n)
+ struct vfull
+ {
+ odb::nullable<int> id1;
+ odb::nullable<int> id2;
+ };
+
+#endif
+
+ #pragma db view object(obj1 = o1) object(obj2 = o2 inner: o1::n == o2::n)
+ struct vinner
+ {
+ int id1;
+ int id2;
+ };
+
+ #pragma db view object(obj1 = o1) object(obj2 = o2 cross)
+ struct vcross
+ {
+ int id1;
+ int id2;
+ };
+
+ // Inner JOIN via relationship/container.
+ //
+ #pragma db object
+ struct obj3
+ {
+ obj3 (int id = 0, int n_ = 0): id3 (id), n (n_) {}
+
+ #pragma db id
+ int id3;
+
+ int n;
+ };
+
+ #pragma db object no_id
+ struct obj4
+ {
+ obj4 (int id = 0, int n_ = 0): id4 (id), n (n_) {}
+
+ #pragma db id
+ int id4;
+
+ int n;
+ std::vector<obj3*> o3;
+ };
+
+ #pragma db view object(obj4) object(obj3 inner)
+ struct vrel
+ {
+ int id4;
+ };
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/view/basics/testscript b/odb-tests/common/view/basics/testscript
new file mode 100644
index 0000000..faa8408
--- /dev/null
+++ b/odb-tests/common/view/basics/testscript
@@ -0,0 +1,33 @@
+# file : common/view/basics/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/view/olv/.gitignore b/odb-tests/common/view/olv/.gitignore
new file mode 100644
index 0000000..2b95165
--- /dev/null
+++ b/odb-tests/common/view/olv/.gitignore
@@ -0,0 +1,46 @@
+# ODB-generated files.
+#
+test1-odb.?xx
+test1-odb-*.?xx
+test1.sql
+test1-*.sql
+
+test2-odb.?xx
+test2-odb-*.?xx
+test2.sql
+test2-*.sql
+
+test3-odb.?xx
+test3-odb-*.?xx
+test3.sql
+test3-*.sql
+
+test4-odb.?xx
+test4-odb-*.?xx
+test4.sql
+test4-*.sql
+
+test5-odb.?xx
+test5-odb-*.?xx
+test5.sql
+test5-*.sql
+
+test6-odb.?xx
+test6-odb-*.?xx
+test6.sql
+test6-*.sql
+
+test7-odb.?xx
+test7-odb-*.?xx
+test7.sql
+test7-*.sql
+
+test8-odb.?xx
+test8-odb-*.?xx
+test8.sql
+test8-*.sql
+
+test9-odb.?xx
+test9-odb-*.?xx
+test9.sql
+test9-*.sql
diff --git a/odb-tests/common/view/olv/buildfile b/odb-tests/common/view/olv/buildfile
new file mode 100644
index 0000000..1edf0b4
--- /dev/null
+++ b/odb-tests/common/view/olv/buildfile
@@ -0,0 +1,50 @@
+# file : common/view/olv/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+hdrs = test1 test2 test3 test4 test5 test6 test7 test8 test9
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+for h: $hdrs
+{
+ exe{driver}: {hxx ixx cxx}{$h-odb}
+
+ <{hxx ixx cxx}{$h-odb}>: hxx{$h} libue{test-meta}
+
+ for db: $databases
+ {
+ exe{driver}: {hxx ixx cxx}{$h-odb-$db}: include = $multi
+ <{hxx ixx cxx}{$h-odb-$db}>: hxx{$h} libue{test-meta}
+ }
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix t_view_olv_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../../alias{database-client}: include = adhoc
+
+testscript@./: schemas = $hdrs
diff --git a/odb-tests/common/view/olv/driver.cxx b/odb-tests/common/view/olv/driver.cxx
new file mode 100644
index 0000000..77cf76e
--- /dev/null
+++ b/odb-tests/common/view/olv/driver.cxx
@@ -0,0 +1,646 @@
+// file : common/view/olv/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test object loading views.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+#include <typeinfo>
+
+#include <odb/session.hxx>
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test1.hxx"
+#include "test2.hxx"
+#include "test3.hxx"
+#include "test4.hxx"
+#include "test5.hxx"
+#include "test6.hxx"
+#include "test7.hxx"
+#include "test8.hxx"
+#include "test9.hxx"
+
+#include "test1-odb.hxx"
+#include "test2-odb.hxx"
+#include "test3-odb.hxx"
+#include "test4-odb.hxx"
+#include "test5-odb.hxx"
+#include "test6-odb.hxx"
+#include "test7-odb.hxx"
+#include "test8-odb.hxx"
+#include "test9-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Test basic object loading functionality.
+ //
+ {
+ using namespace test1;
+
+ {
+ object1 o1a (1, 123);
+ object2 o2 (1, "abc");
+
+ transaction t (db->begin ());
+ db->persist (o1a);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<view1> query;
+
+ transaction t (db->begin ());
+ view1 v (db->query_value<view1> (query::object1::n == 123));
+ assert (v.o2->s == "abc");
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ view2 v (db->query_value<view2> ());
+ assert (v.o1->n == 123 && v.o2->s == "abc");
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ view3 v (db->query_value<view3> ());
+ assert (v.o1->n == 123 && v.o2->s == "abc");
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ view4 v (db->query_value<view4> ());
+ assert (v.s == "abc" && v.o2->s == "abc" && v.id == 1 &&
+ v.o1->n == 123 && v.n == 123);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ view4 v (db->query_value<view4> ());
+ assert (v.s == "abc" && v.o2->s == "abc" && v.id == 1 &&
+ v.o1->n == 123 && v.n == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<view5> query;
+
+ object1 o1b (123, 1);
+
+ transaction t (db->begin ());
+ db->persist (o1b);
+ view5 v (db->query_value<view5> (query::o1b::n == 1));
+ assert (v.o1a->n == 123 && v.o2->s == "abc" && v.o1b->n == 1);
+ t.commit ();
+ }
+ }
+
+ // Test loading of object pointers inside objects.
+ //
+ {
+ using namespace test2;
+
+ shared_ptr<object1> o1 (new object1 (123));
+ shared_ptr<object2> o2 (new object2 ("abc", o1));
+
+ {
+
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ view1 v (db->query_value<view1> ());
+ assert (v.o2->s == "abc" && v.o2->o1->n == 123);
+ t.commit ();
+ }
+
+ {
+ // Check session interaction.
+ //
+ transaction t (db->begin ());
+ session s;
+ shared_ptr<object2> o2a (db->load<object2> (o2->id));
+ view1 v (db->query_value<view1> ());
+ assert (v.o2 == o2a);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ session s;
+ view2 v (db->query_value<view2> ());
+ assert (v.o1->n == 123 && v.o2->s == "abc" && v.o2->o1 == v.o1);
+ t.commit ();
+ }
+
+ shared_ptr<object3> o3 (new object3 (o2));
+
+ {
+ transaction t (db->begin ());
+ db->persist (o3);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ session s;
+ view3 v (db->query_value<view3> ());
+ assert (v.o1->n == 123 && v.o3->o2->s == "abc" &&
+ v.o3->o2->o1 == v.o1);
+ t.commit ();
+ }
+
+ shared_ptr<object1> o1b (new object1 (234));
+ shared_ptr<object2> o2b (new object2 ("bcd", o1b));
+ shared_ptr<object4> o4 (new object4);
+ o4->o2.push_back (o2);
+ o4->o2.push_back (o2b);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1b);
+ db->persist (o2b);
+ db->persist (o4);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ view4 v (db->query_value<view4> ());
+ assert (v.o4->o2[0]->s == "abc" && v.o4->o2[0]->o1->n == 123 &&
+ v.o4->o2[1]->s == "bcd" && v.o4->o2[1]->o1->n == 234);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<view5> query;
+ typedef odb::result<view5> result;
+
+ transaction t (db->begin ());
+ session s;
+ result r (db->query<view5> ("ORDER BY" + query::object1::id));
+ result::iterator i (r.begin ());
+
+ assert (i != r.end ());
+ {
+ const view5& v (*i);
+
+ assert (v.o4->o2[0]->s == "abc" && v.o4->o2[0]->o1->n == 123 &&
+ v.o4->o2[1]->s == "bcd" && v.o4->o2[1]->o1->n == 234 &&
+ v.o4->o2[0]->o1 == v.o1);
+ }
+ assert (++i != r.end ());
+ {
+ const view5& v (*i);
+
+ assert (v.o4->o2[0]->s == "abc" && v.o4->o2[0]->o1->n == 123 &&
+ v.o4->o2[1]->s == "bcd" && v.o4->o2[1]->o1->n == 234 &&
+ v.o4->o2[1]->o1 == v.o1);
+ }
+ assert (++i == r.end ());
+ t.commit ();
+ }
+
+ shared_ptr<object5> o5 (new object5 (o1b, o2));
+
+ {
+ transaction t (db->begin ());
+ db->persist (o5);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ view6 v (db->query_value<view6> ());
+ assert (v.o1a->n == 123 && v.o1b->n == 234);
+ t.commit ();
+ }
+ }
+
+ // Test JOINs for pointed-to objects, existing and automatically added.
+ //
+ {
+ using namespace test3;
+
+ shared_ptr<object1> o1 (new object1 (123));
+ shared_ptr<object2> o2 (new object2 ("abc"));
+
+ o1->o2 = o2;
+ o2->o1 = o1;
+
+ {
+
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ view1a v (db->query_value<view1a> ());
+ // VC11
+ assert (v.o1->n == 123 && v.o1->o2.object_id<object2> () == o2->id);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ view1b v (db->query_value<view1b> ());
+ // VC11
+ assert (v.o1->n == 123 && v.o1->o2.object_id<object2> () == o2->id);
+ t.commit ();
+ }
+
+ // Container case.
+ //
+
+ shared_ptr<object3> o3 (new object3 (123));
+ shared_ptr<object4> o4 (new object4 ("abc"));
+
+ o3->o4 = o4;
+ o4->o3.push_back (o3);
+
+ {
+
+ transaction t (db->begin ());
+ db->persist (o3);
+ db->persist (o4);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ view2a v (db->query_value<view2a> ());
+ // VC11
+ assert (v.o3->n == 123 && v.o3->o4.object_id<object4> () == o4->id);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ view2b v (db->query_value<view2b> ());
+ // VC11
+ assert (v.o3->n == 123 && v.o3->o4.object_id<object4> () == o4->id);
+ t.commit ();
+ }
+ }
+
+ // Test by-value load.
+ //
+ {
+ using namespace test4;
+
+ {
+ object1 o1 (1, 123);
+ object2 o2 (1, "abc", &o1);
+
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ view1 v (db->query_value<view1> ());
+ assert (v.o1.n == 123);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ view1a v (db->query_value<view1a> ());
+ assert (!v.o1_null && v.o1.n == 123);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ view1b v (db->query_value<view1b> ());
+ assert (/*v.o1_p == &v.o1 && */ v.o1.n == 123); // Copy ctor.
+ t.commit ();
+ }
+
+ {
+ typedef odb::result<view1c> result;
+
+ transaction t (db->begin ());
+ result r (db->query<view1c> ());
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+
+ object1 o1;
+ view1c v (o1);
+ i.load (v);
+
+ assert (v.o1_p == &o1 && o1.n == 123);
+
+ assert (++i == r.end ());
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ session s;
+ view2 v (db->query_value<view2> ());
+ assert (v.o1.n == 123 && v.o2.s == "abc" && v.o2.o1 == &v.o1);
+ t.commit ();
+ }
+
+ object1 o1b (2, 234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1b);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<view2a> query;
+
+ transaction t (db->begin ());
+ session s;
+ view2a v (db->query_value<view2a> (query::object1::id == 2));
+ assert (v.o1.n == 234 && v.o2_null);
+ t.commit ();
+ }
+
+ shared_ptr<object3> o3 (new object3 (1, 123));
+
+ {
+ transaction t (db->begin ());
+ db->persist (o3);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ {
+ view3 v (db->query_value<view3> ());
+ assert (v.o3_p == &v.o3 && v.o3.n == 123); // Load into value.
+ }
+ session s; // Load into cache.
+ shared_ptr<object3> o3a (db->load<object3> (o3->id));
+ {
+ view3 v (db->query_value<view3> ());
+ assert (v.o3_p == o3a.get ()); // Load from cache.
+ }
+ t.commit ();
+ }
+ }
+
+ // Test NULL object pointers.
+ //
+ {
+ using namespace test5;
+
+ shared_ptr<object1> o1a (new object1 (123));
+ shared_ptr<object1> o1b (new object1 (234));
+ shared_ptr<object2> o2 (new object2 ("abc", o1a));
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1a);
+ db->persist (o1b);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<view1> query;
+ typedef odb::result<view1> result;
+
+ transaction t (db->begin ());
+ session s;
+ result r (db->query<view1> ("ORDER BY" + query::object1::id));
+ result::iterator i (r.begin ());
+
+ assert (i != r.end ());
+ {
+ const view1& v (*i);
+ assert (v.o1->n == 123 && v.o2->s == "abc" && v.o2->o1 == v.o1);
+ }
+ assert (++i != r.end ());
+ {
+ const view1& v (*i);
+ assert (v.o1->n == 234 && !v.o2);
+ }
+ assert (++i == r.end ());
+ t.commit ();
+ }
+
+ shared_ptr<object3> o3a (new object3 (make_pair (1, 1), 123));
+ shared_ptr<object3> o3b (new object3 (make_pair (2, 2), 234));
+ shared_ptr<object4> o4 (new object4 ("abc", o3a));
+
+ {
+ transaction t (db->begin ());
+ db->persist (o3a);
+ db->persist (o3b);
+ db->persist (o4);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<view2> query;
+ typedef odb::result<view2> result;
+
+ transaction t (db->begin ());
+ session s;
+ result r (db->query<view2> ("ORDER BY" + query::object3::n));
+ result::iterator i (r.begin ());
+
+ assert (i != r.end ());
+ {
+ const view2& v (*i);
+ assert (v.o3->n == 123 && v.o4->s == "abc" && v.o4->o3 == v.o3);
+ }
+ assert (++i != r.end ());
+ {
+ const view2& v (*i);
+ assert (v.o3->n == 234 && !v.o4);
+ }
+ assert (++i == r.end ());
+ t.commit ();
+ }
+ }
+
+ // Test interaction with sections.
+ //
+ {
+ using namespace test6;
+
+ shared_ptr<object1> o1 (new object1 (123));
+ shared_ptr<object2> o2 (new object2 ("abc", o1));
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ view1 v (db->query_value<view1> ());
+
+ assert (v.o1->n == 123 && v.o2->s == "abc" &&
+ !v.o2->r.loaded () && !v.o2->o1);
+
+ db->load (*v.o2, v.o2->r);
+ assert (v.o2->r.loaded () && v.o2->o1 && v.o2->o1->n == 123);
+
+ t.commit ();
+ }
+ }
+
+ // Test explicit conversion to smart pointer member.
+ //
+ {
+ using namespace test7;
+
+ object1 o1 (123);
+ object2 o2 ("abc", &o1);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ session s;
+ view1 v (db->query_value<view1> ());
+ assert (v.o1->n == 123 && v.o2->s == "abc" && v.o2->o1 == v.o1.get ());
+ t.commit ();
+ }
+ }
+
+ // Test loading objects without id.
+ //
+ {
+ using namespace test8;
+
+ object1 o1 (123);
+ object2 o2 ("abc", &o1);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ session s;
+ view1 v (db->query_value<view1> ());
+ assert (v.o1->n == 123 && v.o2->s == "abc" && v.o2->o1 == v.o1.get ());
+ t.commit ();
+ }
+ }
+
+ // Test loading polymorphic objects.
+ //
+ {
+ using namespace test9;
+
+ root r (1);
+ base b (2, "a");
+ derived d (3, "b", true);
+
+ {
+ transaction t (db->begin ());
+ db->persist (r);
+ db->persist (b);
+ db->persist (d);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+
+ // Load via root.
+ //
+ {
+ view1r r (db->query_value<view1r> (query<view1r>::n == 1));
+ auto& o (*r.o);
+ assert (r.n == 1 && r.o->n == 1 && typeid (o) == typeid (root));
+ }
+
+ {
+ view1r r (db->query_value<view1r> (query<view1r>::n == 2));
+ auto& o (*r.o);
+ assert (r.n == 2 && r.o->n == 2 && typeid (o) == typeid (base));
+ base& b (dynamic_cast<base&> (*r.o));
+ assert (b.s == "a");
+ }
+
+ {
+ view1r r (db->query_value<view1r> (query<view1r>::n == 3));
+ auto& o (*r.o);
+ assert (r.n == 3 && r.o->n == 3 && typeid (o) == typeid (derived));
+ derived& d (dynamic_cast<derived&> (o));
+ assert (d.s == "b" && d.b);
+ }
+
+ // Load via base.
+ //
+ {
+ view1b r (db->query_value<view1b> (query<view1b>::n == 2));
+ assert (r.s == "a" && r.n == 2 && r.o->n == 2 && b.s == "a");
+ }
+
+ {
+ view1b r (db->query_value<view1b> (query<view1b>::n == 3));
+ auto& o (*r.o);
+ assert (r.s == "b" && r.n == 3 && r.o->n == 3 &&
+ typeid (o) == typeid (derived));
+ derived& d (dynamic_cast<derived&> (o));
+ assert (d.s == "b" && d.b);
+ }
+
+ // Load via derived.
+ //
+ {
+ view1d r (db->query_value<view1d> ());
+ assert (r.s == "b" && r.n == 3 &&
+ r.o->n == 3 && r.o->s == "b" && r.o->b);
+ }
+
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/view/olv/test1.hxx b/odb-tests/common/view/olv/test1.hxx
new file mode 100644
index 0000000..0de9483
--- /dev/null
+++ b/odb-tests/common/view/olv/test1.hxx
@@ -0,0 +1,116 @@
+// file : common/view/olv/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+#include <string>
+#include <memory> // unique_ptr
+#include <utility> // std::move
+
+#include <odb/core.hxx>
+
+// Test basic object loading functionality.
+//
+#pragma db namespace table("t1_") pointer(std::unique_ptr)
+namespace test1
+{
+ #pragma db object
+ struct object1
+ {
+ object1 (int id_ = 0, int n_ = 0): id (id_), n (n_) {}
+
+ #pragma db id
+ int id;
+
+ int n;
+ };
+
+ #pragma db object
+ struct object2
+ {
+ object2 (int id_ = 0, const char* s_ = ""): id (id_), s (s_) {}
+
+ #pragma db id
+ int id;
+
+ std::string s;
+ };
+
+ #pragma db view object(object1) object(object2: object1::id == object2::id)
+ struct view1
+ {
+ // VC12 workaround (no default move constructor generation).
+ //
+ view1 () {}
+ view1 (view1&& x): o2 (std::move (x.o2)) {}
+
+ std::unique_ptr<object2> o2;
+ };
+
+ #pragma db view object(object1) object(object2: object1::id == object2::id)
+ struct view2
+ {
+ // VC12 workaround (no default move constructor generation).
+ //
+ view2 () {}
+ view2 (view2&& x): o2 (std::move (x.o2)), o1 (std::move (x.o1)) {}
+
+ std::unique_ptr<object2> o2;
+ std::unique_ptr<object1> o1;
+ };
+
+ #pragma db view object(object1 = o1) object(object2 = o2: o1::id == o2::id)
+ struct view3
+ {
+ // VC12 workaround (no default move constructor generation).
+ //
+ view3 () {}
+ view3 (view3&& x): o1 (std::move (x.o1)), o2 (std::move (x.o2)) {}
+
+ std::unique_ptr<object1> o1;
+ std::unique_ptr<object2> o2;
+ };
+
+ #pragma db view object(object1 = o1) object(object2 = o2: o1::id == o2::id)
+ struct view4
+ {
+ // VC12 workaround (no default move constructor generation).
+ //
+ view4 () {}
+ view4 (view4&& x): s (std::move (x.s)),
+ o2 (std::move (x.o2)),
+ id (x.id),
+ o1 (std::move (x.o1)),
+ n (x.n) {}
+
+ std::string s;
+ std::unique_ptr<object2> o2;
+
+ #pragma db column(o1::id)
+ int id;
+
+ std::unique_ptr<object1> o1;
+ int n;
+ };
+
+ #pragma db view \
+ object(object1) \
+ object(object2: object1::id == object2::id) \
+ object(object1 = o1b: object1::id == o1b::n)
+ struct view5
+ {
+ // VC12 workaround (no default move constructor generation).
+ //
+ view5 () {}
+ view5 (view5&& x): o1a (std::move (x.o1a)),
+ o2 (std::move (x.o2)),
+ o1b (std::move (x.o1b)) {}
+
+ std::unique_ptr<object1> o1a;
+ std::unique_ptr<object2> o2;
+ std::unique_ptr<object1> o1b;
+ };
+}
+
+#endif // TEST1_HXX
diff --git a/odb-tests/common/view/olv/test2.hxx b/odb-tests/common/view/olv/test2.hxx
new file mode 100644
index 0000000..a769daa
--- /dev/null
+++ b/odb-tests/common/view/olv/test2.hxx
@@ -0,0 +1,122 @@
+// file : common/view/olv/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+#include <string>
+#include <vector>
+#include <memory> // shared_ptr
+
+#include <odb/core.hxx>
+
+// Test loading of object pointers inside objects.
+//
+#pragma db namespace table("t2_") pointer(std::shared_ptr) session
+namespace test2
+{
+ using std::shared_ptr;
+
+ #pragma db object
+ struct object1
+ {
+ object1 (int n_ = 0): n (n_) {}
+
+ #pragma db id auto
+ int id;
+
+ int n;
+ };
+
+ #pragma db object
+ struct object2
+ {
+ object2 () {}
+ object2 (const char* s_, shared_ptr<object1> o1_): s (s_), o1 (o1_) {}
+
+ #pragma db id auto
+ int id;
+
+ std::string s;
+ shared_ptr<object1> o1;
+ };
+
+ #pragma db view object(object1) object(object2)
+ struct view1
+ {
+ shared_ptr<object2> o2;
+ };
+
+ #pragma db view object(object1) object(object2)
+ struct view2
+ {
+ shared_ptr<object2> o2; // "Unfortunate" order.
+ shared_ptr<object1> o1;
+ };
+
+ #pragma db object
+ struct object3
+ {
+ object3 () {}
+ object3 (shared_ptr<object2> o2_): o2 (o2_) {}
+
+ #pragma db id auto
+ int id;
+
+ shared_ptr<object2> o2;
+ };
+
+ #pragma db view object(object1) object(object2) object(object3)
+ struct view3
+ {
+ shared_ptr<object3> o3; // "Unfortunate" order.
+ shared_ptr<object1> o1;
+ };
+
+ #pragma db object
+ struct object4
+ {
+ #pragma db id auto
+ int id;
+
+ std::vector<shared_ptr<object2>> o2;
+ };
+
+ #pragma db view object(object4)
+ struct view4
+ {
+ shared_ptr<object4> o4;
+ };
+
+ #pragma db view object(object4) object (object2) object(object1)
+ struct view5
+ {
+ shared_ptr<object4> o4; // "Unfortunate" order.
+ shared_ptr<object1> o1;
+ };
+
+ #pragma db object
+ struct object5
+ {
+ object5 () {}
+ object5 (shared_ptr<object1> o1_, shared_ptr<object2> o2_)
+ : o1 (o1_), o2 (o2_) {}
+
+ #pragma db id auto
+ int id;
+
+ shared_ptr<object1> o1;
+ shared_ptr<object2> o2;
+ };
+
+ #pragma db view object(object5) object (object2) \
+ object(object1 = o1a: object2::o1) \
+ object(object1 = o1b: object5::o1)
+ struct view6
+ {
+ shared_ptr<object1> o1a;
+ shared_ptr<object1> o1b;
+ };
+}
+
+#endif // TEST2_HXX
diff --git a/odb-tests/common/view/olv/test3.hxx b/odb-tests/common/view/olv/test3.hxx
new file mode 100644
index 0000000..8cf4344
--- /dev/null
+++ b/odb-tests/common/view/olv/test3.hxx
@@ -0,0 +1,106 @@
+// file : common/view/olv/test3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST3_HXX
+#define TEST3_HXX
+
+#include <string>
+#include <vector>
+#include <memory> // shared_ptr
+
+#include <odb/core.hxx>
+#include <odb/lazy-ptr.hxx>
+
+// Test JOINs for pointed-to objects, existing and automatically added.
+//
+#pragma db namespace table("t3_") pointer(std::shared_ptr) session
+namespace test3
+{
+ using std::shared_ptr;
+
+ struct object2;
+
+ #pragma db object
+ struct object1
+ {
+ object1 (int n_ = 0): n (n_) {}
+
+ #pragma db id auto
+ int id;
+
+ int n;
+
+ #pragma db inverse(o1)
+ odb::lazy_weak_ptr<object2> o2;
+ };
+
+ #pragma db object
+ struct object2
+ {
+ object2 (const char* s_ = ""): s (s_) {}
+
+ #pragma db id auto
+ int id;
+
+ std::string s;
+
+ shared_ptr<object1> o1;
+ };
+
+ #pragma db view object(object1) object(object2)
+ struct view1a // Existing JOIN.
+ {
+ shared_ptr<object1> o1;
+ };
+
+ #pragma db view object(object1)
+ struct view1b // Automatic JOIN.
+ {
+ shared_ptr<object1> o1;
+ };
+
+ // Container case.
+ //
+ struct object4;
+
+ #pragma db object
+ struct object3
+ {
+ object3 (int n_ = 0): n (n_) {}
+
+ #pragma db id auto
+ int id;
+
+ int n;
+
+ #pragma db inverse(o3)
+ odb::lazy_weak_ptr<object4> o4;
+ };
+
+ #pragma db object
+ struct object4
+ {
+ object4 (const char* s_ = ""): s (s_) {}
+
+ #pragma db id auto
+ int id;
+
+ std::string s;
+
+ std::vector<shared_ptr<object3>> o3;
+ };
+
+ #pragma db view object(object3) object(object4 = o4)
+ struct view2a // Existing JOIN.
+ {
+ shared_ptr<object3> o3;
+ };
+
+ #pragma db view object(object3)
+ struct view2b // Automatic JOIN.
+ {
+ shared_ptr<object3> o3;
+ };
+}
+
+#endif // TEST3_HXX
diff --git a/odb-tests/common/view/olv/test4.hxx b/odb-tests/common/view/olv/test4.hxx
new file mode 100644
index 0000000..4d8b804
--- /dev/null
+++ b/odb-tests/common/view/olv/test4.hxx
@@ -0,0 +1,174 @@
+// file : common/view/olv/test4.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST4_HXX
+#define TEST4_HXX
+
+#include <string>
+#include <memory> // shared_ptr
+#include <cassert>
+
+#include <odb/core.hxx>
+
+// Test by-value load.
+//
+#pragma db namespace table("t4_") session
+namespace test4
+{
+ #pragma db object
+ struct object1
+ {
+ object1 (int id_ = 0, int n_ = 0): id (id_), n (n_) {}
+
+ #pragma db id
+ int id;
+
+ int n;
+ };
+
+ #pragma db object
+ struct object2
+ {
+ object2 (int id_ = 0, const char* s_ = "", object1* o1_ = 0)
+ : id (id_), s (s_), o1 (o1_) {}
+
+ #pragma db id
+ int id;
+
+ std::string s;
+ object1* o1; // Shallow copy.
+ };
+
+ typedef object1* object1_ptr;
+ typedef object2* object2_ptr;
+
+ #pragma db view object(object1)
+ struct view1
+ {
+ #pragma db member(o1_) virtual(object1_ptr) get(&this.o1) set()
+
+ #pragma db transient
+ object1 o1;
+ };
+
+ #pragma db view object(object1) transient
+ struct view1a
+ {
+ view1a (): o1_null (true) {}
+
+ #pragma db member(o1_) virtual(object1_ptr) get(&this.o1) \
+ set(this.o1_null = !(?))
+
+ object1 o1;
+ bool o1_null;
+ };
+
+ #pragma db view object(object1)
+ struct view1b
+ {
+ view1b (): o1_p (0) {}
+
+ #pragma db transient
+ object1 o1;
+
+ #pragma db get(&this.o1) set(o1_p = (?))
+ object1* o1_p;
+ };
+
+ #pragma db view object(object1)
+ struct view1c
+ {
+ view1c (object1& o1): o1_p (&o1) {}
+
+ object1* o1_p;
+ };
+
+ #pragma db view object(object1) object(object2) transient
+ struct view2
+ {
+ #pragma db member(o2_) virtual(object2_ptr) get(&this.o2) set()
+ #pragma db member(o1_) virtual(object1_ptr) get(&this.o1) set()
+
+ object1 o1;
+ object2 o2;
+
+ view2 () {}
+ view2 (const view2& v)
+ : o1 (v.o1), o2 (v.o2)
+ {
+ if (v.o2.o1 != 0)
+ {
+ assert (v.o2.o1 == &v.o1);
+ o2.o1 = &o1;
+ }
+ }
+ };
+
+ #pragma db view object(object1) object(object2) transient
+ struct view2a
+ {
+ #pragma db member(o2_) virtual(object2_ptr) get(&this.o2) \
+ set(o2_null = !(?))
+ #pragma db member(o1_) virtual(object1_ptr) get(&this.o1) set()
+
+ object1 o1;
+ object2 o2;
+ bool o2_null;
+
+ view2a () {}
+ view2a (const view2a& v)
+ : o1 (v.o1), o2 (v.o2)
+ {
+ if (v.o2.o1 != 0)
+ {
+ assert (v.o2.o1 == &v.o1);
+ o2.o1 = &o1;
+ }
+ }
+ };
+
+ // Test loading into raw pointer with non-raw object pointer.
+ //
+ using std::shared_ptr;
+
+ #pragma db object pointer(shared_ptr)
+ struct object3
+ {
+ object3 (int id_ = 0, int n_ = 0): id (id_), n (n_) {}
+
+ #pragma db id
+ int id;
+
+ int n;
+ };
+
+ #pragma db view object(object3)
+ struct view3
+ {
+ // This view implements the following slightly twisted logic: if the
+ // object is already in the cache, then set o3_p to that. Otherwise,
+ // load it into the by-value instance. We can also check whether o3_p
+ // points to o3 to distinguish between the two outcomes.
+ //
+
+ // 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_o3 (object3* p) {o3_p = p;} // &o3 or NULL.
+ void set_o3 (shared_ptr<object3> p) {o3_p = p.get ();} // From cache.
+
+ #pragma db get(&this.o3) set(set_o3(?))
+ object3* o3_p;
+
+ #pragma db transient
+ object3 o3;
+
+ // Return-by-value support (query_value()).
+ //
+ view3 (): o3_p (0) {}
+ view3 (const view3& x): o3_p (x.o3_p == &x.o3 ? &o3 : x.o3_p), o3 (x.o3) {}
+ };
+}
+
+#endif // TEST4_HXX
diff --git a/odb-tests/common/view/olv/test5.hxx b/odb-tests/common/view/olv/test5.hxx
new file mode 100644
index 0000000..e3a671b
--- /dev/null
+++ b/odb-tests/common/view/olv/test5.hxx
@@ -0,0 +1,86 @@
+// file : common/view/olv/test5.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST5_HXX
+#define TEST5_HXX
+
+#include <string>
+#include <memory> // shared_ptr
+#include <utility> // pair
+
+#include <odb/core.hxx>
+
+// Test NULL object pointers.
+//
+#pragma db namespace table("t5_") pointer(std::shared_ptr) session
+namespace test5
+{
+ using std::shared_ptr;
+
+ #pragma db object
+ struct object1
+ {
+ object1 (int n_ = 0): n (n_) {}
+
+ #pragma db id auto
+ int id;
+
+ int n;
+ };
+
+ #pragma db object
+ struct object2
+ {
+ object2 () {}
+ object2 (const char* s_, shared_ptr<object1> o1_): s (s_), o1 (o1_) {}
+
+ #pragma db id auto
+ int id;
+
+ std::string s;
+ shared_ptr<object1> o1;
+ };
+
+ #pragma db view object(object1) object(object2)
+ struct view1
+ {
+ shared_ptr<object1> o1;
+ shared_ptr<object2> o2;
+ };
+
+ typedef std::pair<int, int> comp_id;
+ #pragma db value(comp_id)
+
+ #pragma db object
+ struct object3
+ {
+ object3 (comp_id id_ = comp_id (), int n_ = 0): id (id_), n (n_) {}
+
+ #pragma db id
+ comp_id id;
+
+ int n;
+ };
+
+ #pragma db object
+ struct object4
+ {
+ object4 () {}
+ object4 (const char* s_, shared_ptr<object3> o3_): s (s_), o3 (o3_) {}
+
+ #pragma db id auto
+ int id;
+
+ std::string s;
+ shared_ptr<object3> o3;
+ };
+
+ #pragma db view object(object3) object(object4)
+ struct view2
+ {
+ shared_ptr<object4> o4;
+ shared_ptr<object3> o3;
+ };
+}
+
+#endif // TEST5_HXX
diff --git a/odb-tests/common/view/olv/test6.hxx b/odb-tests/common/view/olv/test6.hxx
new file mode 100644
index 0000000..5336fa6
--- /dev/null
+++ b/odb-tests/common/view/olv/test6.hxx
@@ -0,0 +1,57 @@
+// file : common/view/olv/test6.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST6_HXX
+#define TEST6_HXX
+
+#include <string>
+#include <memory> // shared_ptr
+
+#include <odb/core.hxx>
+#include <odb/section.hxx>
+
+// Test interaction with sections.
+//
+#pragma db namespace table("t6_") pointer(std::shared_ptr)
+namespace test6
+{
+ using std::shared_ptr;
+
+ #pragma db object
+ struct object1
+ {
+ object1 (int n_ = 0): n (n_) {}
+
+ #pragma db id auto
+ int id;
+
+ int n;
+ };
+
+ #pragma db object
+ struct object2
+ {
+ object2 () {}
+ object2 (const char* s_, shared_ptr<object1> o1_): s (s_), o1 (o1_) {}
+
+ #pragma db id auto
+ int id;
+
+ std::string s;
+
+ #pragma db load(lazy)
+ odb::section r;
+
+ #pragma db section(r)
+ shared_ptr<object1> o1;
+ };
+
+ #pragma db view object(object1) object(object2)
+ struct view1
+ {
+ shared_ptr<object1> o1;
+ shared_ptr<object2> o2;
+ };
+}
+
+#endif // TEST6_HXX
diff --git a/odb-tests/common/view/olv/test7.hxx b/odb-tests/common/view/olv/test7.hxx
new file mode 100644
index 0000000..dbdc663
--- /dev/null
+++ b/odb-tests/common/view/olv/test7.hxx
@@ -0,0 +1,57 @@
+// file : common/view/olv/test7.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST7_HXX
+#define TEST7_HXX
+
+#include <string>
+#include <memory> // unique_ptr
+#include <utility> // std::move
+
+#include <odb/core.hxx>
+
+// Test explicit conversion to smart pointer member.
+//
+#pragma db namespace table("t7_") pointer(*) session
+namespace test7
+{
+ using std::unique_ptr;
+
+ #pragma db object
+ struct object1
+ {
+ object1 (int n_ = 0): n (n_) {}
+
+ #pragma db id auto
+ int id;
+
+ int n;
+ };
+
+ #pragma db object
+ struct object2
+ {
+ object2 () {}
+ object2 (const char* s_, object1* o1_): s (s_), o1 (o1_) {}
+
+ #pragma db id auto
+ int id;
+
+ std::string s;
+ object1* o1; // Shallow.
+ };
+
+ #pragma db view object(object1) object(object2)
+ struct view1
+ {
+ // VC12 workaround (no default move constructor generation).
+ //
+ view1 () {}
+ view1 (view1&& x): o2 (std::move (x.o2)), o1 (std::move (x.o1)) {}
+
+ unique_ptr<object2> o2;
+ unique_ptr<object1> o1;
+ };
+}
+
+#endif // TEST7_HXX
diff --git a/odb-tests/common/view/olv/test8.hxx b/odb-tests/common/view/olv/test8.hxx
new file mode 100644
index 0000000..607d222
--- /dev/null
+++ b/odb-tests/common/view/olv/test8.hxx
@@ -0,0 +1,54 @@
+// file : common/view/olv/test8.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST8_HXX
+#define TEST8_HXX
+
+#include <string>
+#include <memory> // unique_ptr
+#include <utility> // std::move
+
+#include <odb/core.hxx>
+
+// Test loading objects without id.
+//
+#pragma db namespace table("t8_") pointer(*) session
+namespace test8
+{
+ using std::unique_ptr;
+
+ #pragma db object
+ struct object1
+ {
+ object1 (int n_ = 0): n (n_) {}
+
+ #pragma db id auto
+ int id;
+
+ int n;
+ };
+
+ #pragma db object no_id
+ struct object2
+ {
+ object2 () {}
+ object2 (const char* s_, object1* o1_): s (s_), o1 (o1_) {}
+
+ std::string s;
+ object1* o1; // Shallow.
+ };
+
+ #pragma db view object(object1) object(object2)
+ struct view1
+ {
+ // VC12 workaround (no default move constructor generation).
+ //
+ view1 () {}
+ view1 (view1&& x): o2 (std::move (x.o2)), o1 (std::move (x.o1)) {}
+
+ unique_ptr<object2> o2;
+ unique_ptr<object1> o1;
+ };
+}
+
+#endif // TEST8_HXX
diff --git a/odb-tests/common/view/olv/test9.hxx b/odb-tests/common/view/olv/test9.hxx
new file mode 100644
index 0000000..b109de3
--- /dev/null
+++ b/odb-tests/common/view/olv/test9.hxx
@@ -0,0 +1,78 @@
+// file : common/view/olv/test9.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST9_HXX
+#define TEST9_HXX
+
+#include <string>
+#include <memory> // shared_ptr
+
+#include <odb/core.hxx>
+
+// Test loading polymorphic objects.
+//
+#pragma db namespace table("t9_") session
+namespace test9
+{
+ using std::shared_ptr;
+
+ #pragma db object polymorphic pointer(shared_ptr)
+ struct root
+ {
+ virtual ~root () {}
+ root (int n_ = 0): n (n_) {}
+
+ #pragma db id auto
+ int id;
+
+ int n;
+ };
+
+ #pragma db object
+ struct base: root
+ {
+ base (int n_ = 0, const char* s_ = ""): root (n_), s (s_) {}
+
+ std::string s;
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ derived (int n_ = 0, const char* s_ = "", bool b_ = false)
+ : base (n_, s_), b (b_) {}
+
+ bool b;
+ };
+
+ // Load via root.
+ //
+ #pragma db view object(root)
+ struct view1r
+ {
+ shared_ptr<root> o;
+ int n;
+ };
+
+ // Load via base.
+ //
+ #pragma db view object(base)
+ struct view1b
+ {
+ std::string s;
+ shared_ptr<base> o;
+ int n;
+ };
+
+ // Load via derived.
+ //
+ #pragma db view object(derived)
+ struct view1d
+ {
+ std::string s;
+ shared_ptr<derived> o;
+ int n;
+ };
+}
+
+#endif // TEST9_HXX
diff --git a/odb-tests/common/view/olv/testscript b/odb-tests/common/view/olv/testscript
new file mode 100644
index 0000000..160426d
--- /dev/null
+++ b/odb-tests/common/view/olv/testscript
@@ -0,0 +1,39 @@
+# file : common/view/olv/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../../mysql-schema.testscript
+
+ for s: $schemas
+ cat $out_base/"$s"($multi ? '-mysql' : '').sql | $create_schema_cmd
+ end;
+
+ $* ($multi ? 'mysql' : ) $mysql_options
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../../pgsql-schema.testscript
+
+ for s: $schemas
+ $create_schema_cmd -f $out_base/"$s"($multi ? '-pgsql' : '').sql
+ end;
+
+ $* ($multi ? 'pgsql' : ) $pgsql_options
+}
diff --git a/odb-tests/common/virtual/buildfile b/odb-tests/common/virtual/buildfile
new file mode 100644
index 0000000..96d062e
--- /dev/null
+++ b/odb-tests/common/virtual/buildfile
@@ -0,0 +1,42 @@
+# file : common/virtual/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix virtual_ \
+ --generate-schema \
+ --generate-query \
+ --generate-session
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/virtual/driver.cxx b/odb-tests/common/virtual/driver.cxx
new file mode 100644
index 0000000..f96f543
--- /dev/null
+++ b/odb-tests/common/virtual/driver.cxx
@@ -0,0 +1,154 @@
+// file : common/virtual/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test virtual data members.
+//
+
+#include <memory> // std::auto_ptr
+#include <iostream>
+
+#include <odb/session.hxx>
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Test basic virtual data member functionality.
+ //
+ {
+ using namespace test1;
+
+ object o;
+ o.i (123);
+ o.c1.i = 123;
+ o.c1.s = "abc";
+ o.v1.push_back ("abc");
+ o.v1.push_back ("abd");
+ o.v1.push_back ("abe");
+ o.p1 = new object;
+
+ {
+ transaction t (db->begin ());
+ db->persist (*o.p1);
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id1.v));
+ t.commit ();
+
+ assert (o == *p);
+ }
+ }
+
+ // Test pragma resolution to virtual data member.
+ //
+ {
+ using namespace test2;
+
+ object1 o1 (1);
+ o1.o2 = new object2 (1);
+ o1.o2->o1 = &o1;
+
+ {
+ transaction t (db->begin ());
+ db->persist (*o1.o2);
+ o1.n1 = o1.o2->id;
+ db->persist (o1);
+ t.commit ();
+ }
+
+ {
+ session s;
+ transaction t (db->begin ());
+ unique_ptr<object1> p (db->load<object1> (o1.id));
+ t.commit ();
+
+ assert (p->o2->id == o1.o2->id);
+ }
+
+ {
+ typedef odb::query<view1> query;
+ typedef odb::result<view1> result;
+
+ transaction t (db->begin ());
+ result r (db->query<view1> (query::object2::id == o1.o2->id));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->i == o1.n1);
+ assert (++i == r.end ());
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<view2> query;
+ typedef odb::result<view2> result;
+
+ transaction t (db->begin ());
+ result r (db->query<view2> (query::o2::id == o1.o2->id));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->i == o1.n1);
+ assert (++i == r.end ());
+ t.commit ();
+ }
+
+ {
+ typedef odb::result<view3> result;
+
+ transaction t (db->begin ());
+ result r (db->query<view3> ());
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->i == o1.n1);
+ assert (++i == r.end ());
+ t.commit ();
+ }
+ }
+
+ // Use virtual data members to implement multi-member composite object id.
+ //
+ {
+ using namespace test3;
+
+ person o;
+ o.first_ = "John";
+ o.last_ = "Doe";
+
+ name id;
+ {
+ transaction t (db->begin ());
+ id = db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<person> p (db->load<person> (id));
+ t.commit ();
+
+ assert (o.first_ == p->first_ && o.last_ == p->last_);
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/virtual/test.hxx b/odb-tests/common/virtual/test.hxx
new file mode 100644
index 0000000..2654d09
--- /dev/null
+++ b/odb-tests/common/virtual/test.hxx
@@ -0,0 +1,171 @@
+// file : common/virtual/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <vector>
+
+#include <odb/core.hxx>
+
+// Test basic virtual data member functionality.
+//
+#pragma db namespace table("t1_")
+namespace test1
+{
+ #pragma db value
+ struct comp
+ {
+ int i;
+
+ #pragma db transient
+ std::string s;
+ #pragma db member(s_) virtual(std::string) access(s)
+
+ bool operator== (const comp& v) const
+ {
+ return i == v.i && s == v.s;
+ }
+ };
+
+ #pragma db object transient
+ struct object
+ {
+ object (): p1 (0) {}
+ ~object () {delete p1;}
+
+ struct {unsigned long v;} id1;
+
+ #pragma db id
+ #pragma db member(id) get(id1.v) virtual(unsigned long) set(id1.v)
+ #pragma db member(id) auto
+
+ int i () const {return i1;}
+ void i (int i) {i1 = i;}
+ int i1;
+
+ comp c1;
+ #pragma db member(c) virtual(comp) access(c1)
+
+ typedef std::vector<std::string> strings;
+ strings v1;
+ #pragma db member(v) virtual(strings) access(v1)
+
+ typedef object* object_ptr;
+ object_ptr p1;
+ #pragma db member(p) virtual(object_ptr) access(p1)
+
+ bool operator== (const object& o) const
+ {
+ return id1.v == o.id1.v &&
+ i1 == o.i1 &&
+ c1 == o.c1 &&
+ v1 == o.v1 &&
+ (p1 != 0 ? o.p1 != 0 && *p1 == *o.p1 : o.p1 == 0);
+ }
+ };
+
+ #pragma db member(object::i) virtual(int)
+}
+
+#pragma db member(test1::object::id) column("oid")
+
+// Test pragma resolution to virtual data member.
+//
+#pragma db namespace table("t2_")
+namespace test2
+{
+ struct object1;
+ struct object2;
+
+ typedef object1* object1_ptr;
+ typedef object2* object2_ptr;
+
+ #pragma db object
+ struct object2
+ {
+ object2 (unsigned long i = 0): id (i) {}
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db inverse(o)
+ object1_ptr o1;
+ };
+
+ #pragma db object
+ struct object1
+ {
+ object1 (unsigned long i = 0): id (i), o2 (0) {}
+ ~object1 () {delete o2;}
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db transient
+ object2_ptr o2;
+ #pragma db member(o) virtual(object2_ptr) access(o2)
+
+ #pragma db transient
+ unsigned long n1;
+ #pragma db member(n) virtual(unsigned long) access(n1)
+ #pragma db index member(n)
+ };
+
+ #pragma db view object(object1) object(object2)
+ struct view1
+ {
+ #pragma db column(object1::n)
+ unsigned long i;
+ };
+
+ #pragma db view object(object1 = o1) object(object2 = o2: o1::n == o2::id)
+ struct view2
+ {
+ #pragma db column(o1::n)
+ unsigned long i;
+ };
+
+ #pragma db view object(object1: object1::n != 0)
+ struct view3
+ {
+ #pragma db column(test2::object1::n)
+ unsigned long i;
+ };
+}
+
+// Use virtual data members to implement multi-member composite object id.
+//
+#pragma db namespace table("t3_")
+namespace test3
+{
+ #pragma db value
+ struct name
+ {
+ name () {}
+ name (const std::string& f, const std::string& l)
+ : first (f), last(l) {}
+
+ std::string first;
+ std::string last;
+
+ bool operator< (const name& x) const
+ {
+ return first < x.first || (first == x.first && last < x.last);
+ }
+ };
+
+ #pragma db object transient
+ struct person
+ {
+ std::string first_;
+ std::string last_;
+
+ #pragma db member(name) virtual(name) id \
+ get(::test3::name (this.first_, this.last_)) \
+ set(this.first_ = (?).first; this.last_ = (?).last)
+ };
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/virtual/testscript b/odb-tests/common/virtual/testscript
new file mode 100644
index 0000000..769c7f9
--- /dev/null
+++ b/odb-tests/common/virtual/testscript
@@ -0,0 +1,33 @@
+# file : common/virtual/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/common/wrapper/buildfile b/odb-tests/common/wrapper/buildfile
new file mode 100644
index 0000000..57f43f2
--- /dev/null
+++ b/odb-tests/common/wrapper/buildfile
@@ -0,0 +1,40 @@
+# file : common/wrapper/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libodb = libodb%lib{odb}
+
+libs =
+
+for db: $databases
+ import libs += libodb-$db%lib{odb-$db}
+
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix wrapper_ \
+ --generate-schema
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/wrapper/driver.cxx b/odb-tests/common/wrapper/driver.cxx
new file mode 100644
index 0000000..9c352fc
--- /dev/null
+++ b/odb-tests/common/wrapper/driver.cxx
@@ -0,0 +1,216 @@
+// file : common/wrapper/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test wrapper machinery.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+ // Test 1: simple values.
+ //
+ {
+ using namespace test1;
+
+ unsigned long id1, id2;
+ {
+ object1 o1;
+ object2 o2;
+
+ o1.num.reset (new int (123));
+ o1.nstrs.push_back (nullable_string ());
+ o1.nstrs.push_back (nullable_string ("123"));
+
+ o2.sstrs.push_back (str_sptr ());
+ o2.sstrs.push_back (str_sptr (new string ("123")));
+
+ transaction t (db->begin ());
+ id1 = db->persist (o1);
+ id2 = db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object1> o1 (db->load<object1> (id1));
+ unique_ptr<object2> o2 (db->load<object2> (id2));
+ t.commit ();
+
+ assert (*o1->num == 123);
+ assert (o1->str.get () == 0);
+ assert (o1->nstr.null ());
+ assert (o1->nstrs[0].null ());
+ assert (o1->nstrs[1].get () == "123");
+
+ assert (!o2->sstr);
+ assert (!o2->sstrs[0]);
+ assert (*o2->sstrs[1] == "123");
+ }
+ }
+
+ //
+ // Composite values.
+ //
+ unsigned long id;
+ {
+ comp_object co;
+
+ co.c1.reset (new comp1 ("123", 123));
+ co.vc1.push_back (comp1 ("1", 1));
+ co.vc1.push_back (comp1 ("2", 2));
+ co.vc1.push_back (comp1 ("3", 3));
+
+ co.c2.reset (new comp2 ("123", 123));
+ co.c2->strs.push_back ("1");
+ co.c2->strs.push_back ("2");
+ co.c2->strs.push_back ("3");
+
+ {
+ transaction t (db->begin ());
+ id = db->persist (co);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<comp_object> o (db->load<comp_object> (id));
+ t.commit ();
+
+ assert (*o->c1 == *co.c1);
+ assert (o->vc1 == co.vc1);
+ assert (*o->c2 == *co.c2);
+ }
+ }
+
+ //
+ // Containers.
+ //
+ {
+ cont_object co;
+
+ co.nums.reset (new vector<int>);
+ co.nums->push_back (1);
+ co.nums->push_back (2);
+ co.nums->push_back (3);
+
+ co.c.num = 123;
+ co.c.strs.reset (new vector<string>);
+ co.c.strs->push_back ("1");
+ co.c.strs->push_back ("2");
+ co.c.strs->push_back ("3");
+
+ {
+ transaction t (db->begin ());
+ id = db->persist (co);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<cont_object> o (db->load<cont_object> (id));
+ t.commit ();
+
+ assert (*o->nums == *co.nums);
+ assert (o->c == co.c);
+ }
+ }
+
+ // Test 5: composite NULL values.
+ //
+ {
+ using namespace test5;
+
+ object o1, o2;
+
+ o1.v.push_back (nullable<comp> ());
+
+ o2.p.reset (new comp (1, "a"));
+ o2.n = comp (2, "b");
+ o2.v.push_back (comp (3, "c"));
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p1 (db->load<object> (o1.id));
+ unique_ptr<object> p2 (db->load<object> (o2.id));
+ t.commit ();
+
+ assert (p1->p.get () == 0);
+ assert (!p1->n);
+ assert (!p1->v[0]);
+
+ assert (p2->p.get () != 0 && *p2->p == *o2.p);
+ assert (p2->n && *p2->n == *o2.n);
+ assert (p2->v[0] && *p2->v[0] == *o2.v[0]);
+ }
+
+ // Update.
+ //
+ {
+ o1.p.reset (new comp (1, "a"));
+ o1.n = comp (2, "b");
+ o1.v[0] = comp (3, "c");
+
+ o2.p.reset ();
+ o2.n.reset ();
+ o2.v[0].reset ();
+
+ transaction t (db->begin ());
+ db->update (o1);
+ db->update (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p1 (db->load<object> (o1.id));
+ unique_ptr<object> p2 (db->load<object> (o2.id));
+ t.commit ();
+
+ assert (p1->p.get () != 0 && *p1->p == *o1.p);
+ assert (p1->n && *p1->n == *o1.n);
+ assert (p1->v[0] && *p1->v[0] == *o1.v[0]);
+
+ assert (p2->p.get () == 0);
+ assert (!p2->n);
+ assert (!p2->v[0]);
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/wrapper/test.hxx b/odb-tests/common/wrapper/test.hxx
new file mode 100644
index 0000000..91f5758
--- /dev/null
+++ b/odb-tests/common/wrapper/test.hxx
@@ -0,0 +1,214 @@
+// file : common/wrapper/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <memory> // std::unique_ptr
+#include <vector>
+
+#include <odb/core.hxx>
+#include <odb/nullable.hxx>
+
+using odb::nullable;
+
+// Test 1: simple values.
+//
+#pragma db namespace table("t1_")
+namespace test1
+{
+ typedef nullable<std::string> nullable_string;
+
+ typedef std::unique_ptr<int> num_uptr;
+ typedef std::unique_ptr<std::string> str_uptr;
+ typedef std::shared_ptr<std::string> str_sptr;
+
+ #pragma db object table("obj1")
+ struct object1
+ {
+ #pragma db id auto
+ unsigned long id_;
+
+ num_uptr num;
+
+ #pragma db null
+ str_uptr str;
+
+ nullable_string nstr;
+ std::vector<nullable_string> nstrs;
+ };
+
+ #pragma db object
+ struct object2
+ {
+ #pragma db id auto
+ unsigned long id_;
+
+ #pragma db null
+ str_sptr sstr;
+
+ #pragma db value_null
+ std::vector<str_sptr> sstrs;
+ };
+}
+
+//
+// Composite values.
+//
+
+#pragma db value
+struct comp1
+{
+ comp1 (): num (0) {}
+ comp1 (const std::string& s, int n): str (s), num (n) {}
+
+ std::string str;
+ int num;
+};
+
+inline bool
+operator== (const comp1& x, const comp1& y)
+{
+ return x.str == y.str && x.num == y.num;
+}
+
+
+#pragma db value
+struct comp2
+{
+ comp2 () {}
+ comp2 (const std::string& s, int n): str (s), num (n) {}
+
+ std::string str;
+ int num;
+
+ std::vector<std::string> strs;
+};
+
+inline bool
+operator== (const comp2& x, const comp2& y)
+{
+ return x.str == y.str && x.num == y.num && x.strs == y.strs;
+}
+
+struct comp3;
+
+typedef std::unique_ptr<comp1> comp1_uptr;
+typedef std::unique_ptr<comp2> comp2_uptr;
+typedef std::unique_ptr<comp3> comp3_uptr;
+
+#pragma db object
+struct comp_object
+{
+ #pragma db id auto
+ unsigned long id_;
+
+ comp1_uptr c1; // Wrapped comp value.
+ std::vector<nullable<comp1> > vc1; // Container of wrapped comp values.
+ comp2_uptr c2; // Container inside wrapped comp value.
+};
+
+// This one is just a compilation test to cover more convolute cases.
+//
+#pragma db value
+struct comp3: comp2
+{
+ comp1_uptr c1;
+ std::vector<nullable<comp1> > vc1;
+};
+
+#pragma db object
+struct comp_object2
+{
+ #pragma db id auto
+ unsigned long id_;
+
+ comp3_uptr c3;
+};
+
+//
+// Containers.
+//
+
+typedef std::unique_ptr<std::vector<int>> nums_uptr;
+typedef std::unique_ptr<std::vector<std::string>> strs_uptr;
+
+#pragma db value
+struct cont_comp
+{
+ int num;
+ strs_uptr strs;
+};
+
+inline bool
+operator== (const cont_comp& x, const cont_comp& y)
+{
+ return x.num == y.num && *x.strs == *y.strs;
+}
+
+#pragma db object
+struct cont_object
+{
+ #pragma db id auto
+ unsigned long id_;
+
+ nums_uptr nums; // Wrapped container.
+ cont_comp c; // Wrapped container in comp value.
+};
+
+// Test composite NULL values.
+//
+#pragma db namespace table("t5_")
+namespace test5
+{
+ #pragma db value
+ struct base
+ {
+ base () {}
+ base (int n): num (n) {}
+
+ int num = 0;
+ };
+
+ inline bool
+ operator== (const base& x, const base& y)
+ {
+ return x.num == y.num;
+ }
+
+ #pragma db value
+ struct comp: base
+ {
+ comp () {}
+ comp (int n, const std::string s): base (n), str (s), extra (n + 1) {}
+
+ std::string str;
+ base extra;
+
+ odb::nullable<int> always_null;
+ };
+
+ inline bool
+ operator== (const comp& x, const comp& y)
+ {
+ return static_cast<const base&> (x) == y &&
+ x.str == y.str && x.extra == y.extra;
+ }
+
+ #pragma db object
+ struct object
+ {
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db null
+ std::unique_ptr<comp> p;
+
+ odb::nullable<comp> n;
+
+ std::vector< odb::nullable<comp> > v;
+ };
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/wrapper/testscript b/odb-tests/common/wrapper/testscript
new file mode 100644
index 0000000..6630813
--- /dev/null
+++ b/odb-tests/common/wrapper/testscript
@@ -0,0 +1,33 @@
+# file : common/wrapper/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if $pgsql
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+ $*
+}
diff --git a/odb-tests/database-options.testscript b/odb-tests/database-options.testscript
new file mode 100644
index 0000000..0e97522
--- /dev/null
+++ b/odb-tests/database-options.testscript
@@ -0,0 +1,75 @@
+# file : database-options.testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+# For the enabled databases create the test driver option lists (*_options)
+# for subsequent use in the tests. Also create the database-specific client
+# option lists (*_client_options) and command lines (*_client_cmd) which can
+# be used as a base for the data manipulation commands.
+#
++if $mysql
+ mysql_options = --user $config.odb_tests.mysql.user \
+ --database $config.odb_tests.mysql.database
+
+ mysql_client_options = --user $config.odb_tests.mysql.user \
+ --database $config.odb_tests.mysql.database
+
+ if $defined(config.odb_tests.mysql.passwd)
+ mysql_options += --password $config.odb_tests.mysql.passwd
+ mysql_client_options += --password=$config.odb_tests.mysql.passwd
+ end
+
+ if $defined(config.odb_tests.mysql.host)
+ mysql_options += --host $config.odb_tests.mysql.host
+ mysql_client_options += --host $config.odb_tests.mysql.host
+ end
+
+ if $defined(config.odb_tests.mysql.port)
+ mysql_options += --port $config.odb_tests.mysql.port
+ mysql_client_options += --port $config.odb_tests.mysql.port
+ end
+
+ if $defined(config.odb_tests.mysql.socket)
+ mysql_options += --socket $config.odb_tests.mysql.socket
+ mysql_client_options += --socket $config.odb_tests.mysql.socket
+ end
+
+ mysql_client_cmd = $path($mysql_client) $mysql_client_options
+end
+
++if $sqlite
+ sqlite_options = --database odb-test.db
+
+ # Note that we currently don't manipulate the data using the sqlite3
+ # utility. Thus, we don't create the sqlite client option list and command
+ # line.
+ #
+end
+
++if $pgsql
+ pgsql_options = --user $config.odb_tests.pgsql.user \
+ --database $config.odb_tests.pgsql.database
+
+ pgsql_client_options = --quiet \
+ --set ON_ERROR_STOP=1 \
+ --username $config.odb_tests.pgsql.user \
+ --dbname $config.odb_tests.pgsql.database
+
+ if $defined(config.odb_tests.pgsql.host)
+ pgsql_options += --host $config.odb_tests.pgsql.host
+ pgsql_client_options += --host $config.odb_tests.pgsql.host
+ end
+
+ if $defined(config.odb_tests.pgsql.port)
+ pgsql_options += --port $config.odb_tests.pgsql.port
+ pgsql_client_options += --port $config.odb_tests.pgsql.port
+ end
+
+ pgsql_client_cmd = $path($pgsql_client) $pgsql_client_options
+
+ export PGOPTIONS=--client-min-messages=warning
+end
+
+# Set the default schema file name, which can be overridden by the subsequent
+# tests in their own scopes.
+#
+schema = test
diff --git a/odb-tests/evolution/.gitignore b/odb-tests/evolution/.gitignore
new file mode 100644
index 0000000..9a410c7
--- /dev/null
+++ b/odb-tests/evolution/.gitignore
@@ -0,0 +1,15 @@
+# ODB-generated files.
+#
+test1-odb.?xx
+test1.sql
+model.xml
+
+test2-odb.?xx
+test2.sql
+
+test3-odb.?xx
+test3.sql
+test3-002-post.sql
+test3-002-pre.sql
+test3-003-post.sql
+test3-003-pre.sql
diff --git a/odb-tests/evolution/add-column/buildfile b/odb-tests/evolution/add-column/buildfile
new file mode 100644
index 0000000..efa6b6d
--- /dev/null
+++ b/odb-tests/evolution/add-column/buildfile
@@ -0,0 +1,63 @@
+# file : evolution/add-column/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+ assert (!$multi) "multi-database mode is not supported by this test"
+
+db = ($databases[0])
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-$db%lib{odb-$db}
+import libs += lib{common}
+
+hdrs = test1 test2 test3
+
+exe{driver}: {hxx cxx}{* -*-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+for h: $hdrs
+ exe{driver}: {hxx ixx cxx}{$h-odb}: hxx{$h} libue{test-meta} hxx{model}
+
+# Make sure testN.hxx are compiled serially since they share the changelog.
+#
+# @@ TODO: make order-only when supported by build2.
+#
+{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix evo_add_c_ \
+ --schema-version-table evo_add_c_sv \
+ --generate-schema \
+ --generate-query \
+ --at-once \
+ --changelog $out_base/model.xml
+
+<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog
+<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration
+
+<{hxx ixx cxx}{test3-odb}>:
+{
+ odb_options += --omit-create
+ schema_versions = 002 003
+}
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
+
+testscript@./:
+{
+ db = $db
+ schemas = $hdrs
+}
diff --git a/odb-tests/evolution/add-column/driver.cxx b/odb-tests/evolution/add-column/driver.cxx
new file mode 100644
index 0000000..c729732
--- /dev/null
+++ b/odb-tests/evolution/add-column/driver.cxx
@@ -0,0 +1,130 @@
+// file : evolution/add-column/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test adding a new column.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test2.hxx"
+#include "test3.hxx"
+#include "test2-odb.hxx"
+#include "test3-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv, false));
+
+ db->schema_version_table ("evo_add_c_sv");
+
+ bool embedded (schema_catalog::exists (*db));
+
+ // 1 - base version
+ // 2 - migration
+ // 3 - current version
+ //
+ unsigned short pass (*argv[argc - 1] - '0');
+
+ switch (pass)
+ {
+ case 1:
+ {
+ using namespace v2;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::drop_schema (*db);
+ schema_catalog::create_schema (*db, "", false);
+ schema_catalog::migrate_schema (*db, 2);
+ t.commit ();
+ }
+
+ object o (1);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+ break;
+ }
+ case 2:
+ {
+ using namespace v3;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_pre (*db, 3);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+
+ assert (!p->str);
+ assert (p->num == 999);
+
+ // Migration.
+ //
+ p->str = "abc";
+ p->num = 123;
+ db->update (*p);
+
+ t.commit ();
+ }
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_post (*db, 3);
+ t.commit ();
+ }
+ break;
+ }
+ case 3:
+ {
+ using namespace v3;
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+
+ assert (p->str && *p->str == "abc");
+ assert (p->num == 123);
+
+ t.commit ();
+ }
+ break;
+ }
+ default:
+ {
+ cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl;
+ return 1;
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/evolution/add-column/model.hxx b/odb-tests/evolution/add-column/model.hxx
new file mode 100644
index 0000000..6ac9160
--- /dev/null
+++ b/odb-tests/evolution/add-column/model.hxx
@@ -0,0 +1,38 @@
+// file : evolution/add-column/model.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef MODEL_VERSION
+# error model.hxx included directly
+#endif
+
+#include <string>
+
+#include <odb/core.hxx>
+#include <odb/nullable.hxx>
+
+#pragma db model version(1, MODEL_VERSION)
+
+#define MODEL_NAMESPACE_IMPL(V) v##V
+#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V)
+
+namespace MODEL_NAMESPACE(MODEL_VERSION)
+{
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+#if MODEL_VERSION == 3
+ odb::nullable<std::string> str;
+
+ #pragma db default(999)
+ unsigned long num;
+#endif
+ };
+}
+
+#undef MODEL_NAMESPACE
+#undef MODEL_NAMESPACE_IMPL
diff --git a/odb-tests/evolution/add-column/test1.hxx b/odb-tests/evolution/add-column/test1.hxx
new file mode 100644
index 0000000..b0d7fda
--- /dev/null
+++ b/odb-tests/evolution/add-column/test1.hxx
@@ -0,0 +1,9 @@
+// file : evolution/add-column/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+#pragma db model version(1, 1)
+
+#endif // TEST1_HXX
diff --git a/odb-tests/evolution/add-column/test2.hxx b/odb-tests/evolution/add-column/test2.hxx
new file mode 100644
index 0000000..b62530a
--- /dev/null
+++ b/odb-tests/evolution/add-column/test2.hxx
@@ -0,0 +1,11 @@
+// file : evolution/add-column/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+#define MODEL_VERSION 2
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST2_HXX
diff --git a/odb-tests/evolution/add-column/test3.hxx b/odb-tests/evolution/add-column/test3.hxx
new file mode 100644
index 0000000..b24dba1
--- /dev/null
+++ b/odb-tests/evolution/add-column/test3.hxx
@@ -0,0 +1,11 @@
+// file : evolution/add-column/test3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST3_HXX
+#define TEST3_HXX
+
+#define MODEL_VERSION 3
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST3_HXX
diff --git a/odb-tests/evolution/add-column/testscript b/odb-tests/evolution/add-column/testscript
new file mode 100644
index 0000000..f4e3d99
--- /dev/null
+++ b/odb-tests/evolution/add-column/testscript
@@ -0,0 +1,54 @@
+# file : evolution/add-column/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../$db-schema.testscript
+
+test.arguments += $($(db)_options)
+
+: basics
+:
+if! $sqlite
+{
+ ss =; # Schema modification base file names.
+
+ # Drop everything.
+ #
+ for s: $schemas
+ ss =+ $s
+ end;
+
+ # Add base schema.
+ #
+ ss += test3-002-pre test3-002-post;
+
+ # Add migration.
+ #
+ ss += test3-003-pre test3-003-post;
+
+ # Run tests.
+ #
+ for s: $ss
+ f = $out_base/"$s".sql
+
+ if $mysql
+ cat $f | $create_schema_cmd
+ elif $pgsql
+ $create_schema_cmd -f $f
+ end
+
+ if ($s == 'test3-002-post')
+ $* 1
+ elif ($s == 'test3-003-pre')
+ $* 2
+ elif ($s == 'test3-003-post')
+ $* 3
+ end
+ end
+}
+else
+{
+ $* 1 &odb-test.db;
+ $* 2;
+ $* 3
+}
diff --git a/odb-tests/evolution/add-foreign-key/buildfile b/odb-tests/evolution/add-foreign-key/buildfile
new file mode 100644
index 0000000..0fe5468
--- /dev/null
+++ b/odb-tests/evolution/add-foreign-key/buildfile
@@ -0,0 +1,64 @@
+# file : evolution/add-foreign-key/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+ assert (!$multi) "multi-database mode is not supported by this test"
+
+db = ($databases[0])
+
+import libodb = libodb%lib{odb}
+import libodb_db = libodb-$db%lib{odb-$db}
+import libcommon = lib{common}
+
+hdrs = test1 test2 test3
+
+exe{driver}: {hxx cxx}{* -*-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb and libcommon
+# libraries are resolved for the odb_compile ad hoc rule (see build/root.build
+# for details).
+#
+libue{test-meta}: $libodb $libcommon
+
+for h: $hdrs
+ exe{driver}: {hxx ixx cxx}{$h-odb}: hxx{$h} libue{test-meta} hxx{model}
+
+# Make sure testN.hxx are compiled serially since they share the changelog.
+#
+# @@ TODO: make order-only when supported by build2.
+#
+{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb}
+
+exe{driver}: libue{test-meta} $libodb_db
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix evo_add_fk_ \
+ --schema-version-table evo_add_fk_sv \
+ --generate-schema \
+ --generate-query \
+ --at-once \
+ --changelog $out_base/model.xml \
+ --fkeys-deferrable-mode not_deferrable
+
+<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog
+<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration
+
+<{hxx ixx cxx}{test3-odb}>:
+{
+ odb_options += --omit-create
+ schema_versions = 002 003
+}
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
+
+testscript@./:
+{
+ db = $db
+ schemas = $hdrs
+}
diff --git a/odb-tests/evolution/add-foreign-key/driver.cxx b/odb-tests/evolution/add-foreign-key/driver.cxx
new file mode 100644
index 0000000..dae9a9c
--- /dev/null
+++ b/odb-tests/evolution/add-foreign-key/driver.cxx
@@ -0,0 +1,178 @@
+// file : evolution/add-foreign-key/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test adding a foreign key.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
+#include <libcommon/config.hxx> // DATABASE_XXX
+#include <libcommon/common.hxx>
+
+#include "test2.hxx"
+#include "test3.hxx"
+#include "test2-odb.hxx"
+#include "test3-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv, false));
+
+ db->schema_version_table ("evo_add_fk_sv");
+
+ bool embedded (schema_catalog::exists (*db));
+
+ // 1 - base version
+ // 2 - migration
+ // 3 - current version
+ //
+ unsigned short pass (*argv[argc - 1] - '0');
+
+ switch (pass)
+ {
+ case 1:
+ {
+ using namespace v2;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::drop_schema (*db);
+ schema_catalog::create_schema (*db, "", false);
+ schema_catalog::migrate_schema (*db, 2);
+ t.commit ();
+ }
+
+ object o (1);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+ break;
+ }
+ case 2:
+ {
+ using namespace v3;
+
+#ifdef DATABASE_SQLITE
+ // In SQLite we can only add foreign keys inline in the column
+ // definition.
+ //
+ db->connection ()->execute ("PRAGMA foreign_keys=OFF");
+#endif
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_pre (*db, 3);
+ t.commit ();
+ }
+
+ // Both pointers are now NULL.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+
+ assert (p->o1 == 0 && p->o2 == 0);
+
+ // Migration. The foreign key constraint is not yet there.
+ //
+ p->o1 = new object1 (1);
+ p->o2 = new object2 (1);
+ db->update (*p);
+
+ t.commit ();
+ }
+
+ // Migration. Add the missing objects.
+ //
+ object1 o1 (1);
+ object2 o2 (1);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_post (*db, 3);
+ t.commit ();
+ }
+ break;
+ }
+ case 3:
+ {
+ using namespace v3;
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->o1->id_ == 1);
+ assert (p->o2->id_ == 1);
+ t.commit ();
+ }
+
+ // Now the foreign key constraint is there.
+ //
+ try
+ {
+ object o (2);
+ o.o1 = new object1 (2);
+ o.o2 = new object2 (2);
+
+ transaction t (db->begin ());
+ db->persist (o);
+ assert (false);
+ }
+ catch (const odb::exception& ) {}
+
+ // As well as the NOT NULL.
+ //
+#ifndef DATABASE_SQLITE
+ try
+ {
+ object o (3);
+ o.o2 = 0;
+
+ transaction t (db->begin ());
+ db->persist (o);
+ assert (false);
+ }
+ catch (const odb::exception& ) {}
+#endif
+ break;
+ }
+ default:
+ {
+ cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl;
+ return 1;
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/evolution/add-foreign-key/model.hxx b/odb-tests/evolution/add-foreign-key/model.hxx
new file mode 100644
index 0000000..17cd85f
--- /dev/null
+++ b/odb-tests/evolution/add-foreign-key/model.hxx
@@ -0,0 +1,66 @@
+// file : evolution/add-foreign-key/model.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef MODEL_VERSION
+# error model.hxx included directly
+#endif
+
+#include <string>
+
+#include <odb/core.hxx>
+
+#include <libcommon/config.hxx> // DATABASE_XXX
+
+#pragma db model version(1, MODEL_VERSION)
+
+#define MODEL_NAMESPACE_IMPL(V) v##V
+#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V)
+
+namespace MODEL_NAMESPACE(MODEL_VERSION)
+{
+#if MODEL_VERSION == 3
+ #pragma db object
+ struct object1
+ {
+ object1 (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+ };
+
+ #pragma db object
+ struct object2
+ {
+ object2 (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+ };
+#endif
+
+ #pragma db object
+ struct object
+ {
+ #pragma db id
+ unsigned long id_;
+
+#if MODEL_VERSION == 2
+ object (unsigned long id = 0): id_ (id) {}
+#else
+ object1* o1;
+
+ // There is no support for changing a column to NOT NULL in SQLite.
+ //
+#ifndef ODB_DATABASE_SQLITE
+ #pragma db not_null
+#endif
+ object2* o2;
+
+ object (unsigned long id = 0): id_ (id), o1 (0), o2 (0) {}
+ ~object () {delete o1; delete o2;}
+#endif
+ };
+}
+
+#undef MODEL_NAMESPACE
+#undef MODEL_NAMESPACE_IMPL
diff --git a/odb-tests/evolution/add-foreign-key/test1.hxx b/odb-tests/evolution/add-foreign-key/test1.hxx
new file mode 100644
index 0000000..05c78c3
--- /dev/null
+++ b/odb-tests/evolution/add-foreign-key/test1.hxx
@@ -0,0 +1,9 @@
+// file : evolution/add-foreign-key/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+#pragma db model version(1, 1)
+
+#endif // TEST1_HXX
diff --git a/odb-tests/evolution/add-foreign-key/test2.hxx b/odb-tests/evolution/add-foreign-key/test2.hxx
new file mode 100644
index 0000000..c57d9a1
--- /dev/null
+++ b/odb-tests/evolution/add-foreign-key/test2.hxx
@@ -0,0 +1,11 @@
+// file : evolution/add-foreign-key/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+#define MODEL_VERSION 2
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST2_HXX
diff --git a/odb-tests/evolution/add-foreign-key/test3.hxx b/odb-tests/evolution/add-foreign-key/test3.hxx
new file mode 100644
index 0000000..c844469
--- /dev/null
+++ b/odb-tests/evolution/add-foreign-key/test3.hxx
@@ -0,0 +1,11 @@
+// file : evolution/add-foreign-key/test3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST3_HXX
+#define TEST3_HXX
+
+#define MODEL_VERSION 3
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST3_HXX
diff --git a/odb-tests/evolution/add-foreign-key/testscript b/odb-tests/evolution/add-foreign-key/testscript
new file mode 100644
index 0000000..5447082
--- /dev/null
+++ b/odb-tests/evolution/add-foreign-key/testscript
@@ -0,0 +1,54 @@
+# file : evolution/add-foreign-key/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../$db-schema.testscript
+
+test.arguments += $($(db)_options)
+
+: basics
+:
+if! $sqlite
+{
+ ss =; # Schema modification base file names.
+
+ # Drop everything.
+ #
+ for s: $schemas
+ ss =+ $s
+ end;
+
+ # Add base schema.
+ #
+ ss += test3-002-pre test3-002-post;
+
+ # Add migration.
+ #
+ ss += test3-003-pre test3-003-post;
+
+ # Run tests.
+ #
+ for s: $ss
+ f = $out_base/"$s".sql
+
+ if $mysql
+ cat $f | $create_schema_cmd
+ elif $pgsql
+ $create_schema_cmd -f $f
+ end
+
+ if ($s == 'test3-002-post')
+ $* 1
+ elif ($s == 'test3-003-pre')
+ $* 2
+ elif ($s == 'test3-003-post')
+ $* 3
+ end
+ end
+}
+else
+{
+ $* 1 &odb-test.db;
+ $* 2;
+ $* 3
+}
diff --git a/odb-tests/evolution/add-index/buildfile b/odb-tests/evolution/add-index/buildfile
new file mode 100644
index 0000000..9835d1e
--- /dev/null
+++ b/odb-tests/evolution/add-index/buildfile
@@ -0,0 +1,63 @@
+# file : evolution/add-index/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+ assert (!$multi) "multi-database mode is not supported by this test"
+
+db = ($databases[0])
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-$db%lib{odb-$db}
+import libs += lib{common}
+
+hdrs = test1 test2 test3
+
+exe{driver}: {hxx cxx}{* -*-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+for h: $hdrs
+ exe{driver}: {hxx ixx cxx}{$h-odb}: hxx{$h} libue{test-meta} hxx{model}
+
+# Make sure testN.hxx are compiled serially since they share the changelog.
+#
+# @@ TODO: make order-only when supported by build2.
+#
+{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix evo_add_i_ \
+ --schema-version-table evo_add_i_sv \
+ --generate-schema \
+ --generate-query \
+ --at-once \
+ --changelog $out_base/model.xml
+
+<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog
+<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration
+
+<{hxx ixx cxx}{test3-odb}>:
+{
+ odb_options += --omit-create
+ schema_versions = 002 003
+}
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
+
+testscript@./:
+{
+ db = $db
+ schemas = $hdrs
+}
diff --git a/odb-tests/evolution/add-index/driver.cxx b/odb-tests/evolution/add-index/driver.cxx
new file mode 100644
index 0000000..679aa4c
--- /dev/null
+++ b/odb-tests/evolution/add-index/driver.cxx
@@ -0,0 +1,170 @@
+// file : evolution/add-index/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test adding a new index.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test2.hxx"
+#include "test3.hxx"
+#include "test2-odb.hxx"
+#include "test3-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv, false));
+
+ db->schema_version_table ("evo_add_i_sv");
+
+ bool embedded (schema_catalog::exists (*db));
+
+ // 1 - base version
+ // 2 - migration
+ // 3 - current version
+ //
+ unsigned short pass (*argv[argc - 1] - '0');
+
+ switch (pass)
+ {
+ case 1:
+ {
+ using namespace v2;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::drop_schema (*db);
+ schema_catalog::create_schema (*db, "", false);
+ schema_catalog::migrate_schema (*db, 2);
+ t.commit ();
+ }
+
+ object o0 (0);
+ o0.num = 123;
+
+ object o1 (1);
+ o1.num = 234;
+
+ object o2 (2);
+ o2.num = 234;
+
+ // Duplicates are ok.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o0);
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+ break;
+ }
+ case 2:
+ {
+ using namespace v3;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_pre (*db, 3);
+ t.commit ();
+ }
+
+ object o3 (3);
+ o3.num = 234;
+
+ // Duplicates are still ok but we need to remove them before the
+ // post migration step.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o3);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (
+ "ORDER BY" + query::num + "," + query::id));
+
+ unsigned long prev (0);
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ if (i->num == prev)
+ db->erase (*i);
+
+ prev = i->num;
+ }
+
+ t.commit ();
+ }
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_post (*db, 3);
+ t.commit ();
+ }
+ break;
+ }
+ case 3:
+ {
+ using namespace v3;
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p0 (db->load<object> (0));
+ unique_ptr<object> p1 (db->load<object> (1));
+
+ assert (p0->num == 123);
+ assert (p1->num == 234);
+
+ t.commit ();
+ }
+
+ try
+ {
+ object o2 (2);
+ o2.num = 234;
+
+ transaction t (db->begin ());
+ db->persist (o2);
+ assert (false);
+ }
+ catch (const odb::exception& ) {}
+
+ break;
+ }
+ default:
+ {
+ cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl;
+ return 1;
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/evolution/add-index/model.hxx b/odb-tests/evolution/add-index/model.hxx
new file mode 100644
index 0000000..fec75cc
--- /dev/null
+++ b/odb-tests/evolution/add-index/model.hxx
@@ -0,0 +1,33 @@
+// file : evolution/add-index/model.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef MODEL_VERSION
+# error model.hxx included directly
+#endif
+
+#include <odb/core.hxx>
+
+#pragma db model version(1, MODEL_VERSION)
+
+#define MODEL_NAMESPACE_IMPL(V) v##V
+#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V)
+
+namespace MODEL_NAMESPACE(MODEL_VERSION)
+{
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+#if MODEL_VERSION == 3
+ #pragma db unique
+#endif
+ unsigned long num;
+ };
+}
+
+#undef MODEL_NAMESPACE
+#undef MODEL_NAMESPACE_IMPL
diff --git a/odb-tests/evolution/add-index/test1.hxx b/odb-tests/evolution/add-index/test1.hxx
new file mode 100644
index 0000000..1be2b5b
--- /dev/null
+++ b/odb-tests/evolution/add-index/test1.hxx
@@ -0,0 +1,9 @@
+// file : evolution/add-index/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+#pragma db model version(1, 1)
+
+#endif // TEST1_HXX
diff --git a/odb-tests/evolution/add-index/test2.hxx b/odb-tests/evolution/add-index/test2.hxx
new file mode 100644
index 0000000..a0faca9
--- /dev/null
+++ b/odb-tests/evolution/add-index/test2.hxx
@@ -0,0 +1,11 @@
+// file : evolution/add-index/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+#define MODEL_VERSION 2
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST2_HXX
diff --git a/odb-tests/evolution/add-index/test3.hxx b/odb-tests/evolution/add-index/test3.hxx
new file mode 100644
index 0000000..aab9c86
--- /dev/null
+++ b/odb-tests/evolution/add-index/test3.hxx
@@ -0,0 +1,11 @@
+// file : evolution/add-index/test3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST3_HXX
+#define TEST3_HXX
+
+#define MODEL_VERSION 3
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST3_HXX
diff --git a/odb-tests/evolution/add-index/testscript b/odb-tests/evolution/add-index/testscript
new file mode 100644
index 0000000..ceb17ff
--- /dev/null
+++ b/odb-tests/evolution/add-index/testscript
@@ -0,0 +1,54 @@
+# file : evolution/add-index/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../$db-schema.testscript
+
+test.arguments += $($(db)_options)
+
+: basics
+:
+if! $sqlite
+{
+ ss =; # Schema modification base file names.
+
+ # Drop everything.
+ #
+ for s: $schemas
+ ss =+ $s
+ end;
+
+ # Add base schema.
+ #
+ ss += test3-002-pre test3-002-post;
+
+ # Add migration.
+ #
+ ss += test3-003-pre test3-003-post;
+
+ # Run tests.
+ #
+ for s: $ss
+ f = $out_base/"$s".sql
+
+ if $mysql
+ cat $f | $create_schema_cmd
+ elif $pgsql
+ $create_schema_cmd -f $f
+ end
+
+ if ($s == 'test3-002-post')
+ $* 1
+ elif ($s == 'test3-003-pre')
+ $* 2
+ elif ($s == 'test3-003-post')
+ $* 3
+ end
+ end
+}
+else
+{
+ $* 1 &odb-test.db;
+ $* 2;
+ $* 3
+}
diff --git a/odb-tests/evolution/add-table/buildfile b/odb-tests/evolution/add-table/buildfile
new file mode 100644
index 0000000..d17cd70
--- /dev/null
+++ b/odb-tests/evolution/add-table/buildfile
@@ -0,0 +1,63 @@
+# file : evolution/add-table/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+ assert (!$multi) "multi-database mode is not supported by this test"
+
+db = ($databases[0])
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-$db%lib{odb-$db}
+import libs += lib{common}
+
+hdrs = test1 test2 test3
+
+exe{driver}: {hxx cxx}{* -*-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+for h: $hdrs
+ exe{driver}: {hxx ixx cxx}{$h-odb}: hxx{$h} libue{test-meta} hxx{model}
+
+# Make sure testN.hxx are compiled serially since they share the changelog.
+#
+# @@ TODO: make order-only when supported by build2.
+#
+{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix evo_add_t_ \
+ --schema-version-table evo_add_t_sv \
+ --generate-schema \
+ --generate-query \
+ --at-once \
+ --changelog $out_base/model.xml
+
+<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog
+<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration
+
+<{hxx ixx cxx}{test3-odb}>:
+{
+ odb_options += --omit-create
+ schema_versions = 002 003
+}
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
+
+testscript@./:
+{
+ db = $db
+ schemas = $hdrs
+}
diff --git a/odb-tests/evolution/add-table/driver.cxx b/odb-tests/evolution/add-table/driver.cxx
new file mode 100644
index 0000000..36f4d15
--- /dev/null
+++ b/odb-tests/evolution/add-table/driver.cxx
@@ -0,0 +1,145 @@
+// file : evolution/add-table/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test adding a new table (object, container).
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
+#include <libcommon/config.hxx> // DATABASE_XXX
+#include <libcommon/common.hxx>
+
+#include "test2.hxx"
+#include "test3.hxx"
+#include "test2-odb.hxx"
+#include "test3-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv, false));
+
+ db->schema_version_table ("evo_add_t_sv");
+
+ bool embedded (schema_catalog::exists (*db));
+
+ // 1 - base version
+ // 2 - migration
+ // 3 - current version
+ //
+ unsigned short pass (*argv[argc - 1] - '0');
+
+ switch (pass)
+ {
+ case 1:
+ {
+ using namespace v2;
+
+ if (embedded)
+ {
+ // SQLite has broken foreign keys when it comes to DDL.
+ //
+#ifdef DATABASE_SQLITE
+ db->connection ()->execute ("PRAGMA foreign_keys=OFF");
+#endif
+ transaction t (db->begin ());
+ schema_catalog::drop_schema (*db);
+ schema_catalog::create_schema (*db, "", false);
+ schema_catalog::migrate_schema (*db, 2);
+ t.commit ();
+
+#ifdef DATABASE_SQLITE
+ db->connection ()->execute ("PRAGMA foreign_keys=ON");
+#endif
+ }
+
+ object o (1);
+ o.str = "abc";
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+ break;
+ }
+ case 2:
+ {
+ using namespace v3;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_pre (*db, 3);
+ t.commit ();
+ }
+
+ object1 o1;
+ o1.nums.push_back (1);
+ o1.nums.push_back (2);
+ o1.nums.push_back (3);
+
+ {
+ transaction t (db->begin ());
+ o1.o = db->load<object> (1);
+ db->persist (o1);
+ t.commit ();
+ }
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_post (*db, 3);
+ t.commit ();
+ }
+ break;
+ }
+ case 3:
+ {
+ using namespace v3;
+
+ typedef odb::query<object1> query;
+ typedef odb::result<object1> result;
+
+ {
+ transaction t (db->begin ());
+
+ result r (db->query<object1> (query::o->str == "abc"));
+ result::iterator i (r.begin ()), e (r.end ());
+
+ assert (i != e &&
+ i->o->id_ == 1 &&
+ i->nums[0] == 1 && i->nums[1] == 2 && i->nums[2] == 3);
+ assert (++i == e);
+
+ t.commit ();
+ }
+
+ break;
+ }
+ default:
+ {
+ cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl;
+ return 1;
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/evolution/add-table/model.hxx b/odb-tests/evolution/add-table/model.hxx
new file mode 100644
index 0000000..208a156
--- /dev/null
+++ b/odb-tests/evolution/add-table/model.hxx
@@ -0,0 +1,48 @@
+// file : evolution/add-table/model.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef MODEL_VERSION
+# error model.hxx included directly
+#endif
+
+#include <vector>
+#include <string>
+
+#include <odb/core.hxx>
+
+#pragma db model version(1, MODEL_VERSION)
+
+#define MODEL_NAMESPACE_IMPL(V) v##V
+#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V)
+
+namespace MODEL_NAMESPACE(MODEL_VERSION)
+{
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string str;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db object
+ struct object1
+ {
+ object1 (): o (0) {}
+ ~object1 () {delete o;}
+
+ #pragma db id auto
+ unsigned long id_;
+
+ object* o;
+ std::vector<int> nums;
+ };
+#endif
+}
+
+#undef MODEL_NAMESPACE
+#undef MODEL_NAMESPACE_IMPL
diff --git a/odb-tests/evolution/add-table/test1.hxx b/odb-tests/evolution/add-table/test1.hxx
new file mode 100644
index 0000000..adc51a1
--- /dev/null
+++ b/odb-tests/evolution/add-table/test1.hxx
@@ -0,0 +1,9 @@
+// file : evolution/add-table/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+#pragma db model version(1, 1)
+
+#endif // TEST1_HXX
diff --git a/odb-tests/evolution/add-table/test2.hxx b/odb-tests/evolution/add-table/test2.hxx
new file mode 100644
index 0000000..ca03cef
--- /dev/null
+++ b/odb-tests/evolution/add-table/test2.hxx
@@ -0,0 +1,11 @@
+// file : evolution/add-table/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+#define MODEL_VERSION 2
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST2_HXX
diff --git a/odb-tests/evolution/add-table/test3.hxx b/odb-tests/evolution/add-table/test3.hxx
new file mode 100644
index 0000000..39406a2
--- /dev/null
+++ b/odb-tests/evolution/add-table/test3.hxx
@@ -0,0 +1,11 @@
+// file : evolution/add-table/test3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST3_HXX
+#define TEST3_HXX
+
+#define MODEL_VERSION 3
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST3_HXX
diff --git a/odb-tests/evolution/add-table/testscript b/odb-tests/evolution/add-table/testscript
new file mode 100644
index 0000000..9317e37
--- /dev/null
+++ b/odb-tests/evolution/add-table/testscript
@@ -0,0 +1,54 @@
+# file : evolution/add-table/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../$db-schema.testscript
+
+test.arguments += $($(db)_options)
+
+: basics
+:
+if! $sqlite
+{
+ ss =; # Schema modification base file names.
+
+ # Drop everything.
+ #
+ for s: $schemas
+ ss =+ $s
+ end;
+
+ # Add base schema.
+ #
+ ss += test3-002-pre test3-002-post;
+
+ # Add migration.
+ #
+ ss += test3-003-pre test3-003-post;
+
+ # Run tests.
+ #
+ for s: $ss
+ f = $out_base/"$s".sql
+
+ if $mysql
+ cat $f | $create_schema_cmd
+ elif $pgsql
+ $create_schema_cmd -f $f
+ end
+
+ if ($s == 'test3-002-post')
+ $* 1
+ elif ($s == 'test3-003-pre')
+ $* 2
+ elif ($s == 'test3-003-post')
+ $* 3
+ end
+ end
+}
+else
+{
+ $* 1 &odb-test.db;
+ $* 2;
+ $* 3
+}
diff --git a/odb-tests/evolution/alter-column/buildfile b/odb-tests/evolution/alter-column/buildfile
new file mode 100644
index 0000000..08b0ed7
--- /dev/null
+++ b/odb-tests/evolution/alter-column/buildfile
@@ -0,0 +1,63 @@
+# file : evolution/alter-column/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+ assert (!$multi) "multi-database mode is not supported by this test"
+
+db = ($databases[0])
+
+import libodb = libodb%lib{odb}
+import libodb_db = libodb-$db%lib{odb-$db}
+import libcommon = lib{common}
+
+hdrs = test1 test2 test3
+
+exe{driver}: {hxx cxx}{* -*-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb and libcommon
+# libraries are resolved for the odb_compile ad hoc rule (see build/root.build
+# for details).
+#
+libue{test-meta}: $libodb $libcommon
+
+for h: $hdrs
+ exe{driver}: {hxx ixx cxx}{$h-odb}: hxx{$h} libue{test-meta} hxx{model}
+
+# Make sure testN.hxx are compiled serially since they share the changelog.
+#
+# @@ TODO: make order-only when supported by build2.
+#
+{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb}
+
+exe{driver}: libue{test-meta} $libodb_db
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix evo_alter_c_ \
+ --schema-version-table evo_alter_c_sv \
+ --generate-schema \
+ --generate-query \
+ --at-once \
+ --changelog $out_base/model.xml
+
+<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog
+<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration
+
+<{hxx ixx cxx}{test3-odb}>:
+{
+ odb_options += --omit-create
+ schema_versions = 002 003
+}
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
+
+testscript@./:
+{
+ db = $db
+ schemas = $hdrs
+}
diff --git a/odb-tests/evolution/alter-column/driver.cxx b/odb-tests/evolution/alter-column/driver.cxx
new file mode 100644
index 0000000..f307546
--- /dev/null
+++ b/odb-tests/evolution/alter-column/driver.cxx
@@ -0,0 +1,164 @@
+// file : evolution/alter-column/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test altering a column.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test2.hxx"
+#include "test3.hxx"
+#include "test2-odb.hxx"
+#include "test3-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv, false));
+
+ db->schema_version_table ("evo_alter_c_sv");
+
+ // SQLite doesn't support altering of columns.
+ //
+#ifndef DATABASE_SQLITE
+ bool embedded (schema_catalog::exists (*db));
+
+ // 1 - base version
+ // 2 - migration
+ // 3 - current version
+ //
+ unsigned short pass (*argv[argc - 1] - '0');
+
+ switch (pass)
+ {
+ case 1:
+ {
+ using namespace v2;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::drop_schema (*db);
+ schema_catalog::create_schema (*db, "", false);
+ schema_catalog::migrate_schema (*db, 2);
+ t.commit ();
+ }
+
+ object o (1);
+ o.num = 123;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+ break;
+ }
+ case 2:
+ {
+ using namespace v3;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_pre (*db, 3);
+ t.commit ();
+ }
+
+ // NULL is already in effect; NOT NULL is not yet.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+
+ assert (!p->str);
+ assert (p->num && *p->num == 123);
+ assert (!p->num1);
+
+ // Migration.
+ //
+ p->str = "abc";
+ p->num.reset ();
+ p->num1 = 123;
+ db->update (*p);
+
+ t.commit ();
+ }
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_post (*db, 3);
+ t.commit ();
+ }
+ break;
+ }
+ case 3:
+ {
+ using namespace v3;
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+
+ assert (p->str && *p->str == "abc");
+ assert (!p->num);
+
+ t.commit ();
+ }
+
+ // NOT NULL is now in effect.
+ //
+ try
+ {
+ object o2 (2);
+ o2.num1 = 234; // str is NULL
+
+ transaction t (db->begin ());
+ db->persist (o2);
+ assert (false);
+ }
+ catch (const odb::exception& ) {}
+
+ try
+ {
+ object o3 (3);
+ o3.str = "bcd"; // num1 is NULL
+
+ transaction t (db->begin ());
+ db->persist (o3);
+ assert (false);
+ }
+ catch (const odb::exception& ) {}
+
+ break;
+ }
+ default:
+ {
+ cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl;
+ return 1;
+ }
+ }
+#endif // DATABASE_SQLITE
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/evolution/alter-column/model.hxx b/odb-tests/evolution/alter-column/model.hxx
new file mode 100644
index 0000000..8378887
--- /dev/null
+++ b/odb-tests/evolution/alter-column/model.hxx
@@ -0,0 +1,56 @@
+// file : evolution/alter-column/model.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef MODEL_VERSION
+# error model.hxx included directly
+#endif
+
+#include <string>
+
+#include <odb/core.hxx>
+#include <odb/nullable.hxx>
+
+#include <libcommon/config.hxx> // DATABASE_XXX
+
+#pragma db model version(1, MODEL_VERSION)
+
+#define MODEL_NAMESPACE_IMPL(V) v##V
+#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V)
+
+namespace MODEL_NAMESPACE(MODEL_VERSION)
+{
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ // SQLite doesn't support altering of columns.
+ //
+#ifndef DATABASE_SQLITE
+#if MODEL_VERSION == 2
+ odb::nullable<std::string> str;
+
+ unsigned long num;
+#else
+ // Use nullable to be able to access during migration.
+ //
+ #pragma db not_null
+ odb::nullable<std::string> str;
+
+ odb::nullable<unsigned long> num;
+
+ // Test adding NOT NULL column. It should be added NULL in pre
+ // and then converted to NOT NULL in post.
+ //
+ #pragma db not_null
+ odb::nullable<unsigned long> num1;
+#endif
+#endif
+ };
+}
+
+#undef MODEL_NAMESPACE
+#undef MODEL_NAMESPACE_IMPL
diff --git a/odb-tests/evolution/alter-column/test1.hxx b/odb-tests/evolution/alter-column/test1.hxx
new file mode 100644
index 0000000..9353558
--- /dev/null
+++ b/odb-tests/evolution/alter-column/test1.hxx
@@ -0,0 +1,9 @@
+// file : evolution/alter-column/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+#pragma db model version(1, 1)
+
+#endif // TEST1_HXX
diff --git a/odb-tests/evolution/alter-column/test2.hxx b/odb-tests/evolution/alter-column/test2.hxx
new file mode 100644
index 0000000..e2dbed3
--- /dev/null
+++ b/odb-tests/evolution/alter-column/test2.hxx
@@ -0,0 +1,11 @@
+// file : evolution/alter-column/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+#define MODEL_VERSION 2
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST2_HXX
diff --git a/odb-tests/evolution/alter-column/test3.hxx b/odb-tests/evolution/alter-column/test3.hxx
new file mode 100644
index 0000000..ac08e2f
--- /dev/null
+++ b/odb-tests/evolution/alter-column/test3.hxx
@@ -0,0 +1,11 @@
+// file : evolution/alter-column/test3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST3_HXX
+#define TEST3_HXX
+
+#define MODEL_VERSION 3
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST3_HXX
diff --git a/odb-tests/evolution/alter-column/testscript b/odb-tests/evolution/alter-column/testscript
new file mode 100644
index 0000000..83e1a0f
--- /dev/null
+++ b/odb-tests/evolution/alter-column/testscript
@@ -0,0 +1,55 @@
+# file : evolution/alter-column/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../$db-schema.testscript
+
+test.arguments += $($(db)_options)
+
+: basics
+:
+if! $sqlite
+{
+ ss =; # Schema modification base file names.
+
+ # Drop everything.
+ #
+ for s: $schemas
+ ss =+ $s
+ end;
+
+ # Add base schema.
+ #
+ ss += test3-002-pre test3-002-post;
+
+ # Add migration.
+ #
+ ss += test3-003-pre test3-003-post;
+
+ # Run tests.
+ #
+ for s: $ss
+ f = $out_base/"$s".sql
+
+ if $mysql
+ cat $f | $create_schema_cmd
+ elif $pgsql
+ $create_schema_cmd -f $f
+ end
+
+ if ($s == 'test3-002-post')
+ $* 1
+ elif ($s == 'test3-003-pre')
+ $* 2
+ elif ($s == 'test3-003-post')
+ $* 3
+ end
+ end
+}
+else
+{
+ # Note that SQLite doesn't support altering of columns and so the driver is
+ # trivial. We, however, still run it once for good measure.
+ #
+ $* 1
+}
diff --git a/odb-tests/evolution/combined/buildfile b/odb-tests/evolution/combined/buildfile
new file mode 100644
index 0000000..0ccce68
--- /dev/null
+++ b/odb-tests/evolution/combined/buildfile
@@ -0,0 +1,64 @@
+# file : evolution/combined/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+ assert (!$multi) "multi-database mode is not supported by this test"
+
+db = ($databases[0])
+
+import libodb = libodb%lib{odb}
+import libodb_db = libodb-$db%lib{odb-$db}
+import libcommon = lib{common}
+
+hdrs = test1 test2 test3
+
+exe{driver}: {hxx cxx}{* -*-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb and libcommon
+# libraries are resolved for the odb_compile ad hoc rule (see build/root.build
+# for details).
+#
+libue{test-meta}: $libodb $libcommon
+
+for h: $hdrs
+ exe{driver}: {hxx ixx cxx}{$h-odb}: hxx{$h} libue{test-meta} hxx{model}
+
+# Make sure testN.hxx are compiled serially since they share the changelog.
+#
+# @@ TODO: make order-only when supported by build2.
+#
+{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb}
+
+exe{driver}: libue{test-meta} $libodb_db
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix evo_comb_ \
+ --schema-version-table evo_comb_sv \
+ --generate-schema \
+ --generate-query \
+ --at-once \
+ --changelog $out_base/model.xml \
+ --sqlite-override-null
+
+<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog
+<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration
+
+<{hxx ixx cxx}{test3-odb}>:
+{
+ odb_options += --omit-create
+ schema_versions = 002 003
+}
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
+
+testscript@./:
+{
+ db = $db
+ schemas = $hdrs
+}
diff --git a/odb-tests/evolution/combined/driver.cxx b/odb-tests/evolution/combined/driver.cxx
new file mode 100644
index 0000000..027bddd
--- /dev/null
+++ b/odb-tests/evolution/combined/driver.cxx
@@ -0,0 +1,162 @@
+// file : evolution/combined/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Combined schema evolution test.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test2.hxx"
+#include "test3.hxx"
+#include "test2-odb.hxx"
+#include "test3-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv, false));
+
+ db->schema_version_table ("evo_comb_sv");
+
+ bool embedded (schema_catalog::exists (*db));
+
+ // 1 - base version
+ // 2 - migration
+ // 3 - current version
+ //
+ unsigned short pass (*argv[argc - 1] - '0');
+
+ switch (pass)
+ {
+ case 1:
+ {
+ using namespace v2;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::drop_schema (*db);
+ schema_catalog::create_schema (*db, "", false);
+ schema_catalog::migrate_schema (*db, 2);
+ t.commit ();
+ }
+
+ object o ("1");
+ o.dui = 1;
+ o.anui = 1;
+ o.dnui = 1;
+ o.dc = 1;
+ o.dt.push_back (1);
+ o.aui = 1;
+
+#ifndef DATABASE_SQLITE
+ o.dfk = new object1 (1);
+ o.acn = 1;
+ o.acnn.reset ();
+ o.afk = 1;
+#endif
+
+ {
+ transaction t (db->begin ());
+#ifndef DATABASE_SQLITE
+ db->persist (o.dfk);
+#endif
+ db->persist (o);
+ t.commit ();
+ }
+ break;
+ }
+ case 2:
+ {
+ using namespace v3;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_pre (*db, 3);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> ("1"));
+
+ assert (p->ac1 == 999);
+ assert (!p->ac2);
+
+#ifndef DATABASE_SQLITE
+ assert (!p->ac3);
+#endif
+ // Migrate.
+ //
+ p->at.push_back ("abc");
+
+#ifndef DATABASE_SQLITE
+ p->dfk = 999;
+ p->ac3 = 1;
+ p->acn.reset ();
+ p->acnn = 1;
+#endif
+ db->update (*p);
+
+ t.commit ();
+ }
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_post (*db, 3);
+ t.commit ();
+ }
+ break;
+ }
+ case 3:
+ {
+ using namespace v3;
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> ("1"));
+
+ // Check post-migration.
+ //
+ assert (p->at[0] == "abc");
+
+#ifndef DATABASE_SQLITE
+ assert (p->dfk == 999);
+ assert (*p->ac3 == 1);
+ assert (!p->acn);
+ assert (*p->acnn == 1);
+#endif
+ t.commit ();
+ }
+ break;
+ }
+ default:
+ {
+ cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl;
+ return 1;
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/evolution/combined/model.hxx b/odb-tests/evolution/combined/model.hxx
new file mode 100644
index 0000000..5f1180b
--- /dev/null
+++ b/odb-tests/evolution/combined/model.hxx
@@ -0,0 +1,174 @@
+// file : evolution/combined/model.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef MODEL_VERSION
+# error model.hxx included directly
+#endif
+
+#include <string>
+#include <vector>
+
+#include <odb/core.hxx>
+#include <odb/nullable.hxx>
+
+#include <libcommon/config.hxx> // DATABASE_XXX
+
+#pragma db model version(1, MODEL_VERSION)
+
+#define MODEL_NAMESPACE_IMPL(V) v##V
+#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V)
+
+namespace MODEL_NAMESPACE(MODEL_VERSION)
+{
+ #pragma db object
+ struct object1
+ {
+ object1 (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+ };
+
+ #pragma db object
+ struct object
+ {
+ #pragma db id
+ std::string id_;
+
+ //
+ // Pre pass 1.
+ //
+
+ // Drop unique index.
+ //
+#if MODEL_VERSION == 2
+ #pragma db unique
+#endif
+ unsigned long dui;
+
+ // Alter table drop foreign key. Not supported by SQLite.
+ //
+#ifndef DATABASE_SQLITE
+#if MODEL_VERSION == 2
+ object1* dfk;
+#else
+ #pragma db null
+ unsigned long dfk;
+#endif
+#endif
+
+ // Add table.
+ //
+#if MODEL_VERSION == 3
+ std::vector<std::string> at;
+#endif
+
+ // Add column.
+ //
+#if MODEL_VERSION == 3
+ #pragma db default(999)
+ unsigned long ac1;
+
+ odb::nullable<unsigned long> ac2;
+
+ // Initially added as NULL, converted to NOT NULL. Not supported by SQLite.
+ //
+#ifndef DATABASE_SQLITE
+ #pragma db not_null
+ odb::nullable<unsigned long> ac3;
+#endif
+#endif
+
+ // Alter column NULL. Not supported by SQLite.
+ //
+#ifndef DATABASE_SQLITE
+#if MODEL_VERSION == 2
+ unsigned long acn;
+#else
+ odb::nullable<unsigned long> acn;
+#endif
+#endif
+
+ //
+ // Pre pass 2.
+ //
+
+ // Add non-unique indexes.
+ //
+#if MODEL_VERSION == 3
+ #pragma db index
+#endif
+ unsigned long anui;
+
+ //
+ // Post pass 1.
+ //
+
+ // Drop non-unique indexes.
+ //
+#if MODEL_VERSION == 2
+ #pragma db index
+#endif
+ unsigned long dnui;
+
+ //
+ // Post pass 2.
+ //
+
+ // Drop table.
+ //
+#if MODEL_VERSION == 2
+ std::vector<unsigned long> dt;
+#endif
+
+ // Drop column. Logical drop (set NULL) in SQLite.
+ //
+#if MODEL_VERSION == 2
+ unsigned long dc;
+#endif
+
+ // Alter column NOT NULL. Not supported by SQLite.
+ //
+#ifndef DATABASE_SQLITE
+#if MODEL_VERSION == 3
+ #pragma db not_null
+#endif
+ odb::nullable<unsigned long> acnn;
+#endif
+
+ // Alter table add foreign key. Not supported by SQLite.
+ //
+#ifndef DATABASE_SQLITE
+#if MODEL_VERSION == 2
+ #pragma db null
+ unsigned long afk;
+#else
+ object1* afk;
+#endif
+#endif
+
+ // Add unique index.
+ //
+#if MODEL_VERSION == 3
+ #pragma db unique
+#endif
+ unsigned long aui;
+
+ public:
+#ifndef DATABASE_SQLITE
+#if MODEL_VERSION == 2
+
+ object (std::string id = ""): id_ (id), dfk (0) {}
+ ~object () {delete dfk;}
+#else
+ object (std::string id = ""): id_ (id), afk (0) {}
+ ~object () {delete afk;}
+#endif
+#else
+ object (std::string id = ""): id_ (id) {}
+#endif
+ };
+}
+
+#undef MODEL_NAMESPACE
+#undef MODEL_NAMESPACE_IMPL
diff --git a/odb-tests/evolution/combined/test1.hxx b/odb-tests/evolution/combined/test1.hxx
new file mode 100644
index 0000000..1a18aff
--- /dev/null
+++ b/odb-tests/evolution/combined/test1.hxx
@@ -0,0 +1,9 @@
+// file : evolution/combined/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+#pragma db model version(1, 1)
+
+#endif // TEST1_HXX
diff --git a/odb-tests/evolution/combined/test2.hxx b/odb-tests/evolution/combined/test2.hxx
new file mode 100644
index 0000000..8eb7ac8
--- /dev/null
+++ b/odb-tests/evolution/combined/test2.hxx
@@ -0,0 +1,11 @@
+// file : evolution/combined/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+#define MODEL_VERSION 2
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST2_HXX
diff --git a/odb-tests/evolution/combined/test3.hxx b/odb-tests/evolution/combined/test3.hxx
new file mode 100644
index 0000000..05a052f
--- /dev/null
+++ b/odb-tests/evolution/combined/test3.hxx
@@ -0,0 +1,11 @@
+// file : evolution/combined/test3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST3_HXX
+#define TEST3_HXX
+
+#define MODEL_VERSION 3
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST3_HXX
diff --git a/odb-tests/evolution/combined/testscript b/odb-tests/evolution/combined/testscript
new file mode 100644
index 0000000..a7bb826
--- /dev/null
+++ b/odb-tests/evolution/combined/testscript
@@ -0,0 +1,54 @@
+# file : evolution/combined/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../$db-schema.testscript
+
+test.arguments += $($(db)_options)
+
+: basics
+:
+if! $sqlite
+{
+ ss =; # Schema modification base file names.
+
+ # Drop everything.
+ #
+ for s: $schemas
+ ss =+ $s
+ end;
+
+ # Add base schema.
+ #
+ ss += test3-002-pre test3-002-post;
+
+ # Add migration.
+ #
+ ss += test3-003-pre test3-003-post;
+
+ # Run tests.
+ #
+ for s: $ss
+ f = $out_base/"$s".sql
+
+ if $mysql
+ cat $f | $create_schema_cmd
+ elif $pgsql
+ $create_schema_cmd -f $f
+ end
+
+ if ($s == 'test3-002-post')
+ $* 1
+ elif ($s == 'test3-003-pre')
+ $* 2
+ elif ($s == 'test3-003-post')
+ $* 3
+ end
+ end
+}
+else
+{
+ $* 1 &odb-test.db;
+ $* 2;
+ $* 3
+}
diff --git a/odb-tests/evolution/data/buildfile b/odb-tests/evolution/data/buildfile
new file mode 100644
index 0000000..d013cab
--- /dev/null
+++ b/odb-tests/evolution/data/buildfile
@@ -0,0 +1,63 @@
+# file : evolution/data/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+ assert (!$multi) "multi-database mode is not supported by this test"
+
+db = ($databases[0])
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-$db%lib{odb-$db}
+import libs += lib{common}
+
+hdrs = test1 test2 test3
+
+exe{driver}: {hxx cxx}{* -*-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+for h: $hdrs
+ exe{driver}: {hxx ixx cxx}{$h-odb}: hxx{$h} libue{test-meta} hxx{model}
+
+# Make sure testN.hxx are compiled serially since they share the changelog.
+#
+# @@ TODO: make order-only when supported by build2.
+#
+{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix evo_data_ \
+ --schema-version-table evo_data_sv \
+ --generate-schema \
+ --generate-query \
+ --at-once \
+ --changelog $out_base/model.xml
+
+<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog
+<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration
+
+<{hxx ixx cxx}{test3-odb}>:
+{
+ odb_options += --omit-create
+ schema_versions = 002 003
+}
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
+
+testscript@./:
+{
+ db = $db
+ schemas = $hdrs
+}
diff --git a/odb-tests/evolution/data/driver.cxx b/odb-tests/evolution/data/driver.cxx
new file mode 100644
index 0000000..966c72e
--- /dev/null
+++ b/odb-tests/evolution/data/driver.cxx
@@ -0,0 +1,179 @@
+// file : evolution/data/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test data migration support.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
+#include <libcommon/config.hxx> // DATABASE_XXX
+#include <libcommon/common.hxx>
+
+#include "test2.hxx"
+#include "test3.hxx"
+#include "test2-odb.hxx"
+#include "test3-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+void
+migrate1 (database& db)
+{
+ using namespace v2;
+ using namespace v3;
+
+ unique_ptr<object1> o1 (db.load<object1> (1));
+ object2 o2 (1);
+ o2.num = o1->num;
+ db.persist (o2);
+}
+
+void
+migrate2 (database& db)
+{
+ using namespace v2;
+ using namespace v3;
+
+ unique_ptr<object1> o1 (db.load<object1> (2));
+ object2 o2 (2);
+ o2.num = o1->num;
+ db.persist (o2);
+}
+
+static const data_migration_entry<3, 1> migrate2_entry (&migrate2);
+
+int
+main (int argc, char* argv[])
+{
+ schema_catalog::data_migration_function<3, 1> (&migrate1);
+
+ schema_catalog::data_migration_function<3, 1> (
+ [] (database& db)
+ {
+ using namespace v2;
+ using namespace v3;
+
+ unique_ptr<object1> o1 (db.load<object1> (11));
+ object2 o2 (11);
+ o2.num = o1->num;
+ db.persist (o2);
+ });
+
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv, false));
+
+ db->schema_version_table ("evo_data_sv");
+
+ bool embedded (schema_catalog::exists (*db));
+
+ // 1 - base version
+ // 2 - migration
+ // 3 - current version
+ //
+ unsigned short pass (*argv[argc - 1] - '0');
+
+ switch (pass)
+ {
+ case 1:
+ {
+ using namespace v2;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::drop_schema (*db);
+ schema_catalog::create_schema (*db, "", false);
+ schema_catalog::migrate_schema (*db, 2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+
+ {
+ object1 o1 (1);
+ o1.num = 123;
+ db->persist (o1);
+ }
+
+ {
+ object1 o1 (2);
+ o1.num = 123;
+ db->persist (o1);
+ }
+
+ {
+ object1 o1 (11);
+ o1.num = 123;
+ db->persist (o1);
+ }
+
+ t.commit ();
+ }
+ break;
+ }
+ case 2:
+ {
+ {
+ transaction t (db->begin ());
+
+ if (embedded)
+ schema_catalog::migrate (*db);
+ else
+ schema_catalog::migrate_data (*db);
+
+ t.commit ();
+ }
+
+ break;
+ }
+ case 3:
+ {
+ using namespace v3;
+
+ {
+ transaction t (db->begin ());
+
+ {
+ unique_ptr<object2> o2 (db->load<object2> (1));
+ assert (o2->num == 123);
+ }
+
+ {
+ unique_ptr<object2> o2 (db->load<object2> (2));
+ assert (o2->num == 123);
+ }
+
+ {
+ unique_ptr<object2> o2 (db->load<object2> (11));
+ assert (o2->num == 123);
+ }
+
+ t.commit ();
+ }
+
+ break;
+ }
+ default:
+ {
+ cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl;
+ return 1;
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/evolution/data/model.hxx b/odb-tests/evolution/data/model.hxx
new file mode 100644
index 0000000..680bc55
--- /dev/null
+++ b/odb-tests/evolution/data/model.hxx
@@ -0,0 +1,45 @@
+// file : evolution/data/model.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef MODEL_VERSION
+# error model.hxx included directly
+#endif
+
+#include <odb/core.hxx>
+
+#pragma db model version(1, MODEL_VERSION)
+
+#define MODEL_NAMESPACE_IMPL(V) v##V
+#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V)
+
+namespace MODEL_NAMESPACE(MODEL_VERSION)
+{
+#if MODEL_VERSION == 2
+ #pragma db object
+ struct object1
+ {
+ object1 (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ int num;
+ };
+#endif
+
+#if MODEL_VERSION == 3
+ #pragma db object
+ struct object2
+ {
+ object2 (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ int num;
+ };
+#endif
+}
+
+#undef MODEL_NAMESPACE
+#undef MODEL_NAMESPACE_IMPL
diff --git a/odb-tests/evolution/data/test1.hxx b/odb-tests/evolution/data/test1.hxx
new file mode 100644
index 0000000..976ed04
--- /dev/null
+++ b/odb-tests/evolution/data/test1.hxx
@@ -0,0 +1,9 @@
+// file : evolution/data/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+#pragma db model version(1, 1)
+
+#endif // TEST1_HXX
diff --git a/odb-tests/evolution/data/test2.hxx b/odb-tests/evolution/data/test2.hxx
new file mode 100644
index 0000000..bd72940
--- /dev/null
+++ b/odb-tests/evolution/data/test2.hxx
@@ -0,0 +1,11 @@
+// file : evolution/data/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+#define MODEL_VERSION 2
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST2_HXX
diff --git a/odb-tests/evolution/data/test3.hxx b/odb-tests/evolution/data/test3.hxx
new file mode 100644
index 0000000..0f47099
--- /dev/null
+++ b/odb-tests/evolution/data/test3.hxx
@@ -0,0 +1,11 @@
+// file : evolution/data/test3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST3_HXX
+#define TEST3_HXX
+
+#define MODEL_VERSION 3
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST3_HXX
diff --git a/odb-tests/evolution/data/testscript b/odb-tests/evolution/data/testscript
new file mode 100644
index 0000000..5c91396
--- /dev/null
+++ b/odb-tests/evolution/data/testscript
@@ -0,0 +1,54 @@
+# file : evolution/data/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../$db-schema.testscript
+
+test.arguments += $($(db)_options)
+
+: basics
+:
+if! $sqlite
+{
+ ss =; # Schema modification base file names.
+
+ # Drop everything.
+ #
+ for s: $schemas
+ ss =+ $s
+ end;
+
+ # Add base schema.
+ #
+ ss += test3-002-pre test3-002-post;
+
+ # Add migration.
+ #
+ ss += test3-003-pre test3-003-post;
+
+ # Run tests.
+ #
+ for s: $ss
+ f = $out_base/"$s".sql
+
+ if $mysql
+ cat $f | $create_schema_cmd
+ elif $pgsql
+ $create_schema_cmd -f $f
+ end
+
+ if ($s == 'test3-002-post')
+ $* 1
+ elif ($s == 'test3-003-pre')
+ $* 2
+ elif ($s == 'test3-003-post')
+ $* 3
+ end
+ end
+}
+else
+{
+ $* 1 &odb-test.db;
+ $* 2;
+ $* 3
+}
diff --git a/odb-tests/evolution/drop-column/buildfile b/odb-tests/evolution/drop-column/buildfile
new file mode 100644
index 0000000..c508f76
--- /dev/null
+++ b/odb-tests/evolution/drop-column/buildfile
@@ -0,0 +1,64 @@
+# file : evolution/drop-column/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+ assert (!$multi) "multi-database mode is not supported by this test"
+
+db = ($databases[0])
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-$db%lib{odb-$db}
+import libs += lib{common}
+
+hdrs = test1 test2 test3
+
+exe{driver}: {hxx cxx}{* -*-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+for h: $hdrs
+ exe{driver}: {hxx ixx cxx}{$h-odb}: hxx{$h} libue{test-meta} hxx{model}
+
+# Make sure testN.hxx are compiled serially since they share the changelog.
+#
+# @@ TODO: make order-only when supported by build2.
+#
+{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix evo_drop_c_ \
+ --schema-version-table evo_drop_c_sv \
+ --generate-schema \
+ --generate-query \
+ --at-once \
+ --changelog $out_base/model.xml \
+ --sqlite-override-null
+
+<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog
+<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration
+
+<{hxx ixx cxx}{test3-odb}>:
+{
+ odb_options += --omit-create
+ schema_versions = 002 003
+}
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
+
+testscript@./:
+{
+ db = $db
+ schemas = $hdrs
+}
diff --git a/odb-tests/evolution/drop-column/driver.cxx b/odb-tests/evolution/drop-column/driver.cxx
new file mode 100644
index 0000000..6f2a735
--- /dev/null
+++ b/odb-tests/evolution/drop-column/driver.cxx
@@ -0,0 +1,131 @@
+// file : evolution/drop-column/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test dropping a column.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test2.hxx"
+#include "test3.hxx"
+#include "test2-odb.hxx"
+#include "test3-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv, false));
+
+ db->schema_version_table ("evo_drop_c_sv");
+
+ bool embedded (schema_catalog::exists (*db));
+
+ // 1 - base version
+ // 2 - migration
+ // 3 - current version
+ //
+ unsigned short pass (*argv[argc - 1] - '0');
+
+ switch (pass)
+ {
+ case 1:
+ {
+ using namespace v2;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::drop_schema (*db);
+ schema_catalog::create_schema (*db, "", false);
+ schema_catalog::migrate_schema (*db, 2);
+ t.commit ();
+ }
+
+ object o (1);
+ o.str = "abc";
+ o.num = 123;
+ o.ptr = new object1 (1, 2);
+
+ {
+ transaction t (db->begin ());
+ db->persist (*o.ptr);
+ db->persist (o);
+ t.commit ();
+ }
+ break;
+ }
+ case 2:
+ {
+ using namespace v3;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_pre (*db, 3);
+ t.commit ();
+ }
+
+ // Things are still there.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+
+ assert (p->str == "abc");
+ assert (p->num == 123);
+ assert (p->ptr->id.x == 1 && p->ptr->id.y == 2);
+
+ t.commit ();
+ }
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_post (*db, 3);
+ t.commit ();
+ }
+ break;
+ }
+ case 3:
+ {
+ using namespace v3;
+
+ // Now they are gone.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->str == "" && p->ptr == 0);
+ db->erase<object1> (value (1, 2)); // SQLite logical delete test.
+ t.commit ();
+ }
+ break;
+ }
+ default:
+ {
+ cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl;
+ return 1;
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/evolution/drop-column/model.hxx b/odb-tests/evolution/drop-column/model.hxx
new file mode 100644
index 0000000..09b63b9
--- /dev/null
+++ b/odb-tests/evolution/drop-column/model.hxx
@@ -0,0 +1,59 @@
+// file : evolution/drop-column/model.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef MODEL_VERSION
+# error model.hxx included directly
+#endif
+
+#include <string>
+
+#include <odb/core.hxx>
+#include <odb/nullable.hxx>
+
+#pragma db model version(1, MODEL_VERSION)
+
+#define MODEL_NAMESPACE_IMPL(V) v##V
+#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V)
+
+namespace MODEL_NAMESPACE(MODEL_VERSION)
+{
+ #pragma db value
+ struct value
+ {
+ value (int x_ = 0, int y_ = 0): x (x_), y (y_) {}
+ int x;
+ int y;
+ };
+
+ #pragma db object
+ struct object1
+ {
+ object1 (int x = 0, int y = 0): id (x, y) {}
+
+ #pragma db id
+ value id;
+ };
+
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id), ptr (0) {}
+ ~object () {delete ptr;}
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string str;
+ unsigned long num;
+ object1* ptr;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::str) deleted(3)
+ #pragma db member(object::num) deleted(3)
+ #pragma db member(object::ptr) deleted(3)
+#endif
+}
+
+#undef MODEL_NAMESPACE
+#undef MODEL_NAMESPACE_IMPL
diff --git a/odb-tests/evolution/drop-column/test1.hxx b/odb-tests/evolution/drop-column/test1.hxx
new file mode 100644
index 0000000..1efb1fa
--- /dev/null
+++ b/odb-tests/evolution/drop-column/test1.hxx
@@ -0,0 +1,9 @@
+// file : evolution/drop-column/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+#pragma db model version(1, 1)
+
+#endif // TEST1_HXX
diff --git a/odb-tests/evolution/drop-column/test2.hxx b/odb-tests/evolution/drop-column/test2.hxx
new file mode 100644
index 0000000..2842cd3
--- /dev/null
+++ b/odb-tests/evolution/drop-column/test2.hxx
@@ -0,0 +1,11 @@
+// file : evolution/drop-column/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+#define MODEL_VERSION 2
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST2_HXX
diff --git a/odb-tests/evolution/drop-column/test3.hxx b/odb-tests/evolution/drop-column/test3.hxx
new file mode 100644
index 0000000..d1ce616
--- /dev/null
+++ b/odb-tests/evolution/drop-column/test3.hxx
@@ -0,0 +1,11 @@
+// file : evolution/drop-column/test3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST3_HXX
+#define TEST3_HXX
+
+#define MODEL_VERSION 3
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST3_HXX
diff --git a/odb-tests/evolution/drop-column/testscript b/odb-tests/evolution/drop-column/testscript
new file mode 100644
index 0000000..abb81a8
--- /dev/null
+++ b/odb-tests/evolution/drop-column/testscript
@@ -0,0 +1,54 @@
+# file : evolution/drop-column/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../$db-schema.testscript
+
+test.arguments += $($(db)_options)
+
+: basics
+:
+if! $sqlite
+{
+ ss =; # Schema modification base file names.
+
+ # Drop everything.
+ #
+ for s: $schemas
+ ss =+ $s
+ end;
+
+ # Add base schema.
+ #
+ ss += test3-002-pre test3-002-post;
+
+ # Add migration.
+ #
+ ss += test3-003-pre test3-003-post;
+
+ # Run tests.
+ #
+ for s: $ss
+ f = $out_base/"$s".sql
+
+ if $mysql
+ cat $f | $create_schema_cmd
+ elif $pgsql
+ $create_schema_cmd -f $f
+ end
+
+ if ($s == 'test3-002-post')
+ $* 1
+ elif ($s == 'test3-003-pre')
+ $* 2
+ elif ($s == 'test3-003-post')
+ $* 3
+ end
+ end
+}
+else
+{
+ $* 1 &odb-test.db;
+ $* 2;
+ $* 3
+}
diff --git a/odb-tests/evolution/drop-foreign-key/buildfile b/odb-tests/evolution/drop-foreign-key/buildfile
new file mode 100644
index 0000000..6d1441e
--- /dev/null
+++ b/odb-tests/evolution/drop-foreign-key/buildfile
@@ -0,0 +1,64 @@
+# file : evolution/drop-foreign-key/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+ assert (!$multi) "multi-database mode is not supported by this test"
+
+db = ($databases[0])
+
+import libodb = libodb%lib{odb}
+import libodb_db = libodb-$db%lib{odb-$db}
+import libcommon = lib{common}
+
+hdrs = test1 test2 test3
+
+exe{driver}: {hxx cxx}{* -*-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb and libcommon
+# libraries are resolved for the odb_compile ad hoc rule (see build/root.build
+# for details).
+#
+libue{test-meta}: $libodb $libcommon
+
+for h: $hdrs
+ exe{driver}: {hxx ixx cxx}{$h-odb}: hxx{$h} libue{test-meta} hxx{model}
+
+# Make sure testN.hxx are compiled serially since they share the changelog.
+#
+# @@ TODO: make order-only when supported by build2.
+#
+{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb}
+
+exe{driver}: libue{test-meta} $libodb_db
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix evo_drop_fk_ \
+ --schema-version-table evo_drop_fk_sv \
+ --generate-schema \
+ --generate-query \
+ --at-once \
+ --changelog $out_base/model.xml \
+ --fkeys-deferrable-mode not_deferrable
+
+<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog
+<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration
+
+<{hxx ixx cxx}{test3-odb}>:
+{
+ odb_options += --omit-create
+ schema_versions = 002 003
+}
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
+
+testscript@./:
+{
+ db = $db
+ schemas = $hdrs
+}
diff --git a/odb-tests/evolution/drop-foreign-key/driver.cxx b/odb-tests/evolution/drop-foreign-key/driver.cxx
new file mode 100644
index 0000000..c2829c4
--- /dev/null
+++ b/odb-tests/evolution/drop-foreign-key/driver.cxx
@@ -0,0 +1,149 @@
+// file : evolution/drop-foreign-key/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test dropping a foreign key.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test2.hxx"
+#include "test3.hxx"
+#include "test2-odb.hxx"
+#include "test3-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv, false));
+
+ db->schema_version_table ("evo_drop_fk_sv");
+
+ // SQLite doesn't support dropping of foreign keys.
+ //
+#ifndef DATABASE_SQLITE
+ bool embedded (schema_catalog::exists (*db));
+
+ // 1 - base version
+ // 2 - migration
+ // 3 - current version
+ //
+ unsigned short pass (*argv[argc - 1] - '0');
+
+ switch (pass)
+ {
+ case 1:
+ {
+ using namespace v2;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::drop_schema (*db);
+ schema_catalog::create_schema (*db, "", false);
+ schema_catalog::migrate_schema (*db, 2);
+ t.commit ();
+ }
+
+ object o (1);
+ o.o1 = new object (2);
+ o.o2 = new object (3);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o.o1);
+ db->persist (o.o2);
+ db->persist (o);
+ t.commit ();
+ }
+
+ // The foreign key constraint is there.
+ //
+ try
+ {
+ object o (11);
+ o.o1 = new object (12);
+ o.o2 = new object (13);
+
+ transaction t (db->begin ());
+ db->persist (o);
+ assert (false);
+ }
+ catch (const odb::exception& ) {}
+ break;
+ }
+ case 2:
+ {
+ using namespace v3;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_pre (*db, 3);
+ t.commit ();
+ }
+
+ // The data is still there but the constraints are gone.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+
+ assert (p->o1 == 2);
+ assert (p->o2 == 3);
+
+ db->erase<object> (p->o1);
+ db->erase<object> (p->o2);
+
+ t.commit ();
+ }
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_post (*db, 3);
+ t.commit ();
+ }
+ break;
+ }
+ case 3:
+ {
+ using namespace v3;
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->o1 == 2);
+ assert (p->o2 == 3);
+ t.commit ();
+ }
+ break;
+ }
+ default:
+ {
+ cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl;
+ return 1;
+ }
+ }
+#endif // DATABASE_SQLITE
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/evolution/drop-foreign-key/model.hxx b/odb-tests/evolution/drop-foreign-key/model.hxx
new file mode 100644
index 0000000..a74d061
--- /dev/null
+++ b/odb-tests/evolution/drop-foreign-key/model.hxx
@@ -0,0 +1,50 @@
+// file : evolution/drop-foreign-key/model.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef MODEL_VERSION
+# error model.hxx included directly
+#endif
+
+#include <string>
+
+#include <odb/core.hxx>
+
+#include <libcommon/config.hxx> // DATABASE_XXX
+
+#pragma db model version(1, MODEL_VERSION)
+
+#define MODEL_NAMESPACE_IMPL(V) v##V
+#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V)
+
+namespace MODEL_NAMESPACE(MODEL_VERSION)
+{
+ #pragma db object
+ struct object
+ {
+ #pragma db id
+ unsigned long id_;
+
+ // SQLite doesn't support dropping of foreign keys.
+ //
+#ifndef DATABASE_SQLITE
+#if MODEL_VERSION == 2
+ object* o1;
+ object* o2;
+
+ object (unsigned long id = 0): id_ (id), o1 (0), o2 (0) {}
+ ~object () {delete o1; delete o2;}
+#else
+ #pragma db null
+ unsigned long o1;
+
+ #pragma db null
+ unsigned long o2;
+
+ object (unsigned long id = 0): id_ (id) {}
+#endif
+#endif
+ };
+}
+
+#undef MODEL_NAMESPACE
+#undef MODEL_NAMESPACE_IMPL
diff --git a/odb-tests/evolution/drop-foreign-key/test1.hxx b/odb-tests/evolution/drop-foreign-key/test1.hxx
new file mode 100644
index 0000000..5476796
--- /dev/null
+++ b/odb-tests/evolution/drop-foreign-key/test1.hxx
@@ -0,0 +1,9 @@
+// file : evolution/drop-foreign-key/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+#pragma db model version(1, 1)
+
+#endif // TEST1_HXX
diff --git a/odb-tests/evolution/drop-foreign-key/test2.hxx b/odb-tests/evolution/drop-foreign-key/test2.hxx
new file mode 100644
index 0000000..f091a25
--- /dev/null
+++ b/odb-tests/evolution/drop-foreign-key/test2.hxx
@@ -0,0 +1,11 @@
+// file : evolution/drop-foreign-key/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+#define MODEL_VERSION 2
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST2_HXX
diff --git a/odb-tests/evolution/drop-foreign-key/test3.hxx b/odb-tests/evolution/drop-foreign-key/test3.hxx
new file mode 100644
index 0000000..beb8c42
--- /dev/null
+++ b/odb-tests/evolution/drop-foreign-key/test3.hxx
@@ -0,0 +1,11 @@
+// file : evolution/drop-foreign-key/test3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST3_HXX
+#define TEST3_HXX
+
+#define MODEL_VERSION 3
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST3_HXX
diff --git a/odb-tests/evolution/drop-foreign-key/testscript b/odb-tests/evolution/drop-foreign-key/testscript
new file mode 100644
index 0000000..637093b
--- /dev/null
+++ b/odb-tests/evolution/drop-foreign-key/testscript
@@ -0,0 +1,55 @@
+# file : evolution/drop-foreign-key/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../$db-schema.testscript
+
+test.arguments += $($(db)_options)
+
+: basics
+:
+if! $sqlite
+{
+ ss =; # Schema modification base file names.
+
+ # Drop everything.
+ #
+ for s: $schemas
+ ss =+ $s
+ end;
+
+ # Add base schema.
+ #
+ ss += test3-002-pre test3-002-post;
+
+ # Add migration.
+ #
+ ss += test3-003-pre test3-003-post;
+
+ # Run tests.
+ #
+ for s: $ss
+ f = $out_base/"$s".sql
+
+ if $mysql
+ cat $f | $create_schema_cmd
+ elif $pgsql
+ $create_schema_cmd -f $f
+ end
+
+ if ($s == 'test3-002-post')
+ $* 1
+ elif ($s == 'test3-003-pre')
+ $* 2
+ elif ($s == 'test3-003-post')
+ $* 3
+ end
+ end
+}
+else
+{
+ # Note that SQLite doesn't support dropping of foreign keys and so the
+ # driver is trivial. We, however, still run it once for good measure.
+ #
+ $* 1
+}
diff --git a/odb-tests/evolution/drop-index/buildfile b/odb-tests/evolution/drop-index/buildfile
new file mode 100644
index 0000000..16cf57e
--- /dev/null
+++ b/odb-tests/evolution/drop-index/buildfile
@@ -0,0 +1,63 @@
+# file : evolution/drop-index/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+ assert (!$multi) "multi-database mode is not supported by this test"
+
+db = ($databases[0])
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-$db%lib{odb-$db}
+import libs += lib{common}
+
+hdrs = test1 test2 test3
+
+exe{driver}: {hxx cxx}{* -*-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+for h: $hdrs
+ exe{driver}: {hxx ixx cxx}{$h-odb}: hxx{$h} libue{test-meta} hxx{model}
+
+# Make sure testN.hxx are compiled serially since they share the changelog.
+#
+# @@ TODO: make order-only when supported by build2.
+#
+{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix evo_drop_i_ \
+ --schema-version-table evo_drop_i_sv \
+ --generate-schema \
+ --generate-query \
+ --at-once \
+ --changelog $out_base/model.xml
+
+<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog
+<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration
+
+<{hxx ixx cxx}{test3-odb}>:
+{
+ odb_options += --omit-create
+ schema_versions = 002 003
+}
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
+
+testscript@./:
+{
+ db = $db
+ schemas = $hdrs
+}
diff --git a/odb-tests/evolution/drop-index/driver.cxx b/odb-tests/evolution/drop-index/driver.cxx
new file mode 100644
index 0000000..515591d
--- /dev/null
+++ b/odb-tests/evolution/drop-index/driver.cxx
@@ -0,0 +1,159 @@
+// file : evolution/drop-index/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test dropping an index.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test2.hxx"
+#include "test3.hxx"
+#include "test2-odb.hxx"
+#include "test3-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv, false));
+
+ db->schema_version_table ("evo_drop_i_sv");
+
+ bool embedded (schema_catalog::exists (*db));
+
+ // 1 - base version
+ // 2 - migration
+ // 3 - current version
+ //
+ unsigned short pass (*argv[argc - 1] - '0');
+
+ switch (pass)
+ {
+ case 1:
+ {
+ using namespace v2;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::drop_schema (*db);
+ schema_catalog::create_schema (*db, "", false);
+ schema_catalog::migrate_schema (*db, 2);
+ t.commit ();
+ }
+
+ object o0 (0);
+ o0.num = 123;
+
+ object o1 (1);
+ o1.num = 234;
+
+ object o2 (2);
+ o2.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o0);
+ db->persist (o1);
+ t.commit ();
+ }
+
+ // Duplicates are not ok.
+ //
+ try
+ {
+ transaction t (db->begin ());
+ db->persist (o2);
+ assert (false);
+ }
+ catch (const odb::exception& ) {}
+
+ break;
+ }
+ case 2:
+ {
+ using namespace v3;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_pre (*db, 3);
+ t.commit ();
+ }
+
+ // Duplicates are now ok.
+ //
+ object o2 (2);
+ o2.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o2);
+ t.commit ();
+ }
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_post (*db, 3);
+ t.commit ();
+ }
+ break;
+ }
+ case 3:
+ {
+ using namespace v3;
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p0 (db->load<object> (0));
+ unique_ptr<object> p1 (db->load<object> (1));
+ unique_ptr<object> p2 (db->load<object> (2));
+
+ assert (p0->num == 123);
+ assert (p1->num == 234);
+ assert (p2->num == 234);
+
+ t.commit ();
+ }
+
+ // Duplicates are still ok.
+ //
+ object o3 (3);
+ o3.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o3);
+ t.commit ();
+ }
+
+ break;
+ }
+ default:
+ {
+ cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl;
+ return 1;
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/evolution/drop-index/model.hxx b/odb-tests/evolution/drop-index/model.hxx
new file mode 100644
index 0000000..5e163ca
--- /dev/null
+++ b/odb-tests/evolution/drop-index/model.hxx
@@ -0,0 +1,33 @@
+// file : evolution/drop-index/model.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef MODEL_VERSION
+# error model.hxx included directly
+#endif
+
+#include <odb/core.hxx>
+
+#pragma db model version(1, MODEL_VERSION)
+
+#define MODEL_NAMESPACE_IMPL(V) v##V
+#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V)
+
+namespace MODEL_NAMESPACE(MODEL_VERSION)
+{
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+#if MODEL_VERSION == 2
+ #pragma db unique
+#endif
+ unsigned long num;
+ };
+}
+
+#undef MODEL_NAMESPACE
+#undef MODEL_NAMESPACE_IMPL
diff --git a/odb-tests/evolution/drop-index/test1.hxx b/odb-tests/evolution/drop-index/test1.hxx
new file mode 100644
index 0000000..5a279bb
--- /dev/null
+++ b/odb-tests/evolution/drop-index/test1.hxx
@@ -0,0 +1,9 @@
+// file : evolution/drop-index/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+#pragma db model version(1, 1)
+
+#endif // TEST1_HXX
diff --git a/odb-tests/evolution/drop-index/test2.hxx b/odb-tests/evolution/drop-index/test2.hxx
new file mode 100644
index 0000000..78b89f2
--- /dev/null
+++ b/odb-tests/evolution/drop-index/test2.hxx
@@ -0,0 +1,11 @@
+// file : evolution/drop-index/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+#define MODEL_VERSION 2
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST2_HXX
diff --git a/odb-tests/evolution/drop-index/test3.hxx b/odb-tests/evolution/drop-index/test3.hxx
new file mode 100644
index 0000000..17b670d
--- /dev/null
+++ b/odb-tests/evolution/drop-index/test3.hxx
@@ -0,0 +1,11 @@
+// file : evolution/drop-index/test3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST3_HXX
+#define TEST3_HXX
+
+#define MODEL_VERSION 3
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST3_HXX
diff --git a/odb-tests/evolution/drop-index/testscript b/odb-tests/evolution/drop-index/testscript
new file mode 100644
index 0000000..3bb6271
--- /dev/null
+++ b/odb-tests/evolution/drop-index/testscript
@@ -0,0 +1,54 @@
+# file : evolution/drop-index/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../$db-schema.testscript
+
+test.arguments += $($(db)_options)
+
+: basics
+:
+if! $sqlite
+{
+ ss =; # Schema modification base file names.
+
+ # Drop everything.
+ #
+ for s: $schemas
+ ss =+ $s
+ end;
+
+ # Add base schema.
+ #
+ ss += test3-002-pre test3-002-post;
+
+ # Add migration.
+ #
+ ss += test3-003-pre test3-003-post;
+
+ # Run tests.
+ #
+ for s: $ss
+ f = $out_base/"$s".sql
+
+ if $mysql
+ cat $f | $create_schema_cmd
+ elif $pgsql
+ $create_schema_cmd -f $f
+ end
+
+ if ($s == 'test3-002-post')
+ $* 1
+ elif ($s == 'test3-003-pre')
+ $* 2
+ elif ($s == 'test3-003-post')
+ $* 3
+ end
+ end
+}
+else
+{
+ $* 1 &odb-test.db;
+ $* 2;
+ $* 3
+}
diff --git a/odb-tests/evolution/drop-table/buildfile b/odb-tests/evolution/drop-table/buildfile
new file mode 100644
index 0000000..373ae9f
--- /dev/null
+++ b/odb-tests/evolution/drop-table/buildfile
@@ -0,0 +1,63 @@
+# file : evolution/drop-table/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+ assert (!$multi) "multi-database mode is not supported by this test"
+
+db = ($databases[0])
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-$db%lib{odb-$db}
+import libs += lib{common}
+
+hdrs = test1 test2 test3
+
+exe{driver}: {hxx cxx}{* -*-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+for h: $hdrs
+ exe{driver}: {hxx ixx cxx}{$h-odb}: hxx{$h} libue{test-meta} hxx{model}
+
+# Make sure testN.hxx are compiled serially since they share the changelog.
+#
+# @@ TODO: make order-only when supported by build2.
+#
+{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix evo_drop_t_ \
+ --schema-version-table evo_drop_t_sv \
+ --generate-schema \
+ --generate-query \
+ --at-once \
+ --changelog $out_base/model.xml
+
+<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog
+<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration
+
+<{hxx ixx cxx}{test3-odb}>:
+{
+ odb_options += --omit-create
+ schema_versions = 002 003
+}
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
+
+testscript@./:
+{
+ db = $db
+ schemas = $hdrs
+}
diff --git a/odb-tests/evolution/drop-table/driver.cxx b/odb-tests/evolution/drop-table/driver.cxx
new file mode 100644
index 0000000..8ef2e47
--- /dev/null
+++ b/odb-tests/evolution/drop-table/driver.cxx
@@ -0,0 +1,173 @@
+// file : evolution/drop-table/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test dropping a table (object, container).
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test2.hxx"
+#include "test3.hxx"
+#include "test2-odb.hxx"
+#include "test3-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv, false));
+
+ db->schema_version_table ("evo_drop_t_sv");
+
+ bool embedded (schema_catalog::exists (*db));
+
+ // 1 - base version
+ // 2 - migration
+ // 3 - current version
+ //
+ unsigned short pass (*argv[argc - 1] - '0');
+
+ switch (pass)
+ {
+ case 1:
+ {
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::drop_schema (*db);
+ schema_catalog::create_schema (*db, "", false);
+ schema_catalog::migrate_schema (*db, 2);
+ t.commit ();
+ }
+
+ {
+ using namespace v2;
+
+ object1 o1;
+ o1.o = new object (1);
+ o1.o->str = "abc";
+ o1.nums.push_back (1);
+ o1.nums.push_back (2);
+ o1.nums.push_back (3);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1.o);
+ db->persist (o1);
+ t.commit ();
+ }
+ }
+
+ // Polymorphism test.
+ //
+ {
+ // We have to use v3 here because the discriminator includes
+ // the namespace.
+ //
+ using namespace v3;
+
+ base b (123, "abc");
+ derived d1 (234, "bcd");
+ derived d2 (345, "cde");
+
+ {
+ transaction t (db->begin ());
+ db->persist (b);
+ db->persist (d1);
+ db->persist (d2);
+ t.commit ();
+ }
+ }
+
+ break;
+ }
+ case 2:
+ {
+ using namespace v3;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_pre (*db, 3);
+ t.commit ();
+ }
+
+ // Both object and object1 are still there so we can migrate the data.
+ //
+ typedef odb::query<object1> query;
+ typedef odb::result<object1> result;
+
+ {
+ transaction t (db->begin ());
+
+ result r (db->query<object1> (query::o->str == "abc"));
+ result::iterator i (r.begin ()), e (r.end ());
+
+ assert (i != e &&
+ i->o->id_ == 1 &&
+ i->nums[0] == 1 && i->nums[1] == 2 && i->nums[2] == 3);
+ assert (++i == e);
+
+ t.commit ();
+ }
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_post (*db, 3);
+ t.commit ();
+ }
+
+ break;
+ }
+ case 3:
+ {
+ using namespace v3;
+
+ // Only object is still there.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->str == "abc");
+ t.commit ();
+ }
+
+ // Polymorphism test.
+ //
+ {
+ transaction t (db->begin ());
+ assert (size (db->query<root> ()) == 1);
+ assert (size (db->query<base> ()) == 1);
+ t.commit ();
+ }
+
+ break;
+ }
+ default:
+ {
+ cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl;
+ return 1;
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/evolution/drop-table/model.hxx b/odb-tests/evolution/drop-table/model.hxx
new file mode 100644
index 0000000..2c02d09
--- /dev/null
+++ b/odb-tests/evolution/drop-table/model.hxx
@@ -0,0 +1,94 @@
+// file : evolution/drop-table/model.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef MODEL_VERSION
+# error model.hxx included directly
+#endif
+
+#include <vector>
+#include <string>
+
+#include <odb/core.hxx>
+
+#pragma db model version(1, MODEL_VERSION)
+
+#define MODEL_NAMESPACE_IMPL(V) v##V
+#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V)
+
+namespace MODEL_NAMESPACE(MODEL_VERSION)
+{
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string str;
+ };
+
+ #pragma db object
+ struct object1
+ {
+ object1 (): o (0) {}
+ ~object1 () {delete o;}
+
+ #pragma db id auto
+ unsigned long id_;
+
+ object* o;
+ std::vector<int> nums;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db object(object1) deleted(3)
+#endif
+
+ // Make sure we also clean up base tables when dropping a
+ // table corresponding to the polymorphic derived object.
+ //
+ #pragma db value
+ struct value
+ {
+ value (unsigned long n = 0, const std::string& s = ""): num (n), str (s) {}
+
+ unsigned long num;
+ std::string str;
+ };
+
+ #pragma db object polymorphic
+ struct root
+ {
+ root (unsigned long n = 0, const std::string& s = ""): id (n, s) {}
+ virtual ~root () {}
+
+ #pragma db id
+ value id;
+ };
+
+ #pragma db object
+ struct base: root
+ {
+ base (unsigned long n = 0, const std::string& s = "")
+ : root (n, s), num (n) {}
+
+ unsigned long num;
+ };
+
+ #pragma db object
+ struct derived: base
+ {
+ derived (unsigned long n = 0, const std::string& s = "")
+ : base (n, s), str (s) {}
+
+ std::string str;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db object(derived) deleted(3)
+#endif
+}
+
+#undef MODEL_NAMESPACE
+#undef MODEL_NAMESPACE_IMPL
diff --git a/odb-tests/evolution/drop-table/test1.hxx b/odb-tests/evolution/drop-table/test1.hxx
new file mode 100644
index 0000000..a9ec64c
--- /dev/null
+++ b/odb-tests/evolution/drop-table/test1.hxx
@@ -0,0 +1,9 @@
+// file : evolution/drop-table/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+#pragma db model version(1, 1)
+
+#endif // TEST1_HXX
diff --git a/odb-tests/evolution/drop-table/test2.hxx b/odb-tests/evolution/drop-table/test2.hxx
new file mode 100644
index 0000000..c9ec149
--- /dev/null
+++ b/odb-tests/evolution/drop-table/test2.hxx
@@ -0,0 +1,11 @@
+// file : evolution/drop-table/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+#define MODEL_VERSION 2
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST2_HXX
diff --git a/odb-tests/evolution/drop-table/test3.hxx b/odb-tests/evolution/drop-table/test3.hxx
new file mode 100644
index 0000000..8d3f17b
--- /dev/null
+++ b/odb-tests/evolution/drop-table/test3.hxx
@@ -0,0 +1,11 @@
+// file : evolution/drop-table/test3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST3_HXX
+#define TEST3_HXX
+
+#define MODEL_VERSION 3
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST3_HXX
diff --git a/odb-tests/evolution/drop-table/testscript b/odb-tests/evolution/drop-table/testscript
new file mode 100644
index 0000000..a67ec7d
--- /dev/null
+++ b/odb-tests/evolution/drop-table/testscript
@@ -0,0 +1,54 @@
+# file : evolution/drop-table/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../$db-schema.testscript
+
+test.arguments += $($(db)_options)
+
+: basics
+:
+if! $sqlite
+{
+ ss =; # Schema modification base file names.
+
+ # Drop everything.
+ #
+ for s: $schemas
+ ss =+ $s
+ end;
+
+ # Add base schema.
+ #
+ ss += test3-002-pre test3-002-post;
+
+ # Add migration.
+ #
+ ss += test3-003-pre test3-003-post;
+
+ # Run tests.
+ #
+ for s: $ss
+ f = $out_base/"$s".sql
+
+ if $mysql
+ cat $f | $create_schema_cmd
+ elif $pgsql
+ $create_schema_cmd -f $f
+ end
+
+ if ($s == 'test3-002-post')
+ $* 1
+ elif ($s == 'test3-003-pre')
+ $* 2
+ elif ($s == 'test3-003-post')
+ $* 3
+ end
+ end
+}
+else
+{
+ $* 1 &odb-test.db;
+ $* 2;
+ $* 3
+}
diff --git a/odb-tests/evolution/embedded/buildfile b/odb-tests/evolution/embedded/buildfile
new file mode 100644
index 0000000..095e476
--- /dev/null
+++ b/odb-tests/evolution/embedded/buildfile
@@ -0,0 +1,59 @@
+# file : evolution/embedded/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+ assert (!$multi) "multi-database mode is not supported by this test"
+
+db = ($databases[0])
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-$db%lib{odb-$db}
+import libs += lib{common}
+
+hdrs = test1 test2 test3
+
+exe{driver}: {hxx cxx}{* -*-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+for h: $hdrs
+ exe{driver}: {hxx ixx cxx}{$h-odb}: hxx{$h} libue{test-meta} hxx{model}
+
+# Make sure testN.hxx are compiled serially since they share the changelog.
+#
+# @@ TODO: make order-only when supported by build2.
+#
+{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix evo_embedded_ \
+ --schema-version-table evo_embedded_sv \
+ --generate-schema \
+ --schema-format embedded \
+ --generate-query \
+ --at-once \
+ --changelog $out_base/model.xml
+
+<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog
+<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration
+<{hxx ixx cxx}{test3-odb}>: odb_options += --omit-create
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
+
+testscript@./:
+{
+ db = $db
+ schemas = $hdrs
+}
diff --git a/odb-tests/evolution/embedded/driver.cxx b/odb-tests/evolution/embedded/driver.cxx
new file mode 100644
index 0000000..ac317cc
--- /dev/null
+++ b/odb-tests/evolution/embedded/driver.cxx
@@ -0,0 +1,185 @@
+// file : evolution/embedded/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test embedded schema migration.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
+#include <libcommon/config.hxx> // DATABASE_XXX
+#include <libcommon/common.hxx>
+
+#ifdef DATABASE_PGSQL
+# include <odb/pgsql/connection.hxx>
+#endif
+
+#include "test2.hxx"
+#include "test3.hxx"
+#include "test2-odb.hxx"
+#include "test3-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv, false));
+
+ db->schema_version_table ("evo_embedded_sv");
+
+ // 1 - base version
+ // 2 - migration
+ // 3 - current version
+ //
+ unsigned short pass (*argv[argc - 1] - '0');
+
+ switch (pass)
+ {
+ case 1:
+ {
+ using namespace v2;
+
+ {
+ transaction t (db->begin ());
+ schema_catalog::drop_schema (*db);
+ t.commit ();
+ }
+
+ // PostgreSQL cannot continue a transaction after a query failed. We
+ // have a workaround but only for 9.4+.
+ //
+#ifdef DATABASE_PGSQL
+ {
+ odb::connection_ptr c (db->connection ());
+ int v (static_cast<odb::pgsql::connection&> (*c).server_version ());
+ if (v < 90400)
+ assert (db->schema_version () == 0);
+ }
+#endif
+
+ {
+ transaction t (db->begin ());
+ assert (db->schema_version () == 0);
+
+ schema_catalog::create_schema (*db, "", false);
+
+ assert (db->schema_version () == 1 && !db->schema_migration ());
+
+ schema_catalog::migrate_schema (*db, 2);
+ t.commit ();
+ }
+
+ assert (db->schema_version () == 2 && !db->schema_migration ());
+
+ {
+ transaction t (db->begin ());
+ object1 o1 (1);
+ o1.num = 123;
+ db->persist (o1);
+ t.commit ();
+ }
+ break;
+ }
+ case 2:
+ {
+ using namespace v2;
+ using namespace v3;
+
+ // Check version information correctness.
+ //
+ assert (schema_catalog::base_version (*db) == 1);
+ assert (schema_catalog::current_version (*db) == 3);
+ assert (schema_catalog::next_version (*db, 0) == 3);
+ assert (schema_catalog::next_version (*db, 1) == 2);
+ assert (schema_catalog::next_version (*db) == 3);
+ assert (schema_catalog::next_version (*db, 3) == 4);
+
+ {
+ assert (db->schema_version () == 2 && !db->schema_migration ());
+
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_pre (*db, 3);
+ t.commit ();
+ }
+
+ assert (db->schema_version () == 3 && db->schema_migration ());
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object1> o1 (db->load<object1> (1));
+ object2 o2 (1);
+ o2.num = o1->num;
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_post (*db, 3);
+ t.commit ();
+
+ assert (db->schema_version () == 3 && !db->schema_migration ());
+ }
+ break;
+ }
+ case 3:
+ {
+ using namespace v3;
+
+ // In transaction.
+ //
+ {
+ transaction t (db->begin ());
+ assert (db->schema_version () == 3 && !db->schema_migration ());
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object2> o2 (db->load<object2> (1));
+ assert (o2->num == 123);
+ t.commit ();
+ }
+
+ // Test the case where there is still no version table.
+ //
+ db->schema_version_migration (0, false);
+
+ {
+ transaction t (db->begin ());
+
+#ifdef DATABASE_ORACLE
+ db->execute ("DROP TABLE \"evo_embedded_sv\"");
+#else
+ db->execute ("DROP TABLE evo_embedded_sv");
+#endif
+ t.commit ();
+ }
+
+ assert (db->schema_version () == 0);
+ break;
+ }
+ default:
+ {
+ cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl;
+ return 1;
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/evolution/embedded/model.hxx b/odb-tests/evolution/embedded/model.hxx
new file mode 100644
index 0000000..f3aa7a4
--- /dev/null
+++ b/odb-tests/evolution/embedded/model.hxx
@@ -0,0 +1,45 @@
+// file : evolution/embedded/model.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef MODEL_VERSION
+# error model.hxx included directly
+#endif
+
+#include <odb/core.hxx>
+
+#pragma db model version(1, MODEL_VERSION)
+
+#define MODEL_NAMESPACE_IMPL(V) v##V
+#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V)
+
+namespace MODEL_NAMESPACE(MODEL_VERSION)
+{
+#if MODEL_VERSION == 2
+ #pragma db object
+ struct object1
+ {
+ object1 (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ int num;
+ };
+#endif
+
+#if MODEL_VERSION == 3
+ #pragma db object
+ struct object2
+ {
+ object2 (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ int num;
+ };
+#endif
+}
+
+#undef MODEL_NAMESPACE
+#undef MODEL_NAMESPACE_IMPL
diff --git a/odb-tests/evolution/embedded/test1.hxx b/odb-tests/evolution/embedded/test1.hxx
new file mode 100644
index 0000000..32903a1
--- /dev/null
+++ b/odb-tests/evolution/embedded/test1.hxx
@@ -0,0 +1,9 @@
+// file : evolution/embedded/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+#pragma db model version(1, 1)
+
+#endif // TEST1_HXX
diff --git a/odb-tests/evolution/embedded/test2.hxx b/odb-tests/evolution/embedded/test2.hxx
new file mode 100644
index 0000000..fce8760
--- /dev/null
+++ b/odb-tests/evolution/embedded/test2.hxx
@@ -0,0 +1,11 @@
+// file : evolution/embedded/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+#define MODEL_VERSION 2
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST2_HXX
diff --git a/odb-tests/evolution/embedded/test3.hxx b/odb-tests/evolution/embedded/test3.hxx
new file mode 100644
index 0000000..d49ecc5
--- /dev/null
+++ b/odb-tests/evolution/embedded/test3.hxx
@@ -0,0 +1,11 @@
+// file : evolution/embedded/test3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST3_HXX
+#define TEST3_HXX
+
+#define MODEL_VERSION 3
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST3_HXX
diff --git a/odb-tests/evolution/embedded/testscript b/odb-tests/evolution/embedded/testscript
new file mode 100644
index 0000000..3ddf983
--- /dev/null
+++ b/odb-tests/evolution/embedded/testscript
@@ -0,0 +1,21 @@
+# file : evolution/embedded/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
+test.arguments += $($(db)_options)
+
+: basics
+:
+if! $sqlite
+{
+ $* 1;
+ $* 2;
+ $* 3
+}
+else
+{
+ $* 1 &odb-test.db;
+ $* 2;
+ $* 3
+}
diff --git a/odb-tests/evolution/soft-add/buildfile b/odb-tests/evolution/soft-add/buildfile
new file mode 100644
index 0000000..6a61697
--- /dev/null
+++ b/odb-tests/evolution/soft-add/buildfile
@@ -0,0 +1,64 @@
+# file : evolution/soft-add/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+ assert (!$multi) "multi-database mode is not supported by this test"
+
+db = ($databases[0])
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-$db%lib{odb-$db}
+import libs += lib{common}
+
+hdrs = test1 test2 test3
+
+exe{driver}: {hxx cxx}{* -*-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+for h: $hdrs
+ exe{driver}: {hxx ixx cxx}{$h-odb}: hxx{$h} libue{test-meta} hxx{model}
+
+# Make sure testN.hxx are compiled serially since they share the changelog.
+#
+# @@ TODO: make order-only when supported by build2.
+#
+{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix evo_soft_a_ \
+ --schema-version-table evo_soft_a_sv \
+ --generate-schema \
+ --generate-query \
+ --generate-prepared \
+ --at-once \
+ --changelog $out_base/model.xml
+
+<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog
+<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration
+
+<{hxx ixx cxx}{test3-odb}>:
+{
+ odb_options += --omit-create
+ schema_versions = 002 003
+}
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
+
+testscript@./:
+{
+ db = $db
+ schemas = $hdrs
+}
diff --git a/odb-tests/evolution/soft-add/driver.cxx b/odb-tests/evolution/soft-add/driver.cxx
new file mode 100644
index 0000000..a18c6a2
--- /dev/null
+++ b/odb-tests/evolution/soft-add/driver.cxx
@@ -0,0 +1,2224 @@
+// file : evolution/soft-add/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test soft-add functionality.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
+#include <libcommon/config.hxx> // DATABASE_XXX
+#include <libcommon/common.hxx>
+
+#include "test2.hxx"
+#include "test3.hxx"
+#include "test2-odb.hxx"
+#include "test3-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv, false));
+
+ db->schema_version_table ("evo_soft_a_sv");
+
+ bool embedded (schema_catalog::exists (*db));
+
+ // 1 - base version
+ // 2 - migration
+ // 3 - current version
+ //
+ unsigned short pass (*argv[argc - 1] - '0');
+
+ switch (pass)
+ {
+ case 1:
+ {
+ using namespace v3; // NOTE: not v2.
+
+ if (embedded)
+ {
+ // SQLite has broken foreign keys when it comes to DDL.
+ //
+#ifdef DATABASE_SQLITE
+ db->connection ()->execute ("PRAGMA foreign_keys=OFF");
+#endif
+ transaction t (db->begin ());
+ schema_catalog::drop_schema (*db);
+ schema_catalog::create_schema (*db, "", false);
+ schema_catalog::migrate_schema (*db, 2);
+ t.commit ();
+
+#ifdef DATABASE_SQLITE
+ db->connection ()->execute ("PRAGMA foreign_keys=ON");
+#endif
+ }
+
+ // Test basic soft-added member logic.
+ //
+ {
+ using namespace test2;
+
+ // None of the database operations should yet include the
+ // added members.
+ //
+ {
+ object o (1);
+ o.str = "abc";
+ o.num = 123;
+ o.vec.push_back (123);
+ o.ptr = new object1 (1);
+ o.ptr->ptrs.push_back (&o);
+
+ transaction t (db->begin ());
+ db->persist (o);
+ db->persist (*o.ptr);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->str == "" && p->num == 123 &&
+ p->vec.empty () && p->ptr == 0);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end () &&
+ i->str == "" && i->num == 123 &&
+ i->vec.empty () && i->ptr == 0);
+
+ try
+ {
+ db->query<object> (query::str.is_null ()); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+
+ t.commit ();
+ }
+
+ object o (2);
+ o.str = "bcd";
+ o.num = 234;
+ o.vec.push_back (234);
+ o.ptr = new object1 (2);
+ o.ptr->ptrs.push_back (&o);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ db->persist (*o.ptr);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->str == "" && p->num == 234 &&
+ p->vec.empty () && p->ptr == 0);
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+ o.vec.modify (0)++;
+ delete o.ptr;
+ o.ptr = 0;
+
+ {
+ transaction t (db->begin ());
+ db->erase<object1> (2);
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->str == "" && p->num == 235 &&
+ p->vec.empty () && p->ptr == 0);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<object> (2);
+ t.commit ();
+ }
+ }
+
+ // Test empty statement handling (INSERT, UPDATE).
+ //
+ {
+ using namespace test3;
+
+ // None of the database operations should yet include the
+ // added members.
+ //
+ object o;
+ o.str = "bcd";
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (o.id));
+ assert (p->str == "");
+ t.commit ();
+ }
+
+ o.str += 'e';
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (o.id));
+ assert (p->str == "");
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ // Test empty statement handling (SELECT in polymorphic loader).
+ //
+ {
+ using namespace test4;
+
+ // None of the database operations should yet include the
+ // added members.
+ //
+ object o (1);
+ o.str = "abc";
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<base> p (db->load<base> (1));
+ assert (static_cast<object&> (*p).str == "");
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ // Test container with soft-added value member.
+ //
+ {
+ using namespace test5;
+
+ // None of the database operations should yet include the
+ // added members.
+ //
+ {
+ object o (1);
+ o.vec.push_back (value ("abc", 123));
+
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->vec[0].str == "" && p->vec[0].num == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.vec.push_back (value ("bcd", 234));
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->vec[0].str == "" && p->vec[0].num == 234);
+ t.commit ();
+ }
+
+ o.vec.modify (0).str += 'e';
+ o.vec.modify (0).num++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->vec[0].str == "" && p->vec[0].num == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<object> (2);
+ t.commit ();
+ }
+ }
+
+ // Test view with soft-added member.
+ //
+ {
+ using namespace test6;
+
+ // None of the database operations should yet include the
+ // added members.
+ //
+ {
+ object o (1);
+ o.str = "abc";
+ o.num = 123;
+
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<view> query;
+ typedef odb::result<view> result;
+
+ transaction t (db->begin ());
+ result r (db->query<view> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "" && i->num == 123);
+
+ try
+ {
+ db->query<object> (query::str == "abc"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+
+ t.commit ();
+ }
+ }
+
+ // Test soft-added section member.
+ //
+ {
+ using namespace test7;
+
+ // None of the database operations should yet include the
+ // added members.
+ //
+ {
+ object o (1);
+ o.str = "abc";
+ o.num = 123;
+ o.vec.push_back (123);
+
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+
+ try
+ {
+ db->load (*p, p->s); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+
+ t.commit ();
+ }
+
+ object o (2);
+ o.str = "bcd";
+ o.num = 234;
+ o.vec.push_back (234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+ o.vec.modify (0)++;
+ o.s.change ();
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<object> (2);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added members of a section.
+ //
+ {
+ using namespace test8;
+
+ // None of the database operations should yet include the
+ // added members.
+ //
+ {
+ object o (1);
+ o.str = "abc";
+ o.num = 123;
+ o.vec.push_back (123);
+
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ db->load (*p, p->s);
+ assert (p->str == "" && p->num == 123 && p->vec.empty ());
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ db->load (*i, i->s);
+ assert (i->str == "" && i->num == 123 && i->vec.empty ());
+
+ try
+ {
+ db->query<object> (query::str == "abc"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+
+ t.commit ();
+ }
+
+ object o (2);
+ o.str = "bcd";
+ o.num = 234;
+ o.vec.push_back (234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->str == "" && p->num == 234 && p->vec.empty ());
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+ o.vec.modify (0)++; // No longer automatically marked as changed.
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->str == "" && p->num == 234 && p->vec.empty ());
+ t.commit ();
+ }
+
+ o.s.change ();
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->str == "" && p->num == 235 && p->vec.empty ());
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<object> (2);
+ t.commit ();
+ }
+ }
+
+ // Test basic soft-added member logic in polymorphic classes.
+ //
+ {
+ using namespace test9;
+
+ // None of the database operations should yet include the
+ // added members.
+ //
+ {
+ object o (1);
+ o.bstr = "ab";
+ o.dstr = "abc";
+ o.num = 123;
+
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (static_cast<object*> (db->load<base> (1)));
+ assert (p->bstr == "" && p->dstr == "" && p->num == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<base> query;
+ typedef odb::result<base> result;
+
+ transaction t (db->begin ());
+ result r (db->query<base> (query::id == 1));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ object& o (static_cast<object&> (*i));
+ assert (o.bstr == "" && o.dstr == "" && o.num == 123);
+
+ try
+ {
+ db->query<base> (query::bstr == "ab"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end () &&
+ i->bstr == "" && i->dstr == "" && i->num);
+
+ try
+ {
+ db->query<object> (query::dstr == "abc"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+
+ t.commit ();
+ }
+
+ object o (2);
+ o.bstr = "bc";
+ o.dstr = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->bstr == "" && p->dstr == "" && p->num == 234);
+ t.commit ();
+ }
+
+ o.bstr += 'd';
+ o.dstr += 'e';
+ o.num++;
+
+ {
+ transaction t (db->begin ());
+ db->update (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->bstr == "" && p->dstr == "" && p->num == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<base> (2);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added section member in polymorphic classes.
+ //
+ {
+ using namespace test10;
+
+ // None of the database operations should yet include the
+ // added members.
+ //
+ {
+ object o (1);
+ o.bstr = "ab";
+ o.dstr = "abc";
+ o.num = 123;
+
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> p (db->load<base> (1));
+
+ try
+ {
+ db->load (*p, p->s); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+
+ t.commit ();
+ }
+
+ object o (2);
+ o.bstr = "bc";
+ o.dstr = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (static_cast<base&> (o));
+ t.commit ();
+ }
+
+ o.bstr += 'd';
+ o.dstr += 'e';
+ o.num++;
+ o.s.change ();
+
+ {
+ transaction t (db->begin ());
+ db->update (static_cast<base&> (o));
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<base> (2);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added members of a section in polymorphic classes.
+ //
+ {
+ using namespace test11;
+
+ // None of the database operations should yet include the
+ // added members.
+ //
+ {
+ object o (1);
+ o.bstr = "ab";
+ o.dstr = "abc";
+ o.num = 123;
+
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> p (db->load<base> (1));
+ db->load (*p, p->s);
+ object& o (static_cast<object&> (*p));
+ assert (o.bstr == "" && o.dstr == "" && o.num == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<base> query;
+ typedef odb::result<base> result;
+
+ transaction t (db->begin ());
+ result r (db->query<base> (query::id == 1));
+ result::iterator i (r.begin ());
+ db->load (*i, i->s);
+ assert (i != r.end ());
+ object& o (static_cast<object&> (*i));
+ assert (o.bstr == "" && o.dstr == "" && o.num == 123);
+
+ try
+ {
+ db->query<base> (query::bstr == "ab"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ db->load (*i, i->s);
+ assert (i != r.end () &&
+ i->bstr == "" && i->dstr == "" && i->num);
+
+ try
+ {
+ db->query<object> (query::dstr == "abc"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+
+ t.commit ();
+ }
+
+ object o (2);
+ o.bstr = "bc";
+ o.dstr = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->bstr == "" && p->dstr == "" && p->num == 234);
+ t.commit ();
+ }
+
+ o.bstr += 'd';
+ o.dstr += 'e';
+ o.num++;
+ o.s.change ();
+
+ {
+ transaction t (db->begin ());
+ db->update (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->bstr == "" && p->dstr == "" && p->num == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<base> (2);
+ t.commit ();
+ }
+
+ // Test empty statement detection in sections.
+ //
+ base b (3);
+ b.bstr = "bc";
+
+ {
+ transaction t (db->begin ());
+ db->persist (b);
+ unique_ptr<base> p (db->load<base> (3));
+ db->load (*p, p->s);
+ assert (p->bstr == "");
+ t.commit ();
+ }
+
+ b.bstr += 'd';
+ b.s.change ();
+
+ {
+ transaction t (db->begin ());
+ db->update (b);
+ unique_ptr<base> p (db->load<base> (3));
+ db->load (*p, p->s);
+ assert (p->bstr == "");
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (b);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added member and optimistic concurrency.
+ //
+ {
+ using namespace test12;
+
+ // None of the database operations should yet include the
+ // added members.
+ //
+ {
+ object o (1);
+ o.str = "abc";
+ o.num = 123;
+
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->str == "" && p->num == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "" && i->num == 123);
+
+ try
+ {
+ db->query<object> (query::str == "abc"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+
+ t.commit ();
+ }
+
+ object o (2);
+ o.str = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->str == "" && p->num == 234);
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+
+ {
+ transaction t (db->begin ());
+ unsigned long long v (o.v_);
+ db->update (o);
+ assert (o.v_ != v);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->str == "" && p->num == 235 && p->v_ == o.v_);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<object> (2);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added member in an object without id.
+ //
+ {
+ using namespace test13;
+
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ // None of the database operations should yet include the
+ // added members.
+ //
+ {
+ object o;
+ o.str = "abc";
+ o.num = 123;
+
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "" && i->num == 123);
+
+ try
+ {
+ db->query<object> (query::str == "abc"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+
+ t.commit ();
+ }
+
+ object o;
+ o.str = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+
+ result r (db->query<object> (query::num == 234));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "" && i->num == 234);
+
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase_query<object> (query::num == 234);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added member in an object with auto id.
+ //
+ {
+ using namespace test14;
+
+ // None of the database operations should yet include the
+ // added members.
+ //
+ unsigned long id;
+ {
+ object o;
+ o.str = "abc";
+ o.num = 123;
+
+ transaction t (db->begin ());
+ db->persist (o);
+ id = o.id;
+ t.commit ();
+ }
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (id));
+ assert (p->str == "" && p->num == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "" && i->num == 123);
+
+ try
+ {
+ db->query<object> (query::str == "abc"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+
+ t.commit ();
+ }
+
+ object o;
+ o.str = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (o.id));
+ assert (p->str == "" && p->num == 234);
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (o.id));
+ assert (p->str == "" && p->num == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<object> (o.id);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added container member in a non-versioned object.
+ //
+ {
+ using namespace test21;
+
+ // None of the database operations should yet include the
+ // added members.
+ //
+ {
+ object o (1);
+ o.num = 123;
+ o.vec.push_back (123);
+
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->num == 123 && p->vec.empty ());
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->num == 123 && i->vec.empty ());
+ t.commit ();
+ }
+
+ object o (2);
+ o.num = 234;
+ o.vec.push_back (234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->num == 234 && p->vec.empty ());
+ t.commit ();
+ }
+
+ o.num++;
+ o.vec.modify (0)++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->num == 235 && p->vec.empty ());
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<object> (2);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added container member in a non-versioned section.
+ //
+ {
+ using namespace test22;
+
+ // None of the database operations should yet include the
+ // added members.
+ //
+ {
+ object o (1);
+ o.num = 123;
+ o.vec.push_back (123);
+
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ db->load (*p, p->s);
+ assert (p->num == 123 && p->vec.empty ());
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ db->load (*i, i->s);
+ assert (i->num == 123 && i->vec.empty ());
+ t.commit ();
+ }
+
+ object o (2);
+ o.num = 234;
+ o.vec.push_back (234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->num == 234 && p->vec.empty ());
+ t.commit ();
+ }
+
+ o.num++;
+ o.vec.modify (0)++; // No longer automatically marks as changed.
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->num == 234 && p->vec.empty ());
+ t.commit ();
+ }
+
+ o.s.change ();
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->num == 235 && p->vec.empty ());
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<object> (2);
+ t.commit ();
+ }
+ }
+
+ break;
+ }
+ case 2:
+ {
+ using namespace v3;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_pre (*db, 3);
+ t.commit ();
+ }
+
+ // Test basic soft-added member logic.
+ //
+ {
+ using namespace test2;
+
+ // All the database operations should now include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ p->str = "abc";
+ p->vec.push_back (123);
+ delete p->ptr;
+ p->ptr = new object1 (1);
+ db->update (*p);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->str == "abc" && p->num == 123 &&
+ p->vec[0] == 123 && p->ptr->id_ == 1);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::str.is_not_null () &&
+ query::ptr->id == 1));
+ result::iterator i (r.begin ());
+ assert (i != r.end () &&
+ i->str == "abc" && i->num == 123 &&
+ i->vec[0] == 123 && i->ptr->id_ == 1);
+ t.commit ();
+ }
+
+ object o (2);
+ o.str = "bcd";
+ o.num = 234;
+ o.vec.push_back (234);
+ o.ptr = new object1 (2);
+ o.ptr->ptrs.push_back (&o);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ db->persist (*o.ptr);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->str == "bcd" && p->num == 234 &&
+ p->vec[0] == 234 && p->ptr->id_ == 2);
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+ o.vec.modify (0)++;
+ delete o.ptr;
+ o.ptr = 0;
+
+ {
+ transaction t (db->begin ());
+ db->erase<object1> (2);
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->str == "bcde" && p->num == 235 &&
+ p->vec[0] == 235 && p->ptr == 0);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ // Test container with soft-added value member.
+ //
+ {
+ using namespace test5;
+
+ // All the database operations should now include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ p->vec.modify (0).str = "abc";
+ db->update (*p);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->vec[0].str == "abc" && p->vec[0].num == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.vec.push_back (value ("bcd", 234));
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->vec[0].str == "bcd" && p->vec[0].num == 234);
+ t.commit ();
+ }
+
+ o.vec.modify (0).str += 'e';
+ o.vec.modify (0).num++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->vec[0].str == "bcde" && p->vec[0].num == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ // Test view with soft-added member.
+ //
+ {
+ using namespace test6;
+
+ // All the database operations should now include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ p->str = "abc";
+ db->update (*p);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<view> query;
+ typedef odb::result<view> result;
+
+ transaction t (db->begin ());
+ result r (db->query<view> (query::str == "abc"));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "abc" && i->num == 123);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added section member.
+ //
+ {
+ using namespace test7;
+
+ // All the database operations should now include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ db->load (*p, p->s);
+ p->str = "abc";
+ p->vec.push_back (123);
+ db->update (*p, p->s);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ db->load (*p, p->s);
+ assert (p->str == "abc" && p->num == 123 && p->vec[0] == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::str == "abc"));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ db->load (*i, i->s);
+ assert (i->str == "abc" && i->num == 123 && i->vec[0] == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.str = "bcd";
+ o.num = 234;
+ o.vec.push_back (234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->str == "bcd" && p->num == 234 && p->vec[0] == 234);
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+ o.vec.modify (0)++; // Automatically marks section as updated.
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->str == "bcde" && p->num == 235 && p->vec[0] == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added members of a section.
+ //
+ {
+ using namespace test8;
+
+ // All the database operations should now include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ db->load (*p, p->s);
+ p->str = "abc";
+ p->vec.push_back (123);
+ db->update (*p, p->s);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ db->load (*p, p->s);
+ assert (p->str == "abc" && p->num == 123 && p->vec[0] == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::str == "abc"));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ db->load (*i, i->s);
+ assert (i->str == "abc" && i->num == 123 && i->vec[0] == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.str = "bcd";
+ o.num = 234;
+ o.vec.push_back (234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->str == "bcd" && p->num == 234 && p->vec[0] == 234);
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+ o.vec.modify (0)++; // Automatically marks section as updated.
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->str == "bcde" && p->num == 235 && p->vec[0] == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ // Test basic soft-added member logic in polymorphic classes.
+ //
+ {
+ using namespace test9;
+
+ // All the database operations should now include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ p->bstr = "ab";
+ p->dstr = "abc";
+ db->update (*p);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (static_cast<object*> (db->load<base> (1)));
+ assert (p->bstr == "ab" && p->dstr == "abc" && p->num == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<base> query;
+ typedef odb::result<base> result;
+
+ transaction t (db->begin ());
+ result r (db->query<base> (query::bstr == "ab"));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ object& o (static_cast<object&> (*i));
+ assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.bstr = "bc";
+ o.dstr = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->bstr == "bc" && p->dstr == "bcd" && p->num == 234);
+ t.commit ();
+ }
+
+ o.bstr += 'd';
+ o.dstr += 'e';
+ o.num++;
+
+ {
+ transaction t (db->begin ());
+ db->update (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->bstr == "bcd" && p->dstr == "bcde" && p->num == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (static_cast<base&> (o));
+ t.commit ();
+ }
+ }
+
+ // Test soft-added section member in polymorphic classes.
+ //
+ {
+ using namespace test10;
+
+ // All the database operations should now include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ db->load (*p, p->s);
+ p->bstr = "ab";
+ p->dstr = "abc";
+ db->update (*p, p->s);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> p (db->load<base> (1));
+ db->load (*p, p->s);
+ object& o (static_cast<object&> (*p));
+ assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<base> query;
+ typedef odb::result<base> result;
+
+ transaction t (db->begin ());
+ result r (db->query<base> (query::bstr == "ab"));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ db->load (*i, i->s);
+ object& o (static_cast<object&> (*i));
+ assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.bstr = "bc";
+ o.dstr = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->bstr == "bc" && p->dstr == "bcd" && p->num == 234);
+ t.commit ();
+ }
+
+ o.bstr += 'd';
+ o.dstr += 'e';
+ o.num++;
+ o.s.change ();
+
+ {
+ transaction t (db->begin ());
+ db->update (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->bstr == "bcd" && p->dstr == "bcde" && p->num == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (static_cast<base&> (o));
+ t.commit ();
+ }
+ }
+
+ // Test soft-added members of a section in polymorphic classes.
+ //
+ {
+ using namespace test11;
+
+ // All the database operations should now include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ db->load (*p, p->s);
+ p->bstr = "ab";
+ p->dstr = "abc";
+ db->update (*p, p->s);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> p (db->load<base> (1));
+ db->load (*p, p->s);
+ object& o (static_cast<object&> (*p));
+ assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<base> query;
+ typedef odb::result<base> result;
+
+ transaction t (db->begin ());
+ result r (db->query<base> (query::bstr == "ab"));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ db->load (*i, i->s);
+ object& o (static_cast<object&> (*i));
+ assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.bstr = "bc";
+ o.dstr = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->bstr == "bc" && p->dstr == "bcd" && p->num == 234);
+ t.commit ();
+ }
+
+ o.bstr += 'd';
+ o.dstr += 'e';
+ o.num++;
+ o.s.change ();
+
+ {
+ transaction t (db->begin ());
+ db->update (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->bstr == "bcd" && p->dstr == "bcde" && p->num == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (static_cast<base&> (o));
+ t.commit ();
+ }
+ }
+
+ // Test soft-added member and optimistic concurrency.
+ //
+ {
+ using namespace test12;
+
+ // All the database operations should now include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ p->str = "abc";
+ db->update (*p);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->str == "abc" && p->num == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::str == "abc"));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "abc" && i->num == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.str = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->str == "bcd" && p->num == 234);
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+
+ {
+ transaction t (db->begin ());
+ unsigned long long v (o.v_);
+ db->update (o);
+ assert (o.v_ != v);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->str == "bcde" && p->num == 235 && p->v_ == o.v_);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added member in an object without id.
+ //
+ {
+ using namespace test13;
+
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ // All the database operations should now include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ result r (db->query<object> (query::str == "abc"));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "abc" && i->num == 123);
+ t.commit ();
+ }
+
+ object o;
+ o.str = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+
+ result r (db->query<object> (query::str == "bcd"));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "bcd" && i->num == 234);
+
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase_query<object> (query::str == "bcd");
+ t.commit ();
+ }
+ }
+
+ // Test soft-added member in an object with auto id.
+ //
+ {
+ using namespace test14;
+
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ // All the database operations should now include the added
+ // members.
+ //
+ unsigned long id;
+ {
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ i->str = "abc";
+ db->update (*i);
+ id = i->id;
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (id));
+ assert (p->str == "abc" && p->num == 123);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ result r (db->query<object> (query::str == "abc"));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "abc" && i->num == 123);
+ t.commit ();
+ }
+
+ object o;
+ o.str = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (o.id));
+ assert (p->str == "bcd" && p->num == 234);
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (o.id));
+ assert (p->str == "bcde" && p->num == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added container member in a non-versioned object.
+ //
+ {
+ using namespace test21;
+
+ // All the database operations should now include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ p->vec.push_back (123);
+ db->update (*p);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->num == 123 && p->vec[0] == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->num == 123 && i->vec[0] == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.num = 234;
+ o.vec.push_back (234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->num == 234 && p->vec[0] == 234);
+ t.commit ();
+ }
+
+ o.num++;
+ o.vec.modify (0)++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->num == 235 && p->vec[0] == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added container member in a non-versioned section.
+ //
+ {
+ using namespace test22;
+
+ // All the database operations should now include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ db->load (*p, p->s);
+ p->vec.push_back (123);
+ db->update (*p, p->s);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ db->load (*p, p->s);
+ assert (p->num == 123 && p->vec[0] == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ db->load (*i, i->s);
+ assert (i->num == 123 && i->vec[0] == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.num = 234;
+ o.vec.push_back (234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->num == 234 && p->vec[0] == 234);
+ t.commit ();
+ }
+
+ o.num++;
+ o.vec.modify (0)++; // Automatically marks section as changed.
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->num == 235 && p->vec[0] == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_post (*db, 3);
+ t.commit ();
+ }
+ break;
+ }
+ case 3:
+ {
+ using namespace v3;
+
+ // Test basic soft-added member logic.
+ //
+ {
+ using namespace test2;
+
+ // All the database operations should still include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->str == "abc" && p->num == 123 &&
+ p->vec[0] == 123 && p->ptr->id_ == 1);
+ t.commit ();
+ }
+ }
+
+ // Test container with soft-added value member.
+ //
+ {
+ using namespace test5;
+
+ // All the database operations should still include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->vec[0].str == "abc" && p->vec[0].num == 123);
+ t.commit ();
+ }
+ }
+
+ // Test view with soft-added member.
+ //
+ {
+ using namespace test6;
+
+ // All the database operations should still include the added
+ // members.
+ //
+ {
+ typedef odb::query<view> query;
+ typedef odb::result<view> result;
+
+ transaction t (db->begin ());
+ result r (db->query<view> (query::str == "abc"));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "abc" && i->num == 123);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added section member.
+ //
+ {
+ using namespace test7;
+
+ // All the database operations should still include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ db->load (*p, p->s);
+ assert (p->str == "abc" && p->num == 123 && p->vec[0] == 123);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added members of a section.
+ //
+ {
+ using namespace test8;
+
+ // All the database operations should still include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ db->load (*p, p->s);
+ assert (p->str == "abc" && p->num == 123 && p->vec[0] == 123);
+ t.commit ();
+ }
+ }
+
+ // Test basic soft-added member logic in polymorphic classes.
+ //
+ {
+ using namespace test9;
+
+ // All the database operations should still include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (static_cast<object*> (db->load<base> (1)));
+ assert (p->bstr == "ab" && p->dstr == "abc" && p->num == 123);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added section member in polymorphic classes.
+ //
+ {
+ using namespace test10;
+
+ // All the database operations should still include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> p (db->load<base> (1));
+ db->load (*p, p->s);
+ object& o (static_cast<object&> (*p));
+ assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added members of a section in polymorphic classes.
+ //
+ {
+ using namespace test11;
+
+ // All the database operations should still include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> p (db->load<base> (1));
+ db->load (*p, p->s);
+ object& o (static_cast<object&> (*p));
+ assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added member and optimistic concurrency.
+ //
+ {
+ using namespace test12;
+
+ // All the database operations should still include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->str == "abc" && p->num == 123);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added member in an object without id.
+ //
+ {
+ using namespace test13;
+
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ // All the database operations should still include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ result r (db->query<object> (query::str == "abc"));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "abc" && i->num == 123);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added member in an object with auto id.
+ //
+ {
+ using namespace test14;
+
+ // All the database operations should still include the added
+ // members.
+ //
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::str == "abc"));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "abc" && i->num == 123);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added container member in a non-versioned object.
+ //
+ {
+ using namespace test21;
+
+ // All the database operations should still include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->num == 123 && p->vec[0] == 123);
+ t.commit ();
+ }
+ }
+
+ // Test soft-added container member in a non-versioned section.
+ //
+ {
+ using namespace test22;
+
+ // All the database operations should still include the added
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ db->load (*p, p->s);
+ assert (p->num == 123 && p->vec[0] == 123);
+ t.commit ();
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl;
+ return 1;
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/evolution/soft-add/model.hxx b/odb-tests/evolution/soft-add/model.hxx
new file mode 100644
index 0000000..39d63c4
--- /dev/null
+++ b/odb-tests/evolution/soft-add/model.hxx
@@ -0,0 +1,504 @@
+// file : evolution/soft-add/model.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef MODEL_VERSION
+# error model.hxx included directly
+#endif
+
+#include <string>
+
+#include <odb/core.hxx>
+#include <odb/vector.hxx>
+#include <odb/section.hxx>
+#include <odb/lazy-ptr.hxx>
+
+#pragma db model version(1, MODEL_VERSION)
+
+#define MODEL_NAMESPACE_IMPL(V) v##V
+#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V)
+
+namespace MODEL_NAMESPACE(MODEL_VERSION)
+{
+ // The test numbers are made to correspond to the soft-delete ones.
+ //
+
+ // Test basic soft-added member logic.
+ //
+ #pragma db namespace table("t2_")
+ namespace test2
+ {
+ struct object;
+
+ #pragma db object
+ struct object1
+ {
+ object1 (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ odb::vector<odb::lazy_ptr<object> > ptrs;
+ };
+
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id), ptr (0) {}
+ ~object () {delete ptr;}
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string str;
+ unsigned long num;
+ odb::vector<int> vec;
+
+ #pragma db inverse(ptrs)
+ object1* ptr;
+ };
+
+#if MODEL_VERSION == 3
+ // Make it a LOB for Oracle and long data for SQL Server.
+ //
+ #pragma db member(object::str) added(3) \
+ oracle:type("CLOB") \
+ mssql:type("VARCHAR(max)")
+ #pragma db member(object::vec) added(3)
+ #pragma db member(object::ptr) added(3)
+#else
+ #pragma db member(object::str) transient
+ #pragma db member(object::vec) transient
+ #pragma db member(object::ptr) transient
+#endif
+ }
+
+ // Test empty statement handling (INSERT, UPDATE).
+ //
+ #pragma db namespace table("t3_")
+ namespace test3
+ {
+ #pragma db object
+ struct object
+ {
+ #pragma db id auto
+ unsigned long id;
+
+ std::string str;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::str) added(3)
+#else
+ #pragma db member(object::str) transient
+#endif
+ }
+
+ // Test empty statement handling (SELECT in polymorphic loader).
+ //
+ #pragma db namespace table("t4_")
+ namespace test4
+ {
+ #pragma db object polymorphic
+ struct base
+ {
+ virtual
+ ~base () {}
+ base (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+ };
+
+ #pragma db object
+ struct object: base
+ {
+ object (unsigned long id = 0): base (id) {}
+
+ std::string str;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::str) added(3)
+#else
+ #pragma db member(object::str) transient
+#endif
+ }
+
+ // Test container with soft-added value member.
+ //
+ #pragma db namespace table("t5_")
+ namespace test5
+ {
+ #pragma db value
+ struct value
+ {
+ value () {}
+ value (const std::string& s, unsigned long n): str (s), num (n) {}
+
+ std::string str;
+ unsigned long num;
+ };
+
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ odb::vector<value> vec;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(value::str) added(3)
+#else
+ #pragma db member(value::str) transient
+#endif
+ }
+
+ // Test view with soft-added member.
+ //
+ #pragma db namespace table("t6_")
+ namespace test6
+ {
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string str;
+ unsigned long num;
+ };
+
+ #pragma db view object(object)
+ struct view
+ {
+ std::string str;
+ unsigned long num;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::str) added(3)
+ #pragma db member(view::str) added(3)
+#else
+ #pragma db member(object::str) transient
+ #pragma db member(view::str) transient
+#endif
+ }
+
+ // Test soft-added section member.
+ //
+ #pragma db namespace table("t7_")
+ namespace test7
+ {
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+#if MODEL_VERSION == 3
+ #pragma db load(lazy) update(change) added(3)
+ odb::section s;
+#endif
+
+ #pragma db section(s)
+ std::string str;
+
+ unsigned long num;
+
+ #pragma db section(s)
+ odb::vector<int> vec;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::str) section(s)
+ #pragma db member(object::vec) section(s)
+#else
+ #pragma db member(object::str) transient
+ #pragma db member(object::vec) transient
+#endif
+ }
+
+ // Test soft-added members of a section.
+ //
+ #pragma db namespace table("t8_")
+ namespace test8
+ {
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db load(lazy) update(change)
+ odb::section s;
+
+ std::string str;
+
+ #pragma db section(s)
+ unsigned long num;
+
+ odb::vector<int> vec;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::str) added(3) section(s)
+ #pragma db member(object::vec) added(3) section(s)
+#else
+ #pragma db member(object::str) transient
+ #pragma db member(object::vec) transient
+#endif
+ }
+
+ // Test basic soft-added member logic in polymorphic classes.
+ //
+ #pragma db namespace table("t9_")
+ namespace test9
+ {
+ #pragma db object polymorphic
+ struct base
+ {
+ virtual
+ ~base () {}
+ base (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string bstr;
+ };
+
+ #pragma db object
+ struct object: base
+ {
+ object (unsigned long id = 0): base (id) {}
+
+ std::string dstr;
+ unsigned long num;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(base::bstr) added(3)
+ #pragma db member(object::dstr) added(3)
+#else
+ #pragma db member(base::bstr) transient
+ #pragma db member(object::dstr) transient
+#endif
+ }
+
+ // Test soft-added section member in polymorphic classes.
+ //
+ #pragma db namespace table("t10_")
+ namespace test10
+ {
+ #pragma db object polymorphic
+ struct base
+ {
+ virtual
+ ~base () {}
+ base (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+#if MODEL_VERSION == 3
+ #pragma db load(lazy) update(change) added(3)
+ odb::section s;
+#endif
+
+ std::string bstr;
+ };
+
+ #pragma db object
+ struct object: base
+ {
+ object (unsigned long id = 0): base (id) {}
+
+ std::string dstr;
+ unsigned long num;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(base::bstr) section(s)
+ #pragma db member(object::dstr) section(s)
+#else
+ #pragma db member(base::bstr) transient
+ #pragma db member(object::dstr) transient
+#endif
+ }
+
+ // Test soft-added members of a section in polymorphic classes.
+ //
+ #pragma db namespace table("t11_")
+ namespace test11
+ {
+ #pragma db object polymorphic
+ struct base
+ {
+ virtual
+ ~base () {}
+ base (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db load(lazy) update(change)
+ odb::section s;
+
+ std::string bstr;
+ };
+
+ #pragma db object
+ struct object: base
+ {
+ object (unsigned long id = 0): base (id) {}
+
+ std::string dstr;
+
+ #pragma db section(s)
+ unsigned long num;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(base::bstr) added(3) section(s)
+ #pragma db member(object::dstr) added(3) section(s)
+#else
+ #pragma db member(base::bstr) transient
+ #pragma db member(object::dstr) transient
+#endif
+ }
+
+ // Test soft-added member and optimistic concurrency.
+ //
+ #pragma db namespace table("t12_")
+ namespace test12
+ {
+ #pragma db object optimistic
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db version mssql:type("ROWVERSION")
+ unsigned long long v_;
+
+ std::string str;
+ unsigned long num;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::str) added(3)
+#else
+ #pragma db member(object::str) transient
+#endif
+ }
+
+ // Test soft-added member in an object without id.
+ //
+ #pragma db namespace table("t13_")
+ namespace test13
+ {
+ #pragma db object no_id
+ struct object
+ {
+ std::string str;
+ unsigned long num;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::str) added(3) default("abc") \
+ mysql:type("VARCHAR(255)")
+#else
+ #pragma db member(object::str) transient
+#endif
+ }
+
+ // Test soft-added member in an object with auto id.
+ //
+ #pragma db namespace table("t14_")
+ namespace test14
+ {
+ #pragma db object
+ struct object
+ {
+ std::string str;
+ unsigned long num;
+
+ #pragma db id auto
+ unsigned long id;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::str) added(3)
+#else
+ #pragma db member(object::str) transient
+#endif
+ }
+
+ // Test soft-added container member in a non-versioned object.
+ //
+ #pragma db namespace table("t21_")
+ namespace test21
+ {
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ unsigned long num;
+ odb::vector<int> vec;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::vec) added(3)
+#else
+ #pragma db member(object::vec) transient
+#endif
+ }
+
+ // Test soft-added container member in a non-versioned section.
+ //
+ #pragma db namespace table("t22_")
+ namespace test22
+ {
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db load(lazy) update(change)
+ odb::section s;
+
+ #pragma db section(s)
+ unsigned long num;
+
+ odb::vector<int> vec;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::vec) added(3) section(s)
+#else
+ #pragma db member(object::vec) transient
+#endif
+ }
+}
+
+#undef MODEL_NAMESPACE
+#undef MODEL_NAMESPACE_IMPL
diff --git a/odb-tests/evolution/soft-add/test1.hxx b/odb-tests/evolution/soft-add/test1.hxx
new file mode 100644
index 0000000..461d663
--- /dev/null
+++ b/odb-tests/evolution/soft-add/test1.hxx
@@ -0,0 +1,9 @@
+// file : evolution/soft-add/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+#pragma db model version(1, 1)
+
+#endif // TEST1_HXX
diff --git a/odb-tests/evolution/soft-add/test2.hxx b/odb-tests/evolution/soft-add/test2.hxx
new file mode 100644
index 0000000..746da4b
--- /dev/null
+++ b/odb-tests/evolution/soft-add/test2.hxx
@@ -0,0 +1,11 @@
+// file : evolution/soft-add/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+#define MODEL_VERSION 2
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST2_HXX
diff --git a/odb-tests/evolution/soft-add/test3.hxx b/odb-tests/evolution/soft-add/test3.hxx
new file mode 100644
index 0000000..f2990d0
--- /dev/null
+++ b/odb-tests/evolution/soft-add/test3.hxx
@@ -0,0 +1,11 @@
+// file : evolution/soft-add/test3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST3_HXX
+#define TEST3_HXX
+
+#define MODEL_VERSION 3
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST3_HXX
diff --git a/odb-tests/evolution/soft-add/testscript b/odb-tests/evolution/soft-add/testscript
new file mode 100644
index 0000000..9563f33
--- /dev/null
+++ b/odb-tests/evolution/soft-add/testscript
@@ -0,0 +1,54 @@
+# file : evolution/soft-add/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../$db-schema.testscript
+
+test.arguments += $($(db)_options)
+
+: basics
+:
+if! $sqlite
+{
+ ss =; # Schema modification base file names.
+
+ # Drop everything.
+ #
+ for s: $schemas
+ ss =+ $s
+ end;
+
+ # Add base schema.
+ #
+ ss += test3-002-pre test3-002-post;
+
+ # Add migration.
+ #
+ ss += test3-003-pre test3-003-post;
+
+ # Run tests.
+ #
+ for s: $ss
+ f = $out_base/"$s".sql
+
+ if $mysql
+ cat $f | $create_schema_cmd
+ elif $pgsql
+ $create_schema_cmd -f $f
+ end
+
+ if ($s == 'test3-002-post')
+ $* 1
+ elif ($s == 'test3-003-pre')
+ $* 2
+ elif ($s == 'test3-003-post')
+ $* 3
+ end
+ end
+}
+else
+{
+ $* 1 &odb-test.db;
+ $* 2;
+ $* 3
+}
diff --git a/odb-tests/evolution/soft-delete/buildfile b/odb-tests/evolution/soft-delete/buildfile
new file mode 100644
index 0000000..1d98505
--- /dev/null
+++ b/odb-tests/evolution/soft-delete/buildfile
@@ -0,0 +1,65 @@
+# file : evolution/soft-delete/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+ assert (!$multi) "multi-database mode is not supported by this test"
+
+db = ($databases[0])
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-$db%lib{odb-$db}
+import libs += lib{common}
+
+hdrs = test1 test2 test3
+
+exe{driver}: {hxx cxx}{* -*-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+for h: $hdrs
+ exe{driver}: {hxx ixx cxx}{$h-odb}: hxx{$h} libue{test-meta} hxx{model}
+
+# Make sure testN.hxx are compiled serially since they share the changelog.
+#
+# @@ TODO: make order-only when supported by build2.
+#
+{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix evo_soft_d_ \
+ --schema-version-table evo_soft_d_sv \
+ --generate-schema \
+ --generate-query \
+ --generate-prepared \
+ --at-once \
+ --changelog $out_base/model.xml \
+ --sqlite-override-null
+
+<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog
+<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration
+
+<{hxx ixx cxx}{test3-odb}>:
+{
+ odb_options += --omit-create
+ schema_versions = 002 003
+}
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
+
+testscript@./:
+{
+ db = $db
+ schemas = $hdrs
+}
diff --git a/odb-tests/evolution/soft-delete/driver.cxx b/odb-tests/evolution/soft-delete/driver.cxx
new file mode 100644
index 0000000..ea9e7f0
--- /dev/null
+++ b/odb-tests/evolution/soft-delete/driver.cxx
@@ -0,0 +1,2207 @@
+// file : evolution/soft-delete/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test soft-delete functionality.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
+#include <libcommon/config.hxx> // DATABASE_XXX
+#include <libcommon/common.hxx>
+
+#include "test2.hxx"
+#include "test3.hxx"
+#include "test2-odb.hxx"
+#include "test3-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv, false));
+
+ db->schema_version_table ("evo_soft_d_sv");
+
+ bool embedded (schema_catalog::exists (*db));
+
+ // 1 - base version
+ // 2 - migration
+ // 3 - current version
+ //
+ unsigned short pass (*argv[argc - 1] - '0');
+
+ switch (pass)
+ {
+ case 1:
+ {
+ using namespace v2;
+
+ if (embedded)
+ {
+ // SQLite has broken foreign keys when it comes to DDL.
+ //
+#ifdef DATABASE_SQLITE
+ db->connection ()->execute ("PRAGMA foreign_keys=OFF");
+#endif
+ transaction t (db->begin ());
+ schema_catalog::drop_schema (*db);
+ schema_catalog::create_schema (*db, "", false);
+ schema_catalog::migrate_schema (*db, 2);
+ t.commit ();
+
+#ifdef DATABASE_SQLITE
+ db->connection ()->execute ("PRAGMA foreign_keys=ON");
+#endif
+ }
+
+ // Test soft-deleted objects.
+ //
+ {
+ using namespace test1;
+
+ object o (1);
+ o.num = 123;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+ }
+
+ // Test basic soft-deleted member logic.
+ //
+ {
+ using namespace test2;
+
+ object o (1);
+ o.str = "abc";
+ o.num = 123;
+ o.vec.push_back (123);
+ o.ptr = new object1 (1);
+ o.ptr->ptrs.push_back (&o);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ db->persist (*o.ptr);
+ t.commit ();
+ }
+ }
+
+ // Test container with soft-deleted value member.
+ //
+ {
+ using namespace test5;
+
+ object o (1);
+ o.vec.push_back (value ("abc", 123));
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+ }
+
+ // Test view with soft-deleted member.
+ //
+ {
+ using namespace test6;
+
+ object o (1);
+ o.str = "abc";
+ o.num = 123;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted section member.
+ //
+ {
+ using namespace test7;
+
+ object o (1);
+ o.str = "abc";
+ o.num = 123;
+ o.vec.push_back (123);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted members of a section.
+ //
+ {
+ using namespace test8;
+
+ object o (1);
+ o.str = "abc";
+ o.num = 123;
+ o.vec.push_back (123);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+ }
+
+ // Test basic soft-deleted member logic in polymorphic classes.
+ //
+ {
+ // We have to use v3 here because the discriminator includes
+ // the namespace.
+ //
+ using namespace v3::test9;
+
+ object o (1);
+ o.bstr = "ab";
+ o.dstr = "abc";
+ o.num = 123;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted section member in polymorphic classes.
+ //
+ {
+ // We have to use v3 here because the discriminator includes
+ // the namespace.
+ //
+ using namespace v3::test10;
+
+ object o (1);
+ o.bstr = "ab";
+ o.dstr = "abc";
+ o.num = 123;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted members of a section in polymorphic classes.
+ //
+ {
+ // We have to use v3 here because the discriminator includes
+ // the namespace.
+ //
+ using namespace v3::test11;
+
+ object o (1);
+ o.bstr = "ab";
+ o.dstr = "abc";
+ o.num = 123;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted member and optimistic concurrency.
+ //
+ {
+ using namespace test12;
+
+ object o (1);
+ o.str = "abc";
+ o.num = 123;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted member in an object without id.
+ //
+ {
+ using namespace test13;
+
+ object o;
+ o.str = "abc";
+ o.num = 123;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted member in an object with auto id.
+ //
+ {
+ using namespace test14;
+
+ object o;
+ o.str = "abc";
+ o.num = 123;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+ }
+
+ // Test summarily deleted composite values.
+ //
+ {
+ using namespace test15;
+
+ object o (1);
+ o.v.reset (new value);
+ o.v->str = "abc";
+ o.v->vec.push_back (123);
+ o.num = 123;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted container member in a non-versioned object.
+ //
+ {
+ using namespace test21;
+
+ object o (1);
+ o.num = 123;
+ o.vec.push_back (123);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted container member in a non-versioned section.
+ //
+ {
+ using namespace test22;
+
+ object o (1);
+ o.num = 123;
+ o.vec.push_back (123);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+ }
+
+ break;
+ }
+ case 2:
+ {
+ using namespace v3;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_pre (*db, 3);
+ t.commit ();
+ }
+
+ // Test soft-deleted objects.
+ //
+ {
+ using namespace test1;
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->num == 123);
+ t.commit ();
+ }
+ }
+
+ // Test basic soft-deleted member logic.
+ //
+ {
+ using namespace test2;
+
+ // All the database operations should still include the deleted
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->str == "abc" && p->num == 123 &&
+ p->vec[0] == 123 && p->ptr->id_ == 1);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::str == "abc" &&
+ query::ptr->id == 1));
+ result::iterator i (r.begin ());
+ assert (i != r.end () &&
+ i->str == "abc" && i->num == 123 &&
+ i->vec[0] == 123 && i->ptr->id_ == 1);
+ t.commit ();
+ }
+
+ object o (2);
+ o.str = "bcd";
+ o.num = 234;
+ o.vec.push_back (234);
+ o.ptr = new object1 (2);
+ o.ptr->ptrs.push_back (&o);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ db->persist (*o.ptr);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->str == "bcd" && p->num == 234 &&
+ p->vec[0] == 234 && p->ptr->id_ == 2);
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+ o.vec.modify (0)++;
+ delete o.ptr;
+ o.ptr = 0;
+
+ {
+ transaction t (db->begin ());
+ db->erase<object1> (2);
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->str == "bcde" && p->num == 235 &&
+ p->vec[0] == 235 && p->ptr == 0);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ // Test container with soft-deleted value member.
+ //
+ {
+ using namespace test5;
+
+ // All the database operations should still include the deleted
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->vec[0].str == "abc" && p->vec[0].num == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.vec.push_back (value ("bcd", 234));
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->vec[0].str == "bcd" && p->vec[0].num == 234);
+ t.commit ();
+ }
+
+ o.vec.modify (0).str += 'e';
+ o.vec.modify (0).num++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->vec[0].str == "bcde" && p->vec[0].num == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ // Test view with soft-deleted member.
+ //
+ {
+ using namespace test6;
+
+ // All the database operations should still include the deleted
+ // members.
+ //
+ {
+ typedef odb::query<view> query;
+ typedef odb::result<view> result;
+
+ transaction t (db->begin ());
+ result r (db->query<view> (query::str == "abc"));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "abc" && i->num == 123);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted section member.
+ //
+ {
+ using namespace test7;
+
+ // All the database operations should still include the deleted
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ db->load (*p, p->s);
+ assert (p->str == "abc" && p->num == 123 && p->vec[0] == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::str == "abc"));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ db->load (*i, i->s);
+ assert (i->str == "abc" && i->num == 123 && i->vec[0] == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.str = "bcd";
+ o.num = 234;
+ o.vec.push_back (234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->str == "bcd" && p->num == 234 && p->vec[0] == 234);
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+ o.vec.modify (0)++; // Automatically marks section as updated.
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->str == "bcde" && p->num == 235 && p->vec[0] == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted members of a section.
+ //
+ {
+ using namespace test8;
+
+ // All the database operations should still include the deleted
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ db->load (*p, p->s);
+ assert (p->str == "abc" && p->num == 123 && p->vec[0] == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::str == "abc"));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ db->load (*i, i->s);
+ assert (i->str == "abc" && i->num == 123 && i->vec[0] == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.str = "bcd";
+ o.num = 234;
+ o.vec.push_back (234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->str == "bcd" && p->num == 234 && p->vec[0] == 234);
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+ o.vec.modify (0)++; // Automatically marks section as updated.
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->str == "bcde" && p->num == 235 && p->vec[0] == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ // Test basic soft-deleted member logic in polymorphic classes.
+ //
+ {
+ using namespace test9;
+
+ // All the database operations should still include the deleted
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (static_cast<object*> (db->load<base> (1)));
+ assert (p->bstr == "ab" && p->dstr == "abc" && p->num == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<base> query;
+ typedef odb::result<base> result;
+
+ transaction t (db->begin ());
+ result r (db->query<base> (query::bstr == "ab"));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ object& o (static_cast<object&> (*i));
+ assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.bstr = "bc";
+ o.dstr = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->bstr == "bc" && p->dstr == "bcd" && p->num == 234);
+ t.commit ();
+ }
+
+ o.bstr += 'd';
+ o.dstr += 'e';
+ o.num++;
+
+ {
+ transaction t (db->begin ());
+ db->update (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->bstr == "bcd" && p->dstr == "bcde" && p->num == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (static_cast<base&> (o));
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted section member in polymorphic classes.
+ //
+ {
+ using namespace test10;
+
+ // All the database operations should still include the deleted
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> p (db->load<base> (1));
+ db->load (*p, p->s);
+ object& o (static_cast<object&> (*p));
+ assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<base> query;
+ typedef odb::result<base> result;
+
+ transaction t (db->begin ());
+ result r (db->query<base> (query::bstr == "ab"));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ db->load (*i, i->s);
+ object& o (static_cast<object&> (*i));
+ assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.bstr = "bc";
+ o.dstr = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->bstr == "bc" && p->dstr == "bcd" && p->num == 234);
+ t.commit ();
+ }
+
+ o.bstr += 'd';
+ o.dstr += 'e';
+ o.num++;
+ o.s.change ();
+
+ {
+ transaction t (db->begin ());
+ db->update (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->bstr == "bcd" && p->dstr == "bcde" && p->num == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (static_cast<base&> (o));
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted members of a section in polymorphic classes.
+ //
+ {
+ using namespace test11;
+
+ // All the database operations should still include the deleted
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> p (db->load<base> (1));
+ db->load (*p, p->s);
+ object& o (static_cast<object&> (*p));
+ assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<base> query;
+ typedef odb::result<base> result;
+
+ transaction t (db->begin ());
+ result r (db->query<base> (query::bstr == "ab"));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ db->load (*i, i->s);
+ object& o (static_cast<object&> (*i));
+ assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.bstr = "bc";
+ o.dstr = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->bstr == "bc" && p->dstr == "bcd" && p->num == 234);
+ t.commit ();
+ }
+
+ o.bstr += 'd';
+ o.dstr += 'e';
+ o.num++;
+ o.s.change ();
+
+ {
+ transaction t (db->begin ());
+ db->update (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->bstr == "bcd" && p->dstr == "bcde" && p->num == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (static_cast<base&> (o));
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted member and optimistic concurrency.
+ //
+ {
+ using namespace test12;
+
+ // All the database operations should still include the deleted
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->str == "abc" && p->num == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::str == "abc"));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "abc" && i->num == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.str = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->str == "bcd" && p->num == 234);
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+
+ {
+ transaction t (db->begin ());
+ unsigned long long v (o.v_);
+ db->update (o);
+ assert (o.v_ != v);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->str == "bcde" && p->num == 235 && p->v_ == o.v_);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted member in an object without id.
+ //
+ {
+ using namespace test13;
+
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ // All the database operations should still include the deleted
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ result r (db->query<object> (query::str == "abc"));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "abc" && i->num == 123);
+ t.commit ();
+ }
+
+ object o;
+ o.str = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+
+ result r (db->query<object> (query::str == "bcd"));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "bcd" && i->num == 234);
+
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase_query<object> (query::str == "bcd");
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted member in an object with auto id.
+ //
+ {
+ using namespace test14;
+
+ // All the database operations should still include the deleted
+ // members.
+ //
+ unsigned long id;
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::str == "abc"));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "abc" && i->num == 123);
+ id = i->id;
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (id));
+ assert (p->str == "abc" && p->num == 123);
+ t.commit ();
+ }
+
+ object o;
+ o.str = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (o.id));
+ assert (p->str == "bcd" && p->num == 234);
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (o.id));
+ assert (p->str == "bcde" && p->num == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ // Test summarily deleted composite values.
+ //
+ {
+ using namespace test15;
+
+ // All the database operations should still include the deleted
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->v->str == "abc" && p->num == 123 &&
+ p->v->vec[0] == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::v.str == "abc"));
+ result::iterator i (r.begin ());
+ assert (i != r.end () &&
+ i->v->str == "abc" && i->num == 123 &&
+ i->v->vec[0] == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.v.reset (new value);
+ o.v->str = "bcd";
+ o.num = 234;
+ o.v->vec.push_back (234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->v->str == "bcd" && p->num == 234 &&
+ p->v->vec[0] == 234);
+ t.commit ();
+ }
+
+ o.v->str += 'e';
+ o.num++;
+ o.v->vec.modify (0)++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->v->str == "bcde" && p->num == 235 &&
+ p->v->vec[0] == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted container member in a non-versioned object.
+ //
+ {
+ using namespace test21;
+
+ // All the database operations should still include the deleted
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->num == 123 && p->vec[0] == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->num == 123 && i->vec[0] == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.num = 234;
+ o.vec.push_back (234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->num == 234 && p->vec[0] == 234);
+ t.commit ();
+ }
+
+ o.num++;
+ o.vec.modify (0)++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->num == 235 && p->vec[0] == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted container member in a non-versioned section.
+ //
+ {
+ using namespace test22;
+
+ // All the database operations should still include the deleted
+ // members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ db->load (*p, p->s);
+ assert (p->num == 123 && p->vec[0] == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ db->load (*i, i->s);
+ assert (i->num == 123 && i->vec[0] == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.num = 234;
+ o.vec.push_back (234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->num == 234 && p->vec[0] == 234);
+ t.commit ();
+ }
+
+ o.num++;
+ o.vec.modify (0)++; // Automatically marks section as changed.
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->num == 235 && p->vec[0] == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+ }
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_post (*db, 3);
+ t.commit ();
+ }
+ break;
+ }
+ case 3:
+ {
+ using namespace v3;
+
+ // Test soft-deleted objects.
+ //
+ {
+ using namespace test1;
+
+ try
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1)); // No such table.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+ }
+
+ // Test basic soft-deleted member logic.
+ //
+ {
+ using namespace test2;
+
+ // Now none of the database operations should include the
+ // deleted members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->str == "" && p->num == 123 &&
+ p->vec.empty () && p->ptr == 0);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end () &&
+ i->str == "" && i->num == 123 &&
+ i->vec.empty () && i->ptr == 0);
+
+ // Logical delete in SQLite.
+ //
+#ifndef DATABASE_SQLITE
+ try
+ {
+ db->query<object> (query::str == "abc"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+#else
+ assert (size (db->query<object> (query::str.is_null ())) == 1);
+#endif
+ t.commit ();
+ }
+
+ object o (2);
+ o.str = "bcd";
+ o.num = 234;
+ o.vec.push_back (234);
+ o.ptr = new object1 (2);
+ o.ptr->ptrs.push_back (&o);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ db->persist (*o.ptr);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->str == "" && p->num == 234 &&
+ p->vec.empty () && p->ptr == 0);
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+ o.vec.modify (0)++;
+ delete o.ptr;
+ o.ptr = 0;
+
+ {
+ transaction t (db->begin ());
+ db->erase<object1> (2);
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->str == "" && p->num == 235 &&
+ p->vec.empty () && p->ptr == 0);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<object> (2);
+ t.commit ();
+ }
+ }
+
+ // Test empty statement handling (INSERT, UPDATE).
+ //
+ {
+ using namespace test3;
+
+ // Now none of the database operations should include the
+ // deleted member.
+ //
+ object o;
+ o.str = "bcd";
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (o.id));
+ assert (p->str == "");
+ t.commit ();
+ }
+
+ o.str += 'e';
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (o.id));
+ assert (p->str == "");
+ t.commit ();
+ }
+ }
+
+ // Test empty statement handling (SELECT in polymorphic loader).
+ //
+ {
+ using namespace test4;
+
+ // Now none of the database operations should include the
+ // deleted member.
+ //
+ object o (1);
+ o.str = "abc";
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<base> p (db->load<base> (1));
+ assert (static_cast<object&> (*p).str == "");
+ t.commit ();
+ }
+ }
+
+ // Test container with soft-deleted value member.
+ //
+ {
+ using namespace test5;
+
+ // Now none of the database operations should include the
+ // deleted members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->vec[0].str == "" && p->vec[0].num == 123);
+ t.commit ();
+ }
+
+ object o (2);
+ o.vec.push_back (value ("bcd", 234));
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->vec[0].str == "" && p->vec[0].num == 234);
+ t.commit ();
+ }
+
+ o.vec.modify (0).str += 'e';
+ o.vec.modify (0).num++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->vec[0].str == "" && p->vec[0].num == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<object> (2);
+ t.commit ();
+ }
+ }
+
+ // Test view with soft-deleted member.
+ //
+ {
+ using namespace test6;
+
+ // Now none of the database operations should include the
+ // deleted members.
+ //
+ {
+ typedef odb::query<view> query;
+ typedef odb::result<view> result;
+
+ transaction t (db->begin ());
+ result r (db->query<view> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "" && i->num == 123);
+
+ // Logical delete in SQLite.
+ //
+#ifndef DATABASE_SQLITE
+ try
+ {
+ db->query<object> (query::str == "abc"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+#else
+ assert (size (db->query<object> (query::str.is_null ())) == 1);
+#endif
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted section member.
+ //
+ {
+ using namespace test7;
+
+ // Now none of the database operations should include the
+ // deleted members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+
+ // Logical delete in SQLite.
+ //
+#ifndef DATABASE_SQLITE
+ try
+ {
+ db->load (*p, p->s); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+#endif
+ t.commit ();
+ }
+
+ object o (2);
+ o.str = "bcd";
+ o.num = 234;
+ o.vec.push_back (234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+ o.vec.modify (0)++;
+ o.s.change ();
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<object> (2);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted members of a section.
+ //
+ {
+ using namespace test8;
+
+ // Now none of the database operations should include the
+ // deleted members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ db->load (*p, p->s);
+ assert (p->str == "" && p->num == 123 && p->vec.empty ());
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ db->load (*i, i->s);
+ assert (i->str == "" && i->num == 123 && i->vec.empty ());
+
+ // Logical delete in SQLite.
+ //
+#ifndef DATABASE_SQLITE
+ try
+ {
+ db->query<object> (query::str == "abc"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+#else
+ assert (size (db->query<object> (query::str.is_null ())) == 1);
+#endif
+ t.commit ();
+ }
+
+ object o (2);
+ o.str = "bcd";
+ o.num = 234;
+ o.vec.push_back (234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->str == "" && p->num == 234 && p->vec.empty ());
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+ o.vec.modify (0)++; // No longer automatically marked as changed.
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->str == "" && p->num == 234 && p->vec.empty ());
+ t.commit ();
+ }
+
+ o.s.change ();
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->str == "" && p->num == 235 && p->vec.empty ());
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<object> (2);
+ t.commit ();
+ }
+ }
+
+ // Test basic soft-deleted member logic in polymorphic classes.
+ //
+ {
+ using namespace test9;
+
+ // Now none of the database operations should include the
+ // deleted members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (static_cast<object*> (db->load<base> (1)));
+ assert (p->bstr == "" && p->dstr == "" && p->num == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<base> query;
+ typedef odb::result<base> result;
+
+ transaction t (db->begin ());
+ result r (db->query<base> (query::id == 1));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ object& o (static_cast<object&> (*i));
+ assert (o.bstr == "" && o.dstr == "" && o.num == 123);
+
+ // Logical delete in SQLite.
+ //
+#ifndef DATABASE_SQLITE
+ try
+ {
+ db->query<base> (query::bstr == "ab"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+#else
+ assert (size (db->query<base> (query::bstr.is_null ())) == 1);
+#endif
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end () &&
+ i->bstr == "" && i->dstr == "" && i->num);
+
+ // Logical delete in SQLite.
+ //
+#ifndef DATABASE_SQLITE
+ try
+ {
+ db->query<object> (query::dstr == "abc"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+#else
+ assert (size (db->query<object> (query::dstr.is_null ())) == 1);
+#endif
+ t.commit ();
+ }
+
+ object o (2);
+ o.bstr = "bc";
+ o.dstr = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->bstr == "" && p->dstr == "" && p->num == 234);
+ t.commit ();
+ }
+
+ o.bstr += 'd';
+ o.dstr += 'e';
+ o.num++;
+
+ {
+ transaction t (db->begin ());
+ db->update (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->bstr == "" && p->dstr == "" && p->num == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<base> (2);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted section member in polymorphic classes.
+ //
+ {
+ using namespace test10;
+
+ // Now none of the database operations should include the
+ // deleted members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> p (db->load<base> (1));
+
+ // Logical delete in SQLite.
+ //
+#ifndef DATABASE_SQLITE
+ try
+ {
+ db->load (*p, p->s); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+#endif
+ t.commit ();
+ }
+
+ object o (2);
+ o.bstr = "bc";
+ o.dstr = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (static_cast<base&> (o));
+ t.commit ();
+ }
+
+ o.bstr += 'd';
+ o.dstr += 'e';
+ o.num++;
+ o.s.change ();
+
+ {
+ transaction t (db->begin ());
+ db->update (static_cast<base&> (o));
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<base> (2);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted members of a section in polymorphic classes.
+ //
+ {
+ using namespace test11;
+
+ // Now none of the database operations should include the
+ // deleted members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<base> p (db->load<base> (1));
+ db->load (*p, p->s);
+ object& o (static_cast<object&> (*p));
+ assert (o.bstr == "" && o.dstr == "" && o.num == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<base> query;
+ typedef odb::result<base> result;
+
+ transaction t (db->begin ());
+ result r (db->query<base> (query::id == 1));
+ result::iterator i (r.begin ());
+ db->load (*i, i->s);
+ assert (i != r.end ());
+ object& o (static_cast<object&> (*i));
+ assert (o.bstr == "" && o.dstr == "" && o.num == 123);
+
+ // Logical delete in SQLite.
+ //
+#ifndef DATABASE_SQLITE
+ try
+ {
+ db->query<base> (query::bstr == "ab"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+#else
+ assert (size (db->query<base> (query::bstr.is_null ())) == 1);
+#endif
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ db->load (*i, i->s);
+ assert (i != r.end () &&
+ i->bstr == "" && i->dstr == "" && i->num);
+
+ // Logical delete in SQLite.
+ //
+#ifndef DATABASE_SQLITE
+ try
+ {
+ db->query<object> (query::dstr == "abc"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+#else
+ assert (size (db->query<object> (query::dstr.is_null ())) == 1);
+#endif
+ t.commit ();
+ }
+
+ object o (2);
+ o.bstr = "bc";
+ o.dstr = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->bstr == "" && p->dstr == "" && p->num == 234);
+ t.commit ();
+ }
+
+ o.bstr += 'd';
+ o.dstr += 'e';
+ o.num++;
+ o.s.change ();
+
+ {
+ transaction t (db->begin ());
+ db->update (static_cast<base&> (o));
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->bstr == "" && p->dstr == "" && p->num == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<base> (2);
+ t.commit ();
+ }
+
+ // Test empty statement detection in sections.
+ //
+ base b (3);
+ b.bstr = "bc";
+
+ {
+ transaction t (db->begin ());
+ db->persist (b);
+ unique_ptr<base> p (db->load<base> (3));
+ db->load (*p, p->s);
+ assert (p->bstr == "");
+ t.commit ();
+ }
+
+ b.bstr += 'd';
+ b.s.change ();
+
+ {
+ transaction t (db->begin ());
+ db->update (b);
+ unique_ptr<base> p (db->load<base> (3));
+ db->load (*p, p->s);
+ assert (p->bstr == "");
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted member and optimistic concurrency.
+ //
+ {
+ using namespace test12;
+
+ // Now none of the database operations should include the
+ // deleted members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->str == "" && p->num == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "" && i->num == 123);
+
+ // Logical delete in SQLite.
+ //
+#ifndef DATABASE_SQLITE
+ try
+ {
+ db->query<object> (query::str == "abc"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+#else
+ assert (size (db->query<object> (query::str.is_null ())) == 1);
+#endif
+ t.commit ();
+ }
+
+ object o (2);
+ o.str = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->str == "" && p->num == 234);
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+
+ {
+ transaction t (db->begin ());
+ unsigned long long v (o.v_);
+ db->update (o);
+ assert (o.v_ != v);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->str == "" && p->num == 235 && p->v_ == o.v_);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<object> (2);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted member in an object without id.
+ //
+ {
+ using namespace test13;
+
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ // Now none of the database operations should include the
+ // deleted members.
+ //
+ {
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "" && i->num == 123);
+
+ // Logical delete in SQLite.
+ //
+#ifndef DATABASE_SQLITE
+ try
+ {
+ db->query<object> (query::str == "abc"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+#else
+ assert (size (db->query<object> (query::str.is_null ())) == 1);
+#endif
+ t.commit ();
+ }
+
+ object o;
+ o.str = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+
+ result r (db->query<object> (query::num == 234));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "" && i->num == 234);
+
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase_query<object> (query::num == 234);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted member in an object with auto id.
+ //
+ {
+ using namespace test14;
+
+ // Now none of the database operations should include the
+ // deleted members.
+ //
+ unsigned long id;
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->str == "" && i->num == 123);
+ id = i->id;
+
+ // Logical delete in SQLite.
+ //
+#ifndef DATABASE_SQLITE
+ try
+ {
+ db->query<object> (query::str == "abc"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+#else
+ assert (size (db->query<object> (query::str.is_null ())) == 1);
+#endif
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (id));
+ assert (p->str == "" && p->num == 123);
+ t.commit ();
+ }
+
+ object o;
+ o.str = "bcd";
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (o.id));
+ assert (p->str == "" && p->num == 234);
+ t.commit ();
+ }
+
+ o.str += 'e';
+ o.num++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (o.id));
+ assert (p->str == "" && p->num == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<object> (o.id);
+ t.commit ();
+ }
+ }
+
+ // Test summarily deleted composite values.
+ //
+ {
+ using namespace test15;
+
+ // Now none of the database operations should include the
+ // deleted members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->v.get () == 0 && p->num == 123);
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->v.get () == 0 && i->num == 123);
+
+ // Logical delete in SQLite.
+ //
+#ifndef DATABASE_SQLITE
+ try
+ {
+ db->query<object> (query::v.str == "abc"); // No such column.
+ assert (false);
+ }
+ catch (const odb::exception&) {}
+#else
+ assert (size (db->query<object> (query::v.str.is_null ())) == 1);
+#endif
+ t.commit ();
+ }
+
+ object o (2);
+ o.num = 234;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->v.get () == 0 && p->num == 234);
+ t.commit ();
+ }
+
+ o.num++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->v.get () == 0 && p->num == 235);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<object> (2);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted container member in a non-versioned object.
+ //
+ {
+ using namespace test21;
+
+ // Now none of the database operations should include the
+ // deleted members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ assert (p->num == 123 && p->vec.empty ());
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->num == 123 && i->vec.empty ());
+ t.commit ();
+ }
+
+ object o (2);
+ o.num = 234;
+ o.vec.push_back (234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->num == 234 && p->vec.empty ());
+ t.commit ();
+ }
+
+ o.num++;
+ o.vec.modify (0)++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ assert (p->num == 235 && p->vec.empty ());
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<object> (2);
+ t.commit ();
+ }
+ }
+
+ // Test soft-deleted container member in a non-versioned section.
+ //
+ {
+ using namespace test22;
+
+ // Now none of the database operations should include the
+ // deleted members.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> p (db->load<object> (1));
+ db->load (*p, p->s);
+ assert (p->num == 123 && p->vec.empty ());
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::num == 123));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ db->load (*i, i->s);
+ assert (i->num == 123 && i->vec.empty ());
+ t.commit ();
+ }
+
+ object o (2);
+ o.num = 234;
+ o.vec.push_back (234);
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->num == 234 && p->vec.empty ());
+ t.commit ();
+ }
+
+ o.num++;
+ o.vec.modify (0)++; // No longer automatically marks as changed.
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->num == 234 && p->vec.empty ());
+ t.commit ();
+ }
+
+ o.s.change ();
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ unique_ptr<object> p (db->load<object> (2));
+ db->load (*p, p->s);
+ assert (p->num == 235 && p->vec.empty ());
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase<object> (2);
+ t.commit ();
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl;
+ return 1;
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/evolution/soft-delete/model.hxx b/odb-tests/evolution/soft-delete/model.hxx
new file mode 100644
index 0000000..093aadf
--- /dev/null
+++ b/odb-tests/evolution/soft-delete/model.hxx
@@ -0,0 +1,512 @@
+// file : evolution/soft-delete/model.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef MODEL_VERSION
+# error model.hxx included directly
+#endif
+
+#include <string>
+#include <memory> // unique_ptr
+
+#include <odb/core.hxx>
+#include <odb/vector.hxx>
+#include <odb/section.hxx>
+#include <odb/lazy-ptr.hxx>
+
+#pragma db model version(1, MODEL_VERSION)
+
+#define MODEL_NAMESPACE_IMPL(V) v##V
+#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V)
+
+namespace MODEL_NAMESPACE(MODEL_VERSION)
+{
+ // Test soft-deleted objects.
+ //
+ #pragma db namespace table("t1_")
+ namespace test1
+ {
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ unsigned long num;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db object(object) deleted(3)
+#endif
+ }
+
+ // Test basic soft-deleted member logic.
+ //
+ #pragma db namespace table("t2_")
+ namespace test2
+ {
+ struct object;
+
+ #pragma db object
+ struct object1
+ {
+ object1 (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ odb::vector<odb::lazy_ptr<object> > ptrs;
+ };
+
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id), ptr (0) {}
+ ~object () {delete ptr;}
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string str;
+ unsigned long num;
+ odb::vector<int> vec;
+
+ #pragma db inverse(ptrs)
+ object1* ptr;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::str) deleted(3)
+ #pragma db member(object::vec) deleted(3)
+ #pragma db member(object::ptr) deleted(3)
+#endif
+ }
+
+ // Test empty statement handling (INSERT, UPDATE).
+ //
+ #pragma db namespace table("t3_")
+ namespace test3
+ {
+ #pragma db object
+ struct object
+ {
+ #pragma db id auto
+ unsigned long id;
+
+ std::string str;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::str) deleted(3)
+#endif
+ }
+
+ // Test empty statement handling (SELECT in polymorphic loader).
+ //
+ #pragma db namespace table("t4_")
+ namespace test4
+ {
+ #pragma db object polymorphic
+ struct base
+ {
+ virtual
+ ~base () {}
+ base (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+ };
+
+ #pragma db object
+ struct object: base
+ {
+ object (unsigned long id = 0): base (id) {}
+
+ std::string str;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::str) deleted(3)
+#endif
+ }
+
+ // Test container with soft-deleted value member.
+ //
+ #pragma db namespace table("t5_")
+ namespace test5
+ {
+ #pragma db value
+ struct value
+ {
+ value () {}
+ value (const std::string& s, unsigned long n): str (s), num (n) {}
+
+ std::string str;
+ unsigned long num;
+ };
+
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ odb::vector<value> vec;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(value::str) deleted(3)
+#endif
+ }
+
+ // Test view with soft-deleted member.
+ //
+ #pragma db namespace table("t6_")
+ namespace test6
+ {
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string str;
+ unsigned long num;
+ };
+
+ #pragma db view object(object)
+ struct view
+ {
+ std::string str;
+ unsigned long num;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::str) deleted(3)
+ #pragma db member(view::str) deleted(3)
+#endif
+ }
+
+ // Test soft-deleted section member.
+ //
+ #pragma db namespace table("t7_")
+ namespace test7
+ {
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db load(lazy) update(change)
+ odb::section s;
+
+ #pragma db section(s)
+ std::string str;
+
+ unsigned long num;
+
+ #pragma db section(s)
+ odb::vector<int> vec;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::s) deleted(3)
+#endif
+ }
+
+ // Test soft-deleted members of a section.
+ //
+ #pragma db namespace table("t8_")
+ namespace test8
+ {
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db load(lazy) update(change)
+ odb::section s;
+
+ #pragma db section(s)
+ std::string str;
+
+ #pragma db section(s)
+ unsigned long num;
+
+ #pragma db section(s)
+ odb::vector<int> vec;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::str) deleted(3)
+ #pragma db member(object::vec) deleted(3)
+#endif
+ }
+
+ // Test basic soft-deleted member logic in polymorphic classes.
+ //
+ #pragma db namespace table("t9_")
+ namespace test9
+ {
+ #pragma db object polymorphic
+ struct base
+ {
+ virtual
+ ~base () {}
+ base (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string bstr;
+ };
+
+ #pragma db object
+ struct object: base
+ {
+ object (unsigned long id = 0): base (id) {}
+
+ std::string dstr;
+ unsigned long num;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(base::bstr) deleted(3)
+ #pragma db member(object::dstr) deleted(3)
+#endif
+ }
+
+ // Test soft-deleted section member in polymorphic classes.
+ //
+ #pragma db namespace table("t10_")
+ namespace test10
+ {
+ #pragma db object polymorphic
+ struct base
+ {
+ virtual
+ ~base () {}
+ base (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db load(lazy) update(change)
+ odb::section s;
+
+ #pragma db section(s)
+ std::string bstr;
+ };
+
+ #pragma db object
+ struct object: base
+ {
+ object (unsigned long id = 0): base (id) {}
+
+ #pragma db section(s)
+ std::string dstr;
+
+ unsigned long num;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(base::s) deleted(3)
+#endif
+ }
+
+ // Test soft-deleted members of a section in polymorphic classes.
+ //
+ #pragma db namespace table("t11_")
+ namespace test11
+ {
+ #pragma db object polymorphic
+ struct base
+ {
+ virtual
+ ~base () {}
+ base (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db load(lazy) update(change)
+ odb::section s;
+
+ #pragma db section(s)
+ std::string bstr;
+ };
+
+ #pragma db object
+ struct object: base
+ {
+ object (unsigned long id = 0): base (id) {}
+
+ #pragma db section(s)
+ std::string dstr;
+
+ #pragma db section(s)
+ unsigned long num;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(base::bstr) deleted(3)
+ #pragma db member(object::dstr) deleted(3)
+#endif
+ }
+
+ // Test soft-deleted member and optimistic concurrency.
+ //
+ #pragma db namespace table("t12_")
+ namespace test12
+ {
+ #pragma db object optimistic
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db version mssql:type("ROWVERSION")
+ unsigned long long v_;
+
+ std::string str;
+ unsigned long num;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::str) deleted(3)
+#endif
+ }
+
+ // Test soft-deleted member in an object without id.
+ //
+ #pragma db namespace table("t13_")
+ namespace test13
+ {
+ #pragma db object no_id
+ struct object
+ {
+ std::string str;
+ unsigned long num;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::str) deleted(3)
+#endif
+ }
+
+ // Test soft-deleted member in an object with auto id.
+ //
+ #pragma db namespace table("t14_")
+ namespace test14
+ {
+ #pragma db object
+ struct object
+ {
+ std::string str;
+ unsigned long num;
+
+ #pragma db id auto
+ unsigned long id;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::str) deleted(3)
+#endif
+ }
+
+ // Test summarily deleted composite values.
+ //
+ #pragma db namespace table("t15_")
+ namespace test15
+ {
+ #pragma db value
+ struct value
+ {
+ std::string str;
+ odb::vector<int> vec;
+ };
+
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ std::unique_ptr<value> v;
+ unsigned long num;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(value::str) deleted(3)
+ #pragma db member(value::vec) deleted(3)
+#endif
+ }
+
+ // Test soft-deleted container member in a non-versioned object.
+ //
+ #pragma db namespace table("t21_")
+ namespace test21
+ {
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ unsigned long num;
+ odb::vector<int> vec;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::vec) deleted(3)
+#endif
+ }
+
+ // Test soft-deleted container member in a non-versioned section.
+ //
+ #pragma db namespace table("t22_")
+ namespace test22
+ {
+ #pragma db object
+ struct object
+ {
+ object (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db load(lazy) update(change)
+ odb::section s;
+
+ #pragma db section(s)
+ unsigned long num;
+
+ #pragma db section(s)
+ odb::vector<int> vec;
+ };
+
+#if MODEL_VERSION == 3
+ #pragma db member(object::vec) deleted(3)
+#endif
+ }
+}
+
+#undef MODEL_NAMESPACE
+#undef MODEL_NAMESPACE_IMPL
diff --git a/odb-tests/evolution/soft-delete/test1.hxx b/odb-tests/evolution/soft-delete/test1.hxx
new file mode 100644
index 0000000..d4df90f
--- /dev/null
+++ b/odb-tests/evolution/soft-delete/test1.hxx
@@ -0,0 +1,9 @@
+// file : evolution/soft-delete/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+#pragma db model version(1, 1)
+
+#endif // TEST1_HXX
diff --git a/odb-tests/evolution/soft-delete/test2.hxx b/odb-tests/evolution/soft-delete/test2.hxx
new file mode 100644
index 0000000..3b2b5b4
--- /dev/null
+++ b/odb-tests/evolution/soft-delete/test2.hxx
@@ -0,0 +1,11 @@
+// file : evolution/soft-delete/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+#define MODEL_VERSION 2
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST2_HXX
diff --git a/odb-tests/evolution/soft-delete/test3.hxx b/odb-tests/evolution/soft-delete/test3.hxx
new file mode 100644
index 0000000..5a90ab2
--- /dev/null
+++ b/odb-tests/evolution/soft-delete/test3.hxx
@@ -0,0 +1,11 @@
+// file : evolution/soft-delete/test3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST3_HXX
+#define TEST3_HXX
+
+#define MODEL_VERSION 3
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST3_HXX
diff --git a/odb-tests/evolution/soft-delete/testscript b/odb-tests/evolution/soft-delete/testscript
new file mode 100644
index 0000000..615bdb1
--- /dev/null
+++ b/odb-tests/evolution/soft-delete/testscript
@@ -0,0 +1,54 @@
+# file : evolution/soft-delete/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../$db-schema.testscript
+
+test.arguments += $($(db)_options)
+
+: basics
+:
+if! $sqlite
+{
+ ss =; # Schema modification base file names.
+
+ # Drop everything.
+ #
+ for s: $schemas
+ ss =+ $s
+ end;
+
+ # Add base schema.
+ #
+ ss += test3-002-pre test3-002-post;
+
+ # Add migration.
+ #
+ ss += test3-003-pre test3-003-post;
+
+ # Run tests.
+ #
+ for s: $ss
+ f = $out_base/"$s".sql
+
+ if $mysql
+ cat $f | $create_schema_cmd
+ elif $pgsql
+ $create_schema_cmd -f $f
+ end
+
+ if ($s == 'test3-002-post')
+ $* 1
+ elif ($s == 'test3-003-pre')
+ $* 2
+ elif ($s == 'test3-003-post')
+ $* 3
+ end
+ end
+}
+else
+{
+ $* 1 &odb-test.db;
+ $* 2;
+ $* 3
+}
diff --git a/odb-tests/evolution/version/buildfile b/odb-tests/evolution/version/buildfile
new file mode 100644
index 0000000..f11559f
--- /dev/null
+++ b/odb-tests/evolution/version/buildfile
@@ -0,0 +1,63 @@
+# file : evolution/version/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+ assert (!$multi) "multi-database mode is not supported by this test"
+
+db = ($databases[0])
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-$db%lib{odb-$db}
+import libs += lib{common}
+
+hdrs = test1 test2 test3
+
+exe{driver}: {hxx cxx}{* -*-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+for h: $hdrs
+ exe{driver}: {hxx ixx cxx}{$h-odb}: hxx{$h} libue{test-meta} hxx{model}
+
+# Make sure testN.hxx are compiled serially since they share the changelog.
+#
+# @@ TODO: make order-only when supported by build2.
+#
+{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix evo_version_ \
+ --schema-version-table evo_version_sv \
+ --generate-schema \
+ --generate-query \
+ --at-once \
+ --changelog $out_base/model.xml
+
+<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog
+<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration
+
+<{hxx ixx cxx}{test3-odb}>:
+{
+ odb_options += --omit-create
+ schema_versions = 002 003
+}
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
+
+testscript@./:
+{
+ db = $db
+ schemas = $hdrs
+}
diff --git a/odb-tests/evolution/version/driver.cxx b/odb-tests/evolution/version/driver.cxx
new file mode 100644
index 0000000..cc1b4b9
--- /dev/null
+++ b/odb-tests/evolution/version/driver.cxx
@@ -0,0 +1,161 @@
+// file : evolution/version/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test schema version access via the database instance.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
+#include <libcommon/config.hxx> // DATABASE_XXX
+#include <libcommon/common.hxx>
+
+#include "test2.hxx"
+#include "test3.hxx"
+#include "test2-odb.hxx"
+#include "test3-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_database (argc, argv, false));
+
+ db->schema_version_table ("evo_version_sv");
+
+ bool embedded (schema_catalog::exists (*db));
+
+ // 1 - base version
+ // 2 - migration
+ // 3 - current version
+ //
+ unsigned short pass (*argv[argc - 1] - '0');
+
+ switch (pass)
+ {
+ case 1:
+ {
+ using namespace v2;
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::drop_schema (*db);
+
+ assert (db->schema_version () == 0);
+
+ schema_catalog::create_schema (*db, "", false);
+
+ assert (db->schema_version () == 1 && !db->schema_migration ());
+
+ schema_catalog::migrate_schema (*db, 2);
+ t.commit ();
+ }
+
+ assert (db->schema_version () == 2 && !db->schema_migration ());
+
+ {
+ transaction t (db->begin ());
+ object1 o1 (1);
+ o1.num = 123;
+ db->persist (o1);
+ t.commit ();
+ }
+ break;
+ }
+ case 2:
+ {
+ using namespace v2;
+ using namespace v3;
+
+ if (embedded)
+ {
+ assert (db->schema_version () == 2 && !db->schema_migration ());
+
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_pre (*db, 3);
+ t.commit ();
+ }
+
+ assert (db->schema_version () == 3 && db->schema_migration ());
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object1> o1 (db->load<object1> (1));
+ object2 o2 (1);
+ o2.num = o1->num;
+ db->persist (o2);
+ t.commit ();
+ }
+
+ if (embedded)
+ {
+ transaction t (db->begin ());
+ schema_catalog::migrate_schema_post (*db, 3);
+ t.commit ();
+
+ assert (db->schema_version () == 3 && !db->schema_migration ());
+ }
+ break;
+ }
+ case 3:
+ {
+ using namespace v3;
+
+ // In transaction.
+ //
+ {
+ transaction t (db->begin ());
+ assert (db->schema_version () == 3 && !db->schema_migration ());
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object2> o2 (db->load<object2> (1));
+ assert (o2->num == 123);
+ t.commit ();
+ }
+
+ // Test the case where there is still no version table.
+ //
+ db->schema_version_migration (0, false);
+
+ {
+ transaction t (db->begin ());
+
+#ifdef DATABASE_ORACLE
+ db->execute ("DROP TABLE \"evo_version_sv\"");
+#else
+ db->execute ("DROP TABLE evo_version_sv");
+#endif
+ t.commit ();
+ }
+
+ assert (db->schema_version () == 0);
+ break;
+ }
+ default:
+ {
+ cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl;
+ return 1;
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/evolution/version/model.hxx b/odb-tests/evolution/version/model.hxx
new file mode 100644
index 0000000..cdda00e
--- /dev/null
+++ b/odb-tests/evolution/version/model.hxx
@@ -0,0 +1,45 @@
+// file : evolution/version/model.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef MODEL_VERSION
+# error model.hxx included directly
+#endif
+
+#include <odb/core.hxx>
+
+#pragma db model version(1, MODEL_VERSION)
+
+#define MODEL_NAMESPACE_IMPL(V) v##V
+#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V)
+
+namespace MODEL_NAMESPACE(MODEL_VERSION)
+{
+#if MODEL_VERSION == 2
+ #pragma db object
+ struct object1
+ {
+ object1 (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ int num;
+ };
+#endif
+
+#if MODEL_VERSION == 3
+ #pragma db object
+ struct object2
+ {
+ object2 (unsigned long id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ int num;
+ };
+#endif
+}
+
+#undef MODEL_NAMESPACE
+#undef MODEL_NAMESPACE_IMPL
diff --git a/odb-tests/evolution/version/test1.hxx b/odb-tests/evolution/version/test1.hxx
new file mode 100644
index 0000000..a50e54c
--- /dev/null
+++ b/odb-tests/evolution/version/test1.hxx
@@ -0,0 +1,9 @@
+// file : evolution/version/test1.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST1_HXX
+#define TEST1_HXX
+
+#pragma db model version(1, 1)
+
+#endif // TEST1_HXX
diff --git a/odb-tests/evolution/version/test2.hxx b/odb-tests/evolution/version/test2.hxx
new file mode 100644
index 0000000..f7fc1b7
--- /dev/null
+++ b/odb-tests/evolution/version/test2.hxx
@@ -0,0 +1,11 @@
+// file : evolution/version/test2.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST2_HXX
+#define TEST2_HXX
+
+#define MODEL_VERSION 2
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST2_HXX
diff --git a/odb-tests/evolution/version/test3.hxx b/odb-tests/evolution/version/test3.hxx
new file mode 100644
index 0000000..364ee31
--- /dev/null
+++ b/odb-tests/evolution/version/test3.hxx
@@ -0,0 +1,11 @@
+// file : evolution/version/test3.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST3_HXX
+#define TEST3_HXX
+
+#define MODEL_VERSION 3
+#include "model.hxx"
+#undef MODEL_VERSION
+
+#endif // TEST3_HXX
diff --git a/odb-tests/evolution/version/testscript b/odb-tests/evolution/version/testscript
new file mode 100644
index 0000000..1a41b2d
--- /dev/null
+++ b/odb-tests/evolution/version/testscript
@@ -0,0 +1,54 @@
+# file : evolution/version/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../$db-schema.testscript
+
+test.arguments += $($(db)_options)
+
+: basics
+:
+if! $sqlite
+{
+ ss =; # Schema modification base file names.
+
+ # Drop everything.
+ #
+ for s: $schemas
+ ss =+ $s
+ end;
+
+ # Add base schema.
+ #
+ ss += test3-002-pre test3-002-post;
+
+ # Add migration.
+ #
+ ss += test3-003-pre test3-003-post;
+
+ # Run tests.
+ #
+ for s: $ss
+ f = $out_base/"$s".sql
+
+ if $mysql
+ cat $f | $create_schema_cmd
+ elif $pgsql
+ $create_schema_cmd -f $f
+ end
+
+ if ($s == 'test3-002-post')
+ $* 1
+ elif ($s == 'test3-003-pre')
+ $* 2
+ elif ($s == 'test3-003-post')
+ $* 3
+ end
+ end
+}
+else
+{
+ $* 1 &odb-test.db;
+ $* 2;
+ $* 3
+}
diff --git a/odb-tests/libcommon/.gitignore b/odb-tests/libcommon/.gitignore
new file mode 100644
index 0000000..a994ddc
--- /dev/null
+++ b/odb-tests/libcommon/.gitignore
@@ -0,0 +1,3 @@
+# Generated config header.
+#
+config.hxx
diff --git a/odb-tests/libcommon/buffer.hxx b/odb-tests/libcommon/buffer.hxx
new file mode 100644
index 0000000..41b7e46
--- /dev/null
+++ b/odb-tests/libcommon/buffer.hxx
@@ -0,0 +1,104 @@
+// file : libcommon/buffer.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef LIBCOMMON_BUFFER_HXX
+#define LIBCOMMON_BUFFER_HXX
+
+#include <new>
+#include <cstddef> // std::size_t
+#include <cstring> // std::{memcmp,memcpy}
+
+struct basic_buffer_base
+{
+ ~basic_buffer_base ()
+ {
+ operator delete (data_);
+ }
+
+ basic_buffer_base ()
+ : data_ (0), size_ (0)
+ {
+ }
+
+ basic_buffer_base (const void* data, std::size_t size)
+ : data_ (0), size_ (size)
+ {
+ data_ = operator new (size_);
+ std::memcpy (data_, data, size_);
+ }
+
+ basic_buffer_base (const basic_buffer_base& y)
+ : data_ (0), size_ (0)
+ {
+ assign (y.data_, y.size_);
+ }
+
+ basic_buffer_base&
+ operator= (const basic_buffer_base& y)
+ {
+ if (this != &y)
+ assign (y.data_, y.size_);
+
+ return *this;
+ }
+
+ void
+ assign (const void* data, std::size_t size)
+ {
+ if (size_ < size)
+ {
+ void *p (operator new (size));
+ operator delete (data_);
+ data_ = p;
+ }
+
+ std::memcpy (data_, data, size);
+ size_ = size;
+ }
+
+ std::size_t
+ size () const
+ {
+ return size_;
+ }
+
+ bool
+ operator== (const basic_buffer_base& y) const
+ {
+ return size_ == y.size_ && std::memcmp (data_, y.data_, size_) == 0;
+ }
+
+protected:
+ void* data_;
+ std::size_t size_;
+};
+
+template <typename T>
+struct basic_buffer: basic_buffer_base
+{
+ basic_buffer ()
+ {
+ }
+
+ basic_buffer (const T* data, std::size_t size)
+ : basic_buffer_base (data, size)
+ {
+ }
+
+ T*
+ data ()
+ {
+ return static_cast<T*> (data_);
+ }
+
+ const T*
+ data () const
+ {
+ return static_cast<const T*> (data_);
+ }
+};
+
+typedef basic_buffer<char> buffer;
+typedef basic_buffer<unsigned char> ubuffer;
+
+#endif // LIBCOMMON_BUFFER_HXX
diff --git a/odb-tests/libcommon/buildfile b/odb-tests/libcommon/buildfile
new file mode 100644
index 0000000..eb61455
--- /dev/null
+++ b/odb-tests/libcommon/buildfile
@@ -0,0 +1,50 @@
+# file : libcommon/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import intf_libs = libodb%lib{odb}
+
+for db: $databases
+ import intf_libs += libodb-$db%lib{odb-$db}
+
+lib{common}: {hxx ixx txx cxx}{** -config} hxx{config} $intf_libs
+
+# Generated config file.
+#
+using autoconf
+
+hxx{config}: in{config}
+{
+ DATABASE_MYSQL = $mysql
+ DATABASE_SQLITE = $sqlite
+ DATABASE_PGSQL = $pgsql
+ DATABASE_ORACLE = $oracle
+ DATABASE_MSSQL = $mssql
+ MULTI_DATABASE = $multi
+}
+
+# Build options.
+#
+cxx.poptions =+ "-I$out_root" "-I$src_root"
+
+{hbmia obja}{*}: cxx.poptions += -DLIBCOMMON_STATIC_BUILD
+{hbmis objs}{*}: cxx.poptions += -DLIBCOMMON_SHARED_BUILD
+
+# Export options.
+#
+lib{common}:
+{
+ cxx.export.poptions = "-I$out_root" "-I$src_root"
+ cxx.export.libs = $intf_libs
+}
+
+liba{common}: cxx.export.poptions += -DLIBCOMMON_STATIC
+libs{common}: cxx.export.poptions += -DLIBCOMMON_SHARED
+
+# For pre-releases use the complete version to make sure they cannot
+# be used in place of another pre-release or the final version. See
+# the version module for details on the version.* variable values.
+#
+if $version.pre_release
+ lib{common}: bin.lib.version = "-$version.project_id"
+else
+ lib{common}: bin.lib.version = "-$version.major.$version.minor"
diff --git a/odb-tests/libcommon/common.cxx b/odb-tests/libcommon/common.cxx
new file mode 100644
index 0000000..4b4603d
--- /dev/null
+++ b/odb-tests/libcommon/common.cxx
@@ -0,0 +1,364 @@
+// file : libcommon/common.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstdlib> // std::exit
+#include <utility> // std::move
+#include <iostream>
+
+#include <odb/database.hxx>
+
+#include <libcommon/config.hxx>
+#include <libcommon/common.hxx>
+
+// MySQL.
+//
+#if defined(DATABASE_MYSQL)
+
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/connection-factory.hxx>
+
+static std::unique_ptr<odb::database>
+create_mysql_database (int& argc, char* argv[], bool, size_t max_connections)
+{
+ using namespace std;
+ using namespace odb::core;
+ namespace mysql = odb::mysql;
+
+ unique_ptr<mysql::connection_factory> f;
+
+ if (max_connections != 0)
+ f.reset (new mysql::connection_pool_factory (max_connections));
+
+ return unique_ptr<database> (
+ new mysql::database (argc, argv, false, "", 0, move (f)));
+}
+#endif // MySQL
+
+
+// SQLite.
+//
+#if defined(DATABASE_SQLITE)
+
+#include <odb/connection.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/connection-factory.hxx>
+
+static std::unique_ptr<odb::database>
+create_sqlite_database (int& argc,
+ char* argv[],
+ bool schema,
+ size_t max_connections)
+{
+ using namespace std;
+ using namespace odb::core;
+ namespace sqlite = odb::sqlite;
+
+ unique_ptr<sqlite::connection_factory> f;
+
+ if (max_connections != 0)
+ f.reset (new sqlite::connection_pool_factory (max_connections));
+
+ unique_ptr<database> db (
+ new sqlite::database (
+ argc, argv, false,
+ SQLITE_OPEN_READWRITE
+ | SQLITE_OPEN_CREATE
+#ifdef SQLITE_OPEN_URI
+ | SQLITE_OPEN_URI
+#endif
+ ,
+ true,
+ "",
+ move (f)));
+
+ // Create the database schema. Due to bugs in SQLite foreign key
+ // support for DDL statements, we need to temporarily disable
+ // foreign keys.
+ //
+ if (schema)
+ {
+ connection_ptr c (db->connection ());
+
+ c->execute ("PRAGMA foreign_keys=OFF");
+
+ transaction t (c->begin ());
+ schema_catalog::create_schema (*db);
+ t.commit ();
+
+ c->execute ("PRAGMA foreign_keys=ON");
+ }
+
+ return db;
+}
+#endif // SQLite
+
+
+// PostgreSQL.
+//
+#if defined(DATABASE_PGSQL)
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/connection-factory.hxx>
+
+static std::unique_ptr<odb::database>
+create_pgsql_database (int& argc, char* argv[], bool, size_t max_connections)
+{
+ using namespace std;
+ using namespace odb::core;
+ namespace pgsql = odb::pgsql;
+
+ unique_ptr<pgsql::connection_factory> f;
+
+ if (max_connections != 0)
+ f.reset (new pgsql::connection_pool_factory (max_connections));
+
+ return unique_ptr<database> (
+ new pgsql::database (argc, argv, false, "", move (f)));
+}
+#endif // PostgreSQL
+
+
+// Oracle.
+//
+#if defined(DATABASE_ORACLE)
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/connection-factory.hxx>
+
+static std::unique_ptr<odb::database>
+create_oracle_database (int& argc, char* argv[], bool, size_t max_connections)
+{
+ using namespace std;
+ using namespace odb::core;
+ namespace oracle = odb::oracle;
+
+ unique_ptr<oracle::connection_factory> f;
+
+ if (max_connections != 0)
+ f.reset (new oracle::connection_pool_factory (max_connections));
+
+ // Set client database character set and client national character set
+ // to UTF-8.
+ //
+ return unique_ptr<database> (
+ new oracle::database (argc, argv, false, 873, 873, 0, move (f)));
+}
+#endif // Oracle
+
+// SQL Server.
+//
+#if defined(DATABASE_MSSQL)
+
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/connection-factory.hxx>
+
+static std::unique_ptr<odb::database>
+create_mssql_database (int& argc, char* argv[], bool, size_t max_connections)
+{
+ using namespace std;
+ using namespace odb::core;
+ namespace mssql = odb::mssql;
+
+ unique_ptr<mssql::connection_factory> f;
+
+ if (max_connections != 0)
+ f.reset (new mssql::connection_pool_factory (max_connections));
+
+ return unique_ptr<database> (
+ new mssql::database (argc, argv, false, "",
+ mssql::isolation_read_committed, 0, move (f)));
+}
+#endif // SQL Server
+
+//
+//
+std::unique_ptr<odb::database>
+create_database (int argc,
+ char* argv[],
+ bool schema,
+ size_t max_connections,
+#if defined(MULTI_DATABASE)
+ odb::database_id db
+#else
+ odb::database_id
+#endif
+)
+{
+ using namespace std;
+ using namespace odb::core;
+
+ char** argp = argv + 1; // Position of the next argument. Assignment for VC8.
+ int argn (argc - 1); // Number of arguments left.
+
+#if defined(MULTI_DATABASE)
+ // Figure out which database we are creating. We may be given the
+ // database name as a program argument or as an id.
+ //
+ if (db == odb::id_common && argn != 0)
+ {
+ string s (*argp);
+
+ if (s == "mysql")
+ db = odb::id_mysql;
+ else if (s == "sqlite")
+ db = odb::id_sqlite;
+ else if (s == "pgsql")
+ db = odb::id_pgsql;
+ else if (s == "oracle")
+ db = odb::id_oracle;
+ else if (s == "mssql")
+ db = odb::id_mssql;
+
+ if (db != odb::id_common)
+ {
+ argp++;
+ argn--;
+ }
+ }
+
+ if (db == odb::id_common)
+ {
+ cerr << "Usage: " << argv[0] << " <db> [options]" << endl;
+ exit (1);
+ }
+#endif
+
+ if (argn != 0 && *argp == string ("--help"))
+ {
+#if defined(MULTI_DATABASE)
+ cout << "Usage: " << argv[0] << " <db> [options]" << endl;
+#else
+ cout << "Usage: " << argv[0] << " [options]" << endl;
+#endif
+
+ cout << "Options:" << endl;
+
+#if defined(MULTI_DATABASE)
+ switch (db)
+ {
+ case odb::id_mysql:
+#if defined(DATABASE_MYSQL)
+ odb::mysql::database::print_usage (cout);
+#else
+ assert (false);
+#endif
+ break;
+ case odb::id_sqlite:
+#if defined(DATABASE_SQLITE)
+ odb::sqlite::database::print_usage (cout);
+#else
+ assert (false);
+#endif
+ break;
+ case odb::id_pgsql:
+#if defined(DATABASE_PGSQL)
+ odb::pgsql::database::print_usage (cout);
+#else
+ assert (false);
+#endif
+ break;
+ case odb::id_oracle:
+#if defined(DATABASE_ORACLE)
+ odb::oracle::database::print_usage (cout);
+#else
+ assert (false);
+#endif
+ break;
+ case odb::id_mssql:
+#if defined(DATABASE_MSSQL)
+ odb::mssql::database::print_usage (cout);
+#else
+ assert (false);
+#endif
+ break;
+ case odb::id_common:
+ assert (false);
+ }
+#elif defined(DATABASE_MYSQL)
+ odb::mysql::database::print_usage (cout);
+#elif defined(DATABASE_SQLITE)
+ odb::sqlite::database::print_usage (cout);
+#elif defined(DATABASE_PGSQL)
+ odb::pgsql::database::print_usage (cout);
+#elif defined(DATABASE_ORACLE)
+ odb::oracle::database::print_usage (cout);
+#elif defined(DATABASE_MSSQL)
+ odb::mssql::database::print_usage (cout);
+#else
+# error unknown database
+#endif
+
+ exit (0);
+ }
+
+#if defined(MULTI_DATABASE)
+ switch (db)
+ {
+ case odb::id_mysql:
+#if defined(DATABASE_MYSQL)
+ return create_mysql_database (argc, argv, schema, max_connections);
+#else
+ assert (false);
+ break;
+#endif
+ case odb::id_sqlite:
+#if defined(DATABASE_SQLITE)
+ return create_sqlite_database (argc, argv, schema, max_connections);
+#else
+ assert (false);
+ break;
+#endif
+ case odb::id_pgsql:
+#if defined(DATABASE_PGSQL)
+ return create_pgsql_database (argc, argv, schema, max_connections);
+#else
+ assert (false);
+ break;
+#endif
+ case odb::id_oracle:
+#if defined(DATABASE_ORACLE)
+ return create_oracle_database (argc, argv, schema, max_connections);
+#else
+ assert (false);
+ break;
+#endif
+ case odb::id_mssql:
+#if defined(DATABASE_MSSQL)
+ return create_mssql_database (argc, argv, schema, max_connections);
+#else
+ assert (false);
+ break;
+#endif
+ case odb::id_common:
+ assert (false);
+ }
+ return unique_ptr<database> ();
+#elif defined(DATABASE_MYSQL)
+ return create_mysql_database (argc, argv, schema, max_connections);
+#elif defined(DATABASE_SQLITE)
+ return create_sqlite_database (argc, argv, schema, max_connections);
+#elif defined(DATABASE_PGSQL)
+ return create_pgsql_database (argc, argv, schema, max_connections);
+#elif defined(DATABASE_ORACLE)
+ return create_oracle_database (argc, argv, schema, max_connections);
+#elif defined(DATABASE_MSSQL)
+ return create_mssql_database (argc, argv, schema, max_connections);
+#else
+# error unknown database
+#endif
+}
+
+bool
+size_available ()
+{
+#if defined(MULTI_DATABASE) || \
+ defined(DATABASE_SQLITE) || \
+ defined(DATABASE_ORACLE) || \
+ defined(DATABASE_MSSQL)
+ return false;
+#else
+ return true;
+#endif
+}
diff --git a/odb-tests/libcommon/common.hxx b/odb-tests/libcommon/common.hxx
new file mode 100644
index 0000000..9ab978d
--- /dev/null
+++ b/odb-tests/libcommon/common.hxx
@@ -0,0 +1,47 @@
+// file : libcommon/common.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef LIBCOMMON_COMMON_HXX
+#define LIBCOMMON_COMMON_HXX
+
+#include <memory> // std::unique_ptr
+#include <cstddef> // std::size_t
+
+#include <odb/result.hxx>
+#include <odb/database.hxx>
+
+#include <libcommon/export.hxx>
+
+LIBCOMMON_SYMEXPORT std::unique_ptr<odb::database>
+create_database (int argc,
+ char* argv[],
+ bool create_schema = true,
+ std::size_t max_connections = 0,
+ odb::database_id db = odb::id_common);
+
+template <typename T>
+std::unique_ptr<T>
+create_specific_database (int argc,
+ char* argv[],
+ bool create_schema = true,
+ std::size_t max_connections = 0)
+{
+ std::unique_ptr<odb::database> r (
+ create_database (argc, argv,
+ create_schema,
+ max_connections,
+ T::database_id));
+
+ return std::unique_ptr<T> (&dynamic_cast<T&> (*r.release ()));
+}
+
+// This function returns an accurate result only if the result iterator
+// hasn't been advanced and after the call the result is no longer valid.
+//
+template <typename T>
+std::size_t
+size (odb::result<T>);
+
+#include <libcommon/common.txx>
+
+#endif // LIBCOMMON_COMMON_HXX
diff --git a/odb-tests/libcommon/common.txx b/odb-tests/libcommon/common.txx
new file mode 100644
index 0000000..caa7481
--- /dev/null
+++ b/odb-tests/libcommon/common.txx
@@ -0,0 +1,24 @@
+// file : libcommon/common.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// We have to use this helper function instead of just checking which
+// database is used because the DATABASE_* macro may not be defined
+// in a project that includes this header.
+//
+LIBCOMMON_SYMEXPORT bool
+size_available ();
+
+template <typename T>
+std::size_t
+size (odb::result<T> r)
+{
+ if (size_available ())
+ return r.size ();
+ else
+ {
+ std::size_t n (0);
+ for (typename odb::result<T>::iterator i (r.begin ()); i != r.end (); ++i)
+ n++;
+ return n;
+ }
+}
diff --git a/odb-tests/libcommon/concrete.hxx b/odb-tests/libcommon/concrete.hxx
new file mode 100644
index 0000000..e0f64a5
--- /dev/null
+++ b/odb-tests/libcommon/concrete.hxx
@@ -0,0 +1,57 @@
+// file : libcommon/concrete.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef LIBCOMMON_CONCRETE_HXX
+#define LIBCOMMON_CONCRETE_HXX
+
+#include <libcommon/config.hxx>
+
+// Namespace alias for the concrete database namespace.
+//
+#if defined(MULTI_DATABASE)
+
+// Fallback to common interface.
+//
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+namespace odb_db = odb;
+
+#elif defined(DATABASE_MYSQL)
+
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/transaction.hxx>
+
+namespace odb_db = odb::mysql;
+
+#elif defined(DATABASE_SQLITE)
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/transaction.hxx>
+
+namespace odb_db = odb::sqlite;
+
+#elif defined(DATABASE_PGSQL)
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/transaction.hxx>
+
+namespace odb_db = odb::pgsql;
+
+#elif defined(DATABASE_ORACLE)
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/transaction.hxx>
+
+namespace odb_db = odb::oracle;
+
+#elif defined(DATABASE_MSSQL)
+
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/transaction.hxx>
+
+namespace odb_db = odb::mssql;
+
+#endif
+
+#endif // LIBCOMMON_CONCRETE_HXX
diff --git a/odb-tests/libcommon/config.hxx.in b/odb-tests/libcommon/config.hxx.in
new file mode 100644
index 0000000..ff90e61
--- /dev/null
+++ b/odb-tests/libcommon/config.hxx.in
@@ -0,0 +1,14 @@
+// file : libcommon/config.hxx.in
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef LIBCOMMON_CONFIG_HXX
+#define LIBCOMMON_CONFIG_HXX
+
+#undef DATABASE_MYSQL
+#undef DATABASE_SQLITE
+#undef DATABASE_PGSQL
+#undef DATABASE_ORACLE
+#undef DATABASE_MSSQL
+#undef MULTI_DATABASE
+
+#endif // LIBCOMMON_CONFIG_HXX
diff --git a/odb-tests/libcommon/export.hxx b/odb-tests/libcommon/export.hxx
new file mode 100644
index 0000000..0de4565
--- /dev/null
+++ b/odb-tests/libcommon/export.hxx
@@ -0,0 +1,39 @@
+#pragma once
+
+// Normally we don't export class templates (but do complete specializations),
+// inline functions, and classes with only inline member functions. Exporting
+// classes that inherit from non-exported/imported bases (e.g., std::string)
+// will end up badly. The only known workarounds are to not inherit or to not
+// export. Also, MinGW GCC doesn't like seeing non-exported functions being
+// used before their inline definition. The workaround is to reorder code. In
+// the end it's all trial and error.
+
+#if defined(LIBCOMMON_STATIC) // Using static.
+# define LIBCOMMON_SYMEXPORT
+#elif defined(LIBCOMMON_STATIC_BUILD) // Building static.
+# define LIBCOMMON_SYMEXPORT
+#elif defined(LIBCOMMON_SHARED) // Using shared.
+# ifdef _WIN32
+# define LIBCOMMON_SYMEXPORT __declspec(dllimport)
+# else
+# define LIBCOMMON_SYMEXPORT
+# endif
+#elif defined(LIBCOMMON_SHARED_BUILD) // Building shared.
+# ifdef _WIN32
+# define LIBCOMMON_SYMEXPORT __declspec(dllexport)
+# else
+# define LIBCOMMON_SYMEXPORT
+# endif
+#else
+// If none of the above macros are defined, then we assume we are being used
+// by some third-party build system that cannot/doesn't signal the library
+// type. Note that this fallback works for both static and shared libraries
+// provided the library only exports functions (in other words, no global
+// exported data) and for the shared case the result will be sub-optimal
+// compared to having dllimport. If, however, your library does export data,
+// then you will probably want to replace the fallback with the (commented
+// out) error since it won't work for the shared case.
+//
+# define LIBCOMMON_SYMEXPORT // Using static or shared.
+//# error define LIBCOMMON_STATIC or LIBCOMMON_SHARED preprocessor macro to signal libcommon library type being linked
+#endif
diff --git a/odb-tests/manifest b/odb-tests/manifest
new file mode 100644
index 0000000..22e03d9
--- /dev/null
+++ b/odb-tests/manifest
@@ -0,0 +1,146 @@
+: 1
+name: odb-tests
+version: 2.5.0-b.26.z
+project: odb
+type: tests
+language: c++
+summary: ODB compiler tests
+license: GPL-2.0-only
+description-file: README.md
+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 ; Mailing list
+
+# @@ TMP Bump the toolchain version to 0.17.0 after it is released.
+#
+depends: * build2 >= 0.16.0-
+depends: * bpkg >= 0.16.0-
+
+depends: * odb == $
+
+depends: libodb == $
+depends: libodb-mysql == $ ? ($mysql)
+depends: libodb-sqlite == $ ? ($sqlite)
+depends: libodb-pgsql == $ ? ($pgsql)
+depends: libodb-oracle == $ ? ($oracle)
+depends: libodb-mssql == $ ? ($mssql)
+
+# Reflect whether libodb-pgsql is likely to support bulk operations. This is
+# normally the case on POSIX platforms if libodb-pgsql is linked against libpq
+# of the version 14 or above.
+#
+# Note, though, that this approach doesn't work well for libodb-pgsql
+# configured as a system package, since the version of libpq it is linked to
+# may not be the same as what we can get from the repository. Also note that
+# the following depends clause (without alternatives) is required to avoid the
+# 'unable to select dependency alternative' failure when libodb-pgsql is
+# configured as a system package.
+#
+depends: libpq ? ($pgsql)
+depends: libpq >= 14.0.0 ? ($pgsql) config.odb_tests.pgsql.bulk_default=true | \
+ libpq >= 7.4.0 ? ($pgsql) config.odb_tests.pgsql.bulk_default=false
+
+# @@ TODO/LATER: use an alternative to automatically detect whether we are
+# using MySQL or MariaDB? But maybe we don't need to know?
+
+depends: * mysql-client >= 5.0.3 ? ($mysql)
+depends: * psql >= 7.4.0 ? ($pgsql)
+
+# This package configuration is for building with the default bots on target
+# configurations where GCC is the host compiler.
+#
+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.
+
+# These package configurations are for building with the custom bots on target
+# configurations that were customized to use GCC as the host compiler and/or
+# provide proprietary database clients.
+#
+# Note that they serve dual function: they make sure the default configuration
+# is not used (i.e., they match the corresponding configurations in libodb*).
+# And they allow CI'ing odb-tests by itself (thus we specify the bot keys both
+# in libodb* and here).
+#
+custom-builds: latest
+custom-builds: -static ; Implementation uses plugins and requires -fPIC.
+#custom-build-bot: -- see below.
+
+custom-multi-builds: latest
+custom-multi-builds: -static ; Implementation uses plugins and requires -fPIC.
+#custom-multi-build-bot: -- see below.
+
+custom-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuF4YmJmPHY52Q6N+YO0M
+lt/fCovdezleb2tVplyTnvbyAiPdmYCIIjVrsqUn3y46PdFtWEiSdsrCcncoxi6H
+8KelOB/oQ9pNTyEvwGKEH5ZIU7noLZYdXEfoNdvdL/pbY/7uLBZOSekfdQShZtbe
+uOZCM2Mhg2DD76TP/VAwaXuDCnEvxxU/yneUl5ZaBo62AWNrYJuSGAliCOpVpl6X
+X1kbHOvnCx7c9e3LxgaVivPaeZRKYg0OaFt96SBYEZzNPvjA8pMuKuj/vatHaCQ3
+NO9+r3TJ+4dQd7qN6Ju3zUJq9J/ndSh4lPvUalvvhdykecefhcyHwRZOG4xyFMFE
+nJM4sM+aZu6WoKATIKtk7On70inVr0sZJXwJ4Lt4oqaK2VthcSTby3wf2Yv4p5hL
+zNo31cCPmBRYzABcIc6ADYvexVK4uCwaim8xs7RK5Ug2Gv6vUWoRNZW8grIgDwUY
+5pZ4Zk3hW4ii2vehTaVrrmdW6XipIsT+ayiVX7eWuHHNxAeCojXVjOJu9B0ExMlD
+5tHZCs+SNdV5MceexecbptB7fZtRebP120yjLiSnZ5FpaQ1stusr0hSg+VQaX4np
+f5m1W/CcDr53PKWg/ayY9nWMUQaIwH4b69kLM+VTpYSbzu5UQJkmNBNq2EOHgoTv
+9MLA+cE/nNJ/rMI//MZ1+kcCAwEAAQ==
+-----END PUBLIC KEY-----
+\
+
+custom-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuLYZ68rotGDAtWViFHOe
+XEsmZB8BGI+af1ixh9JOy9BE4ohGTfPr1YsjGDzh+PhOVLAtyykOoT/qG7cuGB0T
+gBInoRrgVB2/ZKTMwxeDGb/TA3uykaXxcw7/liTsizHAY+phCNTbke8iER5Y78js
+9GlnTPmNhwFqEj2fwCz+2o08eyZvZ9Vj1fH/bFDCmDmU33JR3crtJlC8wPiF70Ho
+FJzHFdaFQl3MxvEV92HjOsyqozMi6tAVVefN1vapVQeNtjkB0Di18p0/EMugEuGU
+OxktjDHQWNaV8Ao6cCDk6OkJnM3ZNL1no3cV4cuF+/xI8UZzwfPoBnwg/s183Qzu
+pHHKOSHmuO0oVE/XohJhepSw3tb+wf5BwejRhYHikIjqCxJdm9H0QTiqXT82y24K
+yg3gkRMOgqnVxERKKP4ZknLSMQCEKiND/t2zdLJ/lxH9eHZdPHKk3OZZG292j+Bh
+fknxcTKNk1Dmf32Irs5hVrjsoU8eAutbItovzXdBaj//rn/ry/kUlCa1Ov6iLIDJ
+gyxmsDlgKNR/uE9ogmDn0ishJIoCmxeqenRfJkttr9pEsDsUFuB425QGqiSxa1jh
+PCNca3iRtO44wADXaQMTGpvLzBfdfVc8LoFpn+kynN0V1MvxAX4mHRXxw8ERXd3U
+dpHDhOthPLolJQrYKb/YyW8CAwEAAQ==
+-----END PUBLIC KEY-----
+\
+
+custom-multi-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuF4YmJmPHY52Q6N+YO0M
+lt/fCovdezleb2tVplyTnvbyAiPdmYCIIjVrsqUn3y46PdFtWEiSdsrCcncoxi6H
+8KelOB/oQ9pNTyEvwGKEH5ZIU7noLZYdXEfoNdvdL/pbY/7uLBZOSekfdQShZtbe
+uOZCM2Mhg2DD76TP/VAwaXuDCnEvxxU/yneUl5ZaBo62AWNrYJuSGAliCOpVpl6X
+X1kbHOvnCx7c9e3LxgaVivPaeZRKYg0OaFt96SBYEZzNPvjA8pMuKuj/vatHaCQ3
+NO9+r3TJ+4dQd7qN6Ju3zUJq9J/ndSh4lPvUalvvhdykecefhcyHwRZOG4xyFMFE
+nJM4sM+aZu6WoKATIKtk7On70inVr0sZJXwJ4Lt4oqaK2VthcSTby3wf2Yv4p5hL
+zNo31cCPmBRYzABcIc6ADYvexVK4uCwaim8xs7RK5Ug2Gv6vUWoRNZW8grIgDwUY
+5pZ4Zk3hW4ii2vehTaVrrmdW6XipIsT+ayiVX7eWuHHNxAeCojXVjOJu9B0ExMlD
+5tHZCs+SNdV5MceexecbptB7fZtRebP120yjLiSnZ5FpaQ1stusr0hSg+VQaX4np
+f5m1W/CcDr53PKWg/ayY9nWMUQaIwH4b69kLM+VTpYSbzu5UQJkmNBNq2EOHgoTv
+9MLA+cE/nNJ/rMI//MZ1+kcCAwEAAQ==
+-----END PUBLIC KEY-----
+\
+
+custom-multi-build-bot:
+\
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuLYZ68rotGDAtWViFHOe
+XEsmZB8BGI+af1ixh9JOy9BE4ohGTfPr1YsjGDzh+PhOVLAtyykOoT/qG7cuGB0T
+gBInoRrgVB2/ZKTMwxeDGb/TA3uykaXxcw7/liTsizHAY+phCNTbke8iER5Y78js
+9GlnTPmNhwFqEj2fwCz+2o08eyZvZ9Vj1fH/bFDCmDmU33JR3crtJlC8wPiF70Ho
+FJzHFdaFQl3MxvEV92HjOsyqozMi6tAVVefN1vapVQeNtjkB0Di18p0/EMugEuGU
+OxktjDHQWNaV8Ao6cCDk6OkJnM3ZNL1no3cV4cuF+/xI8UZzwfPoBnwg/s183Qzu
+pHHKOSHmuO0oVE/XohJhepSw3tb+wf5BwejRhYHikIjqCxJdm9H0QTiqXT82y24K
+yg3gkRMOgqnVxERKKP4ZknLSMQCEKiND/t2zdLJ/lxH9eHZdPHKk3OZZG292j+Bh
+fknxcTKNk1Dmf32Irs5hVrjsoU8eAutbItovzXdBaj//rn/ry/kUlCa1Ov6iLIDJ
+gyxmsDlgKNR/uE9ogmDn0ishJIoCmxeqenRfJkttr9pEsDsUFuB425QGqiSxa1jh
+PCNca3iRtO44wADXaQMTGpvLzBfdfVc8LoFpn+kynN0V1MvxAX4mHRXxw8ERXd3U
+dpHDhOthPLolJQrYKb/YyW8CAwEAAQ==
+-----END PUBLIC KEY-----
+\
diff --git a/odb-tests/mssql/custom/custom.sql b/odb-tests/mssql/custom/custom.sql
new file mode 100644
index 0000000..44ef512
--- /dev/null
+++ b/odb-tests/mssql/custom/custom.sql
@@ -0,0 +1,42 @@
+/* This file contains helper functions.
+ */
+
+IF OBJECT_ID('dbo.variant_to_string', 'FN') IS NOT NULL
+ DROP FUNCTION dbo.variant_to_string;
+GO
+
+IF OBJECT_ID('dbo.string_to_variant', 'FN') IS NOT NULL
+ DROP FUNCTION dbo.string_to_variant;
+GO
+
+CREATE FUNCTION dbo.variant_to_string (@val SQL_VARIANT) RETURNS VARCHAR(max)
+AS
+BEGIN
+ RETURN CAST(SQL_VARIANT_PROPERTY(@val, 'BaseType') AS SYSNAME) + ' ' +
+ CAST(@val AS VARCHAR(max))
+END;
+GO
+
+CREATE FUNCTION dbo.string_to_variant (@val VARCHAR(max)) RETURNS SQL_VARIANT
+AS
+BEGIN
+ DECLARE @ret SQL_VARIANT
+
+ DECLARE @pos BIGINT
+ DECLARE @vtype SYSNAME
+ DECLARE @vtext VARCHAR(max)
+
+ SET @pos = CHARINDEX(' ', @val)
+ SET @vtype = SUBSTRING(@val, 1, @pos - 1)
+ SET @vtext = SUBSTRING(@val, @pos + 1, LEN(@val))
+
+ IF @vtype = 'tinyint' SET @ret = CAST(@vtext AS TINYINT)
+ ELSE IF @vtype = 'smallint' SET @ret = CAST(@vtext AS SMALLINT)
+ ELSE IF @vtype = 'int' SET @ret = CAST(@vtext AS INT)
+ ELSE IF @vtype = 'bigint' SET @ret = CAST(@vtext AS BIGINT)
+ ELSE IF @vtype = 'char' SET @ret = CAST(@vtext AS CHAR(8000))
+ ELSE IF @vtype = 'varchar' SET @ret = CAST(@vtext AS VARCHAR(8000))
+
+ RETURN @ret
+END;
+GO
diff --git a/odb-tests/mssql/custom/driver.cxx b/odb-tests/mssql/custom/driver.cxx
new file mode 100644
index 0000000..bde7eb6
--- /dev/null
+++ b/odb-tests/mssql/custom/driver.cxx
@@ -0,0 +1,135 @@
+// file : mssql/custom/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test custom database type mapping in SQL Server.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+namespace mssql = odb::mssql;
+using namespace mssql;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ object o (1);
+
+ o.v = variant (123);
+ o.vv.push_back (variant (string (1024, 'a')));
+ o.vv.push_back (variant (123));
+
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ o.p = point (1.1111, 2222222222.2);
+ o.pv.push_back (point (1.1234, 2.2345));
+ o.pv.push_back (point (3.3456, 4.4567));
+ o.pv.push_back (point (0.0000001, 0.000000001)); // Scientific notation.
+#endif
+
+ o.xml = "<root x=\"1\"><a>AAA</a><b>BBB</b><c>CCC</c></root>";
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+
+ // Query.
+ //
+ typedef mssql::query<object> query;
+ typedef odb::result<object> result;
+
+ {
+ transaction t (db->begin ());
+
+ // Variant comparison.
+ //
+ {
+ result r (db->query<object> (query::v == o.v));
+ assert (!r.empty ());
+ }
+
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ // Point comparison.
+ //
+ {
+ result r (db->query<object> (query::p == o.p));
+ assert (!r.empty ());
+ }
+
+ // Point comparison using native query.
+ //
+ {
+ result r (db->query<object> (
+ query::p + ".STEquals(" + query::_val (o.p) + ") = 1"));
+ assert (!r.empty ());
+ }
+
+ // Access to individual members.
+ //
+ {
+ result r (db->query<object> (query::p.x == o.p.x));
+ assert (!r.empty ());
+ }
+#endif
+
+ t.commit ();
+ }
+
+ // Update.
+ //
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ o.p.x++;
+ o.p.y--;
+ o.pv[1].x--;
+ o.pv[1].y++;
+#endif
+
+ o.xml = "<root x=\"2\"><a>BBB</a><b>CCC</b><c>DDD</c></root>";
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/mssql/custom/query.hxx b/odb-tests/mssql/custom/query.hxx
new file mode 100644
index 0000000..fc63378
--- /dev/null
+++ b/odb-tests/mssql/custom/query.hxx
@@ -0,0 +1,183 @@
+// file : mssql/custom/query.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef QUERY_HXX
+#define QUERY_HXX
+
+#include <string>
+
+#include <odb/mssql/query.hxx>
+
+#include "test.hxx" // point
+
+namespace odb
+{
+ namespace mssql
+ {
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ template <>
+ struct query_column<point, id_string>
+ {
+ private:
+ const char* table_;
+ const char* column_;
+ const char* conversion_;
+
+ unsigned short prec_;
+ unsigned short scale_;
+
+ std::string x_column_;
+ std::string y_column_;
+
+ // Sub-columns for individual members.
+ //
+ public:
+ query_column<double, id_float8> x, y;
+
+ // is_null, is_not_null
+ //
+ public:
+ query_base
+ is_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NULL";
+ return q;
+ }
+
+ query_base
+ is_not_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NOT NULL";
+ return q;
+ }
+
+ // =
+ //
+ public:
+ query_base
+ equal (const point& v) const
+ {
+ return equal (val_bind<point> (v));
+ }
+
+ query_base
+ equal (val_bind<point> v) const
+ {
+ query_base q (table_, column_);
+ q += ".STEquals(";
+ q.append<point, id_string> (v, conversion_);
+ q += ") = 1";
+ return q;
+ }
+
+ query_base
+ equal (ref_bind<point> r) const
+ {
+ query_base q (table_, column_);
+ q += ".STEquals(";
+ q.append<point, id_string> (r, conversion_);
+ q += ") = 1";
+ return q;
+ }
+
+ friend query_base
+ operator== (const query_column& c, const point& v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const point& v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, val_bind<point> v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (val_bind<point> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, ref_bind<point> r)
+ {
+ return c.equal (r);
+ }
+
+ friend query_base
+ operator== (ref_bind<point> r, const query_column& c)
+ {
+ return c.equal (r);
+ }
+
+ // Column comparison.
+ //
+ public:
+ query_base
+ operator== (const query_column<point, id_string>& c) const
+ {
+ query_base q (table_, column_);
+ q += ".STEquals(";
+ q.append (c.table (), c.column ());
+ q += ") = 1";
+ return q;
+ }
+
+ public:
+ query_column (const char* table,
+ const char* column,
+ const char* conv,
+ unsigned short prec = 0,
+ unsigned short scale = 0xFFFF)
+ : table_ (table), column_ (column), conversion_ (conv),
+ prec_ (prec), scale_ (scale),
+ x_column_ (std::string (column) + ".STX"),
+ y_column_ (std::string (column) + ".STY"),
+ x (table, x_column_.c_str (), 0),
+ y (table, y_column_.c_str (), 0)
+ {
+ }
+
+ const char*
+ table () const
+ {
+ return table_;
+ }
+
+ const char*
+ column () const
+ {
+ return column_;
+ }
+
+ const char*
+ conversion () const
+ {
+ return conversion_;
+ }
+
+ unsigned short
+ prec () const
+ {
+ return prec_;
+ }
+
+ unsigned short
+ scale () const
+ {
+ return scale_;
+ }
+ };
+#endif // SQL Server > 2005
+ }
+}
+
+#endif // QUERY_HXX
diff --git a/odb-tests/mssql/custom/test.hxx b/odb-tests/mssql/custom/test.hxx
new file mode 100644
index 0000000..4b8a5d7
--- /dev/null
+++ b/odb-tests/mssql/custom/test.hxx
@@ -0,0 +1,121 @@
+// file : mssql/types/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <vector>
+
+#include <odb/core.hxx>
+
+// Map SQL Server SQL_VARIANT type to our variant C++ class that is capable
+// of storing either an integer or a string (QVariant and boost::variant
+// would be natural alternatives to our own type). The SQL Server functions
+// that are used in the 'to' and 'from' expressions below are defined in
+// the custom.sql file. The other half of this mapping is in traits.hxx
+// (value_traits<variant, id_long_string>).
+//
+#pragma db map type("SQL_VARIANT") \
+ as("VARCHAR(max)") \
+ to("dbo.string_to_variant((?))") \
+ from("dbo.variant_to_string((?))")
+
+#pragma db value type("SQL_VARIANT")
+struct variant
+{
+ variant (unsigned long v = 0): val_type (type_int), int_val (v) {}
+ variant (const std::string& v): val_type (type_str), str_val (v) {}
+
+ enum {type_int, type_str} val_type;
+ unsigned long int_val;
+ std::string str_val;
+};
+
+inline bool
+operator== (const variant& a, const variant& b)
+{
+ if (a.val_type != b.val_type)
+ return false;
+
+ switch (a.val_type)
+ {
+ case variant::type_int:
+ return a.int_val == b.int_val;
+ case variant::type_str:
+ return a.str_val == b.str_val;
+ }
+
+ return false;
+}
+
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+// Map GEOMETRY SQL Server type to the point C++ struct. The other half
+// of this mapping is in traits.hxx (value_traits<point, id_string>).
+// Note that GEOMETRY is not available in SQL Server 2005.
+//
+#pragma db map type("GEOMETRY") \
+ as("VARCHAR(256)") \
+ to("GEOMETRY::STGeomFromText((?), 0)") \
+ from("(?).STAsText()")
+
+#pragma db value type("GEOMETRY")
+struct point
+{
+ point () {}
+ point (double x_, double y_): x (x_), y (y_) {}
+
+ double x;
+ double y;
+};
+
+inline bool
+operator== (const point& a, const point& b)
+{
+ return a.x == b.x && a.y == b.y;
+}
+#endif // SQL Server > 2005
+
+// Map XML SQL Server type to std::string (or any other type that provides
+// the value_traits<?, id_long_string> specialization). Note also that
+// another alternative would be to interface with the XML data type using
+// VARBINARY or NVARCHAR. Here we use implicit string to/from XML conversion,
+// however, CAST/CONVERT can be used instead for greater control over
+// whitespace handling, etc.
+//
+#pragma db map type("XML *(\\(.+\\))?") as("VARCHAR(max)")
+
+#pragma db object
+struct object
+{
+ object () {}
+ object (unsigned long id_) : id (id_) {}
+
+ #pragma db id
+ unsigned long id;
+
+ variant v;
+ std::vector<variant> vv;
+
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ point p;
+ std::vector<point> pv;
+#endif
+
+ #pragma db type("XML")
+ std::string xml;
+
+ bool
+ operator== (const object& y) const
+ {
+ return id == y.id
+ && vv == y.vv
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ && p == y.p
+ && pv == y.pv
+#endif
+ && xml == y.xml;
+ }
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/mssql/custom/traits.cxx b/odb-tests/mssql/custom/traits.cxx
new file mode 100644
index 0000000..3f14ae7
--- /dev/null
+++ b/odb-tests/mssql/custom/traits.cxx
@@ -0,0 +1,128 @@
+// file : mssql/types/traits.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include "traits.hxx"
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ void value_traits<variant, id_long_string>::
+ param_callback (const void* context,
+ size_t*,
+ const void** buffer,
+ size_t* size,
+ chunk_type* chunk,
+ void* tmp_buf,
+ size_t tmp_capacity)
+ {
+ const variant& v (*static_cast<const variant*> (context));
+ string str;
+
+ switch (v.val_type)
+ {
+ case variant::type_int:
+ {
+ ostringstream os;
+ os << v.int_val;
+
+ str = "bigint ";
+ str += os.str ();
+ break;
+ }
+ case variant::type_str:
+ {
+ str = "varchar ";
+ str += v.str_val;
+ break;
+ }
+ }
+
+ // Here we assume that the temoprary buffer is large enough to fit
+ // the whole string in one go. If that were not the case, then we
+ // would have had to chunk it.
+ //
+ assert (tmp_capacity >= str.size ());
+ memcpy (tmp_buf, str.c_str (), str.size ());
+
+ *buffer = tmp_buf;
+ *size = str.size ();
+ *chunk = chunk_one;
+ }
+
+ void value_traits<variant, id_long_string>::
+ result_callback (void* context,
+ size_t*,
+ void** buffer,
+ size_t* size,
+ chunk_type chunk,
+ size_t,
+ void* tmp_buf,
+ size_t tmp_capacity)
+ {
+ variant& v (*static_cast<variant*> (context));
+
+ switch (chunk)
+ {
+ case chunk_null:
+ case chunk_one:
+ {
+ assert (false); // The value cannot be NULL or empty.
+ break;
+ }
+ case chunk_first:
+ {
+ // Use the variant's string value as a temporary buffer. If this
+ // were not possible, we could have allocated one as part of
+ // context.
+ //
+ v.str_val.clear ();
+
+ *buffer = tmp_buf;
+ *size = tmp_capacity;
+ break;
+ }
+ case chunk_next:
+ {
+ v.str_val.append (static_cast<char*> (tmp_buf), *size);
+
+ *buffer = tmp_buf;
+ *size = tmp_capacity;
+ break;
+ }
+ case chunk_last:
+ {
+ v.str_val.append (static_cast<char*> (tmp_buf), *size);
+
+ // Figure out what we've got.
+ //
+ string::size_type p (v.str_val.find (' '));
+ assert (p != string::npos); // Must have type followed by value.
+ string type (v.str_val, 0, p);
+ string text (v.str_val, p + 1, string::npos);
+
+ if (type == "tinyint" ||
+ type == "smallint" ||
+ type == "int" ||
+ type == "bigint")
+ {
+ istringstream is (text);
+ is >> v.int_val;
+ v.val_type = variant::type_int;
+ }
+ else if (type == "char" || type == "varchar")
+ {
+ v.str_val = text;
+ v.val_type = variant::type_str;
+ }
+ else
+ assert (false); // Unknown type.
+
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/odb-tests/mssql/custom/traits.hxx b/odb-tests/mssql/custom/traits.hxx
new file mode 100644
index 0000000..2bd99cb
--- /dev/null
+++ b/odb-tests/mssql/custom/traits.hxx
@@ -0,0 +1,148 @@
+// file : mssql/types/traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TRAITS_HXX
+#define TRAITS_HXX
+
+#include <limits> // std::numeric_limits
+#include <sstream>
+#include <cstring> // std::memcpy
+#include <cassert>
+
+#include <odb/mssql/traits.hxx>
+
+#include "test.hxx" // variant, point
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <>
+ class value_traits<variant, id_long_string>
+ {
+ public:
+ typedef variant value_type;
+ typedef variant query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (variant& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &result_callback;
+ context = &v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const variant& v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = &v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+
+ template <>
+ struct type_traits<variant>
+ {
+ static const database_type_id db_type_id = id_long_string;
+
+ struct conversion
+ {
+ static const char* to () {return "dbo.string_to_variant((?))";}
+ };
+ };
+
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ template <>
+ class value_traits<point, id_string>
+ {
+ public:
+ typedef point value_type;
+ typedef point query_type;
+
+ typedef char* image_type;
+
+ static void
+ set_value (point& v,
+ const char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (is_null)
+ v = point ();
+ else
+ {
+ // Point format is "POINT (x y)".
+ //
+ std::istringstream is (std::string (b + 7, n - 7));
+
+ is >> v.x;
+ is >> v.y;
+ }
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const point& v)
+ {
+ is_null = false;
+ std::ostringstream os;
+
+ // The formula for the number of decimla digits required is given in:
+ //
+ // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1822.pdf
+ //
+ os.precision (std::numeric_limits<double>::digits10);
+ // os.precision (2 + std::numeric_limits<double>::digits * 301/1000);
+
+ os << "POINT (" << v.x << ' ' << v.y << ')';
+
+ const std::string& s (os.str ());
+ n = s.size ();
+ assert (n <= c);
+ std::memcpy (b, s.c_str (), n);
+ }
+ };
+
+ template <>
+ struct type_traits<point>
+ {
+ static const database_type_id db_type_id = id_string;
+
+ struct conversion
+ {
+ static const char* to () {return "GEOMETRY::STGeomFromText((?), 0)";}
+ };
+ };
+#endif // SQL Server > 2005
+ }
+}
+
+#endif // TRAITS_HXX
diff --git a/odb-tests/mssql/database/driver.cxx b/odb-tests/mssql/database/driver.cxx
new file mode 100644
index 0000000..08ad34f
--- /dev/null
+++ b/odb-tests/mssql/database/driver.cxx
@@ -0,0 +1,105 @@
+// file : mssql/database/driver.cxx
+// license : GNU GPL; see accompanying LICENSE file
+
+// Test that database constructors are unambiguous (compilation only).
+//
+
+#include <string>
+#include <cassert>
+
+#include <odb/mssql/database.hxx>
+
+namespace mssql = odb::mssql;
+using namespace mssql;
+
+static const char* isolation_map[] = {"1", "2", "3", "5", "4"};
+
+static bool
+check_isolation (connection& c, transaction_isolation i)
+{
+ std::string s ("SELECT 1 FROM sys.dm_exec_sessions WHERE session_id = @@SPID"
+ " AND transaction_isolation_level = ");
+ s += isolation_map[i];
+ return c.execute (s) == 1;
+}
+
+int
+main (int argc, char* argv[])
+{
+ // This code should not execute.
+ //
+ if (argc == 0)
+ {
+ {
+ database d1 ("bob", "secret", "db1", "server1");
+ database d2 ("bob", "secret", "db1", "server1", "driver1");
+ database d3 ("bob", "secret", "db1", "server1", "driver1", "extra");
+ database d4 ("bob", "secret", "db1", "server1", "driver1", "extra",
+ isolation_read_uncommitted);
+ }
+
+ {
+ database d1 ("bob", "secret", "db1", protocol_auto);
+ database d2 ("bob", "secret", "db1", protocol_auto, "server1");
+ database d3 ("bob", "secret", "db1", protocol_auto, "server1", "inst1");
+ database d4 ("bob", "secret", "db1", protocol_auto, "server1", "inst1",
+ "driver1");
+ database d5 ("bob", "secret", "db1", protocol_auto, "server1", "inst1",
+ "driver1", "extra");
+ database d6 ("bob", "secret", "db1", protocol_auto, "server1", "inst1",
+ "driver1", "extra", isolation_read_uncommitted);
+ }
+
+ {
+ database d1 ("bob", "secret", "db1", "server1", 0);
+ database d2 ("bob", "secret", "db1", "server1", 999, "driver1");
+ database d3 ("bob", "secret", "db1", "server1", 999, "driver1", "extra");
+ database d4 ("bob", "secret", "db1", "server1", 999, "driver1", "extra",
+ isolation_read_uncommitted);
+ }
+
+ {
+ database d1 ("conn1");
+ database d2 ("conn1", isolation_read_uncommitted);
+ }
+
+ {
+ database d1 (argc, argv);
+ database d2 (argc, argv, false);
+ database d3 (argc, argv, true, "extra");
+ database d4 (argc, argv, false, "extra", isolation_read_uncommitted);
+ }
+ }
+
+ // Test transaction isolation levels.
+ //
+ {
+ database d (argc, argv, false, "", isolation_read_uncommitted);
+ connection_ptr c (d.connection ());
+ assert (check_isolation (*c, isolation_read_uncommitted));
+ }
+
+ {
+ database d (argc, argv, false, "");
+ connection_ptr c (d.connection ());
+ assert (check_isolation (*c, isolation_read_committed));
+ }
+
+ {
+ database d (argc, argv, false, "", isolation_repeatable_read);
+ connection_ptr c (d.connection ());
+ assert (check_isolation (*c, isolation_repeatable_read));
+ }
+
+ {
+ database d (argc, argv, false, "", isolation_snapshot);
+ connection_ptr c (d.connection ());
+ assert (check_isolation (*c, isolation_snapshot));
+ }
+
+ {
+ database d (argc, argv, false, "", isolation_serializable);
+ connection_ptr c (d.connection ());
+ assert (check_isolation (*c, isolation_serializable));
+ }
+}
diff --git a/odb-tests/mssql/native/driver.cxx b/odb-tests/mssql/native/driver.cxx
new file mode 100644
index 0000000..f4b4fd7
--- /dev/null
+++ b/odb-tests/mssql/native/driver.cxx
@@ -0,0 +1,73 @@
+// file : mssql/native/driver.cxx
+// license : GNU GPL; see accompanying LICENSE file
+
+// Test native SQL execution.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/transaction.hxx>
+
+#include <common/common.hxx>
+
+using namespace std;
+namespace mssql = odb::mssql;
+using namespace mssql;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ // Create the database schema.
+ //
+ {
+ transaction t (db->begin ());
+
+ db->execute ("IF OBJECT_ID('mssql_native_test', 'U') IS NOT NULL\n"
+ " DROP TABLE mssql_native_test");
+
+ db->execute ("CREATE TABLE mssql_native_test (n int)");
+
+ t.commit ();
+ }
+
+ // Insert a few rows.
+ //
+ {
+ transaction t (db->begin ());
+
+ assert (
+ db->execute ("INSERT INTO mssql_native_test (n) VALUES (1)") == 1);
+
+ assert (
+ db->execute ("INSERT INTO mssql_native_test (n) VALUES (2)") == 1);
+
+ t.commit ();
+ }
+
+ // Select a few rows.
+ //
+ {
+ transaction t (db->begin ());
+
+ assert (
+ db->execute ("SELECT n FROM mssql_native_test WHERE n < 3") == 2);
+
+ assert (
+ db->execute ("SELECT n FROM mssql_native_test WHERE n > 3") == 0);
+
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/mssql/query/driver.cxx b/odb-tests/mssql/query/driver.cxx
new file mode 100644
index 0000000..5600c81
--- /dev/null
+++ b/odb-tests/mssql/query/driver.cxx
@@ -0,0 +1,188 @@
+// file : mssql/query/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test SQL Server-specific query support aspects.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+namespace mssql = odb::mssql;
+using namespace mssql;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ {
+ object o1;
+ object o2;
+ object o3;
+
+ o1.num = 1;
+ o1.str = "aaa";
+ o1.nstr = L"aaa";
+ o1.lstr.assign (1024, 'a');
+ o1.lnstr.assign (1024, L'a');
+ o1.smoney = 11000;
+ o1.money = 1.1;
+
+ o2.num = 2;
+ o2.str = "bbb";
+ o2.nstr = L"bbb";
+ o2.lstr.assign (1024, 'b');
+ o2.lnstr.assign (1024, L'b');
+ o2.smoney = 22000;
+ o2.money = 2.2;
+
+ o3.num = 3;
+ o3.str = "ccc";
+ o3.nstr = L"ccc";
+ o3.lstr.assign (1024, 'c');
+ o3.lnstr.assign (1024, L'c');
+ o3.smoney = 33000;
+ o3.money = 3.3;
+
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o3);
+ t.commit ();
+ }
+
+ typedef mssql::query<object> query;
+ typedef odb::result<object> result;
+
+ {
+ transaction t (db->begin ());
+
+ // Money and small money.
+ //
+ {
+ result r (db->query<object> (query::smoney < 22000));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->smoney == 11000);
+ assert (++i == r.end ());
+ }
+
+ {
+ result r (db->query<object> ("smoney < " + query::_val (2.1F)));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->smoney == 11000);
+ assert (++i == r.end ());
+ }
+
+ {
+ result r (db->query<object> (query::money < 2.2));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->money == 1.1);
+ assert (++i == r.end ());
+ }
+
+ {
+ result r (db->query<object> ("money < " + query::_val (2.2)));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->money == 1.1);
+ assert (++i == r.end ());
+ }
+
+ // Short/long string.
+ //
+ {
+ result r (db->query<object> (query::str < "bbb"));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->str == "aaa");
+ assert (++i == r.end ());
+ }
+
+ {
+ result r (db->query<object> ("str < " + query::_val ("bbb", 3)));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->str == "aaa");
+ assert (++i == r.end ());
+ }
+
+ {
+ result r (db->query<object> (query::nstr < L"bbb"));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->nstr == L"aaa");
+ assert (++i == r.end ());
+ }
+
+ {
+ result r (db->query<object> (query::lstr < string (1024, 'b')));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->lstr == string (1024, 'a'));
+ assert (++i == r.end ());
+ }
+
+ {
+ string v (1024, 'b');
+ result r (db->query<object> (query::lstr < query::_ref (v)));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->lstr == string (1024, 'a'));
+ assert (++i == r.end ());
+ }
+
+ {
+ result r (db->query<object> (query::lnstr < wstring (1024, L'b')));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->lnstr == wstring (1024, L'a'));
+ assert (++i == r.end ());
+ }
+
+ // Test image copying with long data.
+ //
+ {
+ result r (db->query<object> (query::str < "ccc"));
+ result::iterator i (r.begin ());
+
+ assert (i != r.end ());
+ ++i;
+ assert (i != r.end ());
+
+ {
+ result r (db->query<object> (query::str < "bbb"));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (i->str == "aaa");
+ assert (++i == r.end ());
+ }
+
+ assert (i->str == "bbb"); // Load from copy.
+ assert (++i == r.end ());
+ }
+
+ t.commit ();
+ }
+
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/mssql/query/test.hxx b/odb-tests/mssql/query/test.hxx
new file mode 100644
index 0000000..85c644e
--- /dev/null
+++ b/odb-tests/mssql/query/test.hxx
@@ -0,0 +1,38 @@
+// file : mssql/query/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ #pragma db id auto
+ unsigned long id_;
+
+ unsigned int num;
+
+ #pragma db type ("SMALLMONEY")
+ int smoney;
+
+ #pragma db type ("MONEY")
+ double money;
+
+ #pragma db type ("VARCHAR(256)")
+ std::string str;
+
+ #pragma db type ("NVARCHAR(128)")
+ std::wstring nstr;
+
+ #pragma db type ("VARCHAR(max)")
+ std::string lstr;
+
+ #pragma db type ("NVARCHAR(max)")
+ std::wstring lnstr;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/mssql/stored-proc/driver.cxx b/odb-tests/mssql/stored-proc/driver.cxx
new file mode 100644
index 0000000..2389798
--- /dev/null
+++ b/odb-tests/mssql/stored-proc/driver.cxx
@@ -0,0 +1,231 @@
+// file : mssql/stored-proc/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test SQL Server stored procedure support.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+namespace mssql = odb::mssql;
+using namespace mssql;
+
+void
+create_procedure (database& db, const string& name, const string& body)
+{
+ transaction t (db.begin ());
+
+ string s (db.query_value<default_schema> ().name);
+
+ db.execute (
+ "IF EXISTS ("
+ " SELECT * FROM sysobjects"
+ " WHERE name = '" + name + "' AND user_name(uid) = '" + s +"')"
+ " DROP PROCEDURE [" + s + "].[" + name + "]");
+
+ db.execute ("CREATE PROCEDURE [" + s + "].[" + name + "] " + body);
+
+ t.commit ();
+}
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ object o1 (1, "a");
+ object o2 (2, "b");
+ object o3 (3, "c");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o3);
+ t.commit ();
+ }
+
+ {
+ create_procedure (
+ *db, "select_all_objects",
+ "AS"
+ " SELECT num, str FROM mssql_stored_proc_object ORDER BY id;");
+
+ typedef odb::result<select_all_objects> result;
+
+ transaction t (db->begin ());
+
+ result r (db->query<select_all_objects> ());
+
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ cout << i->num << " " << i->str << endl;
+ cout << endl;
+
+ t.commit ();
+ }
+
+ {
+ create_procedure (
+ *db, "select_objects",
+ "(@id INT, @n VARCHAR(512))"
+ "AS"
+ " SELECT str FROM mssql_stored_proc_object "
+ " WHERE [id] = @id OR [num] = @n ORDER BY id;");
+
+ typedef mssql::query<select_objects> query;
+ typedef odb::result<select_objects> result;
+
+ transaction t (db->begin ());
+
+ result r (db->query<select_objects> (
+ query::_val (o1.id) + "," + query::_val (o2.num)));
+
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ cout << i->str << endl;
+ cout << endl;
+
+ t.commit ();
+ }
+
+ {
+ create_procedure (
+ *db, "objects_min_max",
+ "(@min INT = NULL OUTPUT, @max INT = NULL OUTPUT)"
+ "AS"
+ " SELECT @min = MIN(num), @max = MAX(num)"
+ " FROM mssql_stored_proc_object;");
+
+ create_procedure (
+ *db, "objects_min_max_odb",
+ "AS"
+ " DECLARE @min INT, @max INT;"
+ " EXEC objects_min_max @min OUTPUT, @max OUTPUT;"
+ " SELECT @min, @max;");
+
+ transaction t (db->begin ());
+
+ objects_min_max omm (db->query_value<objects_min_max> ());
+ cout << omm.num_min << " " << omm.num_max << endl
+ << endl;
+
+ t.commit ();
+ }
+
+ {
+ create_procedure (
+ *db, "insert_object_id",
+ "(@n INT, @s VARCHAR(512))"
+ "AS"
+ " INSERT INTO mssql_stored_proc_object([num], [str])"
+ " VALUES(@n, @s);");
+
+ {
+ typedef mssql::query<insert_object> query;
+
+ transaction t (db->begin ());
+
+ db->query_one<insert_object> (
+ query::_val (4) + "," + query::_val ("d"));
+
+ auto_ptr<object> o (db->load<object> (4));
+ cout << o->num << " " << o->str << endl
+ << endl;
+
+ t.commit ();
+ }
+
+ {
+ typedef mssql::query<no_result> query;
+
+ transaction t (db->begin ());
+
+ db->query_one<no_result> (
+ "EXEC insert_object_id" + query::_val (5) + "," + query::_val ("e"));
+
+ auto_ptr<object> o (db->load<object> (5));
+ cout << o->num << " " << o->str << endl
+ << endl;
+
+ t.commit ();
+ }
+ }
+
+ {
+ create_procedure (
+ *db, "insert_object_id",
+ "(@n INT, @s VARCHAR(512), @id INT = NULL OUTPUT)"
+ "AS"
+ " INSERT INTO mssql_stored_proc_object([num], [str])"
+ " VALUES(@n, @s);"
+ " SET @id = SCOPE_IDENTITY();"
+ " RETURN 123;");
+
+ typedef mssql::query<insert_object_id> query;
+
+ {
+ create_procedure (
+ *db, "insert_object_id_odb",
+ "(@n INT, @s VARCHAR(512))"
+ "AS"
+ " DECLARE @id INT;"
+ " DECLARE @ret INT;"
+ " EXEC @ret = insert_object_id @n, @s, @id OUTPUT;"
+ " SELECT @ret, @id;");
+
+ transaction t (db->begin ());
+
+ insert_object_id io (
+ db->query_value<insert_object_id> (
+ query::_val (6) + "," + query::_val ("f")));
+
+ cout << io.ret << " " << io.id << endl
+ << endl;
+
+ t.commit ();
+ }
+
+ // An alternative implementation that produces a different
+ // result set configuration at the ODBC level.
+ //
+ {
+ create_procedure (
+ *db, "insert_object_id_odb",
+ "(@n INT, @s VARCHAR(512))"
+ "AS"
+ " DECLARE @id INT;"
+ " DECLARE @ret INT;"
+ " DECLARE @tbl TABLE(dummy INT);"
+ " INSERT INTO @tbl EXEC @ret = insert_object_id @n, @s, @id OUTPUT;"
+ " SELECT @ret, @id;");
+
+ transaction t (db->begin ());
+
+ insert_object_id io (
+ db->query_value<insert_object_id> (
+ query::_val (7) + "," + query::_val ("g")));
+
+ cout << io.ret << " " << io.id << endl
+ << endl;
+
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/mssql/stored-proc/test.hxx b/odb-tests/mssql/stored-proc/test.hxx
new file mode 100644
index 0000000..5958ea3
--- /dev/null
+++ b/odb-tests/mssql/stored-proc/test.hxx
@@ -0,0 +1,63 @@
+// file : mssql/stored-proc/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+
+#include <odb/core.hxx>
+
+#pragma db view query("SELECT SCHEMA_NAME()")
+struct default_schema
+{
+ std::string name;
+};
+
+#pragma db object
+struct object
+{
+ object () {}
+ object (unsigned int n, std::string s): num (n), str (s) {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ unsigned int num;
+ std::string str;
+};
+
+#pragma db view
+struct no_result {};
+
+#pragma db view query("EXEC select_all_objects")
+struct select_all_objects
+{
+ unsigned int num;
+ std::string str;
+};
+
+#pragma db view query("EXEC select_objects (?)")
+struct select_objects
+{
+ std::string str;
+};
+
+#pragma db view query("EXEC objects_min_max_odb")
+struct objects_min_max
+{
+ unsigned int num_min;
+ unsigned int num_max;
+};
+
+#pragma db view query("EXEC insert_object_id (?)")
+struct insert_object {};
+
+#pragma db view query("EXEC insert_object_id_odb (?)")
+struct insert_object_id
+{
+ unsigned int ret;
+ unsigned long id;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/mssql/template/driver.cxx b/odb-tests/mssql/template/driver.cxx
new file mode 100644
index 0000000..ded03f1
--- /dev/null
+++ b/odb-tests/mssql/template/driver.cxx
@@ -0,0 +1,40 @@
+// file : mssql/template/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// PLACE TEST DESCRIPTION HERE
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+namespace mssql = odb::mssql;
+using namespace mssql;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/mssql/template/template-vc10.vcxproj b/odb-tests/mssql/template/template-vc10.vcxproj
new file mode 100644
index 0000000..5875d9b
--- /dev/null
+++ b/odb-tests/mssql/template/template-vc10.vcxproj
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib\common-d.lib;odb-mssql-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib64\common-d.lib;odb-mssql-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib\common.lib;odb-mssql.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib64\common.lib;odb-mssql.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(m4_patsubst(__value__(odb_options), __value__(src_base)/, ) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/mssql/template/template-vc10.vcxproj.filters b/odb-tests/mssql/template/template-vc10.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/mssql/template/template-vc10.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/mssql/template/template-vc11.vcxproj b/odb-tests/mssql/template/template-vc11.vcxproj
new file mode 100644
index 0000000..0bee18f
--- /dev/null
+++ b/odb-tests/mssql/template/template-vc11.vcxproj
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib\common-d.lib;odb-mssql-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib64\common-d.lib;odb-mssql-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib\common.lib;odb-mssql.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib64\common.lib;odb-mssql.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(m4_patsubst(__value__(odb_options), __value__(src_base)/, ) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/mssql/template/template-vc11.vcxproj.filters b/odb-tests/mssql/template/template-vc11.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/mssql/template/template-vc11.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/mssql/template/template-vc12.vcxproj b/odb-tests/mssql/template/template-vc12.vcxproj
new file mode 100644
index 0000000..35ffb0f
--- /dev/null
+++ b/odb-tests/mssql/template/template-vc12.vcxproj
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib\common-d.lib;odb-mssql-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib64\common-d.lib;odb-mssql-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib\common.lib;odb-mssql.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib64\common.lib;odb-mssql.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(m4_patsubst(__value__(odb_options), __value__(src_base)/, ) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/mssql/template/template-vc12.vcxproj.filters b/odb-tests/mssql/template/template-vc12.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/mssql/template/template-vc12.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/mssql/template/template-vc8.vcproj b/odb-tests/mssql/template/template-vc8.vcproj
new file mode 100644
index 0000000..cfd697f
--- /dev/null
+++ b/odb-tests/mssql/template/template-vc8.vcproj
@@ -0,0 +1,354 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\libcommon\lib\common-d.lib odb-mssql-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\libcommon\lib\common.lib odb-mssql.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\libcommon\lib64\common-d.lib odb-mssql-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\libcommon\lib64\common.lib odb-mssql.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(m4_patsubst(__value__(odb_options), __value__(src_base)/, ) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1400 -I$(SolutionDir)\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/mssql/template/template-vc9.vcproj b/odb-tests/mssql/template/template-vc9.vcproj
new file mode 100644
index 0000000..72a95d9
--- /dev/null
+++ b/odb-tests/mssql/template/template-vc9.vcproj
@@ -0,0 +1,361 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\libcommon\lib\common-d.lib odb-mssql-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\libcommon\lib\common.lib odb-mssql.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\libcommon\lib64\common-d.lib odb-mssql-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\libcommon\lib64\common.lib odb-mssql.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(m4_patsubst(__value__(odb_options), __value__(src_base)/, ) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/mssql/template/test.hxx b/odb-tests/mssql/template/test.hxx
new file mode 100644
index 0000000..0bc1f95
--- /dev/null
+++ b/odb-tests/mssql/template/test.hxx
@@ -0,0 +1,25 @@
+// file : mssql/template/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/mssql/types/driver.cxx b/odb-tests/mssql/types/driver.cxx
new file mode 100644
index 0000000..d900a95
--- /dev/null
+++ b/odb-tests/mssql/types/driver.cxx
@@ -0,0 +1,381 @@
+// file : mssql/types/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test SQL Server type conversion.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/exceptions.hxx>
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+namespace mssql = odb::mssql;
+using namespace mssql;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ {
+ object o (1);
+
+ o.bit_ = 1;
+ o.utint_ = 222;
+ o.stint_ = -123;
+ o.usint_ = 65000;
+ o.ssint_ = -12345;
+ o.uint_ = 4294967290U;
+ o.sint_ = -1234567890;
+ o.ubint_ = 18446744073709551610ULL;
+ o.sbint_ = -1234567890123456789LL;
+
+ o.fsm_ = -214748.3648F;
+ o.dsm_ = 214748.3647;
+ o.ism_ = -2147483647 -1;
+
+ o.dm1_ = -922337203685477.5808;
+ o.dm2_ = 922337203685476.3520; // 922337203685477.5807
+ o.im_ = 9223372036854775807LL;
+
+ o.f4_ = 123.123F;
+ o.f8_ = 123.1234567;
+
+ o.schar_ = "short data char ";
+ o.svchar_ = "short data varchar";
+
+ o.lchar_.assign (1025, 'a');
+ o.lvchar_ = "long data varchar"; // Test the short string optimization.
+ o.mvchar_.assign (70000, 'm');
+ o.text_.assign (70000, 't');
+
+ o.snchar_ = L"short data nchar\x1FFF\xD7FF ";
+ o.snvchar_ = L"short data nvarchar \x1FFF\xD7FF";
+
+ o.lnchar_.assign (513, L'\x1234');
+ o.lnvchar_ = L""; // Test empty string.
+ o.mnvchar_.assign (70000, L'\x2345');
+ o.ntext_.assign (70000, L'\x4356');
+
+ const char sdata[] = "abc""\x00\x01""def";
+ memcpy (o.sbin_, sdata, sizeof (sdata));
+ o.svbin_.assign (sdata, sdata + sizeof (sdata));
+
+ string ldata (256 * 1024, '\x01');
+ memset (o.lbin_, 2, sizeof (o.lbin_));
+ o.lvbin_.assign (50, '\x03');
+ o.mvbin_.assign (ldata.begin (), ldata.end ());
+ o.image_.assign (ldata.begin (), ldata.end ());
+
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ o.date_ = date_time (2011, 12, 20, 0, 0, 0, 0, 0, 0);
+ o.time7_ = date_time (0, 0, 0, 13, 34, 39, 123456789, 0, 0);
+ o.time4_ = date_time (0, 0, 0, 13, 34, 39, 123456700, 0, 0);
+#endif
+ o.sdt_ = date_time (2011, 12, 20, 15, 44, 29, 123456700, 0, 0);
+ o.dt_ = date_time (2011, 12, 20, 15, 44, 29, 123456700, 0, 0);
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ o.dt2_ = date_time (2011, 12, 20, 15, 44, 29, 123456700, 0, 0);
+ o.dto7_ = date_time (2011, 12, 20, 15, 44, 29, 123456700, 2, 0);
+ o.dto0_ = date_time (2011, 12, 20, 15, 44, 29, 123456700, 2, 0);
+#endif
+
+#ifdef _WIN32
+ // 6F846D41-C89A-4E4D-B22F-56443CFA543F
+ o.guid_.Data1 = 0x6F846D41;
+ o.guid_.Data2 = 0xC89A;
+ o.guid_.Data3 = 0x4E4D;
+ memcpy (&o.guid_.Data4, "\xB2\x2F\x56\x44\x3C\xFA\x54\x3F", 8);
+#endif
+ memcpy (o.uuid_, "\x6F\x84\x6D\x41\xC8\x9A\x4E\x4D\xB2\x2F"
+ "\x56\x44\x3C\xFA\x54\x3F", 16);
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ o.time7_ = date_time (0, 0, 0, 13, 34, 39, 123456700, 0, 0);
+ o.time4_ = date_time (0, 0, 0, 13, 34, 39, 123400000, 0, 0);
+#endif
+ o.sdt_ = date_time (2011, 12, 20, 15, 44, 0, 0, 0, 0);
+ o.dt_ = date_time (2011, 12, 20, 15, 44, 29, 123000000, 0, 0);
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ o.dto0_ = date_time (2011, 12, 20, 15, 44, 29, 0, 2, 0);
+#endif
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+
+ typedef mssql::query<object> query;
+ typedef odb::result<object> result;
+
+ // Test UUID in queries.
+ //
+ {
+ char uuid[16];
+ memcpy (uuid, o.uuid_, 16);
+
+ transaction t (db->begin ());
+
+ {
+ result r (db->query<object> (query::uuid == uuid));
+ assert (size (r) == 1);
+ }
+
+ {
+ result r (db->query<object> (query::uuid == query::_val (uuid)));
+ assert (size (r) == 1);
+ }
+
+ {
+ result r (db->query<object> (query::uuid == query::_ref (uuid)));
+ assert (size (r) == 1);
+ }
+
+ {
+ const char* d (uuid);
+ result r (db->query<object> (query::uuid == d));
+ assert (size (r) == 1);
+ }
+
+ t.commit ();
+ }
+
+ // Test short/long data in queries.
+ //
+ {
+ transaction t (db->begin ());
+
+ {
+ result r (db->query<object> (query::svchar == o.svchar_));
+ assert (size (r) == 1);
+ }
+
+ {
+ result r (db->query<object> (query::snvchar == o.snvchar_));
+ assert (size (r) == 1);
+ }
+
+ {
+ result r (db->query<object> (query::mvchar == o.mvchar_));
+ assert (size (r) == 1);
+ }
+
+ {
+ result r (db->query<object> (query::mnvchar == o.mnvchar_));
+ assert (size (r) == 1);
+ }
+
+ t.commit ();
+ }
+ }
+
+ // Test long NULL data.
+ //
+ {
+ long_null o1 (1);
+ long_null o2 (2);
+ o2.str_.reset (new string);
+ o2.str_->assign (70000, 'x');
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<long_null> p1 (db->load<long_null> (1));
+ auto_ptr<long_null> p2 (db->load<long_null> (2));
+ t.commit ();
+
+ assert (o1 == *p1);
+ assert (o2 == *p2);
+ }
+ }
+
+ // Test long data in containers.
+ //
+ {
+ long_cont o (1);
+ o.v.push_back (long_comp ("aaa", 123));
+ o.v.push_back (long_comp (string (500, 'b'), 234));
+ o.v.push_back (long_comp (string (70000, 'c'), 345));
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<long_cont> p (db->load<long_cont> (1));
+ t.commit ();
+
+ assert (o == *p);
+ }
+ }
+
+ // Test char/wchar_t arrays.
+ //
+ {
+ char_array o1 (1, "", L"");
+ char_array o2 (2, "1234567890", L"12345678\x1FFF\xD7FF");
+ char_array o3 (3, "1234567890123456", L"12345678901234\x1FFF\xD7FF");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o3);
+ t.commit ();
+ }
+
+ // SQL Server returns padded values for CHAR(N)/NCHAR(N).
+ //
+ memcpy (o1.s2, " ", 16);
+ o1.s3[0] = o1.c1 = ' ';
+ memcpy (o2.s2, "1234567890 ", 16);
+
+ memset (o1.ls2, ' ', 1025);
+ memset (o2.ls2 + 10, ' ', 1025 - 10);
+
+ memcpy (o1.ws2, L" ", 16 * sizeof (wchar_t));
+ o1.ws3[0] = o1.wc1 = L' ';
+ memcpy (o2.ws2, L"12345678\x1FFF\xD7FF ", 16 * sizeof (wchar_t));
+
+ for (size_t i (0); i < 257; ++i)
+ o1.lws2[i] = L' ';
+
+ for (size_t i (10); i < 257; ++i)
+ o2.lws2[i] = L' ';
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<char_array> p1 (db->load<char_array> (1));
+ auto_ptr<char_array> p2 (db->load<char_array> (2));
+ auto_ptr<char_array> p3 (db->load<char_array> (3));
+ t.commit ();
+
+ assert (o1 == *p1);
+ assert (o2 == *p2);
+ assert (o3 == *p3);
+ }
+ }
+
+ // Test optimistic concurrency using ROWVERSION.
+ //
+ {
+ rowversion o (123);
+ o.str = "abc";
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ assert (o.ver != 0);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<rowversion> p (db->load<rowversion> (o.id_));
+ assert (p->ver == o.ver);
+ p->str += 'd';
+ db->update (*p);
+ assert (p->ver > o.ver);
+
+ // Double-check object version was updated.
+ //
+ {
+ auto_ptr<rowversion> p1 (db->load<rowversion> (o.id_));
+ assert (p->ver == p1->ver);
+ }
+
+ o.str += 'D';
+ try
+ {
+ db->update (o);
+ assert (false);
+ }
+ catch (const odb::object_changed&) {}
+ db->reload (o);
+ assert (o.ver == p->ver);
+ o.str += 'D';
+ db->update (o);
+ t.commit ();
+ }
+ }
+
+ {
+ rowversion_auto o;
+ o.str = "abc";
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ assert (o.ver != 0);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<rowversion_auto> p (db->load<rowversion_auto> (o.id_));
+ assert (p->ver == o.ver);
+ p->str += 'd';
+ db->update (*p);
+ assert (p->ver > o.ver);
+ o.str += 'D';
+ try
+ {
+ db->update (o);
+ assert (false);
+ }
+ catch (const odb::object_changed&) {}
+ db->reload (o);
+ assert (o.ver == p->ver);
+ o.str += 'D';
+ db->update (o);
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/mssql/types/test.hxx b/odb-tests/mssql/types/test.hxx
new file mode 100644
index 0000000..5d651a8
--- /dev/null
+++ b/odb-tests/mssql/types/test.hxx
@@ -0,0 +1,517 @@
+// file : mssql/types/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#ifdef _WIN32
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include <windows.h> // GUID
+#elif defined(HOST_WIN32)
+typedef struct _GUID
+{
+ unsigned int Data1;
+ unsigned short Data2;
+ unsigned short Data3;
+ unsigned char Data4[8];
+} GUID;
+#endif
+
+#include <common/config.hxx> // HAVE_CXX11
+
+#include <string>
+#include <vector>
+#include <memory> // std::auto_ptr
+#include <cstring> // std::memcmp, std::memcpy, std::str[n]cmp, std::strlen
+#include <cwchar> // std::wcslen, std::wcs[n]cmp
+
+#include <odb/core.hxx>
+
+struct date_time
+{
+ date_time ()
+ {
+ }
+
+ date_time (short y,
+ unsigned short m,
+ unsigned short d,
+ unsigned short h,
+ unsigned short min,
+ unsigned short sec,
+ unsigned int f,
+ short tzh,
+ short tzm)
+ : year (y),
+ month (m),
+ day (d),
+ hour (h),
+ minute (min),
+ second (sec),
+ fraction (f),
+ timezone_hour (tzh),
+ timezone_minute (tzm)
+ {
+ }
+
+ bool
+ operator== (const date_time& y) const
+ {
+ return
+ year == y.year &&
+ month == y.month &&
+ day == y.day &&
+ hour == y.hour &&
+ minute == y.minute &&
+ second == y.second &&
+ fraction == y.fraction &&
+ timezone_hour == y.timezone_hour &&
+ timezone_minute == y.timezone_minute;
+ }
+
+ short year;
+ unsigned short month;
+ unsigned short day;
+ unsigned short hour;
+ unsigned short minute;
+ unsigned short second;
+ unsigned int fraction;
+ short timezone_hour;
+ short timezone_minute;
+};
+
+#pragma db object
+struct object
+{
+ object () {}
+ object (unsigned int id): id_ (id) {}
+
+ #pragma db id
+ unsigned int id_;
+
+ // Integer types.
+ //
+ #pragma db type ("BIT")
+ unsigned char bit_;
+
+ #pragma db type ("TINYINT")
+ unsigned char utint_;
+
+ #pragma db type ("TINYINT")
+ unsigned char stint_;
+
+ #pragma db type ("SMALLINT")
+ unsigned short usint_;
+
+ #pragma db type ("SMALLINT")
+ short ssint_;
+
+ #pragma db type ("INT")
+ unsigned int uint_;
+
+ #pragma db type ("INTEGER")
+ int sint_;
+
+ #pragma db type ("BIGINT")
+ unsigned long long ubint_;
+
+ #pragma db type ("BIGINT")
+ long long sbint_;
+
+ // Floating/fixed point types.
+ //
+ #pragma db type ("SMALLMONEY")
+ float fsm_;
+
+ #pragma db type ("SMALLMONEY")
+ double dsm_;
+
+ #pragma db type ("SMALLMONEY")
+ int ism_;
+
+ #pragma db type ("MONEY")
+ double dm1_;
+
+ #pragma db type ("MONEY")
+ double dm2_;
+
+ #pragma db type ("MONEY")
+ long long im_;
+
+ #pragma db type ("REAL")
+ float f4_;
+
+ #pragma db type ("FLOAT")
+ double f8_;
+
+ // Strings.
+ //
+ #pragma db type ("CHAR(20)")
+ std::string schar_;
+
+ #pragma db type ("VARCHAR(128)")
+ std::string svchar_;
+
+ #pragma db type ("CHAR(1025)")
+ std::string lchar_;
+
+ #pragma db type ("CHARACTER VARYING(8000)")
+ std::string lvchar_;
+
+ #pragma db type ("VARCHAR(max)")
+ std::string mvchar_;
+
+ #pragma db type ("TEXT")
+ std::string text_;
+
+ // National strings.
+ //
+ #pragma db type ("NCHAR(20)")
+ std::wstring snchar_;
+
+ #pragma db type ("NVARCHAR(128)")
+ std::wstring snvchar_;
+
+ #pragma db type ("NCHAR(513)")
+ std::wstring lnchar_;
+
+ #pragma db type ("NATIONAL CHARACTER VARYING(4000)")
+ std::wstring lnvchar_;
+
+ #pragma db type ("NVARCHAR(max)")
+ std::wstring mnvchar_;
+
+ #pragma db type ("NTEXT")
+ std::wstring ntext_;
+
+ // Binary.
+ //
+ #pragma db type ("BINARY(9)")
+ unsigned char sbin_[9];
+
+ #pragma db type ("VARBINARY(256)")
+ std::vector<char> svbin_;
+
+ #pragma db type ("BINARY(1025)")
+ char lbin_[1025];
+
+ #pragma db type ("BINARY VARYING(8000)")
+ std::vector<char> lvbin_;
+
+ #pragma db type ("VARBINARY(max)")
+ std::vector<unsigned char> mvbin_;
+
+ #pragma db type ("IMAGE")
+ std::vector<char> image_;
+
+ // Date-time. SQL Server 2005 (9.0) only has DATETIME and SMALLDATETIME.
+ //
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ #pragma db type ("DATE")
+ date_time date_;
+
+ #pragma db type ("TIME")
+ date_time time7_;
+
+ #pragma db type ("TIME(4)")
+ date_time time4_;
+#endif
+
+ #pragma db type ("SMALLDATETIME")
+ date_time sdt_;
+
+ #pragma db type ("DATETIME")
+ date_time dt_;
+
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ #pragma db type ("DATETIME2")
+ date_time dt2_;
+
+ #pragma db type ("DATETIMEOFFSET")
+ date_time dto7_;
+
+ #pragma db type ("DATETIMEOFFSET(0)")
+ date_time dto0_;
+#endif
+
+ // Other types.
+ //
+#if defined(_WIN32) || defined(HOST_WIN32)
+ //#pragma db type ("UNIQUEIDENTIFIER")
+ GUID guid_;
+#endif
+
+ #pragma db type ("UNIQUEIDENTIFIER")
+ char uuid_[16];
+
+ bool
+ operator== (const object& y) const
+ {
+ return
+ id_ == y.id_ &&
+ bit_ == y.bit_ &&
+ utint_ == y.utint_ &&
+ stint_ == y.stint_ &&
+ usint_ == y.usint_ &&
+ ssint_ == y.ssint_ &&
+ uint_ == y.uint_ &&
+ sint_ == y.sint_ &&
+ ubint_ == y.ubint_ &&
+ sbint_ == y.sbint_ &&
+ fsm_ == y.fsm_ &&
+ dsm_ == y.dsm_ &&
+ ism_ == y.ism_ &&
+ dm1_ == y.dm1_ &&
+ dm2_ == y.dm2_ &&
+ im_ == y.im_ &&
+ f4_ == y.f4_ &&
+ f8_ == y.f8_ &&
+
+ schar_ == y.schar_ &&
+ svchar_ == y.svchar_ &&
+ lchar_ == y.lchar_ &&
+ lvchar_ == y.lvchar_ &&
+ mvchar_ == y.mvchar_ &&
+ text_ == y.text_ &&
+
+ snchar_ == y.snchar_ &&
+ snvchar_ == y.snvchar_ &&
+ lnchar_ == y.lnchar_ &&
+ lnvchar_ == y.lnvchar_ &&
+ mnvchar_ == y.mnvchar_ &&
+ ntext_ == y.ntext_ &&
+
+ std::memcmp (sbin_, y.sbin_, sizeof (sbin_)) == 0 &&
+ svbin_ == y.svbin_ &&
+ std::memcmp (lbin_, y.lbin_, sizeof (lbin_)) == 0 &&
+ lvbin_ == y.lvbin_ &&
+ mvbin_ == y.mvbin_ &&
+ image_ == y.image_
+
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ && date_ == y.date_
+ && time7_ == y.time7_
+ && time4_ == y.time4_
+#endif
+ && sdt_ == y.sdt_
+ && dt_ == y.dt_
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ && dt2_ == y.dt2_
+ && dto7_ == y.dto7_
+ && dto0_ == y.dto0_
+#endif
+
+#ifdef _WIN32
+ && std::memcmp (&guid_, &y.guid_, sizeof (guid_)) == 0
+#endif
+ && std::memcmp (uuid_, y.uuid_, sizeof (uuid_)) == 0;
+ }
+};
+
+// Test long NULL data.
+//
+#pragma db object
+struct long_null
+{
+ long_null () {}
+ long_null (unsigned int id): id_ (id) {}
+
+ #pragma db id
+ unsigned int id_;
+
+ #pragma db type ("VARCHAR(max)") null
+#ifdef HAVE_CXX11
+ std::unique_ptr<std::string> str_;
+#else
+ std::auto_ptr<std::string> str_;
+#endif
+
+ bool
+ operator== (const long_null& y) const
+ {
+ return
+ id_ == y.id_ &&
+ ((str_.get () == 0 && y.str_.get () == 0) || *str_ == *y.str_);
+ }
+};
+
+// Test long data in containers, in particular column re-arrangement.
+//
+#pragma db value
+struct long_comp
+{
+ long_comp () {}
+ long_comp (std::string s, unsigned int n): str (s), num (n) {}
+
+ #pragma db type ("VARCHAR(max)")
+ std::string str;
+
+ unsigned int num;
+
+ bool
+ operator== (const long_comp& y) const
+ {
+ return str == y.str && num == y.num;
+ }
+};
+
+#pragma db object
+struct long_cont
+{
+ long_cont () {}
+ long_cont (unsigned int id): id_ (id) {}
+
+ #pragma db id
+ unsigned int id_;
+
+ std::vector<long_comp> v;
+
+ bool
+ operator== (const long_cont& y) const
+ {
+ return id_ == y.id_ && v == y.v;
+ }
+};
+
+// Test char/wchar_t arrays.
+//
+#pragma db object
+struct char_array
+{
+ char_array () {}
+ char_array (unsigned long id, const char* s, const wchar_t* ws)
+ : id_ (id)
+ {
+ std::memcpy (s1, s, std::strlen (s) + 1); // VC++ strncpy deprecation.
+ std::memcpy (s2, s, std::strlen (s) + 1);
+ s3[0] = c1 = *s;
+
+ std::memcpy (ws1, ws, (std::wcslen (ws) + 1) * sizeof (wchar_t));
+ std::memcpy (ws2, ws, (std::wcslen (ws) + 1) * sizeof (wchar_t));
+ ws3[0] = wc1 = *ws;
+
+ if (std::strlen (s) == sizeof (s2))
+ {
+ std::memset (ls1, '1', 1025);
+ ls1[1025] = '\0';
+ std::memset (ls2, '2', 1025);
+
+ for (std::size_t i (0); i < 257; ++i)
+ {
+ lws1[i] = L'1';
+ lws2[i] = L'2';
+ }
+ lws1[257] = L'\0';
+ }
+ else
+ {
+ std::memcpy (ls1, s, std::strlen (s) + 1); // VC++ strcpy deprecation.
+ std::memcpy (ls2, s, std::strlen (s) + 1);
+
+ std::memcpy (lws1, ws, (std::wcslen (ws) + 1) * sizeof (wchar_t));
+ std::memcpy (lws2, ws, (std::wcslen (ws) + 1) * sizeof (wchar_t));
+ }
+ }
+
+ #pragma db id
+ unsigned long id_;
+
+ //
+ //
+ char s1[17];
+
+ #pragma db type("CHAR(16)")
+ char s2[16];
+
+ char s3[1];
+ char c1;
+
+ // Long data.
+ //
+ char ls1[1026];
+
+ #pragma db type("CHAR(1025)")
+ char ls2[1025];
+
+ //
+ //
+ wchar_t ws1[17];
+
+ #pragma db type("NCHAR(16)")
+ wchar_t ws2[16];
+
+ wchar_t ws3[1];
+ wchar_t wc1;
+
+ // Long data.
+ //
+ wchar_t lws1[258];
+
+ #pragma db type("NCHAR(257)")
+ wchar_t lws2[257];
+
+ bool
+ operator== (const char_array& y) const
+ {
+ return id_ == y.id_ &&
+
+ std::strcmp (s1, y.s1) == 0 &&
+ std::strncmp (s2, y.s2, sizeof (s2)) == 0 &&
+ s3[0] == y.s3[0] &&
+ c1 == y.c1 &&
+
+ std::strcmp (ls1, y.ls1) == 0 &&
+ std::strncmp (ls2, y.ls2, sizeof (ls2)) == 0 &&
+
+ std::wcscmp (ws1, y.ws1) == 0 &&
+ std::wcsncmp (ws2, y.ws2, sizeof (ws2) / sizeof (wchar_t)) == 0 &&
+ ws3[0] == y.ws3[0] &&
+ wc1 == y.wc1 &&
+
+ std::wcscmp (lws1, y.lws1) == 0 &&
+ std::wcsncmp (lws2, y.lws2, sizeof (lws2) / sizeof (wchar_t)) == 0;
+ }
+};
+
+// Test optimistic concurrency using ROWVERSION, both with auto and
+// manually-assigned ids.
+//
+#pragma db object optimistic
+struct rowversion
+{
+ rowversion (unsigned int id = 0): id_ (id), ver (0) {}
+
+ #pragma db id
+ unsigned int id_;
+
+ #pragma db version type("ROWVERSION")
+#ifdef _WIN32
+ unsigned __int64 ver;
+#else
+ unsigned long long ver;
+#endif
+
+ std::string str;
+};
+
+#pragma db object optimistic
+struct rowversion_auto
+{
+ rowversion_auto (): ver (0) {}
+
+ #pragma db id auto
+ unsigned int id_;
+
+ #pragma db version type("ROWVERSION")
+#ifdef _WIN32
+ unsigned __int64 ver;
+#else
+ unsigned long long ver;
+#endif
+
+ std::string str;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/mssql/types/traits.hxx b/odb-tests/mssql/types/traits.hxx
new file mode 100644
index 0000000..5881f50
--- /dev/null
+++ b/odb-tests/mssql/types/traits.hxx
@@ -0,0 +1,223 @@
+// file : mssql/types/traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TRAITS_HXX
+#define TRAITS_HXX
+
+#include <odb/mssql/mssql-fwd.hxx> // date, time, datetime, datetimeoffset
+#include <odb/mssql/traits.hxx>
+
+#include "test.hxx" // date_time
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <>
+ class value_traits<date_time, id_date>
+ {
+ public:
+ typedef date_time value_type;
+ typedef date_time query_type;
+ typedef date image_type;
+
+ static void
+ set_value (date_time& v, const date& i, bool is_null)
+ {
+ if (!is_null)
+ {
+ v.year = i.year;
+ v.month = i.month;
+ v.day = i.day;
+ v.hour = 0;
+ v.minute = 0;
+ v.second = 0;
+ v.fraction = 0;
+ v.timezone_hour = 0;
+ v.timezone_minute = 0;
+ }
+ }
+
+ static void
+ set_image (date& i, bool& is_null, const date_time& v)
+ {
+ is_null = false;
+ i.year = v.year;
+ i.month = v.month;
+ i.day = v.day;
+ }
+ };
+
+ template <>
+ class value_traits<date_time, id_time>
+ {
+ public:
+ typedef date_time value_type;
+ typedef date_time query_type;
+ typedef time image_type;
+
+ static void
+ set_value (date_time& v, const time& i, bool is_null)
+ {
+ if (!is_null)
+ {
+ v.year = 0;
+ v.month = 0;
+ v.day = 0;
+ v.hour = i.hour;
+ v.minute = i.minute;
+ v.second = i.second;
+ v.fraction = i.fraction;
+ v.timezone_hour = 0;
+ v.timezone_minute = 0;
+ }
+ }
+
+ static void
+ set_image (time& i, unsigned short s, bool& is_null, const date_time& v)
+ {
+ const unsigned int divider[8] =
+ {
+ 1000000000,
+ 100000000,
+ 10000000,
+ 1000000,
+ 100000,
+ 10000,
+ 1000,
+ 100
+ };
+
+ is_null = false;
+ i.hour = v.hour;
+ i.minute = v.minute;
+ i.second = v.second;
+ i.fraction = v.fraction - v.fraction % divider[s];
+ }
+ };
+
+ template <>
+ class value_traits<date_time, id_datetime>
+ {
+ public:
+ typedef date_time value_type;
+ typedef date_time query_type;
+ typedef datetime image_type;
+
+ static void
+ set_value (date_time& v, const datetime& i, bool is_null)
+ {
+ if (!is_null)
+ {
+ v.year = i.year;
+ v.month = i.month;
+ v.day = i.day;
+ v.hour = i.hour;
+ v.minute = i.minute;
+ v.second = i.second;
+ v.fraction = i.fraction;
+ v.timezone_hour = 0;
+ v.timezone_minute = 0;
+ }
+ }
+
+ static void
+ set_image (datetime& i,
+ unsigned short s,
+ bool& is_null,
+ const date_time& v)
+ {
+ const unsigned int divider[8] =
+ {
+ 1000000000,
+ 100000000,
+ 10000000,
+ 1000000,
+ 100000,
+ 10000,
+ 1000,
+ 100
+ };
+
+ is_null = false;
+ i.year = v.year;
+ i.month = v.month;
+ i.day = v.day;
+ i.hour = v.hour;
+ i.minute = v.minute;
+
+ // Scale value 8 indicates we are dealing with SMALLDATETIME
+ // which has the minutes precision.
+ //
+ if (s != 8)
+ {
+ i.second = v.second;
+ i.fraction = v.fraction - v.fraction % divider[s];
+ }
+ else
+ {
+ i.second = 0;
+ i.fraction = 0;
+ }
+ }
+ };
+
+ template <>
+ class value_traits<date_time, id_datetimeoffset>
+ {
+ public:
+ typedef date_time value_type;
+ typedef date_time query_type;
+ typedef datetimeoffset image_type;
+
+ static void
+ set_value (date_time& v, const datetimeoffset& i, bool is_null)
+ {
+ if (!is_null)
+ {
+ v.year = i.year;
+ v.month = i.month;
+ v.day = i.day;
+ v.hour = i.hour;
+ v.minute = i.minute;
+ v.second = i.second;
+ v.fraction = i.fraction;
+ v.timezone_hour = i.timezone_hour;
+ v.timezone_minute = i.timezone_minute;
+ }
+ }
+
+ static void
+ set_image (datetimeoffset& i,
+ unsigned short s,
+ bool& is_null,
+ const date_time& v)
+ {
+ const unsigned int divider[8] =
+ {
+ 1000000000,
+ 100000000,
+ 10000000,
+ 1000000,
+ 100000,
+ 10000,
+ 1000,
+ 100
+ };
+
+ is_null = false;
+ i.year = v.year;
+ i.month = v.month;
+ i.day = v.day;
+ i.hour = v.hour;
+ i.minute = v.minute;
+ i.second = v.second;
+ i.fraction = v.fraction - v.fraction % divider[s];
+ i.timezone_hour = v.timezone_hour;
+ i.timezone_minute = v.timezone_minute;
+ }
+ };
+ }
+}
+
+#endif // TRAITS_HXX
diff --git a/odb-tests/mysql-schema.testscript b/odb-tests/mysql-schema.testscript
new file mode 100644
index 0000000..dfa7852
--- /dev/null
+++ b/odb-tests/mysql-schema.testscript
@@ -0,0 +1,9 @@
+# file : mysql-schema.testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+# Create the MySQL database schema creation canned command base.
+#
+create_schema_cmd = [cmdline] \
+ $mysql_client_cmd 2>&1 | \
+ sed -e "'"'s/^mysql: \[Warning\] Using a password on the command .*//'"'" | \
+ sed -n -e "'"'s/(.+)/\1/p'"'" >&2
diff --git a/odb-tests/mysql.testscript b/odb-tests/mysql.testscript
new file mode 100644
index 0000000..e218ece
--- /dev/null
+++ b/odb-tests/mysql.testscript
@@ -0,0 +1,12 @@
+# file : mysql.testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+# Create the MySQL database schema creation canned command and setup the test
+# driver command line for the subsequent tests.
+#
+.include mysql-schema.testscript
+
+schema_file = [path] $out_base/"$schema"($multi ? '-mysql' : '').sql
+create_schema = [cmdline] cat "'""$schema_file""'" | $create_schema_cmd
+
+test.arguments += ($multi ? 'mysql' : ) $mysql_options
diff --git a/odb-tests/mysql/custom/buildfile b/odb-tests/mysql/custom/buildfile
new file mode 100644
index 0000000..f18b16b
--- /dev/null
+++ b/odb-tests/mysql/custom/buildfile
@@ -0,0 +1,40 @@
+# file : mysql/custom/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($mysql) "mysql should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-mysql%lib{odb-mysql}
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix mysql_custom_ \
+ --generate-schema \
+ --default-database common \
+ --generate-query \
+ --hxx-prologue '#include "traits.hxx"' \
+ --hxx-prologue '#include "query.hxx"'
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{mysql-client}: include = adhoc
diff --git a/odb-tests/mysql/custom/driver.cxx b/odb-tests/mysql/custom/driver.cxx
new file mode 100644
index 0000000..526dbdc
--- /dev/null
+++ b/odb-tests/mysql/custom/driver.cxx
@@ -0,0 +1,117 @@
+// file : mysql/custom/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test custom database type mapping in MySQL.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+namespace mysql = odb::mysql;
+using namespace mysql;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ object o (1);
+ o.p = point (1.1111, 2222222222.2);
+ o.pv.push_back (point (1.1234, 2.2345));
+ o.pv.push_back (point (3.3456, 4.4567));
+ // VC just cannot roundtrip this.
+#ifndef _MSC_VER
+ o.pv.push_back (point (0.0000001, 0.000000001)); // Scientific notation.
+#endif
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+
+ // Query.
+ //
+ typedef mysql::query<object> query;
+ typedef odb::result<object> result;
+
+ {
+ transaction t (db->begin ());
+
+ // Point comparison.
+ //
+ {
+ result r (db->query<object> (query::p == o.p));
+ assert (!r.empty ());
+ }
+
+ // Point comparison using native query.
+ //
+ {
+ result r (db->query<object> (query::p + "=" + query::_val (o.p)));
+ assert (!r.empty ());
+ }
+
+ // Access to individual members.
+ //
+ {
+ result r (db->query<object> (query::p.x == o.p.x));
+ assert (!r.empty ());
+ }
+
+ t.commit ();
+ }
+
+ // Update.
+ //
+ o.p.x++;
+ o.p.y--;
+ o.pv[1].x--;
+ o.pv[1].y++;
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/mysql/custom/query.hxx b/odb-tests/mysql/custom/query.hxx
new file mode 100644
index 0000000..2fa8f73
--- /dev/null
+++ b/odb-tests/mysql/custom/query.hxx
@@ -0,0 +1,160 @@
+// file : mysql/custom/query.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef QUERY_HXX
+#define QUERY_HXX
+
+#include <string>
+
+#include <odb/mysql/query.hxx>
+
+#include "test.hxx" // point
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <>
+ struct query_column<point, id_string>
+ {
+ private:
+ const char* table_;
+ const char* column_;
+ const char* conversion_;
+
+ std::string x_table_;
+ std::string y_table_;
+ std::string s_column_;
+
+ // Sub-columns for individual members.
+ //
+ public:
+ query_column<double, id_double> x, y;
+
+ // is_null, is_not_null
+ //
+ public:
+ query_base
+ is_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NULL";
+ return q;
+ }
+
+ query_base
+ is_not_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NOT NULL";
+ return q;
+ }
+
+ // =
+ //
+ public:
+ query_base
+ equal (const point& v) const
+ {
+ return equal (val_bind<point> (v));
+ }
+
+ query_base
+ equal (val_bind<point> v) const
+ {
+ query_base q (table_, column_);
+ q += "=";
+ q.append<point, id_string> (v, conversion_);
+ return q;
+ }
+
+ query_base
+ equal (ref_bind<point> r) const
+ {
+ query_base q (table_, column_);
+ q += "=";
+ q.append<point, id_string> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator== (const query_column& c, const point& v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const point& v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, val_bind<point> v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (val_bind<point> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, ref_bind<point> r)
+ {
+ return c.equal (r);
+ }
+
+ friend query_base
+ operator== (ref_bind<point> r, const query_column& c)
+ {
+ return c.equal (r);
+ }
+
+ // Column comparison.
+ //
+ public:
+ query_base
+ operator== (const query_column<point, id_string>& c) const
+ {
+ query_base q (table_, column_);
+ q += "=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ public:
+ query_column (const char* table, const char* column, const char* conv)
+ : table_ (table), column_ (column), conversion_ (conv),
+ x_table_ ("ST_X(" + std::string (table)), // @@ Not very clean.
+ y_table_ ("ST_Y(" + std::string (table)),
+ s_column_ (std::string (column) + ")"), // X & Y column suffix.
+ x (x_table_.c_str (), s_column_.c_str (), 0),
+ y (y_table_.c_str (), s_column_.c_str (), 0)
+ {
+ }
+
+ const char*
+ table () const
+ {
+ return table_;
+ }
+
+ const char*
+ column () const
+ {
+ return column_;
+ }
+
+ const char*
+ conversion () const
+ {
+ return conversion_;
+ }
+ };
+ }
+}
+
+#endif // QUERY_HXX
diff --git a/odb-tests/mysql/custom/test.hxx b/odb-tests/mysql/custom/test.hxx
new file mode 100644
index 0000000..82cc59d
--- /dev/null
+++ b/odb-tests/mysql/custom/test.hxx
@@ -0,0 +1,54 @@
+// file : mysql/custom/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <vector>
+
+#include <odb/core.hxx>
+
+// Map GEOMETRY MySQL type to the point C++ struct. The other half
+// of this mapping is in traits.hxx (value_traits<point, id_string>).
+//
+#pragma db map type("GEOMETRY") \
+ as("VARCHAR(256)") \
+ to("ST_GeomFromText((?))") \
+ from("ST_AsText((?))")
+
+#pragma db value type("GEOMETRY")
+struct point
+{
+ point () {}
+ point (double x_, double y_): x (x_), y (y_) {}
+
+ double x;
+ double y;
+};
+
+inline bool
+operator== (const point& a, const point& b)
+{
+ return a.x == b.x && a.y == b.y;
+}
+
+#pragma db object
+struct object
+{
+ object () {}
+ object (unsigned long id_) : id (id_) {}
+
+ #pragma db id
+ unsigned long id;
+
+ point p;
+ std::vector<point> pv;
+
+ bool
+ operator== (const object& y) const
+ {
+ return id == y.id && p == y.p && pv == y.pv;
+ }
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/mysql/custom/testscript b/odb-tests/mysql/custom/testscript
new file mode 100644
index 0000000..9bc8839
--- /dev/null
+++ b/odb-tests/mysql/custom/testscript
@@ -0,0 +1,11 @@
+# file : mysql/custom/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../mysql.testscript
+
++$create_schema
+
+: basics
+:
+$*
diff --git a/odb-tests/mysql/custom/traits.hxx b/odb-tests/mysql/custom/traits.hxx
new file mode 100644
index 0000000..5386d86
--- /dev/null
+++ b/odb-tests/mysql/custom/traits.hxx
@@ -0,0 +1,88 @@
+// file : mysql/types/traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TRAITS_HXX
+#define TRAITS_HXX
+
+#include <limits> // std::numeric_limits
+#include <sstream>
+#include <cstring> // std::memcpy
+
+#include <odb/mysql/traits.hxx>
+
+#include "test.hxx" // point
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <>
+ class value_traits<point, id_string>
+ {
+ public:
+ typedef point value_type;
+ typedef point query_type;
+
+ typedef char* image_type;
+
+ static void
+ set_value (point& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (is_null)
+ v = point ();
+ else
+ {
+ // Point format is "POINT(x y)".
+ //
+ std::istringstream is (std::string (b.data () + 6, n - 6));
+
+ is >> v.x;
+ is >> v.y;
+ }
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const point& v)
+ {
+ is_null = false;
+ std::ostringstream os;
+
+ // The formula for the number of decimla digits required is given in:
+ //
+ // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1822.pdf
+ //
+ os.precision (std::numeric_limits<double>::digits10);
+ // os.precision (2 + std::numeric_limits<double>::digits * 301/1000);
+
+ os << "POINT(" << v.x << ' ' << v.y << ')';
+
+ const std::string& s (os.str ());
+ n = s.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), s.c_str (), n);
+ }
+ };
+
+ template <>
+ struct type_traits<point>
+ {
+ static const database_type_id db_type_id = id_string;
+
+ struct conversion
+ {
+ static const char* to () {return "ST_GeomFromText((?))";}
+ };
+ };
+ }
+}
+
+#endif // TRAITS_HXX
diff --git a/odb-tests/mysql/database/buildfile b/odb-tests/mysql/database/buildfile
new file mode 100644
index 0000000..5731f6f
--- /dev/null
+++ b/odb-tests/mysql/database/buildfile
@@ -0,0 +1,14 @@
+# file : mysql/database/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($mysql) "mysql should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libs = libodb-mysql%lib{odb-mysql}
+
+exe{driver}: {hxx cxx}{*} $libs testscript
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
diff --git a/odb-tests/mysql/database/driver.cxx b/odb-tests/mysql/database/driver.cxx
new file mode 100644
index 0000000..525ee87
--- /dev/null
+++ b/odb-tests/mysql/database/driver.cxx
@@ -0,0 +1,72 @@
+// file : mysql/database/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test that database constructors are unambiguous (compilation only).
+//
+
+#include <string>
+
+#include <odb/mysql/database.hxx>
+
+#undef NDEBUG
+#include <cassert>
+
+using std::string;
+namespace mysql = odb::mysql;
+using namespace mysql;
+
+int
+main (int argc, char* argv[])
+{
+ // This code should not execute.
+ //
+ if (argc != 0)
+ return 0;
+
+ {
+ database d1 (0, 0, 0);
+ database d2 ("bob", "secret", "db1");
+ database d3 ("bob", "secret", "db1", "server1");
+ database d4 ("bob", "secret", "db1", "server1", 999);
+ database d5 ("bob", "secret", "db1", "server1", 999, "sock1");
+ database d6 ("bob", "secret", "db1", "server1", 999, "sock1", "charset1");
+ }
+
+ std::string u ("bob"), p ("secret"), db ("bd1"), h ("server1"),
+ s ("sock1"), cs ("charset1");
+
+ {
+ database d1 (u, p, db);
+ database d2 (u, p, db, h);
+ database d3 (u, p, db, h, 999);
+ database d4 (u, p, db, h, 999, &s);
+ database d5 (u, p, db, h, 999, &s, cs);
+ }
+
+ {
+ database d1 (u, 0, db);
+ database d2 (u, &p, db);
+ database d3 (u, &p, db, h);
+ database d4 (u, &p, db, h, 999);
+ database d5 (u, &p, db, h, 999, &s);
+ database d6 (u, &p, db, h, 999, &s, cs);
+ }
+
+ {
+ database d1 (u, p, db, h, 999, "socket1");
+ database d2 (u, p, db, h, 999, s);
+ database d3 (u, p, db, h, 999, s, cs);
+ }
+
+ {
+ database d1 (u, 0, db, h, 999, s);
+ database d2 (u, &p, db, h, 999, "socket1");
+ database d3 (u, &p, db, h, 999, s, cs);
+ }
+
+ {
+ database d1 (argc, argv);
+ database d2 (argc, argv, false);
+ database d3 (argc, argv, true, "charset1");
+ }
+}
diff --git a/odb-tests/mysql/database/testscript b/odb-tests/mysql/database/testscript
new file mode 100644
index 0000000..c2ff256
--- /dev/null
+++ b/odb-tests/mysql/database/testscript
@@ -0,0 +1,6 @@
+# file : mysql/database/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+: basics
+:
+$*
diff --git a/odb-tests/mysql/index/buildfile b/odb-tests/mysql/index/buildfile
new file mode 100644
index 0000000..0ad9d8f
--- /dev/null
+++ b/odb-tests/mysql/index/buildfile
@@ -0,0 +1,37 @@
+# file : mysql/index/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($mysql) "mysql should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-mysql%lib{odb-mysql}
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix mysql_index_ \
+ --generate-schema \
+ --default-database common
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{mysql-client}: include = adhoc
diff --git a/odb-tests/mysql/index/driver.cxx b/odb-tests/mysql/index/driver.cxx
new file mode 100644
index 0000000..4d0b7ad
--- /dev/null
+++ b/odb-tests/mysql/index/driver.cxx
@@ -0,0 +1,44 @@
+// file : mysql/index/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test MySQL index creation. See also the common test.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+namespace mysql = odb::mysql;
+using namespace mysql;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ // This is just a schema creation test.
+ //
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/mysql/index/test.hxx b/odb-tests/mysql/index/test.hxx
new file mode 100644
index 0000000..b4f6ae8
--- /dev/null
+++ b/odb-tests/mysql/index/test.hxx
@@ -0,0 +1,20 @@
+// file : mysql/template/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ #pragma db id auto
+ unsigned long id_;
+
+ std::string s;
+ #pragma db index method("BTREE") member(s, "(200) DESC")
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/mysql/index/testscript b/odb-tests/mysql/index/testscript
new file mode 100644
index 0000000..26dfc4f
--- /dev/null
+++ b/odb-tests/mysql/index/testscript
@@ -0,0 +1,11 @@
+# file : mysql/index/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../mysql.testscript
+
++$create_schema
+
+: basics
+:
+$*
diff --git a/odb-tests/mysql/native/buildfile b/odb-tests/mysql/native/buildfile
new file mode 100644
index 0000000..1ae0212
--- /dev/null
+++ b/odb-tests/mysql/native/buildfile
@@ -0,0 +1,19 @@
+# file : mysql/native/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($mysql) "mysql should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libs = libodb-mysql%lib{odb-mysql}
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{*} $libs testscript
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{mysql-client}: include = adhoc
diff --git a/odb-tests/mysql/native/driver.cxx b/odb-tests/mysql/native/driver.cxx
new file mode 100644
index 0000000..9b34fd2
--- /dev/null
+++ b/odb-tests/mysql/native/driver.cxx
@@ -0,0 +1,75 @@
+// file : mysql/native/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test native SQL execution.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+namespace mysql = odb::mysql;
+using namespace mysql;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ // Create the database schema.
+ //
+ {
+ transaction t (db->begin ());
+
+ db->execute ("DROP TABLE IF EXISTS mysql_native_test");
+
+ db->execute ("CREATE TABLE mysql_native_test (n INT PRIMARY KEY) "
+ "ENGINE=InnoDB");
+
+ t.commit ();
+ }
+
+ // Insert a few rows.
+ //
+ {
+ transaction t (db->begin ());
+
+ assert (
+ db->execute ("INSERT INTO mysql_native_test (n) VALUES (1)") == 1);
+
+ assert (
+ db->execute ("INSERT INTO mysql_native_test (n) VALUES (2)") == 1);
+
+ t.commit ();
+ }
+
+ // Select a few rows.
+ //
+ {
+ transaction t (db->begin ());
+
+ assert (
+ db->execute ("SELECT n FROM mysql_native_test WHERE n < 3") == 2);
+
+ assert (
+ db->execute ("SELECT n FROM mysql_native_test WHERE n > 3") == 0);
+
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/mysql/native/testscript b/odb-tests/mysql/native/testscript
new file mode 100644
index 0000000..21327ff
--- /dev/null
+++ b/odb-tests/mysql/native/testscript
@@ -0,0 +1,9 @@
+# file : mysql/native/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../mysql.testscript
+
+: basics
+:
+$*
diff --git a/odb-tests/mysql/truncation/buildfile b/odb-tests/mysql/truncation/buildfile
new file mode 100644
index 0000000..94c7429
--- /dev/null
+++ b/odb-tests/mysql/truncation/buildfile
@@ -0,0 +1,38 @@
+# file : mysql/truncation/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($mysql) "mysql should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-mysql%lib{odb-mysql}
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix mysql_truncation_ \
+ --generate-schema \
+ --default-database common \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{mysql-client}: include = adhoc
diff --git a/odb-tests/mysql/truncation/driver.cxx b/odb-tests/mysql/truncation/driver.cxx
new file mode 100644
index 0000000..21084f5
--- /dev/null
+++ b/odb-tests/mysql/truncation/driver.cxx
@@ -0,0 +1,192 @@
+// file : mysql/truncation/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test insufficient buffer/truncation handling.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+namespace mysql = odb::mysql;
+using namespace mysql;
+
+int
+main (int argc, char* argv[])
+{
+ // The default pre-allocated buffer is 256 bytes long.
+ //
+ string long_str (300, 'c'); // This will get the buffer to 512
+ string longer_str (1025, 'b');
+
+ try
+ {
+ // Test basic operations.
+ //
+ {
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ // Run persist/load so that the initial bindings are established
+ // (version == 0).
+ //
+ {
+ object1 o (1);
+ o.str_ = "test string";
+
+ transaction t (db->begin ());
+ db->persist (o);
+ db->load (1, o);
+ t.commit ();
+ }
+
+ {
+ object2 o (2);
+ o.str_ = "test string";
+
+ transaction t (db->begin ());
+ db->persist (o);
+ db->load (2, o);
+ t.commit ();
+ }
+
+ // Store/load the long string which should trigger buffer growth.
+ //
+ {
+ object1 o (3);
+ o.str_ = long_str;
+
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object2> o (db->load<object2> (3));
+ assert (o->str_ == long_str);
+ t.commit ();
+ }
+
+ // Store/load longer string.
+ //
+ {
+ object1 o (3);
+ o.str_ = longer_str;
+
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object2> o (db->load<object2> (3));
+ assert (o->str_ == longer_str);
+ t.commit ();
+ }
+ }
+
+ // Test query.
+ //
+ {
+ typedef mysql::query<object1> query;
+ typedef odb::result<object1> result;
+
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ // Run persist/query so that the initial bindings are established
+ // (version == 0).
+ //
+ {
+ object1 o (20);
+ o.str_ = "test string";
+
+ transaction t (db->begin ());
+ db->persist (o);
+ o.id_++;
+ db->persist (o);
+ o.id_++;
+ db->persist (o);
+
+ result r (db->query<object1> (query::id == 20));
+ assert (r.begin ()->id_ == 20);
+ t.commit ();
+ }
+
+ // Test buffer growth with cached result.
+ //
+ {
+ object1 o;
+
+ transaction t (db->begin ());
+
+ result r (db->query<object1> (query::id >= 20));
+ result::iterator i (r.begin ());
+
+ o.id_ = i->id_;
+ o.str_ = long_str;
+
+ // This forces buffer growth in the middle of result iteration.
+ //
+ db->update (o);
+
+ ++i;
+ assert (i->str_ == "test string");
+
+ o.id_ = i->id_;
+ o.str_ = longer_str;
+ db->update (o);
+
+ ++i;
+ assert (i->str_ == "test string");
+
+ t.commit ();
+ }
+ }
+
+ // Test containers.
+ //
+ {
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ // Use different connections to persist and load the object.
+ //
+ connection_ptr c1 (db->connection ());
+ connection_ptr c2 (db->connection ());
+
+ container o (1);
+ o.vec_.push_back (string (513, 'x'));
+
+ {
+ transaction t (c1->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (c2->begin ());
+ unique_ptr<container> p (db->load<container> (1));
+ t.commit ();
+
+ assert (p->vec_ == o.vec_);
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/mysql/truncation/test.hxx b/odb-tests/mysql/truncation/test.hxx
new file mode 100644
index 0000000..1883b0e
--- /dev/null
+++ b/odb-tests/mysql/truncation/test.hxx
@@ -0,0 +1,48 @@
+// file : mysql/truncation/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <vector>
+
+#include <odb/core.hxx>
+
+#pragma db object table ("test")
+struct object1
+{
+ object1 () {}
+ object1 (unsigned long id): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string str_;
+};
+
+#pragma db object table ("test")
+struct object2
+{
+ object2 () {}
+ object2 (unsigned long id): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string str_;
+};
+
+#pragma db object
+struct container
+{
+ container () {}
+ container (unsigned long id) : id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ std::vector<std::string> vec_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/mysql/truncation/testscript b/odb-tests/mysql/truncation/testscript
new file mode 100644
index 0000000..b9b6792
--- /dev/null
+++ b/odb-tests/mysql/truncation/testscript
@@ -0,0 +1,11 @@
+# file : mysql/truncation/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../mysql.testscript
+
++$create_schema
+
+: basics
+:
+$*
diff --git a/odb-tests/mysql/types/buildfile b/odb-tests/mysql/types/buildfile
new file mode 100644
index 0000000..9065b7d
--- /dev/null
+++ b/odb-tests/mysql/types/buildfile
@@ -0,0 +1,39 @@
+# file : mysql/types/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($mysql) "mysql should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-mysql%lib{odb-mysql}
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix mysql_types_ \
+ --generate-schema \
+ --default-database common \
+ --generate-query \
+ --hxx-prologue '#include "traits.hxx"'
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{mysql-client}: include = adhoc
diff --git a/odb-tests/mysql/types/driver.cxx b/odb-tests/mysql/types/driver.cxx
new file mode 100644
index 0000000..2354b04
--- /dev/null
+++ b/odb-tests/mysql/types/driver.cxx
@@ -0,0 +1,168 @@
+// file : mysql/types/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test MySQL type conversion.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+namespace mysql = odb::mysql;
+using namespace mysql;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ mysql_version v;
+ {
+ transaction t (db->begin ());
+ db->query<mysql_version> ().begin ().load (v);
+ t.commit ();
+ }
+
+ //cerr << "MySQL " << v.major << '.' << v.minor << '.' << v.release
+ // << " protocol " << v.protocol << endl;
+
+ object o (1);
+
+ o.bool_ = true;
+ o.schar_ = -123;
+ o.uchar_ = 123;
+ o.short_ = -12345;
+ o.ushort_ = 12345;
+ o.mint_ = -123456;
+ o.umint_ = 123456;
+ o.int_ = -123456;
+ o.uint_ = 123456;
+ o.long_long_ = -123456;
+ o.ulong_long_ = 123456;
+
+ o.float_ = 1.123F;
+ o.float8_ = 1.123;
+ o.double_ = 1.123;
+ o.decimal_ = "123.456";
+
+ o.date_ = date_time (false, 2010, 8, 29, 0, 0, 0);
+ o.time_ = date_time (true, 0, 0, 0, 12, 26, 59);
+ o.date_time_ = date_time (false, 2010, 8, 29, 12, 26, 59);
+ o.timestamp_ = date_time (false, 2010, 8, 29, 12, 26, 59);
+ o.year_ = 2010;
+
+ // If we are running against MySQL 5.6.4 or later, add fractional
+ // seconds and also alter the table to allow sub-second precision.
+ //
+ if (v.major > 5 ||
+ (v.major == 5 && (v.minor > 6 ||
+ (v.minor == 6 && v.release >= 4))))
+ {
+ o.time_.microseconds = 123456;
+ o.date_time_.microseconds = 234567;
+ o.timestamp_.microseconds = 345678;
+
+ transaction t (db->begin ());
+ db->execute ("ALTER TABLE `mysql_types_object`" \
+ " MODIFY COLUMN `time` TIME(6)," \
+ " MODIFY COLUMN `date_time` DATETIME(6)," \
+ " MODIFY COLUMN `timestamp` TIMESTAMP(6)");
+ t.commit ();
+ }
+
+ string short_str (128, 's');
+ string medium_str (250, 'm');
+ string long_str (2040, 'l');
+
+ const char* sb (short_str.c_str ()), *se (sb + short_str.size ());
+ const char* mb (medium_str.c_str ()), *me (mb + medium_str.size ());
+ const char* lb (long_str.c_str ()), *le (lb + long_str.size ());
+
+ o.char_ = short_str;
+ o.binary_.assign (sb, se);
+ o.varchar_ = medium_str;
+ o.varbinary_.assign (mb, me);
+ o.tinytext_ = short_str;
+ o.tinyblob_.assign (sb, se);
+ o.text_ = long_str;
+ o.blob_.assign (lb, le);
+ o.mediumtext_ = long_str;
+ o.mediumblob_.assign (lb, le);
+ o.longtext_ = long_str;
+ o.longblob_.assign (lb, le);
+
+ o.bit_.a = 1;
+ o.bit_.b = 0;
+ o.bit_.c = 0;
+ o.bit_.d = 1;
+
+ o.enum_def_ = green;
+ o.enum_cst_ = blue;
+ o.enum_str_ = "green";
+ o.set_.insert ("green");
+ o.set_.insert ("red");
+ o.set_.insert ("blue");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ //
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+
+ // Test char array.
+ //
+ {
+ char_array o1 (1, "");
+ char_array o2 (2, "1234567890");
+ char_array o3 (3, "1234567890123456");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o3);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<char_array> p1 (db->load<char_array> (1));
+ unique_ptr<char_array> p2 (db->load<char_array> (2));
+ unique_ptr<char_array> p3 (db->load<char_array> (3));
+ t.commit ();
+
+ assert (o1 == *p1);
+ assert (o2 == *p2);
+ assert (o3 == *p3);
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/mysql/types/test.hxx b/odb-tests/mysql/types/test.hxx
new file mode 100644
index 0000000..6a8527b
--- /dev/null
+++ b/odb-tests/mysql/types/test.hxx
@@ -0,0 +1,336 @@
+// file : mysql/types/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <set>
+#include <string>
+#include <vector>
+#include <memory> // std::unique_ptr
+#include <cstring> // std::memcpy, std::str[n]cmp, std::strlen
+
+#include <odb/core.hxx>
+
+typedef std::vector<char> buffer;
+
+struct date_time
+{
+ date_time ()
+ : negative (false),
+ year (0),
+ month (0),
+ day (0),
+ hour (0),
+ minute (0),
+ second (0),
+ microseconds (0)
+ {
+ }
+
+ date_time (bool n,
+ unsigned int y,
+ unsigned int m,
+ unsigned int d,
+ unsigned int h,
+ unsigned int min,
+ unsigned int sec,
+ unsigned int msec = 0)
+ : negative (n),
+ year (y),
+ month (m),
+ day (d),
+ hour (h),
+ minute (min),
+ second (sec),
+ microseconds (msec)
+ {
+ }
+
+ bool
+ operator== (const date_time& y) const
+ {
+ return
+ negative == y.negative &&
+ year == y.year &&
+ month == y.month &&
+ day == y.day &&
+ hour == y.hour &&
+ minute == y.minute &&
+ second == y.second &&
+ microseconds == y.microseconds;
+ }
+
+ bool negative;
+ unsigned int year;
+ unsigned int month;
+ unsigned int day;
+ unsigned int hour;
+ unsigned int minute;
+ unsigned int second;
+ unsigned int microseconds;
+};
+
+struct bitfield
+{
+ unsigned int a: 1;
+ unsigned int b: 1;
+ unsigned int c: 1;
+ unsigned int d: 1;
+};
+
+inline bool
+operator== (bitfield x, bitfield y)
+{
+ return
+ x.a == y.a &&
+ x.b == y.b &&
+ x.c == y.c &&
+ x.d == y.d;
+}
+
+#pragma db value(bitfield) type ("BIT(4)")
+
+typedef std::set<std::string> set;
+typedef std::unique_ptr<std::string> string_ptr;
+
+enum color {red, green, blue};
+
+#pragma db object
+struct object
+{
+ object () {}
+ object (unsigned long id): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ // Integral types.
+ //
+ #pragma db type ("BOOL")
+ bool bool_;
+
+ #pragma db type ("TINYINT")
+ signed char schar_;
+
+ #pragma db type ("TINYINT UNSIGNED")
+ unsigned char uchar_;
+
+ #pragma db type ("SMALLINT")
+ short short_;
+
+ #pragma db type ("SMALLINT UNSIGNED")
+ unsigned short ushort_;
+
+ #pragma db type ("MEDIUMINT")
+ int mint_;
+
+ #pragma db type ("MEDIUMINT UNSIGNED")
+ unsigned int umint_;
+
+ #pragma db type ("INT")
+ int int_;
+
+ #pragma db type ("INT UNSIGNED")
+ unsigned int uint_;
+
+ #pragma db type ("BIGINT")
+ long long long_long_;
+
+ #pragma db type ("BIGINT UNSIGNED")
+ unsigned long long ulong_long_;
+
+ // Float types.
+ //
+ #pragma db type ("FLOAT")
+ float float_;
+
+ #pragma db type ("FLOAT(32)")
+ double float8_;
+
+ #pragma db type ("DOUBLE")
+ double double_;
+
+ #pragma db type ("DECIMAL(6,3)")
+ std::string decimal_;
+
+ // Data-time types.
+ //
+ #pragma db type ("DATE")
+ date_time date_;
+
+ #pragma db type ("TIME")
+ date_time time_;
+
+ #pragma db type ("DATETIME")
+ date_time date_time_;
+
+ #pragma db type ("TIMESTAMP")
+ date_time timestamp_;
+
+ #pragma db type ("YEAR")
+ short year_;
+
+ // String and binary types.
+ //
+ #pragma db type ("CHAR(128)")
+ std::string char_;
+
+ #pragma db type ("BINARY(128)")
+ buffer binary_;
+
+ #pragma db type ("VARCHAR(256)")
+ std::string varchar_;
+
+ #pragma db type ("VARBINARY(256)")
+ buffer varbinary_;
+
+ #pragma db type ("TINYTEXT")
+ std::string tinytext_;
+
+ #pragma db type ("TINYBLOB")
+ buffer tinyblob_;
+
+ #pragma db type ("TEXT")
+ std::string text_;
+
+ #pragma db type ("BLOB")
+ buffer blob_;
+
+ #pragma db type ("MEDIUMTEXT")
+ std::string mediumtext_;
+
+ #pragma db type ("MEDIUMBLOB")
+ buffer mediumblob_;
+
+ #pragma db type ("LONGTEXT")
+ std::string longtext_;
+
+ #pragma db type ("LONGBLOB")
+ buffer longblob_;
+
+ // Other types.
+ //
+ // #pragma db type ("BIT(4)") - assigned by #pragma db value
+ bitfield bit_;
+
+ // Test ENUM representations (integer and string).
+ //
+ color enum_def_;
+
+ // Map to a custom MySQL ENUM type.
+ //
+ #pragma db type ("ENUM('R', 'G', 'B')")
+ color enum_cst_;
+
+ #pragma db type ("ENUM('red', 'green', 'blue')")
+ std::string enum_str_;
+
+ #pragma db type ("SET('red', 'green', 'blue')")
+ set set_;
+
+ // Test NULL value.
+ //
+ #pragma db type ("TEXT") null
+ string_ptr null_;
+
+ bool
+ operator== (const object& y) const
+ {
+ return
+ id_ == y.id_ &&
+ bool_ == y.bool_ &&
+ schar_ == y.schar_ &&
+ uchar_ == y.uchar_ &&
+ short_ == y.short_ &&
+ ushort_ == y.ushort_ &&
+ mint_ == y.mint_ &&
+ umint_ == y.umint_ &&
+ int_ == y.int_ &&
+ uint_ == y.uint_ &&
+ long_long_ == y.long_long_ &&
+ ulong_long_ == y.ulong_long_ &&
+ float_ == y.float_ &&
+ float8_ == y.float8_ &&
+ double_ == y.double_ &&
+ decimal_ == y.decimal_ &&
+ date_ == y.date_ &&
+ time_ == y.time_ &&
+ date_time_ == y.date_time_ &&
+ timestamp_ == y.timestamp_ &&
+ year_ == y.year_ &&
+ char_ == y.char_ &&
+ binary_ == y.binary_ &&
+ varchar_ == y.varchar_ &&
+ varbinary_ == y.varbinary_ &&
+ tinytext_ == y.tinytext_ &&
+ tinyblob_ == y.tinyblob_ &&
+ text_ == y.text_ &&
+ blob_ == y.blob_ &&
+ mediumtext_ == y.mediumtext_ &&
+ mediumblob_ == y.mediumblob_ &&
+ longtext_ == y.longtext_ &&
+ longblob_ == y.longblob_ &&
+ bit_ == y.bit_ &&
+ enum_def_ == y.enum_def_ &&
+ enum_cst_ == y.enum_cst_ &&
+ enum_str_ == y.enum_str_ &&
+ set_ == y.set_ &&
+ ((null_.get () == 0 && y.null_.get () == 0) || *null_ == *y.null_);
+ }
+};
+
+// Test char array.
+//
+#pragma db object
+struct char_array
+{
+ char_array () {}
+ char_array (unsigned long id, const char* s)
+ : id_ (id)
+ {
+ std::memcpy (s1, s, std::strlen (s) + 1); // VC++ strncpy deprecation.
+ std::memcpy (s2, s, std::strlen (s) + 1);
+ s3[0] = c1 = *s;
+ }
+
+ #pragma db id
+ unsigned long id_;
+
+ char s1[17];
+
+ #pragma db type("CHAR(16)")
+ char s2[16];
+
+ char s3[1];
+ char c1;
+
+ bool
+ operator== (const char_array& y) const
+ {
+ return id_ == y.id_ &&
+ std::strcmp (s1, y.s1) == 0 &&
+ std::strncmp (s2, y.s2, sizeof (s2)) == 0 &&
+ s3[0] == y.s3[0] &&
+ c1 == y.c1;
+ }
+};
+
+// MySQL server version view.
+//
+#pragma db view query( \
+ "SELECT " \
+ "CAST(SUBSTRING_INDEX(@@version, '.', 1) AS UNSIGNED)," \
+ "CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(@@version, '.', 2), '.', -1) AS UNSIGNED)," \
+ "CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(@@version, '-', 1), '.', -1) AS UNSIGNED)," \
+ "@@protocol_version")
+struct mysql_version
+{
+ unsigned int major;
+ unsigned int minor;
+ unsigned int release;
+
+ unsigned int protocol;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/mysql/types/testscript b/odb-tests/mysql/types/testscript
new file mode 100644
index 0000000..2962d1c
--- /dev/null
+++ b/odb-tests/mysql/types/testscript
@@ -0,0 +1,11 @@
+# file : mysql/types/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../mysql.testscript
+
++$create_schema
+
+: basics
+:
+$*
diff --git a/odb-tests/mysql/types/traits.hxx b/odb-tests/mysql/types/traits.hxx
new file mode 100644
index 0000000..d4ce200
--- /dev/null
+++ b/odb-tests/mysql/types/traits.hxx
@@ -0,0 +1,198 @@
+// file : mysql/types/traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TRAITS_HXX
+#define TRAITS_HXX
+
+#include <cstring> // std::memcpy, std::memset
+
+#include <odb/mysql/traits.hxx>
+
+#include "test.hxx" // date_time, string_ptr
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <database_type_id ID>
+ class value_traits<date_time, ID>
+ {
+ public:
+ typedef date_time value_type;
+ typedef date_time query_type;
+ typedef MYSQL_TIME image_type;
+
+ static void
+ set_value (date_time& v, const MYSQL_TIME& i, bool is_null)
+ {
+ if (!is_null)
+ {
+ v.negative = i.neg;
+ v.year = i.year;
+ v.month = i.month;
+ v.day = i.day;
+ v.hour = i.hour;
+ v.minute = i.minute;
+ v.second = i.second;
+ v.microseconds = static_cast<unsigned int> (i.second_part);
+ }
+ else
+ v = date_time ();
+ }
+
+ static void
+ set_image (MYSQL_TIME& i, bool& is_null, const date_time& v)
+ {
+ is_null = false;
+ i.neg = v.negative;
+ i.year = v.year;
+ i.month = v.month;
+ i.day = v.day;
+ i.hour = v.hour;
+ i.minute = v.minute;
+ i.second = v.second;
+ i.second_part = v.microseconds;
+ }
+ };
+
+ template <>
+ class value_traits<bitfield, id_bit>
+ {
+ public:
+ typedef bitfield value_type;
+ typedef bitfield query_type;
+ typedef unsigned char* image_type;
+
+ static void
+ set_value (bitfield& v,
+ const unsigned char* s,
+ std::size_t,
+ bool is_null)
+ {
+ if (!is_null)
+ {
+ v.a = *s & 1;
+ v.b = (*s >> 1) & 1;
+ v.c = (*s >> 2) & 1;
+ v.d = (*s >> 3) & 1;
+ }
+ else
+ v.a = v.b = v.c = v.d = 0;
+ }
+
+ static void
+ set_image (unsigned char* s,
+ std::size_t,
+ std::size_t& n,
+ bool& is_null,
+ bitfield v)
+ {
+ is_null = false;
+ n = 1;
+ *s = v.a | (v.b << 1) | (v.c << 2) | (v.d << 3);
+ }
+ };
+
+ template <>
+ class value_traits<set, id_set>
+ {
+ public:
+ typedef set value_type;
+ typedef set query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (set& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ v.clear ();
+
+ if (!is_null)
+ {
+ const char* s (b.data ());
+ const char* e (s + n);
+
+ while (s < e)
+ {
+ const char* p (s);
+
+ while (p < e && *p != ',')
+ ++p;
+
+ v.insert (std::string (s, p - s));
+ s = p;
+
+ if (p != e)
+ ++s;
+ }
+ }
+ }
+
+ static void
+ set_image (details::buffer& buf,
+ std::size_t& n,
+ bool& is_null,
+ const set& v)
+ {
+ is_null = false;
+ n = 0;
+
+ for (set::const_iterator b (v.begin ()), i (b); i != v.end (); ++i)
+ {
+ std::size_t m (i->size () + (i != b ? 1 : 0));
+
+ if (n + m > buf.capacity ())
+ buf.capacity (n + m, n);
+
+ if (i != b)
+ buf.data ()[n++] = ',';
+
+ std::memcpy (buf.data () + n, i->c_str (), i->size ());
+ n += i->size ();
+ }
+ }
+ };
+
+ template <>
+ class value_traits<string_ptr, id_string>
+ {
+ public:
+ typedef string_ptr value_type;
+ typedef std::string query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (string_ptr& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ v.reset (is_null ? 0 : new std::string (b.data (), n));
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const string_ptr& v)
+ {
+ is_null = v.get () == 0;
+
+ if (!is_null)
+ {
+ n = v->size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ std::memcpy (b.data (), v->c_str (), n);
+ }
+ }
+ };
+ }
+}
+
+#endif // TRAITS_HXX
diff --git a/odb-tests/oracle/custom/custom.sql b/odb-tests/oracle/custom/custom.sql
new file mode 100644
index 0000000..6e22903
--- /dev/null
+++ b/odb-tests/oracle/custom/custom.sql
@@ -0,0 +1,53 @@
+/* This file contains custom type definitions and helper functions.
+ */
+
+/* For some reason CREATE OR REPLACE TYPE does not work on Oracle 10.2. */
+BEGIN
+ BEGIN
+ EXECUTE IMMEDIATE 'DROP TYPE Numbers';
+ EXCEPTION
+ WHEN OTHERS THEN
+ IF SQLCODE != -4043 THEN RAISE; END IF;
+ END;
+END;
+/
+
+CREATE TYPE Numbers AS VARRAY(100) OF NUMBER(10);
+/
+
+CREATE OR REPLACE FUNCTION string_to_numbers(in_str IN VARCHAR2) RETURN Numbers
+IS
+ ret Numbers := Numbers();
+ s_pos NUMBER := 1;
+ e_pos NUMBER := 0;
+BEGIN
+ IF in_str IS NOT NULL THEN
+ LOOP
+ e_pos := INSTR(in_str, ',', s_pos);
+ EXIT WHEN e_pos = 0;
+ ret.extend;
+ ret(ret.COUNT) := CAST(SUBSTR(in_str, s_pos, e_pos - s_pos) AS NUMBER);
+ s_pos := e_pos + 1;
+ END LOOP;
+ ret.extend;
+ ret(ret.COUNT) := CAST(SUBSTR(in_str, s_pos) AS NUMBER);
+ END IF;
+ RETURN ret;
+END;
+/
+
+CREATE OR REPLACE FUNCTION numbers_to_string(in_nums IN Numbers) RETURN VARCHAR2
+IS
+ ret VARCHAR2(1500);
+BEGIN
+ IF in_nums.COUNT != 0 THEN
+ FOR i IN in_nums.FIRST .. in_nums.LAST LOOP
+ IF i != in_nums.FIRST THEN
+ ret := ret || ',';
+ END IF;
+ ret := ret || in_nums(i);
+ END LOOP;
+ END IF;
+ RETURN ret;
+END;
+/
diff --git a/odb-tests/oracle/custom/driver.cxx b/odb-tests/oracle/custom/driver.cxx
new file mode 100644
index 0000000..45f0a4e
--- /dev/null
+++ b/odb-tests/oracle/custom/driver.cxx
@@ -0,0 +1,77 @@
+// file : oracle/custom/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test custom database type mapping in Oracle.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+namespace oracle = odb::oracle;
+using namespace oracle;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ object o (1);
+ o.iv.push_back (123);
+ o.iv.push_back (234);
+ o.iv.push_back (-345);
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+
+ // Update.
+ //
+ o.iv[0]++;
+ o.iv.pop_back ();
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/oracle/custom/test.hxx b/odb-tests/oracle/custom/test.hxx
new file mode 100644
index 0000000..523d50b
--- /dev/null
+++ b/odb-tests/oracle/custom/test.hxx
@@ -0,0 +1,40 @@
+// file : oracle/custom/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <vector>
+
+#include <odb/core.hxx>
+
+// Map Numbers VARRAY Oracle type to std::vector<int>. This type is defined
+// in the custom.sql file along with two helper functions that convert
+// between Numbers and its string representation. The other half of this
+// mapping is in traits.hxx (value_traits<std::vector<int>, id_string>).
+//
+#pragma db map type("Numbers") \
+ as("VARCHAR2(1500)") \
+ to("CAST(string_to_numbers((?)) AS Numbers)") \
+ from("numbers_to_string((?))")
+
+#pragma db object
+struct object
+{
+ object () {}
+ object (unsigned long id_) : id (id_) {}
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db type("Numbers")
+ std::vector<int> iv;
+
+ bool
+ operator== (const object& y) const
+ {
+ return id == y.id && iv == y.iv;
+ }
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/oracle/custom/traits.hxx b/odb-tests/oracle/custom/traits.hxx
new file mode 100644
index 0000000..8df2f91
--- /dev/null
+++ b/odb-tests/oracle/custom/traits.hxx
@@ -0,0 +1,76 @@
+// file : oracle/types/traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TRAITS_HXX
+#define TRAITS_HXX
+
+#include <vector>
+#include <sstream>
+#include <cstring> // std::memcpy
+#include <cassert> // std::memcpy
+
+#include <odb/oracle/traits.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+
+ 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 char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ v.clear ();
+
+ if (!is_null)
+ {
+ // Array format is "n1,n2,n3...".
+ //
+ std::istringstream is (std::string (b, n));
+
+ for (char c; !is.eof (); is >> c)
+ {
+ v.push_back (int ());
+ is >> v.back ();
+ }
+ }
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ std::ostringstream os;
+
+ for (value_type::const_iterator i (v.begin ()), e (v.end ()); i != e;)
+ {
+ os << *i;
+
+ if (++i != e)
+ os << ',';
+ }
+
+ const std::string& s (os.str ());
+ n = s.size ();
+ assert (n <= c);
+ std::memcpy (b, s.c_str (), n);
+ }
+ };
+ }
+}
+
+#endif // TRAITS_HXX
diff --git a/odb-tests/oracle/database/driver.cxx b/odb-tests/oracle/database/driver.cxx
new file mode 100644
index 0000000..499f136
--- /dev/null
+++ b/odb-tests/oracle/database/driver.cxx
@@ -0,0 +1,33 @@
+// file : oracle/database/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test that database constructors are unambiguous (compilation only).
+//
+
+#include <odb/oracle/database.hxx>
+
+namespace oracle = odb::oracle;
+using namespace oracle;
+
+int
+main (int argc, char* argv[])
+{
+ // This code should not execute.
+ //
+ if (argc != 0)
+ return 0;
+
+ {
+ database d1 ("bob", "secret", "db1");
+ }
+
+ {
+ database d1 ("bob", "secret", "svc1", "server1");
+ database d2 ("bob", "secret", "svc1", "server1", 999);
+ }
+
+ {
+ database d1 (argc, argv);
+ database d2 (argc, argv, false);
+ }
+}
diff --git a/odb-tests/oracle/native/driver.cxx b/odb-tests/oracle/native/driver.cxx
new file mode 100644
index 0000000..541d21e
--- /dev/null
+++ b/odb-tests/oracle/native/driver.cxx
@@ -0,0 +1,77 @@
+// file : oracle/native/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test native SQL execution.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/transaction.hxx>
+
+#include <common/common.hxx>
+
+using namespace std;
+namespace oracle = odb::oracle;
+using namespace oracle;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ // Create the database schema.
+ //
+ {
+ transaction t (db->begin ());
+
+ db->execute ("BEGIN "
+ " EXECUTE IMMEDIATE 'DROP TABLE oracle_native_test'; "
+ " EXCEPTION "
+ " WHEN OTHERS THEN "
+ " IF SQLCODE != -942 THEN RAISE; END IF; "
+ "END;");
+
+ db->execute ("CREATE TABLE oracle_native_test (n NUMERIC(10,0))");
+
+ t.commit ();
+ }
+
+ // Insert a few rows.
+ //
+ {
+ transaction t (db->begin ());
+
+ assert (
+ db->execute ("INSERT INTO oracle_native_test (n) VALUES (1)") == 1);
+
+ assert (
+ db->execute ("INSERT INTO oracle_native_test (n) VALUES (2)") == 1);
+
+ t.commit ();
+ }
+
+ // Select a few rows.
+ //
+ {
+ transaction t (db->begin ());
+
+ assert (
+ db->execute ("SELECT n FROM oracle_native_test WHERE n < 3") == 2);
+
+ assert (
+ db->execute ("SELECT n FROM oracle_native_test WHERE n > 3") == 0);
+
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/oracle/template/driver.cxx b/odb-tests/oracle/template/driver.cxx
new file mode 100644
index 0000000..13317df
--- /dev/null
+++ b/odb-tests/oracle/template/driver.cxx
@@ -0,0 +1,40 @@
+// file : oracle/template/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// PLACE TEST DESCRIPTION HERE
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+namespace oracle = odb::oracle;
+using namespace oracle;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/oracle/template/template-vc10.vcxproj b/odb-tests/oracle/template/template-vc10.vcxproj
new file mode 100644
index 0000000..92066cf
--- /dev/null
+++ b/odb-tests/oracle/template/template-vc10.vcxproj
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib\common-d.lib;odb-oracle-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib64\common-d.lib;odb-oracle-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib\common.lib;odb-oracle.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib64\common.lib;odb-oracle.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(m4_patsubst(__value__(odb_options), __value__(src_base)/, ) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/oracle/template/template-vc10.vcxproj.filters b/odb-tests/oracle/template/template-vc10.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/oracle/template/template-vc10.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/oracle/template/template-vc11.vcxproj b/odb-tests/oracle/template/template-vc11.vcxproj
new file mode 100644
index 0000000..a92e437
--- /dev/null
+++ b/odb-tests/oracle/template/template-vc11.vcxproj
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib\common-d.lib;odb-oracle-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib64\common-d.lib;odb-oracle-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib\common.lib;odb-oracle.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib64\common.lib;odb-oracle.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(m4_patsubst(__value__(odb_options), __value__(src_base)/, ) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/oracle/template/template-vc11.vcxproj.filters b/odb-tests/oracle/template/template-vc11.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/oracle/template/template-vc11.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/oracle/template/template-vc12.vcxproj b/odb-tests/oracle/template/template-vc12.vcxproj
new file mode 100644
index 0000000..de35c03
--- /dev/null
+++ b/odb-tests/oracle/template/template-vc12.vcxproj
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib\common-d.lib;odb-oracle-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib64\common-d.lib;odb-oracle-d.lib;odb-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib\common.lib;odb-oracle.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\libcommon\lib64\common.lib;odb-oracle.lib;odb.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(m4_patsubst(__value__(odb_options), __value__(src_base)/, ) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/oracle/template/template-vc12.vcxproj.filters b/odb-tests/oracle/template/template-vc12.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/oracle/template/template-vc12.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/oracle/template/template-vc8.vcproj b/odb-tests/oracle/template/template-vc8.vcproj
new file mode 100644
index 0000000..71e2b52
--- /dev/null
+++ b/odb-tests/oracle/template/template-vc8.vcproj
@@ -0,0 +1,354 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\libcommon\lib\common-d.lib odb-oracle-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\libcommon\lib\common.lib odb-oracle.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\libcommon\lib64\common-d.lib odb-oracle-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\libcommon\lib64\common.lib odb-oracle.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(m4_patsubst(__value__(odb_options), __value__(src_base)/, ) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1400 -I$(SolutionDir)\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/oracle/template/template-vc9.vcproj b/odb-tests/oracle/template/template-vc9.vcproj
new file mode 100644
index 0000000..de9804e
--- /dev/null
+++ b/odb-tests/oracle/template/template-vc9.vcproj
@@ -0,0 +1,361 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\libcommon\lib\common-d.lib odb-oracle-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\libcommon\lib\common.lib odb-oracle.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\libcommon\lib64\common-d.lib odb-oracle-d.lib odb-d.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\libcommon\lib64\common.lib odb-oracle.lib odb.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(m4_patsubst(__value__(odb_options), __value__(src_base)/, ) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/oracle/template/test.hxx b/odb-tests/oracle/template/test.hxx
new file mode 100644
index 0000000..bfa9cc9
--- /dev/null
+++ b/odb-tests/oracle/template/test.hxx
@@ -0,0 +1,25 @@
+// file : oracle/template/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/oracle/types/driver.cxx b/odb-tests/oracle/types/driver.cxx
new file mode 100644
index 0000000..2e3e2e7
--- /dev/null
+++ b/odb-tests/oracle/types/driver.cxx
@@ -0,0 +1,366 @@
+// file : oracle/types/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test Oracle type conversion.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+namespace oracle = odb::oracle;
+using namespace oracle;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ // Create an Oracle database instance, setting both the client database
+ // and national character set to UTF-8.
+ //
+ auto_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ object o (1);
+
+ o.int_ = -123456;
+ o.uint_ = 123456;
+ o.long_long_ = -123456;
+ o.ulong_long_ = 123456;
+
+ o.float_ = 1.123F;
+ o.double_ = 1.123;
+ o.num_float_ = 1.123F;
+ o.num_double_ = 1.123;
+ o.binary_float_ = 1.123F;
+ o.binary_double_ = 1.123;
+
+ o.date_ = date_time (2010, 8, 29, 15, 33, 18, 0);
+ o.timestamp_ = date_time (1996, 3, 9, 18, 2, 54, 123000);
+ o.interval_ds_ = time_interval (0, 0, 13, 15, 23, 19, 123000);
+ o.interval_ym_ = time_interval (12, 3, 0, 0, 0, 0, 0);
+
+ string vshort_str (8, 's');
+ string short_str (13, 's');
+ string medium_str (104, 'm');
+ string long_str (1018, 'l');
+ string vlong_str (15000, 'v');
+
+ o.char_ = short_str;
+ o.varchar2_ = medium_str;
+ o.clob_.assign (vlong_str.data (), vlong_str.data () + vlong_str.size ());
+
+ o.nchar_ = vshort_str;
+ o.nvarchar2_ = medium_str;
+ o.nclob_.assign (vlong_str.data (), vlong_str.data () + vlong_str.size ());
+
+ o.empty_c_.push_back ("");
+
+ o.raw_.assign (long_str.data (), long_str.data () + long_str.size ());
+ o.blob_.assign (vlong_str.data (), vlong_str.data () + vlong_str.size ());
+
+ o.strs_.push_back (short_str);
+ o.strs_.push_back (medium_str);
+ o.strs_.push_back (long_str);
+ o.strs_.push_back (vlong_str);
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+
+ // Test character set conversion.
+ //
+ const char* unicode_str = "a \xD5\x95 \xEA\xAA\xAA \xF2\xAA\xAA\xAA";
+
+ // Testing of character set conversion to and from the client's database
+ // character set is disabled as the server database character set may
+ // not be able to represent some Unicode characters. If this were the case
+ // the test outcome would be a false negative.
+ //
+ // o.char_ = unicode_str;
+ // o.varchar2_ = unicode_str;
+ // o.clob_ = unicode_str;
+
+ o.nchar_ = unicode_str;
+ o.nvarchar2_ = unicode_str;
+ o.nclob_ = unicode_str;
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+
+ // Test 64 bit integers.
+ //
+ big_int bi1 (1, 0x8000000000000000LL);
+ big_int bi2 (2, -123456);
+ big_int bi3 (3, 0);
+ big_int bi4 (4, 123456);
+ big_int bi5 (5, 0xFFFFFFFFFFFFFFFFULL);
+
+ big_uint bui1 (1, 0);
+ big_uint bui2 (2, 123456);
+ big_uint bui3 (3, 0xFFFFFFFFFFFFFFFFULL);
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (bi1);
+ db->persist (bi2);
+ db->persist (bi3);
+ db->persist (bi4);
+ db->persist (bi5);
+ db->persist (bui1);
+ db->persist (bui2);
+ db->persist (bui3);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<big_int> bil1 (db->load<big_int> (1));
+ auto_ptr<big_int> bil2 (db->load<big_int> (2));
+ auto_ptr<big_int> bil3 (db->load<big_int> (3));
+ auto_ptr<big_int> bil4 (db->load<big_int> (4));
+ auto_ptr<big_int> bil5 (db->load<big_int> (5));
+ auto_ptr<big_uint> buil1 (db->load<big_uint> (1));
+ auto_ptr<big_uint> buil2 (db->load<big_uint> (2));
+ auto_ptr<big_uint> buil3 (db->load<big_uint> (3));
+ t.commit ();
+
+ assert (bi1 == *bil1);
+ assert (bi2 == *bil2);
+ assert (bi3 == *bil3);
+ assert (bi4 == *bil4);
+ assert (bi5 == *bil5);
+ assert (bui1 == *buil1);
+ assert (bui2 == *buil2);
+ assert (bui3 == *buil3);
+ }
+
+ // Test large BLOBs.
+ //
+ descriptor b1 (1);
+ b1.blob.assign (50000, 'b');
+ b1.timestamp = date_time (1996, 3, 9, 18, 2, 54, 123000);
+ b1.interval_ds = time_interval (0, 0, 13, 15, 23, 19, 123000);
+ b1.interval_ym = time_interval (12, 3, 0, 0, 0, 0, 0);
+
+ descriptor b2 (2);
+ b2.blob.assign (500000, 'b');
+ b2.timestamp = date_time (1997, 4, 10, 19, 3, 55, 234000);
+ b2.interval_ds = time_interval (0, 0, 14, 16, 24, 20, 234000);
+ b2.interval_ym = time_interval (13, 4, 0, 0, 0, 0, 0);
+
+ descriptor b3 (3);
+ b3.blob.assign (5000, 'b');
+ b3.timestamp = date_time (1995, 2, 8, 17, 1, 53, 120000);
+ b3.interval_ds = time_interval (0, 0, 12, 14, 22, 18, 120000);
+ b3.interval_ym = time_interval (11, 2, 0, 0, 0, 0, 0);
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (b1);
+ db->persist (b2);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<descriptor> p1 (db->load<descriptor> (1));
+ auto_ptr<descriptor> p2 (db->load<descriptor> (2));
+ t.commit ();
+
+ assert (b1 == *p1);
+ assert (b2 == *p2);
+ }
+
+ // Test image copying with descriptor-based type (LOB, date-time) data.
+ //
+ {
+ typedef oracle::query<descriptor> query;
+ typedef odb::result<descriptor> result;
+
+ transaction t (db->begin ());
+
+ // Pre-bind the image for other operations.
+ //
+ {
+ db->persist (b3);
+ db->update (b3);
+ db->reload (b3);
+ db->erase (b3);
+ }
+
+
+ result r (db->query<descriptor> (query::id < 3));
+ result::iterator i (r.begin ());
+
+ assert (i != r.end ());
+
+ {
+ result r (db->query<descriptor> (query::id > 1));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (*i == b2);
+ assert (++i == r.end ());
+ }
+
+ assert (*i == b1); // Load from copy (copy c-tor).
+
+ ++i;
+ assert (i != r.end ());
+
+ {
+ result r (db->query<descriptor> (query::id < 2));
+ result::iterator i (r.begin ());
+ assert (i != r.end ());
+ assert (*i == b1);
+ assert (++i == r.end ());
+ }
+
+ assert (*i == b2); // Load from copy (copy assign).
+ assert (++i == r.end ());
+
+ // Make sure all other operations are still working.
+ //
+ {
+ db->persist (b3);
+#ifdef HAVE_CXX11
+ unique_ptr<descriptor> p (db->load<descriptor> (3));
+#else
+ auto_ptr<descriptor> p (db->load<descriptor> (3));
+#endif
+ assert (b3 == *p);
+ b3.blob.push_back (123);
+ db->update (b3);
+ db->reload (p);
+ assert (b3 == *p);
+ db->erase (b3);
+ }
+
+ t.commit ();
+ }
+
+ // Test descriptor management in TIMESTAMP and INTERVAL images.
+ //
+ {
+ typedef oracle::query<object> query;
+ typedef odb::result<object> result;
+
+ query q (query::timestamp == o.timestamp_ &&
+ query::interval_ym == o.interval_ym_ &&
+ query::interval_ds == o.interval_ds_);
+
+ transaction t (db->begin ());
+
+ {
+ result r (db->query<object> (q));
+ assert (size (r) == 1);
+ }
+
+ {
+ result r (db->query<object> (q));
+ assert (size (r) == 1);
+ }
+
+ {
+ // Query temporary.
+ //
+ result r (db->query<object> (
+ query::timestamp == o.timestamp_ &&
+ query::interval_ym == o.interval_ym_ &&
+ query::interval_ds == o.interval_ds_));
+
+ query dummy (query::timestamp == o.timestamp_ &&
+ query::interval_ym == o.interval_ym_ &&
+ query::interval_ds == o.interval_ds_);
+
+ assert (size (r) == 1);
+ }
+
+ t.commit ();
+ }
+
+ // Test char array.
+ //
+ {
+ char_array o1 (1, "");
+ char_array o2 (2, "1234567890");
+ char_array o3 (3, "1234567890123456");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o3);
+ t.commit ();
+ }
+
+ // Oracle returns padded values for CHAR(N) unless they are
+ // empty (represented as NULL).
+ //
+ memcpy (o2.s2, "1234567890 ", 16);
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<char_array> p1 (db->load<char_array> (1));
+ auto_ptr<char_array> p2 (db->load<char_array> (2));
+ auto_ptr<char_array> p3 (db->load<char_array> (3));
+ t.commit ();
+
+ assert (o1 == *p1);
+ assert (o2 == *p2);
+ assert (o3 == *p3);
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/oracle/types/test.hxx b/odb-tests/oracle/types/test.hxx
new file mode 100644
index 0000000..255bc08
--- /dev/null
+++ b/odb-tests/oracle/types/test.hxx
@@ -0,0 +1,353 @@
+// file : oracle/types/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <common/config.hxx> // HAVE_CXX11
+
+#include <string>
+#include <vector>
+#include <memory> // std::auto_ptr
+#include <cstring> // std::memcpy, std::str[n]cmp, std::strlen
+
+#include <odb/core.hxx>
+
+struct date_time
+{
+ date_time ()
+ {
+ }
+
+ date_time (unsigned short y,
+ unsigned char m,
+ unsigned char d,
+ unsigned char h,
+ unsigned char min,
+ unsigned char sec,
+ unsigned int nsec)
+ : year (y),
+ month (m),
+ day (d),
+ hour (h),
+ minute (min),
+ second (sec),
+ nanosecond (nsec)
+ {
+ }
+
+ bool
+ operator== (const date_time& y) const
+ {
+ return
+ year == y.year &&
+ month == y.month &&
+ day == y.day &&
+ hour == y.hour &&
+ minute == y.minute &&
+ second == y.second &&
+ nanosecond == y.nanosecond;
+ }
+
+ unsigned short year;
+ unsigned char month;
+ unsigned char day;
+ unsigned char hour;
+ unsigned char minute;
+ unsigned char second;
+ unsigned int nanosecond;
+};
+
+struct time_interval
+{
+ time_interval ()
+ {
+ }
+
+ time_interval (int y, int m, int d, int h, int min, int sec, int nsec)
+ : year (y),
+ month (m),
+ day (d),
+ hour (h),
+ minute (min),
+ second (sec),
+ nanosecond (nsec)
+ {
+ }
+
+ bool
+ operator== (const time_interval& y) const
+ {
+ return
+ year == y.year &&
+ month == y.month &&
+ day == y.day &&
+ hour == y.hour &&
+ minute == y.minute &&
+ second == y.second &&
+ nanosecond == y.nanosecond;
+ }
+
+ int year;
+ int month;
+ int day;
+ int hour;
+ int minute;
+ int second;
+ int nanosecond;
+};
+
+#ifdef HAVE_CXX11
+typedef std::unique_ptr<std::string> string_ptr;
+#else
+typedef std::auto_ptr<std::string> string_ptr;
+#endif
+
+typedef std::vector<std::string> strings;
+
+#pragma db object
+struct object
+{
+ object () {}
+ object (unsigned long id): id_ (id) {}
+
+ #pragma db id
+ unsigned int id_;
+
+ // Integral types.
+ //
+ #pragma db type ("NUMBER(10)")
+ int int_;
+
+ #pragma db type ("NUMBER(10)")
+ unsigned uint_;
+
+ #pragma db type ("NUMBER(19)")
+ long long long_long_;
+
+ #pragma db type ("NUMBER(20)")
+ unsigned long long ulong_long_;
+
+ // Float types.
+ //
+ #pragma db type ("FLOAT(24)")
+ float float_;
+
+ #pragma db type ("FLOAT(53)")
+ double double_;
+
+ #pragma db type ("NUMBER(7,3)")
+ float num_float_;
+
+ #pragma db type ("NUMBER(15,5)")
+ double num_double_;
+
+ #pragma db type ("BINARY_FLOAT")
+ float binary_float_;
+
+ #pragma db type ("BINARY_DOUBLE")
+ double binary_double_;
+
+ // Data-time types.
+ //
+ #pragma db type ("DATE")
+ date_time date_;
+
+ #pragma db type ("TIMESTAMP(6)")
+ date_time timestamp_;
+
+ #pragma db type ("INTERVAL DAY TO SECOND")
+ time_interval interval_ds_;
+
+ #pragma db type ("INTERVAL YEAR TO MONTH")
+ time_interval interval_ym_;
+
+ // String and binary types.
+ //
+ #pragma db type ("CHAR(13)")
+ std::string char_;
+
+ #pragma db type ("VARCHAR2(512)") null
+ std::string varchar2_;
+
+ #pragma db type ("NCHAR(8)")
+ std::string nchar_;
+
+ #pragma db type ("NVARCHAR2(512)") null
+ std::string nvarchar2_;
+
+ // Oracle treats empty and NULL VARCHAR2 the same. Test that we
+ // handle this.
+ //
+ std::string empty_;
+ std::vector<std::string> empty_c_;
+
+ #pragma db type ("RAW(1024)")
+ std::vector<char> raw_;
+
+ // LOB types.
+ //
+ #pragma db type ("BLOB")
+ std::vector<char> blob_;
+
+ #pragma db type ("CLOB")
+ std::string clob_;
+
+ #pragma db type ("NCLOB")
+ std::string nclob_;
+
+ // Test containers of LOBs
+ //
+ #pragma db value_type ("CLOB")
+ strings strs_;
+
+ // Test NULL value.
+ //
+ #pragma db type ("VARCHAR2(32)") null
+ string_ptr null_;
+
+ bool
+ operator== (const object& y) const
+ {
+ return
+ id_ == y.id_ &&
+ int_ == y.int_ &&
+ uint_ == y.uint_ &&
+ long_long_ == y.long_long_ &&
+ ulong_long_ == y.ulong_long_ &&
+ float_ == y.float_ &&
+ double_ == y.double_ &&
+ num_float_ == y.num_float_ &&
+ num_double_ == y.num_double_ &&
+ binary_float_ == y.binary_float_ &&
+ binary_double_ == y.binary_double_ &&
+ date_ == y.date_ &&
+ timestamp_ == y.timestamp_ &&
+ interval_ds_ == y.interval_ds_ &&
+ interval_ym_ == y.interval_ym_ &&
+ char_ == y.char_ &&
+ varchar2_ == y.varchar2_ &&
+ nchar_ == y.nchar_ &&
+ nvarchar2_ == y.nvarchar2_ &&
+ empty_ == y.empty_ &&
+ empty_c_ == y.empty_c_ &&
+ raw_ == y.raw_ &&
+ blob_ == y.blob_ &&
+ clob_ == y.clob_ &&
+ nclob_ == y.nclob_ &&
+ strs_ == y.strs_ &&
+ ((null_.get () == 0 && y.null_.get () == 0) || *null_ == *y.null_);
+ }
+};
+
+#pragma db object
+struct big_uint
+{
+ big_uint (unsigned int id = 0, unsigned long long v = 0)
+ : id_ (id), value (v)
+ {
+ }
+
+ #pragma db id
+ unsigned int id_;
+
+ unsigned long long value;
+
+ bool
+ operator== (const big_uint& y) const
+ {
+ return id_ == y.id_ && value == y.value;
+ }
+};
+
+#pragma db object
+struct big_int
+{
+ big_int (unsigned int id = 0, long long v = 0)
+ : id_ (id), value (v)
+ {
+ }
+
+ #pragma db id
+ unsigned int id_;
+
+ long long value;
+
+ bool
+ operator== (const big_int& y) const
+ {
+ return id_ == y.id_ && value == y.value;
+ }
+};
+
+#pragma db object
+struct descriptor
+{
+ descriptor (unsigned int id = 0): id_ (id) {}
+
+ #pragma db id
+ unsigned int id_;
+
+ #pragma db type ("BLOB")
+ std::vector<char> blob;
+
+ #pragma db type ("TIMESTAMP(6)")
+ date_time timestamp;
+
+ #pragma db type ("INTERVAL DAY TO SECOND")
+ time_interval interval_ds;
+
+ #pragma db type ("INTERVAL YEAR TO MONTH")
+ time_interval interval_ym;
+
+ bool
+ operator== (const descriptor& y) const
+ {
+ return id_ == y.id_ &&
+ blob == y.blob &&
+ timestamp == y.timestamp &&
+ interval_ds == y.interval_ds &&
+ interval_ym == y.interval_ym;
+ }
+};
+
+// Test char array.
+//
+#pragma db object
+struct char_array
+{
+ char_array () {}
+ char_array (unsigned long id, const char* s)
+ : id_ (id)
+ {
+ std::memcpy (s1, s, std::strlen (s) + 1); // VC++ strncpy deprecation.
+ std::memcpy (s2, s, std::strlen (s) + 1);
+ s3[0] = c1 = *s;
+ }
+
+ #pragma db id
+ unsigned long id_;
+
+ char s1[17];
+
+ #pragma db type("CHAR(16)") null
+ char s2[16];
+
+ #pragma db null
+ char s3[1];
+
+ #pragma db null
+ char c1;
+
+ bool
+ operator== (const char_array& y) const
+ {
+ return id_ == y.id_ &&
+ std::strcmp (s1, y.s1) == 0 &&
+ std::strncmp (s2, y.s2, sizeof (s2)) == 0 &&
+ s3[0] == y.s3[0] &&
+ c1 == y.c1;
+ }
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/oracle/types/traits.hxx b/odb-tests/oracle/types/traits.hxx
new file mode 100644
index 0000000..ad747d8
--- /dev/null
+++ b/odb-tests/oracle/types/traits.hxx
@@ -0,0 +1,192 @@
+// file : oracle/types/traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TRAITS_HXX
+#define TRAITS_HXX
+
+#include <odb/oracle/oracle-types.hxx> // datetime, interval_ym, interval_ds
+#include <odb/oracle/traits.hxx>
+
+#include <odb/oracle/details/date.hxx>
+
+#include "test.hxx" // date_time, time_interval
+
+namespace odb
+{
+ namespace oracle
+ {
+ template <>
+ class value_traits<date_time, id_date>
+ {
+ public:
+ typedef date_time value_type;
+ typedef date_time query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (date_time& v, const char* i, bool is_null)
+ {
+ if (!is_null)
+ {
+ short y (0);
+ unsigned char m (0), d (0), h (0), mins (0), s (0);
+
+ details::get_date (i, y, m, d, h, mins, s);
+
+ v.year = y;
+ v.month = m;
+ v.day = d;
+ v.hour = h;
+ v.minute = mins;
+ v.second = s;
+
+ // Oracle DATE does not support fractional seconds.
+ //
+ v.nanosecond = 0;
+ }
+ }
+
+ static void
+ set_image (char* i, bool& is_null, const date_time& v)
+ {
+ is_null = false;
+ details::set_date (i,
+ static_cast<unsigned short> (v.year),
+ v.month,
+ v.day,
+ v.hour,
+ v.minute,
+ v.second);
+ }
+ };
+
+ template <>
+ class value_traits<date_time, id_timestamp>
+ {
+ public:
+ typedef date_time value_type;
+ typedef date_time query_type;
+ typedef datetime image_type;
+
+ static void
+ set_value (date_time& v, const datetime& i, bool is_null)
+ {
+ if (!is_null)
+ {
+ sb2 y (0);
+ ub1 m (0), d (0), h (0), mins (0), s (0);
+ ub4 ns (0);
+
+ i.get (y, m, d, h, mins, s, ns);
+
+ v.year = y;
+ v.month = m;
+ v.day = d;
+ v.hour = h;
+ v.minute = mins;
+ v.second = s;
+ v.nanosecond = ns;
+ }
+ }
+
+ static void
+ set_image (datetime& i,
+ bool& is_null,
+ const date_time& v)
+ {
+ is_null = false;
+
+ i.set (static_cast<sb2> (v.year),
+ v.month,
+ v.day,
+ v.hour,
+ v.minute,
+ v.second,
+ v.nanosecond);
+ }
+ };
+
+ template <>
+ class value_traits<time_interval, id_interval_ds>
+ {
+ public:
+ typedef time_interval value_type;
+ typedef time_interval query_type;
+ typedef interval_ds image_type;
+
+ static void
+ set_value (time_interval& v,
+ const interval_ds& i,
+ bool is_null)
+ {
+ if (!is_null)
+ {
+ sb4 d (0), h (0), m (0), s (0), ns (0);
+ i.get (d, h, m, s, ns);
+
+ v.year = 0;
+ v.month = 0;
+ v.day = static_cast<unsigned char> (d);
+ v.hour = static_cast<unsigned char> (h);
+ v.minute = static_cast<unsigned char> (m);
+ v.second = static_cast<unsigned char> (s);
+ v.nanosecond = static_cast<unsigned int> (ns);
+ }
+ }
+
+ static void
+ set_image (interval_ds& i,
+ bool& is_null,
+ const time_interval& v)
+ {
+ is_null = false;
+
+ i.set (v.day,
+ v.hour,
+ v.minute,
+ v.second,
+ static_cast<sb4> (v.nanosecond));
+ }
+ };
+
+ template <>
+ class value_traits<time_interval, id_interval_ym>
+ {
+ public:
+ typedef time_interval value_type;
+ typedef time_interval query_type;
+ typedef interval_ym image_type;
+
+ static void
+ set_value (time_interval& v,
+ const interval_ym& i,
+ bool is_null)
+ {
+ if (!is_null)
+ {
+ sb4 y (0), m (0);
+ i.get (y, m);
+
+ v.year = static_cast<unsigned short> (y);
+ v.month = static_cast<unsigned char> (m);
+ v.day = 0;
+ v.hour = 0;
+ v.minute = 0;
+ v.second = 0;
+ v.nanosecond = 0;
+ }
+ }
+
+ static void
+ set_image (interval_ym& i,
+ bool& is_null,
+ const time_interval& v)
+ {
+ is_null = false;
+ i.set (v.year, v.month);
+ }
+ };
+ }
+}
+
+#endif // TRAITS_HXX
diff --git a/odb-tests/pgsql-schema.testscript b/odb-tests/pgsql-schema.testscript
new file mode 100644
index 0000000..8659bcd
--- /dev/null
+++ b/odb-tests/pgsql-schema.testscript
@@ -0,0 +1,6 @@
+# file : pgsql-schema.testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+# Create the PostgreSQL database schema creation canned command base.
+#
+create_schema_cmd = [cmdline] $pgsql_client_cmd
diff --git a/odb-tests/pgsql.testscript b/odb-tests/pgsql.testscript
new file mode 100644
index 0000000..e4b4636
--- /dev/null
+++ b/odb-tests/pgsql.testscript
@@ -0,0 +1,12 @@
+# file : pgsql.testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+# Create the PostgreSQL database schema creation canned command and setup the
+# test driver command line for the subsequent tests.
+#
+.include pgsql-schema.testscript
+
+schema_file = [path] $out_base/"$schema"($multi ? '-pgsql' : '').sql
+create_schema = [cmdline] $create_schema_cmd -f $schema_file
+
+test.arguments += ($multi ? 'pgsql' : ) $pgsql_options
diff --git a/odb-tests/pgsql/buildfile b/odb-tests/pgsql/buildfile
new file mode 100644
index 0000000..c6385c0
--- /dev/null
+++ b/odb-tests/pgsql/buildfile
@@ -0,0 +1,6 @@
+# file : pgsql/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -bulk/}
+
+./: bulk/: include = ($pgsql && !$multi && $pgsql_bulk)
diff --git a/odb-tests/pgsql/bulk/buildfile b/odb-tests/pgsql/bulk/buildfile
new file mode 100644
index 0000000..1885fdd
--- /dev/null
+++ b/odb-tests/pgsql/bulk/buildfile
@@ -0,0 +1,39 @@
+# file : pgsql/bulk/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($pgsql) "pgsql should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+ assert ($pgsql_bulk) "bulk operations are disabled for pgsql"
+}
+
+import libodb = libodb%lib{odb}
+
+import libs = libpq%lib{pq}
+import libs += libodb-pgsql%lib{odb-pgsql}
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix pgsql_bulk_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{pgsql-client}: include = adhoc
diff --git a/odb-tests/pgsql/bulk/driver.cxx b/odb-tests/pgsql/bulk/driver.cxx
new file mode 100644
index 0000000..6b30073
--- /dev/null
+++ b/odb-tests/pgsql/bulk/driver.cxx
@@ -0,0 +1,363 @@
+// file : pgsql/bulk/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test transaction savepoints.
+//
+
+#include <libpq-fe.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <sys/select.h>
+
+// Note: hack (also, some platforms, like Mac OS, provide it).
+//
+#include <arpa/inet.h>
+#ifndef htonll
+# define htonll(x) ((((long long)htonl(x)) << 32) + htonl((x) >> 32))
+#endif
+
+#undef NDEBUG
+#include <assert.h>
+
+static const size_t columns = 3;
+
+struct data
+{
+ long long id;
+ long long idata;
+ const char* sdata;
+};
+
+static char* values[columns];
+static int lengths[columns];
+static int formats[columns] = {1, 1, 1};
+
+static const unsigned int types[columns] = {
+ 20, // int8
+ 20, // int8
+ 25 // text
+};
+
+static void
+init (const struct data* d)
+{
+ values[0] = (char*)&d->id;
+ lengths[0] = sizeof (d->id);
+
+ values[1] = (char*)&d->idata;
+ lengths[1] = sizeof (d->idata);
+
+ values[2] = (char*)d->sdata;
+ lengths[2] = strlen (d->sdata);
+}
+
+static void
+execute (PGconn* conn, const struct data* ds, size_t n)
+{
+ int sock = PQsocket (conn);
+ assert (sock != -1);
+
+ if (PQsetnonblocking (conn, 1) == -1 ||
+ PQenterPipelineMode (conn) == 0)
+ assert (false);
+
+ // True if we've written and read everything, respectively.
+ //
+ bool wdone = false;
+ bool rdone = false;
+
+ size_t wn = 0;
+ size_t rn = 0;
+
+ while (!rdone)
+ {
+ fd_set wds;
+ if (!wdone)
+ {
+ FD_ZERO (&wds);
+ FD_SET (sock, &wds);
+ }
+
+ fd_set rds;
+ FD_ZERO (&rds);
+ FD_SET (sock, &rds);
+
+ if (select (sock + 1, &rds, wdone ? NULL : &wds, NULL, NULL) == -1)
+ {
+ if (errno == EINTR)
+ continue;
+
+ assert (false);
+ }
+
+ // Try to minimize the chance of blocking the server by first processing
+ // the result and then sending more queries.
+ //
+ if (FD_ISSET (sock, &rds))
+ {
+ if (PQconsumeInput (conn) == 0)
+ assert (false);
+
+ while (wn > rn && PQisBusy (conn) == 0)
+ {
+ //fprintf (stderr, "PQgetResult %zu\n", rn);
+
+ PGresult* res = PQgetResult (conn);
+ assert (res != NULL);
+ ExecStatusType stat = PQresultStatus (res);
+
+ if (stat == PGRES_PIPELINE_SYNC)
+ {
+ assert (wdone && rn == n);
+ PQclear (res);
+ rdone = true;
+ break;
+ }
+
+ if (stat == PGRES_FATAL_ERROR)
+ {
+ const char* s = PQresultErrorField (res, PG_DIAG_SQLSTATE);
+
+ if (strcmp (s, "23505") == 0)
+ fprintf (stderr, "duplicate id at %zu\n", rn);
+ }
+
+ PQclear (res);
+ assert (rn != n);
+ ++rn;
+
+ // We get a NULL result after each query result.
+ //
+ {
+ PGresult* end = PQgetResult (conn);
+ assert (end == NULL);
+ }
+ }
+ }
+
+ if (!wdone && FD_ISSET (sock, &wds))
+ {
+ // Send queries until we get blocked (write-biased). This feels like
+ // a better overall strategy to keep the server busy compared to
+ // sending one query at a time and then re-checking if there is
+ // anything to read because the results of INSERT/UPDATE/DELETE are
+ // presumably small and quite a few of them can get buffered before
+ // the server gets blocked.
+ //
+ for (;;)
+ {
+ if (wn < n)
+ {
+ //fprintf (stderr, "PQsendQueryPrepared %zu\n", wn);
+
+ init (ds + wn);
+
+ if (PQsendQueryPrepared (conn,
+ "persist_object",
+ (int)(columns),
+ values,
+ lengths,
+ formats,
+ 1) == 0)
+ assert (false);
+
+ if (++wn == n)
+ {
+ if (PQpipelineSync (conn) == 0)
+ assert (false);
+
+ ++wn;
+ }
+ }
+
+ // PQflush() result:
+ //
+ // 0 -- success (queue is now empty)
+ // 1 -- blocked
+ // -1 -- error
+ //
+ int r = PQflush (conn);
+ assert (r != -1);
+
+ if (r == 0)
+ {
+ if (wn < n)
+ {
+ // If we continue here, then we are write-biased. And if we
+ // break, then we are read-biased.
+ //
+#if 0
+ break;
+#else
+ continue;
+#endif
+ }
+
+ wdone = true;
+ }
+
+ break; // Blocked or done.
+ }
+ }
+ }
+
+ if (PQexitPipelineMode (conn) == 0 ||
+ PQsetnonblocking (conn, 0) == -1)
+ assert (false);
+}
+
+static void
+test (PGconn* conn)
+{
+ const size_t batch = 500;
+ struct data ds[batch];
+
+ for (size_t i = 0; i != batch; ++i)
+ {
+ ds[i].id = htonll (i == batch / 2 ? i - 1 : i); // Cause duplicate PK.
+ ds[i].idata = htonll (i);
+ ds[i].sdata = "abc";
+ }
+
+ // Prepare the statement.
+ //
+ {
+ PGresult* res = PQprepare (
+ conn,
+ "persist_object",
+ "INSERT INTO \"pgsql_bulk_object\" "
+ "(\"id\", "
+ "\"idata\", "
+ "\"sdata\") "
+ "VALUES "
+ "($1, $2, $3)",
+ (int)(columns),
+ types);
+ assert (PQresultStatus (res) == PGRES_COMMAND_OK);
+ PQclear (res);
+ }
+
+ // Begin transaction.
+ //
+ {
+ PGresult* res = PQexec (conn, "begin");
+ assert (PQresultStatus (res) == PGRES_COMMAND_OK);
+ PQclear (res);
+ }
+
+ execute (conn, ds, batch);
+
+ // Commit transaction.
+ //
+ {
+ PGresult* res = PQexec (conn, "commit");
+ assert (PQresultStatus (res) == PGRES_COMMAND_OK);
+ PQclear (res);
+ }
+}
+
+#include <vector>
+#include <memory> // std::unique_ptr
+#include <cstring>
+#include <iostream>
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+namespace pgsql = odb::pgsql;
+using namespace pgsql;
+
+int
+main (int argc, char* argv[])
+{
+ bool fail_already_persistent (false);
+
+ for (int i (1); i != argc; ++i)
+ {
+ if (strcmp (argv[i], "--fail-already-persistent") == 0)
+ {
+ fail_already_persistent = true;
+
+ for (; i != argc - 1; ++i)
+ argv[i] = argv[i + 1];
+
+ --argc;
+
+ break;
+ }
+ }
+
+ try
+ {
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ connection_ptr cn (db->connection ());
+
+ if (false)
+ {
+ PGconn* conn (cn->handle ());
+ test (conn);
+ }
+
+ {
+ const unsigned long n (500);
+
+ vector<object> os;
+
+ for (unsigned long i (0); i != n; ++i)
+ {
+ os.push_back (object {i, i, string (i, 'x')});
+
+ if (fail_already_persistent && i == n / 2)
+ os.push_back (object {i, i, to_string (i)});
+ }
+
+ {
+ transaction t (cn->begin ());
+ db->persist (os.begin (), os.end ());
+ t.commit ();
+ }
+
+ {
+ transaction t (cn->begin ());
+ db->find<object> (2);
+ t.commit ();
+ }
+
+ for (unsigned long i (0); i != n; ++i)
+ {
+ //assert (os[i].id == i + 1);
+ os[i].idata++;
+ }
+
+ {
+ transaction t (cn->begin ());
+ db->update (os.begin (), os.end ());
+ t.commit ();
+ }
+
+ {
+ transaction t (cn->begin ());
+ db->erase (os.begin (), os.end ());
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/pgsql/bulk/test.hxx b/odb-tests/pgsql/bulk/test.hxx
new file mode 100644
index 0000000..25dd138
--- /dev/null
+++ b/odb-tests/pgsql/bulk/test.hxx
@@ -0,0 +1,34 @@
+// file : pgsql/savepoint/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+
+#include <odb/core.hxx>
+
+#pragma db object bulk(1000)
+struct object
+{
+ /*
+ object (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object ()
+ {
+ }
+ */
+
+ #pragma db id //auto
+ unsigned long id;
+
+ unsigned long idata;
+
+ //#pragma db
+ std::string sdata;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/pgsql/bulk/testscript b/odb-tests/pgsql/bulk/testscript
new file mode 100644
index 0000000..60b7d92
--- /dev/null
+++ b/odb-tests/pgsql/bulk/testscript
@@ -0,0 +1,18 @@
+# file : pgsql/custom/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../pgsql.testscript
+
++$create_schema
+
+: basics
+:
+{
+ $*;
+
+ $* --fail-already-persistent 2>>EOE != 0
+ multiple exceptions, 252 elements attempted, 1 failed, fatal:
+ [251] object already persistent
+ EOE
+}
diff --git a/odb-tests/pgsql/custom/buildfile b/odb-tests/pgsql/custom/buildfile
new file mode 100644
index 0000000..b3032fa
--- /dev/null
+++ b/odb-tests/pgsql/custom/buildfile
@@ -0,0 +1,40 @@
+# file : pgsql/custom/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($pgsql) "pgsql should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-pgsql%lib{odb-pgsql}
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix pgsql_custom_ \
+ --generate-schema \
+ --default-database common \
+ --generate-query \
+ --hxx-prologue '#include "traits.hxx"' \
+ --hxx-prologue '#include "query.hxx"'
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{pgsql-client}: include = adhoc
diff --git a/odb-tests/pgsql/custom/driver.cxx b/odb-tests/pgsql/custom/driver.cxx
new file mode 100644
index 0000000..91ff0ce
--- /dev/null
+++ b/odb-tests/pgsql/custom/driver.cxx
@@ -0,0 +1,125 @@
+// file : pgsql/custom/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test custom database type mapping in PostgreSQL.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+namespace pgsql = odb::pgsql;
+using namespace pgsql;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ object o (1);
+ o.p = point (1.1111, 2222222222.2);
+ o.pv.push_back (point (1.1234, 2.2345));
+ o.pv.push_back (point (3.3456, 4.4567));
+ o.pv.push_back (point (0.0000001, 0.000000001)); // Scientific notation.
+
+ o.n1 = "23.5154";
+ o.n2 = "235154";
+ o.n3 = "2222222222222222222222222222.111111111111111111111111111111";
+
+ o.iv.push_back (123);
+ o.iv.push_back (234);
+ o.iv.push_back (-345);
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+
+ // Query.
+ //
+ typedef pgsql::query<object> query;
+ typedef odb::result<object> result;
+
+ {
+ transaction t (db->begin ());
+
+ // Point comparison.
+ //
+ {
+ result r (db->query<object> (query::p == o.p));
+ assert (!r.empty ());
+ }
+
+ // Point comparison using native query.
+ //
+ {
+ result r (db->query<object> (query::p + "~=" + query::_val (o.p)));
+ assert (!r.empty ());
+ }
+
+ // Access to individual members.
+ //
+ {
+ result r (db->query<object> (query::p.x == o.p.x));
+ assert (!r.empty ());
+ }
+
+ t.commit ();
+ }
+
+ // Update.
+ //
+ o.p.x++;
+ o.p.y--;
+ o.pv[1].x--;
+ o.pv[1].y++;
+ o.n3 += "999";
+ o.iv[0]++;
+ o.iv.pop_back ();
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/pgsql/custom/query.hxx b/odb-tests/pgsql/custom/query.hxx
new file mode 100644
index 0000000..8b89047
--- /dev/null
+++ b/odb-tests/pgsql/custom/query.hxx
@@ -0,0 +1,158 @@
+// file : pgsql/custom/query.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef QUERY_HXX
+#define QUERY_HXX
+
+#include <string>
+
+#include <odb/pgsql/query.hxx>
+
+#include "test.hxx" // point
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <>
+ struct query_column<point, id_string>
+ {
+ private:
+ const char* table_;
+ const char* column_;
+ const char* conversion_;
+
+ std::string x_column_;
+ std::string y_column_;
+
+ // Sub-columns for individual members.
+ //
+ public:
+ query_column<double, id_double> x, y;
+
+ // is_null, is_not_null
+ //
+ public:
+ query_base
+ is_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NULL";
+ return q;
+ }
+
+ query_base
+ is_not_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NOT NULL";
+ return q;
+ }
+
+ // =
+ //
+ public:
+ query_base
+ equal (const point& v) const
+ {
+ return equal (val_bind<point> (v));
+ }
+
+ query_base
+ equal (val_bind<point> v) const
+ {
+ query_base q (table_, column_);
+ q += "~=";
+ q.append<point, id_string> (v, conversion_);
+ return q;
+ }
+
+ query_base
+ equal (ref_bind<point> r) const
+ {
+ query_base q (table_, column_);
+ q += "~=";
+ q.append<point, id_string> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator== (const query_column& c, const point& v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const point& v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, val_bind<point> v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (val_bind<point> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, ref_bind<point> r)
+ {
+ return c.equal (r);
+ }
+
+ friend query_base
+ operator== (ref_bind<point> r, const query_column& c)
+ {
+ return c.equal (r);
+ }
+
+ // Column comparison.
+ //
+ public:
+ query_base
+ operator== (const query_column<point, id_string>& c) const
+ {
+ query_base q (table_, column_);
+ q += "~=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ public:
+ query_column (const char* table, const char* column, const char* conv)
+ : table_ (table), column_ (column), conversion_ (conv),
+ x_column_ (std::string (column) + "[0]"),
+ y_column_ (std::string (column) + "[1]"),
+ x (table, x_column_.c_str (), 0),
+ y (table, y_column_.c_str (), 0)
+ {
+ }
+
+ const char*
+ table () const
+ {
+ return table_;
+ }
+
+ const char*
+ column () const
+ {
+ return column_;
+ }
+
+ const char*
+ conversion () const
+ {
+ return conversion_;
+ }
+ };
+ }
+}
+
+#endif // QUERY_HXX
diff --git a/odb-tests/pgsql/custom/test.hxx b/odb-tests/pgsql/custom/test.hxx
new file mode 100644
index 0000000..ec3d496
--- /dev/null
+++ b/odb-tests/pgsql/custom/test.hxx
@@ -0,0 +1,86 @@
+// file : pgsql/custom/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <vector>
+
+#include <odb/core.hxx>
+
+// Map POINT PostgreSQL type to the point C++ struct. The other half
+// of this mapping is in traits.hxx (value_traits<point, id_string>).
+//
+#pragma db map type("POINT") as("TEXT") to("(?)::POINT") from("(?)::TEXT")
+
+#pragma db value type("POINT")
+struct point
+{
+ point () {}
+ point (double x_, double y_): x (x_), y (y_) {}
+
+ double x;
+ double y;
+};
+
+inline bool
+operator== (const point& a, const point& b)
+{
+ return a.x == b.x && a.y == b.y;
+}
+
+// Map NUMERIC PostgreSQL type to std::string (or any other type that
+// provides the value_traits<?, id_string> specialization).
+//
+#pragma db map type("NUMERIC *(\\(.+\\))?") \
+ as("TEXT") \
+ to("(?)::NUMERIC$1") \
+ from("(?)::TEXT")
+
+// Map INTEGER[] PostgreSQL type to std::vector<int>. The other half of
+// this mapping is in traits.hxx (value_traits<std::vector<int>, id_string>).
+//
+#pragma db map type("INTEGER *\\[(\\d*)\\]") \
+ as("TEXT") \
+ to("(?)::INTEGER[$1]") \
+ from("(?)::TEXT")
+
+#pragma db object
+struct object
+{
+ object () {}
+ object (unsigned long id_) : id (id_) {}
+
+ #pragma db id
+ unsigned long id;
+
+ point p;
+ std::vector<point> pv;
+
+ #pragma db type("NUMERIC(6, 4)")
+ std::string n1;
+
+ #pragma db type("NUMERIC(6)")
+ std::string n2;
+
+ #pragma db type("NUMERIC")
+ std::string n3;
+
+ #pragma db type("INTEGER [123]")
+ std::vector<int> iv;
+
+ bool
+ operator== (const object& y) const
+ {
+ return id == y.id &&
+ p == y.p &&
+ pv == y.pv &&
+ n1 == y.n1 &&
+ n2 == y.n2 &&
+ n3 == y.n3 &&
+ iv == y.iv;
+ }
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/pgsql/custom/testscript b/odb-tests/pgsql/custom/testscript
new file mode 100644
index 0000000..8e1448b
--- /dev/null
+++ b/odb-tests/pgsql/custom/testscript
@@ -0,0 +1,11 @@
+# file : pgsql/custom/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../pgsql.testscript
+
++$create_schema
+
+: basics
+:
+$*
diff --git a/odb-tests/pgsql/custom/traits.hxx b/odb-tests/pgsql/custom/traits.hxx
new file mode 100644
index 0000000..c45dec0
--- /dev/null
+++ b/odb-tests/pgsql/custom/traits.hxx
@@ -0,0 +1,166 @@
+// file : pgsql/custom/traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TRAITS_HXX
+#define TRAITS_HXX
+
+#include <limits> // std::numeric_limits
+#include <vector>
+#include <sstream>
+#include <cstring> // std::memcpy
+
+#include <odb/pgsql/traits.hxx>
+
+#include "test.hxx" // point
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <>
+ class value_traits<point, id_string>
+ {
+ public:
+ typedef point value_type;
+ typedef point query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (point& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (is_null)
+ v = point ();
+ else
+ {
+ // Point format is "(x,y)".
+ //
+ char c;
+ std::istringstream is (std::string (b.data (), n));
+
+ is >> c; // '('
+ is >> v.x;
+ is >> c; // ','
+ is >> v.y;
+ }
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const point& v)
+ {
+ is_null = false;
+ std::ostringstream os;
+
+ // The formula for the number of decimla digits required is given in:
+ //
+ // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1822.pdf
+ //
+ os.precision (std::numeric_limits<double>::digits10);
+ // os.precision (2 + std::numeric_limits<double>::digits * 301/1000);
+
+ os << '(' << v.x << ',' << v.y << ')';
+
+ const std::string& s (os.str ());
+ n = s.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), s.c_str (), n);
+ }
+ };
+
+ template <>
+ struct type_traits<point>
+ {
+ static const database_type_id db_type_id = id_string;
+
+ struct conversion
+ {
+ static const char* to () {return "(?)::POINT";}
+ };
+ };
+
+ 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)
+ {
+ // Array format is "{n1,n2,n3...}".
+ //
+ 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);
+ }
+ };
+
+ template <>
+ struct type_traits<std::vector<int> >
+ {
+ static const database_type_id db_type_id = id_string;
+
+ struct conversion
+ {
+ static const char* to () {return "(?)::INTEGER[]";}
+ };
+ };
+ }
+}
+
+#endif // TRAITS_HXX
diff --git a/odb-tests/pgsql/database/buildfile b/odb-tests/pgsql/database/buildfile
new file mode 100644
index 0000000..217cc1f
--- /dev/null
+++ b/odb-tests/pgsql/database/buildfile
@@ -0,0 +1,14 @@
+# file : pgsql/database/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($pgsql) "pgsql should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libs = libodb-pgsql%lib{odb-pgsql}
+
+exe{driver}: {hxx cxx}{*} $libs testscript
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
diff --git a/odb-tests/pgsql/database/driver.cxx b/odb-tests/pgsql/database/driver.cxx
new file mode 100644
index 0000000..56bde0a
--- /dev/null
+++ b/odb-tests/pgsql/database/driver.cxx
@@ -0,0 +1,44 @@
+// file : pgsql/database/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test that database constructors are unambiguous (compilation only).
+//
+
+#include <odb/pgsql/database.hxx>
+
+#undef NDEBUG
+#include <cassert>
+
+namespace pgsql = odb::pgsql;
+using namespace pgsql;
+
+int
+main (int argc, char* argv[])
+{
+ // This code should not execute.
+ //
+ if (argc != 0)
+ return 0;
+
+ {
+ database d1 ("bob", "secret", "db1");
+ database d2 ("bob", "secret", "db1", "server1");
+ database d3 ("bob", "secret", "db1", "server1", 999);
+ database d4 ("bob", "secret", "db1", "server1", 999, "extra");
+ }
+
+ {
+ database d1 ("bob", "secret", "db1", "server1", "ext1");
+ database d2 ("bob", "secret", "db1", "server1", "ext1", "extra");
+ }
+
+ {
+ database d1 ("conninfo");
+ }
+
+ {
+ database d1 (argc, argv);
+ database d2 (argc, argv, false);
+ database d3 (argc, argv, true, "extra");
+ }
+}
diff --git a/odb-tests/pgsql/database/testscript b/odb-tests/pgsql/database/testscript
new file mode 100644
index 0000000..f57a8e1
--- /dev/null
+++ b/odb-tests/pgsql/database/testscript
@@ -0,0 +1,6 @@
+# file : pgsql/database/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+: basics
+:
+$*
diff --git a/odb-tests/pgsql/index/buildfile b/odb-tests/pgsql/index/buildfile
new file mode 100644
index 0000000..1ae888c
--- /dev/null
+++ b/odb-tests/pgsql/index/buildfile
@@ -0,0 +1,37 @@
+# file : pgsql/index/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($pgsql) "pgsql should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-pgsql%lib{odb-pgsql}
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix pgsql_index_ \
+ --generate-schema \
+ --default-database common
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{pgsql-client}: include = adhoc
diff --git a/odb-tests/pgsql/index/driver.cxx b/odb-tests/pgsql/index/driver.cxx
new file mode 100644
index 0000000..dc4c11e
--- /dev/null
+++ b/odb-tests/pgsql/index/driver.cxx
@@ -0,0 +1,44 @@
+// file : pgsql/index/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test PostgreSQL index creation. See also the common test.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+namespace pgsql = odb::pgsql;
+using namespace pgsql;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ // This is just a schema creation test.
+ //
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/pgsql/index/test.hxx b/odb-tests/pgsql/index/test.hxx
new file mode 100644
index 0000000..d728188
--- /dev/null
+++ b/odb-tests/pgsql/index/test.hxx
@@ -0,0 +1,19 @@
+// file : pgsql/index/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ #pragma db id auto
+ unsigned long id_;
+
+ int i;
+ #pragma db index type("UNIQUE CONCURRENTLY") method("BTREE") member(i)
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/pgsql/index/testscript b/odb-tests/pgsql/index/testscript
new file mode 100644
index 0000000..671a8c3
--- /dev/null
+++ b/odb-tests/pgsql/index/testscript
@@ -0,0 +1,11 @@
+# file : pgsql/index/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../pgsql.testscript
+
++$create_schema
+
+: basics
+:
+$*
diff --git a/odb-tests/pgsql/native/buildfile b/odb-tests/pgsql/native/buildfile
new file mode 100644
index 0000000..da034db
--- /dev/null
+++ b/odb-tests/pgsql/native/buildfile
@@ -0,0 +1,19 @@
+# file : pgsql/native/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($pgsql) "pgsql should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libs = libodb-pgsql%lib{odb-pgsql}
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{*} $libs testscript
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{pgsql-client}: include = adhoc
diff --git a/odb-tests/pgsql/native/driver.cxx b/odb-tests/pgsql/native/driver.cxx
new file mode 100644
index 0000000..843db41
--- /dev/null
+++ b/odb-tests/pgsql/native/driver.cxx
@@ -0,0 +1,75 @@
+// file : pgsql/native/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test PostgreSQL native SQL execution.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+namespace pgsql = odb::pgsql;
+using namespace pgsql;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ // Create the database schema.
+ //
+ {
+ transaction t (db->begin ());
+
+ db->execute ("DROP TABLE IF EXISTS pgsql_native_test");
+ db->execute ("CREATE TABLE pgsql_native_test (n INT PRIMARY KEY)");
+
+ t.commit ();
+ }
+
+ // Insert a few rows.
+ //
+ {
+ transaction t (db->begin ());
+
+ assert (
+ db->execute ("INSERT INTO pgsql_native_test (n) VALUES (1)") == 1);
+
+ assert (
+ db->execute ("INSERT INTO pgsql_native_test (n) VALUES (2)") == 1);
+
+ t.commit ();
+ }
+
+ // select a few rows.
+ //
+ {
+ transaction t (db->begin ());
+
+ assert (
+ db->execute ("SELECT n FROM pgsql_native_test WHERE n < 3") == 2);
+
+ assert (
+ db->execute ("SELECT n FROM pgsql_native_test WHERE n > 3") == 0);
+
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/odb-tests/pgsql/native/testscript b/odb-tests/pgsql/native/testscript
new file mode 100644
index 0000000..4fd9d2c
--- /dev/null
+++ b/odb-tests/pgsql/native/testscript
@@ -0,0 +1,9 @@
+# file : pgsql/custom/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../pgsql.testscript
+
+: basics
+:
+$*
diff --git a/odb-tests/pgsql/truncation/buildfile b/odb-tests/pgsql/truncation/buildfile
new file mode 100644
index 0000000..4f3823f
--- /dev/null
+++ b/odb-tests/pgsql/truncation/buildfile
@@ -0,0 +1,38 @@
+# file : pgsql/truncation/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($pgsql) "pgsql should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-pgsql%lib{odb-pgsql}
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix pgsql_truncation_ \
+ --generate-schema \
+ --default-database common \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{pgsql-client}: include = adhoc
diff --git a/odb-tests/pgsql/truncation/driver.cxx b/odb-tests/pgsql/truncation/driver.cxx
new file mode 100644
index 0000000..337a26e
--- /dev/null
+++ b/odb-tests/pgsql/truncation/driver.cxx
@@ -0,0 +1,163 @@
+// file : pgsql/truncation/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test insufficient buffer/truncation handling.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+namespace pgsql = odb::pgsql;
+using namespace pgsql;
+
+int
+main (int argc, char* argv[])
+{
+ // The default pre-allocated buffer is 512 bytes long.
+ //
+ string long_str (640, 'c'); // This will get the buffer to 1024
+ string longer_str (1025, 'b');
+
+ try
+ {
+ // Test basic operations.
+ //
+ {
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ // Run persist/load so that the initial bindings are established
+ // (version == 0).
+ //
+ {
+ object1 o (1);
+ o.str_ = "test string";
+
+ transaction t (db->begin ());
+ db->persist (o);
+ db->load (1, o);
+ t.commit ();
+ }
+
+ {
+ object2 o (2);
+ o.str_ = "test string";
+
+ transaction t (db->begin ());
+ db->persist (o);
+ db->load (2, o);
+ t.commit ();
+ }
+
+ // Store/load the long string which should trigger buffer growth.
+ //
+ {
+ object1 o (3);
+ o.str_ = long_str;
+
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object2> o (db->load<object2> (3));
+ assert (o->str_ == long_str);
+ t.commit ();
+ }
+
+ // Store/load longer string.
+ //
+ {
+ object1 o (3);
+ o.str_ = longer_str;
+
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object2> o (db->load<object2> (3));
+ assert (o->str_ == longer_str);
+ t.commit ();
+ }
+ }
+
+ // Test query.
+ //
+ {
+ typedef pgsql::query<object1> query;
+ typedef odb::result<object1> result;
+
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ // Run persist/query so that the initial bindings are established
+ // (version == 0).
+ //
+ {
+ object1 o (20);
+ o.str_ = "test string";
+
+ transaction t (db->begin ());
+ db->persist (o);
+ o.id_++;
+ db->persist (o);
+ o.id_++;
+ db->persist (o);
+
+ result r (db->query<object1> (query::id == 20));
+ assert (r.begin ()->id_ == 20);
+ t.commit ();
+ }
+
+ // Test buffer growth with cached result.
+ //
+ {
+ object1 o;
+
+ transaction t (db->begin ());
+
+ result r (db->query<object1> (query::id >= 20));
+ result::iterator i (r.begin ());
+
+ o.id_ = i->id_;
+ o.str_ = long_str;
+
+ // This forces buffer growth in the middle of result iteration.
+ //
+ db->update (o);
+
+ ++i;
+ assert (i->str_ == "test string");
+
+ o.id_ = i->id_;
+ o.str_ = longer_str;
+ db->update (o);
+
+ ++i;
+ assert (i->str_ == "test string");
+
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/pgsql/truncation/test.hxx b/odb-tests/pgsql/truncation/test.hxx
new file mode 100644
index 0000000..73110c0
--- /dev/null
+++ b/odb-tests/pgsql/truncation/test.hxx
@@ -0,0 +1,46 @@
+// file : pgsql/truncation/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <odb/core.hxx>
+
+#pragma db object table ("test")
+struct object1
+{
+ object1 (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object1 ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string str_;
+};
+
+#pragma db object table ("test")
+struct object2
+{
+ object2 (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object2 ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string str_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/pgsql/truncation/testscript b/odb-tests/pgsql/truncation/testscript
new file mode 100644
index 0000000..c57b723
--- /dev/null
+++ b/odb-tests/pgsql/truncation/testscript
@@ -0,0 +1,11 @@
+# file : pgsql/truncation/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../pgsql.testscript
+
++$create_schema
+
+: basics
+:
+$*
diff --git a/odb-tests/pgsql/types/buildfile b/odb-tests/pgsql/types/buildfile
new file mode 100644
index 0000000..cb861cb
--- /dev/null
+++ b/odb-tests/pgsql/types/buildfile
@@ -0,0 +1,39 @@
+# file : pgsql/types/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($pgsql) "pgsql should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libodb = libodb%lib{odb}
+import libcommon = lib{common}
+import libs = libodb-pgsql%lib{odb-pgsql}
+
+exe{driver}: {hxx cxx}{* -*-odb} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb and libcommon
+# libraries are resolved for the odb_compile ad hoc rule (see build/root.build
+# for details).
+#
+libue{test-meta}: $libodb $libcommon
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix pgsql_types_ \
+ --generate-schema \
+ --default-database common \
+ --generate-query \
+ --cxx-prologue '#include "traits.hxx"'
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{pgsql-client}: include = adhoc
diff --git a/odb-tests/pgsql/types/driver.cxx b/odb-tests/pgsql/types/driver.cxx
new file mode 100644
index 0000000..710f601
--- /dev/null
+++ b/odb-tests/pgsql/types/driver.cxx
@@ -0,0 +1,165 @@
+// file : pgsql/types/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test PostgreSQL type conversion.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+namespace pgsql = odb::pgsql;
+using namespace pgsql;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ object o (1);
+
+ o.bool_ = true;
+ o.short_ = 12345;
+ o.int_ = -123456;
+ o.long_long_ = 123456;
+
+ o.float_ = 1.123F;
+ o.float8_ = 1.123;
+ o.double_ = 1.123;
+
+ o.date_ = 4015;
+ o.time_ = 48180000000LL;
+ o.timestamp_ = 346896000LL;
+
+ string short_str (128, 's');
+ string medium_str (250, 'm');
+ string long_str (2040, 'l');
+
+ o.char_ = short_str;
+ o.varchar_ = medium_str;
+ o.text_ = long_str;
+
+ o.bytea_.assign (long_str.c_str (), long_str.c_str () + long_str.size ());
+
+ unsigned char varbit_buf[8] = {1, 3, 1, 3, 1, 3, 1, 3};
+ o.varbit_.size = 52;
+ o.varbit_.ubuffer_ = ubuffer (varbit_buf, 8);
+
+ o.bit_.a = 0;
+ o.bit_.b = 1;
+ o.bit_.c = 0;
+ o.bit_.d = 1;
+
+ // 6F846D41-C89A-4E4D-B22F-56443CFA543F
+ memcpy (o.uuid_, "\x6F\x84\x6D\x41\xC8\x9A\x4E\x4D\xB2\x2F"
+ "\x56\x44\x3C\xFA\x54\x3F", 16);
+
+ o.enum_ = green;
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+
+ typedef pgsql::query<object> query;
+ typedef odb::result<object> result;
+
+ // Test UUID in queries.
+ //
+ {
+ char uuid[16];
+ memcpy (uuid, o.uuid_, 16);
+
+ transaction t (db->begin ());
+
+ {
+ result r (db->query<object> (query::uuid == uuid));
+ assert (size (r) == 1);
+ }
+
+ {
+ result r (db->query<object> (query::uuid == query::_val (uuid)));
+ assert (size (r) == 1);
+ }
+
+ {
+ result r (db->query<object> (query::uuid == query::_ref (uuid)));
+ assert (size (r) == 1);
+ }
+
+ {
+ const char* d (uuid);
+ result r (db->query<object> (query::uuid == d));
+ assert (size (r) == 1);
+ }
+
+ t.commit ();
+ }
+
+ // Test char array.
+ //
+ {
+ char_array o1 (1, "");
+ char_array o2 (2, "1234567890");
+ char_array o3 (3, "1234567890123456");
+
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o3);
+ t.commit ();
+ }
+
+ // PostgreSQL returns padded values for CHAR(N).
+ //
+ memcpy (o1.s2, " ", 16);
+ o1.s3[0] = o1.c1 = ' ';
+ memcpy (o2.s2, "1234567890 ", 16);
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<char_array> p1 (db->load<char_array> (1));
+ unique_ptr<char_array> p2 (db->load<char_array> (2));
+ unique_ptr<char_array> p3 (db->load<char_array> (3));
+ t.commit ();
+
+ assert (o1 == *p1);
+ assert (o2 == *p2);
+ assert (o3 == *p3);
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/pgsql/types/test.hxx b/odb-tests/pgsql/types/test.hxx
new file mode 100644
index 0000000..462ebad
--- /dev/null
+++ b/odb-tests/pgsql/types/test.hxx
@@ -0,0 +1,220 @@
+// file : pgsql/types/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <set>
+#include <string>
+#include <vector>
+#include <memory> // std::unique_ptr
+#include <cstring> // std::memcmp, std::memcpy, std::str[n]cmp, std::strlen
+#include <cstddef> // std::size_t
+
+#include <odb/core.hxx>
+
+#include <libcommon/buffer.hxx>
+
+struct bitfield
+{
+ unsigned int a: 1;
+ unsigned int b: 1;
+ unsigned int c: 1;
+ unsigned int d: 1;
+};
+
+inline bool
+operator== (bitfield x, bitfield y)
+{
+ return
+ x.a == y.a &&
+ x.b == y.b &&
+ x.c == y.c &&
+ x.d == y.d;
+}
+
+struct varbit
+{
+ std::size_t size;
+ ubuffer ubuffer_;
+
+ bool
+ compare (const varbit& x) const
+ {
+ if (size != x.size)
+ return false;
+
+ std::size_t byte_len = size / 8;
+
+ if (std::memcmp (ubuffer_.data (), x.ubuffer_.data (), byte_len) != 0)
+ return false;
+
+ std::size_t trailing_bits = size % 8;
+
+ if (trailing_bits != 0)
+ {
+ unsigned char mask (0xFFU << (8 - trailing_bits));
+
+ return (ubuffer_.data ()[byte_len] & mask) ==
+ (x.ubuffer_.data ()[byte_len] & mask);
+ }
+
+ return true;
+ }
+};
+
+inline bool
+operator== (const varbit& x, const varbit& y)
+{
+ return x.compare (y);
+}
+
+#pragma db value(bitfield) type ("BIT(4)")
+
+typedef std::unique_ptr<std::string> string_ptr;
+
+enum color {red, green, blue};
+
+#pragma db object
+struct object
+{
+ object () {}
+ object (unsigned long id): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ // Integral types.
+ //
+ #pragma db type ("BOOL")
+ bool bool_;
+
+ #pragma db type ("SMALLINT")
+ short short_;
+
+ #pragma db type ("INT")
+ int int_;
+
+ #pragma db type ("BIGINT")
+ long long long_long_;
+
+ // Float types.
+ //
+ #pragma db type ("REAL")
+ float float_;
+
+ #pragma db type ("FLOAT(32)")
+ double float8_;
+
+ #pragma db type ("DOUBLE PRECISION")
+ double double_;
+
+ // Data-time types.
+ //
+ #pragma db type ("DATE")
+ int date_;
+
+ #pragma db type ("TIME")
+ long long time_;
+
+ #pragma db type ("TIMESTAMP")
+ long long timestamp_;
+
+ // String and binary types.
+ //
+ #pragma db type ("CHAR(128)")
+ std::string char_;
+
+ #pragma db type ("VARCHAR(256)")
+ std::string varchar_;
+
+ #pragma db type ("TEXT")
+ std::string text_;
+
+ #pragma db type ("BYTEA")
+ std::vector<char> bytea_;
+
+ #pragma db type ("VARBIT(1024)")
+ varbit varbit_;
+
+ // #pragma db type ("BIT(4)") - assigned by #pragma db value
+ bitfield bit_;
+
+ // Other types.
+ //
+ #pragma db type ("UUID")
+ char uuid_[16];
+
+ // Test ENUM representation.
+ //
+ color enum_;
+
+ // Test NULL value.
+ //
+ #pragma db type ("TEXT") null
+ string_ptr null_;
+
+ bool
+ operator== (const object& y) const
+ {
+ return
+ id_ == y.id_ &&
+ bool_ == y.bool_ &&
+ short_ == y.short_ &&
+ int_ == y.int_ &&
+ long_long_ == y.long_long_ &&
+ float_ == y.float_ &&
+ float8_ == y.float8_ &&
+ double_ == y.double_ &&
+ date_ == y.date_ &&
+ time_ == y.time_ &&
+ timestamp_ == y.timestamp_ &&
+ char_ == y.char_ &&
+ varchar_ == y.varchar_ &&
+ text_ == y.text_ &&
+ bytea_ == y.bytea_ &&
+ bit_ == y.bit_ &&
+ varbit_ == y.varbit_ &&
+ memcmp (uuid_, y.uuid_, 16) == 0 &&
+ enum_ == y.enum_ &&
+ ((null_.get () == 0 && y.null_.get () == 0) || *null_ == *y.null_);
+ }
+};
+
+// Test char array.
+//
+#pragma db object
+struct char_array
+{
+ char_array () {}
+ char_array (unsigned long id, const char* s)
+ : id_ (id)
+ {
+ std::memcpy (s1, s, std::strlen (s) + 1); // VC++ strncpy deprecation.
+ std::memcpy (s2, s, std::strlen (s) + 1);
+ s3[0] = c1 = *s;
+ }
+
+ #pragma db id
+ unsigned long id_;
+
+ char s1[17];
+
+ #pragma db type("CHAR(16)")
+ char s2[16];
+
+ char s3[1];
+ char c1;
+
+ bool
+ operator== (const char_array& y) const
+ {
+ return id_ == y.id_ &&
+ std::strcmp (s1, y.s1) == 0 &&
+ std::strncmp (s2, y.s2, sizeof (s2)) == 0 &&
+ s3[0] == y.s3[0] &&
+ c1 == y.c1;
+ }
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/pgsql/types/testscript b/odb-tests/pgsql/types/testscript
new file mode 100644
index 0000000..57218e1
--- /dev/null
+++ b/odb-tests/pgsql/types/testscript
@@ -0,0 +1,11 @@
+# file : pgsql/types/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../pgsql.testscript
+
++$create_schema
+
+: basics
+:
+$*
diff --git a/odb-tests/pgsql/types/traits.hxx b/odb-tests/pgsql/types/traits.hxx
new file mode 100644
index 0000000..a1c8fbe
--- /dev/null
+++ b/odb-tests/pgsql/types/traits.hxx
@@ -0,0 +1,171 @@
+// file : pgsql/types/traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TRAITS_HXX
+#define TRAITS_HXX
+
+#include <cassert>
+#include <cstring> // std::memcpy, std::memset
+
+#include <odb/pgsql/traits.hxx>
+#include <odb/pgsql/details/endian-traits.hxx>
+
+#include "test.hxx" // varbit, ubuffer, string_ptr
+
+namespace odb
+{
+ namespace pgsql
+ {
+ // The first 4 bytes of the image is a signed int specifying the
+ // number of significant bits contained by the BIT. The following
+ // bytes contain the bit data.
+ //
+ template <>
+ class value_traits<bitfield, id_bit>
+ {
+ public:
+ typedef bitfield value_type;
+ typedef bitfield query_type;
+ typedef unsigned char* image_type;
+
+ static void
+ set_value (bitfield& v,
+ const unsigned char* i,
+ std::size_t,
+ bool is_null)
+ {
+ if (!is_null)
+ {
+ assert (
+ details::endian_traits::ntoh (
+ *reinterpret_cast<const int*> (i)) == 4);
+
+ const unsigned char* d (i + 4);
+
+ v.a = *d >> 4 & 1;
+ v.b = (*d >> 5) & 1;
+ v.c = (*d >> 6) & 1;
+ v.d = (*d >> 7) & 1;
+ }
+ else
+ v.a = v.b = v.c = v.d = 0;
+ }
+
+ static void
+ set_image (unsigned char* i,
+ std::size_t,
+ std::size_t& n,
+ bool& is_null,
+ bitfield v)
+ {
+ is_null = false;
+ n = 5;
+
+ *reinterpret_cast<int*> (i) = details::endian_traits::hton (4);
+ *(i + 4) = v.a << 4 | (v.b << 5) | (v.c << 6) | (v.d << 7);
+ }
+ };
+
+ // The first 4 bytes of the image is a signed int specifying the
+ // number of significant bits contained by the VARBIT. The following
+ // bytes contain the VARBIT data.
+ //
+ template <>
+ class value_traits<varbit, id_varbit>
+ {
+ public:
+ typedef varbit value_type;
+ typedef varbit query_type;
+ typedef details::ubuffer image_type;
+
+ static void
+ set_value (varbit& v,
+ const details::ubuffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ {
+ v.size = static_cast<std::size_t> (
+ details::endian_traits::ntoh (
+ *reinterpret_cast<const int*> (b.data ())));
+
+ std::size_t byte_len (v.size / 8 + (v.size % 8 > 0 ? 1 : 0));
+ assert (n >= byte_len + 4);
+
+ v.ubuffer_.assign (b.data () + 4, byte_len);
+ }
+
+ else
+ {
+ v.size = 0;
+ v.ubuffer_.assign (0, 0);
+ }
+ }
+
+ static void
+ set_image (details::ubuffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const varbit& v)
+ {
+ is_null = false;
+ n = 4 + v.size / 8 + (v.size % 8 > 0 ? 1 : 0);
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ // PostgreSQL requires all trailing bits of a VARBIT image
+ // to be zero.
+ //
+ std::memset (b.data (), 0, b.capacity ());
+
+ *reinterpret_cast<int*> (b.data ()) =
+ details::endian_traits::hton (static_cast<int> (v.size));
+
+ if (v.size != 0)
+ std::memcpy (b.data () + 4, v.ubuffer_.data (), n - 4);
+ }
+ };
+
+ template <>
+ class value_traits<string_ptr, id_string>
+ {
+ public:
+ typedef string_ptr value_type;
+ typedef std::string query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (string_ptr& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ v.reset (is_null ? 0 : new std::string (b.data (), n));
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const string_ptr& v)
+ {
+ is_null = v.get () == 0;
+
+ if (!is_null)
+ {
+ n = v->size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ std::memcpy (b.data (), v->c_str (), n);
+ }
+ }
+ };
+ }
+}
+
+#endif // TRAITS_HXX
diff --git a/odb-tests/qt/common/basic/driver.cxx b/odb-tests/qt/common/basic/driver.cxx
new file mode 100644
index 0000000..342a313
--- /dev/null
+++ b/odb-tests/qt/common/basic/driver.cxx
@@ -0,0 +1,66 @@
+// file : qt/common/basic/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test Qt basic type persistence (common part).
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ object o (1);
+ o.uuid_ = QUuid::createUuid ();
+ o.null_ = QUuid ();
+ o.zero_ = QUuid ();
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> p (db->load<object> (o.id_));
+ t.commit ();
+
+ assert (*p == o);
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::uuid == o.uuid_));
+ result::iterator i (r.begin ());
+ assert (i != r.end () && i->id_ == o.id_);
+ assert (++i == r.end ());
+ t.commit ();
+ }
+
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/qt/common/basic/test.hxx b/odb-tests/qt/common/basic/test.hxx
new file mode 100644
index 0000000..e67302e
--- /dev/null
+++ b/odb-tests/qt/common/basic/test.hxx
@@ -0,0 +1,35 @@
+// file : qt/common/basic/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <QtCore/QUuid>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object () {}
+ object (unsigned long id): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ QUuid uuid_;
+ QUuid null_;
+
+ #pragma db not_null
+ QUuid zero_;
+
+ bool operator== (const object& x) const
+ {
+ return id_ == x.id_ &&
+ uuid_ == x.uuid_ &&
+ null_ == x.null_ &&
+ zero_ == x.zero_;
+ }
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/qt/common/containers/basics/driver.cxx b/odb-tests/qt/common/containers/basics/driver.cxx
new file mode 100644
index 0000000..5e38ee1
--- /dev/null
+++ b/odb-tests/qt/common/containers/basics/driver.cxx
@@ -0,0 +1,569 @@
+// file : qt/common/containers/basics/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test basic Qt containers persistence.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <QtCore/QCoreApplication>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ QCoreApplication app (argc, argv);
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ for (unsigned short i (0); i < 2; ++i)
+ {
+ object empty ("empty"), med ("medium"), full ("full");
+
+ //
+ // empty
+ //
+
+ empty.num = 0;
+ empty.str = "";
+
+ //
+ // med
+ //
+
+ med.num = 999;
+ med.str = "xxx";
+
+ // vector
+ //
+ med.nv.push_back (123);
+ med.nv.push_back (234);
+
+ med.sv.push_back ("aaa");
+ med.sv.push_back ("bbbb");
+
+ med.cv.push_back (comp (123, "aaa"));
+ med.cv.push_back (comp (234, "bbbb"));
+
+ med.uv.push_back (123);
+ med.uv.push_back (234);
+
+ // list
+ //
+ med.sl.push_back ("aaa");
+ med.sl.push_back ("bbbb");
+
+ med.nl.push_back (123);
+ med.nl.push_back (234);
+
+ med.cl.push_back (comp (123, "aaa"));
+ med.cl.push_back (comp (234, "bbbb"));
+
+ // linked list
+ //
+ med.sll.push_back ("aaa");
+ med.sll.push_back ("bbbb");
+
+ med.nll.push_back (123);
+ med.nll.push_back (234);
+
+ med.cll.push_back (comp (123, "aaa"));
+ med.cll.push_back (comp (234, "bbbb"));
+
+ // set
+ //
+ med.ns.insert (123);
+ med.ns.insert (234);
+
+ med.ss.insert ("aaa");
+ med.ss.insert ("bbbb");
+
+ // map
+ //
+ med.nsm[123] = "aaa";
+ med.nsm[234] = "bbbb";
+
+ med.snm["aaa"] = 123;
+ med.snm["bbbb"] = 234;
+
+ med.ncm[123] = comp (123, "aaa");
+ med.ncm[234] = comp (234, "bbbb");
+
+ med.csm[comp (123, "aaa")] = "aaa";
+ med.csm[comp (234, "bbbb")] = "bbbb";
+
+ // multimap
+ //
+ med.nsmm.insert (123, "aaa");
+ med.nsmm.insert (123, "bbbb");
+ med.nsmm.insert (234, "ccccc");
+
+ med.snmm.insert ("aaa", 123);
+ med.snmm.insert ("aaa", 234);
+ med.snmm.insert ("bbb", 345);
+
+ med.ncmm.insert (123, comp (123, "aaa"));
+ med.ncmm.insert (123, comp (234, "bbbb"));
+ med.ncmm.insert (234, comp (345, "ccccc"));
+
+ // hash
+ //
+ med.nsh[123] = "aaa";
+ med.nsh[234] = "bbbb";
+
+ med.snh["aaa"] = 123;
+ med.snh["bbb"] = 234;
+
+ med.sch["iii"] = comp (123, "aaa");
+ med.sch["jjj"] = comp (234, "bbbb");
+
+ // multihash
+ //
+ med.nsmh.insert (123, "aaa");
+ med.nsmh.insert (123, "bbbb");
+ med.nsmh.insert (234, "ccccc");
+
+ med.snmh.insert ("aaa", 123);
+ med.snmh.insert ("aaa", 234);
+ med.snmh.insert ("bbb", 345);
+
+ med.ncmh.insert (123, comp (123, "aaa"));
+ med.ncmh.insert (123, comp (234, "bbbb"));
+ med.ncmh.insert (234, comp (345, "ccccc"));
+
+ //
+ // full
+ //
+
+ full.num = 9999;
+ full.str = "xxxx";
+
+ // vector
+ //
+ full.nv.push_back (1234);
+ full.nv.push_back (2345);
+ full.nv.push_back (3456);
+
+ full.sv.push_back ("aaaa");
+ full.sv.push_back ("bbbbb");
+ full.sv.push_back ("cccccc");
+
+ full.cv.push_back (comp (1234, "aaaa"));
+ full.cv.push_back (comp (2345, "bbbbb"));
+ full.cv.push_back (comp (3456, "cccccc"));
+
+ full.uv.push_back (1234);
+ full.uv.push_back (2345);
+ full.uv.push_back (3456);
+
+ // list
+ //
+ full.sl.push_back ("aaaa");
+ full.sl.push_back ("bbbbb");
+ full.sl.push_back ("cccccc");
+
+ full.nl.push_back (1234);
+ full.nl.push_back (2345);
+ full.nl.push_back (3456);
+
+ full.cl.push_back (comp (1234, "aaaa"));
+ full.cl.push_back (comp (2345, "bbbbb"));
+ full.cl.push_back (comp (3456, "cccccc"));
+
+ // linked list
+ //
+ full.sll.push_back ("aaaa");
+ full.sll.push_back ("bbbbb");
+ full.sll.push_back ("cccccc");
+
+ full.nll.push_back (1234);
+ full.nll.push_back (2345);
+ full.nll.push_back (3456);
+
+ full.cll.push_back (comp (1234, "aaaa"));
+ full.cll.push_back (comp (2345, "bbbbb"));
+ full.cll.push_back (comp (3456, "cccccc"));
+
+ // set
+ //
+ full.ns.insert (1234);
+ full.ns.insert (2345);
+ full.ns.insert (3456);
+
+ full.ss.insert ("aaaa");
+ full.ss.insert ("bbbbb");
+ full.ss.insert ("cccccc");
+
+ // map
+ //
+ full.nsm[1234] = "aaaa";
+ full.nsm[2345] = "bbbbb";
+ full.nsm[3456] = "cccccc";
+
+ full.snm["aaaa"] = 1234;
+ full.snm["bbbb"] = 2345;
+ full.snm["cccc"] = 3456;
+
+ full.ncm[1234] = comp (1234, "aaaa");
+ full.ncm[2345] = comp (2345, "bbbbb");
+ full.ncm[3456] = comp (3456, "cccccc");
+
+ full.csm[comp (1234, "aaaa")] = "aaaa";
+ full.csm[comp (2345, "bbbb")] = "bbbbb";
+ full.csm[comp (3456, "cccc")] = "cccccc";
+
+ // multimap
+ //
+ full.nsmm.insert (1234, "aaaa");
+ full.nsmm.insert (1234, "bbbbb");
+ full.nsmm.insert (2345, "cccccc");
+ full.nsmm.insert (2345, "ddddddd");
+
+ full.snmm.insert ("aaaa", 1234);
+ full.snmm.insert ("aaaa", 2345);
+ full.snmm.insert ("bbbb", 3456);
+ full.snmm.insert ("bbbb", 4567);
+
+ full.ncmm.insert (1234, comp (1234, "aaaa"));
+ full.ncmm.insert (1234, comp (2345, "bbbbb"));
+ full.ncmm.insert (2345, comp (3456, "cccccc"));
+ full.ncmm.insert (2345, comp (4567, "ddddddd"));
+
+ // hash
+ //
+ full.nsh[1234] = "aaaa";
+ full.nsh[2345] = "bbbbb";
+ full.nsh[3456] = "cccccc";
+
+ full.snh["aaaa"] = 1234;
+ full.snh["bbbb"] = 2345;
+ full.snh["cccc"] = 3456;
+
+ full.sch["iiii"] = comp (1234, "aaaa");
+ full.sch["jjjj"] = comp (2345, "bbbbb");
+ full.sch["kkkk"] = comp (3456, "cccccc");
+
+ // multihash
+ //
+ full.nsmh.insert (1234, "aaaa");
+ full.nsmh.insert (1234, "bbbbb");
+ full.nsmh.insert (2345, "cccccc");
+ full.nsmh.insert (2345, "ddddddd");
+
+ full.snmh.insert ("aaaa", 1234);
+ full.snmh.insert ("aaaa", 2345);
+ full.snmh.insert ("bbbb", 3456);
+ full.snmh.insert ("bbbb", 4567);
+
+ full.ncmh.insert (1234, comp (1234, "aaaa"));
+ full.ncmh.insert (1234, comp (2345, "bbbbb"));
+ full.ncmh.insert (2345, comp (3456, "cccccc"));
+ full.ncmh.insert (2345, comp (4567, "ddddddd"));
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (empty);
+ db->persist (med);
+ db->persist (full);
+ t.commit ();
+ }
+
+ // load & check
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> e (db->load<object> ("empty"));
+ auto_ptr<object> m (db->load<object> ("medium"));
+ auto_ptr<object> f (db->load<object> ("full"));
+ t.commit ();
+
+ assert (empty == *e);
+ assert (med == *m);
+ assert (full == *f);
+ }
+
+ //
+ // empty
+ //
+
+ empty.num = 99;
+ empty.str = "xx";
+
+ // vector
+ //
+ empty.nv.push_back (12);
+ empty.sv.push_back ("aa");
+ empty.cv.push_back (comp (12, "aa"));
+ empty.uv.push_back (12);
+
+ // list
+ //
+ empty.sl.push_back ("aa");
+ empty.nl.push_back (12);
+ empty.cl.push_back (comp (12, "aa"));
+
+ // linked list
+ //
+ empty.nll.push_back (12);
+ empty.sll.push_back ("aa");
+ empty.cll.push_back (comp (12, "aa"));
+
+ // set
+ //
+ empty.ns.insert (12);
+ empty.ss.insert ("aa");
+
+ // map
+ //
+ empty.nsm[12] = "aa";
+ empty.snm["aa"] = 12;
+ empty.ncm[12] = comp (12, "aa");
+ empty.csm[comp (12, "aa")] = "aa";
+
+ // multimap
+ //
+ empty.nsmm.insert (12, "aa");
+ empty.nsmm.insert (12, "bbb");
+ empty.nsmm.insert (23, "cccc");
+ empty.snmm.insert ("aa", 12);
+ empty.snmm.insert ("aa", 23);
+ empty.snmm.insert ("bb", 34);
+ empty.ncmm.insert (12, comp (12, "aa"));
+ empty.ncmm.insert (12, comp (23, "bb"));
+ empty.ncmm.insert (23, comp (34, "cc"));
+
+ // hash
+ //
+ empty.nsh[12] = "aa";
+ empty.snh["aa"] = 12;
+ empty.sch["ii"] = comp (12, "aa");
+
+ // multihash
+ //
+ empty.nsmh.insert (12, "aa");
+ empty.nsmh.insert (12, "bbb");
+ empty.nsmh.insert (23, "cccc");
+ empty.snmh.insert ("aa", 12);
+ empty.snmh.insert ("aa", 23);
+ empty.snmh.insert ("bb", 34);
+ empty.ncmh.insert (12, comp (12, "aa"));
+ empty.ncmh.insert (12, comp (23, "bb"));
+ empty.ncmh.insert (23, comp (34, "cc"));
+
+ //
+ // med
+ //
+
+ med.num = 0;
+ med.str = "";
+
+ // vector
+ //
+ med.nv.clear ();
+ med.sv.clear ();
+ med.cv.clear ();
+ med.uv.clear ();
+
+ // list
+ //
+ med.sl.clear ();
+ med.nl.clear ();
+ med.cl.clear ();
+
+ // linked list
+ //
+ med.nll.clear ();
+ med.sll.clear ();
+ med.cll.clear ();
+
+ // set
+ //
+ med.ns.clear ();
+ med.ss.clear ();
+
+ // map
+ //
+ med.nsm.clear ();
+ med.snm.clear ();
+ med.ncm.clear ();
+ med.csm.clear ();
+
+ // multimap
+ //
+ med.nsmm.clear ();
+ med.snmm.clear ();
+ med.ncmm.clear ();
+
+ // hash
+ //
+ med.nsh.clear ();
+ med.snh.clear ();
+ med.sch.clear ();
+
+ // multihash
+ //
+ med.nsmh.clear ();
+ med.snmh.clear ();
+ med.ncmh.clear ();
+
+
+ //
+ // full
+ //
+
+ full.num++;
+ full.str += "x";
+
+ // vector
+ //
+ full.nv.back ()++;
+ full.nv.push_back (4567);
+
+ full.sv.back () += "c";
+ full.sv.push_back ("ddddddd");
+
+ full.cv.back ().num++;
+ full.cv.back ().str += "c";
+ full.cv.push_back (comp (4567, "ddddddd"));
+
+ full.uv.back ()++;
+ full.uv.push_back (4567);
+
+ // list
+ //
+ full.sl.back () += "c";
+ full.sl.push_back ("ddddddd");
+
+ full.nl.back ()++;
+ full.nl.push_back (4567);
+
+ full.cl.back ().num++;
+ full.cl.back ().str += "c";
+ full.cl.push_back (comp (4567, "ddddddd"));
+
+ // linked list
+ //
+ full.sll.back () += "c";
+ full.sll.push_back ("ddddddd");
+
+ full.nll.back ()++;
+ full.nll.push_back (4567);
+
+ full.cll.back ().num++;
+ full.cll.back ().str += "c";
+ full.cll.push_back (comp (4567, "ddddddd"));
+
+ // set
+ //
+ full.ns.insert (4567);
+ full.ss.insert ("ddddddd");
+
+ // map
+ //
+ full.nsm[3456] += "c";
+ full.nsm[4567] = "ddddddd";
+
+ full.snm["cccc"]++;
+ full.snm["dddd"] = 4567;
+
+ full.ncm[3456].num++;
+ full.ncm[3456].str += "c";
+ full.ncm[4567] = comp (4567, "ddddddd");
+
+ full.csm[comp (3456, "cccc")] += "c";
+ full.csm[comp (4567, "dddd")] = "ddddddd";
+
+ // multimap
+ //
+ full.nsmm.find (2345).value () += "d";
+ full.nsmm.insert (3456, "eeeeeeee");
+
+ full.snmm.find ("bbbb").value ()++;
+ full.snmm.insert ("cccc", 5678);
+
+ full.ncmm.find (1234).value ().num++;
+ full.ncmm.find (2345).value ().str += "d";
+ full.ncmm.insert (3456, comp (5678, "eeeeeeee"));
+
+ // hash
+ //
+ full.nsh[3456] += "c";
+ full.nsh[4567] = "ddddddd";
+
+ full.snh["cccc"]++;
+ full.snh["dddd"] = 4567;
+
+ full.sch["iiii"].num++;
+ full.sch["jjjj"].str += "b";
+ full.sch["kkkk"] = comp (4567, "dddddddd");
+
+ // multihash
+ //
+ full.nsmh.find (2345).value () += "d";
+ full.nsmh.insert (3456, "eeeeeeee");
+
+ full.snmh.find ("bbbb").value ()++;
+ full.snmh.insert ("cccc", 5678);
+
+ full.ncmh.find (1234).value ().num++;
+ full.ncmh.find (2345).value ().str += "d";
+ full.ncmh.insert (3456, comp (5678, "eeeeeeee"));
+
+ // update
+ //
+ {
+ transaction t (db->begin ());
+ db->update (empty);
+ db->update (med);
+ db->update (full);
+ t.commit ();
+ }
+
+ // load & check
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> e (db->load<object> ("empty"));
+ auto_ptr<object> m (db->load<object> ("medium"));
+ auto_ptr<object> f (db->load<object> ("full"));
+ t.commit ();
+
+ assert (empty == *e);
+ assert (med == *m);
+ assert (full == *f);
+ }
+
+ // erase
+ //
+ if (i == 0)
+ {
+ transaction t (db->begin ());
+ db->erase<object> ("empty");
+ db->erase<object> ("medium");
+ db->erase<object> ("full");
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/qt/common/containers/basics/test.hxx b/odb-tests/qt/common/containers/basics/test.hxx
new file mode 100644
index 0000000..2c61c88
--- /dev/null
+++ b/odb-tests/qt/common/containers/basics/test.hxx
@@ -0,0 +1,224 @@
+// file : qt/common/containers/basics/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <QtCore/QString>
+#include <QtCore/QVector>
+#include <QtCore/QList>
+#include <QtCore/QLinkedList>
+#include <QtCore/QSet>
+#include <QtCore/QMap>
+#include <QtCore/QMultiMap>
+#include <QtCore/QHash>
+#include <QtCore/QMultiHash>
+
+#include <odb/core.hxx>
+
+#pragma db value
+struct comp
+{
+ comp () {}
+ comp (int n, const QString& s) : num (n), str (s) {}
+
+ #pragma db column("number")
+ int num;
+ QString str;
+};
+
+inline bool
+operator== (const comp& x, const comp& y)
+{
+ return x.num == y.num && x.str == y.str;
+}
+
+inline bool
+operator< (const comp& x, const comp& y)
+{
+ return x.num != y.num ? x.num < y.num : x.str < y.str;
+}
+
+typedef QVector<int> num_vector;
+typedef QVector<QString> str_vector;
+typedef QVector<comp> comp_vector;
+
+typedef QList<int> num_list;
+typedef QList<QString> str_list;
+typedef QList<comp> comp_list;
+
+typedef QLinkedList<int> num_linked_list;
+typedef QLinkedList<QString> str_linked_list;
+typedef QLinkedList<comp> comp_linked_list;
+
+typedef QSet<int> num_set;
+typedef QSet<QString> str_set;
+
+typedef QMap<int, QString> num_str_map;
+typedef QMap<QString, int> str_num_map;
+typedef QMap<int, comp> num_comp_map;
+typedef QMap<comp, QString> comp_str_map;
+
+typedef QMultiMap<int, QString> num_str_multimap;
+typedef QMultiMap<QString, int> str_num_multimap;
+typedef QMultiMap<int, comp> num_comp_multimap;
+
+typedef QHash<int, QString> num_str_hash;
+typedef QHash<QString, int> str_num_hash;
+typedef QHash<QString, comp> str_comp_hash;
+
+typedef QMultiHash<int, QString> num_str_multihash;
+typedef QMultiHash<QString, int> str_num_multihash;
+typedef QMultiHash<int, comp> num_comp_multihash;
+
+#pragma db value
+struct cont_comp1
+{
+ // This composite value does not have any columns.
+ //
+ num_vector sv; // Have the name "conflict" with the one in the object.
+};
+
+#pragma db value
+struct cont_comp2
+{
+ cont_comp2 (): num (777), str ("ggg") {}
+
+ int num;
+ str_list sl;
+ QString str;
+};
+
+#pragma db object
+struct object
+{
+ object (): nv (comp1_.sv), sl (comp2_.sl) {}
+ object (const QString& id) : id_ (id), nv (comp1_.sv), sl (comp2_.sl) {}
+
+ #pragma db id
+ QString id_;
+
+ int num;
+
+ cont_comp1 comp1_;
+ cont_comp2 comp2_;
+
+ // vector
+ //
+ #pragma db transient
+ num_vector& nv;
+
+ #pragma db table("object_strings") id_column ("obj_id")
+ str_vector sv;
+
+ #pragma db value_column("")
+ comp_vector cv;
+
+ #pragma db unordered
+ num_vector uv;
+
+ // list
+ //
+ #pragma db transient
+ str_list& sl;
+
+ num_list nl;
+ comp_list cl;
+
+ // linked list
+ //
+ str_linked_list sll;
+ num_linked_list nll;
+ comp_linked_list cll;
+
+ // set
+ //
+ num_set ns;
+ str_set ss;
+
+ // map
+ //
+ num_str_map nsm;
+ str_num_map snm;
+ num_comp_map ncm;
+ comp_str_map csm;
+
+ // multimap
+ //
+ num_str_multimap nsmm;
+ str_num_multimap snmm;
+ num_comp_multimap ncmm;
+
+ // hash
+ //
+ num_str_hash nsh;
+ str_num_hash snh;
+ str_comp_hash sch;
+
+ // multihash
+ //
+ num_str_multihash nsmh;
+ str_num_multihash snmh;
+ num_comp_multihash ncmh;
+
+ QString str;
+};
+
+inline bool
+operator== (const object& x, const object& y)
+{
+ if (x.uv.size () != y.uv.size ())
+ return false;
+
+ int xs (0), ys (0);
+
+ for (num_vector::size_type i (0); i < x.uv.size (); ++i)
+ {
+ xs += x.uv[i];
+ ys += y.uv[i];
+ }
+
+ return
+ x.id_ == y.id_ &&
+ x.num == y.num &&
+
+ x.comp2_.num == y.comp2_.num &&
+ x.comp2_.str == y.comp2_.str &&
+
+ x.nv == y.nv &&
+ x.sv == y.sv &&
+ x.cv == y.cv &&
+ xs == ys &&
+
+ x.sl == y.sl &&
+ x.nl == y.nl &&
+ x.cl == y.cl &&
+
+ x.nll == y.nll &&
+ x.sll == y.sll &&
+ x.cll == y.cll &&
+
+ x.ns == y.ns &&
+ x.ss == y.ss &&
+
+ x.nsm == y.nsm &&
+ x.snm == y.snm &&
+ x.ncm == y.ncm &&
+ x.csm == y.csm &&
+
+ x.nsmm.uniqueKeys () == y.nsmm.uniqueKeys () &&
+ x.snmm.uniqueKeys () == y.snmm.uniqueKeys () &&
+ x.ncmm.uniqueKeys () == y.ncmm.uniqueKeys () &&
+
+ x.nsh == y.nsh &&
+ x.snh == y.snh &&
+ x.sch == y.sch &&
+
+ x.nsmh.uniqueKeys () == y.nsmh.uniqueKeys () &&
+ x.snmh.uniqueKeys () == y.snmh.uniqueKeys () &&
+ x.ncmh.uniqueKeys () == y.ncmh.uniqueKeys () &&
+
+ x.str == y.str;
+}
+
+#endif // TEST_HXX
diff --git a/odb-tests/qt/common/containers/change-tracking/driver.cxx b/odb-tests/qt/common/containers/change-tracking/driver.cxx
new file mode 100644
index 0000000..8157d96
--- /dev/null
+++ b/odb-tests/qt/common/containers/change-tracking/driver.cxx
@@ -0,0 +1,643 @@
+// file : qt/common/containers/change-tracking/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test change-tracking Qt containers.
+//
+
+#include <common/config.hxx> // HAVE_CXX11
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#ifdef HAVE_CXX11
+# include <utility> // std::move
+#endif
+
+#include <QtCore/QtGlobal> // QT_VERSION, Q_FOREACH
+#include <QtCore/QCoreApplication>
+
+#include <odb/tracer.hxx>
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <odb/qt/list-iterator.hxx>
+#include <odb/qt/mutable-list-iterator.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+struct counting_tracer: odb::tracer
+{
+ void
+ reset (transaction& tr) {u = i = d = s = t = 0; tr.tracer (*this);}
+
+ virtual void
+ execute (odb::connection&, const char* stmt)
+ {
+ string p (stmt, 6);
+ if (p == "UPDATE")
+ u++;
+ else if (p == "INSERT")
+ i++;
+ else if (p == "DELETE")
+ d++;
+ else if (p == "SELECT")
+ s++;
+ t++;
+ }
+
+ size_t u, i, d, s, t;
+};
+
+static counting_tracer tr;
+
+// Compilation test: instantiate all the functions. Only do this if we
+// have a fairly recent version of Qt, otherwise some underlying
+// functions will be missing.
+//
+#if QT_VERSION >= 0x050000
+template class QOdbList<short>;
+template class QOdbListIteratorImpl<QOdbList<short> >;
+template class QOdbListIterator<short>;
+template class QMutableOdbListIterator<short>;
+#endif
+
+void
+f (const QList<int>&) {}
+
+int
+main (int argc, char* argv[])
+{
+ QCoreApplication app (argc, argv);
+
+ try
+ {
+ // Test extended interface.
+ //
+ {
+ typedef QOdbList<int> list;
+
+ list ol;
+ QList<int> ql;
+ f (ol); // Implicit conversion to QList.
+ list ol1 (ql); // Initialization from QList.
+ ol = ql; // Assignement from QList.
+
+ // Container comparison.
+ //
+ if (ol != ol1 ||
+ ol != ql ||
+ ql != ol1)
+ ol.clear ();
+
+ // Container operators.
+ //
+ ol += ol1;
+ ol += ql;
+ ol = ol1 + ql;
+ ol = ql + ol1;
+
+ // Iterator comparison/conversion.
+ //
+#ifndef QT_STRICT_ITERATORS
+ list::const_iterator i (ol.begin ());
+ if (i != ol.end ())
+ i = ol.end ();
+#endif
+
+ Q_FOREACH (const int& i, ol)
+ cerr << i;
+ }
+
+ auto_ptr<database> db (create_database (argc, argv));
+
+ // Test traits logic.
+ //
+ {
+ object o ("1");
+ o.i = 123;
+ o.s.push_back ("a");
+
+ assert (!o.s._tracking ());
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ assert (o.s._tracking ());
+
+ // load
+ //
+ {
+ transaction t (db->begin ());
+#ifdef HAVE_CXX11
+ unique_ptr<object> p (db->load<object> ("1"));
+#else
+ auto_ptr<object> p (db->load<object> ("1"));
+#endif
+ assert (p->s._tracking ());
+ t.commit ();
+ }
+
+ // update
+ //
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ assert (o.s._tracking ());
+
+ // erase
+ //
+ {
+ transaction t (db->begin ());
+ db->erase (o);
+ t.commit ();
+ }
+
+ assert (!o.s._tracking ());
+ }
+
+ // Test change tracking.
+ //
+ object o ("1");
+ o.i = 123;
+ o.s.push_back ("a");
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // push_back/pop_back
+ //
+ {
+ o.s.push_back ("b"); // insert
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 1 && tr.i == 1 && tr.t == 2);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.pop_back ();
+ o.s.push_back ("c"); // update
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 2 && tr.t == 2);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.pop_back (); // delete
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 1 && tr.d == 1 && tr.t == 2);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.push_back ("b");
+ o.s.pop_back (); // no-op
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 1 && tr.t == 1);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ // insert
+ //
+ {
+ o.s.clear ();
+ o.s.push_back ("a");
+ o.s.push_back ("b");
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.insert (o.s.begin (), "a1"); // insert front
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 3 && tr.i == 1 && tr.t == 4);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.insert (o.s.begin () + 1, "a2"); // insert middle
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 3 && tr.i == 1 && tr.t == 4);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.insert (o.s.end (), "b1"); // insert back
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 1 && tr.i == 1 && tr.t == 2);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ // erase
+ //
+ {
+ o.s.clear ();
+ o.s.push_back ("a");
+ o.s.push_back ("b");
+ o.s.push_back ("c");
+ o.s.push_back ("d");
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.erase (o.s.begin ()); // erase front
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 4 && tr.d == 1 && tr.t == 5);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.erase (o.s.begin () + 1); // erase middle
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 2 && tr.d == 1 && tr.t == 3);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.erase (o.s.end () - 1); // erase back
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 1 && tr.d == 1 && tr.t == 2);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ // modify
+ //
+ {
+ o.s.clear ();
+ o.s.push_back ("a");
+ o.s.push_back ("b");
+ o.s.push_back ("c");
+ o.s.push_back ("d");
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.modify (1) += 'b';
+ o.s.modify_back () += 'd';
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 3 && tr.t == 3);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.modify_front () += 'a';
+ o.s.modify_back () += 'd';
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 3 && tr.t == 3);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.begin ().modify () += 'a';
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 2 && tr.t == 2);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.mbegin ();
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 5 && tr.t == 5);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ // clear
+ //
+ {
+ o.s.clear ();
+ o.s.push_back ("a");
+ o.s.push_back ("b");
+ o.s.push_back ("c");
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.clear ();
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 1 && tr.d == 1 && tr.t == 2);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ // assign
+ //
+ {
+ o.s.clear ();
+ o.s.push_back ("a");
+ o.s.push_back ("b");
+ o.s.push_back ("c");
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ QList<QString> v;
+ v.push_back ("1");
+ v.push_back ("2");
+ v.push_back ("3");
+ v.push_back ("4");
+ o.s = v;
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 4 && tr.i == 1 && tr.t == 5);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ // removeOne/removeAll
+ //
+ {
+ o.s.clear ();
+ o.s.push_back ("a");
+ o.s.push_back ("a");
+ o.s.push_back ("b");
+ o.s.push_back ("c");
+ o.s.push_back ("a");
+ o.s.push_back ("d");
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.removeOne ("c");
+ assert (o.s.size () == 5 && o.s[3] == "a");
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 3 && tr.d == 1 && tr.t == 4);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ o.s.removeAll ("a");
+ assert (o.s.size () == 2 && o.s[0] == "b" && o.s[1] == "d");
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 3 && tr.d == 1 && tr.t == 4);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ // Transaction rollback.
+ //
+ {
+ o.s.clear ();
+ o.s.push_back ("a");
+ o.s.push_back ("b");
+ o.s.push_back ("c");
+
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (*db->load<object> ("1") == o);
+ t.commit ();
+ }
+
+ {
+ {
+ o.s.push_back ("d");
+
+ transaction t (db->begin ());
+ db->update (o);
+ t.rollback ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 1 && tr.i == 4 && tr.d == 1 && tr.t == 6);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (o);
+ assert (tr.u == 1 && tr.t == 1);
+ t.commit ();
+ }
+ }
+
+ // Armed copy.
+ //
+ {
+
+#ifdef HAVE_CXX11
+ unique_ptr<object> c;
+#else
+ auto_ptr<object> c;
+#endif
+
+
+ {
+ o.s.pop_back ();
+
+ transaction t (db->begin ());
+ db->update (o);
+ c.reset (new object (o));
+ t.rollback ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (c);
+ assert (tr.u == 1 && tr.i == 3 && tr.d == 1 && tr.t == 5);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (c);
+ assert (tr.u == 1 && tr.t == 1);
+ t.commit ();
+ }
+ }
+
+ // Armed swap.
+ //
+#if QT_VERSION >= 0x040800
+ {
+ object c (o);
+
+ {
+ o.s.push_back ("d");
+
+ transaction t (db->begin ());
+ db->update (o);
+ assert (o.s._tracking () && !c.s._tracking ());
+ c.s.swap (o.s);
+ assert (!o.s._tracking () && c.s._tracking ());
+ t.rollback ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (c);
+ assert (tr.u == 1 && tr.i == 4 && tr.d == 1 && tr.t == 6);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (c);
+ assert (tr.u == 1 && tr.t == 1);
+ t.commit ();
+ }
+ }
+#endif
+
+ // Armed move.
+ //
+#ifdef HAVE_CXX11
+ {
+ unique_ptr<object> c;
+
+ {
+ o.s.pop_back ();
+
+ transaction t (db->begin ());
+ db->update (o);
+ assert (o.s._tracking ());
+ c.reset (new object (std::move (o)));
+ assert (!o.s._tracking () && c->s._tracking ());
+ t.rollback ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (c);
+ assert (tr.u == 1 && tr.i == 2 && tr.d == 1 && tr.t == 4);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tr.reset (t);
+ db->update (c);
+ assert (tr.u == 1 && tr.t == 1);
+ t.commit ();
+ }
+ }
+#endif
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/qt/common/containers/change-tracking/test.hxx b/odb-tests/qt/common/containers/change-tracking/test.hxx
new file mode 100644
index 0000000..6067581
--- /dev/null
+++ b/odb-tests/qt/common/containers/change-tracking/test.hxx
@@ -0,0 +1,46 @@
+// file : qt/common/containers/change-tracking/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <common/config.hxx> // HAVE_CXX11
+
+#include <memory>
+
+#ifdef HAVE_CXX11
+# include <utility> // std::move
+#endif
+
+#include <QtCore/QString>
+
+#include <odb/core.hxx>
+#include <odb/qt/list.hxx>
+
+#ifdef HAVE_CXX11
+#pragma db object pointer(std::unique_ptr)
+#else
+#pragma db object pointer(std::auto_ptr)
+#endif
+struct object
+{
+ object () {}
+ object (const QString& id): id_ (id) {}
+
+#ifdef HAVE_CXX11
+ object (const object& x): id_ (x.id_), i (x.i), s (x.s) {}
+ object (object&& x): id_ (std::move (x.id_)), i (x.i), s (std::move (x.s)) {}
+#endif
+
+ #pragma db id
+ QString id_;
+
+ unsigned int i;
+
+ QOdbList<QString> s;
+
+ inline bool
+ operator== (const object& o) {return id_ == o.id_ && i == o.i && s == o.s;}
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/qt/common/smart-ptr/driver.cxx b/odb-tests/qt/common/smart-ptr/driver.cxx
new file mode 100644
index 0000000..2c7c38a
--- /dev/null
+++ b/odb-tests/qt/common/smart-ptr/driver.cxx
@@ -0,0 +1,248 @@
+// file : qt/common/smart-ptr/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test Qt smart pointers.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <QtCore/QSharedPointer>
+#include <QtCore/QCoreApplication>
+
+#include <odb/database.hxx>
+#include <odb/session.hxx>
+#include <odb/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+// Force instantiation of all QLazySharedPointer and QLazyWeakPointer
+// class template members.
+//
+template class QLazySharedPointer<cont>;
+template class QLazyWeakPointer<cont>;
+
+int
+main (int argc, char* argv[])
+{
+ QCoreApplication app (argc, argv);
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ QSharedPointer<cont> c1 (new cont (1));
+ QSharedPointer<cont> c2 (new cont (2));
+
+ // Test boolean conversion operator.
+ //
+ {
+ assert (!QLazySharedPointer<cont> ());
+ assert (!QLazyWeakPointer<cont> ());
+ assert (QLazySharedPointer<cont> (c1));
+ assert (QLazySharedPointer<cont> (*db, 1));
+ assert (QLazyWeakPointer<cont> (c1));
+ assert (QLazyWeakPointer<cont> (*db, 1));
+ }
+
+ // Test loaded () implementation.
+ //
+ {
+ assert (QLazySharedPointer<cont> ().loaded ());
+ assert (!QLazySharedPointer<cont> (c1).loaded ());
+ assert (!QLazySharedPointer<cont> (*db, 1).loaded ());
+ assert (QLazySharedPointer<cont> (*db, c1).loaded ());
+ assert (QLazyWeakPointer<cont> ().loaded ());
+ assert (!QLazyWeakPointer<cont> (c1).loaded ());
+ assert (!QLazyWeakPointer<cont> (*db, 1).loaded ());
+ assert (QLazyWeakPointer<cont> (*db, c1).loaded ());
+ }
+
+ // Test comparison operators.
+ //
+ {
+ // Transient QLazySharedPointer.
+ //
+ assert (QLazySharedPointer<cont> () == QLazySharedPointer<cont> ());
+ assert (QLazySharedPointer<cont> () != QLazySharedPointer<cont> (c1));
+ assert (QLazySharedPointer<cont> (c1) != QLazySharedPointer<cont> (c2));
+ assert (QLazySharedPointer<cont> (c2) == QLazySharedPointer<cont> (c2));
+
+ // Persistent QLazySharedPointer.
+ //
+ QLazySharedPointer<cont> ls1 (*db, 1), ls2 (*db, c2);
+ assert (ls1 != QLazySharedPointer<cont> ());
+ assert (ls1 != QLazySharedPointer<cont> (c1));
+ assert (ls1 == QLazySharedPointer<cont> (*db, c1));
+ assert (ls1 != ls2);
+ assert (ls2 == QLazySharedPointer<cont> (c2));
+
+ // Transient QLazyWeakPointer.
+ //
+ assert (QLazyWeakPointer<cont> () == QLazyWeakPointer<cont> ());
+ assert (QLazyWeakPointer<cont> () != QLazyWeakPointer<cont> (c1));
+ assert (QLazyWeakPointer<cont> (c1) != QLazyWeakPointer<cont> (c2));
+ assert (QLazyWeakPointer<cont> (c2) == QLazyWeakPointer<cont> (c2));
+ assert (QLazyWeakPointer<cont> () == QLazySharedPointer<cont> ());
+ assert (QLazyWeakPointer<cont> () != QLazySharedPointer<cont> (c1));
+ assert (QLazyWeakPointer<cont> (c1) != QLazySharedPointer<cont> (c2));
+ assert (QLazyWeakPointer<cont> (c2) == QLazySharedPointer<cont> (c2));
+
+ // Persistent QLazyWeakPointer.
+ //
+ QLazyWeakPointer<cont> lw1 (*db, 1), lw2 (*db, c2);
+ assert (lw1 != QLazyWeakPointer<cont> ());
+ assert (lw1 != QLazyWeakPointer<cont> (c1));
+ assert (lw1 == QLazyWeakPointer<cont> (*db, c1));
+ assert (lw1 != lw2);
+ assert (lw2 == QLazyWeakPointer<cont> (c2));
+ assert (ls1 == lw1);
+ assert (ls1 != QLazyWeakPointer<cont> (c1));
+ assert (ls1 == QLazyWeakPointer<cont> (*db, c1));
+ assert (ls1 != lw2);
+ assert (ls2 == QLazyWeakPointer<cont> (c2));
+ }
+
+ // Test swap.
+ //
+ {
+ QLazySharedPointer<cont> lx (*db, 1), ly;
+ swap (lx, ly);
+
+ assert (lx.isNull ());
+ assert (ly == QLazySharedPointer<cont> (*db, c1));
+ }
+
+ // Persist.
+ //
+ QSharedPointer<obj> o1 (new obj (1));
+ QSharedPointer<obj> o2 (new obj (2));
+ QSharedPointer<obj> o3 (new obj (3));
+ QSharedPointer<obj> o4 (new obj (4));
+
+ o1->c = c1;
+ o2->c = c1;
+ o3->c = c2;
+ o4->c = c2;
+
+ {
+ transaction t (db->begin ());
+
+ db->persist (c1);
+
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o3);
+ db->persist (o4);
+
+ db->persist (c2);
+
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ session s;
+ transaction t (db->begin ());
+
+ QSharedPointer<cont> c (db->load<cont> (1));
+ QSharedPointer<obj> o (db->load<obj> (1));
+
+ // Ensure that lazy pointers are present but not loaded.
+ //
+ assert (c->o.size () == 2);
+ assert (!c->o[0].loaded ());
+ assert (!c->o[1].loaded ());
+ assert (!o->c.loaded ());
+
+ // Ensure that the correct object IDs were loaded.
+ //
+ assert (c->o[0].objectId<obj> () == 1);
+ assert (c->o[1].objectId<obj> () == 2);
+ assert (o->c.objectId<obj> () == 1);
+
+ // Load the lazy pointer targets ensuring that the loaded
+ // targets correspond to the cached session objects.
+ //
+ QSharedPointer<cont> cl (o->c.load ());
+ QSharedPointer<obj> ol (c->o[0].load ());
+
+ assert (c->o[0].loaded ());
+ assert (o->c.loaded ());
+
+ assert (cl == c);
+ assert (ol == o);
+
+ t.commit ();
+ }
+
+ // Test lazy weak locking and reloading.
+ //
+ {
+ // No session.
+ //
+ transaction t (db->begin ());
+ QSharedPointer<cont> c (db->load<cont> (1));
+
+ // Lock.
+ //
+ assert (!c->o[1].loaded ());
+ QLazySharedPointer<obj> l (c->o[1].toStrongRef ());
+ assert (!l.loaded ());
+ assert (l.objectId<obj> () == c->o[1].objectId<obj> ());
+
+ // Reload.
+ //
+ assert (!c->o[1].loaded ());
+
+ QSharedPointer<obj> ol (c->o[1].load ());
+ assert (c->o[1].loaded ());
+
+ ol.clear ();
+ assert (!c->o[1].loaded ());
+
+ ol = c->o[1].load ();
+ assert (c->o[1].loaded ());
+
+ t.commit ();
+ }
+
+ //
+ // Test QSharedPointer as a value wrapper.
+ //
+
+ {
+ obj2 o1 (1);
+ obj2 o2 (2);
+ o2.num = QSharedPointer<unsigned long> (new unsigned long (123));
+
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ QSharedPointer<obj2> o1 (db->load<obj2> (1));
+ QSharedPointer<obj2> o2 (db->load<obj2> (2));
+ t.commit ();
+
+ assert (!o1->num);
+ assert (o2->num && *o2->num == 123);
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/qt/common/smart-ptr/test.hxx b/odb-tests/qt/common/smart-ptr/test.hxx
new file mode 100644
index 0000000..2c8bf36
--- /dev/null
+++ b/odb-tests/qt/common/smart-ptr/test.hxx
@@ -0,0 +1,77 @@
+// file : qt/common/smart-ptr/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <vector>
+
+#include <QtCore/QSharedPointer>
+
+#include <odb/core.hxx>
+#include <odb/qt/lazy-ptr.hxx>
+
+struct obj;
+
+#pragma db object
+struct cont
+{
+ cont ()
+ {
+ }
+
+ cont (unsigned long id)
+ : id (id)
+ {
+ }
+
+ #pragma db id
+ unsigned long id;
+
+ typedef std::vector<QLazyWeakPointer<obj> > obj_list;
+
+ #pragma db inverse(c) value_not_null
+ obj_list o;
+};
+
+#pragma db object
+struct obj
+{
+ obj ()
+ {
+ }
+
+ obj (unsigned long id)
+ : id (id)
+ {
+ }
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db not_null
+ QLazySharedPointer<cont> c;
+};
+
+// Test QSharedPointer as a value wrapper.
+//
+#pragma db object
+struct obj2
+{
+ obj2 ()
+ {
+ }
+
+ obj2 (unsigned long id)
+ : id (id)
+ {
+ }
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db null
+ QSharedPointer<unsigned long> num;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/qt/common/template/driver.cxx b/odb-tests/qt/common/template/driver.cxx
new file mode 100644
index 0000000..30f9fc6
--- /dev/null
+++ b/odb-tests/qt/common/template/driver.cxx
@@ -0,0 +1,43 @@
+// file : qt/common/template/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// PLACE TEST DESCRIPTION HERE
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <QtCore/QCoreApplication>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ QCoreApplication app (argc, argv);
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/qt/common/template/template-qt4-vc10.vcxproj b/odb-tests/qt/common/template/template-qt4-vc10.vcxproj
new file mode 100644
index 0000000..fddcac3
--- /dev/null
+++ b/odb-tests/qt/common/template/template-qt4-vc10.vcxproj
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-__value__(database)-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-__value__(database)-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-__value__(database).lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-__value__(database).lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 --database __value__(database) __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>
+ <ItemGroup>
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx)
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__source_entry__(test-odb.cxx)
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/common/template/template-qt4-vc10.vcxproj.filters b/odb-tests/qt/common/template/template-qt4-vc10.vcxproj.filters
new file mode 100644
index 0000000..f3ee658
--- /dev/null
+++ b/odb-tests/qt/common/template/template-qt4-vc10.vcxproj.filters
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx)
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__source_filter_entry__(test-odb.cxx)
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/common/template/template-qt4-vc11.vcxproj b/odb-tests/qt/common/template/template-qt4-vc11.vcxproj
new file mode 100644
index 0000000..a71ac6b
--- /dev/null
+++ b/odb-tests/qt/common/template/template-qt4-vc11.vcxproj
@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-__value__(database)-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-__value__(database)-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-__value__(database).lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-__value__(database).lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 --database __value__(database) __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>
+ <ItemGroup>
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx)
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__source_entry__(test-odb.cxx)
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/common/template/template-qt4-vc11.vcxproj.filters b/odb-tests/qt/common/template/template-qt4-vc11.vcxproj.filters
new file mode 100644
index 0000000..f3ee658
--- /dev/null
+++ b/odb-tests/qt/common/template/template-qt4-vc11.vcxproj.filters
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx)
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__source_filter_entry__(test-odb.cxx)
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/common/template/template-qt4-vc12.vcxproj b/odb-tests/qt/common/template/template-qt4-vc12.vcxproj
new file mode 100644
index 0000000..eaa4d12
--- /dev/null
+++ b/odb-tests/qt/common/template/template-qt4-vc12.vcxproj
@@ -0,0 +1,185 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-__value__(database)-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-__value__(database)-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-__value__(database).lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-__value__(database).lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 --database __value__(database) __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>
+ <ItemGroup>
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx)
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__source_entry__(test-odb.cxx)
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/common/template/template-qt4-vc12.vcxproj.filters b/odb-tests/qt/common/template/template-qt4-vc12.vcxproj.filters
new file mode 100644
index 0000000..f3ee658
--- /dev/null
+++ b/odb-tests/qt/common/template/template-qt4-vc12.vcxproj.filters
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx)
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__source_filter_entry__(test-odb.cxx)
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/common/template/template-qt4-vc8.vcproj b/odb-tests/qt/common/template/template-qt4-vc8.vcproj
new file mode 100644
index 0000000..63fc22c
--- /dev/null
+++ b/odb-tests/qt/common/template/template-qt4-vc8.vcproj
@@ -0,0 +1,353 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-__value__(database)-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-__value__(database).lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-__value__(database)-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-__value__(database).lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__source_entry__(test-odb.cxx)
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe --database __value__(database) __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1400 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx)
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/qt/common/template/template-qt4-vc9.vcproj b/odb-tests/qt/common/template/template-qt4-vc9.vcproj
new file mode 100644
index 0000000..b37675e
--- /dev/null
+++ b/odb-tests/qt/common/template/template-qt4-vc9.vcproj
@@ -0,0 +1,360 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-__value__(database)-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-__value__(database).lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-__value__(database)-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-__value__(database).lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__source_entry__(test-odb.cxx)
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe --database __value__(database) __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx)
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/qt/common/template/template-qt5-vc10.vcxproj b/odb-tests/qt/common/template/template-qt5-vc10.vcxproj
new file mode 100644
index 0000000..add071a
--- /dev/null
+++ b/odb-tests/qt/common/template/template-qt5-vc10.vcxproj
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-__value__(database)-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-__value__(database)-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-__value__(database).lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-__value__(database).lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 --database __value__(database) __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>
+ <ItemGroup>
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx)
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__source_entry__(test-odb.cxx)
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/common/template/template-qt5-vc10.vcxproj.filters b/odb-tests/qt/common/template/template-qt5-vc10.vcxproj.filters
new file mode 100644
index 0000000..f3ee658
--- /dev/null
+++ b/odb-tests/qt/common/template/template-qt5-vc10.vcxproj.filters
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx)
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__source_filter_entry__(test-odb.cxx)
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/common/template/template-qt5-vc11.vcxproj b/odb-tests/qt/common/template/template-qt5-vc11.vcxproj
new file mode 100644
index 0000000..07daa25
--- /dev/null
+++ b/odb-tests/qt/common/template/template-qt5-vc11.vcxproj
@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-__value__(database)-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-__value__(database)-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-__value__(database).lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-__value__(database).lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 --database __value__(database) __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>
+ <ItemGroup>
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx)
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__source_entry__(test-odb.cxx)
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/common/template/template-qt5-vc11.vcxproj.filters b/odb-tests/qt/common/template/template-qt5-vc11.vcxproj.filters
new file mode 100644
index 0000000..f3ee658
--- /dev/null
+++ b/odb-tests/qt/common/template/template-qt5-vc11.vcxproj.filters
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx)
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__source_filter_entry__(test-odb.cxx)
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/common/template/template-qt5-vc12.vcxproj b/odb-tests/qt/common/template/template-qt5-vc12.vcxproj
new file mode 100644
index 0000000..a64ba40
--- /dev/null
+++ b/odb-tests/qt/common/template/template-qt5-vc12.vcxproj
@@ -0,0 +1,185 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-__value__(database)-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-__value__(database)-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-__value__(database).lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-__value__(database).lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 --database __value__(database) __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>
+ <ItemGroup>
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx)
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__source_entry__(test-odb.cxx)
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/common/template/template-qt5-vc12.vcxproj.filters b/odb-tests/qt/common/template/template-qt5-vc12.vcxproj.filters
new file mode 100644
index 0000000..f3ee658
--- /dev/null
+++ b/odb-tests/qt/common/template/template-qt5-vc12.vcxproj.filters
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx)
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__source_filter_entry__(test-odb.cxx)
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/common/template/template-qt5-vc9.vcproj b/odb-tests/qt/common/template/template-qt5-vc9.vcproj
new file mode 100644
index 0000000..57b4edc
--- /dev/null
+++ b/odb-tests/qt/common/template/template-qt5-vc9.vcproj
@@ -0,0 +1,360 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-__value__(database)-d.lib odb-qt-d.lib odb-d.lib Qt5Cored.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-__value__(database).lib odb-qt.lib odb.lib Qt5Core.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-__value__(database)-d.lib odb-qt-d.lib odb-d.lib Qt5Cored.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;__upcase__(database_)__upcase__(__value__(database));HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-__value__(database).lib odb-qt.lib odb.lib Qt5Core.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__source_entry__(test-odb.cxx)
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe --database __value__(database) __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx)
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/qt/common/template/test.hxx b/odb-tests/qt/common/template/test.hxx
new file mode 100644
index 0000000..6f38186
--- /dev/null
+++ b/odb-tests/qt/common/template/test.hxx
@@ -0,0 +1,25 @@
+// file : qt/common/template/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/qt/mssql/basic/driver.cxx b/odb-tests/qt/mssql/basic/driver.cxx
new file mode 100644
index 0000000..fca335d
--- /dev/null
+++ b/odb-tests/qt/mssql/basic/driver.cxx
@@ -0,0 +1,65 @@
+// file : qt/mssql/basic/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test Qt basic type persistence. SQL Server version.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <QtCore/QCoreApplication>
+
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ QCoreApplication app (argc, argv);
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ object o;
+ o.id_ = "object 1";
+ o.sstr_ = QString (512, 's');
+ o.lstr_ = QString (65000, 'l'); // Test temp buffer boundary.
+ o.snstr_ = QString (512, QChar (0x1234));
+ o.lnstr_ = QString (65536, QChar (0x2345));
+ o.sbuf_ = QByteArray (512, 0x01);
+ o.lbuf_ = QByteArray (65536, 0x02);
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> p (db->load<object> (o.id_));
+ t.commit ();
+
+ assert (*p == o);
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/qt/mssql/basic/test.hxx b/odb-tests/qt/mssql/basic/test.hxx
new file mode 100644
index 0000000..bce87b6
--- /dev/null
+++ b/odb-tests/qt/mssql/basic/test.hxx
@@ -0,0 +1,46 @@
+// file : qt/mssql/basic/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <QtCore/QString>
+#include <QtCore/QByteArray>
+
+#pragma db object
+struct object
+{
+ bool
+ operator== (const object& x) const
+ {
+ return
+ id_ == x.id_ &&
+ sstr_ == x.sstr_ &&
+ lstr_ == x.lstr_ &&
+ snstr_ == x.snstr_ &&
+ lnstr_ == x.lnstr_ &&
+ sbuf_ == x.sbuf_ &&
+ lbuf_ == x.lbuf_;
+ }
+
+ #pragma db id
+ QString id_;
+
+ QString sstr_;
+
+ #pragma db type ("VARCHAR(max)")
+ QString lstr_;
+
+ #pragma db type ("NVARCHAR(512)")
+ QString snstr_;
+
+ #pragma db type ("NVARCHAR(max)")
+ QString lnstr_;
+
+ #pragma db type ("VARBINARY(512)")
+ QByteArray sbuf_;
+
+ QByteArray lbuf_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/qt/mssql/date-time/driver.cxx b/odb-tests/qt/mssql/date-time/driver.cxx
new file mode 100644
index 0000000..9b4320e
--- /dev/null
+++ b/odb-tests/qt/mssql/date-time/driver.cxx
@@ -0,0 +1,94 @@
+// file : qt/mssql/date-time/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test Qt date/time type persistence. SQL Server version.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <QtCore/QDateTime>
+#include <QtCore/QCoreApplication>
+
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ QCoreApplication app (argc, argv);
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ object o;
+
+ // Check persistence of null values.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> ol (db->load<object> (o.id));
+ t.commit ();
+
+ assert (ol->is_null ());
+ }
+
+ // Check persistence of valid dates and times.
+ //
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ QDateTime t (QDateTime::currentDateTime ());
+
+ o.date = t.date ();
+ o.date_time = t;
+#endif
+
+ // In DATETIME fractional seconds are rounded to .000, .003, or .007.
+ //
+ o.date_time_dt = QDateTime (QDate (2012, 1, 14),
+ QTime (11, 57, 13, 3));
+
+ // SMALLDATETIME doesn't have seconds (always 0).
+ //
+ o.date_time_sdt = QDateTime (QDate (2012, 1, 14),
+ QTime (11, 57, 0, 0));
+
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ o.time = t.time ();
+#endif
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> ol (db->load<object> (o.id));
+ t.commit ();
+
+ assert (*ol == o);
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/qt/mssql/date-time/test.hxx b/odb-tests/qt/mssql/date-time/test.hxx
new file mode 100644
index 0000000..71769b9
--- /dev/null
+++ b/odb-tests/qt/mssql/date-time/test.hxx
@@ -0,0 +1,68 @@
+// file : qt/mssql/date-time/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <QtCore/QDateTime>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ #pragma db id auto
+ unsigned long id;
+
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ QDate date;
+
+ QDateTime date_time;
+#endif
+
+ #pragma db type ("DATETIME")
+ QDateTime date_time_dt;
+
+ #pragma db type ("SMALLDATETIME")
+ QDateTime date_time_sdt;
+
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ QTime time;
+#endif
+
+ bool
+ operator== (const object& x) const
+ {
+ return
+ id == x.id
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ && date == x.date
+ && date_time == x.date_time
+#endif
+ && date_time_dt == x.date_time_dt
+ && date_time_sdt == x.date_time_sdt
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ && time == x.time
+#endif
+ ;
+ }
+
+ bool
+ is_null () const
+ {
+ return
+ true
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ && date.isNull ()
+ && date_time.isNull ()
+#endif
+ && date_time_dt.isNull ()
+ && date_time_sdt.isNull ()
+#if !defined(MSSQL_SERVER_VERSION) || MSSQL_SERVER_VERSION >= 1000
+ && time.isNull ()
+#endif
+ ;
+ }
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/qt/mssql/template/driver.cxx b/odb-tests/qt/mssql/template/driver.cxx
new file mode 100644
index 0000000..97ef6ef
--- /dev/null
+++ b/odb-tests/qt/mssql/template/driver.cxx
@@ -0,0 +1,43 @@
+// file : qt/mssql/template/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// PLACE TEST DESCRIPTION HERE
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <QtCore/QCoreApplication>
+
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ QCoreApplication app (argc, argv);
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/qt/mssql/template/template-qt4-vc10.vcxproj b/odb-tests/qt/mssql/template/template-qt4-vc10.vcxproj
new file mode 100644
index 0000000..e4902d0
--- /dev/null
+++ b/odb-tests/qt/mssql/template/template-qt4-vc10.vcxproj
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-mssql-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-mssql-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-mssql.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-mssql.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/mssql/template/template-qt4-vc10.vcxproj.filters b/odb-tests/qt/mssql/template/template-qt4-vc10.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/mssql/template/template-qt4-vc10.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/mssql/template/template-qt4-vc11.vcxproj b/odb-tests/qt/mssql/template/template-qt4-vc11.vcxproj
new file mode 100644
index 0000000..b9dd89c
--- /dev/null
+++ b/odb-tests/qt/mssql/template/template-qt4-vc11.vcxproj
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-mssql-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-mssql-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-mssql.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-mssql.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/mssql/template/template-qt4-vc11.vcxproj.filters b/odb-tests/qt/mssql/template/template-qt4-vc11.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/mssql/template/template-qt4-vc11.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/mssql/template/template-qt4-vc12.vcxproj b/odb-tests/qt/mssql/template/template-qt4-vc12.vcxproj
new file mode 100644
index 0000000..0916a38
--- /dev/null
+++ b/odb-tests/qt/mssql/template/template-qt4-vc12.vcxproj
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-mssql-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-mssql-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-mssql.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-mssql.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/mssql/template/template-qt4-vc12.vcxproj.filters b/odb-tests/qt/mssql/template/template-qt4-vc12.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/mssql/template/template-qt4-vc12.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/mssql/template/template-qt4-vc8.vcproj b/odb-tests/qt/mssql/template/template-qt4-vc8.vcproj
new file mode 100644
index 0000000..38916fa
--- /dev/null
+++ b/odb-tests/qt/mssql/template/template-qt4-vc8.vcproj
@@ -0,0 +1,354 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-mssql-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-mssql.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-mssql-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-mssql.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1400 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/qt/mssql/template/template-qt4-vc9.vcproj b/odb-tests/qt/mssql/template/template-qt4-vc9.vcproj
new file mode 100644
index 0000000..da1b5ab
--- /dev/null
+++ b/odb-tests/qt/mssql/template/template-qt4-vc9.vcproj
@@ -0,0 +1,361 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-mssql-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-mssql.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-mssql-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-mssql.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/qt/mssql/template/template-qt5-vc10.vcxproj b/odb-tests/qt/mssql/template/template-qt5-vc10.vcxproj
new file mode 100644
index 0000000..508e262
--- /dev/null
+++ b/odb-tests/qt/mssql/template/template-qt5-vc10.vcxproj
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-mssql-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-mssql-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-mssql.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-mssql.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/mssql/template/template-qt5-vc10.vcxproj.filters b/odb-tests/qt/mssql/template/template-qt5-vc10.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/mssql/template/template-qt5-vc10.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/mssql/template/template-qt5-vc11.vcxproj b/odb-tests/qt/mssql/template/template-qt5-vc11.vcxproj
new file mode 100644
index 0000000..a974034
--- /dev/null
+++ b/odb-tests/qt/mssql/template/template-qt5-vc11.vcxproj
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-mssql-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-mssql-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-mssql.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-mssql.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/mssql/template/template-qt5-vc11.vcxproj.filters b/odb-tests/qt/mssql/template/template-qt5-vc11.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/mssql/template/template-qt5-vc11.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/mssql/template/template-qt5-vc12.vcxproj b/odb-tests/qt/mssql/template/template-qt5-vc12.vcxproj
new file mode 100644
index 0000000..40e2fbb
--- /dev/null
+++ b/odb-tests/qt/mssql/template/template-qt5-vc12.vcxproj
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-mssql-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-mssql-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-mssql.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-mssql.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/mssql/template/template-qt5-vc12.vcxproj.filters b/odb-tests/qt/mssql/template/template-qt5-vc12.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/mssql/template/template-qt5-vc12.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/mssql/template/template-qt5-vc9.vcproj b/odb-tests/qt/mssql/template/template-qt5-vc9.vcproj
new file mode 100644
index 0000000..66ea9ab
--- /dev/null
+++ b/odb-tests/qt/mssql/template/template-qt5-vc9.vcproj
@@ -0,0 +1,361 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-mssql-d.lib odb-qt-d.lib odb-d.lib Qt5Cored.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-mssql.lib odb-qt.lib odb.lib Qt5Core.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-mssql-d.lib odb-qt-d.lib odb-d.lib Qt5Cored.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-mssql.lib odb-qt.lib odb.lib Qt5Core.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/qt/mssql/template/test.hxx b/odb-tests/qt/mssql/template/test.hxx
new file mode 100644
index 0000000..033703a
--- /dev/null
+++ b/odb-tests/qt/mssql/template/test.hxx
@@ -0,0 +1,25 @@
+// file : qt/mssql/template/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/qt/mysql/basic/driver.cxx b/odb-tests/qt/mysql/basic/driver.cxx
new file mode 100644
index 0000000..b685d68
--- /dev/null
+++ b/odb-tests/qt/mysql/basic/driver.cxx
@@ -0,0 +1,60 @@
+// file : qt/mysql/basic/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test Qt basic type persistence. MySQL version.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <QtCore/QCoreApplication>
+
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ QCoreApplication app (argc, argv);
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ object o;
+ o.str = "Constantin Michael";
+ o.blob = QByteArray ("\0x13\0xDE\0x00\0x00\0x00\0x54\0xF2\0x6A", 8);
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> p (db->load<object> (o.str));
+ t.commit ();
+
+ assert (*p == o);
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/qt/mysql/basic/test.hxx b/odb-tests/qt/mysql/basic/test.hxx
new file mode 100644
index 0000000..6dd246e
--- /dev/null
+++ b/odb-tests/qt/mysql/basic/test.hxx
@@ -0,0 +1,27 @@
+// file : qt/mysql/basic/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <QtCore/QString>
+#include <QtCore/QByteArray>
+
+#pragma db object
+struct object
+{
+ bool
+ operator== (const object& x) const
+ {
+ return
+ str == x.str &&
+ blob == x.blob;
+ }
+
+ #pragma db id
+ QString str;
+
+ QByteArray blob;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/qt/mysql/date-time/driver.cxx b/odb-tests/qt/mysql/date-time/driver.cxx
new file mode 100644
index 0000000..94cf653
--- /dev/null
+++ b/odb-tests/qt/mysql/date-time/driver.cxx
@@ -0,0 +1,178 @@
+// file : qt/mysql/date-time/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test Qt date/time type persistence. MySQL version.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <QtCore/QDateTime>
+#include <QtCore/QCoreApplication>
+
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+bool
+test_out_of_range_value (object&, database&);
+
+int
+main (int argc, char* argv[])
+{
+ QCoreApplication app (argc, argv);
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ mysql_version v;
+ {
+ transaction t (db->begin ());
+ db->query<mysql_version> ().begin ().load (v);
+ t.commit ();
+ }
+
+ // If we are running against MySQL 5.6.4 or later alter the tables
+ // to allow sub-second precision.
+ //
+ bool fs (v.major > 5 ||
+ (v.major == 5 && (v.minor > 6 ||
+ (v.minor == 6 && v.release >= 4))));
+ if (fs)
+ {
+ transaction t (db->begin ());
+ db->execute ("ALTER TABLE `qt_mysql_dt_object`" \
+ " MODIFY COLUMN `date_time` DATETIME(3)," \
+ " MODIFY COLUMN `timestamp` TIMESTAMP(3) NULL," \
+ " MODIFY COLUMN `time` TIME(3)");
+ t.commit ();
+ }
+
+ //
+ // Check valid dates and times.
+ //
+
+ object o;
+
+ // Check persistence of null values.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> ol (db->load<object> (o.id));
+ t.commit ();
+
+ assert (ol->is_null ());
+ }
+
+ // Check persistence of valid dates and times.
+ //
+
+ // Create a QDateTime containing the current date and time
+ // but with the milliseconds zeroed. MySQL prior to 5.6.4
+ // does not support sub-second prevision.
+ //
+ QDateTime t (QDateTime::currentDateTime ());
+
+ if (!fs)
+ t.setTime (QTime (t.time ().hour (),
+ t.time ().minute (),
+ t.time ().second ()));
+
+ o.date = t.date ();
+ o.date_time = t;
+ o.timestamp = t;
+ o.time = t.time ();
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> ol (db->load<object> (o.id));
+ t.commit ();
+
+ assert (*ol == o);
+ }
+
+ //
+ // Check invalid dates and times.
+ //
+
+ {
+ // Test out of range dates.
+ //
+ object or1, or2;
+ or1.date = QDate (999, 12, 31);
+ or2.date = QDate (10000, 1, 1);
+
+ transaction t (db->begin ());
+ assert (test_out_of_range_value (or1, *db));
+ assert (test_out_of_range_value (or2, *db));
+ t.commit ();
+ }
+
+ {
+ // Test out of range date-times.
+ //
+ object or1, or2;
+ or1.date_time = QDateTime (QDate (999, 12, 31), QTime (23, 59, 59));
+ or2.date_time = QDateTime (QDate (10000, 1, 1));
+
+ transaction t (db->begin ());
+ assert (test_out_of_range_value (or1, *db));
+ assert (test_out_of_range_value (or2, *db));
+ t.commit ();
+ }
+
+ {
+ // Test out of range timestamps.
+ //
+ object or1, or2;
+ or1.timestamp = QDateTime (QDate (1970, 1, 1));
+ or2.timestamp = QDateTime (QDate (2038, 1, 19), QTime (3, 14, 8));
+
+ transaction t (db->begin ());
+ assert (test_out_of_range_value (or1, *db));
+ assert (test_out_of_range_value (or2, *db));
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
+
+bool
+test_out_of_range_value (object& x, database& db)
+{
+ try
+ {
+ db.persist (x);
+ return false;
+ }
+ catch (const odb::qt::date_time::value_out_of_range&)
+ {
+ }
+
+ return true;
+}
diff --git a/odb-tests/qt/mysql/date-time/test.hxx b/odb-tests/qt/mysql/date-time/test.hxx
new file mode 100644
index 0000000..ba31da5
--- /dev/null
+++ b/odb-tests/qt/mysql/date-time/test.hxx
@@ -0,0 +1,70 @@
+// file : qt/mysql/date-time/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <vector>
+
+#include <QtCore/QDateTime>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ bool
+ operator== (const object& x) const
+ {
+ return
+ id == x.id &&
+ date == x.date &&
+ date_time == x.date_time &&
+ timestamp == x.timestamp &&
+ time == x.time;
+ }
+
+ bool
+ is_null () const
+ {
+ return
+ date.isNull () &&
+ date_time.isNull () &&
+ timestamp.isNull () &&
+ time.isNull ();
+ }
+
+ #pragma db id auto
+ unsigned long id;
+
+ QDate date;
+ QDateTime date_time;
+
+ // Make timestamp NULL-able to suppress the auto-initialization and
+ // auto-update characteristics of the TIMESTAMP datatype, and to
+ // allow NULL values.
+ //
+ #pragma db type("TIMESTAMP") null
+ QDateTime timestamp;
+
+ QTime time;
+};
+
+// MySQL server version view.
+//
+#pragma db view query( \
+ "SELECT " \
+ "CAST(SUBSTRING_INDEX(@@version, '.', 1) AS UNSIGNED)," \
+ "CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(@@version, '.', 2), '.', -1) AS UNSIGNED)," \
+ "CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(@@version, '-', 1), '.', -1) AS UNSIGNED)," \
+ "@@protocol_version")
+struct mysql_version
+{
+ unsigned int major;
+ unsigned int minor;
+ unsigned int release;
+
+ unsigned int protocol;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/qt/mysql/template/driver.cxx b/odb-tests/qt/mysql/template/driver.cxx
new file mode 100644
index 0000000..1e293b4
--- /dev/null
+++ b/odb-tests/qt/mysql/template/driver.cxx
@@ -0,0 +1,43 @@
+// file : qt/mysql/template/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// PLACE TEST DESCRIPTION HERE
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <QtCore/QCoreApplication>
+
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ QCoreApplication app (argc, argv);
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/qt/mysql/template/template-qt4-vc10.vcxproj b/odb-tests/qt/mysql/template/template-qt4-vc10.vcxproj
new file mode 100644
index 0000000..0f57bbe
--- /dev/null
+++ b/odb-tests/qt/mysql/template/template-qt4-vc10.vcxproj
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-mysql-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-mysql-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-mysql.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-mysql.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/mysql/template/template-qt4-vc10.vcxproj.filters b/odb-tests/qt/mysql/template/template-qt4-vc10.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/mysql/template/template-qt4-vc10.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/mysql/template/template-qt4-vc11.vcxproj b/odb-tests/qt/mysql/template/template-qt4-vc11.vcxproj
new file mode 100644
index 0000000..33cb785
--- /dev/null
+++ b/odb-tests/qt/mysql/template/template-qt4-vc11.vcxproj
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-mysql-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-mysql-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-mysql.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-mysql.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/mysql/template/template-qt4-vc11.vcxproj.filters b/odb-tests/qt/mysql/template/template-qt4-vc11.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/mysql/template/template-qt4-vc11.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/mysql/template/template-qt4-vc12.vcxproj b/odb-tests/qt/mysql/template/template-qt4-vc12.vcxproj
new file mode 100644
index 0000000..6a2812a
--- /dev/null
+++ b/odb-tests/qt/mysql/template/template-qt4-vc12.vcxproj
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-mysql-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-mysql-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-mysql.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-mysql.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/mysql/template/template-qt4-vc12.vcxproj.filters b/odb-tests/qt/mysql/template/template-qt4-vc12.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/mysql/template/template-qt4-vc12.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/mysql/template/template-qt4-vc8.vcproj b/odb-tests/qt/mysql/template/template-qt4-vc8.vcproj
new file mode 100644
index 0000000..25e096f
--- /dev/null
+++ b/odb-tests/qt/mysql/template/template-qt4-vc8.vcproj
@@ -0,0 +1,354 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-mysql-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-mysql.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-mysql-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-mysql.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1400 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/qt/mysql/template/template-qt4-vc9.vcproj b/odb-tests/qt/mysql/template/template-qt4-vc9.vcproj
new file mode 100644
index 0000000..a904d40
--- /dev/null
+++ b/odb-tests/qt/mysql/template/template-qt4-vc9.vcproj
@@ -0,0 +1,361 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-mysql-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-mysql.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-mysql-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-mysql.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/qt/mysql/template/template-qt5-vc10.vcxproj b/odb-tests/qt/mysql/template/template-qt5-vc10.vcxproj
new file mode 100644
index 0000000..27eb1a8
--- /dev/null
+++ b/odb-tests/qt/mysql/template/template-qt5-vc10.vcxproj
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-mysql-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-mysql-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-mysql.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-mysql.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/mysql/template/template-qt5-vc10.vcxproj.filters b/odb-tests/qt/mysql/template/template-qt5-vc10.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/mysql/template/template-qt5-vc10.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/mysql/template/template-qt5-vc11.vcxproj b/odb-tests/qt/mysql/template/template-qt5-vc11.vcxproj
new file mode 100644
index 0000000..7a2e2b9
--- /dev/null
+++ b/odb-tests/qt/mysql/template/template-qt5-vc11.vcxproj
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-mysql-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-mysql-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-mysql.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-mysql.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/mysql/template/template-qt5-vc11.vcxproj.filters b/odb-tests/qt/mysql/template/template-qt5-vc11.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/mysql/template/template-qt5-vc11.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/mysql/template/template-qt5-vc12.vcxproj b/odb-tests/qt/mysql/template/template-qt5-vc12.vcxproj
new file mode 100644
index 0000000..f75d5d3
--- /dev/null
+++ b/odb-tests/qt/mysql/template/template-qt5-vc12.vcxproj
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-mysql-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-mysql-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-mysql.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-mysql.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/mysql/template/template-qt5-vc12.vcxproj.filters b/odb-tests/qt/mysql/template/template-qt5-vc12.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/mysql/template/template-qt5-vc12.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/mysql/template/template-qt5-vc9.vcproj b/odb-tests/qt/mysql/template/template-qt5-vc9.vcproj
new file mode 100644
index 0000000..c17267b
--- /dev/null
+++ b/odb-tests/qt/mysql/template/template-qt5-vc9.vcproj
@@ -0,0 +1,361 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-mysql-d.lib odb-qt-d.lib odb-d.lib Qt5Cored.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-mysql.lib odb-qt.lib odb.lib Qt5Core.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-mysql-d.lib odb-qt-d.lib odb-d.lib Qt5Cored.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-mysql.lib odb-qt.lib odb.lib Qt5Core.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/qt/mysql/template/test.hxx b/odb-tests/qt/mysql/template/test.hxx
new file mode 100644
index 0000000..94b806d
--- /dev/null
+++ b/odb-tests/qt/mysql/template/test.hxx
@@ -0,0 +1,25 @@
+// file : qt/mysql/template/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/qt/oracle/basic/driver.cxx b/odb-tests/qt/oracle/basic/driver.cxx
new file mode 100644
index 0000000..43a20e2
--- /dev/null
+++ b/odb-tests/qt/oracle/basic/driver.cxx
@@ -0,0 +1,81 @@
+// file : qt/oracle/basic/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test Qt basic type persistence. Oracle version.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+#include <string>
+
+#include <QtCore/QCoreApplication>
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ QCoreApplication app (argc, argv);
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ string short_str (13, 's');
+ string medium_str (150, 'm');
+ string long_str (20000, 'v');
+ // Oracle UTF-8 support is limited to 3-byte sequences.
+ //
+ string unicode_str ("a \xD5\x95 \xEA\xAA\xAA \xE2\x82\xAC bcdef");
+
+ object o;
+
+ o.char_= QString::fromStdString (short_str);
+ o.varchar2 = QString::fromStdString (medium_str);
+ o.clob = QString::fromStdString (long_str);
+
+ // fromStdString() assumes ASCII in Qt4 and UTF-8 in Qt5.
+ //
+ o.nchar= QString::fromUtf8 (unicode_str.c_str (),
+ static_cast<int> (unicode_str.size ()));
+ o.nvarchar2 = QString::fromUtf8 (unicode_str.c_str (),
+ static_cast<int> (unicode_str.size ()));
+ o.nclob = QString::fromStdString (long_str);
+
+ o.raw = QByteArray ("\0x13\0xDE\0x00\0x00\0x00\0x54\0xF2\0x6A", 8);
+ o.blob = QByteArray (long_str.c_str (),
+ static_cast<int> (long_str.size ()));
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> p (db->load<object> (o.varchar2));
+ t.commit ();
+ assert (*p == o);
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/qt/oracle/basic/test.hxx b/odb-tests/qt/oracle/basic/test.hxx
new file mode 100644
index 0000000..6781a97
--- /dev/null
+++ b/odb-tests/qt/oracle/basic/test.hxx
@@ -0,0 +1,51 @@
+// file : qt/oracle/basic/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <QtCore/QString>
+#include <QtCore/QByteArray>
+
+#pragma db object
+struct object
+{
+ bool
+ operator== (const object& x) const
+ {
+ return
+ varchar2 == x.varchar2 &&
+ char_ == x.char_ &&
+ nchar == x.nchar &&
+ nvarchar2 == x.nvarchar2 &&
+ clob == x.clob &&
+ nclob == x.nclob &&
+ blob == x.blob &&
+ raw == x.raw;
+ }
+
+ #pragma db id
+ QString varchar2;
+
+ #pragma db type ("CHAR(13)")
+ QString char_;
+
+ #pragma db type ("NCHAR(13)")
+ QString nchar;
+
+ #pragma db type ("NVARCHAR2(512)")
+ QString nvarchar2;
+
+ #pragma db type ("CLOB")
+ QString clob;
+
+ #pragma db type ("NCLOB")
+ QString nclob;
+
+ QByteArray blob;
+
+ #pragma db type ("RAW(128)")
+ QByteArray raw;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/qt/oracle/date-time/driver.cxx b/odb-tests/qt/oracle/date-time/driver.cxx
new file mode 100644
index 0000000..d13fab7
--- /dev/null
+++ b/odb-tests/qt/oracle/date-time/driver.cxx
@@ -0,0 +1,80 @@
+// file : qt/oracle/date-time/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test Qt date/time type persistence. Oracle version.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <QtCore/QDateTime>
+#include <QtCore/QCoreApplication>
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ QCoreApplication app (argc, argv);
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ object o;
+
+ // Check persistence of null values.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> ol (db->load<object> (o.id));
+ t.commit ();
+
+ assert (ol->is_null ());
+ }
+
+ // Check persistence of valid dates and times.
+ //
+ QDateTime t (QDateTime::currentDateTime ());
+
+ o.date = t.date ();
+ o.date_time = t;
+ o.date_time_d = QDateTime (QDate (2012, 6, 27), QTime (14, 17, 05, 0));
+ o.time = t.time ();
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> ol (db->load<object> (o.id));
+ t.commit ();
+
+ assert (*ol == o);
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/qt/oracle/date-time/test.hxx b/odb-tests/qt/oracle/date-time/test.hxx
new file mode 100644
index 0000000..93bc5b7
--- /dev/null
+++ b/odb-tests/qt/oracle/date-time/test.hxx
@@ -0,0 +1,45 @@
+// file : qt/oracle/date-time/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <QtCore/QDateTime>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ bool
+ operator== (const object& x) const
+ {
+ return
+ id == x.id &&
+ date == x.date &&
+ date_time == x.date_time &&
+ date_time_d == x.date_time_d &&
+ time == x.time;
+ }
+
+ bool
+ is_null () const
+ {
+ return
+ date.isNull () &&
+ date_time.isNull () &&
+ date_time_d.isNull () &&
+ time.isNull ();
+ }
+
+ #pragma db id auto
+ unsigned long id;
+
+ QDate date;
+ QDateTime date_time;
+ #pragma db type("DATE")
+ QDateTime date_time_d;
+ QTime time;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/qt/oracle/template/driver.cxx b/odb-tests/qt/oracle/template/driver.cxx
new file mode 100644
index 0000000..9b092a9
--- /dev/null
+++ b/odb-tests/qt/oracle/template/driver.cxx
@@ -0,0 +1,43 @@
+// file : qt/oracle/template/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// PLACE TEST DESCRIPTION HERE
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <QtCore/QCoreApplication>
+
+#include <odb/oracle/database.hxx>
+#include <odb/oracle/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ QCoreApplication app (argc, argv);
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/qt/oracle/template/template-qt4-vc10.vcxproj b/odb-tests/qt/oracle/template/template-qt4-vc10.vcxproj
new file mode 100644
index 0000000..6e7c369
--- /dev/null
+++ b/odb-tests/qt/oracle/template/template-qt4-vc10.vcxproj
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-oracle-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-oracle-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-oracle.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-oracle.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/oracle/template/template-qt4-vc10.vcxproj.filters b/odb-tests/qt/oracle/template/template-qt4-vc10.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/oracle/template/template-qt4-vc10.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/oracle/template/template-qt4-vc11.vcxproj b/odb-tests/qt/oracle/template/template-qt4-vc11.vcxproj
new file mode 100644
index 0000000..ad1e888
--- /dev/null
+++ b/odb-tests/qt/oracle/template/template-qt4-vc11.vcxproj
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-oracle-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-oracle-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-oracle.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-oracle.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/oracle/template/template-qt4-vc11.vcxproj.filters b/odb-tests/qt/oracle/template/template-qt4-vc11.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/oracle/template/template-qt4-vc11.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/oracle/template/template-qt4-vc12.vcxproj b/odb-tests/qt/oracle/template/template-qt4-vc12.vcxproj
new file mode 100644
index 0000000..88dffa8
--- /dev/null
+++ b/odb-tests/qt/oracle/template/template-qt4-vc12.vcxproj
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-oracle-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-oracle-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-oracle.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-oracle.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/oracle/template/template-qt4-vc12.vcxproj.filters b/odb-tests/qt/oracle/template/template-qt4-vc12.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/oracle/template/template-qt4-vc12.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/oracle/template/template-qt4-vc8.vcproj b/odb-tests/qt/oracle/template/template-qt4-vc8.vcproj
new file mode 100644
index 0000000..187254b
--- /dev/null
+++ b/odb-tests/qt/oracle/template/template-qt4-vc8.vcproj
@@ -0,0 +1,354 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-oracle-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-oracle.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-oracle-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-oracle.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1400 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/qt/oracle/template/template-qt4-vc9.vcproj b/odb-tests/qt/oracle/template/template-qt4-vc9.vcproj
new file mode 100644
index 0000000..292b899
--- /dev/null
+++ b/odb-tests/qt/oracle/template/template-qt4-vc9.vcproj
@@ -0,0 +1,361 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-oracle-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-oracle.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-oracle-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-oracle.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/qt/oracle/template/template-qt5-vc10.vcxproj b/odb-tests/qt/oracle/template/template-qt5-vc10.vcxproj
new file mode 100644
index 0000000..c388d6b
--- /dev/null
+++ b/odb-tests/qt/oracle/template/template-qt5-vc10.vcxproj
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-oracle-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-oracle-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-oracle.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-oracle.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/oracle/template/template-qt5-vc10.vcxproj.filters b/odb-tests/qt/oracle/template/template-qt5-vc10.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/oracle/template/template-qt5-vc10.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/oracle/template/template-qt5-vc11.vcxproj b/odb-tests/qt/oracle/template/template-qt5-vc11.vcxproj
new file mode 100644
index 0000000..79d5d15
--- /dev/null
+++ b/odb-tests/qt/oracle/template/template-qt5-vc11.vcxproj
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-oracle-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-oracle-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-oracle.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-oracle.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/oracle/template/template-qt5-vc11.vcxproj.filters b/odb-tests/qt/oracle/template/template-qt5-vc11.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/oracle/template/template-qt5-vc11.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/oracle/template/template-qt5-vc12.vcxproj b/odb-tests/qt/oracle/template/template-qt5-vc12.vcxproj
new file mode 100644
index 0000000..2ae82a6
--- /dev/null
+++ b/odb-tests/qt/oracle/template/template-qt5-vc12.vcxproj
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-oracle-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-oracle-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-oracle.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-oracle.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/oracle/template/template-qt5-vc12.vcxproj.filters b/odb-tests/qt/oracle/template/template-qt5-vc12.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/oracle/template/template-qt5-vc12.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/oracle/template/template-qt5-vc9.vcproj b/odb-tests/qt/oracle/template/template-qt5-vc9.vcproj
new file mode 100644
index 0000000..2712157
--- /dev/null
+++ b/odb-tests/qt/oracle/template/template-qt5-vc9.vcproj
@@ -0,0 +1,361 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-oracle-d.lib odb-qt-d.lib odb-d.lib Qt5Cored.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-oracle.lib odb-qt.lib odb.lib Qt5Core.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-oracle-d.lib odb-qt-d.lib odb-d.lib Qt5Cored.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-oracle.lib odb-qt.lib odb.lib Qt5Core.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/qt/oracle/template/test.hxx b/odb-tests/qt/oracle/template/test.hxx
new file mode 100644
index 0000000..0ad65cf
--- /dev/null
+++ b/odb-tests/qt/oracle/template/test.hxx
@@ -0,0 +1,25 @@
+// file : qt/oracle/template/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/qt/pgsql/basic/driver.cxx b/odb-tests/qt/pgsql/basic/driver.cxx
new file mode 100644
index 0000000..4c6a78a
--- /dev/null
+++ b/odb-tests/qt/pgsql/basic/driver.cxx
@@ -0,0 +1,60 @@
+// file : qt/pgsql/basic/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test Qt basic type persistence. PostgreSQL version.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <QtCore/QCoreApplication>
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ QCoreApplication app (argc, argv);
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ object o;
+ o.str = "Constantin Michael";
+ o.blob = QByteArray ("\0x13\0xDE\0x00\0x00\0x00\0x54\0xF2\0x6A", 8);
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> p (db->load<object> (o.str));
+ t.commit ();
+
+ assert (*p == o);
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/qt/pgsql/basic/test.hxx b/odb-tests/qt/pgsql/basic/test.hxx
new file mode 100644
index 0000000..d4a1ba6
--- /dev/null
+++ b/odb-tests/qt/pgsql/basic/test.hxx
@@ -0,0 +1,27 @@
+// file : qt/pgsql/basic/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <QtCore/QString>
+#include <QtCore/QByteArray>
+
+#pragma db object
+struct object
+{
+ bool
+ operator== (const object& x) const
+ {
+ return
+ str == x.str &&
+ blob == x.blob;
+ }
+
+ #pragma db id
+ QString str;
+
+ QByteArray blob;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/qt/pgsql/date-time/driver.cxx b/odb-tests/qt/pgsql/date-time/driver.cxx
new file mode 100644
index 0000000..43a676d
--- /dev/null
+++ b/odb-tests/qt/pgsql/date-time/driver.cxx
@@ -0,0 +1,108 @@
+// file : qt/pgsql/date-time/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test Qt date/time type persistence. PostgreSQL version.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <QtCore/QDateTime>
+#include <QtCore/QCoreApplication>
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+bool
+test_out_of_range_value (object&, database&);
+
+int
+main (int argc, char* argv[])
+{
+ QCoreApplication app (argc, argv);
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ // Check persistence of null values.
+ //
+ {
+ object o;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> ol (db->load<object> (o.id));
+ t.commit ();
+
+ assert (ol->is_null ());
+ }
+ }
+
+ // Check persistence of valid dates and times.
+ //
+ {
+ QDateTime ct (QDateTime::currentDateTime ());
+
+ object o;
+ o.date = ct.date ();
+ o.time = ct.time ();
+ o.date_time = ct;
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> ol (db->load<object> (o.id));
+ t.commit ();
+
+ assert (*ol == o);
+ }
+ }
+
+ // Test a QDateTime value before PG epoch.
+ //
+ {
+ object o;
+ o.date_time = QDateTime (QDate (1969, 12, 31), QTime (23, 59, 59, 123));
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> ol (db->load<object> (o.id));
+ t.commit ();
+
+ assert (*ol == o);
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/qt/pgsql/date-time/test.hxx b/odb-tests/qt/pgsql/date-time/test.hxx
new file mode 100644
index 0000000..2da5587
--- /dev/null
+++ b/odb-tests/qt/pgsql/date-time/test.hxx
@@ -0,0 +1,43 @@
+// file : qt/pgsql/date-time/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <vector>
+
+#include <QtCore/QDateTime>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ bool
+ operator== (const object& x) const
+ {
+ return
+ id == x.id &&
+ date == x.date &&
+ time == x.time &&
+ date_time == x.date_time;
+ }
+
+ bool
+ is_null () const
+ {
+ return
+ date.isNull () &&
+ time.isNull () &&
+ date_time.isNull ();
+ }
+
+ #pragma db id auto
+ unsigned long id;
+
+ QDate date;
+ QTime time;
+ QDateTime date_time;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/qt/pgsql/template/driver.cxx b/odb-tests/qt/pgsql/template/driver.cxx
new file mode 100644
index 0000000..71934a7
--- /dev/null
+++ b/odb-tests/qt/pgsql/template/driver.cxx
@@ -0,0 +1,43 @@
+// file : qt/pgsql/template/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// PLACE TEST DESCRIPTION HERE
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <QtCore/QCoreApplication>
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ QCoreApplication app (argc, argv);
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/qt/pgsql/template/template-qt4-vc10.vcxproj b/odb-tests/qt/pgsql/template/template-qt4-vc10.vcxproj
new file mode 100644
index 0000000..1c1e1a7
--- /dev/null
+++ b/odb-tests/qt/pgsql/template/template-qt4-vc10.vcxproj
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-pgsql-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-pgsql-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-pgsql.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-pgsql.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/pgsql/template/template-qt4-vc10.vcxproj.filters b/odb-tests/qt/pgsql/template/template-qt4-vc10.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/pgsql/template/template-qt4-vc10.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/pgsql/template/template-qt4-vc11.vcxproj b/odb-tests/qt/pgsql/template/template-qt4-vc11.vcxproj
new file mode 100644
index 0000000..a7e7a86
--- /dev/null
+++ b/odb-tests/qt/pgsql/template/template-qt4-vc11.vcxproj
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-pgsql-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-pgsql-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-pgsql.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-pgsql.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/pgsql/template/template-qt4-vc11.vcxproj.filters b/odb-tests/qt/pgsql/template/template-qt4-vc11.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/pgsql/template/template-qt4-vc11.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/pgsql/template/template-qt4-vc12.vcxproj b/odb-tests/qt/pgsql/template/template-qt4-vc12.vcxproj
new file mode 100644
index 0000000..f1b9924
--- /dev/null
+++ b/odb-tests/qt/pgsql/template/template-qt4-vc12.vcxproj
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-pgsql-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-pgsql-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-pgsql.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-pgsql.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/pgsql/template/template-qt4-vc12.vcxproj.filters b/odb-tests/qt/pgsql/template/template-qt4-vc12.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/pgsql/template/template-qt4-vc12.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/pgsql/template/template-qt4-vc8.vcproj b/odb-tests/qt/pgsql/template/template-qt4-vc8.vcproj
new file mode 100644
index 0000000..3f364d2
--- /dev/null
+++ b/odb-tests/qt/pgsql/template/template-qt4-vc8.vcproj
@@ -0,0 +1,354 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-pgsql-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-pgsql.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-pgsql-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-pgsql.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1400 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/qt/pgsql/template/template-qt4-vc9.vcproj b/odb-tests/qt/pgsql/template/template-qt4-vc9.vcproj
new file mode 100644
index 0000000..2e9e84b
--- /dev/null
+++ b/odb-tests/qt/pgsql/template/template-qt4-vc9.vcproj
@@ -0,0 +1,361 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-pgsql-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-pgsql.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-pgsql-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-pgsql.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/qt/pgsql/template/template-qt5-vc10.vcxproj b/odb-tests/qt/pgsql/template/template-qt5-vc10.vcxproj
new file mode 100644
index 0000000..cb6aa0c
--- /dev/null
+++ b/odb-tests/qt/pgsql/template/template-qt5-vc10.vcxproj
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-pgsql-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-pgsql-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-pgsql.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-pgsql.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/pgsql/template/template-qt5-vc10.vcxproj.filters b/odb-tests/qt/pgsql/template/template-qt5-vc10.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/pgsql/template/template-qt5-vc10.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/pgsql/template/template-qt5-vc11.vcxproj b/odb-tests/qt/pgsql/template/template-qt5-vc11.vcxproj
new file mode 100644
index 0000000..863bd51
--- /dev/null
+++ b/odb-tests/qt/pgsql/template/template-qt5-vc11.vcxproj
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-pgsql-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-pgsql-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-pgsql.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-pgsql.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/pgsql/template/template-qt5-vc11.vcxproj.filters b/odb-tests/qt/pgsql/template/template-qt5-vc11.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/pgsql/template/template-qt5-vc11.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/pgsql/template/template-qt5-vc12.vcxproj b/odb-tests/qt/pgsql/template/template-qt5-vc12.vcxproj
new file mode 100644
index 0000000..5d958c2
--- /dev/null
+++ b/odb-tests/qt/pgsql/template/template-qt5-vc12.vcxproj
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-pgsql-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-pgsql-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-pgsql.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-pgsql.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/pgsql/template/template-qt5-vc12.vcxproj.filters b/odb-tests/qt/pgsql/template/template-qt5-vc12.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/pgsql/template/template-qt5-vc12.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/pgsql/template/template-qt5-vc9.vcproj b/odb-tests/qt/pgsql/template/template-qt5-vc9.vcproj
new file mode 100644
index 0000000..b32a1e7
--- /dev/null
+++ b/odb-tests/qt/pgsql/template/template-qt5-vc9.vcproj
@@ -0,0 +1,361 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-pgsql-d.lib odb-qt-d.lib odb-d.lib Qt5Cored.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-pgsql.lib odb-qt.lib odb.lib Qt5Core.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-pgsql-d.lib odb-qt-d.lib odb-d.lib Qt5Cored.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-pgsql.lib odb-qt.lib odb.lib Qt5Core.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/qt/pgsql/template/test.hxx b/odb-tests/qt/pgsql/template/test.hxx
new file mode 100644
index 0000000..8b866bd
--- /dev/null
+++ b/odb-tests/qt/pgsql/template/test.hxx
@@ -0,0 +1,25 @@
+// file : qt/pgsql/template/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/qt/sqlite/basic/driver.cxx b/odb-tests/qt/sqlite/basic/driver.cxx
new file mode 100644
index 0000000..3bae10b
--- /dev/null
+++ b/odb-tests/qt/sqlite/basic/driver.cxx
@@ -0,0 +1,60 @@
+// file : qt/sqlite/basic/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test Qt basic type persistence. SQLite version.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <QtCore/QCoreApplication>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ QCoreApplication app (argc, argv);
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ object o;
+ o.str = QString::fromUtf8 ("Constantin Micha\xC3\x88l");
+ o.blob = QByteArray ("\0x13\0xDE\0x00\0x00\0x00\0x54\0xF2\0x6A", 8);
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> p (db->load<object> (o.str));
+ t.commit ();
+
+ assert (*p == o);
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/qt/sqlite/basic/test.hxx b/odb-tests/qt/sqlite/basic/test.hxx
new file mode 100644
index 0000000..b0cdf46
--- /dev/null
+++ b/odb-tests/qt/sqlite/basic/test.hxx
@@ -0,0 +1,27 @@
+// file : qt/sqlite/basic/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <QtCore/QString>
+#include <QtCore/QByteArray>
+
+#pragma db object
+struct object
+{
+ bool
+ operator== (const object& x) const
+ {
+ return
+ str == x.str &&
+ blob == x.blob;
+ }
+
+ #pragma db id
+ QString str;
+
+ QByteArray blob;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/qt/sqlite/date-time/driver.cxx b/odb-tests/qt/sqlite/date-time/driver.cxx
new file mode 100644
index 0000000..adf7889
--- /dev/null
+++ b/odb-tests/qt/sqlite/date-time/driver.cxx
@@ -0,0 +1,140 @@
+// file : qt/sqlite/date-time/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test Qt date/time type persistence. SQLite version.
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <QtCore/QDateTime>
+#include <QtCore/QCoreApplication>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+bool
+test_out_of_range_value (object&, database&);
+
+int
+main (int argc, char* argv[])
+{
+ QCoreApplication app (argc, argv);
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ // Check persistence of null values.
+ //
+ object o1;
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> ol1 (db->load<object> (o1.id));
+ t.commit ();
+
+ assert (ol1->is_null ());
+ }
+
+ QDateTime ct (QDateTime::currentDateTime ());
+ QDateTime ct_no_ms = QDateTime (QDate (ct.date ().year (),
+ ct.date ().month (),
+ ct.date ().day ()),
+ QTime (ct.time ().hour (),
+ ct.time ().minute (),
+ ct.time ().second ()));
+
+ // Check persistence of valid dates and times.
+ //
+ object o2;
+ {
+ o2.date = ct.date ();
+ o2.unix_day = ct.date ();
+ o2.time = ct.time ();
+
+ // QTime stored as SQLite INTEGER has second resolution.
+ // The millsecond part is zeroed to avoid false negatives.
+ //
+ o2.seconds = ct_no_ms.time ();
+
+ o2.date_time = ct;
+
+ // UNIX time has second reolution. The millsecond part is
+ // zeroed to avoid false negatives.
+ //
+ o2.unix_time = ct_no_ms;
+
+ transaction t (db->begin ());
+ db->persist (o2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ auto_ptr<object> ol2 (db->load<object> (o2.id));
+ t.commit ();
+
+ assert (*ol2 == o2);
+ }
+
+ // Test out of range values for QDateTime traits UNIX time
+ // implementation.
+ //
+ {
+ object o;
+ o.unix_time = QDateTime (QDate (1969, 12, 31),
+ QTime (23, 59, 59),
+ Qt::UTC);
+
+ assert (test_out_of_range_value (o, *db));
+ }
+
+ // Test out of range values for QDate traits UNIX time
+ // implementation.
+ //
+ {
+ object o;
+ o.unix_day = QDate (QDate (1969, 12, 31));
+
+ assert (test_out_of_range_value (o, *db));
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
+
+bool
+test_out_of_range_value (object& x, database& db)
+{
+ try
+ {
+ transaction t (db.begin ());
+ db.persist (x);
+ t.rollback ();
+
+ return false;
+ }
+ catch (const odb::qt::date_time::value_out_of_range&)
+ {
+ }
+
+ return true;
+}
diff --git a/odb-tests/qt/sqlite/date-time/test.hxx b/odb-tests/qt/sqlite/date-time/test.hxx
new file mode 100644
index 0000000..9795846
--- /dev/null
+++ b/odb-tests/qt/sqlite/date-time/test.hxx
@@ -0,0 +1,60 @@
+// file : qt/sqlite/date-time/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <vector>
+
+#include <QtCore/QDateTime>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ bool
+ operator== (const object& x) const
+ {
+ return
+ id == x.id &&
+ date == x.date &&
+ unix_day == x.unix_day &&
+ time == x.time &&
+ seconds == x.seconds &&
+ date_time == x.date_time &&
+ unix_time == x.unix_time;
+ }
+
+ bool
+ is_null () const
+ {
+ return
+ date.isNull () &&
+ unix_day.isNull () &&
+ time.isNull () &&
+ seconds.isNull () &&
+ date_time.isNull () &&
+ unix_time.isNull ();
+ }
+
+ #pragma db id auto
+ unsigned long id;
+
+ QDate date;
+
+ #pragma db type ("INTEGER")
+ QDate unix_day;
+
+ QTime time;
+
+ #pragma db type ("INTEGER")
+ QTime seconds;
+
+ QDateTime date_time;
+
+ #pragma db type ("INTEGER")
+ QDateTime unix_time;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/qt/sqlite/template/driver.cxx b/odb-tests/qt/sqlite/template/driver.cxx
new file mode 100644
index 0000000..dc1181c
--- /dev/null
+++ b/odb-tests/qt/sqlite/template/driver.cxx
@@ -0,0 +1,43 @@
+// file : qt/sqlite/template/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// PLACE TEST DESCRIPTION HERE
+//
+
+#include <memory> // std::auto_ptr
+#include <cassert>
+#include <iostream>
+
+#include <QtCore/QCoreApplication>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/transaction.hxx>
+
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ QCoreApplication app (argc, argv);
+
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/qt/sqlite/template/template-qt4-vc10.vcxproj b/odb-tests/qt/sqlite/template/template-qt4-vc10.vcxproj
new file mode 100644
index 0000000..5d4cd02
--- /dev/null
+++ b/odb-tests/qt/sqlite/template/template-qt4-vc10.vcxproj
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-sqlite-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-sqlite-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-sqlite.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-sqlite.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/sqlite/template/template-qt4-vc10.vcxproj.filters b/odb-tests/qt/sqlite/template/template-qt4-vc10.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/sqlite/template/template-qt4-vc10.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/sqlite/template/template-qt4-vc11.vcxproj b/odb-tests/qt/sqlite/template/template-qt4-vc11.vcxproj
new file mode 100644
index 0000000..217b75e
--- /dev/null
+++ b/odb-tests/qt/sqlite/template/template-qt4-vc11.vcxproj
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-sqlite-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-sqlite-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-sqlite.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-sqlite.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/sqlite/template/template-qt4-vc11.vcxproj.filters b/odb-tests/qt/sqlite/template/template-qt4-vc11.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/sqlite/template/template-qt4-vc11.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/sqlite/template/template-qt4-vc12.vcxproj b/odb-tests/qt/sqlite/template/template-qt4-vc12.vcxproj
new file mode 100644
index 0000000..43959de
--- /dev/null
+++ b/odb-tests/qt/sqlite/template/template-qt4-vc12.vcxproj
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-sqlite-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-sqlite-d.lib;odb-qt-d.lib;odb-d.lib;QtCored4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-sqlite.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-sqlite.lib;odb-qt.lib;odb.lib;QtCore4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/sqlite/template/template-qt4-vc12.vcxproj.filters b/odb-tests/qt/sqlite/template/template-qt4-vc12.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/sqlite/template/template-qt4-vc12.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/sqlite/template/template-qt4-vc8.vcproj b/odb-tests/qt/sqlite/template/template-qt4-vc8.vcproj
new file mode 100644
index 0000000..16623a8
--- /dev/null
+++ b/odb-tests/qt/sqlite/template/template-qt4-vc8.vcproj
@@ -0,0 +1,354 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-sqlite-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-sqlite.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-sqlite-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-sqlite.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1400 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/qt/sqlite/template/template-qt4-vc9.vcproj b/odb-tests/qt/sqlite/template/template-qt4-vc9.vcproj
new file mode 100644
index 0000000..b52f2ec
--- /dev/null
+++ b/odb-tests/qt/sqlite/template/template-qt4-vc9.vcproj
@@ -0,0 +1,361 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-sqlite-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-sqlite.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-sqlite-d.lib odb-qt-d.lib odb-d.lib QtCored4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-sqlite.lib odb-qt.lib odb.lib QtCore4.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/qt/sqlite/template/template-qt5-vc10.vcxproj b/odb-tests/qt/sqlite/template/template-qt5-vc10.vcxproj
new file mode 100644
index 0000000..b0f2c28
--- /dev/null
+++ b/odb-tests/qt/sqlite/template/template-qt5-vc10.vcxproj
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-sqlite-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-sqlite-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-sqlite.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-sqlite.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1600 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/sqlite/template/template-qt5-vc10.vcxproj.filters b/odb-tests/qt/sqlite/template/template-qt5-vc10.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/sqlite/template/template-qt5-vc10.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/sqlite/template/template-qt5-vc11.vcxproj b/odb-tests/qt/sqlite/template/template-qt5-vc11.vcxproj
new file mode 100644
index 0000000..d0ca5d9
--- /dev/null
+++ b/odb-tests/qt/sqlite/template/template-qt5-vc11.vcxproj
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-sqlite-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-sqlite-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-sqlite.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-sqlite.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/sqlite/template/template-qt5-vc11.vcxproj.filters b/odb-tests/qt/sqlite/template/template-qt5-vc11.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/sqlite/template/template-qt5-vc11.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/sqlite/template/template-qt5-vc12.vcxproj b/odb-tests/qt/sqlite/template/template-qt5-vc12.vcxproj
new file mode 100644
index 0000000..dc303a6
--- /dev/null
+++ b/odb-tests/qt/sqlite/template/template-qt5-vc12.vcxproj
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{__uuid__()}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>__value__(name)</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>driver</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common-d.lib;odb-sqlite-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common-d.lib;odb-sqlite-d.lib;odb-qt-d.lib;odb-d.lib;Qt5Cored.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib\common.lib;odb-sqlite.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;_CONSOLE;HAVE_CONFIG_VC_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SolutionDir)\..\..\libcommon</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4068;4355;4800;4290;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(SolutionDir)\..\..\libcommon\lib64\common.lib;odb-sqlite.lib;odb-qt.lib;odb.lib;Qt5Core.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+__ifelse__(__value__(odb_options),,,
+m4_dnl
+ <ItemGroup>
+__custom_build_entry__(
+test.hxx,
+odb test.hxx,
+odb.exe --std c++11 __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1700 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+ </ItemGroup>)
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_entry__(test-odb.hxx)
+__header_entry__(test-odb.ixx))
+__header_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/odb-tests/qt/sqlite/template/template-qt5-vc12.vcxproj.filters b/odb-tests/qt/sqlite/template/template-qt5-vc12.vcxproj.filters
new file mode 100644
index 0000000..8ac18a3
--- /dev/null
+++ b/odb-tests/qt/sqlite/template/template-qt5-vc12.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>cxx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{__uuid__()}</UniqueIdentifier>
+ <Extensions>h;hxx;ixx;txx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+__ifelse__(__value__(odb_options),,,
+__header_filter_entry__(test.hxx)
+__header_filter_entry__(test-odb.hxx)
+__header_filter_entry__(test-odb.ixx))
+__header_filter_entries__(extra_headers)
+ </ItemGroup>
+ <ItemGroup>
+__source_filter_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_filter_entry__(test-odb.cxx))
+__source_filter_entries__(extra_sources)
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/odb-tests/qt/sqlite/template/template-qt5-vc9.vcproj b/odb-tests/qt/sqlite/template/template-qt5-vc9.vcproj
new file mode 100644
index 0000000..79c9389
--- /dev/null
+++ b/odb-tests/qt/sqlite/template/template-qt5-vc9.vcproj
@@ -0,0 +1,361 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="__value__(name)"
+ ProjectGUID="{__uuid__()}"
+ RootNamespace="__value__(name)"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common-d.lib odb-sqlite-d.lib odb-qt-d.lib odb-d.lib Qt5Cored.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib\common.lib odb-sqlite.lib odb-qt.lib odb.lib Qt5Core.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;HAVE_CONFIG_VC_H"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common-d.lib odb-sqlite-d.lib odb-qt-d.lib odb-d.lib Qt5Cored.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4068 /wd4355 /wd4800 /wd4290"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="$(SolutionDir)\..\..\libcommon"
+ PreprocessorDefinitions="WIN32;_CONSOLE;HAVE_CONFIG_VC_H"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(SolutionDir)\..\..\libcommon\lib64\common.lib odb-sqlite.lib odb-qt.lib odb.lib Qt5Core.lib"
+ OutputFile="$(OutDir)\driver.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cxx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__source_entry__(driver.cxx)
+__ifelse__(__value__(odb_options),,,__source_entry__(test-odb.cxx))
+__source_entries__(extra_sources)
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hxx;ixx;txx"
+ UniqueIdentifier="{__uuid__()}"
+ >
+__ifelse__(__value__(odb_options),,,
+__file_entry_custom_build__(
+test.hxx,
+odb test.hxx,
+odb.exe __xml__(__shell_quotes__(__value__(odb_options) -DHAVE_CONFIG_VC_H -DODB_MSC_VER=1500 -I$(SolutionDir)\..\..\libcommon)) test.hxx,
+test-odb.hxx;test-odb.ixx;test-odb.cxx)
+__file_entry__(test-odb.hxx)
+__file_entry__(test-odb.ixx))
+__file_entries__(extra_headers)
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/odb-tests/qt/sqlite/template/test.hxx b/odb-tests/qt/sqlite/template/test.hxx
new file mode 100644
index 0000000..1a8d616
--- /dev/null
+++ b/odb-tests/qt/sqlite/template/test.hxx
@@ -0,0 +1,25 @@
+// file : qt/sqlite/template/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ object (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/sqlite-schema.testscript b/odb-tests/sqlite-schema.testscript
new file mode 100644
index 0000000..687f594
--- /dev/null
+++ b/odb-tests/sqlite-schema.testscript
@@ -0,0 +1,5 @@
+# Note that we currently don't manipulate the data using the sqlite3 utility.
+# The database schema is created implicitly by the database object creation
+# function called by the test driver.
+#
+#create_schema_cmd =
diff --git a/odb-tests/sqlite.testscript b/odb-tests/sqlite.testscript
new file mode 100644
index 0000000..986bb37
--- /dev/null
+++ b/odb-tests/sqlite.testscript
@@ -0,0 +1,10 @@
+# file : sqlite.testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+# Setup the test driver command line for the subsequent SQLite tests.
+#
+# Note that for SQLite the schema is created implicitly by the database object
+# creation function called by the test driver.
+#
+test.arguments += ($multi ? 'sqlite' : ) $sqlite_options
+test.cleanups += &odb-test.db # See database-options.testscript for details.
diff --git a/odb-tests/sqlite/attach/buildfile b/odb-tests/sqlite/attach/buildfile
new file mode 100644
index 0000000..bff5435
--- /dev/null
+++ b/odb-tests/sqlite/attach/buildfile
@@ -0,0 +1,35 @@
+# file : sqlite/attach/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($sqlite) "sqlite should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-sqlite%lib{odb-sqlite}
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix sqlitex_attach_ \
+ --generate-schema \
+ --default-database common \
+ --generate-query \
+ --schema main
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
diff --git a/odb-tests/sqlite/attach/driver.cxx b/odb-tests/sqlite/attach/driver.cxx
new file mode 100644
index 0000000..25d279f
--- /dev/null
+++ b/odb-tests/sqlite/attach/driver.cxx
@@ -0,0 +1,109 @@
+// file : sqlite/attach/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test attached database support.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/schema-catalog.hxx>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+namespace sqlite = odb::sqlite;
+using namespace sqlite;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> mdb (create_specific_database<database> (argc, argv));
+
+ {
+ object o ("one");
+
+ connection_ptr c (mdb->connection ());
+
+ database adb (c, ":memory:", "adb");
+
+ // Create schema similar to create_database().
+ //
+ {
+ c->execute ("PRAGMA foreign_keys=OFF");
+
+ transaction t (c->begin ());
+ odb::schema_catalog::create_schema (adb);
+ t.commit ();
+
+ c->execute ("PRAGMA foreign_keys=ON");
+ }
+
+ {
+ transaction t (c->begin ());
+ mdb->persist (o);
+ adb.persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (c->begin ());
+ unique_ptr<object> p (adb.load<object> (o.id));
+ t.commit ();
+
+ assert (p->str == o.str);
+ }
+
+ {
+ o.str = "two";
+
+ transaction t (c->begin ());
+ adb.update (o);
+ t.commit ();
+ }
+
+ {
+ typedef sqlite::query<object> query;
+
+ transaction t (c->begin ());
+ unique_ptr<object> p (adb.query_one<object> (query::str == "two"));
+ t.commit ();
+
+ assert (p->str == o.str);
+ }
+
+ {
+ transaction t (c->begin ());
+ adb.erase (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (c->begin ());
+ unique_ptr<object> p (mdb->load<object> (o.id));
+ t.commit ();
+
+ assert (p.get () != 0);
+ }
+
+ adb.detach ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/sqlite/attach/test.hxx b/odb-tests/sqlite/attach/test.hxx
new file mode 100644
index 0000000..b3b8f63
--- /dev/null
+++ b/odb-tests/sqlite/attach/test.hxx
@@ -0,0 +1,32 @@
+// file : sqlite/attach/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+#include <odb/nullable.hxx>
+
+#include <string>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ explicit
+ object (const std::string& s): str (s) {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ #pragma db unique
+ std::string str;
+
+private:
+ object () {}
+
+ friend class odb::access;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/sqlite/attach/testscript b/odb-tests/sqlite/attach/testscript
new file mode 100644
index 0000000..db5089d
--- /dev/null
+++ b/odb-tests/sqlite/attach/testscript
@@ -0,0 +1,9 @@
+# file : sqlite/attach/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../sqlite.testscript
+
+: basics
+:
+$*
diff --git a/odb-tests/sqlite/auto/buildfile b/odb-tests/sqlite/auto/buildfile
new file mode 100644
index 0000000..46bcc39
--- /dev/null
+++ b/odb-tests/sqlite/auto/buildfile
@@ -0,0 +1,35 @@
+# file : sqlite/auto/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($sqlite) "sqlite should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-sqlite%lib{odb-sqlite}
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix sqlitex_auto_ \
+ --generate-schema \
+ --default-database common \
+ --generate-query \
+ --sqlite-lax-auto-id
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
diff --git a/odb-tests/sqlite/auto/driver.cxx b/odb-tests/sqlite/auto/driver.cxx
new file mode 100644
index 0000000..bcd42c1
--- /dev/null
+++ b/odb-tests/sqlite/auto/driver.cxx
@@ -0,0 +1,76 @@
+// file : sqlite/auto/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test manual/automatic id assignment.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+namespace sqlite = odb::sqlite;
+using namespace sqlite;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ // object
+ //
+ {
+ unsigned long id1, id2, id3;
+ {
+ object o1 (1, "one");
+ object o2 ("two");
+ object o3 (3, "three");
+
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o3);
+ t.commit ();
+
+ id1 = *o1.id_;
+ id2 = *o2.id_;
+ id3 = *o3.id_;
+
+ assert (id1 == 1);
+ assert (id3 == 3);
+
+ assert (id2 != id1);
+ assert (id2 != id3);
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (id1));
+ unique_ptr<object> o2 (db->load<object> (id2));
+ unique_ptr<object> o3 (db->load<object> (id3));
+ t.commit ();
+
+ assert (*o1->id_ == id1 && o1->str_ == "one");
+ assert (*o2->id_ == id2 && o2->str_ == "two");
+ assert (*o3->id_ == id3 && o3->str_ == "three");
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/sqlite/auto/test.hxx b/odb-tests/sqlite/auto/test.hxx
new file mode 100644
index 0000000..9905182
--- /dev/null
+++ b/odb-tests/sqlite/auto/test.hxx
@@ -0,0 +1,32 @@
+// file : sqlite/auto/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+#include <odb/nullable.hxx>
+
+#include <string>
+
+#include <odb/core.hxx>
+
+#pragma db object
+struct object
+{
+ explicit
+ object (const std::string& str): str_ (str) {}
+ object (unsigned long id, const std::string& str): id_ (id), str_ (str) {}
+
+ #pragma db auto id
+ odb::nullable<unsigned long> id_;
+
+ std::string str_;
+
+private:
+ object () {}
+
+ friend class odb::access;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/sqlite/auto/testscript b/odb-tests/sqlite/auto/testscript
new file mode 100644
index 0000000..a5a7da5
--- /dev/null
+++ b/odb-tests/sqlite/auto/testscript
@@ -0,0 +1,9 @@
+# file : sqlite/auto/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../sqlite.testscript
+
+: basics
+:
+$*
diff --git a/odb-tests/sqlite/custom/buildfile b/odb-tests/sqlite/custom/buildfile
new file mode 100644
index 0000000..de199b4
--- /dev/null
+++ b/odb-tests/sqlite/custom/buildfile
@@ -0,0 +1,34 @@
+# file : sqlite/custom/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($sqlite) "sqlite should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-sqlite%lib{odb-sqlite}
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix sqlitex_custom_ \
+ --generate-schema \
+ --default-database common \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
diff --git a/odb-tests/sqlite/custom/driver.cxx b/odb-tests/sqlite/custom/driver.cxx
new file mode 100644
index 0000000..0627708
--- /dev/null
+++ b/odb-tests/sqlite/custom/driver.cxx
@@ -0,0 +1,78 @@
+// file : sqlite/custom/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test custom database type mapping in SQLite.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+namespace sqlite = odb::sqlite;
+using namespace sqlite;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ object o (1);
+ o.nv.push_back ("123"); // INTEGER
+ o.nv.push_back ("1.23"); // REAL
+ o.nv.push_back ("abc"); // TEXT
+
+ // Persist.
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ // Load.
+ //
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+
+ // Update.
+ //
+ o.nv[1] += "4";
+
+ {
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/sqlite/custom/test.hxx b/odb-tests/sqlite/custom/test.hxx
new file mode 100644
index 0000000..54a2ba3
--- /dev/null
+++ b/odb-tests/sqlite/custom/test.hxx
@@ -0,0 +1,39 @@
+// file : sqlite/custom/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <vector>
+
+#include <odb/core.hxx>
+
+// Map NUMERIC SQLite type to std::string (or any other type that
+// provides the value_traits<?, id_string> specialization). By
+// default ODB treats NUMERIC as REAL. Note also that we don't
+// need to specify to/from conversions since SQLite will convert
+// implicitly.
+//
+#pragma db map type("NUMERIC") as("TEXT")
+
+#pragma db object
+struct object
+{
+ object () {}
+ object (unsigned long id_): id (id_) {}
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db value_type("NUMERIC")
+ std::vector<std::string> nv;
+
+ bool
+ operator== (const object& y) const
+ {
+ return id == y.id && nv == y.nv;
+ }
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/sqlite/custom/testscript b/odb-tests/sqlite/custom/testscript
new file mode 100644
index 0000000..53561ae
--- /dev/null
+++ b/odb-tests/sqlite/custom/testscript
@@ -0,0 +1,9 @@
+# file : sqlite/custom/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../sqlite.testscript
+
+: basics
+:
+$*
diff --git a/odb-tests/sqlite/database/buildfile b/odb-tests/sqlite/database/buildfile
new file mode 100644
index 0000000..01e4c38
--- /dev/null
+++ b/odb-tests/sqlite/database/buildfile
@@ -0,0 +1,14 @@
+# file : sqlite/database/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($sqlite) "sqlite should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libs = libodb-sqlite%lib{odb-sqlite}
+
+exe{driver}: {hxx cxx}{*} $libs testscript
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
diff --git a/odb-tests/sqlite/database/driver.cxx b/odb-tests/sqlite/database/driver.cxx
new file mode 100644
index 0000000..5e16653
--- /dev/null
+++ b/odb-tests/sqlite/database/driver.cxx
@@ -0,0 +1,40 @@
+// file : sqlite/database/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test that database constructors are unambiguous and some other things.
+//
+
+#include <odb/sqlite/database.hxx>
+
+#undef NDEBUG
+#include <cassert>
+
+namespace sqlite = odb::sqlite;
+using namespace sqlite;
+
+int
+main (int argc, char* argv[])
+{
+ // Test UTF-16 to UTF-8 conversion.
+ //
+#ifdef _WIN32
+ {
+ database d (L"t\x00C8st");
+ assert (d.name () == "t\xC3\x88st");
+ }
+#endif
+
+ // This code should not execute.
+ //
+ if (argc != 0)
+ return 0;
+
+ {
+ database d1 ("db1");
+ }
+
+ {
+ database d1 (argc, argv);
+ database d2 (argc, argv, false);
+ }
+}
diff --git a/odb-tests/sqlite/database/testscript b/odb-tests/sqlite/database/testscript
new file mode 100644
index 0000000..b9c0f5f
--- /dev/null
+++ b/odb-tests/sqlite/database/testscript
@@ -0,0 +1,6 @@
+# file : sqlite/database/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+: basics
+:
+$*
diff --git a/odb-tests/sqlite/native/buildfile b/odb-tests/sqlite/native/buildfile
new file mode 100644
index 0000000..76f6b53
--- /dev/null
+++ b/odb-tests/sqlite/native/buildfile
@@ -0,0 +1,15 @@
+# file : sqlite/native/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($sqlite) "sqlite should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libs = libodb-sqlite%lib{odb-sqlite}
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{*} $libs testscript
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
diff --git a/odb-tests/sqlite/native/driver.cxx b/odb-tests/sqlite/native/driver.cxx
new file mode 100644
index 0000000..c87aa5d
--- /dev/null
+++ b/odb-tests/sqlite/native/driver.cxx
@@ -0,0 +1,74 @@
+// file : sqlite/native/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test native SQL execution.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+namespace sqlite = odb::sqlite;
+using namespace sqlite;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (
+ create_specific_database<database> (argc, argv, false));
+
+ // Create the database schema.
+ //
+ {
+ transaction t (db->begin ());
+
+ db->execute ("DROP TABLE IF EXISTS sqlitex_native_test");
+ db->execute ("CREATE TABLE sqlitex_native_test (n INTEGER PRIMARY KEY)");
+
+ t.commit ();
+ }
+
+ // Insert a few rows.
+ //
+ {
+ transaction t (db->begin ());
+
+ assert (
+ db->execute ("INSERT INTO sqlitex_native_test (n) VALUES (1)") == 1);
+
+ assert (
+ db->execute ("INSERT INTO sqlitex_native_test (n) VALUES (2)") == 1);
+
+ t.commit ();
+ }
+
+ // Select a few rows.
+ //
+ {
+ transaction t (db->begin ());
+
+ assert (
+ db->execute ("SELECT n FROM sqlitex_native_test WHERE n < 3") == 2);
+
+ assert (
+ db->execute ("SELECT n FROM sqlitex_native_test WHERE n > 3") == 0);
+
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/sqlite/native/testscript b/odb-tests/sqlite/native/testscript
new file mode 100644
index 0000000..16f05e0
--- /dev/null
+++ b/odb-tests/sqlite/native/testscript
@@ -0,0 +1,9 @@
+# file : sqlite/native/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../sqlite.testscript
+
+: basics
+:
+$*
diff --git a/odb-tests/sqlite/stream/buildfile b/odb-tests/sqlite/stream/buildfile
new file mode 100644
index 0000000..e5baeb5
--- /dev/null
+++ b/odb-tests/sqlite/stream/buildfile
@@ -0,0 +1,34 @@
+# file : sqlite/stream/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($sqlite) "sqlite should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libodb = libodb%lib{odb}
+import libodb_sqlite = libodb-sqlite%lib{odb-sqlite}
+import libs = lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb and
+# libodb-sqlite libraries are resolved for the odb_compile ad hoc rule (see
+# build/root.build for details).
+#
+libue{test-meta}: $libodb $libodb_sqlite
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix sqlitex_stream_ \
+ --generate-schema \
+ --default-database common \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
diff --git a/odb-tests/sqlite/stream/driver.cxx b/odb-tests/sqlite/stream/driver.cxx
new file mode 100644
index 0000000..86522ec
--- /dev/null
+++ b/odb-tests/sqlite/stream/driver.cxx
@@ -0,0 +1,281 @@
+// file : sqlite/stream/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test SQLite BLOB/TEXT incremental I/O.
+//
+
+#include <vector>
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/transaction.hxx>
+
+#include <odb/sqlite/text-stream.hxx>
+#include <odb/sqlite/blob-stream.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+namespace sqlite = odb::sqlite;
+using namespace sqlite;
+
+template <typename S>
+void
+print (const S&)
+{
+ /*
+ cerr << s.db () << '.'
+ << s.table () << '.'
+ << s.column () << '#'
+ << s.rowid () << endl;
+ */
+}
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ string txt (1024 * 1024, 't');
+ vector<char> blb (1024 * 1024, 'b');
+
+ object o;
+
+ {
+ transaction tx (db->begin ());
+
+ o.t.size (txt.size ());
+ o.b.size (blb.size ());
+ o.bv.push_back (blob (blb.size ()));
+ o.bv.push_back (blob (blb.size ()));
+
+ db->persist (o);
+
+ print (o.t);
+ print (o.b);
+ print (o.bv[0]);
+ print (o.bv[1]);
+
+ blob_stream bs (o.b, true);
+ bs.write (blb.data (), blb.size ());
+
+ text_stream ts (o.t, true);
+ ts.write (txt.data (), txt.size ());
+
+ for (vector<blob>::iterator i (o.bv.begin ()); i != o.bv.end (); ++i)
+ {
+ blob_stream bs (*i, true);
+ bs.write (blb.data (), blb.size ());
+ }
+
+ tx.commit ();
+ }
+
+ {
+ transaction tx (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ print (p->t);
+ print (p->b);
+ print (p->bv[0]);
+ print (p->bv[1]);
+
+ text_stream ts (p->t, false);
+ string t (ts.size (), '*');
+ ts.read (const_cast<char*> (t.c_str ()), t.size ());
+ assert (t == txt);
+
+ blob_stream bs (p->b, false);
+ vector<char> b (bs.size (), '\0');
+ bs.read (b.data (), b.size ());
+ assert (b == blb);
+
+ for (vector<blob>::iterator i (p->bv.begin ()); i != p->bv.end (); ++i)
+ {
+ blob_stream bs (*i, false);
+ vector<char> b (bs.size (), '\0');
+ bs.read (b.data (), b.size ());
+ assert (b == blb);
+ }
+
+ assert (p->nb.null ());
+
+ tx.commit ();
+ }
+
+ 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 ());
+
+ o.t.clear ();
+ o.t.size (txt.size ());
+
+ o.b.clear ();
+ o.b.size (blb.size ());
+
+ o.bv[0].clear ();
+ o.bv[0].size (blb.size ());
+
+ o.bv[1].clear ();
+ o.bv[1].size (blb.size ());
+
+ o.nb = blob (blb.size ());
+
+ db->update (o);
+
+ print (o.t);
+ print (o.b);
+ print (o.bv[0]);
+ print (o.bv[1]);
+ print (*o.nb);
+
+ {
+ text_stream ts (o.t, true);
+ ts.write (txt.data (), txt.size ());
+ }
+
+ {
+ blob_stream bs (o.b, true);
+ bs.write (blb.data (), blb.size ());
+ }
+
+ for (vector<blob>::iterator i (o.bv.begin ()); i != o.bv.end (); ++i)
+ {
+ blob_stream bs (*i, true);
+ bs.write (blb.data (), blb.size ());
+ }
+
+ {
+ blob_stream bs (*o.nb, true);
+ bs.write (blb.data (), blb.size ());
+ }
+
+ tx.commit ();
+ }
+
+ {
+ transaction tx (db->begin ());
+ unique_ptr<object> p (db->load<object> (o.id));
+
+ print (p->t);
+ print (p->b);
+ print (p->bv[0]);
+ print (p->bv[1]);
+ print (*p->nb);
+
+ text_stream ts (p->t, false);
+ string t (ts.size (), '*');
+ ts.read (const_cast<char*> (t.c_str ()), t.size ());
+ assert (t == txt);
+
+ blob_stream bs (p->b, false);
+ vector<char> b (bs.size (), '\0');
+ bs.read (b.data (), b.size ());
+ assert (b == blb);
+
+ for (vector<blob>::iterator i (p->bv.begin ()); i != p->bv.end (); ++i)
+ {
+ blob_stream bs (*i, false);
+ vector<char> b (bs.size (), '\0');
+ bs.read (b.data (), b.size ());
+ assert (b == blb);
+ }
+
+ {
+ blob_stream bs (*p->nb, false);
+ vector<char> b (bs.size (), '\0');
+ bs.read (b.data (), b.size ());
+ assert (b == blb);
+ }
+
+ tx.commit ();
+ }
+
+ // Test query.
+ //
+
+ txt.resize (32);
+ blb.resize (64);
+
+ {
+ transaction tx (db->begin ());
+
+ o.t.size (txt.size ());
+ o.b.size (blb.size ());
+ o.bv.clear ();
+ o.nb.reset ();
+
+ db->update (o);
+
+ text_stream ts (o.t, true);
+ ts.write (txt.data (), txt.size ());
+
+ blob_stream bs (o.b, true);
+ bs.write (blb.data (), blb.size ());
+
+ tx.commit ();
+ }
+
+ {
+ typedef sqlite::query<object> query;
+ transaction tx (db->begin ());
+
+ {
+ object o1 (db->query_value<object> (query::t == txt));
+
+ blob_stream bs (o1.b, false);
+ vector<char> b (bs.size (), '\0');
+ bs.read (b.data (), b.size ());
+ assert (b == blb);
+ }
+
+ {
+ object o1 (db->query_value<object> (query::b == blb));
+
+ text_stream ts (o1.t, false);
+ string t (ts.size (), '*');
+ ts.read (const_cast<char*> (t.c_str ()), t.size ());
+ assert (t == txt);
+ }
+
+ tx.commit ();
+ }
+
+ // Test view.
+ //
+ {
+ typedef sqlite::query<view> query;
+ transaction tx (db->begin ());
+
+ view v (db->query_value<view> (query::t == txt));
+
+ blob_stream bs (v.b, false);
+ vector<char> b (bs.size (), '\0');
+ bs.read (b.data (), b.size ());
+ assert (b == blb);
+
+ tx.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/sqlite/stream/test.hxx b/odb-tests/sqlite/stream/test.hxx
new file mode 100644
index 0000000..9189a87
--- /dev/null
+++ b/odb-tests/sqlite/stream/test.hxx
@@ -0,0 +1,37 @@
+// file : sqlite/stream/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <vector>
+
+#include <odb/core.hxx>
+#include <odb/nullable.hxx>
+
+#include <odb/sqlite/text.hxx>
+#include <odb/sqlite/blob.hxx>
+
+#pragma db object
+struct object
+{
+ #pragma db id auto
+ unsigned long id;
+
+ odb::sqlite::text t;
+
+ #pragma db column("_123foo_bar")
+ odb::sqlite::blob b;
+
+ std::vector<odb::sqlite::blob> bv;
+
+ odb::nullable<odb::sqlite::blob> nb;
+};
+
+#pragma db view object(object)
+struct view
+{
+ odb::sqlite::blob b;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/sqlite/stream/testscript b/odb-tests/sqlite/stream/testscript
new file mode 100644
index 0000000..2b9f019
--- /dev/null
+++ b/odb-tests/sqlite/stream/testscript
@@ -0,0 +1,9 @@
+# file : sqlite/stream/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../sqlite.testscript
+
+: basics
+:
+$*
diff --git a/odb-tests/sqlite/transaction/buildfile b/odb-tests/sqlite/transaction/buildfile
new file mode 100644
index 0000000..1653c29
--- /dev/null
+++ b/odb-tests/sqlite/transaction/buildfile
@@ -0,0 +1,33 @@
+# file : sqlite/transaction/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($sqlite) "sqlite should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-sqlite%lib{odb-sqlite}
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix sqlitex_transaction_ \
+ --generate-schema \
+ --default-database common
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
diff --git a/odb-tests/sqlite/transaction/driver.cxx b/odb-tests/sqlite/transaction/driver.cxx
new file mode 100644
index 0000000..80a0804
--- /dev/null
+++ b/odb-tests/sqlite/transaction/driver.cxx
@@ -0,0 +1,61 @@
+// file : sqlite/transaction/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test esoteric SQLite transaction semantics aspects.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+namespace sqlite = odb::sqlite;
+using namespace sqlite;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ // In SQLite, when a commit fails because of the deferred foreign
+ // key constraint violation, the transaction is not automatically
+ // rolled back. Make sure we compensate for that.
+ //
+ try
+ {
+ object o;
+ o.p = odb::lazy_ptr<object1> (*db, 0);
+
+ transaction t (db->begin ());
+ db->persist(o);
+ t.commit ();
+ }
+ catch (const odb::exception&)
+ {
+ }
+
+ // Make sure we can start a new transaction.
+ //
+ {
+ transaction t (db->begin ());
+ t.commit ();
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/sqlite/transaction/test.hxx b/odb-tests/sqlite/transaction/test.hxx
new file mode 100644
index 0000000..bfb981b
--- /dev/null
+++ b/odb-tests/sqlite/transaction/test.hxx
@@ -0,0 +1,26 @@
+// file : sqlite/transaction/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <odb/core.hxx>
+#include <odb/lazy-ptr.hxx>
+
+#pragma db object
+struct object1
+{
+ #pragma db id
+ unsigned long id_;
+};
+
+#pragma db object
+struct object
+{
+ #pragma db id auto
+ unsigned long id_;
+
+ odb::lazy_ptr<object1> p;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/sqlite/transaction/testscript b/odb-tests/sqlite/transaction/testscript
new file mode 100644
index 0000000..c3388cb
--- /dev/null
+++ b/odb-tests/sqlite/transaction/testscript
@@ -0,0 +1,9 @@
+# file : sqlite/transaction/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../sqlite.testscript
+
+: basics
+:
+$*
diff --git a/odb-tests/sqlite/truncation/buildfile b/odb-tests/sqlite/truncation/buildfile
new file mode 100644
index 0000000..232b030
--- /dev/null
+++ b/odb-tests/sqlite/truncation/buildfile
@@ -0,0 +1,34 @@
+# file : sqlite/truncation/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($sqlite) "sqlite should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-sqlite%lib{odb-sqlite}
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix sqlitex_truncation_ \
+ --generate-schema \
+ --default-database common \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
diff --git a/odb-tests/sqlite/truncation/driver.cxx b/odb-tests/sqlite/truncation/driver.cxx
new file mode 100644
index 0000000..a2f6d66
--- /dev/null
+++ b/odb-tests/sqlite/truncation/driver.cxx
@@ -0,0 +1,163 @@
+// file : sqlite/truncation/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test insufficient buffer/truncation handling.
+//
+
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+namespace sqlite = odb::sqlite;
+using namespace sqlite;
+
+int
+main (int argc, char* argv[])
+{
+ // The default pre-allocated buffer is 512 bytes long.
+ //
+ string long_str (640, 'c'); // This will get the buffer to 1024
+ string longer_str (1025, 'b');
+
+ try
+ {
+ // Test basic operations.
+ //
+ {
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ // Run persist/load so that the initial bindings are established
+ // (version == 0).
+ //
+ {
+ object1 o (1);
+ o.str_ = "test string";
+
+ transaction t (db->begin ());
+ db->persist (o);
+ db->load (1, o);
+ t.commit ();
+ }
+
+ {
+ object2 o (2);
+ o.str_ = "test string";
+
+ transaction t (db->begin ());
+ db->persist (o);
+ db->load (2, o);
+ t.commit ();
+ }
+
+ // Store/load the long string which should trigger buffer growth.
+ //
+ {
+ object1 o (3);
+ o.str_ = long_str;
+
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object2> o (db->load<object2> (3));
+ assert (o->str_ == long_str);
+ t.commit ();
+ }
+
+ // Store/load longer string.
+ //
+ {
+ object1 o (3);
+ o.str_ = longer_str;
+
+ transaction t (db->begin ());
+ db->update (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object2> o (db->load<object2> (3));
+ assert (o->str_ == longer_str);
+ t.commit ();
+ }
+ }
+
+ // Test query.
+ //
+ {
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ typedef sqlite::query<object1> query;
+ typedef odb::result<object1> result;
+
+ // Run persist/query so that the initial bindings are established
+ // (version == 0).
+ //
+ {
+ object1 o (20);
+ o.str_ = "test string";
+
+ transaction t (db->begin ());
+ db->persist (o);
+ o.id_++;
+ db->persist (o);
+ o.id_++;
+ db->persist (o);
+
+ result r (db->query<object1> (query::id == 20));
+ assert (r.begin ()->id_ == 20);
+ t.commit ();
+ }
+
+ // Test buffer growth with cached result.
+ //
+ {
+ object1 o;
+
+ transaction t (db->begin ());
+
+ result r (db->query<object1> (query::id >= 20));
+ result::iterator i (r.begin ());
+
+ o.id_ = i->id_;
+ o.str_ = long_str;
+
+ // This forces buffer growth in the middle of result iteration.
+ //
+ db->update (o);
+
+ ++i;
+ assert (i->str_ == "test string");
+
+ o.id_ = i->id_;
+ o.str_ = longer_str;
+ db->update (o);
+
+ ++i;
+ assert (i->str_ == "test string");
+
+ t.commit ();
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/sqlite/truncation/test.hxx b/odb-tests/sqlite/truncation/test.hxx
new file mode 100644
index 0000000..8ca44ea
--- /dev/null
+++ b/odb-tests/sqlite/truncation/test.hxx
@@ -0,0 +1,46 @@
+// file : sqlite/truncation/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <odb/core.hxx>
+
+#pragma db object table ("test")
+struct object1
+{
+ object1 (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object1 ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string str_;
+};
+
+#pragma db object table ("test")
+struct object2
+{
+ object2 (unsigned long id)
+ : id_ (id)
+ {
+ }
+
+ object2 ()
+ {
+ }
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string str_;
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/sqlite/truncation/testscript b/odb-tests/sqlite/truncation/testscript
new file mode 100644
index 0000000..97e04f7
--- /dev/null
+++ b/odb-tests/sqlite/truncation/testscript
@@ -0,0 +1,9 @@
+# file : sqlite/truncation/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../sqlite.testscript
+
+: basics
+:
+$*
diff --git a/odb-tests/sqlite/types/buildfile b/odb-tests/sqlite/types/buildfile
new file mode 100644
index 0000000..305ecba
--- /dev/null
+++ b/odb-tests/sqlite/types/buildfile
@@ -0,0 +1,35 @@
+# file : sqlite/types/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert ($sqlite) "sqlite should be configured for this test"
+ assert (!$multi) "multi-database mode is not supported by this test"
+}
+
+import libodb = libodb%lib{odb}
+
+import libs = libodb-sqlite%lib{odb-sqlite}
+import libs += lib{common}
+
+exe{driver}: {hxx cxx}{* -*-odb} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix sqlitex_types_ \
+ --generate-schema \
+ --default-database common \
+ --generate-query \
+ --cxx-prologue '#include "traits.hxx"'
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
diff --git a/odb-tests/sqlite/types/driver.cxx b/odb-tests/sqlite/types/driver.cxx
new file mode 100644
index 0000000..b444d94
--- /dev/null
+++ b/odb-tests/sqlite/types/driver.cxx
@@ -0,0 +1,113 @@
+// file : sqlite/types/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test SQLite type conversion.
+//
+
+#include <limits> // std::numeric_limits
+#include <memory> // std::unique_ptr
+#include <iostream>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/transaction.hxx>
+
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+namespace sqlite = odb::sqlite;
+using namespace sqlite;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ unique_ptr<database> db (create_specific_database<database> (argc, argv));
+
+ object o (1);
+
+ o.bool_ = true;
+ o.integer_ = -123456;
+ o.real_ = 1.123;
+ o.nan_ = numeric_limits<double>::quiet_NaN ();
+
+ string long_str (2040, 'l');
+
+ o.text_ = long_str;
+#ifdef _WIN32
+ o.wtext_ = L"t\x00C8st string";
+#endif
+ o.blob_.assign (long_str.c_str (), long_str.c_str () + long_str.size ());
+
+ {
+ transaction t (db->begin ());
+ db->persist (o);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<object> o1 (db->load<object> (1));
+ t.commit ();
+
+ assert (o == *o1);
+ }
+
+#ifdef _WIN32
+ {
+ typedef sqlite::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+ result r (db->query<object> (query::wtext == L"t\x00C8st string"));
+ assert (!r.empty ());
+ t.commit ();
+ }
+#endif
+
+ // Test char/wchar_t arrays
+ //
+ {
+#ifndef _WIN32
+ char_array o1 (1, "");
+ char_array o2 (2, "1234567890");
+ char_array o3 (3, "12345678901234567");
+#else
+ char_array o1 (1, "", L"");
+ char_array o2 (2, "1234567890", L"123456789\x00C8");
+ char_array o3 (3, "12345678901234567", L"1234567890123456\x00C8");
+#endif
+
+ {
+ transaction t (db->begin ());
+ db->persist (o1);
+ db->persist (o2);
+ db->persist (o3);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ unique_ptr<char_array> p1 (db->load<char_array> (1));
+ unique_ptr<char_array> p2 (db->load<char_array> (2));
+ unique_ptr<char_array> p3 (db->load<char_array> (3));
+ t.commit ();
+
+ assert (o1 == *p1);
+ assert (o2 == *p2);
+ assert (o3 == *p3);
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/sqlite/types/test.hxx b/odb-tests/sqlite/types/test.hxx
new file mode 100644
index 0000000..fe0b274
--- /dev/null
+++ b/odb-tests/sqlite/types/test.hxx
@@ -0,0 +1,125 @@
+// file : sqlite/types/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <set>
+#include <string>
+#include <vector>
+#include <memory> // std::unique_ptr
+#include <cstring> // std::memcpy, std::str[n]cmp, std::strlen
+
+#ifdef _WIN32
+# include <cwchar> // std::wcslen, std::wcs[n]cmp
+#endif
+
+#include <odb/core.hxx>
+
+typedef std::unique_ptr<std::string> string_ptr;
+
+#pragma db object
+struct object
+{
+ object () {}
+ object (unsigned long id): id_ (id) {}
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db type("BOOL")
+ bool bool_;
+
+ #pragma db type("INTEGER")
+ int integer_;
+
+ #pragma db type("REAL")
+ double real_;
+
+ double nan_; // Represented in SQLite as NULL.
+
+ #pragma db type("TEXT")
+ std::string text_;
+
+#ifdef _WIN32
+ std::wstring wtext_;
+#endif
+
+ #pragma db type("BLOB")
+ std::vector<char> blob_;
+
+ // Test NULL value.
+ //
+ #pragma db type("TEXT") null
+ string_ptr null_;
+
+ bool
+ operator== (const object& y) const
+ {
+ return id_ == y.id_
+ && bool_ == y.bool_
+ && integer_ == y.integer_
+ && real_ == y.real_
+ && nan_ != nan_
+ && text_ == y.text_
+#ifdef _WIN32
+ && wtext_ == y.wtext_
+#endif
+ && blob_ == y.blob_
+ && ((null_.get () == 0 && y.null_.get () == 0) || *null_ == *y.null_);
+ }
+};
+
+// Test char/wchar_t arrays.
+//
+#pragma db object
+struct char_array
+{
+ char_array () {}
+ char_array (unsigned long id
+ , const char* s
+#ifdef _WIN32
+ , const wchar_t* ws
+#endif
+ )
+ : id_ (id)
+ {
+ std::memcpy (s1, s, std::strlen (s) + 1); // VC++ strncpy deprecation.
+ s2[0] = c1 = *s;
+
+#ifdef _WIN32
+ std::memcpy (ws1, ws, (std::wcslen (ws) + 1) * sizeof (wchar_t));
+ ws2[0] = wc1 = *ws;
+#endif
+ }
+
+ #pragma db id
+ unsigned long id_;
+
+ char s1[17];
+ char s2[1];
+ char c1;
+
+#ifdef _WIN32
+ wchar_t ws1[17];
+ wchar_t ws2[1];
+ wchar_t wc1;
+#endif
+
+ bool
+ operator== (const char_array& y) const
+ {
+ return id_ == y.id_
+ && std::strncmp (s1, y.s1, sizeof (s1)) == 0
+ && s2[0] == y.s2[0]
+ && c1 == y.c1
+#ifdef _WIN32
+ && std::wcsncmp (ws1, y.ws1, sizeof (ws1) / 2) == 0
+ && ws2[0] == y.ws2[0]
+ && wc1 == y.wc1
+#endif
+ ;
+ }
+};
+
+#endif // TEST_HXX
diff --git a/odb-tests/sqlite/types/testscript b/odb-tests/sqlite/types/testscript
new file mode 100644
index 0000000..5e50d32
--- /dev/null
+++ b/odb-tests/sqlite/types/testscript
@@ -0,0 +1,9 @@
+# file : sqlite/types/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+.include ../../sqlite.testscript
+
+: basics
+:
+$*
diff --git a/odb-tests/sqlite/types/traits.hxx b/odb-tests/sqlite/types/traits.hxx
new file mode 100644
index 0000000..9483f58
--- /dev/null
+++ b/odb-tests/sqlite/types/traits.hxx
@@ -0,0 +1,57 @@
+// file : sqlite/types/traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TRAITS_HXX
+#define TRAITS_HXX
+
+#include <cstring> // std::memcpy, std::memset
+
+#include <odb/sqlite/traits.hxx>
+
+#include "test.hxx" // string_ptr
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <>
+ class value_traits<string_ptr, id_text>
+ {
+ public:
+ typedef string_ptr value_type;
+ typedef std::string query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (string_ptr& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ v.reset (is_null ? 0 : new std::string (b.data (), n));
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const string_ptr& v)
+ {
+ is_null = v.get () == 0;
+
+ if (!is_null)
+ {
+ n = v->size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ std::memcpy (b.data (), v->c_str (), n);
+ }
+ }
+ };
+ }
+}
+
+#endif // TRAITS_HXX
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/GPLv3 b/odb/GPLv3
index 94a9ed0..94a9ed0 100644
--- a/GPLv3
+++ b/odb/GPLv3
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/README b/odb/README
index 90078e5..90078e5 100644
--- a/README
+++ b/odb/README
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/build/bootstrap.build b/odb/build/bootstrap.build
index 9e30b56..9e30b56 100644
--- a/build/bootstrap.build
+++ b/odb/build/bootstrap.build
diff --git a/build/export.build b/odb/build/export.build
index 6293a45..6293a45 100644
--- a/build/export.build
+++ b/odb/build/export.build
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/common-query.cxx b/odb/common-query.cxx
deleted file mode 100644
index 517c92c..0000000
--- a/odb/common-query.cxx
+++ /dev/null
@@ -1,1413 +0,0 @@
-// file : odb/common-query.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <sstream>
-
-#include <odb/common-query.hxx>
-
-using namespace std;
-
-// query_utils
-//
-
-string query_utils::
-depth_suffix (size_t d)
-{
- if (d != 0)
- {
- ostringstream os;
- os << d;
- return '_' + os.str ();
- }
-
- return string ();
-}
-
-// Collect nested (composite) types as generated by query_columns.
-//
-struct query_nested_types: object_columns_base, virtual context
-{
- query_nested_types (bool ptr): ptr_ (ptr), in_ptr_ (false), depth_ (0) {}
-
- virtual void
- traverse_object (semantics::class_& c)
- {
- // We don't want to traverse bases.
- //
- names (c);
- }
-
- virtual void
- traverse_composite (semantics::data_member* m, semantics::class_& c)
- {
- if (m != 0)
- {
- string name (prefix_ + public_name (*m));
- name += in_ptr_ ? "_column_class" : "_class";
- name += query_utils::depth_suffix (depth_);
- name += '_';
- types.push_back (name);
-
- depth_++;
- string p (prefix_);
- prefix_ = name + "::";
- object_columns_base::traverse_composite (m, c);
- prefix_ = p;
- depth_--;
- }
- else
- object_columns_base::traverse_composite (m, c); // Base
- }
-
- virtual void
- traverse_pointer (semantics::data_member& m, semantics::class_& c)
- {
- // The same logic as in query_columns.
- //
- if (inverse (m, key_prefix_))
- return;
-
- bool poly_ref (m.count ("polymorphic-ref"));
-
- if (composite_wrapper (utype (*id_member (c))))
- {
- if (ptr_ || poly_ref)
- object_columns_base::traverse_pointer (m, c);
- else
- {
- in_ptr_ = true;
- object_columns_base::traverse_pointer (m, c);
- in_ptr_ = false;
- }
- }
- }
-
-public:
- strings types;
-
-protected:
- bool ptr_;
- bool in_ptr_; // True while we are "inside" an object pointer.
- string prefix_;
- size_t depth_;
-};
-
-void query_utils::
-inst_query_columns (bool decl,
- bool ptr,
- string const& type,
- string const& alias,
- semantics::class_& c)
-{
- inst_header (decl);
- os << (ptr ? "pointer_" : "") << "query_columns<" << endl
- << " " << type << "," << endl
- << " id_" << db << "," << endl
- << " " << alias << " >;"
- << endl;
-
- // If we are generating extern declarations, we also have to generate
- // them for all the nested (composite) structs. That's what VC++ needs.
- //
- if (decl)
- {
- query_nested_types t (ptr);
- t.traverse (c);
-
- for (strings::iterator i (t.types.begin ()); i != t.types.end (); ++i)
- {
- inst_header (decl, true); // Omit export, GCC doesn't like it.
- os << (ptr ? "pointer_" : "") << "query_columns<" << endl
- << " " << type << "," << endl
- << " id_" << db << "," << endl
- << " " << alias << " >::" << *i << ";"
- << endl;
- }
- }
-}
-
-// query_tags
-//
-
-void query_tags::
-traverse (semantics::class_& c)
-{
- if (object (c) || composite (c))
- {
- object_columns_base::traverse (c);
- }
- else if (view (c))
- {
- if (c.get<size_t> ("object-count") != 0)
- {
- view_objects& objs (c.get<view_objects> ("objects"));
-
- for (view_objects::const_iterator i (objs.begin ());
- i < objs.end ();
- ++i)
- {
- if (i->kind != view_object::object)
- continue; // Skip tables.
-
- if (i->alias.empty ())
- continue;
-
- generate (i->alias);
- }
- }
- }
- // Otherwise it is a transient base (of a composite value).
-
- if (nl_)
- os << endl;
-}
-
-void query_tags::
-traverse_object (semantics::class_& c)
-{
- names (c); // We don't want to traverse bases.
-}
-
-void query_tags::
-traverse_composite (semantics::data_member* m, semantics::class_& c)
-{
- // Base type.
- //
- if (m == 0)
- {
- object_columns_base::traverse_composite (m, c);
- return;
- }
-
- // Don't generate an empty struct if we don't have any pointers.
- //
- if (!has_a (c, test_pointer))
- return;
-
- if (nl_)
- os << endl;
-
- os << "struct " << public_name (*m) << "_tag" <<
- query_utils::depth_suffix (depth_)
- << "{";
-
- depth_++;
- object_columns_base::traverse_composite (m, c);
- depth_--;
-
- os << "};";
-
- nl_ = false;
-}
-
-void query_tags::
-traverse_pointer (semantics::data_member& m, semantics::class_&)
-{
- // Ignore polymorphic id references.
- //
- if (m.count ("polymorphic-ref"))
- return;
-
- generate (public_name (m));
-}
-
-void query_tags::
-generate (string const& name)
-{
- os << "struct " << name << "_tag;";
- nl_ = true;
-}
-
-// query_alias_traits
-//
-
-query_alias_traits::
-query_alias_traits (semantics::class_& c, bool decl)
- : decl_ (decl), depth_ (0)
-{
- scope_ = "access::";
- scope_ += (object (c) ? "object_traits_impl" : "view_traits_impl");
- scope_ += "< " + class_fq_name (c) + ", id_" + db.string () + " >";
-}
-
-void query_alias_traits::
-traverse_object (semantics::class_& c)
-{
- // We don't want to traverse bases.
- //
- names (c);
-}
-
-void query_alias_traits::
-traverse_composite (semantics::data_member* m, semantics::class_& c)
-{
- // Base type.
- //
- if (m == 0)
- {
- object_columns_base::traverse_composite (m, c);
- return;
- }
-
- string old_scope (scope_);
- scope_ += "::" + public_name (*m) + "_tag" +
- query_utils::depth_suffix (depth_);
-
- depth_++;
- object_columns_base::traverse_composite (m, c);
- depth_--;
-
- scope_ = old_scope;
-}
-
-void query_alias_traits::
-traverse_pointer (semantics::data_member& m, semantics::class_& c)
-{
- // Ignore polymorphic id references.
- //
- if (m.count ("polymorphic-ref"))
- return;
-
- if (decl_)
- generate_decl (public_name (m), c);
- else
- generate_def (m, c);
-}
-
-void query_alias_traits::
-generate_decl (string const& tag, semantics::class_& c)
-{
- semantics::class_* poly_root (polymorphic (c));
- bool poly_derived (poly_root != 0 && poly_root != &c);
- semantics::class_* poly_base (poly_derived ? &polymorphic_base (c) : 0);
-
- if (poly_derived)
- generate_decl (tag, *poly_base);
-
- string const& fq_name (class_fq_name (c));
-
- os << "template <>" << endl
- << "struct " << exp << "alias_traits<" << endl
- << " " << fq_name << "," << endl
- << " id_" << db << "," << endl
- << " " << scope_ << "::" << tag << "_tag>"
- << "{";
-
- if (poly_derived)
- os << "typedef alias_traits<" << endl
- << " " << class_fq_name (*poly_base) << "," << endl
- << " id_" << db << "," << endl
- << " " << scope_ << "::" << tag << "_tag>" << endl
- << "base_traits;"
- << endl;
-
- // For dynamic multi-database support also generate common traits
- // alias. Note that the tag type is the same since they all are
- // derived from object_traits.
- //
- if (db != database::common && multi_dynamic)
- os << "typedef alias_traits<" << endl
- << " " << fq_name << "," << endl
- << " id_common," << endl
- << " " << scope_ << "::" << tag << "_tag>" << endl
- << "common_traits;"
- << endl;
-
- generate_decl_body (); // Table name, etc.
-
- os << "};";
-}
-
-void query_alias_traits::
-generate_decl_body ()
-{
-}
-
-void query_alias_traits::
-generate_def (semantics::data_member&, semantics::class_&)
-{
-}
-
-void query_alias_traits::
-generate_def (string const&, semantics::class_&, string const&)
-{
-}
-
-// query_columns_base
-//
-
-query_columns_base::
-query_columns_base (semantics::class_& c, bool decl, bool inst)
- : decl_ (decl), inst_ (inst), depth_ (0)
-{
- string const& n (class_fq_name (c));
-
- if (decl)
- scope_ = "access::object_traits_impl< " + n + ", id_" +
- db.string () + " >";
- else
- scope_ = "query_columns_base< " + n + ", id_" + db.string () + " >";
-}
-
-void query_columns_base::
-traverse_object (semantics::class_& c)
-{
- // We don't want to traverse bases.
- //
- names (c);
-}
-
-void query_columns_base::
-traverse_composite (semantics::data_member* m, semantics::class_& c)
-{
- // Base type.
- //
- if (m == 0)
- {
- object_columns_base::traverse_composite (m, c);
- return;
- }
-
- // Don't generate an empty struct if we don't have any pointers.
- //
- if (!has_a (c, test_pointer))
- return;
-
- string name (public_name (*m));
- string dsuffix (query_utils::depth_suffix (depth_));
-
- if (decl_)
- {
- os << "// " << name << endl
- << "//" << endl
- << "struct " << name << "_base" << dsuffix << '_'
- << "{";
-
- string old_scope (scope_);
- scope_ += "::" + name + "_tag" + dsuffix;
-
- depth_++;
- object_columns_base::traverse_composite (m, c);
- depth_--;
-
- scope_ = old_scope;
-
- os << "};";
- }
- else
- {
- string old_scope (scope_);
- scope_ += "::" + name + "_base" + dsuffix + '_';
-
- depth_++;
- object_columns_base::traverse_composite (m, c);
- depth_--;
-
- scope_ = old_scope;
- }
-}
-
-void query_columns_base::
-traverse_pointer (semantics::data_member& m, semantics::class_& c)
-{
- // Ignore polymorphic id references.
- //
- if (m.count ("polymorphic-ref"))
- return;
-
- string name (public_name (m));
- string const& fq_name (class_fq_name (c));
- bool inv (inverse (m, key_prefix_));
-
- if (decl_)
- {
- os << "// " << name << endl
- << "//" << endl;
-
- os << "typedef" << endl
- << "odb::alias_traits<" << endl
- << " " << fq_name << "," << endl
- << " id_" << db << "," << endl
- << " " << scope_ << "::" << name << "_tag>" << endl
- << name << "_alias_;"
- << endl;
-
- if (inv)
- {
- os << "typedef" << endl
- << "odb::query_pointer<" << endl
- << " odb::pointer_query_columns<" << endl
- << " " << fq_name << "," << endl
- << " id_" << db << "," << endl
- << " " << name << "_alias_ > >" << endl
- << name << "_type_ ;"
- << endl
- << "static " << const_ << name << "_type_ " << name << ";"
- << endl;
- }
- }
- else if (inst_)
- {
- generate_inst (m, c);
- }
- else
- {
- // Generate explicit template instantiation directive for the
- // pointed-to pointer_query_columns.
- //
- if (multi_dynamic)
- generate_inst (m, c);
-
- if (inv)
- os << const_ << scope_ << "::" << name << "_type_" << endl
- << scope_ << "::" << name << ";"
- << endl;
- }
-}
-
-void query_columns_base::
-generate_inst (semantics::data_member& m, semantics::class_& c)
-{
- string name (public_name (m));
- string const& fq_name (class_fq_name (c));
-
- string alias (scope_ + "::" + name + "_alias_");
-
- // Instantiate base [pointer_]query_columns.
- //
- {
- instance<query_columns_base_insts> b (true, inst_, alias, true);
- traversal::inherits i (*b);
- inherits (c, i);
- }
-
- // If the pointed-to class has no pointers of its own then
- // pointer_query_columns just derives from query_columns and
- // that's what we need to instantiate.
- //
- inst_query_columns (inst_,
- has_a (c, test_pointer | include_base),
- fq_name,
- alias,
- c);
-}
-
-// query_columns
-//
-
-query_columns::
-query_columns (bool decl, bool ptr, semantics::class_& c)
- : decl_ (decl), ptr_ (ptr), poly_ref_ (false), in_ptr_ (false),
- fq_name_ (class_fq_name (c)),
- resue_abstract_ (abstract (c) && !polymorphic (c)),
- depth_ (0)
-{
-}
-
-void query_columns::
-traverse_object (semantics::class_& c)
-{
- // We don't want to traverse bases.
- //
- names (c);
-}
-
-void query_columns::
-traverse_composite (semantics::data_member* m, semantics::class_& c)
-{
- // Base type.
- //
- if (m == 0)
- {
- object_columns_base::traverse_composite (m, c);
- return;
- }
-
- // Use _class_ instead of _type_ to avoid potential clashes between
- // the class and member names.
- //
- string name (public_name (*m));
- string suffix (in_ptr_ ? "_column_class" : "_class");
-
- // Add depth to the nested composite to avoid potential name conflicts
- // in situations like these:
- //
- // struct inner { ... };
- // struct outer { inner value; };
- // struct object { outer value; }
- //
- string dsuffix (query_utils::depth_suffix (depth_));
- suffix += dsuffix;
- suffix += '_';
-
- depth_++;
-
- if (decl_)
- {
- os << "// " << name << endl
- << "//" << endl
- << "struct ";
-
- // For some bizarre reason VC++ needs the export directive for
- // a type nested in an (exported) template. This appears not
- // to cause any problems for GCC.
- //
- // We only generate the export directive if we are also
- // explicitly instantiating the query_columns templates.
- //
- if (multi_dynamic && !resue_abstract_)
- os << exp;
-
- os << name << suffix;
-
- // Derive from the base in query_columns_base. It contains columns
- // data for the pointer members.
- //
- if (!ptr_ && !poly_ref_ && has_a (c, test_pointer))
- os << ": " << name << "_base" << dsuffix << '_';
-
- os << "{";
-
- if (!const_.empty ())
- os << name << suffix << " ()" // Need user-defined default c-tor for
- << "{" // initialization of static const.
- << "}";
-
- object_columns_base::traverse_composite (m, c);
-
- os << "};";
-
- if (!in_ptr_)
- os << "static " << const_ << name << suffix << " " << name << ";"
- << endl;
- }
- else
- {
- // Handle nested members first.
- //
- string old_scope (scope_);
- scope_ += "::" + name + suffix;
-
- object_columns_base::traverse_composite (m, c);
-
- scope_ = old_scope;
-
- // Composite member. Note that here we don't use suffix for the in-
- // pointer case because the actual pointer column type derives from
- // the composite column type (dual interface; see traverse_pointer()
- // below).
- //
- string tmpl (ptr_ ? "pointer_query_columns" : "query_columns");
- tmpl += "< " + fq_name_ + ", id_" + db.string () + ", A >" + scope_;
-
- os << "template <typename A>" << endl
- << const_ << "typename " << tmpl << "::" << name <<
- (in_ptr_ ? string ("_type_") : suffix) << endl
- << tmpl << "::" << name << ";"
- << endl;
- }
-
- depth_--;
-}
-
-void query_columns::
-column_ctor (string const&, string const&, string const&)
-{
-}
-
-void query_columns::
-column_common (semantics::data_member& m,
- string const& type,
- string const&,
- string const& suffix)
-{
- string name (public_name (m));
-
- if (decl_)
- {
- os << "// " << name << endl
- << "//" << endl;
-
- os << "typedef odb::query_column< " << type << " > " << name <<
- suffix << ";"
- << endl;
- }
- else
- {
- // Note that here we don't use suffix.
- //
- string tmpl (ptr_ ? "pointer_query_columns" : "query_columns");
- tmpl += "< " + fq_name_ + ", id_" + db.string () + ", A >" + scope_;
-
- os << "template <typename A>" << endl
- << const_ << "typename " << tmpl << "::" << name << "_type_" << endl
- << tmpl << "::" << name << ";"
- << endl;
- }
-}
-
-bool query_columns::
-traverse_column (semantics::data_member& m, string const& column, bool)
-{
- semantics::names* hint;
- semantics::type* t (&utype (m, hint));
-
- // Unwrap it if it is a wrapper.
- //
- if (semantics::type* wt = wrapper (*t, hint))
- t = &utype (*wt, hint);
-
- column_common (m, t->fq_name (hint), column);
-
- if (decl_)
- {
- string name (public_name (m));
-
- os << "static " << const_ << name << "_type_ " << name << ";"
- << endl;
- }
-
- return true;
-}
-
-void query_columns::
-traverse_pointer (semantics::data_member& m, semantics::class_& c)
-{
- // If this is for the pointer_query_columns and the member is not
- // inverse, then create the normal member corresponding to the id
- // column. This will allow the user to check it for NULL or to
- // compare ids. In case this is for query_columns, then for the
- // inverse member everything has been generated in query_columns_base.
- //
- if (inverse (m, key_prefix_))
- return;
-
- // If we ignore polymorphic references, then a view that uses a custom
- // join condition based on id will use the id column from the base
- // table. But the base table hasn't been joined yet. To resolve this
- // we will generate the id member that points to our column.
- //
- poly_ref_ = m.count ("polymorphic-ref");
-
- string name (public_name (m));
-
- data_member_path& id (*id_member (c));
- semantics::names* hint;
- semantics::type& t (utype (id, hint));
-
- if (composite_wrapper (t))
- {
- // Composite id.
- //
-
- // For pointer_query_columns and poly refs generate normal composite
- // mapping.
- //
- if (ptr_ || poly_ref_)
- object_columns_base::traverse_pointer (m, c);
- else
- {
- // If this is a non-inverse relationship, then make the column have
- // a dual interface: that of an object pointer and of an id column.
- // The latter allows the user to, for example, use the is_null()
- // test in a natural way. For inverse relationships there is no
- // column and so the column interface is not available.
- //
- in_ptr_ = true;
- object_columns_base::traverse_pointer (m, c);
- in_ptr_ = false;
-
- if (decl_)
- {
- os << "typedef" << endl
- << "odb::query_pointer<" << endl
- << " odb::pointer_query_columns<" << endl
- << " " << class_fq_name (c) << "," << endl
- << " id_" << db << "," << endl
- << " " << name << "_alias_ > >" << endl
- << name << "_pointer_type_;"
- << endl;
-
- os << "struct " << name << "_type_: " <<
- name << "_pointer_type_, " <<
- name << "_column_class" << query_utils::depth_suffix (depth_) << '_'
- << "{";
-
- if (!const_.empty ())
- os << name << "_type_ ()" // Need user-defined default c-tor for
- << "{" // initialization of static const.
- << "}";
-
- os << "};";
-
- os << "static " << const_ << name << "_type_ " << name << ";"
- << endl;
- }
- }
- }
- else
- {
- // Simple id.
- //
- string type (t.fq_name (hint));
- string col (column_name (m, key_prefix_, default_name_, column_prefix_));
-
- // For pointer_query_columns and poly refs generate normal column mapping.
- //
- if (ptr_ || poly_ref_)
- column_common (m, type, col);
- else
- {
- // If this is a non-inverse relationship, then make the column have
- // a dual interface: that of an object pointer and of an id column.
- // The latter allows the user to, for example, use the is_null()
- // test in a natural way. For inverse relationships there is no
- // column and so the column interface is not available.
- //
- column_common (m, type, col, "_column_type_");
-
- if (decl_)
- {
- os << "typedef" << endl
- << "odb::query_pointer<" << endl
- << " odb::pointer_query_columns<" << endl
- << " " << class_fq_name (c) << "," << endl
- << " id_" << db << "," << endl
- << " " << name << "_alias_ > >" << endl
- << name << "_pointer_type_;"
- << endl;
-
- os << "struct " << name << "_type_: " <<
- name << "_pointer_type_, " << name << "_column_type_"
- << "{";
-
- column_ctor (type, name + "_type_", name + "_column_type_");
-
- os << "};";
- }
- }
-
- if (decl_)
- os << "static " << const_ << name << "_type_ " << name << ";"
- << endl;
- }
-
- poly_ref_ = false;
-}
-
-// query_columns_bases
-//
-
-void query_columns_bases::
-traverse (type& c)
-{
- // Ignore transient bases. Not used for views.
- //
- if (!object (c))
- return;
-
- if (first_)
- {
- os << ":" << endl
- << " ";
- first_ = false;
- }
- else
- os << "," << endl
- << " ";
-
- os << (ptr_ ? "pointer_query_columns" : "query_columns") << "< " <<
- class_fq_name (c) << ", id_" << db << ", ";
-
- // If our base is polymorphic, then it has its own table/alias.
- //
- if (polymorphic (c))
- os << "typename A::base_traits";
- else
- os << "A";
-
- os << " >";
-}
-
-// query_columns_base_aliases
-//
-
-void query_columns_base_aliases::
-traverse (type& c)
-{
- // Ignore transient bases. Not used for views.
- //
- if (!object (c))
- return;
-
- string const& name (class_name (c));
-
- os << "// " << name << endl
- << "//" << endl
- << "typedef " << (ptr_ ? "pointer_query_columns" : "query_columns") <<
- "< " << class_fq_name (c) << ", id_" << db << ", ";
-
- if (polymorphic (c))
- os << "typename A::base_traits";
- else
- os << "A";
-
- os << " > " << name << ";"
- << endl;
-}
-
-// query_columns_base_insts
-//
-
-query_columns_base_insts::
-query_columns_base_insts (bool test_ptr,
- bool decl,
- string const& alias,
- bool poly)
- : test_ptr_ (test_ptr), decl_ (decl), alias_ (alias), poly_ (poly)
-{
- *this >> inherits_ >> *this;
-}
-
-query_columns_base_insts::
-query_columns_base_insts (query_columns_base_insts const& x)
- : context (), // @@ -Wextra
- test_ptr_ (x.test_ptr_),
- decl_ (x.decl_),
- alias_ (x.alias_),
- poly_ (x.poly_)
-{
- *this >> inherits_ >> *this;
-}
-
-void query_columns_base_insts::
-traverse (type& c)
-{
- if (!object (c))
- return;
-
- bool poly (polymorphic (c));
- if (poly && (poly != poly_))
- return;
-
- bool ptr (has_a (c, test_pointer | include_base));
-
- string old_alias;
- if (poly)
- {
- old_alias = alias_;
- alias_ += "::base_traits";
- }
-
- // Instantiate bases recursively.
- //
- inherits (c, inherits_);
-
- inst_query_columns (decl_,
- test_ptr_ && ptr,
- class_fq_name (c),
- alias_,
- c);
-
- if (!test_ptr_ && ptr)
- inst_query_columns (decl_, true, class_fq_name (c), alias_, c);
-
- if (poly)
- alias_ = old_alias;
-}
-
-// query_columns_type
-//
-
-void query_columns_type::
-traverse (type& c)
-{
- string const& type (class_fq_name (c));
-
- if (ptr_)
- {
- os << "template <typename A>" << endl
- << "struct pointer_query_columns< " << type << ", id_" << db << ", A >";
-
- // If we don't have pointers (in the whole hierarchy), then
- // pointer_query_columns and query_columns are the same.
- //
- if (!has_a (c, test_pointer | include_base))
- {
- os << ":" << endl
- << " query_columns< " << type << ", id_" << db << ", A >"
- << "{"
- << "};";
- }
- else
- {
- {
- instance<query_columns_bases> b (ptr_);
- traversal::inherits i (*b);
- inherits (c, i);
- }
-
- os << "{";
-
- {
- instance<query_columns_base_aliases> b (ptr_);
- traversal::inherits i (*b);
- inherits (c, i);
- }
-
- {
- bool true_ (true);
- instance<query_columns> t (true_, ptr_, c); //@@ forwarding
- t->traverse (c);
- }
-
- os << "};";
-
- generate_impl (c);
- }
- }
- else if (decl_)
- {
- bool has_ptr (has_a (c, test_pointer | exclude_base));
-
- if (has_ptr)
- {
- // Generate aliases.
- //
- {
- bool true_ (true); //@@ (im)perfect forwarding
- instance<query_alias_traits> t (c, true_);
- t->traverse (c);
- }
-
- // This class contains everything for inverse pointers and
- // aliases for non-inverse ones. It doesn't depend on the
- // table alias (A) template argument.
- //
- os << "template <>" << endl
- << "struct " << exp << "query_columns_base< " << type << ", " <<
- "id_" << db << " >"
- << "{";
-
- bool true_ (true); //@@ (im)perfect forwarding.
- bool false_ (false);
- instance<query_columns_base> t (c, true_, false_);
- t->traverse (c);
-
- os << "};";
- }
-
- os << "template <typename A>" << endl
- << "struct query_columns< " << type << ", id_" << db << ", A >";
-
- if (has_ptr)
- os << ":" << endl
- << " query_columns_base< " << type << ", id_" << db << " >";
-
- {
- instance<query_columns_bases> b (ptr_, !has_ptr);
- traversal::inherits i (*b);
- inherits (c, i);
- }
-
- os << "{";
-
- {
- instance<query_columns_base_aliases> b (ptr_);
- traversal::inherits i (*b);
- inherits (c, i);
- }
-
- {
- instance<query_columns> t (decl_, ptr_, c);
- t->traverse (c);
- }
-
- os << "};";
-
- generate_impl (c);
- }
- else if (inst_)
- {
- // If we have the extern symbol, generate extern template declarations.
- //
- if (!ext.empty ())
- {
- bool has_ptr (has_a (c, test_pointer | exclude_base));
- bool reuse_abst (abstract (c) && !polymorphic (c));
-
- if (has_ptr || !reuse_abst)
- {
- os << "#ifdef " << ext << endl
- << endl;
-
- if (has_ptr)
- {
- bool true_ (true); //@@ (im)perfect forwarding.
- bool false_ (false);
-
- instance<query_columns_base> t (c, false_, true_);
- t->traverse (c);
- }
-
- // Don't generate it for reuse-abstract classes.
- //
- if (!reuse_abst)
- generate_inst (c);
-
- os << "#endif // " << ext << endl
- << endl;
- }
- }
- }
- else
- {
- bool has_ptr (has_a (c, test_pointer | exclude_base));
-
- // Generate alias_traits specializations. While the class
- // is generated even if our base has a pointer, there is
- // not source code if we don't have pointers ourselves.
- //
- if (has_ptr)
- {
- bool false_ (false); //@@ (im)perfect forwarding
- instance<query_alias_traits> t (c, false_);
- t->traverse (c);
- }
-
- // query_columns_base
- //
- if (has_ptr)
- {
- bool false_ (false); //@@ (im)perfect forwarding.
- instance<query_columns_base> t (c, false_, false_);
- t->traverse (c);
- }
-
- // Explicit template instantiations. Don't generate it for reuse-
- // abstract classes.
- //
- if (multi_dynamic && (!abstract (c) || polymorphic (c)))
- generate_inst (c);
- }
-}
-
-void query_columns_type::
-generate_impl (type& c)
-{
- string guard;
-
- // Exclude definitions (they will be explicitly instantiated once in
- // the source file) unless we have the extern symbol. In this case
- // the extern template declaration will make sure we don't get
- // instantiations in multiple places and we will avoid the VC++
- // warning C4661 (no definition provided).
- //
- if (multi_dynamic && ext.empty ())
- {
- guard = make_guard ("ODB_" + db.string () + "_QUERY_COLUMNS_DEF");
-
- os << "#ifdef " << guard << endl
- << endl;
- }
-
- {
- bool false_ (false);
- instance<query_columns> t (false_, ptr_, c);
- t->traverse (c);
- }
-
- if (!guard.empty ())
- os << "#endif // " << guard << endl
- << endl;
-}
-
-void query_columns_type::
-generate_inst (type& c)
-{
- string const& type (class_fq_name (c));
-
- // Explicit template instantiations. Here is what we need to
- // instantiate
- //
- // 1. Reuse inheritance bases all the way to the ultimate base.
- // Unlike poly inheritance, reuse inheritance uses the table
- // alias of the derived type. Note that bases can have object
- // pointers of their own but their types have already been
- // instantiated by step 3 below performed for the base object.
- //
- // 2. Object pointers. Note that while object pointers cannot have
- // their own pointers, they can have reuse inheritance bases.
- //
- // 3. The query_columns class for the table itself.
- //
- // We also need to repeat these steps for pointer_query_columns
- // since it is used by views.
- //
- string alias ("access::object_traits_impl< " + type + ", id_" +
- db.string () + " >");
-
- // 1
- //
- {
- instance<query_columns_base_insts> b (false, inst_, alias, false);
- traversal::inherits i (*b);
- inherits (c, i);
- }
-
- // 2: Handled by query_columns_base (which is where we generate
- // all the aliases for object pointers).
- //
-
- // 3
- //
- inst_query_columns (inst_, false, type, alias, c);
-
- if (has_a (c, test_pointer | exclude_base))
- inst_query_columns (inst_, true, type, alias, c);
-}
-
-// view_query_columns_type
-//
-
-void view_query_columns_type::
-traverse (type& c)
-{
- if (decl_)
- generate_decl (c);
- else
- generate_def (c);
-}
-
-void view_query_columns_type::
-generate_decl (type& c)
-{
- string const& type (class_fq_name (c));
- size_t obj_count (c.get<size_t> ("object-count"));
- view_objects& objs (c.get<view_objects> ("objects"));
-
- // Generate alias_traits specializations.
- //
- {
- bool true_ (true); //@@ (im)perfect forwarding
- instance<query_alias_traits> at (c, true_);
-
- for (view_objects::const_iterator i (objs.begin ()); i < objs.end (); ++i)
- {
- if (i->kind != view_object::object)
- continue; // Skip tables.
-
- if (i->alias.empty ())
- continue;
-
- semantics::class_& o (*i->obj);
- qname const& t (table_name (o));
-
- // Check that the alias is not the same as the table name
- // (if this is a polymorphic object, then the alias is just
- // a prefix).
- //
- if (polymorphic (o) || t.qualified () || i->alias != t.uname ())
- at->generate_decl (i->alias, o);
- }
- }
-
- // If we have the extern symbol, generate extern template declarations.
- // Do it before query_columns since the inheritance will trigger
- // instantiation and we won't be able to change visibility (GCC).
- //
- if (obj_count != 0 && multi_dynamic && !ext.empty ())
- {
- os << "#ifdef " << ext << endl
- << endl;
-
- generate_inst (c);
-
- os << "#endif // " << ext << endl
- << endl;
- }
-
- // query_columns
- //
- os << "struct " << exp << "access::view_traits_impl< " << type << ", " <<
- "id_" << db << " >::query_columns";
-
- if (obj_count > 1)
- {
- os << "{";
-
- for (view_objects::const_iterator i (objs.begin ()); i < objs.end (); ++i)
- {
- if (i->kind != view_object::object)
- continue; // Skip tables.
-
- bool alias (!i->alias.empty ());
- semantics::class_& o (*i->obj);
- string const& oname (alias ? i->alias : class_name (o));
- string const& otype (class_fq_name (o));
- qname const& table (table_name (o));
-
- os << "// " << oname << endl
- << "//" << endl
- << "typedef" << endl
- << "odb::pointer_query_columns<" << endl
- << " " << otype << "," << endl
- << " id_" << db << "," << endl;
-
- if (alias && (polymorphic (o) ||
- table.qualified () ||
- i->alias != table.uname ()))
- {
- os << " odb::alias_traits< " << otype << "," << endl
- << " id_" << db << "," << endl
- << " access::view_traits_impl< " << type << ", id_" << db <<
- " >::" << i->alias << "_tag> >" << endl;
- }
- else
- os << " odb::access::object_traits_impl< " << otype << ", id_" <<
- db << " > >" << endl;
-
- os << oname << ";"
- << endl;
- }
-
- os << "};";
- }
- else
- {
- // For a single object view we generate a shortcut without
- // an intermediate typedef.
- //
- view_object const* vo (0);
- for (view_objects::const_iterator i (objs.begin ());
- vo == 0 && i < objs.end ();
- ++i)
- {
- if (i->kind == view_object::object)
- vo = &*i;
- }
-
- bool alias (!vo->alias.empty ());
- semantics::class_& o (*vo->obj);
- string const& otype (class_fq_name (o));
- qname const& table (table_name (o));
-
- os << ":" << endl
- << " odb::pointer_query_columns<" << endl
- << " " << otype << "," << endl
- << " id_" << db << "," << endl;
-
- if (alias && (polymorphic (o) ||
- table.qualified () ||
- vo->alias != table.uname ()))
- {
- os << " odb::alias_traits<" << endl
- << " " << otype << "," << endl
- << " id_" << db << "," << endl
- << " access::view_traits_impl< " << type << ", id_" <<
- db << " >::" << vo->alias << "_tag> >";
- }
- else
- os << " odb::access::object_traits_impl< " << otype <<
- ", id_" << db << " > >";
-
- os << "{"
- << "};";
- }
-}
-
-void view_query_columns_type::
-generate_def (type& c)
-{
- view_objects& objs (c.get<view_objects> ("objects"));
-
- // Generate alias_traits specializations.
- //
- {
- bool false_ (false); //@@ (im)perfect forwarding
- instance<query_alias_traits> at (c, false_);
-
- for (view_objects::const_iterator i (objs.begin ());
- i < objs.end ();
- ++i)
- {
- if (i->kind != view_object::object)
- continue; // Skip tables.
-
- if (i->alias.empty ())
- continue;
-
- semantics::class_& o (*i->obj);
- qname const& t (table_name (o));
-
- // Check that the alias is not the same as the table name
- // (if this is a polymorphic object, then the alias is just
- // a prefix).
- //
- if (polymorphic (o) || t.qualified () || i->alias != t.uname ())
- {
- at->generate_def (i->alias, o, i->alias);
- }
- }
- }
-
- if (multi_dynamic)
- generate_inst (c);
-}
-
-void view_query_columns_type::
-generate_inst (type& c)
-{
- string const& type (class_fq_name (c));
- view_objects& objs (c.get<view_objects> ("objects"));
-
- string traits ("access::view_traits_impl< " + type + ", id_" +
- db.string () + " >");
-
- // Instantiate [pointer_]query_columns.
- //
- for (view_objects::const_iterator i (objs.begin ());
- i < objs.end ();
- ++i)
- {
- if (i->kind != view_object::object)
- continue; // Skip tables.
-
- if (i->alias.empty ())
- continue; // Instantiated by object.
-
- semantics::class_& o (*i->obj);
- qname const& t (table_name (o));
-
- // Check that the alias is not the same as the table name
- // (if this is a polymorphic object, then the alias is just
- // a prefix).
- //
- if (polymorphic (o) || t.qualified () || i->alias != t.uname ())
- {
- string const& otype (class_fq_name (o));
- string alias ("odb::alias_traits<\n"
- " " + otype + ",\n"
- " id_" + db.string () + ",\n"
- " " + traits + "::" + i->alias + "_tag>");
-
- // Instantiate base [pointer_]query_columns.
- //
- {
- instance<query_columns_base_insts> b (true, decl_, alias, true);
- traversal::inherits i (*b);
- inherits (o, i);
- }
-
- // If the pointed-to class has no pointers of its own then
- // pointer_query_columns just derives from query_columns and
- // that's what we need to instantiate.
- //
- inst_query_columns (decl_,
- has_a (o, test_pointer | include_base),
- otype,
- alias,
- o);
- }
- }
-}
diff --git a/odb/context.cxx b/odb/context.cxx
deleted file mode 100644
index e220d0e..0000000
--- a/odb/context.cxx
+++ /dev/null
@@ -1,3289 +0,0 @@
-// file : odb/context.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <odb/gcc.hxx>
-
-#include <cctype> // std::toupper
-#include <cassert>
-#include <sstream>
-
-#include <odb/context.hxx>
-#include <odb/common.hxx>
-#include <odb/pragma.hxx>
-#include <odb/cxx-lexer.hxx>
-#include <odb/diagnostics.hxx>
-
-#include <odb/relational/mssql/context.hxx>
-#include <odb/relational/mysql/context.hxx>
-#include <odb/relational/oracle/context.hxx>
-#include <odb/relational/pgsql/context.hxx>
-#include <odb/relational/sqlite/context.hxx>
-
-using namespace std;
-
-static inline void
-add_space (string& s)
-{
- string::size_type n (s.size ());
- if (n != 0 && s[n - 1] != ' ')
- s += ' ';
-}
-
-//
-// custom_cxx_type
-//
-string custom_cxx_type::
-translate (string const& val, const cxx_tokens& expr)
-{
- // Similar to member_access::translate() and a few other places.
- //
- string r;
-
- cxx_tokens_lexer l;
- l.start (expr);
-
- string tl;
- for (cpp_ttype tt (l.next (tl)), ptt (CPP_EOF); tt != CPP_EOF;)
- {
- // Try to format the expression to resemble the style of the
- // generated code.
- //
- switch (tt)
- {
- case CPP_NOT:
- {
- add_space (r);
- r += '!';
- break;
- }
- case CPP_COMMA:
- {
- r += ", ";
- break;
- }
- case CPP_OPEN_PAREN:
- {
- if (ptt == CPP_NAME ||
- ptt == CPP_KEYWORD)
- add_space (r);
-
- r += '(';
- break;
- }
- case CPP_CLOSE_PAREN:
- {
- r += ')';
- break;
- }
- case CPP_OPEN_SQUARE:
- {
- r += '[';
- break;
- }
- case CPP_CLOSE_SQUARE:
- {
- r += ']';
- break;
- }
- case CPP_OPEN_BRACE:
- {
- add_space (r);
- r += "{ ";
- break;
- }
- case CPP_CLOSE_BRACE:
- {
- add_space (r);
- r += '}';
- break;
- }
- case CPP_SEMICOLON:
- {
- r += ';';
- break;
- }
- case CPP_ELLIPSIS:
- {
- add_space (r);
- r += "...";
- break;
- }
- case CPP_PLUS:
- case CPP_MINUS:
- {
- bool unary (ptt != CPP_NAME &&
- ptt != CPP_SCOPE &&
- ptt != CPP_NUMBER &&
- ptt != CPP_STRING &&
- ptt != CPP_CLOSE_PAREN &&
- ptt != CPP_PLUS_PLUS &&
- ptt != CPP_MINUS_MINUS);
-
- if (!unary)
- add_space (r);
-
- r += cxx_lexer::token_spelling[tt];
-
- if (!unary)
- r += ' ';
- break;
- }
- case CPP_PLUS_PLUS:
- case CPP_MINUS_MINUS:
- {
- if (ptt != CPP_NAME &&
- ptt != CPP_CLOSE_PAREN &&
- ptt != CPP_CLOSE_SQUARE)
- add_space (r);
-
- r += cxx_lexer::token_spelling[tt];
- break;
- }
- case CPP_DEREF:
- case CPP_DEREF_STAR:
- case CPP_DOT:
- case CPP_DOT_STAR:
- {
- r += cxx_lexer::token_spelling[tt];
- break;
- }
- case CPP_STRING:
- {
- if (ptt == CPP_NAME ||
- ptt == CPP_KEYWORD ||
- ptt == CPP_STRING ||
- ptt == CPP_NUMBER)
- add_space (r);
-
- r += context::strlit (tl);
- break;
- }
- case CPP_NUMBER:
- {
- if (ptt == CPP_NAME ||
- ptt == CPP_KEYWORD ||
- ptt == CPP_STRING ||
- ptt == CPP_NUMBER)
- add_space (r);
-
- r += tl;
- break;
- }
- case CPP_SCOPE:
- {
- // Add space except for a few common cases.
- //
- if (ptt != CPP_NAME &&
- ptt != CPP_OPEN_PAREN &&
- ptt != CPP_OPEN_SQUARE)
- add_space (r);
-
- r += cxx_lexer::token_spelling[tt];
- break;
- }
- case CPP_NAME:
- {
- // Start of a name.
- //
- if (ptt == CPP_NAME ||
- ptt == CPP_KEYWORD ||
- ptt == CPP_STRING ||
- ptt == CPP_NUMBER)
- add_space (r);
-
- r += tl;
- break;
- }
- case CPP_QUERY:
- {
- if (ptt == CPP_OPEN_PAREN)
- {
- // Get the next token and see if it is ')'.
- //
- ptt = tt;
- tt = l.next (tl);
-
- if (tt == CPP_CLOSE_PAREN)
- r += val;
- else
- {
- add_space (r);
- r += "? ";
- }
- continue; // We have already gotten the next token.
- }
- }
- // Fall through.
- default:
- {
- // Handle CPP_KEYWORD here to avoid a warning (it is not
- // part of the cpp_ttype enumeration).
- //
- if (tt == CPP_KEYWORD)
- {
- if (ptt == CPP_NAME ||
- ptt == CPP_KEYWORD ||
- ptt == CPP_STRING ||
- ptt == CPP_NUMBER)
- add_space (r);
-
- r += tl;
- }
- else
- {
- // All the other operators.
- //
- add_space (r);
- r += cxx_lexer::token_spelling[tt];
- r += ' ';
- }
- break;
- }
- }
-
- //
- // Watch out for the continue statements above if you add any
- // logic here.
- //
-
- ptt = tt;
- tt = l.next (tl);
- }
-
- return r;
-}
-
-
-//
-// view_object
-//
-
-string view_object::
-name () const
-{
- if (!alias.empty ())
- return alias;
-
- return kind == object ? context::class_name (*obj) : tbl_name.string ();
-}
-
-//
-// member_access
-//
-
-bool member_access::
-placeholder () const
-{
- for (cxx_tokens::const_iterator i (expr.begin ()), e (expr.end ()); i != e;)
- {
- if (i->type == CPP_OPEN_PAREN)
- {
- if (++i != e && i->type == CPP_QUERY)
- {
- if (++i != e && i->type == CPP_CLOSE_PAREN)
- return true;
- }
- }
- else
- ++i;
- }
-
- return false;
-}
-
-string member_access::
-translate (string const& obj, string const& val, string const& db) const
-{
- if (empty ())
- {
- error (loc) << "non-empty " << kind << " expression required" << endl;
- throw operation_failed ();
- }
-
- // This code is similar to translate_expression() from relations/source.cxx.
- //
- string r;
-
- cxx_tokens_lexer l;
- l.start (expr);
-
- string tl;
- for (cpp_ttype tt (l.next (tl)), ptt (CPP_EOF); tt != CPP_EOF;)
- {
- // Try to format the expression to resemble the style of the
- // generated code.
- //
- switch (tt)
- {
- case CPP_COMMA:
- {
- r += ", ";
- break;
- }
- case CPP_OPEN_PAREN:
- {
- if (ptt == CPP_NAME ||
- ptt == CPP_KEYWORD)
- add_space (r);
-
- r += '(';
- break;
- }
- case CPP_CLOSE_PAREN:
- {
- r += ')';
- break;
- }
- case CPP_OPEN_SQUARE:
- {
- r += '[';
- break;
- }
- case CPP_CLOSE_SQUARE:
- {
- r += ']';
- break;
- }
- case CPP_OPEN_BRACE:
- {
- add_space (r);
- r += "{ ";
- break;
- }
- case CPP_CLOSE_BRACE:
- {
- add_space (r);
- r += '}';
- break;
- }
- case CPP_SEMICOLON:
- {
- r += ';';
- break;
- }
- case CPP_ELLIPSIS:
- {
- add_space (r);
- r += "...";
- break;
- }
- case CPP_PLUS:
- case CPP_MINUS:
- {
- bool unary (ptt != CPP_NAME &&
- ptt != CPP_SCOPE &&
- ptt != CPP_NUMBER &&
- ptt != CPP_STRING &&
- ptt != CPP_CLOSE_PAREN &&
- ptt != CPP_PLUS_PLUS &&
- ptt != CPP_MINUS_MINUS);
-
- if (!unary)
- add_space (r);
-
- r += cxx_lexer::token_spelling[tt];
-
- if (!unary)
- r += ' ';
- break;
- }
- case CPP_PLUS_PLUS:
- case CPP_MINUS_MINUS:
- {
- if (ptt != CPP_NAME &&
- ptt != CPP_CLOSE_PAREN &&
- ptt != CPP_CLOSE_SQUARE)
- add_space (r);
-
- r += cxx_lexer::token_spelling[tt];
- break;
- }
- case CPP_DEREF:
- case CPP_DEREF_STAR:
- case CPP_DOT:
- case CPP_DOT_STAR:
- {
- r += cxx_lexer::token_spelling[tt];
- break;
- }
- case CPP_STRING:
- {
- if (ptt == CPP_NAME ||
- ptt == CPP_KEYWORD ||
- ptt == CPP_STRING ||
- ptt == CPP_NUMBER)
- add_space (r);
-
- r += context::strlit (tl);
- break;
- }
- case CPP_NUMBER:
- {
- if (ptt == CPP_NAME ||
- ptt == CPP_KEYWORD ||
- ptt == CPP_STRING ||
- ptt == CPP_NUMBER)
- add_space (r);
-
- r += tl;
- break;
- }
- case CPP_SCOPE:
- {
- // Add space except for a few common cases.
- //
- if (ptt != CPP_NAME &&
- ptt != CPP_OPEN_PAREN &&
- ptt != CPP_OPEN_SQUARE)
- add_space (r);
-
- r += cxx_lexer::token_spelling[tt];
- break;
- }
- case CPP_NAME:
- {
- // Start of a name.
- //
- if (ptt == CPP_NAME ||
- ptt == CPP_KEYWORD ||
- ptt == CPP_STRING ||
- ptt == CPP_NUMBER)
- add_space (r);
-
- r += tl;
- break;
- }
- case CPP_NOT:
- case CPP_QUERY:
- {
- if (ptt == CPP_OPEN_PAREN)
- {
- // Get the next token and see if it is ')'.
- //
- ptt = tt;
- tt = l.next (tl);
-
- if (tt == CPP_CLOSE_PAREN)
- {
- if (ptt == CPP_NOT)
- {
- if (db.empty ())
- {
- error (loc) << "database instance (!) not available in this "
- << "context" << endl;
- throw operation_failed ();
- }
-
- r += db;
- }
- else
- r += val;
- }
- else
- {
- add_space (r);
- r += (ptt == CPP_NOT ? "!" : "? ");
- }
- continue; // We have already gotten the next token.
- }
- }
- // Fall through.
- default:
- {
- // Handle CPP_KEYWORD here to avoid a warning (it is not
- // part of the cpp_ttype enumeration).
- //
- if (tt == CPP_KEYWORD)
- {
- if (ptt == CPP_NAME ||
- ptt == CPP_KEYWORD ||
- ptt == CPP_STRING ||
- ptt == CPP_NUMBER)
- add_space (r);
-
- // Translate 'this'.
- //
- r += (tl == "this" ? obj : tl);
- }
- else
- {
- // All the other operators.
- //
- add_space (r);
- r += cxx_lexer::token_spelling[tt];
- r += ' ';
- }
- break;
- }
- }
-
- //
- // Watch out for the continue statements above if you add any
- // logic here.
- //
-
- ptt = tt;
- tt = l.next (tl);
- }
-
- return r;
-}
-
-// Sections.
-//
-main_section_type main_section;
-
-bool main_section_type::
-compare (object_section const& s) const
-{
- main_section_type const* ms (dynamic_cast<main_section_type const*> (&s));
- return ms != 0 && *this == *ms;
-}
-
-bool user_section::
-compare (object_section const& s) const
-{
- user_section const* us (dynamic_cast<user_section const*> (&s));
- return us != 0 && *this == *us;
-}
-
-user_section* user_section::
-total_base () const
-{
- if (base != 0)
- {
- semantics::class_* poly_root (context::polymorphic (*object));
- if (poly_root != 0 && poly_root != *object)
- return base;
- }
-
- return 0;
-}
-
-size_t user_sections::
-count (unsigned short f) const
-{
- size_t r (0);
-
- semantics::class_* poly_root (context::polymorphic (*object));
- bool poly_derived (poly_root != 0 && poly_root != object);
-
- if (poly_derived && (f & count_total) != 0)
- r = context::polymorphic_base (*object).get<user_sections> (
- "user-sections").count (f);
-
- for (const_iterator i (begin ()); i != end (); ++i)
- {
- // Skip special sections unless we were explicitly asked to count them.
- //
- if (i->special == user_section::special_version &&
- (f & count_special_version) == 0)
- continue;
-
- // Skip non-versioned sections if we are only interested in the
- // versioned ones.
- //
- if ((f & count_versioned_only) != 0 &&
- !context::added (*i->member) && !context::deleted (*i->member))
- continue;
-
- bool ovd (i->base != 0 && poly_derived);
-
- if (i->load != user_section::load_eager)
- {
- if (i->load_empty ())
- {
- if ((f & count_load_empty) != 0)
- {
- if (ovd)
- {
- if ((f & count_override) != 0)
- {
- r++;
- continue; // Count each section only once.
- }
- }
- else
- {
- if ((f & count_new) != 0 || (f & count_total) != 0)
- {
- r++;
- continue;
- }
- }
- }
- }
- else
- {
- if ((f & count_load) != 0)
- {
- if (ovd)
- {
- if ((f & count_override) != 0)
- {
- r++;
- continue;
- }
- }
- else
- {
- if ((f & count_new) != 0 || (f & count_total) != 0)
- {
- r++;
- continue;
- }
- }
- }
- }
- }
-
- if (i->update_empty ())
- {
- if ((f & count_update_empty) != 0)
- {
- if (ovd)
- {
- if ((f & count_override) != 0)
- {
- r++;
- continue;
- }
- }
- else
- {
- if ((f & count_new) != 0 || (f & count_total) != 0)
- {
- r++;
- continue;
- }
- }
- }
- }
- else
- {
- if ((f & count_update) != 0)
- {
- if (ovd)
- {
- if ((f & count_override) != 0)
- {
- r++;
- continue;
- }
- }
- else
- {
- if ((f & count_new) != 0 || (f & count_total) != 0)
- {
- r++;
- continue;
- }
- }
- }
- }
-
- if (i->optimistic ())
- {
- if ((f & count_optimistic) != 0)
- {
- if (ovd)
- {
- if ((f & count_override) != 0)
- {
- r++;
- continue;
- }
- }
- else
- {
- if ((f & count_new) != 0 || (f & count_total) != 0)
- {
- r++;
- continue;
- }
- }
- }
- }
- }
-
- return r;
-}
-
-//
-// context
-//
-
-namespace
-{
- char const* keywords[] =
- {
- "NULL",
- "and",
- "asm",
- "auto",
- "bitand",
- "bitor",
- "bool",
- "break",
- "case",
- "catch",
- "char",
- "class",
- "compl",
- "const",
- "const_cast",
- "continue",
- "default",
- "delete",
- "do",
- "double",
- "dynamic_cast",
- "else",
- "end_eq",
- "enum",
- "explicit",
- "export",
- "extern",
- "false",
- "float",
- "for",
- "friend",
- "goto",
- "if",
- "inline",
- "int",
- "long",
- "mutable",
- "namespace",
- "new",
- "not",
- "not_eq",
- "operator",
- "or",
- "or_eq",
- "private",
- "protected",
- "public",
- "register",
- "reinterpret_cast",
- "return",
- "short",
- "signed",
- "sizeof",
- "static",
- "static_cast",
- "struct",
- "switch",
- "template",
- "this",
- "throw",
- "true",
- "try",
- "typedef",
- "typeid",
- "typename",
- "union",
- "unsigned",
- "using",
- "virtual",
- "void",
- "volatile",
- "wchar_t",
- "while",
- "xor",
- "xor_eq"
- };
-}
-
-unique_ptr<context>
-create_context (ostream& os,
- semantics::unit& unit,
- options const& ops,
- features& f,
- semantics::relational::model* m)
-{
- unique_ptr<context> r;
-
- switch (ops.database ()[0])
- {
- case database::common:
- {
- r.reset (new context (os, unit, ops, f));
- break;
- }
- case database::mssql:
- {
- r.reset (new relational::mssql::context (os, unit, ops, f, m));
- break;
- }
- case database::mysql:
- {
- r.reset (new relational::mysql::context (os, unit, ops, f, m));
- break;
- }
- case database::oracle:
- {
- r.reset (new relational::oracle::context (os, unit, ops, f, m));
- break;
- }
- case database::pgsql:
- {
- r.reset (new relational::pgsql::context (os, unit, ops, f, m));
- break;
- }
- case database::sqlite:
- {
- r.reset (new relational::sqlite::context (os, unit, ops, f, m));
- break;
- }
- }
-
- return r;
-}
-
-context::
-~context ()
-{
- if (current_ == this)
- current_ = 0;
-}
-
-context::
-context (ostream& os_,
- semantics::unit& u,
- options_type const& ops,
- features_type& f,
- data_ptr d)
- : data_ (d ? d : data_ptr (new (shared) data (os_))),
- extra (data_->extra_),
- os (data_->os_),
- unit (u),
- options (ops),
- features (f),
- db (options.database ()[0]),
- in_comment (data_->in_comment_),
- exp (data_->exp_),
- ext (data_->ext_),
- keyword_set (data_->keyword_set_),
- include_regex (data_->include_regex_),
- accessor_regex (data_->accessor_regex_),
- modifier_regex (data_->modifier_regex_),
- embedded_schema (
- ops.generate_schema () &&
- ops.schema_format ()[db].count (schema_format::embedded)),
- separate_schema (
- ops.generate_schema () &&
- ops.schema_format ()[db].count (schema_format::separate)),
- multi_static (ops.multi_database () == multi_database::static_),
- multi_dynamic (ops.multi_database () == multi_database::dynamic),
- force_versioned (false),
- top_object (data_->top_object_),
- cur_object (data_->cur_object_)
-{
- assert (current_ == 0);
- current_ = this;
-
- // Write boolean values as true/false.
- //
- os.setf (ios_base::boolalpha);
-
- // Export control.
- //
- if (!ops.export_symbol ()[db].empty ())
- exp = ops.export_symbol ()[db] + " ";
-
- ext = ops.extern_symbol ()[db];
-
- for (size_t i (0); i < sizeof (keywords) / sizeof (char*); ++i)
- data_->keyword_set_.insert (keywords[i]);
-
- // SQL name regex.
- //
- if (ops.table_regex ().count (db) != 0)
- {
- strings const& s (ops.table_regex ()[db]);
- data_->sql_name_regex_[sql_name_table].assign (s.begin (), s.end ());
- }
-
- if (ops.column_regex ().count (db) != 0)
- {
- strings const& s (ops.column_regex ()[db]);
- data_->sql_name_regex_[sql_name_column].assign (s.begin (), s.end ());
- }
-
- if (ops.index_regex ().count (db) != 0)
- {
- strings const& s (ops.index_regex ()[db]);
- data_->sql_name_regex_[sql_name_index].assign (s.begin (), s.end ());
- }
-
- if (ops.fkey_regex ().count (db) != 0)
- {
- strings const& s (ops.fkey_regex ()[db]);
- data_->sql_name_regex_[sql_name_fkey].assign (s.begin (), s.end ());
- }
-
- if (ops.sequence_regex ().count (db) != 0)
- {
- strings const& s (ops.sequence_regex ()[db]);
- data_->sql_name_regex_[sql_name_sequence].assign (s.begin (), s.end ());
- }
-
- if (ops.statement_regex ().count (db) != 0)
- {
- strings const& s (ops.statement_regex ()[db]);
- data_->sql_name_regex_[sql_name_statement].assign (s.begin (), s.end ());
- }
-
- if (ops.sql_name_regex ().count (db) != 0)
- {
- strings const& s (ops.sql_name_regex ()[db]);
- data_->sql_name_regex_[sql_name_all].assign (s.begin (), s.end ());
- }
-
- // Include regex.
- //
- for (strings::const_iterator i (ops.include_regex ().begin ());
- i != ops.include_regex ().end (); ++i)
- data_->include_regex_.push_back (regexsub (*i));
-
- // Common accessor/modifier naming variants. Try the user-supplied and
- // more specific ones first.
- //
- for (strings::const_iterator i (ops.accessor_regex ().begin ());
- i != ops.accessor_regex ().end (); ++i)
- data_->accessor_regex_.push_back (regexsub (*i));
-
- data_->accessor_regex_.push_back (regexsub ("/(.+)/get_$1/")); // get_foo
- data_->accessor_regex_.push_back (regexsub ("/(.+)/get\\u$1/")); // getFoo
- data_->accessor_regex_.push_back (regexsub ("/(.+)/get$1/")); // getfoo
- data_->accessor_regex_.push_back (regexsub ("/(.+)/$1/")); // foo
-
- for (strings::const_iterator i (ops.modifier_regex ().begin ());
- i != ops.modifier_regex ().end (); ++i)
- data_->modifier_regex_.push_back (regexsub (*i));
-
- data_->modifier_regex_.push_back (regexsub ("/(.+)/set_$1/")); // set_foo
- data_->modifier_regex_.push_back (regexsub ("/(.+)/set\\u$1/")); // setFoo
- data_->modifier_regex_.push_back (regexsub ("/(.+)/set$1/")); // setfoo
- data_->modifier_regex_.push_back (regexsub ("/(.+)/$1/")); // foo
-}
-
-context::
-context ()
- : data_ (current ().data_),
- extra (current ().extra),
- os (current ().os),
- unit (current ().unit),
- options (current ().options),
- features (current ().features),
- db (current ().db),
- in_comment (current ().in_comment),
- exp (current ().exp),
- ext (current ().ext),
- keyword_set (current ().keyword_set),
- include_regex (current ().include_regex),
- accessor_regex (current ().accessor_regex),
- modifier_regex (current ().modifier_regex),
- embedded_schema (current ().embedded_schema),
- separate_schema (current ().separate_schema),
- multi_static (current ().multi_static),
- multi_dynamic (current ().multi_dynamic),
- force_versioned (current ().force_versioned),
- top_object (current ().top_object),
- cur_object (current ().cur_object)
-{
-}
-
-context* context::current_;
-
-semantics::data_member* context::
-id (data_member_path const& mp)
-{
- semantics::data_member* idf (mp.front ());
-
- if (!id (*idf))
- return 0;
-
- // This is for special ids, such as polymorphic-ref, which
- // don't have "id-member" set (and we want to keep it that
- // way since it is not really a full-fledged id).
- //
- if (idf->get<string> ("id").empty ()) // Not a nested id.
- return idf;
-
- const data_member_path& id (
- *id_member (
- dynamic_cast<semantics::class_&> (idf->scope ())));
-
- // Now we need to make sure id is a prefix of mp;
- //
- return mp.sub (id) ? idf : 0;
-}
-
-semantics::data_member* context::
-object_pointer (data_member_path const& mp)
-{
- for (data_member_path::const_reverse_iterator i (mp.rbegin ());
- i != mp.rend (); ++i)
- {
- if (object_pointer (utype (**i)))
- return *i;
- }
-
- return 0;
-}
-
-bool context::
-readonly (data_member_path const& mp, data_member_scope const& ms)
-{
- assert (mp.size () == ms.size ());
-
- data_member_scope::const_reverse_iterator si (ms.rbegin ());
-
- for (data_member_path::const_reverse_iterator pi (mp.rbegin ());
- pi != mp.rend ();
- ++pi, ++si)
- {
- semantics::data_member& m (**pi);
-
- if (m.count ("readonly"))
- return true;
-
- // Check if any of the classes in the inheritance chain for the
- // class containing this member are readonly.
- //
- class_inheritance_chain const& ic (*si);
-
- assert (ic.back () == &m.scope ());
-
- for (class_inheritance_chain::const_reverse_iterator ci (ic.rbegin ());
- ci != ic.rend ();
- ++ci)
- {
- semantics::class_& c (**ci);
-
- if (c.count ("readonly"))
- return true;
- }
- }
-
- return false;
-}
-
-bool context::
-readonly (semantics::data_member& m)
-{
- if (m.count ("readonly"))
- return true;
-
- // Check if the whole class (object or composite value) is marked
- // as readonly.
- //
- if (m.scope ().count ("readonly"))
- return true;
-
- return false;
-}
-
-bool context::
-null (data_member_path const& mp) const
-{
- // Outer members can override the null-ability of the inner ones. So
- // start from the most outer member.
- //
- for (data_member_path::const_iterator i (mp.begin ()); i != mp.end (); ++i)
- {
- if (null (**i))
- return true;
- }
-
- return false;
-}
-
-bool context::
-null (semantics::data_member& m) const
-{
- semantics::names* hint;
- semantics::type& t (utype (m, hint));
-
- if (object_pointer (t))
- {
- // By default pointers can be null.
- //
- if (m.count ("null"))
- return true;
-
- if (!m.count ("not-null"))
- {
- if (t.count ("null"))
- return true;
-
- if (!t.count ("not-null"))
- return true;
- }
-
- return false;
- }
- else
- {
- // Everything else by default is not null.
- //
- if (m.count ("null"))
- return true;
-
- if (!m.count ("not-null"))
- {
- if (t.count ("null"))
- return true;
-
- if (!t.count ("not-null"))
- {
- semantics::type* pt;
-
- // Check if this type is a wrapper.
- //
- if (t.get<bool> ("wrapper"))
- {
- // First see if it is null by default.
- //
- if (t.get<bool> ("wrapper-null-handler") &&
- t.get<bool> ("wrapper-null-default"))
- return true;
-
- // Otherwise, check the wrapped type.
- //
- pt = t.get<semantics::type*> ("wrapper-type");
- hint = t.get<semantics::names*> ("wrapper-hint");
- pt = &utype (*pt, hint);
-
- if (pt->count ("null"))
- return true;
-
- if (pt->count ("not-null"))
- return false;
- }
- else
- pt = &t;
- }
- }
-
- return false;
- }
-}
-
-bool context::
-null (semantics::data_member& m, string const& kp) const
-{
- if (kp.empty ())
- return null (m);
-
- semantics::type& c (utype (m));
- semantics::type& t (utype (m, kp));
-
- if (object_pointer (t))
- {
- if (m.count (kp + "-null"))
- return true;
-
- if (!m.count (kp + "-not-null"))
- {
- if (c.count (kp + "-null"))
- return true;
-
- if (!c.count (kp + "-not-null"))
- {
- if (t.count ("null"))
- return true;
-
- if (!t.count ("not-null"))
- {
- return true;
- }
- }
- }
-
- return false;
- }
- else
- {
- if (m.count (kp + "-null"))
- return true;
-
- if (!m.count (kp + "-not-null"))
- {
- if (c.count (kp + "-null"))
- return true;
-
- if (!c.count (kp + "-not-null"))
- {
- if (t.count ("null"))
- return true;
-
- if (!t.count ("not-null"))
- {
- semantics::type* pt;
-
- // Check if this type is a wrapper.
- //
- if (t.get<bool> ("wrapper"))
- {
- // First see if it is null by default.
- //
- if (t.get<bool> ("wrapper-null-handler") &&
- t.get<bool> ("wrapper-null-default"))
- return true;
-
- // Otherwise, check the wrapped type.
- //
- pt = t.get<semantics::type*> ("wrapper-type");
- pt = &utype (*pt);
-
- if (pt->count ("null"))
- return true;
-
- if (pt->count ("not-null"))
- return false;
- }
- else
- pt = &t;
- }
- }
- }
-
- return false;
- }
-}
-
-size_t context::
-polymorphic_depth (semantics::class_& c)
-{
- if (c.count ("polymorphic-depth"))
- return c.get<size_t> ("polymorphic-depth");
-
- // Calculate our hierarchy depth (number of classes).
- //
- using semantics::class_;
-
- class_* root (polymorphic (c));
- assert (root != 0);
-
- size_t r (1); // One for the root.
-
- for (class_* b (&c); b != root; b = &polymorphic_base (*b))
- ++r;
-
- c.set ("polymorphic-depth", r);
- return r;
-}
-
-context::class_kind_type context::
-class_kind (semantics::class_& c)
-{
- if (object (c))
- return class_object;
- else if (view (c))
- return class_view;
- else if (composite (c))
- return class_composite;
- else
- return class_other;
-}
-
-string context::
-class_name (semantics::class_& c)
-{
- return c.is_a<semantics::class_instantiation> ()
- ? c.get<semantics::names*> ("tree-hint")->name ()
- : c.name ();
-}
-
-string context::
-class_fq_name (semantics::class_& c)
-{
- return c.is_a<semantics::class_instantiation> ()
- ? c.fq_name (c.get<semantics::names*> ("tree-hint"))
- : c.fq_name ();
-}
-
-semantics::scope& context::
-class_scope (semantics::class_& c)
-{
- return c.is_a<semantics::class_instantiation> ()
- ? c.get<semantics::names*> ("tree-hint")->scope ()
- : c.scope ();
-}
-
-semantics::path context::
-class_file (semantics::class_& c)
-{
- // If we have an explicit definition location, use that.
- //
- if (c.count ("definition"))
- return semantics::path (LOCATION_FILE (c.get<location_t> ("definition")));
- //
- // Otherwise, if it is a template instantiation, use the location
- // of the qualifier.
- //
- else if (c.is_a<semantics::class_instantiation> ())
- return semantics::path (LOCATION_FILE (c.get<location_t> ("location")));
- else
- return c.file ();
-}
-
-location_t context::
-class_location (semantics::class_& c)
-{
- return c.count ("definition")
- ? c.get<location_t> ("definition")
- : class_real_location (c);
-}
-
-location_t context::
-class_real_location (semantics::class_& c)
-{
- return c.is_a<semantics::class_instantiation> ()
- ? c.get<location_t> ("location")
- : real_source_location (TYPE_NAME (c.tree_node ()));
-}
-
-string context::
-upcase (string const& s)
-{
- string r;
- string::size_type n (s.size ());
-
- r.reserve (n);
-
- for (string::size_type i (0); i < n; ++i)
- r.push_back (toupper (s[i]));
-
- return r;
-}
-
-void context::
-diverge (streambuf* sb)
-{
- data_->os_stack_.push (data_->os_.rdbuf ());
- data_->os_.rdbuf (sb);
-}
-
-void context::
-restore ()
-{
- data_->os_.rdbuf (data_->os_stack_.top ());
- data_->os_stack_.pop ();
-}
-
-semantics::type& context::
-utype (semantics::type& t)
-{
- if (semantics::qualifier* q = dynamic_cast<semantics::qualifier*> (&t))
- return q->base_type ();
- else
- return t;
-}
-
-semantics::type& context::
-utype (semantics::type& t, semantics::names*& hint)
-{
- if (semantics::qualifier* q = dynamic_cast<semantics::qualifier*> (&t))
- {
- hint = q->qualifies ().hint ();
- return q->base_type ();
- }
- else
- return t;
-}
-
-semantics::type& context::
-utype (semantics::data_member& m,
- semantics::names*& hint,
- string const& kp,
- const custom_cxx_type** translation)
-{
- semantics::type* t (0);
-
- if (kp.empty ())
- {
- t = &m.type ();
-
- if (semantics::qualifier* q = dynamic_cast<semantics::qualifier*> (t))
- {
- hint = q->qualifies ().hint ();
- t = &q->base_type ();
- }
- else
- hint = m.belongs ().hint ();
- }
- else
- {
- if (m.count (kp + "-tree-type"))
- t = indirect_type (m, kp, hint);
- else
- {
- t = &utype (m);
-
- // "See through" wrappers.
- //
- if (semantics::type* wt = wrapper (*t))
- t = indirect_type (utype (*wt), kp, hint);
- else
- t = indirect_type (*t, kp, hint);
- }
- }
-
- // Do we need to map this type?
- //
- // @@ Need to cache the result on the member.
- //
- if (translation != 0)
- *translation = 0;
-
- for (semantics::scope* s (&m.scope ());; s = &s->scope_ ())
- {
- using semantics::namespace_;
-
- if (namespace_* ns = dynamic_cast<namespace_*> (s))
- {
- if (ns->extension ())
- s = &ns->original ();
- }
-
- if (s->count ("custom-cxx-type-map"))
- {
- typedef custom_cxx_type_map map;
-
- map& m (s->get<map> ("custom-cxx-type-map"));
- map::const_iterator i (m.find (t));
-
- if (i != m.end ())
- {
- hint = i->second->as_hint;
- t = i->second->as;
-
- if (translation != 0)
- *translation = i->second;
-
- // Currently we only support one level of mapping, but I am
- // sure someone will want multiple levels.
- //
- break;
- }
- }
-
- if (s->global_scope ())
- break;
- }
-
- return *t;
-}
-
-bool context::
-const_type (semantics::type& t)
-{
- if (semantics::qualifier* q = dynamic_cast<semantics::qualifier*> (&t))
- return q->const_ ();
-
- return false;
-}
-
-string context::
-type_ref_type (semantics::type& t,
- semantics::names* hint,
- bool mc,
- string const& var,
- bool decay)
-{
- using semantics::array;
- string r;
-
- // Note that trailing const syntax is used for a reason (consider
- // t == const foo*). We may also have to decay then top-level array.
- //
- array* a;
- if (decay && (a = dynamic_cast<array*> (&utype (t))) != 0)
- {
- semantics::type& bt (a->base_type ());
- hint = a->contains ().hint ();
-
- if (bt.is_a<array> ())
- {
- // If we need to add/strip const or no name was used in the
- // declaration, then create an array declaration (e.g., for
- // char x[2][3] we will have char const (*x)[3]).
- //
- if (mc != const_type (t) || hint == 0)
- return type_val_type (bt, 0, mc, "(*" + var + ")");
- }
-
- // Array base type is always cvr-unqualified.
- //
- if (mc)
- r = bt.fq_name (hint) + " const";
- else
- r = bt.fq_name (hint);
-
- r += '*';
-
- if (!var.empty ())
- r += ' ' + var;
- }
- else
- {
- if (mc == const_type (t))
- r = t.fq_name (hint);
- else if (mc)
- r = t.fq_name (hint) + " const";
- else
- {
- semantics::type& ut (utype (t, hint));
- r = ut.fq_name (hint);
- }
-
- r += '&';
-
- if (!var.empty ())
- r += ' ' + var;
- }
-
- return r;
-}
-
-string context::
-type_val_type (semantics::type& t,
- semantics::names* hint,
- bool mc,
- string const& var)
-{
- using semantics::array;
- string r;
-
- // Arrays are a complicated case. Firstly, we may need to add/strip const
- // to/from the base type. Secondly, the array dimensions are written after
- // the variable name. All this is further complicated by multiple dimensions.
- // Thanks, Dennis!
- //
- if (array* a = dynamic_cast<array*> (&utype (t)))
- {
- semantics::type& bt (a->base_type ());
-
- // If we don't need to add/strip const and a name was used in the
- // declaration, then use that name.
- //
- if (mc == const_type (t) && hint != 0)
- {
- r = t.fq_name (hint);
-
- if (!var.empty ())
- r += ' ' + var;
- }
- else
- {
- // Otherwise, construct the array declaration.
- //
- string v (var);
- v += '[';
- ostringstream ostr;
- ostr << a->size ();
- v += ostr.str ();
-
- if (a->size () > 0xFFFFFFFF)
- v += "ULL";
- else if (a->size () > 2147483647)
- v += "U";
-
- v += ']';
-
- r = type_val_type (bt, a->contains ().hint (), mc, v);
- }
- }
- else
- {
- if (mc == const_type (t))
- r = t.fq_name (hint);
- else if (mc)
- r = t.fq_name (hint) + " const";
- else
- {
- semantics::type& ut (utype (t, hint));
- r = ut.fq_name (hint);
- }
-
- if (!var.empty ())
- r += ' ' + var;
- }
-
- return r;
-}
-
-void context::
-set_member (semantics::data_member& m,
- const string& obj,
- const string& val,
- const string& db,
- const string& type)
-{
- member_access& ma (m.get<member_access> ("set"));
-
- // If this is a custom expression, output the location of where
- // it came from.
- //
- if (!ma.synthesized)
- os << "// From " << location_string (ma.loc, true) << endl;
-
- if (ma.placeholder ())
- {
- // Cast the database to the concrete type this code is generated
- // for. This way the user is free to use either the concrete or
- // the common.
- //
- string d;
- if (!db.empty ())
- d = "static_cast<" + context::db.string () + "::database&> (" + db + ")";
-
- os << ma.translate (obj, val, d) << ";";
- }
- else
- {
- // If this member is const and we have a synthesized direct access,
- // then cast away constness. Otherwise, we assume that the user-
- // provided expression handles this.
- //
- bool cast (!type.empty () && ma.direct () && const_member (m));
- if (cast)
- os << "const_cast< " << type << "& > (" << endl;
-
- os << ma.translate (obj);
-
- if (cast)
- os << ")";
-
- os << " = " << val << ";";
- }
-}
-
-void context::
-inc_member (semantics::data_member& m,
- const string& obj,
- const string& gobj,
- const string& type)
-{
- member_access& ma (m.get<member_access> ("set"));
-
- // If this is a custom expression, output the location of where
- // it came from.
- //
- if (!ma.synthesized)
- os << "// From " << location_string (ma.loc, true) << endl;
-
- if (ma.placeholder ())
- {
- member_access& gma (m.get<member_access> ("get"));
-
- if (!gma.synthesized)
- os << "// From " << location_string (gma.loc, true) << endl;
-
- os << ma.translate (obj, gma.translate (gobj) + " + 1") << ";";
- }
- else
- {
- // If this member is const and we have a synthesized direct access,
- // then cast away constness. Otherwise, we assume that the user-
- // provided expression handles this.
- //
- os << "++";
-
- bool cast (ma.direct () && const_member (m));
- if (cast)
- os << "const_cast< " << type << "& > (" << endl;
-
- os << ma.translate (obj);
-
- if (cast)
- os << ")";
-
- os << ";";
- }
-}
-
-void context::
-resolve_data_members (data_member_path& r,
- semantics::class_& c,
- const string& name,
- const location& l,
- cxx_string_lexer& lex)
-{
- using semantics::class_;
- using semantics::data_member;
-
- // The name was already verified to be syntactically correct so
- // we don't need to do any extra error checking in this area.
- //
- lex.start (name);
-
- try
- {
- string tl;
- cpp_ttype tt (lex.next (tl));
-
- data_member& m (c.lookup<data_member> (tl, class_::include_hidden));
-
- r.push_back (&m);
-
- if (container (m))
- return;
-
- // Resolve nested members if any.
- //
- for (tt = lex.next (tl); tt == CPP_DOT; tt = lex.next (tl))
- {
- lex.next (tl); // Get CPP_NAME.
-
- data_member& om (*r.back ());
-
- // Check that the outer member is composite and also unwrap it while
- // at it.
- //
- class_* comp (composite_wrapper (utype (om)));
- if (comp == 0)
- {
- error (l) << "data member '" << om.name () << "' is not composite"
- << endl;
- throw operation_failed ();
- }
-
- data_member& nm (
- comp->lookup<data_member> (tl, class_::include_hidden));
-
- r.push_back (&nm);
-
- if (container (nm))
- return;
- }
- }
- catch (semantics::unresolved const& e)
- {
- if (e.type_mismatch)
- error (l) << "name '" << e.name << "' does not refer to a data member"
- << endl;
- else
- error (l) << "unable to resolve data member '" << e.name << endl;
-
- throw operation_failed ();
- }
- catch (semantics::ambiguous const& e)
- {
- error (l) << "data member name '" << e.first.name () << "' is ambiguous"
- << endl;
-
- info (e.first.named ().location ()) << "could resolve to " <<
- "this data member" << endl;
-
- info (e.second.named ().location ()) << "or could resolve " <<
- "to this data member" << endl;
-
- throw operation_failed ();
- }
-}
-
-bool context::
-composite_ (semantics::class_& c)
-{
- bool r (c.count ("value") && !c.count ("simple") && !c.count ("container"));
- c.set ("composite-value", r);
- return r;
-}
-
-// context::table_prefix
-//
-context::table_prefix::
-table_prefix (semantics::class_& c)
- : level (1)
-{
- context& ctx (context::current ());
-
- ns_schema = ctx.schema (class_scope (c));
- ns_prefix = ctx.table_name_prefix (class_scope (c));
- prefix = ctx.table_name (c, &derived);
- prefix += "_";
-}
-
-void context::table_prefix::
-append (semantics::data_member& m)
-{
- assert (level > 0);
-
- context& ctx (context::current ());
-
- // If a custom table prefix was specified, then ignore the top-level
- // table prefix (this corresponds to a container directly inside an
- // object) but keep the schema unless the alternative schema is fully
- // qualified.
- //
- if (m.count ("table"))
- {
- qname p, n (m.get<qname> ("table"));
-
- if (n.fully_qualified ())
- p = n.qualifier ();
- else
- {
- if (n.qualified ())
- {
- p = ns_schema;
- p.append (n.qualifier ());
- }
- else
- p = prefix.qualifier ();
- }
-
- if (level == 1)
- {
- p.append (ns_prefix);
- derived = false;
- }
- else
- p.append (prefix.uname ());
-
- p += n.uname ();
- prefix.swap (p);
- }
- // Otherwise use the member name and add an underscore unless it is
- // already there.
- //
- else
- {
- string name (ctx.public_name_db (m));
- size_t n (name.size ());
-
- prefix += name;
-
- if (n != 0 && name[n - 1] != '_')
- prefix += "_";
-
- derived = true;
- }
-
- level++;
-}
-
-qname context::
-schema (semantics::scope& s) const
-{
- if (s.count ("qualified-schema"))
- return s.get<qname> ("qualified-schema");
-
- qname r;
-
- for (semantics::scope* ps (&s);; ps = &ps->scope_ ())
- {
- using semantics::namespace_;
-
- namespace_* ns (dynamic_cast<namespace_*> (ps));
-
- if (ns == 0)
- continue; // Some other scope.
-
- if (ns->extension ())
- ns = &ns->original ();
-
- bool sf (ns->count ("schema"));
- bool tf (ns->count ("table"));
-
- if (tf)
- {
- qname n (ns->get<qname> ("table"));
- tf = n.qualified ();
-
- // If we have both schema and qualified table prefix, see which
- // takes precedence based on order.
- //
-
- if (tf && sf)
- {
- if (ns->get<location_t> ("table-location") >
- ns->get<location_t> ("schema-location"))
- sf = false;
- else
- tf = false;
- }
- }
-
- if (sf || tf)
- {
- qname n (
- sf
- ? ns->get<qname> ("schema")
- : ns->get<qname> ("table").qualifier ());
- n.append (r);
- n.swap (r);
- }
-
- if (r.fully_qualified () || ns->global_scope ())
- break;
- }
-
- // If we are still not fully qualified, add the schema that was
- // specified on the command line.
- //
- if (!r.fully_qualified () && options.schema ().count (db) != 0)
- {
- qname n (options.schema ()[db]);
- n.append (r);
- n.swap (r);
- }
-
- s.set ("qualified-schema", r);
- return r;
-}
-
-string context::
-table_name_prefix (semantics::scope& s) const
-{
- if (s.count ("table-prefix"))
- return s.get<string> ("table-prefix");
-
- string r;
-
- for (semantics::scope* ps (&s);; ps = &ps->scope_ ())
- {
- using semantics::namespace_;
-
- namespace_* ns (dynamic_cast<namespace_*> (ps));
-
- if (ns == 0)
- continue; // Some other scope.
-
- if (ns->extension ())
- ns = &ns->original ();
-
- if (ns->count ("table"))
- {
- qname n (ns->get<qname> ("table"));
- r = n.uname () + r;
- }
-
- if (ns->global_scope ())
- break;
- }
-
- // Add the prefix that was specified on the command line.
- //
- if (options.table_prefix ().count (db) != 0)
- r = options.table_prefix ()[db] + r;
-
- s.set ("table-prefix", r);
- return r;
-}
-
-qname context::
-table_name (semantics::class_& c, bool* pd) const
-{
- if (c.count ("qualified-table"))
- return c.get<qname> ("qualified-table");
-
- qname r;
- bool sf (c.count ("schema"));
- bool derived;
-
- if (c.count ("table"))
- {
- r = c.get<qname> ("table");
-
- if (sf)
- {
- // If we have both schema and qualified table, see which takes
- // precedence based on order. If the table is unqualifed, then
- // add the schema.
- //
- sf = !r.qualified () ||
- c.get<location_t> ("table-location") <
- c.get<location_t> ("schema-location");
- }
-
- derived = false;
- }
- else
- {
- r = class_name (c);
- derived = true;
- }
-
- if (sf)
- {
- qname n (c.get<qname> ("schema"));
- n.append (r.uname ());
- n.swap (r);
- }
-
- // Unless we are fully qualified, add any schemas that were
- // specified on the namespaces and/or with the command line
- // option.
- //
- if (!r.fully_qualified ())
- {
- qname n (schema (class_scope (c)));
- n.append (r);
- n.swap (r);
- }
-
- // Add the table prefix if any.
- //
- r.uname () = table_name_prefix (class_scope (c)) + r.uname ();
-
- if (derived)
- r.uname () = transform_name (r.uname (), sql_name_table);
-
- c.set ("qualified-table", r);
-
- if (pd != 0)
- *pd = derived;
-
- return r;
-}
-
-qname context::
-table_name (semantics::class_& obj, data_member_path const& mp) const
-{
- table_prefix tp (obj);
-
- if (mp.size () == 1)
- {
- // Container directly in the object.
- //
- return table_name (*mp.back (), tp);
- }
- else
- {
- data_member_path::const_iterator i (mp.begin ());
-
- // The last member is the container.
- //
- for (data_member_path::const_iterator e (mp.end () - 1); i != e; ++i)
- tp.append (**i);
-
- return table_name (**i, tp);
- }
-}
-
-// The table prefix passed as the second argument must include the table
-// prefix specified on namespaces and with the --table-prefix option.
-//
-qname context::
-table_name (semantics::data_member& m, table_prefix const& p) const
-{
- assert (p.level > 0);
- qname r;
- string rn;
- bool derived; // Any of the components in the table name is derived.
-
- // If a custom table name was specified, then ignore the top-level
- // table prefix (this corresponds to a container directly inside an
- // object). If the container table is unqualifed, then we use the
- // object schema. If it is fully qualified, then we use that name.
- // Finally, if it is qualified but not fully qualifed, then we
- // append the object's namespace schema.
- //
- if (m.count ("table"))
- {
- qname n (m.get<qname> ("table"));
-
- if (n.fully_qualified ())
- r = n.qualifier ();
- else
- {
- if (n.qualified ())
- {
- r = p.ns_schema;
- r.append (n.qualifier ());
- }
- else
- r = p.prefix.qualifier ();
- }
-
- if (p.level == 1)
- {
- rn = p.ns_prefix;
- derived = false;
- }
- else
- {
- rn = p.prefix.uname ();
- derived = p.derived;
- }
-
- rn += n.uname ();
- }
- else
- {
- r = p.prefix.qualifier ();
- rn = p.prefix.uname () + public_name_db (m);
- derived = true;
- }
-
- if (derived)
- r.append (transform_name (rn, sql_name_table));
- else
- r.append (rn);
-
- return r;
-}
-
-// context::column_prefix
-//
-context::column_prefix::
-column_prefix (data_member_path const& mp, bool l)
- : derived (false), underscore (false)
-{
- if (mp.size () < (l ? 1 : 2))
- return;
-
- for (data_member_path::const_iterator i (mp.begin ()),
- e (mp.end () - (l ? 0 : 1)); i != e; ++i)
- append (**i);
-}
-
-void context::column_prefix::
-append (semantics::data_member& m, string const& kp, string const& dn)
-{
- bool d;
- context& ctx (context::current ());
-
- if (kp.empty ())
- prefix += ctx.column_name (m, d);
- else
- prefix += ctx.column_name (m, kp, dn, d);
-
- // If the user provided the column prefix, then use it verbatime.
- // Otherwise, append the underscore, unless it is already there.
- //
- underscore = false;
- if (d)
- {
- size_t n (prefix.size ());
-
- if (n != 0 && prefix[n - 1] != '_')
- {
- prefix += '_';
- underscore = true;
- }
- }
-
- derived = derived || d;
-}
-
-string context::
-column_name (semantics::data_member& m, bool& derived) const
-{
- derived = !m.count ("column");
- return derived
- ? public_name_db (m)
- : m.get<table_column> ("column").column;
-}
-
-string context::
-column_name (semantics::data_member& m, column_prefix const& cp) const
-{
- bool d;
- const string& cn (column_name (m, d));
- string n (cp.prefix);
-
- if (cn.empty () && cp.underscore)
- n.resize (n.size () - 1); // Strip underscore that was auto added.
-
- n += cn;
-
- // If any component is derived, then run it through the SQL name regex.
- //
- if (d || cp.derived)
- n = transform_name (n, sql_name_column);
-
- return n;
-}
-
-string context::
-column_name (semantics::data_member& m,
- string const& p,
- string const& d,
- bool& derived) const
-{
- if (p.empty () && d.empty ())
- return column_name (m, derived);
-
- // A container column name can be specified for the member or for the
- // container type.
- //
- string key (p + "-column");
- derived = false;
-
- if (m.count (key))
- return m.get<string> (key);
- else
- {
- semantics::type& t (utype (m));
-
- if (t.count (key))
- return t.get<string> (key);
- }
-
- derived = true;
- return d;
-}
-
-string context::
-column_name (semantics::data_member& m,
- string const& kp,
- string const& dn,
- column_prefix const& cp) const
-{
- bool d;
- const string& cn (column_name (m, kp, dn, d));
- string n (cp.prefix);
-
- if (cn.empty () && cp.underscore)
- n.resize (n.size () - 1); // Strip underscore that was auto-added.
-
- n += cn;
-
- // If any component is derived, the run it through the SQL name regex.
- //
- if (d || cp.derived)
- n = transform_name (n, sql_name_column);
-
- return n;
-}
-
-string context::
-column_name (data_member_path const& mp) const
-{
- return column_name (*mp.back (), column_prefix (mp));
-}
-
-string context::
-column_type (const data_member_path& mp, string const& kp, bool id)
-{
- if (kp.empty ())
- {
- // Return the id type if this member is or is a part of an object id
- // or pointer to object.
- //
- return mp.back ()->get<string> (
- id || context::id (mp) || object_pointer (mp)
- ? "column-id-type"
- : "column-type");
- }
- else
- return indirect_value<string> (*mp.back (), kp + "-column-type");
-}
-
-string context::
-column_type (semantics::data_member& m, string const& kp)
-{
- return kp.empty ()
- ? m.get<string> ("column-type")
- : indirect_value<string> (m, kp + "-column-type");
-}
-
-string context::
-column_options (semantics::data_member& m)
-{
- // Accumulate options from both type and member.
- //
- semantics::type* t (&utype (m));
-
- if (semantics::class_* p = object_pointer (*t))
- t = &utype (*id_member (*p));
-
- if (semantics::type* w = wrapper (*t))
- t = w;
-
- string r;
-
- if (t->count ("options"))
- {
- strings const& o (t->get<strings> ("options"));
-
- for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
- {
- if (i->empty ())
- r.clear ();
- else
- {
- if (!r.empty ())
- r += ' ';
-
- r += *i;
- }
- }
- }
-
- if (m.count ("options"))
- {
- strings const& o (m.get<strings> ("options"));
-
- for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
- {
- if (i->empty ())
- r.clear ();
- else
- {
- if (!r.empty ())
- r += ' ';
-
- r += *i;
- }
- }
- }
-
- return r;
-}
-
-string context::
-column_options (semantics::data_member& m, string const& kp)
-{
- if (kp.empty ())
- return column_options (m);
-
- string k (kp + "-options");
-
- // Accumulate options from type, container, and member.
- //
- semantics::type& c (utype (m));
- semantics::type* t (&utype (m, kp));
-
- if (semantics::class_* p = object_pointer (*t))
- t = &utype (*id_member (*p));
-
- if (semantics::type* w = wrapper (*t))
- t = w;
-
- string r;
-
- if (t->count ("options"))
- {
- strings const& o (t->get<strings> ("options"));
-
- for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
- {
- if (i->empty ())
- r.clear ();
- else
- {
- if (!r.empty ())
- r += ' ';
-
- r += *i;
- }
- }
- }
-
- if (c.count (k))
- {
- strings const& o (c.get<strings> (k));
-
- for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
- {
- if (i->empty ())
- r.clear ();
- else
- {
- if (!r.empty ())
- r += ' ';
-
- r += *i;
- }
- }
- }
-
- if (m.count (k))
- {
- strings const& o (m.get<strings> (k));
-
- for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
- {
- if (i->empty ())
- r.clear ();
- else
- {
- if (!r.empty ())
- r += ' ';
-
- r += *i;
- }
- }
- }
-
- return r;
-}
-
-context::type_map_type::const_iterator context::type_map_type::
-find (semantics::type& t, semantics::names* hint)
-{
- const_iterator e (end ()), i (e);
-
- // First check the hinted name. This allows us to handle things like
- // size_t which is nice to map to the same type irrespective of the
- // actual type. Since this type can be an alias for the one we are
- // interested in, go into nested hints.
- //
- for (; hint != 0 && i == e; hint = hint->hint ())
- i = base::find (t.fq_name (hint));
-
- // If the hinted name didn't work, try the primary name (e.g.,
- // ::std::string) instead of a user typedef (e.g., my_string).
- //
- if (i == e)
- i = base::find (t.fq_name ());
-
- return i;
-}
-
-string context::
-database_type_impl (semantics::type& t,
- semantics::names* hint,
- bool id,
- bool* null)
-{
- using semantics::enum_;
-
- // By default map an enum as its underlying type.
- //
- if (enum_* e = dynamic_cast<enum_*> (&t))
- return database_type_impl (
- e->underlying_type (), e->underlying_type_hint (), id, null);
-
- // Built-in type mapping.
- //
- type_map_type::const_iterator i (data_->type_map_.find (t, hint));
- if (i != data_->type_map_.end ())
- {
- if (null != 0)
- *null = i->second.null;
- return id ? i->second.id_type : i->second.type;
- }
-
- return string ();
-}
-
-static string
-public_name_impl (semantics::data_member& m)
-{
- string s (m.name ());
- size_t n (s.size ());
-
- // Do basic processing: remove trailing and leading underscores
- // as well as the 'm_' prefix.
- //
- // @@ What if the resulting names conflict?
- //
- size_t b (0), e (n - 1);
-
- if (n > 2 && s[0] == 'm' && s[1] == '_')
- b += 2;
-
- for (; b <= e && s[b] == '_'; b++) ;
- for (; e >= b && s[e] == '_'; e--) ;
-
- return b > e ? s : string (s, b, e - b + 1);
-}
-
-string context::
-public_name_db (semantics::data_member& m) const
-{
- return public_name_impl (m);
-}
-
-string context::
-compose_name (string const& prefix, string const& name)
-{
- string r (prefix);
- size_t n (r.size ());
-
- // Add an underscore unless one is already in the prefix or
- // the name is empty. Similarly, remove it if it is there but
- // the name is empty.
- //
- if (n != 0)
- {
- if (r[n - 1] != '_')
- {
- if (!name.empty ())
- r += '_';
- }
- else
- {
- if (name.empty ())
- r.resize (n - 1);
- }
- }
-
- r += name;
- return r;
-}
-
-string context::
-transform_name (string const& name, sql_name_type type) const
-{
- string r;
-
- if (!data_->sql_name_regex_[type].empty () ||
- !data_->sql_name_regex_[sql_name_all].empty ())
- {
- bool t (options.sql_name_regex_trace ());
-
- if (t)
- cerr << "name: '" << name << "'" << endl;
-
- bool found (false);
-
- // First try the type-specific transformations, if that didn't work,
- // try common transformations.
- //
- for (unsigned short j (0); !found && j < 2; ++j)
- {
- regex_mapping const& rm = data_->sql_name_regex_[
- j == 0 ? type : sql_name_all];
-
- for (regex_mapping::const_iterator i (rm.begin ()); i != rm.end (); ++i)
- {
- if (t)
- cerr << "try: '" << i->regex () << "' : ";
-
- if (i->match (name))
- {
- r = i->replace (name);
- found = true;
-
- if (t)
- cerr << "'" << r << "' : ";
- }
-
- if (t)
- cerr << (found ? '+' : '-') << endl;
-
- if (found)
- break;
- }
- }
-
- if (!found)
- r = name;
- }
- else
- r = name;
-
- if (options.sql_name_case ().count (db) != 0)
- {
- switch (options.sql_name_case ()[db])
- {
- case name_case::upper:
- {
- r = data_->sql_name_upper_.replace (r);
- break;
- }
- case name_case::lower:
- {
- r = data_->sql_name_lower_.replace (r);
- break;
- }
- }
- }
-
- return r;
-}
-
-string context::
-public_name (semantics::data_member& m, bool e) const
-{
- return e ? escape (public_name_impl (m)) : public_name_impl (m);
-}
-
-string context::
-flat_name (string const& fq)
-{
- string r;
- r.reserve (fq.size ());
-
- for (string::size_type i (0), n (fq.size ()); i < n; ++i)
- {
- char c (fq[i]);
-
- if (c == ':')
- {
- if (!r.empty ())
- r += '_';
- ++i; // Skip the second ':'.
- }
- else
- r += c;
- }
-
- return r;
-}
-
-string context::
-escape (string const& name) const
-{
- typedef string::size_type size;
-
- string r;
- size n (name.size ());
-
- // In most common cases we will have that many characters.
- //
- r.reserve (n);
-
- for (size i (0); i < n; ++i)
- {
- char c (name[i]);
-
- if (i == 0)
- {
- if (!((c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') ||
- c == '_'))
- r = (c >= '0' && c <= '9') ? "cxx_" : "cxx";
- }
-
- if (!((c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') ||
- (c >= '0' && c <= '9') ||
- c == '_'))
- r += '_';
- else
- r += c;
- }
-
- if (r.empty ())
- r = "cxx";
-
- // Custom reserved words.
- //
- /*
- reserved_name_map_type::const_iterator i (reserved_name_map.find (r));
-
- if (i != reserved_name_map.end ())
- {
- if (!i->second.empty ())
- return i->second;
- else
- r += L'_';
- }
- */
-
- // Keywords
- //
- if (keyword_set.find (r) != keyword_set.end ())
- {
- r += '_';
-
- // Re-run custom words.
- //
- /*
- i = reserved_name_map.find (r);
-
- if (i != reserved_name_map.end ())
- {
- if (!i->second.empty ())
- return i->second;
- else
- r += L'_';
- }
- */
- }
-
- return r;
-}
-
-string context::
-make_guard (string const& s) const
-{
- // Split words, e.g., "FooBar" to "Foo_Bar" and convert everything
- // to upper case.
- //
- string r;
- for (string::size_type i (0), n (s.size ()); i < n - 1; ++i)
- {
- char c1 (s[i]);
- char c2 (s[i + 1]);
-
- r += toupper (c1);
-
- if (isalpha (c1) && isalpha (c2) && islower (c1) && isupper (c2))
- r += "_";
- }
- r += toupper (s[s.size () - 1]);
-
- return escape (r);
-}
-
-static string
-charlit (unsigned int u)
-{
- string r ("\\x");
- bool lead (true);
-
- for (short i (7); i >= 0; --i)
- {
- unsigned int x ((u >> (i * 4)) & 0x0F);
-
- if (lead)
- {
- if (x == 0)
- continue;
-
- lead = false;
- }
-
- r += static_cast<char> (x < 10 ? ('0' + x) : ('A' + x - 10));
- }
-
- return r;
-}
-
-static string
-strlit_ascii (string const& str)
-{
- string r;
- string::size_type n (str.size ());
-
- // In most common cases we will have that many chars.
- //
- r.reserve (n + 2);
-
- r += '"';
-
- bool escape (false);
-
- for (string::size_type i (0); i < n; ++i)
- {
- unsigned int u (static_cast<unsigned int> (str[i]));
-
- // [128 - ] - unrepresentable
- // 127 - \x7F
- // [32 - 126] - as is
- // [0 - 31] - \X or \xXX
- //
-
- if (u < 32 || u == 127)
- {
- switch (u)
- {
- case '\n':
- {
- r += "\\n";
- break;
- }
- case '\t':
- {
- r += "\\t";
- break;
- }
- case '\v':
- {
- r += "\\v";
- break;
- }
- case '\b':
- {
- r += "\\b";
- break;
- }
- case '\r':
- {
- r += "\\r";
- break;
- }
- case '\f':
- {
- r += "\\f";
- break;
- }
- case '\a':
- {
- r += "\\a";
- break;
- }
- default:
- {
- r += charlit (u);
- escape = true;
- break;
- }
- }
- }
- else if (u < 127)
- {
- if (escape)
- {
- // Close and open the string so there are no clashes.
- //
- r += '"';
- r += '"';
-
- escape = false;
- }
-
- switch (u)
- {
- case '"':
- {
- r += "\\\"";
- break;
- }
- case '\\':
- {
- r += "\\\\";
- break;
- }
- default:
- {
- r += static_cast<char> (u);
- break;
- }
- }
- }
- else
- {
- // @@ Unrepresentable character.
- //
- r += '?';
- }
- }
-
- r += '"';
-
- return r;
-}
-
-string context::
-strlit (string const& str)
-{
- return strlit_ascii (str);
-}
-
-void context::
-inst_header (bool decl, bool omit_exp)
-{
- if (decl && !ext.empty ())
- os << ext << " ";
-
- os << "template struct";
-
- if (!omit_exp && !exp.empty ())
- {
- // If we are generating an explicit instantiation directive rather
- // than the extern template declaration, then omit the export symbol
- // if we already have it in the header (i.e., extern symbol specified
- // and defined). If we don't do that, then we get GCC warnings saying
- // that the second set of visibility attributes is ignored.
- //
- if (!decl && !ext.empty ())
- os << endl
- << "#ifndef " << ext << endl
- << options.export_symbol ()[db] << endl
- << "#endif" << endl;
- else
- os << " " << exp;
- }
- else
- os << " ";
-}
-
-namespace
-{
- struct column_count_impl: object_members_base
- {
- column_count_impl (object_section* section = 0)
- : object_members_base (false, section)
- {
- }
-
- virtual void
- traverse_pointer (semantics::data_member& m, semantics::class_& c)
- {
- // Object pointers in views require special treatment.
- //
- if (view_member (m))
- {
- using semantics::class_;
-
- column_count_type cc;
-
- if (class_* root = polymorphic (c))
- {
- // For a polymorphic class we are going to load all the members
- // from all the bases (i.e., equivalent to the first statement
- // in the list of SELECT statements generated for the object).
- // So our count should be the same as the first value in the
- // generated column_counts array.
- //
- for (class_* b (&c);; b = &polymorphic_base (*b))
- {
- column_count_type const& ccb (column_count (*b, section_));
-
- cc.total += ccb.total - (b != root ? ccb.id : 0);
- cc.separate_load += ccb.separate_load;
- cc.soft += ccb.soft;
-
- if (b == root)
- break;
- }
- }
- else
- cc = column_count (c, section_);
-
- c_.total += cc.total - cc.separate_load;
-
- if (added (member_path_) != 0 || deleted (member_path_) != 0)
- c_.soft += cc.total;
- else
- c_.soft += cc.soft;
- }
- else
- {
- size_t t (c_.total);
-
- object_members_base::traverse_pointer (m, c);
-
- if (context::inverse (m))
- {
- size_t n (c_.total - t);
-
- c_.inverse += n;
-
- if (separate_update (member_path_))
- c_.separate_update -= n;
- }
- }
- }
-
- virtual void
- traverse_simple (semantics::data_member& m)
- {
- c_.total++;
-
- bool ro (context::readonly (member_path_, member_scope_));
-
- if (id ())
- c_.id++;
- else if (ro)
- c_.readonly++;
- else if (context::version (m))
- c_.optimistic_managed++;
-
- // For now discriminator can only be a simple value.
- //
- if (discriminator (m))
- c_.discriminator++;
-
- {
- unsigned long long av (added (member_path_));
- unsigned long long dv (deleted (member_path_));
-
- // If the addition/deletion version is the same as the section's,
- // then don't count.
- //
- if (user_section* s = dynamic_cast<user_section*> (section_))
- {
- if (av == added (*s->member))
- av = 0;
-
- if (dv == deleted (*s->member))
- dv = 0;
- }
-
- if (av != 0)
- c_.added++;
-
- if (dv != 0)
- c_.deleted++;
-
- if (av != 0 || dv != 0)
- c_.soft++;
- }
-
- if (separate_load (member_path_))
- c_.separate_load++;
-
- if (separate_update (member_path_) && !ro)
- c_.separate_update++;
- }
-
- context::column_count_type c_;
- };
-}
-
-context::column_count_type context::
-column_count (semantics::class_& c, object_section* s)
-{
- if (s == 0)
- {
- // Whole class.
- //
- if (!c.count ("column-count"))
- {
- column_count_impl t;
- t.traverse (c);
- c.set ("column-count", t.c_);
- }
-
- return c.get<column_count_type> ("column-count");
- }
- else
- {
- column_count_impl t (s);
- t.traverse (c);
- return t.c_;
- }
-}
-
-namespace
-{
- struct has_a_impl: object_members_base
- {
- has_a_impl (unsigned short flags, object_section* s)
- : object_members_base ((flags & context::include_base) != 0, s),
- r_ (0),
- flags_ (flags)
- {
- }
-
- size_t
- result () const
- {
- return r_;
- }
-
- virtual bool
- section_test (data_member_path const& mp)
- {
- object_section& s (section (mp));
-
- // Include eager loaded members into the main section if requested.
- //
- return section_ == 0 ||
- *section_ == s ||
- ((flags_ & include_eager_load) != 0 &&
- *section_ == main_section &&
- !s.separate_load ());
- }
-
- virtual void
- traverse_pointer (semantics::data_member& m, semantics::class_&)
- {
- // Ignore polymorphic id references; they are represented as
- // pointers but are normally handled in a special way.
- //
- if (m.count ("polymorphic-ref"))
- return;
-
- // Ignore added/deleted members if so requested.
- //
- if (check_soft ())
- return;
-
- if (context::is_a (member_path_, member_scope_, flags_))
- r_++;
-
- // No need to go inside.
- }
-
- virtual void
- traverse_simple (semantics::data_member&)
- {
- // Ignore added/deleted members if so requested.
- //
- if (check_soft ())
- return;
-
- if (context::is_a (member_path_, member_scope_, flags_))
- r_++;
- }
-
- virtual void
- traverse_container (semantics::data_member& m, semantics::type&)
- {
- // Ignore added/deleted members if so requested.
- //
- if (check_soft ())
- return;
-
- // Ignore versioned containers if so requested.
- //
- if ((flags_ & exclude_versioned) != 0 && versioned (m))
- return;
-
- // We don't cross the container boundaries (separate table).
- //
- unsigned short f (flags_ & (context::test_container |
- context::test_straight_container |
- context::test_inverse_container |
- context::test_readonly_container |
- context::test_readwrite_container |
- context::test_smart_container));
-
- if (context::is_a (member_path_,
- member_scope_,
- f,
- context::container_vt (m),
- "value"))
- r_++;
- }
-
- virtual void
- traverse_object (semantics::class_& c)
- {
- if ((flags_ & context::exclude_base) == 0)
- inherits (c);
-
- names (c);
- }
-
- private:
- bool
- check_soft ()
- {
- if ((flags_ & exclude_added) != 0 || (flags_ & exclude_deleted) != 0)
- {
- unsigned long long av (added (member_path_));
- unsigned long long dv (deleted (member_path_));
-
- // If the addition/deletion version is the same as the section's,
- // then don't exclude.
- //
- if (user_section* s = dynamic_cast<user_section*> (section_))
- {
- if (av == added (*s->member))
- av = 0;
-
- if (dv == deleted (*s->member))
- dv = 0;
- }
-
- if ((av != 0 && (flags_ & exclude_added) != 0) ||
- (dv != 0 && (flags_ & exclude_deleted) != 0))
- return true;
- }
-
- return false;
- }
-
- private:
- size_t r_;
- unsigned short flags_;
- };
-}
-
-bool context::
-is_a (data_member_path const& mp,
- data_member_scope const& ms,
- unsigned short f,
- semantics::type& t,
- string const& kp)
-{
- bool r (false);
-
- semantics::data_member& m (*mp.back ());
-
- if (f & test_pointer)
- r = r || object_pointer (t);
-
- if (f & test_eager_pointer)
- r = r || (object_pointer (t) && !lazy_pointer (t));
-
- if (f & test_lazy_pointer)
- r = r || (object_pointer (t) && lazy_pointer (t));
-
- semantics::type* c;
- if ((f & (test_container |
- test_straight_container |
- test_inverse_container |
- test_readonly_container |
- test_readwrite_container |
- test_smart_container)) != 0 &&
- (c = container (m)) != 0)
- {
- if (f & test_container)
- r = r || true;
-
- if (f & test_straight_container)
- r = r || !inverse (m, kp);
-
- if (f & test_inverse_container)
- r = r || inverse (m, kp);
-
- if (f & test_readonly_container)
- r = r || readonly (mp, ms);
-
- if (f & test_readwrite_container)
- r = r || (!inverse (m, kp) && !readonly (mp, ms));
-
- if (f & test_smart_container)
- r = r || (!inverse (m, kp) && !unordered (m) && container_smart (*c));
- }
-
- return r;
-}
-
-size_t context::
-has_a (semantics::class_& c, unsigned short flags, object_section* s)
-{
- has_a_impl impl (flags, s);
- impl.dispatch (c);
- return impl.result ();
-}
-
-string context::
-process_include_path (string const& ip, bool prefix, char open)
-{
- bool t (options.include_regex_trace ());
- string p (prefix ? options.include_prefix () : string ());
-
- if (!p.empty () && p[p.size () - 1] != '/')
- p.append ("/");
-
- string path (p + ip), r;
-
- if (t)
- cerr << "include: '" << path << "'" << endl;
-
- bool found (false);
-
- for (regex_mapping::const_iterator i (include_regex.begin ());
- i != include_regex.end (); ++i)
- {
- if (t)
- cerr << "try: '" << i->regex () << "' : ";
-
- if (i->match (path))
- {
- r = i->replace (path);
- found = true;
-
- if (t)
- cerr << "'" << r << "' : ";
- }
-
- if (t)
- cerr << (found ? '+' : '-') << endl;
-
- if (found)
- break;
- }
-
- if (!found)
- r = path;
-
- // Add brackets or quotes unless the path already has them.
- //
- if (!r.empty () && r[0] != '"' && r[0] != '<')
- {
- bool b (open == '<' || (open == '\0' && options.include_with_brackets ()));
- char op (b ? '<' : '"'), cl (b ? '>' : '"');
- r = op + r + cl;
- }
-
- return r;
-}
diff --git a/odb/context.hxx b/odb/context.hxx
deleted file mode 100644
index da975f3..0000000
--- a/odb/context.hxx
+++ /dev/null
@@ -1,1933 +0,0 @@
-// file : odb/context.hxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#ifndef ODB_CONTEXT_HXX
-#define ODB_CONTEXT_HXX
-
-#include <odb/gcc-fwd.hxx>
-
-#include <map>
-#include <set>
-#include <list>
-#include <stack>
-#include <vector>
-#include <string>
-#include <memory> // std::unique_ptr
-#include <ostream>
-#include <cstddef> // std::size_t
-#include <iostream>
-
-#include <cutl/re.hxx>
-#include <cutl/shared-ptr.hxx>
-
-#include <odb/options.hxx>
-#include <odb/features.hxx>
-#include <odb/location.hxx>
-#include <odb/cxx-token.hxx>
-#include <odb/semantics.hxx>
-#include <odb/semantics/relational/name.hxx>
-#include <odb/semantics/relational/model.hxx>
-#include <odb/traversal.hxx>
-
-using std::endl;
-using std::cerr;
-
-// Regex.
-//
-using cutl::re::regex;
-using cutl::re::regexsub;
-typedef cutl::re::format regex_format;
-
-typedef std::vector<regexsub> regex_mapping;
-
-// Forward-declarations.
-//
-class cxx_string_lexer;
-
-// Generic exception thrown to indicate a failure when diagnostics
-// has already been issued (to stderr).
-//
-class operation_failed {};
-
-// Keep this enum synchronized with the one in libodb/odb/pointer-traits.hxx.
-//
-enum pointer_kind
-{
- pk_raw,
- pk_unique,
- pk_shared,
- pk_weak
-};
-
-// Keep this enum synchronized with the one in libodb/odb/container-traits.hxx.
-//
-enum container_kind
-{
- ck_ordered,
- ck_set,
- ck_multiset,
- ck_map,
- ck_multimap
-};
-
-// The same as class_kind in libodb/odb/traits.hxx.
-//
-enum class_kind
-{
- class_object,
- class_view,
- class_composite,
- class_other
-};
-
-// Data member path.
-//
-// If it is a direct member of an object, then we will have just
-// one member. However, if this is a member inside a composite
-// value, then we will have a "path" constructed out of members
-// that lead all the way from the object member to the innermost
-// composite value member.
-//
-struct data_member_path: std::vector<semantics::data_member*>
-{
- data_member_path () {}
-
- explicit
- data_member_path (semantics::data_member& m) {push_back (&m);}
-
- // Return true if this is a sub-path of (or equal to) the
- // specified path.
- //
- bool
- sub (const data_member_path& p) const
- {
- size_t n (p.size ());
-
- if (n > size ())
- return false;
-
- for (size_t i (0); i != n; ++i)
- if ((*this)[i] != p[i])
- return false;
-
- return true;
- }
-};
-
-// Class inheritance chain, from the most derived to base.
-//
-typedef std::vector<semantics::class_*> class_inheritance_chain;
-
-// A list of inheritance chains for a data member in an object.
-// The first entry in this list would correspond to the object.
-// All subsequent entries, if any, correspond to composite
-// values.
-//
-typedef std::vector<class_inheritance_chain> data_member_scope;
-
-//
-// Semantic graph context types.
-//
-
-// Custom C++ type mapping.
-//
-struct custom_cxx_type
-{
- custom_cxx_type (): type_node (0), as_node (0) {}
-
- std::string
- translate_to (std::string const& v) const {return translate (v, to);}
-
- std::string
- translate_from (std::string const& v) const {return translate (v, from);}
-
- tree type_node;
- std::string type_name;
- semantics::type* type;
- semantics::names* type_hint;
-
- tree as_node;
- std::string as_name;
- semantics::type* as;
- semantics::names* as_hint;
-
- cxx_tokens to;
- bool to_move; // Single (?), so can move.
-
- cxx_tokens from;
- bool from_move; // Single (?), so can move.
-
- location_t loc;
- tree scope; // Scope for which this mapping is defined.
-
-private:
- static std::string
- translate (std::string const&, const cxx_tokens&);
-};
-
-typedef std::vector<custom_cxx_type> custom_cxx_types;
-typedef std::map<semantics::type*, custom_cxx_type*> custom_cxx_type_map;
-
-
-// Object or view pointer.
-//
-struct class_pointer
-{
- std::string name;
- tree scope;
- location_t loc;
-};
-
-//
-//
-struct default_value
-{
- enum kind_type
- {
- reset, // Default value reset.
- null,
- boolean, // Literal contains value (true or false).
- integer, // Integer number. Literal contains sign.
- floating, // Floating-point number.
- string, // Literal contains value.
- enumerator // Literal is the name, enum_value is the tree node.
- };
-
- kind_type kind;
- std::string literal;
-
- union
- {
- tree enum_value;
- unsigned long long int_value;
- double float_value;
- };
-};
-
-// Database potentially-qualified and unqualifed names.
-//
-using semantics::relational::qname;
-using semantics::relational::uname;
-
-// Object or table associated with the view.
-//
-struct view_object
-{
- // Return a diagnostic name for this association. It is either the
- // alias, unqualified object name, or string representation of the
- // table name.
- //
- std::string
- name () const;
-
- enum kind_type { object, table };
- enum join_type { left, right, full, inner, cross };
-
- kind_type kind;
- join_type join;
- tree obj_node; // Tree node if kind is object.
- std::string obj_name; // Name as specified in the pragma if kind is object.
- qname tbl_name; // Table name if kind is table.
- std::string alias;
- tree scope;
- location_t loc;
- semantics::class_* obj;
- semantics::data_member* ptr; // Corresponding object pointer, if any.
-
- cxx_tokens cond; // Join condition tokens.
-};
-
-typedef std::vector<view_object> view_objects;
-
-// The view_alias_map does not contain entries for tables.
-//
-typedef std::map<std::string, view_object*> view_alias_map;
-typedef std::map<semantics::class_*, view_object*> view_object_map;
-
-// Collection of relationships via which the objects are joined.
-// We need this information to figure out which alias/table
-// names to use for columns corresponding to inverse object
-// pointers inside objects that this view may be loading.
-//
-// The first object is the pointer (i.e., the one containing
-// this data member) while the other is the pointee. In other
-// words, first -> second. We always store the direct (i.e.,
-// non-inverse) side of the relationship. Note also that there
-// could be multiple objects joined using the same relationship.
-//
-typedef
-std::multimap<data_member_path, std::pair<view_object*, view_object*> >
-view_relationship_map;
-
-//
-//
-struct view_query
-{
- view_query (): distinct (false), for_update (false) {}
-
- enum kind_type
- {
- runtime,
- complete_select, // SELECT query.
- complete_execute, // Stored procedure call.
- condition
- };
-
- kind_type kind;
- std::string literal;
- cxx_tokens expr;
- tree scope;
- location_t loc;
-
- // Result modifiers (only for condition).
- //
- bool distinct; // SELECT DISTINCT
- bool for_update; // SELECT FOR UPDATE
-};
-
-//
-//
-struct table_column
-{
- qname table;
- std::string column;
- bool expr; // True if column is an expression, and therefore should not
- // be quoted.
-
- table_column () {}
-
- explicit
- table_column (const std::string& c): column (c), expr (false) {}
-};
-
-//
-//
-struct column_expr_part
-{
- enum kind_type
- {
- literal,
- reference
- };
-
- kind_type kind;
- std::string value;
- qname table; // Table name/alias for references.
- data_member_path member_path; // Path to member for references.
-
- // Scope and location of this pragma. Used to resolve the member name.
- //
- tree scope;
- location_t loc;
-};
-
-struct column_expr: std::vector<column_expr_part>
-{
- location_t loc;
-};
-
-//
-//
-struct member_access
-{
- member_access (const location& l, const char* k, bool s)
- : loc (l), kind (k), synthesized (s), by_value (false) {}
-
- // Return true of we have the (?) placeholder.
- //
- bool
- placeholder () const;
-
- // Return true if this is a synthesized expression that goes
- // directly for the member.
- //
- bool
- direct () const
- {
- return synthesized && expr.size () == 3; // this.member
- }
-
- bool
- empty () const
- {
- return expr.empty ();
- }
-
- // Issues diagnostics and throws operation_failed if expression is
- // empty.
- //
- std::string
- translate (std::string const& obj,
- std::string const& val = std::string (),
- std::string const& db = std::string ()) const;
-
- location loc;
- const char* kind; // accessor/modifier; used for diagnostics.
- bool synthesized; // If true, then this is a synthesized expression.
- cxx_tokens expr;
- bool by_value; // True if accessor returns by value. False doesn't
- // necessarily mean that it is by reference.
-};
-
-//
-//
-struct model_version
-{
- unsigned long long base;
- unsigned long long current;
- bool open;
-};
-
-// Sections.
-//
-struct object_section
-{
- virtual bool
- compare (object_section const&) const = 0;
-
- virtual bool
- separate_load () const = 0;
-
- virtual bool
- separate_update () const = 0;
-};
-
-inline bool
-operator== (object_section const& x, object_section const& y)
-{
- return x.compare (y);
-}
-
-inline bool
-operator!= (object_section const& x, object_section const& y)
-{
- return !x.compare (y);
-}
-
-// Main section.
-//
-struct main_section_type: object_section
-{
- virtual bool
- compare (object_section const& s) const;
-
- virtual bool
- separate_load () const {return false;}
-
- virtual bool
- separate_update () const {return false;}
-};
-
-inline bool
-operator== (main_section_type const&, main_section_type const&)
-{
- return true; // There is only one main section.
-}
-
-extern main_section_type main_section;
-
-// User-defined section.
-//
-struct user_section: object_section
-{
- enum load_type
- {
- load_eager,
- load_lazy
- };
-
- enum update_type
- {
- update_always,
- update_change,
- update_manual
- };
-
- enum special_type
- {
- special_ordinary,
- special_version // Fake section for optimistic version update.
- };
-
- user_section (semantics::data_member& m,
- semantics::class_& o,
- std::size_t i,
- load_type l,
- update_type u,
- special_type s = special_ordinary)
- : member (&m), object (&o), base (0), index (i),
- load (l), update (u), special (s),
- total (0), inverse (0), readonly (0), versioned (false),
- containers (false), readwrite_containers (false),
- versioned_containers (false), readwrite_versioned_containers (false) {}
-
- virtual bool
- compare (object_section const& s) const;
-
- virtual bool
- separate_load () const {return load != load_eager;}
-
- virtual bool
- separate_update () const
- {
- // A separately-loaded section is always separately-updated since
- // it might not be loaded when update is requested.
- //
- return separate_load () || update != update_always;
- }
-
- bool
- load_empty () const;
-
- bool
- update_empty () const;
-
- bool
- empty () const
- {
- return load_empty () && update_empty ();
- }
-
- // A section is optimistic if the object that contains it is optimistic.
- // For polymorphic hierarchies, only sections contained in the root are
- // considered optimistic.
- //
- bool
- optimistic () const;
-
- semantics::data_member* member; // Data member of this section.
- semantics::class_* object; // Object containing this section.
- user_section* base; // Base of this section.
- std::size_t index; // Index of this sections.
-
- load_type load;
- update_type update;
- special_type special;
-
- // Column counts.
- //
- std::size_t total;
- std::size_t inverse;
- std::size_t readonly;
-
- bool versioned;
-
- bool containers;
- bool readwrite_containers;
-
- bool versioned_containers;
- bool readwrite_versioned_containers;
-
- // Total counts across overrides.
- //
- std::size_t
- total_total () const
- {
- user_section* b (total_base ());
- return total + (b == 0 ? 0 : b->total_total ());
- }
-
- std::size_t
- total_inverse () const
- {
- user_section* b (total_base ());
- return inverse + (b == 0 ? 0 : b->total_inverse ());
- }
-
- std::size_t
- total_readonly () const
- {
- user_section* b (total_base ());
- return readonly + (b == 0 ? 0 : b->total_readonly ());
- }
-
- bool
- total_containers ()
- {
- user_section* b (total_base ());
- return containers || (b != 0 && b->total_containers ());
- }
-
- bool
- total_readwrite_containers ()
- {
- user_section* b (total_base ());
- return readwrite_containers ||
- (b != 0 && b->total_readwrite_containers ());
- }
-
-private:
- user_section*
- total_base () const;
-};
-
-inline bool
-operator== (user_section const& x, user_section const& y)
-{
- return x.member == y.member;
-}
-
-// Using list for pointer for element stability (see user_section::base).
-//
-struct user_sections: std::list<user_section>
-{
- // Count sections that have something to load.
- //
- static unsigned short const count_load = 0x01;
-
- // Count sections that are non-eager but have nothing to load.
- //
- static unsigned short const count_load_empty = 0x02;
-
- // Count sections that have something to update.
- //
- static unsigned short const count_update = 0x04;
-
- // Count sections that have nothing to update.
- //
- static unsigned short const count_update_empty = 0x08;
-
- // Count sections that are optimistic.
- //
- static unsigned short const count_optimistic = 0x10;
-
- // Modifiers:
- //
-
- // Don't exclude fake optimistic version update section from the count.
- //
- static unsigned short const count_special_version = 0x20;
-
- // Only count versioned sections.
- //
- static unsigned short const count_versioned_only = 0x40;
-
-
- // Count all sections, but excluding special.
- //
- static unsigned short const count_all = count_update |
- count_update_empty;
-
- static unsigned short const count_new = 0x1000;
- static unsigned short const count_override = 0x2000;
- static unsigned short const count_total = 0x4000;
-
- std::size_t
- count (unsigned short flags) const;
-
- user_sections (semantics::class_& o): object (&o) {};
- semantics::class_* object;
-};
-
-// Context.
-//
-class context
-{
-public:
- typedef std::size_t size_t;
- typedef std::string string;
- typedef std::vector<string> strings;
- typedef std::ostream ostream;
-
- typedef ::options options_type;
-
- static string
- upcase (string const&);
-
-public:
- // Return cvr-unqualified base of the type, or type itself, if it is
- // not qualified.
- //
- static semantics::type&
- utype (semantics::type&);
-
- // The same as above, but also returns the name hint for the unqualified
- // type. If the original type is already unqualified, then the hint
- // argument is not modified.
- //
- static semantics::type&
- utype (semantics::type&, semantics::names*& hint);
-
- // The same for a member's type but also do custom C++ type translation.
- //
- static semantics::type&
- utype (semantics::data_member& m, const custom_cxx_type** translation = 0)
- {
- semantics::names* hint (0);
- return utype (m, hint, string (), translation);
- }
-
- static semantics::type&
- utype (semantics::data_member& m,
- string const& key_prefix,
- const custom_cxx_type** translation = 0)
- {
- semantics::names* hint (0);
- return utype (m, hint, key_prefix, translation);
- }
-
- // In addition to the unqualified type, this version also returns the
- // name hint for this type. If the member type is already unqualified,
- // then the hint is from the belongs edge. Otherwise, it is from the
- // qualifies edge.
- //
- static semantics::type&
- utype (semantics::data_member&,
- semantics::names*& hint,
- string const& key_prefix = string (),
- const custom_cxx_type** translation = 0);
-
- static semantics::type&
- utype (const data_member_path& mp, const custom_cxx_type** translation = 0)
- {
- return utype (*mp.back (), translation);
- }
-
- static semantics::type&
- utype (const data_member_path& mp,
- string const& key_prefix,
- const custom_cxx_type** translation = 0)
- {
- return utype (*mp.back (), key_prefix, translation);
- }
-
- static semantics::type&
- utype (const data_member_path& mp,
- semantics::names*& hint,
- string const& key_prefix = string (),
- const custom_cxx_type** translation = 0)
- {
- return utype (*mp.back (), hint, key_prefix, translation);
- }
-
- // For arrays this function returns true if the (innermost) element
- // type is const.
- //
- static bool
- const_type (semantics::type&);
-
- static bool
- const_member (semantics::data_member& m) {return const_type (m.type ());}
-
- // Form a reference type for a not mapped, actual member type. If
- // make_const is true, then add top-level const qualifier, unless
- // it is already there. If it is false, then strip it if it is
- // already there. If var is not empty, then embed the variable
- // name into the type (e.g., char (*v)[3]). If decay_array is
- // false then don't decay the (top-level) array to a pointer.
- //
- static string
- member_ref_type (semantics::data_member& m,
- bool make_const,
- string const& var = "",
- bool decay_array = true)
- {
- return type_ref_type (
- m.type (), m.belongs ().hint (), make_const, var, decay_array);
- }
-
- static string
- type_ref_type (semantics::type&,
- semantics::names* hint,
- bool make_const,
- string const& var = "",
- bool decay_array = true);
-
- // Form a value type for a not mapped, actual member type. If make_const
- // is true, then add top-level const qualifier, unless it is already
- // there. If it is false, then strip it if it is already there. If var is
- // not empty, then embed the variable name into the type (e.g., char v[3]).
- //
- static string
- member_val_type (semantics::data_member& m,
- bool make_const,
- string const& var = "")
- {
- return type_val_type (m.type (), m.belongs ().hint (), make_const, var);
- }
-
- static string
- type_val_type (semantics::type&,
- semantics::names* hint,
- bool make_const,
- string const& var = "");
-
- // Member access helpers. Database can be empty. If type is not empty,
- // then it should be the non-cvr type of the member (e.g., id_type).
- //
- void
- set_member (semantics::data_member& m,
- const std::string& obj,
- const std::string& val,
- const std::string& db,
- const std::string& type = "");
-
- void
- inc_member (semantics::data_member& m,
- const std::string& sobj, // Set expression object.
- const std::string& gobj, // Get expression object.
- const std::string& type = "");
-
-public:
- // Resolve data member name in the form "a.b.c" to the data member path,
- // issuing diagnostics and throwing operation_filed in case of an error.
- // This function stops if it encounters a container leaving lex usable
- // to continue parsing.
- //
- void
- resolve_data_members (data_member_path& append,
- semantics::class_& scope,
- const std::string& name,
- const location&,
- cxx_string_lexer&);
-
- data_member_path
- resolve_data_members (semantics::class_& scope,
- const std::string& name,
- const location& l,
- cxx_string_lexer& lex)
- {
- data_member_path r;
- resolve_data_members (r, scope, name, l, lex);
- return r;
- }
-
- // Predicates.
- //
-public:
- static bool
- object (semantics::type& t)
- {
- return t.count ("object");
- }
-
- static bool
- view (semantics::type& t)
- {
- return t.count ("view");
- }
-
- // Direct member of a view.
- //
- static bool
- view_member (semantics::data_member& m)
- {
- return view (dynamic_cast<semantics::class_&> (m.scope ()));
- }
-
- // Check whether the type is a wrapper. Return the wrapped type if
- // it is a wrapper and NULL otherwise. Note that the returned type
- // may be cvr-qualified.
- //
- static semantics::type*
- wrapper (semantics::type& t)
- {
- return t.count ("wrapper") && t.get<bool> ("wrapper")
- ? t.get<semantics::type*> ("wrapper-type")
- : 0;
- }
-
- static semantics::type*
- wrapper (semantics::type& t, semantics::names*& hint)
- {
- if (t.count ("wrapper") && t.get<bool> ("wrapper"))
- {
- hint = t.get<semantics::names*> ("wrapper-hint");
- return t.get<semantics::type*> ("wrapper-type");
- }
- else
- return 0;
- }
-
- // Composite value type is a class type that was explicitly marked
- // as value type and there was no database type mapping provided for
- // it by the user (specifying the database type makes the value type
- // simple).
- //
- static bool
- composite (semantics::class_& c)
- {
- if (c.count ("composite-value"))
- return c.get<bool> ("composite-value");
- else
- return composite_ (c);
- }
-
- // Return the class object if this type is a composite value type
- // and NULL otherwise.
- //
- static semantics::class_*
- composite (semantics::type& t)
- {
- semantics::class_* c (dynamic_cast<semantics::class_*> (&t));
- return c != 0 && composite (*c) ? c : 0;
- }
-
- // As above but also "sees through" wrappers.
- //
- static semantics::class_*
- composite_wrapper (semantics::type& t)
- {
- if (semantics::class_* c = composite (t))
- return c;
- else if (semantics::type* wt = wrapper (t))
- return composite (utype (*wt));
- else
- return 0;
- }
-
- // Check if a data member is a container. "Sees through" wrappers and
- // returns the actual container type or NULL if not a container.
- //
- // We require data member as input instead of the type because the
- // same type (e.g., vector<char>) can be used for both container
- // and simple value members.
- //
- static semantics::type*
- container (semantics::data_member& m)
- {
- // The same type can be used as both a container and a simple value.
- //
- if (m.count ("simple"))
- return 0;
-
- semantics::type* t (&utype (m));
-
- if (semantics::type* wt = wrapper (*t))
- t = &utype (*wt);
-
- return t->count ("container-kind") ? t : 0;
- }
-
- static semantics::class_*
- object_pointer (semantics::type& t)
- {
- return t.get<semantics::class_*> ("element-type", 0);
- }
-
- // If this data member is or is part of an object pointer, then
- // return the member that is the pointer. Otherwise, return 0.
- //
- static semantics::data_member*
- object_pointer (data_member_path const&);
-
- static semantics::class_*
- points_to (semantics::data_member& m)
- {
- return m.get<semantics::class_*> ("points-to", 0);
- }
-
- static bool
- abstract (semantics::class_& c)
- {
- // If a class is abstract in the C++ sense then it is also abstract in
- // the database sense.
- //
- return c.abstract () || c.count ("abstract");
- }
-
- static bool
- session (semantics::class_& c)
- {
- return c.get<bool> ("session");
- }
-
- static bool
- transient (semantics::data_member& m)
- {
- return m.count ("transient");
- }
-
- // Return the deletion version or 0 if not soft-deleted.
- //
- static unsigned long long
- deleted (semantics::class_& c, location_t* l = 0)
- {
- unsigned long long v (c.get<unsigned long long> ("deleted", 0));
-
- if (v != 0 && l != 0)
- *l = c.get<location_t> ("deleted-location");
-
- return v;
- }
-
- static unsigned long long
- deleted (semantics::data_member& m, location_t* l = 0)
- {
- unsigned long long v (m.get<unsigned long long> ("deleted", 0));
-
- if (v != 0 && l != 0)
- *l = m.get<location_t> ("deleted-location");
-
- return v;
- }
-
- static unsigned long long
- deleted (data_member_path const& mp, location_t* l = 0)
- {
- unsigned long long r (0);
-
- // Find the earliest version since this member was deleted.
- //
- for (data_member_path::const_reverse_iterator i (mp.rbegin ());
- i != mp.rend (); ++i)
- {
- unsigned long long v ((*i)->get<unsigned long long> ("deleted", 0));
- if (v != 0 && (r == 0 || v < r))
- {
- r = v;
-
- if (l != 0)
- *l = (*i)->get<location_t> ("deleted-location");
- }
- }
-
- return r;
- }
-
- static semantics::data_member*
- deleted_member (data_member_path const& mp)
- {
- semantics::data_member* m (0);
-
- // Find the earliest version since this member was deleted.
- //
- unsigned long long r (0);
- for (data_member_path::const_reverse_iterator i (mp.rbegin ());
- i != mp.rend (); ++i)
- {
- unsigned long long v ((*i)->get<unsigned long long> ("deleted", 0));
- if (v != 0 && (r == 0 || v < r))
- {
- r = v;
- m = *i;
- }
- }
-
- return m;
- }
-
- // Return the addition version or 0 if not soft-added.
- //
- static unsigned long long
- added (semantics::class_& c) // Used for composite only.
- {
- return c.get<unsigned long long> ("added", 0);
- }
-
- static unsigned long long
- added (semantics::data_member& m)
- {
- return m.get<unsigned long long> ("added", 0);
- }
-
- static unsigned long long
- added (data_member_path const& mp)
- {
- unsigned long long r (0);
-
- // Find the latest version since this member was added.
- //
- for (data_member_path::const_reverse_iterator i (mp.rbegin ());
- i != mp.rend (); ++i)
- {
- unsigned long long v ((*i)->get<unsigned long long> ("added", 0));
- if (v != 0 && v > r)
- r = v;
- }
-
- return r;
- }
-
- static semantics::data_member*
- added_member (data_member_path const& mp)
- {
- semantics::data_member* m (0);
-
- // Find the latest version since this member was added.
- //
- unsigned long long r (0);
- for (data_member_path::const_reverse_iterator i (mp.rbegin ());
- i != mp.rend (); ++i)
- {
- unsigned long long v ((*i)->get<unsigned long long> ("added", 0));
- if (v != 0 && v > r)
- {
- r = v;
- m = *i;
- }
- }
-
- return m;
- }
-
- static bool
- id (semantics::data_member& m)
- {
- return m.count ("id");
- }
-
- // If this data member is or is part of an id member, then return
- // the member that is marked as the id. Otherwise, return 0.
- //
- static semantics::data_member*
- id (data_member_path const&);
-
- static bool
- auto_ (semantics::data_member& m)
- {
- return id (m) && m.count ("auto");
- }
-
- // Must be a path returned by id(). In other words, it assumes
- // the path is to the id member.
- //
- static bool
- auto_ (data_member_path& mp)
- {
- return mp.front ()->count ("auto");
- }
-
- // The member scope is used to override readonly status when a readonly
- // class (object or composite value) inherits from a readwrite base.
- //
- static bool
- readonly (data_member_path const&, data_member_scope const&);
-
- static bool
- readonly (semantics::data_member&);
-
- static bool
- readonly (semantics::class_& c)
- {
- return c.count ("readonly");
- }
-
- // Null-able.
- //
- bool
- null (data_member_path const&) const;
-
- bool
- null (semantics::data_member&) const;
-
- bool
- null (semantics::data_member&, string const& key_prefix) const;
-
- // Optimistic concurrency.
- //
- static semantics::data_member*
- optimistic (semantics::class_& c)
- {
- // Set by the validator.
- //
- return c.get<semantics::data_member*> ("optimistic-member", 0);
- }
-
- static bool
- version (semantics::data_member& m)
- {
- return m.count ("version");
- }
-
- static bool
- version (const data_member_path& mp)
- {
- return mp.size () == 1 && mp.back ()->count ("version");
- }
-
- // Polymorphic inheritance. Return root of the hierarchy or NULL if
- // not polymorphic.
- //
- static semantics::class_*
- polymorphic (semantics::class_& c)
- {
- // Set by the validator.
- //
- return c.get<semantics::class_*> ("polymorphic-root", 0);
- }
-
- static semantics::class_&
- polymorphic_base (semantics::class_& c)
- {
- // Set by the validator.
- //
- return *c.get<semantics::class_*> ("polymorphic-base");
- }
-
- static size_t
- polymorphic_depth (semantics::class_&);
-
- static bool
- discriminator (semantics::data_member& m)
- {
- return m.count ("discriminator");
- }
-
- static semantics::data_member*
- discriminator (semantics::class_& c)
- {
- // Set by type processor.
- //
- return c.get<semantics::data_member*> ("discriminator", 0);
- }
-
- // Model version.
- //
- bool
- versioned () const
- {
- return unit.count ("model-version") != 0;
- }
-
- model_version const&
- version () const
- {
- return unit.get<model_version> ("model-version");
- }
-
- // Versioned object, view, or composite.
- //
- static bool
- versioned (semantics::class_& c)
- {
- // Set by processor.
- //
- return c.count ("versioned") != 0;
- }
-
- // Versioned container.
- //
- static bool
- versioned (semantics::data_member& m)
- {
- // Set by processor.
- //
- return container (m)->count ("versioned");
- }
-
- // Object sections.
- //
- static object_section&
- section (semantics::data_member& m)
- {
- object_section* s (m.get<object_section*> ("section", 0));
- return s == 0 ? main_section : *s;
- }
-
- static object_section&
- section (data_member_path const& mp)
- {
- // The direct member of the object specifies the section. If the
- // path is empty (which can happen, for example, for a container
- // element), assume it is the main section.
- //
- //
- return mp.empty () ? main_section : section (*mp.front ());
- }
-
- // Member belongs to a section that is loaded separately.
- //
- static bool
- separate_load (semantics::data_member& m)
- {
- return section (m).separate_load ();
- }
-
- static bool
- separate_load (data_member_path const& mp)
- {
- return section (mp).separate_load ();
- }
-
- // Member belongs to a section that is updated separately.
- //
- static bool
- separate_update (semantics::data_member& m)
- {
- return section (m).separate_update ();
- }
-
- static bool
- separate_update (data_member_path const& mp)
- {
- return section (mp).separate_update ();
- }
-
- //
- //
- typedef ::class_kind class_kind_type;
-
- static class_kind_type
- class_kind (semantics::class_&);
-
- // Return class names. For ordinary classes, this will be the class
- // name itself. For class template instantiations this will be the
- // typedef name used in the pragma.
- //
- static string
- class_name (semantics::class_&);
-
- static string
- class_fq_name (semantics::class_&);
-
- // Return class scope. For ordinary classes, this will be the scope
- // where the class is defined. For class template instantiations this
- // will be the scope of the typedef name used in the pragma.
- //
- static semantics::scope&
- class_scope (semantics::class_&);
-
- // Return the class file. For ordinary classes, this will be the file
- // where the class is defined. For class template instantiations this
- // will be the file containing the pragma.
- //
- static semantics::path
- class_file (semantics::class_&);
-
- // Return the location (as location_t; useful for comparison) of
- // an "ODB class" (i.e., an object, view, or composite value),
- // taking into account things like definition point overrides,
- // etc.
- //
- location_t
- class_location (semantics::class_&);
-
- // Same as above, but returns "real" location, that is, ignoring
- // the definition point overrides.
- //
- location_t
- class_real_location (semantics::class_&);
-
- // Database names and types.
- //
-public:
- // Schema name for a namespace.
- //
- qname
- schema (semantics::scope&) const;
-
- // Table name prefix for a namespace.
- //
- string
- table_name_prefix (semantics::scope&) const;
-
- //
- //
- struct table_prefix
- {
- table_prefix (): level (0), derived (false) {}
- table_prefix (semantics::class_&);
-
- void
- append (semantics::data_member&);
-
- qname ns_schema; // Object's namespace schema.
- string ns_prefix; // Object's namespace table prefix.
- qname prefix;
- size_t level;
- bool derived; // One of the components in the prefix was derived.
- };
-
- qname
- table_name (semantics::class_&, bool* derived = 0) const;
-
- qname
- table_name (semantics::class_&, data_member_path const&) const;
-
- // Table name for the container member. The table prefix passed as the
- // second argument must include the table prefix specified with the
- // --table-prefix option.
- //
- qname
- table_name (semantics::data_member&, table_prefix const&) const;
-
- //
- //
- struct column_prefix
- {
- column_prefix (): derived (false), underscore (false) {}
-
- column_prefix (semantics::data_member& m,
- string const& key_prefix = string (),
- string const& default_name = string ())
- : derived (false), underscore (false)
- {
- append (m, key_prefix, default_name);
- }
-
- // If the last argument is true, the prefix will include the last member
- // in the path.
- //
- column_prefix (data_member_path const&, bool last = false);
-
- bool
- empty () const {return prefix.empty ();}
-
- void
- append (semantics::data_member&,
- string const& key_prefix = string (),
- string const& default_name = string ());
-
- string prefix;
- bool derived; // One of the components in the prefix was derived.
- bool underscore; // Trailing underscore was automatically added.
- };
-
- string
- column_name (semantics::data_member&, bool& derived) const;
-
- string
- column_name (semantics::data_member&, column_prefix const&) const;
-
- string
- column_name (semantics::data_member&,
- string const& key_prefix,
- string const& default_name,
- bool& derived) const;
-
- string
- column_name (semantics::data_member&,
- string const& key_prefix,
- string const& default_name,
- column_prefix const&) const;
-
- string
- column_name (data_member_path const&) const;
-
- //
- //
- string
- column_type (const data_member_path&,
- string const& key_prefix = string (),
- bool id = false); // Pass true if this type is object id other
- // than because of the members in the path.
- string
- column_type (semantics::data_member&, string const& key_prefix = string ());
-
- string
- column_options (semantics::data_member&);
-
- string
- column_options (semantics::data_member&, string const& key_prefix);
-
- // Cleaned-up member name that can be used for database names.
- //
- string
- public_name_db (semantics::data_member&) const;
-
- // Compose the name by inserting/removing an underscore, as necessary.
- //
- static string
- compose_name (string const& prefix, string const& name);
-
- // SQL name transformations.
- //
- enum sql_name_type
- {
- sql_name_all,
- sql_name_table,
- sql_name_column,
- sql_name_index,
- sql_name_fkey,
- sql_name_sequence,
- sql_name_statement,
- sql_name_count
- };
-
- string
- transform_name (string const& name, sql_name_type) const;
-
- // C++ names.
- //
-public:
- // Cleaned-up and potentially escaped member name that can be used
- // in public C++ interfaces.
- //
- string
- public_name (semantics::data_member&, bool escape = true) const;
-
- // "Flatten" fully-qualified C++ name by replacing '::' with '_'
- // and removing leading '::', if any.
- //
- static string
- flat_name (string const& fqname);
-
- // Escape C++ keywords, reserved names, and illegal characters.
- //
- string
- escape (string const&) const;
-
- // Make C++ include guard name by split words, e.g., "FooBar" to
- // "Foo_Bar" and converting everything to upper case.
- //
- string
- make_guard (string const&) const;
-
- // Return a string literal that can be used in C++ source code. It
- // includes "".
- //
- static string
- strlit (string const&);
-
-public:
- // Generate explicit instantiation headers with all the necessary
- // extern and export symbols.
- //
- void
- inst_header (bool decl, bool omit_exp = false);
-
- // Counts and other information.
- //
-public:
- struct column_count_type
- {
- column_count_type ()
- : total (0),
- id (0),
- inverse (0),
- readonly (0),
- optimistic_managed (0),
- discriminator (0),
- added (0),
- deleted (0),
- soft (0),
- separate_load (0),
- separate_update (0)
- {
- }
-
- size_t total;
- size_t id;
- size_t inverse;
- size_t readonly;
- size_t optimistic_managed;
- size_t discriminator;
-
- size_t added; // Soft-added.
- size_t deleted; // Soft-deleted.
- size_t soft; // Soft-added/deleted (a column can be both).
-
- size_t separate_load;
- size_t separate_update; // Only readwrite.
- };
-
- static column_count_type
- column_count (semantics::class_&, object_section* = 0);
-
- static data_member_path*
- id_member (semantics::class_& c)
- {
- // Set by the processor. May not be there for reuse-abstract
- // classes or classes without object id.
- //
- return c.count ("id-member") ? &c.get<data_member_path> ("id-member") : 0;
- }
-
- // Object pointer information.
- //
-public:
- typedef ::pointer_kind pointer_kind_type;
-
- pointer_kind_type
- pointer_kind (semantics::type& p)
- {
- return p.get<pointer_kind_type> ("pointer-kind");
- }
-
- bool
- lazy_pointer (semantics::type& p)
- {
- return p.get<bool> ("pointer-lazy");
- }
-
- bool
- weak_pointer (semantics::type& p)
- {
- return pointer_kind (p) == pk_weak;
- }
-
- static data_member_path*
- inverse (semantics::data_member& m)
- {
- return object_pointer (utype (m)) && m.count ("inverse")
- ? &m.get<data_member_path> ("inverse")
- : 0;
- }
-
- data_member_path*
- inverse (semantics::data_member& m, string const& key_prefix)
- {
- if (key_prefix.empty ())
- return inverse (m);
-
- if (!object_pointer (utype (m, key_prefix)))
- return 0;
-
- string k (key_prefix + "-inverse");
- return m.count (k) ? &m.get<data_member_path> (k) : 0;
- }
-
- // Container information.
- //
-public:
- typedef ::container_kind container_kind_type;
-
- static container_kind_type
- container_kind (semantics::type& c)
- {
- return c.get<container_kind_type> ("container-kind");
- }
-
- static bool
- container_smart (semantics::type& c)
- {
- return c.get<bool> ("container-smart");
- }
-
- static semantics::type&
- container_idt (semantics::data_member& m, const custom_cxx_type** trans = 0)
- {
- return utype (m, "id", trans);
- }
-
- static semantics::type&
- container_vt (semantics::data_member& m, const custom_cxx_type** trans = 0)
- {
- return utype (m, "value", trans);
- }
-
- static semantics::type&
- container_it (semantics::data_member& m, const custom_cxx_type** trans = 0)
- {
- return utype (m, "index", trans);
- }
-
- static semantics::type&
- container_kt (semantics::data_member& m, const custom_cxx_type** trans = 0)
- {
- return utype (m, "key", trans);
- }
-
- static bool
- unordered (semantics::data_member& m)
- {
- if (m.count ("unordered"))
- return true;
-
- if (semantics::type* c = container (m))
- return c->count ("unordered");
-
- return false;
- }
-
- // The 'is a' and 'has a' tests. The has_a() test currently does not
- // cross the container boundaries.
- //
-public:
- static unsigned short const test_pointer = 0x01;
- static unsigned short const test_eager_pointer = 0x02;
- static unsigned short const test_lazy_pointer = 0x04;
- static unsigned short const test_container = 0x08;
- static unsigned short const test_straight_container = 0x10;
- static unsigned short const test_inverse_container = 0x20;
- static unsigned short const test_readonly_container = 0x40;
- static unsigned short const test_readwrite_container = 0x80;
- static unsigned short const test_smart_container = 0x100;
-
- // Exclude versioned containers.
- //
- static unsigned short const exclude_versioned = 0x200;
-
- // Treat eager loaded members as belonging to the main section.
- // If this flag is specified, then section must be main_section.
- //
- static unsigned short const include_eager_load = 0x800;
-
- // Exclude added/deleted members.
- //
- static unsigned short const exclude_added = 0x1000;
- static unsigned short const exclude_deleted = 0x2000;
-
- // By default the test goes into bases for non-polymorphic
- // hierarchies and doesn't go for polymorphic. The following
- // flags can be used to alter this behavior.
- //
- static unsigned short const exclude_base = 0x4000;
- static unsigned short const include_base = 0x8000;
-
- bool
- is_a (data_member_path const& mp,
- data_member_scope const& ms,
- unsigned short flags)
- {
- return is_a (mp, ms, flags, utype (*mp.back ()), "");
- }
-
- bool
- is_a (data_member_path const&,
- data_member_scope const&,
- unsigned short flags,
- semantics::type&,
- string const& key_prefix);
-
- // Return the number of matching entities. Can be used as a just
- // a bool value (0 means no match).
- //
- size_t
- has_a (semantics::class_&, unsigned short flags, object_section* = 0);
-
-public:
- // Process include path by adding the prefix, putting it through
- // the include regex list, and adding opening and closing include
- // characters ("" or <>) if necessary. The prefix argument indicates
- // whether the include prefix specified with the --include-prefix
- // option should be added. The open argument can be used to specify
- // the opening character. It can have three values: ", <, or \0. In
- // case of \0, the character is determined based on the value of the
- // --include-with-bracket option.
- //
- string
- process_include_path (string const&, bool prefix = true, char open = '\0');
-
- // Diverge output.
- //
-public:
- void
- diverge (std::ostream& os)
- {
- diverge (os.rdbuf ());
- }
-
- void
- diverge (std::streambuf* sb);
-
- void
- restore ();
-
- // Implementation details.
- //
-private:
- static bool
- composite_ (semantics::class_&);
-
- template <typename X>
- static X
- indirect_value (semantics::context const& c, string const& key)
- {
- typedef X (*func) ();
- std::type_info const& ti (c.type_info (key));
-
- if (ti == typeid (func))
- return c.get<func> (key) ();
- else
- return c.get<X> (key);
- }
-
- static semantics::type*
- indirect_type (semantics::context const& c,
- string const& kp,
- semantics::names*& hint)
- {
- typedef semantics::type* (*func) (semantics::names*&);
-
- string const tk (kp + "-tree-type");
- std::type_info const& ti (c.type_info (tk));
-
- if (ti == typeid (func))
- return c.get<func> (tk) (hint);
- else
- {
- hint = c.get<semantics::names*> (kp + "-tree-hint");
- return c.get<semantics::type*> (tk);
- }
- }
-
-public:
- typedef std::set<string> keyword_set_type;
-
- struct db_type_type
- {
- db_type_type () {}
- db_type_type (string const& t, string const& it, bool n)
- : type (t), id_type (it), null (n)
- {
- }
-
- string type;
- string id_type;
- bool null;
- };
-
- struct type_map_type: std::map<string, db_type_type>
- {
- typedef std::map<string, db_type_type> base;
-
- const_iterator
- find (semantics::type&, semantics::names* hint);
- };
-
-protected:
- struct data
- {
- virtual
- ~data () {}
-
- data (std::ostream& os)
- : extra_ (0),
- os_ (os.rdbuf ()),
- in_comment_ (false),
- top_object_ (0),
- cur_object_ (0),
- sql_name_upper_ ("(.+)", "\\U$1"),
- sql_name_lower_ ("(.+)", "\\L$1")
- {
- }
-
- public:
- void* extra_;
-
- std::ostream os_;
- std::stack<std::streambuf*> os_stack_;
-
- bool in_comment_;
-
- semantics::class_* top_object_;
- semantics::class_* cur_object_;
-
- string exp_;
- string ext_;
-
- keyword_set_type keyword_set_;
- type_map_type type_map_;
-
- regex_mapping sql_name_regex_[sql_name_count];
- regexsub sql_name_upper_;
- regexsub sql_name_lower_;
-
- regex_mapping include_regex_;
- regex_mapping accessor_regex_;
- regex_mapping modifier_regex_;
- };
-
- typedef cutl::shared_ptr<data> data_ptr;
- data_ptr data_;
-
-public:
- typedef ::features features_type;
-
- void*& extra; // Extra data that may need to be shared by a sub-system.
-
- std::ostream& os;
- semantics::unit& unit;
- options_type const& options;
- features_type& features;
- database const db;
-
- bool& in_comment;
-
- string& exp; // Export symbol (with trailing space if specified).
- string& ext; // Extern symbol.
-
- keyword_set_type const& keyword_set;
-
- regex_mapping const& include_regex;
- regex_mapping const& accessor_regex;
- regex_mapping const& modifier_regex;
-
- bool embedded_schema;
- bool separate_schema;
-
- bool multi_static;
- bool multi_dynamic;
-
- bool force_versioned; // Force statement processing for debugging.
-
- // Outermost object or view currently being traversed.
- //
- semantics::class_*& top_object;
-
- // Object or view currently being traversed. It can be the same as
- // top_object or it can a base of top_object.
- //
- semantics::class_*& cur_object;
-
- // Per-database customizable functionality.
- //
-protected:
- // Return empty string if there is no mapping. The type passed is
- // already cvr-unqualified. The null out argument indicates whether
- // the column should allow NULL values by default.
- //
- string
- database_type (semantics::type& t,
- semantics::names* hint,
- bool id,
- bool* null = 0)
- {
- return current ().database_type_impl (t, hint, id, null);
- }
-
- // The default implementation uses the type map (populated by the database-
- // specific context implementation) to come up with a mapping.
- //
- virtual string
- database_type_impl (semantics::type&, semantics::names*, bool, bool*);
-
-public:
- typedef context root_context;
-
- virtual
- ~context ();
- context ();
- context (std::ostream&,
- semantics::unit&,
- options_type const&,
- features_type&,
- data_ptr = data_ptr ());
-
- static context&
- current ()
- {
- return *current_;
- }
-
-private:
- static context* current_;
-
-#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
- context&
- operator= (context const&) = delete;
-#else
-private:
- context&
- operator= (context const&);
-#endif
-};
-
-// Create concrete database context.
-//
-std::unique_ptr<context>
-create_context (std::ostream&,
- semantics::unit&,
- options const&,
- features&,
- semantics::relational::model*);
-
-// Checks if scope Y names any of X.
-//
-template <typename X, typename Y>
-bool
-has (Y& y)
-{
- for (semantics::scope::names_iterator i (y.names_begin ()),
- e (y.names_end ()); i != e; ++i)
- if (i->named (). template is_a<X> ())
- return true;
-
- return false;
-}
-
-#include <odb/context.ixx>
-
-#endif // ODB_CONTEXT_HXX
diff --git a/odb/cxx-lexer.cxx b/odb/cxx-lexer.cxx
deleted file mode 100644
index 67493b7..0000000
--- a/odb/cxx-lexer.cxx
+++ /dev/null
@@ -1,364 +0,0 @@
-// file : odb/cxx-lexer.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <odb/gcc.hxx>
-
-#include <stdio.h>
-#include <stdarg.h>
-
-#include <new> // std::bad_alloc
-#include <cassert>
-#include <iostream>
-
-#include <odb/cxx-lexer.hxx>
-
-using namespace std;
-
-//
-// cxx_lexer
-//
-
-// Token spelling. See cpplib.h for details.
-//
-#define OP(e, s) s ,
-#define TK(e, s) #e ,
-char const* cxx_lexer::token_spelling[N_TTYPES + 1] = { TTYPE_TABLE "KEYWORD"};
-#undef OP
-#undef TK
-
-cxx_lexer::
-~cxx_lexer ()
-{
-}
-
-//
-// cxx_tokens_lexer
-//
-
-void cxx_tokens_lexer::
-start (cxx_tokens const& ts, location_t start_loc)
-{
- tokens_ = &ts;
- cur_ = ts.begin ();
- loc_ = start_loc;
-}
-
-cpp_ttype cxx_tokens_lexer::
-next (std::string& token, tree* node)
-{
- if (cur_ != tokens_->end ())
- {
- loc_ = cur_->loc;
- token = cur_->literal;
- if (node != 0)
- *node = cur_->node;
- return static_cast<cpp_ttype> (cur_++->type);
- }
- else
- return CPP_EOF;
-}
-
-location_t cxx_tokens_lexer::
-location () const
-{
- return loc_;
-}
-
-//
-// cxx_pragma_lexer
-//
-
-void cxx_pragma_lexer::
-start ()
-{
- token_ = &token_data_;
- type_ = &type_data_;
-}
-
-string cxx_pragma_lexer::
-start (tree& token, cpp_ttype& type)
-{
- token_ = &token;
- type_ = &type;
-
- return translate ();
-}
-
-cpp_ttype cxx_pragma_lexer::
-next (string& token, tree* node)
-{
- *type_ = pragma_lex (token_);
-
- // See if this is a keyword using the C++ parser machinery and
- // the current C++ dialect.
- //
- if (*type_ == CPP_NAME &&
-#if BUILDING_GCC_MAJOR >= 8
- IDENTIFIER_KEYWORD_P (*token_)
-#else
- C_IS_RESERVED_WORD (*token_)
-#endif
- )
- *type_ = CPP_KEYWORD;
-
- if (node != 0 && node != token_)
- *node = *token_;
-
- token = translate ();
- return *type_;
-}
-
-location_t cxx_pragma_lexer::
-location () const
-{
- // Starting from GCC 6 the input location seem to require the same
- // translation as what we do in real_source_location().
- //
-#if BUILDING_GCC_MAJOR >= 6
- return linemap_resolve_location (
- line_table, input_location, LRK_MACRO_EXPANSION_POINT, 0);
-#else
- return input_location;
-#endif
-}
-
-string cxx_pragma_lexer::
-translate ()
-{
- string r;
-
- if (*type_ == CPP_NAME || *type_ == CPP_KEYWORD)
- r = IDENTIFIER_POINTER (*token_);
- else if (*type_ == CPP_STRING)
- r = TREE_STRING_POINTER (*token_);
-
- return r;
-}
-
-//
-// cxx_string_lexer
-//
-
-// Diagnostics callback.
-//
-extern "C" bool
-cpp_diagnostic_callback (
- cpp_reader* reader,
-#if BUILDING_GCC_MAJOR >= 9
- cpp_diagnostic_level level,
-#else
- int level,
-#endif
-#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 5
-#if BUILDING_GCC_MAJOR >= 9
- cpp_warning_reason,
-#else
- int /*reason*/, // Added in GCC 4.6.0.
-#endif
-#endif
-#if BUILDING_GCC_MAJOR <= 5
- location_t,
- unsigned int,
-#else
- rich_location*,
-#endif
- char const* msg,
- va_list *ap)
-{
- char const* kind (0);
- switch (level)
- {
- case CPP_DL_NOTE:
- case CPP_DL_WARNING_SYSHDR:
- case CPP_DL_WARNING:
- case CPP_DL_PEDWARN:
- // Ignore these.
- break;
- case CPP_DL_ERROR:
- case CPP_DL_FATAL:
- kind = "error";
- break;
- case CPP_DL_ICE:
- kind = "ice";
- break;
- default:
- kind = "unknown";
- break;
- }
-
- if (kind != 0)
- {
- fprintf (stderr, "%s: ", kind);
- vfprintf (stderr, msg, *ap);
- fprintf (stderr, "\n");
-
- // By resetting the callback we indicate to cxx_string_lexer that there
- // was an error.
- //
-#if BUILDING_GCC_MAJOR >= 9
- cpp_get_callbacks (reader)->diagnostic = 0;
-#else
- cpp_get_callbacks (reader)->error = 0;
-#endif
- return true;
- }
-
- return false;
-}
-
-cxx_string_lexer::
-cxx_string_lexer ()
- : reader_ (0)
-{
-#if BUILDING_GCC_MAJOR >= 5
- linemap_init (&line_map_, UNKNOWN_LOCATION);
-#else
- linemap_init (&line_map_);
-#endif
-
-#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 6
- line_map_.round_alloc_size = ggc_round_alloc_size;
-#endif
-
- linemap_add (&line_map_, LC_ENTER, 0, "<memory>", 0);
-
- reader_ = cpp_create_reader (
- cxx_dialect == cxx0x // Nothing new for C++14.
-#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 6
- ? CLK_CXX11
-#else
- ? CLK_CXX0X
-#endif
- : CLK_CXX98,
- 0,
- &line_map_);
-
- if (reader_ == 0)
- throw bad_alloc ();
-
- callbacks_ = cpp_get_callbacks (reader_);
-}
-
-cxx_string_lexer::
-~cxx_string_lexer ()
-{
- if (reader_ != 0)
- cpp_destroy (reader_);
-
- // Was removed as "dead code" in GCC 4.7.0.
- //
-#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
- linemap_free (&line_map_);
-#endif
-}
-
-void cxx_string_lexer::
-start (string const& data)
-{
- // The previous lexing session should have popped the buffer.
- //
- assert (cpp_get_buffer (reader_) == 0);
-
-#if BUILDING_GCC_MAJOR >= 9
- callbacks_->diagnostic = &cpp_diagnostic_callback;
-#else
- callbacks_->error = &cpp_diagnostic_callback;
-#endif
-
- data_ = data;
- buf_ = data;
- buf_ += '\n';
- loc_ = 0;
-
- cpp_push_buffer (
- reader_,
- reinterpret_cast<unsigned char const*> (buf_.c_str ()),
- buf_.size (),
- true);
-}
-
-cpp_ttype cxx_string_lexer::
-next (string& token, tree* node)
-{
- token.clear ();
- cpp_token const* t (cpp_get_token (reader_));
-
- // If there was an error, the callback will be reset to 0. Diagnostics has
- // already been issued.
- //
-#if BUILDING_GCC_MAJOR >= 9
- if (callbacks_->diagnostic == 0)
-#else
- if (callbacks_->error == 0)
-#endif
- throw invalid_input ();
-
- cpp_ttype tt (t->type);
-
- switch (tt)
- {
- case CPP_NAME:
- {
- char const* name (
- reinterpret_cast<char const*> (NODE_NAME (t->val.node.node)));
-
- // See if this is a keyword using the C++ parser machinery and
- // the current C++ dialect.
- //
- tree id (get_identifier (name));
-
- if (
-#if BUILDING_GCC_MAJOR >= 8
- IDENTIFIER_KEYWORD_P (id)
-#else
- C_IS_RESERVED_WORD (id)
-#endif
- )
- tt = CPP_KEYWORD;
-
- if (node != 0)
- *node = id;
-
- token = name;
- break;
- }
- case CPP_STRING:
- case CPP_NUMBER:
- {
- if (node != 0)
- *node = 0; // Doesn't seem to be available.
-
- cpp_string const& s (t->val.str);
- token.assign (reinterpret_cast<char const*> (s.text), s.len);
- break;
- }
- default:
- {
- if (tt <= CPP_LAST_PUNCTUATOR)
- {
- if (node != 0)
- *node = 0;
- token = token_spelling[tt];
- }
- else
- {
- cerr << "unexpected token '" << token_spelling[tt] << "' in '" <<
- data_ << "'" << endl;
- throw invalid_input ();
- }
- break;
- }
- }
-
- // Cache the location of this token.
- //
- loc_ = t->src_loc;
-
- return tt;
-}
-
-location_t cxx_string_lexer::
-location () const
-{
- return loc_;
-}
diff --git a/odb/diagnostics.hxx b/odb/diagnostics.hxx
deleted file mode 100644
index 91ec068..0000000
--- a/odb/diagnostics.hxx
+++ /dev/null
@@ -1,96 +0,0 @@
-// file : odb/diagnostics.hxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#ifndef ODB_DIAGNOSTICS_HXX
-#define ODB_DIAGNOSTICS_HXX
-
-#include <odb/gcc-fwd.hxx>
-
-#include <string>
-#include <cstddef>
-#include <iostream>
-
-#include <cutl/fs/path.hxx>
-
-#include <odb/location.hxx>
-
-using std::endl;
-
-std::ostream&
-error (cutl::fs::path const&, std::size_t line, std::size_t clmn);
-
-std::ostream&
-warn (cutl::fs::path const&, std::size_t line, std::size_t clmn);
-
-std::ostream&
-info (cutl::fs::path const&, std::size_t line, std::size_t clmn);
-
-inline std::ostream&
-error (location const& l)
-{
- return error (l.file, l.line, l.column);
-}
-
-inline std::ostream&
-warn (location const&l)
-{
- return warn (l.file, l.line, l.column);
-}
-
-inline std::ostream&
-info (location const&l)
-{
- return info (l.file, l.line, l.column);
-}
-
-std::ostream&
-error (location_t);
-
-std::ostream&
-warn (location_t);
-
-std::ostream&
-info (location_t);
-
-//
-//
-class cxx_lexer;
-
-std::ostream&
-error (cxx_lexer&);
-
-std::ostream&
-warn (cxx_lexer&);
-
-std::ostream&
-info (cxx_lexer&);
-
-// Location as a string in the "<file>:<line>:<column>" format.
-//
-std::string
-location_string (cutl::fs::path const&,
- std::size_t line,
- std::size_t clmn,
- bool leaf = false);
-
-inline std::string
-location_string (location const& l, bool leaf = false)
-{
- return location_string (l.file, l.line, l.column, leaf);
-}
-
-std::string
-location_string (location_t, bool leaf = false);
-
-// location_t macro wrappers.
-//
-cutl::fs::path
-location_file (location_t);
-
-std::size_t
-location_line (location_t);
-
-std::size_t
-location_column (location_t);
-
-#endif // ODB_DIAGNOSTICS_HXX
diff --git a/odb/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/doc/default.css b/odb/doc/default.css
index 889f46b..889f46b 100644
--- a/doc/default.css
+++ b/odb/doc/default.css
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>&nbsp;</h1>
+ <h1>&nbsp;</h1>
+ <h1>&nbsp;</h1>
+ <h1>&nbsp;</h1>
+ <h1>&nbsp;</h1>
+ <h1>&nbsp;</h1>
+ <h1>&nbsp;</h1>
+ <h1>&nbsp;</h1>
+</div>
+ <p>Copyright &#169; 2009-2020 Code Synthesis.</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="&#169; 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 &#169; 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&nbsp;&nbsp;
+ <span style="font-weight: normal;">OBJECT-RELATIONAL MAPPING</span></a></h1>
+
+ <p>Part I describes the essential database concepts, APIs, and tools that
+ together comprise the object-relational mapping for C++ as implemented
+ by ODB. It consists of the following chapters.</p>
+
+ <table class="toc">
+ <tr><th>1</th><td><a href="#1">Introduction</a></td></tr>
+ <tr><th>2</th><td><a href="#2">Hello World Example</a></td></tr>
+ <tr><th>3</th><td><a href="#3">Working with Persistent Objects</a></td></tr>
+ <tr><th>4</th><td><a href="#4">Querying the Database</a></td></tr>
+ <tr><th>5</th><td><a href="#5">Containers</a></td></tr>
+ <tr><th>6</th><td><a href="#6">Relationships</a></td></tr>
+ <tr><th>7</th><td><a href="#7">Value Types</a></td></tr>
+ <tr><th>8</th><td><a href="#8">Inheritance</a></td></tr>
+ <tr><th>10</th><td><a href="#10">Views</a></td></tr>
+ <tr><th>11</th><td><a href="#11">Session</a></td></tr>
+ <tr><th>12</th><td><a href="#12">Optimistic Concurrency</a></td></tr>
+ <tr><th>13</th><td><a href="#13">Database Schema Evolution</a></td></tr>
+ <tr><th>14</th><td><a href="#14">ODB Pragma Language</a></td></tr>
+ </table>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="1">1 Introduction</a></h1>
+
+ <p>ODB is an object-relational mapping (ORM) system for C++. It provides
+ tools, APIs, and library support that allow you to persist C++ objects
+ to a relational database (RDBMS) without having to deal with tables,
+ columns, or SQL and without manually writing any of the mapping code.</p>
+
+ <p>ODB is highly flexible and customizable. It can either completely
+ hide the relational nature of the underlying database or expose
+ some of the details as required. For example, you can automatically
+ map basic C++ types to suitable SQL types, generate the relational
+ database schema for your persistent classes, and use simple, safe,
+ and yet powerful object query language instead of SQL. Or you can
+ assign SQL types to individual data members, use the existing
+ database schema, run native SQL <code>SELECT</code> queries, and
+ call stored procedures. In fact, at an extreme, ODB can be used
+ as <em>just</em> a convenient way to handle results of native SQL
+ queries.</p>
+
+ <p>ODB is not a framework. It does not dictate how you should write
+ your application. Rather, it is designed to fit into your
+ style and architecture by only handling object persistence
+ and not interfering with any other functionality. There is
+ no common base type that all persistent classes should derive
+ from nor are there any restrictions on the data member types
+ in persistent classes. Existing classes can be made persistent
+ with a few or no modifications.</p>
+
+ <p>ODB has been designed for high performance and low memory
+ overhead. Prepared statements are used to send and receive
+ object state in binary format instead of text which reduces
+ the load on the application and the database server. Extensive
+ caching of connections, prepared statements, and buffers saves
+ time and resources on connection establishment, statement parsing,
+ and memory allocations. For each supported database system the
+ native C API is used instead of ODBC or higher-level wrapper
+ APIs to reduce overhead and provide the most efficient implementation
+ for each database operation. Finally, persistent classes have
+ zero memory overhead. There are no hidden "database" members
+ that each class must have nor are there per-object data structures
+ allocated by ODB.</p>
+
+ <p>In this chapter we present a high-level overview of ODB.
+ We will start with the ODB architecture and then outline the
+ workflow of building an application that uses ODB. We will
+ then continue by contrasting the drawbacks of the traditional
+ way of saving C++ objects to relational databases with the
+ benefits of using ODB for object persistence. We conclude the
+ chapter by discussing the C++ standards supported by ODB. The
+ next chapter takes a more hands-on approach and shows the
+ concrete steps necessary to implement object persistence in
+ a simple "Hello World" application.</p>
+
+ <h2><a name="1.1">1.1 Architecture and Workflow</a></h2>
+
+ <p>From the application developer's perspective, ODB
+ consists of three main components: the ODB compiler, the common
+ runtime library, called <code>libodb</code>, and the
+ database-specific runtime libraries, called
+ <code>libodb-&lt;database></code>, where &lt;database> is
+ the name of the database system this runtime
+ is for, for example, <code>libodb-mysql</code>. For instance,
+ if the application is going to use the MySQL database for
+ object persistence, then the three ODB components that this
+ application will use are the ODB compiler, <code>libodb</code>
+ and <code>libodb-mysql</code>.</p>
+
+ <p>The ODB compiler generates the database support code for
+ persistent classes in your application. The input to the ODB
+ compiler is one or more C++ header files defining C++ classes
+ that you want to make persistent. For each input header file
+ the ODB compiler generates a set of C++ source files implementing
+ conversion between persistent C++ classes defined in this
+ header and their database representation. The ODB compiler
+ can also generate a database schema file that creates tables
+ necessary to store the persistent classes.</p>
+
+ <p>The ODB compiler is a real C++ compiler except that it produces
+ C++ instead of assembly or machine code. In particular, it is not
+ an ad-hoc header pre-processor that is only capable of recognizing
+ a subset of C++. ODB is capable of parsing any standard C++ code.</p>
+
+ <p>The common runtime library defines database system-independent
+ interfaces that your application can use to manipulate persistent
+ objects. The database-specific runtime library provides implementations
+ of these interfaces for a concrete database as well as other
+ database-specific utilities that are used by the generated code.
+ Normally, the application does not use the database-specific
+ runtime library directly but rather works with it via the common
+ interfaces from <code>libodb</code>. The following diagram shows
+ the object persistence architecture of an application that uses
+ MySQL as the underlying database system:</p>
+
+ <!-- align=center is needed for html2ps -->
+ <div class="img" align="center"><img src="odb-arch.png"/></div>
+
+ <p>The ODB system also defines two special-purpose languages:
+ the ODB Pragma Language and ODB Query Language. The ODB Pragma
+ Language is used to communicate various properties of persistent
+ classes to the ODB compiler by means of special <code>#pragma</code>
+ directives embedded in the C++ header files. It controls aspects
+ of the object-relational mapping such as names of tables and columns
+ that are used for persistent classes and their members or mapping between
+ C++ types and database types.</p>
+
+ <p>The ODB Query Language is an object-oriented database query
+ language that can be used to search for objects matching
+ certain criteria. It is modeled after and is integrated into
+ C++ allowing you to write expressive and safe queries that look
+ and feel like ordinary C++.</p>
+
+ <p>The use of the ODB compiler to generate database support code
+ adds an additional step to your application build sequence. The
+ following diagram outlines the typical build workflow of an
+ application that uses ODB:</p>
+
+ <!-- align=center is needed for html2ps -->
+ <div class="img" align="center"><img src="odb-flow.png"/></div>
+
+ <h2><a name="1.2">1.2 Benefits</a></h2>
+
+ <p>The traditional way of saving C++ objects to relational databases
+ requires that you manually write code which converts between the database
+ and C++ representations of each persistent class. The actions that
+ such code usually performs include conversion between C++ values and
+ strings or database types, preparation and execution of SQL queries,
+ as well as handling the result sets. Writing this code manually has
+ the following drawbacks:</p>
+
+ <ul class="list">
+ <li><b>Difficult and time consuming.</b> Writing database conversion
+ code for any non-trivial application requires extensive
+ knowledge of the specific database system and its APIs.
+ It can also take a considerable amount of time to write
+ and maintain. Supporting multi-threaded applications can
+ complicate this task even further.</li>
+
+ <li><b>Suboptimal performance.</b> Optimal conversion often
+ requires writing large amounts of extra code, such as
+ parameter binding for prepared statements and caching
+ of connections, statements, and buffers. Writing code
+ like this in an ad-hoc manner is often too difficult
+ and time consuming.</li>
+
+ <li><b>Database vendor lock-in.</b> The conversion code is written for
+ a specific database which makes it hard to switch to another
+ database vendor.</li>
+
+ <li><b>Lack of type safety.</b> It is easy to misspell column names or
+ pass incompatible values in SQL queries. Such errors will
+ only be detected at runtime.</li>
+
+ <li><b>Complicates the application.</b> The database conversion code
+ often ends up interspersed throughout the application making it
+ hard to debug, change, and maintain.</li>
+ </ul>
+
+ <p>In contrast, using ODB for C++ object persistence has the
+ following benefits:</p>
+
+ <ul class="list">
+ <li><b>Ease of use.</b> ODB automatically generates database conversion
+ code from your C++ class declarations and allows you to manipulate
+ persistent objects using simple and thread-safe object-oriented
+ database APIs.</li>
+
+ <li><b>Concise code.</b> With ODB hiding the details of the underlying
+ database, the application logic is written using the natural object
+ vocabulary instead of tables, columns and SQL. The resulting code
+ is simpler and thus easier to read and understand.</li>
+
+ <li><b>Optimal performance.</b> ODB has been designed for high performance
+ and low memory overhead. All the available optimization techniques,
+ such as prepared statements and extensive connection, statement,
+ and buffer caching, are used to provide the most efficient
+ implementation for each database operation.</li>
+
+ <li><b>Database portability.</b> Because the database conversion code
+ is automatically generated, it is easy to switch from one database
+ vendor to another. In fact, it is possible to test your application
+ on several database systems before making a choice.</li>
+
+ <li><b>Safety.</b> The ODB object persistence and query APIs are
+ statically typed. You use C++ identifiers instead of strings
+ to refer to object members and the generated code makes sure
+ database and C++ types are compatible. All this helps catch
+ programming errors at compile-time rather than at runtime.</li>
+
+ <li><b>Maintainability.</b> Automatic code generation minimizes the
+ effort needed to adapt the application to changes in persistent
+ classes. The database support code is kept separately from the
+ class declarations and application logic. This makes the
+ application easier to debug and maintain.</li>
+ </ul>
+
+ <p>Overall, ODB provides an easy to use yet flexible and powerful
+ object-relational mapping (ORM) system for C++. Unlike other
+ ORM implementations for C++ that still require you to write
+ database conversion or member registration code for each
+ persistent class, ODB keeps persistent classes purely
+ declarative. The functional part, the database conversion
+ code, is automatically generated by the ODB compiler from
+ these declarations.</p>
+
+ <h2><a name="1.3">1.3 Supported C++ Standards</a></h2>
+
+ <p>ODB provides support for ISO/IEC C++ 1998/2003 (C++98/03),
+ ISO/IEC TR 19768 C++ Library Extensions (C++ TR1), and
+ ISO/IEC C++ 2011 (C++11). While the majority of the examples in
+ this manual use C++98/03, support for the new functionality and
+ library components introduced in TR1 and C++11 are discussed
+ throughout the document. The <code>c++11</code> example in the
+ <code>odb-examples</code> package also shows ODB support for
+ various C++11 features.</p>
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="2">2 Hello World Example</a></h1>
+
+ <p>In this chapter we will show how to create a simple C++
+ application that relies on ODB for object persistence using
+ the traditional "Hello World" example. In particular, we will
+ discuss how to declare persistent classes, generate database
+ support code, as well as compile and run our application. We
+ will also learn how to make objects persistent, load, update
+ and delete persistent objects, as well as query the database
+ for persistent objects that match certain criteria. The example
+ also shows how to define and use views, a mechanism that allows
+ us to create projections of persistent objects, database tables,
+ or to handle results of native SQL queries or stored procedure
+ calls.</p>
+
+ <p>The code presented in this chapter is based on the
+ <code>hello</code> example which can be found in the
+ <code>odb-examples</code> package of the ODB distribution.</p>
+
+ <h2><a name="2.1">2.1 Declaring Persistent Classes</a></h2>
+
+ <p>In our "Hello World" example we will depart slightly from
+ the norm and say hello to people instead of the world. People
+ in our application will be represented as objects of C++ class
+ <code>person</code> which is saved in <code>person.hxx</code>:</p>
+
+ <pre class="cxx">
+// person.hxx
+//
+
+#include &lt;string>
+
+class person
+{
+public:
+ person (const std::string&amp; first,
+ const std::string&amp; last,
+ unsigned short age);
+
+ const std::string&amp; first () const;
+ const std::string&amp; last () const;
+
+ unsigned short age () const;
+ void age (unsigned short);
+
+private:
+ std::string first_;
+ std::string last_;
+ unsigned short age_;
+};
+ </pre>
+
+ <p>In order not to miss anyone whom we need to greet, we would like
+ to save the <code>person</code> objects in a database. To achieve this
+ we declare the <code>person</code> class as persistent:</p>
+
+ <pre class="cxx">
+// person.hxx
+//
+
+#include &lt;string>
+
+#include &lt;odb/core.hxx> // (1)
+
+#pragma db object // (2)
+class person
+{
+ ...
+
+private:
+ person () {} // (3)
+
+ friend class odb::access; // (4)
+
+ #pragma db id auto // (5)
+ unsigned long id_; // (5)
+
+ std::string first_;
+ std::string last_;
+ unsigned short age_;
+};
+ </pre>
+
+ <p>To be able to save the <code>person</code> objects in the database
+ we had to make five changes, marked with (1) to (5), to the original
+ class definition. The first change is the inclusion of the ODB
+ header <code>&lt;odb/core.hxx></code>. This header provides a number
+ of core ODB declarations, such as <code>odb::access</code>, that
+ are used to define persistent classes.</p>
+
+ <p>The second change is the addition of <code>db&nbsp;object</code>
+ pragma just before the class definition. This pragma tells the
+ ODB compiler that the class that follows is persistent. Note
+ that making a class persistent does not mean that all objects
+ of this class will automatically be stored in the database.
+ You would still create ordinary or <em>transient</em> instances
+ of this class just as you would before. The difference is that
+ now you can make such transient instances persistent, as we will
+ see shortly.</p>
+
+ <p>The third change is the addition of the default constructor.
+ The ODB-generated database support code will use this constructor
+ when instantiating an object from the persistent state. Just as we have
+ done for the <code>person</code> class, you can make the default
+ constructor private or protected if you don't want to make it
+ available to the users of your class. Note also that with some
+ limitations it is possible to have a persistent class without
+ the default constructor.</p>
+
+ <p>With the fourth change we make the <code>odb::access</code> class a
+ friend of our <code>person</code> class. This is necessary to make
+ the default constructor and the data members accessible to the
+ database support code. If your class has a public default constructor and
+ either public data members or public accessors and modifiers for the
+ data members, then the <code>friend</code> declaration is unnecessary.</p>
+
+ <p>The final change adds a data member called <code>id_</code> which
+ is preceded by another pragma. In ODB every persistent object normally
+ has a unique, within its class, identifier. Or, in other words, no two
+ persistent instances of the same type have equal identifiers. While it
+ is possible to define a persistent class without an object id, the number
+ of database operations that can be performed on such a class is limited.
+ For our class we use an integer id. The <code>db&nbsp;id auto</code>
+ pragma that precedes the <code>id_</code> member tells the ODB compiler
+ that the following member is the object's identifier. The
+ <code>auto</code> specifier indicates that it is a database-assigned
+ id. A unique id will be automatically generated by the database and
+ assigned to the object when it is made persistent.</p>
+
+ <p>In this example we chose to add an identifier because none of
+ the existing members could serve the same purpose. However, if
+ a class already has a member with suitable properties, then it
+ is natural to use that member as an identifier. For example,
+ if our <code>person</code> class contained some form of personal
+ identification (SSN in the United States or ID/passport number
+ in other countries), then we could use that as an id. Or, if
+ we stored an email associated with each person, then we could
+ have used that if each person is presumed to have a unique
+ email address.</p>
+
+ <p>As another example, consider the following alternative version
+ of the <code>person</code> class. Here we use one of
+ the existing data members as id. Also the data members are kept
+ private and are instead accessed via public accessor and modifier
+ functions. Finally, the ODB pragmas are grouped together and are
+ placed after the class definition. They could have also been moved
+ into a separate header leaving the original class completely
+ unchanged (for more information on such a non-intrusive conversion
+ refer to <a href="#14">Chapter 14, "ODB Pragma Language"</a>).</p>
+
+ <pre class="cxx">
+class person
+{
+public:
+ person ();
+
+ const std::string&amp; email () const;
+ void email (const std::string&amp;);
+
+ const std::string&amp; get_name () const;
+ std::string&amp; set_name ();
+
+ unsigned short getAge () const;
+ void setAge (unsigned short);
+
+private:
+ std::string email_;
+ std::string name_;
+ unsigned short age_;
+};
+
+#pragma db object(person)
+#pragma db member(person::email_) id
+ </pre>
+
+ <p>Now that we have the header file with the persistent class, let's
+ see how we can generate that database support code.</p>
+
+ <h2><a name="2.2">2.2 Generating Database Support Code</a></h2>
+
+ <p>The persistent class definition that we created in the previous
+ section was particularly light on any code that could actually
+ do the job and store the person's data to a database. There
+ was no serialization or deserialization code, not even data member
+ registration, that you would normally have to write by hand in
+ other ORM libraries for C++. This is because in ODB code
+ that translates between the database and C++ representations
+ of an object is automatically generated by the ODB compiler.</p>
+
+ <p>To compile the <code>person.hxx</code> header we created in the
+ previous section and generate the support code for the MySQL
+ database, we invoke the ODB compiler from a terminal (UNIX) or
+ a command prompt (Windows):</p>
+
+ <pre class="terminal">
+odb -d mysql --generate-query person.hxx
+ </pre>
+
+ <p>We will use MySQL as the database of choice in the remainder of
+ this chapter, though other supported database systems can be used
+ instead.</p>
+
+ <p>If you haven't installed the common ODB runtime library
+ (<code>libodb</code>) or installed it into a directory where
+ C++ compilers don't search for headers by default,
+ then you may get the following error:</p>
+
+ <pre class="terminal">
+person.hxx:10:24: fatal error: odb/core.hxx: No such file or directory
+ </pre>
+
+ <p>To resolve this you will need to specify the <code>libodb</code> headers
+ location with the <code>-I</code> preprocessor option, for example:</p>
+
+ <pre class="terminal">
+odb -I.../libodb -d mysql --generate-query person.hxx
+ </pre>
+
+ <p>Here <code>.../libodb</code> represents the path to the
+ <code>libodb</code> directory.</p>
+
+ <p>The above invocation of the ODB compiler produces three C++ files:
+ <code>person-odb.hxx</code>, <code>person-odb.ixx</code>,
+ <code>person-odb.cxx</code>. You normally don't use types
+ or functions contained in these files directly. Rather, all
+ you have to do is include <code>person-odb.hxx</code> in
+ C++ files where you are performing database operations
+ with classes from <code>person.hxx</code> as well as compile
+ <code>person-odb.cxx</code> and link the resulting object
+ file to your application.</p>
+
+ <p>You may be wondering what the <code>--generate-query</code>
+ option is for. It instructs the ODB compiler to generate
+ optional query support code that we will use later in our
+ "Hello World" example. Another option that we will find
+ useful is <code>--generate-schema</code>. This option
+ makes the ODB compiler generate a fourth file,
+ <code>person.sql</code>, which is the database schema
+ for the persistent classes defined in <code>person.hxx</code>:</p>
+
+ <pre class="terminal">
+odb -d mysql --generate-query --generate-schema person.hxx
+ </pre>
+
+ <p>The database schema file contains SQL statements that creates
+ tables necessary to store the persistent classes. We will learn
+ how to use it in the next section.</p>
+
+ <p>If you would like to see a list of all the available ODB compiler
+ options, refer to the
+ <a href="http://www.codesynthesis.com/products/odb/doc/odb.xhtml">ODB
+ Compiler Command Line Manual</a>.</p>
+
+ <p>Now that we have the persistent class and the database support
+ code, the only part that is left is the application code that
+ does something useful with all of this. But before we move on to
+ the fun part, let's first learn how to build and run an application
+ that uses ODB. This way when we have some application code
+ to try, there are no more delays before we can run it.</p>
+
+ <h2><a name="2.3">2.3 Compiling and Running</a></h2>
+
+ <p>Assuming that the <code>main()</code> function with the application
+ code is saved in <code>driver.cxx</code> and the database support
+ code and schema are generated as described in the previous section,
+ to build our application we will first need to compile all the C++
+ source files and then link them with two ODB runtime libraries.</p>
+
+ <p>On UNIX, the compilation part can be done with the following commands
+ (substitute <code>c++</code> with your C++ compiler name; for Microsoft
+ Visual Studio setup, see the <code>odb-examples</code> package):</p>
+
+ <pre class="terminal">
+c++ -c driver.cxx
+c++ -c person-odb.cxx
+ </pre>
+
+ <p>Similar to the ODB compilation, if you get an error stating that
+ a header in <code>odb/</code> or <code>odb/mysql</code> directory
+ is not found, you will need to use the <code>-I</code>
+ preprocessor option to specify the location of the common ODB runtime
+ library (<code>libodb</code>) and MySQL ODB runtime library
+ (<code>libodb-mysql</code>).</p>
+
+ <p>Once the compilation is done, we can link the application with
+ the following command:</p>
+
+ <pre class="terminal">
+c++ -o driver driver.o person-odb.o -lodb-mysql -lodb
+ </pre>
+
+ <p>Notice that we link our application with two ODB libraries:
+ <code>libodb</code> which is a common runtime library and
+ <code>libodb-mysql</code> which is a MySQL runtime library
+ (if you use another database, then the name of this library
+ will change accordingly). If you get an error saying that
+ one of these libraries could not be found, then you will need
+ to use the <code>-L</code> linker option to specify their locations.</p>
+
+ <p>Before we can run our application we need to create a database
+ schema using the generated <code>person.sql</code> file. For MySQL
+ we can use the <code>mysql</code> client program, for example:</p>
+
+ <pre class="terminal">
+mysql --user=odb_test --database=odb_test &lt; person.sql
+ </pre>
+
+ <p>The above command will log in to a local MySQL server as user
+ <code>odb_test</code> without a password and use the database
+ named <code>odb_test</code>. Beware that after executing this
+ command, all the data stored in the <code>odb_test</code> database
+ will be deleted.</p>
+
+ <p>Note also that using a standalone generated SQL file is not the
+ only way to create a database schema in ODB. We can also embed
+ the schema directly into our application or use custom schemas
+ that were not generated by the ODB compiler. Refer to
+ <a href="#3.4">Section 3.4, "Database"</a> for details.</p>
+
+ <p>Once the database schema is ready, we run our application
+ using the same login and database name:</p>
+
+ <pre class="terminal">
+./driver --user odb_test --database odb_test
+ </pre>
+
+
+ <h2><a name="2.4">2.4 Making Objects Persistent</a></h2>
+
+ <p>Now that we have the infrastructure work out of the way, it
+ is time to see our first code fragment that interacts with the
+ database. In this section we will learn how to make <code>person</code>
+ objects persistent:</p>
+
+ <pre class="cxx">
+// driver.cxx
+//
+
+#include &lt;memory> // std::auto_ptr
+#include &lt;iostream>
+
+#include &lt;odb/database.hxx>
+#include &lt;odb/transaction.hxx>
+
+#include &lt;odb/mysql/database.hxx>
+
+#include "person.hxx"
+#include "person-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr&lt;database> db (new odb::mysql::database (argc, argv));
+
+ unsigned long john_id, jane_id, joe_id;
+
+ // Create a few persistent person objects.
+ //
+ {
+ person john ("John", "Doe", 33);
+ person jane ("Jane", "Doe", 32);
+ person joe ("Joe", "Dirt", 30);
+
+ transaction t (db->begin ());
+
+ // Make objects persistent and save their ids for later use.
+ //
+ john_id = db->persist (john);
+ jane_id = db->persist (jane);
+ joe_id = db->persist (joe);
+
+ t.commit ();
+ }
+ }
+ catch (const odb::exception&amp; e)
+ {
+ cerr &lt;&lt; e.what () &lt;&lt; endl;
+ return 1;
+ }
+}
+ </pre>
+
+ <p>Let's examine this code piece by piece. At the beginning we include
+ a bunch of headers. After the standard C++ headers we include
+ <code>&lt;odb/database.hxx></code>
+ and <code>&lt;odb/transaction.hxx></code> which define database
+ system-independent <code>odb::database</code> and
+ <code>odb::transaction</code> interfaces. Then we include
+ <code>&lt;odb/mysql/database.hxx></code> which defines the
+ MySQL implementation of the <code>database</code> interface. Finally,
+ we include <code>person.hxx</code> and <code>person-odb.hxx</code>
+ which define our persistent <code>person</code> class.</p>
+
+ <p>Then we have two <code>using namespace</code> directives. The first
+ one brings in the names from the standard namespace and the second
+ brings in the ODB declarations which we will use later in the file.
+ Notice that in the second directive we use the <code>odb::core</code>
+ namespace instead of just <code>odb</code>. The former only brings
+ into the current namespace the essential ODB names, such as the
+ <code>database</code> and <code>transaction</code> classes, without
+ any of the auxiliary objects. This minimizes the likelihood of name
+ conflicts with other libraries. Note also that you should continue
+ using the <code>odb</code> namespace when qualifying individual names.
+ For example, you should write <code>odb::database</code>, not
+ <code>odb::core::database</code>.</p>
+
+ <p>Once we are in <code>main()</code>, the first thing we do is create
+ the MySQL database object. Notice that this is the last line in
+ <code>driver.cxx</code> that mentions MySQL explicitly; the rest
+ of the code works through the common interfaces and is database
+ system-independent. We use the <code>argc</code>/<code>argv</code>
+ <code>mysql::database</code> constructor which automatically
+ extract the database parameters, such as login name, password,
+ database name, etc., from the command line. In your own applications
+ you may prefer to use other <code>mysql::database</code>
+ constructors which allow you to pass this information directly
+ (<a href="#17.2">Section 17.2, "MySQL Database Class"</a>).</p>
+
+ <p>Next, we create three <code>person</code> objects. Right now they are
+ transient objects, which means that if we terminate the application
+ at this point, they will be gone without any evidence of them ever
+ existing. The next line starts a database transaction. We discuss
+ transactions in detail later in this manual. For now, all we need
+ to know is that all ODB database operations must be performed within
+ a transaction and that a transaction is an atomic unit of work; all
+ database operations performed within a transaction either succeed
+ (committed) together or are automatically undone (rolled back).</p>
+
+ <p>Once we are in a transaction, we call the <code>persist()</code>
+ database function on each of our <code>person</code> objects.
+ At this point the state of each object is saved in the database.
+ However, note that this state is not permanent until and unless
+ the transaction is committed. If, for example, our application
+ crashes at this point, there will still be no evidence of our
+ objects ever existing.</p>
+
+ <p>In our case, one more thing happens when we call <code>persist()</code>.
+ Remember that we decided to use database-assigned identifiers for our
+ <code>person</code> objects. The call to <code>persist()</code> is
+ where this assignment happens. Once this function returns, the
+ <code>id_</code> member contains this object's unique identifier.
+ As a convenience, the <code>persist()</code> function also returns
+ a copy of the object's identifier that it made persistent. We
+ save the returned identifier for each object in a local variable.
+ We will use these identifiers later in the chapter to perform other
+ database operations on our persistent objects.</p>
+
+ <p>After we have persisted our objects, it is time to commit the
+ transaction and make the changes permanent. Only after the
+ <code>commit()</code> function returns successfully, are we
+ guaranteed that the objects are made persistent. Continuing
+ with the crash example, if our application terminates after
+ the commit for whatever reason, the objects' state in the
+ database will remain intact. In fact, as we will discover
+ shortly, our application can be restarted and load the
+ original objects from the database. Note also that a
+ transaction must be committed explicitly with the
+ <code>commit()</code> call. If the <code>transaction</code>
+ object leaves scope without the transaction being
+ explicitly committed or rolled back, it will automatically be
+ rolled back. This behavior allows you not to worry about
+ exceptions being thrown within a transaction; if they
+ cross the transaction boundary, the transaction will
+ automatically be rolled back and all the changes made
+ to the database undone.</p>
+
+ <p>The final bit of code in our example is the <code>catch</code>
+ block that handles the database exceptions. We do this by catching
+ the base ODB exception (<a href="#3.14">Section 3.14, "ODB
+ Exceptions"</a>) and printing the diagnostics.</p>
+
+ <p>Let's now compile (<a href="#2.3">Section 2.3, "Compiling and
+ Running"</a>) and then run our first ODB application:</p>
+
+ <pre class="terminal">
+mysql --user=odb_test --database=odb_test &lt; person.sql
+./driver --user odb_test --database odb_test
+ </pre>
+
+ <p>Our first application doesn't print anything except for error
+ messages so we can't really tell whether it actually stored the
+ objects' state in the database. While we will make our application
+ more entertaining shortly, for now we can use the <code>mysql</code>
+ client to examine the database content. It will also give us a feel
+ for how the objects are stored:</p>
+
+ <pre class="terminal">
+mysql --user=odb_test --database=odb_test
+
+Welcome to the MySQL monitor.
+
+mysql> select * from person;
+
++----+-------+------+-----+
+| id | first | last | age |
++----+-------+------+-----+
+| 1 | John | Doe | 33 |
+| 2 | Jane | Doe | 32 |
+| 3 | Joe | Dirt | 30 |
++----+-------+------+-----+
+3 rows in set (0.00 sec)
+
+mysql> quit
+ </pre>
+
+ <p>Another way to get more insight into what's going on under the hood,
+ is to trace the SQL statements executed by ODB as a result of
+ each database operation. Here is how we can enable tracing just for
+ the duration of our transaction:</p>
+
+ <pre class="cxx">
+ // Create a few persistent person objects.
+ //
+ {
+ ...
+
+ transaction t (db->begin ());
+
+ t.tracer (stderr_tracer);
+
+ // Make objects persistent and save their ids for later use.
+ //
+ john_id = db->persist (john);
+ jane_id = db->persist (jane);
+ joe_id = db->persist (joe);
+
+ t.commit ();
+ }
+ </pre>
+
+ <p>With this modification our application now produces the following
+ output:</p>
+
+ <pre class="terminal">
+INSERT INTO `person` (`id`,`first`,`last`,`age`) VALUES (?,?,?,?)
+INSERT INTO `person` (`id`,`first`,`last`,`age`) VALUES (?,?,?,?)
+INSERT INTO `person` (`id`,`first`,`last`,`age`) VALUES (?,?,?,?)
+ </pre>
+
+ <p>Note that we see question marks instead of the actual values
+ because ODB uses prepared statements and sends the data to the
+ database in binary form. For more information on tracing, refer
+ to <a href="#3.13">Section 3.13, "Tracing SQL Statement Execution"</a>.
+ In the next section we will see how to access persistent objects
+ from our application.</p>
+
+ <h2><a name="2.5">2.5 Querying the Database for Objects</a></h2>
+
+ <p>So far our application doesn't resemble a typical "Hello World"
+ example. It doesn't print anything except for error messages.
+ Let's change that and teach our application to say hello to
+ people from our database. To make it a bit more interesting,
+ let's say hello only to people over 30:</p>
+
+ <pre class="cxx">
+// driver.cxx
+//
+
+...
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ ...
+
+ // Create a few persistent person objects.
+ //
+ {
+ ...
+ }
+
+ typedef odb::query&lt;person> query;
+ typedef odb::result&lt;person> result;
+
+ // Say hello to those over 30.
+ //
+ {
+ transaction t (db->begin ());
+
+ result r (db->query&lt;person> (query::age > 30));
+
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ cout &lt;&lt; "Hello, " &lt;&lt; i->first () &lt;&lt; "!" &lt;&lt; endl;
+ }
+
+ t.commit ();
+ }
+ }
+ catch (const odb::exception&amp; e)
+ {
+ cerr &lt;&lt; e.what () &lt;&lt; endl;
+ return 1;
+ }
+}
+ </pre>
+
+ <p>The first half of our application is the same as before and is
+ replaced with "..." in the above listing for brevity. Again, let's
+ examine the rest of it piece by piece.</p>
+
+ <p>The two <code>typedef</code>s create convenient aliases for two
+ template instantiations that will be used a lot in our application.
+ The first is the query type for the <code>person</code> objects
+ and the second is the result type for that query.</p>
+
+ <p>Then we begin a new transaction and call the <code>query()</code>
+ database function. We pass a query expression
+ (<code>query::age > 30</code>) which limits the returned objects
+ only to those with the age greater than 30. We also save the result
+ of the query in a local variable.</p>
+
+ <p>The next few lines perform a standard for-loop iteration
+ over the result sequence printing hello for every returned person.
+ Then we commit the transaction and that's it. Let's see what
+ this application will print:</p>
+
+ <pre class="terminal">
+mysql --user=odb_test --database=odb_test &lt; person.sql
+./driver --user odb_test --database odb_test
+
+Hello, John!
+Hello, Jane!
+ </pre>
+
+
+ <p>That looks about right, but how do we know that the query actually
+ used the database instead of just using some in-memory artifacts of
+ the earlier <code>persist()</code> calls? One way to test this
+ would be to comment out the first transaction in our application
+ and re-run it without re-creating the database schema. This way the
+ objects that were persisted during the previous run will be returned.
+ Alternatively, we can just re-run the same application without
+ re-creating the schema and notice that we now show duplicate
+ objects:</p>
+
+ <pre class="terminal">
+./driver --user odb_test --database odb_test
+
+Hello, John!
+Hello, Jane!
+Hello, John!
+Hello, Jane!
+ </pre>
+
+ <p>What happens here is that the previous run of our application
+ persisted a set of <code>person</code> objects and when we re-run
+ the application, we persist another set with the same names but
+ with different ids. When we later run the query, matches from
+ both sets are returned. We can change the line where we print
+ the "Hello" string as follows to illustrate this point:</p>
+
+ <pre class="cxx">
+cout &lt;&lt; "Hello, " &lt;&lt; i->first () &lt;&lt; " (" &lt;&lt; i->id () &lt;&lt; ")!" &lt;&lt; endl;
+ </pre>
+
+ <p>If we now re-run this modified program, again without re-creating
+ the database schema, we will get the following output:</p>
+
+ <pre class="terminal">
+./driver --user odb_test --database odb_test
+
+Hello, John (1)!
+Hello, Jane (2)!
+Hello, John (4)!
+Hello, Jane (5)!
+Hello, John (7)!
+Hello, Jane (8)!
+ </pre>
+
+ <p>The identifiers 3, 6, and 9 that are missing from the above list belong
+ to the "Joe Dirt" objects which are not selected by this query.</p>
+
+ <h2><a name="2.6">2.6 Updating Persistent Objects</a></h2>
+
+ <p>While making objects persistent and then selecting some of them using
+ queries are two useful operations, most applications will also need
+ to change the object's state and then make these changes persistent.
+ Let's illustrate this by updating Joe's age who just had a birthday:</p>
+
+ <pre class="cxx">
+// driver.cxx
+//
+
+...
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ ...
+
+ unsigned long john_id, jane_id, joe_id;
+
+ // Create a few persistent person objects.
+ //
+ {
+ ...
+
+ // Save object ids for later use.
+ //
+ john_id = john.id ();
+ jane_id = jane.id ();
+ joe_id = joe.id ();
+ }
+
+ // Joe Dirt just had a birthday, so update his age.
+ //
+ {
+ transaction t (db->begin ());
+
+ auto_ptr&lt;person> joe (db->load&lt;person> (joe_id));
+ joe->age (joe->age () + 1);
+ db->update (*joe);
+
+ t.commit ();
+ }
+
+ // Say hello to those over 30.
+ //
+ {
+ ...
+ }
+ }
+ catch (const odb::exception&amp; e)
+ {
+ cerr &lt;&lt; e.what () &lt;&lt; endl;
+ return 1;
+ }
+}
+ </pre>
+
+ <p>The beginning and the end of the new transaction are the same as
+ the previous two. Once within a transaction, we call the
+ <code>load()</code> database function to instantiate a
+ <code>person</code> object with Joe's persistent state. We
+ pass Joe's object identifier that we stored earlier when we
+ made this object persistent. While here we use
+ <code>std::auto_ptr</code> to manage the returned object, we
+ could have also used another smart pointer, for example
+ <code>std::unique_ptr</code> from C++11 or <code>shared_ptr</code>
+ from TR1, C++11, or Boost. For more information
+ on the object lifetime management and the smart pointers that we
+ can use for that, see <a href="#3.3">Section 3.3, "Object
+ and View Pointers"</a>.</p>
+
+ <p>With the instantiated object in hand we increment the age
+ and call the <code>update()</code> function to update
+ the object's state in the database. Once the transaction is
+ committed, the changes are made permanent.</p>
+
+ <p>If we now run this application, we will see Joe in the output
+ since he is now over 30:</p>
+
+ <pre class="terminal">
+mysql --user=odb_test --database=odb_test &lt; person.sql
+./driver --user odb_test --database odb_test
+
+Hello, John!
+Hello, Jane!
+Hello, Joe!
+ </pre>
+
+ <p>What if we didn't have an identifier for Joe? Maybe this object
+ was made persistent in another run of our application or by another
+ application altogether. Provided that we only have one Joe Dirt
+ in the database, we can use the query facility to come up with
+ an alternative implementation of the above transaction:</p>
+
+ <pre class="cxx">
+ // Joe Dirt just had a birthday, so update his age. An
+ // alternative implementation without using the object id.
+ //
+ {
+ transaction t (db->begin ());
+
+ // Here we know that there can be only one Joe Dirt in our
+ // database so we use the query_one() shortcut instead of
+ // manually iterating over the result returned by query().
+ //
+ auto_ptr&lt;person> joe (
+ db->query_one&lt;person> (query::first == "Joe" &amp;&amp;
+ query::last == "Dirt"));
+
+ if (joe.get () != 0)
+ {
+ joe->age (joe->age () + 1);
+ db->update (*joe);
+ }
+
+ t.commit ();
+ }
+ </pre>
+
+ <h2><a name="2.7">2.7 Defining and Using Views</a></h2>
+
+ <p>Suppose that we need to gather some basic statistics about the people
+ stored in our database. Things like the total head count, as well as
+ the minimum and maximum ages. One way to do it would be to query
+ the database for all the <code>person</code> objects and then
+ calculate this information as we iterate over the query result.
+ While this approach may work fine for our database with just three
+ people in it, it would be very inefficient if we had a large
+ number of objects.</p>
+
+ <p>While it may not be conceptually pure from the object-oriented
+ programming point of view, a relational database can perform
+ some computations much faster and much more economically than
+ if we performed the same operations ourselves in the application's
+ process.</p>
+
+ <p>To support such cases ODB provides the notion of views. An ODB view
+ is a C++ <code>class</code> that embodies a light-weight, read-only
+ projection of one or more persistent objects or database tables or
+ the result of a native SQL query execution or stored procedure
+ call.</p>
+
+ <p>Some of the common applications of views include loading a subset of
+ data members from objects or columns database tables, executing and
+ handling results of arbitrary SQL queries, including aggregate
+ queries, as well as joining multiple objects and/or database
+ tables using object relationships or custom join conditions.</p>
+
+ <p>While you can find a much more detailed description of views in
+ <a href="#10">Chapter 10, "Views"</a>, here is how we can define
+ the <code>person_stat</code> view that returns the basic statistics
+ about the <code>person</code> objects:</p>
+
+ <pre class="cxx">
+#pragma db view object(person)
+struct person_stat
+{
+ #pragma db column("count(" + person::id_ + ")")
+ std::size_t count;
+
+ #pragma db column("min(" + person::age_ + ")")
+ unsigned short min_age;
+
+ #pragma db column("max(" + person::age_ + ")")
+ unsigned short max_age;
+};
+ </pre>
+
+ <p>Normally, to get the result of a view we use the same
+ <code>query()</code> function as when querying the database for
+ an object. Here, however, we are executing an aggregate query
+ which always returns exactly one element. Therefore, instead
+ of getting the result instance and then iterating over it, we
+ can use the shortcut <code>query_value()</code> function. Here is
+ how we can load and print our statistics using the view we have
+ just created:</p>
+
+ <pre class="cxx">
+ // Print some statistics about all the people in our database.
+ //
+ {
+ transaction t (db->begin ());
+
+ // The result of this query always has exactly one element.
+ //
+ person_stat ps (db->query_value&lt;person_stat> ());
+
+ cout &lt;&lt; "count : " &lt;&lt; ps.count &lt;&lt; endl
+ &lt;&lt; "min age: " &lt;&lt; ps.min_age &lt;&lt; endl
+ &lt;&lt; "max age: " &lt;&lt; ps.max_age &lt;&lt; endl;
+
+ t.commit ();
+ }
+ </pre>
+
+ <p>If we now add the <code>person_stat</code> view to the
+ <code>person.hxx</code> header, the above transaction
+ to <code>driver.cxx</code>, as well as re-compile and
+ re-run our example, then we will see the following
+ additional lines in the output:</p>
+
+ <pre class="term">
+count : 3
+min age: 31
+max age: 33
+ </pre>
+
+ <h2><a name="2.8">2.8 Deleting Persistent Objects</a></h2>
+
+ <p>The last operation that we will discuss in this chapter is deleting
+ the persistent object from the database. The following code
+ fragment shows how we can delete an object given its identifier:</p>
+
+ <pre class="cxx">
+ // John Doe is no longer in our database.
+ //
+ {
+ transaction t (db->begin ());
+ db->erase&lt;person> (john_id);
+ t.commit ();
+ }
+ </pre>
+
+ <p>To delete John from the database we start a transaction, call
+ the <code>erase()</code> database function with John's object
+ id, and commit the transaction. After the transaction is committed,
+ the erased object is no longer persistent.</p>
+
+ <p>If we don't have an object id handy, we can use queries to find
+ and delete the object:</p>
+
+ <pre class="cxx">
+ // John Doe is no longer in our database. An alternative
+ // implementation without using the object id.
+ //
+ {
+ transaction t (db->begin ());
+
+ // Here we know that there can be only one John Doe in our
+ // database so we use the query_one() shortcut again.
+ //
+ auto_ptr&lt;person> john (
+ db->query_one&lt;person> (query::first == "John" &amp;&amp;
+ query::last == "Doe"));
+
+ if (john.get () != 0)
+ db->erase (*john);
+
+ t.commit ();
+ }
+ </pre>
+
+ <h2><a name="2.9">2.9 Changing Persistent Classes</a></h2>
+
+ <p>When the definition of a transient C++ class is changed, for
+ example by adding or deleting a data member, we don't have to
+ worry about any existing instances of this class not matching
+ the new definition. After all, to make the class changes
+ effective we have to restart the application and none of the
+ transient instances will survive this.</p>
+
+ <p>Things are not as simple for persistent classes. Because they
+ are stored in the database and therefore survive application
+ restarts, we have a new problem: what happens to the state of
+ existing objects (which correspond to the old definition) once
+ we change our persistent class?</p>
+
+ <p>The problem of working with old objects, called <em>database
+ schema evolution</em>, is a complex issue and ODB provides
+ comprehensive support for handling it. While this support
+ is covered in detail in <a href="#13">Chapter 13,
+ "Database Schema Evolution"</a>, let us consider a simple
+ example that should give us a sense of the functionality
+ provided by ODB in this area.</p>
+
+ <p>Suppose that after using our <code>person</code> persistent
+ class for some time and creating a number of databases
+ containing its instances, we realized that for some people
+ we also need to store their middle name. If we go ahead and
+ just add the new data member, everything will work fine
+ with new databases. Existing databases, however, have a
+ table that does not correspond to the new class definition.
+ Specifically, the generated database support code now
+ expects there to be a column to store the middle name.
+ But such a column was never created in the old databases.</p>
+
+ <p>ODB can automatically generate SQL statements that will
+ migrate old databases to match the new class definitions.
+ But first, we need to enable schema evolution support by
+ defining a version for our object model:</p>
+
+ <pre class="cxx">
+// person.hxx
+//
+
+#pragma db model version(1, 1)
+
+class person
+{
+ ...
+
+ std::string first_;
+ std::string last_;
+ unsigned short age_;
+};
+ </pre>
+
+ <p>The first number in the <code>version</code> pragma is the
+ base model version. This is the lowest version we will be
+ able to migrate from. The second number is the current model
+ version. Since we haven't made any changes yet to our
+ persistent class, both of these values are <code>1</code>.</p>
+
+ <p>Next we need to re-compile our <code>person.hxx</code> header
+ file with the ODB compiler, just as we did before:</p>
+
+ <pre class="terminal">
+odb -d mysql --generate-query --generate-schema person.hxx
+ </pre>
+
+ <p>If we now look at the list of files produced by the ODB compiler,
+ we will notice a new file: <code>person.xml</code>. This
+ is a changelog file where the ODB compiler keeps track of the
+ database changes corresponding to our class changes. Note that
+ this file is automatically maintained by the ODB compiler and
+ all we have to do is keep it around between re-compilations.</p>
+
+ <p>Now we are ready to add the middle name to our <code>person</code>
+ class. We also give it a default value (empty string) which
+ is what will be assigned to existing objects in old databases.
+ Notice that we have also incremented the current version:</p>
+
+ <pre class="cxx">
+// person.hxx
+//
+
+#pragma db model version(1, 2)
+
+class person
+{
+ ...
+
+ std::string first_;
+
+ #pragma db default("")
+ std::string middle_;
+
+ std::string last_;
+ unsigned short age_;
+};
+ </pre>
+
+ <p>If we now recompile the <code>person.hxx</code> header again, we will
+ see two extra generated files: <code>person-002-pre.sql</code>
+ and <code>person-002-post.sql</code>. These two files contain
+ schema migration statements from version <code>1</code> to
+ version <code>2</code>. Similar to schema creation, schema
+ migration statements can also be embedded into the generated
+ C++ code.</p>
+
+ <p><code>person-002-pre.sql</code> and <code>person-002-post.sql</code>
+ are the pre and post schema migration files. To migrate
+ one of our old databases, we first execute the pre migration
+ file:</p>
+
+ <pre class="terminal">
+mysql --user=odb_test --database=odb_test &lt; person-002-pre.sql
+ </pre>
+
+ <p>Between the pre and post schema migrations we can run data
+ migration code, if required. At this stage, we can both
+ access the old and store the new data. In our case we don't
+ need any data migration code since we assigned the default
+ value to the middle name for all the existing objects.</p>
+
+ <p>To finish the migration process we execute the post migration
+ statements:</p>
+
+ <pre class="terminal">
+mysql --user=odb_test --database=odb_test &lt; person-002-post.sql
+ </pre>
+
+ <h2><a name="2.10">2.10 Working with Multiple Databases</a></h2>
+
+ <p>Accessing multiple databases (that is, data stores) is simply a
+ matter of creating multiple <code>odb::&lt;db>::database</code>
+ instances representing each database. For example:</p>
+
+ <pre class="cxx">
+odb::mysql::database db1 ("john", "secret", "test_db1");
+odb::mysql::database db2 ("john", "secret", "test_db2");
+ </pre>
+
+ <p>Some database systems also allow attaching multiple databases to
+ the same instance. A more interesting question is how we access
+ multiple database systems (that is, database implementations) from
+ the same application. For example, our application may need to store
+ some objects in a remote MySQL database and others in a local SQLite
+ file. Or, our application may need to be able to store its objects
+ in a database system that is selected by the user at runtime.</p>
+
+ <p>ODB provides comprehensive multi-database support that ranges from
+ tight integration with specific database systems to being able to
+ write database-agnostic code and loading individual database systems
+ support dynamically. While all these aspects are covered in detail
+ in <a href="#16">Chapter 16, "Multi-Database Support"</a>, in this
+ section we will get a taste of this functionality by extending our
+ "Hello World" example to be able to store its data either in MySQL
+ or PostgreSQL (other database systems supported by ODB can be added
+ in a similar manner).</p>
+
+ <p>The first step in adding multi-database support is to re-compile
+ our <code>person.hxx</code> header to generate database support
+ code for additional database systems:</p>
+
+ <pre class="terminal">
+odb --multi-database dynamic -d common -d mysql -d pgsql \
+--generate-query --generate-schema person.hxx
+ </pre>
+
+ <p>The <code>--multi-database</code> ODB compiler option turns on
+ multi-database support. For now it is not important what the
+ <code>dynamic</code> value that we passed to this option means, but
+ if you are curious, see <a href="#16">Chapter 16</a>. The result of this
+ command are three sets of generated files: <code>person-odb.?xx</code>
+ (common interface; corresponds to the <code>common</code> database),
+ <code>person-odb-mysql.?xx</code> (MySQL support code), and
+ <code>person-odb-pgsql.?xx</code> (PostgreSQL support code). There
+ are also two schema files: <code>person-mysql.sql</code> and
+ <code>person-pgsql.sql</code>.</p>
+
+ <p>The only part that we need to change in <code>driver.cxx</code>
+ is how we create the database instance. Specifically, this line:</p>
+
+ <pre class="cxx">
+auto_ptr&lt;database> db (new odb::mysql::database (argc, argv));
+ </pre>
+
+ <p>Now our example is capable of storing its data either in MySQL or
+ PostgreSQL so we need to somehow allow the caller to specify which
+ database we must use. To keep things simple, we will make the first
+ command line argument specify the database system we must use while
+ the rest will contain the database-specific options which we will
+ pass to the <code>odb::&lt;db>::database</code> constructor as
+ before. Let's put all this logic into a separate function which we
+ will call <code>create_database()</code>. Here is what the beginning
+ of our modified <code>driver.cxx</code> will look like (the remainder
+ is unchanged):</p>
+
+ <pre class="cxx">
+// driver.cxx
+//
+
+#include &lt;string>
+#include &lt;memory> // std::auto_ptr
+#include &lt;iostream>
+
+#include &lt;odb/database.hxx>
+#include &lt;odb/transaction.hxx>
+
+#include &lt;odb/mysql/database.hxx>
+#include &lt;odb/pgsql/database.hxx>
+
+#include "person.hxx"
+#include "person-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+auto_ptr&lt;database>
+create_database (int argc, char* argv[])
+{
+ auto_ptr&lt;database> r;
+
+ if (argc &lt; 2)
+ {
+ cerr &lt;&lt; "error: database system name expected" &lt;&lt; endl;
+ return r;
+ }
+
+ string db (argv[1]);
+
+ if (db == "mysql")
+ r.reset (new odb::mysql::database (argc, argv));
+ else if (db == "pgsql")
+ r.reset (new odb::pgsql::database (argc, argv));
+ else
+ cerr &lt;&lt; "error: unknown database system " &lt;&lt; db &lt;&lt; endl;
+
+ return r;
+}
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr&lt;database> db (create_database (argc, argv));
+
+ if (db.get () == 0)
+ return 1; // Diagnostics has already been issued.
+
+ ...
+ </pre>
+
+ <p>And that's it. The only thing left is to build and run our
+ example:</p>
+
+ <pre class="terminal">
+c++ -c driver.cxx
+c++ -c person-odb.cxx
+c++ -c person-odb-mysql.cxx
+c++ -c person-odb-pgsql.cxx
+c++ -o driver driver.o person-odb.o person-odb-mysql.o \
+person-odb-pgsql.o -lodb-mysql -lodb-pgsql -lodb
+ </pre>
+
+ <p>Here is how we can access a MySQL database:</p>
+
+ <pre class="terminal">
+mysql --user=odb_test --database=odb_test &lt; person-mysql.sql
+./driver mysql --user odb_test --database odb_test
+ </pre>
+
+ <p>Or a PostgreSQL database:</p>
+
+ <pre class="terminal">
+psql --user=odb_test --dbname=odb_test -f person-pgsql.sql
+./driver pgsql --user odb_test --database odb_test
+ </pre>
+
+ <h2><a name="2.11">2.11 Summary</a></h2>
+
+ <p>This chapter presented a very simple application which, nevertheless,
+ exercised all of the core database functions: <code>persist()</code>,
+ <code>query()</code>, <code>load()</code>, <code>update()</code>,
+ and <code>erase()</code>. We also saw that writing an application
+ that uses ODB involves the following steps:</p>
+
+ <ol>
+ <li>Declare persistent classes in header files.</li>
+ <li>Compile these headers to generate database support code.</li>
+ <li>Link the application with the generated code and two ODB runtime
+ libraries.</li>
+ </ol>
+
+ <p>Do not be concerned if, at this point, much appears unclear. The intent
+ of this chapter is to give you only a general idea of how to persist C++
+ objects with ODB. We will cover all the details throughout the remainder
+ of this manual.</p>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="3">3 Working with Persistent Objects</a></h1>
+
+ <p>The previous chapters gave us a high-level overview of ODB and
+ showed how to use it to store C++ objects in a database. In this
+ chapter we will examine the ODB object persistence model as
+ well as the core database APIs in greater detail. We will
+ start with basic concepts and terminology in <a href="#3.1">Section
+ 3.1</a> and <a href="#3.3">Section 3.3</a> and continue with the
+ discussion of the <code>odb::database</code> class in
+ <a href="#3.4">Section 3.4</a>, transactions in
+ <a href="#3.5">Section 3.5</a>, and connections in
+ <a href="#3.6">Section 3.6</a>. The remainder of this chapter
+ deals with the core database operations and concludes with
+ the discussion of ODB exceptions.</p>
+
+ <p>In this chapter we will continue to use and expand the
+ <code>person</code> persistent class that we have developed in the
+ previous chapter.</p>
+
+ <h2><a name="3.1">3.1 Concepts and Terminology</a></h2>
+
+ <p>The term <em>database</em> can refer to three distinct things:
+ a general notion of a place where an application stores its data,
+ a software implementation for managing this data (for example
+ MySQL), and, finally, some database software implementations
+ may manage several data stores which are usually distinguished
+ by name. This name is also commonly referred to as a database.</p>
+
+ <p>In this manual, when we use the word <em>database</em>, we
+ refer to the first meaning above, for example,
+ "The <code>update()</code> function saves the object's state to
+ the database." The term Database Management System (DBMS) is
+ often used to refer to the second meaning of the word database.
+ In this manual we will use the term <em>database system</em>
+ for short, for example, "Database system-independent
+ application code." Finally, to distinguish the third meaning
+ from the other two, we will use the term <em>database name</em>,
+ for example, "The second option specifies the database name
+ that the application should use to store its data."</p>
+
+ <p>In C++ there is only one notion of a type and an instance
+ of a type. For example, a fundamental type, such as <code>int</code>,
+ is, for the most part, treated the same as a user defined class
+ type. However, when it comes to persistence, we have to place
+ certain restrictions and requirements on certain C++ types that
+ can be stored in the database. As a result, we divide persistent
+ C++ types into two groups: <em>object types</em> and <em>value
+ types</em>. An instance of an object type is called an <em>object</em>
+ and an instance of a value type &mdash; a <em>value</em>.</p>
+
+ <p>An object is an independent entity. It can be stored, updated,
+ and deleted in the database independent of other objects.
+ Normally, an object has an identifier, called <em>object id</em>,
+ that is unique among all instances of an object type within a
+ database. In contrast, a value can only be stored in the database
+ as part of an object and doesn't have its own unique identifier.</p>
+
+ <p>An object consists of data members which are either values
+ (<a href="#7">Chapter 7, "Value Types"</a>), pointers
+ to other objects (<a href="#6">Chapter 6, "Relationships"</a>), or
+ containers of values or pointers to other objects (<a href="#5">Chapter
+ 5, "Containers")</a>. Pointers to other objects and containers can
+ be viewed as special kinds of values since they also can only
+ be stored in the database as part of an object. Static data members
+ are not stored in the database.</p>
+
+ <p>An object type is a C++ class. Because of this one-to-one
+ relationship, we will use terms <em>object type</em>
+ and <em>object class</em> interchangeably. In contrast,
+ a value type can be a fundamental C++ type, such as
+ <code>int</code> or a class type, such as <code>std::string</code>.
+ If a value consists of other values, then it is called a
+ <em>composite value</em> and its type &mdash; a
+ <em>composite value type</em> (<a href="#7.2">Section 7.2,
+ "Composite Value Types"</a>). Otherwise, the value is
+ called <em>simple value</em> and its type &mdash; a
+ <em>simple value type</em> (<a href="#7.1">Section 7.1,
+ "Simple Value Types"</a>). Note that the distinction between
+ simple and composite values is conceptual rather than
+ representational. For example, <code>std::string</code>
+ is a simple value type because conceptually string is a
+ single value even though the representation of the string
+ class may contain several data members each of which could be
+ considered a value. In fact, the same value type can be
+ viewed (and mapped) as both simple and composite by different
+ applications.</p>
+
+ <p>While not strictly necessary in a purely object-oriented application,
+ practical considerations often require us to only load a
+ subset of an object's data members or a combination of members
+ from several objects. We may also need to factor out some
+ computations to the relational database instead of performing
+ them in the application's process. To support such requirements
+ ODB distinguishes a third kind of C++ types, called <em>views</em>
+ (<a href="#10">Chapter 10, "Views"</a>). An ODB view is a C++
+ <code>class</code> that embodies a light-weight, read-only
+ projection of one or more persistent objects or database
+ tables or the result of a native SQL query execution.</p>
+
+ <p>Understanding how all these concepts map to the relational model
+ will hopefully make these distinctions clearer. In a relational
+ database an object type is mapped to a table and a value type is
+ mapped to one or more columns. A simple value type is mapped
+ to a single column while a composite value type is mapped to
+ several columns. An object is stored as a row in this
+ table and a value is stored as one or more cells in this row.
+ A simple value is stored in a single cell while a composite
+ value occupies several cells. A view is not a persistent
+ entity and it is not stored in the database. Rather, it is a
+ data structure that is used to capture a single row of an SQL
+ query result.</p>
+
+ <p>Going back to the distinction between simple and composite
+ values, consider a date type which has three integer
+ members: year, month, and day. In one application it can be
+ considered a composite value and each member will get its
+ own column in a relational database. In another application
+ it can be considered a simple value and stored in a single
+ column as a number of days from some predefined date.</p>
+
+ <p>Until now, we have been using the term <em>persistent class</em>
+ to refer to object classes. We will continue to do so even though
+ a value type can also be a class. The reason for this asymmetry
+ is the subordinate nature of value types when it comes to
+ database operations. Remember that values are never stored
+ directly but rather as part of an object that contains them.
+ As a result, when we say that we want to make a C++ class
+ persistent or persist an instance of a class in the database,
+ we invariably refer to an object class rather than a value
+ class.</p>
+
+ <p>Normally, you would use object types to model real-world entities,
+ things that have their own identity. For example, in the
+ previous chapter we created a <code>person</code> class to model
+ a person, which is a real-world entity. Name and age, which we
+ used as data members in our <code>person</code> class are clearly
+ values. It is hard to think of age 31 or name "Joe" as having their
+ own identities.</p>
+
+ <p>A good test to determine whether something is an object or
+ a value, is to consider if other objects might reference
+ it. A person is clearly an object because it can be referred
+ to by other objects such as a spouse, an employer, or a
+ bank. On the other hand, a person's age or name is not
+ something that other objects would normally refer to.</p>
+
+ <p>Also, when an object represents a real entity, it is easy to
+ choose a suitable object id. For example, for a
+ person there is an established notion of an identifier
+ (SSN, student id, passport number, etc). Another alternative
+ is to use a person's email address as an identifier.</p>
+
+ <p>Note, however, that these are only guidelines. There could
+ be good reasons to make something that would normally be
+ a value an object. Consider, for example, a database that
+ stores a vast number of people. Many of the <code>person</code>
+ objects in this database have the same names and surnames and
+ the overhead of storing them in every object may negatively
+ affect the performance. In this case, we could make the first name
+ and last name each an object and only store pointers to
+ these objects in the <code>person</code> class.</p>
+
+ <p>An instance of a persistent class can be in one of two states:
+ <em>transient</em> and <em>persistent</em>. A transient
+ instance only has a representation in the application's
+ memory and will cease to exist when the application terminates,
+ unless it is explicitly made persistent. In other words, a
+ transient instance of a persistent class behaves just like an
+ instance of any ordinary C++ class. A persistent instance
+ has a representation in both the application's memory and the
+ database. A persistent instance will remain even after the
+ application terminates unless and until it is explicitly
+ deleted from the database.</p>
+
+ <h2><a name="3.2">3.2 Declaring Persistent Objects and Values</a></h2>
+
+ <p>To make a C++ class a persistent object class we declare
+ it as such using the <code>db&nbsp;object</code> pragma, for
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>The other pragma that we often use is <code>db&nbsp;id</code>
+ which designates one of the data members as an object id, for
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+};
+ </pre>
+
+ <p>The object id can be of a simple or composite (<a href="#7.2.1">Section
+ 7.2.1, "Composite Object Ids"</a>) value type. This type should be
+ default-constructible, copy-constructible, and copy-assignable. It
+ is also possible to declare a persistent class without an object id,
+ however, such a class will have limited functionality
+ (<a href="#14.1.6">Section 14.1.6, "<code>no_id</code>"</a>).</p>
+
+ <p>The above two pragmas are the minimum required to declare a
+ persistent class with an object id. Other pragmas can be used to
+ fine-tune the database-related properties of a class and its
+ members (<a href="#14">Chapter 14, "ODB Pragma Language"</a>).</p>
+
+ <p>Normally, a persistent class should define the default constructor. The
+ generated database support code uses this constructor when
+ instantiating an object from the persistent state. If we add the
+ default constructor only for the database support code, then we
+ can make it private provided we also make the <code>odb::access</code>
+ class, defined in the <code>&lt;odb/core.hxx></code> header, a
+ friend of this object class. For example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/core.hxx>
+
+#pragma db object
+class person
+{
+ ...
+
+private:
+ friend class odb::access;
+ person () {}
+};
+ </pre>
+
+ <p>It is also possible to have an object class without the default
+ constructor. However, in this case, the database operations will
+ only be able to load the persistent state into an existing instance
+ (<a href="#3.9">Section 3.9, "Loading Persistent Objects"</a>,
+ <a href="#4.4">Section 4.4, "Query Result"</a>).</p>
+
+ <p>The ODB compiler also needs access to the non-transient
+ (<a href="#14.4.11">Section 14.4.11, "<code>transient</code>"</a>)
+ data members of a persistent class. The ODB compiler can access
+ such data members directly if they are public. It can also do
+ so if they are private or protected and the <code>odb::access</code>
+ class is declared a friend of the object type. For example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/core.hxx>
+
+#pragma db object
+class person
+{
+ ...
+
+private:
+ friend class odb::access;
+ person () {}
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string name_;
+};
+ </pre>
+
+ <p>If data members are not accessible directly, then the ODB
+ compiler will try to automatically find suitable accessor and
+ modifier functions. To accomplish this, the ODB compiler will
+ try to lookup common accessor and modifier names derived from
+ the data member name. Specifically, for the <code>name_</code>
+ data member in the above example, the ODB compiler will look
+ for accessor functions with names: <code>get_name()</code>,
+ <code>getName()</code>, <code>getname()</code>, and just
+ <code>name()</code> as well as for modifier functions with
+ names: <code>set_name()</code>, <code>setName()</code>,
+ <code>setname()</code>, and just <code>name()</code>. You can
+ also add support for custom name derivations with the
+ <code>--accessor-regex</code> and <code>--modifier-regex</code>
+ ODB compiler options. Refer to the
+ <a href="http://www.codesynthesis.com/products/odb/doc/odb.xhtml">ODB
+ Compiler Command Line Manual</a> for details on these options.
+ The following example illustrates automatic accessor and modifier
+ discovery:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+public:
+ person () {}
+
+ ...
+
+ unsigned long id () const;
+ void id (unsigned long);
+
+ const std::string&amp; get_name () const;
+ std::string&amp; set_name ();
+
+private:
+ #pragma db id
+ unsigned long id_; // Uses id() for access.
+
+ std::string name_; // Uses get_name()/set_name() for access.
+};
+ </pre>
+
+ <p>Finally, if a data member is not directly accessible and the
+ ODB compiler was unable to discover suitable accessor and
+ modifier functions, then we can provide custom accessor
+ and modifier expressions using the <code>db&nbsp;get</code>
+ and <code>db&nbsp;set</code> pragmas. For more information
+ on custom accessor and modifier expressions refer to
+ <a href="#14.4.5">Section 14.4.5,
+ "<code>get</code>/<code>set</code>/<code>access</code>"</a>.</p>
+
+ <p>Data members of a persistent class can also be split into
+ separately-loaded and/or separately-updated sections.
+ For more information on this functionality, refer to
+ <a href="#9">Chapter 9, "Sections"</a>.</p>
+
+ <p>You may be wondering whether we also have to declare value types
+ as persistent. We don't need to do anything special for simple value
+ types such as <code>int</code> or <code>std::string</code> since the
+ ODB compiler knows how to map them to suitable database types and
+ how to convert between the two. On the other hand, if a simple value
+ is unknown to the ODB compiler then we will need to provide the
+ mapping to the database type and, possibly, the code to
+ convert between the two. For more information on how to achieve
+ this refer to the <code>db&nbsp;type</code> pragma description
+ in <a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>.</p>
+
+ <p>Similar to object classes, composite value types have to be
+ explicitly declared as persistent using the <code>db&nbsp;value</code>
+ pragma, for example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class name
+{
+ ...
+
+ std::string first_;
+ std::string last_;
+};
+ </pre>
+
+ <p>Note that a composite value cannot have a data member designated
+ as an object id since, as we have discussed earlier, values do
+ not have a notion of identity. A composite value type also doesn't
+ have to define the default constructor, unless it is used as an
+ element of a container. The ODB compiler uses the same mechanisms
+ to access data members in composite value types as in object types.
+ Composite value types are discussed in more detail in
+ <a href="#7.2">Section 7.2, "Composite Value Types"</a>.</p>
+
+ <h2><a name="3.3">3.3 Object and View Pointers</a></h2>
+
+ <p>As we have seen in the previous chapter, some database operations
+ create dynamically allocated instances of persistent classes and
+ return pointers to these instances. As we will see in later chapters,
+ pointers are also used to establish relationships between objects
+ (<a href="#6">Chapter 6, "Relationships"</a>) as well as to cache
+ persistent objects in a session (<a href="#11">Chapter 11,
+ "Session"</a>). While in most cases you won't need to deal with
+ pointers to views, it is possible to a obtain a dynamically allocated
+ instance of a view using the <code>result_iterator::load()</code>
+ function (<a href="#4.4">Section 4.4, "Query Results"</a>).</p>
+
+ <p>By default, all these mechanisms use raw pointers to return
+ objects and views as well as to pass and cache objects. This
+ is normally sufficient for applications
+ that have simple object lifetime requirements and do not use sessions
+ or object relationships. In particular, a dynamically allocated object
+ or view that is returned as a raw pointer from a database operation
+ can be assigned to a smart pointer of our choice, for example
+ <code>std::auto_ptr</code>, <code>std::unique_ptr</code> from C++11, or
+ <code>shared_ptr</code> from TR1, C++11, or Boost.</p>
+
+ <p>However, to avoid any possibility of a mistake, such as forgetting
+ to use a smart pointer for a returned object or view, as well as to
+ simplify the use of more advanced ODB functionality, such as sessions
+ and bidirectional object relationships, it is recommended that you use
+ smart pointers with the sharing semantics as object pointers.
+ The <code>shared_ptr</code> smart pointer from TR1, C++11, or Boost
+ is a good default choice. However, if sharing is not required and
+ sessions are not used, then <code>std::unique_ptr</code> or
+ <code>std::auto_ptr</code> can be used just as well.</p>
+
+ <p>ODB provides several mechanisms for changing the object or view pointer
+ type. To specify the pointer type on the per object or per view basis
+ we can use the <code>db&nbsp;pointer</code> pragma, for example:</p>
+
+ <pre class="cxx">
+#pragma db object pointer(std::tr1::shared_ptr)
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>We can also specify the default pointer for a group of objects or
+ views at the namespace level:</p>
+
+ <pre class="cxx">
+#pragma db namespace pointer(std::tr1::shared_ptr)
+namespace accounting
+{
+ #pragma db object
+ class employee
+ {
+ ...
+ };
+
+ #pragma db object
+ class employer
+ {
+ ...
+ };
+}
+ </pre>
+
+ <p>Finally, we can use the <code>--default-pointer</code> option to specify
+ the default pointer for the whole file. Refer to the
+ <a href="http://www.codesynthesis.com/products/odb/doc/odb.xhtml">ODB
+ Compiler Command Line Manual</a> for details on this option's argument.
+ The typical usage is shown below:</p>
+
+ <pre class="terminal">
+--default-pointer std::tr1::shared_ptr
+ </pre>
+
+ <p>An alternative to this method with the same effect is to specify the
+ default pointer for the global namespace:</p>
+
+ <pre class="terminal">
+#pragma db namespace() pointer(std::tr1::shared_ptr)
+ </pre>
+
+ <p>Note that we can always override the default pointer specified
+ at the namespace level or with the command line option using
+ the <code>db&nbsp;pointer</code> object or view pragma. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object pointer(std::shared_ptr)
+namespace accounting
+{
+ #pragma db object
+ class employee
+ {
+ ...
+ };
+
+ #pragma db object pointer(std::unique_ptr)
+ class employer
+ {
+ ...
+ };
+}
+ </pre>
+
+ <p>Refer to <a href="#14.1.2">Section 14.1.2, "<code>pointer</code>
+ (object)"</a>, <a href="#14.2.4">Section 14.2.4, "<code>pointer</code>
+ (view)"</a>, and <a href="#14.5.1">Section 14.5.1, "<code>pointer</code>
+ (namespace)"</a> for more information on these mechanisms.</p>
+
+ <p>Built-in support that is provided by the ODB runtime library allows us
+ to use <code>shared_ptr</code> (TR1 or C++11),
+ <code>std::unique_ptr</code> (C++11), or <code>std::auto_ptr</code> as
+ pointer types. Plus, ODB profile libraries, that are available for
+ commonly used frameworks and libraries (such as Boost and Qt),
+ provide support for smart pointers found in these frameworks and
+ libraries (<a href="#III">Part III, "Profiles"</a>). It is also
+ easy to add support for our own smart pointers, as described in
+ <a href="#6.5"> Section 6.5, "Using Custom Smart Pointers"</a>.</p>
+
+ <h2><a name="3.4">3.4 Database</a></h2>
+
+ <p>Before an application can make use of persistence services
+ offered by ODB, it has to create a database class instance. A
+ database instance is the representation of the place where
+ the application stores its persistent objects. We create
+ a database instance by instantiating one of the database
+ system-specific classes. For example, <code>odb::mysql::database</code>
+ would be such a class for the MySQL database system. We will
+ also normally pass a database name as an argument to the
+ class' constructor. The following code fragment
+ shows how we can create a database instance for the MySQL
+ database system:</p>
+
+ <pre class="cxx">
+#include &lt;odb/database.hxx>
+#include &lt;odb/mysql/database.hxx>
+
+auto_ptr&lt;odb::database> db (
+ new odb::mysql::database (
+ "test_user" // database login name
+ "test_password" // database password
+ "test_database" // database name
+ ));
+ </pre>
+
+ <p>The <code>odb::database</code> class is a common interface for
+ all the database system-specific classes provided by ODB. You
+ would normally work with the database
+ instance via this interface unless there is a specific
+ functionality that your application depends on and which is
+ only exposed by a particular system's <code>database</code>
+ class. You will need to include the <code>&lt;odb/database.hxx></code>
+ header file to make this class available in your application.</p>
+
+ <p>The <code>odb::database</code> interface defines functions for
+ starting transactions and manipulating persistent objects.
+ These are discussed in detail in the remainder of this chapter
+ as well as the next chapter which is dedicated to the topic of
+ querying the database for persistent objects. For details on the
+ system-specific <code>database</code> classes, refer to
+ <a href="#II">Part II, "Database Systems"</a>.</p>
+
+ <p>Before we can persist our objects, the corresponding database schema has
+ to be created in the database. The schema contains table definitions and
+ other relational database artifacts that are used to store the state of
+ persistent objects in the database.</p>
+
+ <p>There are several ways to create the database schema. The easiest is to
+ instruct the ODB compiler to generate the corresponding schema from the
+ persistent classes (<code>--generate-schema</code> option). The ODB
+ compiler can generate the schema as a standalone SQL file,
+ embedded into the generated C++ code, or as a separate C++ source file
+ (<code>--schema-format</code> option). If we are using the SQL file
+ to create the database schema, then this file should be executed,
+ normally only once, before the application is started.</p>
+
+ <p>Alternatively, if the schema is embedded directly into the generated
+ code or produced as a separate C++ source file, then we can use the
+ <code>odb::schema_catalog</code> class to create it in the database
+ from within our application, for example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/schema-catalog.hxx>
+
+odb::transaction t (db->begin ());
+odb::schema_catalog::create_schema (*db);
+t.commit ();
+ </pre>
+
+ <p>Refer to the next section for information on the
+ <code>odb::transaction</code> class. The complete version of the above
+ code fragment is available in the <code>schema/embedded</code> example in
+ the <code>odb-examples</code> package.</p>
+
+ <p>The <code>odb::schema_catalog</code> class has the following interface.
+ You will need to include the <code>&lt;odb/schema-catalog.hxx></code>
+ header file to make this class available in your application.</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ class schema_catalog
+ {
+ public:
+ static void
+ create_schema (database&amp;,
+ const std::string&amp; name = "",
+ bool drop = true);
+
+ static void
+ drop_schema (database&amp;, const std::string&amp; name = "");
+
+ static bool
+ exists (database_id, const std::string&amp; name = "");
+
+ static bool
+ exists (const database&amp;, const std::string&amp; name = "")
+ };
+}
+ </pre>
+
+ <p>The first argument to the <code>create_schema()</code> function
+ is the database instance that we would like to create the schema in.
+ The second argument is the schema name. By default, the ODB
+ compiler generates all embedded schemas with the default schema
+ name (empty string). However, if your application needs to
+ have several separate schemas, you can use the
+ <code>--schema-name</code> ODB compiler option to assign
+ custom schema names and then use these names as a second argument
+ to <code>create_schema()</code>. By default, <code>create_schema()</code>
+ will also delete all the database objects (tables, indexes, etc.) if
+ they exist prior to creating the new ones. You can change this
+ behavior by passing <code>false</code> as the third argument. The
+ <code>drop_schema()</code> function allows you to delete all the
+ database objects without creating the new ones.</p>
+
+ <p>If the schema is not found, the <code>create_schema()</code> and
+ <code>drop_schema()</code> functions throw the
+ <code>odb::unknown_schema</code> exception. You can use the
+ <code>exists()</code> function to check whether a schema for the
+ specified database and with the specified name exists in the
+ catalog. Note also that the <code>create_schema()</code> and
+ <code>drop_schema()</code> functions should be called within a
+ transaction.</p>
+
+ <p>ODB also provides support for database schema evolution. Similar
+ to schema creation, schema migration statements can be generated
+ either as standalone SQL files or embedded into the generated C++
+ code. For more information on schema evolution support, refer to
+ <a href="#13">Chapter 13, "Database Schema Evolution"</a>.</p>
+
+ <p>Finally, we can also use a custom database schema with ODB. This approach
+ can work similarly to the standalone SQL file described above except that
+ the database schema is hand-written or produced by another program. Or we
+ could execute custom SQL statements that create the schema directly from
+ our application. To map persistent classes to custom database schemas, ODB
+ provides a wide range of mapping customization pragmas, such
+ as <code>db&nbsp;table</code>, <code>db&nbsp;column</code>,
+ and <code>db&nbsp;type</code> (<a href="#14">Chapter 14, "ODB Pragma
+ Language"</a>). For sample code that shows how to perform such mapping
+ for various C++ constructs, refer to the <code>schema/custom</code>
+ example in the <code>odb-examples</code> package.</p>
+
+ <h2><a name="3.5">3.5 Transactions</a></h2>
+
+ <p>A transaction is an atomic, consistent, isolated and durable
+ (ACID) unit of work. Database operations can only be
+ performed within a transaction and each thread of execution
+ in an application can have only one active transaction at a
+ time.</p>
+
+ <p>By atomicity we mean that when it comes to making changes to
+ the database state within a transaction,
+ either all the changes are applied or none at all. Consider,
+ for example, a transaction that transfers funds between two
+ objects representing bank accounts. If the debit function
+ on the first object succeeds but the credit function on
+ the second fails, the transaction is rolled back and the
+ database state of the first object remains unchanged.</p>
+
+ <p>By consistency we mean that a transaction must take all the
+ objects stored in the database from one consistent state
+ to another. For example, if a bank account object must
+ reference a person object as its owner and we forget to
+ set this reference before making the object persistent,
+ the transaction will be rolled back and the database
+ will remain unchanged.</p>
+
+ <p>By isolation we mean that the changes made to the database
+ state during a transaction are only visible inside this
+ transaction until and unless it is committed. Using the
+ above example with the bank transfer, the results of the
+ debit operation performed on the first object is not
+ visible to other transactions until the credit operation
+ is successfully completed and the transaction is committed.</p>
+
+ <p>By durability we mean that once the transaction is committed,
+ the changes that it made to the database state are permanent
+ and will survive failures such as an application crash. From
+ now on the only way to alter this state is to execute and commit
+ another transaction.</p>
+
+ <p>A transaction is started by calling either the
+ <code>database::begin()</code> or <code>connection::begin()</code>
+ function. The returned transaction handle is stored in
+ an instance of the <code>odb::transaction</code> class.
+ You will need to include the <code>&lt;odb/transaction.hxx></code>
+ header file to make this class available in your application.
+ For example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/transaction.hxx>
+
+transaction t (db.begin ())
+
+// Perform database operations.
+
+t.commit ();
+ </pre>
+
+ <p>The <code>odb::transaction</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ class transaction
+ {
+ public:
+ typedef odb::database database_type;
+ typedef odb::connection connection_type;
+
+ explicit
+ transaction (transaction_impl*, bool make_current = true);
+
+ transaction ();
+
+ void
+ reset (transaction_impl*, bool make_current = true);
+
+ void
+ commit ();
+
+ void
+ rollback ();
+
+ database_type&amp;
+ database ();
+
+ connection_type&amp;
+ connection ();
+
+ bool
+ finilized () const;
+
+ public:
+ static bool
+ has_current ();
+
+ static transaction&amp;
+ current ();
+
+ static void
+ current (transaction&amp;);
+
+ static bool
+ reset_current ();
+
+ // Callback API.
+ //
+ public:
+ ...
+ };
+}
+ </pre>
+
+ <p>The <code>commit()</code> function commits a transaction and
+ <code>rollback()</code> rolls it back. Unless the transaction
+ has been <em>finalized</em>, that is, explicitly committed or rolled
+ back, the destructor of the <code>transaction</code> class will
+ automatically roll it back when the transaction instance goes
+ out of scope. If we try to commit or roll back a finalized
+ transaction, the <code>odb::transaction_already_finalized</code>
+ exception is thrown.</p>
+
+ <p>The <code>database()</code> accessor returns the database this
+ transaction is working on. Similarly, the <code>connection()</code>
+ accessor returns the database connection this transaction is on
+ (<a href="#3.6">Section 3.6, "Connections"</a>).</p>
+
+ <p>The static <code>current()</code> accessor returns the
+ currently active transaction for this thread. If there is no active
+ transaction, this function throws the <code>odb::not_in_transaction</code>
+ exception. We can check whether there is a transaction in effect in
+ this thread using the <code>has_current()</code> static function.</p>
+
+ <p>The <code>make_current</code> argument in the <code>transaction</code>
+ constructor as well as the static <code>current()</code> modifier and
+ <code>reset_current()</code> function give us additional
+ control over the nomination of the currently active transaction.
+ If we pass <code>false</code> as the <code>make_current</code>
+ argument, then the newly created transaction will not
+ automatically be made the active transaction for this
+ thread. Later, we can use the static <code>current()</code> modifier
+ to set this transaction as the active transaction.
+ The <code>reset_current()</code> static function clears the
+ currently active transaction. Together, these mechanisms
+ allow for more advanced use cases, such as multiplexing
+ two or more transactions on the same thread. For example:</p>
+
+ <pre class="cxx">
+transaction t1 (db1.begin ()); // Active transaction.
+transaction t2 (db2.begin (), false); // Not active.
+
+// Perform database operations on db1.
+
+transaction::current (t2); // Deactivate t1, activate t2.
+
+// Perform database operations on db2.
+
+transaction::current (t1); // Switch back to t1.
+
+// Perform some more database operations on db1.
+
+t1.commit ();
+
+transaction::current (t2); // Switch to t2.
+
+// Perform some more database operations on db2.
+
+t2.commit ();
+ </pre>
+
+ <p>The <code>reset()</code> modifier allows us to reuse the same
+ <code>transaction</code> instance to complete several database
+ transactions. Similar to the destructor, <code>reset()</code>
+ will roll the current transaction back if it hasn't been finalized.
+ The default <code>transaction</code> constructor creates a finalized
+ transaction which can later be initialized using <code>reset()</code>.
+ The <code>finilized()</code> accessor can be used to check whether the
+ transaction has been finalized. Here is how we can use this functionality
+ to commit the current transaction and start a new one every time a
+ certain number of database operations has been performed:</p>
+
+ <pre class="cxx">
+transaction t (db.begin ());
+
+for (size_t i (0); i &lt; n; ++i)
+{
+ // Perform a database operation, such as persist an object.
+
+ // Commit the current transaction and start a new one after
+ // every 100 operations.
+ //
+ if (i % 100 == 0)
+ {
+ t.commit ();
+ t.reset (db.begin ());
+ }
+}
+
+t.commit ();
+ </pre>
+
+ <p>For more information on the transaction callback support, refer
+ to <a href="#15.1">Section 15.1, "Transaction Callbacks"</a>.</p>
+
+ <p>Note that in the above discussion of atomicity, consistency,
+ isolation, and durability, all of those guarantees only apply
+ to the object's state in the database as opposed to the object's
+ state in the application's memory. It is possible to roll
+ a transaction back but still have changes from this
+ transaction in the application's memory. An easy way to
+ avoid this potential inconsistency is to instantiate
+ persistent objects only within the transaction scope. Consider,
+ for example, these two implementations of the same transaction:</p>
+
+ <pre class="cxx">
+void
+update_age (database&amp; db, person&amp; p)
+{
+ transaction t (db.begin ());
+
+ p.age (p.age () + 1);
+ db.update (p);
+
+ t.commit ();
+}
+ </pre>
+
+ <p>In the above implementation, if the <code>update()</code> call fails
+ and the transaction is rolled back, the state of the <code>person</code>
+ object in the database and the state of the same object in the
+ application's memory will differ. Now consider an
+ alternative implementation which only instantiates the
+ <code>person</code> object for the duration of the transaction:</p>
+
+ <pre class="cxx">
+void
+update_age (database&amp; db, unsigned long id)
+{
+ transaction t (db.begin ());
+
+ auto_ptr&lt;person> p (db.load&lt;person> (id));
+ p.age (p.age () + 1);
+ db.update (p);
+
+ t.commit ();
+}
+ </pre>
+
+ <p>Of course, it may not always be possible to write the
+ application in this style. Oftentimes we need to access and
+ modify the application's state of persistent objects out of
+ transactions. In this case it may make sense to try to
+ roll back the changes made to the application state if
+ the transaction was rolled back and the database state
+ remains unchanged. One way to do this is to re-load
+ the object's state from the database, for example:</p>
+
+ <pre class="cxx">
+void
+update_age (database&amp; db, person&amp; p)
+{
+ try
+ {
+ transaction t (db.begin ());
+
+ p.age (p.age () + 1);
+ db.update (p);
+
+ t.commit ();
+ }
+ catch (...)
+ {
+ transaction t (db.begin ());
+ db.load (p.id (), p);
+ t.commit ();
+
+ throw;
+ }
+}
+ </pre>
+
+ <p>See also <a href="#15.1">Section 15.1, "Transaction Callbacks"</a>
+ for an alternative approach.</p>
+
+ <h2><a name="3.6">3.6 Connections</a></h2>
+
+ <p>The <code>odb::connection</code> class represents a connection
+ to the database. Normally, you wouldn't work with connections
+ directly but rather let the ODB runtime obtain and release
+ connections as needed. However, certain use cases may require
+ obtaining a connection manually. For completeness, this section
+ describes the <code>connection</code> class and discusses some
+ of its use cases. You may want to skip this section if you are
+ reading through the manual for the first time.</p>
+
+ <p>Similar to <code>odb::database</code>, the <code>odb::connection</code>
+ class is a common interface for all the database system-specific
+ classes provided by ODB. For details on the system-specific
+ <code>connection</code> classes, refer to <a href="#II">Part II,
+ "Database Systems"</a>.</p>
+
+ <p>To make the <code>odb::connection</code> class available in your
+ application you will need to include the <code>&lt;odb/connection.hxx></code>
+ header file. The <code>odb::connection</code> class has the
+ following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ class connection
+ {
+ public:
+ typedef odb::database database_type;
+
+ transaction
+ begin () = 0;
+
+ unsigned long long
+ execute (const char* statement);
+
+ unsigned long long
+ execute (const std::string&amp; statement);
+
+ unsigned long long
+ execute (const char* statement, std::size_t length);
+
+ database_type&amp;
+ database ();
+ };
+
+ typedef details::shared_ptr&lt;connection> connection_ptr;
+}
+ </pre>
+
+ <p>The <code>begin()</code> function is used to start a transaction
+ on the connection. The <code>execute()</code> functions allow
+ us to execute native database statements on the connection.
+ Their semantics are equivalent to the <code>database::execute()</code>
+ functions (<a href="#3.12">Section 3.12, "Executing Native SQL
+ Statements"</a>) except that they can be legally called outside
+ a transaction. Finally, the <code>database()</code> accessor
+ returns a reference to the <code>odb::database</code> instance
+ to which this connection corresponds.</p>
+
+ <p>To obtain a connection we call the <code>database::connection()</code>
+ function. The connection is returned as <code>odb::connection_ptr</code>,
+ which is an implementation-specific smart pointer with the shared
+ pointer semantics. This, in particular, means that the connection
+ pointer can be copied and returned from functions. Once the last
+ instance of <code>connection_ptr</code> pointing to the same
+ connection is destroyed, the connection is returned to the
+ <code>database</code> instance. The following code fragment
+ shows how we can obtain, use, and release a connection:</p>
+
+ <pre class="cxx">
+using namespace odb::core;
+
+database&amp; db = ...
+connection_ptr c (db.connection ());
+
+// Temporarily disable foreign key constraints.
+//
+c->execute ("SET FOREIGN_KEY_CHECKS = 0");
+
+// Start a transaction on this connection.
+//
+transaction t (c->begin ());
+...
+t.commit ();
+
+// Restore foreign key constraints.
+//
+c->execute ("SET FOREIGN_KEY_CHECKS = 1");
+
+// When 'c' goes out of scope, the connection is returned to 'db'.
+ </pre>
+
+ <p>Some of the use cases which may require direct manipulation of
+ connections include out-of-transaction statement execution,
+ such as the execution of connection configuration statements,
+ the implementation of a connection-per-thread policy, and making
+ sure that a set of transactions is executed on the same
+ connection.</p>
+
+ <h2><a name="3.7">3.7 Error Handling and Recovery</a></h2>
+
+ <p>ODB uses C++ exceptions to report database operation errors. Most
+ ODB exceptions signify <em>hard</em> errors or errors that cannot
+ be corrected without some intervention from the application.
+ For example, if we try to load an object with an unknown object
+ id, the <code>odb::object_not_persistent</code> exception is
+ thrown. Our application may be able to correct this error, for
+ instance, by obtaining a valid object id and trying again.
+ The hard errors and corresponding ODB exceptions that can be
+ thrown by each database function are described in the remainder
+ of this chapter with <a href="#3.14">Section 3.14, "ODB Exceptions"</a>
+ providing a quick reference for all the ODB exceptions.</p>
+
+ <p>The second group of ODB exceptions signify <em>soft</em> or
+ <em>recoverable</em> errors. Such errors are temporary
+ failures which normally can be corrected by simply re-executing
+ the transaction. ODB defines three such exceptions:
+ <code>odb::connection_lost</code>, <code>odb::timeout</code>,
+ and <code>odb::deadlock</code>. All recoverable ODB exceptions
+ are derived from the common <code>odb::recoverable</code> base
+ exception which can be used to handle all the recoverable
+ conditions with a single <code>catch</code> block.</p>
+
+ <p>The <code>odb::connection_lost</code> exception is thrown if
+ a connection to the database is lost in the middle of
+ a transaction. In this situation the transaction is aborted but
+ it can be re-tried without any changes. Similarly, the
+ <code>odb::timeout</code> exception is thrown if one of the
+ database operations or the whole transaction has timed out.
+ Again, in this case the transaction is aborted but can be
+ re-tried as is.</p>
+
+ <p>If two or more transactions access or modify more than one object
+ and are executed concurrently by different applications or by
+ different threads within the same application, then it is possible
+ that these transactions will try to access objects in an incompatible
+ order and deadlock. The canonical example of a deadlock are
+ two transactions in which the first has modified <code>object1</code>
+ and is waiting for the second transaction to commit its changes to
+ <code>object2</code> so that it can also update <code>object2</code>.
+ At the same time the second transaction has modified <code>object2</code>
+ and is waiting for the first transaction to commit its changes to
+ <code>object1</code> because it also needs to modify <code>object1</code>.
+ As a result, none of the two transactions can be completed.</p>
+
+ <p>The database system detects such situations and automatically
+ aborts the waiting operation in one of the deadlocked transactions.
+ In ODB this translates to the <code>odb::deadlock</code>
+ recoverable exception being thrown from one of the database functions.</p>
+
+ <p>The following code fragment shows how to handle the recoverable
+ exceptions by restarting the affected transaction:</p>
+
+ <pre class="cxx">
+const unsigned short max_retries = 5;
+
+for (unsigned short retry_count (0); ; retry_count++)
+{
+ try
+ {
+ transaction t (db.begin ());
+
+ ...
+
+ t.commit ();
+ break;
+ }
+ catch (const odb::recoverable&amp; e)
+ {
+ if (retry_count > max_retries)
+ throw retry_limit_exceeded (e.what ());
+ else
+ continue;
+ }
+}
+ </pre>
+
+ <h2><a name="3.8">3.8 Making Objects Persistent</a></h2>
+
+ <p>A newly created instance of a persistent class is transient.
+ We use the <code>database::persist()</code> function template
+ to make a transient instance persistent. This function has four
+ overloaded versions with the following signatures:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ typename object_traits&lt;T>::id_type
+ persist (const T&amp; object);
+
+ template &lt;typename T>
+ typename object_traits&lt;T>::id_type
+ persist (const object_traits&lt;T>::const_pointer_type&amp; object);
+
+ template &lt;typename T>
+ typename object_traits&lt;T>::id_type
+ persist (T&amp; object);
+
+ template &lt;typename T>
+ typename object_traits&lt;T>::id_type
+ persist (const object_traits&lt;T>::pointer_type&amp; object);
+ </pre>
+
+ <p>Here and in the rest of the manual,
+ <code>object_traits&lt;T>::pointer_type</code> and
+ <code>object_traits&lt;T>::const_pointer_type</code> denote the
+ unrestricted and constant object pointer types (<a href="#3.3">Section
+ 3.3, "Object and View Pointers"</a>), respectively.
+ Similarly, <code>object_traits&lt;T>::id_type</code> denotes the object
+ id type. The <code>odb::object_traits</code> template is part of the
+ database support code generated by the ODB compiler.</p>
+
+ <p>The first <code>persist()</code> function expects a constant reference
+ to an instance being persisted. The second function expects a constant
+ object pointer. Both of these functions can only be used on objects with
+ application-assigned object ids (<a href="#14.4.2">Section 14.4.2,
+ "<code>auto</code>"</a>).</p>
+
+ <p>The second and third <code>persist()</code> functions are similar to the
+ first two except that they operate on unrestricted references and object
+ pointers. If the identifier of the object being persisted is assigned
+ by the database, these functions update the id member of the passed
+ instance with the assigned value. All four functions return the object
+ id of the newly persisted object.</p>
+
+ <p>If the database already contains an object of this type with this
+ identifier, the <code>persist()</code> functions throw the
+ <code>odb::object_already_persistent</code> exception. This should
+ never happen for database-assigned object ids as long as the
+ number of objects persisted does not exceed the value space of
+ the id type.</p>
+
+ <p>When calling the <code>persist()</code> functions, we don't need to
+ explicitly specify the template type since it will be automatically
+ deduced from the argument being passed. The following example shows
+ how we can call these functions:</p>
+
+ <pre class="cxx">
+person john ("John", "Doe", 33);
+shared_ptr&lt;person> jane (new person ("Jane", "Doe", 32));
+
+transaction t (db.begin ());
+
+db.persist (john);
+unsigned long jane_id (db.persist (jane));
+
+t.commit ();
+
+cerr &lt;&lt; "Jane's id: " &lt;&lt; jane_id &lt;&lt; endl;
+ </pre>
+
+ <p>Notice that in the above code fragment we have created instances
+ that we were planning to make persistent before starting the
+ transaction. Likewise, we printed Jane's id after we have committed
+ the transaction. As a general rule, you should avoid performing
+ operations within the transaction scope that can be performed
+ before the transaction starts or after it terminates. An active
+ transaction consumes both your application's resources, such as
+ a database connection, as well as the database server's
+ resources, such as object locks. By following the above rule you
+ make sure these resources are released and made available to other
+ threads in your application and to other applications as soon as
+ possible.</p>
+
+ <p>Some database systems support persisting multiple objects with a
+ single underlying statement execution which can result in significantly
+ improved performance. For such database systems ODB provides
+ bulk <code>persist()</code> functions. For details, refer to
+ <a href="#15.3">Section 15.3, "Bulk Database Operations"</a>.</p>
+
+ <h2><a name="3.9">3.9 Loading Persistent Objects</a></h2>
+
+ <p>Once an object is made persistent, and you know its object id, it
+ can be loaded by the application using the <code>database::load()</code>
+ function template. This function has two overloaded versions with
+ the following signatures:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ typename object_traits&lt;T>::pointer_type
+ load (const typename object_traits&lt;T>::id_type&amp; id);
+
+ template &lt;typename T>
+ void
+ load (const typename object_traits&lt;T>::id_type&amp; id, T&amp; object);
+ </pre>
+
+ <p>Given an object id, the first function allocates a new instance
+ of the object class in the dynamic memory, loads its state from
+ the database, and returns the pointer to the new instance. The
+ second function loads the object's state into an existing instance.
+ Both functions throw <code>odb::object_not_persistent</code> if
+ there is no object of this type with this id in the database.</p>
+
+ <p>When we call the first <code>load()</code> function, we need to
+ explicitly specify the object type. We don't need to do this for
+ the second function because the object type will be automatically
+ deduced from the second argument, for example:</p>
+
+ <pre class="cxx">
+transaction t (db.begin ());
+
+auto_ptr&lt;person> jane (db.load&lt;person> (jane_id));
+
+db.load (jane_id, *jane);
+
+t.commit ();
+ </pre>
+
+ <p>In certain situations it may be necessary to reload the state
+ of an object from the database. While this is easy to achieve
+ using the second <code>load()</code> function, ODB provides
+ the <code>database::reload()</code> function template that
+ has a number of special properties. This function has two
+ overloaded versions with the following signatures:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ void
+ reload (T&amp; object);
+
+ template &lt;typename T>
+ void
+ reload (const object_traits&lt;T>::pointer_type&amp; object);
+ </pre>
+
+ <p>The first <code>reload()</code> function expects an object
+ reference, while the second expects an object pointer. Both
+ functions expect the id member in the passed object to contain
+ a valid object identifier and, similar to <code>load()</code>,
+ both will throw <code>odb::object_not_persistent</code> if
+ there is no object of this type with this id in the database.</p>
+
+ <p>The first special property of <code>reload()</code>
+ compared to the <code>load()</code> function is that it
+ does not interact with the session's object cache
+ (<a href="#11.1">Section 11.1, "Object Cache"</a>). That is, if
+ the object being reloaded is already in the cache, then it will
+ remain there after <code>reload()</code> returns. Similarly, if the
+ object is not in the cache, then <code>reload()</code> won't
+ put it there either.</p>
+
+ <p>The second special property of the <code>reload()</code> function
+ only manifests itself when operating on an object with the optimistic
+ concurrency model. In this case, if the states of the object
+ in the application memory and in the database are the same, then
+ no reloading will occur. For more information on optimistic
+ concurrency, refer to <a href="#12">Chapter 12, "Optimistic
+ Concurrency"</a>.</p>
+
+ <p>If we don't know for sure whether an object with a given id
+ is persistent, we can use the <code>find()</code> function
+ instead of <code>load()</code>, for example:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ typename object_traits&lt;T>::pointer_type
+ find (const typename object_traits&lt;T>::id_type&amp; id);
+
+ template &lt;typename T>
+ bool
+ find (const typename object_traits&lt;T>::id_type&amp; id, T&amp; object);
+ </pre>
+
+ <p>If an object with this id is not found in the database, the first
+ <code>find()</code> function returns a <code>NULL</code> pointer
+ while the second function leaves the passed instance unmodified and
+ returns <code>false</code>.</p>
+
+ <p>If we don't know the object id, then we can use queries to
+ find the object (or objects) matching some criteria
+ (<a href="#4">Chapter 4, "Querying the Database"</a>). Note,
+ however, that loading an object's state using its
+ identifier can be significantly faster than executing a query.</p>
+
+
+ <h2><a name="3.10">3.10 Updating Persistent Objects</a></h2>
+
+ <p>If a persistent object has been modified, we can store the updated
+ state in the database using the <code>database::update()</code>
+ function template. This function has three overloaded versions with
+ the following signatures:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ void
+ update (const T&amp; object);
+
+ template &lt;typename T>
+ void
+ update (const object_traits&lt;T>::const_pointer_type&amp; object);
+
+ template &lt;typename T>
+ void
+ update (const object_traits&lt;T>::pointer_type&amp; object);
+ </pre>
+
+ <p>The first <code>update()</code> function expects an object reference,
+ while the other two expect object pointers. If the object passed to
+ one of these functions does not exist in the database,
+ <code>update()</code> throws the <code>odb::object_not_persistent</code>
+ exception (but see a note on optimistic concurrency below).</p>
+
+ <p>Below is an example of the funds transfer that we talked about
+ in the earlier section on transactions. It uses the hypothetical
+ <code>bank_account</code> persistent class:</p>
+
+ <pre class="cxx">
+void
+transfer (database&amp; db,
+ unsigned long from_acc,
+ unsigned long to_acc,
+ unsigned int amount)
+{
+ bank_account from, to;
+
+ transaction t (db.begin ());
+
+ db.load (from_acc, from);
+
+ if (from.balance () &lt; amount)
+ throw insufficient_funds ();
+
+ db.load (to_acc, to);
+
+ to.balance (to.balance () + amount);
+ from.balance (from.balance () - amount);
+
+ db.update (to);
+ db.update (from);
+
+ t.commit ();
+}
+ </pre>
+
+ <p>The same can be accomplished using dynamically allocated objects
+ and the <code>update()</code> function with object pointer argument,
+ for example:</p>
+
+ <pre class="cxx">
+transaction t (db.begin ());
+
+shared_ptr&lt;bank_account> from (db.load&lt;bank_account> (from_acc));
+
+if (from->balance () &lt; amount)
+ throw insufficient_funds ();
+
+shared_ptr&lt;bank_account> to (db.load&lt;bank_account> (to_acc));
+
+to->balance (to->balance () + amount);
+from->balance (from->balance () - amount);
+
+db.update (to);
+db.update (from);
+
+t.commit ();
+ </pre>
+
+ <p>If any of the <code>update()</code> functions are operating on a
+ persistent class with the optimistic concurrency model, then they will
+ throw the <code>odb::object_changed</code> exception if the state of the
+ object in the database has changed since it was last loaded into the
+ application memory. Furthermore, for such classes, <code>update()</code>
+ no longer throws the <code>object_not_persistent</code> exception if
+ there is no such object in the database. Instead, this condition is
+ treated as a change of object state and <code>object_changed</code>
+ is thrown instead. For a more detailed discussion of optimistic
+ concurrency, refer to <a href="#12">Chapter 12, "Optimistic
+ Concurrency"</a>.</p>
+
+ <p>In ODB, persistent classes, composite value types, as well as individual
+ data members can be declared read-only (see <a href="#14.1.4">Section
+ 14.1.4, "<code>readonly</code> (object)"</a>, <a href="#14.3.6">Section
+ 14.3.6, "<code>readonly</code> (composite value)"</a>, and
+ <a href="#14.4.12">Section 14.4.12, "<code>readonly</code>
+ (data member)"</a>).</p>
+
+ <p>If an individual data member is declared read-only, then
+ any changes to this member will be ignored when updating the database
+ state of an object using any of the above <code>update()</code>
+ functions. A <code>const</code> data member is automatically treated
+ as read-only. If a composite value is declared read-only then all its
+ data members are treated as read-only.</p>
+
+ <p>If the whole object is declared read-only then the database state of
+ this object cannot be changed. Calling any of the above
+ <code>update()</code> functions for such an object will result in a
+ compile-time error.</p>
+
+ <p>Similar to <code>persist()</code>, for database systems that support
+ this functionality, ODB provides bulk <code>update()</code> functions.
+ For details, refer to <a href="#15.3">Section 15.3, "Bulk Database
+ Operations"</a>.</p>
+
+ <h2><a name="3.11">3.11 Deleting Persistent Objects</a></h2>
+
+ <p>To delete a persistent object's state from the database we use the
+ <code>database::erase()</code> or <code>database::erase_query()</code>
+ function templates. If the application still has an instance of the
+ erased object, this instance becomes transient. The <code>erase()</code>
+ function has the following overloaded versions:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ void
+ erase (const T&amp; object);
+
+ template &lt;typename T>
+ void
+ erase (const object_traits&lt;T>::const_pointer_type&amp; object);
+
+ template &lt;typename T>
+ void
+ erase (const object_traits&lt;T>::pointer_type&amp; object);
+
+ template &lt;typename T>
+ void
+ erase (const typename object_traits&lt;T>::id_type&amp; id);
+ </pre>
+
+ <p>The first <code>erase()</code> function uses an object itself, in
+ the form of an object reference, to delete its state from the
+ database. The next two functions accomplish the same result but using
+ object pointers. Note that all three functions leave the passed
+ object unchanged. It simply becomes transient. The last function
+ uses the object id to identify the object to be deleted. If the
+ object does not exist in the database, then all four functions
+ throw the <code>odb::object_not_persistent</code> exception
+ (but see a note on optimistic concurrency below).</p>
+
+ <p>We have to specify the object type when calling the last
+ <code>erase()</code> function. The same is unnecessary for the
+ first three functions because the object type will be automatically
+ deduced from their arguments. The following example shows how we
+ can call these functions:</p>
+
+ <pre class="cxx">
+person&amp; john = ...
+shared_ptr&lt;jane> jane = ...
+unsigned long joe_id = ...
+
+transaction t (db.begin ());
+
+db.erase (john);
+db.erase (jane);
+db.erase&lt;person> (joe_id);
+
+t.commit ();
+ </pre>
+
+ <p>If any of the <code>erase()</code> functions except the last one are
+ operating on a persistent class with the optimistic concurrency
+ model, then they will throw the <code>odb::object_changed</code> exception
+ if the state of the object in the database has changed since it was
+ last loaded into the application memory. Furthermore, for such
+ classes, <code>erase()</code> no longer throws the
+ <code>object_not_persistent</code> exception if there is no such
+ object in the database. Instead, this condition is treated as a
+ change of object state and <code>object_changed</code> is thrown
+ instead. For a more detailed discussion of optimistic concurrency,
+ refer to <a href="#12">Chapter 12, "Optimistic Concurrency"</a>.</p>
+
+ <p>Similar to <code>persist()</code> and <code>update()</code>, for
+ database systems that support this functionality, ODB provides
+ bulk <code>erase()</code> functions. For details, refer to
+ <a href="#15.3">Section 15.3, "Bulk Database Operations"</a>.</p>
+
+ <p>The <code>erase_query()</code> function allows us to delete
+ the state of multiple objects matching certain criteria. It uses
+ the query expression of the <code>database::query()</code> function
+ (<a href="#4">Chapter 4, "Querying the Database"</a>) and,
+ because the ODB query facility is optional, it is only available
+ if the <code>--generate-query</code> ODB compiler option was
+ specified. The <code>erase_query()</code> function has the
+ following overloaded versions:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ unsigned long long
+ erase_query ();
+
+ template &lt;typename T>
+ unsigned long long
+ erase_query (const odb::query&lt;T>&amp;);
+ </pre>
+
+ <p>The first <code>erase_query()</code> function is used to delete
+ the state of all the persistent objects of a given type stored
+ in the database. The second function uses the passed query instance
+ to only delete the state of objects matching the query criteria.
+ Both functions return the number of objects erased. When calling
+ the <code>erase_query()</code> function, we have to explicitly
+ specify the object type we are erasing. For example:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;person> query;
+
+transaction t (db.begin ());
+
+db.erase_query&lt;person> (query::last == "Doe" &amp;&amp; query::age &lt; 30);
+
+t.commit ();
+ </pre>
+
+ <p>Unlike the <code>query()</code> function, when calling
+ <code>erase_query()</code> we cannot use members from pointed-to
+ objects in the query expression. However, we can still use
+ a member corresponding to a pointer as an ordinary object
+ member that has the id type of the pointed-to object
+ (<a href="#6">Chapter 6, "Relationships"</a>). This allows us
+ to compare object ids as well as test the pointer for
+ <code>NULL</code>. As an example, the following transaction
+ makes sure that all the <code>employee</code> objects that
+ reference an <code>employer</code> object that is about to
+ be deleted are deleted as well. Here we assume that the
+ <code>employee</code> class contains a pointer to the
+ <code>employer</code> class. Refer to <a href="#6">Chapter 6,
+ "Relationships"</a> for complete definitions of these
+ classes.</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee> query;
+
+transaction t (db.begin ());
+
+employer&amp; e = ... // Employer object to be deleted.
+
+db.erase_query&lt;employee> (query::employer == e.id ());
+db.erase (e);
+
+t.commit ();
+ </pre>
+
+
+ <h2><a name="3.12">3.12 Executing Native SQL Statements</a></h2>
+
+ <p>In some situations we may need to execute native SQL statements
+ instead of using the object-oriented database API described above.
+ For example, we may want to tune the database schema generated
+ by the ODB compiler or take advantage of a feature that is
+ specific to the database system we are using. The
+ <code>database::execute()</code> function, which has three
+ overloaded versions, provides this functionality:</p>
+
+ <pre class="cxx">
+ unsigned long long
+ execute (const char* statement);
+
+ unsigned long long
+ execute (const std::string&amp; statement);
+
+ unsigned long long
+ execute (const char* statement, std::size_t length)
+ </pre>
+
+ <p>The first <code>execute()</code> function expects the SQL statement
+ as a zero-terminated C-string. The last version expects the explicit
+ statement length as the second argument and the statement itself
+ may contain <code>'\0'</code> characters, for example, to represent
+ binary data, if the database system supports it. All three functions
+ return the number of rows that were affected by the statement. For
+ example:</p>
+
+ <pre class="cxx">
+transaction t (db.begin ());
+
+db.execute ("DROP TABLE test");
+db.execute ("CREATE TABLE test (n INT PRIMARY KEY)");
+
+t.commit ();
+ </pre>
+
+ <p>While these functions must always be called within a transaction,
+ it may be necessary to execute a native statement outside a
+ transaction. This can be done using the
+ <code>connection::execute()</code> functions as described in
+ <a href="#3.6">Section 3.6, "Connections"</a>.</p>
+
+ <h2><a name="3.13">3.13 Tracing SQL Statement Execution</a></h2>
+
+ <p>Oftentimes it is useful to understand what SQL statements are
+ executed as a result of high-level database operations. For
+ example, we can use this information to figure out why certain
+ transactions don't produce desired results or why they take
+ longer than expected.</p>
+
+ <p>While this information can usually be obtained from the database
+ logs, ODB provides an application-side SQL statement tracing
+ support that is both more convenient and finer-grained.
+ For example, in a typical situation that calls for tracing
+ we would like to see the SQL statements executed as a result
+ of a specific transaction. While it may be difficult to
+ extract such a subset of statements from the database logs,
+ it is easy to achieve with ODB tracing support:</p>
+
+ <pre class="cxx">
+transaction t (db.begin ());
+t.tracer (stderr_tracer);
+
+...
+
+t.commit ();
+ </pre>
+
+ <p>ODB allows us to specify a tracer on the database, connection,
+ and transaction levels. If specified for the database, then
+ all the statements executed on this database will be traced.
+ On the other hand, if a tracer is specified for the
+ connection, then only the SQL statements executed on this
+ connection will be traced. Similarly, a tracer specified
+ for a transaction will only show statements that are
+ executed as part of this transaction. All three classes
+ (<code>odb::database</code>, <code>odb::connection</code>,
+ and <code>odb::transaction</code>) provide the identical
+ tracing API:</p>
+
+ <pre class="cxx">
+ void
+ tracer (odb::tracer&amp;);
+
+ void
+ tracer (odb::tracer*);
+
+ odb::tracer*
+ tracer () const;
+ </pre>
+
+ <p>The first two <code>tracer()</code> functions allow us to set
+ the tracer object with the second one allowing us to clear the
+ current tracer by passing a <code>NULL</code> pointer. The
+ last <code>tracer()</code> function allows us to get the
+ current tracer object. It returns a <code>NULL</code> pointer
+ if there is no tracer in effect. Note that the tracing API
+ does not manage the lifetime of the tracer object. The tracer
+ should be valid for as long as it is being used. Furthermore,
+ the tracing API is not thread-safe. Trying to set a tracer
+ from multiple threads simultaneously will result in
+ undefined behavior.</p>
+
+ <p>The <code>odb::tracer</code> class defines a callback interface
+ that can be used to create custom tracer implementations. The
+ <code>odb::stderr_tracer</code> and <code>odb::stderr_full_tracer</code>
+ are built-in tracer implementations provided by the ODB runtime.
+ They both print SQL statements being executed to the standard error
+ stream. The full tracer, in addition to tracing statement executions,
+ also traces their preparations and deallocations. One situation where
+ the full tracer can be particularly useful is if a statement (for
+ example a custom query) contains a syntax error. In this case the
+ error will be detected during preparation and, as a result, the
+ statement will never be executed. The only way to see such a statement
+ is by using the full tracing.</p>
+
+ <p>The <code>odb::tracer</code> class is defined in the
+ <code>&lt;odb/tracer.hxx></code> header file which you will need to
+ include in order to make this class available in your application.
+ The <code>odb::tracer</code> interface provided the following
+ callback functions:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ class tracer
+ {
+ public:
+ virtual void
+ prepare (connection&amp;, const statement&amp;);
+
+ virtual void
+ execute (connection&amp;, const statement&amp;);
+
+ virtual void
+ execute (connection&amp;, const char* statement) = 0;
+
+ virtual void
+ deallocate (connection&amp;, const statement&amp;);
+ };
+}
+ </pre>
+
+ <p>The <code>prepare()</code> and <code>deallocate()</code> functions
+ are called when a prepared statement is created and destroyed,
+ respectively. The first <code>execute()</code> function is called
+ when a prepared statement is executed while the second one is called
+ when a normal statement is executed. The default implementations
+ for the <code>prepare()</code> and <code>deallocate()</code>
+ functions do nothing while the first <code>execute()</code> function
+ calls the second one passing the statement text as the second
+ argument. As a result, if all you are interested in are the
+ SQL statements being executed, then you only need to override the
+ second <code>execute()</code> function.</p>
+
+ <p>In addition to the common <code>odb::tracer</code> interface,
+ each database runtime provides a database-specific version
+ as <code>odb::&lt;database>::tracer</code>. It has exactly
+ the same interface as the common version except that the
+ <code>connection</code> and <code>statement</code> types
+ are database-specific, which gives us access to additional,
+ database-specific information.</p>
+
+ <p>As an example, consider a more elaborate, PostgreSQL-specific
+ tracer implementation. Here we rely on the fact that the PostgreSQL
+ ODB runtime uses names to identify prepared statements and this
+ information can be obtained from the <code>odb::pgsql::statement</code>
+ object:</p>
+
+ <pre class="cxx">
+#include &lt;odb/pgsql/tracer.hxx>
+#include &lt;odb/pgsql/database.hxx>
+#include &lt;odb/pgsql/connection.hxx>
+#include &lt;odb/pgsql/statement.hxx>
+
+class pgsql_tracer: public odb::pgsql::tracer
+{
+ virtual void
+ prepare (odb::pgsql::connection&amp; c, const odb::pgsql::statement&amp; s)
+ {
+ cerr &lt;&lt; c.database ().db () &lt;&lt; ": PREPARE " &lt;&lt; s.name ()
+ &lt;&lt; " AS " &lt;&lt; s.text () &lt;&lt; endl;
+ }
+
+ virtual void
+ execute (odb::pgsql::connection&amp; c, const odb::pgsql::statement&amp; s)
+ {
+ cerr &lt;&lt; c.database ().db () &lt;&lt; ": EXECUTE " &lt;&lt; s.name () &lt;&lt; endl;
+ }
+
+ virtual void
+ execute (odb::pgsql::connection&amp; c, const char* statement)
+ {
+ cerr &lt;&lt; c.database ().db () &lt;&lt; ": " &lt;&lt; statement &lt;&lt; endl;
+ }
+
+ virtual void
+ deallocate (odb::pgsql::connection&amp; c, const odb::pgsql::statement&amp; s)
+ {
+ cerr &lt;&lt; c.database ().db () &lt;&lt; ": DEALLOCATE " &lt;&lt; s.name () &lt;&lt; endl;
+ }
+};
+ </pre>
+
+ <p>Note also that you can only set a database-specific tracer object
+ using a database-specific database instance, for example:</p>
+
+ <pre class="cxx">
+pgsql_tracer tracer;
+
+odb::database&amp; db = ...;
+db.tracer (tracer); // Compile error.
+
+odb::pgsql::database&amp; db = ...;
+db.tracer (tracer); // Ok.
+ </pre>
+
+ <h2><a name="3.14">3.14 ODB Exceptions</a></h2>
+
+ <p>In the previous sections we have already mentioned some of the
+ exceptions that can be thrown by the database functions. In this
+ section we will discuss the ODB exception hierarchy and document
+ all the exceptions that can be thrown by the common ODB
+ runtime.</p>
+
+ <p>The root of the ODB exception hierarchy is the abstract
+ <code>odb::exception</code> class. This class derives
+ from <code>std::exception</code> and has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ struct exception: std::exception
+ {
+ virtual const char*
+ what () const throw () = 0;
+ };
+}
+ </pre>
+
+ <p>Catching this exception guarantees that we will catch all the
+ exceptions thrown by ODB. The <code>what()</code> function
+ returns a human-readable description of the condition that
+ triggered the exception.</p>
+
+ <p>The concrete exceptions that can be thrown by ODB are presented
+ in the following listing:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ struct null_pointer: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ // Transaction exceptions.
+ //
+ struct already_in_transaction: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct not_in_transaction: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct transaction_already_finalized: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ // Session exceptions.
+ //
+ struct already_in_session: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct not_in_session: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct session_required: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ // Database operations exceptions.
+ //
+ struct recoverable: exception
+ {
+ };
+
+ struct connection_lost: recoverable
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct timeout: recoverable
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct deadlock: recoverable
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct object_not_persistent: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct object_already_persistent: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct object_changed: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct result_not_cached: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct database_exception: exception
+ {
+ };
+
+ // Polymorphism support exceptions.
+ //
+ struct abstract_class: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct no_type_info: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ // Prepared query support exceptions.
+ //
+ struct prepared_already_cached: exception
+ {
+ const char*
+ name () const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct prepared_type_mismatch: exception
+ {
+ const char*
+ name () const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ // Schema catalog exceptions.
+ //
+ struct unknown_schema: exception
+ {
+ const std::string&amp;
+ name () const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct unknown_schema_version: exception
+ {
+ schema_version
+ version () const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ // Section exceptions.
+ //
+ struct section_not_loaded: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct section_not_in_object: exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ // Bulk operation exceptions.
+ //
+ struct multiple_exceptions: exception
+ {
+ ...
+
+ virtual const char*
+ what () const throw ();
+ };
+}
+ </pre>
+
+ <p>The <code>null_pointer</code> exception is thrown when a
+ pointer to a persistent object declared non-<code>NULL</code>
+ with the <code>db&nbsp;not_null</code> or
+ <code>db&nbsp;value_not_null</code> pragma has the <code>NULL</code>
+ value. See <a href="#6">Chapter 6, "Relationships"</a> for details.</p>
+
+ <p>The next three exceptions (<code>already_in_transaction</code>,
+ <code>not_in_transaction</code>,
+ <code>transaction_already_finalized</code>) are thrown by the
+ <code>odb::transaction</code> class and are discussed
+ in <a href="#3.5">Section 3.5, "Transactions"</a>.</p>
+
+ <p>The next two exceptions (<code>already_in_session</code>, and
+ <code>not_in_session</code>) are thrown by the <code>odb::session</code>
+ class and are discussed in <a href="#11">Chapter 11, "Session"</a>.</p>
+
+ <p>The <code>session_required</code> exception is thrown when ODB detects
+ that correctly loading a bidirectional object relationship requires a
+ session but one is not used. See <a href="#6.2">Section 6.2,
+ "Bidirectional Relationships"</a> for more information on this
+ exception.</p>
+
+ <p>The <code>recoverable</code> exception serves as a common base
+ for all the recoverable exceptions, which are: <code>connection_lost</code>,
+ <code>timeout</code>, and <code>deadlock</code>. The
+ <code>connection_lost</code> exception is thrown when a connection
+ to the database is lost. Similarly, the <code>timeout</code> exception
+ is thrown if one of the database operations or the whole transaction
+ has timed out. The <code>deadlock</code> exception is thrown when a
+ transaction deadlock is detected by the database system. These
+ exceptions can be thrown by any database function. See
+ <a href="#3.7">Section 3.7, "Error Handling and Recovery"</a>
+ for details.</p>
+
+ <p>The <code>object_already_persistent</code> exception is thrown
+ by the <code>persist()</code> database function. See
+ <a href="#3.8">Section 3.8, "Making Objects Persistent"</a>
+ for details.</p>
+
+ <p>The <code>object_not_persistent</code> exception is thrown
+ by the <code>load()</code>, <code>update()</code>, and
+ <code>erase()</code> database functions. Refer to
+ <a href="#3.9">Section 3.9, "Loading Persistent Objects"</a>,
+ <a href="#3.10">Section 3.10, "Updating Persistent Objects"</a>, and
+ <a href="#3.11">Section 3.11, "Deleting Persistent Objects"</a> for
+ more information.</p>
+
+ <p>The <code>object_changed</code> exception is thrown
+ by the <code>update()</code> database function and certain
+ <code>erase()</code> database functions when
+ operating on objects with the optimistic concurrency model. See
+ <a href="#12">Chapter 12, "Optimistic Concurrency"</a> for details.</p>
+
+ <p>The <code>result_not_cached</code> exception is thrown by
+ the query result class. Refer to <a href="#4.4">Section 4.4,
+ "Query Result"</a> for details.</p>
+
+ <p>The <code>database_exception</code> exception is a base class for all
+ database system-specific exceptions that are thrown by the
+ database system-specific runtime library. Refer to <a href="#II">Part
+ II, "Database Systems"</a> for more information.</p>
+
+ <p>The <code>abstract_class</code> exception is thrown by the database
+ functions when we attempt to persist, update, load, or erase an
+ instance of a polymorphic abstract class. For more information
+ on abstract classes, refer to <a href="#14.1.3">Section 14.1.3,
+ "<code>abstract</code>"</a>.</p>
+
+ <p>The <code>no_type_info</code> exception is thrown by the database
+ functions when we attempt to persist, update, load, or erase an
+ instance of a polymorphic class for which no type information
+ is present in the application. This normally means that the
+ generated database support code for this class has not been
+ linked (or dynamically loaded) into the application or the
+ discriminator value has not been mapped to a persistent
+ class. For more information on polymorphism support, refer to
+ <a href="#8.2">Section 8.2, "Polymorphism Inheritance"</a>.</p>
+
+ <p>The <code>prepared_already_cached</code> exception is thrown by the
+ <code>cache_query()</code> function if a prepared query with the
+ specified name is already cached. The <code>prepared_type_mismatch</code>
+ exception is thrown by the <code>lookup_query()</code> function if
+ the specified prepared query object type or parameters type
+ does not match the one in the cache. Refer to <a href="#4.5">Section
+ 4.5, "Prepared Queries"</a> for details.</p>
+
+ <p>The <code>unknown_schema</code> exception is thrown by the
+ <code>odb::schema_catalog</code> class if a schema with the specified
+ name is not found. Refer to <a href="#3.4">Section 3.4, "Database"</a>
+ for details. The <code>unknown_schema_version</code> exception is thrown
+ by the <code>schema_catalog</code> functions that deal with database
+ schema evolution if the passed or current version is unknow. Refer
+ to <a href="#13">Chapter 13, "Database Schema Evolution"</a> for
+ details.</p>
+
+ <p>The <code>section_not_loaded</code> exception is thrown if we
+ attempt to update an object section that hasn't been loaded.
+ The <code>section_not_in_object</code> exception is thrown if
+ the section instance being loaded or updated does not belong
+ to the corresponding object. See <a href="#9">Chapter 9,
+ "Sections"</a> for more information on these exceptions.</p>
+
+ <p>The <code>multiple_exceptions</code> exception is thrown by the
+ bulk API functions. Refer to <a href="#15.3">Section 15.3, "Bulk
+ Database Operations"</a> for details.</p>
+
+ <p>The <code>odb::exception</code> class is defined in the
+ <code>&lt;odb/exception.hxx></code> header file. All the
+ concrete ODB exceptions are defined in
+ <code>&lt;odb/exceptions.hxx></code> which also includes
+ <code>&lt;odb/exception.hxx></code>. Normally you don't
+ need to include either of these two headers because they are
+ automatically included by <code>&lt;odb/database.hxx></code>.
+ However, if the source file that handles ODB exceptions
+ does not include <code>&lt;odb/database.hxx></code>, then
+ you will need to explicitly include one of these headers.</p>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="4">4 Querying the Database</a></h1>
+
+ <p>If we don't know the identifiers of the objects that we are looking
+ for, we can use queries to search the database for objects matching
+ certain criteria. The ODB query facility is optional and we need to
+ explicitly request the generation of the necessary database support
+ code with the <code>--generate-query</code> ODB compiler option.</p>
+
+ <p>ODB provides a flexible query API that offers two distinct levels of
+ abstraction from the database system query language such as SQL.
+ At the high level we are presented with an easy to use yet powerful
+ object-oriented query language, called ODB Query Language. This
+ query language is modeled after and is integrated into C++ allowing
+ us to write expressive and safe queries that look and feel like
+ ordinary C++. We have already seen examples of these queries in the
+ introductory chapters. Below is another, more interesting, example:</p>
+
+ <pre class="cxx">
+ typedef odb::query&lt;person> query;
+ typedef odb::result&lt;person> result;
+
+ unsigned short age;
+ query q (query::first == "John" &amp;&amp; query::age &lt; query::_ref (age));
+
+ for (age = 10; age &lt; 100; age += 10)
+ {
+ result r (db.query&lt;person> (q));
+ ...
+ }
+ </pre>
+
+ <p>At the low level, queries can be written as predicates using
+ the database system-native query language such as the
+ <code>WHERE</code> predicate from the SQL <code>SELECT</code>
+ statement. This language will be referred to as native query
+ language. At this level ODB still takes care of converting
+ query parameters from C++ to the database system format. Below
+ is the re-implementation of the above example using SQL as
+ the native query language:</p>
+
+ <pre class="cxx">
+ query q ("first = 'John' AND age = " + query::_ref (age));
+ </pre>
+
+ <p>Note that at this level we lose the static typing of
+ query expressions. For example, if we wrote something like this:</p>
+
+ <pre class="cxx">
+ query q (query::first == 123 &amp;&amp; query::agee &lt; query::_ref (age));
+ </pre>
+
+ <p>We would get two errors during the C++ compilation. The first would
+ indicate that we cannot compare <code>query::first</code> to an
+ integer and the second would pick the misspelling in
+ <code>query::agee</code>. On the other hand, if we wrote something
+ like this:</p>
+
+ <pre class="cxx">
+ query q ("first = 123 AND agee = " + query::_ref (age));
+ </pre>
+
+ <p>It would compile fine and would trigger an error only when executed
+ by the database system.</p>
+
+ <p>We can also combine the two query languages in a single query, for
+ example:</p>
+
+ <pre class="cxx">
+ query q ("first = 'John' AND" + (query::age &lt; query::_ref (age)));
+ </pre>
+
+ <h2><a name="4.1">4.1 ODB Query Language</a></h2>
+
+ <p>An ODB query is an expression that tells the database system whether
+ any given object matches the desired criteria. As such, a query expression
+ always evaluates as <code>true</code> or <code>false</code>. At
+ the higher level, an expression consists of other expressions
+ combined with logical operators such as <code>&amp;&amp;</code> (AND),
+ <code>||</code> (OR), and <code>!</code> (NOT). For example:</p>
+
+ <pre class="cxx">
+ typedef odb::query&lt;person> query;
+
+ query q (query::first == "John" || query::age == 31);
+ </pre>
+
+ <p>At the core of every query expression lie simple expressions which
+ involve one or more object members, values, or parameters. To
+ refer to an object member we use an expression such as
+ <code>query::first</code> above. The names of members in the
+ <code>query</code> class are derived from the names of data members
+ in the object class by removing the common member name decorations,
+ such as leading and trailing underscores, the <code>m_</code> prefix,
+ etc.</p>
+
+ <p>In a simple expression an object member can be compared to a value,
+ parameter, or another member using a number of predefined operators
+ and functions. The following table gives an overview of the available
+ expressions:</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="operators" border="1">
+ <tr>
+ <th>Operator</th>
+ <th>Description</th>
+ <th>Example</th>
+ </tr>
+
+ <tr>
+ <td><code>==</code></td>
+ <td>equal</td>
+ <td><code>query::age == 31</code></td>
+ </tr>
+
+ <tr>
+ <td><code>!=</code></td>
+ <td>unequal</td>
+ <td><code>query::age != 31</code></td>
+ </tr>
+
+ <tr>
+ <td><code>&lt;</code></td>
+ <td>less than</td>
+ <td><code>query::age &lt; 31</code></td>
+ </tr>
+
+ <tr>
+ <td><code>></code></td>
+ <td>greater than</td>
+ <td><code>query::age > 31</code></td>
+ </tr>
+
+ <tr>
+ <td><code>&lt;=</code></td>
+ <td>less than or equal</td>
+ <td><code>query::age &lt;= 31</code></td>
+ </tr>
+
+ <tr>
+ <td><code>>=</code></td>
+ <td>greater than or equal</td>
+ <td><code>query::age >= 31</code></td>
+ </tr>
+
+ <tr>
+ <td><code>in()</code></td>
+ <td>one of the values</td>
+ <td><code>query::age.in (30, 32, 34)</code></td>
+ </tr>
+
+ <tr>
+ <td><code>in_range()</code></td>
+ <td>one of the values in range</td>
+ <td><code>query::age.in_range (begin, end)</code></td>
+ </tr>
+
+ <tr>
+ <td><code>like()</code></td>
+ <td>matches a pattern</td>
+ <td><code>query::first.like ("J%")</code></td>
+ </tr>
+
+ <tr>
+ <td><code>is_null()</code></td>
+ <td>value is <code>NULL</code></td>
+ <td><code>query::age.is_null ()</code></td>
+ </tr>
+
+ <tr>
+ <td><code>is_not_null()</code></td>
+ <td>value is <code>NOT NULL</code></td>
+ <td><code>query::age.is_not_null ()</code></td>
+ </tr>
+ </table>
+
+ <p>The <code>in()</code> function accepts a maximum of five arguments.
+ Use the <code>in_range()</code> function if you need to compare
+ to more than five values. This function accepts a pair of
+ standard C++ iterators and compares to all the values from
+ the <code>begin</code> position inclusive and until and
+ excluding the <code>end</code> position. The following
+ code fragment shows how we can use these functions:</p>
+
+ <pre class="cxx">
+ std::vector&lt;string> names;
+
+ names.push_back ("John");
+ names.push_back ("Jack");
+ names.push_back ("Jane");
+
+ query q1 (query::first.in ("John", "Jack", "Jane"));
+ query q2 (query::first.in_range (names.begin (), names.end ()));
+ </pre>
+
+ <p>Note that the <code>like()</code> function does not perform any
+ translation of the database system-specific extensions of the
+ SQL <code>LIKE</code> operator. As a result, if you would like
+ your application to be portable among various database systems,
+ then limit the special characters used in the pattern to
+ <code>%</code> (matches zero or more characters) and <code>_</code>
+ (matches exactly one character). It is also possible to specify
+ the escape character as a second argument to the <code>like()</code>
+ function. This character can then be used to escape the special
+ characters (<code>%</code> and <code>_</code>) in the pattern.
+ For example, the following query will match any two characters
+ separated by an underscore:</p>
+
+ <pre class="cxx">
+ query q (query::name.like ("_!__", "!"));
+ </pre>
+
+ <p>The operator precedence in the query expressions are the same
+ as for equivalent C++ operators. We can use parentheses to
+ make sure the expression is evaluated in the desired order.
+ For example:</p>
+
+ <pre class="cxx">
+ query q ((query::first == "John" || query::first == "Jane") &amp;&amp;
+ query::age &lt; 31);
+ </pre>
+
+
+ <h2><a name="4.2">4.2 Parameter Binding</a></h2>
+
+ <p>An instance of the <code>odb::query</code> class encapsulates two
+ parts of information about the query: the query expression and
+ the query parameters. Parameters can be bound to C++ variables
+ either by value or by reference.</p>
+
+ <p>If a parameter is bound by value, then the value for this parameter
+ is copied from the C++ variable to the query instance at the query
+ construction time. On the other hand, if a parameter is bound by
+ reference, then the query instance stores a reference to the
+ bound variable. The actual value of the parameter is only extracted
+ at the query execution time. Consider, for example, the following
+ two queries:</p>
+
+ <pre class="cxx">
+ string name ("John");
+
+ query q1 (query::first == query::_val (name));
+ query q2 (query::first == query::_ref (name));
+
+ name = "Jane";
+
+ db.query&lt;person> (q1); // Find John.
+ db.query&lt;person> (q2); // Find Jane.
+ </pre>
+
+ <p>The <code>odb::query</code> class provides two special functions,
+ <code>_val()</code> and <code>_ref()</code>, that allow us to
+ bind the parameter either by value or by reference, respectively.
+ In the ODB query language, if the binding is not specified
+ explicitly, the value semantic is used by default. In the
+ native query language, binding must always be specified
+ explicitly. For example:</p>
+
+ <pre class="cxx">
+ query q1 (query::age &lt; age); // By value.
+ query q2 (query::age &lt; query::_val (age)); // By value.
+ query q3 (query::age &lt; query::_ref (age)); // By reference.
+
+ query q4 ("age &lt; " + age); // Error.
+ query q5 ("age &lt; " + query::_val (age)); // By value.
+ query q6 ("age &lt; " + query::_ref (age)); // By reference.
+ </pre>
+
+ <p>A query that only has by-value parameters does not depend on any
+ other variables and is self-sufficient once constructed. A query
+ that has one or more by-reference parameters depends on the
+ bound variables until the query is executed. If one such variable
+ goes out of scope and we execute the query, the behavior is
+ undefined.</p>
+
+ <h2><a name="4.3">4.3 Executing a Query</a></h2>
+
+ <p>Once we have the query instance ready and by-reference parameters
+ initialized, we can execute the query using the
+ <code>database::query()</code> function template. It has two
+ overloaded versions:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ result&lt;T>
+ query (bool cache = true);
+
+ template &lt;typename T>
+ result&lt;T>
+ query (const odb::query&lt;T>&amp;, bool cache = true);
+ </pre>
+
+ <p>The first <code>query()</code> function is used to return all the
+ persistent objects of a given type stored in the database.
+ The second function uses the passed query instance to only return
+ objects matching the query criteria. The <code>cache</code> argument
+ determines whether the objects' states should be cached in the
+ application's memory or if they should be returned by the database
+ system one by one as the iteration over the result progresses. The
+ result caching is discussed in detail in the next section.</p>
+
+ <p>When calling the <code>query()</code> function, we have to
+ explicitly specify the object type we are querying. For example:</p>
+
+ <pre class="cxx">
+ typedef odb::query&lt;person> query;
+ typedef odb::result&lt;person> result;
+
+ result all (db.query&lt;person> ());
+ result johns (db.query&lt;person> (query::first == "John"));
+ </pre>
+
+ <p>Note that it is not required to explicitly create a named
+ query variable before executing it. For example, the following
+ two queries are equivalent:</p>
+
+ <pre class="cxx">
+ query q (query::first == "John");
+
+ result r1 (db.query&lt;person> (q));
+ result r1 (db.query&lt;person> (query::first == "John"));
+ </pre>
+
+ <p>Normally, we would create a named query instance if we are
+ planning to run the same query multiple times and would use the
+ in-line version for those that are executed only once (see also
+ <a href="#4.5">Section 4.5, "Prepared Queries"</a> for a more
+ optimal way to re-execute the same query multiple times). A named
+ query instance that does not have any by-reference parameters is
+ immutable and can be shared between multiple threads without
+ synchronization. On the other hand, a query instance with
+ by-reference parameters is modified every time it is executed.
+ If such a query is shared among multiple threads, then access
+ to this query instance must be synchronized from the execution
+ point and until the completion of the iteration over the result.</p>
+
+ <p>It is also possible to create queries from other queries by
+ combining them using logical operators. For example:</p>
+
+ <pre class="cxx">
+result
+find_minors (database&amp; db, const query&amp; name_query)
+{
+ return db.query&lt;person> (name_query &amp;&amp; query::age &lt; 18);
+}
+
+result r (find_minors (db, query::first == "John"));
+ </pre>
+
+ <p>The result of executing a query is zero, one, or more objects
+ matching the query criteria. The <code>query()</code> function
+ returns this result as an instance of the <code>odb::result</code>
+ class template, which provides a stream-like interface and is
+ discussed in detail in the next section.</p>
+
+ <p>In situations where we know that a query produces at most one
+ element, we can instead use the <code>database::query_one()</code> and
+ <code>database::query_value()</code> shortcut functions, for example:</p>
+
+ <pre class="cxx">
+ typedef odb::query&lt;person> query;
+
+ auto_ptr&lt;person> p (
+ db.query_one&lt;person> (
+ query::email == "jon@example.com"));
+ </pre>
+
+ <p>The shortcut query functions have the following signatures:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ typename object_traits&lt;T>::pointer_type
+ query_one ();
+
+ template &lt;typename T>
+ bool
+ query_one (T&amp;);
+
+ template &lt;typename T>
+ T
+ query_value ();
+
+ template &lt;typename T>
+ typename object_traits&lt;T>::pointer_type
+ query_one (const odb::query&lt;T>&amp;);
+
+ template &lt;typename T>
+ bool
+ query_one (const odb::query&lt;T>&amp;, T&amp;);
+
+ template &lt;typename T>
+ T
+ query_value (const odb::query&lt;T>&amp;);
+ </pre>
+
+ <p>Similar to <code>query()</code>, the first three functions are used
+ to return the only persistent object of a given type stored in the
+ database. The second three versions use the passed query instance
+ to only return the object matching the query criteria.</p>
+
+ <p>Similar to the <code>database::find()</code> functions
+ (<a href="#3.9">Section 3.9, "Loading Persistent Objects"</a>),
+ <code>query_one()</code> can either allocate a new instance of the
+ object class in the dynamic memory or it can load the object's state
+ into an existing instance. The <code>query_value()</code> function
+ allocates and returns the object by value.</p>
+
+ <p>The <code>query_one()</code> function allows us to determine
+ if the query result contains zero or one element. If no objects
+ matching the query criteria were found in the database, the
+ first version of <code>query_one()</code> returns the <code>NULL</code>
+ pointer while the second &mdash; <code>false</code>. If the second
+ version returns <code>false</code>, then the passed object
+ remains unchanged. For example:</p>
+
+ <pre class="cxx">
+ if (unique_ptr&lt;person> p = db.query_one&lt;person> (
+ query::email == "jon@example.com"))
+ {
+ ...
+ }
+
+ person p;
+ if (db.query_one&lt;person> (query::email == "jon@example.com", p))
+ {
+ ...
+ }
+ </pre>
+
+ <p>If the query executed using <code>query_one()</code> or
+ <code>query_value()</code> returns more than one element,
+ then these functions fail with an assertion. Additionally,
+ <code>query_value()</code> also fails with an assertion if
+ the query returned no elements.</p>
+
+ <p>Common situations where we can use the shortcut functions are a
+ query condition that uses a data member with the
+ <code>unique</code> constraint (at most one element returned;
+ see <a href="#14.7">Section 14.7, "Index Definition Pragmas"</a>)
+ as well as aggregate queries (exactly one element returned; see
+ <a href="#10">Chapter 10, "Views"</a>).</p>
+
+ <h2><a name="4.4">4.4 Query Result</a></h2>
+
+ <p>The <code>database::query()</code> function returns the result of
+ executing a query as an instance of the <code>odb::result</code>
+ class template, for example:</p>
+
+ <pre class="cxx">
+ typedef odb::query&lt;person> query;
+ typedef odb::result&lt;person> result;
+
+ result johns (db.query&lt;person> (query::first == "John"));
+ </pre>
+
+ <p>It is best to view an instance of <code>odb::result</code>
+ as a handle to a stream, such as a socket stream. While we can
+ make a copy of a result or assign one result to another, the
+ two instances will refer to the same result stream. Advancing
+ the current position in one instance will also advance it in
+ another. The result instance is only usable within the transaction
+ it was created in. Trying to manipulate the result after the
+ transaction has terminated leads to undefined behavior.</p>
+
+ <p>The <code>odb::result</code> class template conforms to the
+ standard C++ sequence requirements and has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ template &lt;typename T>
+ class result
+ {
+ public:
+ typedef odb::result_iterator&lt;T> iterator;
+
+ public:
+ result ();
+
+ result (const result&amp;);
+
+ result&amp;
+ operator= (const result&amp;);
+
+ void
+ swap (result&amp;)
+
+ public:
+ iterator
+ begin ();
+
+ iterator
+ end ();
+
+ public:
+ void
+ cache ();
+
+ bool
+ empty () const;
+
+ std::size_t
+ size () const;
+ };
+}
+ </pre>
+
+ <p>The default constructor creates an empty result set. The
+ <code>cache()</code> function caches the returned objects'
+ state in the application's memory. We have already mentioned
+ result caching when we talked about query execution. As you
+ may remember the <code>database::query()</code> function
+ caches the result unless instructed not to by the caller.
+ The <code>cache()</code> function allows us to
+ cache the result at a later stage if it wasn't already
+ cached during query execution.</p>
+
+ <p>If the result is cached, the database state of all the returned
+ objects is stored in the application's memory. Note that
+ the actual objects are still only instantiated on demand
+ during result iteration. It is the raw database state that
+ is cached in memory. In contrast, for uncached results
+ the object's state is sent by the database system one object
+ at a time as the iteration progresses.</p>
+
+ <p>Uncached results can improve the performance of both the application
+ and the database system in situations where we have a large
+ number of objects in the result or if we will only examine
+ a small portion of the returned objects. However, uncached
+ results have a number of limitations. There can only be one
+ uncached result in a transaction. Creating another result
+ (cached or uncached) by calling <code>database::query()</code>
+ will invalidate the existing uncached result. Furthermore,
+ calling any other database functions, such as <code>update()</code>
+ or <code>erase()</code> will also invalidate the uncached result.
+ It also follows that uncached results cannot be used on objects
+ with containers (<a href="#5">Chapter 5, "Containers"</a>) since
+ loading a container would invalidate the uncached result.</p>
+
+ <p>The <code>empty()</code> function returns <code>true</code> if
+ there are no objects in the result and <code>false</code> otherwise.
+ The <code>size()</code> function can only be called for cached results.
+ It returns the number of objects in the result. If we call this
+ function on an uncached result, the <code>odb::result_not_cached</code>
+ exception is thrown.</p>
+
+ <p>To iterate over the objects in a result we use the
+ <code>begin()</code> and <code>end()</code> functions
+ together with the <code>odb::result&lt;T>::iterator</code>
+ type, for example:</p>
+
+ <pre class="cxx">
+ result r (db.query&lt;person> (query::first == "John"));
+
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ ...
+ }
+ </pre>
+
+ <p>In C++11 we can use the <code>auto</code>-typed variabe instead
+ of spelling the iterator type explicitly, for example:</p>
+
+ <pre class="cxx">
+ for (auto i (r.begin ()); i != r.end (); ++i)
+ {
+ ...
+ }
+ </pre>
+
+ <p>The C++11 range-based <code>for</code>-loop can be used to further
+ simplify the iteration:</p>
+
+ <pre class="cxx">
+ for (person&amp; p: r)
+ {
+ ...
+ }
+ </pre>
+
+ <p>The result iterator is an input iterator which means that the
+ only two position operations that it supports are to move to the
+ next object and to determine whether the end of the result stream
+ has been reached. In fact, the result iterator can only be in two
+ states: the current position and the end position. If we have
+ two iterators pointing to the current position and then we
+ advance one of them, the other will advance as well. This,
+ for example, means that it doesn't make sense to store an
+ iterator that points to some object of interest in the result
+ stream with the intent of dereferencing it after the iteration
+ is over. Instead, we would need to store the object itself. We
+ also cannot iterate over the same result multiple times without
+ re-executing the query.</p>
+
+ <p>The result iterator has the following dereference functions
+ that can be used to access the pointed-to object:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ template &lt;typename T>
+ class result_iterator
+ {
+ public:
+ T*
+ operator-> () const;
+
+ T&amp;
+ operator* () const;
+
+ typename object_traits&lt;T>::pointer_type
+ load ();
+
+ void
+ load (T&amp; x);
+
+ typename object_traits&lt;T>::id_type
+ id ();
+ };
+}
+ </pre>
+
+ <p>When we call the <code>*</code> or <code>-></code> operator,
+ the iterator will allocate a new instance of the object class
+ in the dynamic memory, load its state from the database
+ state, and return a reference or pointer to the new instance. The
+ iterator maintains the ownership of the returned object and will
+ return the same pointer for subsequent calls to either of these
+ operators until it is advanced to the next object or we call
+ the first <code>load()</code> function (see below). For example:</p>
+
+ <pre class="cxx">
+ result r (db.query&lt;person> (query::first == "John"));
+
+ for (result::iterator i (r.begin ()); i != r.end ();)
+ {
+ cout &lt;&lt; i->last () &lt;&lt; endl; // Create an object.
+ person&amp; p (*i); // Reference to the same object.
+ cout &lt;&lt; p.age () &lt;&lt; endl;
+ ++i; // Free the object.
+ }
+ </pre>
+
+ <p>The overloaded <code>result_iterator::load()</code> functions are
+ similar to <code>database::load()</code>. The first function
+ returns a dynamically allocated instance of the current
+ object. As an optimization, if the iterator already owns an object
+ as a result of an earlier
+ call to the <code>*</code> or <code>-></code> operator, then it
+ relinquishes the ownership of this object and returns it instead.
+ This allows us to write code like this without worrying about
+ a double allocation:</p>
+
+ <pre class="cxx">
+ result r (db.query&lt;person> (query::first == "John"));
+
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ if (i->last == "Doe")
+ {
+ auto_ptr p (i.load ());
+ ...
+ }
+ }
+ </pre>
+
+ <p>Note, however, that because of this optimization, a subsequent
+ to <code>load()</code> call to the <code>*</code> or <code>-></code>
+ operator results in the allocation of a new object.</p>
+
+ <p>The second <code>load()</code> function allows
+ us to load the current object's state into an existing instance.
+ For example:</p>
+
+ <pre class="cxx">
+ result r (db.query&lt;person> (query::first == "John"));
+
+ person p;
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ i.load (p);
+ cout &lt;&lt; p.last () &lt;&lt; endl;
+ cout &lt;&lt; i.age () &lt;&lt; endl;
+ }
+ </pre>
+
+ <p>The <code>id()</code> function return the object id of the current
+ object. While we can achieve the same by loading the object and getting
+ its id, this function is more efficient since it doesn't actually
+ create the object. This can be useful when all we need is the object's
+ identifier. For example:</p>
+
+ <pre class="cxx">
+ std::set&lt;unsigned long> set = ...; // Persons of interest.
+
+ result r (db.query&lt;person> (query::first == "John"));
+
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ if (set.find (i.id ()) != set.end ()) // No object loaded.
+ {
+ cout &lt;&lt; i->first () &lt;&lt; endl; // Object loaded.
+ }
+ }
+ </pre>
+
+ <h2><a name="4.5">4.5 Prepared Queries</a></h2>
+
+ <p>Most modern relational database systems have the notion of a prepared
+ statement. Prepared statements allow us to perform the potentially
+ expensive tasks of parsing SQL, preparing the query execution
+ plan, etc., once and then executing the same query multiple
+ times, potentially using different values for parameters in
+ each execution.</p>
+
+ <p>In ODB all the non-query database operations such as
+ <code>persist()</code>, <code>load()</code>, <code>update()</code>,
+ etc., are implemented in terms of prepared statements that are cached
+ and reused. While the <code>query()</code>, <code>query_one()</code>,
+ and <code>query_value()</code> database operations also use prepared
+ statements, these statements are not cached or reused by default since
+ ODB has no knowledge of whether a query will be executed multiple times
+ or only once. Instead, ODB provides a mechanism, called prepared queries,
+ that allows us to prepare a query once and execute it multiple
+ times. In other words, ODB prepared queries are a thin wrapper
+ around the underlying database's prepared statement functionality.</p>
+
+ <p>In most cases ODB shields the application developer from database
+ connection management and multi-threading issues. However, when it
+ comes to prepared queries, a basic understanding of how ODB manages
+ these aspects is required. Conceptually, the <code>odb::database</code>
+ class represents a specific database, that is, a data store. However,
+ underneath, it maintains one or more connections to this database.
+ A connection can be used only by a single thread at a time. When
+ we start a transaction (by calling <code>database::begin()</code>),
+ the transaction instance obtains a connection and holds on to it
+ until the transaction is committed or rolled back. During this time
+ no other thread can use this connection. When the transaction
+ releases the connection, it may be closed or reused by another
+ transaction in this or another thread. What exactly happens to
+ a connection after it has been released depends on the connection
+ factory that is used by the <code>odb::database</code> instance.
+ For more information on connection factories, refer to
+ <a href="#II">Part II, "Database Systems"</a>.</p>
+
+ <p>A query prepared on one connection cannot be executed on another.
+ In other words, a prepared query is associated with the connection.
+ One important implication of this restriction is that we cannot
+ prepare a query in one transaction and then try to execute it
+ in another without making sure that both transactions use the
+ same connection.</p>
+
+ <p>To enable the prepared query functionality we need to specify
+ the <code>--generate-prepared</code> ODB compiler option. If
+ we are planning to always prepare our queries, then we can
+ disable the once-off query execution support by also specifying
+ the <code>--omit-unprepared</code> option.</p>
+
+ <p>To prepare a query we use the <code>prepare_query()</code> function
+ template. This function can be called on both the <code>odb::database</code>
+ and <code>odb::connection</code> instances. The <code>odb::database</code>
+ version simply obtains the connection used by the currently active
+ transaction and calls the corresponding <code>odb::connection</code>
+ version. If no transaction is currently active, then this function
+ throws the <code>odb::not_in_transaction</code> exception
+ (<a href="#3.5">Section 3.5, "Transactions"</a>). The
+ <code>prepare_query()</code> function has the following signature:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ prepared_query&lt;T>
+ prepare_query (const char* name, const odb::query&lt;T>&amp;);
+ </pre>
+
+ <p>The first argument to the <code>prepare_query()</code> function is
+ the prepared query name. This name is used as a key for prepared
+ query caching (discussed later) and must be unique. For some databases,
+ notably PostgreSQL, it is also used as a name of the underlying prepared
+ statement. The name <code>"<i>object</i>_query"</code> (for example,
+ <code>"person_query"</code>) is reserved for the once-off queries
+ executed by the <code>database::query()</code> function. Note that
+ the <code>prepare_query()</code> function makes only a shallow copy
+ of this argument, which means that the name must be valid for the
+ lifetime of the returned <code>prepared_query</code> instance.</p>
+
+ <p>The second argument to the <code>prepare_query()</code> function
+ is the query criteria. It has the same semantics as in the
+ <code>query()</code> function discussed in <a href="#4.3">Section
+ 4.3, "Executing a Query"</a>. Similar to <code>query()</code>, we
+ also have to explicitly specify the object type that we will be
+ querying. For example:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;person> query;
+typedef odb::prepared_query&lt;person> prep_query;
+
+prep_query pq (
+ db.prepare_query&lt;person> ("person-age-query", query::age > 50));
+ </pre>
+
+ <p>The result of executing the <code>prepare_query()</code> function is
+ the <code>prepared_query</code> instance that represent the prepared
+ query. It is best to view <code>prepared_query</code> as a handle to
+ the underlying prepared statement. While we can make a copy of it or
+ assign one <code>prepared_query</code> to another, the two instances
+ will refer to the same prepared statement. Once the last instance of
+ <code>prepared_query</code> referencing a specific prepared statement
+ is destroyed, this statement is released. The <code>prepared_query</code>
+ class template has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ template &lt;typename T>
+ struct prepared_query
+ {
+ prepared_query ();
+
+ prepared_query (const prepared_query&amp;)
+ prepared_query&amp; operator= (const prepared_query&amp;)
+
+ result&lt;T>
+ execute (bool cache = true);
+
+ typename object_traits&lt;T>::pointer_type
+ execute_one ();
+
+ bool
+ execute_one (T&amp; object);
+
+ T
+ execute_value ();
+
+ const char*
+ name () const;
+
+ statement&amp;
+ statement () const;
+
+ operator unspecified_bool_type () const;
+ };
+}
+ </pre>
+
+ <p>The default constructor creates an empty <code>prepared_query</code>
+ instance, that is, an instance that does not reference a prepared
+ statement and therefore cannot be executed. The only way to create
+ a non-empty prepared query is by calling the <code>prepare_query()</code>
+ function discussed above. To test whether the prepared query is empty,
+ we can use the implicit conversion operator to a boolean type. For
+ example:</p>
+
+ <pre class="cxx">
+ prepared_query&lt;person> pq;
+
+ if (pq)
+ {
+ // Not empty.
+ ...
+ }
+ </pre>
+
+ <p>The <code>execute()</code> function executes the query and returns
+ the result instance. The <code>cache</code> argument indicates
+ whether the result should be cached and has the same semantics
+ as in the <code>query()</code> function. In fact, conceptually,
+ <code>prepare_query()</code> and <code>execute()</code> are just
+ the <code>query()</code> function split into two:
+ <code>prepare_query()</code> takes the first
+ <code>query()</code> argument (the query condition) while
+ <code>execute()</code> takes the second (the cache flag). Note
+ also that re-executing a prepared query invalidates the
+ previous execution result, whether cached or uncached. </p>
+
+ <p>The <code>execute_one()</code> and <code>execute_value()</code>
+ functions can be used as shortcuts to execute a query that is
+ known to return at most one or exactly one object, respectively.
+ The arguments and return values in these functions have the same
+ semantics as in <code>query_one()</code> and <code>query_value()</code>.
+ And similar to <code>execute()</code> above, <code>prepare_query()</code>
+ and <code>execute_one/value()</code> can be seen as the
+ <code>query_one/value()</code> function split into two:
+ <code>prepare_query()</code> takes the first
+ <code>query_one/value()</code> argument (the query condition) while
+ <code>execute_one/value()</code> takes the second argument (if any)
+ and returns the result. Note also that <code>execute_one/value()</code>
+ never caches its result but invalidates the result of any previous
+ <code>execute()</code> call on the same prepared query.</p>
+
+ <p>The <code>name()</code> function returns the prepared query name.
+ This is the same name as was passed as the first argument in the
+ <code>prepare_query()</code> call. The <code>statement()</code>
+ function returns a reference to the underlying prepared statement.
+ Note also that calling any of these functions on an empty
+ <code>prepared_query</code> instance results in undefined behavior.</p>
+
+ <p>The simplest use-case for a prepared query is the need to
+ execute the same query multiple times within a single transaction.
+ Consider the following example that queries for people that are older
+ than a number of different ages. This and subsequent code fragments
+ are taken from the <code>prepared</code> example in the
+ <code>odb-examples</code> package.</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;person> query;
+typedef odb::prepared_query&lt;person> prep_query;
+typedef odb::result&lt;person> result;
+
+transaction t (db.begin ());
+
+unsigned short age;
+query q (query::age > query::_ref (age));
+prep_query pq (db.prepare_query&lt;person> ("person-age-query", q));
+
+for (age = 90; age > 40; age -= 10)
+{
+ result r (pq.execute ());
+ ...
+}
+
+t.commit ();
+ </pre>
+
+ <p>Another scenario is the need to reuse the same query in multiple
+ transactions that are executed at once. As was mentioned above,
+ in this case we need to make sure that the prepared query and
+ all the transactions use the same connection. Consider an
+ alternative version of the above example that executes each
+ query in a separate transaction:</p>
+
+ <pre class="cxx">
+connection_ptr conn (db.connection ());
+
+unsigned short age;
+query q (query::age > query::_ref (age));
+prep_query pq (conn->prepare_query&lt;person> ("person-age-query", q));
+
+for (age = 90; age > 40; age -= 10)
+{
+ transaction t (conn->begin ());
+
+ result r (pq.execute ());
+ ...
+
+ t.commit ();
+}
+ </pre>
+
+
+ <p>Note that with this approach we hold on to the database connection
+ until all the transactions involving the prepared query are
+ executed. In particular, this means that while we are busy, the
+ connection cannot be reused by another thread. Therefore, this
+ approach is only recommended if all the transactions are executed
+ close to each other. Also note that an uncached (see below)
+ prepared query is invalidated once we release the connection
+ on which it was prepared.</p>
+
+ <p>If we need to reuse a prepared query in transactions that are
+ executed at various times, potentially in different threads, then
+ the recommended approach is to cache the prepared query on the
+ connection. To support this functionality the <code>odb::database</code>
+ and <code>odb::connection</code> classes provide the following
+ function templates. Similar to <code>prepare_query()</code>,
+ the <code>odb::database</code> versions of the below
+ functions call the corresponding <code>odb::connection</code>
+ versions using the currently active transaction to resolve
+ the connection.</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ void
+ cache_query (const prepared_query&lt;T>&amp;);
+
+ template &lt;typename T, typename P>
+ void
+ cache_query (const prepared_query&lt;T>&amp;,
+ std::[auto|unique]_ptr&lt;P> params);
+
+ template &lt;typename T>
+ prepared_query&lt;T>
+ lookup_query (const char* name);
+
+ template &lt;typename T, typename P>
+ prepared_query&lt;T>
+ lookup_query (const char* name, P*&amp; params);
+ </pre>
+
+ <p>The <code>cache_query()</code> function caches the passed prepared
+ query on the connection. The second overloaded version of
+ <code>cache_query()</code> also takes a pointer to the
+ by-reference query parameters. In C++98/03 it should be
+ <code>std::auto_ptr</code> while in C++11 <code>std::auto_ptr</code>
+ or <code>std::unique_ptr</code> can be used. The
+ <code>cache_query()</code> function assumes ownership of the
+ passed <code>params</code> argument. If a prepared query
+ with the same name is already cached on this connection,
+ then the <code>odb::prepared_already_cached</code> exception
+ is thrown.</p>
+
+ <p>The <code>lookup_query()</code> function looks up a previously
+ cached prepared query given its name. The second overloaded
+ version of <code>lookup_query()</code> also returns a pointer
+ to the by-reference query parameters. If a prepared query
+ with this name has not been cached, then an empty
+ <code>prepared_query</code> instance is returned. If a
+ prepared query with this name has been cached but either
+ the object type or the parameters type does not match
+ that which was cached, then the <code>odb::prepared_type_mismatch</code>
+ exception is thrown.</p>
+
+ <p>As a first example of the prepared query cache functionality,
+ consider the case that does not use any by-reference parameters:</p>
+
+ <pre class="cxx">
+for (unsigned short i (0); i &lt; 5; ++i)
+{
+ transaction t (db.begin ());
+
+ prep_query pq (db.lookup_query&lt;person> ("person-age-query"));
+
+ if (!pq)
+ {
+ pq = db.prepare_query&lt;person> (
+ "person-val-age-query", query::age > 50);
+ db.cache_query (pq);
+ }
+
+ result r (pq.execute ());
+ ...
+
+ t.commit ();
+
+ // Do some other work.
+ //
+ ...
+}
+ </pre>
+
+ <p>The following example shows how to do the same but for a query that
+ includes by-reference parameters. In this case the parameters are
+ cached together with the prepared query.</p>
+
+ <pre class="cxx">
+for (unsigned short age (90); age > 40; age -= 10)
+{
+ transaction t (db.begin ());
+
+ unsigned short* age_param;
+ prep_query pq (
+ db.lookup_query&lt;person> ("person-age-query", age_param));
+
+ if (!pq)
+ {
+ auto_ptr&lt;unsigned short> p (new unsigned short);
+ age_param = p.get ();
+ query q (query::age > query::_ref (*age_param));
+ pq = db.prepare_query&lt;person> ("person-age-query", q);
+ db.cache_query (pq, p); // Assumes ownership of p.
+ }
+
+ *age_param = age; // Initialize the parameter.
+ result r (pq.execute ());
+ ...
+
+ t.commit ();
+
+ // Do some other work.
+ //
+ ...
+}
+ </pre>
+
+ <p>As is evident from the above examples, when we use a prepared
+ query cache, each transaction that executes a query must also
+ include code that prepares and caches this query if it hasn't already
+ been done. If a prepared query is used in a single place in the
+ application, then this is normally not an issue since all the
+ relevant code is kept in one place. However, if the same query
+ is used in several different places in the application, then
+ we may end up duplicating the same preparation and caching
+ code, which makes it hard to maintain.</p>
+
+ <p>To resolve this issue ODB allows us to register a prepared
+ query factory that will be called to prepare and cache a
+ query during the call to <code>lookup_query()</code>. To
+ register a factory we use the <code>database::query_factory()</code>
+ function. In C++98/03 it has the following signature:</p>
+
+ <pre class="cxx">
+ void
+ query_factory (const char* name,
+ void (*factory) (const char* name, connection&amp;));
+ </pre>
+
+ <p>While in C++11 it uses the <code>std::function</code> class
+ template:</p>
+
+ <pre class="cxx">
+ void
+ query_factory (const char* name,
+ std::function&lt;void (const char* name, connection&amp;)>);
+ </pre>
+
+ <p>The first argument to the <code>query_factory()</code> function is
+ the prepared query name that this factory will be called to prepare
+ and cache. An empty name is treated as a fallback wildcard factory
+ that is capable of preparing any query. The second argument is the
+ factory function or, in C++11, function object or lambda.</p>
+
+ <p>The example fragment shows how we can use the prepared query
+ factory:</p>
+
+ <pre class="cxx">
+struct params
+{
+ unsigned short age;
+ string first;
+};
+
+static void
+query_factory (const char* name, connection&amp; c)
+{
+ auto_ptr&lt;params> p (new params);
+ query q (query::age > query::_ref (p->age) &amp;&amp;
+ query::first == query::_ref (p->first));
+ prep_query pq (c.prepare_query&lt;person> (name, q));
+ c.cache_query (pq, p);
+}
+
+db.query_factory ("person-age-name-query", &amp;query_factory);
+
+for (unsigned short age (90); age > 40; age -= 10)
+{
+ transaction t (db.begin ());
+
+ params* p;
+ prep_query pq (db.lookup_query&lt;person> ("person-age-name-query", p));
+ assert (pq);
+
+ p->age = age;
+ p->first = "John";
+ result r (pq.execute ());
+ ...
+
+ t.commit ();
+}
+ </pre>
+
+ <p>In C++11 we could have instead used a lambda function as well as
+ <code>unique_ptr</code> rather than <code>auto_ptr</code>:</p>
+
+ <pre>
+db.query_factory (
+ "person-age-name-query",
+ [] (const char* name, connection&amp; c)
+ {
+ unique_ptr&lt;params> p (new params);
+ query q (query::age > query::_ref (p->age) &amp;&amp;
+ query::first == query::_ref (p->first));
+ prep_query pq (c.prepare_query&lt;person> (name, q));
+ c.cache_query (pq, std::move (p));
+ });
+ </pre>
+
+ Note that the <code>database::query_factory()</code> function is not
+ thread-safe and should be called before starting any threads that may
+ require this functionality. Normally, all the prepared query factories
+ are registered as part of the database instance creation.
+
+ <!-- CHAPTER -->
+
+ <hr class="page-break"/>
+ <h1><a name="5">5 Containers</a></h1>
+
+ <p>The ODB runtime library provides built-in persistence support for all the
+ commonly used standard C++98/03 containers, namely,
+ <code>std::vector</code>, <code>std::list</code>, <code>std::deque</code>,
+ <code>std::set</code>, <code>std::multiset</code>, <code>std::map</code>, and
+ <code>std::multimap</code> as well as C++11 <code>std::array</code>,
+ <code>std::forward_list</code>, <code>std::unordered_set</code>,
+ <code>std::unordered_multiset</code>, <code>std::unordered_map</code>,
+ and <code>std::unordered_multimap</code>.
+ Plus, ODB profile libraries, that are
+ available for commonly used frameworks and libraries (such as Boost and
+ Qt), provide persistence support for containers found in these frameworks
+ and libraries (<a href="#III">Part III, "Profiles"</a>). Both the
+ ODB runtime library and profile libraries also provide a number of
+ change-tracking container equivalents which can be used to minimize
+ the number of database operations necessary to synchronize the container
+ state with the database (<a href="#5.4">Section 5.4, "Change-Tracking
+ Containers"</a>). It is also easy to persist custom container types
+ as discussed later in <a href="#5.5">Section 5.5, "Using Custom
+ Containers"</a>.</p>
+
+ <p>We don't need to do anything special to declare a member of a
+ container type in a persistent class. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+private:
+ std::vector&lt;std::string> nicknames_;
+ ...
+};
+ </pre>
+
+ <p>The complete version of the above code fragment and the other code
+ samples presented in this chapter can be found in the <code>container</code>
+ example in the <code>odb-examples</code> package.</p>
+
+ <p>A data member in a persistent class that is of a container type
+ behaves like a value type. That is, when an object is made persistent,
+ the elements of the container are stored in the database. Similarly,
+ when a persistent object is loaded from the database, the contents
+ of the container are automatically loaded as well. A data member
+ of a container type can also use a smart pointer, as discussed
+ in <a href="#7.3">Section 7.3, "Pointers and <code>NULL</code>
+ Value Semantics"</a>.</p>
+
+ <p>While an ordinary member is mapped to one or more columns in the
+ object's table, a member of a container type is mapped to a separate
+ table. The exact schema of such a table depends on the kind of
+ container. ODB defines the following container kinds: ordered,
+ set, multiset, map, and multimap. The container kinds and the
+ contents of the tables to which they are mapped are discussed
+ in detail in the following sections.</p>
+
+ <p>Containers in ODB can contain simple value types (<a href="#7.1">Section
+ 7.1, "Simple Value Types"</a>), composite value types
+ (<a href="#7.2">Section 7.2, "Composite Value Types"</a>), and pointers
+ to objects (<a href="#6">Chapter 6, "Relationships"</a>). Containers of
+ containers, either directly or indirectly via a composite value
+ type, are not allowed. A key in a map or multimap container can
+ be a simple or composite value type but not a pointer to an object.
+ An index in the ordered container should be a simple integer value
+ type.</p>
+
+ <p>The value type in the ordered, set, and map containers as well as
+ the key type in the map containers should be default-constructible.
+ The default constructor in these types can be made private in which
+ case the <code>odb::access</code> class should be made a friend of
+ the value or key type. For example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class name
+{
+public:
+ name (const std::string&amp;, const std::string&amp;);
+ ...
+private:
+ friend class odb::access;
+ name ();
+ ...
+};
+
+#pragma db object
+class person
+{
+ ...
+private:
+ std::vector&lt;name> aliases_;
+ ...
+};
+ </pre>
+
+
+ <h2><a name="5.1">5.1 Ordered Containers</a></h2>
+
+ <p>In ODB an ordered container is any container that maintains (explicitly
+ or implicitly) an order of its elements in the form of an integer index.
+ Standard C++ containers that are ordered include <code>std::vector</code>
+ <code>std::list</code>, and <code>std::deque</code> as well as C++11 <code>std::array</code> and
+ <code>std::forward_list</code>. While elements in <code>std::set</code>
+ are also kept in a specific order, this order is not based on an
+ integer index but rather on the relationship between elements. As
+ a result, <code>std::set</code> is not considered an ordered
+ container for the purpose of persistence.</p>
+
+ <p>The database table for an ordered container consists of at least
+ three columns. The first column contains the object id of a
+ persistent class instance of which the container is a member.
+ The second column contains the element index within a container.
+ And the last column contains the element value. If the object
+ id or element value are composite, then, instead of a single
+ column, they can occupy multiple columns. For an ordered
+ container table the ODB compiler also defines two indexes:
+ one for the object id column(s) and the other for the index
+ column. Refer to <a href="#14.7">Section 14.7, "Index Definition
+ Pragmas"</a> for more information on how to customize these
+ indexes.</p>
+
+ <p>Consider the following persistent object as an example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+private:
+ #pragma db id auto
+ unsigned long id_;
+
+ std::vector&lt;std::string> nicknames_;
+ ...
+};
+ </pre>
+
+ <p>The resulting database table (called <code>person_nicknames</code>) will
+ contain the object id column of type <code>unsigned&nbsp;long</code>
+ (called <code>object_id</code>), the index column of an integer type
+ (called <code>index</code>), and the value column of type
+ <code>std::string</code> (called <code>value</code>).</p>
+
+ <p>A number of ODB pragmas allow us to customize the table name, column
+ names, and native database types of an ordered container both, on
+ the per-container and per-member basis. For more information on
+ these pragmas, refer to <a href="#14">Chapter 14, "ODB Pragma
+ Language"</a>. The following example shows some of the possible
+ customizations:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+private:
+ #pragma db table("nicknames") \
+ id_column("person_id") \
+ index_type("SMALLINT UNSIGNED") \
+ index_column("nickname_number") \
+ value_type("VARCHAR(255)") \
+ value_column("nickname")
+ std::vector&lt;std::string> nicknames_;
+ ...
+};
+ </pre>
+
+ <p>While the C++ container used in a persistent class may be ordered,
+ sometimes we may wish to store such a container in the database without
+ the order information. In the example above, for instance, the order
+ of person's nicknames is probably not important. To instruct the ODB
+ compiler to ignore the order in ordered containers we can use the
+ <code>db&nbsp;unordered</code> pragma (<a href="#14.3.9">Section 14.3.9,
+ "<code>unordered</code>"</a>, <a href="#14.4.19">Section 14.4.19,
+ "<code>unordered</code>"</a>). For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+private:
+ #pragma db unordered
+ std::vector&lt;std::string> nicknames_;
+ ...
+};
+ </pre>
+
+ <p>The table for an ordered container that is marked unordered won't
+ have the index column and the order in which elements are retrieved
+ from the database may not be the same as the order in which they
+ were stored.</p>
+
+ <h2><a name="5.2">5.2 Set and Multiset Containers</a></h2>
+
+ <p>In ODB set and multiset containers (referred to as just set
+ containers) are associative containers that contain elements
+ based on some relationship between them. A set container may
+ or may not guarantee a particular order of the elements that
+ it stores. Standard C++ containers that are considered set
+ containers for the purpose of persistence include
+ <code>std::set</code> and <code>std::multiset</code> as well
+ as C++11 <code>std::unordered_set</code> and
+ <code>std::unordered_multiset</code>.</p>
+
+ <p>The database table for a set container consists of at least
+ two columns. The first column contains the object id of a
+ persistent class instance of which the container is a member.
+ And the second column contains the element value. If the object
+ id or element value are composite, then, instead of a single
+ column, they can occupy multiple columns. ODB compiler also
+ defines an index on a set container table for the object id
+ column(s). Refer to <a href="#14.7">Section 14.7, "Index Definition
+ Pragmas"</a> for more information on how to customize this
+ index.</p>
+
+ <p>Consider the following persistent object as an example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+private:
+ #pragma db id auto
+ unsigned long id_;
+
+ std::set&lt;std::string> emails_;
+ ...
+};
+ </pre>
+
+ <p>The resulting database table (called <code>person_emails</code>) will
+ contain the object id column of type <code>unsigned&nbsp;long</code>
+ (called <code>object_id</code>) and the value column of type
+ <code>std::string</code> (called <code>value</code>).</p>
+
+ <p>A number of ODB pragmas allow us to customize the table name,
+ column names, and native database types of a set container, both on
+ the per-container and per-member basis. For more information on
+ these pragmas, refer to <a href="#14">Chapter 14, "ODB Pragma
+ Language"</a>. The following example shows some of the possible
+ customizations:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+private:
+ #pragma db table("emails") \
+ id_column("person_id") \
+ value_type("VARCHAR(255)") \
+ value_column("email")
+ std::set&lt;std::string> emails_;
+ ...
+};
+ </pre>
+
+ <h2><a name="5.3">5.3 Map and Multimap Containers</a></h2>
+
+ <p>In ODB map and multimap containers (referred to as just map
+ containers) are associative containers that contain key-value
+ elements based on some relationship between keys. A map container
+ may or may not guarantee a particular order of the elements that
+ it stores. Standard C++ containers that are considered map
+ containers for the purpose of persistence include
+ <code>std::map</code> and <code>std::multimap</code> as well
+ as C++11 <code>std::unordered_map</code> and
+ <code>std::unordered_multimap</code>.</p>
+
+ <p>The database table for a map container consists of at least
+ three columns. The first column contains the object id of a
+ persistent class instance of which the container is a member.
+ The second column contains the element key. And the last column
+ contains the element value. If the object id, element key, or
+ element value are composite, then instead of a single column
+ they can occupy multiple columns. ODB compiler also
+ defines an index on a map container table for the object id
+ column(s). Refer to <a href="#14.7">Section 14.7, "Index Definition
+ Pragmas"</a> for more information on how to customize this
+ index.</p>
+
+ <p>Consider the following persistent object as an example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+private:
+ #pragma db id auto
+ unsigned long id_;
+
+ std::map&lt;unsigned short, float> age_weight_map_;
+ ...
+};
+ </pre>
+
+ <p>The resulting database table (called <code>person_age_weight_map</code>)
+ will contain the object id column of type <code>unsigned&nbsp;long</code>
+ (called <code>object_id</code>), the key column of type
+ <code>unsigned short</code> (called <code>key</code>), and the value
+ column of type <code>float</code> (called <code>value</code>).</p>
+
+ <p>A number of ODB pragmas allow us to customize the table name,
+ column names, and native database types of a map container, both on
+ the per-container and per-member basis. For more information on
+ these pragmas, refer to <a href="#14">Chapter 14, "ODB Pragma
+ Language"</a>. The following example shows some of the possible
+ customizations:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+private:
+ #pragma db table("weight_map") \
+ id_column("person_id") \
+ key_type("INT UNSIGNED") \
+ key_column("age") \
+ value_type("DOUBLE") \
+ value_column("weight")
+ std::map&lt;unsigned short, float> age_weight_map_;
+ ...
+};
+ </pre>
+
+ <h2><a name="5.4">5.4 Change-Tracking Containers</a></h2>
+
+ <p>When a persistent object containing one of the standard containers
+ is updated in the database, ODB has no knowledge of which elements
+ were inserted, erased, or modified. As a result, ODB has no choice
+ but to assume the whole container has changed and update the state
+ of every single element. This can result in a significant overhead
+ if a container contains a large number of elements and we only
+ changed a small subset of them.</p>
+
+ <p>To eliminate this overhead, ODB provides a notion of <em>change-tracking
+ containers</em>. A change-tracking container, besides containing
+ its elements, just like an ordinary container, also includes the
+ change state for each element. When it is time to update such a
+ container in the database, ODB can use this change information to
+ perform a minimum number of database operations necessary to
+ synchronize the container state with the database.</p>
+
+ <p>The current version of the ODB runtime library provides a change-tracking
+ equivalent of <code>std::vector</code> (<a href="#5.4.1">Section 5.4.1,
+ "Change-Tracking <code>vector</code>"</a>) with support for other
+ standard container equivalents planned for future releases. ODB
+ profile libraries also provide change-tracking equivalents for some
+ containers found in the corresponding frameworks and libraries
+ (<a href="#III">Part III, "Profiles"</a>).</p>
+
+ <p>A change-tracking container equivalent can normally be used as a drop-in
+ replacement for an ordinary container except for a few minor
+ interface differences (discussed in the corresponding sub-sections).
+ In particular, we don't need to do anything extra to effect
+ change tracking. ODB will automatically start, stop, and reset
+ change tracking when necessary. The following example illustrates
+ this point using <code>odb::vector</code> as a replacement for
+ <code>std::vector</code>.</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ odb::vector&lt;std::string> names;
+};
+
+person p; // No change tracking (not persistent).
+p.names.push_back ("John Doe");
+
+{
+ transaction t (db.begin ());
+ db.persist (p); // Start change tracking (persistent).
+ t.commit ();
+}
+
+p.names.push_back ("Johnny Doo");
+
+{
+ transaction t (db.begin ());
+ db.update (p); // One INSERT; reset change state.
+ t.commit ();
+}
+
+p.names.modify (0) = "Doe, John"; // Instead of operator[].
+p.names.pop_back ();
+
+{
+ transaction t (db.begin ());
+ db.update (p); // One UPDATE, one DELETE; reset change state.
+ t.commit ();
+}
+
+{
+ transaction t (db.begin ());
+ auto_ptr&lt;person> p1 (db.load&lt;person> (...)); // Start change tracking.
+ p1->names.insert (p1->names.begin (), "Joe Do");
+ db.update (*p1); // One UPDATE, one INSERT; reset change state.
+ t.commit ();
+}
+
+{
+ transaction t (db.begin ());
+ db.erase (p); // One DELETE; stop change tracking (not persistent).
+ t.commit ();
+}
+ </pre>
+
+ <p>One interesting aspect of change tracking is what happens when a
+ transaction that contains an update is later rolled back. In this
+ case, while the change-tracking container has reset the change
+ state (after update), actual changes were not committed to the
+ database. Change-tracking containers handle this case by
+ automatically registering a rollback callback and then, if it is
+ called, marking the container as "completely changed". In this
+ state, the container no longer tracks individual element changes
+ and, when updated, falls back to the complete state update, just
+ like an ordinary container. The following example illustrates
+ this point:</p>
+
+ <pre class="cxx">
+person p;
+p.names.push_back ("John Doe");
+
+{
+ transaction t (db.begin ());
+ db.persist (p); // Start change tracking (persistent).
+ t.commit ();
+}
+
+p.names.push_back ("Johnny Doo");
+
+for (;;)
+{
+ try
+ {
+ transaction t (db.begin ());
+
+ // First try: one INSERT.
+ // Next try: one DELETE, two INSERTs.
+ //
+ db.update (p); // Reset change state.
+
+ t.commit (); // If throws (rollback), mark as completely changed.
+ break;
+ }
+ catch (const odb::recoverable&amp;)
+ {
+ continue;
+ }
+}
+ </pre>
+
+ <p>For the interaction of change-tracking containers with change-updated
+ object sections, refer to <a href="#9.4">Section 9.4, "Sections and
+ Change-Tracking Containers"</a>. Note also that change-tracking
+ containers cannot be accessed with by-value accessors
+ (<a href="#14.4.5">Section 14.4.5,
+ "<code>get</code>/<code>set</code>/<code>access</code>"</a>)
+ since in certain situations such access may involve a
+ modification of the container (for example, clearing the change
+ flag after update).</p>
+
+ <h3><a name="5.4.1">5.4.1 Change-Tracking <code>vector</code></a></h3>
+
+ <p>Class template <code>odb::vector</code>, defined in
+ <code>&lt;odb/vector.hxx></code>, is a change-tracking
+ equivalent for <code>std::vector</code>. It
+ is implemented in terms of <code>std::vector</code> and is
+ implicit-convertible to and implicit-constructible from
+ <code>const std::vector&amp;</code>. In particular, this
+ means that we can use <code>odb::vector</code> instance
+ anywhere <code>const std::vector&amp;</code> is
+ expected. In addition, <code>odb::vector</code> constant
+ iterator (<code>const_iterator</code>) is the same type as
+ that of <code>std::vector</code>.</p>
+
+ <p><code>odb::vector</code> incurs 2-bit per element overhead
+ in order to store the change state. It cannot
+ be stored unordered in the database (<a href="#14.4.19">Section
+ 14.4.19 "<code>unordered</code>"</a>) but can be used as an inverse
+ side of a relationship (<a href="#6.2">6.2 "Bidirectional
+ Relationships"</a>). In this case, no change tracking is performed
+ since no state for such a container is stored in the database.</p>
+
+ <p>The number of database operations required to update the state
+ of <code>odb::vector</code> corresponds well to the complexity
+ of <code>std::vector</code> functions. In particular, adding or
+ removing an element from the back of the vector (for example,
+ with <code>push_back()</code> and <code>pop_back()</code>),
+ requires only a single database statement execution. In contrast,
+ inserting or erasing an element somewhere in the middle of the
+ vector will require a database statement for every element that
+ follows it.</p>
+
+ <p><code>odb::vector</code> replicates most of the <code>std::vector</code>
+ interface as defined in both C++98/03 and C++11 standards. However,
+ functions and operators that provide direct write access to
+ the elements had to be altered or disabled in order to support
+ change tracking. Additional functions used to interface with
+ <code>std::vector</code> and to control the change tracking state
+ were also added. The following listing summarizes the differences
+ between the <code>odb::vector</code> and <code>std::vector</code>
+ interfaces. Any <code>std::vector</code> function or operator
+ not mentioned in this listing has exactly the same signature
+ and semantics in <code>odb::vector</code>. Functions and
+ operators that were disabled are shown as commented out and
+ are followed by functions/operators that replace them.</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ template &lt;class T, class A = std::allocator&lt;T> >
+ class vector
+ {
+ ...
+
+ // Element access.
+ //
+
+ //reference operator[] (size_type);
+ reference modify (size_type);
+
+ //reference at (size_type);
+ reference modify_at (size_type);
+
+ //reference front ();
+ reference modify_front ();
+
+ //reference back ();
+ reference modify_back ();
+
+ //T* data () noexcept;
+ T* modify_data () noexcept; // C++11 only.
+
+ // Iterators.
+ //
+ typedef typename std::vector&lt;T, A>::const_iterator const_iterator;
+
+ class iterator
+ {
+ ...
+
+ // Element Access.
+ //
+
+ //reference operator* () const;
+ const_reference operator* () const;
+ reference modify () const;
+
+ //pointer operator-> () const;
+ const_pointer operator-> () const;
+
+ //reference operator[] (difference_type);
+ const_reference operator[] (difference_type);
+ reference modify (difference_type) const;
+
+ // Interfacing with std::vector::iterator.
+ //
+ typename std::vector&lt;T, A>::iterator base () const;
+ };
+
+ // Return std::vector iterators. The begin() functions mark
+ // all the elements as modified.
+ //
+ typename std::vector&lt;T, A>::iterator mbegin ();
+ typename std::vector&lt;T, A>::iterator mend ();
+ typename std::vector&lt;T, A>::reverse_iterator mrbegin ();
+ typename std::vector&lt;T, A>::reverse_iterator mrend ();
+
+ // Interfacing with std::vector.
+ //
+ vector (const std::vector&lt;T, A>&amp;);
+ vector (std::vector&lt;T, A>&amp;&amp;); // C++11 only.
+
+ vector&amp; operator= (const std::vector&lt;T, A>&amp;);
+ vector&amp; operator= (std::vector&lt;T, A>&amp;&amp;); // C++11 only.
+
+ operator const std::vector&lt;T, A>&amp; () const;
+ std::vector&lt;T, A>&amp; base ();
+ const std::vector&lt;T, A>&amp; base ();
+
+ // Change tracking.
+ //
+ bool _tracking () const;
+ void _start () const;
+ void _stop () const;
+ void _arm (transaction&amp;) const;
+ };
+}
+ </pre>
+
+ <p>The following example highlights some of the differences between
+ the two interfaces. <code>std::vector</code> versions are commented
+ out.</p>
+
+ <pre class="cxx">
+#include &lt;vector>
+#include &lt;odb/vector.hxx>
+
+void f (const std::vector&lt;int>&amp;);
+
+odb::vector&lt;int> v ({1, 2, 3});
+
+f (v); // Ok, implicit conversion.
+
+if (v[1] == 2) // Ok, const access.
+ //v[1]++;
+ v.modify (1)++;
+
+//v.back () = 4;
+v.modify_back () = 4;
+
+for (auto i (v.begin ()); i != v.end (); ++i)
+{
+ if (*i != 0) // Ok, const access.
+ //*i += 10;
+ i.modify () += 10;
+}
+
+std::sort (v.mbegin (), v.mend ());
+ </pre>
+
+ <p>Note also the subtle difference between copy/move construction
+ and copy/move assignment of <code>odb::vector</code> instances.
+ While copy/move constructor will copy/move both the elements as
+ well as their change state, in contrast, assignment is tracked
+ as any other change to the vector content.</p>
+
+ <h2><a name="5.5">5.5 Using Custom Containers</a></h2>
+
+ <p>While the ODB runtime and profile libraries provide support for
+ a wide range of containers, it is also easy to persist custom
+ container types or make a change-tracking version out of one.</p>
+
+ <p>To achieve this you will need to implement the
+ <code>container_traits</code> class template specialization for
+ your container. First, determine the container kind (ordered, set,
+ multiset, map, or multimap) for your container type. Then use a
+ specialization for one of the standard C++ containers found in
+ the common ODB runtime library (<code>libodb</code>) as a base
+ for your own implementation.</p>
+
+ <p>Once the container traits specialization is ready for your container,
+ you will need to include it into the ODB compilation process using
+ the <code>--odb-epilogue</code> option and into the generated header
+ files with the <code>--hxx-prologue</code> option. As an example,
+ suppose we have a hash table container for which we have the traits
+ specialization implemented in the <code>hashtable-traits.hxx</code>
+ file. Then, we can create an ODB compiler options file for this
+ container and save it to <code>hashtable.options</code>:</p>
+
+ <pre>
+# Options file for the hash table container.
+#
+--odb-epilogue '#include "hashtable-traits.hxx"'
+--hxx-prologue '#include "hashtable-traits.hxx"'
+ </pre>
+
+ <p>Now, whenever we compile a header file that uses the hashtable
+ container, we can specify the following command line option to
+ make sure it is recognized by the ODB compiler as a container
+ and the traits file is included in the generated code:</p>
+
+ <pre>
+--options-file hashtable.options
+ </pre>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="6">6 Relationships</a></h1>
+
+ <p>Relationships between persistent objects are expressed with pointers or
+ containers of pointers. The ODB runtime library provides built-in support
+ for <code>shared_ptr</code>/<code>weak_ptr</code> (TR1 or C++11),
+ <code>std::unique_ptr</code> (C++11),
+ <code>std::auto_ptr</code>, and raw pointers. Plus, ODB profile
+ libraries, that are available for commonly used frameworks and libraries
+ (such as Boost and Qt), provide support for smart pointers found in these
+ frameworks and libraries (<a href="#III">Part III, "Profiles"</a>). It is
+ also easy to add support for a custom smart pointer as discussed later
+ in <a href="#6.5"> Section 6.5, "Using Custom Smart Pointers"</a>. Any
+ supported smart pointer can be used in a data member as long as it can be
+ explicitly constructed from the canonical object pointer
+ (<a href="#3.3">Section 3.3, "Object and View Pointers"</a>). For
+ example, we can use <code>weak_ptr</code> if the object pointer
+ is <code>shared_ptr</code>.</p>
+
+ <p>When an object containing a pointer to another object is loaded,
+ the pointed-to object is loaded as well. In some situations this
+ eager loading of the relationships is undesirable since it
+ can lead to a large number of otherwise unused objects being
+ instantiated from the database. To support finer control
+ over relationships loading, the ODB runtime and profile
+ libraries provide the so-called <em>lazy</em> versions of
+ the supported pointers. An object pointed-to by a lazy pointer
+ is not loaded automatically when the containing object is loaded.
+ Instead, we have to explicitly request the instantiation of the
+ pointed-to object. Lazy pointers are discussed in
+ detail in <a href="#6.4">Section 6.4, "Lazy Pointers"</a>.</p>
+
+ <p>As a simple example, consider the following employee-employer
+ relationship. Code examples presented in this chapter
+ will use the <code>shared_ptr</code> and <code>weak_ptr</code>
+ smart pointers from the TR1 (<code>std::tr1</code>) namespace.</p>
+
+ <pre class="cxx">
+#pragma db object
+class employer
+{
+ ...
+
+ #pragma db id
+ std::string name_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string first_name_;
+ std::string last_name_;
+
+ shared_ptr&lt;employer> employer_;
+};
+ </pre>
+
+ <p>By default, an object pointer can be <code>NULL</code>. To
+ specify that a pointer always points to a valid object we can
+ use the <code>not_null</code> pragma (<a href="#14.4.6">Section
+ 14.4.6, "<code>null</code>/<code>not_null</code>"</a>) for
+ single object pointers and the <code>value_not_null</code> pragma
+ (<a href="#14.4.28">Section
+ 14.4.28, "<code>value_null</code>/<code>value_not_null</code>"</a>)
+ for containers of object pointers. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db not_null
+ shared_ptr&lt;employer> current_employer_;
+
+ #pragma db value_not_null
+ std::vector&lt;shared_ptr&lt;employer> > previous_employers_;
+};
+ </pre>
+
+ <p>In this case, if we call either <code>persist()</code> or
+ <code>update()</code> database function on the
+ <code>employee</code> object and the <code>current_employer_</code>
+ pointer or one of the pointers stored in the
+ <code>previous_employers_</code> container is <code>NULL</code>,
+ then the <code>odb::null_pointer</code> exception will be thrown.</p>
+
+ <p>We don't need to do anything special to establish or navigate a
+ relationship between two persistent objects, as shown in the
+ following code fragment:</p>
+
+ <pre class="cxx">
+// Create an employer and a few employees.
+//
+unsigned long john_id, jane_id;
+{
+ shared_ptr&lt;employer> er (new employer ("Example Inc"));
+ shared_ptr&lt;employee> john (new employee ("John", "Doe"));
+ shared_ptr&lt;employee> jane (new employee ("Jane", "Doe"));
+
+ john->employer_ = er;
+ jane->employer_ = er;
+
+ transaction t (db.begin ());
+
+ db.persist (er);
+ john_id = db.persist (john);
+ jane_id = db.persist (jane);
+
+ t.commit ();
+}
+
+// Load a few employee objects and print their employer.
+//
+{
+ session s;
+ transaction t (db.begin ());
+
+ shared_ptr&lt;employee> john (db.load&lt;employee> (john_id));
+ shared_ptr&lt;employee> jane (db.load&lt;employee> (jane_id));
+
+ cout &lt;&lt; john->employer_->name_ &lt;&lt; endl;
+ cout &lt;&lt; jane->employer_->name_ &lt;&lt; endl;
+
+ t.commit ();
+}
+ </pre>
+
+ <p>The only notable line in the above code is the creation of a
+ session before the second transaction starts. As discussed in
+ <a href="#11">Chapter 11, "Session"</a>, a session acts as a cache
+ of persistent objects.
+ By creating a session before loading the <code>employee</code>
+ objects we make sure that their <code>employer_</code> pointers
+ point to the same <code>employer</code> object. Without a
+ session, each <code>employee</code> would have ended up pointing
+ to its own, private instance of the Example Inc employer.</p>
+
+ <p>As a general guideline, you should use a session when loading
+ objects that have pointers to other persistent objects. A
+ session makes sure that for a given object id, a single instance
+ is shared among all other objects that relate to it.</p>
+
+ <p>We can also use data members from pointed-to
+ objects in database queries (<a href="#4">Chapter 4, "Querying the
+ Database"</a>). For each pointer in a persistent class, the query
+ class defines a smart pointer-like member that contains members
+ corresponding to the data members in the pointed-to object. We
+ can then use the access via a pointer syntax (<code>-></code>)
+ to refer to data members in pointed-to objects.
+ For example, the query class for the <code>employee</code> object
+ contains the <code>employer</code> member (its name is derived from the
+ <code>employer_</code> pointer) which in turn contains the
+ <code>name</code> member (its name is derived from the
+ <code>employer::name_</code> data member of the pointed-to object).
+ As a result, we can use the <code>query::employer->name</code>
+ expression while querying the database for the <code>employee</code>
+ objects. For example, the following transaction finds all the
+ employees of Example Inc that have the Doe last name:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee> query;
+typedef odb::result&lt;employee> result;
+
+session s;
+transaction t (db.begin ());
+
+result r (db.query&lt;employee> (
+ query::employer->name == "Example Inc" &amp;&amp; query::last == "Doe"));
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ cout &lt;&lt; i->first_ &lt;&lt; " " &lt;&lt; i->last_ &lt;&lt; endl;
+
+t.commit ();
+ </pre>
+
+ <p>A query class member corresponding to a non-inverse
+ (<a href="#6.2">Section 6.2, "Bidirectional Relationships"</a>) object
+ pointer can also be used as a normal member that has the id type
+ of the pointed-to object. For example, the following query locates
+ all the <code>employee</code> objects that don't have an associated
+ <code>employer</code> object:</p>
+
+ <pre class="cxx">
+result r (db.query&lt;employee> (query::employer.is_null ()));
+ </pre>
+
+ <p>An important concept to keep in mind when working with object
+ relationships is the independence of persistent objects. In particular,
+ when an object containing a pointer to another object is made persistent
+ or is updated, the pointed-to object is not automatically persisted
+ or updated. Rather, only a reference to the object (in the form of the
+ object id) is stored for the pointed-to object in the database.
+ The pointed-to object itself is a separate entity and should
+ be made persistent or updated independently. By default, the
+ same principle also applies to erasing pointed-to objects. That
+ is, we have to make sure all the pointing objects are updated
+ accordingly. However, in the case of erase, we can specify an
+ alternative <code>on-delete</code> semantic as discussed in
+ <a href="#14.4.15">Section 14.4.15, "<code>on_delete</code>"</a>.</p>
+
+ <p>When persisting or updating an object containing a pointer to another
+ object, the pointed-to object must have a valid object id. This,
+ however, may not always be easy to achieve in complex relationships that
+ involve objects with automatically assigned identifiers. In such
+ cases it may be necessary to first persist an object with a pointer
+ set to <code>NULL</code> and then, once the pointed-to object is
+ made persistent and its identifier assigned, set the pointer
+ to the correct value and update the object in the database.</p>
+
+ <p>Persistent object relationships can be divided into two groups:
+ unidirectional and bidirectional. Each group in turn contains
+ several configurations that vary depending on the cardinality
+ of the sides of the relationship. All possible unidirectional
+ and bidirectional configurations are discussed in the following
+ sections.</p>
+
+ <h2><a name="6.1">6.1 Unidirectional Relationships</a></h2>
+
+ <p>In unidirectional relationships we are only interested in navigating
+ from object to object in one direction. Because there is no interest
+ in navigating in the opposite direction, the cardinality of the other
+ end of the relationship is unimportant. As a result, there are only
+ two possible unidirectional relationships: to-one and to-many. Each
+ of these relationships is described in the following sections. For
+ sample code that shows how to work with these relationships, refer
+ to the <code>relationship</code> example in the <code>odb-examples</code>
+ package.</p>
+
+ <h3><a name="6.1.1">6.1.1 To-One Relationships</a></h3>
+
+ <p>An example of a unidirectional to-one relationship is the
+ employee-employer relationship (an employee has one employer).
+ The following persistent C++ classes model this relationship:</p>
+
+ <pre class="cxx">
+#pragma db object
+class employer
+{
+ ...
+
+ #pragma db id
+ std::string name_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db not_null
+ shared_ptr&lt;employer> employer_;
+};
+ </pre>
+
+ <p>The corresponding database tables look like this:</p>
+
+ <pre class="sql">
+CREATE TABLE employer (
+ name VARCHAR (128) NOT NULL PRIMARY KEY);
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ employer VARCHAR (128) NOT NULL REFERENCES employer (name));
+ </pre>
+
+ <h3><a name="6.1.2">6.1.2 To-Many Relationships</a></h3>
+
+ <p>An example of a unidirectional to-many relationship is the
+ employee-project relationship (an employee can be involved
+ in multiple projects). The following persistent C++ classes
+ model this relationship:</p>
+
+ <pre class="cxx">
+#pragma db object
+class project
+{
+ ...
+
+ #pragma db id
+ std::string name_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db value_not_null unordered
+ std::vector&lt;shared_ptr&lt;project> > projects_;
+};
+ </pre>
+
+ <p>The corresponding database tables look like this:</p>
+
+ <pre class="sql">
+CREATE TABLE project (
+ name VARCHAR (128) NOT NULL PRIMARY KEY);
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
+
+CREATE TABLE employee_projects (
+ object_id BIGINT UNSIGNED NOT NULL,
+ value VARCHAR (128) NOT NULL REFERENCES project (name));
+ </pre>
+
+ <p>To obtain a more canonical database schema, the names of tables
+ and columns above can be customized using ODB pragmas
+ (<a href="#14">Chapter 14, "ODB Pragma Language"</a>). For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db value_not_null unordered \
+ id_column("employee_id") value_column("project_name")
+ std::vector&lt;shared_ptr&lt;project> > projects_;
+};
+ </pre>
+
+ <p>The resulting <code>employee_projects</code> table would then
+ look like this:</p>
+
+ <pre class="sql">
+CREATE TABLE employee_projects (
+ employee_id BIGINT UNSIGNED NOT NULL,
+ project_name VARCHAR (128) NOT NULL REFERENCES project (name));
+ </pre>
+
+
+ <h2><a name="6.2">6.2 Bidirectional Relationships</a></h2>
+
+ <p>In bidirectional relationships we are interested in navigating
+ from object to object in both directions. As a result, each
+ object class in a relationship contains a pointer to the other
+ object. If smart pointers are used, then a weak pointer should
+ be used as one of the pointers to avoid ownership cycles. For
+ example:</p>
+
+ <pre class="cxx">
+class employee;
+
+#pragma db object
+class position
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ weak_ptr&lt;employee> employee_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db not_null
+ shared_ptr&lt;position> position_;
+};
+ </pre>
+
+ <p>Note that when we establish a bidirectional relationship, we
+ have to set both pointers consistently. One way to make sure
+ that a relationship is always in a consistent state is to
+ provide a single function that updates both pointers at the
+ same time. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class position: public enable_shared_from_this&lt;position>
+{
+ ...
+
+ void
+ fill (shared_ptr&lt;employee> e)
+ {
+ employee_ = e;
+ e->positions_ = shared_from_this ();
+ }
+
+private:
+ weak_ptr&lt;employee> employee_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+private:
+ friend class position;
+
+ #pragma db not_null
+ shared_ptr&lt;position> position_;
+};
+ </pre>
+
+ <p>At the beginning of this chapter we examined how to use a session
+ to make sure a single object is shared among all other objects pointing
+ to it. With bidirectional relationships involving weak pointers the
+ use of a session becomes even more crucial. Consider the following
+ transaction that tries to load the <code>position</code> object
+ from the above example without using a session:</p>
+
+ <pre class="cxx">
+transaction t (db.begin ())
+shared_ptr&lt;position> p (db.load&lt;position> (1));
+...
+t.commit ();
+ </pre>
+
+ <p>When we load the <code>position</code> object, the <code>employee</code>
+ object, which it points to, is also loaded. While <code>employee</code>
+ is initially stored as <code>shared_ptr</code>, it is then assigned to
+ the <code>employee_</code> member which is <code>weak_ptr</code>. Once
+ the assignment is complete, the shared pointer goes out of scope
+ and the only pointer that points to the newly loaded
+ <code>employee</code> object is the <code>employee_</code> weak
+ pointer. And that means the <code>employee</code> object is deleted
+ immediately after being loaded. To help avoid such pathological
+ situations ODB detects cases where a newly loaded object will
+ immediately be deleted and throws the <code>odb::session_required</code>
+ exception.</p>
+
+ <p>As the exception name suggests, the easiest way to resolve this
+ problem is to use a session:</p>
+
+ <pre class="cxx">
+session s;
+transaction t (db.begin ())
+shared_ptr&lt;position> p (db.load&lt;position> (1));
+...
+t.commit ();
+ </pre>
+
+ <p>In our example, the session will maintain a shared pointer to the
+ loaded <code>employee</code> object preventing its immediate
+ deletion. Another way to resolve this problem is to avoid
+ immediate loading of the pointed-to objects using lazy weak
+ pointers. Lazy pointers are discussed in <a href="#6.4">Section 6.4,
+ "Lazy Pointers"</a> later in this chapter.</p>
+
+ <p>Above, to model a bidirectional relationship in persistent classes,
+ we used two pointers, one in each object. While this is a natural
+ representation in C++, it does not translate to a canonical
+ relational model. Consider the database schema generated for
+ the above two classes:</p>
+
+ <pre class="sql">
+CREATE TABLE position (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ employee BIGINT UNSIGNED REFERENCES employee (id));
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ position BIGINT UNSIGNED NOT NULL REFERENCES position (id));
+ </pre>
+
+ <p>While this database schema is valid, it is unconventional. We have
+ a reference from a row in the <code>position</code> table to a row
+ in the <code>employee</code> table. We also have a reference
+ from this same row in the <code>employee</code> table back to
+ the row in the <code>position</code> table. From the relational
+ point of view, one of these references is redundant since
+ in SQL we can easily navigate in both directions using just one
+ of these references.</p>
+
+ <p>To eliminate redundant database schema references we can use the
+ <code>inverse</code> pragma (<a href="#14.4.14">Section 14.4.14,
+ "<code>inverse</code>"</a>) which tells the ODB compiler that
+ a pointer is the inverse side of a bidirectional relationship.
+ Either side of a relationship can be made inverse. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class position
+{
+ ...
+
+ #pragma db inverse(position_)
+ weak_ptr&lt;employee> employee_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db not_null
+ shared_ptr&lt;position> position_;
+};
+ </pre>
+
+ <p>The resulting database schema looks like this:</p>
+
+ <pre class="sql">
+CREATE TABLE position (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ position BIGINT UNSIGNED NOT NULL REFERENCES position (id));
+ </pre>
+
+ <p>As you can see, an inverse member does not have a corresponding
+ column (or table, in case of an inverse container of pointers)
+ and, from the point of view of database operations, is effectively
+ read-only. The only way to change a bidirectional relationship
+ with an inverse side is to set its direct (non-inverse)
+ pointer. Also note that an ordered container (<a href="#5.1">Section
+ 5.1, "Ordered Containers"</a>) of pointers that is an inverse side
+ of a bidirectional relationship is always treated as unordered
+ (<a href="#14.4.19">Section 14.4.19, "<code>unordered</code>"</a>)
+ because the contents of such a container are implicitly built from
+ the direct side of the relationship which does not contain the
+ element order (index).</p>
+
+ <p>There are three distinct bidirectional relationships that we
+ will cover in the following sections: one-to-one, one-to-many,
+ and many-to-many. We will only talk about bidirectional
+ relationships with inverse sides since they result in canonical
+ database schemas. For sample code that shows how to work with
+ these relationships, refer to the <code>inverse</code> example
+ in the <code>odb-examples</code> package.</p>
+
+ <h3><a name="6.2.1">6.2.1 One-to-One Relationships</a></h3>
+
+ <p>An example of a bidirectional one-to-one relationship is the
+ presented above employee-position relationship (an employee
+ fills one position and a position is filled by one employee).
+ The following persistent C++ classes model this relationship:</p>
+
+ <pre class="cxx">
+class employee;
+
+#pragma db object
+class position
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db inverse(position_)
+ weak_ptr&lt;employee> employee_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db not_null
+ shared_ptr&lt;position> position_;
+};
+ </pre>
+
+ <p>The corresponding database tables look like this:</p>
+
+ <pre class="sql">
+CREATE TABLE position (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ position BIGINT UNSIGNED NOT NULL REFERENCES position (id));
+ </pre>
+
+ <p>If instead the other side of this relationship is made inverse,
+ then the database tables will change as follows:</p>
+
+ <pre class="sql">
+CREATE TABLE position (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ employee BIGINT UNSIGNED REFERENCES employee (id));
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
+ </pre>
+
+ <h3><a name="6.2.2">6.2.2 One-to-Many Relationships</a></h3>
+
+ <p>An example of a bidirectional one-to-many relationship is the
+ employer-employee relationship (an employer has multiple
+ employees and an employee is employed by one employer).
+ The following persistent C++ classes model this relationship:</p>
+
+ <pre class="cxx">
+class employee;
+
+#pragma db object
+class employer
+{
+ ...
+
+ #pragma db id
+ std::string name_;
+
+ #pragma db value_not_null inverse(employer_)
+ std::vector&lt;weak_ptr&lt;employee> > employees_
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db not_null
+ shared_ptr&lt;employer> employer_;
+};
+ </pre>
+
+ <p>The corresponding database tables differ significantly depending
+ on which side of the relationship is made inverse. If the <em>one</em>
+ side (<code>employer</code>) is inverse as in the code
+ above, then the resulting database schema looks like this:</p>
+
+ <pre class="sql">
+CREATE TABLE employer (
+ name VARCHAR (128) NOT NULL PRIMARY KEY);
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ employer VARCHAR (128) NOT NULL REFERENCES employer (name));
+ </pre>
+
+ <p>If instead the <em>many</em> side (<code>employee</code>) of this
+ relationship is made inverse, then the database tables will change
+ as follows:</p>
+
+ <pre class="sql">
+CREATE TABLE employer (
+ name VARCHAR (128) NOT NULL PRIMARY KEY);
+
+CREATE TABLE employer_employees (
+ object_id VARCHAR (128) NOT NULL REFERENCES employer (name),
+ value BIGINT UNSIGNED NOT NULL REFERENCES employee (id));
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
+ </pre>
+
+ <h3><a name="6.2.3">6.2.3 Many-to-Many Relationships</a></h3>
+
+ <p>An example of a bidirectional many-to-many relationship is the
+ employee-project relationship (an employee can work on multiple
+ projects and a project can have multiple participating employees).
+ The following persistent C++ classes model this relationship:</p>
+
+ <pre class="cxx">
+class employee;
+
+#pragma db object
+class project
+{
+ ...
+
+ #pragma db id
+ std::string name_;
+
+ #pragma db value_not_null inverse(projects_)
+ std::vector&lt;weak_ptr&lt;employee> > employees_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db value_not_null unordered
+ std::vector&lt;shared_ptr&lt;project> > projects_;
+};
+ </pre>
+
+ <p>The corresponding database tables look like this:</p>
+
+ <pre class="sql">
+CREATE TABLE project (
+ name VARCHAR (128) NOT NULL PRIMARY KEY);
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
+
+CREATE TABLE employee_projects (
+ object_id BIGINT UNSIGNED NOT NULL REFERENCES employee (id),
+ value VARCHAR (128) NOT NULL REFERENCES project (name));
+ </pre>
+
+ <p>If instead the other side of this relationship is made inverse,
+ then the database tables will change as follows:</p>
+
+ <pre class="sql">
+CREATE TABLE project (
+ name VARCHAR (128) NOT NULL PRIMARY KEY);
+
+CREATE TABLE project_employees (
+ object_id VARCHAR (128) NOT NULL REFERENCES project (name),
+ value BIGINT UNSIGNED NOT NULL REFERENCES employee (id));
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY);
+ </pre>
+
+ <h2><a name="6.3">6.3 Circular Relationships</a></h2>
+
+ <p>A relationship between two persistent classes is circular if each
+ of them references the other. Bidirectional relationships are
+ always circular. A unidirectional relationship combined with
+ inheritance (<a href="#8">Chapter 8, "Inheritance"</a>) can also
+ be circular. For example, the <code>employee</code> class could
+ derive from <code>person</code> which, in turn, could contain a
+ pointer to <code>employee</code>.</p>
+
+ <p>We don't need to do anything extra if persistent classes with
+ circular dependencies are defined in the same header
+ file. Specifically, ODB will make sure that the database tables
+ and foreign key constraints are created in the correct order. As a
+ result, unless you have good reasons not to, it is recommended that
+ you keep persistent classes with circular dependencies in the same
+ header file.</p>
+
+ <p>If you have to keep such classes in separate header files, then
+ there are two extra steps that you may need to take in order to
+ use these classes with ODB. Consider again the example from
+ <a href="#6.2.1">Section 6.2.1, "One-to-One Relationships"</a>
+ but this time with the classes defined in separate headers:</p>
+
+ <pre class="cxx">
+// position.hxx
+//
+class employee;
+
+#pragma db object
+class position
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db inverse(position_)
+ weak_ptr&lt;employee> employee_;
+};
+ </pre>
+
+ <pre class="cxx">
+// employee.hxx
+//
+#include "position.hxx"
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db not_null
+ shared_ptr&lt;position> position_;
+};
+ </pre>
+
+ <p>Note that the <code>position.hxx</code> header contains only the forward
+ declaration for <code>employee</code>. While this is sufficient to
+ define a valid, from the C++ point of view, <code>position</code> class,
+ the ODB compiler needs to "see" the definitions of the pointed-to
+ persistent classes. There are several ways we can fulfil this
+ requirement. The easiest is to simply include <code>employee.hxx</code>
+ at the end of <code>position.hxx</code>:</p>
+
+ <pre class="cxx">
+// position.hxx
+//
+class employee;
+
+#pragma db object
+class position
+{
+ ...
+};
+
+#include "employee.hxx"
+ </pre>
+
+ <p>We can also limit this inclusion only to the time when
+ <code>position.hxx</code> is compiled with the ODB compiler:</p>
+
+ <pre class="cxx">
+// position.hxx
+//
+
+...
+
+#ifdef ODB_COMPILER
+# include "employee.hxx"
+#endif
+ </pre>
+
+ <p>Finally, if we don't want to modify <code>position.hxx</code>,
+ then we can add <code>employee.hxx</code> to the ODB compilation
+ process with the <code>--odb-epilogue</code> option. For example:</p>
+
+ <pre class="terminal">
+odb ... --odb-epilogue "#include \"employee.hxx\"" position.hxx
+ </pre>
+
+ <p>Note also that in this example we didn't have to do anything extra
+ for <code>employee.hxx</code> because it already includes
+ <code>position.hxx</code>. However, if instead it relied only
+ on the forward declaration of the <code>position</code> class,
+ then we would have to handle it in the same way as
+ <code>position.hxx</code>.</p>
+
+ <p>The other difficulty with separately defined classes involving
+ circular relationships has to do with the correct order of foreign
+ key constraint creation in the generated database schema. In
+ the above example, if we generate the database schema as
+ standalone SQL files, then we will end up with two such files:
+ <code>position.sql</code> and <code>employee.sql</code>.
+ If we try to execute <code>employee.sql</code> first, then
+ we will get an error indicating that the table corresponding to
+ the <code>position</code> class and referenced by the foreign
+ key constraint corresponding to the <code>position_</code>
+ pointer does not yet exist.</p>
+
+ <p>Note that there is no such problem if the database schema
+ is embedded in the generated C++ code instead of being produced
+ as standalone SQL files. In this case, the ODB compiler is
+ able to ensure the correct creation order even if the classes
+ are defined in separate header files.</p>
+
+ <p>In certain cases, for example, a bidirectional relationship
+ with an inverse side, this problem can be resolved by executing
+ the database schema creation files in the correct order. In our
+ example, this would be <code>position.sql</code> first
+ and <code>employee.sql</code> second. However, this approach
+ doesn't scale beyond simple object models.</p>
+
+ <p>A more robust solution to this problem is to generate the database
+ schema for all the persistent classes into a single SQL file. This
+ way, the ODB compiler can again ensure the correct creation order
+ of tables and foreign keys. To instruct the ODB compiler to produce
+ a combined schema file for several headers we can use the
+ <code>--generate-schema-only</code> and <code>--at-once</code>
+ options. For example:</p>
+
+ <pre class="terminal">
+odb ... --generate-schema-only --at-once --input-name company \
+position.hxx employee.hxx
+ </pre>
+
+ <p>The result of the above command is a single <code>company.sql</code>
+ file (the name is derived from the <code>--input-name</code> value)
+ that contains the database creation code for both <code>position</code>
+ and <code>employee</code> classes.</p>
+
+ <h2><a name="6.4">6.4 Lazy Pointers</a></h2>
+
+ <p>Consider again the bidirectional, one-to-many employer-employee
+ relationship that was presented earlier in this chapter:</p>
+
+ <pre class="cxx">
+class employee;
+
+#pragma db object
+class employer
+{
+ ...
+
+ #pragma db id
+ std::string name_;
+
+ #pragma db value_not_null inverse(employer_)
+ std::vector&lt;weak_ptr&lt;employee> > employees_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ #pragma db not_null
+ shared_ptr&lt;employer> employer_;
+};
+ </pre>
+
+ <p>Consider also the following transaction which obtains the employer
+ name given the employee id:</p>
+
+ <pre class="cxx">
+unsigned long id = ...
+string name;
+
+session s;
+transaction t (db.begin ());
+
+shared_ptr&lt;employee> e (db.load&lt;employee> (id));
+name = e->employer_->name_;
+
+t.commit ();
+ </pre>
+
+ <p>While this transaction looks very simple, it actually does a lot more
+ than what meets the eye and is necessary. Consider what happens when
+ we load the <code>employee</code> object: the <code>employer_</code>
+ pointer is also automatically loaded which means the <code>employer</code>
+ object corresponding to this employee is also loaded. But the
+ <code>employer</code> object in turn contains the list of pointers
+ to all the employees, which are also loaded. As a result, when object
+ relationships are involved, a simple transaction like the above can
+ load many more objects than is necessary.</p>
+
+ <p>To overcome this problem ODB offers finer grained control over
+ the relationship loading in the form of lazy pointers. A lazy
+ pointer does not automatically load the pointed-to object
+ when the containing object is loaded. Instead, we have to
+ explicitly load the pointed-to object if and when we need to
+ access it.</p>
+
+ <p>The ODB runtime library provides lazy counterparts for all the
+ supported pointers, namely:
+ <code>odb::lazy_shared_ptr</code>/<code>lazy_weak_ptr</code>
+ for C++11 <code>std::shared_ptr</code>/<code>weak_ptr</code>,
+ <code>odb::tr1::lazy_shared_ptr</code>/<code>lazy_weak_ptr</code>
+ for TR1 <code>std::tr1::shared_ptr</code>/<code>weak_ptr</code>,
+ <code>odb::lazy_unique_ptr</code> for C++11 <code>std::unique_ptr</code>,
+ <code>odb::lazy_auto_ptr</code> for <code>std::auto_ptr</code>,
+ and <code>odb::lazy_ptr</code> for raw pointers. The TR1 lazy
+ pointers are defined in the <code>&lt;odb/tr1/lazy-ptr.hxx></code>
+ header while all the others &mdash; in
+ <code>&lt;odb/lazy-ptr.hxx></code>. The ODB profile
+ libraries also provide lazy pointer implementations for smart pointers
+ from popular frameworks and libraries (<a href="#III">Part III,
+ "Profiles"</a>).</p>
+
+ <p>While we will discuss the interface of lazy pointers in more detail
+ shortly, the most commonly used extra function provided by these
+ pointers is <code>load()</code>. This function loads the
+ pointed-to object if it hasn't already been loaded. After
+ the call to this function, the lazy pointer can be used
+ in the the same way as its eager counterpart. The <code>load()</code>
+ function also returns the eager pointer, in case you need to pass
+ it around. For a lazy weak pointer, the
+ <code>load()</code> function also locks the pointer.</p>
+
+ <p>The following example shows how we can change our employer-employee
+ relationship to use lazy pointers. Here we choose to use lazy pointers
+ for both sides of the relationship.</p>
+
+ <pre class="cxx">
+class employee;
+
+#pragma db object
+class employer
+{
+ ...
+
+ #pragma db value_not_null inverse(employer_)
+ std::vector&lt;lazy_weak_ptr&lt;employee> > employees_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db not_null
+ lazy_shared_ptr&lt;employer> employer_;
+};
+ </pre>
+
+ <p>And the transaction is changed like this:</p>
+
+ <pre class="cxx">
+unsigned long id = ...
+string name;
+
+session s;
+transaction t (db.begin ());
+
+shared_ptr&lt;employee> e (db.load&lt;employee> (id));
+e->employer_.load ();
+name = e->employer_->name_;
+
+t.commit ();
+ </pre>
+
+
+ <p>As a general guideline we recommend that you make at least one side
+ of a bidirectional relationship lazy, especially for relationships
+ with a <em>many</em> side.</p>
+
+ <p>A lazy pointer implementation mimics the interface of its eager
+ counterpart which can be used once the pointer is loaded. It also
+ adds a number of additional functions that are specific to the
+ lazy loading functionality. Overall, the interface of a lazy
+ pointer follows this general outline:</p>
+
+ <pre class="cxx">
+template &lt;class T>
+class lazy_ptr
+{
+public:
+ //
+ // The eager pointer interface.
+ //
+
+ // Initialization/assignment from an eager pointer to a
+ // transient object.
+ //
+public:
+ template &lt;class Y> lazy_ptr (const eager_ptr&lt;Y>&amp;);
+ template &lt;class Y> lazy_ptr&amp; operator= (const eager_ptr&lt;Y>&amp;);
+
+ // Lazy loading interface.
+ //
+public:
+ // NULL loaded()
+ //
+ // true true NULL pointer to transient object
+ // false true valid pointer to persistent object
+ // true false unloaded pointer to persistent object
+ // false false valid pointer to transient object
+ //
+ bool loaded () const;
+
+ eager_ptr&lt;T> load () const;
+
+ // Unload the pointer. For transient objects this function is
+ // equivalent to reset().
+ //
+ void unload () const;
+
+ // Get the underlying eager pointer. If this is an unloaded pointer
+ // to a persistent object, then the returned pointer will be NULL.
+ //
+ eager_ptr&lt;T> get_eager () const;
+
+ // Initialization with a persistent loaded object.
+ //
+ template &lt;class Y> lazy_ptr (database&amp;, Y*);
+ template &lt;class Y> lazy_ptr (database&amp;, const eager_ptr&lt;Y>&amp;);
+
+ template &lt;class Y> void reset (database&amp;, Y*);
+ template &lt;class Y> void reset (database&amp;, const eager_ptr&lt;Y>&amp;);
+
+ // Initialization with a persistent unloaded object.
+ //
+ template &lt;class ID> lazy_ptr (database&amp;, const ID&amp;);
+
+ template &lt;class ID> void reset (database&amp;, const ID&amp;);
+
+ // Query object id and database of a persistent object.
+ //
+ template &lt;class O /* = T */>
+ // C++11: template &lt;class O = T>
+ object_traits&lt;O>::id_type object_id () const;
+
+ odb::database&amp; database () const;
+};
+ </pre>
+
+ <p>Note that to initialize a lazy pointer to a persistent object from
+ its eager pointer one must use the constructor or <code>reset()</code>
+ function with the database as its first argument. The database is
+ required to support comparison of unloaded lazy pointers to persistent
+ objects.</p>
+
+ <p>In a lazy weak pointer interface, the <code>load()</code> function
+ returns the <em>strong</em> (shared) eager pointer. The following
+ transaction demonstrates the use of a lazy weak pointer based on
+ the <code>employer</code> and <code>employee</code> classes
+ presented earlier.</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;lazy_weak_ptr&lt;employee> > employees;
+
+session s;
+transaction t (db.begin ());
+
+shared_ptr&lt;employer> er (db.load&lt;employer> ("Example Inc"));
+employees&amp; es (er->employees ());
+
+for (employees::iterator i (es.begin ()); i != es.end (); ++i)
+{
+ // We are only interested in employees with object id less than
+ // 100.
+ //
+ lazy_weak_ptr&lt;employee>&amp; lwp (*i);
+
+ if (lwp.object_id&lt;employee> () &lt; 100)
+ // C++11: if (lwp.object_id () &lt; 100)
+ {
+ shared_ptr&lt;employee> e (lwp.load ()); // Load and lock.
+ cout &lt;&lt; e->first_ &lt;&lt; " " &lt;&lt; e->last_ &lt;&lt; endl;
+ }
+}
+
+t.commit ();
+ </pre>
+
+ <p>Notice that inside the for-loop we use a reference to the lazy
+ weak pointer instead of making a copy. This is not merely to
+ avoid a copy. When a lazy pointer is loaded, all other lazy
+ pointers that point to the same object do not automatically
+ become loaded (though an attempt to load such copies will
+ result in them pointing to the same object, provided the
+ same session is still in effect). By using a reference
+ in the above transaction we make sure that we load the
+ pointer that is contained in the <code>employer</code>
+ object. This way, if we later need to re-examine this
+ <code>employee</code> object, the pointer will already
+ be loaded.</p>
+
+ <p>As another example, suppose we want to add an employee
+ to Example Inc. The straightforward implementation of this
+ transaction is presented below:</p>
+
+ <pre class="cxx">
+session s;
+transaction t (db.begin ());
+
+shared_ptr&lt;employer> er (db.load&lt;employer> ("Example Inc"));
+shared_ptr&lt;employee> e (new employee ("John", "Doe"));
+
+e->employer_ = er;
+er->employees ().push_back (e);
+
+db.persist (e);
+t.commit ();
+ </pre>
+
+ <p>Notice here that we didn't have to update the employer object
+ in the database since the <code>employees_</code> list of
+ pointers is an inverse side of a bidirectional relationship
+ and is effectively read-only, from the persistence point of
+ view.</p>
+
+ <p>A faster implementation of this transaction, that avoids loading
+ the employer object, relies on the ability to initialize an
+ <em>unloaded</em> lazy pointer with the database where the object
+ is stored as well as its identifier:</p>
+
+ <pre class="cxx">
+lazy_shared_ptr&lt;employer> er (db, std::string ("Example Inc"));
+shared_ptr&lt;employee> e (new employee ("John", "Doe"));
+
+e->employer_ = er;
+
+session s;
+transaction t (db.begin ());
+
+db.persist (e);
+
+t.commit ();
+ </pre>
+
+ <p>For the interaction of lazy pointers with lazy-loaded object
+ sections, refer to <a href="#9.3">Section 9.3, "Sections and
+ Lazy Pointers"</a>.</p>
+
+ <h2><a name="6.5">6.5 Using Custom Smart Pointers</a></h2>
+
+ <p>While the ODB runtime and profile libraries provide support for
+ the majority of widely-used pointers, it is also easy to add
+ support for a custom smart pointer.</p>
+
+ <p>To achieve this you will need to implement the
+ <code>pointer_traits</code> class template specialization for
+ your pointer. The first step is to determine the pointer kind
+ since the interface of the <code>pointer_traits</code> specialization
+ varies depending on the pointer kind. The supported pointer kinds
+ are: <em>raw</em> (raw pointer or equivalent, that is, unmanaged),
+ <em>unique</em> (smart pointer that doesn't support sharing),
+ <em>shared</em> (smart pointer that supports sharing), and
+ <em>weak</em> (weak counterpart of the shared pointer). Any of
+ these pointers can be lazy, which also affects the
+ interface of the <code>pointer_traits</code> specialization.</p>
+
+ <p>Once you have determined the pointer kind for your smart pointer,
+ use a specialization for one of the standard pointers found in
+ the common ODB runtime library (<code>libodb</code>) as a base
+ for your own implementation.</p>
+
+ <p>Once the pointer traits specialization is ready, you will need to
+ include it into the ODB compilation process using the
+ <code>--odb-epilogue</code> option and into the generated header
+ files with the <code>--hxx-prologue</code> option. As an example,
+ suppose we have the <code>smart_ptr</code> smart pointer for which
+ we have the traits specialization implemented in the
+ <code>smart-ptr-traits.hxx</code> file. Then, we can create an ODB
+ compiler options file for this pointer and save it to
+ <code>smart-ptr.options</code>:</p>
+
+ <pre>
+# Options file for smart_ptr.
+#
+--odb-epilogue '#include "smart-ptr-traits.hxx"'
+--hxx-prologue '#include "smart-ptr-traits.hxx"'
+ </pre>
+
+ <p>Now, whenever we compile a header file that uses <code>smart_ptr</code>,
+ we can specify the following command line option to make sure it is
+ recognized by the ODB compiler as a smart pointer and the traits file
+ is included in the generated code:</p>
+
+ <pre>
+--options-file smart-ptr.options
+ </pre>
+
+ <p>It is also possible to implement a lazy counterpart for your
+ smart pointer. The ODB runtime library provides a class template
+ that encapsulates the object id management and loading
+ functionality that is needed to implement a lazy pointer. All
+ you need to do is wrap it with an interface that mimics
+ your smart pointer. Using one of the existing lazy pointer
+ implementations (either from the ODB runtime library or one
+ of the profile libraries) as a base for your implementation
+ is the easiest way to get started.</p>
+
+
+ <!-- CHAPTER -->
+
+ <hr class="page-break"/>
+ <h1><a name="7">7 Value Types</a></h1>
+
+ <p>In <a href="#3.1">Section 3.1, "Concepts and Terminology"</a> we have
+ already discussed the notion of values and value types as well as the
+ distinction between simple and composite values. This chapter covers
+ simple and composite value types in more detail.</p>
+
+ <h2><a name="7.1">7.1 Simple Value Types</a></h2>
+
+ <p>A simple value type is a fundamental C++ type or a class type that
+ is mapped to a single database column. For each supported database
+ system the ODB compiler provides a default mapping to suitable
+ database types for most fundamental C++ types, such as <code>int</code>
+ or <code>float</code> as well as some class types, such as
+ <code>std::string</code>. For more information about the default
+ mapping for each database system refer to <a href="#II">Part II,
+ Database Systems</a>. We can also provide a custom mapping for
+ these or our own value types using the <code>db&nbsp;type</code>
+ pragma (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>).</p>
+
+ <h2><a name="7.2">7.2 Composite Value Types</a></h2>
+
+ <p>A composite value type is a <code>class</code> or <code>struct</code>
+ type that is mapped to more than one database column. To declare
+ a composite value type we use the <code>db&nbsp;value</code> pragma,
+ for example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class basic_name
+{
+ ...
+
+ std::string first_;
+ std::string last_;
+};
+ </pre>
+
+ <p>The complete version of the above code fragment and the other code
+ samples presented in this section can be found in the <code>composite</code>
+ example in the <code>odb-examples</code> package.</p>
+
+ <p>A composite value type does not have to define a default constructor,
+ unless it is used as an element of a container. In this case the
+ default constructor can be made private provided we also make the
+ <code>odb::access</code> class, defined in the
+ <code>&lt;odb/core.hxx></code> header, a friend of this value type.
+ For example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/core.hxx>
+
+#pragma db value
+class basic_name
+{
+public:
+ basic_name (const std::string&amp; first, const std::string&amp; last);
+
+ ...
+
+private:
+ friend class odb::access;
+
+ basic_name () {} // Needed for storing basic_name in containers.
+
+ ...
+};
+ </pre>
+
+ <p>The ODB compiler also needs access to the non-transient
+ (<a href="#14.4.11">Section 14.4.11, "<code>transient</code>"</a>)
+ data members of a composite value type. It uses the same mechanisms
+ as for persistent classes which are discussed in
+ <a href="#3.2">Section 3.2, "Declaring Persistent Objects and
+ Values"</a>.</p>
+
+ <p>The members of a composite value can be other value types (either
+ simple or composite), containers (<a href="#5">Chapter 5,
+ "Containers"</a>), and pointers to objects (<a href="#6">Chapter 6,
+ "Relationships"</a>).
+ Similarly, a composite value type can be used in object members,
+ as an element of a container, and as a base for another composite
+ value type. In particular, composite value types can be used as
+ element types in set containers (<a href="#5.2">Section 5.2, "Set
+ and Multiset Containers"</a>) and as key types in map containers
+ (<a href="#5.3">Section 5.3, "Map and Multimap Containers"</a>).
+ A composite value type that is used as an element of a container
+ cannot contain other containers since containers of containers
+ are not allowed. The following example illustrates some of the
+ possible use cases:</p>
+
+ <pre class="cxx">
+#pragma db value
+class basic_name
+{
+ ...
+
+ std::string first_;
+ std::string last_;
+};
+
+typedef std::vector&lt;basic_name> basic_names;
+
+#pragma db value
+class name_extras
+{
+ ...
+
+ std::string nickname_;
+ basic_names aliases_;
+};
+
+#pragma db value
+class name: public basic_name
+{
+ ...
+
+ std::string title_;
+ name_extras extras_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ name name_;
+};
+ </pre>
+
+ <p>A composite value type can be defined inside a persistent class,
+ view, or another composite value and even made private, provided
+ we make <code>odb::access</code> a friend of the containing class,
+ for example:</p>
+
+<pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db value
+ class name
+ {
+ ...
+
+ std::string first_;
+ std::string last_;
+ };
+
+ name name_;
+};
+ </pre>
+
+ <p>A composite value type can also be defined as an instantiation
+ of a C++ class template, for example:</p>
+
+ <pre class="cxx">
+template &lt;typename T>
+struct point
+{
+ T x;
+ T y;
+ T z;
+};
+
+typedef point&lt;int> int_point;
+#pragma db value(int_point)
+
+#pragma db object
+class object
+{
+ ...
+
+ int_point center_;
+};
+ </pre>
+
+ <p>Note that the database support code for such a composite value type
+ is generated when compiling the header containing the
+ <code>db&nbsp;value</code> pragma and not the header containing
+ the template definition or the <code>typedef</code> name. This
+ allows us to use templates defined in other files, such as
+ <code>std::pair</code> defined in the <code>utility</code>
+ standard header file:</p>
+
+ <pre class="cxx">
+#include &lt;utility> // std::pair
+
+typedef std::pair&lt;std::string, std::string> phone_numbers;
+#pragma db value(phone_numbers)
+
+#pragma db object
+class person
+{
+ ...
+
+ phone_numbers phone_;
+};
+ </pre>
+
+ <p>We can also use data members from composite value types
+ in database queries (<a href="#4">Chapter 4, "Querying the
+ Database"</a>). For each composite value in a persistent class, the
+ query class defines a nested member that contains members corresponding
+ to the data members in the value type. We can then use the member access
+ syntax (.) to refer to data members in value types. For example, the
+ query class for the <code>person</code> object presented above
+ contains the <code>name</code> member (its name is derived from
+ the <code>name_</code> data member) which in turn contains the
+ <code>extras</code> member (its name is derived from the
+ <code>name::extras_</code> data member of the composite value type).
+ This process continues recursively for nested composite value types
+ and, as a result, we can use the <code>query::name.extras.nickname</code>
+ expression while querying the database for the <code>person</code>
+ objects. For example:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;person> query;
+typedef odb::result&lt;person> result;
+
+transaction t (db.begin ());
+
+result r (db.query&lt;person> (
+ query::name.extras.nickname == "Squeaky"));
+
+...
+
+t.commit ();
+ </pre>
+
+ <h3><a name="7.2.1">7.2.1 Composite Object Ids</a></h3>
+
+ <p>An object id can be of a composite value type, for example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class name
+{
+ ...
+
+ std::string first_;
+ std::string last_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id
+ name name_;
+};
+ </pre>
+
+ <p>However, a value type that can be used as an object id has a number
+ of restrictions. Such a value type cannot have container, object
+ pointer, or read-only data members. It also must be
+ default-constructible, copy-constructible, and copy-assignable.
+ Furthermore, if the persistent class in which
+ this composite value type is used as object id has session support
+ enabled (<a href="#11">Chapter 11, "Session"</a>), then it must also
+ implement the less-than comparison operator (<code>operator&lt;</code>).</p>
+
+ <h3><a name="7.2.2">7.2.2 Composite Value Column and Table Names</a></h3>
+
+ <p>Customizing a column name for a data member of a simple value
+ type is straightforward: we simply specify the desired name with
+ the <code>db&nbsp;column</code> pragma (<a href="#14.4.9">Section
+ 14.4.9, "<code>column</code>"</a>). For composite value
+ types things are slightly more complex since they are mapped to
+ multiple columns. Consider the following example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class name
+{
+ ...
+
+ std::string first_;
+ std::string last_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id_;
+
+ name name_;
+};
+ </pre>
+
+ <p>The column names for the <code>first_</code> and <code>last_</code>
+ members are constructed by using the sanitized name of the
+ <code>person::name_</code> member as a prefix and the names of the
+ members in the value type (<code>first_</code> and <code>last_</code>)
+ as suffixes. As a result, the database schema for the above classes
+ will look like this:</p>
+
+ <pre class="sql">
+CREATE TABLE person (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ name_first TEXT NOT NULL,
+ name_last TEXT NOT NULL);
+ </pre>
+
+ <p>We can customize both the prefix and the suffix using the
+ <code>db&nbsp;column</code> pragma as shown in the following
+ example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class name
+{
+ ...
+
+ #pragma db column("first_name")
+ std::string first_;
+
+ #pragma db column("last_name")
+ std::string last_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db column("person_")
+ name name_;
+};
+ </pre>
+
+ <p>The database schema changes as follows:</p>
+
+ <pre class="sql">
+CREATE TABLE person (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ person_first_name TEXT NOT NULL,
+ person_last_name TEXT NOT NULL);
+ </pre>
+
+ <p>We can also make the column prefix empty, for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db column("")
+ name name_;
+};
+ </pre>
+
+ <p>This will result in the following schema:</p>
+
+ <pre class="sql">
+CREATE TABLE person (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ first_name TEXT NOT NULL,
+ last_name TEXT NOT NULL);
+ </pre>
+
+ <p>The same principle applies when a composite value type is used
+ as an element of a container, except that instead of
+ <code>db&nbsp;column</code>, either the <code>db&nbsp;value_column</code>
+ (<a href="#14.4.36">Section 14.4.36, "<code>value_column</code>"</a>) or
+ <code>db&nbsp;key_column</code>
+ (<a href="#14.4.35">Section 14.4.35, "<code>key_column</code>"</a>)
+ pragmas are used to specify the column prefix.</p>
+
+ <p>When a composite value type contains a container, an extra table
+ is used to store its elements (<a href="#5">Chapter 5, "Containers"</a>).
+ The names of such tables are constructed in a way similar to the
+ column names, except that by default both the object name and the
+ member name are used as a prefix. For example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class name
+{
+ ...
+
+ std::string first_;
+ std::string last_;
+ std::vector&lt;std::string> nicknames_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ name name_;
+};
+ </pre>
+
+ <p>The corresponding database schema will look like this:</p>
+
+ <pre class="sql">
+CREATE TABLE person_name_nicknames (
+ object_id BIGINT UNSIGNED NOT NULL,
+ index BIGINT UNSIGNED NOT NULL,
+ value TEXT NOT NULL)
+
+CREATE TABLE person (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ name_first TEXT NOT NULL,
+ name_last TEXT NOT NULL);
+ </pre>
+
+ <p>To customize the container table name we can use the
+ <code>db&nbsp;table</code> pragma (<a href="#14.4.20">Section
+ 14.4.20, "<code>table</code>"</a>), for example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class name
+{
+ ...
+
+ #pragma db table("nickname")
+ std::vector&lt;std::string> nicknames_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db table("person_")
+ name name_;
+};
+ </pre>
+
+ <p>This will result in the following schema changes:</p>
+
+ <pre class="sql">
+CREATE TABLE person_nickname (
+ object_id BIGINT UNSIGNED NOT NULL,
+ index BIGINT UNSIGNED NOT NULL,
+ value TEXT NOT NULL)
+ </pre>
+
+ <p>Similar to columns, we can make the table prefix empty.</p>
+
+
+ <h2><a name="7.3">7.3 Pointers and <code>NULL</code> Value Semantics</a></h2>
+
+ <p>Relational database systems have a notion of the special
+ <code>NULL</code> value that is used to indicate the absence
+ of a valid value in a column. While by default ODB maps
+ values to columns that do not allow <code>NULL</code> values,
+ it is possible to change that with the <code>db&nbsp;null</code>
+ pragma (<a href="#14.4.6">Section 14.4.6,
+ "<code>null</code>/<code>not_null</code>"</a>).</p>
+
+ <p>To properly support the <code>NULL</code> semantics, the
+ C++ value type must have a notion of a <code>NULL</code>
+ value or a similar special state concept. Most basic
+ C++ types, such as <code>int</code> or <code>std::string</code>,
+ do not have this notion and therefore cannot be used directly
+ for <code>NULL</code>-enabled data members (in the case of a
+ <code>NULL</code> value being loaded from the database,
+ such data members will be default-initialized).</p>
+
+ <p>To allow the easy conversion of value types that do not support
+ the <code>NULL</code> semantics into the ones that do, ODB
+ provides the <code>odb::nullable</code> class template. It
+ allows us to wrap an existing C++ type into a container-like
+ class that can either be <code>NULL</code> or contain a
+ value of the wrapped type. ODB also automatically enables
+ the <code>NULL</code> values for data members of the
+ <code>odb::nullable</code> type. For example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/nullable.hxx>
+
+#pragma db object
+class person
+{
+ ...
+
+ std::string first_; // TEXT NOT NULL
+ odb::nullable&lt;std::string> middle_; // TEXT NULL
+ std::string last_; // TEXT NOT NULL
+};
+ </pre>
+
+ <p>The <code>odb::nullable</code> class template is defined
+ in the <code>&lt;odb/nullable.hxx></code> header file and
+ has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ template &lt;typename T>
+ class nullable
+ {
+ public:
+ typedef T value_type;
+
+ nullable ();
+ nullable (const T&amp;);
+ nullable (const nullable&amp;);
+ template &lt;typename Y> explicit nullable (const nullable&lt;Y>&amp;);
+
+ nullable&amp; operator= (const T&amp;);
+ nullable&amp; operator= (const nullable&amp;);
+ template &lt;typename Y> nullable&amp; operator= (const nullable&lt;Y>&amp;);
+
+ void swap (nullable&amp;);
+
+ // Accessor interface.
+ //
+ bool null () const;
+
+ T&amp; get ();
+ const T&amp; get () const;
+
+ // Pointer interface.
+ //
+ operator bool_convertible () const;
+
+ T* operator-> ();
+ const T* operator-> () const;
+
+ T&amp; operator* ();
+ const T&amp; operator* () const;
+
+ // Reset to the NULL state.
+ //
+ void reset ();
+ };
+}
+ </pre>
+
+ <p>The following example shows how we can use this interface:</p>
+
+ <pre class="cxx">
+ nullable&lt;string> ns;
+
+ // Using the accessor interface.
+ //
+ if (ns.null ())
+ {
+ s = "abc";
+ }
+ else
+ {
+ string s (ns.get ());
+ ns.reset ();
+ }
+
+ // The same using the pointer interface.
+ //
+ if (!ns)
+ {
+ s = "abc";
+ }
+ else
+ {
+ string s (*ns);
+ ns.reset ();
+ }
+ </pre>
+
+
+ <p>The <code>odb::nullable</code> class template requires the wrapped
+ type to have public default and copy constructors as well as the
+ copy assignment operator. Note also that the <code>odb::nullable</code>
+ implementation is not the most efficient in that it always contains
+ a fully constructed value of the wrapped type. This is normally
+ not a concern for simple types such as the C++ fundamental
+ types or <code>std::string</code>. However, it may become
+ an issue for more complex types. In such cases you may want to
+ consider using a more efficient implementation of the
+ <em>optional value</em> concept such as the
+ <code>optional</code> class template from Boost
+ (<a href="#23.4">Section 23.4, "Optional Library"</a>).</p>
+
+ <p>Another common C++ representation of a value that can be
+ <code>NULL</code> is a pointer. ODB will automatically
+ handle data members that are pointers to values, however,
+ it will not automatically enable <code>NULL</code> values
+ for such data members, as is the case for <code>odb::nullable</code>.
+ Instead, if the <code>NULL</code> value is desired, we will
+ need to enable it explicitly using the <code>db&nbsp;null</code>
+ pragma. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ std::string first_;
+
+ #pragma db null
+ std::auto_ptr&lt;std::string> middle_;
+
+ std::string last_;
+};
+ </pre>
+
+ <p>The ODB compiler includes built-in support for using
+ <code>std::auto_ptr</code>, <code>std::unique_ptr</code> (C++11),
+ and <code>shared_ptr</code> (TR1 or C++11) as pointers to values.
+ Plus, ODB profile libraries, that are
+ available for commonly used frameworks and libraries (such as Boost and
+ Qt), provide support for smart pointers found in these frameworks
+ and libraries (<a href="#III">Part III, "Profiles"</a>).</p>
+
+ <p>ODB also supports the <code>NULL</code> semantics for composite
+ values. In the relational database the <code>NULL</code> composite
+ value is translated to <code>NULL</code> values for all the simple
+ data members of this composite value. For example:</p>
+
+ <pre class="cxx">
+#pragma db value
+struct name
+{
+ std::string first_;
+ odb::nullable&lt;std::string> middle_;
+ std::string last_;
+};
+
+#pragma db object
+class person
+{
+ ...
+ odb::nullable&lt;name> name_;
+};
+ </pre>
+
+ <p>ODB does not support the <code>NULL</code> semantics for containers.
+ This also means that a composite value that contains a container
+ cannot be <code>NULL</code>. With this limitation in mind, we can
+ still use smart pointers in data members of container types. The
+ only restriction is that these pointers must not be <code>NULL</code>.
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ std::auto_ptr&lt;std::vector&lt;std::string> > aliases_;
+};
+ </pre>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="8">8 Inheritance</a></h1>
+
+ <p>In C++ inheritance can be used to achieve two different goals.
+ We can employ inheritance to reuse common data and functionality
+ in multiple classes. For example:</p>
+
+ <pre class="cxx">
+class person
+{
+public:
+ const std::string&amp; first () const;
+ const std::string&amp; last () const;
+
+private:
+ std::string first_;
+ std::string last_;
+};
+
+class employee: public person
+{
+ ...
+};
+
+class contractor: public person
+{
+ ...
+};
+ </pre>
+
+ <p>In the above example both the <code>employee</code> and
+ <code>contractor</code> classes inherit the <code>first_</code>
+ and <code>last_</code> data members as well as the <code>first()</code>
+ and <code>last()</code> accessors from the <code>person</code> base
+ class.</p>
+
+ <p>A common trait of this inheritance style, referred to as <em>reuse
+ inheritance</em> from now on, is the lack of virtual functions and
+ a virtual destructor in the base class. Also with this style the
+ application code is normally written in terms of the derived classes
+ instead of the base.</p>
+
+ <p>The second way to utilize inheritance in C++ is to provide polymorphic
+ behavior through a common interface. In this case the base class
+ defines a number of virtual functions and, normally, a virtual
+ destructor while the derived classes provide specific
+ implementations of these virtual functions. For example:</p>
+
+ <pre class="cxx">
+class person
+{
+public:
+ enum employment_status
+ {
+ unemployed,
+ temporary,
+ permanent,
+ self_employed
+ };
+
+ virtual employment_status
+ employment () const = 0;
+
+ virtual
+ ~person ();
+};
+
+class employee: public person
+{
+public:
+ virtual employment_status
+ employment () const
+ {
+ return temporary_ ? temporary : permanent;
+ }
+
+private:
+ bool temporary_;
+};
+
+class contractor: public person
+{
+public:
+ virtual employment_status
+ employment () const
+ {
+ return self_employed;
+ }
+};
+ </pre>
+
+ <p>With this inheritance style, which we will call <em>polymorphism
+ inheritance</em>, the application code normally works with derived
+ classes via the base class interface. Note also that it is very common
+ to mix both styles in the same hierarchy. For example, the above two
+ code fragments can be combined so that the <code>person</code> base
+ class provides the common data members and functions as well as
+ defines the polymorphic interface.</p>
+
+ <p>The following sections describe the available strategies for
+ mapping reuse and polymorphism inheritance styles to a relational
+ data model. Note also that the distinction between the two styles is
+ conceptual rather than formal. For example, it is possible to treat
+ a class hierarchy that defines virtual functions as a case of reuse
+ inheritance if this results in the desired database mapping and
+ semantics.</p>
+
+ <p>Generally, classes that employ reuse inheritance are mapped to
+ completely independent entities in the database. They use different
+ object id spaces and should always be passed to and returned from
+ the database operations as pointers or references to derived types.
+ In other words, from the persistence point of view, such classes
+ behave as if the data members from the base classes were copied
+ verbatim into the derived ones.</p>
+
+ <p>In contrast, classes that employ polymorphism inheritance share
+ the object id space and can be passed to and returned from the
+ database operations <em>polymorphically</em> as pointers or
+ references to the base class.</p>
+
+ <p>For both inheritance styles it is sometimes desirable to prevent
+ instances of a base class from being stored in the database.
+ To achieve this a persistent
+ class can be declared abstract using the <code>db&nbsp;abstract</code>
+ pragma (<a href="#14.1.3">Section 14.1.3, "<code>abstract</code>"</a>).
+ Note that a <em>C++-abstract</em> class, or a class that
+ has one or more pure virtual functions and therefore cannot be
+ instantiated, is also <em>database-abstract</em>. However, a
+ database-abstract class is not necessarily C++-abstract. The
+ ODB compiler automatically treats C++-abstract classes as
+ database-abstract.</p>
+
+ <h2><a name="8.1">8.1 Reuse Inheritance</a></h2>
+
+ <p>Each non-abstract class from the reuse inheritance hierarchy is
+ mapped to a separate database table that contains all its data
+ members, including those inherited from base classes. An abstract
+ persistent class does not have to define an object id, nor a default
+ constructor, and it does not have a corresponding database table.
+ An abstract class cannot be a pointed-to object in a relationship.
+ Multiple inheritance is supported as long as each base
+ class is only inherited once. The following example shows a
+ persistent class hierarchy employing reuse inheritance:</p>
+
+ <pre class="cxx">
+// Abstract person class. Note that it does not declare the
+// object id.
+//
+#pragma db object abstract
+class person
+{
+ ...
+
+ std::string first_;
+ std::string last_;
+};
+
+// Abstract employee class. It derives from the person class and
+// declares the object id for all the concrete employee types.
+//
+#pragma db object abstract
+class employee: public person
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id_;
+};
+
+// Concrete permanent_employee class. Note that it doesn't define
+// any data members of its own.
+//
+#pragma db object
+class permanent_employee: public employee
+{
+ ...
+};
+
+// Concrete temporary_employee class. It adds the employment
+// duration in months.
+//
+#pragma db object
+class temporary_employee: public employee
+{
+ ...
+
+ unsigned long duration_;
+};
+
+// Concrete contractor class. It derives from the person class
+// (and not employee; an independent contractor is not considered
+// an employee). We use the contractor's external email address
+// as the object id.
+//
+#pragma db object
+class contractor: public person
+{
+ ...
+
+ #pragma db id
+ std::string email_;
+};
+ </pre>
+
+ <p>The sample database schema for this hierarchy is shown below.</p>
+
+ <pre class="sql">
+CREATE TABLE permanent_employee (
+ first TEXT NOT NULL,
+ last TEXT NOT NULL,
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT);
+
+CREATE TABLE temporary_employee (
+ first TEXT NOT NULL,
+ last TEXT NOT NULL,
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ duration BIGINT UNSIGNED NOT NULL);
+
+CREATE TABLE contractor (
+ first TEXT NOT NULL,
+ last TEXT NOT NULL,
+ email VARCHAR (128) NOT NULL PRIMARY KEY);
+ </pre>
+
+ <p>The complete version of the code presented in this section is
+ available in the <code>inheritance/reuse</code> example in the
+ <code>odb-examples</code> package.</p>
+
+ <h2><a name="8.2">8.2 Polymorphism Inheritance</a></h2>
+
+ <p>There are three general approaches to mapping a polymorphic
+ class hierarchy to a relational database. These are
+ <em>table-per-hierarchy</em>, <em>table-per-difference</em>,
+ and <em>table-per-class</em>. With the table-per-hierarchy
+ mapping, all the classes in a hierarchy are stored in a single,
+ "wide" table. <code>NULL</code> values are stored in columns
+ corresponding to data members of derived classes that are
+ not present in any particular instance.</p>
+
+ <p>In the table-per-difference mapping, each class is mapped
+ to a separate table. For a derived class, this table contains
+ only columns corresponding to the data members added by this
+ derived class.</p>
+
+ <p>Finally, in the table-per-class mapping, each class is mapped
+ to a separate table. For a derived class, this table contains
+ columns corresponding to all the data members, from this derived
+ class all the way down to the root of the hierarchy.</p>
+
+ <p>The table-per-difference mapping is generally considered as
+ having the best balance of flexibility, performance, and space
+ efficiency. It also results in a more canonical relational
+ database model compared to the other two approaches. As a
+ result, this is the mapping currently implemented in ODB.
+ Other mappings may be supported in the future. Note that
+ multiple polymorphism inheritance or mixing polymorphism and
+ reuse inheritance is not supported.</p>
+
+ <p>A pointer or reference to an ordinary, non-polymorphic object
+ has just one type &mdash; the class type of that object. When we
+ start working with polymorphic objects, there are two types
+ to consider: the <em>static type</em>, or the declaration type
+ of a reference or pointer, and the object's actual or <em>dynamic
+ type</em>. An example will help illustrate the difference:</p>
+
+ <pre class="cxx">
+class person {...};
+class employee: public person {...};
+
+person p;
+employee e;
+
+person&amp; r1 (p);
+person&amp; r2 (e);
+
+auto_ptr&lt;person> p1 (new employee);
+ </pre>
+
+ <p>In the above example, the <code>r1</code> reference's both static
+ and dynamic types are <code>person</code>.
+ In contrast, the <code>r2</code> reference's static type is
+ <code>person</code> while its dynamic type (the actual object
+ that it refers to) is <code>employee</code>. Similarly,
+ <code>p1</code> points to the object of the <code>person</code>
+ static type but <code>employee</code> dynamic type.</p>
+
+ <p>In C++, the primary mechanisms for working with polymorphic objects
+ are virtual functions. We call a virtual function only knowing the
+ object's static type, but the version corresponding to the object's
+ dynamic type is automatically executed. This is the essence of
+ runtime polymorphism support in C++: we can operate in terms of a base
+ class interface but get the derived class' behavior. Similarly, the
+ essence of the runtime polymorphism support in ODB is to allow us to
+ persist, load, update, and query in terms of the base class interface
+ but have the derived class actually stored in the database.</p>
+
+ <p>To declare a persistent class as polymorphic we use the
+ <code>db&nbsp;polymorphic</code> pragma. We only need to
+ declare the root class of a hierarchy as polymorphic; ODB will
+ treat all the derived classes as polymorphic automatically. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object polymorphic
+class person
+{
+ ...
+
+ virtual
+ ~person () = 0; // Automatically abstract.
+
+ #pragma db id auto
+ unsigned long id_;
+
+ std::string first_;
+ std::string last_;
+};
+
+#pragma db object
+class employee: public person
+{
+ ...
+
+ bool temporary_;
+};
+
+#pragma db object
+class contractor: public person
+{
+
+ std::string email_;
+};
+ </pre>
+
+ <p>A persistent class hierarchy declared polymorphic must also be
+ polymorphic in the C++ sense, that is, the root class must
+ declare or inherit at least one virtual function. It is
+ recommended that the root class also declares a virtual destructor.
+ The root class of the polymorphic hierarchy must contain
+ the data member designated as object id (a persistent class
+ without an object id cannot be polymorphic). Note also that,
+ unlike reuse inheritance, abstract polymorphic classes have
+ a table in the database, just like non-abstract classes.</p>
+
+ <p>Persistent classes in the same polymorphic hierarchy must use the
+ same kind of object pointer (<a href="#3.3">Section 3.3,
+ "Object and View Pointers"</a>). If the object pointer
+ for the root class is specified as a template or using the
+ special raw pointer syntax (<code>*</code>), then the ODB
+ compiler will automatically use the same object pointer
+ for all the derived classes. For example:</p>
+
+ <pre class="cxx">
+#pragma db object polymorphic pointer(std::shared_ptr)
+class person
+{
+ ...
+};
+
+#pragma db object // Object pointer is std::shared_ptr&lt;employee>.
+class employee: public person
+{
+ ...
+};
+
+#pragma db object // Object pointer is std::shared_ptr&lt;contractor>.
+class contractor: public person
+{
+ ...
+};
+ </pre>
+
+ <p>Similarly, if we enable or disable session support
+ (<a href="#11">Chapter 11, "Session"</a>) for the root class, then
+ the ODB compiler will automatically enable or disable it for all
+ the derived classes.</p>
+
+ <p>For polymorphic persistent classes, all the database operations can
+ be performed on objects with different static and dynamic types.
+ Similarly, operations that load persistent objects from the
+ database (<code>load()</code>, <code>query()</code>, etc.), can
+ return objects with different static and dynamic types. For
+ example:</p>
+
+ <pre class="cxx">
+unsigned long id1, id2;
+
+// Persist.
+//
+{
+ shared_ptr&lt;person> p1 (new employee (...));
+ shared_ptr&lt;person> p2 (new contractor (...));
+
+ transaction t (db.begin ());
+ id1 = db.persist (p1); // Stores employee.
+ id2 = db.persist (p2); // Stores contractor.
+ t.commit ();
+}
+
+// Load.
+//
+{
+ shared_ptr&lt;person> p;
+
+ transaction t (db.begin ());
+ p = db.load&lt;person> (id1); // Loads employee.
+ p = db.load&lt;person> (id2); // Loads contractor.
+ t.commit ();
+}
+
+// Query.
+//
+{
+ typedef odb::query&lt;person> query;
+ typedef odb::result&lt;person> result;
+
+ transaction t (db.begin ());
+
+ result r (db.query&lt;person> (query::last == "Doe"));
+
+ for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ {
+ person&amp; p (*i); // Can be employee or contractor.
+ }
+
+ t.commit ();
+}
+
+// Update.
+//
+{
+ shared_ptr&lt;person> p;
+ shared_ptr&lt;employee> e;
+
+ transaction t (db.begin ());
+
+ e = db.load&lt;employee> (id1);
+ e->temporary (false);
+ p = e;
+ db.update (p); // Updates employee.
+
+ t.commit ();
+}
+
+// Erase.
+//
+{
+ shared_ptr&lt;person> p;
+
+ transaction t (db.begin ());
+ p = db.load&lt;person> (id1); // Loads employee.
+ db.erase (p); // Erases employee.
+ db.erase&lt;person> (id2); // Erases contractor.
+ t.commit ();
+}
+ </pre>
+
+
+ <p>The table-per-difference mapping, as supported by ODB, requires
+ two extra columns, in addition to those corresponding to the
+ data members. The first, called <em>discriminator</em>, is added
+ to the table corresponding to the root class of the hierarchy.
+ This column is used to determine the dynamic type of each
+ object. The second column is added to tables corresponding
+ to the derived classes and contains the object id. This
+ column is used to form a foreign key constraint referencing
+ the root class table.</p>
+
+ <p>When querying the database for polymorphic objects, it is
+ possible to obtain the discriminator value without
+ instantiating the object. For example:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;person> query;
+typedef odb::result&lt;person> result;
+
+transaction t (db.begin ());
+
+result r (db.query&lt;person> (query::last == "Doe"));
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+{
+ std::string d (i.discriminator ());
+ ...
+}
+
+t.commit ();
+ </pre>
+
+ <p>In the current implementation, ODB has limited support for
+ customizing names, types, and values of the extra columns.
+ Currently, the discriminator column is always called
+ <code>typeid</code> and contains a namespace-qualified class
+ name (for example, <code>"employee"</code> or
+ <code>"hr::employee"</code>). The id column in the derived
+ class table has the same name as the object id column in
+ the root class table. Future versions of ODB will add support
+ for customizing these extra columns.</p>
+
+ <p>The sample database schema for the above polymorphic hierarchy
+ is shown below.</p>
+
+ <pre class="sql">
+CREATE TABLE person (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ typeid VARCHAR(128) NOT NULL,
+ first TEXT NOT NULL,
+ last TEXT NOT NULL);
+
+CREATE TABLE employee (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ temporary TINYINT(1) NOT NULL,
+
+ CONSTRAINT employee_id_fk
+ FOREIGN KEY (id)
+ REFERENCES person (id)
+ ON DELETE CASCADE);
+
+CREATE TABLE contractor (
+ id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+ email TEXT NOT NULL,
+
+ CONSTRAINT contractor_id_fk
+ FOREIGN KEY (id)
+ REFERENCES person (id)
+ ON DELETE CASCADE);
+ </pre>
+
+ <p>The complete version of the code presented in this section is
+ available in the <code>inheritance/polymorphism</code> example
+ in the <code>odb-examples</code> package.</p>
+
+ <h3><a name="8.2.1">8.2.1 Performance and Limitations</a></h3>
+
+ <p>A database operation on a non-polymorphic object normally translates
+ to a single database statement execution (objects with containers
+ and eager object pointers can be the exception). Because polymorphic
+ objects have their data members
+ stored in multiple tables, some database operations on such objects
+ may result in multiple database statements being executed while others
+ may require more complex statements. There is also some functionality
+ that is not available to polymorphic objects.</p>
+
+ <p>The first part of this section discusses the performance implications
+ to keep in mind when designing and working with polymorphic hierarchies.
+ The second part talks about limitations of polymorphic objects.</p>
+
+ <p>The most important aspect of a polymorphic hierarchy that
+ affects database performance is its depth. The distance between
+ the root of the hierarchy and the derived class translates
+ directly to the number of database statements that will have to
+ be executed in order to persist, update, or erase this derived class.
+ It also translates directly to the number of SQL <code>JOIN</code>
+ clauses that will be needed to load or query the database for this
+ derived class. As a result, to achieve best performance, we should
+ try to keep our polymorphic hierarchies as flat as possible.</p>
+
+ <p>When loading an object or querying the database for objects,
+ ODB will need to execute two statements if this object's static
+ and dynamic types are different but only one statement if
+ they are the same. This example will help illustrate the
+ difference:</p>
+
+ <pre class="cxx">
+unsigned long id;
+
+{
+ employee e (...);
+
+ transaction t (db.begin ());
+ id = db.persist (e);
+ t.commit ();
+}
+
+{
+ shared_ptr&lt;person> p;
+
+ transaction t (db.begin ());
+ p = db.load&lt;person> (id); // Requires two statement.
+ p = db.load&lt;employee> (id); // Requires only one statement.
+ t.commit ();
+}
+ </pre>
+
+ <p>As a result, we should try to load and query using the most
+ derived class possible.</p>
+
+ <p>Finally, for polymorphic objects, erasing via the object instance
+ is faster than erasing via its object id. In the former case the
+ object's dynamic type can be determined locally in the application
+ while in the latter case an extra statement has to be executed to
+ achieve the same result. For example:</p>
+
+ <pre class="cxx">
+shared_ptr&lt;person> p = ...;
+
+transaction t (db.begin ());
+db.erase&lt;person> (p.id ()); // Slower (executes extra statement).
+db.erase (p); // Faster.
+t.commit ();
+ </pre>
+
+ <p>Polymorphic objects can use all the mechanisms that are available
+ to ordinary objects. These include containers (<a href="#5">Chapter 5,
+ "Containers"</a>), object relationships, including to polymorphic
+ objects (<a href="#6">Chapter 6, "Relationships"</a>), views
+ (<a href="#10">Chapter 10, "Views"</a>), session (<a href="#11">Chapter
+ 11, "Session"</a>), and optimistic concurrency (<a href="#12">Chapter
+ 12, "Optimistic Concurrency"</a>). There are, however, a few
+ limitations, mainly due to the underlying use of SQL to access the
+ data.</p>
+
+ <p>When a polymorphic object is "joined" in a view, and the join
+ condition (either in the form of an object pointer or a custom
+ condition) comes from the object itself (as opposed to one of
+ the objects joined previously), then this condition must only
+ use data members from the derived class. For example, consider
+ the following polymorphic object hierarchy and a view:</p>
+
+
+ <pre class="cxx">
+#pragma db object polymorphic
+class employee
+{
+ ...
+};
+
+#pragma db object
+class permanent_employee: public employee
+{
+ ...
+};
+
+#pragma db object
+class temporary_employee: public employee
+{
+ ...
+
+ shared_ptr&lt;permanent_employee> manager_;
+};
+
+#pragma db object
+class contractor: public temporary_employee
+{
+ shared_ptr&lt;permanent_employee> manager_;
+};
+
+#pragma db view object(permanent_employee) \
+ object(contractor: contractor::manager_)
+struct contractor_manager
+{
+ ...
+};
+ </pre>
+
+ <p>This view will not function correctly because the join condition
+ (<code>manager_</code>) comes from the base class
+ (<code>temporary_employee</code>) instead of the derived
+ (<code>contractor</code>). The reason for this limitation is the
+ <code>JOIN</code> clause order in the underlying SQL <code>SELECT</code>
+ statement. In the view presented above, the table corresponding
+ to the base class (<code>temporary_employee</code>) will have to
+ be joined first which will result in this view matching both
+ the <code>temporary_employee</code> and <code>contractor</code>
+ objects instead of just <code>contractor</code>. It is usually
+ possible to resolve this issue by reordering the objects in the
+ view. Our example, for instance, can be fixed by swapping the
+ two objects:</p>
+
+ <pre class="cxx">
+#pragma db view object(contractor) \
+ object(permanent_employee: contractor::manager_)
+struct contractor_manager
+{
+ ...
+};
+ </pre>
+
+ <p>The <code>erase_query()</code> database function (<a href="#3.11">Section
+ 3.11, "Deleting Persistent Objects"</a>) also has limited functionality
+ when used on polymorphic objects. Because many database implementations
+ do not support <code>JOIN</code> clauses in the SQL <code>DELETE</code>
+ statement, only data members from the derived class being erased can
+ be used in the query condition. For example:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee> query;
+
+transaction t (db.begin ());
+db.erase_query&lt;employee> (query::permanent); // Ok.
+db.erase_query&lt;employee> (query::last == "Doe"); // Error.
+t.commit ();
+ </pre>
+
+ <h2><a name="8.3">8.3 Mixed Inheritance</a></h2>
+
+ <p>It is possible to mix the reuse and polymorphism inheritance
+ styles in the same hierarchy. In this case, the reuse inheritance
+ must be used for the "bottom" (base) part of the hierarchy while
+ the polymorphism inheritance &mdash; for the "top" (derived) part.
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+};
+
+#pragma db object polymorphic
+class employee: public person // Reuse inheritance.
+{
+ ...
+};
+
+#pragma db object
+class temporary_employee: public employee // Polymorphism inheritance.
+{
+ ...
+};
+
+#pragma db object
+class permanent_employee: public employee // Polymorphism inheritance.
+{
+ ...
+};
+ </pre>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="9">9 Sections</a></h1>
+
+ <p>ODB sections are an optimization mechanism that allows us to
+ partition data members of a persistent class into groups that
+ can be separately loaded and/or updated. This can be useful,
+ for example, if an object contains expensive to load or update
+ data members (such as <code>BLOB</code>s or containers) and
+ that are accessed or modified infrequently. For example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/section.hxx>
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db load(lazy) update(manual)
+ odb::section keys_;
+
+ #pragma db section(keys_) type("BLOB")
+ char public_key_[1024];
+
+ #pragma db section(keys_) type("BLOB")
+ char private_key_[1024];
+};
+
+transaction t (db.begin ());
+
+auto_ptr&lt;person> p (db.load&lt;person> (...)); // Keys are not loaded.
+
+if (need_keys)
+{
+ db.load (*p, p->keys_); // Load keys.
+ ...
+}
+
+db.update (*p); // Keys are not updated.
+
+if (update_keys)
+{
+ ...
+ db.update (*p, p->keys_); // Update keys.
+}
+
+t.commit ();
+ </pre>
+
+ <p>A complete example that shows how to use sections is available in
+ the <code>section</code> directory in the <code>odb-examples</code>
+ package.</p>
+
+ <p>Why do we need to group data members into sections? Why can't
+ each data member be loaded and updated independently if and
+ when necessary? The reason for this requirement is that loading
+ or updating a group of data members with a single database
+ statement is significantly more efficient than loading or updating
+ each data member with a separate statement. Because ODB
+ prepares and caches statements used to load and update
+ persistent objects, generating a custom statement for
+ a specific set of data members that need to be loaded or
+ updated together is not a viable approach either. To resolve
+ this, ODB allows us to group data members that are
+ often updated and/or loaded together into sections. To
+ achieve the best performance, we should aim to find a balance
+ between having too many sections with too few data
+ members and too few sections with too many data
+ members. We can use the access and modification patterns
+ of our application as a base for this decision.</p>
+
+ <p>To add a new section to a persistent class we declare a new
+ data member of the <code>odb::section</code> type. At this
+ point we also need to specify the loading and updating behavior
+ of this section with the <code>db&nbsp;load</code> and
+ <code>db&nbsp;update</code> pragmas, respectively.</p>
+
+ <p>The loading behavior of a section can be either <code>eager</code>
+ or <code>lazy</code>. An eager-loaded section is always loaded as
+ part of the object load. A lazy-loaded section is not loaded
+ as part of the object load and has to be explicitly loaded with
+ the <code>database::load()</code> function (discussed below) if
+ and when necessary.</p>
+
+ <p>The updating behavior of a section can be <code>always</code>,
+ <code>change</code>, or <code>manual</code>. An always-updated
+ section is always updated as part of the object update,
+ provided it has been loaded. A change-updated section
+ is only updated as part of the object update if it has been loaded
+ and marked as changed. A manually-updated section is never updated
+ as part of the object update and has to be explicitly updated with
+ the <code>database::update()</code> function (discussed below) if
+ and when necessary.</p>
+
+ <p>If no loading behavior is specified explicitly, then an eager-loaded
+ section is assumed. Similarly, if no updating behavior is specified,
+ then an always-updated section is assumed. An eager-loaded, always-updated
+ section is pointless and therefore illegal. Only persistent classes
+ with an object id can have sections.</p>
+
+ <p>To specify that a data member belongs to a section we use the
+ <code>db&nbsp;section</code> pragma with the section's member
+ name as its single argument. Except for special data members
+ such as the object id and optimistic concurrency version, any
+ direct, non-transient member of a persistent class can belong
+ to a section, including composite values, containers, and
+ pointers to objects. For example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class text
+{
+ std::string data;
+ std::string lang;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db load(lazy)
+ odb::section extras_;
+
+ #pragma db section(extras_)
+ text bio_;
+
+ #pragma db section(extras_)
+ std::vector&lt;std::string> nicknames_;
+
+ #pragma db section(extras_)
+ std::shared_ptr&lt;person> emergency_contact_;
+};
+ </pre>
+
+ <p>An empty section is pointless and therefore illegal, except
+ in abstract or polymorphic classes where data members can be
+ added to a section by derived classes (see <a href="#9.1">Section
+ 9.1, "Sections and Inheritance"</a>).</p>
+
+ <p>The <code>odb::section</code> class is defined in the
+ <code>&lt;odb/section.hxx></code> header file and has the
+ following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ class section
+ {
+ public:
+ // Load state.
+ //
+ bool
+ loaded () const;
+
+ void
+ unload ();
+
+ void
+ load ();
+
+ // Change state.
+ //
+ bool
+ changed () const;
+
+ void
+ change ();
+
+ // User data.
+ //
+ unsigned char
+ user_data () const;
+
+ void
+ user_data (unsigned char);
+ };
+}
+ </pre>
+
+ <p>The <code>loaded()</code> accessor can be used to determine whether a
+ section is already loaded. The <code>unload()</code> modifier marks a
+ loaded section as not loaded. This, for example, can be useful if you
+ don't want the section to be reloaded during the object reload. The
+ <code>load()</code> modifier marks an unloaded section as loaded
+ without actually loading any of its data members. This, for example,
+ can be useful if you don't want to load the old state before overwriting
+ it with <code>update()</code>.</p>
+
+ <p>The <code>changed()</code> accessor can be used to query the
+ section's change state. The <code>change()</code> modifier
+ marks the section as changed. It is valid to call this modifier
+ for an unloaded (or transient) section, however, the state will
+ be reset back to unchanged once the section (or object) is loaded.
+ The change state is only relevant to sections with change-updated
+ behavior and is ignored for all other sections.</p>
+
+ <p>The size of the section class is one byte with four bits available
+ to store a custom state via the <code>user_data()</code> accessor
+ and modifier.</p>
+
+ <p>The <code>odb::database</code> class provides special
+ versions of the <code>load()</code> and <code>update()</code>
+ functions that allow us to load and update sections of a
+ persistent class. Their signatures are as follows:</p>
+
+ <pre class="cxx">
+ template &lt;typename T>
+ void
+ load (T&amp; object, section&amp;);
+
+ template &lt;typename T>
+ void
+ update (const T&amp; object, const section&amp;);
+ </pre>
+
+ <p>Before calling the section <code>load()</code> function, the
+ object itself must already be loaded. If the section is already
+ loaded, then the call to <code>load()</code> will reload its
+ data members. It is illegal to explicitly load an eager-loaded
+ section.</p>
+
+ <p>Before calling the section <code>update()</code> function, the
+ section (and therefore the object) must be in the loaded state.
+ If the section is not loaded, the <code>odb::section_not_loaded</code>
+ exception is thrown. The section <code>update()</code> function
+ does not check but does clear the section's change state. In
+ other words, section <code>update()</code> will always update
+ section data members in the database and clear the change flag.
+ Note also that any section, that is, always-, change-, or
+ manually-updated, can be explicitly updated with this function.</p>
+
+ <p>Both section <code>load()</code> and <code>update()</code>, just
+ like the rest of the database operations, must be performed within
+ a transaction. Notice also that both <code>load()</code> and
+ <code>update()</code> expect a reference to the section as
+ their second argument. This reference must refer to the data
+ member in the object passed as the first argument. If instead
+ it refers to some other instance of the <code>section</code>
+ class, for example, a local copy or a temporary, then the
+ <code>odb::section_not_in_object</code> exception is thrown.
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+public:
+ ...
+
+ odb::section
+ keys () const {return keys_;}
+
+private:
+ odb::section keys_;
+
+ ...
+};
+
+auto_ptr&lt;person> p (db.load&lt;person> (...));
+
+section s (p->keys ());
+db.load (*p, s); // Throw section_not_in_object, copy.
+
+db.update (*p, p->keys ()); // Throw section_not_in_object, copy.
+ </pre>
+
+ <p>At first glance it may seem more appropriate to make the
+ <code>section</code> class non-copyable in order to prevent
+ such errors from happening. However, it is perfectly reasonable
+ to expect to be able to copy (or assign) sections as part of
+ the object copying (or assignment). As a result, sections are
+ left copyable and copy-assignable, however, this functionality
+ should not be used in accessors or modifiers. Instead, section
+ accessors and modifiers should always be by-reference. Here is
+ how we can fix our previous example:</p>
+
+<pre class="cxx">
+#pragma db object
+class person
+{
+public:
+ ...
+
+ const odb::section&amp;
+ keys () const {return keys_;}
+
+ odb::section&amp;
+ keys () {return keys_;}
+
+private:
+ odb::section keys_;
+
+ ...
+};
+
+auto_ptr&lt;person> p (db.load&lt;person> (...));
+
+section&amp; s (p->keys ());
+db.load (*p, s); // Ok, reference.
+
+db.update (*p, p->keys ()); // Ok, reference.
+ </pre>
+
+ <p>Several other database operations affect sections. The state of
+ a section in a transient object is undefined. That is, before
+ the call to object <code>persist()</code> or <code>load()</code>
+ functions, or after the call to object <code>erase()</code>
+ function, the values returned by the <code>section::loaded()</code> and
+ <code>section::changed()</code> accessors are undefined.</p>
+
+ <p>After the call to <code>persist()</code>, all sections, including
+ eager-loaded ones, are marked as loaded and unchanged. If instead we
+ are loading an object with the <code>load()</code> call or as
+ a result of a query, then eager-loaded sections are loaded
+ and marked as loaded and unchanged while lazy-loaded ones are marked
+ as unloaded. If a lazy-loaded section is later loaded with the
+ section <code>load()</code> call, then it is marked as loaded and
+ unchanged.</p>
+
+ <p>When we update an object with the <code>update()</code> call,
+ manually-updated sections are ignored while always-updated
+ sections are updated if they are loaded. Change-updated
+ sections are only updated if they are both loaded and marked
+ as changed. After the update, such sections are reset to the
+ unchanged state. When we reload an object with the
+ <code>reload()</code> call, sections that were loaded are
+ automatically reloaded and reset to the unchanged state.</p>
+
+ <p>To further illustrate the state transitions of a section,
+ consider this example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db load(lazy) update(change)
+ odb::section keys_;
+
+ ...
+};
+
+transaction t (db.begin ());
+
+person p ("John", "Doe"); // Section state is undefined (transient).
+
+db.persist (p); // Section state: loaded, unchanged.
+
+auto_ptr&lt;person> l (
+ db.load&lt;person> (...)); // Section state: unloaded, unchanged.
+
+db.update (*l); // Section not updated since not loaded.
+db.update (p); // Section not updated since not changed.
+
+p.keys_.change (); // Section state: loaded, changed.
+db.update (p); // Section updated, state: loaded, unchanged.
+
+db.update (*l, l->keys_); // Throw section_not_loaded.
+db.update (p, p.keys_); // Section updated even though not changed.
+
+db.reload (*l); // Section not reloaded since not loaded.
+db.reload (p); // Section reloaded, state: loaded, unchanged.
+
+db.load (*l, l->keys_); // Section loaded, state: loaded, unchanged.
+db.load (p, p.keys_); // Section reloaded, state: loaded, unchanged.
+
+db.erase (p); // Section state is undefined (transient).
+
+t.commit ();
+ </pre>
+
+ <p>When using change-updated behavior, it is our responsibility to
+ mark the section as changed when any of the data members belonging
+ to this section is modified. A natural place to mark the section
+ as changed is the modifiers for section data members, for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ typedef std::array&lt;char, 1024> key_type;
+
+ const key_type&amp;
+ public_key () const {return public_key_;}
+
+ void
+ public_key (const key_type&amp; k)
+ {
+ public_key_ = k;
+ keys_.change ();
+ }
+
+ const key_type&amp;
+ private_key () const {return private_key_;}
+
+ void
+ private_key (const key_type&amp; k)
+ {
+ private_key_ = k;
+ keys_.change ();
+ }
+
+private:
+ #pragma db load(lazy) update(change)
+ odb::section keys_;
+
+ #pragma db section(keys_) type("BLOB")
+ key_type public_key_;
+
+ #pragma db section(keys_) type("BLOB")
+ key_type private_key_;
+
+ ...
+};
+ </pre>
+
+ <p>One interesting aspect of change-updated sections is what happens
+ when a transaction that performed an object or section update is
+ later rolled back. In this case, while the change state of a
+ section has been reset (after update), actual changes were not
+ committed to the database. Change-updated sections handle this
+ case by automatically registering a rollback callback and then,
+ if it is called, restoring the original change state. The
+ following code illustrates this semantics (continuing with
+ the previous example):</p>
+
+ <pre class="cxx">
+auto_ptr&lt;person> p;
+
+try
+{
+ transaction t (db.begin ());
+ p = db.load&lt;person> (...);
+ db.load (*p, p->keys_);
+
+ p->private_key (new_key); // The section is marked changed.
+ db.update (*p); // The section is reset to unchanged.
+
+ throw failed (); // Triggers rollback.
+ t.commit ();
+}
+catch (const failed&amp;)
+{
+ // The section is restored back to changed.
+}
+ </pre>
+
+
+ <h2><a name="9.1">9.1 Sections and Inheritance</a></h2>
+
+ <p>With both reuse and polymorphism inheritance (<a href="#8">Chapter 8,
+ "Inheritance"</a>) it is possible to add new sections to derived
+ classes. It is also possible to add data members from derived
+ classes to sections declared in the base. For example:</p>
+
+ <pre class="cxx">
+#pragma db object polymorphic
+class person
+{
+ ...
+
+ virtual void
+ print ();
+
+ #pragma db load(lazy)
+ odb::section print_;
+
+ #pragma db section(print_)
+ std::string bio_;
+};
+
+#pragma db object
+class employee: public person
+{
+ ...
+
+ virtual void
+ print ();
+
+ #pragma db section(print_)
+ std::vector&lt;std::string> employment_history_;
+};
+
+transaction t (db.begin ());
+
+auto_ptr&lt;person> p (db.load&lt;person> (...)); // Person or employee.
+db.load (*p, p->print_); // Load data members needed for print.
+p->print ();
+
+t.commit ();
+ </pre>
+
+ <p>When data members of a section are spread over several classes in a
+ reuse inheritance hierarchy, both section load and update are
+ performed with a single database statement. In contrast, with
+ polymorphism inheritance, section load is performed with a
+ single statement while update requires a separate statement
+ for each class that adds to the section.</p>
+
+ <p>Note also that in polymorphism inheritance the section-to-object
+ association is static. Or, in other words, you can load a section
+ via an object only if its static type actually contains this
+ section. The following example will help illustrate this
+ point further:</p>
+
+ <pre class="cxx">
+#pragma db object polymorphic
+class person
+{
+ ...
+};
+
+#pragma db object
+class employee: public person
+{
+ ...
+
+ #pragma db load(lazy)
+ odb::section extras_;
+
+ ...
+};
+
+#pragma db object
+class manager: public employee
+{
+ ...
+};
+
+auto_ptr&lt;manager> m (db.load&lt;manager> (...));
+
+person&amp; p (*m);
+employee&amp; e (*m);
+section&amp; s (m->extras_);
+
+db.load (p, s); // Error: extras_ is not in person.
+db.load (e, s); // Ok: extras_ is in employee.
+ </pre>
+
+ <h2><a name="9.2">9.2 Sections and Optimistic Concurrency</a></h2>
+
+ <p>When sections are used in a class with the optimistic concurrency
+ model (<a href="#12">Chapter 12, "Optimistic Concurrency"</a>),
+ both section update and load operations compare the object version
+ to that in the database and throw the <code>odb::object_changed</code>
+ exception if they do not match. In addition, the section update
+ operation increments the version to indicate that the object state
+ has changed. For example:</p>
+
+ <pre class="cxx">
+#pragma db object optimistic
+class person
+{
+ ...
+
+ #pragma db version
+ unsigned long long version_;
+
+ #pragma db load(lazy)
+ odb::section extras_;
+
+ #pragma db section(extras_)
+ std::string bio_;
+};
+
+auto_ptr&lt;person> p;
+
+{
+ transaction t (db.begin ());
+ p = db.load&lt;person> (...);
+ t.commit ();
+}
+
+{
+ transaction t (db.begin ());
+
+ try
+ {
+ db.load (*p, p->extras_); // Throws if object state has changed.
+ }
+ catch (const object_changed&amp;)
+ {
+ db.reload (*p);
+ db.load (*p, p->extras_); // Cannot fail.
+ }
+
+ t.commit ();
+}
+ </pre>
+
+ <p>Note also that if an object update triggers one or more
+ section updates, then each such update will increment the
+ object version. As a result, an update of an object that
+ contains sections may result in a version increment by
+ more than one.</p>
+
+ <p>When sections are used together with optimistic concurrency and
+ inheritance, an extra step may be required to enable this
+ functionality. If you plan to add new sections to derived
+ classes, then the root class of the hierarchy
+ (the one that declares the version data member) must be
+ declared as sectionable with the <code>db&nbsp;sectionable</code>
+ pragma. For example:</p>
+
+ <pre class="cxx">
+#pragma db object polymorphic sectionable
+class person
+{
+ ...
+
+ #pragma db version
+ unsigned long long version_;
+};
+
+#pragma db object
+class employee: public person
+{
+ ...
+
+ #pragma db load(lazy)
+ odb::section extras_;
+
+ #pragma db section(extras_)
+ std::vector&lt;std::string> employment_history_;
+};
+ </pre>
+
+ <p>This requirement has to do with the need to generate extra
+ version increment code in the root class that will be used
+ by sections added in the derived classes. If you forget to
+ declare the root class as sectionable and later add a
+ section to one of the derived classes, the ODB compiler
+ will issue diagnostics.</p>
+
+ <h2><a name="9.3">9.3 Sections and Lazy Pointers</a></h2>
+
+ <p>If a lazy pointer (<a href="#6.4">Section 6.4, "Lazy Pointers"</a>)
+ belongs to a lazy-loaded section, then we end up with two levels of
+ lazy loading. Specifically, when the section is loaded, the lazy
+ pointer is initialized with the object id but the object itself
+ is not loaded. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db load(lazy)
+ odb::section extras_;
+
+ #pragma db section(extras_)
+ odb::lazy_shared_ptr&lt;employer> employer_;
+};
+
+transaction t (db.begin ());
+
+auto_ptr&lt;employee> e (db.load&lt;employee> (...)); // employer_ is NULL.
+
+db.load (*e, e->extras_); // employer_ contains valid employer id.
+
+e->employer_.load (); // employer_ points to employer object.
+
+t.commit ();
+ </pre>
+
+ <h2><a name="9.4">9.4 Sections and Change-Tracking Containers</a></h2>
+
+ <p>If a change-tracking container (<a href="#5.4">Section 5.4,
+ "Change-Tracking Containers"</a>) belongs to a change-updated
+ section, then prior to an object update ODB will check if the
+ container has been changed and if so, automatically mark the
+ section as changed. For example:</p>
+
+<pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db load(lazy) update(change)
+ odb::section extras_;
+
+ #pragma db section(extras_)
+ odb::vector&lt;std::string> nicknames_;
+};
+
+transaction t (db.begin ());
+
+auto_ptr&lt;person> p (db.load&lt;person> (...));
+db.load (*p, p->extras_);
+
+p->nicknames_.push_back ("JD");
+
+db.update (*p); // Section is automatically updated even
+ // though it was not marked as changed.
+t.commit ();
+ </pre>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="10">10 Views</a></h1>
+
+ <p>An ODB view is a C++ <code>class</code> or <code>struct</code> type
+ that embodies a light-weight, read-only projection of one or more
+ persistent objects or database tables or the result of a native SQL
+ query execution.</p>
+
+ <p>Some of the common applications of views include loading a subset
+ of data members from objects or columns from database tables, executing
+ and handling results of arbitrary SQL queries, including aggregate
+ queries and stored procedure calls, as well as joining multiple
+ objects and/or database tables using object relationships or custom
+ join conditions.</p>
+
+ <p>Many relational databases also define the concept of views. Note,
+ however, that ODB views are not mapped to database views. Rather,
+ by default, an ODB view is mapped to an SQL <code>SELECT</code>
+ query. However, if desired, it is easy to create an ODB view
+ that is based on a database view.</p>
+
+ <p>Usually, views are defined in terms of other persistent entities,
+ such as persistent objects, database tables, sequences, etc.
+ Therefore, before we can examine our first view, we need to
+ define a few persistent objects and a database table. We will
+ use this model in examples throughout this chapter. Here we
+ assume that you are familiar with ODB object relationship
+ support (<a href="#6">Chapter 6, "Relationships"</a>).</p>
+
+ <pre class="cxx">
+#pragma db object
+class country
+{
+ ...
+
+ #pragma db id
+ std::string code_; // ISO 2-letter country code.
+
+ std::string name_;
+};
+
+#pragma db object
+class employer
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string name_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+
+ std::string first_;
+ std::string last_;
+
+ unsigned short age_;
+
+ shared_ptr&lt;country> residence_;
+ shared_ptr&lt;country> nationality_;
+
+ shared_ptr&lt;employer> employed_by_;
+};
+ </pre>
+
+ <p>Besides these objects, we also have the legacy
+ <code>employee_extra</code> table that is not mapped to any persistent
+ class. It has the following definition:</p>
+
+ <pre class="sql">
+CREATE TABLE employee_extra(
+ employee_id INTEGER NOT NULL,
+ vacation_days INTEGER NOT NULL,
+ previous_employer_id INTEGER)
+ </pre>
+
+ <p>The above persistent objects and database table as well as many of
+ the views shown in this chapter are based on the
+ <code>view</code> example which can be found in the
+ <code>odb-examples</code> package of the ODB distribution.</p>
+
+ <p>To declare a view we use the <code>db&nbsp;view</code> pragma,
+ for example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee)
+struct employee_name
+{
+ std::string first;
+ std::string last;
+};
+ </pre>
+
+ <p>The above example shows one of the simplest views that we can create.
+ It has a single associated object (<code>employee</code>) and its
+ purpose is to extract the employee's first and last names without
+ loading any other data, such as the referenced <code>country</code>
+ and <code>employer</code> objects.</p>
+
+ <p>Views use the same query facility (<a href="#4">Chapter 4, "Querying
+ the Database"</a>) as persistent objects. Because support for queries
+ is optional and views cannot be used without this support, you need
+ to compile any header that defines a view with the
+ <code>--generate-query</code> ODB compiler option.</p>
+
+ <p>To query the database for a view we use the
+ <code>database::query()</code>, <code>database::query_one()</code>, or
+ <code>database::query_value()</code> functions in exactly the same way
+ as we would use them to query the database for an object. For example,
+ the following code fragment shows how we can find the names of all the
+ employees that are younger than 31:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee_name> query;
+typedef odb::result&lt;employee_name> result;
+
+transaction t (db.begin ());
+
+result r (db.query&lt;employee_name> (query::age &lt; 31));
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+{
+ const employee_name&amp; en (*i);
+ cout &lt;&lt; en.first &lt;&lt; " " &lt;&lt; en.last &lt;&lt; endl;
+}
+
+t.commit ();
+ </pre>
+
+ <p>A view can be defined as a projection of one or more objects, one
+ or more tables, a combination of objects and tables, or it can be
+ the result of a custom SQL query. The following sections discuss each
+ of these kinds of view in more detail.</p>
+
+ <h2><a name="10.1">10.1 Object Views</a></h2>
+
+ <p>To associate one or more objects with a view we use the
+ <code>db&nbsp;object</code> pragma (<a href="#14.2.1">Section
+ 14.2.1, "<code>object</code>"</a>). We have already seen
+ a simple, single-object view in the introduction to this chapter.
+ To associate the second and subsequent objects we repeat the
+ <code>db&nbsp;object</code> pragma for each additional object,
+ for example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) object(employer)
+struct employee_employer
+{
+ std::string first;
+ std::string last;
+ std::string name;
+};
+ </pre>
+
+ <p>The complete syntax of the <code>db&nbsp;object</code> pragma is
+ shown below:</p>
+
+ <p><code><b>object(</b><i>name</i>
+ [<b>=</b> <i>alias</i>]
+ [<i>join-type</i>]
+ [<b>:</b> <i>join-condition</i>]<b>)</b></code></p>
+
+ <p>The <i>name</i> part is a potentially qualified persistent class
+ name that has been defined previously. The optional <i>alias</i>
+ part gives this object an alias. If provided, the alias is used
+ in several contexts instead of the object's unqualified name. We
+ will discuss aliases further as we cover each of these contexts
+ below. The optional <i>join-type</i> part specifies the way this
+ object is associated. It can be <code>left</code>, <code>right</code>,
+ <code>full</code>, <code>inner</code>, and <code>cross</code>
+ with <code>left</code> being the default.
+ Finally, the optional <i>join-condition</i> part provides the
+ criteria which should be used to associate this object with any
+ of the previously associated objects or, as we will see in
+ <a href="#10.4">Section 10.4, "Mixed Views"</a>, tables. Note that
+ while the first associated object can have an alias, it cannot
+ have a join type or condition.</p>
+
+ <p>For each subsequent associated object the ODB compiler needs
+ a join condition and there are several ways to specify
+ it. The easiest way is to omit it altogether and let the ODB
+ compiler try to come up with a join condition automatically.
+ To do this the ODB compiler will examine each previously
+ associated object for object relationships
+ (<a href="#6">Chapter 6, "Relationships"</a>) that
+ may exist between these objects and the object being associated.
+ If such a relationship exists and is unambiguous, that is
+ there is only one such relationship, then the ODB compiler
+ will automatically use it to come up with the join condition for
+ this object. This is exactly what happens in the previous
+ example: there is a single relationship
+ (<code>employee::employed_by</code>) between the
+ <code>employee</code> and <code>employer</code> objects.</p>
+
+ <p>On the other hand, consider this view:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) object(country)
+struct employee_residence
+{
+ std::string first;
+ std::string last;
+ std::string name;
+};
+ </pre>
+
+ <p>While there is a relationship between <code>country</code> and
+ <code>employee</code>, it is ambiguous. It can be
+ <code>employee::residence_</code> (which is what we want) or
+ it can be <code>employee::nationality_</code> (which we don't
+ want). As result, when compiling the above view, the ODB
+ compiler will issue an error indicating an ambiguous object
+ relationship. To resolve this ambiguity, we can explicitly
+ specify the object relationship that should be used to create
+ the join condition as the name of the corresponding data member.
+ Here is how we can fix the <code>employee_residence</code>
+ view:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) object(country: employee::residence_)
+struct employee_residence
+{
+ std::string first;
+ std::string last;
+ std::string name;
+};
+ </pre>
+
+ <p>It is possible to associate the same object with a single view
+ more than once using different join conditions. However, in
+ this case, we have to use aliases to assign different names
+ for each association. For example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country = res_country: employee::residence_) \
+ object(country = nat_country: employee::nationality_)
+struct employee_country
+{
+ ...
+};
+ </pre>
+
+ <p>Note that correctly defining data members in this view requires
+ the use of a mechanism that we haven't yet covered. We will
+ see how to do this shortly.</p>
+
+ <p>If we assign an alias to an object and refer to a data member of
+ this object in one of the join conditions, we have to use the
+ unqualified alias name instead of the potentially qualified
+ object name. For example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee = ee) object(country: ee::residence_)
+struct employee_residence
+{
+ ...
+};
+ </pre>
+
+ <p>The last way to specify a join condition is to provide a custom
+ query expression. This method is primarily useful if you would
+ like to associate an object using a condition that does not
+ involve an object relationship. Consider, for example, a
+ modified <code>employee</code> object from the beginning of
+ the chapter with an added country of birth member. For one
+ reason or another we have decided not to use a relationship to
+ the <code>country</code> object, as we have done with
+ residence and nationality.</p>
+
+ <pre class="cxx">
+#pragma db object
+class employee
+{
+ ...
+
+ std::string birth_place_; // Country name.
+};
+ </pre>
+
+ <p>If we now want to create a view that returns the birth country code
+ for an employee, then we have to use a custom join condition when
+ associating the <code>country</code> object. For example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country: employee::birth_place_ == country::name_)
+struct employee_birth_code
+{
+ std::string first;
+ std::string last;
+ std::string code;
+};
+ </pre>
+
+ <p>The syntax of the query expression in custom join conditions
+ is the same as in the query facility used to query the database
+ for objects (<a href="#4">Chapter 4, "Querying the Database"</a>)
+ except that for query members, instead of using
+ <code>odb::query&lt;object>::member</code> names, we refer directly
+ to object members.</p>
+
+ <p>Looking at the views we have defined so far, you may be wondering
+ how the ODB compiler knows which view data members correspond to which
+ object data members. While the names are similar, they are not exactly
+ the same, for example <code>employee_name::first</code> and
+ <code>employee::first_</code>.</p>
+
+ <p>As with join conditions, when it comes to associating data members,
+ the ODB compiler tries to do this automatically. It first searches
+ all the associated objects for an exact name match. If no match is
+ found, then the ODB compiler compares the so-called public names.
+ A public name of a member is obtained by removing the common member
+ name decorations, such as leading and trailing underscores, the
+ <code>m_</code> prefix, etc. In both of these searches the ODB
+ compiler also makes sure that the types of the two members are the
+ same or compatible.</p>
+
+ <p>If one of the above searches returned a match and it is unambiguous, that
+ is there is only one match, then the ODB compiler will automatically
+ associate the two members. On the other hand, if no match is found
+ or the match is ambiguous, the ODB compiler will issue an error.
+ To associate two differently-named members or to resolve an ambiguity,
+ we can explicitly specify the member association using the
+ <code>db&nbsp;column</code> pragma (<a href="#14.4.9">Section 14.4.9,
+ "<code>column</code>"</a>). For example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) object(employer)
+struct employee_employer
+{
+ std::string first;
+ std::string last;
+
+ #pragma db column(employer::name_)
+ std::string employer_name;
+};
+ </pre>
+
+ <p>If an object data member specifies the SQL type with
+ the <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section
+ 14.4.3, "<code>type</code>"</a>), then this type is also used for
+ the associated view data members.</p>
+
+ <p>Note also that similar to join conditions, if we assign an alias to
+ an object and refer to a data member of this object in one of the
+ <code>db&nbsp;column</code> pragmas, then we have to use the
+ unqualified alias name instead of the potentially qualified
+ object name. For example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country = res_country: employee::residence_) \
+ object(country = nat_country: employee::nationality_)
+struct employee_country
+{
+ std::string first;
+ std::string last;
+
+ #pragma db column(res_country::name_)
+ std::string res_country_name;
+
+ #pragma db column(nat_country::name_)
+ std::string nat_country_name;
+};
+ </pre>
+
+ <p>Besides specifying just the object member, we can also specify a
+ <em>+-expression</em> in the <code>db&nbsp;column</code> pragma. A
+ +-expression consists of string literals and object
+ member references connected using the <code>+</code> operator.
+ It is primarily useful for defining aggregate views based on
+ SQL aggregate functions, for example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee)
+struct employee_count
+{
+ #pragma db column("count(" + employee::id_ + ")")
+ std::size_t count;
+};
+ </pre>
+
+ <p>When querying the database for a view, we may want to provide
+ additional query criteria based on the objects associated with
+ this view. To support this a view defines query members for all
+ the associated objects which allows us to refer to such objects'
+ members using the <code>odb::query&lt;view>::member</code> expressions.
+ This is similar to how we can refer to object members using the
+ <code>odb::query&lt;object>::member</code> expressions when
+ querying the database for an object. For example:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee_count> query;
+
+transaction t (db.begin ());
+
+// Find the number of employees with the Doe last name. Result of this
+// aggregate query contains only one element so use the query_value()
+// shortcut function.
+//
+employee_count ec (
+ db.query_value&lt;employee_count> (query::last == "Doe"));
+
+cout &lt;&lt; ec.count &lt;&lt; endl;
+
+t.commit ();
+ </pre>
+
+ <p>In the above query we used the last name data member from the associated
+ <code>employee</code> object to only count employees with the specific
+ name.</p>
+
+ <p>When a view has only one associated object, the query members
+ corresponding to this object are defined directly in the
+ <code>odb::query&lt;view></code> scope. For instance,
+ in the above example, we referred to the last name member as
+ <code>odb::query&lt;employee_count>::last</code>. However, if
+ a view has multiple associated objects, then query members
+ corresponding to each such object are defined in a nested
+ scope named after the object. As an example, consider
+ the <code>employee_employer</code> view again:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) object(employer)
+struct employee_employer
+{
+ std::string first;
+ std::string last;
+
+ #pragma db column(employer::name_)
+ std::string employer_name;
+};
+ </pre>
+
+ <p>Now, to refer to the last name data member from the <code>employee</code>
+ object we use the
+ <code>odb::query&lt;...>::employee::last</code> expression.
+ Similarly, to refer to the employer name, we use the
+ <code>odb::query&lt;...>::employer::name</code> expression.
+ For example:</p>
+
+ <pre class="cxx">
+typedef odb::result&lt;employee_employer> result;
+typedef odb::query&lt;employee_employer> query;
+
+transaction t (db.begin ());
+
+result r (db.query&lt;employee_employer> (
+ query::employee::last == "Doe" &amp;&amp;
+ query::employer::name == "Simple Tech Ltd"));
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ cout &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->employer_name &lt;&lt; endl;
+
+t.commit ();
+ </pre>
+
+ <p>If we assign an alias to an object, then this alias is used to
+ name the query members scope instead of the object name. As an
+ example, consider the <code>employee_country</code> view again:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country = res_country: employee::residence_) \
+ object(country = nat_country: employee::nationality_)
+struct employee_country
+{
+ ...
+};
+ </pre>
+
+ <p>And a query which returns all the employees that have the same
+ country of residence and nationality:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee_country> query;
+typedef odb::result&lt;employee_country> result;
+
+transaction t (db.begin ());
+
+result r (db.query&lt;employee_country> (
+ query::res_country::name == query::nat_country::name));
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ cout &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->res_country_name &lt;&lt; endl;
+
+t.commit ();
+ </pre>
+
+ <p>Note also that unlike object query members, view query members do
+ no support referencing members in related objects. For example,
+ the following query is invalid:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee_name> query;
+typedef odb::result&lt;employee_name> result;
+
+transaction t (db.begin ());
+
+result r (db.query&lt;employee_name> (
+ query::employed_by->name == "Simple Tech Ltd"));
+
+t.commit ();
+ </pre>
+
+ <p>To get this behavior, we would instead need to associate the
+ <code>employer</code> object with this view and then use the
+ <code>query::employer::name</code> expression instead of
+ <code>query::employed_by->name</code>.</p>
+
+ <p>As we have discussed above, if specified, an object alias is
+ used instead of the object name in the join condition, data
+ member references in the <code>db&nbsp;column</code> pragma,
+ as well as to name the query members scope. The object alias
+ is also used as a table name alias in the underlying
+ <code>SELECT</code> statement generated by the ODB compiler.
+ Normally, you would not use the table alias directly with
+ object views. However, if for some reason you need to refer
+ to a table column directly, for example, as part of a native
+ query expression, and you need to qualify the column with
+ the table, then you will need to use the table alias instead.</p>
+
+ <h2><a name="10.2">10.2 Object Loading Views</a></h2>
+
+ <p>A special variant of object views is object loading views. Object
+ loading views allow us to load one or more complete objects
+ instead of, or in addition to, a subset of data member. While we
+ can often achieve the same end result by calling
+ <code>database::load()</code>, using a view has several advantages.</p>
+
+ <p>If we need to load multiple objects, then using a view allows us
+ to do this with a single <code>SELECT</code> statement execution
+ instead of one for each object that would be necessary in case of
+ <code>load()</code>. A view can also be useful for loading only
+ a single object if the query criterion that we would like to use
+ involves other, potentially unrelated, objects. We will examine
+ concrete examples of these and other scenarios in the rest of this
+ section.</p>
+
+ <p>To load a complete object as part of a view we use a data member of
+ the pointer to object type, just like for object relationships
+ (<a href="#6">Chapter 6, "Relationships"</a>). As an example, here
+ is how we can load both the <code>employee</code> and
+ <code>employer</code> objects from the previous section with a single
+ statement:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) object(employer)
+struct employee_employer
+{
+ shared_ptr&lt;employee> ee;
+ shared_ptr&lt;employer> er;
+};
+ </pre>
+
+ <p>We use an object loading view just like any other view. In the
+ result of a query, as we would expect, the pointer data members
+ point to the loaded objects. For example:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee_employer> query;
+
+transaction t (db.begin ());
+
+for (const employee_employer&amp; r:
+ db.query&lt;employee_employer> (query::employee::age &lt; 31))
+{
+ cout &lt;&lt; r.ee->age () &lt;&lt; " " &lt;&lt; r.er->name () &lt;&lt; endl;
+}
+
+t.commit ();
+ </pre>
+
+ <p>As another example, consider a query that loads the <code>employer</code>
+ objects using some condition based on its employees. For instance, we
+ want to find all the employers that employ people over 65 years old.
+ We can use this object loading view to implement such a query (notice
+ the <code>distinct</code> result modifier discussed later in
+ <a href="#10.5">Section 10.5, "View Query Conditions"</a>):</p>
+
+ <pre class="cxx">
+#pragma db view object(employer) object(employee) query(distinct)
+struct employer_view
+{
+ shared_ptr&lt;employer> er;
+};
+ </pre>
+
+ <p>And this is how we can use this view to find all the employers that
+ employ seniors:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employer_view> query;
+
+db.query&lt;employer_view> (query::employee::age > 65)
+ </pre>
+
+ <p>We can even use object loading views to load completely unrelated
+ (from the ODB object relationships point of view) objects. For example,
+ the following view will load all the employers that are named the
+ same as a country (notice the <code>inner</code> join type):</p>
+
+ <pre class="cxx">
+#pragma db view object(employer) \
+ object(country inner: employer::name == country::name)
+struct employer_named_country
+{
+ shared_ptr&lt;employer> e;
+ shared_ptr&lt;country> c;
+};
+ </pre>
+
+ <p>An object loading view can contain ordinary data members
+ in addition to object pointers. For example, if we are only
+ interested in the country code in the above view, then we
+ can reimplement it like this:</p>
+
+ <pre class="cxx">
+#pragma db view object(employer) \
+ object(country inner: employer::name == country::name)
+struct employer_named_country
+{
+ shared_ptr&lt;employer> e;
+ std::string code;
+};
+ </pre>
+
+ <p>Object loading views also have a few rules and restrictions.
+ Firstly, the pointed-to object in the data member must be associated
+ with the view. Furthermore, if the associated object has an alias,
+ then the data member name must be the same as the alias (more
+ precisely, the public name derived from the data member must
+ match the alias; which means we can use normal data member
+ decorations such as trailing underscores, etc., see the previous
+ section for more information on public names). The following view
+ illustrates the use of aliases as data member names:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country = res: employee::residence_) \
+ object(country = nat: employee::nationality_)
+struct employee_country
+{
+ shared_ptr&lt;country> res;
+ shared_ptr&lt;country> nat_;
+};
+ </pre>
+
+ <p>Finally, the object pointers must be direct data members of
+ the view. Using, for example, a composite value that contains
+ pointers as a view data member is not supported. Note also
+ that depending on the join type you are using, some of the
+ resulting pointers might be <code>NULL</code>.</p>
+
+ <p>Up until now we have consistently used <code>shared_ptr</code>
+ as an object pointer in our views. Can we use other pointers,
+ such as <code>unique_ptr</code> or raw pointers? To answer
+ this question we first need to discuss what happens with
+ object pointers that may be inside objects that a view
+ loads. As a concrete example, let us revisit the
+ <code>employee_employer</code> view from the beginning of
+ this section:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) object(employer)
+struct employee_employer
+{
+ shared_ptr&lt;employee> ee;
+ shared_ptr&lt;employer> er;
+};
+ </pre>
+
+ <p>This view loads two objects: <code>employee</code> and
+ <code>employer</code>. The <code>employee</code> object,
+ however, also contains a pointer to <code>employer</code>
+ (see the <code>employed_by_</code> data member). In fact,
+ this is the same object that the view loads since <code>employer</code>
+ is associated with the view using this same relationship (ODB
+ automatically uses it since it is the only one). The correct
+ result of loading such a view is then clear: both <code>er</code> and
+ <code>er->employed_by_</code> must point to (or share) the
+ same instance.</p>
+
+ <p>Just like object loading via the <code>database</code> class
+ functions, views achieve this correct behavior of only loading
+ a single instance of the same object with the help of session's
+ object cache (<a href="#11">Chapter 11, "Session"</a>). In fact,
+ object loading views enforce this by throwing the
+ <code>session_required</code> exception if there is no current
+ session and the view loads an object that is also indirectly
+ loaded by one of the other objects. The ODB compiler will also
+ issue diagnostics if such an object has session support
+ disabled (<a href="#14.1.10">Section 14.1.10,
+ "<code>session</code>"</a>).</p>
+
+ <p>With this understanding we can now provide the correct implementation
+ of our transaction that uses the <code>employee_employer</code> view:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee_employer> query;
+
+transaction t (db.begin ());
+odb::session s;
+
+for (const employee_employer&amp; r:
+ db.query&lt;employee_employer> (query::employee::age &lt; 31))
+{
+ assert (r.ee->employed_by_ == r.er);
+ cout &lt;&lt; r.ee->age () &lt;&lt; " " &lt;&lt; r.er->name () &lt;&lt; endl;
+}
+
+t.commit ();
+ </pre>
+
+ <p>It might seem logical, then, to always load all the objects from
+ all the eager relationships with the view. After all, this will
+ lead to them all being loaded with a single statement. While
+ this is theoretically true, the reality is slightly more nuanced.
+ If there is a high probability of the object already have been
+ loaded and sitting in the cache, then not loading the object
+ as part of the view (and therefore not fetching all its data
+ from the database) might result in better performance.</p>
+
+ <p>Now we can also answer the question about which pointers we can
+ use in object loading views. From the above discussion it should
+ be clear that if an object that we are loading is also part of a
+ relationship inside another object that we are loading, then we
+ should use some form of a shared ownership pointer. If, however,
+ there are no relationships involved, as is the case, for example,
+ in our <code>employer_named_country</code> and
+ <code>employee_country</code> views above, then we can use a
+ unique ownership pointer such as <code>unique_ptr</code>.</p>
+
+ <p>Note also that your choice of a pointer type can be limited by the
+ "official" object pointer type assigned to the object
+ (<a href="#3.3">Section 3.3, "Object and View Pointers"</a>).
+ For example, if the object pointer type is <code>shared_ptr</code>,
+ you will not be able to use <code>unique_ptr</code> to load
+ such an object into a view since initializing <code>unique_ptr</code>
+ from <code>shared_ptr</code> would be a mistake.</p>
+
+ <p>Unless you want to perform your own object cleanup, raw object
+ pointers in views are not particularly useful. They do have one
+ special semantics, however: If a raw pointer is used as a view
+ member, then, before creating a new instance, the implementation
+ will check if the member is <code>NULL</code>. If it is not, then
+ it is assumed to point to an existing instance and the implementation
+ will load the data into it instead of creating a new one. The
+ primary use of this special functionality is to implement by-value
+ loading with the ability to detect <code>NULL</code> values.</p>
+
+ <p>To illustrate this functionality, consider the following view that
+ load the employee's residence country by value:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country = res: employee::residence_) transient
+struct employee_res_country
+{
+ typedef country* country_ptr;
+
+ #pragma db member(res_) virtual(country_ptr) get(&amp;this.res) \
+ set(this.res_null = ((?) == nullptr))
+
+ country res;
+ bool res_null;
+};
+ </pre>
+
+ <p>Here we are using a virtual data member
+ (<a href="#14.4.13">Section 14.4.13, "<code>virtual</code>"</a>) to
+ add an object pointer member to the view. Its accessor expression
+ returns the pointer to the <code>res</code> member so that
+ the implementation can load the data into it. The modifier
+ expression checks the passed pointer to initialize the
+ <code>NULL</code> value indicator. Here, the two possible
+ values that can be passed to the modifier expression are
+ the address of the <code>res</code> member that we returned
+ earlier from the accessor and <code>NULL</code> (strictly
+ speaking, there is a third possibility: the address of an
+ object that was found in the session cache).</p>
+
+ <p>If we are not interested in the <code>NULL</code> indicator,
+ then the above view can simplified to this:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country = res: employee::residence_) transient
+struct employee_res_country
+{
+ typedef country* country_ptr;
+
+ #pragma db member(res_) virtual(country_ptr) get(&amp;this.res) set()
+
+ country res;
+};
+ </pre>
+
+ <p>That is, we specify an empty modifier expression which leads to
+ the value being ignored.</p>
+
+ <p>As another example of by-value loading, consider a view that allows
+ us to load objects into existing instances that have been allocated
+ outside the view:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ object(country = res: employee::residence_) \
+ object(country = nat: employee::nationality_)
+struct employee_country
+{
+ employee_country (country&amp; r, country&amp; n): res (&amp;r), nat (&amp;n) {}
+
+ country* res;
+ country* nat;
+};
+ </pre>
+
+ <p>And here is how we can use this view:</p>
+
+ <pre class="cxx">
+typedef odb::result&lt;employee_country> result;
+
+transaction t (db.begin ());
+
+result r (db.query&lt;employee_country> (...);
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+{
+ country res, nat;
+ employee_country v (res, nat);
+ i.load (v);
+
+ if (v.res != nullptr)
+ ... // Result is in res.
+
+ if (v.nat != nullptr)
+ ... // Result is in nat.
+}
+
+t.commit ();
+ </pre>
+
+ <p>As a final example of the by-value loading, consider the following
+ view which implements a slightly more advanced logic: if the object
+ is already in the session cache, then it sets the pointer data member
+ in the view (<code>er_p</code>) to that. Otherwise, it loads the data
+ into the by-value instance (<code>er</code>). We can also check
+ whether the pointer data member points to the instance to distinguish
+ between the two outcomes. And we can check it for <code>nullptr</code>
+ to detect <code>NULL</code> values.</p>
+
+ <pre class="cxx">
+#pragma db view object(employer)
+struct employer_view
+{
+ // Since we may be getting the pointer as both smart and raw, we
+ // need to create a bit of support code to use in the modifier
+ // expression.
+ //
+ void set_er (employer* p) {er_p = p;} // &amp;er or NULL.
+ void set_er (shared_ptr&lt;employer> p) {er_p = p.get ();} // From cache.
+
+ #pragma db get(&amp;this.er) set(set_er(?))
+ employer* er_p;
+
+ #pragma db transient
+ employer er;
+
+ // Return-by-value support (e.g., query_value()).
+ //
+ employer_view (): er_p (0) {}
+ employer_view (const employer_view&amp; x)
+ : er_p (x.er_p == &amp;x.er ? &amp;er : x.er_p), er (x.er) {}
+};
+ </pre>
+
+ <p>We can use object loading views with polymorphic objects
+ (<a href="#8.2">Section 8.2, "Polymorphism Inheritance"</a>). Note,
+ however, that when loading a derived object via the base pointer
+ in a view, a separate statement will be executed to load the
+ dynamic part of the object. There is no support for by-value
+ loading for polymorphic objects.</p>
+
+ <p>We can also use object loading views with objects without id
+ (<a href="#14.1.6">Section 14.1.6, "<code>no_id</code>"</a>).
+ Note, however, that for such objects, <code>NULL</code> values
+ are not automatically detected (since there is no primary key,
+ which is otherwise guaranteed to be not <code>NULL</code>, there
+ might not be a column on which to base this detection). The
+ workaround for this limitation is to load an otherwise not
+ <code>NULL</code> column next to the object which will serve
+ as an indicator. For example:</p>
+
+ <pre class="cxx">
+#pragma db object no_id
+class object
+{
+ ...
+
+ int n; // NOT NULL
+ std::string s;
+};
+
+#include &lt;odb/nullable.hxx>
+
+#pragma db view object(object)
+struct view
+{
+
+ odb::nullable&lt;int> n; // If 'n' is NULL, then, logically, so is 'o'.
+ unique_ptr&lt;object> o;
+};
+ </pre>
+
+ <h2><a name="10.3">10.3 Table Views</a></h2>
+
+ <p>A table view is similar to an object view except that it is
+ based on one or more database tables instead of persistent
+ objects. Table views are primarily useful when dealing with
+ ad-hoc tables that are not mapped to persistent classes.</p>
+
+ <p>To associate one or more tables with a view we use the
+ <code>db&nbsp;table</code> pragma (<a href="#14.2.2">Section 14.2.2,
+ "<code>table</code>"</a>). To associate the second and subsequent
+ tables we repeat the <code>db&nbsp;table</code> pragma for each
+ additional table. For example, the following view is based on the
+ <code>employee_extra</code> legacy table we have defined at the
+ beginning of the chapter.</p>
+
+ <pre class="cxx">
+#pragma db view table("employee_extra")
+struct employee_vacation
+{
+ #pragma db column("employee_id") type("INTEGER")
+ unsigned long employee_id;
+
+ #pragma db column("vacation_days") type("INTEGER")
+ unsigned short vacation_days;
+};
+ </pre>
+
+ <p>Besides the table name in the <code>db&nbsp;table</code> pragma
+ we also have to specify the column name for each view data
+ member. Note that unlike for object views, the ODB compiler
+ does not try to automatically come up with column names for
+ table views. Furthermore, we cannot use references to object
+ members either, since there are no associated objects in table
+ views. Instead, the actual column name or column expression
+ must be specified as a string literal. The column name can
+ also be qualified with a table name either in the
+ <code>"table.column"</code> form or, if either a table
+ or a column name contains a period, in the
+ <code>"table"."column"</code> form. The following example
+ illustrates the use of a column expression:</p>
+
+ <pre class="cxx">
+#pragma db view table("employee_extra")
+struct employee_max_vacation
+{
+ #pragma db column("max(vacation_days)") type("INTEGER")
+ unsigned short max_vacation_days;
+};
+ </pre>
+
+ <p>Both the associated table names and the column names can be qualified
+ with a database schema, for example:</p>
+
+ <pre class="cxx">
+#pragma db view table("hr.employee_extra")
+struct employee_max_vacation
+{
+ #pragma db column("hr.employee_extra.vacation_days") type("INTEGER")
+ unsigned short vacation_days;
+};
+ </pre>
+
+ <p>For more information on database schemas and the format of the
+ qualified names, refer to <a href="#14.1.8">Section 14.1.8,
+ "<code>schema</code>"</a>.</p>
+
+ <p>Note also that in the above examples we specified the SQL type
+ for each of the columns to make sure that the ODB compiler
+ has knowledge of the actual types as specified in the database
+ schema. This is required to obtain correct and optimal
+ generated code.</p>
+
+
+ <p>The complete syntax of the <code>db&nbsp;table</code> pragma
+ is similar to the <code>db&nbsp;object</code> pragma and is shown
+ below:</p>
+
+ <p><code><b>table("</b><i>name</i><b>"</b>
+ [<b>=</b> <b>"</b><i>alias</i><b>"</b>]
+ [<i>join-type</i>]
+ [<b>:</b> <i>join-condition</i>]<b>)</b></code></p>
+
+ <p>The <i>name</i> part is a database table name. The optional
+ <i>alias</i> part gives this table an alias. If provided, the
+ alias must be used instead of the table whenever a reference
+ to a table is used. Contexts where such a reference may
+ be needed include the join condition (discussed below),
+ column names, and query expressions. The optional <i>join-type</i>
+ part specifies the way this table is associated. It can
+ be <code>left</code>, <code>right</code>, <code>full</code>,
+ <code>inner</code>, and <code>cross</code> with <code>left</code>
+ being the default. Finally, the optional <i>join-condition</i>
+ part provides the criteria which should be used to associate this
+ table with any of the previously associated tables or, as we will see in
+ <a href="#10.4">Section 10.4, "Mixed Views"</a>, objects. Note that
+ while the first associated table can have an alias, it cannot have
+ a join type or condition.</p>
+
+ <p>Similar to object views, for each subsequent associated table the
+ ODB compiler needs a join condition. However, unlike for object views,
+ for table views the ODB compiler does not try to come up with one
+ automatically. Furthermore, we cannot use references to object
+ members corresponding to object relationships either, since there
+ are no associated objects in table views. Instead, for each
+ subsequent associated table, a join condition must be
+ specified as a custom query expression. While the syntax of the
+ query expression is the same as in the query facility used to query
+ the database for objects (<a href="#4">Chapter 4, "Querying the
+ Database"</a>), a join condition for a table is normally specified
+ as a single string literal containing a native SQL query expression.</p>
+
+ <p>As an example of a multi-table view, consider the
+ <code>employee_health</code> table that we define in addition
+ to <code>employee_extra</code>:</p>
+
+ <pre class="sql">
+CREATE TABLE employee_health(
+ employee_id INTEGER NOT NULL,
+ sick_leave_days INTEGER NOT NULL)
+ </pre>
+
+ <p>Given these two tables we can now define a view that returns both
+ the vacation and sick leave information for each employee:</p>
+
+ <pre class="cxx">
+#pragma db view table("employee_extra" = "extra") \
+ table("employee_health" = "health": \
+ "extra.employee_id = health.employee_id")
+struct employee_leave
+{
+ #pragma db column("extra.employee_id") type("INTEGER")
+ unsigned long employee_id;
+
+ #pragma db column("vacation_days") type("INTEGER")
+ unsigned short vacation_days;
+
+ #pragma db column("sick_leave_days") type("INTEGER")
+ unsigned short sick_leave_days;
+};
+ </pre>
+
+ <p>Querying the database for a table view is the same as for an
+ object view except that we can only use native query expressions.
+ For example:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee_leave> query;
+typedef odb::result&lt;employee_leave> result;
+
+transaction t (db.begin ());
+
+unsigned short v_min = ...
+unsigned short l_min = ...
+
+result r (db.query&lt;employee_leave> (
+ "vacation_days > " + query::_val(v_min) + "AND" +
+ "sick_leave_days > " + query::_val(l_min)));
+
+t.commit ();
+ </pre>
+
+
+ <h2><a name="10.4">10.4 Mixed Views</a></h2>
+
+ <p>A mixed view has both associated objects and tables. As a first
+ example of a mixed view, let us improve <code>employee_vacation</code>
+ from the previous section to return the employee's first
+ and last names instead of the employee id. To achieve this we
+ have to associate both the <code>employee</code> object and
+ the <code>employee_extra</code> table with the view:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ table("employee_extra" = "extra": "extra.employee_id = " + employee::id_)
+struct employee_vacation
+{
+ std::string first;
+ std::string last;
+
+ #pragma db column("extra.vacation_days") type("INTEGER")
+ unsigned short vacation_days;
+};
+ </pre>
+
+ <p>When querying the database for a mixed view, we can use query members
+ for the parts of the query expression that involves object members
+ but have to fall back to using the native syntax for the parts that
+ involve table columns. For example:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;employee_vacation> query;
+typedef odb::result&lt;employee_vacation> result;
+
+transaction t (db.begin ());
+
+result r (db.query&lt;employee_vacation> (
+ (query::last == "Doe") + "AND extra.vacation_days &lt;> 0"));
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ cout &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->vacation_days &lt;&lt; endl;
+
+t.commit ();
+ </pre>
+
+ <p>As another example, consider a more advanced view that associates
+ two objects via a legacy table. This view allows us to find the
+ previous employer name for each employee:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) \
+ table("employee_extra" = "extra": "extra.employee_id = " + employee::id_) \
+ object(employer: "extra.previous_employer_id = " + employer::id_)
+struct employee_prev_employer
+{
+ std::string first;
+ std::string last;
+
+ // If previous_employer_id is NULL, then the name will be NULL as well.
+ // We use the odb::nullable wrapper to handle this.
+ //
+ #pragma db column(employer::name_)
+ odb::nullable&lt;std::string> prev_employer_name;
+};
+ </pre>
+
+ <h2><a name="10.5">10.5 View Query Conditions</a></h2>
+
+ <p>Object, table, and mixed views can also specify an optional query
+ condition that should be used whenever the database is queried for
+ this view. To specify a query condition we use the
+ <code>db&nbsp;query</code> pragma (<a href="#14.2.3">Section 14.2.3,
+ "<code>query</code>"</a>).</p>
+
+ <p>As an example, consider a view that returns some information about
+ all the employees that are over a predefined retirement age.
+ One way to implement this would be to define a standard object
+ view as we have done in the previous sections and then use a
+ query like this:</p>
+
+ <pre class="cxx">
+result r (db.query&lt;employee_retirement> (query::age > 50));
+ </pre>
+
+ <p>The problem with the above approach is that we have to keep
+ repeating the <code>query::age > 50</code> expression every
+ time we execute the query, even though this expression always
+ stays the same. View query conditions allow us to solve this
+ problem. For example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) query(employee::age > 50)
+struct employee_retirement
+{
+ std::string first;
+ std::string last;
+ unsigned short age;
+};
+ </pre>
+
+ <p>With this improvement we can rewrite our query like this:</p>
+
+ <pre class="cxx">
+result r (db.query&lt;employee_retirement> ());
+ </pre>
+
+ <p>But what if we may also need to restrict the result set based on
+ some varying criteria, such as the employee's last name? Or, in other
+ words, we may need to combine a constant query expression specified
+ in the <code>db&nbsp;query</code> pragma with the varying expression
+ specified at the query execution time. To allow this, the
+ <code>db&nbsp;query</code> pragma syntax supports the use of the special
+ <code>(?)</code> placeholder that indicates the position in the
+ constant query expression where the runtime expression should be
+ inserted. For example:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) query(employee::age > 50 &amp;&amp; (?))
+struct employee_retirement
+{
+ std::string first;
+ std::string last;
+ unsigned short name;
+};
+ </pre>
+
+ <p>With this change we can now use additional query criteria in our
+ view:</p>
+
+ <pre class="cxx">
+result r (db.query&lt;employee_retirement> (query::last == "Doe"));
+ </pre>
+
+ <p>The syntax of the expression in a query condition is the same as in
+ the query facility used to query the database for objects
+ (<a href="#4">Chapter 4, "Querying the Database"</a>) except for
+ two differences. Firstly, for query members, instead of
+ using <code>odb::query&lt;object>::member</code> names, we refer
+ directly to object members, using the object alias instead of the
+ object name if an alias was assigned. Secondly, query conditions
+ support the special <code>(?)</code> placeholder which can be used
+ both in the C++-integrated query expressions as was shown above
+ and in native SQL expressions specified as string literals. The
+ following view is an example of the latter case:</p>
+
+ <pre class="cxx">
+#pragma db view table("employee_extra") \
+ query("vacation_days &lt;> 0 AND (?)")
+struct employee_vacation
+{
+ ...
+};
+ </pre>
+
+ <p>Another common use case for query conditions are views with the
+ <code>ORDER BY</code> or <code>GROUP BY</code> clause. Such
+ clauses are normally present in the same form in every query
+ involving such views. As an example, consider an aggregate
+ view which calculate the minimum and maximum ages of employees
+ for each employer:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee) object(employer) \
+ query((?) + "GROUP BY" + employer::name_)
+struct employer_age
+{
+ #pragma db column(employer::name_)
+ std::string employer_name;
+
+ #pragma db column("min(" + employee::age_ + ")")
+ unsigned short min_age;
+
+ #pragma db column("max(" + employee::age_ + ")")
+ unsigned short max_age;
+};
+ </pre>
+
+ <p>The query condition can be optionally followed (or replaced,
+ if no constant query expression is needed) by one or more
+ <em>result modifiers</em>. Currently supported result modifiers
+ are <code>distinct</code> (which is translated to <code>SELECT
+ DISTINCT</code>) and <code>for_update</code> (which is translated
+ to <code>FOR UPDATE</code> or equivalent for database systems
+ that support it). As an example, consider a view that
+ allows us to get some information about employers ordered
+ by the object id and without any duplicates:</p>
+
+ <pre class="cxx">
+#pragma db view object(employer) object(employee) \
+ query((?) + "ORDER BY" + employer::name_, distinct)
+struct employer_info
+{
+ ...
+};
+ </pre>
+
+ <p>If we don't require ordering, then this view can be re-implemented
+ like this:</p>
+
+ <pre class="cxx">
+#pragma db view object(employer) object(employee) query(distinct)
+struct employer_info
+{
+ ...
+};
+ </pre>
+
+ <h2><a name="10.6">10.6 Native Views</a></h2>
+
+ <p>The last kind of view supported by ODB is a native view. Native
+ views are a low-level mechanism for capturing results of native
+ SQL queries, stored procedure calls, etc. Native views don't have
+ associated tables or objects. Instead, we use the
+ <code>db&nbsp;query</code> pragma to specify the native SQL query,
+ which should normally include the select-list and, if applicable,
+ the from-list. For example, here is how we can re-implement the
+ <code>employee_vacation</code> table view from Section 10.3 above
+ as a native view:</p>
+
+ <pre class="cxx">
+#pragma db view query("SELECT employee_id, vacation_days " \
+ "FROM employee_extra")
+struct employee_vacation
+{
+ #pragma db type("INTEGER")
+ unsigned long employee_id;
+
+ #pragma db type("INTEGER")
+ unsigned short vacation_days;
+};
+ </pre>
+
+ <p>In native views the columns in the query select-list are
+ associated with the view data members in the order specified.
+ That is, the first column is stored in the first member, the
+ second column &mdash; in the second member, and so on. The ODB compiler
+ does not perform any error checking in this association. As a result
+ you must make sure that the number and order of columns in the
+ query select-list match the number and order of data members
+ in the view. This is also the reason why we are not
+ required to provide the column name for each data member in native
+ views, as is the case for object and table views.</p>
+
+ <p>Note also that while it is always possible to implement a table
+ view as a native view, the table views must be preferred since
+ they are safer. In a native view, if you add, remove, or
+ rearrange data members without updating the column list in the
+ query, or vice versa, at best, this will result in a runtime
+ error. In contrast, in a table view such changes will result
+ in the query being automatically updated.</p>
+
+ <p>Similar to object and table views, the query specified for
+ a native view can contain the special <code>(?)</code>
+ placeholder which is replaced with the query expression
+ specified at the query execution time.
+ If the native query does not contain a placeholder, as in
+ the example above, then any query expression specified at
+ the query execution time is appended to the query text
+ along with the <code>WHERE</code> keyword, if required.
+ The following example shows the usage of the placeholder:</p>
+
+ <pre class="cxx">
+#pragma db view query("SELECT employee_id, vacation_days " \
+ "FROM employee_extra " \
+ "WHERE vacation_days &lt;> 0 AND (?)")
+struct employee_vacation
+{
+ ...
+};
+ </pre>
+
+ <p>As another example, consider a view that returns the next
+ value of a database sequence:</p>
+
+ <pre class="cxx">
+#pragma db view query("SELECT nextval('my_seq')")
+struct sequence_value
+{
+ unsigned long long value;
+};
+ </pre>
+
+ <p>While this implementation can be acceptable in some cases, it has
+ a number of drawbacks. Firstly, the name of the sequence is
+ fixed in the view, which means if we have a second sequence, we
+ will have to define another, almost identical view. Similarly,
+ the operation that we perform on the sequence is also fixed.
+ In some situations, instead of returning the next value, we may
+ need the last value.</p>
+
+ <p>Note that we cannot use the placeholder mechanism to resolve
+ these problems since placeholders can only be used in the
+ <code>WHERE</code>, <code>GROUP BY</code>, and similar
+ clauses. In other words, the following won't work:</p>
+
+ <pre class="cxx">
+#pragma db view query("SELECT nextval('(?)')")
+struct sequence_value
+{
+ unsigned long long value;
+};
+
+result r (db.query&lt;sequence_value> ("my_seq"));
+ </pre>
+
+ <p>To support these kinds of use cases, ODB allows us to specify the
+ complete query for a native view at runtime rather than at the view
+ definition. To indicate that a native view has a runtime query,
+ we can either specify the empty <code>db&nbsp;query</code>
+ pragma or omit the pragma altogether. For example:</p>
+
+ <pre class="cxx">
+#pragma db view
+struct sequence_value
+{
+ unsigned long long value;
+};
+ </pre>
+
+ <p>Given this view, we can perform the following queries:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;sequence_value> query;
+typedef odb::result&lt;sequence_value> result;
+
+string seq_name = ...
+
+result l (db.query&lt;sequence_value> (
+ "SELECT lastval('" + seq_name + "')"));
+
+result n (db.query&lt;sequence_value> (
+ "SELECT nextval('" + seq_name + "')"));
+ </pre>
+
+ <p>Native views can also be used to call and handle results of
+ stored procedures. The semantics and limitations of stored
+ procedures vary greatly between database systems while some
+ do not support this functionality at all. As a result, support
+ for calling stored procedures using native views is described
+ for each database system in <a href="#II">Part II, "Database
+ Systems"</a>.</p>
+
+ <h2><a name="10.7">10.7 Other View Features and Limitations</a></h2>
+
+ <p>Views cannot be derived from other views. However, you can derive
+ a view from a transient C++ class. View data members cannot be
+ object pointers. If you need to access data from a pointed-to
+ object, then you will need to associate such an object with
+ the view. Similarly, view data members cannot be containers.
+ These two limitations also apply to composite value types that
+ contain object pointers or containers. Such composite values
+ cannot be used as view data members.</p>
+
+ <p>On the other hand, composite values that do not contain object
+ pointers or containers can be used in views. As an example,
+ consider a modified version of the <code>employee</code> persistent
+ class that stores a person's name as a composite value:</p>
+
+ <pre class="cxx">
+#pragma db value
+class person_name
+{
+ std::string first_;
+ std::string last_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ person_name name_;
+
+ ...
+};
+ </pre>
+
+ <p>Given this change, we can re-implement the <code>employee_name</code>
+ view like this:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee)
+struct employee_name
+{
+ person_name name;
+};
+ </pre>
+
+ <p>It is also possible to extract some or all of the nested members
+ of a composite value into individual view data members. Here is
+ how we could have defined the <code>employee_name</code> view
+ if we wanted to keep its original structure:</p>
+
+ <pre class="cxx">
+#pragma db view object(employee)
+struct employee_name
+{
+ #pragma db column(employee::name.first_)
+ std::string first;
+
+ #pragma db column(employee::name.last_)
+ std::string last;
+};
+ </pre>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="11">11 Session</a></h1>
+
+ <p>A session is an application's unit of work that may encompass several
+ database transactions. In this version of ODB a session is just an
+ object cache. In future versions it may provide additional
+ functionality, such as delayed database operations and automatic
+ object state change tracking. As discussed later in
+ <a href="#11.2">Section 11.2, "Custom Sessions"</a>, it is also
+ possible to provide a custom session implementation that provides
+ these or other features.</p>
+
+ <p>Session support is optional and can be enabled or disabled on the
+ per object basis using the <code>db&nbsp;session</code> pragma, for
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object session
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>We can also enable or disable session support for a group of
+ objects at the namespace level:</p>
+
+ <pre class="cxx">
+#pragma db namespace session
+namespace accounting
+{
+ #pragma db object // Session support is enabled.
+ class employee
+ {
+ ...
+ };
+
+ #pragma db object session(false) // Session support is disabled.
+ class employer
+ {
+ ...
+ };
+}
+ </pre>
+
+ <p>Finally, we can pass the <code>--generate-session</code> ODB compiler
+ option to enable session support by default. With this option session
+ support will be enabled for all the persistent classes except those
+ for which it was explicitly disabled using the
+ <code>db&nbsp;session</code>. An alternative to this method with the
+ same effect is to enable session support for the global namespace:</p>
+
+ <pre class="cxx">
+#pragma db namespace() session
+ </pre>
+
+ <p>Each thread of execution in an application can have only one active
+ session at a time. A session is started by creating an instance of
+ the <code>odb::session</code> class and is automatically terminated
+ when this instance is destroyed. You will need to include the
+ <code>&lt;odb/session.hxx></code> header file to make this class
+ available in your application. For example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/database.hxx>
+#include &lt;odb/session.hxx>
+#include &lt;odb/transaction.hxx>
+
+using namespace odb::core;
+
+{
+ session s;
+
+ // First transaction.
+ //
+ {
+ transaction t (db.begin ());
+ ...
+ t.commit ();
+ }
+
+ // Second transaction.
+ //
+ {
+ transaction t (db.begin ());
+ ...
+ t.commit ();
+ }
+
+ // Session 's' is terminated here.
+}
+ </pre>
+
+ <p>The <code>session</code> class has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ class session
+ {
+ public:
+ session (bool make_current = true);
+ ~session ();
+
+ // Copying or assignment of sessions is not supported.
+ //
+ private:
+ session (const session&amp;);
+ session&amp; operator= (const session&amp;);
+
+ // Current session interface.
+ //
+ public:
+ static session&amp;
+ current ();
+
+ static bool
+ has_current ();
+
+ static void
+ current (session&amp;);
+
+ static void
+ reset_current ();
+
+ static session*
+ current_pointer ();
+
+ static void
+ current_pointer (session*);
+
+ // Object cache interface.
+ //
+ public:
+ template &lt;typename T>
+ struct cache_position {...};
+
+ template &lt;typename T>
+ cache_position&lt;T>
+ cache_insert (database&amp;,
+ const object_traits&lt;T>::id_type&amp;,
+ const object_traits&lt;T>::pointer_type&amp;);
+
+ template &lt;typename T>
+ object_traits&lt;T>::pointer_type
+ cache_find (database&amp;, const object_traits&lt;T>::id_type&amp;) const;
+
+ template &lt;typename T>
+ void
+ cache_erase (const cache_position&lt;T>&amp;);
+
+ template &lt;typename T>
+ void
+ cache_erase (database&amp;, const object_traits&lt;T>::id_type&amp;);
+ };
+}
+ </pre>
+
+ <p>The session constructor creates a new session and, if the
+ <code>make_current</code> argument is <code>true</code>, sets it as a
+ current session for this thread. If we try to make a session current
+ while there is already another session in effect for this thread,
+ then the constructor throws the <code>odb::already_in_session</code>
+ exception. The destructor clears the current session for this
+ thread if this session is the current one.</p>
+
+ <p>The static <code>current()</code> accessor returns the currently active
+ session for this thread. If there is no active session, this function
+ throws the <code>odb::not_in_session</code> exception. We can check
+ whether there is a session in effect in this thread using the
+ <code>has_current()</code> static function.</p>
+
+ <p>The static <code>current()</code> modifier allows us to set the
+ current session for this thread. The <code>reset_current()</code>
+ static function clears the current session. These two functions
+ allow for more advanced use cases, such as multiplexing
+ two or more sessions on the same thread.</p>
+
+ <p>The static <code>current_pointer()</code> overloaded functions
+ provided the same functionality but using pointers. Specifically,
+ the <code>current_pointer()</code> accessor can be used to
+ test whether there is a current session and get a pointer to it
+ all with a single call.</p>
+
+ <p>We normally don't use the object cache interface directly. However,
+ it could be useful in some cases, for example, to find out whether
+ an object has already been loaded. Note that when calling
+ <code>cache_insert()</code>, <code>cache_find()</code>, or
+ the second version of <code>cache_erase()</code>, you need to
+ specify the template argument (object type) explicitly. It is
+ also possible to access the underlying cache data structures
+ directly. This can be useful if, for example, you want to
+ iterate over the objects store in the cache. Refer to the ODB
+ runtime header files for more details on this direct access.</p>
+
+ <h2><a name="11.1">11.1 Object Cache</a></h2>
+
+ <p>A session is an object cache. Every time a session-enabled object is
+ made persistent by calling the <code>database::persist()</code> function
+ (<a href="#3.8">Section 3.8, "Making Objects Persistent"</a>), loaded
+ by calling the <code>database::load()</code> or
+ <code>database::find()</code> function (the pointer-returning overloads
+ only; <a href="#3.9">Section 3.9, "Loading Persistent Objects"</a>),
+ or loaded by iterating over a query result (<a href="#4.4">Section 4.4,
+ "Query Result"</a>), the pointer to the persistent object, in the form
+ of the canonical object pointer (<a href="#3.3">Section 3.3, "Object
+ and View Pointers"</a>), is stored in the session. For as long as the
+ session is in effect, any subsequent calls to load the same object will
+ return the cached instance. When an object's state is deleted from the
+ database with the <code>database::erase()</code> function
+ (<a href="#3.11">Section 3.11, "Deleting Persistent Objects"</a>), the
+ cached object pointer is removed from the session. For example:</p>
+
+ <pre class="cxx">
+shared_ptr&lt;person> p (new person ("John", "Doe"));
+
+session s;
+transaction t (db.begin ());
+
+unsigned long id (db.persist (p)); // p is cached in s.
+shared_ptr&lt;person> p1 (db.load&lt;person> (id)); // p1 same as p.
+
+t.commit ();
+ </pre>
+
+
+ <p>The per-object caching policies depend on the object pointer kind
+ (<a href="#6.5">Section 6.5, "Using Custom Smart Pointers"</a>).
+ Objects with a unique pointer, such as <code>std::auto_ptr</code>
+ or <code>std::unique_ptr</code>, as an object pointer are never
+ cached since it is not possible to have two such pointers pointing
+ to the same object. When an object is persisted via a pointer or
+ loaded as a dynamically allocated instance, objects with both raw
+ and shared pointers as object pointers are cached. If an object is
+ persisted as a reference or loaded into a pre-allocated instance,
+ the object is only cached if its object pointer is a raw pointer.</p>
+
+ <p>Also note that when we persist an object as a constant reference
+ or constant pointer, the session caches such an object as
+ unrestricted (non-<code>const</code>). This can lead to undefined
+ behavior if the object being persisted was actually created as
+ <code>const</code> and is later found in the session cache and
+ used as non-<code>const</code>. As a result, when using sessions,
+ it is recommended that all persistent objects be created as
+ non-<code>const</code> instances. The following code fragment
+ illustrates this point:</p>
+
+ <pre class="cxx">
+void save (database&amp; db, shared_ptr&lt;const person> p)
+{
+ transaction t (db.begin ());
+ db.persist (p); // Persisted as const pointer.
+ t.commit ();
+}
+
+session s;
+
+shared_ptr&lt;const person> p1 (new const person ("John", "Doe"));
+unsigned long id1 (save (db, p1)); // p1 is cached in s as non-const.
+
+{
+ transaction t (db.begin ());
+ shared_ptr&lt;person> p (db.load&lt;person> (id1)); // p == p1
+ p->age (30); // Undefined behavior since p1 was created const.
+ t.commit ();
+}
+
+shared_ptr&lt;const person> p2 (new person ("Jane", "Doe"));
+unsigned long id2 (save (db, p2)); // p2 is cached in s as non-const.
+
+{
+ transaction t (db.begin ());
+ shared_ptr&lt;person> p (db.load&lt;person> (id2)); // p == p2
+ p->age (30); // Ok, since p2 was not created const.
+ t.commit ();
+}
+ </pre>
+
+ <h2><a name="11.2">11.2 Custom Sessions</a></h2>
+
+ <p>ODB can use a custom session implementation instead of the
+ default <code>odb::session</code>. There could be multiple
+ reasons for an application to provide its own session. For
+ example, the application may already include a notion of an
+ object cache or registry which ODB can re-use. A custom
+ session can also provide additional functionality, such as
+ automatic change tracking, delayed database operations, or
+ object eviction. Finally, the session-per-thread approach used
+ by <code>odb::session</code> may not be suitable for all
+ applications. For instance, some may need a thread-safe
+ session that can be shared among multiple threads. For
+ an example of a custom session that implements automatic
+ change tracking by keeping original copies of the objects,
+ refer to the <code>common/session/custom</code> test
+ in the <code>odb-tests</code> package.</p>
+
+ <p>To use a custom session we need to specify its type with
+ the <code>--session-type</code> ODB compiler command line
+ option. We also need to include its definition into the
+ generated header file. This can be achieved with the
+ <code>--hxx-prologue</code> option. For example, if our
+ custom session is called <code>app::session</code> and
+ is defined in the <code>app/session.hxx</code> header
+ file, then the corresponding ODB compiler options would
+ look like this:</p>
+
+ <pre class="terminal">
+odb --hxx-prologue "#include \"app/session.hxx\"" \
+--session-type ::app::session ...
+ </pre>
+
+ <p>A custom session should provide the following interface:</p>
+
+ <pre class="cxx">
+class custom_session
+{
+public:
+ static bool
+ _has_cache ();
+
+ // Cache management functions.
+ //
+ template &lt;typename T>
+ struct cache_position
+ {
+ ...
+ };
+
+ template &lt;typename T>
+ static cache_position&lt;T>
+ _cache_insert (odb::database&amp;,
+ const typename odb::object_traits&lt;T>::id_type&amp;,
+ const typename odb::object_traits&lt;T>::pointer_type&amp;);
+
+ template &lt;typename T>
+ static typename odb::object_traits&lt;T>::pointer_type
+ _cache_find (odb::database&amp;,
+ const typename odb::object_traits&lt;T>::id_type&amp;);
+
+ template &lt;typename T>
+ static void
+ _cache_erase (const cache_position&lt;T>&amp;);
+
+ // Notification functions.
+ //
+ template &lt;typename T>
+ static void
+ _cache_persist (const cache_position&lt;T>&amp;);
+
+ template &lt;typename T>
+ static void
+ _cache_load (const cache_position&lt;T>&amp;);
+
+ template &lt;typename T>
+ static void
+ _cache_update (odb::database&amp;, const T&amp; obj);
+
+ template &lt;typename T>
+ static void
+ _cache_erase (odb::database&amp;,
+ const typename odb::object_traits&lt;T>::id_type&amp;);
+};
+ </pre>
+
+ <p>The <code>_has_cache()</code> function shall return <code>true</code>
+ if the object cache is in effect in the current thread.</p>
+
+ <p>The <code>cache_position</code> class template represents a position
+ in the cache of the inserted object. It should be default and
+ copy-constructible as well as copy-assignable. The default
+ constructor shall create a special empty/<code>NULL</code>
+ position. A call of any of the cache management or notification
+ functions with such an empty/<code>NULL</code> position shall be
+ ignored.</p>
+
+ <p>The <code>_cache_insert()</code> function shall add the object into
+ the object cache and return its position. The <code>_cache_find()</code>
+ function looks an object up in the object cache given its id.
+ It returns a <code>NULL</code> pointer if the object is not
+ found. The <code>_cache_erase()</code> cache management function
+ shall remove the object from the cache. It is called
+ if the database operation that caused the object to be inserted
+ (for example, load) failed. Note also that after insertion the object
+ state is undefined. You can only access the object state
+ (for example, make a copy or clear a flag) from one of the
+ notification functions discussed below.</p>
+
+ <p>The notification functions are called after an object has
+ been persisted, loaded, updated, or erased, respectively. If
+ your session implementation does not need some of the
+ notifications, you still have to provide their functions,
+ however, you can leave their implementations empty.</p>
+
+ <p>Notice also that all the cache management and notification
+ functions are static. This is done in order to allow for a
+ custom notion of a current session. Normally, the first
+ step a non-empty implementation will perform is lookup the
+ current session.</p>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="12">12 Optimistic Concurrency</a></h1>
+
+ <p>The ODB transaction model (<a href="#3.5">Section 3.5,
+ "Transactions"</a>) guarantees consistency as long as we perform all the
+ database operations corresponding to a specific application transaction
+ in a single database transaction. That is, if we load an object within a
+ database transaction and update it in the same transaction, then we are
+ guaranteed that the object state that we are updating in the database is
+ exactly the same as the state we have loaded. In other words, it is
+ impossible for another process or thread to modify the object state
+ in the database between these load and update operations.</p>
+
+ <p>In this chapter we use the term <em>application transaction</em>
+ to refer to a set of operations on persistent objects that an
+ application needs to perform in order to implement some
+ application-specific functionality. The term <em>database
+ transaction</em> refers to the set of database operations
+ performed between the ODB <code>begin()</code> and <code>commit()</code>
+ calls. Up until now we have treated application transactions and
+ database transactions as essentially the same thing.</p>
+
+ <p>While this model is easy to understand and straightforward to use,
+ it may not be suitable for applications that have long application
+ transactions. The canonical example of such a situation is an
+ application transaction that requires user input between loading
+ an object and updating it. Such an operation may take an arbitrary
+ long time to complete and performing it within a single database
+ transaction will consume database resources as well as prevent
+ other processes/threads from updating the object for too long.</p>
+
+ <p>The solution to this problem is to break up the long-lived
+ application transaction into several short-lived database
+ transactions. In our example that would mean loading the object
+ in one database transaction, waiting for user input, and then
+ updating the object in another database transaction. For example:</p>
+
+ <pre class="cxx">
+unsigned long id = ...;
+person p;
+
+{
+ transaction t (db.begin ());
+ db.load (id, p);
+ t.commit ();
+}
+
+cerr &lt;&lt; "enter age for " &lt;&lt; p.first () &lt;&lt; " " &lt;&lt; p.last () &lt;&lt; endl;
+unsigned short age;
+cin >> age;
+p.age (age);
+
+{
+ transaction t (db.begin ());
+ db.update (p);
+ t.commit ();
+}
+ </pre>
+
+ <p>This approach works well if we only have one process/thread that can ever
+ update the object. However, if we have multiple processes/threads
+ modifying the same object, then this approach does not guarantee
+ consistency anymore. Consider what happens in the above example if
+ another process updates the person's last name while we are waiting for
+ the user input. Since we loaded the object before this change occured,
+ our version of the person's data will still have the old name. Once we
+ receive the input from the user, we go ahead and update the object,
+ overwriting both the old age with the new one (correct) and the new name
+ with the old one (incorrect).</p>
+
+ <p>While there is no way to restore the consistency guarantee in
+ an application transaction that consists of multiple database
+ transactions, ODB provides a mechanism, called optimistic
+ concurrency, that allows applications to detect and potentially
+ recover from such inconsistencies.</p>
+
+ <p>In essence, the optimistic concurrency model detects mismatches
+ between the current object state in the database and the state
+ when it was loaded into the application memory. Such a mismatch
+ would mean that the object was changed by another process or
+ thread. There are several ways to implement such state mismatch
+ detection. Currently, ODB uses object versioning while other
+ methods, such as timestamps, may be supported in the future.</p>
+
+ <p>To declare a persistent class with the optimistic concurrency model we
+ use the <code>optimistic</code> pragma (<a href="#14.1.5">Section 14.1.5,
+ "<code>optimistic</code>"</a>). We also use the <code>version</code>
+ pragma (<a href="#14.4.16">Section 14.4.16, "<code>version</code>"</a>)
+ to specify which data member will store the object version. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object optimistic
+class person
+{
+ ...
+
+ #pragma db version
+ unsigned long version_;
+};
+ </pre>
+
+ <p>The version data member is managed by ODB. It is initialized to
+ <code>1</code> when the object is made persistent and incremented
+ by <code>1</code> with each update. The <code>0</code> version value
+ is not used by ODB and the application can use it as a special value,
+ for example, to indicate that the object is transient. Note that
+ for optimistic concurrency to function properly, the application
+ should not modify the version member after making the object persistent
+ or loading it from the database and until deleting the state of this
+ object from the database. To avoid any accidental modifications
+ to the version member, we can declare it <code>const</code>, for
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object optimistic
+class person
+{
+ ...
+
+ #pragma db version
+ const unsigned long version_;
+};
+ </pre>
+
+ <p>When we call the <code>database::update()</code> function
+ (<a href="#3.10">Section 3.10, "Updating Persistent Objects"</a>) and pass
+ an object that has an outdated state, the <code>odb::object_changed</code>
+ exception is thrown. At this point the application has two
+ recovery options: it can abort and potentially restart the
+ application transaction or it can reload the new object
+ state from the database, re-apply or merge the changes, and call
+ <code>update()</code> again. Note that aborting an application
+ transaction that performs updates in multiple database transactions
+ may require reverting changes that have already been committed to
+ the database. As a result, this strategy works best if all the
+ updates are performed in the last database transaction of the
+ application transaction. This way the changes can be reverted
+ by simply rolling back this last database transaction.</p>
+
+ <p>The following example shows how we can reimplement the above
+ transaction using the second recovery option:</p>
+
+ <pre class="cxx">
+unsigned long id = ...;
+person p;
+
+{
+ transaction t (db.begin ());
+ db.load (id, p);
+ t.commit ();
+}
+
+cerr &lt;&lt; "enter age for " &lt;&lt; p.first () &lt;&lt; " " &lt;&lt; p.last () &lt;&lt; endl;
+unsigned short age;
+cin >> age;
+p.age (age);
+
+{
+ transaction t (db.begin ());
+
+ try
+ {
+ db.update (p);
+ }
+ catch (const object_changed&amp;)
+ {
+ db.reload (p);
+ p.age (age);
+ db.update (p);
+ }
+
+ t.commit ();
+}
+ </pre>
+
+ <p>An important point to note in the above code fragment is that the second
+ <code>update()</code> call cannot throw the <code>object_changed</code>
+ exception because we are reloading the state of the object
+ and updating it within the same database transaction.</p>
+
+ <p>Depending on the recovery strategy employed by the application,
+ an application transaction with a failed update can be significantly
+ more expensive than a successful one. As a result, optimistic
+ concurrency works best for situations with low to medium contention
+ levels where the majority of the application transactions complete
+ without update conflicts. This is also the reason why this concurrency
+ model is called optimistic.</p>
+
+ <p>In addition to updates, ODB also performs state mismatch detection
+ when we are deleting an object from the database
+ (<a href="#3.11">Section 3.11, "Deleting Persistent Objects"</a>).
+ To understand why this can be important, consider the following
+ application transaction:</p>
+
+ <pre class="cxx">
+unsigned long id = ...;
+person p;
+
+{
+ transaction t (db.begin ());
+ db.load (id, p);
+ t.commit ();
+}
+
+string answer;
+cerr &lt;&lt; "age is " &lt;&lt; p.age () &lt;&lt; ", delete?" &lt;&lt; endl;
+getline (cin, answer);
+
+if (answer == "yes")
+{
+ transaction t (db.begin ());
+ db.erase (p);
+ t.commit ();
+}
+ </pre>
+
+ <p>Consider again what happens if another process or thread updates
+ the object by changing the person's age while we are waiting for
+ the user input. In this case, the user makes the decision based on
+ a certain age while we may delete (or not delete) an object that has
+ a completely different age. Here is how we can fix this problem
+ using optimistic concurrency:</p>
+
+ <pre class="cxx">
+unsigned long id = ...;
+person p;
+
+{
+ transaction t (db.begin ());
+ db.load (id, p);
+ t.commit ();
+}
+
+string answer;
+for (bool done (false); !done; )
+{
+ if (answer.empty ())
+ cerr &lt;&lt; "age is " &lt;&lt; p.age () &lt;&lt; ", delete?" &lt;&lt; endl;
+ else
+ cerr &lt;&lt; "age changed to " &lt;&lt; p.age () &lt;&lt; ", still delete?" &lt;&lt; endl;
+
+ getline (cin, answer);
+
+ if (answer == "yes")
+ {
+ transaction t (db.begin ());
+
+ try
+ {
+ db.erase (p);
+ done = true;
+ }
+ catch (const object_changed&amp;)
+ {
+ db.reload (p);
+ }
+
+ t.commit ();
+ }
+ else
+ done = true;
+}
+ </pre>
+
+ <p>Note that state mismatch detection is performed only if we delete
+ an object by passing the object instance to the <code>erase()</code>
+ function. If we want to delete an object with the optimistic concurrency
+ model regardless of its state, then we need to use the <code>erase()</code>
+ function that deletes an object given its id, for example:</p>
+
+ <pre class="cxx">
+{
+ transaction t (db.begin ());
+ db.erase (p.id ());
+ t.commit ();
+}
+ </pre>
+
+ <p>Finally, note that for persistent classes with the optimistic concurrency
+ model both the <code>update()</code> function as well as the
+ <code>erase()</code> function that accepts an object instance as its
+ argument no longer throw the <code>object_not_persistent</code>
+ exception if there is no such object in the database. Instead,
+ this condition is treated as a change of object state and the
+ <code>object_changed</code> exception is thrown instead.</p>
+
+ <p>For complete sample code that shows how to use optimistic
+ concurrency, refer to the <code>optimistic</code> example in
+ the <code>odb-examples</code> package.</p>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="13">13 Database Schema Evolution</a></h1>
+
+ <p>When we add new persistent classes or change the existing ones, for
+ example, by adding or deleting data members, the database schema
+ necessary to store the new object model changes as well. At the
+ same time, we may have existing databases that contain existing data.
+ If new versions of your application don't need to handle
+ old databases, then the schema creating functionality is all that
+ you need. However, most applications will need to work with data
+ stored by older versions of the same application.</p>
+
+ <p>We will call <em>database schema evolution</em> the overall task
+ of updating the database to match the changes in the object model.
+ Schema evolution usually consists of two sub-tasks: <em>schema
+ migration</em> and <em>data migration</em>. Schema migration
+ modifies the database schema to correspond to the current
+ object model. In a relational database, this, for example, could
+ require adding or dropping tables and columns. The data migration
+ task involves converting the data stored in the existing database
+ from the old format to the new one.</p>
+
+ <p>If performed manually, database schema evolution is a tedious and
+ error-prone task. As a result, ODB provides comprehensive support
+ for automated or, more precisely, semi-automated schema
+ evolution. Specifically, ODB does fully-automatic schema
+ migration and provides facilities to help you with data
+ migration.</p>
+
+ <p>The topic of schema evolution is a complex and sensitive
+ issue since normally there would be valuable, production data at
+ stake. As a result, the approach taken by ODB is to provide simple
+ and bullet-proof elementary building blocks (or migration steps)
+ that we can understand and trust. Using these elementary blocks we
+ can then implement more complex migration scenarios. In particular,
+ ODB does not try to handle data migration automatically since in most
+ cases this requires understanding of application-specific semantics.
+ In other words, there is no magic.</p>
+
+ <p>There are two general approaches to working with older data: the
+ application can either convert it to correspond to the new format
+ or it can be made capable of working with multiple versions of this
+ format. There is also a hybrid approach where the application
+ may convert the data to the new format gradually as part of its
+ normal functionality. ODB is capable of handling all these
+ scenarios. That is, there is support for working with older
+ models without performing any migration (schema or data).
+ Alternatively, we can migrate the schema after
+ which we have the choice of either also immediately migrating the
+ data (<em>immediate data migration</em>) or doing it gradually
+ (<em>gradual data migration</em>).</p>
+
+ <p>Schema evolution is already a complex task and we should not
+ unnecessarily use a more complex approach where a simpler one
+ would be sufficient. From the above, the simplest approach is
+ the immediate schema migration that does not require any data
+ migration. An example of such a change would be adding a new
+ data member with the default value (<a href="#14.3.4">Section
+ 14.3.4, "<code>default</code>"</a>). This case ODB can handle
+ completely automatically.</p>
+
+ <p>If we do require data migration, then the next simplest approach
+ is the immediate schema and data migration. Here we have to write
+ custom migration code. However, it is separate from the rest of
+ the core application logic and is executed at a well defined point
+ (database migration). In other words, the core application logic
+ need not be aware of older model versions. The potential drawback
+ of this approach is performance. It may take a lot of resources
+ and/or time to convert all the data upfront.</p>
+
+ <p>If the immediate migration is not possible, then the next option
+ is the immediate schema migration followed by the gradual data
+ migration. With this approach, both old and new data must co-exist
+ in the new database. We also have to change the application
+ logic to both account for different sources of the same data (for
+ example, when either an old or new version of the object is loaded)
+ as well as migrate the data when appropriate (for example, when
+ the old version of the object is updated). At some point, usually
+ when the majority of the data has been converted, gradual migrations
+ are terminated with an immediate migration.</p>
+
+ <p>The most complex approach is working with multiple versions of
+ the database without performing any migrations, schema or data.
+ ODB does provide support for implementing this approach
+ (<a href="#13.4">Section 13.4, "Soft Object Model Changes"</a>),
+ however we will not cover it any further in this chapter.
+ Generally, this will require embedding knowledge about each
+ version into the core application logic which makes it hard
+ to maintain for any non-trivial object model.</p>
+
+ <p>Note also that when it comes to data migration, we can use
+ the immediate variant for some changes and gradual for others.
+ We will discuss various migration scenarios in greater detail
+ in section <a href="#13.3">Section 13.3, "Data Migration"</a>.</p>
+
+ <h2><a name="13.1">13.1 Object Model Version and Changelog</a></h2>
+
+ <p>To enable schema evolution support in ODB we need to specify
+ the object model version, or, more precisely, two versions.
+ The first is the base model version. It is the lowest
+ version from which we will be able to migrate. The second
+ version is the current model version. In ODB we can migrate
+ from multiple previous versions by successively migrating
+ from one to the next until we reach the current version.
+ We use the <code>db&nbsp;model&nbsp;version</code> pragma
+ to specify both the base and current versions.</p>
+
+ <p>When we enable schema evolution for the first time, our
+ base and current versions will be the same, for example:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 1)
+ </pre>
+
+ <p>Once we release our application, its users may create databases
+ with the schema corresponding to this version of the object
+ model. This means that if we make any modifications to our
+ object model that also change the schema, then we will need
+ to be able to migrate the old databases to this new schema.
+ As a result, before making any new changes after a release,
+ we increment the current version, for example:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 2)
+ </pre>
+
+ <p>To put this another way, we can stay on the same version
+ during development and keep adding new changes to it. But
+ once we release it, any new changes to the object model will
+ have to be done in a new version.</p>
+
+ <p>It is easy to forget to increment the version before
+ making new changes to the object model. To help solve this
+ problem, the <code>db&nbsp;model&nbsp;version</code> pragma
+ accepts a third optional argument that specify whether the
+ current version is open or closed for changes. For example:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 2, open) // Can add new changes to
+ // version 2.
+ </pre>
+
+ <pre class="cxx">
+#pragma db model version(1, 2, closed) // Can no longer add new
+ // changes to version 2.
+ </pre>
+
+ <p>If the current version is closed, ODB will refuse to accept
+ any new schema changes. In this situation you would
+ normally increment the current version and mark it as open
+ or you could re-open the existing version if, for example,
+ you need to fix something. Note, however, that re-opening
+ versions that have been released will most likely result
+ in migration malfunctions. By default the version is open.</p>
+
+ <p>Normally, an application will have a range of older database
+ versions from which it is able to migrate. When we change
+ this range by removing support for older versions, we also
+ need to adjust the base model version. This will make sure
+ that ODB does not keep unnecessary information around.</p>
+
+ <p>A model version (both base and current) is a 64-bit unsigned
+ integer (<code>unsigned&nbsp;long&nbsp;long</code>). <code>0</code>
+ is reserved to signify special situations, such as the lack of
+ schema in the database. Other than that, we can use any values
+ as versions as long as they are monotonically increasing. In
+ particular, we don't have to start with version <code>1</code>
+ and can increase the versions by any increment.</p>
+
+ <p>One versioning approach is to use an independent
+ object model version by starting from version <code>1</code>
+ and also incrementing by <code>1</code>. The alternative
+ is to make the model version correspond to the application
+ version. For example, if our application is using the
+ <code>X.Y.Z</code> version format, then we could encode it
+ as a hexadecimal number and use that as our model version,
+ for example:</p>
+
+ <pre class="cxx">
+#pragma db model version(0x020000, 0x020306) // 2.0.0-2.3.6
+ </pre>
+
+ <p>Most real-world object models will be spread over multiple
+ header files and it will be burdensome to repeat the
+ <code>db&nbsp;model&nbsp;version</code> pragma in each of
+ them. The recommended way to handle this situation is to
+ place the <code>version</code> pragma into a separate header
+ file and include it into the object model files. If your
+ project already has a header file that defines the
+ application version, then it is natural to place this
+ pragma there. For example:</p>
+
+ <pre class="cxx">
+// version.hxx
+//
+// Define the application version.
+//
+
+#define MYAPP_VERSION 0x020306 // 2.3.6
+
+#ifdef ODB_COMPILER
+#pragma db model version(1, 7)
+#endif
+ </pre>
+
+ <p>Note that we can also use macros in the <code>version</code>
+ pragma which allows us to specify all the versions in a single
+ place. For example:</p>
+
+ <pre class="cxx">
+#define MYAPP_VERSION 0x020306 // 2.3.6
+#define MYAPP_BASE_VERSION 0x020000 // 2.0.0
+
+#ifdef ODB_COMPILER
+#pragma db model version(MYAPP_BASE_VERSION, MYAPP_VERSION)
+#endif
+ </pre>
+
+ <p>It is also possible to have multiple object models within the
+ same application that have different versions. Such models
+ must be independent, that is, no headers from one model shall
+ include a header from another. You will also need to assign
+ different schema names to each model with the
+ <code>--schema-name</code> ODB compiler option.</p>
+
+ <p>Once we specify the object model version, the ODB compiler
+ starts tracking database schema changes in a changelog file.
+ Changelog has an XML-based, line-oriented format. It uses
+ XML in order to provide human readability while also
+ facilitating, if desired, processing and analysis with
+ custom tools. The line orientation makes it easy to review
+ with tools like <code>diff</code>.</p>
+
+ <p>The changelog is maintained by the ODB compiler. Specifically,
+ you do not need to make any manual changes to this file. You
+ will, however, need to keep it around from one invocation of
+ the ODB compiler to the next. In other words, the changelog
+ file is both the input and the output of the ODB compiler. This,
+ for example, means that if your project's source code is stored
+ in a version control repository, then you will most likely want
+ to store the changelog there as well. If you delete the changelog,
+ then any ability to do schema migration will be lost.</p>
+
+ <p>The only operation that you may want to perform with the
+ changelog is to review the database schema changes that resulted
+ from the C++ object model changes. For this you can use a tool
+ like <code>diff</code> or, better yet, the change review facilities
+ offered by your revision control system. For this purpose the
+ contents of a changelog will be self-explanatory.</p>
+
+ <p>As an example, consider the following initial object model:</p>
+
+ <pre class="cxx">
+// person.hxx
+//
+
+#include &lt;string>
+
+#pragma db model version(1, 1)
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id_;
+
+ std::string first_;
+ std::string last_;
+};
+ </pre>
+
+ <p>We then compile this header file with the ODB compiler (using the
+ PostgreSQL database as an example):</p>
+
+ <pre class="terminal">
+odb --database pgsql --generate-schema person.hxx
+ </pre>
+
+ <p>If we now look at the list of generated files, then in addition to
+ the now familiar <code>person-odb.?xx</code> and <code>person.sql</code>,
+ we will also see <code>person.xml</code> &mdash; the changelog file.
+ Just for illustration, below are the contents of this changelog.</p>
+
+ <pre class="xml">
+&lt;changelog database="pgsql">
+ &lt;model version="1">
+ &lt;table name="person" kind="object">
+ &lt;column name="id" type="BIGINT" null="false"/>
+ &lt;column name="first" type="TEXT" null="false"/>
+ &lt;column name="last" type="TEXT" null="false"/>
+ &lt;primary-key auto="true">
+ &lt;column name="id"/>
+ &lt;/primary-key>
+ &lt;/table>
+ &lt;/model>
+&lt;/changelog>
+ </pre>
+
+ <p>Let's say we now would like to add another data member to the
+ <code>person</code> class &mdash; the middle name. We increment
+ the version and make the change:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 2)
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id_;
+
+ std::string first_;
+ std::string middle_;
+ std::string last_;
+};
+ </pre>
+
+ <p>We use exactly the same command line to re-compile our file:</p>
+
+ <pre class="terminal">
+odb --database pgsql --generate-schema person.hxx
+ </pre>
+
+ <p>This time the ODB compiler will read the old changelog, update
+ it, and write out the new version. Again, for illustration only,
+ below are the updated changelog contents:</p>
+
+ <pre class="xml">
+&lt;changelog database="pgsql">
+ &lt;changeset version="2">
+ &lt;alter-table name="person">
+ &lt;add-column name="middle" type="TEXT" null="false"/>
+ &lt;/alter-table>
+ &lt;/changeset>
+
+ &lt;model version="1">
+ &lt;table name="person" kind="object">
+ &lt;column name="id" type="BIGINT" null="false"/>
+ &lt;column name="first" type="TEXT" null="false"/>
+ &lt;column name="last" type="TEXT" null="false"/>
+ &lt;primary-key auto="true">
+ &lt;column name="id"/>
+ &lt;/primary-key>
+ &lt;/table>
+ &lt;/model>
+&lt;/changelog>
+ </pre>
+
+ <p>Just to reiterate, while the changelog may look like it could
+ be written by hand, it is maintained completely automatically
+ by the ODB compiler and the only reason you may want to look
+ at its contents is to review the database schema changes. For
+ example, if we compare the above two changelogs with
+ <code>diff</code>, we will get the following summary of the
+ database schema changes:</p>
+
+ <pre class="xml">
+--- person.xml.orig
++++ person.xml
+@@ -1,4 +1,10 @@
+&lt;changelog database="pgsql">
+<span style="color: #009E00">+ &lt;changeset version="2">
++ &lt;alter-table name="person">
++ &lt;add-column name="middle" type="TEXT" null="false"/>
++ &lt;/alter-table>
++ &lt;/changeset>
++</span>
+ &lt;model version="1">
+ &lt;table name="person" kind="object">
+ &lt;column name="id" type="BIGINT" null="false"/>
+ </pre>
+
+ <p>The changelog is only written when we generate the database schema,
+ that is, the <code>--generate-schema</code> option is specified.
+ Invocations of the ODB compiler that only produce the database
+ support code (C++) do not read or update the changelog. To put it
+ another way, the changelog tracks changes in the resulting database
+ schema, not the C++ object model.</p>
+
+ <p>ODB ignores column order when comparing database schemas. This means
+ that we can re-order data members in a class without causing any
+ schema changes. Member renames, however, will result in schema
+ changes since the column name changes as well (unless we specified
+ the column name explicitly). From ODB's perspective such a rename
+ looks like the deletion of one data member and the addition of
+ another. If we don't want this to be treated as a schema change,
+ then we will need to keep the old column name by explicitly
+ specifying it with the <code>db&nbsp;column</code> pragma. For
+ example, here is how we can rename <code>middle_</code> to
+ <code>middle_name_</code> without causing any schema changes:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 2)
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db column("middle") // Keep the original column name.
+ std::string middle_name_;
+
+ ...
+};
+ </pre>
+
+ <p>If your object model consists of a large number of header files and
+ you generate the database schema for each of them individually, then
+ a changelog will be created for each of your header files. This may
+ be what you want, however, the large number of changelogs can quickly
+ become unwieldy. In fact, if you are generating the database schema
+ as standalone SQL files, then you may have already experienced a
+ similar problem caused by a large number of <code>.sql</code> files,
+ one for each header.</p>
+
+ <p>The solution to both of these problems is to generate a combined
+ database schema file and a single changelog. For example, assume
+ we have three header files in our object model:
+ <code>person.hxx</code>, <code>employee.hxx</code>, and
+ <code>employer.hxx</code>. To generate the database support code
+ we compile them as usual but without specifying the
+ <code>--generate-schema</code> option. In this case no changelog
+ is created or updated:</p>
+
+ <pre class="terminal">
+odb --database pgsql person.hxx
+odb --database pgsql employee.hxx
+odb --database pgsql employer.hxx
+ </pre>
+
+ <p>To generate the database schema, we perform a separate invocation
+ of the ODB compiler. This time, however, we instruct it to only
+ generate the schema (<code>--generate-schema-only</code>) and
+ produce it combined (<code>--at-once</code>) for all the files
+ in our object model:</p>
+
+ <pre class="terminal">
+odb --database pgsql --generate-schema-only --at-once \
+--input-name company person.hxx employee.hxx employer.hxx
+ </pre>
+
+ <p>The result of the above command is a single <code>company.sql</code>
+ file (the name is derived from the <code>--input-name</code> value)
+ that contains the database schema for our entire object model. There
+ is also a single corresponding changelog file &mdash;
+ <code>company.xml</code>.</p>
+
+ <p>The same can be achieved for the embedded schema by instructing
+ the ODB compiler to generate the database creation code into a
+ separate C++ file (<code>--schema-format&nbsp;separate</code>):</p>
+
+ <pre class="terminal">
+odb --database pgsql --generate-schema-only --schema-format separate \
+--at-once --input-name company person.hxx employee.hxx employer.hxx
+ </pre>
+
+ <p>The result of this command is a single <code>company-schema.cxx</code>
+ file and, again, <code>company.xml</code>.</p>
+
+ <p>Note also that by default the changelog file is not placed into
+ the directory specified with the <code>--output-dir</code> option.
+ This is due to the changelog being both an input and an output file
+ at the same time. As a result, by default, the ODB compiler will
+ place it in the directory of the input header file.</p>
+
+ <p>There is, however, a number of command line options (including
+ <code>--changelog-dir</code>) that allow us to fine-tune the name and
+ location of the changelog file. For example, you can instruct the ODB
+ compiler to read the changelog from one file while writing it to
+ another. This, for example, can be useful if you want to review
+ the changes before discarding the old file. For more information
+ on these options, refer to the
+ <a href="http://www.codesynthesis.com/products/odb/doc/odb.xhtml">ODB
+ Compiler Command Line Manual</a> and search for "changelog".</p>
+
+ <p>When we were discussing version increments above, we used the
+ terms <em>development</em> and <em>release</em>. Specifically,
+ we talked about keeping the same object model versions during
+ development periods and incrementing them after releases.
+ What is a development period and a release in this context?
+ These definitions can vary from project to project.
+ Generally, during a development period we work on one or
+ more changes to the object model that result in the changes
+ to the database schema. A release is a point where we
+ make our changes available to someone else who may have an
+ older database to migrate from. In the traditional sense, a release
+ is a point where you make a new version of your application available
+ to its users. However, for schema evolution purposes, a release
+ could also mean simply making your schema-altering changes
+ available to other developers on your team. Let us consider
+ two common scenarios to illustrate how all this fits together.</p>
+
+ <p>One way to setup a project would be to re-use the application
+ development period and application release for schema evolution.
+ That is, during a new application version development we keep
+ a single object model version and when we release the application,
+ we increment the model version. In this case it makes sense to
+ also reuse the application version as a model version for
+ consistency. Here is a step-by-step guide for this setup:</p>
+
+ <ol>
+ <li>During development, keep the current object model version open.</li>
+
+ <li>Before the release (for example, when entering a "feature freeze")
+ close the version.</li>
+
+ <li>After the release, update the version and open it.</li>
+
+ <li>For each new feature, review the changeset at the top of the
+ changelog, for example, with <code>diff</code> or your
+ version control facilities. If you are using a version
+ control, then this is best done just before committing
+ your changes to the repository.</li>
+ </ol>
+
+ <p>An alternative way to setup schema versioning in a project would
+ be to define the development period as working on a single
+ feature and the release as making this feature available to
+ other people (developers, testers, etc.) on your team, for
+ example, by committing the changes to a public version control
+ repository. In this case, the object model version will be
+ independent of the application version and can simply be
+ a sequence that starts with <code>1</code> and is
+ incremented by <code>1</code>. Here is a step-by-step guide
+ for this setup:</p>
+
+ <ol>
+ <li>Keep the current model version closed. Once a change is made
+ that affects the database schema, the ODB compiler will refuse
+ to update the changelog.</li>
+
+ <li>If the change is legitimate, open a new version, that is,
+ increment the current version and make it open.</li>
+
+ <li>Once the feature is implemented and tested, review the final
+ set of database changes (with <code>diff</code> or your
+ version control facilities), close the version, and commit
+ the changes to the version control repository (if using).</li>
+ </ol>
+
+ <p>If you are using a version control repository that supports
+ pre-commit checks, then you may want to consider adding such
+ a check to make sure the committed version is always closed.</p>
+
+ <p>If we are just starting schema evolution in our project, which
+ approach should we choose? The two approaches will work better
+ in different situations since they have a different set of
+ advantages and disadvantages. The first approach, which we
+ can call version per application release, is best suited
+ for simpler projects with smaller releases since otherwise
+ a single migration will bundle a large number of unrelated
+ actions corresponding to different features. This can
+ become difficult to review and, if things go wrong, debug.</p>
+
+ <p>The second approach, which we can call version per feature,
+ is much more modular and provides a number of additional benefits.
+ We can perform migrations for each feature as a discreet step
+ which makes it easier to debug. We can also place each such
+ migration step into a separate transaction further improving
+ reliability. It also scales much better in larger teams
+ where multiple developers can work concurrently on features
+ that affect the database schema. For example, if you find
+ yourself in a situation where another developer on your
+ team used the same version as you and managed to commit his
+ changes before you (that is, you have a merge conflict),
+ then you can simply change the version to the next available
+ one, regenerate the changelog, and continue with your commit.</p>
+
+ <p>Overall, unless you have strong reasons to prefer the version
+ per application release approach, rather choose version per
+ feature even though it may seem more complex at the
+ beginning. Also, if you do select the first approach, consider
+ provisioning for switching to the second method by reserving
+ a sub-version number. For example, for an application version
+ in the form <code>2.3.4</code> you can make the object model
+ version to be in the form <code>0x0203040000</code>, reserving
+ the last two bytes for a sub-version. Later on you can use it to
+ switch to the version per feature approach.</p>
+
+ <h2><a name="13.2">13.2 Schema Migration</a></h2>
+
+ <p>Once we enable schema evolution by specifying the object model
+ version, in addition to the schema creation statements, the
+ ODB compiler starts generating schema migration statements
+ for each version all the way from the base to the current.
+ As with schema creation, schema migration can be generated
+ either as a set of SQL files or embedded into the generated
+ C++ code (<code>--schema-format</code> option).</p>
+
+ <p>For each migration step, that is from one version to the next,
+ ODB generates two sets of statements: pre-migration and
+ post-migration. The pre-migration statements <em>"relax"</em>
+ the database schema so that both old and new data can co-exist.
+ At this stage new columns and tables are added while old
+ constraints are dropped. The post-migration statements
+ <em>"tighten"</em> the database schema back so that only
+ data conforming to the new format can remain. At this stage
+ old columns and tables are dropped and new constraints are
+ added. Now you can probably guess where the data
+ migration fits into this &mdash; between the pre and post
+ schema migrations where we can both access the old data
+ and create the new one.</p>
+
+ <p>If the schema is being generated as standalone SQL files,
+ then we end up with a pair of files for each step: the pre-migration
+ file and the post-migration file. For the <code>person</code>
+ example we started in the previous section we will have the
+ <code>person-002-pre.sql</code> and <code>person-002-post.sql</code>
+ files. Here <code>002</code> is the version <em>to</em> which
+ we are migrating while the <code>pre</code> and <code>post</code>
+ suffixes specify the migration stage. So if we wanted to migrate
+ a <code>person</code> database from version <code>1</code>
+ to <code>2</code>, then we would first execute
+ <code>person-002-pre.sql</code>, then migrate the data, if any
+ (discussed in more detail in the next section), and finally
+ execute <code>person-002-post.sql</code>. If our database is
+ several versions behind, for example the database has version
+ <code>1</code> while the current version is <code>5</code>,
+ then we simply perform this set of steps for each version
+ until we reach the current version.</p>
+
+ <p>If we look at the contents of the <code>person-002-pre.sql</code>
+ file, we will see the following (or equivalent, depending on the
+ database used) statement:</p>
+
+ <pre class="sql">
+ALTER TABLE "person"
+ ADD COLUMN "middle" TEXT NULL;
+ </pre>
+
+ <p>As we would expect, this statement adds a new column corresponding
+ to the new data member. An observant reader would notice,
+ however, that the column is added as <code>NULL</code>
+ even though we never requested this semantics in our object model.
+ Why is the column added as <code>NULL</code>? If during migration
+ the <code>person</code> table already contains rows (that is, existing
+ objects), then an attempt to add a non-<code>NULL</code> column that
+ doesn't have a default value will fail. As a result, ODB will initially
+ add a new column that doesn't have a default value as <code>NULL</code>
+ but then clean this up at the post-migration stage. This way your data
+ migration code is given a chance to assign some meaningful values for
+ the new data member for all the existing objects. Here are the contents
+ of the <code>person-002-post.sql</code> file:</p>
+
+ <pre class="sql">
+ALTER TABLE "person"
+ ALTER COLUMN "middle" SET NOT NULL;
+ </pre>
+
+ <p>Currently ODB directly supports the following elementary database
+ schema changes:</p>
+
+ <ul class="list">
+ <li>add table</li>
+ <li>drop table</li>
+ <li>add column</li>
+ <li>drop column</li>
+ <li>alter column, set <code>NULL</code>/<code>NOT NULL</code></li>
+ <li>add foreign key</li>
+ <li>drop foreign key</li>
+ <li>add index</li>
+ <li>drop index</li>
+ </ul>
+
+ <p>More complex changes can normally be implemented in terms of
+ these building blocks. For example, to change a type of a
+ data member (which leads to a change of a column type), we
+ can add a new data member with the desired type (add column),
+ migrate the data, and then delete the old data member (drop
+ column). ODB will issue diagnostics for cases that are
+ currently not supported directly. Note also that some database
+ systems (notably SQLite) have a number of limitations in their
+ support for schema changes. For more information on these
+ database-specific limitations, refer to the "Limitations" sections
+ in <a href="#II">Part II, "Database Systems"</a>.</p>
+
+ <p>How do we know what the current database version is? That is, the
+ version <em>from</em> which we need to migrate? We need to know this,
+ for example, in order to determine the set of migrations we have to
+ perform. By default, when schema evolution is enabled, ODB maintains
+ this information in a special table called <code>schema_version</code>
+ that has the following (or equivalent, depending on the database
+ used) definition:</p>
+
+ <pre class="sql">
+CREATE TABLE "schema_version" (
+ "name" TEXT NOT NULL PRIMARY KEY,
+ "version" BIGINT NOT NULL,
+ "migration" BOOLEAN NOT NULL);
+ </pre>
+
+ <p>The <code>name</code> column is the schema name as specified with
+ the <code>--schema-name</code> option. It is empty for the default
+ schema. The <code>version</code> column contains the current database
+ version. And, finally, the <code>migration</code> flag indicates
+ whether we are in the process of migrating the database, that is,
+ between the pre and post-migration stages.</p>
+
+ <p>The schema creation statements (<code>person.sql</code> in our case)
+ create this table and populate it with the initial model version. For
+ example, if we executed <code>person.sql</code> corresponding to
+ version <code>1</code> of our object model, then <code>name</code>
+ would have been empty (which signifies the default schema since we
+ didn't specify <code>--schema-name</code>), <code>version</code> will
+ be <code>1</code> and <code>migration</code> will be
+ <code>FALSE</code>.</p>
+
+ <p>The pre-migration statements update the version and set the migration
+ flag to <code>TRUE</code>. Continuing with our example, after executing
+ <code>person-002-pre.sql</code>, <code>version</code> will
+ become <code>2</code> and <code>migration</code> will be set to
+ <code>TRUE</code>. The post-migration statements simply clear the
+ migration flag. In our case, after running
+ <code>person-002-post.sql</code>, <code>version</code> will
+ remain <code>2</code> while <code>migration</code> will be reset
+ to <code>FALSE</code>.</p>
+
+ <p>Note also that above we mentioned that the schema creation statements
+ (<code>person.sql</code>) create the <code>schema_version</code> table.
+ This means that if we enable schema evolution support in the middle
+ of a project, then we could already have existing databases that
+ don't include this table. As a result, ODB will not be able to handle
+ migrations for such databases unless we manually add the
+ <code>schema_version</code> table and populate it with the correct
+ version information. For this reason, it is highly recommended that
+ you consider whether to use schema evolution and, if so, enable it
+ from the beginning of your project.</p>
+
+ <p>The <code>odb::database</code> class provides an API for accessing
+ and modifying the current database version:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ typedef unsigned long long schema_version;
+
+ struct LIBODB_EXPORT schema_version_migration
+ {
+ schema_version_migration (schema_version = 0,
+ bool migration = false);
+
+ schema_version version;
+ bool migration;
+
+ // This class also provides the ==, !=, &lt;, >, &lt;=, and >= operators.
+ // Version ordering is as follows: {1,f} &lt; {2,t} &lt; {2,f} &lt; {3,t}.
+ };
+
+ class database
+ {
+ public:
+ ...
+
+ schema_version
+ schema_version (const std::string&amp; name = "") const;
+
+ bool
+ schema_migration (const std::string&amp; name = "") const;
+
+ const schema_version_migration&amp;
+ schema_version_migration (const std::string&amp; name = "") const;
+
+ // Set schema version and migration state manually.
+ //
+ void
+ schema_version_migration (schema_version,
+ bool migration,
+ const std::string&amp; name = "");
+
+ void
+ schema_version_migration (const schema_version_migration&amp;,
+ const std::string&amp; name = "");
+
+ // Set default schema version table for all schemas.
+ //
+ void
+ schema_version_table (const std::string&amp; table_name);
+
+ // Set schema version table for a specific schema.
+ //
+ void
+ schema_version_table (const std::string&amp; table_name,
+ const std::string&amp; name);
+ };
+}
+ </pre>
+
+ <p>The <code>schema_version()</code> and <code>schema_migration()</code>
+ accessors return the current database version and migration flag,
+ respectively. The optional <code>name</code> argument is the schema
+ name. If the database schema hasn't been created (that is, there is
+ no corresponding entry in the <code>schema_version</code> table or
+ this table does not exist), then <code>schema_version()</code> returns
+ <code>0</code>. The <code>schema_version_migration()</code> accessor
+ returns both version and migration flag together in the
+ <code>schema_version_migration</code> <code>struct</code>.</p>
+
+ <p>You may already have a version table in your database or you (or your
+ database administrator) may prefer to keep track of versions your own
+ way. You can instruct ODB not to create the <code>schema_version</code>
+ table with the <code>--suppress-schema-version</code> option. However,
+ ODB still needs to know the current database version in order for certain
+ schema evolution mechanisms to function properly. As a result, in
+ this case, you will need to set the schema version on the database
+ instance manually using the schema_version_migration() modifier.
+ Note that the modifier API is not thread-safe. That is, you should
+ not modify the schema version while other threads may be accessing
+ or modifying the same information.</p>
+
+ <p>Note also that the accessors we discussed above will only query the
+ <code>schema_version</code> table once and, if the version could
+ be determined, cache the result. If, however, the version could
+ not be determined (that is, <code>schema_version()</code> returned
+ 0), then a subsequent call will re-query the table. While it is
+ probably a bad idea to modify the database schema while the
+ application is running (other than via the <code>schema_catalog</code>
+ API, as discussed below), if for some reason you need ODB to re-query
+ the version, then you can manually set it to 0 using the
+ <code>schema_version_migration()</code> modifier.</p>
+
+ <p>It is also possible to change the name of the table that stores
+ the schema version using the <code>--schema-version-table</code>
+ option. You will also need to specify this alternative name on
+ the <code>database</code> instance using the <code>schema_version_table()</code>
+ modifier. The first version specifies the default table that is
+ used for all the schema names. The second version specifies the
+ table for a specific schema. The table name should be
+ database-quoted, if necessary.</p>
+
+ <p>If we are generating our schema migrations as standalone SQL files,
+ then the migration workflow could look like this:</p>
+
+ <ol>
+ <li>The database administrator determines the current database version.
+ If migration is required, then for each migration step (that
+ is, from one version to the next), they perform the following:</li>
+
+ <li>Execute the pre-migration file.</li>
+
+ <li>Execute our application (or a separate migration program)
+ to perform data migration (discussed later). Our application
+ can determine that is is being executed in the "migration mode"
+ by calling <code>schema_migration()</code> and then which
+ migration code to run by calling <code>schema_version()</code>.</li>
+
+ <li>Execute the post-migration file.</li>
+ </ol>
+
+ <p>These steps become more integrated and automatic if we embed the
+ schema creation and migration code into the generated C++ code.
+ Now we can perform schema creation, schema migration, and data
+ migration as well as determine when each step is necessary
+ programmatically from within the application.</p>
+
+ <p>Schema evolution support adds the following extra functions to
+ the <code>odb::schema_catalog</code> class, which we first discussed
+ in <a href="#3.4">Section 3.4, "Database"</a>.</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ class schema_catalog
+ {
+ public:
+ ...
+
+
+ // Schema migration.
+ //
+ static void
+ migrate_schema_pre (database&amp;,
+ schema_version,
+ const std::string&amp; name = "");
+
+ static void
+ migrate_schema_post (database&amp;,
+ schema_version,
+ const std::string&amp; name = "");
+
+ static void
+ migrate_schema (database&amp;,
+ schema_version,
+ const std::string&amp; name = "");
+
+ // Data migration.
+ //
+ // Discussed in the next section.
+
+
+ // Combined schema and data migration.
+ //
+ static void
+ migrate (database&amp;,
+ schema_version = 0,
+ const std::string&amp; name = "");
+
+ // Schema version information.
+ //
+ static schema_version
+ base_version (const database&amp;,
+ const std::string&amp; name = "");
+
+ static schema_version
+ base_version (database_id,
+ const std::string&amp; name = "");
+
+ static schema_version
+ current_version (const database&amp;,
+ const std::string&amp; name = "");
+
+ static schema_version
+ current_version (database_id,
+ const std::string&amp; name = "");
+
+ static schema_version
+ next_version (const database&amp;,
+ schema_version = 0,
+ const std::string&amp; name = "");
+
+ static schema_version
+ next_version (database_id,
+ schema_version,
+ const std::string&amp; name = "");
+ };
+}
+ </pre>
+
+ <p>The <code>migrate_schema_pre()</code> and
+ <code>migrate_schema_post()</code> static functions perform
+ a single stage (that is, pre or post) of a single migration
+ step (that is, from one version to the next). The <code>version</code>
+ argument specifies the version we are migrating to. For
+ instance, in our <code>person</code> example, if we know that
+ the database version is <code>1</code> and the next version
+ is <code>2</code>, then we can execute code like this:</p>
+
+ <pre class="cxx">
+transaction t (db.begin ());
+
+schema_catalog::migrate_schema_pre (db, 2);
+
+// Data migration goes here.
+
+schema_catalog::migrate_schema_post (db, 2);
+
+t.commit ();
+ </pre>
+
+ <p>If you don't have any data migration code to run, then you can
+ perform both stages with a single call using the
+ <code>migrate_schema()</code> static function.</p>
+
+ <p>The <code>migrate()</code> static function perform both schema
+ and data migration (we discuss data migration in the next section).
+ It can also perform several migration steps at once. If we don't
+ specify its target version, then it will migrate (if necessary)
+ all the way to the current model version. As an extra convenience,
+ <code>migrate()</code> will also create the database schema if
+ none exists. As a result, if we don't have any data migration
+ code or we have registered it with <code>schema_catalog</code> (as
+ discussed later), then the database schema creation and migration,
+ whichever is necessary, if at all, can be performed with a single
+ function call:</p>
+
+ <pre class="cxx">
+transaction t (db.begin ());
+schema_catalog::migrate (db);
+t.commit ();
+ </pre>
+
+ <p>Note also that <code>schema_catalog</code> is integrated with the
+ <code>odb::database</code> schema version API. In particular,
+ <code>schema_catalog</code> functions will query and synchronize
+ the schema version on the <code>database</code> instance if and
+ when required.</p>
+
+ <p>The <code>schema_catalog</code> class also allows you to iterate
+ over known versions (remember, there could be "gaps" in version
+ numbers) with the <code>base_version()</code>,
+ <code>current_version()</code> and <code>next_version()</code>
+ static functions. The <code>base_version()</code> and
+ <code>current_version()</code> functions return the base and
+ current object model versions, respectively. That is, the
+ lowest version from which we can migrate and the version that
+ we ultimately want to migrate to. The <code>next_version()</code>
+ function returns the next known version. If the passed version is
+ greater or equal to the current version, then this function
+ will return the current version plus one (that is, one past
+ current). If we don't specify the version, then
+ <code>next_version()</code> will use the current database version
+ as the starting point. Note also that the schema version information
+ provided by these functions is only available if we embed the schema
+ migration code into the generated C++ code. For standalone SQL file
+ migrations this information is normally not needed since the migration
+ process is directed by an external entity, such as a database
+ administrator or a script.</p>
+
+ <p>Most <code>schema_catalog</code> functions presented above also accept
+ the optional schema name argument. If the passed schema name is not
+ found, then the <code>odb::unknown_schema</code> exception is
+ thrown. Similarly, functions that accept the schema version argument will
+ throw the <code>odb::unknown_schema_version</code> exception if the
+ passed or current version is invalid. Refer to <a href="#3.14">Section
+ 3.14, "ODB Exceptions"</a> for more information on these exceptions.</p>
+
+ <p>To illustrate how all these parts fit together, consider the
+ following more realistic database schema management example.
+ Here we want to handle the schema creation in a special way
+ and perform each migration step in its own transaction.</p>
+
+ <pre class="cxx">
+schema_version v (db.schema_version ());
+schema_version bv (schema_catalog::base_version (db));
+schema_version cv (schema_catalog::current_version (db));
+
+if (v == 0)
+{
+ // No schema in the database. Create the schema and
+ // initialize the database.
+ //
+ transaction t (db.begin ());
+ schema_catalog::create_schema (db);
+
+ // Populate the database with initial data, if any.
+
+ t.commit ();
+}
+else if (v &lt; cv)
+{
+ // Old schema (and data) in the database, migrate them.
+ //
+
+ if (v &lt; bv)
+ {
+ // Error: migration from this version is no longer supported.
+ }
+
+ for (v = schema_catalog::next_version (db, v);
+ v &lt;= cv;
+ v = schema_catalog::next_version (db, v))
+ {
+ transaction t (db.begin ());
+ schema_catalog::migrate_schema_pre (db, v);
+
+ // Data migration goes here.
+
+ schema_catalog::migrate_schema_post (db, v);
+ t.commit ();
+ }
+}
+else if (v > cv)
+{
+ // Error: old application trying to access new database.
+}
+ </pre>
+
+ <h2><a name="13.3">13.3 Data Migration</a></h2>
+
+ <p>In quite a few cases specifying the default value for new data
+ members will be all that's required to handle the existing objects.
+ For example, the natural default value for the new middle name
+ that we have added is an empty string. And we can handle
+ this case with the <code>db&nbsp;default</code> pragma and without
+ any extra C++ code:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 2)
+
+#pragma db object
+class person
+{
+ ...
+
+
+ #pragma db default("")
+ std::string middle_;
+};
+ </pre>
+
+ <p>However, there will be situations where we would need to perform
+ more elaborate data migrations, that is, convert old data to the
+ new format. As an example, suppose we want to add gender to our
+ <code>person</code> class. And, instead of leaving it unassigned
+ for all the existing objects, we will try to guess it from the
+ first name. This is not particularly accurate but it could be
+ sufficient for our hypothetical application:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 3)
+
+enum gender {male, female};
+
+#pragma db object
+class person
+{
+ ...
+
+ gender gender_;
+};
+ </pre>
+
+ <p>As we have discussed earlier, there are two ways to perform data
+ migration: immediate and gradual. To recap, with immediate
+ migration we migrate all the existing objects at once, normally
+ after the schema pre-migration statements but before the
+ post-migration statements. With gradual migration, we make sure
+ the new object model can accommodate both old and new data and
+ gradually migrate existing objects as the application runs and
+ the opportunities to do so arise, for example, an object is
+ updated.</p>
+
+ <p>There is also another option for data migration that is not
+ discussed further in this section. Instead of using our C++
+ object model we could execute ad-hoc SQL statements that
+ perform the necessary conversions and migrations directly
+ on the database server. While in certain cases this can be
+ a better option from the performance point of view, this
+ approach is often limited in terms of the migration logic
+ that we can handle.</p>
+
+ <h2><a name="13.3.1">13.3.1 Immediate Data Migration</a></h2>
+
+ <p>Let's first see how we can implement an immediate migration for the
+ new <code>gender_</code> data member we have added above. If we
+ are using standalone SQL files for migration, then we could add
+ code along these lines somewhere early in <code>main()</code>,
+ before the main application logic:</p>
+
+ <pre class="cxx">
+int
+main ()
+{
+ ...
+
+ odb::database&amp; db = ...
+
+ // Migrate data if necessary.
+ //
+ if (db.schema_migration ())
+ {
+ switch (db.schema_version ())
+ {
+ case 3:
+ {
+ // Assign gender to all the existing objects.
+ //
+ transaction t (db.begin ());
+
+ for (person&amp; p: db.query&lt;person> ())
+ {
+ p.gender (guess_gender (p.first ()));
+ db.update (p);
+ }
+
+ t.commit ();
+ break;
+ }
+ }
+ }
+
+ ...
+}
+ </pre>
+
+ <p>If you have a large number of objects to migrate, it may also be
+ a good idea, from the performance point of view, to break one big
+ transaction that we now have into multiple smaller transactions
+ (<a href="#3.5">Section 3.5, "Transactions"</a>). For example:</p>
+
+ <pre class="cxx">
+case 3:
+ {
+ transaction t (db.begin ());
+
+ size_t n (0);
+ for (person&amp; p: db.query&lt;person> ())
+ {
+ p.gender (guess_gender (p.first ()));
+ db.update (p);
+
+ // Commit the current transaction and start a new one after
+ // every 100 updates.
+ //
+ if (n++ % 100 == 0)
+ {
+ t.commit ();
+ t.reset (db.begin ());
+ }
+ }
+
+ t.commit ();
+ break;
+ }
+ </pre>
+
+ <p>While it looks straightforward enough, as we add more migration
+ snippets, this approach can quickly become unmaintainable. Instead
+ of having all the migrations in a single function and determining
+ when to run each piece ourselves, we can package each migration into
+ a separate function, register it with the <code>schema_catalog</code>
+ class, and let ODB figure out when to run which migration functions.
+ To support this functionality, <code>schema_catalog</code> provides
+ the following data migration API:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ class schema_catalog
+ {
+ public:
+ ...
+
+ // Data migration.
+ //
+ static std::size_t
+ migrate_data (database&amp;,
+ schema_version = 0,
+ const std::string&amp; name = "");
+
+ typedef void data_migration_function_type (database&amp;);
+
+ // Common (for all the databases) data migration, C++98/03 version:
+ //
+ template &lt;schema_version v, schema_version base>
+ static void
+ data_migration_function (data_migration_function_type*,
+ const std::string&amp; name = "");
+
+ // Common (for all the databases) data migration, C++11 version:
+ //
+ template &lt;schema_version v, schema_version base>
+ static void
+ data_migration_function (std::function&lt;data_migration_function_type>,
+ const std::string&amp; name = "");
+
+ // Database-specific data migration, C++98/03 version:
+ //
+ template &lt;schema_version v, schema_version base>
+ static void
+ data_migration_function (database&amp;,
+ data_migration_function_type*,
+ const std::string&amp; name = "");
+
+ template &lt;schema_version v, schema_version base>
+ static void
+ data_migration_function (database_id,
+ data_migration_function_type*,
+ const std::string&amp; name = "");
+
+ // Database-specific data migration, C++11 version:
+ //
+ template &lt;schema_version v, schema_version base>
+ static void
+ data_migration_function (database&amp;,
+ std::function&lt;data_migration_function_type>,
+ const std::string&amp; name = "");
+
+ template &lt;schema_version v, schema_version base>
+ static void
+ data_migration_function (database_id,
+ std::function&lt;data_migration_function_type>,
+ const std::string&amp; name = "");
+ };
+
+ // Static data migration function registration, C++98/03 version:
+ //
+ template &lt;schema_version v, schema_version base>
+ struct data_migration_entry
+ {
+ data_migration_entry (data_migration_function_type*,
+ const std::string&amp; name = "");
+
+ data_migration_entry (database_id,
+ data_migration_function_type*,
+ const std::string&amp; name = "");
+ };
+
+ // Static data migration function registration, C++11 version:
+ //
+ template &lt;schema_version v, schema_version base>
+ struct data_migration_entry
+ {
+ data_migration_entry (std::function&lt;data_migration_function_type>,
+ const std::string&amp; name = "");
+
+ data_migration_entry (database_id,
+ std::function&lt;data_migration_function_type>,
+ const std::string&amp; name = "");
+ };
+}
+ </pre>
+
+ <p>The <code>migrate_data()</code> static function performs data
+ migration for the specified version. If no version is specified,
+ then it will use the current database version and also check
+ whether the database is in migration, that is,
+ <code>database::schema_migration()</code> returns <code>true</code>.
+ As a result, all we need to do in our <code>main()</code> is call
+ this function. It will check if migration is required and if so,
+ call all the migration functions registered for this version. For
+ example:</p>
+
+ <pre class="cxx">
+int
+main ()
+{
+ ...
+
+ database&amp; db = ...
+
+ // Check if we need to migrate any data and do so
+ // if that's the case.
+ //
+ schema_catalog::migrate_data (db);
+
+ ...
+}
+ </pre>
+
+ <p>The <code>migrate_data()</code> function returns the number of
+ migration functions called. You can use this value for debugging
+ or logging.</p>
+
+ <p>The only other step that we need to perform is register our data
+ migration functions with <code>schema_catalog</code>. At the
+ lower level we can call the <code>data_migration_function()</code>
+ static function for every migration function we have, for example,
+ at the beginning of <code>main()</code>. For each version, data
+ migration functions are called in the order of registration.</p>
+
+ <p>A more convenient approach, however, is to use the
+ <code>data_migration_entry</code> helper class template to register the
+ migration functions during static initialization. This way we
+ can keep the migration function and its registration code next
+ to each other. Here is how we can reimplement our <code>gender</code>
+ migration code to use this mechanism:</p>
+
+ <pre class="cxx">
+static void
+migrate_gender (odb::database&amp; db)
+{
+ transaction t (db.begin ());
+
+ for (person&amp; p: db.query&lt;person> ())
+ {
+ p.gender (guess_gender (p.first ()));
+ db.update (p);
+ }
+
+ t.commit ();
+}
+
+static const odb::data_migration_entry&lt;3, MYAPP_BASE_VERSION>
+migrate_gender_entry (&amp;migrate_gender);
+ </pre>
+
+ <p>The first template argument to the <code>data_migration_entry</code>
+ class template is the version we want this data migration function
+ to be called for. The second template argument is the base model
+ version. This second argument is necessary to detect the situation
+ where we no longer need this data migration function. Remember
+ that when we move the base model version forward, migrations from
+ any version below the new base are no longer possible. We, however,
+ may still have migration functions registered for those lower
+ versions. Since these functions will never be called, they are
+ effectively dead code and it would be useful to identify and
+ remove them. To assist with this, <code>data_migration_entry</code>
+ (and lower lever <code>data_migration_function()</code>) will
+ check at compile time (that is, <code>static_assert</code>) that
+ the registration version is greater than the base model version.</p>
+
+ <p>In the above example we use the <code>MYAPP_BASE_VERSION</code>
+ macro that is presumably defined in a central place, for example,
+ <code>version.hxx</code>. This is the recommended approach since
+ we can update the base version in a single place and have the
+ C++ compiler automatically identify all the data migration
+ functions that can be removed.</p>
+
+ <p>In C++11 we can also create a template alias so that we don't
+ have to repeat the base model macro in every registration, for
+ example:</p>
+
+ <pre class="cxx">
+template &lt;schema_version v>
+using migration_entry = odb::data_migration_entry&lt;v, MYAPP_BASE_VERSION>;
+
+static const migration_entry&lt;3>
+migrate_gender_entry (&amp;migrate_gender);
+ </pre>
+
+ <p>For cases where you need to by-pass the base version check, for
+ example, to implement your own registration helper, ODB also
+ provides "unsafe" versions of the <code>data_migration_function()</code>
+ functions that take the version as a function argument rather than
+ as a template parameter.</p>
+
+ <p>In C++11 we can also use lambdas as migration functions, which makes
+ the migration code more concise:</p>
+
+ <pre class="cxx">
+static const migration_entry&lt;3>
+migrate_gender_entry (
+ [] (odb::database&amp; db)
+ {
+ transaction t (db.begin ());
+
+ for (person&amp; p: db.query&lt;person> ())
+ {
+ p.gender (guess_gender (p.first ()));
+ db.update (p);
+ }
+
+ t.commit ();
+ });
+ </pre>
+
+ <p>If we are using embedded schema migrations, then both schema and
+ data migration is integrated and can be performed with a single
+ call to the <code>schema_catalog::migrate()</code> function that
+ we discussed earlier. For example:</p>
+
+<pre class="cxx">
+int
+main ()
+{
+ ...
+
+ database&amp; db = ...
+
+ // Check if we need to migrate the database and do so
+ // if that's the case.
+ //
+ {
+ transaction t (db.begin ());
+ schema_catalog::migrate (db);
+ t.commit ();
+ }
+
+ ...
+}
+ </pre>
+
+ <p>Note, however, that in this case we call <code>migrate()</code>
+ within a transaction (for the schema migration part) which means
+ that our migration functions will also be called within this
+ transaction. As a result, we will need to adjust our migration
+ functions not to start their own transaction:</p>
+
+ <pre class="cxx">
+static void
+migrate_gender (odb::database&amp; db)
+{
+ // Assume we are already in a transaction.
+ //
+ for (person&amp; p: db.query&lt;person> ())
+ {
+ p.gender (guess_gender (p.first ()));
+ db.update (p);
+ }
+}
+ </pre>
+
+ <p>If, however, we want more granular transactions, then we can
+ use the lower-level <code>schema_catalog</code> functions to
+ gain more control, as we have seen at the end of the previous
+ section. Here is the relevant part of that example with
+ an added data migration call:</p>
+
+ <pre class="cxx">
+ // Old schema (and data) in the database, migrate them.
+ //
+ for (v = schema_catalog::next_version (db, v);
+ v &lt;= cv;
+ v = schema_catalog::next_version (db, v))
+ {
+ transaction t (db.begin ());
+ schema_catalog::migrate_schema_pre (db, v);
+ schema_catalog::migrate_data (db, v);
+ schema_catalog::migrate_schema_post (db, v);
+ t.commit ();
+ }
+ </pre>
+
+ <h2><a name="13.3.2">13.3.2 Gradual Data Migration</a></h2>
+
+ <p>If the number of existing objects that require migration is large,
+ then an all-at-once, immediate migration, while simple, may not
+ be practical from a performance point of view. In this case,
+ we can perform a gradual migration as the application does
+ its normal functions.</p>
+
+ <p>With gradual migrations, the object model must be capable of
+ representing data that conforms to both old and new formats at
+ the same time since, in general, the database will contain a
+ mixture of old and new objects. For example, in case of our
+ <code>gender</code> data member, we need a special value that
+ represents the "no gender assigned yet" case (an old object).
+ We also need to assign this special value to all the existing
+ objects during the schema pre-migration stage. One way to do
+ this would be add a special value to our <code>gender</code>
+ enum and then make it the default value with the
+ <code>db&nbsp;default</code> pragma. A cleaner and easier approach,
+ however, is to use <code>NULL</code> as a special value. We
+ can add support for the <code>NULL</code> value semantics
+ to any existing type by wrapping it with
+ <code>odb::nullable</code>, <code>boost::optional</code>
+ or similar (<a href="#7.3">Section 7.3, "Pointers and <code>NULL</code>
+ Value Semantics"</a>). We also don't need to specify the default value
+ explicitly since <code>NULL</code> is used automatically. Here
+ is how we can use this approach in our <code>gender</code>
+ example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/nullable.hxx>
+
+#pragma db object
+class person
+{
+ ...
+
+ odb::nullable&lt;gender> gender_;
+};
+ </pre>
+
+ <p>A variety of strategies can be employed to implement gradual
+ migrations. For example, we can migrate the data when the object
+ is updated as part of the normal application logic. While there
+ is no migration cost associated with this approach (the object
+ is updated anyway), depending on how often objects are typically
+ updated, this strategy can take a long time to complete. An
+ alternative strategy would be to perform an update whenever
+ an old object is loaded. Yet another strategy is to have a
+ separate thread that slowly migrates all the old objects as
+ the application runs.</p>
+
+ <p>As an example, let us implement the first approach for our
+ <code>gender</code> migration. While we could have added
+ the necessary code throughout the application, from the
+ maintenance point of view, it is best to try and localize
+ the gradual migration logic to the persistent classes that
+ it affects. And for this database operation callbacks
+ (<a href="#14.1.7">Section 14.1.7, "<code>callback</code>"</a>)
+ are a very useful mechanism. In our case, all we have to do is handle
+ the <code>post_load</code> event where we guess the gender
+ if it is <code>NULL</code>:</p>
+
+ <pre class="cxx">
+#include &lt;odb/core.hxx> // odb::database
+#include &lt;odb/callback.hxx> // odb::callback_event
+#include &lt;odb/nullable.hxx>
+
+#pragma db object callback(migrate)
+class person
+{
+ ...
+
+ void
+ migrate (odb::callback_event e, odb::database&amp;)
+ {
+ if (e == odb::callback_event::post_load)
+ {
+ // Guess gender if not assigned.
+ //
+ if (gender_.null ())
+ gender_ = guess_gender (first_);
+ }
+ }
+
+ odb::nullable&lt;gender> gender_;
+};
+ </pre>
+
+ <p>In particular, we don't have to touch any of the accessors
+ or modifiers or the application logic &mdash; all of them
+ can assume that the value can never be <code>NULL</code>.
+ And when the object is next updated, the new <code>gender</code>
+ value will be stored automatically.</p>
+
+ <p>All gradual migrations normally end up with a terminating
+ immediate migration some number of versions down the line,
+ when the bulk of the objects has presumably been converted.
+ This way we don't have to keep the gradual migration code
+ around forever. Here is how we could implement a terminating
+ migration for our example:</p>
+
+ <pre class="cxx">
+// person.hxx
+//
+#pragma db model version(1, 4)
+
+#pragma db object
+class person
+{
+ ...
+
+ gender gender_;
+};
+
+// person.cxx
+//
+static void
+migrate_gender (odb::database&amp; db)
+{
+ typedef odb::query&lt;person> query;
+
+ for (person&amp; p: db.query&lt;person> (query::gender.is_null ()))
+ {
+ p.gender (guess_gender (p.first ()));
+ db.update (p);
+ }
+}
+
+static const odb::data_migration_entry&lt;4, MYAPP_BASE_VERSION>
+migrate_gender_entry (&amp;migrate_gender);
+ </pre>
+
+ <p>A couple of points to note about this code. Firstly, we
+ removed all the gradual migration logic (the callback)
+ from the class and replaced it with the immediate migration
+ function. We also removed the <code>odb::nullable</code>
+ wrapper (and therefore disallowed the <code>NULL</code> values)
+ since after this migration all the objects will have been
+ converted. Finally, in the migration function, we only query
+ the database for objects that need migration, that is, have
+ <code>NULL</code> gender.</p>
+
+ <h2><a name="13.4">13.4 Soft Object Model Changes</a></h2>
+
+ <p>Let us consider another common kind of object model change:
+ we delete an old member, add a new one, and need to copy
+ the data from the old to the new, perhaps applying some
+ conversion. For example, we may realize that in our application
+ it is a better idea to store a person's name as a single string
+ rather than split it into three fields. So what we would like to do
+ is add a new data member, let's call it <code>name_</code>, convert
+ all the existing split names, and then delete the <code>first_</code>,
+ <code>middle_</code>, and <code>last_</code> data members.</p>
+
+ <p>While this sounds straightforward, there is a problem. If we
+ delete (that is, physically remove from the source code) the
+ old data members, then we won't be able to access the old
+ data. The data will still be available in the database between
+ the schema pre and post-migrations, it is just we will no longer
+ be able to access it through our object model. And if we keep
+ the old data members around, then the old data will remain
+ stored in the database even after the schema post-migration.</p>
+
+ <p>There is also a more subtle problem that has to do with existing
+ migrations for the previous versions. Remember, in version <code>3</code>
+ of our <code>person</code> example we added the <code>gender_</code>
+ data member. We also have a data migration function which guesses
+ the gender based on the first name. Deleting the <code>first_</code>
+ data member from our class will obviously break this code. But
+ even adding the new <code>name_</code> data member will cause
+ problems because when we try to update the object in order to
+ store the new gender, ODB will try to update <code>name_</code>
+ as well. But there is no corresponding column in the database
+ yet. When we run this migration function, we are still several
+ versions away from the point where the <code>name</code> column
+ will be added.</p>
+
+ <p>This is a very subtle but also very important implication to
+ understand. Unlike the main application logic, which only needs
+ to deal with the current model version, data migration code works
+ on databases that can be multiple versions behind the current
+ version.</p>
+
+ <p>How can we resolve this problem? It appears what we need is the
+ ability to add or delete data members starting from a specific
+ version. In ODB this mechanism is called soft member additions
+ and deletions. A soft-added member is only treated as persistent
+ starting from the addition version. A soft-deleted member is
+ persistent until the deletion version (but including the migration
+ stage). In its essence, soft model changes allow us to maintain
+ multiple versions of our object model all with a single set of
+ persistent classes. Let us now see how this functionality can
+ help implement our changes:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 4)
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id_;
+
+ #pragma db deleted(4)
+ std::string first_;
+
+ #pragma db deleted(4)
+ std::string middle_;
+
+ #pragma db deleted(4)
+ std::string last_;
+
+ #pragma db added(4)
+ std::string name_;
+
+ gender gender_;
+};
+ </pre>
+
+ <p>The migration function for this change could then look like
+ this:</p>
+
+ <pre class="cxx">
+static void
+migrate_name (odb::database&amp; db)
+{
+ for (person&amp; p: db.query&lt;person> ())
+ {
+ p.name (p.first () + " " +
+ p.middle () + (p.middle ().empty () ? "" : " ") +
+ p.last ());
+ db.update (p);
+ }
+}
+
+static const odb::data_migration_entry&lt;4, MYAPP_BASE_VERSION>
+migrate_name_entry (&amp;migrate_name);
+ </pre>
+
+ <p>Note also that no changes are required to the gender migration
+ function.</p>
+
+ <p>As you may have noticed, in the code above we assumed that the
+ <code>person</code> class still provides public accessors for
+ the now deleted data members. This might not be ideal since now
+ they should not be used by the application logic. The only code
+ that may still need to access them is the migration functions. The
+ recommended way to resolve this is to remove the accessors/modifiers
+ corresponding to the deleted data member, make migration functions
+ static functions of the class being migrated, and then access
+ the deleted data members directly. For example:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 4)
+
+#pragma db object
+class person
+{
+ ...
+
+private:
+ friend class odb::access;
+
+ #pragma db id auto
+ unsigned long id_;
+
+ #pragma db deleted(4)
+ std::string first_;
+
+ #pragma db deleted(4)
+ std::string middle_;
+
+ #pragma db deleted(4)
+ std::string last_;
+
+ #pragma db added(4)
+ std::string name_;
+
+ gender gender_;
+
+private:
+ static void
+ migrate_gender (odb::database&amp;);
+
+ static void
+ migrate_name (odb::database&amp;);
+};
+
+void person::
+migrate_gender (odb::database&amp; db)
+{
+ for (person&amp; p: db.query&lt;person> ())
+ {
+ p.gender_ = guess_gender (p.first_);
+ db.update (p);
+ }
+}
+
+static const odb::data_migration_entry&lt;3, MYAPP_BASE_VERSION>
+migrate_name_entry (&amp;migrate_gender);
+
+void person::
+migrate_name (odb::database&amp; db)
+{
+ for (person&amp; p: db.query&lt;person> ())
+ {
+ p.name_ = p.first_ + " " +
+ p.middle_ + (p.middle_.empty () ? "" : " ") +
+ p.last_;
+ db.update (p);
+ }
+}
+
+static const odb::data_migration_entry&lt;4, MYAPP_BASE_VERSION>
+migrate_name_entry (&amp;migrate_name);
+ </pre>
+
+ <p>Another potential issue with the soft-deletion is the requirement
+ to keep the delete data members in the class. While they will not
+ be initialized in the normal operation of the application (that
+ is, not a migration), this can still be a problem if we need to
+ minimize the memory footprint of our classes. For example, we may
+ cache a large number of objects in memory and having three
+ <code>std::string</code> data members can be a significant
+ overhead.</p>
+
+ <p>The recommended way to resolve this issue is to place all the
+ deleted data members into a dynamically allocated composite
+ value type. For example:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 4)
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id_;
+
+ #pragma db added(4)
+ std::string name_;
+
+ gender gender_;
+
+ #pragma db value
+ struct deleted_data
+ {
+ #pragma db deleted(4)
+ std::string first_;
+
+ #pragma db deleted(4)
+ std::string middle_;
+
+ #pragma db deleted(4)
+ std::string last_;
+ };
+
+ #pragma db column("")
+ std::unique_ptr&lt;deleted_data> dd_;
+
+ ...
+};
+ </pre>
+
+ <p>ODB will then automatically allocate the deleted value type if
+ any of the deleted data members are being loaded. During the normal
+ operation, however, the pointer will stay <code>NULL</code> and
+ therefore reduce the common case overhead to a single pointer
+ per class. Note that we make the composite value column prefix
+ empty (the <code>db&nbsp;column("")</code> pragma) in order to
+ keep the same column names for the deleted data members.</p>
+
+ <p>Soft-added and deleted data members can be used in objects,
+ composite values, views, and container value types. We can
+ also soft-add and delete data members of simple, composite,
+ pointer to object, and container types. Only special data
+ members, such as the object id and the optimistic concurrency
+ version, cannot be soft-added or deleted.</p>
+
+ <p>It is also possible to soft-delete a persistent class. We
+ can still work with the existing objects of such a class,
+ however, no table is created in new databases for soft-deleted
+ classes. To put it another way, a soft-delete class is like an
+ abstract class (no table) but which can still be loaded, updated,
+ etc. Soft-added persistent classes do not make much sense and
+ are therefore not supported.</p>
+
+ <p>As an example of a soft-deleted class, suppose we want to
+ replace our <code>person</code> class with the new
+ <code>employee</code> object and migrate the data. Here is
+ how we could do this:</p>
+
+ <pre class="cxx">
+#pragma db model version(1, 5)
+
+#pragma db object deleted(5)
+class person
+{
+ ...
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id_;
+
+ std::string name_;
+ gender gender_;
+
+ static void
+ migrate_person (odb::database&amp;);
+};
+
+void employee::
+migrate_person (odb::database&amp; db)
+{
+ for (person&amp; p: db.query&lt;person> ())
+ {
+ employee e (p.name (), p.gender ());
+ db.persist (e);
+ }
+}
+
+static const odb::data_migration_entry&lt;5, MYAPP_BASE_VERSION>
+migrate_person_entry (&amp;migrate_person);
+ </pre>
+
+ <p>As we have seen above, hard member additions and deletions can
+ (and most likely will) break existing data migration code. Why,
+ then, not treat all the changes, or at least additions, as soft?
+ ODB requires you to explicitly request this semantics because
+ support for soft-added and deleted data members incurs runtime
+ overhead. And there can be plenty of cases where there is no
+ existing data migration and therefore hard additions and deletions
+ are sufficient.</p>
+
+ <p>In some cases a hard addition or deletion will result in a
+ compile-time error. For example, one of the data migration
+ functions may reference the data member we just deleted. In
+ many cases, however, such errors can only be detected at
+ runtime, and, worse yet, only when the migration function
+ is executed. For example, we may hard-add a new data member
+ that an existing migration function will try to indirectly
+ store in the database as part of an object update. As a result,
+ it is highly recommended that you always test your application
+ with the database that starts at the base version so that every
+ data migration function is called and therefore ensured to
+ still work correctly.</p>
+
+ <p>To help with this problem you can also instruct ODB to warn
+ you about any hard additions or deletions with the
+ <code>--warn-hard-add</code>, <code>--warn-hard-delete</code>,
+ and <code>--warn-hard</code> command line options. ODB will
+ only warn you about hard changes in the current version and
+ only for as long as it is open, which makes this mechanism
+ fairly usable.</p>
+
+ <p>You may also be wondering why we have to specify the addition
+ and deletion versions explicitly. It may seem like the ODB compiler
+ should be able to figure this out automatically. While it is
+ theoretically possible, to achieve this, ODB would have to also
+ maintain a separate changelog of the C++ object model in
+ addition to the database schema changelog it already maintains.
+ While being a lot more complex, such an additional changelog
+ would also complicate the workflow significantly. In this light,
+ maintaining this change information as part of the original
+ source files appears to be a cleaner and simpler approach.</p>
+
+ <p>As we discussed before, when we move the base model version
+ forward we essentially drop support for migrations from
+ versions before the new base. As a result, it is no longer
+ necessary to maintain the soft semantics of additions and
+ deletions up to and including the new base version. ODB
+ will issue diagnostics for all such members and classes.
+ For soft deletions we can simply remove the data member or
+ class entirely. For soft additions we only need to remove the
+ <code>db&nbsp;added</code> pragma.</p>
+
+ <h2><a name="13.4.1">13.4.1 Reuse Inheritance Changes</a></h2>
+
+ <p>Besides adding and deleting data members, another way to alter
+ the object's table is using reuse-style inheritance. If we add
+ a new reuse base, then, from the database schema point of view,
+ this is equivalent to adding all its columns to the derived
+ object's table. Similarly, deleting reuse inheritance results in
+ all the base's columns being deleted from the derived's table.</p>
+
+ <p>In the future ODB may provide direct support for soft addition
+ and deletion of inheritance. Currently, however, this semantics
+ can be emulated with soft-added and deleted data members. The
+ following table describes the most common scenarios depending
+ on where columns are added or deleted, that is, base table,
+ derived table, or both.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table class="scenarios" border="1">
+ <tr>
+ <th>DELETE</th>
+ <th style="width: 40%">HARD</th>
+ <th style="width: 40%">SOFT</th>
+ </tr>
+
+ <tr>
+ <td>In both (delete inheritance and base)</td>
+ <td>Delete inheritance and base. Move object id to derived.</td>
+ <td>Soft-delete base. Mark all data members (except id) in
+ base as soft-deleted.</td>
+ </tr>
+
+ <tr>
+ <td>In base only (delete base)</td>
+ <td>Option 1: mark base as abstract.<br/><br/>
+ Option 2: move all the base member to derived, delete base.</td>
+ <td>Soft-delete base.</td>
+ </tr>
+
+ <tr>
+ <td>In derived only (delete inheritance)</td>
+ <td>Delete inheritance, add object id to derived.</td>
+ <td>Option 1: copy base to a new soft-deleted base, inherit
+ from it instead. Mark all the data members (expect id) in
+ this new base as soft-deleted. Note: we add the new base
+ as soft-deleted to get notified when we can remove it.<br/><br/>
+ Option 2: Copy all the data members from base to derived
+ and mark them as soft-deleted in derived.</td>
+ </tr>
+ </table>
+
+
+ <table class="scenarios" border="1">
+ <tr>
+ <th>ADD</th>
+ <th style="width: 40%">HARD</th>
+ <th style="width: 40%">SOFT</th>
+ </tr>
+
+ <tr>
+ <td>In both (add new base and inheritance)</td>
+ <td>Add new base and inheritance. Potentially move object id
+ member from derived to base.</td>
+ <td>Add new base and mark all its data members as soft-added.
+ Add inheritance. Move object id from derived to base.</td>
+ </tr>
+
+ <tr>
+ <td>In base only (refactor existing data to new base)</td>
+ <td>Add new base and move data members from derived to base.
+ Note: in most cases the new base will be made abstract
+ which make this scenario non-schema changing.</td>
+ <td>The same as HARD.</td>
+ </tr>
+
+ <tr>
+ <td>In derived only (add inheritance to existing base)</td>
+ <td>Add inheritance, delete object id in derived.</td>
+ <td>Copy existing base to a new abstract base and inherit
+ from it. Mark all the database members in the new base
+ as soft-added (except object id). When notified by the
+ ODB compiler that the soft addition of the data members
+ is no longer necessary, delete the copy and inherit from
+ the original base.</td>
+ </tr>
+ </table>
+
+ <h2><a name="13.4.2">13.4.2 Polymorphism Inheritance Changes</a></h2>
+
+ <p>Unlike reuse inheritance, adding or deleting a polymorphic base
+ does not result in the base's data members being added or deleted
+ from the derived object's table because each class in a polymorphic
+ hierarchy is stored in a separate table. There are, however, other
+ complications due to the presence of special columns (discriminator
+ in the root table and object id links in derived tables) which makes
+ altering the hierarchy structure difficult to handle automatically.
+ Adding or deleting (including soft-deleting) of leaf classes (or
+ leaf sub-hierarchies) in a polymorphic hierarchy is fully supported.
+ Any more complex changes, such as adding or deleting the root or
+ an intermediate base or getting an existing class into or out of
+ a polymorphic hierarchy can be handled by creating a new leaf class
+ (or leaf sub-hierarchy), soft-deleting the old class, and migrating
+ the data.</p>
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="14">14 ODB Pragma Language</a></h1>
+
+ <p>As we have already seen in previous chapters, ODB uses a pragma-based
+ language to capture database-specific information about C++ types.
+ This chapter describes the ODB pragma language in more detail. It
+ can be read together with other chapters in the manual to get a
+ sense of what kind of configurations and mapping fine-tuning are
+ possible. You can also use this chapter as a reference at a later
+ stage.</p>
+
+ <p>An ODB pragma has the following syntax:</p>
+
+ <p><code>#pragma db <i>qualifier</i> [<i>specifier</i> <i>specifier</i> ...]</code></p>
+
+ <p>The <em>qualifier</em> tells the ODB compiler what kind of C++ construct
+ this pragma describes. Valid qualifiers are <code>object</code>,
+ <code>view</code>, <code>value</code>, <code>member</code>,
+ <code>namespace</code>, <code>model</code>, <code>index</code>, and
+ <code>map</code>.
+ A pragma with the <code>object</code> qualifier describes a persistent
+ object type. It tells the ODB compiler that the C++ class it describes
+ is a persistent class. Similarly, pragmas with the <code>view</code>
+ qualifier describe view types, the <code>value</code> qualifier
+ describes value types and the <code>member</code> qualifier is used
+ to describe data members of persistent object, view, and value types.
+ The <code>namespace</code> qualifier is used to describe common
+ properties of objects, views, and value types that belong to
+ a C++ namespace while the <code>model</code> qualifier describes
+ the whole C++ object model. The <code>index</code> qualifier defines
+ a database index. And, finally, the <code>map</code> qualifier
+ describes a mapping between additional database types and types
+ for which ODB provides built-in support.</p>
+
+ <p>The <em>specifier</em> informs the ODB compiler about a particular
+ database-related property of the C++ declaration. For example, the
+ <code>id</code> member specifier tells the ODB compiler that this
+ member contains this object's identifier. Below is the declaration
+ of the <code>person</code> class that shows how we can use ODB
+ pragmas:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+private:
+ #pragma db member id
+ unsigned long id_;
+ ...
+};
+ </pre>
+
+ <p>In the above example we don't explicitly specify which C++ class or
+ data member the pragma belongs to. Rather, the pragma applies to
+ a C++ declaration that immediately follows the pragma. Such pragmas
+ are called <em>positioned pragmas</em>. In positioned pragmas that
+ apply to data members, the <code>member</code> qualifier can be
+ omitted for brevity, for example:</p>
+
+ <pre class="cxx">
+ #pragma db id
+ unsigned long id_;
+ </pre>
+
+ <p>Note also that if the C++ declaration immediately following a
+ position pragma is incompatible with the pragma qualifier, an
+ error will be issued. For example:</p>
+
+ <pre class="cxx">
+ #pragma db object // Error: expected class instead of data member.
+ unsigned long id_;
+ </pre>
+
+ <p>While keeping the C++ declarations and database declarations close
+ together eases maintenance and increases readability, we can also
+ place them in different parts of the same header file or even
+ factor them to a separate file. To achieve this we use the so called
+ <em>named pragmas</em>. Unlike positioned pragmas, named pragmas
+ explicitly specify the C++ declaration to which they apply by
+ adding the declaration name after the pragma qualifier. For example:</p>
+
+ <pre class="cxx">
+class person
+{
+ ...
+private:
+ unsigned long id_;
+ ...
+};
+
+#pragma db object(person)
+#pragma db member(person::id_) id
+ </pre>
+
+ <p>Note that in the named pragmas for data members the <code>member</code>
+ qualifier is no longer optional. The C++ declaration name in the
+ named pragmas is resolved using the standard C++ name resolution
+ rules, for example:</p>
+
+ <pre class="cxx">
+namespace db
+{
+ class person
+ {
+ ...
+ private:
+ unsigned long id_;
+ ...
+ };
+}
+
+namespace db
+{
+ #pragma db object(person) // Resolves db::person.
+}
+
+#pragma db member(db::person::id_) id
+ </pre>
+
+ <p>As another example, the following code fragment shows how to use the
+ named value type pragma to map a C++ type to a native database type:</p>
+
+ <pre class="cxx">
+#pragma db value(bool) type("INT")
+
+#pragma db object
+class person
+{
+ ...
+private:
+ bool married_; // Mapped to INT NOT NULL database type.
+ ...
+};
+ </pre>
+
+ <p>If we would like to factor the ODB pragmas into a separate file,
+ we can include this file into the original header file (the one
+ that defines the persistent types) using the <code>#include</code>
+ directive, for example:</p>
+
+ <pre class="cxx">
+// person.hxx
+
+class person
+{
+ ...
+};
+
+#ifdef ODB_COMPILER
+# include "person-pragmas.hxx"
+#endif
+ </pre>
+
+ <p>Alternatively, instead of using the <code>#include</code> directive,
+ we can use the <code>--odb-epilogue</code> option to make the pragmas
+ known to the ODB compiler when compiling the original header file,
+ for example:</p>
+
+ <pre class="terminal">
+--odb-epilogue '#include "person-pragmas.hxx"'
+ </pre>
+
+ <p>The following sections cover the specifiers applicable to all the
+ qualifiers mentioned above.</p>
+
+ <p>The C++ header file that defines our persistent classes and
+ normally contains one or more ODB pragmas is compiled by both
+ the ODB compiler to generate the database support code and
+ the C++ compiler to build the application. Some C++ compilers
+ issue warnings about pragmas that they do not recognize. There
+ are several ways to deal with this problem which are covered
+ at the end of this chapter in <a href="#14.9">Section 14.9,
+ "C++ Compiler Warnings"</a>.</p>
+
+ <h2><a name="14.1">14.1 Object Type Pragmas</a></h2>
+
+ <p>A pragma with the <code>object</code> qualifier declares a C++ class
+ as a persistent object type. The qualifier can be optionally followed,
+ in any order, by one or more specifiers summarized in the table below:</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table class="specifiers" border="1">
+ <tr>
+ <th>Specifier</th>
+ <th>Summary</th>
+ <th>Section</th>
+ </tr>
+
+ <tr>
+ <td><code>table</code></td>
+ <td>table name for a persistent class</td>
+ <td><a href="#14.1.1">14.1.1</a></td>
+ </tr>
+
+ <tr>
+ <td><code>pointer</code></td>
+ <td>pointer type for a persistent class</td>
+ <td><a href="#14.1.2">14.1.2</a></td>
+ </tr>
+
+ <tr>
+ <td><code>abstract</code></td>
+ <td>persistent class is abstract</td>
+ <td><a href="#14.1.3">14.1.3</a></td>
+ </tr>
+
+ <tr>
+ <td><code>readonly</code></td>
+ <td>persistent class is read-only</td>
+ <td><a href="#14.1.4">14.1.4</a></td>
+ </tr>
+
+ <tr>
+ <td><code>optimistic</code></td>
+ <td>persistent class with the optimistic concurrency model</td>
+ <td><a href="#14.1.5">14.1.5</a></td>
+ </tr>
+
+ <tr>
+ <td><code>no_id</code></td>
+ <td>persistent class has no object id</td>
+ <td><a href="#14.1.6">14.1.6</a></td>
+ </tr>
+
+ <tr>
+ <td><code>callback</code></td>
+ <td>database operations callback</td>
+ <td><a href="#14.1.7">14.1.7</a></td>
+ </tr>
+
+ <tr>
+ <td><code>schema</code></td>
+ <td>database schema for a persistent class</td>
+ <td><a href="#14.1.8">14.1.8</a></td>
+ </tr>
+
+ <tr>
+ <td><code>polymorphic</code></td>
+ <td>persistent class is polymorphic</td>
+ <td><a href="#14.1.9">14.1.9</a></td>
+ </tr>
+
+ <tr>
+ <td><code>session</code></td>
+ <td>enable/disable session support for a persistent class</td>
+ <td><a href="#14.1.10">14.1.10</a></td>
+ </tr>
+
+ <tr>
+ <td><code>definition</code></td>
+ <td>definition location for a persistent class</td>
+ <td><a href="#14.1.11">14.1.11</a></td>
+ </tr>
+
+ <tr>
+ <td><code>transient</code></td>
+ <td>all non-virtual data members in a persistent class are transient</td>
+ <td><a href="#14.1.12">14.1.12</a></td>
+ </tr>
+
+ <tr>
+ <td><code>sectionable</code></td>
+ <td>support addition of new sections in derived classes</td>
+ <td><a href="#14.1.13">14.1.13</a></td>
+ </tr>
+
+ <tr>
+ <td><code>deleted</code></td>
+ <td>persistent class is soft-deleted</td>
+ <td><a href="#14.1.14">14.1.14</a></td>
+ </tr>
+
+ <tr>
+ <td><code>bulk</code></td>
+ <td>enable bulk operations for a persistent class</td>
+ <td><a href="#14.1.15">14.1.15</a></td>
+ </tr>
+
+ <tr>
+ <td><code>options</code></td>
+ <td>database options for a persistent class</td>
+ <td><a href="#14.1.16">14.1.16</a></td>
+ </tr>
+
+ </table>
+
+ <h3><a name="14.1.1">14.1.1 <code>table</code></a></h3>
+
+ <p>The <code>table</code> specifier specifies the table name that should
+ be used to store objects of the persistent class in a relational
+ database. For example:</p>
+
+ <pre class="cxx">
+#pragma db object table("people")
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>If the table name is not specified, the class name is used as the
+ table name. The table name can be qualified with a database
+ schema, for example:</p>
+
+ <pre class="cxx">
+#pragma db object table("census.people")
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>For more information on database schemas and the format of the
+ qualified names, refer to <a href="#14.1.8">Section 14.1.8,
+ "<code>schema</code>"</a>.</p>
+
+ <h3><a name="14.1.2">14.1.2 <code>pointer</code></a></h3>
+
+ <p>The <code>pointer</code> specifier specifies the object pointer type
+ for the persistent class. The object pointer type is used to return,
+ pass, and cache dynamically allocated instances of a persistent
+ class. For example:</p>
+
+ <pre class="cxx">
+#pragma db object pointer(std::tr1::shared_ptr&lt;person>)
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>There are several ways to specify an object pointer with the
+ <code>pointer</code> specifier. We can use a complete pointer
+ type as shown in the example above. Alternatively, we can
+ specify only the template name of a smart pointer in which
+ case the ODB compiler will automatically append the class
+ name as a template argument. The following example is therefore
+ equivalent to the one above:</p>
+
+ <pre class="cxx">
+#pragma db object pointer(std::tr1::shared_ptr)
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>If you would like to use the raw pointer as an object pointer,
+ you can use <code>*</code> as a shortcut:</p>
+
+ <pre class="cxx">
+#pragma db object pointer(*) // Same as pointer(person*)
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>If a pointer type is not explicitly specified, the default pointer,
+ specified at the namespace level (<a href="#14.5.1">Section 14.5.1,
+ "<code>pointer</code>"</a>) or with the <code>--default-pointer</code>
+ ODB compiler option, is used. If neither of these two mechanisms is
+ used to specify the pointer, then the raw pointer is used by default.</p>
+
+ <p>For a more detailed discussion of object pointers, refer to
+ <a href="#3.3">Section 3.3, "Object and View Pointers"</a>.</p>
+
+ <h3><a name="14.1.3">14.1.3 <code>abstract</code></a></h3>
+
+ <p>The <code>abstract</code> specifier specifies that the persistent class
+ is abstract. An instance of an abstract class cannot be stored in
+ the database and is normally used as a base for other persistent
+ classes. For example:</p>
+
+ <pre class="cxx">
+#pragma db object abstract
+class person
+{
+ ...
+};
+
+#pragma db object
+class employee: public person
+{
+ ...
+};
+
+#pragma db object
+class contractor: public person
+{
+ ...
+};
+ </pre>
+
+ <p>Persistent classes with pure virtual functions are automatically
+ treated as abstract by the ODB compiler. For a more detailed
+ discussion of persistent class inheritance, refer to
+ <a href="#8">Chapter 8, "Inheritance"</a>.</p>
+
+ <h3><a name="14.1.4">14.1.4 <code>readonly</code></a></h3>
+
+ <p>The <code>readonly</code> specifier specifies that the persistent class
+ is read-only. The database state of read-only objects cannot be
+ updated. In particular, this means that you cannot call the
+ <code>database::update()</code> function (<a href="#3.10">Section 3.10,
+ "Updating Persistent Objects"</a>) for such objects. For example:</p>
+
+ <pre class="cxx">
+#pragma db object readonly
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>Read-only and read-write objects can derive from each other without
+ any restrictions. When a read-only object derives from a read-write
+ object, the resulting whole object is read-only, including the part
+ corresponding to the read-write base. On the other hand, when a
+ read-write object derives from a read-only object, all the data
+ members that correspond to the read-only base are treated as
+ read-only while the rest is treated as read-write.</p>
+
+ <p>Note that it is also possible to declare individual data members
+ (<a href="#14.4.12">Section 14.4.12, "<code>readonly</code>"</a>)
+ as well as composite value types (<a href="#14.3.6">Section 14.3.6,
+ "<code>readonly</code>"</a>) as read-only.</p>
+
+ <h3><a name="14.1.5">14.1.5 <code>optimistic</code></a></h3>
+
+ <p>The <code>optimistic</code> specifier specifies that the persistent class
+ has the optimistic concurrency model. A class with the optimistic
+ concurrency model must also specify the data member that is used to
+ store the object version using the <code>version</code> pragma
+ (<a href="#14.4.16">Section 14.4.16, "<code>version</code>"</a>).
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object optimistic
+class person
+{
+ ...
+
+ #pragma db version
+ unsigned long version_;
+};
+ </pre>
+
+ <p>If a base class has the optimistic concurrency model, then all its derived
+ classes will automatically have the optimistic concurrency model. The
+ current implementation also requires that in any given inheritance
+ hierarchy the object id and the version data members reside in the
+ same class.</p>
+
+ <p>For a more detailed discussion of optimistic concurrency, refer to
+ <a href="#12">Chapter 12, "Optimistic Concurrency"</a>.</p>
+
+ <h3><a name="14.1.6">14.1.6 <code>no_id</code></a></h3>
+
+ <p>The <code>no_id</code> specifier specifies that the persistent class
+ has no object id. For example:</p>
+
+ <pre class="cxx">
+#pragma db object no_id
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>A persistent class without an object id has limited functionality.
+ Such a class cannot be loaded with the <code>database::load()</code>
+ or <code>database::find()</code> functions (<a href="#3.9">Section 3.9,
+ "Loading Persistent Objects"</a>), updated with the
+ <code>database::update()</code> function (<a href="#3.10">Section 3.10,
+ "Updating Persistent Objects"</a>), or deleted with the
+ <code>database::erase()</code> function (<a href="#3.11">Section 3.11,
+ "Deleting Persistent Objects"</a>). To load and delete
+ objects without ids you can use the <code>database::query()</code>
+ (<a href="#4">Chapter 4, "Querying the Database"</a>) and
+ <code>database::erase_query()</code> (<a href="#3.11">Section 3.11,
+ "Deleting Persistent Objects"</a>) functions, respectively.
+ There is no way to update such objects except by using native SQL
+ statements (<a href="#3.12">Section 3.12, "Executing Native SQL
+ Statements"</a>).</p>
+
+ <p>Furthermore, persistent classes without object ids cannot have container
+ data members nor can they be used in object relationships. Such objects
+ are not entered into the session object cache
+ (<a href="#11.1">Section 11.1, "Object Cache"</a>) either.</p>
+
+ <p>To declare a persistent class with an object id, use the data member
+ <code>id</code> specifier (<a href="#14.4.1">Section 14.4.1,
+ "<code>id</code>"</a>).</p>
+
+ <h3><a name="14.1.7">14.1.7 <code>callback</code></a></h3>
+
+ <p>The <code>callback</code> specifier specifies the persist class
+ member function that should be called before and after a
+ database operation is performed on an object of this class.
+ For example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/callback.hxx>
+
+#pragma db object callback(init)
+class person
+{
+ ...
+
+ void
+ init (odb::callback_event, odb::database&amp;);
+};
+ </pre>
+
+ <p>The callback function has the following signature and can be
+ overloaded for constant objects:</p>
+
+ <pre class="cxx">
+void
+name (odb::callback_event, odb::database&amp;);
+
+void
+name (odb::callback_event, odb::database&amp;) const;
+ </pre>
+
+ <p>The first argument to the callback function is the event that
+ triggered this call. The <code>odb::callback_event</code>
+ enum-like type is defined in the <code>&lt;odb/callback.hxx></code>
+ header file and has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ struct callback_event
+ {
+ enum value
+ {
+ pre_persist,
+ post_persist,
+ pre_load,
+ post_load,
+ pre_update,
+ post_update,
+ pre_erase,
+ post_erase
+ };
+
+ callback_event (value v);
+ operator value () const;
+ };
+}
+ </pre>
+
+ <p>The second argument to the callback function is the database
+ on which the operation is about to be performed or has just
+ been performed. A callback function can be inline or virtual.</p>
+
+ <p>The callback function for the <code>*_persist</code>,
+ <code>*_update</code>, and <code>*_erase</code> events is always
+ called on the constant object reference while for the <code>*_load</code>
+ events &mdash; always on the unrestricted reference.</p>
+
+ <p>If only the non-<code>const</code> version of the callback function
+ is provided, then only the <code>*_load</code> events will be delivered.
+ If only the <code>const</code> version is provided, then all the
+ events will be delivered to this function. Finally, if both versions
+ are provided, then the <code>*_load</code> events will be delivered
+ to the non-<code>const</code> version while all others &mdash; to the
+ <code>const</code> version. If you need to modify the object in one
+ of the "<code>const</code>" events, then you can safely cast away
+ <code>const</code>-ness using the <code>const_cast</code> operator if
+ you know that none of the objects will be created const. Alternatively,
+ if you cannot make this assumption, then you can declare the data
+ members you wish to modify as <code>mutable</code>.</p>
+
+ <p>A database operations callback can be used to implement object-specific
+ pre and post initializations, registrations, and cleanups. As an example,
+ the following code fragment outlines an implementation of a
+ <code>person</code> class that maintains the transient <code>age</code>
+ data member in addition to the persistent date of birth. A callback
+ is used to calculate the value of the former from the latter every
+ time a <code>person</code> object is loaded from the database.</p>
+
+ <pre class="cxx">
+#include &lt;odb/core.hxx>
+#include &lt;odb/callback.hxx>
+
+#pragma db object callback(init)
+class person
+{
+ ...
+
+private:
+ friend class odb::access;
+
+ date born_;
+
+ #pragma db transient
+ unsigned short age_;
+
+ void
+ init (odb::callback_event e, odb::database&amp;)
+ {
+ switch (e)
+ {
+ case odb::callback_event::post_load:
+ {
+ // Calculate age from the date of birth.
+ ...
+ break;
+ }
+ default:
+ break;
+ }
+ }
+};
+ </pre>
+
+ <h3><a name="14.1.8">14.1.8 <code>schema</code></a></h3>
+
+ <p>The <code>schema</code> specifier specifies a database schema
+ that should be used for the persistent class.</p>
+
+ <p>In relational databases the term schema can refer to two related
+ but ultimately different concepts. Normally it means a collection
+ of tables, indexes, sequences, etc., that are created in the
+ database or the actual DDL statements that create these
+ database objects. Some database implementations support what
+ would be more accurately called a <em>database namespace</em>
+ but is also called a schema. In this sense, a schema is a
+ separate namespace in which tables, indexes, sequences, etc.,
+ can be created. For example, two tables that have the same
+ name can coexist in the same database if they belong to
+ different schemas. In this section when we talk about a
+ schema, we refer to the <em>database namespace</em> meaning
+ of this term. </p>
+
+ <p>When schemas are in use, a database object name is qualified
+ with a schema. For example:</p>
+
+ <pre class="sql">
+CREATE TABLE accounting.employee (...)
+
+SELECT ... FROM accounting.employee WHERE ...
+ </pre>
+
+ <p>In the above example <code>accounting</code> is the schema
+ and the <code>employee</code> table belongs to this
+ schema.</p>
+
+ <p>Not all database implementations support schemas. Some
+ implementation that don't support schemas (for example,
+ MySQL, SQLite) allow the use of the above syntax to specify
+ the database name. Yet others may support several levels
+ of qualification. For example, Microsoft SQL Server has
+ three levels starting with the linked database server,
+ followed by the database, and then followed by
+ the schema:
+ <code>server1.company1.accounting.employee</code>.
+ While the actual meaning of the qualifier in a qualified name
+ vary from one database implementation to another, here we
+ refer to all of them collectively as a schema.</p>
+
+ <p>In ODB, a schema for a table of a persistent class can be
+ specified at the class level, C++ namespace level, or the
+ file level. To assign a schema to a specific persistent class
+ we can use the <code>schema</code> specifier, for example:</p>
+
+ <pre class="cxx">
+#pragma db object schema("accounting")
+class employee
+{
+ ...
+};
+ </pre>
+
+ <p>If we are also assigning a table name, then we can use
+ a shorter notation by specifying both the schema and
+ the table name in the <code>table</code> specifier:</p>
+
+ <pre class="cxx">
+#pragma db object table("accounting.employee")
+class employee
+{
+ ...
+};
+ </pre>
+
+ <p>If we want to assign a schema to all the persistent classes
+ in a C++ namespace, then, instead of specifying the schema
+ for each class, we can specify it once at the C++ namespace level.
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db namespace schema("accounting")
+namespace accounting
+{
+ #pragma db object
+ class employee
+ {
+ ...
+ };
+
+ #pragma db object
+ class employer
+ {
+ ...
+ };
+}
+ </pre>
+
+ <p>If we want to assign a schema to all the persistent classes in
+ a file, then we can use the <code>--schema</code> ODB compiler
+ option. For example:</p>
+
+ <pre class="terminal">
+odb ... --schema accounting ...
+ </pre>
+
+ <p>An alternative to this approach with the same effect is to
+ assign a schema to the global namespace:</p>
+
+ <pre class="cxx">
+#pragma db namespace() schema("accounting")
+ </pre>
+
+ <p>By default schema qualifications are accumulated starting from
+ the persistent class, continuing with the namespace hierarchy
+ to which this class belongs, and finishing with the schema
+ specified with the <code>--schema</code> option. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db namespace schema("audit_db")
+namespace audit
+{
+ #pragma db namespace schema("accounting")
+ namespace accounting
+ {
+ #pragma db object
+ class employee
+ {
+ ...
+ };
+ }
+}
+ </pre>
+
+ <p>If we compile the above code fragment with the
+ <code>--schema&nbsp;server1</code> option, then the
+ <code>employee</code> table will have the
+ <code>server1.audit_db.accounting.employee</code> qualified
+ name.</p>
+
+ <p>In some situations we may want to prevent such accumulation
+ of the qualifications. To accomplish this we can use the
+ so-called fully-qualified names, which have the empty leading
+ name component. This is analogous to the C++ fully-qualified
+ names in the <code>::accounting::employee</code> form. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db namespace schema("accounting")
+namespace accounting
+{
+ #pragma db object schema(".hr")
+ class employee
+ {
+ ...
+ };
+
+ #pragma db object
+ class employer
+ {
+ ...
+ };
+}
+ </pre>
+
+ <p>In the above code fragment, the <code>employee</code> table will
+ have the <code>hr.employee</code> qualified name while the
+ <code>employer</code> &mdash; <code>accounting.employer</code>.
+ Note also that the empty leading name component is a special
+ ODB syntax and is not propagated to the actual database names
+ (using a name like <code>.hr.employee</code> to refer to a table
+ will most likely result in an error).</p>
+
+ <p>Auxiliary database objects for a persistent class, such as indexes,
+ sequences, triggers, etc., are all created in the same schema
+ as the class table. By default, this is also true for the
+ container tables. However, if you need to store a container
+ table in a different schema, then you can provide a qualified
+ name using the <code>table</code> specifier, for example:</p>
+
+ <pre class="cxx">
+#pragma db object table("accounting.employee")
+class employee
+{
+ ...
+
+ #pragma db object table("operations.projects")
+ std::vector&lt;std::string> projects_;
+};
+ </pre>
+
+ <p>The standard syntax for qualified names used in the
+ <code>schema</code> and <code>table</code> specifiers as well
+ as the view <code>column</code> specifier (<a href="#14.4.10">Section
+ 14.4.10, "<code>column</code> (view)"</a>) has the
+ <code>"</code><i>name</i><code>.</code><i>name</i>...<code>"</code>
+ form where, as discussed above, the leading name component
+ can be empty to denote a fully qualified name. This form, however,
+ doesn't work if one of the name components contains periods. To
+ support such cases the alternative form is available:
+ <code>"</code><i>name</i><code>"."</code><i>name</i><code>"</code>...
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object table("accounting_1.2"."employee")
+class employee
+{
+ ...
+};
+ </pre>
+
+ <p>Finally, to specify an unqualified name that contains periods
+ we can use the following special syntax:</p>
+
+ <pre class="cxx">
+#pragma db object schema(."accounting_1.2") table("employee")
+class employee
+{
+ ...
+};
+ </pre>
+
+ <p>Table prefixes (<a href="#14.5.2">Section 14.5.2, "<code>table</code>"</a>)
+ can be used as an alternative to database schemas if the target
+ database system does not support schemas.</p>
+
+ <h3><a name="14.1.9">14.1.9 <code>polymorphic</code></a></h3>
+
+ <p>The <code>polymorphic</code> specifier specifies that the persistent
+ class is polymorphic. For more information on polymorphism support,
+ refer to <a href="#8">Chapter 8, "Inheritance"</a>.</p>
+
+ <h3><a name="14.1.10">14.1.10 <code>session</code></a></h3>
+
+ <p>The <code>session</code> specifier specifies whether to enable
+ session support for the persistent class. For example:</p>
+
+ <pre class="cxx">
+#pragma db object session // Enable.
+class person
+{
+ ...
+};
+
+#pragma db object session(true) // Enable.
+class employee
+{
+ ...
+};
+
+#pragma db object session(false) // Disable.
+class employer
+{
+ ...
+};
+ </pre>
+
+ <p>Session support is disabled by default unless the
+ <code>--generate-session</code> ODB compiler option is specified
+ or session support is enabled at the namespace level
+ (<a href="#14.5.4">Section 14.5.4, "<code>session</code>"</a>).
+ For more information on sessions, refer to <a href="#11">Chapter
+ 11, "Session"</a>.</p>
+
+ <h3><a name="14.1.11">14.1.11 <code>definition</code></a></h3>
+
+ <p>The <code>definition</code> specifier specifies an alternative
+ <em>definition location</em> for the persistent class. By
+ default, the ODB compiler generates the database support code for
+ a persistent class when we compile the header file that
+ defines this class. However, if the <code>definition</code>
+ specifier is used, then the ODB compiler will instead generate
+ the database support code when we compile the header file
+ containing this pragma.</p>
+
+ <p>For more information on this functionality, refer to
+ <a href="#14.3.7">Section 14.3.7, "<code>definition</code>"</a>.</p>
+
+ <h3><a name="14.1.12">14.1.12 <code>transient</code></a></h3>
+
+ <p>The <code>transient</code> specifier instructs the ODB compiler to
+ treat all non-virtual data members in the persistent class as transient
+ (<a href="#14.4.1">Section 14.4.1, "<code>transient</code>"</a>).
+ This specifier is primarily useful when declaring virtual data
+ members, as discussed in <a href="#14.4.13">Section 14.4.13,
+ "<code>virtual</code>"</a>.</p>
+
+ <h3><a name="14.1.13">14.1.13 <code>sectionable</code></a></h3>
+
+ <p>The <code>sectionable</code> specifier instructs the ODB compiler
+ to generate support for the addition of new object sections in
+ derived classes in a hierarchy with the optimistic concurrency
+ model. For more information on this functionality, refer to
+ <a href="#9.2">Section 9.2, "Sections and Optimistic
+ Concurrency"</a>.</p>
+
+ <h3><a name="14.1.14">14.1.14 <code>deleted</code></a></h3>
+
+ <p>The <code>deleted</code> specifier marks the persistent class as
+ soft-deleted. The single required argument to this specifier is
+ the deletion version. For more information on this functionality,
+ refer to <a href="#13.4">Section 13.4, "Soft Object Model
+ Changes"</a>.</p>
+
+ <h3><a name="14.1.15">14.1.15 <code>bulk</code></a></h3>
+
+ <p>The <code>bulk</code> specifier enables bulk operation support for
+ the persistent class. The single required argument to this specifier
+ is the batch size. For more information on this functionality, refer
+ to <a href="#15.3">Section 15.3, "Bulk Database Operations"</a>.</p>
+
+ <h3><a name="14.1.16">14.1.16 <code>options</code></a></h3>
+
+ <p>The <code>options</code> specifier specifies additional table
+ definition options that should be used for the persistent class. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object options("PARTITION BY RANGE (age)")
+class person
+{
+ ...
+
+ unsigned short age_;
+};
+ </pre>
+
+ <p>Table definition options for a container table can be specified with
+ the <code>options</code> data member specifier
+ (<a href="#14.4.8">Section 14.4.8, "<code>options</code>"</a>). For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db options("PARTITION BY RANGE (index)")
+ std::vector&lt;std::string> aliases_;
+};
+ </pre>
+
+
+ <h2><a name="14.2">14.2 View Type Pragmas</a></h2>
+
+ <p>A pragma with the <code>view</code> qualifier declares a C++ class
+ as a view type. The qualifier can be optionally followed,
+ in any order, by one or more specifiers summarized in the
+ table below:</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table class="specifiers" border="1">
+ <tr>
+ <th>Specifier</th>
+ <th>Summary</th>
+ <th>Section</th>
+ </tr>
+
+ <tr>
+ <td><code>object</code></td>
+ <td>object associated with a view</td>
+ <td><a href="#14.2.1">14.2.1</a></td>
+ </tr>
+
+ <tr>
+ <td><code>table</code></td>
+ <td>table associated with a view</td>
+ <td><a href="#14.2.2">14.2.2</a></td>
+ </tr>
+
+ <tr>
+ <td><code>query</code></td>
+ <td>view query condition</td>
+ <td><a href="#14.2.3">14.2.3</a></td>
+ </tr>
+
+ <tr>
+ <td><code>pointer</code></td>
+ <td>pointer type for a view</td>
+ <td><a href="#14.2.4">14.2.4</a></td>
+ </tr>
+
+ <tr>
+ <td><code>callback</code></td>
+ <td>database operations callback</td>
+ <td><a href="#14.2.5">14.2.5</a></td>
+ </tr>
+
+ <tr>
+ <td><code>definition</code></td>
+ <td>definition location for a view</td>
+ <td><a href="#14.2.6">14.2.6</a></td>
+ </tr>
+
+ <tr>
+ <td><code>transient</code></td>
+ <td>all non-virtual data members in a view are transient</td>
+ <td><a href="#14.2.7">14.2.7</a></td>
+ </tr>
+
+ </table>
+
+ <p>For more information on view types refer to <a href="#10"> Chapter 10,
+ "Views"</a>.</p>
+
+ <h3><a name="14.2.1">14.2.1 <code>object</code></a></h3>
+
+ <p>The <code>object</code> specifier specifies a persistent class
+ that should be associated with the view. For more information
+ on object associations refer to <a href="#10.1">Section 10.1, "Object
+ Views"</a>.</p>
+
+ <h3><a name="14.2.2">14.2.2 <code>table</code></a></h3>
+
+ <p>The <code>table</code> specifier specifies a database table
+ that should be associated with the view. For more information
+ on table associations refer to <a href="#10.3">Section 10.3, "Table
+ Views"</a>.</p>
+
+ <h3><a name="14.2.3">14.2.3 <code>query</code></a></h3>
+
+ <p>The <code>query</code> specifier specifies a query condition
+ and, optionally, result modifiers for an object or table view
+ or a native SQL query for a native view. An empty <code>query</code>
+ specifier indicates that a native SQL query is provided at runtime.
+ For more information on query conditions refer to
+ <a href="#10.5">Section 10.5, "View Query Conditions"</a>. For
+ more information on native SQL queries, refer to
+ <a href="#10.6">Section 10.6, "Native Views"</a>.</p>
+
+ <h3><a name="14.2.4">14.2.4 <code>pointer</code></a></h3>
+
+ <p>The <code>pointer</code> specifier specifies the view pointer type
+ for the view class. Similar to objects, the view pointer type is used
+ to return dynamically allocated instances of a view class. The
+ semantics of the <code>pointer</code> specifier for a view are the
+ same as those of the <code>pointer</code> specifier for an object
+ (<a href="#14.1.2">Section 14.1.2, "<code>pointer</code>"</a>).</p>
+
+ <h3><a name="14.2.5">14.2.5 <code>callback</code></a></h3>
+
+ <p>The <code>callback</code> specifier specifies the view class
+ member function that should be called before and after an
+ instance of this view class is created as part of the query
+ result iteration. The semantics of the <code>callback</code>
+ specifier for a view are similar to those of the
+ <code>callback</code> specifier for an object
+ (<a href="#14.1.7">Section 14.1.7, "<code>callback</code>"</a>)
+ except that the only events that can trigger a callback
+ call in the case of a view are <code>pre_load</code> and
+ <code>post_load</code>.</p>
+
+ <h3><a name="14.2.6">14.2.6 <code>definition</code></a></h3>
+
+ <p>The <code>definition</code> specifier specifies an alternative
+ <em>definition location</em> for the view class. By
+ default, the ODB compiler generates the database support code for
+ a view class when we compile the header file that
+ defines this class. However, if the <code>definition</code>
+ specifier is used, then the ODB compiler will instead generate
+ the database support code when we compile the header file
+ containing this pragma.</p>
+
+ <p>For more information on this functionality, refer to
+ <a href="#14.3.7">Section 14.3.7, "<code>definition</code>"</a>.</p>
+
+ <h3><a name="14.2.7">14.2.7 <code>transient</code></a></h3>
+
+ <p>The <code>transient</code> specifier instructs the ODB compiler
+ to treat all non-virtual data members in the view class as transient
+ (<a href="#14.4.1">Section 14.4.1, "<code>transient</code>"</a>).
+ This specifier is primarily useful when declaring virtual data
+ members, as discussed in <a href="#14.4.13">Section 14.4.13,
+ "<code>virtual</code>"</a>.</p>
+
+ <h2><a name="14.3">14.3 Value Type Pragmas</a></h2>
+
+ <p>A pragma with the <code>value</code> qualifier describes a value
+ type. It can be optionally followed, in any order, by one or more
+ specifiers summarized in the table below:</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table class="specifiers" border="1">
+ <tr>
+ <th>Specifier</th>
+ <th>Summary</th>
+ <th>Section</th>
+ </tr>
+
+ <tr>
+ <td><code>type</code></td>
+ <td>database type for a value type</td>
+ <td><a href="#14.3.1">14.3.1</a></td>
+ </tr>
+
+ <tr>
+ <td><code>id_type</code></td>
+ <td>database type for a value type when used as an object id</td>
+ <td><a href="#14.3.2">14.3.2</a></td>
+ </tr>
+
+ <tr>
+ <td><code>null</code>/<code>not_null</code></td>
+ <td>type can/cannot be <code>NULL</code></td>
+ <td><a href="#14.3.3">14.3.3</a></td>
+ </tr>
+
+ <tr>
+ <td><code>default</code></td>
+ <td>default value for a value type</td>
+ <td><a href="#14.3.4">14.3.4</a></td>
+ </tr>
+
+ <tr>
+ <td><code>options</code></td>
+ <td>database options for a value type</td>
+ <td><a href="#14.3.5">14.3.5</a></td>
+ </tr>
+
+ <tr>
+ <td><code>readonly</code></td>
+ <td>composite value type is read-only</td>
+ <td><a href="#14.3.6">14.3.6</a></td>
+ </tr>
+
+ <tr>
+ <td><code>definition</code></td>
+ <td>definition location for a composite value type</td>
+ <td><a href="#14.3.7">14.3.7</a></td>
+ </tr>
+
+ <tr>
+ <td><code>transient</code></td>
+ <td>all non-virtual data members in a composite value are transient</td>
+ <td><a href="#14.3.8">14.3.8</a></td>
+ </tr>
+
+ <tr>
+ <td><code>unordered</code></td>
+ <td>ordered container should be stored unordered</td>
+ <td><a href="#14.3.9">14.3.9</a></td>
+ </tr>
+
+ <tr>
+ <td><code>index_type</code></td>
+ <td>database type for a container's index type</td>
+ <td><a href="#14.3.10">14.3.10</a></td>
+ </tr>
+
+ <tr>
+ <td><code>key_type</code></td>
+ <td>database type for a container's key type</td>
+ <td><a href="#14.3.11">14.3.11</a></td>
+ </tr>
+
+ <tr>
+ <td><code>value_type</code></td>
+ <td>database type for a container's value type</td>
+ <td><a href="#14.3.12">14.3.12</a></td>
+ </tr>
+
+ <tr>
+ <td><code>value_null</code>/<code>value_not_null</code></td>
+ <td>container's value can/cannot be <code>NULL</code></td>
+ <td><a href="#14.3.13">14.3.13</a></td>
+ </tr>
+
+ <tr>
+ <td><code>id_options</code></td>
+ <td>database options for a container's id column</td>
+ <td><a href="#14.3.14">14.3.14</a></td>
+ </tr>
+
+ <tr>
+ <td><code>index_options</code></td>
+ <td>database options for a container's index column</td>
+ <td><a href="#14.3.15">14.3.15</a></td>
+ </tr>
+
+ <tr>
+ <td><code>key_options</code></td>
+ <td>database options for a container's key column</td>
+ <td><a href="#14.3.16">14.3.16</a></td>
+ </tr>
+
+ <tr>
+ <td><code>value_options</code></td>
+ <td>database options for a container's value column</td>
+ <td><a href="#14.3.17">14.3.17</a></td>
+ </tr>
+
+ <tr>
+ <td><code>id_column</code></td>
+ <td>column name for a container's object id</td>
+ <td><a href="#14.3.18">14.3.18</a></td>
+ </tr>
+
+ <tr>
+ <td><code>index_column</code></td>
+ <td>column name for a container's index</td>
+ <td><a href="#14.3.19">14.3.19</a></td>
+ </tr>
+
+ <tr>
+ <td><code>key_column</code></td>
+ <td>column name for a container's key</td>
+ <td><a href="#14.3.20">14.3.20</a></td>
+ </tr>
+
+ <tr>
+ <td><code>value_column</code></td>
+ <td>column name for a container's value</td>
+ <td><a href="#14.3.21">14.3.21</a></td>
+ </tr>
+
+ </table>
+
+ <p>Many of the value type specifiers have corresponding member type
+ specifiers with the same names (<a href="#14.4">Section 14.4,
+ "Data Member Pragmas"</a>). The behavior of such specifiers
+ for members is similar to that for value types. The only difference
+ is the scope. A particular value type specifier applies to all the
+ members of this value type that don't have a pre-member version
+ of the specifier, while the member specifier always applies only
+ to a single member. Also, with a few exceptions, member specifiers
+ take precedence over and override parameters specified with value
+ specifiers.</p>
+
+ <h3><a name="14.3.1">14.3.1 <code>type</code></a></h3>
+
+ <p>The <code>type</code> specifier specifies the native database type
+ that should be used for data members of this type. For example:</p>
+
+ <pre class="cxx">
+#pragma db value(bool) type("INT")
+
+#pragma db object
+class person
+{
+ ...
+
+ bool married_; // Mapped to INT NOT NULL database type.
+};
+ </pre>
+
+ <p>The ODB compiler provides the default mapping between common C++
+ types, such as <code>bool</code>, <code>int</code>, and
+ <code>std::string</code> and the database types for each supported
+ database system. For more information on the default mapping,
+ refer to <a href="#II">Part II, "Database Systems"</a>. The
+ <code>null</code> and <code>not_null</code> (<a href="#14.3.3">Section
+ 14.3.3, "<code>null</code>/<code>not_null</code>"</a>) specifiers
+ can be used to control the <code>NULL</code> semantics of a type.</p>
+
+ <p>In the above example we changed the mapping for the <code>bool</code>
+ type which is now mapped to the <code>INT</code> database type. In
+ this case, the <code>value</code> pragma is all that is necessary
+ since the ODB compiler will be able to figure out how to store
+ a boolean value as an integer in the database. However, there
+ could be situations where the ODB compiler will not know how to
+ handle the conversion between the C++ and database representations
+ of a value. Consider, as an example, a situation where the
+ boolean value is stored in the database as a string:</p>
+
+ <pre class="cxx">
+#pragma db value(bool) type("VARCHAR(5)")
+ </pre>
+
+ <p>The possible database values for the C++ <code>true</code> value could
+ be <code>"true"</code>, or <code>"TRUE"</code>, or <code>"True"</code>.
+ Or, maybe, all of the above could be valid. The ODB compiler has no way
+ of knowing how your application wants to convert <code>bool</code>
+ to a string and back. To support such custom value type mappings,
+ ODB allows you to provide your own database conversion functions
+ by specializing the <code>value_traits</code> class template. The
+ <code>mapping</code> example in the <code>odb-examples</code>
+ package shows how to do this for all the supported database systems.</p>
+
+ <h3><a name="14.3.2">14.3.2 <code>id_type</code></a></h3>
+
+ <p>The <code>id_type</code> specifier specifies the native database type
+ that should be used for data members of this type that are designated as
+ object identifiers (<a href="#14.4.1">Section 14.4.1,
+ "<code>id</code>"</a>). In combination with the <code>type</code>
+ specifier (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>)
+ <code>id_type</code> allows you to map a C++ type differently depending
+ on whether it is used in an ordinary member or an object id. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db value(std::string) type("TEXT") id_type("VARCHAR(64)")
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id
+ std::string email_; // Mapped to VARCHAR(64) NOT NULL.
+
+ std::string name_; // Mapped to TEXT NOT NULL.
+};
+ </pre>
+
+ <p>Note that there is no corresponding member type specifier for
+ <code>id_type</code> since the desired result can be achieved
+ with just the <code>type</code> specifier, for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id type("VARCHAR(128)")
+ std::string email_;
+};
+ </pre>
+
+ <h3><a name="14.3.3">14.3.3 <code>null</code>/<code>not_null</code></a></h3>
+
+ <p>The <code>null</code> and <code>not_null</code> specifiers specify that
+ a value type or object pointer can or cannot be <code>NULL</code>,
+ respectively. By default, value types are assumed not to allow
+ <code>NULL</code> values while object pointers are assumed to
+ allow <code>NULL</code> values. Data members of types that allow
+ <code>NULL</code> values are mapped in a relational database to
+ columns that allow <code>NULL</code> values. For example:</p>
+
+ <pre class="cxx">
+using std::tr1::shared_ptr;
+
+typedef shared_ptr&lt;std::string> string_ptr;
+#pragma db value(string_ptr) type("TEXT") null
+
+#pragma db object
+class person
+{
+ ...
+
+ string_ptr name_; // Mapped to TEXT NULL.
+};
+
+typedef shared_ptr&lt;person> person_ptr;
+#pragma db value(person_ptr) not_null
+ </pre>
+
+ <p>The <code>NULL</code> semantics can also be specified on the
+ per-member basis (<a href="#14.4.6">Section 14.4.6,
+ "<code>null</code>/<code>not_null</code>"</a>). If both a type and
+ a member have <code>null</code>/<code>not_null</code> specifiers,
+ then the member specifier takes precedence. If a member specifier
+ relaxes the <code>NULL</code> semantics (that is, if a member has
+ the <code>null</code> specifier and the type has the explicit
+ <code>not_null</code> specifier), then a warning is issued.</p>
+
+ <p>It is also possible to override a previously specified
+ <code>null</code>/<code>not_null</code> specifier. This is
+ primarily useful if a third-party type, for example,
+ one provided by a profile library (<a href="#III">Part III,
+ "Profiles"</a>), allows <code>NULL</code> values but in your
+ object model data members of this type should never be
+ <code>NULL</code>. In this case you can use the <code>not_null</code>
+ specifier to disable <code>NULL</code> values for this type for the
+ entire translation unit. For example:</p>
+
+ <pre class="cxx">
+// By default, null_string allows NULL values.
+//
+#include &lt;null-string.hxx>
+
+// Disable NULL values for all the null_string data members.
+//
+#pragma db value(null_string) not_null
+ </pre>
+
+ <p>For a more detailed discussion of the <code>NULL</code> semantics
+ for values, refer to <a href="#7.3">Section 7.3, "Pointers and
+ <code>NULL</code> Value Semantics"</a>. For a more detailed
+ discussion of the <code>NULL</code> semantics for object pointers,
+ refer to <a href="#6">Chapter 6, "Relationships"</a>.</p>
+
+ <h3><a name="14.3.4">14.3.4 <code>default</code></a></h3>
+
+ <p>The <code>default</code> specifier specifies the database default value
+ that should be used for data members of this type. For example:</p>
+
+ <pre class="cxx">
+#pragma db value(std::string) default("")
+
+#pragma db object
+class person
+{
+ ...
+
+ std::string name_; // Mapped to TEXT NOT NULL DEFAULT ''.
+};
+ </pre>
+
+ <p>The semantics of the <code>default</code> specifier for a value type
+ are similar to those of the <code>default</code> specifier for a
+ data member (<a href="#14.4.7">Section 14.4.7,
+ "<code>default</code>"</a>).</p>
+
+ <h3><a name="14.3.5">14.3.5 <code>options</code></a></h3>
+
+ <p>The <code>options</code> specifier specifies additional column
+ definition options that should be used for data members of this
+ type. For example:</p>
+
+ <pre class="cxx">
+#pragma db value(std::string) options("COLLATE binary")
+
+#pragma db object
+class person
+{
+ ...
+
+ std::string name_; // Mapped to TEXT NOT NULL COLLATE binary.
+};
+ </pre>
+
+ <p>The semantics of the <code>options</code> specifier for a value type
+ are similar to those of the <code>options</code> specifier for a
+ data member (<a href="#14.4.8">Section 14.4.8,
+ "<code>options</code>"</a>).</p>
+
+ <h3><a name="14.3.6">14.3.6 <code>readonly</code></a></h3>
+
+ <p>The <code>readonly</code> specifier specifies that the composite
+ value type is read-only. Changes to data members of a read-only
+ composite value type are ignored when updating the database
+ state of an object (<a href="#3.10">Section 3.10, "Updating Persistent
+ Objects"</a>) containing such a value type. Note that this specifier
+ is only valid for composite value types. For example:</p>
+
+ <pre class="cxx">
+#pragma db value readonly
+class person_name
+{
+ ...
+};
+ </pre>
+
+ <p>Read-only and read-write composite values can derive from each other
+ without any restrictions. When a read-only value derives from a
+ read-write value, the resulting whole value is read-only, including
+ the part corresponding to the read-write base. On the other hand, when a
+ read-write value derives from a read-only value, all the data
+ members that correspond to the read-only base are treated as
+ read-only while the rest is treated as read-write.</p>
+
+ <p>Note that it is also possible to declare individual data members
+ (<a href="#14.4.12">Section 14.4.12, "<code>readonly</code>"</a>)
+ as well as whole objects (<a href="#14.1.4">Section 14.1.4,
+ "<code>readonly</code>"</a>) as read-only.</p>
+
+ <h3><a name="14.3.7">14.3.7 <code>definition</code></a></h3>
+
+ <p>The <code>definition</code> specifier specifies an alternative
+ <em>definition location</em> for the composite value type. By
+ default, the ODB compiler generates the database support code for
+ a composite value type when we compile the header file that
+ defines this value type. However, if the <code>definition</code>
+ specifier is used, then the ODB compiler will instead generate
+ the database support code when we compile the header file containing
+ this pragma.</p>
+
+ <p>This mechanism is primarily useful for converting third-party
+ types to ODB composite value types. In such cases we normally
+ cannot modify the header files to add the necessary pragmas.
+ It is also often inconvenient to compile these header files
+ with the ODB compiler. With the <code>definition</code>
+ specifier we can create a <em>wrapper header</em> that contains
+ the necessary pragmas and instructs the ODB compiler to generate
+ the database support code for a third-party type when we compile
+ the wrapper header. As an example, consider <code>struct timeval</code>
+ that is defined in the <code>&lt;sys/time.h></code> system header.
+ This type has the following (or similar) definition:</p>
+
+ <pre class="cxx">
+struct timeval
+{
+ long tv_sec;
+ long tv_usec;
+};
+ </pre>
+
+ <p>If we would like to make this type an ODB composite value type,
+ then we can create a wrapper header, for example
+ <code>time-mapping.hxx</code>, with the following content:</p>
+
+ <pre class="cxx">
+#ifndef TIME_MAPPING_HXX
+#define TIME_MAPPING_HXX
+
+#include &lt;sys/time.h>
+
+#pragma db value(timeval) definition
+#pragma db member(timeval::tv_sec) column("sec")
+#pragma db member(timeval::tv_usec) column("usec")
+
+#endif // TIME_MAPPING_HXX
+ </pre>
+
+ <p>If we now compile this header with the ODB compiler, the
+ resulting <code>time-mapping-odb.?xx</code> files will
+ contain the database support code for <code>struct timeval</code>.
+ To use <code>timeval</code> in our persistent classes, we simply
+ include the <code>time-mapping.hxx</code> header:</p>
+
+ <pre class="cxx">
+#include &lt;sys/time.h>
+#include "time-mapping.hxx"
+
+#pragma db object
+class object
+{
+ timeval timestamp;
+};
+ </pre>
+
+ <h3><a name="14.3.8">14.3.8 <code>transient</code></a></h3>
+
+ <p>The <code>transient</code> specifier instructs the ODB compiler
+ to treat all non-virtual data members in the composite value type
+ as transient (<a href="#14.4.1">Section 14.4.1,
+ "<code>transient</code>"</a>). This specifier is primarily useful
+ when declaring virtual data members, as discussed in
+ <a href="#14.4.13">Section 14.4.13, "<code>virtual</code>"</a>.</p>
+
+ <h3><a name="14.3.9">14.3.9 <code>unordered</code></a></h3>
+
+ <p>The <code>unordered</code> specifier specifies that the ordered
+ container should be stored unordered in the database. The database
+ table for such a container will not contain the index column
+ and the order in which elements are retrieved from the database may
+ not be the same as the order in which they were stored. For example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;std::string> names;
+#pragma db value(names) unordered
+ </pre>
+
+ <p>For a more detailed discussion of ordered containers and their
+ storage in the database, refer to <a href="#5.1">Section 5.1,
+ "Ordered Containers"</a>.</p>
+
+ <h3><a name="14.3.10">14.3.10 <code>index_type</code></a></h3>
+
+ <p>The <code>index_type</code> specifier specifies the native
+ database type that should be used for the ordered container's
+ index column. The semantics of <code>index_type</code>
+ are similar to those of the <code>type</code> specifier
+ (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>). The native
+ database type is expected to be an integer type. For example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;std::string> names;
+#pragma db value(names) index_type("SMALLINT UNSIGNED")
+ </pre>
+
+ <h3><a name="14.3.11">14.3.11 <code>key_type</code></a></h3>
+
+ <p>The <code>key_type</code> specifier specifies the native
+ database type that should be used for the map container's
+ key column. The semantics of <code>key_type</code>
+ are similar to those of the <code>type</code> specifier
+ (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>). For
+ example:</p>
+
+ <pre class="cxx">
+typedef std::map&lt;unsigned short, float> age_weight_map;
+#pragma db value(age_weight_map) key_type("INT UNSIGNED")
+ </pre>
+
+ <h3><a name="14.3.12">14.3.12 <code>value_type</code></a></h3>
+
+ <p>The <code>value_type</code> specifier specifies the native
+ database type that should be used for the container's
+ value column. The semantics of <code>value_type</code>
+ are similar to those of the <code>type</code> specifier
+ (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>). For
+ example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;std::string> names;
+#pragma db value(names) value_type("VARCHAR(255)")
+ </pre>
+
+ <p>The <code>value_null</code> and <code>value_not_null</code>
+ (<a href="#14.3.13">Section 14.3.13,
+ "<code>value_null</code>/<code>value_not_null</code>"</a>) specifiers
+ can be used to control the <code>NULL</code> semantics of a value
+ column.</p>
+
+ <h3><a name="14.3.13">14.3.13 <code>value_null</code>/<code>value_not_null</code></a></h3>
+
+ <p>The <code>value_null</code> and <code>value_not_null</code> specifiers
+ specify that the container type's element value can or cannot be
+ <code>NULL</code>, respectively. The semantics of <code>value_null</code>
+ and <code>value_not_null</code> are similar to those of the
+ <code>null</code> and <code>not_null</code> specifiers
+ (<a href="#14.3.3">Section 14.3.3, "<code>null</code>/<code>not_null</code>"</a>).
+ For example:</p>
+
+ <pre class="cxx">
+using std::tr1::shared_ptr;
+
+#pragma db object
+class account
+{
+ ...
+};
+
+typedef std::vector&lt;shared_ptr&lt;account> > accounts;
+#pragma db value(accounts) value_not_null
+ </pre>
+
+ <p>For set and multiset containers (<a href="#5.2">Section 5.2, "Set and
+ Multiset Containers"</a>) the element value is automatically treated
+ as not allowing a <code>NULL</code> value.</p>
+
+
+ <h3><a name="14.3.14">14.3.14 <code>id_options</code></a></h3>
+
+ <p>The <code>id_options</code> specifier specifies additional
+ column definition options that should be used for the container's
+ id column. For example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;std::string> nicknames;
+#pragma db value(nicknames) id_options("COLLATE binary")
+ </pre>
+
+ <p>The semantics of the <code>id_options</code> specifier for a container
+ type are similar to those of the <code>id_options</code> specifier for
+ a container data member (<a href="#14.4.29">Section 14.4.29,
+ "<code>id_options</code>"</a>).</p>
+
+
+ <h3><a name="14.3.15">14.3.15 <code>index_options</code></a></h3>
+
+ <p>The <code>index_options</code> specifier specifies additional
+ column definition options that should be used for the container's
+ index column. For example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;std::string> nicknames;
+#pragma db value(nicknames) index_options("ZEROFILL")
+ </pre>
+
+ <p>The semantics of the <code>index_options</code> specifier for a container
+ type are similar to those of the <code>index_options</code> specifier for
+ a container data member (<a href="#14.4.30">Section 14.4.30,
+ "<code>index_options</code>"</a>).</p>
+
+
+ <h3><a name="14.3.16">14.3.16 <code>key_options</code></a></h3>
+
+ <p>The <code>key_options</code> specifier specifies additional
+ column definition options that should be used for the container's
+ key column. For example:</p>
+
+ <pre class="cxx">
+typedef std::map&lt;std::string, std::string> properties;
+#pragma db value(properties) key_options("COLLATE binary")
+ </pre>
+
+ <p>The semantics of the <code>key_options</code> specifier for a container
+ type are similar to those of the <code>key_options</code> specifier for
+ a container data member (<a href="#14.4.31">Section 14.4.31,
+ "<code>key_options</code>"</a>).</p>
+
+
+ <h3><a name="14.3.17">14.3.17 <code>value_options</code></a></h3>
+
+ <p>The <code>value_options</code> specifier specifies additional
+ column definition options that should be used for the container's
+ value column. For example:</p>
+
+ <pre class="cxx">
+typedef std::set&lt;std::string> nicknames;
+#pragma db value(nicknames) value_options("COLLATE binary")
+ </pre>
+
+ <p>The semantics of the <code>value_options</code> specifier for a container
+ type are similar to those of the <code>value_options</code> specifier for
+ a container data member (<a href="#14.4.32">Section 14.4.32,
+ "<code>value_options</code>"</a>).</p>
+
+
+ <h3><a name="14.3.18">14.3.18 <code>id_column</code></a></h3>
+
+ <p>The <code>id_column</code> specifier specifies the column
+ name that should be used to store the object id in the
+ container's table. For example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;std::string> names;
+#pragma db value(names) id_column("id")
+ </pre>
+
+ <p>If the column name is not specified, then <code>object_id</code>
+ is used by default.</p>
+
+ <h3><a name="14.3.19">14.3.19 <code>index_column</code></a></h3>
+
+ <p>The <code>index_column</code> specifier specifies the column
+ name that should be used to store the element index in the
+ ordered container's table. For example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;std::string> names;
+#pragma db value(names) index_column("name_number")
+ </pre>
+
+ <p>If the column name is not specified, then <code>index</code>
+ is used by default.</p>
+
+ <h3><a name="14.3.20">14.3.20 <code>key_column</code></a></h3>
+
+ <p>The <code>key_column</code> specifier specifies the column
+ name that should be used to store the key in the map
+ container's table. For example:</p>
+
+ <pre class="cxx">
+typedef std::map&lt;unsigned short, float> age_weight_map;
+#pragma db value(age_weight_map) key_column("age")
+ </pre>
+
+ <p>If the column name is not specified, then <code>key</code>
+ is used by default.</p>
+
+ <h3><a name="14.3.21">14.3.21 <code>value_column</code></a></h3>
+
+ <p>The <code>value_column</code> specifier specifies the column
+ name that should be used to store the element value in the
+ container's table. For example:</p>
+
+ <pre class="cxx">
+typedef std::map&lt;unsigned short, float> age_weight_map;
+#pragma db value(age_weight_map) value_column("weight")
+ </pre>
+
+ <p>If the column name is not specified, then <code>value</code>
+ is used by default.</p>
+
+ <!-- Data Member Pragmas -->
+
+
+ <h2><a name="14.4">14.4 Data Member Pragmas</a></h2>
+
+ <p>A pragma with the <code>member</code> qualifier or a positioned
+ pragma without a qualifier describes a data member. It can
+ be optionally followed, in any order, by one or more specifiers
+ summarized in the table below:</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table class="specifiers" border="1">
+ <tr>
+ <th>Specifier</th>
+ <th>Summary</th>
+ <th>Section</th>
+ </tr>
+
+ <tr>
+ <td><code>id</code></td>
+ <td>member is an object id</td>
+ <td><a href="#14.4.1">14.4.1</a></td>
+ </tr>
+
+ <tr>
+ <td><code>auto</code></td>
+ <td>id is assigned by the database</td>
+ <td><a href="#14.4.2">14.4.2</a></td>
+ </tr>
+
+ <tr>
+ <td><code>type</code></td>
+ <td>database type for a member</td>
+ <td><a href="#14.4.3">14.4.3</a></td>
+ </tr>
+
+ <tr>
+ <td><code>id_type</code></td>
+ <td>database type for a member when used as an object id</td>
+ <td><a href="#14.4.4">14.4.4</a></td>
+ </tr>
+
+ <tr>
+ <td><code>get</code>/<code>set</code>/<code>access</code></td>
+ <td>member accessor/modifier expressions</td>
+ <td><a href="#14.4.5">14.4.5</a></td>
+ </tr>
+
+ <tr>
+ <td><code>null</code>/<code>not_null</code></td>
+ <td>member can/cannot be <code>NULL</code></td>
+ <td><a href="#14.4.6">14.4.6</a></td>
+ </tr>
+
+ <tr>
+ <td><code>default</code></td>
+ <td>default value for a member</td>
+ <td><a href="#14.4.7">14.4.7</a></td>
+ </tr>
+
+ <tr>
+ <td><code>options</code></td>
+ <td>database options for a member</td>
+ <td><a href="#14.4.8">14.4.8</a></td>
+ </tr>
+
+ <tr>
+ <td><code>column</code></td>
+ <td>column name for a member of an object or composite value</td>
+ <td><a href="#14.4.9">14.4.9</a></td>
+ </tr>
+
+ <tr>
+ <td><code>column</code></td>
+ <td>column name for a member of a view</td>
+ <td><a href="#14.4.10">14.4.10</a></td>
+ </tr>
+
+ <tr>
+ <td><code>transient</code></td>
+ <td>member is not stored in the database</td>
+ <td><a href="#14.4.11">14.4.11</a></td>
+ </tr>
+
+ <tr>
+ <td><code>readonly</code></td>
+ <td>member is read-only</td>
+ <td><a href="#14.4.12">14.4.12</a></td>
+ </tr>
+
+ <tr>
+ <td><code>virtual</code></td>
+ <td>declare a virtual data member</td>
+ <td><a href="#14.4.13">14.4.13</a></td>
+ </tr>
+
+ <tr>
+ <td><code>inverse</code></td>
+ <td>member is an inverse side of a bidirectional relationship</td>
+ <td><a href="#14.4.14">14.4.14</a></td>
+ </tr>
+
+ <tr>
+ <td><code>on_delete</code></td>
+ <td><code>ON DELETE</code> clause for object pointer member</td>
+ <td><a href="#14.4.15">14.4.15</a></td>
+ </tr>
+
+ <tr>
+ <td><code>version</code></td>
+ <td>member stores object version</td>
+ <td><a href="#14.4.16">14.4.16</a></td>
+ </tr>
+
+ <tr>
+ <td><code>index</code></td>
+ <td>define database index for a member</td>
+ <td><a href="#14.4.17">14.4.17</a></td>
+ </tr>
+
+ <tr>
+ <td><code>unique</code></td>
+ <td>define unique database index for a member</td>
+ <td><a href="#14.4.18">14.4.18</a></td>
+ </tr>
+
+ <tr>
+ <td><code>unordered</code></td>
+ <td>ordered container should be stored unordered</td>
+ <td><a href="#14.4.19">14.4.19</a></td>
+ </tr>
+
+ <tr>
+ <td><code>table</code></td>
+ <td>table name for a container</td>
+ <td><a href="#14.4.20">14.4.20</a></td>
+ </tr>
+
+ <tr>
+ <td><code>load</code>/<code>update</code></td>
+ <td>loading/updating behavior for a section</td>
+ <td><a href="#14.4.21">14.4.21</a></td>
+ </tr>
+
+ <tr>
+ <td><code>section</code></td>
+ <td>member belongs to a section</td>
+ <td><a href="#14.4.22">14.4.22</a></td>
+ </tr>
+
+ <tr>
+ <td><code>added</code></td>
+ <td>member is soft-added</td>
+ <td><a href="#14.4.23">14.4.23</a></td>
+ </tr>
+
+ <tr>
+ <td><code>deleted</code></td>
+ <td>member is soft-deleted</td>
+ <td><a href="#14.4.24">14.4.24</a></td>
+ </tr>
+
+ <tr>
+ <td><code>index_type</code></td>
+ <td>database type for a container's index type</td>
+ <td><a href="#14.4.25">14.4.25</a></td>
+ </tr>
+
+ <tr>
+ <td><code>key_type</code></td>
+ <td>database type for a container's key type</td>
+ <td><a href="#14.4.26">14.4.26</a></td>
+ </tr>
+
+ <tr>
+ <td><code>value_type</code></td>
+ <td>database type for a container's value type</td>
+ <td><a href="#14.4.27">14.4.27</a></td>
+ </tr>
+
+ <tr>
+ <td><code>value_null</code>/<code>value_not_null</code></td>
+ <td>container's value can/cannot be <code>NULL</code></td>
+ <td><a href="#14.4.28">14.4.28</a></td>
+ </tr>
+
+ <tr>
+ <td><code>id_options</code></td>
+ <td>database options for a container's id column</td>
+ <td><a href="#14.4.29">14.4.29</a></td>
+ </tr>
+
+ <tr>
+ <td><code>index_options</code></td>
+ <td>database options for a container's index column</td>
+ <td><a href="#14.4.30">14.4.30</a></td>
+ </tr>
+
+ <tr>
+ <td><code>key_options</code></td>
+ <td>database options for a container's key column</td>
+ <td><a href="#14.4.31">14.4.31</a></td>
+ </tr>
+
+ <tr>
+ <td><code>value_options</code></td>
+ <td>database options for a container's value column</td>
+ <td><a href="#14.4.32">14.4.32</a></td>
+ </tr>
+
+ <tr>
+ <td><code>id_column</code></td>
+ <td>column name for a container's object id</td>
+ <td><a href="#14.4.33">14.4.33</a></td>
+ </tr>
+
+ <tr>
+ <td><code>index_column</code></td>
+ <td>column name for a container's index</td>
+ <td><a href="#14.4.34">14.4.34</a></td>
+ </tr>
+
+ <tr>
+ <td><code>key_column</code></td>
+ <td>column name for a container's key</td>
+ <td><a href="#14.4.35">14.4.35</a></td>
+ </tr>
+
+ <tr>
+ <td><code>value_column</code></td>
+ <td>column name for a container's value</td>
+ <td><a href="#14.4.36">14.4.36</a></td>
+ </tr>
+
+ </table>
+
+ <p>Many of the member specifiers have corresponding value type
+ specifiers with the same names (<a href="#14.3">Section 14.3,
+ "Value Type Pragmas"</a>). The behavior of such specifiers
+ for members is similar to that for value types. The only difference
+ is the scope. A particular value type specifier applies to all the
+ members of this value type that don't have a pre-member version
+ of the specifier, while the member specifier always applies only
+ to a single member. Also, with a few exceptions, member specifiers
+ take precedence over and override parameters specified with value
+ specifiers.</p>
+
+ <h3><a name="14.4.1">14.4.1 <code>id</code></a></h3>
+
+ <p>The <code>id</code> specifier specifies that the data member contains
+ the object id. In a relational database, an identifier member is
+ mapped to a primary key. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id
+ std::string email_;
+};
+ </pre>
+
+ <p>Normally, every persistent class has a data member designated as an
+ object's identifier. However, it is possible to declare a
+ persistent class without an id using the object <code>no_id</code>
+ specifier (<a href="#14.1.6">Section 14.1.6, "<code>no_id</code>"</a>).</p>
+
+ <p>Note also that the <code>id</code> specifier cannot be used for data
+ members of composite value types or views.</p>
+
+ <h3><a name="14.4.2">14.4.2 <code>auto</code></a></h3>
+
+ <p>The <code>auto</code> specifier specifies that the object's identifier
+ is automatically assigned by the database. Only a member that was
+ designated as an object id can have this specifier. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id_;
+};
+ </pre>
+
+ <p>Note that automatically-assigned object ids are not reused.
+ If you have a high object turnover (that is, objects are routinely
+ made persistent and then erased), then care must be taken not to
+ run out of object ids. In such situations, using
+ <code>unsigned&nbsp;long&nbsp;long</code> as the identifier type
+ is a safe choice.</p>
+
+ <p>For additional information on the automatic identifier assignment,
+ refer to <a href="#3.8">Section 3.8, "Making Objects Persistent"</a>.</p>
+
+ <p>Note also that the <code>auto</code> specifier cannot be specified
+ for data members of composite value types or views.</p>
+
+ <h3><a name="14.4.3">14.4.3 <code>type</code></a></h3>
+
+ <p>The <code>type</code> specifier specifies the native database type
+ that should be used for the data member. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db type("INT")
+ bool married_;
+};
+ </pre>
+
+ <p>The <code>null</code> and <code>not_null</code> (<a href="#14.4.6">Section
+ 14.4.6, "<code>null</code>/<code>not_null</code>"</a>) specifiers
+ can be used to control the <code>NULL</code> semantics of a data member.
+ It is also possible to specify the database type on the per-type instead
+ of the per-member basis using the value <code>type</code>
+ specifier (<a href="#14.3.1">Section 14.3.1, "<code>type</code>"</a>).</p>
+
+ <h3><a name="14.4.4">14.4.4 <code>id_type</code></a></h3>
+
+ <p>The <code>id_type</code> specifier specifies the native database type
+ that should be used for the data member when it is part of an
+ object identifier. This specifier only makes sense when applied to
+ a member of a composite value type that is used for both id and
+ non-id members. For example:</p>
+
+ <pre class="cxx">
+#pragma db value
+class name
+{
+ ...
+
+ #pragma db type("VARCHAR(256)") id_type("VARCHAR(64)")
+ std::string first_;
+
+ #pragma db type("VARCHAR(256)") id_type("VARCHAR(64)")
+ std::string last_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id
+ name name_; // name_.first_, name_.last_ mapped to VARCHAR(64)
+
+ name alias_; // alias_.first_, alias_.last_ mapped to VARCHAR(256)
+};
+ </pre>
+
+ <h3><a name="14.4.5">14.4.5 <code>get</code>/<code>set</code>/<code>access</code></a></h3>
+
+ <p>The <code>get</code> and <code>set</code> specifiers specify the
+ data member accessor and modifier expressions, respectively. If
+ provided, the generated database support code will use these
+ expressions to access and modify the data member when performing
+ database operations. The <code>access</code> specifier can be used
+ as a shortcut to specify both the accessor and modifier if they
+ happen to be the same.</p>
+
+ <p>In its simplest form the accessor or modifier expression can be
+ just a name. Such a name should resolve either to another data
+ member of the same type or to a suitable accessor or modifier
+ member function. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+public:
+ const std::string&amp; name () const;
+ void name (const std::string&amp;);
+private:
+ #pragma db access(name)
+ std::string name_;
+};
+ </pre>
+
+ <p>A suitable accessor function is a <code>const</code> member function
+ that takes no arguments and whose return value can be implicitly
+ converted to the <code>const</code> reference to the member type
+ (<code>const&nbsp;std::string&amp;</code> in the example above).
+ An accessor function that returns a <code>const</code> reference
+ to the data member is called <em>by-reference accessor</em>.
+ Otherwise, it is called <em>by-value accessor</em>.</p>
+
+ <p>A suitable modifier function can be of two forms. It can be the
+ so called <em>by-reference modifier</em> which is a member function
+ that takes no arguments and returns a non-<code>const</code> reference
+ to the data member (<code>std::string&amp;</code> in the example above).
+ Alternatively, it can be the so called <em>by-value modifier</em> which
+ is a member function taking a single argument &mdash; the new value
+ &mdash; that can be implicitly initialized from a variable of the member
+ type (<code>std::string</code> in the example above). The return value
+ of a by-value modifier, if any, is ignored. If both by-reference and
+ by-value modifiers are available, then ODB prefers the by-reference
+ version since it is more efficient. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+public:
+ std::string get_name () const; // By-value accessor.
+ std::string&amp; set_name (); // By-reference modifier.
+ void set_name (std::string const&amp;); // By-value modifier.
+private:
+ #pragma db get(get_name) \ // Uses by-value accessor.
+ set(set_name) // Uses by-reference modifier.
+ std::string name_;
+};
+ </pre>
+
+ <p>Note that in many cases it is not necessary to specify accessor and
+ modifier functions explicitly since the ODB compiler will try to
+ discover them automatically in case the data member will be inaccessible
+ to the generated code. In particular, in both of the above examples
+ the ODB compiler would have successfully discovered the necessary
+ functions. For more information on this functionality, refer to
+ <a href="#3.2">Section 3.2, "Declaring Persistent Objects and
+ Values"</a>.</p>
+
+ <p>Note also that by-value accessors and by-value modifiers cannot be
+ used for certain data members in certain situations. These limitations
+ are discussed in more detail later in this section.</p>
+
+ <p>Accessor and modifier expressions can be more elaborate than simple
+ names. An accessor expression is any C++ expression that can be
+ used to initialize a <code>const</code> reference to the member
+ type. Similar to accessor functions, which are just a special case
+ of accessor expressions, an accessor expression that evaluates to a
+ <code>const</code> reference to the data member is called
+ <em>by-reference accessor expression</em>. Otherwise, it is
+ called <em>by-value accessor expression</em>.</p>
+
+ <p>Modifier expressions can also be of two forms: <em>by-reference
+ modifier expression</em> and <em>by-value modifier expression</em>
+ (again, modifier functions are just a special case of modifier
+ expressions). A by-reference modifier expression is any C++
+ expression that evaluates to the non-<code>const</code> reference
+ to the member type. A by-value modifier expression can be a
+ single or multiple (separated by semicolon) C++ statements
+ with the effect of setting the new member value.</p>
+
+ <p>There are two special placeholders that are recognized by the
+ ODB compiler in accessor and modifier expressions. The first
+ is the <code>this</code> keyword which denotes a reference
+ (note: not a pointer) to the persistent object. In accessor
+ expressions this reference is <code>const</code> while in
+ modifier expressions it is non-<code>const</code>. If an
+ expression does not contain the <code>this</code> placeholder,
+ then the ODB compiler automatically prefixes it with <code>this.</code>
+ sequence.</p>
+
+ <p>The second placeholder, the <code>(?)</code> sequence, is used
+ to denote the new value in by-value modifier expressions. The
+ ODB compiler replaces the question mark with the variable name,
+ keeping the surrounding parenthesis. The following example shows
+ a few more interesting accessor and modifier expressions:</p>
+
+ <pre class="cxx">
+#pragma db value
+struct point
+{
+ point (int, int);
+
+ int x;
+ int y;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ public:
+ const char* name () const;
+ void name (const char*);
+ private:
+ #pragma db get(std::string (this.name ())) \
+ set(name ((?).c_str ())) // The same as this.name (...).
+ std::string name_;
+
+ public:
+ const std::unique_ptr&lt;account>&amp; acc () const;
+ void acc (std::unique_ptr&lt;account>);
+ private:
+ #pragma db set(acc (std::move (?)))
+ std::unique_ptr&lt;account> acc_;
+
+ public:
+ int loc_x () const
+ int loc_y () const
+ void loc_x (int);
+ void loc_y (int);
+ private:
+ #pragma db get(point (this.loc_x (), this.loc_y ())) \
+ set(this.loc_x ((?).x); this.loc_y ((?).y))
+ point loc_;
+};
+ </pre>
+
+ <p>When the data member is of an array type, then the terms "reference"
+ and "member type" in the above discussion should be replaced with
+ "pointer" and "array element type", respectively. That is, the accessor
+ expression for an array member is any C++ expression that can be
+ used to initialize a <code>const</code> pointer to the array
+ element type, and so on. The following example shows common
+ accessor and modifier signatures for array members:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ public:
+ const char* id () const; // By-reference accessor.
+ void id (const char*); // By-value modifier.
+ private:
+ char id_[16];
+
+ public:
+ const char* pub_key () const; // By-reference accessor.
+ char* pub_key (); // By-reference modifier.
+ private:
+ char pub_key_[2048];
+};
+ </pre>
+
+ <p>Accessor and modifier expressions can be used with data members
+ of simple value, composite value, container, and object pointer
+ types. They can be used for data members in persistent classes,
+ composite value types, and views. There is also a mechanism
+ related to accessors and modifiers called virtual data members
+ and which is discussed in <a href="#14.4.13">Section 14.4.13,
+ "<code>virtual</code>"</a>.</p>
+
+ <p>There are, however, certain limitations when it comes to using
+ by-value accessor and modifier expressions. First of all, if a
+ by-value modifier is used, then the data member type should be
+ default-constructible. Furthermore, a composite value type that
+ has a container member cannot be modified with a by-value modifier.
+ Only a by-reference modifier expression can be used. The ODB
+ compiler will detect such cases and issue diagnostics. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db value
+struct name
+{
+ std::string first_;
+ std::string last_;
+ std::vector&lt;std::string> aliases_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+public:
+ const name&amp; name () const;
+ void name (const name&amp;);
+private:
+ #pragma db access(name) // Error: by-value modifier.
+ name name_;
+};
+ </pre>
+
+ <p>In certain database systems it is also not possible to use by-value
+ accessor and modifier expression with certain database types.
+ The ODB compiler is only able to detect such cases and issue diagnostics
+ if you specified accessor/modifier function names as opposed to custom
+ expressions. For more information on these database and type-specific
+ limitations, refer to the "Limitations" sections in <a href="#II">Part
+ II, "Database Systems"</a>.</p>
+
+ <h3><a name="14.4.6">14.4.6 <code>null</code>/<code>not_null</code></a></h3>
+
+ <p>The <code>null</code> and <code>not_null</code> specifiers specify that
+ the data member can or cannot be <code>NULL</code>, respectively.
+ By default, data members of basic value types for which database
+ mapping is provided by the ODB compiler do not allow <code>NULL</code>
+ values while data members of object pointers allow <code>NULL</code>
+ values. Other value types, such as those provided by the profile
+ libraries (<a href="#III">Part III, "Profiles"</a>), may or may
+ not allow <code>NULL</code> values, depending on the semantics
+ of each value type. Consult the relevant documentation to find
+ out more about the <code>NULL</code> semantics for such value
+ types. A data member containing the object id (<a href="#14.4.1">Section
+ 14.4.1, "<code>id</code>"</a>) is automatically treated as not
+ allowing a <code>NULL</code> value. Data members that
+ allow <code>NULL</code> values are mapped in a relational database
+ to columns that allow <code>NULL</code> values. For example:</p>
+
+ <pre class="cxx">
+using std::tr1::shared_ptr;
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db null
+ std::string name_;
+};
+
+#pragma db object
+class account
+{
+ ...
+
+ #pragma db not_null
+ shared_ptr&lt;person> holder_;
+};
+ </pre>
+
+ <p>The <code>NULL</code> semantics can also be specified on the
+ per-type basis (<a href="#14.3.3">Section 14.3.3,
+ "<code>null</code>/<code>not_null</code>"</a>). If both a type and
+ a member have <code>null</code>/<code>not_null</code> specifiers,
+ then the member specifier takes precedence. If a member specifier
+ relaxes the <code>NULL</code> semantics (that is, if a member has
+ the <code>null</code> specifier and the type has the explicit
+ <code>not_null</code> specifier), then a warning is issued.</p>
+
+ <p>For a more detailed discussion of the <code>NULL</code> semantics
+ for values, refer to <a href="#7.3">Section 7.3, "Pointers and
+ <code>NULL</code> Value Semantics"</a>. For a more detailed
+ discussion of the <code>NULL</code> semantics for object pointers,
+ refer to <a href="#6">Chapter 6, "Relationships"</a>.</p>
+
+ <h3><a name="14.4.7">14.4.7 <code>default</code></a></h3>
+
+ <p>The <code>default</code> specifier specifies the database default value
+ that should be used for the data member. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db default(-1)
+ int age_; // Mapped to INT NOT NULL DEFAULT -1.
+};
+ </pre>
+
+ <p>A default value can be the special <code>null</code> keyword,
+ a <code>bool</code> literal (<code>true</code> or <code>false</code>),
+ an integer literal, a floating point literal, a string literal, or
+ an enumerator name. If you need to specify a default value that is
+ an expression, for example an SQL function call, then you can use
+ the <code>options</code> specifier (<a href="#14.4.8">Section
+ 14.4.8, "<code>options</code>"</a>) instead. For example:</p>
+
+ <pre class="cxx">
+enum gender {male, female, undisclosed};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db default(null)
+ odb::nullable&lt;std::string> middle_; // DEFAULT NULL
+
+ #pragma db default(false)
+ bool married_; // DEFAULT 0/FALSE
+
+ #pragma db default(0.0)
+ float weight_; // DEFAULT 0.0
+
+ #pragma db default("Mr")
+ string title_; // DEFAULT 'Mr'
+
+ #pragma db default(undisclosed)
+ gender gender_; // DEFAULT 2/'undisclosed'
+
+ #pragma db options("DEFAULT CURRENT_TIMESTAMP()")
+ date timestamp_; // DEFAULT CURRENT_TIMESTAMP()
+};
+ </pre>
+
+ <p>Default values specified as enumerators are only supported for
+ members that are mapped to an <code>ENUM</code> or an integer
+ type in the database, which is the case for the automatic
+ mapping of C++ enums and enum classes to suitable database
+ types as performed by the ODB compiler. If you have mapped
+ a C++ enum or enum class to another database type, then you
+ should use a literal corresponding to that type to specify
+ the default value. For example:</p>
+
+ <pre class="cxx">
+enum gender {male, female, undisclosed};
+#pragma db value(gender) type("VARCHAR(11)")
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db default("undisclosed")
+ gender gender_; // DEFAULT 'undisclosed'
+};
+ </pre>
+
+ <p>A default value can also be specified on the per-type basis
+ (<a href="#14.3.4">Section 14.3.4, "<code>default</code>"</a>).
+ An empty <code>default</code> specifier can be used to reset
+ a default value that was previously specified on the per-type
+ basis. For example:</p>
+
+ <pre class="cxx">
+#pragma db value(std::string) default("")
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db default()
+ std::string name_; // No default value.
+};
+ </pre>
+
+ <p>A data member containing the object id (<a href="#14.4.1">Section
+ 14.4.1, "<code>id</code>"</a> ) is automatically treated as not
+ having a default value even if its type specifies a default value.</p>
+
+ <p>Note also that default values do not affect the generated C++ code
+ in any way. In particular, no automatic initialization of data members
+ with their default values is performed at any point. If you need such
+ an initialization, you will need to implement it yourself, for example,
+ in your persistent class constructors. The default values only
+ affect the generated database schemas and, in the context of ODB,
+ are primarily useful for schema evolution.</p>
+
+ <p>Additionally, the <code>default</code> specifier cannot be specified
+ for view data members.</p>
+
+ <h3><a name="14.4.8">14.4.8 <code>options</code></a></h3>
+
+ <p>The <code>options</code> specifier specifies additional column
+ definition options that should be used for the data member. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db options("CHECK(email != '')")
+ std::string email_; // Mapped to TEXT NOT NULL CHECK(email != '').
+};
+ </pre>
+
+ <p>Note that if specified for the container member, then instead of the
+ column definition options it specifies the table definition options for
+ the container table (<a href="#14.1.16">Section 14.1.16,
+ "<code>options</code>"</a>).</p>
+
+ <p>Options can also be specified on the per-type basis
+ (<a href="#14.3.5">Section 14.3.5, "<code>options</code>"</a>).
+ By default, options are accumulating. That is, the ODB compiler
+ first adds all the options specified for a value type followed
+ by all the options specified for a data member. To clear the
+ accumulated options at any point in this sequence you can use
+ an empty <code>options</code> specifier. For example:</p>
+
+ <pre class="cxx">
+#pragma db value(std::string) options("COLLATE binary")
+
+#pragma db object
+class person
+{
+ ...
+
+ std::string first_; // TEXT NOT NULL COLLATE binary
+
+ #pragma db options("CHECK(last != '')")
+ std::string last_; // TEXT NOT NULL COLLATE binary CHECK(last != '')
+
+ #pragma db options()
+ std::string title_; // TEXT NOT NULL
+
+ #pragma db options() options("CHECK(email != '')")
+ std::string email_; // TEXT NOT NULL CHECK(email != '')
+};
+ </pre>
+
+ <p>ODB provides dedicated specifiers for specifying column types
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>),
+ <code>NULL</code> constraints (<a href="#14.4.6">Section 14.4.6,
+ "<code>null</code>/<code>not_null</code>"</a>), and default
+ values (<a href="#14.4.7">Section 14.4.7, "<code>default</code>"</a>).
+ For ODB to function correctly these specifiers should always be
+ used instead of the opaque <code>options</code> specifier for
+ these components of a column definition.</p>
+
+ <p>Note also that the <code>options</code> specifier cannot be specified
+ for view data members.</p>
+
+ <h3><a name="14.4.9">14.4.9 <code>column</code> (object, composite value)</a></h3>
+
+ <p>The <code>column</code> specifier specifies the column name
+ that should be used to store the data member of a persistent class
+ or composite value type in a relational database. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id column("person_id")
+ unsigned long id_;
+};
+ </pre>
+
+ <p>For a member of a composite value type, the <code>column</code> specifier
+ specifies the column name prefix. Refer to <a href="#7.2.2">Section 7.2.2,
+ "Composite Value Column and Table Names"</a> for details.</p>
+
+ <p>If the column name is not specified, it is derived from the member's
+ so-called public name. A public member name is obtained by removing
+ the common data member name decorations, such as leading and trailing
+ underscores, the <code>m_</code> prefix, etc.</p>
+
+ <h3><a name="14.4.10">14.4.10 <code>column</code> (view)</a></h3>
+
+ <p>The <code>column</code> specifier can be used to specify the associated
+ object data member, the potentially qualified column name, or the column
+ expression for the data member of a view class. For more information,
+ refer to <a href="#10.1">Section 10.1, "Object Views"</a> and
+ <a href="#10.3">Section 10.3, "Table Views"</a>.</p>
+
+ <h3><a name="14.4.11">14.4.11 <code>transient</code></a></h3>
+
+ <p>The <code>transient</code> specifier instructs the ODB compiler
+ not to store the data member in the database. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ date born_;
+
+ #pragma db transient
+ unsigned short age_; // Computed from born_.
+};
+ </pre>
+
+ <p>This pragma is usually used on computed members, pointers and
+ references that are only meaningful in the application's
+ memory, as well as utility members such as mutexes, etc.</p>
+
+ <h3><a name="14.4.12">14.4.12 <code>readonly</code></a></h3>
+
+ <p>The <code>readonly</code> specifier specifies that the data member of
+ an object or composite value type is read-only. Changes to a read-only
+ data member are ignored when updating the database state of an object
+ (<a href="#3.10">Section 3.10, "Updating Persistent Objects"</a>)
+ containing such a member. Since views are read-only, it is not
+ necessary to use this specifier for view data members. Object id
+ (<a href="#14.4.1">Section 14.4.1, "<code>id</code>"</a>)
+ and inverse (<a href="#14.4.14">Section 14.4.14,
+ "<code>inverse</code>"</a>) data members are automatically treated
+ as read-only and must not be explicitly declared as such. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db readonly
+ date born_;
+};
+ </pre>
+
+ <p>Besides simple value members, object pointer, container, and composite
+ value members can also be declared read-only. A change of a pointed-to
+ object is ignored when updating the state of a read-only object
+ pointer. Similarly, any changes to the number or order of
+ elements or to the element values themselves are ignored when
+ updating the state of a read-only container. Finally, any changes
+ to the members of a read-only composite value type are also ignored
+ when updating the state of such a composite value.</p>
+
+ <p>ODB automatically treats <code>const</code> data members as read-only.
+ For example, the following <code>person</code> object is equivalent
+ to the above declaration for the database persistence purposes:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ const date born_; // Automatically read-only.
+};
+ </pre>
+
+ <p>When declaring an object pointer <code>const</code>, make sure to
+ declare the pointer as <code>const</code> rather than (or in addition
+ to) the object itself. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ const person* father_; // Read-write pointer to a read-only object.
+ person* const mother_; // Read-only pointer to a read-write object.
+};
+ </pre>
+
+ <p>Note that in case of a wrapper type (<a href="#7.3">Section 7.3,
+ "Pointers and <code>NULL</code> Value Semantics"</a>), both the
+ wrapper and the wrapped type must be <code>const</code> in
+ order for the ODB compiler to automatically treat the data
+ member as read-only. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ const std::auto_ptr&lt;const date> born_;
+};
+ </pre>
+
+ <p>Read-only members are useful when dealing with
+ asynchronous changes to the state of a data member in the
+ database which should not be overwritten. In other cases,
+ where the state of a data member never changes, declaring such a member
+ read-only allows ODB to perform more efficient object updates.
+ In such cases, however, it is conceptually more correct to
+ declare such a data member as <code>const</code> rather than
+ as read-only.</p>
+
+ <p>Note that it is also possible to declare composite value types
+ (<a href="#14.3.6">Section 14.3.6, "<code>readonly</code>"</a>)
+ as well as whole objects (<a href="#14.1.4">Section 14.1.4,
+ "<code>readonly</code>"</a>) as read-only.</p>
+
+ <h3><a name="14.4.13">14.4.13 <code>virtual</code></a></h3>
+
+ <p>The <code>virtual</code> specifier is used to declare a virtual
+ data member in an object, view, or composite value type. A virtual
+ data member is an <em>imaginary</em> data member that is only
+ used for the purpose of database persistence. A virtual data
+ member does not actually exist (that is, occupy space) in the
+ C++ class. Note also that virtual data members have nothing to
+ do with C++ virtual functions or virtual inheritance. Specifically,
+ no virtual function call overhead is incurred when using virtual
+ data members.</p>
+
+ <p>To declare a virtual data member we must specify the data
+ member name using the <code>member</code> specifier. We must
+ also specify the data member type with the <code>virtual</code>
+ specifier. Finally, the virtual data member declaration must
+ also specify the accessor and modifier expressions, unless
+ suitable accessor and modifier functions can automatically be
+ found by the ODB compiler (<a href="#14.4.5">Section 14.4.5,
+ "<code>get</code>/<code>set</code>/<code>access</code>"</a>).
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ // Transient real data member that actually stores the data.
+ //
+ #pragma db transient
+ std::string name_;
+
+ // Virtual data member.
+ //
+ #pragma db member(name) virtual(std::string) access(name_)
+};
+ </pre>
+
+ <p>From the pragma language point of view, a virtual data member
+ behaves exactly like a normal data member. Specifically, we
+ can reference the virtual data member after it has been
+ declared and use positioned pragmas before its declaration.
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db transient
+ std::string name_;
+
+ #pragma db access(name_)
+ #pragma db member(name) virtual(std::string)
+};
+
+#pragma db member(person::name) column("person_name")
+#pragma db index member(person::name)
+ </pre>
+
+ <p>We can also declare a virtual data member outside the class
+ scope:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ std::string name_;
+};
+
+#pragma db member(person::name_) transient
+#pragma db member(person::name) virtual(std::string) access(name_)
+ </pre>
+
+ <p>While in the above examples using virtual data members doesn't
+ seem to yield any benefits, this mechanism can be useful in a
+ number of situations. As one example, consider the need to
+ aggregate or dis-aggregate a data member:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db transient
+ std::pair&lt;std::string, std::string> name_;
+
+ #pragma db member(first) virtual(std::string) access(name_.first)
+ #pragma db member(last) virtual(std::string) access(name_.second)
+};
+ </pre>
+
+ <p>We can also use virtual data members to implement composite
+ object ids that are spread over multiple data members:</p>
+
+ <pre class="cxx">
+#pragma db value
+struct name
+{
+ name () {}
+ name (std::string const&amp; f, std::string const&amp; l)
+ : first (f), last(l) {}
+
+ std::string first;
+ std::string last;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db transient
+ std::string first_;
+
+ #pragma db transient
+ std::string last_;
+
+ #pragma db member(name) virtual(name) id \
+ get(::name (this.first_, this.last_)) \
+ set(this.first_ = (?).first; this.last_ = (?).last)
+};
+ </pre>
+
+ <p>Another common situation that calls for virtual data members is
+ a class that uses the pimpl idiom. While the following code
+ fragment outlines the idea, for details refer to the
+ <code>pimpl</code> example in the <code>odb-examples</code>
+ package.</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+public:
+ std::string const&amp; name () const;
+ void name (std::string const&amp;);
+
+ unsigned short age () const;
+ void age (unsigned short);
+
+ ...
+
+private:
+ class impl;
+
+ #pragma db transient
+ impl* pimpl_;
+
+ #pragma db member(name) virtual(std::string) // Uses name().
+ #pragma db member(age) virtual(unsigned short) // Uses age().
+};
+ </pre>
+
+ <p>The above example also shows that names used for virtual data
+ members (<code>name</code> and <code>age</code> in our case) can
+ be the same as the names of accessor/modifier functions. The only
+ names that virtual data members cannot clash with are those of
+ other data members, virtual or real.</p>
+
+ <p>A common pattern in the above examples is the need to
+ declare the real data member that actually stores the
+ data as transient. If all the real data members in a
+ class are treated as transient, then we can use the
+ class-level <code>transient</code> specifier
+ (<a href="#14.1.12">Section 14.1.12, "<code>transient</code>
+ (object)"</a>,
+ <a href="#14.3.8">Section 14.3.8, "<code>transient</code>
+ (composite value)"</a>,
+ <a href="#14.2.7">Section 14.2.7, "<code>transient</code>
+ (view)"</a>)
+ instead of doing it for each individual member. For example: </p>
+
+ <pre class="cxx">
+#pragma db object transient
+class person
+{
+ ...
+
+ std::string first_; // Transient.
+ std::string last_; // Transient.
+
+ #pragma db member(name) virtual(name) ...
+};
+ </pre>
+
+ <p>The ability to treat all the real data members as transient
+ becomes more important if we don't know the names of these
+ data members. This is often the case when we are working
+ with third-party types that document the accessor and
+ modifier functions but not the names of their private data
+ members. As an example, consider the <code>point</code> class
+ defined in a third-party <code>&lt;point></code> header file:</p>
+
+ <pre class="cxx">
+class point
+{
+public:
+ point ();
+ point (int x, int y);
+
+ int x () const;
+ int y () const;
+
+ void x (int);
+ void y (int);
+
+private:
+ ...
+};
+ </pre>
+
+ <p>To convert this class to an ODB composite value type we could
+ create the <code>point-mapping.hxx</code> file with the following
+ content:</p>
+
+ <pre class="cxx">
+#include &lt;point>
+
+#pragma db value(point) transient definition
+#pragma db member(point::x) virtual(int)
+#pragma db member(point::y) virtual(int)
+ </pre>
+
+ <p>Virtual data members can be of simple value, composite value,
+ container, or object pointer types. They can be used in persistent
+ classes, composite value types, and views.</p>
+
+ <h3><a name="14.4.14">14.4.14 <code>inverse</code></a></h3>
+
+ <p>The <code>inverse</code> specifier specifies that the data member of
+ an object pointer or a container of object pointers type is an
+ inverse side of a bidirectional object relationship. The single
+ required argument to this specifier is the corresponding data
+ member name in the referenced object. For example:</p>
+
+ <pre class="cxx">
+using std::tr1::shared_ptr;
+using std::tr1::weak_ptr;
+
+class person;
+
+#pragma db object pointer(shared_ptr)
+class employer
+{
+ ...
+
+ std::vector&lt;shared_ptr&lt;person> > employees_;
+};
+
+#pragma db object pointer(shared_ptr)
+class person
+{
+ ...
+
+ #pragma db inverse(employee_)
+ weak_ptr&lt;employer> employer_;
+};
+ </pre>
+
+ <p>An inverse member does not have a corresponding column or, in case
+ of a container, table in the resulting database schema. Instead, the
+ column or table from the referenced object is used to retrieve the
+ relationship information. Only ordered and set containers can be used
+ for inverse members. If an inverse member is of an ordered container
+ type, it is automatically marked as unordered
+ (<a href="#14.4.19">Section 14.4.19, "<code>unordered</code>"</a>).</p>
+
+ <p>For a more detailed discussion of inverse members, refer to
+ <a href="#6.2">Section 6.2, "Bidirectional Relationships"</a>.</p>
+
+ <h3><a name="14.4.15">14.4.15 <code>on_delete</code></a></h3>
+
+ <p>The <code>on_delete</code> specifier specifies the on-delete semantics
+ for a data member of an object pointer or a container of object
+ pointers type. The single required argument to this specifier must
+ be either <code>cascade</code> or <code>set_null</code>.</p>
+
+ <p>The <code>on_delete</code> specifier is translated directly to the
+ corresponding <code>ON DELETE</code> SQL clause. That is, if
+ <code>cascade</code> is specified, then when a pointed-to object
+ is erased from the database, the database state of the pointing
+ object is automatically erased as well. If <code>set_null</code> is
+ specified, then when a pointed-to object is erased from the database,
+ the database state of the pointing object is automatically updated
+ to set the pointer column to <code>NULL</code>. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class employer
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id_;
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db on_delete(cascade)
+ employer* employer_;
+};
+
+unsigned long id;
+
+{
+ employer e;
+ person p;
+ p.employer_ = &amp;e;
+
+ transaction t (db.begin ());
+
+ id = db.persist (e);
+ db.persist (p);
+
+ t.commit ();
+}
+
+{
+ transaction t (db.begin ());
+
+ // Database state of the person object is erased as well.
+ //
+ db.erase&lt;employer> (id);
+
+ t.commit ();
+}
+ </pre>
+
+
+ <p>Note that this is a database-level functionality and care must be
+ taken in order not to end up with inconsistent object states in the
+ application's memory and database. The following example illustrates
+ the kind of problems one may encounter:</p>
+
+ <pre class="cxx">
+#pragma db object
+class employer
+{
+ ...
+};
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db on_delete(set_null)
+ employer* employer_;
+};
+
+employer e;
+person p;
+p.employer_ = &amp;e;
+
+{
+ transaction t (db.begin ());
+ db.persist (e);
+ db.persist (p);
+ t.commit ();
+}
+
+{
+ transaction t (db.begin ());
+
+ // The employer column is set to NULL in the database but
+ // not the p.employer_ data member in the application.
+ //
+ db.erase (e);
+ t.commit ();
+}
+
+{
+ transaction t (db.begin ());
+
+ // Override the employer column with an invalid pointer.
+ //
+ db.update (p);
+
+ t.commit ();
+}
+ </pre>
+
+ <p>Note that even optimistic concurrency will not resolve such
+ issues unless you are using database-level support for optimistic
+ concurrency as well (for example, <code>ROWVERSION</code> in SQL
+ Server).</p>
+
+ <p>The <code>on_delete</code> specifier is only valid for non-inverse
+ object pointer data members. If the <code>set_null</code> semantics
+ is used, then the pointer must allow the <code>NULL</code> value.</p>
+
+ <h3><a name="14.4.16">14.4.16 <code>version</code></a></h3>
+
+ <p>The <code>version</code> specifier specifies that the data member stores
+ the object version used to support optimistic concurrency. If a class
+ has a version data member, then it must also be declared as having the
+ optimistic concurrency model using the <code>optimistic</code> pragma
+ (<a href="#14.1.5">Section 14.1.5, "<code>optimistic</code>"</a>). For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object optimistic
+class person
+{
+ ...
+
+ #pragma db version
+ unsigned long version_;
+};
+ </pre>
+
+ <p>A version member must be of an integral C++ type and must map to
+ an integer or similar database type. Note also that object versions
+ are not reused. If you have a high update frequency, then care must
+ be taken not to run out of versions. In such situations, using
+ <code>unsigned&nbsp;long&nbsp;long</code> as the version type is a safe
+ choice.</p>
+
+ <p>For a more detailed discussion of optimistic concurrency, refer to
+ <a href="#12">Chapter 12, "Optimistic Concurrency"</a>.</p>
+
+ <h3><a name="14.4.17">14.4.17 <code>index</code></a></h3>
+
+ <p>The <code>index</code> specifier instructs the ODB compiler to define
+ a database index for the data member. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db index
+ std::string name_;
+};
+ </pre>
+
+ <p>For more information on defining database indexes, refer to
+ <a href="#14.7">Section 14.7, "Index Definition Pragmas"</a>.</p>
+
+ <h3><a name="14.4.18">14.4.18 <code>unique</code></a></h3>
+
+ <p>The <code>index</code> specifier instructs the ODB compiler to define
+ a unique database index for the data member. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db unique
+ std::string name_;
+};
+ </pre>
+
+ <p>For more information on defining database indexes, refer to
+ <a href="#14.7">Section 14.7, "Index Definition Pragmas"</a>.</p>
+
+ <h3><a name="14.4.19">14.4.19 <code>unordered</code></a></h3>
+
+ <p>The <code>unordered</code> specifier specifies that the member of
+ an ordered container type should be stored unordered in the database.
+ The database table for such a member will not contain the index column
+ and the order in which elements are retrieved from the database may
+ not be the same as the order in which they were stored. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db unordered
+ std::vector&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <p>For a more detailed discussion of ordered containers and their
+ storage in the database, refer to <a href="#5.1">Section 5.1,
+ "Ordered Containers"</a>.</p>
+
+ <h3><a name="14.4.20">14.4.20 <code>table</code></a></h3>
+
+ <p>The <code>table</code> specifier specifies the table name that should
+ be used to store the contents of the container member. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db table("nicknames")
+ std::vector&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <p>If the table name is not specified, then the container table name
+ is constructed by concatenating the object's table name, underscore,
+ and the public member name. The public member name is obtained
+ by removing the common member name decorations, such as leading and
+ trailing underscores, the <code>m_</code> prefix, etc. In the example
+ above, without the <code>table</code> specifier, the container's
+ table name would have been <code>person_nicknames</code>.</p>
+
+ <p>The <code>table</code> specifier can also be used for members of
+ composite value types. In this case it specifies the table name
+ prefix for container members inside the composite value type. Refer
+ to <a href="#7.2.2">Section 7.2.2, "Composite Value Column and Table
+ Names"</a> for details.</p>
+
+ <p>The container table name can be qualified with a database
+ schema, for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db table("extras.nicknames")
+ std::vector&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <p>For more information on database schemas and the format of the
+ qualified names, refer to <a href="#14.1.8">Section 14.1.8,
+ "<code>schema</code>"</a>.</p>
+
+ <h3><a name="14.4.21">14.4.21 <code>load</code>/<code>update</code></a></h3>
+
+ <p>The <code>load</code> and <code>update</code> specifiers specify the
+ loading and updating behavior for an object section, respectively.
+ Valid values for the <code>load</code> specifier are
+ <code>eager</code> (default) and <code>lazy</code>. Valid values for
+ the <code>update</code> specifier are <code>always</code> (default),
+ <code>change</code>, and <code>manual</code>. For more information
+ on object sections, refer to <a href="#9">Chapter 9, "Sections"</a>.</p>
+
+ <h3><a name="14.4.22">14.4.22 <code>section</code></a></h3>
+
+ <p>The <code>section</code> specifier indicates that a data member
+ of a persistent class belongs to an object section. The single
+ required argument to this specifier is the name of the section
+ data member. This specifier can only be used on direct data
+ members of a persistent class. For more information on object
+ sections, refer to <a href="#9">Chapter 9, "Sections"</a>.</p>
+
+ <h3><a name="14.4.23">14.4.23 <code>added</code></a></h3>
+
+ <p>The <code>added</code> specifier marks the data member as
+ soft-added. The single required argument to this specifier is
+ the addition version. For more information on this functionality,
+ refer to <a href="#13.4">Section 13.4, "Soft Object Model
+ Changes"</a>.</p>
+
+ <h3><a name="14.4.24">14.4.24 <code>deleted</code></a></h3>
+
+ <p>The <code>deleted</code> specifier marks the data member as
+ soft-deleted. The single required argument to this specifier is
+ the deletion version. For more information on this functionality,
+ refer to <a href="#13.4">Section 13.4, "Soft Object Model
+ Changes"</a>.</p>
+
+ <h3><a name="14.4.25">14.4.25 <code>index_type</code></a></h3>
+
+ <p>The <code>index_type</code> specifier specifies the native
+ database type that should be used for an ordered container's
+ index column of the data member. The semantics of <code>index_type</code>
+ are similar to those of the <code>type</code> specifier
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>). The native
+ database type is expected to be an integer type. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db index_type("SMALLINT UNSIGNED")
+ std::vector&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <h3><a name="14.4.26">14.4.26 <code>key_type</code></a></h3>
+
+ <p>The <code>key_type</code> specifier specifies the native
+ database type that should be used for a map container's
+ key column of the data member. The semantics of <code>key_type</code>
+ are similar to those of the <code>type</code> specifier
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>). For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db key_type("INT UNSIGNED")
+ std::map&lt;unsigned short, float> age_weight_map_;
+};
+ </pre>
+
+ <h3><a name="14.4.27">14.4.27 <code>value_type</code></a></h3>
+
+ <p>The <code>value_type</code> specifier specifies the native
+ database type that should be used for a container's
+ value column of the data member. The semantics of <code>value_type</code>
+ are similar to those of the <code>type</code> specifier
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>). For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db value_type("VARCHAR(255)")
+ std::vector&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <p>The <code>value_null</code> and <code>value_not_null</code>
+ (<a href="#14.4.28">Section 14.4.28,
+ "<code>value_null</code>/<code>value_not_null</code>"</a>) specifiers
+ can be used to control the <code>NULL</code> semantics of a value
+ column.</p>
+
+ <h3><a name="14.4.28">14.4.28 <code>value_null</code>/<code>value_not_null</code></a></h3>
+
+ <p>The <code>value_null</code> and <code>value_not_null</code> specifiers
+ specify that a container's element value for the data member can or
+ cannot be <code>NULL</code>, respectively. The semantics of
+ <code>value_null</code> and <code>value_not_null</code> are similar
+ to those of the <code>null</code> and <code>not_null</code> specifiers
+ (<a href="#14.4.6">Section 14.4.6, "<code>null</code>/<code>not_null</code>"</a>).
+ For example:</p>
+
+ <pre class="cxx">
+using std::tr1::shared_ptr;
+
+#pragma db object
+class person
+{
+ ...
+};
+
+#pragma db object
+class account
+{
+ ...
+
+ #pragma db value_not_null
+ std::vector&lt;shared_ptr&lt;person> > holders_;
+};
+ </pre>
+
+ <p>For set and multiset containers (<a href="#5.2">Section 5.2, "Set and
+ Multiset Containers"</a>) the element value is automatically treated
+ as not allowing a <code>NULL</code> value.</p>
+
+ <h3><a name="14.4.29">14.4.29 <code>id_options</code></a></h3>
+
+ <p>The <code>id_options</code> specifier specifies additional
+ column definition options that should be used for a container's
+ id column of the data member. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id options("COLLATE binary")
+ std::string name_;
+
+ #pragma db id_options("COLLATE binary")
+ std::vector&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <p>The semantics of <code>id_options</code> are similar to those
+ of the <code>options</code> specifier (<a href="#14.4.8">Section
+ 14.4.8, "<code>options</code>"</a>).</p>
+
+ <h3><a name="14.4.30">14.4.30 <code>index_options</code></a></h3>
+
+ <p>The <code>index_options</code> specifier specifies additional
+ column definition options that should be used for a container's
+ index column of the data member. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db index_options("ZEROFILL")
+ std::vector&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <p>The semantics of <code>index_options</code> are similar to those
+ of the <code>options</code> specifier (<a href="#14.4.8">Section
+ 14.4.8, "<code>options</code>"</a>).</p>
+
+ <h3><a name="14.4.31">14.4.31 <code>key_options</code></a></h3>
+
+ <p>The <code>key_options</code> specifier specifies additional
+ column definition options that should be used for a container's
+ key column of the data member. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db key_options("COLLATE binary")
+ std::map&lt;std::string, std::string> properties_;
+};
+ </pre>
+
+ <p>The semantics of <code>key_options</code> are similar to those
+ of the <code>options</code> specifier (<a href="#14.4.8">Section
+ 14.4.8, "<code>options</code>"</a>).</p>
+
+ <h3><a name="14.4.32">14.4.32 <code>value_options</code></a></h3>
+
+ <p>The <code>value_options</code> specifier specifies additional
+ column definition options that should be used for a container's
+ value column of the data member. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db value_options("COLLATE binary")
+ std::set&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <p>The semantics of <code>value_options</code> are similar to those
+ of the <code>options</code> specifier (<a href="#14.4.8">Section
+ 14.4.8, "<code>options</code>"</a>).</p>
+
+ <h3><a name="14.4.33">14.4.33 <code>id_column</code></a></h3>
+
+ <p>The <code>id_column</code> specifier specifies the column
+ name that should be used to store the object id in a
+ container's table for the data member. The semantics of
+ <code>id_column</code> are similar to those of the
+ <code>column</code> specifier
+ (<a href="#14.4.9">Section 14.4.9, "<code>column</code>"</a>).
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id_column("person_id")
+ std::vector&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <p>If the column name is not specified, then <code>object_id</code>
+ is used by default.</p>
+
+ <h3><a name="14.4.34">14.4.34 <code>index_column</code></a></h3>
+
+ <p>The <code>index_column</code> specifier specifies the column
+ name that should be used to store the element index in an
+ ordered container's table for the data member. The semantics of
+ <code>index_column</code> are similar to those of the
+ <code>column</code> specifier
+ (<a href="#14.4.9">Section 14.4.9, "<code>column</code>"</a>).
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db index_column("nickname_number")
+ std::vector&lt;std::string> nicknames_;
+};
+ </pre>
+
+ <p>If the column name is not specified, then <code>index</code>
+ is used by default.</p>
+
+ <h3><a name="14.4.35">14.4.35 <code>key_column</code></a></h3>
+
+ <p>The <code>key_column</code> specifier specifies the column
+ name that should be used to store the key in a map
+ container's table for the data member. The semantics of
+ <code>key_column</code> are similar to those of the
+ <code>column</code> specifier
+ (<a href="#14.4.9">Section 14.4.9, "<code>column</code>"</a>).
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db key_column("age")
+ std::map&lt;unsigned short, float> age_weight_map_;
+};
+ </pre>
+
+ <p>If the column name is not specified, then <code>key</code>
+ is used by default.</p>
+
+ <h3><a name="14.4.36">14.4.36 <code>value_column</code></a></h3>
+
+ <p>The <code>value_column</code> specifier specifies the column
+ name that should be used to store the element value in a
+ container's table for the data member. The semantics of
+ <code>value_column</code> are similar to those of the
+ <code>column</code> specifier
+ (<a href="#14.4.9">Section 14.4.9, "<code>column</code>"</a>).
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db value_column("weight")
+ std::map&lt;unsigned short, float> age_weight_map_;
+};
+ </pre>
+
+ <p>If the column name is not specified, then <code>value</code>
+ is used by default.</p>
+
+ <h2><a name="14.5">14.5 Namespace Pragmas</a></h2>
+
+ <p>A pragma with the <code>namespace</code> qualifier describes a
+ C++ namespace. Similar to other qualifiers, <code>namespace</code>
+ can also refer to a named C++ namespace, for example:</p>
+
+ <pre class="cxx">
+namespace test
+{
+ ...
+}
+
+#pragma db namespace(test) ...
+ </pre>
+
+ <p>To refer to the global namespace in the <code>namespace</code>
+ qualifier the following special syntax is used:</p>
+
+ <pre class="cxx">
+#pragma db namespace() ....
+ </pre>
+
+ <p>The <code>namespace</code> qualifier can be optionally followed,
+ in any order, by one or more specifiers summarized in the
+ table below:</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table class="specifiers" border="1">
+ <tr>
+ <th>Specifier</th>
+ <th>Summary</th>
+ <th>Section</th>
+ </tr>
+
+ <tr>
+ <td><code>pointer</code></td>
+ <td>pointer type for persistent classes and views inside a namespace</td>
+ <td><a href="#14.5.1">14.5.1</a></td>
+ </tr>
+
+ <tr>
+ <td><code>table</code></td>
+ <td>table name prefix for persistent classes inside a namespace</td>
+ <td><a href="#14.5.2">14.5.2</a></td>
+ </tr>
+
+ <tr>
+ <td><code>schema</code></td>
+ <td>database schema for persistent classes inside a namespace</td>
+ <td><a href="#14.5.3">14.5.3</a></td>
+ </tr>
+
+ <tr>
+ <td><code>session</code></td>
+ <td>enable/disable session support for persistent classes inside a namespace</td>
+ <td><a href="#14.5.4">14.5.4</a></td>
+ </tr>
+
+ </table>
+
+ <h3><a name="14.5.1">14.5.1 <code>pointer</code></a></h3>
+
+ <p>The <code>pointer</code> specifier specifies the default pointer
+ type for persistent classes and views inside the namespace. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db namespace pointer(std::tr1::shared_ptr)
+namespace accounting
+{
+ #pragma db object
+ class employee
+ {
+ ...
+ };
+
+ #pragma db object
+ class employer
+ {
+ ...
+ };
+}
+ </pre>
+
+ <p>There are only two valid ways to specify a pointer with the
+ <code>pointer</code> specifier at the namespace level. We can
+ specify the template name of a smart pointer in which
+ case the ODB compiler will automatically append the class
+ name as a template argument. Or we can use <code>*</code>
+ to denote a raw pointer.</p>
+
+ <p>Note also that we can always override the default pointer
+ specified at the namespace level for any persistent class
+ or view inside this namespace. For example:</p>
+
+ <pre class="cxx">
+#pragma db namespace pointer(std::unique_ptr)
+namespace accounting
+{
+ #pragma db object pointer(std::shared_ptr)
+ class employee
+ {
+ ...
+ };
+
+ #pragma db object
+ class employer
+ {
+ ...
+ };
+}
+ </pre>
+
+ <p>For a more detailed discussion of object and view pointers, refer
+ to <a href="#3.3">Section 3.3, "Object and View Pointers"</a>.</p>
+
+ <h3><a name="14.5.2">14.5.2 <code>table</code></a></h3>
+
+ <p>The <code>table</code> specifier specifies a table prefix
+ that should be added to table names of persistent classes inside
+ the namespace. For example:</p>
+
+ <pre class="cxx">
+#pragma db namespace table("acc_")
+namespace accounting
+{
+ #pragma db object table("employees")
+ class employee
+ {
+ ...
+ };
+
+ #pragma db object table("employers")
+ class employer
+ {
+ ...
+ };
+}
+ </pre>
+
+ <p>In the above example the resulting table names will be
+ <code>acc_employees</code> and <code>acc_employers</code>.</p>
+
+ <p>The table name prefix can also be specified with the
+ <code>--table-prefix</code> ODB compiler option. Note
+ that table prefixes specified at the namespace level as well
+ as with the command line option are accumulated. For example:</p>
+
+ <pre class="cxx">
+#pragma db namespace() table("audit_")
+
+#pragma db namespace table("hr_")
+namespace hr
+{
+ #pragma db object table("employees")
+ class employee
+ {
+ ...
+ };
+}
+
+#pragma db object table("employers")
+class employer
+{
+ ...
+};
+ </pre>
+
+ <p>If we compile the above example with the
+ <code>--table-prefix&nbsp;test_</code> option, then the
+ <code>employee</code> class table will be called
+ <code>test_audit_hr_employees</code> and <code>employer</code> &mdash;
+ <code>test_audit_employers</code>.</p>
+
+ <p>Table prefixes can be used as an alternative to database schemas
+ (<a href="#14.1.8">Section 14.1.8, "<code>schema</code>"</a>) if
+ the target database system does not support schemas.</p>
+
+ <h3><a name="14.5.3">14.5.3 <code>schema</code></a></h3>
+
+ <p>The <code>schema</code> specifier specifies a database schema
+ that should be used for persistent classes inside the namespace.
+ For more information on specifying a database schema refer to
+ <a href="#14.1.8">Section 14.1.8, "<code>schema</code>"</a>.</p>
+
+ <h3><a name="14.5.4">14.5.4 <code>session</code></a></h3>
+
+ <p>The <code>session</code> specifier specifies whether to enable
+ session support for persistent classes inside the namespace. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db namespace session
+namespace hr
+{
+ #pragma db object // Enabled.
+ class employee
+ {
+ ...
+ };
+
+ #pragma db object session(false) // Disabled.
+ class employer
+ {
+ ...
+ };
+}
+ </pre>
+
+ <p>Session support is disabled by default unless the
+ <code>--generate-session</code> ODB compiler option is specified.
+ Session support specified at the namespace level can be overridden
+ on the per object basis (<a href="#14.1.10">Section 14.1.10,
+ "<code>session</code>"</a>). For more information on sessions,
+ refer to <a href="#11">Chapter 11, "Session"</a>.</p>
+
+<h2><a name="14.6">14.6 Object Model Pragmas</a></h2>
+
+ <p>A pragma with the <code>model</code> qualifier describes the
+ whole C++ object model. For example:</p>
+
+ <pre class="cxx">
+#pragma db model ...
+ </pre>
+
+ <p>The <code>model</code> qualifier can be followed, in any order,
+ by one or more specifiers summarized in the table below:</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table class="specifiers" border="1">
+ <tr>
+ <th>Specifier</th>
+ <th>Summary</th>
+ <th>Section</th>
+ </tr>
+
+ <tr>
+ <td><code>version</code></td>
+ <td>object model version</td>
+ <td><a href="#14.6.1">14.6.1</a></td>
+ </tr>
+
+ </table>
+
+ <h3><a name="14.6.1">14.6.1 <code>version</code></a></h3>
+
+ <p>The <code>version</code> specifier specifies the object model
+ version when schema evolution support is used. The first two
+ required arguments to this specifier are the base and current
+ model versions, respectively. The third optional argument
+ specifies whether the current version is open for changes.
+ Valid values for this argument are <code>open</code> (the
+ default) and <code>closed</code>. For more information on
+ this functionality, refer to <a href="#13">Chapter 13,
+ "Database Schema Evolution"</a>.</p>
+
+
+ <h2><a name="14.7">14.7 Index Definition Pragmas</a></h2>
+
+ <p>While it is possible to manually add indexes to the generated
+ database schema, it is more convenient to do this as part of
+ the persistent class definitions. A pragma with the <code>index</code>
+ qualifier describes a database index. It has the following
+ general format:</p>
+
+<pre class="cxx">
+#pragma db index[("&lt;name>")] \
+ [unique|type("&lt;type>")] \
+ [method("&lt;method>")] \
+ [options("&lt;index-options>")] \
+ member(&lt;name>[, "&lt;column-options>"])... \
+ members(&lt;name>[,&lt;name>...])...
+</pre>
+
+ <p>The <code>index</code> qualifier can optionally specify the
+ index name. If the index name is not specified, then one is
+ automatically derived by appending the <code>_i</code> suffix
+ to the column name of the index member. If the name is not
+ specified and the index contains multiple members, then the
+ index definition is invalid.</p>
+
+ <p>The optional <code>type</code>, <code>method</code>, and
+ <code>options</code> clauses specify the index type, for
+ example <code>UNIQUE</code>, index method, for example
+ <code>BTREE</code>, and index options, respectively. The
+ <code>unique</code> clause is a shortcut for
+ <code>type("UNIQUE")</code>. Note that not all database
+ systems support specifying an index method or options.
+ For more information on the database system-specific index
+ types, methods, and options, refer to <a href="#II">Part II,
+ "Database Systems"</a>.</p>
+
+ <p>To specify index members we can use the <code>member</code>
+ or <code>members</code> clauses, or a mix of the two. The
+ <code>member</code> clause allows us to specify a single
+ index member with optional column options, for example,
+ <code>"ASC"</code>. If we need to create a composite
+ index that contains multiple members, then we can repeat
+ the <code>member</code> clause several times or, if the
+ members don't have any column options, we can use a single
+ <code>members</code> clause instead. Similar to the index
+ type, method, and options, the format of column options is
+ database system-specific. For more details, refer to
+ <a href="#II">Part II, "Database Systems"</a>.</p>
+
+ <p>The following code fragment shows some typical examples
+ of index definitions:</p>
+
+<pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ int x;
+ int y;
+ int z1;
+ int z2;
+
+ // An index for member x with automatically-assigned name x_i.
+ //
+ #pragma db index member(x)
+
+ // A unique index named y_index for member y which is sorted in
+ // the descending order. The index is using the BTREE method.
+ //
+ #pragma db index("y_index") unique method("BTREE") member(y, "DESC")
+
+ // A composite BITMAP index named z_i for members z1 and z2.
+ //
+ #pragma db index("z_i") type("BITMAP") members(z1, z2)
+};
+</pre>
+
+ <p>ODB also offers a shortcut for defining an index with the default
+ method and options for a single data member. Such an index can
+ be defined using the <code>index</code> (<a href="#14.4.17">Section
+ 14.4.17, "<code>index</code>"</a>) or <code>unique</code>
+ (<a href="#14.4.18">Section 14.4.18, "<code>unique</code>"</a>)
+ member specifier. For example:</p>
+
+<pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db index
+ int x;
+
+ #pragma db type("INT") unique
+ int y;
+};
+</pre>
+
+ <p>The above example is semantically equivalent to the following
+ more verbose version:</p>
+
+<pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ int x;
+
+ #pragma db type("INT")
+ int y;
+
+ #pragma db index member(x)
+ #pragma db index unique member(y)
+};
+</pre>
+
+ <p>While it is convenient to define an index inside a persistent
+ class, it is also possible to do that out of the class body. In this
+ case, the index name, if specified, must be prefixed with the
+ potentially-qualified class name. For example:</p>
+
+<pre class="cxx">
+namespace n
+{
+ #pragma db object
+ class object
+ {
+ ...
+
+ int x;
+ int y;
+ };
+
+ // An index for member x in persistent class object with automatically-
+ // assigned name x_i.
+ //
+ #pragma db index(object) member(x)
+}
+
+// An index named y_index for member y in persistent class n::object.
+//
+#pragma db index(n::object::"y_index") member(y)
+</pre>
+
+ <p>It is possible to define an index on a member that is of a
+ composite value type or on some of its nested members. For
+ example:</p>
+
+<pre class="cxx">
+#pragma db value
+struct point
+{
+ int x;
+ int y;
+ int z;
+};
+
+#pragma db object
+class object
+{
+ // An index that includes all of the p1's nested members.
+ //
+ #pragma db index
+ point p1;
+
+ point p2;
+
+ // An index that includes only p2.x and p2.y.
+ //
+ #pragma db index("p2_xy_i") members(p2.x, p2.y)
+};
+</pre>
+
+ <p>When generating a schema for a container member (<a href="#5">Chapter 5,
+ "Containers"</a>), ODB automatically defines two indexes on the container
+ table. One is for the object id that references the object table and the
+ other is for the index column in case the container is ordered
+ (<a href="#5.1">Section 5.1, "Ordered Containers"</a>). By default these
+ indexes use the default index name, type, method, and options. The
+ <code>index</code> pragma allows us to customize these two indexes by
+ recognizing the special <code>id</code> and <code>index</code> nested
+ member names when specified after a container member. For example:</p>
+
+<pre class="cxx">
+#pragma db object
+class object
+{
+ std::vector&lt;int> v;
+
+ // Change the container id index name.
+ //
+ #pragma db index("id_index") member(v.id)
+
+ // Change the container index index method.
+ //
+ #pragma db index method("BTREE") member(v.index)
+};
+</pre>
+
+ <h2><a name="14.8">14.8 Database Type Mapping Pragmas</a></h2>
+
+ <p>A pragma with the <code>map</code> qualifier describes a
+ mapping between two database types. For each database system
+ ODB provides built-in support for a core set of database types,
+ such as integers, strings, binary, etc. However, many database
+ systems provide extended types such as geospatial types,
+ user-defined types, and collections (arrays, table types,
+ key-value stores, etc). In order to support such extended types,
+ ODB allows us to map them to one of the built-in types, normally
+ a string or a binary. Given the text or binary representation
+ of the data we can then extract it into our chosen C++ data type
+ and thus establish a mapping between an extended database type and
+ its C++ equivalent.</p>
+
+ <p>The <code>map</code> pragma has the following format:</p>
+
+<pre class="cxx">
+#pragma db map type("regex") as("subst") [to("subst")] [from("subst")]
+</pre>
+
+ <p>The <code>type</code> clause specifies the name of the database type
+ that we are mapping. We will refer to it as the <em>mapped type</em>
+ from now on. The name of the mapped type is a Perl-like regular
+ expression pattern that is matched in the case-insensitive mode.</p>
+
+ <p>The <code>as</code> clause specifies the name of the database type
+ that we are mapping the mapped type to. We will refer to it as
+ the <em>interface type</em> from now on. The name of the interface
+ type is a regular expression substitution and should expand to a
+ name of a database type for which ODB provides built-in support.</p>
+
+ <p>The optional <code>to</code> and <code>from</code> clauses specify the
+ database conversion expressions between the mapped type and the
+ interface type. The <code>to</code> expression converts from the
+ interface type to the mapped type and <code>from</code> converts
+ in the other direction. If no explicit conversion is required for
+ either direction, then the corresponding clause can be omitted.
+ The conversion expressions are regular expression substitutions.
+ They must contain the special <code>(?)</code> placeholder which will
+ be replaced with the actual value to be converted. Turning on SQL
+ statement tracing (<a href="#3.13">Section 3.13, "Tracing SQL
+ Statement Execution"</a>) can be useful for debugging conversion
+ expressions. This allows you to see the substituted expressions
+ as used in the actual statements.</p>
+
+ <p>As an example, the following <code>map</code> pragma maps the
+ PostgreSQL array of <code>INTEGER</code>'s to <code>TEXT</code>:</p>
+
+<pre class="cxx">
+#pragma db map type("INTEGER *\\[(\\d*)\\]") \
+ as("TEXT") \
+ to("(?)::INTEGER[$1]") \
+ from("(?)::TEXT")
+</pre>
+
+ <p>With the above mapping we can now have a persistent class that
+ has a member of the PostgreSQL array type:</p>
+
+<pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type("INTEGER[]")
+ std::string array_;
+};
+</pre>
+
+ <p>In PostgreSQL the array literal has the <code>{n1,n2,...}</code> form.
+ As a result, we need to make sure that we pass the correct text
+ representation in the <code>array_</code> member, for example:</p>
+
+<pre class="cxx">
+object o;
+o.array_ = "{1,2,3}";
+db.persist (o);
+</pre>
+
+ <p>Of course, <code>std::string</code> is not the most natural
+ representation of an array of integers in C++. Instead,
+ <code>std::vector&lt;int></code> would have been much more
+ appropriate. To add support for mapping
+ <code>std::vector&lt;int></code> to PostgreSQL <code>INTEGER[]</code>
+ we need to provide a <code>value_traits</code> specialization
+ that implements conversion between the PostgreSQL text representation
+ of an array and <code>std::vector&lt;int></code>. Below is a sample
+ implementation:</p>
+
+<pre class="cxx">
+namespace odb
+{
+ namespace pgsql
+ {
+ template &lt;>
+ class value_traits&lt;std::vector&lt;int>, id_string>
+ {
+ public:
+ typedef std::vector&lt;int> value_type;
+ typedef value_type query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type&amp; v,
+ const details::buffer&amp; b,
+ std::size_t n,
+ bool is_null)
+ {
+ v.clear ();
+
+ if (!is_null)
+ {
+ char c;
+ std::istringstream is (std::string (b.data (), n));
+
+ is >> c; // '{'
+
+ for (c = static_cast&lt;char> (is.peek ()); c != '}'; is >> c)
+ {
+ v.push_back (int ());
+ is >> v.back ();
+ }
+ }
+ }
+
+ static void
+ set_image (details::buffer&amp; b,
+ std::size_t&amp; n,
+ bool&amp; is_null,
+ const value_type&amp; v)
+ {
+ is_null = false;
+ std::ostringstream os;
+
+ os &lt;&lt; '{';
+
+ for (value_type::const_iterator i (v.begin ()), e (v.end ());
+ i != e;)
+ {
+ os &lt;&lt; *i;
+
+ if (++i != e)
+ os &lt;&lt; ',';
+ }
+
+ os &lt;&lt; '}';
+
+ const std::string&amp; s (os.str ());
+ n = s.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), s.c_str (), n);
+ }
+ };
+ }
+}
+</pre>
+
+ <p>Once this specialization is included in the generated code (see
+ the <code>mapping</code> example in the <code>odb-examples</code>
+ package for details), we can use <code>std::vector&lt;int></code>
+ instead of <code>std::string</code> in our persistent class:</p>
+
+<pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type("INTEGER[]")
+ std::vector&lt;int> array_;
+};
+</pre>
+
+ <p>If we wanted to always map <code>std::vector&lt;int></code>
+ to PostgreSQL <code>INTEGER[]</code>, then we could instead
+ write:</p>
+
+<pre class="cxx">
+typedef std::vector&lt;int> int_vector;
+#pragma db value(int_vector) type("INTEGER[]")
+
+#pragma db object
+class object
+{
+ ...
+
+ std::vector&lt;int> array_; // Mapped to INTEGER[].
+};
+</pre>
+
+ <p>While the above example only shows how to handle PostgreSQL arrays,
+ other types in PostgreSQL and in other databases can be supported
+ in a similar way. The <code>odb-tests</code> package contains a
+ set of tests in the <code>&lt;database>/custom</code> directories that,
+ for each database, shows how to provide custom mapping for some of
+ the extended types.</p>
+
+ <h2><a name="14.9">14.9 C++ Compiler Warnings</a></h2>
+
+ <p>When a C++ header file defining persistent classes and containing
+ ODB pragmas is used to build the application, the C++ compiler may
+ issue warnings about pragmas that it doesn't recognize. There
+ are several ways to deal with this problem. The easiest is to
+ disable such warnings using one of the compiler-specific command
+ line options or warning control pragmas. This method is described
+ in the following sub-section for popular C++ compilers.</p>
+
+ <p>There are also several C++ compiler-independent methods that we
+ can employ. The first is to use the <code>PRAGMA_DB</code> macro,
+ defined in <code>&lt;odb/core.hxx></code>, instead of using
+ <code>#pragma&nbsp;db</code> directly. This macro expands to the
+ ODB pragma when compiled with the ODB compiler and to an empty
+ declaration when compiled with other compilers. The following example
+ shows how we can use this macro:</p>
+
+ <pre class="cxx">
+#include &lt;odb/core.hxx>
+
+PRAGMA_DB(object)
+class person
+{
+ ...
+
+ PRAGMA_DB(id)
+ unsigned long id_;
+};
+ </pre>
+
+ <p>An alternative to using the <code>PRAGMA_DB</code> macro is to
+ group the <code>#pragma&nbsp;db</code> directives in blocks that are
+ conditionally included into compilation only when compiled with the
+ ODB compiler. For example:</p>
+
+ <pre class="cxx">
+class person
+{
+ ...
+
+ unsigned long id_;
+};
+
+#ifdef ODB_COMPILER
+# pragma db object(person)
+# pragma db member(person::id_) id
+#endif
+ </pre>
+
+ <p>The disadvantage of this approach is that it can quickly become
+ overly verbose when positioned pragmas are used.</p>
+
+ <h3><a name="14.9.1">14.9.1 GNU C++</a></h3>
+
+ <p>GNU g++ does not issue warnings about unknown pragmas
+ unless requested with the <code>-Wall</code> command line option.
+ To disable only the unknown pragma warning, we can add the
+ <code>-Wno-unknown-pragmas</code> option after <code>-Wall</code>,
+ for example:</p>
+
+ <pre class="terminal">
+g++ -Wall -Wno-unknown-pragmas ...
+ </pre>
+
+ <h3><a name="14.9.2">14.9.2 Visual C++</a></h3>
+
+ <p>Microsoft Visual C++ issues an unknown pragma warning (C4068) at
+ warning level 1 or higher. This means that unless we have disabled
+ the warnings altogether (level 0), we will see this warning.</p>
+
+ <p>To disable this warning via the compiler command line, we can add
+ the <code>/wd4068</code> C++ compiler option in Visual Studio 2008
+ and earlier. In Visual Studio 2010 and later there is now a special
+ GUI field where we can enter warning numbers that should be disabled.
+ Simply enter 4068 into this field.</p>
+
+ <p>We can also disable this warning for only a specific header or
+ a fragment of a header using the warning control pragma. For
+ example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/core.hxx>
+
+#pragma warning (push)
+#pragma warning (disable:4068)
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#pragma warning (pop)
+ </pre>
+
+ <h3><a name="14.9.3">14.9.3 Sun C++</a></h3>
+
+ <p>The Sun C++ compiler does not issue warnings about unknown pragmas
+ unless the <code>+w</code> or <code>+w2</code> option is specified.
+ To disable only the unknown pragma warning we can add the
+ <code>-erroff=unknownpragma</code> option anywhere on the
+ command line, for example:</p>
+
+ <pre class="terminal">
+CC +w -erroff=unknownpragma ...
+ </pre>
+
+ <h3><a name="14.9.4">14.9.4 IBM XL C++</a></h3>
+
+ <p>IBM XL C++ issues an unknown pragma warning (1540-1401) by default.
+ To disable this warning we can add the <code>-qsuppress=1540-1401</code>
+ command line option, for example:</p>
+
+ <pre class="terminal">
+xlC -qsuppress=1540-1401 ...
+ </pre>
+
+ <h3><a name="14.9.5">14.9.5 HP aC++</a></h3>
+
+ <p>HP aC++ (aCC) issues an unknown pragma warning (2161) by default.
+ To disable this warning we can add the <code>+W2161</code>
+ command line option, for example:</p>
+
+ <pre class="terminal">
+aCC +W2161 ...
+ </pre>
+
+ <h3><a name="14.9.6">14.9.6 Clang</a></h3>
+
+ <p>Clang does not issue warnings about unknown pragmas
+ unless requested with the <code>-Wall</code> command line option.
+ To disable only the unknown pragma warning, we can add the
+ <code>-Wno-unknown-pragmas</code> option after <code>-Wall</code>,
+ for example:</p>
+
+ <pre class="terminal">
+clang++ -Wall -Wno-unknown-pragmas ...
+ </pre>
+
+ <p>We can also disable this warning for only a specific header or
+ a fragment of a header using the warning control pragma. For
+ example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/core.hxx>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunknown-pragmas"
+
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db id
+ unsigned long id_;
+};
+
+#pragma clang diagnostic pop
+ </pre>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="15">15 Advanced Techniques and Mechanisms</a></h1>
+
+ <p>This chapter covers more advanced techniques and mechanisms
+ provided by ODB that may be useful in certain situations.</p>
+
+ <h2><a name="15.1">15.1 Transaction Callbacks</a></h2>
+
+ <p>The ODB transaction class (<code>odb::transaction</code>) allows
+ an application to register a callback that will be called after
+ the transaction is finalized, that is, committed or rolled back.
+ This mechanism can be used, for example, to restore values that
+ were updated during the transaction execution to their original
+ states if the transaction is rolled back.</p>
+
+ <p>The callback management interface of the <code>transaction</code>
+ class is shown below.</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ class transaction
+ {
+ ...
+
+ public:
+ static const unsigned short event_commit = 0x01;
+ static const unsigned short event_rollback = 0x02;
+ static const unsigned short event_all = event_commit | event_rollback;
+
+ typedef void (*callback_type) (
+ unsigned short event, void* key, unsigned long long data);
+
+ void
+ callback_register (callback_type callback,
+ void* key,
+ unsigned short event = event_all,
+ unsigned long long data = 0,
+ transaction** state = 0);
+
+
+ void
+ callback_unregister (void* key);
+
+ void
+ callback_update (void* key,
+ unsigned short event,
+ unsigned long long data = 0,
+ transaction** state = 0);
+ }
+}
+ </pre>
+
+ <p>The <code>callback_register()</code> function registers a
+ post-commit/rollback callback. The <code>callback</code>
+ argument is the function that should be called. The
+ <code>key</code> argument is used by the transaction
+ to identify this callback. It is also normally used
+ to pass an address of the data object on which the
+ callback function will work. The <code>event</code>
+ argument is the bitwise-or of the events that should
+ trigger the callback.</p>
+
+ <p>The optional data argument can be used to store any POD
+ user data that doesn't exceed 8 bytes in size and doesn't require
+ alignment greater than <code>unsigned long long</code>. For
+ example, we could store an old value of a flag or a counter
+ that needs to be restored in case of a roll back.</p>
+
+ <p>The optional <code>state</code> argument can be used to
+ indicate that the callback has been unregistered because
+ the transaction was finalized. In this case the transaction
+ automatically resets the passed pointer to 0. This is
+ primarily useful if we are interested in only one of
+ the events (commit or rollback).</p>
+
+ <p>The <code>callback_unregister()</code> function unregisters a previously
+ registered callback. If the number of registered callbacks is
+ large, then this can be a slow operation. Generally, the callback
+ mechanism is optimized for cases where the callbacks stay
+ registered until the transaction is finalized.</p>
+
+ <p>Note also that you don't need to unregister a callback that has
+ been called or auto-reset using the <code>state</code> argument
+ passed to <code>callback_register()</code>. This function does nothing
+ if the key is not found.</p>
+
+ <p>The <code>callback_update()</code> function can be used to update
+ the <code>event</code>, <code>data</code>, and <code>state</code>
+ values of a previously registered callback. Similar to
+ <code>callback_unregister()</code>, this is a potentially slow
+ operation.</p>
+
+ <p>When the callback is called, it is passed the event that
+ triggered it, as well as the <code>key</code> and
+ <code>data</code> values that were passed to the
+ <code>callback_register()</code> function. Note also that the order
+ in which the callbacks are called is unspecified. The rollback
+ event can be triggered by an exception. In this case, if the
+ callback throws, the program will be terminated.</p>
+
+ <p>The following example shows how we can use transaction
+ callbacks together with database operation callbacks
+ (<a href="#14.1.7">Section 14.1.7, "<code>callback</code>"</a>)
+ to manage the object's "dirty" flag.</p>
+
+ <pre class="cxx">
+#include &lt;odb/callback.hxx>
+#include &lt;odb/transaction.hxx>
+
+#pragma db object callback(update)
+class object
+{
+ ...
+
+ #pragma db transient
+ mutable bool dirty_;
+
+ // Non-NULL value indicates that we are registered
+ // with this transaction.
+ //
+ #pragma db transient
+ mutable odb::transaction* tran_;
+
+ void
+ update (odb::callback_event e, odb::database&amp;) const
+ {
+ using namespace odb::core;
+
+ if (e == callback_event::post_update)
+ return;
+
+ // Mark the object as clean again but register a
+ // transaction callback in case the update is rolled
+ // back.
+ //
+ tran_ = &amp;transaction::current ();
+ tran_->callback_register (&amp;rollback,
+ const_cast&lt;object*> (this),
+ transaction::event_rollback,
+ 0,
+ &amp;tran_);
+ dirty_ = false;
+ }
+
+ static void
+ rollback (unsigned short, void* key, unsigned long long)
+ {
+ // Restore the dirty flag since the changes have been
+ // rolled back.
+ //
+ object&amp; o (*static_cast&lt;object*> (key));
+ o.dirty_ = true;
+ }
+
+ ~object ()
+ {
+ // Unregister the callback if we are going away before
+ // the transaction.
+ //
+ if (tran_ != 0)
+ tran_->callback_unregister (this);
+ }
+};
+ </pre>
+
+ <h2><a name="15.2">15.2 Persistent Class Template Instantiations</a></h2>
+
+ <p>Similar to composite value types (<a href="#7.2">Section 7.2, "Composite
+ Value Types"</a>), a persistent object can be defined as an instantiation
+ of a C++ class template, for example:</p>
+
+ <pre class="cxx">
+template &lt;typename T>
+class person
+{
+ ...
+
+ T first_;
+ T last_;
+};
+
+typedef person&lt;std::string> std_person;
+
+#pragma db object(std_person)
+#pragma db member(std_person::last_) id
+ </pre>
+
+ <p>Note that the database support code for such a persistent object
+ is generated when compiling the header containing the
+ <code>db&nbsp;object</code> pragma and not the header containing
+ the template definition or the <code>typedef</code> name. This
+ allows us to use templates defined in other files, for example:</p>
+
+ <pre class="cxx">
+#include &lt;utility> // std::pair
+
+typedef std::pair&lt;unsigned int, std::string> person;
+#pragma db object(person)
+#pragma db member(person::first) id auto column("id")
+#pragma db member(person::second) column("name")
+ </pre>
+
+ <p>You may also have to explicitly specify the object type in
+ calls to certain <code>database</code> class functions due
+ to the inability do distinguish, at the API level, between
+ smart pointers and persistent objects defined as class
+ template instantiations. For example:</p>
+
+ <pre class="cxx">
+person p;
+
+db.update (p); // Error.
+db.reload (p); // Error.
+db.erase (p); // Error.
+
+db.update&lt;person> (p); // Ok.
+db.reload&lt;person> (p); // Ok.
+db.erase&lt;person> (p); // Ok.
+ </pre>
+
+ <p>It also makes sense to factor persistent data members that do not
+ depend on template arguments into a common, non-template base class.
+ The following more realistic example illustrates this approach:</p>
+
+ <pre class="cxx">
+#pragma db object abstract
+class base_common
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id;
+};
+
+template &lt;typename T>
+class base: public base_common
+{
+ ...
+
+ T value;
+};
+
+typedef base&lt;std::string> string_base;
+#pragma db object(string_base) abstract
+
+#pragma db object
+class derived: public string_base
+{
+ ...
+};
+ </pre>
+
+ <h2><a name="15.3">15.3 Bulk Database Operations</a></h2>
+
+ <p>Some database systems supported by ODB provide a mechanism, often called
+ bulk or batch statement execution, that allows us to execute the same SQL
+ statement on multiple sets of data at once and with a single database API
+ call (or equivalent). This often results in significantly better
+ performance if we need to execute the same statement for a large number
+ of data sets (thousands to millions).</p>
+
+ <p>ODB translates this mechanism to bulk operations which allow us to
+ persist, update, or erase a range of objects in the database. Currently,
+ from all the database systems supported by ODB, only Oracle, Microsoft
+ SQL Server, and PostgreSQL are capable of bulk operations (but
+ see <a href="#19.5.7">Section 19.5.7, "Bulk Operations Support"</a> for
+ PostgreSQL limitations). There is also currently no emulation of the bulk
+ API for other databases nor dynamic multi-database support. As a result,
+ if you are using dynamic multi-database support, you will need to "drop
+ down" to static support in order to access the bulk API. Refer
+ to <a href="#16">Chapter 16, "Multi-Database Support"</a> for
+ details.</p>
+
+ <p>As we will discuss later in this section, bulk operations have
+ complex failure semantics that is dictated by the underlying
+ database API. As a result, support for bulk persist, update,
+ and erase is limited to persistent classes for which these
+ operations can be performed with a single database statement
+ execution. In particular, bulk operations are not available
+ for polymorphic objects (<a href="#8.2">Section 8.2,
+ "Polymorphism Inheritance"</a>) or objects that have
+ containers (inverse containers of object pointers are an
+ exception). Furthermore, for objects that have sections
+ (<a href="#9">Chapter 9, "Sections"</a>) the bulk update operation
+ will only be available if all the sections are manually-updated.
+ On the other hand, bulk operations are supported for objects
+ that use optimistic concurrency (<a href="#12">Chapter 12,
+ "Optimistic Concurrency"</a>) or have no object id
+ (<a href="#14.1.6">Section 14.1.6, "<code>no_id</code>"</a>).</p>
+
+ <p>To enable the generation of bulk operation support for a persistent
+ class we use the <code>bulk</code> pragma. For example:</p>
+
+ <pre class="cxx">
+#pragma db object bulk(5000)
+class person
+{
+ ...
+
+ #pragma db id auto
+ unsigned long id;
+};
+ </pre>
+
+ <p>The single argument to the <code>bulk</code> pragma is the batch
+ size. The batch size specifies the maximum number of data sets that
+ should be handled with a single underlying statement execution (or
+ equivalent). If the range that we want to perform the bulk operation on
+ contains more objects than the batch size, then ODB will split this
+ operation into multiple underlying statement executions (batches). To
+ illustrate this point with an example, suppose we want to persist 53,000
+ objects and the batch size is 5,000. ODB will then execute the statement
+ 11 times, the first 10 times with 5,000 data sets each, and the last time
+ with the remaining 3,000 data sets.</p>
+
+ <p>The commonly used batch sizes are in the 2,000-5,000 range, though
+ smaller or larger batches could provide better performance,
+ depending on the situation. As a result, it is recommended to
+ experiment with different batch sizes to determine the optimum
+ number for a particular object and its use-cases. Note also that
+ you may achieve better performance by also splitting a large bulk
+ operation into multiple transactions (<a href="#3.5">Section 3.5,
+ "Transactions"</a>).</p>
+
+ <p>For database systems that do not support bulk operations the
+ <code>bulk</code> pragma is ignored. It is also possible to
+ specify different batch sizes for different database systems
+ by using the database prefix, for example:</p>
+
+ <pre class="cxx">
+#pragma db object mssql:bulk(3000) oracle:bulk(4000) pgsql:bulk(2000)
+class person
+{
+ ...
+};
+ </pre>
+
+ <p>Note that while specifying the batch size at compile time might
+ seem inflexible, this approach allows ODB to place internal
+ arrays of the fixed batch size on the stack rather than
+ allocating them in the dynamic memory. However, specifying the
+ batch size at runtime may be supported in the future.</p>
+
+ <p>Once the bulk support is enabled for a particular object, we can
+ use the following <code>database</code> functions to perform bulk
+ operations:</p>
+
+ <pre class="cxx">
+template &lt;typename I>
+void
+persist (I begin, I end, bool continue_failed = true);
+
+template &lt;typename I>
+void
+update (I begin, I end, bool continue_failed = true);
+
+template &lt;typename I>
+void
+erase (I obj_begin, I obj_end, bool continue_failed = true);
+
+template &lt;typename T, typename I>
+void
+erase (I id_begin, I id_end, bool continue_failed = true);
+ </pre>
+
+ <p>Every bulk API function expects a range of elements, passed in
+ the canonical C++ form as a pair of input iterators. In case of
+ <code>persist()</code>, <code>update()</code>, and the first
+ <code>erase()</code> overload, we pass a range of objects,
+ either as references or as pointers, raw or smart. The following
+ example illustrates the most common scenarios using the
+ <code>persist()</code> call:</p>
+
+ <pre class="cxx">
+// C array of objects.
+//
+person a[2] {{"John", "Doe"}, {"Jane", "Doe"}};
+
+db.persist (a, a + sizeof(a) / sizeof(a[0]));
+
+
+// Vector of objects.
+//
+std::vector&lt;person> v {{"John", "Doe"}, {"Jane", "Doe"}};
+
+db.persist (v.begin (), v.end ());
+
+
+// C array of raw pointers to objects.
+//
+person p1 ("John", "Doe");
+person p2 ("Jane", "Doe");
+person* pa[2] {&amp;p1, &amp;p2};
+
+db.persist (pa, pa + sizeof(pa) / sizeof(pa[0]));
+
+
+// Vector of raw pointers to objects.
+//
+std::vector&lt;person*> pv {&amp;p1, &amp;p2};
+
+db.persist (pv.begin (), pv.end ());
+
+
+// Vector of smart (shared) pointers to objects.
+//
+std::vector&lt;std::shared_ptr&lt;person>> sv {
+ std::make_shared&lt;person> ("John", "Doe"),
+ std::make_shared&lt;person> ("Jane", "Doe")};
+
+db.persist (sv.begin (), sv.end ());
+ </pre>
+
+ <p>The ability to perform a bulk operation on a range of raw pointers
+ to objects can be especially useful when the application stores
+ objects in a way that does not easily conform to the pair of
+ iterators interface. In such cases we can create a temporary
+ container of shallow pointers to objects and use that to perform
+ the bulk operation, for example:</p>
+
+ <pre class="cxx">
+struct person_entry
+{
+ person obj;
+
+ // Some additional data.
+ ...
+};
+
+typedef std::vector&lt;person_entry> people;
+
+void
+persist (odb::database&amp; db, people&amp; p)
+{
+ std::vector&lt;person*> tmp;
+ tmp.reserve (p.size ());
+ std::for_each (p.begin (),
+ p.end (),
+ [&amp;tmp] (person_entry&amp; pe)
+ {
+ tmp.push_back (&amp;pe.obj);
+ });
+
+
+ db.persist (tmp.begin (), tmp.end ());
+}
+ </pre>
+
+ <p>The second overload of the bulk <code>erase()</code> function
+ allows us to pass a range of object ids rather than objects
+ themselves. As with the corresponding non-bulk version, we
+ have to specify the object type explicitly, for example:</p>
+
+ <pre class="cxx">
+std::vector&lt;unsigned long> ids {1, 2};
+
+db.erase&lt;person> (ids.begin (), ids.end ());
+ </pre>
+
+ <p>Conceptually, a bulk operation is equivalent to performing the
+ corresponding non-bulk version in a loop, except when it comes to the
+ failure semantics. Some databases that currently are capable of bulk
+ operations (specifically, Oracle and SQL Server) do not stop when a data
+ set in a batch fails (for example, because of a unique constraint
+ violation). Instead, they continue executing subsequent data sets until
+ every element in the batch has been attempted. The
+ <code>continue_failed</code> argument in the bulk functions listed
+ above specifies whether ODB should extend this behavior and continue
+ with subsequent batches if the one it has tried to execute has failed
+ elements. The default behavior is to continue.</p>
+
+ <p>The consequence of this failure semantics is that we may have
+ multiple elements in the range failed for different reasons.
+ For example, if we tried to persist a number of objects, some
+ of them might have failed because they are already persistent
+ while others &mdash; because of a unique constraint violation.
+ As a result, ODB uses the special <code>odb::multiple_exceptions</code>
+ class to report failures in the bulk API functions. This
+ exception is thrown if one or more elements in the range have
+ failed and it contains the error information in the form of other
+ ODB exception for each failed position. The
+ <code>multiple_exceptions</code> class has the following interface:</p>
+
+ <pre class="cxx">
+struct multiple_exceptions: odb::exception
+{
+ // Element type.
+ //
+ struct value_type
+ {
+ std::size_t
+ position () const;
+
+ const odb::exception&amp;
+ exception () const;
+
+ bool
+ maybe () const;
+ };
+
+ // Iteration.
+ //
+ typedef std::set&lt;value_type> set_type;
+
+ typedef set_type::const_iterator iterator;
+ typedef set_type::const_iterator const_iterator;
+
+ iterator
+ begin () const;
+
+ iterator
+ end () const;
+
+ // Lookup.
+ //
+ const value_type*
+ operator[] (std::size_t) const;
+
+ // Severity, failed and attempted counts.
+ //
+ std::size_t
+ attempted () const;
+
+ std::size_t
+ failed () const;
+
+ bool
+ fatal () const;
+
+ void
+ fatal (bool);
+
+ // Direct data access.
+ //
+ const set_type&amp;
+ set () const;
+
+ // odb::exception interface.
+ //
+ virtual const char*
+ what () const throw ();
+};
+ </pre>
+
+ <p>The <code>multiple_exceptions</code> class has a map-like interface
+ with the key being the position in the range and the value being
+ the exception plus the <code>maybe</code> flag (discussed below).
+ As a result, we can either iterate over the failed positions or
+ we can check whether a specific position in the range has failed.
+ The following example shows what a <code>catch</code>-handler for
+ this exception might look like:</p>
+
+ <pre class="cxx">
+std::vector&lt;person> objs {{"John", "Doe"}, {"Jane", "Doe"}};
+
+try
+{
+ db.persist (objs.begin (), objs.end ());
+}
+catch (const odb::multiple_exceptions&amp; me)
+{
+ for (const auto&amp; v: me)
+ {
+ size_t p (v.position ());
+
+ try
+ {
+ throw v.exception ();
+ }
+ catch (const odb::object_already_persistent&amp;)
+ {
+ cerr &lt;&lt; p &lt;&lt; ": duplicate id: " &lt;&lt; objs[p].id () &lt;&lt; endl;
+ }
+ catch (const odb::exception&amp; e)
+ {
+ cerr &lt;&lt; p &lt;&lt; ": " &lt;&lt; e.what () &lt;&lt; endl;
+ }
+ }
+}
+ </pre>
+
+ <p>If, however, all we want is to show the diagnostics to the user,
+ then the string returned by the <code>what()</code> function
+ will contain the error information for each failed position.
+ Here is what it might look like (using Oracle as an example):</p>
+
+ <pre class="terminal">
+multiple exceptions, 4 elements attempted, 2 failed:
+[0] object already persistent
+[3] 1: ORA-00001: unique constraint (ODB_TEST.person_last_i) violated
+ </pre>
+
+ <p>Some databases that currently are capable of bulk operations
+ (specifically, Oracle and SQL Server) return a total count of affected
+ rows rather than individual counts for each data set. This limitation
+ prevents ODB from being able to always determine which elements in the
+ batch haven't affected any rows and, for the update and erase operations,
+ translate this to the <code>object_not_persistent</code> exceptions. As a
+ result, if some elements in the batch haven't affected any rows and ODB
+ is unable to determine exactly which ones, it will mark all the elements
+ in this batch as "maybe not persistent". That is, it will insert
+ the <code>object_not_persistent</code> exception and set
+ the <code>maybe</code> flag for every position in the batch. The
+ diagnostics string returned by <code>what()</code> will also reflect this
+ situation, for example (assuming batch size of 3):</p>
+
+ <pre class="terminal">
+multiple exceptions, 4 elements attempted, 4 failed:
+[0-2] (some) object not persistent
+[3] object not persistent
+ </pre>
+
+ <p>The way to handle and recover from such "maybe failures" will have
+ to be application-specific. For example, for some applications the
+ fact that some objects no longer exist in the database when
+ performing bulk erase might be an ignorable error. If, however,
+ the application needs to determine exactly which elements in the batch
+ have failed, then a <code>load()</code> call will be required for each
+ element in the batch (or a query using a view to avoid loading all
+ the data members; <a href="#10">Chapter 10, "Views"</a>). This is also
+ something to keep in mind when selecting the batch size since for
+ larger sizes it will be more expensive (more loads to perform) to
+ handle such "maybe failures". If the failures are not uncommon, as
+ is the case, for example, when using optimistic concurrency, then
+ it may make sense to use a smaller batch.</p>
+
+ <p>The lookup operator (<code>operator[]</code>) returns <code>NULL</code>
+ if the element at this position has no exception. Note also that the
+ returned value is <code>value_type*</code> and not
+ <code>odb::exception*</code> in order to provide access to the
+ <code>maybe</code> flag discussed above.</p>
+
+ <p>The <code>multiple_exceptions</code> class also provides access
+ to the number of positions attempted (the <code>attempted()</code>
+ accessor) and failed (the <code>failed()</code> accessor). Note
+ that the failed count includes the "maybe failed" positions.</p>
+
+ <p>The <code>multiple_exceptions</code> exception can also be fatal.
+ If the <code>fatal()</code> accessor returns <code>true</code>, then
+ (some of) the exceptions were fatal. In this case, even for positions
+ that did not fail, no attempts were made to complete the operation
+ and the transaction must be aborted.</p>
+
+ <p>If <code>fatal()</code> returns false, then the operation on the
+ elements that don't have an exception has succeeded. The application
+ can ignore the errors or try to correct the errors and re-attempt
+ the operation on the elements that did fail. In either case, the
+ transaction can be committed.</p>
+
+ <p>An example of a fatal exception would be the situation where the
+ execution of the underlying statement failed summarily, without
+ attempting any data sets, for instance, because of an error in
+ the statement itself.</p>
+
+ <p>The <code>fatal()</code> modifier allows you to "upgrade" an
+ exception to fatal, for example, for specific database error
+ codes.</p>
+
+
+ <!-- PART -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="II">PART II&nbsp;&nbsp;
+ <span style="font-weight: normal;">DATABASE SYSTEMS</span></a></h1>
+
+ <p>Part II covers topics specific to the database system
+ implementations and their support in ODB. The first chapter in
+ Part II discusses how to use multiple database systems in the
+ same application. The subsequent chapters describe the system-specific
+ <code>database</code> classes as well as the default mapping
+ between basic C++ value types and native database types. Part
+ II consists of the following chapters.</p>
+
+ <table class="toc">
+ <tr><th>16</th><td><a href="#16">Multi-Database Support</a></td></tr>
+ <tr><th>17</th><td><a href="#17">MySQL Database</a></td></tr>
+ <tr><th>18</th><td><a href="#18">SQLite Database</a></td></tr>
+ <tr><th>19</th><td><a href="#19">PostgreSQL Database</a></td></tr>
+ <tr><th>20</th><td><a href="#20">Oracle Database</a></td></tr>
+ <tr><th>21</th><td><a href="#21">Microsoft SQL Server Database</a></td></tr>
+ </table>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="16">16 Multi-Database Support</a></h1>
+
+ <p>Some applications may need to access multiple database systems, either
+ simultaneously or one at a time. For example, an application may
+ utilize an embedded database such as SQLite as a local cache and use
+ a client-server database such as PostgreSQL for more permanent
+ but slower to access remote storage. Or an application may need
+ to be able to store its data in any database selected at runtime
+ by the user. Yet another scenario is the data migration from one
+ database system to another. In this case, multi-database support
+ is only required for a short period. It is also plausible that an
+ application implements all three of these scenarios, that is, it
+ uses SQLite as a local cache, allows the user to select the remote
+ database system, and supports data migration from one remote database
+ system to another.</p>
+
+ <p>ODB provides two types of multi-database support: <em>static</em>
+ and <em>dynamic</em>. With static support we use the
+ database system-specific interfaces to perform database
+ operations. That is, instead of using <code>odb::database</code>,
+ <code>odb::transaction</code>, or <code>odb::query</code>, we
+ would use, for example, <code>odb::sqlite::database</code>,
+ <code>odb::sqlite::transaction</code>, or
+ <code>odb::sqlite::query</code> to access an SQLite database.</p>
+
+ <p>In contrast, with <em>dynamic</em> multi-database support we can
+ use the common interface to access any database without having to
+ know which one it is. At runtime, ODB will automatically dispatch
+ a call on the common interface to the specific database implementation
+ based on the actual <code>database</code> instance being
+ used. In fact, this mechanism is very similar to C++ virtual
+ functions.</p>
+
+ <p>Both static and dynamic multi-database support have a different set
+ of advantages and disadvantages which makes them more or less suitable
+ for different use cases. Static support has zero overhead compared
+ to single-database support and allows us to use database
+ system-specific features, extensions, etc. At the same time, the
+ code that we write will be tied to the specific database system.
+ As a result, this type of multi-database support is more
+ suitable for situations where different parts of an application
+ access different but specific database systems. For example,
+ using SQLite as a local cache most likely falls into this
+ category since we are using a specific database system (SQLite)
+ and the code that will check the cache will most likely (but
+ not necessarily) be separate from the code that interact with
+ the remote database. Another example where static multi-database
+ support might be more suitable is a once-off data migration from
+ one database system to another. In this case both the source and
+ target are specific database systems. In contrast, if data migration
+ from one database system to another is a general feature in an
+ application, then dynamic multi-database support might be more
+ suitable.</p>
+
+ <p>The main advantage of dynamic multi-database support is the
+ database system-independence of the code that we write. The same
+ application code will work with any database system supported by
+ ODB and the generated database support code can be packaged into
+ separate libraries and loaded dynamically by the application. The
+ disadvantages of dynamic support are slight overhead and certain
+ limitations in functionality compared to static support (see
+ <a href="#16.2">Section 16.2, "Dynamic Multi-Database Support"</a>
+ for details). As a result, dynamic multi-database support is most
+ suitable to situations where we need the same code to
+ work with a range of database systems. For example, if your
+ application must be able to store its data in any database
+ selected by the user, then dynamic support is probably the
+ best option.</p>
+
+ <p>Note also that it is possible to mix and match static and dynamic
+ support in the same application. In fact, dynamic support is built
+ on top of static support so it is possible to use the same database
+ system both "statically" and "dynamically". In particular, the ability
+ to "drop down" from dynamic to static support can be used to overcome
+ the functionality limitations mentioned above. Finally,
+ single-database support is just a special case of static
+ multi-database support with a single database system.</p>
+
+ <p>By default ODB assumes single-database support. To enable
+ multi-database support we use the <code>--multi-database</code>
+ (or <code>-m</code>) ODB compiler option. This option is also used to
+ specify the support type: <code>static</code> or <code>dynamic</code>.
+ For example:</p>
+
+ <pre class="terminal">
+odb -m static ... person.hxx
+ </pre>
+
+ <p>With multi-database support enabled, we can now generate the database
+ support code for several database systems. This can be accomplished
+ either with a single ODB compiler invocation by specifying multiple
+ <code>--database</code> (or <code>-d</code>) options or with multiple
+ ODB compiler invocations. Both approaches produce the same result,
+ for example:</p>
+
+ <pre class="terminal">
+odb -m static -d common -d sqlite -d pgsql person.hxx
+ </pre>
+
+ <p>Is equivalent to:</p>
+
+ <pre class="terminal">
+odb -m static -d common person.hxx
+odb -m static -d sqlite person.hxx
+odb -m static -d pgsql person.hxx
+ </pre>
+
+ <p>Notice that the first <code>-d</code> option has <code>common</code>
+ as its value. This is not a real database system. Rather, it instructs
+ the ODB compiler to generate code that is common to all the database
+ systems and, in case of dynamic support, is also the common
+ interfaces.</p>
+
+ <p>If you look at the result of the above commands, you will also notice
+ changes in the output file names. In the single-database mode the ODB
+ compiler produces a single set of the <code>person-odb.?xx</code> files
+ which contain both the common as well as the database specific
+ generated code (since there is only one database system in use,
+ there is no reason to split the two). In contrast, in the
+ multi-database mode, the <code>person-odb.?xx</code> set of files
+ contains the common code while the database system-specific code is
+ written to files in the form <code>person-odb-&lt;db>.?xx</code>.
+ That is, <code>person-odb-sqlite.?xx</code> for SQLite,
+ <code>person-odb-pgsql.?xx</code> for PostgreSQL, etc.</p>
+
+ <p>If we need dynamic support for some databases and static for
+ others, then the <code>common</code> code must be generated
+ in the dynamic mode. For example, if we need static support
+ for SQLite and dynamic support for PostgreSQL and Oracle, then
+ the ODB compiler invocations could look like this:</p>
+
+ <pre class="terminal">
+odb -m dynamic -d common person.hxx
+odb -m static -d sqlite person.hxx
+odb -m dynamic -d pgsql person.hxx
+odb -m dynamic -d oracle person.hxx
+ </pre>
+
+ <p>With multi-database support enabled, it is possible to restrict ODB
+ pragmas to apply only to a specific database system (unrestricted
+ pragmas apply to all the databases). For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db pgsql:type("VARCHAR(128)") sqlite:type("TEXT")
+ std::string name_;
+
+ unsigned short age_;
+
+ #pragma db pgsql index member(age_)
+};
+ </pre>
+
+ <p>Above, the pragma for the <code>name_</code> data member shows the
+ use of a database prefix (for example, <code>pgsql:</code>) that
+ only applies to the specifier that follows. The pragma that defines
+ an index on the <code>age_</code> data member shows the use of a
+ database prefix that applies to the whole pragma. In this case the
+ database name must immediately follow the <code>db</code> keyword.</p>
+
+
+ <p>Similar to pragmas, ODB compiler options that determine the kind
+ (for example, <code>--schema-format</code>), names (for example,
+ <code>--odb-file-suffix</code>), or content (for example, prologue
+ and epilogue options) of the output files can be prefixed with the
+ database name. For example:</p>
+
+ <pre class="terminal">
+odb --odb-file-suffix common:-odb-common ...
+ </pre>
+
+ <p>Dynamic multi-database support requires consistent mapping across
+ all the databases. That is, the same classes and data members
+ should be mapped to objects, simple/composite values, etc., for
+ all the databases. In contrast, static multi-database support
+ does not have this restriction. Specifically, with static support,
+ some data members can be transient for some database systems.
+ Similarly, the same class (for example, <code>point</code>) can
+ be mapped to a simple value in one database (for example, to the
+ <code>POINT</code> PostgreSQL type) and to a composite value
+ in another (for example, in SQLite, which does not have a
+ built-in point type).</p>
+
+ <p>The following sections discuss static and dynamic multi-database
+ support in more detail.</p>
+
+
+ <h2><a name="16.1">16.1 Static Multi-Database Support</a></h2>
+
+ <p>With static multi-database support, instead of including
+ <code>person-odb.hxx</code>, application source code has
+ to include <code>person-odb-&lt;db>.hxx</code> header files
+ corresponding to the database systems that will be used.</p>
+
+ <p>The application code has to also use database system-specific
+ interfaces when performing database operations. As an example,
+ consider the following transaction in a single-database
+ application. It uses the common interfaces, that is, classes
+ from the <code>odb</code> namespace.</p>
+
+ <pre class="cxx">
+#include "person-odb.hxx"
+
+odb::database&amp; db = ...
+
+typedef odb::query&lt;person> query;
+typedef odb::result&lt;person> result;
+
+odb::transaction t (db.begin ());
+result r (db.query&lt;person> (query::age &lt; 30));
+...
+t.commit ();
+ </pre>
+
+ <p>In an application that employs static multi-database support
+ the same transaction for SQLite would be rewritten like this:</p>
+
+ <pre class="cxx">
+#include "person-odb-sqlite.hxx"
+
+odb::sqlite::database&amp; db = ...
+
+typedef odb::sqlite::query&lt;person> query;
+typedef odb::result&lt;person> result; // odb:: not odb::sqlite::
+
+odb::sqlite::transaction t (db.begin ());
+result r (db.query&lt;person> (query::age &lt; 30));
+...
+t.commit ();
+ </pre>
+
+ <p>That is, the <code>database</code>, <code>transaction</code>, and
+ <code>query</code> classes now come from the <code>odb::sqlite</code>
+ namespace instead of <code>odb</code>. Other classes that have
+ database system-specific interfaces are <code>connection</code>,
+ <code>statement</code>, and <code>tracer</code>. Note that
+ all of them derive from the corresponding common versions. It
+ is also possible to use common <code>transaction</code>,
+ <code>connection</code>, and <code>statement</code> classes
+ with static support, if desired.</p>
+
+ <p>Notice that we didn't use the <code>odb::sqlite</code> namespace
+ for the <code>result</code> class template. This is because
+ <code>result</code> is database system-independent. All other
+ classes defined in namespace <code>odb</code>, except those
+ specifically mentioned above, are database system-independent.
+ In particular, <code>result</code>, <code>prepared_query</code>,
+ <code>session</code>, <code>schema_catalog</code>, and all the
+ exceptions are database system-independent.</p>
+
+ <p>Writing <code>odb::sqlite::</code> before every name can quickly
+ become burdensome. As we have seen before, in single-database
+ applications that use the common interface we can add the
+ <code>using namespace</code> directive to avoid qualifying
+ each name. For example:</p>
+
+ <pre class="cxx">
+#include "person-odb.hxx"
+
+odb::database&amp; db = ...
+
+{
+ using namespace odb::core;
+
+ typedef query&lt;person> person_query;
+ typedef result&lt;person> person_result;
+
+ transaction t (db.begin ());
+ person_result r (db.query&lt;person> (person_query::age &lt; 30));
+ ...
+ t.commit ();
+}
+ </pre>
+
+ <p>A similar mechanism is available in multi-database support. Each
+ database runtime defines the <code>odb::&lt;db>::core</code>
+ namespace that contains all the database system-independent
+ names as well as the database system-specific ones for this
+ database. Here is how we can rewire the above transaction
+ using this approach:</p>
+
+ <pre class="cxx">
+#include "person-odb-sqlite.hxx"
+
+odb::sqlite::database&amp; db = ...
+
+{
+ using namespace odb::sqlite::core;
+
+ typedef query&lt;person> person_query;
+ typedef result&lt;person> person_result;
+
+ transaction t (db.begin ());
+ person_result r (db.query&lt;person> (person_query::age &lt; 30));
+ ...
+ t.commit ();
+}
+ </pre>
+
+ <p>If the <code>using namespace</code> directive cannot be used, for
+ example, because the same code fragment accesses several databases,
+ then we can still make the namespace qualifications more concise
+ by assigning shorter aliases to database namespaces. For example:</p>
+
+ <pre class="cxx">
+#include "person-odb-pgsql.hxx"
+#include "person-odb-sqlite.hxx"
+
+namespace pg = odb::pgsql;
+namespace sl = odb::sqlite;
+
+pg::database&amp; pg_db = ...
+sl::database&amp; sl_db = ...
+
+typedef pg::query&lt;person> pg_query;
+typedef sl::query&lt;person> sl_query;
+typedef odb::result&lt;person> result;
+
+// First check the local cache.
+//
+odb::transaction t (sl_db.begin ()); // Note: using common transaction.
+result r (sl_db.query&lt;person> (sl_query::age &lt; 30));
+
+// If no hits, try the remote database.
+//
+if (r.empty ())
+{
+ t.commit (); // End the SQLite transaction.
+ t.reset (pg_db.begin ()); // Start the PostgreSQL transaction.
+
+ r = pg_db.query&lt;person> (pg_query::age &lt; 30);
+}
+
+// Handle the result.
+//
+...
+
+t.commit ();
+ </pre>
+
+ <p>With static multi-database support we can make one of the databases
+ the default database with the <code>--default-database</code> option.
+ The default database can be accessed via the common interface, just
+ like with single-database support. For example:</p>
+
+ <pre class="terminal">
+odb -m static -d common -d pgsql -d sqlite --default-database pgsql ...
+ </pre>
+
+ <p>The default database mechanism can be useful when one of the
+ databases is primary or when retrofitting multi-database support
+ into an existing single-database application. For example, if
+ we are adding SQLite as a local cache into an existing
+ application that uses PostgreSQL as its only database, then
+ by making PostgreSQL the default database we avoid having to
+ change all the existing code. Note that if dynamic multi-database
+ support is enabled, then the common (dynamic) interface is always
+ made the default database.</p>
+
+ <h2><a name="16.2">16.2 Dynamic Multi-Database Support</a></h2>
+
+ <p>With dynamic multi-database support, application source code only
+ needs to include the <code>person-odb.hxx</code> header file, just
+ like with single-database support. In particular, we don't need
+ to include any of the <code>person-odb-&lt;db>.hxx</code> files
+ unless we would also like to use certain database systems in the
+ static multi-database mode.</p>
+
+ <p>When performing database operations, the application code
+ uses the common interfaces from the <code>odb</code> namespace,
+ just like with single-database support. As an example, consider
+ a function that can be used to load an object either from a local
+ SQLite cache or a remote PostgreSQL database (in reality, this
+ function can be used with any database system support by ODB
+ provided we generated the database support code for this database
+ and linked it into our application):</p>
+
+ <pre class="cxx">
+#include "person-odb.hxx"
+
+std::unique_ptr&lt;person>
+load (odb::database&amp; db, const std::string&amp; name)
+{
+ odb::transaction t (db.begin ());
+ std::unique_ptr&lt;person> p (db.find (name));
+ t.commit ();
+ return p;
+}
+
+odb::pgsql::database&amp; pg_db = ...
+odb::sqlite::database&amp; sl_db = ...
+
+// First try the local cache.
+//
+std::unique_ptr&lt;person> p (load (sl_db, "John Doe"));
+
+// If not found, try the remote database.
+//
+if (p == 0)
+ p = load (pg_db, "John Doe");
+
+...
+ </pre>
+
+ <p>As you can see, we can use dynamic multi-database support just like
+ single-database support except that now our code can work with
+ different database systems. Note, however, one difference: with
+ single-database support we could perform database operations using
+ either the common <code>odb::database</code> or a database system-specific
+ (for example, <code>odb::sqlite::database</code>) interface
+ with the same effect. In contrast, with dynamic multi-database support,
+ the use of the database system-specific interface results in the
+ switch to the static mode (for which, as was mentioned earlier, we would
+ need to include the corresponding <code>person-odb-&lt;db>.hxx</code>
+ header file). As we will discuss shortly, switching from dynamic to
+ static mode can be used to overcome limitations imposed by dynamic
+ multi-database support.</p>
+
+ <p>Dynamic multi-database support has certain overheads and limitations
+ compared to static support. For database operations, the generated code
+ maintains function tables that are used to dispatch calls to the database
+ system-specific implementations. In single-database and static
+ multi-database support, the <code>query</code> type implements a thin
+ wrapper around the underlying database system's <code>SELECT</code>
+ statement. With dynamic multi-database support, because the
+ underlying database system is only known at query execution
+ (or preparation) time, the <code>query</code> type stores a
+ database system-independent representation of the query that
+ is then translated to the database system-specific form. Because
+ of this database system-independent representation, dynamic
+ support queries have a number of limitations. Specifically, dynamic
+ queries do not support parameter binding in native query fragments.
+ They also make copies of by-value parameterd (by-reference parameters
+ can be used to remove this overhead). Finally, parameters of array
+ types (for example, <code>char[256]</code>) can only be bound
+ by-reference.</p>
+
+ <p>As we mentioned earlier, switching from dynamic to static mode
+ can be an effective way to overcome these limitations. As an
+ example, consider a function that prints the list of people of
+ a certain age. The caller also specified the limit on the number
+ of entries to print. Some database systems, for example, PostgreSQL,
+ allow us to propagate this limit to the database server with the
+ <code>LIMIT</code> clause. To add this clause we would need to
+ construct a native query fragment and, as we discussed above, we
+ won't be able to bind a parameter (the limit) while in the dynamic
+ mode. The following implementation shows how we can overcome this
+ by switching to the static mode and using the PostgreSQL-specific
+ interface:</p>
+
+ <pre class="cxx">
+#include "person-odb.hxx"
+#include "person-odb-pgsql.hxx" // Needed for static mode.
+
+void
+print (odb::database&amp; db, unsigned short age, unsigned long limit)
+{
+ typedef odb::query&lt;person> query;
+ typedef odb::result&lt;person> result;
+
+ odb::transaction t (db.begin ());
+
+ query q (query::age == age);
+ result r;
+
+ if (db.id () == odb::id_pgsql)
+ {
+ // We are using PostgreSQL. Drop down to the static mode and
+ // add the LIMIT clause to the query.
+ //
+ namespace pg = odb::pgsql;
+ typedef pg::query&lt;person> pg_query;
+
+ pg::database&amp; pg_db (static_cast&lt;pg::database&amp;> (db));
+ pg_query pg_q (pg_query (q) + "LIMIT" + pg_query::_val (limit));
+ r = pg_db.query&lt;person> (pg_q);
+ }
+ else
+ r = db.query&lt;person> (q);
+
+ // Handle the result up to the limit elements.
+ //
+ ...
+
+ t.commit ();
+}
+
+odb::pgsql::database&amp; pg_db = ...
+odb::sqlite::database&amp; sl_db = ...
+
+print (sl_db, 30, 100);
+print (sl_db, 30, 100);
+ </pre>
+
+ <p>A few things to note about this example. First, we use the
+ <code>database::id()</code> function to determine the actual database
+ system we use. This function has the following signature:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ enum database_id
+ {
+ id_mysql,
+ id_sqlite,
+ id_pgsql,
+ id_oracle,
+ id_mssql,
+ id_common
+ };
+
+ class database
+ {
+ public:
+ ...
+
+ database_id
+ id () const;
+ }
+}
+ </pre>
+
+ <p>Note that <code>database::id()</code> can never return the
+ <code>id_common</code> value.</p>
+
+ <p>The other thing to note is how we translate the dynamic query
+ to the database system-specific one (the <code>pg_query (q)</code>
+ expression). Every <code>odb::&lt;db>::query</code> class provides
+ such a translation constructor.</p>
+
+ <h3><a name="16.2.2">16.2.2 Dynamic Loading of Database Support Code</a></h3>
+
+ <p>With dynamic multi-database support, the generated database support
+ code automatically registers itself with the function tables that
+ we mentioned earlier. This makes it possible to package the generated
+ code for each database into a separate dynamic-link library (Windows
+ DLL) or dynamic shared object (Unix DSO; collectively referred to as
+ DLLs from now on) and load/unload them from the application
+ dynamically using APIs such as Win32 <code>LoadLibrary()</code> or
+ POSIX <code>dlopen()</code>. This allows the application address
+ space to contain code only for database systems that are actually
+ needed in any particular moment. Another advantage of this approach
+ is the ability to distribute individual database system support
+ separately.</p>
+
+ <p>This section provides an overview of how to package the generated
+ database support code into DLLs for both Windows and Unix using
+ GNU/Linux as an example. Note also that if static multi-database
+ support is used for a particular database system, then the dynamic
+ loading cannot be used for this database. It is, however, still
+ possible to package the generated code into a DLL but this DLL
+ will have to be linked to the executable at link-time rather
+ than at runtime. If dynamic loading is desirable in this situation,
+ then another alternative would be to package the functionality
+ that requires static support together with the database support
+ code into the DLL and import this functionality dynamically
+ using the <code>GetProcAddress()</code> (Win32) or <code>dlsym()</code>
+ (Unix) function.</p>
+
+ <p>The first step in packaging the generated code into DLLs is to
+ set up the symbol exporting. This step is required for
+ Windows DLLs but is optional for Unix DSOs. Most modern Unix
+ systems (such as GNU/Linux) provide control over symbol
+ visibility, which is a mechanism similar to Windows symbol
+ exporting. Notable advantages of using this mechanism to
+ explicitly specify which symbols are visible include
+ smaller Unix DSOs and faster load times. If, however, you are
+ not planning to control symbol visibility on Unix, then you can
+ skip directly to the second step below.</p>
+
+ <p>An important point to understand is that we only need to export
+ the common interface, that is, the classes defined in the
+ <code>person-odb.hxx</code> header. In particular, we don't need
+ to export the database system-specific classes defined in
+ the <code>person-odb-&lt;db>.hxx</code>, unless we are also using
+ this database in the static mode (in which case, the procedure
+ described below will need to be repeated for that database as
+ well).</p>
+
+ <p>The ODB compiler provides two command line options,
+ <code>--export-symbol</code> and <code>--extern-symbol</code>,
+ which can be used to insert the export and extern
+ macros in all the necessary places in the generated header file.
+ You are probably familiar with the concept of export macro which
+ expands to an export directive if we are building the DLL and to
+ an import directive if we are building client code. The
+ extern macro is a supplementary mechanism which is necessary to
+ export explicit template instantiations used by the generated
+ code when query support is enabled. As we will see shortly, the
+ extern macro must expand into the <code>extern</code> C++ keyword
+ in certain situations and must be left undefined in others. To
+ manage all these macro definitions, it is customary to create the
+ so called export header. Based on a single macro that is normally
+ defined in the project file or on the command line and which
+ indicates whether we are building the DLL or client code, the
+ export header file sets the export and extern macros to their
+ appropriate values. Continuing with our person example, on Windows
+ the export header, which we will call <code>person-export.hxx</code>,
+ could look like this:</p>
+
+ <pre class="cxx">
+// person-export.hxx
+//
+// Define PERSON_BUILD_DLL if we are building the DLL. Leave it
+// undefined in client code.
+//
+#ifndef PERSON_EXPORT_HXX
+#define PERSON_EXPORT_HXX
+
+#ifdef PERSON_BUILD_DLL
+# define PERSON_EXPORT __declspec(dllexport)
+#else
+# define PERSON_EXPORT __declspec(dllimport)
+# define PERSON_EXTERN extern
+#endif
+
+#endif // PERSON_EXPORT_HXX
+ </pre>
+
+ <p>The equivalent export header for GCC on GNU/Linux is shown below.
+ Note also that on GNU/Linux, by default, all symbols are visible
+ and we need to add the GCC <code>-fvisibility=hidden</code> option to
+ make them hidden by default.</p>
+
+ <pre class="cxx">
+// person-export.hxx
+//
+#ifndef PERSON_EXPORT_HXX
+#define PERSON_EXPORT_HXX
+
+#define PERSON_EXPORT __attribute__ ((visibility ("default")))
+#define PERSON_EXTERN extern
+
+#endif // PERSON_EXPORT_HXX
+ </pre>
+
+ <p>Next we need to export the <code>person</code> persistent class
+ using the export macro and re-compile our <code>person.hxx</code> file
+ with the <code>--export-symbol</code> and <code>--extern-symbol</code>
+ options. We will also need to include <code>person-export.hxx</code>
+ into the generated <code>person-odb.hxx</code> file. For that we use
+ the <code>--hxx-prologue</code> option. Here is how we can do
+ this with multiple invocations of the ODB compiler:</p>
+
+ <pre class="terminal">
+odb -m dynamic -d common --hxx-prologue "#include \"person-export.hxx\"" \
+--export-symbol PERSON_EXPORT --extern-symbol PERSON_EXTERN person.hxx
+
+odb -m dynamic -d sqlite person.hxx
+odb -m dynamic -d pgsql person.hxx
+ </pre>
+
+ <p>It is also possible to achieve the same with a single invocation.
+ Here we need to restrict some option values to apply only to the
+ <code>common</code> database:</p>
+
+ <pre class="terminal">
+odb -m dynamic -d common -d sqlite -d pgsql \
+--hxx-prologue "common:#include \"person-export.hxx\"" \
+--export-symbol common:PERSON_EXPORT --extern-symbol common:PERSON_EXTERN \
+person.hxx
+ </pre>
+
+ <p>The second step in packaging the generated code into DLLs is to
+ decide where to place the generated common interface code. One
+ option is to place it into a DLL of its own so that we will end
+ up with (replace <code>*.dll</code> with <code>lib*.so</code> for
+ Unix): <code>person.dll</code> plus <code>person-sqlite.dll</code> and
+ <code>person-pgsql.dll</code>, which both link to <code>person.dll</code>,
+ as well as <code>person.exe</code>, which links to <code>person.dll</code>
+ and dynamically loads <code>person-sqlite.dll</code>
+ and/or <code>person-pgsql.dll</code>. If this is the organization
+ that you prefer, then the next step is to build all the DLLs as you
+ normally would any other DLL, placing <code>person-odb.cxx</code>
+ and <code>person.cxx</code> into <code>person.dll</code>,
+ <code>person-odb-sqlite.cxx</code> into <code>person-sqlite.dll</code>,
+ etc. Note that in the pure dynamic multi-database support,
+ <code>person-sqlite.dll</code> and <code>person-pgsql.dll</code>
+ do not export any symbols.</p>
+
+ <p>We can improve on the above organization by getting rid of
+ <code>person.dll</code>, which is not really necessary unless
+ we have multiple executables sharing the same database support.
+ To achieve this, we will place <code>person-odb.cxx</code> into
+ <code>person.exe</code> and export its symbols from the executable
+ instead of a DLL. Exporting symbols from an executable is a seldom
+ used functionality, especially on Windows, however, it is well
+ supported on both Windows and most Unix platforms. Note also that
+ this approach won't work if we also use one of the databases in the
+ static mode.</p>
+
+ <p>On Windows all we have to do is place <code>person-odb.cxx</code>
+ into the executable and compile it as we would in a DLL (that is,
+ with the <code>PERSON_BUILD_DLL</code> macro defined). If Windows
+ linker detects that an executable exports any symbols, then it
+ will automatically create the corresponding import library
+ (<code>person.lib</code> in our case). We then use this import
+ library to build <code>person-sqlite.dll</code> and
+ <code>person-pgsql.dll</code> as before.</p>
+
+ <p>To export symbols from an executable on GNU/Linux all we need to
+ do is add the <code>-rdynamic</code> option when linking our
+ executable.</p>
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="17">17 MySQL Database</a></h1>
+
+ <p>To generate support code for the MySQL database you will need
+ to pass the "<code>--database&nbsp;mysql</code>"
+ (or "<code>-d&nbsp;mysql</code>") option to the ODB compiler.
+ Your application will also need to link to the MySQL ODB runtime
+ library (<code>libodb-mysql</code>). All MySQL-specific ODB
+ classes are defined in the <code>odb::mysql</code> namespace.</p>
+
+ <h2><a name="17.1">17.1 MySQL Type Mapping</a></h2>
+
+ <p>The following table summarizes the default mapping between basic
+ C++ value types and MySQL database types. This mapping can be
+ customized on the per-type and per-member basis using the ODB
+ Pragma Language (<a href="#14">Chapter 14, "ODB Pragma
+ Language"</a>).</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>C++ Type</th>
+ <th>MySQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>bool</code></td>
+ <td><code>TINYINT(1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char</code></td>
+ <td><code>CHAR(1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>signed char</code></td>
+ <td><code>TINYINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned char</code></td>
+ <td><code>TINYINT UNSIGNED</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>short</code></td>
+ <td><code>SMALLINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned short</code></td>
+ <td><code>SMALLINT UNSIGNED</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>int</code></td>
+ <td><code>INT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned int</code></td>
+ <td><code>INT UNSIGNED</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long</code></td>
+ <td><code>BIGINT UNSIGNED</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long long</code></td>
+ <td><code>BIGINT UNSIGNED</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>float</code></td>
+ <td><code>FLOAT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>double</code></td>
+ <td><code>DOUBLE</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>std::string</code></td>
+ <td><code>TEXT/VARCHAR(128)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char[N]</code></td>
+ <td><code>VARCHAR(N-1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+ </table>
+
+ <p>It is possible to map the <code>char</code> C++ type to an integer
+ database type (for example, <code>TINYINT</code>) using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>).</p>
+
+ <p>Note that the <code>std::string</code> type is mapped
+ differently depending on whether a member of this type
+ is an object id or not. If the member is an object id,
+ then for this member <code>std::string</code> is mapped
+ to the <code>VARCHAR(128)</code> MySQL type. Otherwise,
+ it is mapped to <code>TEXT</code>.</p>
+
+ <p>Additionally, by default, C++ enums and C++11 enum classes are
+ automatically mapped to suitable MySQL types. Contiguous
+ enumerations with the zero first enumerator are mapped to
+ the MySQL <code>ENUM</code> type. All other enumerations
+ are mapped to the MySQL types corresponding to their
+ underlying integral types (see table above). In both
+ cases the default <code>NULL</code> semantics is
+ <code>NOT NULL</code>. For example:</p>
+
+ <pre class="cxx">
+enum color {red, green, blue};
+enum class taste: unsigned char
+{
+ bitter = 1, // Non-zero first enumerator.
+ sweet,
+ sour = 4, // Non-contiguous.
+ salty
+};
+
+#pragma db object
+class object
+{
+ ...
+
+ color color_; // Mapped to ENUM ('red', 'green', 'blue') NOT NULL.
+ taste taste_; // Mapped to TINYNT UNSIGNED NOT NULL.
+};
+ </pre>
+
+ <p>The only built-in mapping provided for the MySQL <code>DECIMAL</code>
+ type is to <code>std::string/char[N]</code>, for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type ("DECIMAL(6,3)")
+ std::string value_;
+};
+ </pre>
+
+ <p>You can, however, map <code>DECIMAL</code> to a custom C++ type by
+ providing a suitable <code>odb::mysql::value_traits</code>
+ specialization.</p>
+
+ <p>It is also possible to add support for additional MySQL types,
+ such as geospatial types. For more information, refer to
+ <a href="#14.8">Section 14.8, "Database Type Mapping
+ Pragmas"</a>.</p>
+
+ <h3><a name="17.1.1">17.1.1 String Type Mapping</a></h3>
+
+ <p>The MySQL ODB runtime library provides support for mapping the
+ <code>std::string</code>, <code>char[N]</code>, and
+ <code>std::array&lt;char, N></code> types to the MySQL <code>CHAR</code>,
+ <code>VARCHAR</code>, <code>TEXT</code>, <code>NCHAR</code>, and
+ <code>NVARCHAR</code> types. However, these mappings are not enabled
+ by default (in particular, by default, <code>std::array</code> will
+ be treated as a container). To enable the alternative mappings for
+ these types we need to specify the database type explicitly using
+ the <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section
+ 14.4.3, "<code>type</code>"</a>), for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type("CHAR(2)")
+ char state_[2];
+
+ #pragma db type("VARCHAR(128)")
+ std::string name_;
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+#pragma db value(std::string) type("VARCHAR(128)")
+
+#pragma db object
+class object
+{
+ ...
+
+ std::string name_; // Mapped to VARCHAR(128).
+};
+ </pre>
+
+ <p>The <code>char[N]</code> and <code>std::array&lt;char, N></code> values
+ may or may not be zero-terminated. When extracting such values from the
+ database, ODB will append the zero terminator if there is enough
+ space.</p>
+
+ <h3><a name="17.1.2">17.1.2 Binary Type Mapping</a></h3>
+
+ <p>The MySQL ODB runtime library provides support for mapping the
+ <code>std::vector&lt;char></code>,
+ <code>std::vector&lt;unsigned&nbsp;char></code>,
+ <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
+ <code>std::array&lt;char, N></code>, and
+ <code>std::array&lt;unsigned char, N></code>
+ types to the MySQL <code>BINARY</code>, <code>VARBINARY</code>,
+ and <code>BLOB</code> types. However, these mappings are not enabled
+ by default (in particular, by default, <code>std::vector</code> and
+ <code>std::array</code> will be treated as containers). To enable the
+ alternative mappings for these types we need to specify the database
+ type explicitly using the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), for
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type("BLOB")
+ std::vector&lt;char> buf_;
+
+ #pragma db type("BINARY(16)")
+ unsigned char uuid_[16];
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;char> buffer;
+#pragma db value(buffer) type("BLOB")
+
+#pragma db object
+class object
+{
+ ...
+
+ buffer buf_; // Mapped to BLOB.
+};
+ </pre>
+
+ <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
+ the Database"</a>) <code>char[N]</code> and
+ <code>std::array&lt;char, N></code> parameters are by default passed
+ as a string rather than a binary. To pass such parameters as a binary,
+ we need to specify the database type explicitly in the
+ <code>_val()</code>/<code>_ref()</code> calls. Note also that we
+ don't need to do this for the integrated queries, for example:</p>
+
+ <pre class="cxx">
+char u[16] = {...};
+
+db.query&lt;object> ("uuid = " + query::_val&lt;odb::mysql::id_blob> (u));
+db.query&lt;object> (query::uuid == query::_ref (u));
+ </pre>
+
+ <h2><a name="17.2">17.2 MySQL Database Class</a></h2>
+
+ <p>The MySQL <code>database</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mysql
+ {
+ class database: public odb::database
+ {
+ public:
+ database (const char* user,
+ const char* passwd,
+ const char* db,
+ const char* host = 0,
+ unsigned int port = 0,
+ const char* socket = 0,
+ const char* charset = 0,
+ unsigned long client_flags = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; user,
+ const std::string&amp; passwd,
+ const std::string&amp; db,
+ const std::string&amp; host = "",
+ unsigned int port = 0,
+ const std::string* socket = 0,
+ const std::string&amp; charset = "",
+ unsigned long client_flags = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; user,
+ const std::string* passwd,
+ const std::string&amp; db,
+ const std::string&amp; host = "",
+ unsigned int port = 0,
+ const std::string* socket = 0,
+ const std::string&amp; charset = "",
+ unsigned long client_flags = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; user,
+ const std::string&amp; passwd,
+ const std::string&amp; db,
+ const std::string&amp; host,
+ unsigned int port,
+ const std::string&amp; socket,
+ const std::string&amp; charset = "",
+ unsigned long client_flags = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; user,
+ const std::string* passwd,
+ const std::string&amp; db,
+ const std::string&amp; host,
+ unsigned int port,
+ const std::string&amp; socket,
+ const std::string&amp; charset = "",
+ unsigned long client_flags = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (int&amp; argc,
+ char* argv[],
+ bool erase = false,
+ const std::string&amp; charset = "",
+ unsigned long client_flags = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ static void
+ print_usage (std::ostream&amp;);
+
+ public:
+ const char*
+ user () const;
+
+ const char*
+ password () const;
+
+ const char*
+ db () const;
+
+ const char*
+ host () const;
+
+ unsigned int
+ port () const;
+
+ const char*
+ socket () const;
+
+ const char*
+ charset () const;
+
+ unsigned long
+ client_flags () const;
+
+ public:
+ connection_ptr
+ connection ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/mysql/database.hxx></code>
+ header file to make this class available in your application.</p>
+
+ <p>The overloaded <code>database</code> constructors allow us
+ to specify MySQL database parameters that should be used when
+ connecting to the database. In MySQL <code>NULL</code> and an
+ empty string are treated as the same values for all the
+ string parameters except <code>password</code> and
+ <code>socket</code>.</p>
+
+ <p>The <code>charset</code> argument allows us to specify the client
+ character set, that is, the character set in which the application
+ will encode its text data. Note that this can be different from
+ the MySQL server character set. If this argument is not specified or
+ is empty, then the default MySQL client character set is used, normally
+ <code>latin1</code>. Commonly used values for this argument are
+ <code>latin1</code> (equivalent to Windows cp1252 and similar to
+ ISO-8859-1) and <code>utf8</code>. For other possible values
+ as well as more information on character set support in MySQL,
+ refer to the MySQL documentation.</p>
+
+ <p>The <code>client_flags</code> argument allows us to specify various
+ MySQL client library flags. For more information on the possible
+ values, refer to the MySQL C API documentation. The
+ <code>CLIENT_FOUND_ROWS</code> flag is always set by the MySQL ODB
+ runtime regardless of whether it was passed in the
+ <code>client_flags</code> argument.</p>
+
+ <p>The last constructor extracts the database parameters
+ from the command line. The following options are recognized:</p>
+
+ <pre class="terminal">
+ --user &lt;login>
+ --password &lt;password>
+ --database &lt;name>
+ --host &lt;host>
+ --port &lt;integer>
+ --socket &lt;socket>
+ --options-file &lt;file>
+ </pre>
+
+ <p>The <code>--options-file</code> option allows us to specify some
+ or all of the database options in a file with each option appearing
+ on a separate line followed by a space and an option value.</p>
+
+ <p>If the <code>erase</code> argument to this constructor is true,
+ then the above options are removed from the <code>argv</code>
+ array and the <code>argc</code> count is updated accordingly.
+ This is primarily useful if your application accepts other
+ options or arguments and you would like to get the MySQL
+ options out of the <code>argv</code> array.</p>
+
+ <p>This constructor throws the <code>odb::mysql::cli_exception</code>
+ exception if the MySQL option values are missing or invalid.
+ See section <a href="#17.4">Section 17.4, "MySQL Exceptions"</a>
+ for more information on this exception.</p>
+
+ <p>The static <code>print_usage()</code> function prints the list of options
+ with short descriptions that are recognized by this constructor.</p>
+
+ <p>The last argument to all of the constructors is a pointer to the
+ connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
+ in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
+ non-<code>NULL</code> value, the database instance assumes ownership
+ of the factory instance. The connection factory interface as well as
+ the available implementations are described in the next section.</p>
+
+ <p>The set of accessor functions following the constructors allows us
+ to query the parameters of the <code>database</code> instance.</p>
+
+ <p>The <code>connection()</code> function returns a pointer to the
+ MySQL database connection encapsulated by the
+ <code>odb::mysql::connection</code> class. For more information
+ on <code>mysql::connection</code>, refer to <a href="#17.3">Section
+ 17.3, "MySQL Connection and Connection Factory"</a>.</p>
+
+ <h2><a name="17.3">17.3 MySQL Connection and Connection Factory</a></h2>
+
+ <p>The <code>mysql::connection</code> class has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mysql
+ {
+ class connection: public odb::connection
+ {
+ public:
+ connection (database&amp;);
+ connection (database&amp;, MYSQL*);
+
+ MYSQL*
+ handle ();
+ };
+
+ typedef details::shared_ptr&lt;connection> connection_ptr;
+ }
+}
+ </pre>
+
+ <p>For more information on the <code>odb::connection</code> interface,
+ refer to <a href="#3.6">Section 3.6, "Connections"</a>. The first
+ overloaded <code>mysql::connection</code> constructor establishes a
+ new MySQL connection. The second constructor allows us to create
+ a <code>connection</code> instance by providing an already connected
+ native MySQL handle. Note that the <code>connection</code>
+ instance assumes ownership of this handle. The <code>handle()</code>
+ accessor returns the MySQL handle corresponding to the connection.</p>
+
+ <p>The <code>mysql::connection_factory</code> abstract class has the
+ following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mysql
+ {
+ class connection_factory
+ {
+ public:
+ virtual void
+ database (database&amp;) = 0;
+
+ virtual connection_ptr
+ connect () = 0;
+ };
+ }
+}
+ </pre>
+
+ <p>The <code>database()</code> function is called when a connection
+ factory is associated with a database instance. This happens in
+ the <code>odb::mysql::database</code> class constructors. The
+ <code>connect()</code> function is called whenever a database
+ connection is requested.</p>
+
+ <p>The two implementations of the <code>connection_factory</code>
+ interface provided by the MySQL ODB runtime are
+ <code>new_connection_factory</code> and
+ <code>connection_pool_factory</code>. You will need to include
+ the <code>&lt;odb/mysql/connection-factory.hxx></code>
+ header file to make the <code>connection_factory</code> interface
+ and these implementation classes available in your application.</p>
+
+ <p>The <code>new_connection_factory</code> class creates a new
+ connection whenever one is requested. When a connection is no
+ longer needed, it is released and closed. The
+ <code>new_connection_factory</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mysql
+ {
+ class new_connection_factory: public connection_factory
+ {
+ public:
+ new_connection_factory ();
+ };
+};
+ </pre>
+
+ <p>The <code>connection_pool_factory</code> class implements a
+ connection pool. It has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mysql
+ {
+ class connection_pool_factory: public connection_factory
+ {
+ public:
+ connection_pool_factory (std::size_t max_connections = 0,
+ std::size_t min_connections = 0,
+ bool ping = true);
+
+ protected:
+ class pooled_connection: public connection
+ {
+ public:
+ pooled_connection (database_type&amp;);
+ pooled_connection (database_type&amp;, MYSQL*);
+ };
+
+ typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
+
+ virtual pooled_connection_ptr
+ create ();
+ };
+};
+ </pre>
+
+ <p>The <code>max_connections</code> argument in the
+ <code>connection_pool_factory</code> constructor specifies the maximum
+ number of concurrent connections that this pool factory will
+ maintain. Similarly, the <code>min_connections</code> argument
+ specifies the minimum number of available connections that
+ should be kept open. The <code>ping</code> argument specifies
+ whether the factory should validate the connection before
+ returning it to the caller.</p>
+
+ <p>Whenever a connection is requested, the pool factory first
+ checks if there is an unused connection that can be returned.
+ If there is none, the pool factory checks the
+ <code>max_connections</code> value to see if a new connection
+ can be created. If the total number of connections maintained
+ by the pool is less than this value, then a new connection is
+ created and returned. Otherwise, the caller is blocked until
+ a connection becomes available.</p>
+
+ <p>When a connection is released, the pool factory first checks
+ if there are blocked callers waiting for a connection. If so, then
+ one of them is unblocked and is given the connection. Otherwise,
+ the pool factory checks whether the total number of connections
+ maintained by the pool is greater than the <code>min_connections</code>
+ value. If that's the case, the connection is closed. Otherwise, the
+ connection is added to the pool of available connections to be
+ returned on the next request. In other words, if the number of
+ connections maintained by the pool exceeds <code>min_connections</code>
+ and there are no callers waiting for a new connection,
+ then the pool will close the excess connections.</p>
+
+ <p>If the <code>max_connections</code> value is 0, then the pool will
+ create a new connection whenever all of the existing connections
+ are in use. If the <code>min_connections</code> value is 0, then
+ the pool will never close a connection and instead maintain all
+ the connections that were ever created.</p>
+
+ <p>Connection validation (the <code>ping</code> argument) is useful
+ if your application may experience long periods of inactivity. In
+ such cases the MySQL server may close network connections that have
+ been inactive for too long. If during connection validation the pool
+ factory detects that the connection has been terminated, it silently
+ closes it and tries to find or create another connection instead.</p>
+
+ <p>The <code>create()</code> virtual function is called whenever the
+ pool needs to create a new connection. By deriving from the
+ <code>connection_pool_factory</code> class and overriding this
+ function we can implement custom connection establishment
+ and configuration.</p>
+
+ <p>If you pass <code>NULL</code> as the connection factory to
+ one of the <code>database</code> constructors, then the
+ <code>connection_pool_factory</code> instance will be
+ created by default with the min and max connections values
+ set to <code>0</code> and connection validation enabled.
+ The following code fragment shows how we can pass our own
+ connection factory instance:</p>
+
+ <pre class="cxx">
+#include &lt;odb/database.hxx>
+
+#include &lt;odb/mysql/database.hxx>
+#include &lt;odb/mysql/connection-factory.hxx>
+
+int
+main (int argc, char* argv[])
+{
+ auto_ptr&lt;odb::mysql::connection_factory> f (
+ new odb::mysql::connection_pool_factory (20));
+
+ auto_ptr&lt;odb::database> db (
+ new mysql::database (argc, argv, false, 0, f));
+}
+ </pre>
+
+ <h2><a name="17.4">17.4 MySQL Exceptions</a></h2>
+
+ <p>The MySQL ODB runtime library defines the following MySQL-specific
+ exceptions:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mysql
+ {
+ class database_exception: odb::database_exception
+ {
+ public:
+ unsigned int
+ error () const;
+
+ const std::string&amp;
+ sqlstate () const;
+
+ const std::string&amp;
+ message () const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class cli_exception: odb::exception
+ {
+ public:
+ virtual const char*
+ what () const throw ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/mysql/exceptions.hxx></code>
+ header file to make these exceptions available in your application.</p>
+
+ <p>The <code>odb::mysql::database_exception</code> is thrown if
+ a MySQL database operation fails. The MySQL-specific error
+ information is accessible via the <code>error()</code>,
+ <code>sqlstate()</code>, and <code>message()</code> functions.
+ All this information is also combined and returned in a
+ human-readable form by the <code>what()</code> function.</p>
+
+ <p>The <code>odb::mysql::cli_exception</code> is thrown by the
+ command line parsing constructor of the <code>odb::mysql::database</code>
+ class if the MySQL option values are missing or invalid. The
+ <code>what()</code> function returns a human-readable description
+ of an error.</p>
+
+ <h2><a name="17.5">17.5 MySQL Limitations</a></h2>
+
+ <p>The following sections describe MySQL-specific limitations imposed
+ by the current MySQL and ODB runtime versions.</p>
+
+ <h3><a name="17.5.1">17.5.1 Foreign Key Constraints</a></h3>
+
+ <p>ODB relies on standard SQL behavior which requires that foreign
+ key constraints checking is deferred until the transaction is
+ committed. The only behaviors supported by MySQL are to either
+ check such constraints immediately (InnoDB engine) or to ignore
+ foreign key constraints altogether (all other engines). As a
+ result, by default, schemas generated by the ODB compiler for
+ MySQL have foreign key definitions commented out. They are
+ retained only for documentation.</p>
+
+ <p>You can override the default behavior and instruct the ODB
+ compiler to generate non-deferrable foreign keys by specifying
+ the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
+ compiler option. Note, however, that in this case the order in
+ which you persist, update, and erase objects within a transaction
+ becomes important.</p>
+
+ <h2><a name="17.6">17.6 MySQL Index Definitions</a></h2>
+
+ <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
+ "Index Definition Pragmas"</a>) is used to define a MySQL index,
+ the <code>type</code> clause specifies the index type (for example,
+ <code>UNIQUE</code>, <code>FULLTEXT</code>, <code>SPATIAL</code>),
+ the <code>method</code> clause specifies the index method (for
+ example, <code>BTREE</code>, <code>HASH</code>), and the
+ <code>options</code> clause is not used. The column options
+ can be used to specify column length limits and the sort order.
+ For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ std::string name_;
+
+ #pragma db index method("HASH") member(name_, "(100) DESC")
+};
+ </pre>
+
+ <h2><a name="17.7">17.7 MySQL Stored Procedures</a></h2>
+
+ <p>ODB native views (<a href="#10.6">Section 10.6, "Native Views"</a>)
+ can be used to call MySQL stored procedures. For example, assuming
+ we are using the <code>person</code> class from <a href="#2">Chapter
+ 2, "Hello World Example"</a> (and the corresponding <code>person</code>
+ table), we can create a stored procedure that given the min and max
+ ages returns some information about all the people in that range:</p>
+
+ <pre class="sql">
+CREATE PROCEDURE person_range (
+ IN min_age SMALLINT,
+ IN max_age SMALLINT)
+BEGIN
+ SELECT age, first, last FROM person
+ WHERE age >= min_age AND age &lt;= max_age;
+END
+ </pre>
+
+ <p>Given the above stored procedure we can then define an ODB view
+ that can be used to call it and retrieve its result:</p>
+
+ <pre class="cxx">
+#pragma db view query("CALL person_range((?))")
+struct person_range
+{
+ unsigned short age;
+ std::string first;
+ std::string last;
+};
+ </pre>
+
+ <p>The following example shows how we can use the above view to
+ print the list of people in a specific age range:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;person_range> query;
+typedef odb::result&lt;person_range> result;
+
+transaction t (db.begin ());
+
+result r (
+ db.query&lt;person_range> (
+ query::_val (1) + "," + query::_val (18)));
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ cerr &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->age &lt;&lt; endl;
+
+t.commit ();
+ </pre>
+
+ <p>Note that as with all native views, the order and types of data members
+ must match those of columns in the <code>SELECT</code> list inside
+ the stored procedure.</p>
+
+ <p>There are also a number of limitations when it comes to support for
+ MySQL stored procedures in ODB views. First of all, you have to use
+ MySQL server and client libraries version 5.5.3 or later since this
+ is the version in which support for calling stored procedures with
+ prepared statements was first added (the
+ <code>mysql_stmt_next_result()</code> function).</p>
+
+ <p>In MySQL, a stored procedure can produce multiple results.
+ For example, if a stored procedure executes several
+ <code>SELECT</code> statements, then the result of calling such
+ a procedure consists of two row sets, one for each <code>SELECT</code>
+ statement. Additionally, if the procedure has any <code>OUT</code>
+ or <code>INOUT</code> parameters, then their values are returned as
+ an additional special row set containing only a single row.
+ Because such multiple row sets can contain varying number
+ and type of columns, they cannot be all extracted into a
+ single view. As a result, an ODB view will only extract the
+ data from the first row set and ignore all the subsequent
+ ones.</p>
+
+ <p>In particular, this means that we can use an ODB view to extract
+ the values of the <code>OUT</code> and <code>INOUT</code>
+ parameters provided that the stored procedure does not generate
+ any other row sets. For example:</p>
+
+ <pre class="sql">
+CREATE PROCEDURE person_min_max_age (
+ OUT min_age SMALLINT,
+ OUT max_age SMALLINT)
+BEGIN
+ SELECT MIN(age), MAX(age) INTO min_age, max_age FROM person;
+END
+ </pre>
+
+ <pre class="cxx">
+#pragma db view query("CALL person_min_max_age((?))")
+struct person_min_max_age
+{
+ unsigned short min_age;
+ unsigned short max_age;
+};
+ </pre>
+
+ <pre class="cxx">
+typedef odb::query&lt;person_min_max_age> query;
+
+transaction t (db.begin ());
+
+// We know this query always returns a single row, so use query_value().
+// We have to pass dummy values for OUT parameters.
+//
+person_min_max_age mma (
+ db.query_value&lt;person_min_max_age> (
+ query::_val (0) + "," + query::_val (0)));
+
+cerr &lt;&lt; mma.min_age &lt;&lt; " " &lt;&lt; mma.max_age &lt;&lt; endl;
+
+t.commit ();
+ </pre>
+
+ <p>Another limitation that stems from having multiple results is the
+ inability to cache the result of a stored procedure call. In
+ other words, a MySQL stored procedure call always produces an
+ uncached query result (<a href="#4.4">Section 4.4, "Query
+ Result"</a>).</p>
+
+ <hr class="page-break"/>
+ <h1><a name="18">18 SQLite Database</a></h1>
+
+ <p>To generate support code for the SQLite database you will need
+ to pass the "<code>--database&nbsp;sqlite</code>"
+ (or "<code>-d&nbsp;sqlite</code>") option to the ODB compiler.
+ Your application will also need to link to the SQLite ODB runtime
+ library (<code>libodb-sqlite</code>). All SQLite-specific ODB
+ classes are defined in the <code>odb::sqlite</code> namespace.</p>
+
+ <h2><a name="18.1">18.1 SQLite Type Mapping</a></h2>
+
+ <p>The following table summarizes the default mapping between basic
+ C++ value types and SQLite database types. This mapping can be
+ customized on the per-type and per-member basis using the ODB
+ Pragma Language (<a href="#14">Chapter 14, "ODB Pragma
+ Language"</a>).</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>C++ Type</th>
+ <th>SQLite Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>bool</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>signed char</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned char</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>short</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned short</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>int</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned int</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long long</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long long</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>float</code></td>
+ <td><code>REAL</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>double</code></td>
+ <td><code>REAL</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>std::string</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char[N]</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>std::wstring (Windows only)</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>wchar_t[N] (Windows only)</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>odb::sqlite::text</code></td>
+ <td><code>TEXT (STREAM)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>odb::sqlite::blob</code></td>
+ <td><code>BLOB (STREAM)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+ </table>
+
+ <p>It is possible to map the <code>char</code> C++ type to the
+ <code>INTEGER</code> SQLite type using the <code>db&nbsp;type</code>
+ pragma (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>).</p>
+
+ <p>SQLite represents the <code>NaN</code> <code>FLOAT</code> value
+ as a <code>NULL</code> value. As a result, columns of the
+ <code>float</code> and <code>double</code> types are by default
+ declared as <code>NULL</code>. However, you can override this by
+ explicitly declaring them as <code>NOT NULL</code> with the
+ <code>db&nbsp;not_null</code> pragma (<a href="#14.4.6">Section
+ 14.4.6, "<code>null/not_null</code>"</a>).</p>
+
+ <p>Additionally, by default, C++ enums and C++11 enum classes are
+ automatically mapped to the SQLite <code>INTEGER</code> type with
+ the default <code>NULL</code> semantics being <code>NOT NULL</code>.
+ For example:</p>
+
+ <pre class="cxx">
+enum color {red, green, blue};
+enum class taste: unsigned char
+{
+ bitter = 1,
+ sweet,
+ sour = 4,
+ salty
+};
+
+#pragma db object
+class object
+{
+ ...
+
+ color color_; // Automatically mapped to INTEGER.
+ taste taste_; // Automatically mapped to INTEGER.
+};
+ </pre>
+
+ <p>Note also that SQLite only operates with signed integers and the largest
+ value that an SQLite database can store is a signed 64-bit integer. As
+ a result, greater <code>unsigned&nbsp;long</code> and
+ <code>unsigned&nbsp;long&nbsp;long</code> values will be represented in
+ the database as negative values.</p>
+
+ <p>It is also possible to add support for additional SQLite types,
+ such as <code>NUMERIC</code>. For more information, refer to
+ <a href="#14.8">Section 14.8, "Database Type Mapping
+ Pragmas"</a>.</p>
+
+ <h3><a name="18.1.1">18.1.1 String Type Mapping</a></h3>
+
+ <p>The SQLite ODB runtime library provides support for mapping the
+ <code>std::array&lt;char, N></code> and, on Windows,
+ <code>std::array&lt;wchar_t, N></code> types to the SQLite
+ <code>TEXT</code> type. However, this mapping is not enabled by
+ default (in particular, by default, <code>std::array</code> will
+ be treated as a container). To enable the alternative mapping for
+ this type we need to specify the database type explicitly using
+ the <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section
+ 14.4.3, "<code>type</code>"</a>), for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type("TEXT")
+ std::array&lt;char, 128> name_;
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+typedef std::array&lt;char, 128> name_type;
+#pragma db value(name_type) type("TEXT")
+
+#pragma db object
+class object
+{
+ ...
+
+ name_type name_; // Mapped to TEXT.
+};
+ </pre>
+
+ <p>The <code>char[N]</code>, <code>std::array&lt;char, N></code>,
+ <code>wchar_t[N]</code>, and <code>std::array&lt;wchar_t, N></code>
+ values may or may not be zero-terminated. When extracting such values
+ from the database, ODB will append the zero terminator if there is
+ enough space.</p>
+
+ <h3><a name="18.1.2">18.1.2 Binary Type Mapping</a></h3>
+
+ <p>The SQLite ODB runtime library provides support for mapping the
+ <code>std::vector&lt;char></code>,
+ <code>std::vector&lt;unsigned&nbsp;char></code>,
+ <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
+ <code>std::array&lt;char, N></code>, and
+ <code>std::array&lt;unsigned char, N></code>
+ types to the SQLite <code>BLOB</code> type. However, these mappings
+ are not enabled by default (in particular, by default,
+ <code>std::vector</code> and <code>std::array</code> will be treated
+ as containers). To enable the alternative mappings for these types
+ we need to specify the database type explicitly using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>), for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type("BLOB")
+ std::vector&lt;char> buf_;
+
+ #pragma db type("BLOB")
+ unsigned char uuid_[16];
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;char> buffer;
+#pragma db value(buffer) type("BLOB")
+
+#pragma db object
+class object
+{
+ ...
+
+ buffer buf_; // Mapped to BLOB.
+};
+ </pre>
+
+ <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
+ the Database"</a>) <code>char[N]</code> and
+ <code>std::array&lt;char, N></code> parameters are by default passed
+ as a string rather than a binary. To pass such parameters as a binary,
+ we need to specify the database type explicitly in the
+ <code>_val()</code>/<code>_ref()</code> calls. Note also that we
+ don't need to do this for the integrated queries, for example:</p>
+
+ <pre class="cxx">
+char u[16] = {...};
+
+db.query&lt;object> ("uuid = " + query::_val&lt;odb::sqlite::id_blob> (u));
+db.query&lt;object> (query::uuid == query::_ref (u));
+ </pre>
+
+ <h3><a name="18.1.3">18.1.3 Incremental <code>BLOB</code>/<code>TEXT</code> I/O</a></h3>
+
+ <p>This section describes the SQLite ODB runtime library support for
+ incremental reading and writing of <code>BLOB</code> and
+ <code>TEXT</code> values. The provided API is a thin wrapper
+ around the native SQLite <code>sqlite3_blob_*()</code> function
+ family. As a result, it is highly recommended that you familiarize
+ yourself with the semantics of this SQLite functionality before
+ continuing with this section.</p>
+
+ <p>The SQLite runtime provides the <code>blob</code> and
+ <code>text</code> types that can be used to represent
+ <code>BLOB</code> and <code>TEXT</code> data members
+ that will be read/written using the incremental I/O.
+ For example:</p>
+
+ <pre class="cxx">
+#include &lt;odb/sqlite/blob.hxx>
+#include &lt;odb/sqlite/text.hxx>
+
+#pragma db object
+class object
+{
+public
+ #pragma db id auto
+ unsigned long id;
+
+ odb::sqlite::blob b; // Mapped to BLOB.
+ odb::sqlite::text t; // Mapped to TEXT.
+};
+ </pre>
+
+ <p>The <code>blob</code> and <code>text</code> types should be
+ viewed as <em>descriptors</em> of the <code>BLOB</code> and
+ <code>TEXT</code> values (rather than the values themselves)
+ that can be used to <em>open</em> the values for reading or
+ writing. These two types have an identical interface that
+ is presented below. Notice that it is essentially the list
+ of arguments (except for <code>size</code> which is discussed
+ below) to the <code>sqlite3_blob_open()</code> function:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace sqlite
+ {
+ class blob|text
+ {
+ public:
+ explicit
+ blob|text (std::size_t = 0);
+
+ std::size_t size ()
+ void size (std::size_t);
+
+ const std::string&amp; db () const;
+ const std::string&amp; table () const;
+ const std::string&amp; column () const;
+ long long rowid () const;
+
+ void
+ clear ();
+ };
+ }
+}
+ </pre>
+
+ <p>To read/write data from/to a incremental <code>BLOB</code> or
+ <code>TEXT</code> value we use the corresponding
+ <code>blob_stream</code> and <code>text_stream</code>
+ stream types. Their interfaces closely mimic the
+ underlying <code>sqlite3_blob_*()</code> functions
+ and are presented below. Note that in order to create
+ a stream we have to pass the corresponding descriptor:</p>
+
+ <pre class="cxx">
+#include &lt;odb/sqlite/stream.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class stream
+ {
+ public:
+ stream (const char* db,
+ const char* table,
+ const char* column,
+ long long rowid,
+ bool rw);
+
+ std::size_t
+ size () const;
+
+ // The following two functions throw std::invalid_argument if
+ // offset + n is past size().
+ //
+ void
+ read (void* buf, std::size_t n, std::size_t offset = 0);
+
+ void
+ write (const void* buf, std::size_t n, std::size_t offset = 0);
+
+ sqlite3_blob*
+ handle () const;
+
+ // Close without reporting errors, if any.
+ //
+ ~stream ();
+
+ // Close with reporting errors, if any.
+ //
+ void
+ close ();
+
+ // Open the same BLOB but in a different row. Can be faster
+ // than creating a new stream instance. Note that the stream
+ // must be in the open state prior to calling this function.
+ //
+ void
+ reopen (long long rowid);
+ };
+ }
+}
+
+#include &lt;odb/sqlite/blob-stream.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class blob_stream: public stream
+ {
+ public:
+ blob_stream (const blob&amp;, bool rw);
+ };
+ }
+}
+
+#include &lt;odb/sqlite/text-stream.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class text_stream: public stream
+ {
+ public:
+ text_stream (const text&amp;, bool rw);
+ };
+ }
+}
+ </pre>
+
+ <p>The <code>rw</code> argument to the constructors above
+ specifies whether to open the value for reading only
+ (<code>false</code>) or to read and write
+ (<code>true</code>).</p>
+
+ <p>In SQLite the incremental <code>BLOB</code> and
+ <code>TEXT</code> sizes are fixed in the sense that
+ they must be specified before the object is persisted
+ or updated and the following write operations can
+ only write that much data. This is what the <code>size</code>
+ data member in the descriptors is for. You can also determine
+ the size of the opened value, for both reading and writing,
+ using the <code>size()</code> stream function. The
+ following example puts all of this together:</p>
+
+ <pre class="cxx">
+#include &lt;odb/sqlite/blob-stream.hxx>
+#include &lt;odb/sqlite/text-stream.hxx>
+
+string txt (1024 * 1024, 't');
+vector&lt;char> blb (1024 * 1024, 'b');
+
+object o;
+
+// Persist.
+//
+{
+ transaction tx (db.begin ());
+
+ // Specify the sizes of the values before calling persist().
+ //
+ o.t.size (txt.size ());
+ o.b.size (blb.size ());
+
+ db.persist (o);
+
+ // Write the data.
+ //
+ blob_stream bs (o.b, true); // Open for read/write.
+ assert (bs.size () == blb.size ());
+ bs.write (blb.data (), blb.size ());
+
+ text_stream ts (o.t, true); // Open for read/write.
+ assert (ts.size () == txt.size ());
+ ts.write (txt.data (), txt.size ());
+
+ tx.commit ();
+}
+
+// Load.
+//
+{
+ transaction tx (db.begin ());
+ auto_ptr&lt;object> p (db.load&lt;object> (o.id));
+
+ text_stream ts (p->t, false); // Open for reading.
+ vector&lt;char> t (ts.size () + 1, '\0');
+ ts.read (t.data (), t.size () - 1);
+ assert (string (t.data ()) == txt);
+
+ blob_stream bs (p->b, false); // Open for reading.
+ vector&lt;char> b (bs.size (), '\0');
+ bs.read (b.data (), b.size ());
+ assert (b == blb);
+
+ tx.commit ();
+}
+
+// Update
+//
+txt.resize (txt.size () + 1, 't');
+txt[0] = 'A';
+txt[txt.size () - 1] = 'Z';
+
+blb.resize (blb.size () - 1);
+blb.front () = 'A';
+blb.back () = 'Z';
+
+{
+ transaction tx (db.begin ());
+
+ // Specify the new sizes of the values before calling update().
+ //
+ o.t.size (txt.size ());
+ o.b.size (blb.size ());
+
+ db.update (o);
+
+ // Write the data.
+ //
+ blob_stream bs (o.b, true);
+ bs.write (blb.data (), blb.size ());
+
+ text_stream ts (o.t, true);
+ ts.write (txt.data (), txt.size ());
+
+ tx.commit ();
+}
+ </pre>
+
+ <p>For the most part, the incremental <code>BLOB</code> and
+ <code>TEXT</code> descriptors can be used as any other
+ simple values. Specifically, they can be used as container
+ elements (<a href="#5">Chapter 5, "Containers"</a>), as
+ <code>NULL</code>-able values (<a href="#7.3">Section 7.3,
+ "Pointers and NULL Value Semantics"</a>), and in views
+ (<a href="#10">Chapter 10, "Views"</a>). The following
+ example illustrates the use within a view:</p>
+
+ <pre class="cxx">
+#pragma db view object(object)
+struct load_b
+{
+ odb::sqlite::blob b;
+};
+
+typedef odb::query&lt;load_b> query;
+
+transaction tx (db.begin ());
+
+for (load_b&amp; lb: db.query&lt;load_b> (query::t == "test"))
+{
+ blob_stream bs (lb.b, false);
+ vector&lt;char> b (bs.size (), '\0');
+ bs.read (b.data (), b.size ());
+}
+
+tx.commit ();
+ </pre>
+
+ <p>However, being a low-level, SQLite-specific mechanism, the
+ incremental I/O has a number of nuances that should be kept in
+ mind. Firstly, the streams should be opened within a transaction
+ and, unless already closed, they will be automatically closed
+ when the transaction is committed or rolled back. The following
+ modification of the previous example helps to illustrate this
+ point:</p>
+
+ <pre class="cxx">
+{
+ transaction tx (db.begin ());
+
+ // ...
+
+ db.persist (o);
+
+ blob_stream bs (o.b, true);
+
+ tx.commit ();
+
+ // ERROR: stream is closed.
+ //
+ bs.write (blb.data (), blb.size ());
+}
+
+// ERROR: not in transaction.
+//
+text_stream ts (o.t, true);
+ </pre>
+
+ <p>Because loading an object with an incremental <code>BLOB</code> or
+ <code>TEXT</code> value involves additional actions after the
+ database function returns (that is, reading the actual data),
+ certain commonly-expected "round-trip" assumptions will no
+ longer hold unless special steps are taken, for instance
+ (again, continuing with our example):</p>
+
+ <pre class="cxx">
+transaction tx (db.begin ());
+
+auto_ptr&lt;object> p (db.load&lt;object> (o.id));
+p->name = "foo"; // Update some other member.
+db.update (*p); // Bad behavior: incremental BLOB/TEXT invalidated.
+
+tx.commit ();
+ </pre>
+
+ <p>One way to restore the expected behavior is to place the
+ incremental <code>BLOB</code> and <code>TEXT</code> values
+ into their own, separately loaded/updated sections
+ (<a href="#9">Chapter 9, "Sections"</a>). The alternative
+ approach would be to perform the incremental I/O as part
+ of the database operation <code>post_*</code> callbacks
+ (<a href="#14.1.7">Section 14.1.7, "<code>callback</code>"</a>).</p>
+
+ <p>Finally, note that when using incremental <code>TEXT</code>
+ values, the data that we read/write is the raw bytes in
+ the encoding used by the database (<code>UTF-8</code> by
+ default; see SQLite <code>PRAGMA encoding</code> documentation
+ for details).</p>
+
+ <h2><a name="18.2">18.2 SQLite Database Class</a></h2>
+
+ <p>The SQLite <code>database</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace sqlite
+ {
+ class database: public odb::database
+ {
+ public:
+ database (const std::string&amp; name,
+ int flags = SQLITE_OPEN_READWRITE,
+ bool foreign_keys = true,
+ const std::string&amp; vfs = "",
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+#ifdef _WIN32
+ database (const std::wstring&amp; name,
+ int flags = SQLITE_OPEN_READWRITE,
+ bool foreign_keys = true,
+ const std::string&amp; vfs = "",
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+#endif
+
+ database (int&amp; argc,
+ char* argv[],
+ bool erase = false,
+ int flags = SQLITE_OPEN_READWRITE,
+ bool foreign_keys = true,
+ const std::string&amp; vfs = "",
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ static void
+ print_usage (std::ostream&amp;);
+
+ public:
+ const std::string&amp;
+ name () const;
+
+ int
+ flags () const;
+
+ public:
+ transaction
+ begin_immediate ();
+
+ transaction
+ begin_exclusive ();
+
+ public:
+ connection_ptr
+ connection ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/sqlite/database.hxx></code>
+ header file to make this class available in your application.</p>
+
+ <p>The first constructor opens the specified SQLite database. The
+ <code>name</code> argument is the database file name to open in
+ the UTF-8 encoding. If this argument is empty, then a temporary,
+ on-disk database is created. If this argument is the
+ <code>:memory:</code> special value, then a temporary, in-memory
+ database is created. The <code>flags</code> argument allows us to
+ specify SQLite opening flags. For more information on the possible
+ values, refer to the <code>sqlite3_open_v2()</code> function description
+ in the SQLite C API documentation. The <code>foreign_keys</code>
+ argument specifies whether foreign key constraints checking
+ should be enabled. See <a href="#18.5.3">Section 18.5.3,
+ "Foreign Key Constraints"</a> for more information on foreign
+ keys. The <code>vfs</code> argument specifies the SQLite
+ virtual file system module that should be used to access the
+ database. If this argument is empty, then the default vfs module
+ is used. Again, refer to the <code>sqlite3_open_v2()</code> function
+ documentation for detail.</p>
+
+ <p>The following example shows how we can open the <code>test.db</code>
+ database in the read-write mode and create it if it does not exist:</p>
+
+ <pre class="cxx">
+auto_ptr&lt;odb::database> db (
+ new odb::sqlite::database (
+ "test.db",
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE));
+ </pre>
+
+ <p>The second constructor is the same as the first except that the database
+ name is passes as <code>std::wstring</code> in the UTF-16 encoding. This
+ constructor is only available when compiling for Windows.</p>
+
+ <p>The third constructor extracts the database parameters from the
+ command line. The following options are recognized:</p>
+
+ <pre class="terminal">
+ --database &lt;name>
+ --create
+ --read-only
+ --options-file &lt;file>
+ </pre>
+
+ <p>By default, this constructor opens the database in the read-write mode
+ (<code>SQLITE_OPEN_READWRITE</code> flag). If the <code>--create</code>
+ flag is specified, then the database file is created if it does
+ not already exist (<code>SQLITE_OPEN_CREATE</code> flag). If the
+ <code>--read-only</code> flag is specified, then the database is
+ opened in the read-only mode (<code>SQLITE_OPEN_READONLY</code>
+ flag instead of <code>SQLITE_OPEN_READWRITE</code>). The
+ <code>--options-file</code> option allows us to specify some
+ or all of the database options in a file with each option appearing
+ on a separate line followed by a space and an option value.</p>
+
+ <p>If the <code>erase</code> argument to this constructor is true,
+ then the above options are removed from the <code>argv</code>
+ array and the <code>argc</code> count is updated accordingly.
+ This is primarily useful if your application accepts other
+ options or arguments and you would like to get the SQLite
+ options out of the <code>argv</code> array.</p>
+
+ <p>The <code>flags</code> argument has the same semantics as in
+ the first constructor. Flags from the command line always override
+ the corresponding values specified with this argument.</p>
+
+ <p>The third constructor throws the <code>odb::sqlite::cli_exception</code>
+ exception if the SQLite option values are missing or invalid.
+ See <a href="#18.4">Section 18.4, "SQLite Exceptions"</a>
+ for more information on this exception.</p>
+
+ <p>The static <code>print_usage()</code> function prints the list of options
+ with short descriptions that are recognized by the third constructor.</p>
+
+ <p>The last argument to all of the constructors is a pointer to the
+ connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
+ in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
+ non-<code>NULL</code> value, the database instance assumes ownership
+ of the factory instance. The connection factory interface as well as
+ the available implementations are described in the next section.</p>
+
+ <p>The set of accessor functions following the constructors allows us
+ to query the parameters of the <code>database</code> instance.</p>
+
+ <p>The <code>begin_immediate()</code> and <code>begin_exclusive()</code>
+ functions are the SQLite-specific extensions to the standard
+ <code>odb::database::begin()</code> function (see
+ <a href="#3.5">Section 3.5, "Transactions"</a>). They allow us
+ to start an immediate (<code>BEGIN IMMEDIATE</code>) and an exclusive
+ (<code>BEGIN EXCLUSIVE</code>) SQLite transaction, respectively.
+ For more information on the semantics of the immediate and exclusive
+ transactions, refer to the <code>BEGIN</code> statement description
+ in the SQLite documentation.</p>
+
+ <p>The <code>connection()</code> function returns a pointer to the
+ SQLite database connection encapsulated by the
+ <code>odb::sqlite::connection</code> class. For more information
+ on <code>sqlite::connection</code>, refer to <a href="#18.3">Section
+ 18.3, "SQLite Connection and Connection Factory"</a>.</p>
+
+ <h2><a name="18.3">18.3 SQLite Connection and Connection Factory</a></h2>
+
+ <p>The <code>sqlite::connection</code> class has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace sqlite
+ {
+ class connection: public odb::connection
+ {
+ public:
+ connection (database&amp;, int extra_flags = 0);
+ connection (database&amp;, sqlite3*);
+
+ transaction
+ begin_immediate ();
+
+ transaction
+ begin_exclusive ();
+
+ sqlite3*
+ handle ();
+ };
+
+ typedef details::shared_ptr&lt;connection> connection_ptr;
+ }
+}
+ </pre>
+
+ <p>For more information on the <code>odb::connection</code> interface,
+ refer to <a href="#3.6">Section 3.6, "Connections"</a>. The first
+ overloaded <code>sqlite::connection</code> constructor opens
+ a new SQLite connection. The <code>extra_flags</code> argument can
+ be used to specify extra <code>sqlite3_open_v2()</code> flags
+ that are combined with the flags specified in the
+ <code>sqlite::database</code> constructor. The second constructor
+ allows us to create a <code>connection</code> instance by providing
+ an already open native SQLite handle. Note that the
+ <code>connection</code> instance assumes ownership of this handle.</p>
+
+ <p>The <code>begin_immediate()</code> and <code>begin_exclusive()</code>
+ functions allow us to start an immediate and an exclusive SQLite
+ transaction on the connection, respectively. Their semantics are
+ equivalent to the corresponding functions defined in the
+ <code>sqlite::database</code> class (<a href="#18.2">Section 18.2,
+ "SQLite Database Class"</a>). The <code>handle()</code> accessor
+ returns the SQLite handle corresponding to the connection.</p>
+
+ <p>The <code>sqlite::connection_factory</code> abstract class has the
+ following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace sqlite
+ {
+ class connection_factory
+ {
+ public:
+ virtual void
+ database (database&amp;) = 0;
+
+ virtual connection_ptr
+ connect () = 0;
+ };
+ }
+}
+ </pre>
+
+ <p>The <code>database()</code> function is called when a connection
+ factory is associated with a database instance. This happens in
+ the <code>odb::sqlite::database</code> class constructors. The
+ <code>connect()</code> function is called whenever a database
+ connection is requested.</p>
+
+ <p>The three implementations of the <code>connection_factory</code>
+ interface provided by the SQLite ODB runtime library are
+ <code>single_connection_factory</code>,
+ <code>new_connection_factory</code>, and
+ <code>connection_pool_factory</code>. You will need to include
+ the <code>&lt;odb/sqlite/connection-factory.hxx></code>
+ header file to make the <code>connection_factory</code> interface
+ and these implementation classes available in your application.</p>
+
+ <p>The <code>single_connection_factory</code> class creates a
+ single connection that is shared between all the threads in
+ an application. If the connection is currently not in use,
+ then it is returned to the caller. Otherwise, the caller is
+ blocked until the connection becomes available. The
+ <code>single_connection_factory</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace sqlite
+ {
+ class single_connection_factory: public connection_factory
+ {
+ public:
+ single_connection_factory ();
+
+ protected:
+ class single_connection: public connection
+ {
+ public:
+ single_connection (database&amp;, int extra_flags = 0);
+ single_connection (database&amp;, sqlite3*);
+ };
+
+ typedef details::shared_ptr&lt;single_connection> single_connection_ptr;
+
+ virtual single_connection_ptr
+ create ();
+ };
+};
+ </pre>
+
+ <p>The <code>create()</code> virtual function is called when the
+ factory needs to create the connection. By deriving from the
+ <code>single_connection_factory</code> class and overriding this
+ function we can implement custom connection establishment
+ and configuration.</p>
+
+ <p>The <code>new_connection_factory</code> class creates a new
+ connection whenever one is requested. When a connection is no
+ longer needed, it is released and closed. The
+ <code>new_connection_factory</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace sqlite
+ {
+ class new_connection_factory: public connection_factory
+ {
+ public:
+ new_connection_factory ();
+ };
+};
+ </pre>
+
+ <p>The <code>connection_pool_factory</code> class implements a
+ connection pool. It has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace sqlite
+ {
+ class connection_pool_factory: public connection_factory
+ {
+ public:
+ connection_pool_factory (std::size_t max_connections = 0,
+ std::size_t min_connections = 0);
+
+ protected:
+ class pooled_connection: public connection
+ {
+ public:
+ pooled_connection (database_type&amp;, int extra_flags = 0);
+ pooled_connection (database_type&amp;, sqlite3*);
+ };
+
+ typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
+
+ virtual pooled_connection_ptr
+ create ();
+ };
+};
+ </pre>
+
+ <p>The <code>max_connections</code> argument in the
+ <code>connection_pool_factory</code> constructor specifies the maximum
+ number of concurrent connections that this pool factory will
+ maintain. Similarly, the <code>min_connections</code> argument
+ specifies the minimum number of available connections that
+ should be kept open.</p>
+
+ <p>Whenever a connection is requested, the pool factory first
+ checks if there is an unused connection that can be returned.
+ If there is none, the pool factory checks the
+ <code>max_connections</code> value to see if a new connection
+ can be created. If the total number of connections maintained
+ by the pool is less than this value, then a new connection is
+ created and returned. Otherwise, the caller is blocked until
+ a connection becomes available.</p>
+
+ <p>When a connection is released, the pool factory first checks
+ if there are blocked callers waiting for a connection. If so, then
+ one of them is unblocked and is given the connection. Otherwise,
+ the pool factory checks whether the total number of connections
+ maintained by the pool is greater than the <code>min_connections</code>
+ value. If that's the case, the connection is closed. Otherwise, the
+ connection is added to the pool of available connections to be
+ returned on the next request. In other words, if the number of
+ connections maintained by the pool exceeds <code>min_connections</code>
+ and there are no callers waiting for a new connection,
+ then the pool will close the excess connections.</p>
+
+ <p>If the <code>max_connections</code> value is 0, then the pool will
+ create a new connection whenever all of the existing connections
+ are in use. If the <code>min_connections</code> value is 0, then
+ the pool will never close a connection and instead maintain all
+ the connections that were ever created.</p>
+
+ <p>The <code>create()</code> virtual function is called whenever the
+ pool needs to create a new connection. By deriving from the
+ <code>connection_pool_factory</code> class and overriding this
+ function we can implement custom connection establishment
+ and configuration.</p>
+
+ <p>By default, connections created by <code>new_connection_factory</code>
+ and <code>connection_pool_factory</code> enable the SQLite shared cache
+ mode and use the unlock notify functionality to aid concurrency. To
+ disable the shared cache mode you can pass the
+ <code>SQLITE_OPEN_PRIVATECACHE</code> flag when creating the database
+ instance. For more information on the shared cache mode refer to the
+ SQLite documentation.</p>
+
+ <p>If you pass <code>NULL</code> as the connection factory to one of the
+ <code>database</code> constructors, then the <code>connection_pool_factory</code>
+ instance will be created by default with the min and max connections
+ values set to <code>0</code>. The following code fragment shows how we
+ can pass our own connection factory instance:</p>
+
+ <pre class="cxx">
+#include &lt;odb/database.hxx>
+
+#include &lt;odb/sqlite/database.hxx>
+#include &lt;odb/sqlite/connection-factory.hxx>
+
+int
+main (int argc, char* argv[])
+{
+ auto_ptr&lt;odb::sqlite::connection_factory> f (
+ new odb::sqlite::connection_pool_factory (20));
+
+ auto_ptr&lt;odb::database> db (
+ new sqlite::database (argc, argv, false, SQLITE_OPEN_READWRITE, f));
+}
+ </pre>
+
+ <h2><a name="18.4">18.4 SQLite Exceptions</a></h2>
+
+ <p>The SQLite ODB runtime library defines the following SQLite-specific
+ exceptions:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace sqlite
+ {
+ class forced_rollback: odb::recoverable
+ {
+ public:
+ virtual const char*
+ what () const throw ();
+ };
+
+ class database_exception: odb::database_exception
+ {
+ public:
+ int
+ error () const
+
+ int
+ extended_error () const;
+
+ const std::string&amp;
+ message () const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class cli_exception: odb::exception
+ {
+ public:
+ virtual const char*
+ what () const throw ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/sqlite/exceptions.hxx></code>
+ header file to make these exceptions available in your application.</p>
+
+ <p>The <code>odb::sqlite::forced_rollback</code> exception is thrown if
+ SQLite is forcing the current transaction to roll back. For more
+ information on this behavior refer to <a href="#18.5.6">Section 18.5.6,
+ "Forced Rollback"</a>.</p>
+
+ <p>The <code>odb::sqlite::database_exception</code> is thrown if
+ an SQLite database operation fails. The SQLite-specific error
+ information is accessible via the <code>error()</code>,
+ <code>extended_error()</code>, and <code>message()</code> functions.
+ All this information is also combined and returned in a
+ human-readable form by the <code>what()</code> function.</p>
+
+ <p>The <code>odb::sqlite::cli_exception</code> is thrown by the
+ command line parsing constructor of the <code>odb::sqlite::database</code>
+ class if the SQLite option values are missing or invalid. The
+ <code>what()</code> function returns a human-readable description
+ of an error.</p>
+
+
+ <h2><a name="18.5">18.5 SQLite Limitations</a></h2>
+
+ <p>The following sections describe SQLite-specific limitations imposed by
+ the current SQLite and ODB runtime versions.</p>
+
+ <h3><a name="18.5.1">18.5.1 Query Result Caching</a></h3>
+
+ <p>SQLite ODB runtime implementation does not perform query result caching
+ (<a href="#4.4">Section 4.4, "Query Result"</a>) even when explicitly
+ requested. The SQLite API supports interleaving execution of multiple
+ prepared statements on a single connection. As a result, with SQLite, it
+ is possible to have multiple uncached results and calls to other database
+ functions do not invalidate them. The only limitation of the uncached
+ SQLite results is the unavailability of the <code>result::size()</code>
+ function. If you call this function on an SQLite query result, then
+ the <code>odb::result_not_cached</code> exception
+ (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>) is always
+ thrown. Future versions of the SQLite ODB runtime library may add support
+ for result caching.</p>
+
+ <h3><a name="18.5.2">18.5.2 Automatic Assignment of Object Ids</a></h3>
+
+ <p>Due to SQLite API limitations, every automatically assigned object id
+ (<a href="#14.4.2">Section 14.4.2, "<code>auto</code>"</a>) should have
+ the <code>INTEGER</code> SQLite type. While SQLite will treat other
+ integer type names (such as <code>INT</code>, <code>BIGINT</code>, etc.)
+ as <code>INTEGER</code>, automatic id assignment will not work. By default,
+ ODB maps all C++ integral types to <code>INTEGER</code>. This means that
+ the only situation that requires consideration is the assignment of a
+ custom database type using the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>). For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ //#pragma db id auto type("INT") // Will not work.
+ //#pragma db id auto type("INTEGER") // Ok.
+ #pragma db id auto // Ok, Mapped to INTEGER.
+ unsigned int id_;
+};
+ </pre>
+
+ <h3><a name="18.5.3">18.5.3 Foreign Key Constraints</a></h3>
+
+ <p>By default the SQLite ODB runtime enables foreign key constraints
+ checking (<code>PRAGMA foreign_keys=ON</code>). You can disable foreign
+ keys by passing <code>false</code> as the <code>foreign_keys</code>
+ argument to one of the <code>odb::sqlite::database</code> constructors.
+ Foreign keys will also be disabled if the SQLite library is built without
+ support for foreign keys (<code>SQLITE_OMIT_FOREIGN_KEY</code> and
+ <code>SQLITE_OMIT_TRIGGER</code> macros) or if you are using
+ an SQLite version prior to 3.6.19, which does not support foreign
+ key constraints checking.</p>
+
+ <p>If foreign key constraints checking is disabled or not available,
+ then inconsistencies in object relationships will not be detected.
+ Furthermore, using the <code>erase_query()</code> function
+ (<a href="#3.11">Section 3.11, "Deleting Persistent Objects"</a>)
+ to delete persistent objects that contain containers will not work
+ correctly. Container data for such objects will not be deleted.</p>
+
+ <p>When foreign key constraints checking is enabled, then you may
+ get the "foreign key constraint failed" error while re-creating the
+ database schema. This error is due to bugs in the SQLite DDL foreign
+ keys support. The recommended work-around for this problem is to
+ temporarily disable foreign key constraints checking while
+ re-creating the schema. The following code fragment shows how
+ this can be done:</p>
+
+ <pre class="cxx">
+#include &lt;odb/connection.hxx>
+#include &lt;odb/transaction.hxx>
+#include &lt;odb/schema-catalog.hxx>
+
+odb::database&amp; db = ...
+
+{
+ odb::connection_ptr c (db.connection ());
+
+ c->execute ("PRAGMA foreign_keys=OFF");
+
+ odb::transaction t (c->begin ());
+ odb::schema_catalog::create_schema (db);
+ t.commit ();
+
+ c->execute ("PRAGMA foreign_keys=ON");
+}
+ </pre>
+
+ <p>Finally, ODB assumes the standard SQL behavior which requires
+ that foreign key constraints checking is deferred until the
+ transaction is committed. Default SQLite behavior is to check such
+ constraints immediately. As a result, when used with ODB, a custom
+ database schema that defines foreign key constraints may need to
+ declare such constraints as <code>DEFERRABLE INITIALLY DEFERRED</code>,
+ as shown in the following example. By default, schemas generated by
+ the ODB compiler meet this requirement automatically.</p>
+
+ <pre class="sql">
+CREATE TABLE Employee (
+ ...
+ employer INTEGER REFERENCES Employer(id)
+ DEFERRABLE INITIALLY DEFERRED);
+ </pre>
+
+ <p>You can override the default behavior and instruct the ODB
+ compiler to generate non-deferrable foreign keys by specifying
+ the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
+ compiler option. Note, however, that in this case the order in
+ which you persist, update, and erase objects within a transaction
+ becomes important.</p>
+
+ <h3><a name="18.5.4">18.5.4 Constraint Violations</a></h3>
+
+ <p>Due to the granularity of the SQLite error codes, it is impossible
+ to distinguish between the duplicate primary key and other constraint
+ violations. As a result, when making an object persistent, the SQLite
+ ODB runtime will translate all constraint violation errors to the
+ <code>object_already_persistent</code> exception (<a href="#3.14">Section
+ 3.14, "ODB Exceptions"</a>).</p>
+
+ <h3><a name="18.5.5">18.5.5 Sharing of Queries</a></h3>
+
+ <p>As discussed in <a href="#4.3">Section 4.3, "Executing a Query"</a>, a
+ query instance that does not have any by-reference parameters is
+ immutable and can be shared between multiple threads without
+ synchronization. Currently, the SQLite ODB runtime does not support this
+ functionality. Future versions of the library will remove this
+ limitation.</p>
+
+ <h3><a name="18.5.6">18.5.6 Forced Rollback</a></h3>
+
+ <p>In SQLite 3.7.11 or later, if one of the connections participating in
+ the shared cache rolls back a transaction, then ongoing transactions
+ on other connections in the shared cache may also be forced to roll back.
+ An example of such behavior would be a read-only transaction that is
+ forced to roll back while iterating over the query result because another
+ transaction on another connection was rolled back.</p>
+
+ <p>If a transaction is being forced to roll back by SQLite, then ODB
+ throws <code>odb::sqlite::forced_rollback</code>
+ (<a href="#18.4">Section 18.4, "SQLite Exceptions"</a>) which is
+ a recoverable exception (<a href="#3.7">3.7 Error Handling and
+ Recovery</a>). As a result, the recommended way to handle this
+ exception is to re-execute the affected transaction.</p>
+
+ <h3><a name="18.5.7">18.5.7 Database Schema Evolution</a></h3>
+
+ <p>From the list of schema migration changes supported by ODB
+ (<a href="#13.2">Section 13.2, "Schema Migration"</a>), the
+ following are not supported by SQLite:</p>
+
+ <ul class="list">
+ <li>drop column</li>
+ <li>alter column, set <code>NULL</code>/<code>NOT NULL</code></li>
+ <li>add foreign key</li>
+ <li>drop foreign key</li>
+ </ul>
+
+ <p>The biggest problem is the lack of support for dropping columns.
+ This means that it would be impossible to delete a data member
+ in a persistent class. To work around this limitation ODB
+ implements <em>logical delete</em> for columns that allow
+ <code>NULL</code> values. In this case, instead of dropping
+ the column (in the post-migration stage), the schema migration
+ statements will automatically reset this column in all the
+ existing rows to <code>NULL</code>. Any new rows that are
+ inserted later will also automatically have this column set
+ to <code>NULL</code> (unless the column specifies a default
+ value).</p>
+
+ <p>Since it is also impossible to change the column's
+ <code>NULL</code>/<code>NOT NULL</code> attribute after it
+ has been added, to make schema evolution support usable in
+ SQLite, all the columns should be added as <code>NULL</code>
+ even if semantically they should not allow <code>NULL</code>
+ values. We should also normally refrain from assigning
+ default values to columns (<a href="#14.4.7">Section 14.4.7,
+ <code>default</code></a>), unless the space overhead of
+ a default value is not a concern. Explicitly making all
+ the data members <code>NULL</code> would be burdensome
+ and ODB provides the <code>--sqlite-override-null</code>
+ command line option that forces all the columns, even those
+ that were explicitly marked <code>NOT NULL</code>, to be
+ <code>NULL</code> in SQLite.</p>
+
+ <p>SQLite only supports adding foreign keys as part of the
+ column addition. As a result, we can only add a new
+ data member of an object pointer type if it points
+ to an object with a simple (single-column) object id.</p>
+
+ <p>SQLite also doesn't support dropping foreign keys.
+ Leaving a foreign key around works well with logical
+ delete unless we also want to delete the pointed-to
+ object. In this case we will have to leave an
+ empty table corresponding to the pointed-to object
+ around. An alternative would be to make a copy of the
+ pointing object without the object pointer, migrate the
+ data, and then delete both the old pointing and the
+ pointed-to objects. Since this will result in dropping
+ the pointing table, the foreign key will be dropped
+ as well. Yet another, more radical, solution to this
+ problem is to disable foreign keys checking altogether
+ (see the <code>foreign_keys</code> SQLite pragma).</p>
+
+ <p>To summarize, to make schema evolution support usable
+ in SQLite we should pass the <code>--sqlite-override-null</code>
+ option when compiling our persistent classes and also refrain
+ from assigning default values to data members. Note also that
+ this has to be done from the start so that every column is added
+ as <code>NULL</code> and therefore can be logically deleted later.
+ In particular, you cannot add the <code>--sqlite-override-null</code>
+ option when you realize you need to delete a data member. At this
+ point it is too late since the column has already been added
+ as <code>NOT NULL</code> in existing databases. We should also
+ avoid composite object ids if we are planning to use object
+ relationships.</p>
+
+ <h2><a name="18.6">18.6 SQLite Index Definitions</a></h2>
+
+ <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
+ "Index Definition Pragmas"</a>) is used to define an SQLite index,
+ the <code>type</code> clause specifies the index type (for example,
+ <code>UNIQUE</code>) while the <code>method</code> and
+ <code>options</code> clauses are not used. The column options
+ can be used to specify collations and the sort order. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ std::string name_;
+
+ #pragma db index member(name_, "COLLATE binary DESC")
+};
+ </pre>
+
+ <p>Index names in SQLite are database-global. To avoid name clashes,
+ ODB automatically prefixes each index name with the table name on
+ which it is defined.</p>
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="19">19 PostgreSQL Database</a></h1>
+
+ <p>To generate support code for the PostgreSQL database you will need
+ to pass the "<code>--database&nbsp;pgsql</code>"
+ (or "<code>-d&nbsp;pgsql</code>") option to the ODB compiler.
+ Your application will also need to link to the PostgreSQL ODB runtime
+ library (<code>libodb-pgsql</code>). All PostgreSQL-specific ODB
+ classes are defined in the <code>odb::pgsql</code> namespace.</p>
+
+ <p>ODB utilizes prepared statements extensively. Support for prepared
+ statements was added in PostgreSQL version 7.4 with the introduction
+ of the messaging protocol version 3.0. For this reason, ODB supports
+ only PostgreSQL version 7.4 and later.</p>
+
+ <h2><a name="19.1">19.1 PostgreSQL Type Mapping</a></h2>
+
+ <p>The following table summarizes the default mapping between basic
+ C++ value types and PostgreSQL database types. This mapping can be
+ customized on the per-type and per-member basis using the ODB
+ Pragma Language (<a href="#14">Chapter 14, "ODB Pragma
+ Language"</a>).</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>C++ Type</th>
+ <th>PostgreSQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>bool</code></td>
+ <td><code>BOOLEAN</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char</code></td>
+ <td><code>CHAR(1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>signed char</code></td>
+ <td><code>SMALLINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned char</code></td>
+ <td><code>SMALLINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>short</code></td>
+ <td><code>SMALLINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned short</code></td>
+ <td><code>SMALLINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>int</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned int</code></td>
+ <td><code>INTEGER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>float</code></td>
+ <td><code>REAL</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>double</code></td>
+ <td><code>DOUBLE PRECISION</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>std::string</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char[N]</code></td>
+ <td><code>VARCHAR(N-1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+ </table>
+
+ <p>It is possible to map the <code>char</code> C++ type to an integer
+ database type (for example, <code>SMALLINT</code>) using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>).</p>
+
+ <p>Additionally, by default, C++ enums and C++11 enum classes are
+ automatically mapped to the PostgreSQL types corresponding to their
+ underlying integral types (see table above). The default
+ <code>NULL</code> semantics is <code>NOT NULL</code>. For
+ example:</p>
+
+ <pre class="cxx">
+enum color {red, green, blue};
+enum class taste: unsigned char
+{
+ bitter = 1,
+ sweet,
+ sour = 4,
+ salty
+};
+
+#pragma db object
+class object
+{
+ ...
+
+ color color_; // Automatically mapped to INTEGER.
+ taste taste_; // Automatically mapped to SMALLINT.
+};
+ </pre>
+
+ <p>Note also that because PostgreSQL does not support unsigned integers,
+ the <code>unsigned&nbsp;short</code>, <code>unsigned&nbsp;int</code>, and
+ <code>unsigned&nbsp;long</code>/<code>unsigned&nbsp;long&nbsp;long</code> C++ types
+ are by default mapped to the <code>SMALLINT</code>, <code>INTEGER</code>,
+ and <code>BIGINT</code> PostgreSQL types, respectively. The sign bit
+ of the value stored by the database for these types will contain
+ the most significant bit of the actual unsigned value being
+ persisted.</p>
+
+ <p>It is also possible to add support for additional PostgreSQL types,
+ such as <code>NUMERIC</code>, geometry types, <code>XML</code>,
+ <code>JSON</code>, enumeration types, composite types, arrays,
+ geospatial types, and the key-value store (<code>HSTORE</code>).
+ For more information, refer to <a href="#14.8">Section 14.8,
+ "Database Type Mapping Pragmas"</a>.</p>
+
+ <h3><a name="19.1.1">19.1.1 String Type Mapping</a></h3>
+
+ <p>The PostgreSQL ODB runtime library provides support for mapping the
+ <code>std::string</code>, <code>char[N]</code>, and
+ <code>std::array&lt;char, N></code> types to the PostgreSQL
+ <code>CHAR</code>, <code>VARCHAR</code>, and <code>TEXT</code>
+ types. However, these mappings are not enabled by default (in
+ particular, by default, <code>std::array</code> will be treated
+ as a container). To enable the alternative mappings for these
+ types we need to specify the database type explicitly using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>), for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type("CHAR(2)")
+ char state_[2];
+
+ #pragma db type("VARCHAR(128)")
+ std::string name_;
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+#pragma db value(std::string) type("VARCHAR(128)")
+
+#pragma db object
+class object
+{
+ ...
+
+ std::string name_; // Mapped to VARCHAR(128).
+};
+ </pre>
+
+ <p>The <code>char[N]</code> and <code>std::array&lt;char, N></code> values
+ may or may not be zero-terminated. When extracting such values from the
+ database, ODB will append the zero terminator if there is enough
+ space.</p>
+
+ <h3><a name="19.1.2">19.1.2 Binary Type and <code>UUID</code> Mapping</a></h3>
+
+ <p>The PostgreSQL ODB runtime library provides support for mapping the
+ <code>std::vector&lt;char></code>,
+ <code>std::vector&lt;unsigned&nbsp;char></code>,
+ <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
+ <code>std::array&lt;char, N></code>, and
+ <code>std::array&lt;unsigned char, N></code> types to the PostgreSQL
+ <code>BYTEA</code> type. There is also support for mapping the
+ <code>char[16]</code> array to the PostgreSQL <code>UUID</code> type.
+ However, these mappings are not enabled by default (in particular, by
+ default, <code>std::vector</code> and <code>std::array</code> will be
+ treated as containers). To enable the alternative mappings for these
+ types we need to specify the database type explicitly using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>), for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type("UUID")
+ char uuid_[16];
+
+ #pragma db type("BYTEA")
+ std::vector&lt;char> buf_;
+
+ #pragma db type("BYTEA")
+ unsigned char data_[256];
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;char> buffer;
+#pragma db value(buffer) type("BYTEA")
+
+#pragma db object
+class object
+{
+ ...
+
+ buffer buf_; // Mapped to BYTEA.
+};
+ </pre>
+
+ <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
+ the Database"</a>) <code>char[N]</code> and
+ <code>std::array&lt;char, N></code> parameters are by default passed
+ as a string rather than a binary. To pass such parameters as a binary,
+ we need to specify the database type explicitly in the
+ <code>_val()</code>/<code>_ref()</code> calls. Note also that we
+ don't need to do this for the integrated queries, for example:</p>
+
+ <pre class="cxx">
+char u[16] = {...};
+
+db.query&lt;object> ("uuid = " + query::_val&lt;odb::pgsql::id_uuid> (u));
+db.query&lt;object> ("buf = " + query::_val&lt;odb::pgsql::id_bytea> (u));
+db.query&lt;object> (query::uuid == query::_ref (u));
+ </pre>
+
+ <h2><a name="19.2">19.2 PostgreSQL Database Class</a></h2>
+
+ <p>The PostgreSQL <code>database</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace pgsql
+ {
+ class database: public odb::database
+ {
+ public:
+ database (const std::string&amp; user,
+ const std::string&amp; password,
+ const std::string&amp; db,
+ const std::string&amp; host = "",
+ unsigned int port = 0,
+ const std::string&amp; extra_conninfo = "",
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; user,
+ const std::string&amp; password,
+ const std::string&amp; db,
+ const std::string&amp; host,
+ const std::string&amp; socket_ext,
+ const std::string&amp; extra_conninfo = "",
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; conninfo,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (int&amp; argc,
+ char* argv[],
+ bool erase = false,
+ const std::string&amp; extra_conninfo = "",
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ static void
+ print_usage (std::ostream&amp;);
+
+ public:
+ const std::string&amp;
+ user () const;
+
+ const std::string&amp;
+ password () const;
+
+ const std::string&amp;
+ db () const;
+
+ const std::string&amp;
+ host () const;
+
+ unsigned int
+ port () const;
+
+ const std::string&amp;
+ socket_ext () const;
+
+ const std::string&amp;
+ extra_conninfo () const;
+
+ const std::string&amp;
+ conninfo () const;
+
+ public:
+ connection_ptr
+ connection ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/pgsql/database.hxx></code>
+ header file to make this class available in your application.</p>
+
+ <p>The overloaded <code>database</code> constructors allow us to specify
+ the PostgreSQL database parameters that should be used when connecting
+ to the database. The <code>port</code> argument in the first constructor
+ is an integer value specifying the TCP/IP port number to connect to. A
+ zero port number indicates that the default port should be used.
+ The <code>socket_ext</code> argument in the second constructor is a
+ string value specifying the UNIX-domain socket file name extension.</p>
+
+ <p>The third constructor allows us to specify all the database parameters
+ as a single <code>conninfo</code> string. All other constructors
+ accept additional database connection parameters as the
+ <code>extra_conninfo</code> argument. For more information
+ about the format of the <code>conninfo</code> string, refer to
+ the <code>PQconnectdb()</code> function description in the PostgreSQL
+ documentation. In the case of <code>extra_conninfo</code>, all the
+ database parameters provided in this string will take precedence
+ over those explicitly specified with other constructor arguments.</p>
+
+ <p>The last constructor extracts the database parameters
+ from the command line. The following options are recognized:</p>
+
+ <pre class="terminal">
+ --user &lt;login> | --username &lt;login>
+ --password &lt;password>
+ --database &lt;name> | --dbname &lt;name>
+ --host &lt;host>
+ --port &lt;integer>
+ --options-file &lt;file>
+ </pre>
+
+ <p>The <code>--options-file</code> option allows us to specify some
+ or all of the database options in a file with each option appearing
+ on a separate line followed by a space and an option value.</p>
+
+ <p>If the <code>erase</code> argument to this constructor is true,
+ then the above options are removed from the <code>argv</code>
+ array and the <code>argc</code> count is updated accordingly.
+ This is primarily useful if your application accepts other
+ options or arguments and you would like to get the PostgreSQL
+ options out of the <code>argv</code> array.</p>
+
+ <p>This constructor throws the <code>odb::pgsql::cli_exception</code>
+ exception if the PostgreSQL option values are missing or invalid.
+ See section <a href="#19.4">Section 19.4, "PostgreSQL Exceptions"</a>
+ for more information on this exception.</p>
+
+ <p>The static <code>print_usage()</code> function prints the list of options
+ with short descriptions that are recognized by this constructor.</p>
+
+ <p>The last argument to all of the constructors is a pointer to the
+ connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
+ in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
+ non-<code>NULL</code> value, the database instance assumes ownership
+ of the factory instance. The connection factory interface as well as
+ the available implementations are described in the next section.</p>
+
+ <p>The set of accessor functions following the constructors allows us
+ to query the parameters of the <code>database</code> instance. Note that
+ the <code>conninfo()</code> accessor returns a complete
+ <code>conninfo</code> string which includes parameters that were
+ explicitly specified with the various constructor arguments, as well as
+ the extra parameters passed in the <code>extra_conninfo</code> argument.
+ The <code>extra_conninfo()</code> accessor will return the
+ <code>conninfo</code> string as passed in the <code>extra_conninfo</code>
+ argument.</p>
+
+ <p>The <code>connection()</code> function returns a pointer to the
+ PostgreSQL database connection encapsulated by the
+ <code>odb::pgsql::connection</code> class. For more information
+ on <code>pgsql::connection</code>, refer to <a href="#19.3">Section
+ 19.3, "PostgreSQL Connection and Connection Factory"</a>.</p>
+
+ <h2><a name="19.3">19.3 PostgreSQL Connection and Connection Factory</a></h2>
+
+ <p>The <code>pgsql::connection</code> class has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace pgsql
+ {
+ class connection: public odb::connection
+ {
+ public:
+ connection (database&amp;);
+ connection (database&amp;, PGconn*);
+
+ PGconn*
+ handle ();
+ };
+
+ typedef details::shared_ptr&lt;connection> connection_ptr;
+ }
+}
+ </pre>
+
+ <p>For more information on the <code>odb::connection</code> interface,
+ refer to <a href="#3.6">Section 3.6, "Connections"</a>. The first
+ overloaded <code>pgsql::connection</code> constructor establishes a
+ new PostgreSQL connection. The second constructor allows us to create
+ a <code>connection</code> instance by providing an already connected
+ native PostgreSQL handle. Note that the <code>connection</code>
+ instance assumes ownership of this handle. The <code>handle()</code>
+ accessor returns the PostgreSQL handle corresponding to the connection.</p>
+
+ <p>The <code>pgsql::connection_factory</code> abstract class has the
+ following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace pgsql
+ {
+ class connection_factory
+ {
+ public:
+ virtual void
+ database (database&amp;) = 0;
+
+ virtual connection_ptr
+ connect () = 0;
+ };
+ }
+}
+ </pre>
+
+ <p>The <code>database()</code> function is called when a connection
+ factory is associated with a database instance. This happens in
+ the <code>odb::pgsql::database</code> class constructors. The
+ <code>connect()</code> function is called whenever a database
+ connection is requested.</p>
+
+ <p>The two implementations of the <code>connection_factory</code>
+ interface provided by the PostgreSQL ODB runtime are
+ <code>new_connection_factory</code> and
+ <code>connection_pool_factory</code>. You will need to include
+ the <code>&lt;odb/pgsql/connection-factory.hxx></code>
+ header file to make the <code>connection_factory</code> interface
+ and these implementation classes available in your application.</p>
+
+ <p>The <code>new_connection_factory</code> class creates a new
+ connection whenever one is requested. When a connection is no
+ longer needed, it is released and closed. The
+ <code>new_connection_factory</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace pgsql
+ {
+ class new_connection_factory: public connection_factory
+ {
+ public:
+ new_connection_factory ();
+ };
+};
+ </pre>
+
+ <p>The <code>connection_pool_factory</code> class implements a
+ connection pool. It has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace pgsql
+ {
+ class connection_pool_factory: public connection_factory
+ {
+ public:
+ connection_pool_factory (std::size_t max_connections = 0,
+ std::size_t min_connections = 0);
+
+ protected:
+ class pooled_connection: public connection
+ {
+ public:
+ pooled_connection (database_type&amp;);
+ pooled_connection (database_type&amp;, PGconn*);
+ };
+
+ typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
+
+ virtual pooled_connection_ptr
+ create ();
+ };
+};
+ </pre>
+
+ <p>The <code>max_connections</code> argument in the
+ <code>connection_pool_factory</code> constructor specifies the maximum
+ number of concurrent connections that this pool factory will
+ maintain. Similarly, the <code>min_connections</code> argument
+ specifies the minimum number of available connections that
+ should be kept open.</p>
+
+ <p>Whenever a connection is requested, the pool factory first
+ checks if there is an unused connection that can be returned.
+ If there is none, the pool factory checks the
+ <code>max_connections</code> value to see if a new connection
+ can be created. If the total number of connections maintained
+ by the pool is less than this value, then a new connection is
+ created and returned. Otherwise, the caller is blocked until
+ a connection becomes available.</p>
+
+ <p>When a connection is released, the pool factory first checks
+ if there are blocked callers waiting for a connection. If so, then
+ one of them is unblocked and is given the connection. Otherwise,
+ the pool factory checks whether the total number of connections
+ maintained by the pool is greater than the <code>min_connections</code>
+ value. If that's the case, the connection is closed. Otherwise, the
+ connection is added to the pool of available connections to be
+ returned on the next request. In other words, if the number of
+ connections maintained by the pool exceeds <code>min_connections</code>
+ and there are no callers waiting for a new connection,
+ the pool will close the excess connections.</p>
+
+ <p>If the <code>max_connections</code> value is 0, then the pool will
+ create a new connection whenever all of the existing connections
+ are in use. If the <code>min_connections</code> value is 0, then
+ the pool will never close a connection and instead maintain all
+ the connections that were ever created.</p>
+
+ <p>The <code>create()</code> virtual function is called whenever the
+ pool needs to create a new connection. By deriving from the
+ <code>connection_pool_factory</code> class and overriding this
+ function we can implement custom connection establishment
+ and configuration.</p>
+
+ <p>If you pass <code>NULL</code> as the connection factory to one of the
+ <code>database</code> constructors, then the
+ <code>connection_pool_factory</code> instance will be created by default
+ with the min and max connections values set to <code>0</code>. The
+ following code fragment shows how we can pass our own connection factory
+ instance:</p>
+
+ <pre class="cxx">
+#include &lt;odb/database.hxx>
+
+#include &lt;odb/pgsql/database.hxx>
+#include &lt;odb/pgsql/connection-factory.hxx>
+
+int
+main (int argc, char* argv[])
+{
+ auto_ptr&lt;odb::pgsql::connection_factory> f (
+ new odb::pgsql::connection_pool_factory (20));
+
+ auto_ptr&lt;odb::database> db (
+ new pgsql::database (argc, argv, false, "", f));
+}
+ </pre>
+
+ <h2><a name="19.4">19.4 PostgreSQL Exceptions</a></h2>
+
+ <p>The PostgreSQL ODB runtime library defines the following
+ PostgreSQL-specific exceptions:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace pgsql
+ {
+ class database_exception: odb::database_exception
+ {
+ public:
+ const std::string&amp;
+ message () const;
+
+ const std::string&amp;
+ sqlstate () const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class cli_exception: odb::exception
+ {
+ public:
+ virtual const char*
+ what () const throw ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/pgsql/exceptions.hxx></code>
+ header file to make these exceptions available in your application.</p>
+
+ <p>The <code>odb::pgsql::database_exception</code> is thrown if
+ a PostgreSQL database operation fails. The PostgreSQL-specific error
+ information is accessible via the <code>message()</code> and
+ <code>sqlstate()</code> functions. All this information is also
+ combined and returned in a human-readable form by the <code>what()</code>
+ function.</p>
+
+ <p>The <code>odb::pgsql::cli_exception</code> is thrown by the
+ command line parsing constructor of the <code>odb::pgsql::database</code>
+ class if the PostgreSQL option values are missing or invalid. The
+ <code>what()</code> function returns a human-readable description
+ of an error.</p>
+
+ <h2><a name="19.5">19.5 PostgreSQL Limitations</a></h2>
+
+ <p>The following sections describe PostgreSQL-specific limitations imposed
+ by the current PostgreSQL and ODB runtime versions.</p>
+
+ <h3><a name="19.5.1">19.5.1 Query Result Caching</a></h3>
+
+ <p>The PostgreSQL ODB runtime implementation will always return a
+ cached query result (<a href="#4.4">Section 4.4, "Query Result"</a>)
+ even when explicitly requested not to. This is a limitation of the
+ PostgreSQL client library (<code>libpq</code>) which does not
+ support uncached (streaming) query results.</p>
+
+ <h3><a name="19.5.2">19.5.2 Foreign Key Constraints</a></h3>
+
+ <p>ODB assumes the standard SQL behavior which requires that
+ foreign key constraints checking is deferred until the
+ transaction is committed. Default PostgreSQL behavior is
+ to check such constraints immediately. As a result, when
+ used with ODB, a custom database schema that defines foreign
+ key constraints may need to declare such constraints as
+ <code>INITIALLY DEFERRED</code>, as shown in the following example.
+ By default, schemas generated by the ODB compiler meet this requirement
+ automatically.</p>
+
+ <pre class="sql">
+CREATE TABLE Employee (
+ ...
+ employer BIGINT REFERENCES Employer(id) INITIALLY DEFERRED);
+ </pre>
+
+ <p>You can override the default behavior and instruct the ODB
+ compiler to generate non-deferrable foreign keys by specifying
+ the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
+ compiler option. Note, however, that in this case the order in
+ which you persist, update, and erase objects within a transaction
+ becomes important.</p>
+
+ <h3><a name="19.5.3">19.5.3 Unique Constraint Violations</a></h3>
+
+ <p>Due to the granularity of the PostgreSQL error codes, it is impossible
+ to distinguish between the duplicate primary key and other unique
+ constraint violations. As a result, when making an object persistent,
+ the PostgreSQL ODB runtime will translate all unique constraint violation
+ errors to the <code>object_already_persistent</code> exception
+ (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>).</p>
+
+ <h3><a name="19.5.4">19.5.4 Date-Time Format</a></h3>
+
+ <p>ODB expects the PostgreSQL server to use integers as a binary
+ format for the date-time types, which is the default for most
+ PostgreSQL configurations. When creating a connection, ODB
+ examines the <code>integer_datetimes</code> PostgreSQL server
+ parameter and if it is <code>false</code>,
+ <code>odb::pgsql::database_exception</code> is thrown. You may
+ check the value of this parameter for your server by executing
+ the following SQL query:</p>
+
+ <pre class="sql">
+SHOW integer_datetimes
+ </pre>
+
+ <h3><a name="19.5.5">19.5.5 Timezones</a></h3>
+
+ <p>ODB does not currently natively support the PostgreSQL date-time types
+ with timezone information. However, these types can be accessed by
+ mapping them to one of the natively supported types, as discussed
+ in <a href="#14.8">Section 14.8, "Database Type Mapping Pragmas"</a>.</p>
+
+ <h3><a name="19.5.6">19.5.6 <code>NUMERIC</code> Type Support</a></h3>
+
+ <p>Support for the PostgreSQL <code>NUMERIC</code> type is limited
+ to providing a binary buffer containing the binary representation
+ of the value. For more information on the binary format used to
+ store <code>NUMERIC</code> values refer to the PostgreSQL
+ documentation. An alternative approach to accessing <code>NUMERIC</code>
+ values is to map this type to one of the natively supported
+ ones, as discussed in <a href="#14.8">Section 14.8, "Database
+ Type Mapping Pragmas"</a>.</p>
+
+ <h3><a name="19.5.7">19.5.7 Bulk Operations Support</a></h3>
+
+ <p>Support for bulk operations (<a href="#15.3">Section 15.3, "Bulk
+ Database Operations"</a>) requires PostgreSQL client library
+ (<code>libpq</code>) version 14 or later and PostgreSQL server
+ version 7.4 or later.</p>
+
+
+ <h2><a name="19.6">19.6 PostgreSQL Index Definitions</a></h2>
+
+ <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
+ "Index Definition Pragmas"</a>) is used to define a PostgreSQL index,
+ the <code>type</code> clause specifies the index type (for example,
+ <code>UNIQUE</code>), the <code>method</code> clause specifies the
+ index method (for example, <code>BTREE</code>, <code>HASH</code>,
+ <code>GIN</code>, etc.), and the <code>options</code> clause
+ specifies additional index options, such as storage parameters,
+ table spaces, and the <code>WHERE</code> predicate. To support
+ the definition of concurrent indexes, the <code>type</code>
+ clause can end with the word <code>CONCURRENTLY</code> (upper and
+ lower cases are recognized). The column options can be used to
+ specify collations, operator classes, and the sort order. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ std::string name_;
+
+ #pragma db index \
+ type("UNIQUE CONCURRENTLY") \
+ method("HASH") \
+ member(name_, "DESC") \
+ options("WITH(FILLFACTOR = 80)")
+};
+ </pre>
+
+ <p>Index names in PostgreSQL are schema-global. To avoid name clashes,
+ ODB automatically prefixes each index name with the table name on
+ which it is defined.</p>
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="20">20 Oracle Database</a></h1>
+
+ <p>To generate support code for the Oracle database you will need
+ to pass the "<code>--database&nbsp;oracle</code>"
+ (or "<code>-d&nbsp;oracle</code>") option to the ODB compiler.
+ Your application will also need to link to the Oracle ODB runtime
+ library (<code>libodb-oracle</code>). All Oracle-specific ODB
+ classes are defined in the <code>odb::oracle</code> namespace.</p>
+
+ <h2><a name="20.1">20.1 Oracle Type Mapping</a></h2>
+
+ <p>The following table summarizes the default mapping between basic
+ C++ value types and Oracle database types. This mapping can be
+ customized on the per-type and per-member basis using the ODB
+ Pragma Language (<a href="#14">Chapter 14, "ODB Pragma
+ Language"</a>).</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>C++ Type</th>
+ <th>Oracle Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>bool</code></td>
+ <td><code>NUMBER(1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char</code></td>
+ <td><code>CHAR(1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>signed char</code></td>
+ <td><code>NUMBER(3)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned char</code></td>
+ <td><code>NUMBER(3)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>short</code></td>
+ <td><code>NUMBER(5)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned short</code></td>
+ <td><code>NUMBER(5)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>int</code></td>
+ <td><code>NUMBER(10)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned int</code></td>
+ <td><code>NUMBER(10)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long</code></td>
+ <td><code>NUMBER(19)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long</code></td>
+ <td><code>NUMBER(20)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long long</code></td>
+ <td><code>NUMBER(19)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long long</code></td>
+ <td><code>NUMBER(20)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>float</code></td>
+ <td><code>BINARY_FLOAT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>double</code></td>
+ <td><code>BINARY_DOUBLE</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>std::string</code></td>
+ <td><code>VARCHAR2(512)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char[N]</code></td>
+ <td><code>VARCHAR2(N-1)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>It is possible to map the <code>char</code> C++ type to an integer
+ database type (for example, <code>NUMBER(3)</code>) using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>).</p>
+
+ <p>In Oracle empty <code>VARCHAR2</code> and <code>NVARCHAR2</code>
+ strings are represented as a <code>NULL</code> value. As a result,
+ columns of the <code>std::string</code> and <code>char[N]</code>
+ types are by default declared as <code>NULL</code> except for
+ primary key columns. However, you can override this by explicitly
+ declaring such columns as <code>NOT NULL</code> with the
+ <code>db&nbsp;not_null</code> pragma (<a href="#14.4.6">Section
+ 14.4.6, "<code>null/not_null</code>"</a>). This also means that for
+ object ids that are mapped to these Oracle types, an empty string is
+ an invalid value.</p>
+
+ <p>Additionally, by default, C++ enums and C++11 enum classes are
+ automatically mapped to the Oracle types corresponding to their
+ underlying integral types (see table above). The default
+ <code>NULL</code> semantics is <code>NOT NULL</code>. For
+ example:</p>
+
+ <pre class="cxx">
+enum color {red, green, blue};
+enum class taste: unsigned char
+{
+ bitter = 1,
+ sweet,
+ sour = 4,
+ salty
+};
+
+#pragma db object
+class object
+{
+ ...
+
+ color color_; // Automatically mapped to NUMBER(10).
+ taste taste_; // Automatically mapped to NUMBER(3).
+};
+ </pre>
+
+ <p>It is also possible to add support for additional Oracle types,
+ such as <code>XML</code>, geospatial types, user-defined types,
+ and collections (arrays, table types). For more information, refer to
+ <a href="#14.8">Section 14.8, "Database Type Mapping
+ Pragmas"</a>.</p>
+
+ <h3><a name="20.1.1">20.1.1 String Type Mapping</a></h3>
+
+ <p>The Oracle ODB runtime library provides support for mapping the
+ <code>std::string</code>, <code>char[N]</code>, and
+ <code>std::array&lt;char, N></code> types to the Oracle <code>CHAR</code>,
+ <code>VARCHAR2</code>, <code>CLOB</code>, <code>NCHAR</code>,
+ <code>NVARCHAR2</code>, and <code>NCLOB</code> types. However,
+ these mappings are not enabled by default (in particular, by
+ default, <code>std::array</code> will be treated as a container).
+ To enable the alternative mappings for these types we need to
+ specify the database type explicitly using the <code>db&nbsp;type</code>
+ pragma (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>),
+ for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type ("CHAR(2)")
+ char state_[2];
+
+ #pragma db type ("VARCHAR(128)") null
+ std::string name_;
+
+ #pragma db type ("CLOB")
+ std::string text_;
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+#pragma db value(std::string) type("VARCHAR(128)") null
+
+#pragma db object
+class object
+{
+ ...
+
+ std::string name_; // Mapped to VARCHAR(128).
+
+ #pragma db type ("CLOB")
+ std::string text_; // Mapped to CLOB.
+};
+ </pre>
+
+ <p>The <code>char[N]</code> and <code>std::array&lt;char, N></code> values
+ may or may not be zero-terminated. When extracting such values from the
+ database, ODB will append the zero terminator if there is enough
+ space.</p>
+
+ <h3><a name="20.1.2">20.1.2 Binary Type Mapping</a></h3>
+
+ <p>The Oracle ODB runtime library provides support for mapping the
+ <code>std::vector&lt;char></code>,
+ <code>std::vector&lt;unsigned&nbsp;char></code>,
+ <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
+ <code>std::array&lt;char, N></code>, and
+ <code>std::array&lt;unsigned char, N></code>
+ types to the Oracle <code>BLOB</code> and <code>RAW</code> types.
+ However, these mappings are not enabled by default (in particular, by
+ default, <code>std::vector</code> and <code>std::array</code> will be
+ treated as containers). To enable the alternative mappings for these
+ types we need to specify the database type explicitly using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>), for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type("BLOB")
+ std::vector&lt;char> buf_;
+
+ #pragma db type("RAW(16)")
+ unsigned char uuid_[16];
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;char> buffer;
+#pragma db value(buffer) type("BLOB")
+
+#pragma db object
+class object
+{
+ ...
+
+ buffer buf_; // Mapped to BLOB.
+};
+ </pre>
+
+ <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
+ the Database"</a>) <code>char[N]</code> and
+ <code>std::array&lt;char, N></code> parameters are by default passed
+ as a string rather than a binary. To pass such parameters as a binary,
+ we need to specify the database type explicitly in the
+ <code>_val()</code>/<code>_ref()</code> calls. Note also that we
+ don't need to do this for the integrated queries, for example:</p>
+
+ <pre class="cxx">
+char u[16] = {...};
+
+db.query&lt;object> ("uuid = " + query::_val&lt;odb::oracle::id_raw> (u));
+db.query&lt;object> (query::uuid == query::_ref (u));
+ </pre>
+
+ <h2><a name="20.2">20.2 Oracle Database Class</a></h2>
+
+ <p>The Oracle <code>database</code> class encapsulates the OCI environment
+ handle as well as the database connection string and user credentials
+ that are used to establish connections to the database. It has the
+ following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace oracle
+ {
+ class database: public odb::database
+ {
+ public:
+ database (const std::string&amp; user,
+ const std::string&amp; password,
+ const std::string&amp; db,
+ ub2 charset = 0,
+ ub2 ncharset = 0,
+ OCIEnv* environment = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; user,
+ const std::string&amp; password,
+ const std::string&amp; service,
+ const std::string&amp; host,
+ unsigned int port = 0,
+ ub2 charset = 0,
+ ub2 ncharset = 0,
+ OCIEnv* environment = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (int&amp; argc,
+ char* argv[],
+ bool erase = false,
+ ub2 charset = 0,
+ ub2 ncharset = 0,
+ OCIEnv* environment = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ static void
+ print_usage (std::ostream&amp;);
+
+ public:
+ const std::string&amp;
+ user () const;
+
+ const std::string&amp;
+ password () const;
+
+ const std::string&amp;
+ db () const;
+
+ const std::string&amp;
+ service () const;
+
+ const std::string&amp;
+ host () const;
+
+ unsigned int
+ port () const;
+
+ ub2
+ charset () const;
+
+ ub2
+ ncharset () const;
+
+ OCIEnv*
+ environment ();
+
+ public:
+ connection_ptr
+ connection ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/oracle/database.hxx></code>
+ header file to make this class available in your application.</p>
+
+ <p>The overloaded <code>database</code> constructors allow us to specify the
+ Oracle database parameters that should be used when connecting to the
+ database. The <code>db</code> argument in the first constructor is a
+ connection identifier that specifies the database to connect to. For more
+ information on the format of the connection identifier, refer to the
+ Oracle documentation.</p>
+
+ <p>The second constructor allows us to specify the individual components
+ of a connection identifier as the <code>service</code>, <code>host</code>,
+ and <code>port</code> arguments. If the <code>host</code> argument is
+ empty, then localhost is used by default. Similarly, if the
+ <code>port</code> argument is zero, then the default port is used.</p>
+
+ <p>The last constructor extracts the database parameters
+ from the command line. The following options are recognized:</p>
+
+ <pre class="terminal">
+ --user &lt;login>
+ --password &lt;password>
+ --database &lt;connect-id>
+ --service &lt;name>
+ --host &lt;host>
+ --port &lt;integer>
+ --options-file &lt;file>
+ </pre>
+
+ <p>The <code>--options-file</code> option allows us to specify some
+ or all of the database options in a file with each option appearing
+ on a separate line followed by a space and an option value. Note that it
+ is invalid to specify the <code>--database</code> option
+ together with <code>--service</code>, <code>--host</code>, or
+ <code>--port</code> options.</p>
+
+ <p>If the <code>erase</code> argument to this constructor is true,
+ then the above options are removed from the <code>argv</code>
+ array and the <code>argc</code> count is updated accordingly.
+ This is primarily useful if your application accepts other
+ options or arguments and you would like to get the Oracle
+ options out of the <code>argv</code> array.</p>
+
+ <p>This constructor throws the <code>odb::oracle::cli_exception</code>
+ exception if the Oracle option values are missing or invalid. See section
+ <a href="#20.4">Section 20.4, "Oracle Exceptions"</a> for more
+ information on this exception.</p>
+
+ <p>The static <code>print_usage()</code> function prints the list of options
+ with short descriptions that are recognized by this constructor.</p>
+
+ <p>Additionally, all the constructors have the <code>charset</code>,
+ <code>ncharset</code>, and <code>environment</code> arguments.
+ The <code>charset</code> argument specifies the client-side database
+ character encoding. Character data corresponding to the <code>CHAR</code>,
+ <code>VARCHAR2</code>, and <code>CLOB</code> types will be delivered
+ to and received from the application in this encoding. Similarly,
+ the <code>ncharset</code> argument specifies the client-side national
+ character encoding. Character data corresponding to the <code>NCHAR</code>,
+ <code>NVARCHAR2</code>, and <code>NCLOB</code> types will be delivered
+ to and received from the application in this encoding. For the complete
+ list of available character encoding values, refer to the Oracle
+ documentation. Commonly used encoding values are <code>873</code>
+ (UTF-8), <code>31</code> (ISO-8859-1), and <code>1000</code> (UTF-16).
+ If the database character encoding is not specified, then the
+ <code>NLS_LANG</code> environment/registry variable is used. Similarly,
+ if the national character encoding is not specified, then the
+ <code>NLS_NCHAR</code> environment/registry variable is used. For more
+ information on character encodings, refer to the
+ <code>OCIEnvNlsCreate()</code> function in the Oracle Call Interface
+ (OCI) documentation.</p>
+
+ <p>The <code>environment</code> argument allows us to provide a custom
+ OCI environment handle. If this argument is not <code>NULL</code>,
+ then the passed handle is used in all the OCI function calls made
+ by this <code>database</code> class instance. Note also that the
+ <code>database</code> instance does not assume ownership of the
+ passed environment handle and this handle should be valid for
+ the lifetime of the <code>database</code> instance. If a custom
+ environment handle is used, then the <code>charset</code> and
+ <code>ncharset</code> arguments have no effect.</p>
+
+ <p>The last argument to all of the constructors is a pointer to the
+ connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
+ in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
+ non-<code>NULL</code> value, the database instance assumes ownership
+ of the factory instance. The connection factory interface as well as
+ the available implementations are described in the next section.</p>
+
+ <p>The set of accessor functions following the constructors allows us
+ to query the parameters of the <code>database</code> instance.</p>
+
+ <p>The <code>connection()</code> function returns a pointer to the
+ Oracle database connection encapsulated by the
+ <code>odb::oracle::connection</code> class. For more information
+ on <code>oracle::connection</code>, refer to <a href="#20.3">Section
+ 20.3, "Oracle Connection and Connection Factory"</a>.</p>
+
+ <h2><a name="20.3">20.3 Oracle Connection and Connection Factory</a></h2>
+
+ <p>The <code>oracle::connection</code> class has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace oracle
+ {
+ class connection: public odb::connection
+ {
+ public:
+ connection (database&amp;);
+ connection (database&amp;, OCISvcCtx*);
+
+ OCISvcCtx*
+ handle ();
+
+ OCIError*
+ error_handle ();
+
+ details::buffer&amp;
+ lob_buffer ();
+ };
+
+ typedef details::shared_ptr&lt;connection> connection_ptr;
+ }
+}
+ </pre>
+
+ <p>For more information on the <code>odb::connection</code> interface, refer
+ to <a href="#3.6">Section 3.6, "Connections"</a>. The first overloaded
+ <code>oracle::connection</code> constructor creates a new OCI service
+ context. The OCI statement caching is enabled for the underlying session
+ while the OCI connection pooling and session pooling are not used. The
+ second constructor allows us to create a <code>connection</code> instance by
+ providing an already connected Oracle service context. Note that the
+ <code>connection</code> instance assumes ownership of this handle. The
+ <code>handle()</code> accessor returns the OCI service context handle
+ associated with the <code>connection</code> instance.</p>
+
+ <p>An OCI error handle is allocated for each <code>connection</code>
+ instance and is available via the <code>error_handle()</code> accessor
+ function.</p>
+
+ <p>Additionally, each <code>connection</code> instance maintains a large
+ object (LOB) buffer. This buffer is used by the Oracle ODB runtime
+ as an intermediate storage for piecewise handling of LOB data.
+ By default, the LOB buffer has zero initial capacity and is
+ expanded to 4096 bytes when the first LOB operation is performed.
+ If your application requires a bigger or smaller LOB buffer, you can
+ specify a custom capacity using the <code>lob_buffer()</code>
+ accessor.</p>
+
+ <p>The <code>oracle::connection_factory</code> abstract class has the
+ following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace oracle
+ {
+ class connection_factory
+ {
+ public:
+ virtual void
+ database (database&amp;) = 0;
+
+ virtual connection_ptr
+ connect () = 0;
+ };
+ }
+}
+ </pre>
+
+ <p>The <code>database()</code> function is called when a connection
+ factory is associated with a database instance. This happens in
+ the <code>odb::oracle::database</code> class constructors. The
+ <code>connect()</code> function is called whenever a database
+ connection is requested.</p>
+
+ <p>The two implementations of the <code>connection_factory</code>
+ interface provided by the Oracle ODB runtime are
+ <code>new_connection_factory</code> and
+ <code>connection_pool_factory</code>. You will need to include
+ the <code>&lt;odb/oracle/connection-factory.hxx></code>
+ header file to make the <code>connection_factory</code> interface
+ and these implementation classes available in your application.</p>
+
+ <p>The <code>new_connection_factory</code> class creates a new
+ connection whenever one is requested. When a connection is no
+ longer needed, it is released and closed. The
+ <code>new_connection_factory</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace oracle
+ {
+ class new_connection_factory: public connection_factory
+ {
+ public:
+ new_connection_factory ();
+ };
+};
+ </pre>
+
+ <p>The <code>connection_pool_factory</code> class implements a
+ connection pool. It has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace oracle
+ {
+ class connection_pool_factory: public connection_factory
+ {
+ public:
+ connection_pool_factory (std::size_t max_connections = 0,
+ std::size_t min_connections = 0);
+
+ protected:
+ class pooled_connection: public connection
+ {
+ public:
+ pooled_connection (database_type&amp;);
+ pooled_connection (database_type&amp;, OCISvcCtx*);
+ };
+
+ typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
+
+ virtual pooled_connection_ptr
+ create ();
+ };
+};
+ </pre>
+
+ <p>The <code>max_connections</code> argument in the
+ <code>connection_pool_factory</code> constructor specifies the maximum
+ number of concurrent connections that this pool factory will
+ maintain. Similarly, the <code>min_connections</code> argument
+ specifies the minimum number of available connections that
+ should be kept open.</p>
+
+ <p>Whenever a connection is requested, the pool factory first
+ checks if there is an unused connection that can be returned.
+ If there is none, the pool factory checks the
+ <code>max_connections</code> value to see if a new connection
+ can be created. If the total number of connections maintained
+ by the pool is less than this value, then a new connection is
+ created and returned. Otherwise, the caller is blocked until
+ a connection becomes available.</p>
+
+ <p>When a connection is released, the pool factory first checks
+ if there are blocked callers waiting for a connection. If so, then
+ one of them is unblocked and is given the connection. Otherwise,
+ the pool factory checks whether the total number of connections
+ maintained by the pool is greater than the <code>min_connections</code>
+ value. If that's the case, the connection is closed. Otherwise, the
+ connection is added to the pool of available connections to be
+ returned on the next request. In other words, if the number of
+ connections maintained by the pool exceeds <code>min_connections</code>
+ and there are no callers waiting for a new connection,
+ the pool will close the excess connections.</p>
+
+ <p>If the <code>max_connections</code> value is 0, then the pool will
+ create a new connection whenever all of the existing connections
+ are in use. If the <code>min_connections</code> value is 0, then
+ the pool will never close a connection and instead maintain all
+ the connections that were ever created.</p>
+
+ <p>The <code>create()</code> virtual function is called whenever the
+ pool needs to create a new connection. By deriving from the
+ <code>connection_pool_factory</code> class and overriding this
+ function we can implement custom connection establishment
+ and configuration.</p>
+
+ <p>If you pass <code>NULL</code> as the connection factory to one of the
+ <code>database</code> constructors, then the
+ <code>connection_pool_factory</code> instance will be created by default
+ with the min and max connections values set to <code>0</code>. The
+ following code fragment shows how we can pass our own connection factory
+ instance:</p>
+
+ <pre class="cxx">
+#include &lt;odb/database.hxx>
+
+#include &lt;odb/oracle/database.hxx>
+#include &lt;odb/oracle/connection-factory.hxx>
+
+int
+main (int argc, char* argv[])
+{
+ auto_ptr&lt;odb::oracle::connection_factory> f (
+ new odb::oracle::connection_pool_factory (20));
+
+ auto_ptr&lt;odb::database> db (
+ new oracle::database (argc, argv, false, 0, 0, 0, f));
+}
+ </pre>
+
+ <h2><a name="20.4">20.4 Oracle Exceptions</a></h2>
+
+ <p>The Oracle ODB runtime library defines the following
+ Oracle-specific exceptions:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace oracle
+ {
+ class database_exception: odb::database_exception
+ {
+ public:
+ class record
+ {
+ public:
+ sb4
+ error () const;
+
+ const std::string&amp;
+ message () const;
+ };
+
+ typedef std::vector&lt;record> records;
+
+ typedef records::size_type size_type;
+ typedef records::const_iterator iterator;
+
+ iterator
+ begin () const;
+
+ iterator
+ end () const;
+
+ size_type
+ size () const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class cli_exception: odb::exception
+ {
+ public:
+ virtual const char*
+ what () const throw ();
+ };
+
+ class invalid_oci_handle: odb::exception
+ {
+ public:
+ virtual const char*
+ what () const throw ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/oracle/exceptions.hxx></code>
+ header file to make these exceptions available in your application.</p>
+
+ <p>The <code>odb::oracle::database_exception</code> is thrown if
+ an Oracle database operation fails. The Oracle-specific error
+ information is stored as a series of records, each containing
+ the error code as a signed 4-byte integer and the message string.
+ All this information is also combined and returned in a
+ human-readable form by the <code>what()</code> function.</p>
+
+ <p>The <code>odb::oracle::cli_exception</code> is thrown by the
+ command line parsing constructor of the <code>odb::oracle::database</code>
+ class if the Oracle option values are missing or invalid. The
+ <code>what()</code> function returns a human-readable description
+ of an error.</p>
+
+ <p>The <code>odb::oracle::invalid_oci_handle</code> is thrown if an
+ invalid handle is passed to an OCI function or if an OCI function
+ was unable to allocate a handle. The former normally indicates
+ a programming error while the latter indicates an out of memory
+ condition. The <code>what()</code> function returns a human-readable
+ description of an error.</p>
+
+ <h2><a name="20.5">20.5 Oracle Limitations</a></h2>
+
+ <p>The following sections describe Oracle-specific limitations imposed
+ by the current Oracle and ODB runtime versions.</p>
+
+ <h3><a name="20.5.1">20.5.1 Identifier Truncation</a></h3>
+
+ <p>Oracle limits the length of database identifiers (table, column, etc.,
+ names) to 30 characters. The ODB compiler automatically truncates
+ any identifier that is longer than 30 characters. This, however,
+ can lead to duplicate names. A common symptom of this problem
+ are errors during the database schema creation indicating
+ that a database object with the same name already exists. To
+ resolve this problem we can assign custom, shorter identifiers
+ using the <code>db&nbsp;table</code> and <code>db&nbsp;column</code>
+ pragmas (<a href="#14">Chapter 14, "ODB Pragma Language")</a>. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class long_class_name
+{
+ ...
+
+ std::vector&lt;int> long_container_x_;
+ std::vector&lt;int> long_container_y_;
+};
+ </pre>
+
+ <p>In the above example, the names of the two container tables will be
+ <code>long_class_name_long_container_x_</code> and
+ <code>long_class_name_long_container_y_</code>. However, when
+ truncated to 30 characters, they both become
+ <code>long_class_name_long_container</code>. To resolve this
+ collision we can assign a custom table name for each container:</p>
+
+ <pre class="cxx">
+#pragma db object
+class long_class_name
+{
+ ...
+
+ #pragma db table("long_class_name_cont_x")
+ std::vector&lt;int> long_container_x_;
+
+ #pragma db table("long_class_name_cont_y")
+ std::vector&lt;int> long_container_y_;
+};
+ </pre>
+
+ <h3><a name="20.5.2">20.5.2 Query Result Caching</a></h3>
+
+ <p>Oracle ODB runtime implementation does not perform query result caching
+ (<a href="#4.4">Section 4.4, "Query Result"</a>) even when explicitly
+ requested. The OCI API supports interleaving execution of multiple
+ prepared statements on a single connection. As a result, with OCI,
+ it is possible to have multiple uncached results and calls to other
+ database functions do not invalidate them. The only limitation of
+ the uncached Oracle results is the unavailability of the
+ <code>result::size()</code> function. If you call this function on
+ an Oracle query result, then the <code>odb::result_not_cached</code>
+ exception (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>) is
+ always thrown. Future versions of the Oracle ODB runtime library
+ may add support for result caching.</p>
+
+ <h3><a name="20.5.3">20.5.3 Foreign Key Constraints</a></h3>
+
+ <p>ODB assumes the standard SQL behavior which requires that
+ foreign key constraints checking is deferred until the
+ transaction is committed. Default Oracle behavior is
+ to check such constraints immediately. As a result, when
+ used with ODB, a custom database schema that defines foreign
+ key constraints may need to declare such constraints as
+ <code>INITIALLY DEFERRED</code>, as shown in the following example.
+ By default, schemas generated by the ODB compiler meet this requirement
+ automatically.</p>
+
+ <pre class="sql">
+CREATE TABLE Employee (
+ ...
+ employer NUMBER(20) REFERENCES Employer(id)
+ DEFERRABLE INITIALLY DEFERRED);
+ </pre>
+
+ <p>You can override the default behavior and instruct the ODB
+ compiler to generate non-deferrable foreign keys by specifying
+ the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
+ compiler option. Note, however, that in this case the order in
+ which you persist, update, and erase objects within a transaction
+ becomes important.</p>
+
+ <h3><a name="20.5.4">20.5.4 Unique Constraint Violations</a></h3>
+
+ <p>Due to the granularity of the Oracle error codes, it is impossible
+ to distinguish between the duplicate primary key and other unique
+ constraint violations. As a result, when making an object persistent,
+ the Oracle ODB runtime will translate all unique constraint violation
+ errors to the <code>object_already_persistent</code> exception
+ (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>).</p>
+
+ <h3><a name="20.5.5">20.5.5 Large <code>FLOAT</code> and
+ <code>NUMBER</code> Types</a></h3>
+
+ <p>The Oracle <code>FLOAT</code> type with a binary precision greater
+ than 53 and fixed-point <code>NUMBER</code> type with a decimal
+ precision greater than 15 cannot be automatically extracted
+ into the C++ <code>float</code> and <code>double</code> types.
+ Instead, the Oracle ODB runtime uses a 21-byte buffer containing
+ the binary representation of a value as an image type for such
+ <code>FLOAT</code> and <code>NUMBER</code> types. In order to
+ convert them into an application-specific large number representation,
+ you will need to provide a suitable <code>value_traits</code>
+ template specialization. For more information on the binary format
+ used to store the <code>FLOAT</code> and <code>NUMBER</code> values,
+ refer to the Oracle Call Interface (OCI) documentation.</p>
+
+ <p>An alternative approach to accessing large <code>FLOAT</code> and
+ <code>NUMBER</code> values is to map these type to one of the
+ natively supported ones, as discussed in <a href="#14.8">Section
+ 14.8, "Database Type Mapping Pragmas"</a>.</p>
+
+ <p>Note that a <code>NUMBER</code> type that is used to represent a
+ floating point number (declared by specifying <code>NUMBER</code>
+ without any range and scale) can be extracted into the C++
+ <code>float</code> and <code>double</code> types.</p>
+
+ <h3><a name="20.5.6">20.5.6 Timezones</a></h3>
+
+ <p>ODB does not currently support the Oracle date-time types with timezone
+ information. However, these types can be accessed by mapping them to
+ one of the natively supported types, as discussed in
+ <a href="#14.8">Section 14.8, "Database Type Mapping Pragmas"</a>.</p>
+
+ <h3><a name="20.5.7">20.5.7 <code>LONG</code> Types</a></h3>
+
+ <p>ODB does not support the deprecated Oracle <code>LONG</code> and
+ <code>LONG RAW</code> data types. However, these types can be accessed
+ by mapping them to one of the natively supported types, as discussed
+ in <a href="#14.8">Section 14.8, "Database Type Mapping Pragmas"</a>.</p>
+
+ <h3><a name="20.5.8">20.5.8 LOB Types and By-Value Accessors/Modifiers</a></h3>
+
+ <p>As discussed in <a href="#14.4.5">Section 14.4.5,
+ "<code>get</code>/<code>set</code>/<code>access</code>"</a>, by-value
+ accessor and modifier expressions cannot be used with data members
+ of Oracle large object (LOB) data types: <code>BLOB</code>,
+ <code>CLOB</code>, and <code>NCLOB</code>. The Oracle ODB runtime
+ uses streaming for reading/writing LOB data directly from/to
+ data members. As a result, by-reference accessors and modifiers
+ should be used for these data types.</p>
+
+ <h3><a name="20.5.9">20.5.9 Database Schema Evolution</a></h3>
+
+ <p>In Oracle, the type of the <code>name</code> column in the
+ <code>schema_version</code> table is <code>VARCHAR2(512)</code>.
+ Because this column is a primary key and <code>VARCHAR2</code>
+ represents empty strings as <code>NULL</code> values, it is
+ impossible to store an empty string in this column, which
+ is what is used to represent the default schema name. As a
+ result, in Oracle, the empty schema name is stored as a
+ string containing a single space character. ODB performs
+ all the necessary translations automatically and normally
+ you do not need to worry about this implementation detail
+ unless you are querying or modifying the <code>schema_version</code>
+ table directly.</p>
+
+ <h2><a name="20.6">20.6 Oracle Index Definitions</a></h2>
+
+ <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
+ "Index Definition Pragmas"</a>) is used to define an Oracle index,
+ the <code>type</code> clause specifies the index type (for example,
+ <code>UNIQUE</code>, <code>BITMAP</code>), the <code>method</code>
+ clause is not used, and the <code>options</code> clause specifies
+ additional index properties, such as partitioning, table spaces, etc.
+ The column options can be used to specify the sort order. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ std::string name_;
+
+ #pragma db index \
+ type("BITMAP") \
+ member(name_, "DESC") \
+ options("TABLESPACE TBS1")
+};
+ </pre>
+
+ <p>Index names in Oracle are schema-global. To avoid name clashes,
+ ODB automatically prefixes each index name with the table name on
+ which it is defined.</p>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="21">21 Microsoft SQL Server Database</a></h1>
+
+ <p>To generate support code for the SQL Server database you will need
+ to pass the "<code>--database&nbsp;mssql</code>"
+ (or "<code>-d&nbsp;mssql</code>") option to the ODB compiler.
+ Your application will also need to link to the SQL Server ODB runtime
+ library (<code>libodb-mssql</code>). All SQL Server-specific ODB
+ classes are defined in the <code>odb::mssql</code> namespace.</p>
+
+ <h2><a name="21.1">21.1 SQL Server Type Mapping</a></h2>
+
+ <p>The following table summarizes the default mapping between basic
+ C++ value types and SQL Server database types. This mapping can be
+ customized on the per-type and per-member basis using the ODB
+ Pragma Language (<a href="#14">Chapter 14, "ODB Pragma Language"</a>).</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>C++ Type</th>
+ <th>SQL Server Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>bool</code></td>
+ <td><code>BIT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char</code></td>
+ <td><code>CHAR(1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>signed char</code></td>
+ <td><code>TINYINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned char</code></td>
+ <td><code>TINYINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>short</code></td>
+ <td><code>SMALLINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned short</code></td>
+ <td><code>SMALLINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>int</code></td>
+ <td><code>INT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned int</code></td>
+ <td><code>INT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>long long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>unsigned long long</code></td>
+ <td><code>BIGINT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>float</code></td>
+ <td><code>REAL</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>double</code></td>
+ <td><code>FLOAT</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>std::string</code></td>
+ <td><code>VARCHAR(512)/VARCHAR(256)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>char[N]</code></td>
+ <td><code>VARCHAR(N-1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>std::wstring</code></td>
+ <td><code>NVARCHAR(512)/NVARCHAR(256)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>wchar_t[N]</code></td>
+ <td><code>NVARCHAR(N-1)</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>GUID</code></td>
+ <td><code>UNIQUEIDENTIFIER</code></td>
+ <td><code>NOT NULL</code></td>
+ </tr>
+
+ </table>
+
+ <p>It is possible to map the <code>char</code> C++ type to an integer
+ database type (for example, <code>TINYINT</code>) using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>).</p>
+
+ <p>Note that the <code>std::string</code> and <code>std::wstring</code>
+ types are mapped differently depending on whether a member of one of
+ these types is an object id or not. If the member is an object id,
+ then for this member <code>std::string</code> is mapped
+ to <code>VARCHAR(256)</code> and <code>std::wstring</code> &mdash;
+ to <code>NVARCHAR(256)</code>. Otherwise, <code>std::string</code>
+ is mapped to <code>VARCHAR(512)</code> and <code>std::wstring</code>
+ &mdash; to <code>NVARCHAR(512)</code>. Note also that you can
+ always change this mapping using the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>).</p>
+
+ <p>Additionally, by default, C++ enums and C++11 enum classes are
+ automatically mapped to the SQL Server types corresponding to their
+ underlying integral types (see table above). The default
+ <code>NULL</code> semantics is <code>NOT NULL</code>. For
+ example:</p>
+
+ <pre class="cxx">
+enum color {red, green, blue};
+enum class taste: unsigned char
+{
+ bitter = 1,
+ sweet,
+ sour = 4,
+ salty
+};
+
+#pragma db object
+class object
+{
+ ...
+
+ color color_; // Automatically mapped to INT.
+ taste taste_; // Automatically mapped to TINYINT.
+};
+ </pre>
+
+ <p>Note also that because SQL Server does not support unsigned integers,
+ the <code>unsigned&nbsp;short</code>, <code>unsigned&nbsp;int</code>, and
+ <code>unsigned&nbsp;long</code>/<code>unsigned&nbsp;long&nbsp;long</code> C++ types
+ are by default mapped to the <code>SMALLINT</code>, <code>INT</code>,
+ and <code>BIGINT</code> SQL Server types, respectively. The sign bit
+ of the value stored by the database for these types will contain
+ the most significant bit of the actual unsigned value being
+ persisted. Similarly, because there is no signed version of the
+ <code>TINYINT</code> SQL Server type, by default, the
+ <code>signed char</code> C++ type is mapped to <code>TINYINT</code>.
+ As a result, the most significant bit of the value stored by the
+ database for this type will contain the sign bit of the actual
+ signed value being persisted.</p>
+
+ <p>It is also possible to add support for additional SQL Server types,
+ such as geospatial types, <code>XML</code>, and user-defined types.
+ For more information, refer to <a href="#14.8">Section 14.8, "Database
+ Type Mapping Pragmas"</a>.</p>
+
+ <h3><a name="21.1.1">21.1.1 String Type Mapping</a></h3>
+
+ <p>The SQL Server ODB runtime library provides support for mapping the
+ <code>std::string</code>, <code>char[N]</code>, and
+ <code>std::array&lt;char, N></code> types to the SQL Server
+ <code>CHAR</code>, <code>VARCHAR</code>, and <code>TEXT</code>
+ types as well as the <code>std::wstring</code>, <code>wchar_t[N]</code>,
+ and <code>std::array&lt;wchar_t, N></code> types to <code>NCHAR</code>,
+ <code>NVARCHAR</code>, and <code>NTEXT</code>. However, these mappings
+ are not enabled by default (in particular, by default,
+ <code>std::array</code> will be treated as a container). To enable the
+ alternative mappings for these types we need to specify the database
+ type explicitly using the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), for
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type ("CHAR(2)")
+ char state_[2];
+
+ #pragma db type ("NVARCHAR(max)")
+ std::wstring text_;
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+#pragma db value(std::wstring) type("NVARCHAR(max)")
+
+#pragma db object
+class object
+{
+ ...
+
+ std::wstring text_; // Mapped to NVARCHAR(max).
+};
+ </pre>
+
+ <p>The <code>char[N]</code>, <code>std::array&lt;char, N></code>,
+ <code>wchar_t[N]</code>, and <code>std::array&lt;wchar_t, N></code>
+ values may or may not be zero-terminated. When extracting such values
+ from the database, ODB will append the zero terminator if there is
+ enough space.</p>
+
+ <p>See also <a href="#21.1.4">Section 21.1.4, "Long String and Binary
+ Types"</a> for certain limitations of long string types.</p>
+
+ <h3><a name="21.1.2">21.1.2 Binary Type and <code>UNIQUEIDENTIFIER</code> Mapping</a></h3>
+
+ <p>The SQL Server ODB runtime library also provides support for mapping the
+ <code>std::vector&lt;char></code>,
+ <code>std::vector&lt;unsigned&nbsp;char></code>,
+ <code>char[N]</code>, <code>unsigned&nbsp;char[N]</code>,
+ <code>std::array&lt;char, N></code>, and <code>std::array&lt;unsigned char, N></code>
+ types to the SQL Server <code>BINARY</code>, <code>VARBINARY</code>, and
+ <code>IMAGE</code> types. There is also support for mapping the
+ <code>char[16]</code> array to the SQL Server <code>UNIQUEIDENTIFIER</code>
+ type. However, these mappings are not enabled by default (in particular,
+ by default, <code>std::vector</code> and <code>std::array</code> will
+ be treated as containers). To enable the alternative mappings for these
+ types we need to specify the database type explicitly using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>), for example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ #pragma db type("UNIQUEIDENTIFIER")
+ char uuid_[16];
+
+ #pragma db type("VARBINARY(max)")
+ std::vector&lt;char> buf_;
+
+ #pragma db type("BINARY(256)")
+ unsigned char data_[256];
+};
+ </pre>
+
+ <p>Alternatively, this can be done on the per-type basis, for example:</p>
+
+ <pre class="cxx">
+typedef std::vector&lt;char> buffer;
+#pragma db value(buffer) type("VARBINARY(max)")
+
+#pragma db object
+class object
+{
+ ...
+
+ buffer buf_; // Mapped to VARBINARY(max).
+};
+ </pre>
+
+ <p>Note also that in native queries (<a href="#4">Chapter 4, "Querying
+ the Database"</a>) <code>char[N]</code> and
+ <code>std::array&lt;char, N></code> parameters are by default passed
+ as a string rather than a binary. To pass such parameters as a binary,
+ we need to specify the database type explicitly in the
+ <code>_val()</code>/<code>_ref()</code> calls. Note also that we
+ don't need to do this for the integrated queries, for example:</p>
+
+ <pre class="cxx">
+char u[16] = {...};
+
+db.query&lt;object> ("uuid = " + query::_val&lt;odb::mssql::id_binary> (u));
+db.query&lt;object> (
+ "uuid = " + query::_val&lt;odb::mssql::id_uniqueidentifier> (u));
+db.query&lt;object> (query::uuid == query::_ref (u));
+ </pre>
+
+ <p>See also <a href="#21.1.4">Section 21.1.4, "Long String and Binary
+ Types"</a> for certain limitations of long binary types.</p>
+
+ <h3><a name="21.1.3">21.1.3 <code>ROWVERSION</code> Mapping</a></h3>
+
+ <p><code>ROWVERSION</code> is a special SQL Server data type that is
+ automatically incremented by the database server whenever a row
+ is inserted or updated. As such, it is normally used to implement
+ optimistic concurrency and ODB provides support for using
+ <code>ROWVERSION</code> instead of the more portable approach
+ for optimistic concurrency (<a href="#12">Chapter 12, "Optimistic
+ Concurrency"</a>).</p>
+
+ <p><code>ROWVERSION</code> is a 64-bit value which is mapped by ODB
+ to <code>unsigned long long</code>. As a result, to use
+ <code>ROWVERSION</code> for optimistic concurrency we need to
+ make sure that the version column is of the <code>unsigned long
+ long</code> type. We also need to explicitly specify that it
+ should be mapped to the <code>ROWVERSION</code> data type. For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object optimistic
+class person
+{
+ ...
+
+ #pragma db version type("ROWVERSION")
+ unsigned long long version_;
+};
+ </pre>
+
+ <h3><a name="21.1.4">21.1.4 Long String and Binary Types</a></h3>
+
+ <p>For SQL Server, ODB handles character, national character, and
+ binary data in two different ways depending on its maximum length.
+ If the maximum length (in bytes) is less than or equal to the limit
+ specified with the <code>--mssql-short-limit</code> ODB compiler
+ option (1024 by default), then it is treated as <i>short data</i>,
+ otherwise &mdash; <i>long data</i>. For short data ODB pre-allocates
+ an intermediate buffer of the maximum size and binds it directly
+ to a parameter or result column. This way the underlying database
+ API (ODBC) can read/write directly from/to this buffer. In the case
+ of long data, the data is read/written in chunks using the
+ <code>SQLGetData()</code>/<code>SQLPutData()</code> ODBC functions.
+ While the long data approach reduces the amount of memory used by
+ the application, it may require greater CPU resources.</p>
+
+ <p>Long data has a number of limitations. In particular, when setting
+ a custom short data limit, make sure that it is sufficiently large
+ so that no object id in the application is treated as long data.
+ It is also impossible to load an object or view with long data more
+ than once as part of a query result iteration (<a href="#4.4">Section
+ 4.4, "Query Result"</a>). Any such attempt will result in the
+ <code>odb::mssql::long_data_reload</code> exception
+ (<a href="#21.4">Section 21.4, "SQL Server Exceptions"</a>). For
+ example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ int num_;
+
+ #pragma db type("VARCHAR(max)") // Long data.
+ std::string str_;
+};
+
+typedef odb::query&lt;object> query;
+typedef odb::result&lt;object> result;
+
+transaction t (db.begin ());
+
+result r (db.query&lt;object> (query::num &lt; 100));
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+{
+ if (!i->str_.empty ()) // First load.
+ {
+ object o;
+ i.load (o); // Error: second load, long_data_reload is thrown.
+ }
+}
+
+t.commit ();
+ </pre>
+
+ <p>Finally, if a native view (<a href="#10.6">Section 10.6, "Native
+ Views"</a>) contains one or more long data members, then such
+ members should come last both in the select-list of the native
+ SQL query and the list of data members in the C++ class.</p>
+
+ <h2><a name="21.2">21.2 SQL Server Database Class</a></h2>
+
+ <p>The SQL Server <code>database</code> class encapsulates the ODBC
+ environment handle as well as the server instance address and
+ user credentials that are used to establish connections to the
+ database. It has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mssql
+ {
+ enum protocol
+ {
+ protocol_auto,
+ protocol_tcp, // TCP/IP.
+ protocol_lpc, // Shared memory (local procedure call).
+ protocol_np // Named pipes.
+ };
+
+ enum transaction_isolation
+ {
+ isolation_read_uncommitted,
+ isolation_read_committed, // SQL Server default.
+ isolation_repeatable_read,
+ isolation_snapshot,
+ isolation_serializable
+ };
+
+ class database: public odb::database
+ {
+ public:
+ typedef protocol protocol_type;
+ typedef transaction_isolation transaction_isolation_type;
+
+ database (const std::string&amp; user,
+ const std::string&amp; password,
+ const std::string&amp; db,
+ const std::string&amp; server,
+ const std::string&amp; driver = "",
+ const std::string&amp; extra_connect_string = "",
+ transaction_isolation_type = isolation_read_committed,
+ SQLHENV environment = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; user,
+ const std::string&amp; password,
+ const std::string&amp; db,
+ protocol_type protocol = protocol_auto,
+ const std::string&amp; host = "",
+ const std::string&amp; instance = "",
+ const std::string&amp; driver = "",
+ const std::string&amp; extra_connect_string = "",
+ transaction_isolation_type = isolation_read_committed,
+ SQLHENV environment = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; user,
+ const std::string&amp; password,
+ const std::string&amp; db,
+ const std::string&amp; host,
+ unsigned int port,
+ const std::string&amp; driver = "",
+ const std::string&amp; extra_connect_string = "",
+ transaction_isolation_type = isolation_read_committed,
+ SQLHENV environment = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (const std::string&amp; connect_string,
+ transaction_isolation_type = isolation_read_committed,
+ SQLHENV environment = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ database (int&amp; argc,
+ char* argv[],
+ bool erase = false,
+ const std::string&amp; extra_connect_string = "",
+ transaction_isolation_type = isolation_read_committed,
+ SQLHENV environment = 0,
+ std::[auto|unique]_ptr&lt;connection_factory> = 0);
+
+ static void
+ print_usage (std::ostream&amp;);
+
+ public:
+ const std::string&amp;
+ user () const;
+
+ const std::string&amp;
+ password () const;
+
+ const std::string&amp;
+ db () const;
+
+ protocol_type
+ protocol () const;
+
+ const std::string&amp;
+ host () const;
+
+ const std::string&amp;
+ instance () const;
+
+ unsigned int
+ port () const;
+
+ const std::string&amp;
+ server () const;
+
+ const std::string&amp;
+ driver () const;
+
+ const std::string&amp;
+ extra_connect_string () const;
+
+ transaction_isolation_type
+ transaction_isolation () const;
+
+ const std::string&amp;
+ connect_string () const;
+
+ SQLHENV
+ environment ();
+
+ public:
+ connection_ptr
+ connection ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/mssql/database.hxx></code>
+ header file to make this class available in your application.</p>
+
+ <p>The overloaded <code>database</code> constructors allow us to specify the
+ SQL Server database parameters that should be used when connecting to the
+ database. The <code>user</code> and <code>password</code> arguments
+ specify the login name and password. If <code>user</code> is empty,
+ then Windows authentication is used and the <code>password</code>
+ argument is ignored. The <code>db</code> argument specifies the
+ database name to open. If it is empty, then the default database for
+ the user is used.</p>
+
+ <p>The <code>server</code> argument in the first constructor specifies
+ the SQL Server instance address in the standard SQL Server address
+ format:</p>
+
+ <p>
+ <code>[<i>protocol</i><b>:</b>]<i>host</i>[<b>\</b><i>instance</i>][<b>,</b><i>port</i>]</code>
+ </p>
+
+ <p>Where <code><i>protocol</i></code> can be <code>tcp</code>
+ (TCP/IP), <code>lpc</code> (shared memory), or
+ <code>np</code> (named pipe). If protocol is not specified, then a
+ suitable protocol is automatically selected based on the SQL Server
+ Native Client configuration. The <code><i>host</i></code> component
+ can be a host name or an IP address. If <code><i>instance</i></code>
+ is not specified, then the default SQL Server instance is assumed.
+ If port is not specified, then the default SQL Server port is
+ used (1433). Note that you would normally specify either the
+ instance name or the port, but not both. If both are specified,
+ then the instance name is ignored by the SQL Server Native Client
+ ODBC driver. For more information on the format of the SQL
+ Server address, refer to the SQL Server Native Client ODBC
+ driver documentation.</p>
+
+ <p>The second and third constructors allow us to specify all these address
+ components (protocol, host, instance, and port) as separate
+ arguments. The third constructor always connects using TCP/IP
+ to the specified host and port.</p>
+
+ <p>The <code>driver</code> argument specifies the SQL Server Native
+ Client ODBC driver that should be used to connect to the database.
+ If not specified, then the latest available version is used. The
+ following examples show common ways of connecting to the database
+ using the first three constructors:</p>
+
+ <pre class="cxx">
+// Connect to the default SQL Server instance on the local machine
+// using the default protocol. Login as 'test' with password 'secret'
+// and open the 'example_db' database.
+//
+odb::mssql::database db1 ("test",
+ "secret",
+ "example_db");
+
+// As above except use Windows authentication and open the default
+// database for this user.
+//
+odb::mssql::database db2 ("",
+ "",
+ "");
+
+// Connect to the default SQL Server instance on 'onega' using the
+// default protocol. Login as 'test' with password 'secret' and open
+// the 'example_db' database.
+//
+odb::mssql::database db3 ("test",
+ "secret",
+ "example_db"
+ "onega");
+
+// As above but connect to the 'production' SQL Server instance.
+//
+odb::mssql::database db4 ("test",
+ "secret",
+ "example_db"
+ "onega\\production");
+
+// Same as above but specify protocol, host, and instance as separate
+// arguments.
+//
+odb::mssql::database db5 ("test",
+ "secret",
+ "example_db",
+ odb::mssql::protocol_auto,
+ "onega",
+ "production");
+
+// As above, but use TCP/IP as the protocol.
+//
+odb::mssql::database db6 ("test",
+ "secret",
+ "example_db"
+ "tcp:onega\\production");
+
+// Same as above but using separate arguments.
+//
+odb::mssql::database db7 ("test",
+ "secret",
+ "example_db",
+ odb::mssql::protocol_tcp,
+ "onega",
+ "production");
+
+// As above, but use TCP/IP port instead of the instance name.
+//
+odb::mssql::database db8 ("test",
+ "secret",
+ "example_db"
+ "tcp:onega,1435");
+
+// Same as above but using separate arguments. Note that here we
+// don't need to specify protocol explicitly since it can only
+// be TCP/IP.
+//
+odb::mssql::database db9 ("test",
+ "secret",
+ "example_db",
+ "onega",
+ 1435);
+
+// As above but use the specific SQL Server Native Client ODBC
+// driver version.
+//
+odb::mssql::database dbA ("test",
+ "secret",
+ "example_db"
+ "tcp:onega,1435",
+ "SQL Server Native Client 10.0");
+ </pre>
+
+
+ <p>The fourth constructor allows us to pass a custom ODBC connection
+ string that provides all the information necessary to connect to
+ the database. Note also that all the other constructors have the
+ <code>extra_connect_string</code> argument which can be used to
+ specify additional ODBC connection attributes. For more information
+ on the format of the ODBC connection string, refer to the SQL
+ Server Native Client ODBC driver documentation.</p>
+
+ <p>The last constructor extracts the database parameters
+ from the command line. The following options are recognized:</p>
+
+ <pre class="terminal">
+ --user | -U &lt;login>
+ --password | -P &lt;password>
+ --database | -d &lt;name>
+ --server | -S &lt;address>
+ --driver &lt;name>
+ --options-file &lt;file>
+ </pre>
+
+ <p>The <code>--options-file</code> option allows us to specify some
+ or all of the database options in a file with each option appearing
+ on a separate line followed by a space and an option value.</p>
+
+ <p>If the <code>erase</code> argument to this constructor is true,
+ then the above options are removed from the <code>argv</code>
+ array and the <code>argc</code> count is updated accordingly.
+ This is primarily useful if your application accepts other
+ options or arguments and you would like to get the SQL Server
+ options out of the <code>argv</code> array.</p>
+
+ <p>This constructor throws the <code>odb::mssql::cli_exception</code>
+ exception if the SQL Server option values are missing or invalid. See
+ section <a href="#21.4">Section 21.4, "SQL Server Exceptions"</a> for
+ more information on this exception.</p>
+
+ <p>The static <code>print_usage()</code> function prints the list of options
+ with short descriptions that are recognized by this constructor.</p>
+
+ <p>Additionally, all the constructors have the <code>transaction_isolation</code>
+ and <code>environment</code> arguments. The <code>transaction_isolation</code>
+ argument allows us to specify an alternative transaction isolation level
+ that should be used by all the connections created by this database instance.
+ The <code>environment</code> argument allows us to provide a custom ODBC
+ environment handle. If this argument is not <code>NULL</code>, then the
+ passed handle is used in all the ODBC function calls made by this
+ <code>database</code> instance. Note also that the <code>database</code>
+ instance does not assume ownership of the passed environment handle and
+ this handle should be valid for the lifetime of the <code>database</code>
+ instance.</p>
+
+ <p>The last argument to all of the constructors is a pointer to the
+ connection factory. In C++98/03, it is <code>std::auto_ptr</code> while
+ in C++11 <code>std::unique_ptr</code> is used instead. If we pass a
+ non-<code>NULL</code> value, the database instance assumes ownership
+ of the factory instance. The connection factory interface as well as
+ the available implementations are described in the next section.</p>
+
+ <p>The set of accessor functions following the constructors allows us
+ to query the parameters of the <code>database</code> instance.</p>
+
+ <p>The <code>connection()</code> function returns a pointer to the
+ SQL Server database connection encapsulated by the
+ <code>odb::mssql::connection</code> class. For more information
+ on <code>mssql::connection</code>, refer to <a href="#21.3">Section
+ 21.3, "SQL Server Connection and Connection Factory"</a>.</p>
+
+ <h2><a name="21.3">21.3 SQL Server Connection and Connection Factory</a></h2>
+
+ <p>The <code>mssql::connection</code> class has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mssql
+ {
+ class connection: public odb::connection
+ {
+ public:
+ connection (database&amp;);
+ connection (database&amp;, SQLHDBC handle);
+
+ SQLHDBC
+ handle ();
+
+ details::buffer&amp;
+ long_data_buffer ();
+ };
+
+ typedef details::shared_ptr&lt;connection> connection_ptr;
+ }
+}
+ </pre>
+
+ <p>For more information on the <code>odb::connection</code> interface, refer
+ to <a href="#3.6">Section 3.6, "Connections"</a>. The first overloaded
+ <code>mssql::connection</code> constructor creates a new ODBC connection.
+ The created connection is configured to use the manual commit mode with
+ multiple active result sets (MARS) enabled. The second constructor allows
+ us to create a <code>connection</code> instance by providing an already
+ established ODBC connection. Note that the <code>connection</code>
+ instance assumes ownership of this handle. The <code>handle()</code>
+ accessor returns the underlying ODBC connection handle associated with
+ the <code>connection</code> instance.</p>
+
+ <p>Additionally, each <code>connection</code> instance maintains a long
+ data buffer. This buffer is used by the SQL Server ODB runtime
+ as an intermediate storage for piecewise handling of long data.
+ By default, the long data buffer has zero initial capacity and is
+ expanded to 4096 bytes when the first long data operation is performed.
+ If your application requires a bigger or smaller long data buffer,
+ you can specify a custom capacity using the <code>long_data_buffer()</code>
+ accessor.</p>
+
+ <p>The <code>mssql::connection_factory</code> abstract class has the
+ following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mssql
+ {
+ class connection_factory
+ {
+ public:
+ virtual void
+ database (database&amp;) = 0;
+
+ virtual connection_ptr
+ connect () = 0;
+ };
+ }
+}
+ </pre>
+
+ <p>The <code>database()</code> function is called when a connection
+ factory is associated with a database instance. This happens in
+ the <code>odb::mssql::database</code> class constructors. The
+ <code>connect()</code> function is called whenever a database
+ connection is requested.</p>
+
+ <p>The two implementations of the <code>connection_factory</code>
+ interface provided by the SQL Server ODB runtime are
+ <code>new_connection_factory</code> and
+ <code>connection_pool_factory</code>. You will need to include
+ the <code>&lt;odb/mssql/connection-factory.hxx></code>
+ header file to make the <code>connection_factory</code> interface
+ and these implementation classes available in your application.</p>
+
+ <p>The <code>new_connection_factory</code> class creates a new
+ connection whenever one is requested. When a connection is no
+ longer needed, it is released and closed. The
+ <code>new_connection_factory</code> class has the following
+ interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mssql
+ {
+ class new_connection_factory: public connection_factory
+ {
+ public:
+ new_connection_factory ();
+ };
+};
+ </pre>
+
+ <p>The <code>connection_pool_factory</code> class implements a
+ connection pool. It has the following interface:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mssql
+ {
+ class connection_pool_factory: public connection_factory
+ {
+ public:
+ connection_pool_factory (std::size_t max_connections = 0,
+ std::size_t min_connections = 0);
+
+ protected:
+ class pooled_connection: public connection
+ {
+ public:
+ pooled_connection (database_type&amp;);
+ pooled_connection (database_type&amp;, SQLHDBC handle);
+ };
+
+ typedef details::shared_ptr&lt;pooled_connection> pooled_connection_ptr;
+
+ virtual pooled_connection_ptr
+ create ();
+ };
+};
+ </pre>
+
+ <p>The <code>max_connections</code> argument in the
+ <code>connection_pool_factory</code> constructor specifies the maximum
+ number of concurrent connections that this pool factory will
+ maintain. Similarly, the <code>min_connections</code> argument
+ specifies the minimum number of available connections that
+ should be kept open.</p>
+
+ <p>Whenever a connection is requested, the pool factory first
+ checks if there is an unused connection that can be returned.
+ If there is none, the pool factory checks the
+ <code>max_connections</code> value to see if a new connection
+ can be created. If the total number of connections maintained
+ by the pool is less than this value, then a new connection is
+ created and returned. Otherwise, the caller is blocked until
+ a connection becomes available.</p>
+
+ <p>When a connection is released, the pool factory first checks
+ if there are blocked callers waiting for a connection. If so, then
+ one of them is unblocked and is given the connection. Otherwise,
+ the pool factory checks whether the total number of connections
+ maintained by the pool is greater than the <code>min_connections</code>
+ value. If that's the case, the connection is closed. Otherwise, the
+ connection is added to the pool of available connections to be
+ returned on the next request. In other words, if the number of
+ connections maintained by the pool exceeds <code>min_connections</code>
+ and there are no callers waiting for a new connection,
+ the pool will close the excess connections.</p>
+
+ <p>If the <code>max_connections</code> value is 0, then the pool will
+ create a new connection whenever all of the existing connections
+ are in use. If the <code>min_connections</code> value is 0, then
+ the pool will never close a connection and instead maintain all
+ the connections that were ever created.</p>
+
+ <p>The <code>create()</code> virtual function is called whenever the
+ pool needs to create a new connection. By deriving from the
+ <code>connection_pool_factory</code> class and overriding this
+ function we can implement custom connection establishment
+ and configuration.</p>
+
+ <p>If you pass <code>NULL</code> as the connection factory to one of the
+ <code>database</code> constructors, then the
+ <code>connection_pool_factory</code> instance will be created by default
+ with the min and max connections values set to <code>0</code>. The
+ following code fragment shows how we can pass our own connection factory
+ instance:</p>
+
+ <pre class="cxx">
+#include &lt;odb/database.hxx>
+
+#include &lt;odb/mssql/database.hxx>
+#include &lt;odb/mssql/connection-factory.hxx>
+
+int
+main (int argc, char* argv[])
+{
+ auto_ptr&lt;odb::mssql::connection_factory> f (
+ new odb::mssql::connection_pool_factory (20));
+
+ auto_ptr&lt;odb::database> db (
+ new mssql::database (argc, argv, false, "", 0, f));
+}
+ </pre>
+
+ <h2><a name="21.4">21.4 SQL Server Exceptions</a></h2>
+
+ <p>The SQL Server ODB runtime library defines the following
+ SQL Server-specific exceptions:</p>
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace mssql
+ {
+ class database_exception: odb::database_exception
+ {
+ public:
+ class record
+ {
+ public:
+ SQLINTEGER
+ error () const;
+
+ const std::string&amp;
+ sqlstate () const;
+
+ const std::string&amp;
+ message () const;
+ };
+
+ typedef std::vector&lt;record> records;
+
+ typedef records::size_type size_type;
+ typedef records::const_iterator iterator;
+
+ iterator
+ begin () const;
+
+ iterator
+ end () const;
+
+ size_type
+ size () const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class cli_exception: odb::exception
+ {
+ public:
+ virtual const char*
+ what () const throw ();
+ };
+
+ class long_data_reload: odb::exception
+ {
+ public:
+ virtual const char*
+ what () const throw ();
+ };
+ }
+}
+ </pre>
+
+ <p>You will need to include the <code>&lt;odb/mssql/exceptions.hxx></code>
+ header file to make these exceptions available in your application.</p>
+
+ <p>The <code>odb::mssql::database_exception</code> is thrown if
+ an SQL Server database operation fails. The SQL Server-specific error
+ information is stored as a series of records, each containing
+ the error code as a signed 4-byte integer, the SQLSTATE code,
+ and the message string. All this information is also combined
+ and returned in a human-readable form by the <code>what()</code>
+ function.</p>
+
+ <p>The <code>odb::mssql::cli_exception</code> is thrown by the
+ command line parsing constructor of the <code>odb::mssql::database</code>
+ class if the SQL Server option values are missing or invalid. The
+ <code>what()</code> function returns a human-readable description
+ of an error.</p>
+
+ <p>The <code>odb::mssql::long_data_reload</code> is thrown if an
+ attempt is made to re-load an object or view with long data as
+ part of a query result iteration. For more information, refer
+ to <a href="#21.1">Section 21.1, "SQL Server Type Mapping"</a>.</p>
+
+ <h2><a name="21.5">21.5 SQL Server Limitations</a></h2>
+
+ <p>The following sections describe SQL Server-specific limitations imposed
+ by the current SQL Server and ODB runtime versions.</p>
+
+ <h3><a name="21.5.1">21.5.1 Query Result Caching</a></h3>
+
+ <p>SQL Server ODB runtime implementation does not perform query result
+ caching (<a href="#4.4">Section 4.4, "Query Result"</a>) even when
+ explicitly requested. The ODBC API and the SQL Server Native Client ODBC
+ driver support interleaving execution of multiple prepared statements
+ on a single connection. As a result, it is possible to have multiple
+ uncached results and calls to other database functions do not invalidate
+ them. The only limitation of the uncached SQL Server results is the
+ unavailability of the <code>result::size()</code> function. If you
+ call this function on an SQL Server query result, then the
+ <code>odb::result_not_cached</code> exception (<a href="#3.14">Section
+ 3.14, "ODB Exceptions"</a>) is always thrown. Future versions of the
+ SQL Server ODB runtime library may add support for result caching.</p>
+
+ <h3><a name="21.5.2">21.5.2 Foreign Key Constraints</a></h3>
+
+ <p>ODB assumes the standard SQL behavior which requires that foreign
+ key constraints checking is deferred until the transaction is
+ committed. The only behavior supported by SQL Server is to check
+ such constraints immediately. As a result, by default, schemas
+ generated by the ODB compiler for SQL Server have foreign key
+ definitions commented out. They are retained only for documentation.</p>
+
+ <p>You can override the default behavior and instruct the ODB
+ compiler to generate non-deferrable foreign keys by specifying
+ the <code>--fkeys-deferrable-mode not_deferrable</code> ODB
+ compiler option. Note, however, that in this case the order in
+ which you persist, update, and erase objects within a transaction
+ becomes important.</p>
+
+ <h3><a name="21.5.3">21.5.3 Unique Constraint Violations</a></h3>
+
+ <p>Due to the granularity of the ODBC error codes, it is impossible
+ to distinguish between the duplicate primary key and other unique
+ constraint violations. As a result, when making an object persistent,
+ the SQL Server ODB runtime will translate all unique constraint violation
+ errors to the <code>object_already_persistent</code> exception
+ (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>).</p>
+
+ <h3><a name="21.5.4">21.5.4 Multi-threaded Windows Applications</a></h3>
+
+ <p>Multi-threaded Windows applications must use the
+ <code>_beginthread()</code>/<code>_beginthreadex()</code> and
+ <code>_endthread()</code>/<code>_endthreadex()</code> CRT functions
+ instead of the <code>CreateThread()</code> and <code>EndThread()</code>
+ Win32 functions to start and terminate threads. This is a limitation of
+ the ODBC implementation on Windows.</p>
+
+ <h3><a name="21.5.5">21.5.5 Affected Row Count and DDL Statements</a></h3>
+
+ <p>SQL Server always returns zero as the number of affected rows
+ for DDL statements. In particular, this means that the
+ <code>database::execute()</code> (<a href="#3.12">Section 3.12,
+ "Executing Native SQL Statements"</a>) function will always
+ return zero for such statements.</p>
+
+ <h3><a name="21.5.6">21.5.6 Long Data and Auto Object Ids, <code>ROWVERSION</code></a></h3>
+
+ <p>SQL Server 2005 has a bug that causes it to fail on an <code>INSERT</code>
+ or <code>UPDATE</code> statement with the <code>OUTPUT</code> clause
+ (used to return automatically assigned object ids as well as
+ <code>ROWVERSION</code> values) if one of the inserted columns
+ is long data. The symptom of this bug in ODB is an exception thrown
+ by the <code>database::persist()</code> or <code>database::update()</code>
+ function when used on an object that contains long data and has an
+ automatically assigned object id or uses <code>ROWVERSION</code>-based
+ optimistic concurrency (<a href="#21.1.1">Section 21.1.1,
+ "<code>ROWVERSION</code> Support"</a>). The error message reads "This
+ operation conflicts with another pending operation on this transaction.
+ The operation failed."</p>
+
+ <p>For automatically assigned object ids ODB includes a workaround for
+ this bug which uses a less efficient method to obtain id values for
+ objects that contain long data. To enable this workaround you need
+ to specify that the generated code will be used with SQL Server 2005
+ or later by passing the <code>--mssql-server-version&nbsp;9.0</code>
+ ODB compiler option.</p>
+
+ <p>For <code>ROWVERSION</code>-based optimistic concurrency no workaround
+ is currently provided. The ODB compiler will issue an error for
+ objects that use <code>ROWVERSION</code> for optimistic concurrency
+ and containing long data.</p>
+
+ <h3><a name="21.5.7">21.5.7 Long Data and By-Value Accessors/Modifiers</a></h3>
+
+ <p>As discussed in <a href="#14.4.5">Section 14.4.5,
+ "<code>get</code>/<code>set</code>/<code>access</code>"</a>, by-value
+ accessor and modifier expressions cannot be used with data members
+ of long data types. The SQL Server ODB runtime uses streaming for
+ reading/writing long data directly from/to data members. As a result,
+ by-reference accessors and modifiers should be used for these data
+ types.</p>
+
+ <h3><a name="21.5.8">21.5.8 Bulk Update and <code>ROWVERSION</code></a></h3>
+
+ <p>The bulk update operation (<a href="#15.3">Section 15.3, "Bulk Database
+ Operations"</a>) is not yet supported for persistent classes that use
+ <code>ROWVERSION</code>-based optimistic concurrency. For such classes
+ the bulk <code>update()</code> function is not available. The bulk
+ persist and erase support is still provided.</p>
+
+ <h2><a name="21.6">21.6 SQL Server Index Definitions</a></h2>
+
+ <p>When the <code>index</code> pragma (<a href="#14.7">Section 14.7,
+ "Index Definition Pragmas"</a>) is used to define an SQL Server index,
+ the <code>type</code> clause specifies the index type (for example,
+ <code>UNIQUE</code>, <code>CLUSTERED</code>), the <code>method</code>
+ clause is not used, and the <code>options</code> clause specifies
+ additional index properties. The column options can be used to specify
+ the sort order. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ std::string name_;
+
+ #pragma db index \
+ type("UNIQUE CLUSTERED") \
+ member(name_, "DESC") \
+ options("WITH(FILLFACTOR = 80)")
+};
+ </pre>
+
+ <h2><a name="21.7">21.7 SQL Server Stored Procedures</a></h2>
+
+ <p>ODB native views (<a href="#10.6">Section 10.6, "Native Views"</a>)
+ can be used to call SQL Server stored procedures. For example, assuming
+ we are using the <code>person</code> class from <a href="#2">Chapter
+ 2, "Hello World Example"</a> (and the corresponding <code>person</code>
+ table), we can create a stored procedure that given the min and max
+ ages returns some information about all the people in that range:</p>
+
+ <pre class="sql">
+CREATE PROCEDURE dbo.person_range (
+ @min_age SMALLINT,
+ @max_age SMALLINT)
+AS
+ SELECT age, first, last FROM person
+ WHERE age >= @min_age AND age &lt;= @max_age;
+ </pre>
+
+ <p>Given the above stored procedure we can then define an ODB view
+ that can be used to call it and retrieve its result:</p>
+
+ <pre class="cxx">
+#pragma db view query("EXEC person_range (?)")
+struct person_range
+{
+ unsigned short age;
+ std::string first;
+ std::string last;
+};
+ </pre>
+
+ <p>The following example shows how we can use the above view to
+ print the list of people in a specific age range:</p>
+
+ <pre class="cxx">
+typedef odb::query&lt;person_range> query;
+typedef odb::result&lt;person_range> result;
+
+transaction t (db.begin ());
+
+result r (
+ db.query&lt;person_range> (
+ query::_val (1) + "," + query::_val (18)));
+
+for (result::iterator i (r.begin ()); i != r.end (); ++i)
+ cerr &lt;&lt; i->first &lt;&lt; " " &lt;&lt; i->last &lt;&lt; " " &lt;&lt; i->age &lt;&lt; endl;
+
+t.commit ();
+ </pre>
+
+ <p>Note that as with all native views, the order and types of data members
+ must match those of columns in the <code>SELECT</code> list inside
+ the stored procedure.</p>
+
+ <p>There are also a number of limitations when it comes to calling
+ SQL Server stored procedures with ODB views. There is currently
+ no support for output parameters, however, this is planned for
+ a future version. In the meantime, to call a stored procedure
+ that has output parameters we have to use a wrapper procedure
+ that converts such parameters to a <code>SELECT</code>
+ result. For example, given the following procedure that
+ calculates the age range of the people in our database:</p>
+
+ <pre class="sql">
+CREATE PROCEDURE dbo.person_age_range (
+ @min_age SMALLINT = NULL OUTPUT,
+ @max_age SMALLINT = NULL OUTPUT)
+AS
+ SELECT @min_age = MIN(age), @max_age = MAX(max) FROM person;
+ </pre>
+
+ <p>We can create a wrapper procedure like this:</p>
+
+ <pre class="sql">
+CREATE PROCEDURE dbo.person_age_range_odb
+AS
+ DECLARE @min_age SMALLINT, @max_age SMALLINT;
+ EXEC person_age_range @min_age OUTPUT, @max_age OUTPUT;
+ SELECT @min_age, @max_age;
+ </pre>
+
+ <p>And a view like this:</p>
+
+ <pre class="cxx">
+#pragma db view query("EXEC person_age_range_odb")
+struct person_age_range
+{
+ unsigned short min_age;
+ unsigned short max_age;
+};
+ </pre>
+
+ <p>Which we can then use to call the stored procedure:</p>
+
+ <pre class="cxx">
+transaction t (db.begin ());
+
+person_age_range ar (db.query_value&lt;person_age_range> ());
+cerr &lt;&lt; ar.min_age &lt;&lt; " " &lt;&lt; ar.max_age &lt;&lt; endl;
+
+t.commit ();
+ </pre>
+
+ <p>In SQL Server, a stored procedure can produce multiple results.
+ For example, if a stored procedure executes several
+ <code>SELECT</code> statements, then the result of calling such
+ a procedure consists of two row sets, one for each <code>SELECT</code>
+ statement. Because such multiple row sets can contain varying number
+ and type of columns, they cannot be all extracted into a
+ single view. Consequently, these kind of stored procedures are
+ currently not supported.</p>
+
+ <p>A stored procedure may also produce no row sets at all. For
+ example, a stored procedure that only executes DML statements
+ would exhibit this behavior. To call such a procedure we use
+ an empty view, for example:</p>
+
+ <pre class="sql">
+CREATE PROCEDURE dbo.insert_person (
+ @first VARCHAR(512),
+ @last VARCHAR(512),
+ @age SMALLINT)
+AS
+ INSERT INTO person(first, last, age)
+ VALUES(@first, @last, @age);
+ </pre>
+
+ <pre class="cxx">
+#pragma db view
+struct no_result {};
+
+transaction t (db.begin ());
+
+db.query_one&lt;no_result> (
+ "EXEC insert_person" +
+ query::_val ("John") + "," +
+ query::_val ("Doe") + "," +
+ query::_val (21));
+
+t.commit ();
+ </pre>
+
+ <p>Finally, an SQL Server stored procedure can also return an
+ integer status code. Similar to output parameters, this code
+ can only be observed by an ODB view if it is converted to a
+ <code>SELECT</code> result. For more information on how to
+ do this and for other examples of stored procedure calls,
+ refer to the <code>mssql/stored-proc</code> test in the
+ <code>odb-tests</code> package.</p>
+
+ <!-- PART -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="III">PART III&nbsp;&nbsp;
+ <span style="font-weight: normal;">PROFILES</span></a></h1>
+
+ <p>Part III covers the integration of ODB with popular C++ frameworks
+ and libraries. It consists of the following chapters.</p>
+
+ <table class="toc">
+ <tr><th>22</th><td><a href="#22">Profiles Introduction</a></td></tr>
+ <tr><th>23</th><td><a href="#23">Boost Profile</a></td></tr>
+ <tr><th>24</th><td><a href="#24">Qt Profile</a></td></tr>
+ </table>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="22">22 Profiles Introduction</a></h1>
+
+ <p>ODB profiles are a generic mechanism for integrating ODB with
+ widely-used C++ frameworks and libraries. A profile provides glue
+ code which allows you to seamlessly persist various components, such
+ as smart pointers, containers, and value types found in these
+ frameworks or libraries. The code necessary to implement a profile
+ is packaged into the so called profile library. For example, the
+ Boost profile implementation is provided by the <code>libodb-boost</code>
+ profile library.</p>
+
+ <p>Besides linking the profile library to our application, it is also
+ necessary to let the ODB compiler know which profiles we
+ are using. This is accomplished with the <code>--profile</code>
+ (or <code>-p</code> alias) option. For example:</p>
+
+ <pre class="terminal">
+odb --profile boost ...
+ </pre>
+
+ <p>Some profiles, especially those covering frameworks or libraries that
+ consist of multiple sub-libraries, provide sub-profiles that allow you
+ to pick and choose which components you would like to use in your
+ application. For example, the <code>boost</code> profile contains
+ the <code>boost/data-time</code> sub-profile. If we are only
+ interested in the <code>date_time</code> types, then we can
+ pass <code>boost/data-time</code> instead of <code>boost</code>
+ to the <code>--profile</code> option, for example:</p>
+
+ <pre class="terminal">
+odb --profile boost/date-time ...
+ </pre>
+
+ <p>To summarize, you will need to perform the following steps in order
+ to make use of a profile in your application:</p>
+
+ <ol>
+ <li>ODB compiler: if necessary, specify the path to the profile library
+ headers (<code>-I</code> option).</li>
+ <li>ODB compiler: specify the profile you would like to use with
+ the <code>--profile</code> option.</li>
+ <li>C++ compiler: if necessary, specify the path to the profile library
+ headers (normally <code>-I</code> option).</li>
+ <li>Linker: link the profile library to the application.</li>
+ </ol>
+
+ <p>The remaining chapters in this part of the manual describe the
+ standard profiles provided by ODB.</p>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="23">23 Boost Profile</a></h1>
+
+ <p>The ODB profile implementation for Boost is provided by the
+ <code>libodb-boost</code> library and consists of multiple sub-profiles
+ corresponding to the individual Boost libraries. To enable all the
+ available Boost sub-profiles, pass <code>boost</code> as the profile
+ name to the <code>--profile</code> ODB compiler option. Alternatively,
+ you can enable only specific sub-profiles by passing individual
+ sub-profile names to <code>--profile</code>. The following sections in
+ this chapter discuss each Boost sub-profile in detail. The
+ <code>boost</code> example in the <code>odb-examples</code>
+ package shows how to enable and use the Boost profile.</p>
+
+ <p>Some sub-profiles may throw exceptions to indicate error conditions,
+ such as the inability to store a specific value in a particular database
+ system. All such exceptions derive from the
+ <code>odb::boost::exception</code> class which in turn derives from
+ the root of the ODB exception hierarchy, class <code>odb::exception</code>
+ (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>). The
+ <code>odb::boost::exception</code> class is defined in the
+ <code>&lt;odb/boost/exception.hxx></code> header file and has the
+ same interface as <code>odb::exception</code>. Concrete exceptions
+ that can be thrown by the Boost sub-profiles are described in the
+ following sections.</p>
+
+ <h2><a name="23.1">23.1 Smart Pointers Library</a></h2>
+
+ <p>The <code>smart-ptr</code> sub-profile provides persistence
+ support for a subset of smart pointers from the Boost
+ <code>smart_ptr</code> library. To enable only this profile,
+ pass <code>boost/smart-ptr</code> to the <code>--profile</code>
+ ODB compiler option.</p>
+
+ <p>The currently supported smart pointers are
+ <code>boost::shared_ptr</code> and <code>boost::weak_ptr</code>. For
+ more information on using smart pointers as pointers to objects and
+ views, refer to <a href="#3.3">Section 3.3, "Object and View Pointers"</a>
+ and <a href="#6">Chapter 6, "Relationships"</a>. For more information
+ on using smart pointers as pointers to values, refer to
+ <a href="#7.3">Section 7.3, "Pointers and <code>NULL</code> Value
+ Semantics"</a>. When used as a pointer to a value, only
+ <code>boost::shared_ptr</code> is supported. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db null
+ boost::shared_ptr&lt;std::string> middle_name_;
+};
+ </pre>
+
+ <p>To provide finer grained control over object relationship loading,
+ the <code>smart-ptr</code> sub-profile also provides the lazy
+ counterparts for the above pointers: <code>odb::boost::lazy_shared_ptr</code> and
+ <code>odb::boost::lazy_weak_ptr</code>. You will need to include the
+ <code>&lt;odb/boost/lazy-ptr.hxx></code> header file to make the lazy
+ variants available in your application. For a description of the lazy
+ pointer interface and semantics refer to <a href="#6.4">Section 6.4,
+ "Lazy Pointers"</a>. The following example shows how we can use these
+ smart pointers to establish a relationship between persistent objects.</p>
+
+ <pre class="cxx">
+class employee;
+
+#pragma db object
+class position
+{
+ ...
+
+ #pragma db inverse(position_)
+ odb::boost::lazy_weak_ptr&lt;employee> employee_;
+};
+
+#pragma db object
+class employee
+{
+ ...
+
+ #pragma db not_null
+ boost::shared_ptr&lt;position> position_;
+};
+ </pre>
+
+ <p>Besides providing persistence support for the above smart pointers,
+ the <code>smart-ptr</code> sub-profile also changes the default
+ pointer (<a href="#3.3">Section 3.3, "Object and View Pointers"</a>)
+ to <code>boost::shared_ptr</code>. In particular, this means that
+ database functions that return dynamically allocated objects and views
+ will return them as <code>boost::shared_ptr</code> pointers. To override
+ this behavior, add the <code>--default-pointer</code> option specifying
+ the alternative pointer type after the <code>--profile</code> option.</p>
+
+ <h2><a name="23.2">23.2 Unordered Containers Library</a></h2>
+
+ <p>The <code>unordered</code> sub-profile provides persistence support for
+ the containers from the Boost <code>unordered</code> library. To enable
+ only this profile, pass <code>boost/unordered</code> to
+ the <code>--profile</code> ODB compiler option.</p>
+
+ <p>The supported containers are <code>boost::unordered_set</code>,
+ <code>boost::unordered_map</code>, <code>boost::unordered_multiset</code>,
+ and <code>boost::unordered_multimap</code>. For more information on using
+ the set and multiset containers with ODB, refer to <a href="#5.2">Section
+ 5.2, "Set and Multiset Containers"</a>. For more information on using the
+ map and multimap containers with ODB, refer to <a href="#5.3"> Section
+ 5.3, "Map and Multimap Containers"</a>. The following example shows how
+ the <code>unordered_set</code> container may be used within a persistent
+ object.</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+ boost::unordered_set&lt;std::string&gt; emails_;
+};
+ </pre>
+
+ <h2><a name="23.3">23.3 Multi-Index Container Library</a></h2>
+
+ <p>The <code>multi-index</code> sub-profile provides persistence support for
+ <code>boost::multi_index_container</code> from the Boost Multi-Index
+ library. To enable only this profile, pass <code>boost/multi-index</code>
+ to the <code>--profile</code> ODB compiler option. The following example
+ shows how <code>multi_index_container</code> may be used within a
+ persistent object.</p>
+
+ <pre class="cxx">
+namespace mi = boost::multi_index;
+
+#pragma db object
+class person
+{
+ ...
+
+ typedef
+ mi::multi_index_container&lt;
+ std::string,
+ mi::indexed_by&lt;
+ mi::sequenced&lt;>,
+ mi::ordered_unique&lt;mi::identity&lt;std::string> >
+ >
+ > emails;
+
+ emails emails_;
+};
+ </pre>
+
+ <p>Note that a <code>multi_index_container</code> instantiation is
+ stored differently in the database depending on whether it has
+ any <code>sequenced</code> or <code>random_access</code> indexes.
+ If it does, then it is treated as an ordered container
+ (<a href="#5.1">Section 5.1, "Ordered Containers"</a>) with the
+ first such index establishing the order. Otherwise, it is treated
+ as a set container (<a href="#5.2">Section 5.2, "Set and Multiset
+ Containers"</a>).</p>
+
+ <p>Note also that there is a terminology clash between ODB and Boost
+ Multi-Index. The ODB term <em>ordered container</em> translates
+ to Multi-Index terms <em>sequenced index</em> and <em>random access
+ index</em> while the ODB term <em>set container</em> translates
+ to Multi-Index terms <em>ordered index</em> and <em>hashed
+ index</em>.</p>
+
+ <p>The <code>emails</code> container from the above example is stored
+ as an ordered container. In contrast, the following <code>aliases</code>
+ container is stored as a set.</p>
+
+ <pre class="cxx">
+namespace mi = boost::multi_index;
+
+#pragma db value
+struct name
+{
+ std::string first;
+ std::string last;
+};
+
+bool operator&lt; (const name&amp;, const name&amp;);
+
+#pragma db object
+class person
+{
+ ...
+
+ typedef
+ mi::multi_index_container&lt;
+ name,
+ mi::indexed_by&lt;
+ mi::ordered_unique&lt;mi::identity&lt;name> >
+ mi::ordered_non_unique&lt;
+ mi::member&lt;name, std::string, &amp;name::first>
+ >,
+ mi::ordered_non_unique&lt;
+ mi::member&lt;name, std::string, &amp;name::last>
+ >
+ >
+ > aliases;
+
+ aliases aliases_;
+};
+ </pre>
+
+ <h2><a name="23.4">23.4 Optional Library</a></h2>
+
+ <p>The <code>optional</code> sub-profile provides persistence support for
+ the <code>boost::optional</code> container from the Boost
+ <code>optional</code> library. To enable only this profile, pass
+ <code>boost/optional</code> to the <code>--profile</code> ODB compiler
+ option.</p>
+
+ <p>In a relational database <code>boost::optional</code> is mapped to
+ a column that can have a <code>NULL</code> value. Similar to
+ <code>odb::nullable</code> (<a href="#7.3">Section 7.3, "Pointers and
+ <code>NULL</code> Value Semantics"</a>), it can be used to add the
+ <code>NULL</code> semantics to existing C++ types. For example:</p>
+
+ <pre class="cxx">
+#include &lt;boost/optional.hpp>
+
+#pragma db object
+class person
+{
+ ...
+
+ std::string first_; // TEXT NOT NULL
+ boost::optional&lt;std::string> middle_; // TEXT NULL
+ std::string last_; // TEXT NOT NULL
+};
+ </pre>
+
+ <p>Note also that similar to <code>odb::nullable</code>, when
+ this profile is used, the <code>NULL</code> values are automatically
+ enabled for data members of the <code>boost::optional</code> type.</p>
+
+ <h2><a name="23.5">23.5 Date Time Library</a></h2>
+
+ <p>The <code>date-time</code> sub-profile provides persistence support for a
+ subset of types from the Boost <code>date_time</code> library. It is
+ further subdivided into two sub-profiles, <code>gregorian</code>
+ and <code>posix_time</code>. The <code>gregorian</code> sub-profile
+ provides support for types from the <code>boost::gregorian</code>
+ namespace, while the <code>posix-time</code> sub-profile provides support
+ for types from the <code>boost::posix_time</code> namespace. To enable
+ the entire <code>date-time</code> sub-profile, pass
+ <code>boost/date-time</code> to the <code>--profile</code> ODB compiler
+ option. To enable only the <code>gregorian</code> sub-profile, pass
+ <code>boost/date-time/gregorian</code>, and to enable only the
+ <code>posix-time</code> sub-profile, pass
+ <code>boost/date-time/posix-time</code>.</p>
+
+ <p>The only type that the <code>gregorian</code> sub-profile currently
+ supports is <code>gregorian::date</code>. The types currently supported
+ by the <code>posix-time</code> sub-profile are
+ <code>posix_time::ptime</code> and
+ <code>posix_time::time_duration</code>. The manner in which these types
+ are persisted is database system dependent and is discussed in the
+ sub-sections that follow. The example below shows how
+ <code>gregorian::date</code> may be used within a persistent object.</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+ boost::gregorian::date date_of_birth_;
+};
+ </pre>
+
+ <p>Concrete exceptions that can be thrown by the <code>date-time</code>
+ sub-profile implementation are presented below.</p>
+
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace boost
+ {
+ namespace date_time
+ {
+ struct special_value: odb::boost::exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+
+ struct value_out_of_range: odb::boost::exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+ }
+ }
+}
+ </pre>
+
+ <p>You will need to include the
+ <code>&lt;odb/boost/date-time/exceptions.hxx&gt;</code> header file to
+ make these exceptions available in your application.</p>
+
+ <p>The <code>special_value</code> exception is thrown if an attempt is made
+ to store a Boost date-time special value that cannot be represented in
+ the target database. The <code>value_out_of_range</code> exception is
+ thrown if an attempt is made to store a date-time value that is out of
+ the target database range. The specific conditions under which these
+ exceptions are thrown are database system dependent and are discussed in
+ more detail in the following sub-sections.</p>
+
+ <h3><a name="23.5.1">23.5.1 MySQL Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Boost <code>date_time</code> types and the MySQL database
+ types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost <code>date_time</code> Type</th>
+ <th>MySQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>gregorian::date</code></td>
+ <td><code>DATE</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::ptime</code></td>
+ <td><code>DATETIME</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::time_duration</code></td>
+ <td><code>TIME</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
+ as a <code>NULL</code> value in a MySQL database.</p>
+
+ <p>The <code>posix-time</code> sub-profile implementation also provides
+ support for mapping <code>posix_time::ptime</code> to the
+ <code>TIMESTAMP</code> MySQL type. However, this mapping has to be
+ explicitly requested using the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), as shown in
+ the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+ #pragma db type("TIMESTAMP") not_null
+ boost::posix_time::ptime updated_;
+};
+ </pre>
+
+ <p>Starting with MySQL version 5.6.4 it is possible to store fractional
+ seconds up to microsecond precision in <code>TIME</code>,
+ <code>DATETIME</code>, and <code>TIMESTAMP</code> columns. However,
+ to enable sub-second precision, the corresponding type with the
+ desired precision has to be specified explicitly, as shown in the
+ following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+ #pragma db type("DATETIME(6)") // Microsecond precision.
+ boost::posix_time::ptime updated_;
+};
+ </pre>
+
+ <p>Alternatively, you can enable sub-second precision on the per-type
+ basis, for example:</p>
+
+ <pre class="cxx">
+#pragma db value(boost::posix_time::ptime) type("DATETIME(6)")
+
+#pragma db object
+class person
+{
+ ...
+ boost::posix_time::ptime created_; // Microsecond precision.
+ boost::posix_time::ptime updated_; // Microsecond precision.
+};
+ </pre>
+
+ <p>Some valid Boost date-time values cannot be stored in a MySQL database.
+ An attempt to persist any Boost date-time special value other than
+ <code>date_time::not_a_date_time</code> will result in the
+ <code>special_value</code> exception. An attempt to persist a Boost
+ date-time value that is out of the MySQL type range will result in
+ the <code>out_of_range</code> exception. Refer to the MySQL
+ documentation for more information on the MySQL data type ranges.</p>
+
+ <h3><a name="23.5.2">23.5.2 SQLite Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Boost <code>date_time</code> types and the SQLite database
+ types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost <code>date_time</code> Type</th>
+ <th>SQLite Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>gregorian::date</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::ptime</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::time_duration</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
+ as a <code>NULL</code> value in an SQLite database.</p>
+
+ <p>The <code>date-time</code> sub-profile implementation also provides
+ support for mapping <code>gregorian::date</code> and
+ <code>posix_time::ptime</code> to the <code>INTEGER</code> SQLite type,
+ with the integer value representing the UNIX time. Similarly, an
+ alternative mapping for <code>posix_time::time_duration</code> to the
+ <code>INTEGER</code> type represents the duration as a number of
+ seconds. These mappings have to be explicitly requested using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>), as shown in the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+ #pragma db type("INTEGER")
+ boost::gregorian::date born_;
+};
+ </pre>
+
+ <!--
+
+ <p>The Boost UNIX time interface does not support 64 bit time arithmetic.
+ As a result, the UNIX time representations of <code>gregorian::date</code>
+ and <code>posix_time::ptime</code> are restricted to the 32 bit range.
+ The minimum and maximum date representable by
+ <code>gregorian::date</code> is 1901-12-14 and 2038-01-19 respectively,
+ while the minimum and maximum date-time representable by
+ <code>posix_time::ptime</code> is 1901-12-13&nbsp;20:45:54 GMT and
+ 2038-01-19&nbsp;03:14:07&nbsp;GMT respectively. Persisting and loading
+ of values outside of these ranges will result in undefined behavior.</p>
+
+ -->
+
+ <p>Some valid Boost date-time values cannot be stored in an SQLite database.
+ An attempt to persist any Boost date-time special value other than
+ <code>date_time::not_a_date_time</code> will result in the
+ <code>special_value</code> exception. An attempt to persist a negative
+ <code>posix_time::time_duration</code> value as SQLite <code>TEXT</code>
+ will result in the <code>out_of_range</code> exception.</p>
+
+
+ <h3><a name="23.5.3">23.5.3 PostgreSQL Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Boost <code>date_time</code> types and the PostgreSQL database
+ types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost <code>date_time</code> Type</th>
+ <th>PostgreSQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>gregorian::date</code></td>
+ <td><code>DATE</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::ptime</code></td>
+ <td><code>TIMESTAMP</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::time_duration</code></td>
+ <td><code>TIME</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
+ as a <code>NULL</code> value in a PostgreSQL database.
+ <code>posix_time::ptime</code> values representing the special values
+ <code>date_time::pos_infin</code> and <code>date_time::neg_infin</code>
+ are stored as the special PostgreSQL TIMESTAMP values
+ <code>infinity</code> and <code>-infinity</code>, respectively.</p>
+
+ <p>Some valid Boost date-time values cannot be stored in a PostgreSQL
+ database. The PostgreSQL TIME type represents a clock time, and can
+ therefore only store positive durations with a total length of time less
+ than 24 hours. An attempt to persist a
+ <code>posix_time::time_duration</code> value outside of this range will
+ result in the <code>value_out_of_range</code> exception. An attempt to
+ persist a <code>posix_time::time_duration</code> value representing any
+ special value other than <code>date_time::not_a_date_time</code> will
+ result in the <code>special_value</code> exception.</p>
+
+
+ <h3><a name="23.5.4">23.5.4 Oracle Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Boost <code>date_time</code> types and the Oracle database
+ types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost <code>date_time</code> Type</th>
+ <th>Oracle Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>gregorian::date</code></td>
+ <td><code>DATE</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::ptime</code></td>
+ <td><code>TIMESTAMP</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::time_duration</code></td>
+ <td><code>INTERVAL DAY TO SECOND</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
+ as a <code>NULL</code> value in an Oracle database.</p>
+
+ <p>The <code>date-time</code> sub-profile implementation also provides
+ support for mapping <code>posix_time::ptime</code> to the
+ <code>DATE</code> Oracle type with fractional seconds that may be
+ stored in a <code>ptime</code> instance being ignored. This
+ alternative mapping has to be explicitly requested using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>), as shown in the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+ #pragma db type("DATE")
+ boost::posix_time::ptime updated_;
+};
+ </pre>
+
+ <p>Some valid Boost date-time values cannot be stored in an Oracle database.
+ An attempt to persist a <code>gregorian::date</code>,
+ <code>posix_time::ptime</code>, or
+ <code>posix_time::time_duration</code> value representing any special
+ value other than <code>date_time::not_a_date_time</code> will result in
+ the <code>special_value</code> exception.</p>
+
+
+ <h3><a name="23.5.5">23.5.5 SQL Server Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Boost <code>date_time</code> types and the SQL Server database
+ types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost <code>date_time</code> Type</th>
+ <th>SQL Server Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>gregorian::date</code></td>
+ <td><code>DATE</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::ptime</code></td>
+ <td><code>DATETIME2</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>posix_time::time_duration</code></td>
+ <td><code>TIME</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>The Boost special value <code>date_time::not_a_date_time</code> is stored
+ as a <code>NULL</code> value in an SQL Server database.</p>
+
+ <p>Note that the <code>DATE</code>, <code>TIME</code>, and
+ <code>DATETIME2</code> types are only available in SQL Server 2008 and
+ later. SQL Server 2005 only supports the <code>DATETIME</code> and
+ <code>SMALLDATETIME</code> date-time types. The new types are
+ also unavailable when connecting to an SQL Server 2008 or
+ later with the SQL Server 2005 Native Client ODBC driver.</p>
+
+ <p>The <code>date-time</code> sub-profile implementation provides
+ support for mapping <code>posix_time::ptime</code> to the
+ <code>DATETIME</code> and <code>SMALLDATETIME</code> types,
+ however, this mapping has to be explicitly requested using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>), as shown in the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+ #pragma db type("DATETIME")
+ boost::posix_time::ptime updated_;
+};
+ </pre>
+
+ <p>Some valid Boost date-time values cannot be stored in an SQL Server
+ database. An attempt to persist a <code>gregorian::date</code>,
+ <code>posix_time::ptime</code>, or <code>posix_time::time_duration</code>
+ value representing any special value other than
+ <code>date_time::not_a_date_time</code> will result in the
+ <code>special_value</code> exception. The range of the <code>TIME</code>
+ type in SQL server is from <code>00:00:00.0000000</code> to
+ <code>23:59:59.9999999</code>. An attempt to persist a
+ <code>posix_time::time_duration</code> value out of this range will
+ result in the <code>value_out_of_range</code> exception.</p>
+
+ <h2><a name="23.6">23.6 Uuid Library</a></h2>
+
+ <p>The <code>uuid</code> sub-profile provides persistence support for the
+ <code>uuid</code> type from the Boost <code>uuid</code> library. To
+ enable only this profile, pass <code>boost/uuid</code> to the
+ <code>--profile</code> ODB compiler option.</p>
+
+ <p>The manner in which these types are persisted is database system
+ dependent and is discussed in the sub-sections that follow. By
+ default a data member of the <code>uuid</code> type is mapped to a
+ database column with <code>NULL</code> enabled and nil <code>uuid</code>
+ instances are stored as a <code>NULL</code> value. However, you can
+ change this behavior by declaring the data member <code>NOT NULL</code>
+ with the <code>not_null</code> pragma (<a href="#14.4.6">Section
+ 14.4.6, "<code>null</code>/<code>not_null</code>"</a>). In this
+ case, or if the data member is an object id, the implementation
+ will store nil <code>uuid</code> instances as zero UUID values
+ (<code>{00000000-0000-0000-0000-000000000000}</code>). For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ boost::uuids::uuid x_; // Nil values stored as NULL.
+
+ #pragma db not_null
+ boost::uuids::uuid y_; // Nil values stored as zero.
+};
+ </pre>
+
+ <h3><a name="23.6.1">23.6.1 MySQL Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the Boost
+ <code>uuid</code> type and the MySQL database type.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost Type</th>
+ <th>MySQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>boost::uuids::uuid</code></td>
+ <td><code>BINARY(16)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <h3><a name="23.6.2">23.6.2 SQLite Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the Boost
+ <code>uuid</code> type and the SQLite database type.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost Type</th>
+ <th>SQLite Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>boost::uuids::uuid</code></td>
+ <td><code>BLOB</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <h3><a name="23.6.3">23.6.3 PostgreSQL Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the Boost
+ <code>uuid</code> type and the PostgreSQL database type.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost Type</th>
+ <th>PostgreSQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>boost::uuids::uuid</code></td>
+ <td><code>UUID</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <h3><a name="23.6.4">23.6.4 Oracle Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the Boost
+ <code>uuid</code> type and the Oracle database type.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost Type</th>
+ <th>Oracle Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>boost::uuids::uuid</code></td>
+ <td><code>RAW(16)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <h3><a name="23.6.5">23.6.5 SQL Server Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the Boost
+ <code>uuid</code> type and the SQL Server database type.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Boost Type</th>
+ <th>SQL Server Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>boost::uuids::uuid</code></td>
+ <td><code>UNIQUEIDENTIFIER</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+
+ <!-- CHAPTER -->
+
+
+ <hr class="page-break"/>
+ <h1><a name="24">24 Qt Profile</a></h1>
+
+ <p>The ODB profile implementation for Qt is provided by the
+ <code>libodb-qt</code> library. Both Qt4 and Qt5 as well
+ as C++98/03 and C++11 are supported.</p>
+
+ <p>The Qt profile consists of multiple sub-profiles
+ corresponding to the common type groups within Qt. Currently,
+ only types from the <code>QtCore</code> module are supported. To
+ enable all the available Qt sub-profiles, pass <code>qt</code> as the
+ profile name to the <code>--profile</code> ODB compiler option.
+ Alternatively, you can enable only specific sub-profiles by passing
+ individual sub-profile names to <code>--profile</code>. The following
+ sections in this chapter discuss each Qt sub-profile in detail. The
+ <code>qt</code> example in the <code>odb-examples</code>
+ package shows how to enable and use the Qt profile.</p>
+
+ <p>Some sub-profiles may throw exceptions to indicate error conditions,
+ such as the inability to store a specific value in a particular database
+ system. All such exceptions derive from the
+ <code>odb::qt::exception</code> class which in turn derives from
+ the root of the ODB exception hierarchy, class <code>odb::exception</code>
+ (<a href="#3.14">Section 3.14, "ODB Exceptions"</a>). The
+ <code>odb::qt::exception</code> class is defined in the
+ <code>&lt;odb/qt/exception.hxx></code> header file and has the
+ same interface as <code>odb::exception</code>. Concrete exceptions
+ that can be thrown by the Qt sub-profiles are described in the
+ following sections.</p>
+
+ <h2><a name="24.1">24.1 Basic Types Library</a></h2>
+
+ <p>The <code>basic</code> sub-profile provides persistence support for basic
+ types defined by Qt. To enable only this profile, pass
+ <code>qt/basic</code> to the <code>--profile</code> ODB compiler
+ option.</p>
+
+ <p>The currently supported basic types are <code>QString</code>,
+ <code>QByteArray</code>, and <code>QUuid</code>. The manner in
+ which these types are persisted is database system dependent
+ and is discussed in the sub-sections that follow. The example
+ below shows how <code>QString</code> may be used within a
+ persistent object.</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+ QString name_;
+};
+ </pre>
+
+ <p>By default a data member of the <code>QUuid</code> type is mapped to a
+ database column with <code>NULL</code> enabled and null <code>QUuid</code>
+ instances are stored as a <code>NULL</code> value. However, you can
+ change this behavior by declaring the data member <code>NOT NULL</code>
+ with the <code>not_null</code> pragma (<a href="#14.4.6">Section
+ 14.4.6, "<code>null</code>/<code>not_null</code>"</a>). In this
+ case, or if the data member is an object id, the implementation
+ will store null <code>QUuid</code> instances as zero UUID values
+ (<code>{00000000-0000-0000-0000-000000000000}</code>). For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class object
+{
+ ...
+
+ QUuid x_; // Null values stored as NULL.
+
+ #pragma db not_null
+ QUuid y_; // Null values stored as zero.
+};
+ </pre>
+
+ <h3><a name="24.1.1">24.1.1 MySQL Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported basic Qt types and the MySQL database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Type</th>
+ <th>MySQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QString</code></td>
+ <td><code>TEXT/VARCHAR(128)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QByteArray</code></td>
+ <td><code>BLOB</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QUuid</code></td>
+ <td><code>BINARY(16)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QString</code> and <code>QByteArray</code>
+ types are stored as a <code>NULL</code> value if their
+ <code>isNull()</code> member function returns <code>true</code>.</p>
+
+ <p>Note also that the <code>QString</code> type is mapped
+ differently depending on whether a member of this type
+ is an object id or not. If the member is an object id,
+ then for this member <code>QString</code> is mapped
+ to the <code>VARCHAR(128)</code> MySQL type. Otherwise,
+ it is mapped to <code>TEXT</code>.</p>
+
+ <p>The <code>basic</code> sub-profile also provides support
+ for mapping <code>QString</code> to the <code>CHAR</code>,
+ <code>NCHAR</code>, and <code>NVARCHAR</code> MySQL types.
+ However, these alternative mappings have to be explicitly
+ requested using the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "type"</a>), as shown in
+ the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+
+ #pragma db type("CHAR(2)") not_null
+ QString licenseState_;
+};
+ </pre>
+
+
+ <h3><a name="24.1.2">24.1.2 SQLite Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported basic Qt types and the SQLite database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Type</th>
+ <th>SQLite Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QString</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QByteArray</code></td>
+ <td><code>BLOB</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QUuid</code></td>
+ <td><code>BLOB</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QString</code> and <code>QByteArray</code> types
+ are stored as a <code>NULL</code> value if their <code>isNull()</code>
+ member function returns <code>true</code>.</p>
+
+ <h3><a name="24.1.3">24.1.3 PostgreSQL Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported basic Qt types and the PostgreSQL database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Type</th>
+ <th>PostgreSQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QString</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QByteArray</code></td>
+ <td><code>BYTEA</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QUuid</code></td>
+ <td><code>UUID</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QString</code> and <code>QByteArray</code> types
+ are stored as a <code>NULL</code> value if their <code>isNull()</code>
+ member function returns <code>true</code>.</p>
+
+ <p>The <code>basic</code> sub-profile also provides support
+ for mapping <code>QString</code> to the <code>CHAR</code>
+ and <code>VARCHAR</code> PostgreSQL types.
+ However, these alternative mappings have to be explicitly
+ requested using the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "type"</a>), as shown in
+ the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+
+ #pragma db type("CHAR(2)") not_null
+ QString licenseState_;
+};
+ </pre>
+
+ <h3><a name="24.1.4">24.1.4 Oracle Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported basic Qt types and the Oracle database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Type</th>
+ <th>Oracle Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QString</code></td>
+ <td><code>VARCHAR2(512)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QByteArray</code></td>
+ <td><code>BLOB</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QUuid</code></td>
+ <td><code>RAW(16)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QString</code> and <code>QByteArray</code> types
+ are stored as a <code>NULL</code> value if their <code>isNull()</code>
+ member function returns <code>true</code>.</p>
+
+ <p>The <code>basic</code> sub-profile also provides support
+ for mapping <code>QString</code> to the <code>CHAR</code>,
+ <code>NCHAR</code>, <code>NVARCHAR</code>, <code>CLOB</code>, and
+ <code>NCLOB</code> Oracle types, and for mapping <code>QByteArray</code>
+ to the <code>RAW</code> Oracle type. However, these alternative
+ mappings have to be explicitly requested using the <code>db&nbsp;type</code>
+ pragma (<a href="#14.4.3">Section 14.4.3, "type"</a>), as shown in the
+ following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+
+ #pragma db type("CLOB") not_null
+ QString firstName_;
+
+ #pragma db type("RAW(16)") null
+ QByteArray uuid_;
+};
+ </pre>
+
+ <h3><a name="24.1.5">24.1.5 SQL Server Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported basic Qt types and the SQL Server database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Type</th>
+ <th>SQL Server Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QString</code></td>
+ <td><code>VARCHAR(512)/VARCHAR(256)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QByteArray</code></td>
+ <td><code>VARBINARY(max)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QUuid</code></td>
+ <td><code>UNIQUEIDENTIFIER</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QString</code> and <code>QByteArray</code> types
+ are stored as a <code>NULL</code> value if their <code>isNull()</code>
+ member function returns <code>true</code>.</p>
+
+ <p>Note also that the <code>QString</code> type is mapped
+ differently depending on whether a member of this type
+ is an object id or not. If the member is an object id,
+ then for this member <code>QString</code> is mapped
+ to the <code>VARCHAR(256)</code> SQL Server type. Otherwise,
+ it is mapped to <code>VARCHAR(512)</code>.</p>
+
+ <p>The <code>basic</code> sub-profile also provides support
+ for mapping <code>QString</code> to the <code>CHAR</code>,
+ <code>NCHAR</code>, <code>NVARCHAR</code>, <code>TEXT</code>, and
+ <code>NTEXT</code> SQL Server types, and for mapping
+ <code>QByteArray</code> to the <code>BINARY</code> and
+ <code>IMAGE</code> SQL Server types. However, these alternative
+ mappings have to be explicitly requested using the <code>db&nbsp;type</code>
+ pragma (<a href="#14.4.3">Section 14.4.3, "type"</a>), as shown in the
+ following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+
+ #pragma db type("NVARCHAR(256)") not_null
+ QString firstName_;
+
+ #pragma db type("BINARY(16)") null
+ QByteArray uuid_;
+};
+ </pre>
+
+ <h2><a name="24.2">24.2 Smart Pointers Library</a></h2>
+
+ <p>The <code>smart-ptr</code> sub-profile provides persistence support the
+ Qt smart pointers. To enable only this profile, pass
+ <code>qt/smart-ptr</code> to the <code>--profile</code> ODB compiler
+ option.</p>
+
+ <p>The currently supported smart pointers are
+ <code>QSharedPointer</code> and <code>QWeakPointer</code>.
+ For more information on using smart pointers as pointers to objects
+ and views, refer to <a href="#3.3">Section 3.3, "Object and View
+ Pointers"</a> and <a href="#6">Chapter 6, "Relationships"</a>. For
+ more information on using smart pointers as pointers to values, refer
+ to <a href="#7.3">Section 7.3, "Pointers and <code>NULL</code> Value
+ Semantics"</a>. When used as a pointer to a value, only
+ <code>QSharedPointer</code> is supported. For example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+
+ #pragma db null
+ QSharedPointer&lt;QString> middle_name_;
+};
+ </pre>
+
+ <p>To provide finer grained control over object relationship loading,
+ the <code>smart-ptr</code> sub-profile also provides the lazy
+ counterparts for the above pointers: <code>QLazySharedPointer</code>
+ and <code>QLazyWeakPointer</code>. You will need to include the
+ <code>&lt;odb/qt/lazy-ptr.hxx></code> header file to make the lazy
+ variants available in your application. For a description of the lazy
+ pointer interface and semantics refer to <a href="#6.4">Section 6.4,
+ "Lazy Pointers"</a>. The following example shows how we can use these
+ smart pointers to establish a relationship between persistent objects.</p>
+
+ <pre class="cxx">
+class Employee;
+
+#pragma db object
+class Position
+{
+ ...
+
+ #pragma db inverse(position_)
+ QLazyWeakPointer&lt;Employee> employee_;
+};
+
+#pragma db object
+class Employee
+{
+ ...
+
+ #pragma db not_null
+ QSharedPointer&lt;Position> position_;
+};
+ </pre>
+
+ <p>Besides providing persistence support for the above smart pointers,
+ the <code>smart-ptr</code> sub-profile also changes the default
+ pointer (<a href="#3.3">Section 3.3, "Object and View Pointers"</a>)
+ to <code>QSharedPointer</code>. In particular, this means that
+ database functions that return dynamically allocated objects and views
+ will return them as <code>QSharedPointer</code> pointers. To override
+ this behavior, add the <code>--default-pointer</code> option specifying
+ the alternative pointer type after the <code>--profile</code> option.</p>
+
+ <h2><a name="24.3">24.3 Containers Library</a></h2>
+
+ <p>The <code>containers</code> sub-profile provides persistence support for
+ Qt containers. To enable only this profile, pass
+ <code>qt/containers</code> to the <code>--profile</code> ODB compiler
+ option.</p>
+
+ <p>The currently supported ordered containers are <code>QVector</code>,
+ <code>QList</code>, and <code>QLinkedList</code>. Supported map
+ containers are <code>QMap</code>, <code>QMultiMap</code>,
+ <code>QHash</code>, and <code>QMultiHash</code>. The supported set
+ container is <code>QSet</code>. For more information on using
+ containers with ODB, refer to <a href="#5">Chapter 5, "Containers"</a>.
+ The following example shows how the <code>QSet</code> container may
+ be used within a persistent object.</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+ QSet&lt;QString> emails_;
+};
+ </pre>
+
+ <p>The <code>containers</code> sub-profile also provide a change-tracking
+ equivalent for <code>QList</code> (<a href="#24.3.1">Section 24.3.1,
+ "Change-Tracking <code>QList</code>"</a>) with support for other Qt
+ container equivalents planned for future releases. For general information
+ on change-tracking containers refer to <a href="#5.4">Section 5.4,
+ "Change-Tracking Containers"</a>.</p>
+
+ <h3><a name="24.3.1">24.3.1 Change-Tracking <code>QList</code></a></h3>
+
+ <p>Class template <code>QOdbList</code>, defined in
+ <code>&lt;odb/qt/list.hxx></code>, is a change-tracking
+ equivalent for <code>QList</code>. It
+ is implemented in terms of <code>QList</code> and is
+ implicit-convertible to and implicit-constructible from
+ <code>const QList&amp;</code>. In particular, this
+ means that we can use <code>QOdbList</code> instance
+ anywhere <code>const QList&amp;</code> is
+ expected. In addition, <code>QOdbList</code> constant
+ iterator (<code>const_iterator</code>) is the same type as
+ that of <code>QList</code>.</p>
+
+ <p><code>QOdbList</code> incurs 2-bit per element overhead
+ in order to store the change state. It cannot
+ be stored unordered in the database (<a href="#14.4.19">Section
+ 14.4.19 "<code>unordered</code>"</a>) but can be used as an inverse
+ side of a relationship (<a href="#6.2">6.2 "Bidirectional
+ Relationships"</a>). In this case, no change tracking is performed
+ since no state for such a container is stored in the database.</p>
+
+ <p>The number of database operations required to update the state
+ of <code>QOdbList</code> corresponds well to the complexity
+ of <code>QList</code> functions, except for
+ <code>prepend()</code>/<code>push_front()</code>. In particular, adding
+ or removing an element from the back of the list (for example,
+ with <code>append()</code>/<code>push_back()</code> and
+ <code>removeLast()</code>/<code>pop_back()</code>),
+ requires only a single database statement execution. In contrast,
+ inserting or erasing an element at the beginning or in the middle
+ of the list will require a database statement for every element that
+ follows it.</p>
+
+ <p><code>QOdbList</code> replicates most of the <code>QList</code>
+ interface as defined in both Qt4 and Qt5 and includes support for
+ C++11. However, functions and operators that provide direct write
+ access to the elements had to be altered or disabled in order to
+ support change tracking. Additional functions used to interface with
+ <code>QList</code> and to control the change tracking state
+ were also added. The following listing summarizes the differences
+ between the <code>QOdbList</code> and <code>QList</code>
+ interfaces. Any <code>QList</code> function or operator
+ not mentioned in this listing has exactly the same signature
+ and semantics in <code>QOdbList</code>. Functions and
+ operators that were disabled are shown as commented out and
+ are followed by functions/operators that replace them.</p>
+
+ <pre class="cxx">
+template &lt;typename T>
+class QOdbList
+{
+ ...
+
+ // Element access.
+ //
+
+ //T&amp; operator[] (int);
+ T&amp; modify (int);
+
+ //T&amp; first();
+ T&amp; modifyFirst();
+
+ //T&amp; last();
+ T&amp; modifyLast();
+
+ //T&amp; front();
+ T&amp; modify_front();
+
+ //T&amp; back();
+ T&amp; modify_back();
+
+ // Iterators.
+ //
+ typedef typename QList&lt;T>::const_iterator const_iterator;
+
+ class iterator
+ {
+ ...
+
+ // Element Access.
+ //
+
+ //reference operator* () const;
+ const_reference operator* () const;
+ reference modify () const;
+
+ //pointer operator-> () const;
+ const_pointer operator-> () const;
+
+ //reference operator[] (difference_type);
+ const_reference operator[] (difference_type);
+ reference modify (difference_type) const;
+
+ // Interfacing with QList::iterator.
+ //
+ typename QList&lt;T>::iterator base () const;
+ };
+
+ // Return QList iterators. The begin() functions mark all
+ // the elements as modified.
+ //
+ typename QList&lt;T>::iterator mbegin ();
+ typename QList&lt;T>::iterator modifyBegin ();
+ typename QList&lt;T>::iterator mend ();
+ typename QList&lt;T>::iterator modifyEnd ();
+
+ // Interfacing with QList.
+ //
+ QOdbList (const QList&lt;T>&amp;);
+ QOdbList (QList&lt;T>&amp;&amp;); // C++11 only.
+
+ QOdbList&amp; operator= (const QList&lt;T>&amp;);
+ QOdbList&amp; operator= (QList&lt;T>&amp;&amp;);
+
+ operator const QList&lt;T>&amp; () const;
+ QList&lt;T>&amp; base ();
+ const QList&lt;T>&amp; base () const;
+
+ // Change tracking.
+ //
+ bool _tracking () const;
+ void _start () const;
+ void _stop () const;
+ void _arm (transaction&amp;) const;
+};
+ </pre>
+
+ <p>The following example highlights some of the differences between
+ the two interfaces. <code>QList</code> versions are commented
+ out.</p>
+
+ <pre class="cxx">
+#include &lt;QtCore/QList>
+#include &lt;odb/qt/list.hxx>
+
+void f (const QList&lt;int>&amp;);
+
+QOdbList&lt;int> l ({1, 2, 3});
+
+f (l); // Ok, implicit conversion.
+
+if (l[1] == 2) // Ok, const access.
+ //l[1]++;
+ l.modify (1)++;
+
+//l.last () = 4;
+l.modifyLast () = 4;
+
+for (auto i (l.begin ()); i != l.end (); ++i)
+{
+ if (*i != 0) // Ok, const access.
+ //*i += 10;
+ i.modify () += 10;
+}
+
+qSort (l.modifyBegin (), l.modifyEnd ());
+ </pre>
+
+ <p>Note also the subtle difference between copy/move construction
+ and copy/move assignment of <code>QOdbList</code> instances.
+ While copy/move constructor will copy/move both the elements as
+ well as their change state, in contrast, assignment is tracked
+ as any other change to the vector content.</p>
+
+ <p>The <code>QListIterator</code> and <code>QMutableListIterator</code>
+ equivalents are also provided. These are <code>QOdbListIterator</code>
+ and <code>QMutableOdbListIterator</code> and are defined in
+ <code>&lt;odb/qt/list-iterator.hxx></code> and
+ <code>&lt;odb/qt/mutable-list-iterator.hxx></code>, respectively.</p>
+
+ <p><code>QOdbListIterator</code> has exactly the same interface and
+ semantics as <code>QListIterator</code>. In fact, we can use
+ <code>QListIterator</code> to iterate over a <code>QOdbList</code>
+ instance.</p>
+
+ <p><code>QMutableOdbListIterator</code> also has exactly the same
+ interface as <code>QMutableListIterator</code>. Note, however,
+ that any element that such an iterator passes over with the
+ call to <code>next()</code> is marked as modified.</p>
+
+ <h2><a name="24.4">24.4 Date Time Library</a></h2>
+
+ <p>The <code>date-time</code> sub-profile provides persistence support for
+ the Qt date-time types. To enable only this profile, pass
+ <code>qt/date-time</code> to the <code>--profile</code> ODB compiler
+ option.</p>
+
+ <p>The currently supported date-time types are <code>QDate</code>,
+ <code>QTime</code>, and <code>QDateTime</code>. The manner in which
+ these types are persisted is database system dependent and is
+ discussed in the sub-sections that follow. The example below shows how
+ <code>QDate</code> may be used within a persistent object.</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+ QDate dateOfBirth_;
+};
+ </pre>
+
+ <p>The single concrete exception that can be thrown by the
+ <code>date-time</code> sub-profile implementation is presented below.</p>
+
+
+ <pre class="cxx">
+namespace odb
+{
+ namespace qt
+ {
+ namespace date_time
+ {
+ struct value_out_of_range: odb::qt::exception
+ {
+ virtual const char*
+ what () const throw ();
+ };
+ }
+ }
+}
+ </pre>
+
+ <p>You will need to include the
+ <code>&lt;odb/qt/date-time/exceptions.hxx&gt;</code> header file to
+ make this exception available in your application.</p>
+
+ <p>The <code>value_out_of_range</code> exception is thrown if an attempt
+ is made to store a date-time value that is out of the target database
+ range. The specific conditions under which it is thrown is database
+ system dependent and is discussed in more detail in the
+ following sub-sections.</p>
+
+ <h3><a name="24.4.1">24.4.1 MySQL Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Qt date-time types and the MySQL database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Date Time Type</th>
+ <th>MySQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QDate</code></td>
+ <td><code>DATE</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QTime</code></td>
+ <td><code>TIME</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QDateTime</code></td>
+ <td><code>DATETIME</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
+ <code>QDateTime</code> types are stored as a <code>NULL</code> value
+ if their <code>isNull()</code> member function returns true.</p>
+
+ <p>The <code>date-time</code> sub-profile implementation also provides
+ support for mapping <code>QDateTime</code> to the <code>TIMESTAMP</code>
+ MySQL type. However, this mapping has to be explicitly requested using
+ the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), as shown in
+ the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+ #pragma db type("TIMESTAMP") not_null
+ QDateTime updated_;
+};
+ </pre>
+
+ <p>Starting with MySQL version 5.6.4 it is possible to store fractional
+ seconds up to microsecond precision in <code>TIME</code>,
+ <code>DATETIME</code>, and <code>TIMESTAMP</code> columns. However,
+ to enable sub-second precision, the corresponding type with the
+ desired precision has to be specified explicitly, as shown in the
+ following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+ #pragma db type("DATETIME(3)") // Millisecond precision.
+ QDateTime updated_;
+};
+ </pre>
+
+ <p>Alternatively, you can enable sub-second precision on the per-type
+ basis, for example:</p>
+
+ <pre class="cxx">
+#pragma db value(QDateTime) type("DATETIME(3)")
+
+#pragma db object
+class Person
+{
+ ...
+ QDateTime created_; // Millisecond precision.
+ QDateTime updated_; // Millisecond precision.
+};
+ </pre>
+
+ <p>Some valid Qt date-time values cannot be stored in a MySQL database. An
+ attempt to persist a Qt date-time value that is out of the MySQL type
+ range will result in the <code>out_of_range</code> exception. Refer to
+ the MySQL documentation for more information on the MySQL data type
+ ranges.</p>
+
+ <h3><a name="24.4.2">24.4.2 SQLite Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Qt date-time types and the SQLite database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Date Time Type</th>
+ <th>SQLite Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QDate</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QTime</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QDateTime</code></td>
+ <td><code>TEXT</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
+ <code>QDateTime</code> types are stored as a <code>NULL</code> value
+ if their <code>isNull()</code> member function returns true.</p>
+
+ <p>The <code>date-time</code> sub-profile implementation also provides
+ support for mapping <code>QDate</code> and <code>QDateTime</code> to the
+ SQLite <code>INTEGER</code> type, with the integer value representing the
+ UNIX time. Similarly, an alternative mapping for <code>QTime</code> to
+ the <code>INTEGER</code> type represents a clock time as the number of
+ seconds since midnight. These mappings have to be explicitly requested
+ using the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), as shown
+ in the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class Person
+{
+ ...
+ #pragma db type("INTEGER")
+ QDate born_;
+};
+ </pre>
+
+ <p>Some valid Qt date-time values cannot be stored in an SQLite database.
+ An attempt to persist any Qt date-time value representing a negative UNIX
+ time (any point in time prior to the 1970-01-01&nbsp;00:00:00 UNIX time
+ epoch) as an SQLite <code>INTEGER</code> will result in the
+ <code>out_of_range</code> exception.</p>
+
+ <h3><a name="24.4.3">24.4.3 PostgreSQL Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Qt date-time types and the PostgreSQL database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Date Time Type</th>
+ <th>PostgreSQL Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QDate</code></td>
+ <td><code>DATE</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QTime</code></td>
+ <td><code>TIME</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QDateTime</code></td>
+ <td><code>TIMESTAMP</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
+ <code>QDateTime</code> types are stored as a <code>NULL</code> value
+ if their <code>isNull()</code> member function returns true.</p>
+
+ <h3><a name="24.4.4">24.4.4 Oracle Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Qt date-time types and the Oracle database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Date Time Type</th>
+ <th>Oracle Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QDate</code></td>
+ <td><code>DATE</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QTime</code></td>
+ <td><code>INTERVAL DAY(0) TO SECOND(3)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QDateTime</code></td>
+ <td><code>TIMESTAMP(3)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
+ <code>QDateTime</code> types are stored as a <code>NULL</code> value
+ if their <code>isNull()</code> member function returns true.</p>
+
+ <p>The <code>date-time</code> sub-profile implementation also provides
+ support for mapping <code>QDateTime</code> to the
+ <code>DATE</code> Oracle type with fractional seconds that may be
+ stored in a <code>QDateTime</code> instance being ignored. This
+ alternative mapping has to be explicitly requested using the
+ <code>db&nbsp;type</code> pragma (<a href="#14.4.3">Section 14.4.3,
+ "<code>type</code>"</a>), as shown in the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+ #pragma db type("DATE")
+ QDateTime updated_;
+};
+ </pre>
+
+ <h3><a name="24.4.5">24.4.5 SQL Server Database Type Mapping</a></h3>
+
+ <p>The following table summarizes the default mapping between the currently
+ supported Qt date-time types and the SQL Server database types.</p>
+
+ <!-- border="1" is necessary for html2ps -->
+ <table id="mapping" border="1">
+ <tr>
+ <th>Qt Date Time Type</th>
+ <th>SQL Server Type</th>
+ <th>Default <code>NULL</code> Semantics</th>
+ </tr>
+
+ <tr>
+ <td><code>QDate</code></td>
+ <td><code>DATE</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QTime</code></td>
+ <td><code>TIME(3)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+
+ <tr>
+ <td><code>QDateTime</code></td>
+ <td><code>DATETIME2(3)</code></td>
+ <td><code>NULL</code></td>
+ </tr>
+ </table>
+
+ <p>Instances of the <code>QDate</code>, <code>QTime</code>, and
+ <code>QDateTime</code> types are stored as a <code>NULL</code> value
+ if their <code>isNull()</code> member function returns true.</p>
+
+ <p>Note that the <code>DATE</code>, <code>TIME</code>, and
+ <code>DATETIME2</code> types are only available in SQL Server 2008 and
+ later. SQL Server 2005 only supports the <code>DATETIME</code> and
+ <code>SMALLDATETIME</code> date-time types. The new types are
+ also unavailable when connecting to an SQL Server 2008 or
+ later with the SQL Server 2005 Native Client ODBC driver.</p>
+
+ <p>The <code>date-time</code> sub-profile implementation provides
+ support for mapping <code>QDateTime</code> to the <code>DATETIME</code>
+ and <code>SMALLDATETIME</code> types, however, this mapping has to
+ be explicitly requested using the <code>db&nbsp;type</code> pragma
+ (<a href="#14.4.3">Section 14.4.3, "<code>type</code>"</a>), as
+ shown in the following example:</p>
+
+ <pre class="cxx">
+#pragma db object
+class person
+{
+ ...
+ #pragma db type("DATETIME")
+ QDateTime updated_;
+};
+ </pre>
+
+ </div>
+</div>
+
+</body>
+</html>
diff --git a/doc/odb-arch.png b/odb/doc/odb-arch.png
index 511b198..511b198 100644
--- a/doc/odb-arch.png
+++ b/odb/doc/odb-arch.png
Binary files differ
diff --git a/doc/odb-arch.svg b/odb/doc/odb-arch.svg
index 368c223..368c223 100644
--- a/doc/odb-arch.svg
+++ b/odb/doc/odb-arch.svg
diff --git a/doc/odb-epilogue.1 b/odb/doc/odb-epilogue.1
index e331b23..e331b23 100644
--- a/doc/odb-epilogue.1
+++ b/odb/doc/odb-epilogue.1
diff --git a/doc/odb-epilogue.xhtml b/odb/doc/odb-epilogue.xhtml
index 9eba558..9eba558 100644
--- a/doc/odb-epilogue.xhtml
+++ b/odb/doc/odb-epilogue.xhtml
diff --git a/doc/odb-flow.png b/odb/doc/odb-flow.png
index 0063317..0063317 100644
--- a/doc/odb-flow.png
+++ b/odb/doc/odb-flow.png
Binary files differ
diff --git a/doc/odb-flow.svg b/odb/doc/odb-flow.svg
index 292a121..292a121 100644
--- a/doc/odb-flow.svg
+++ b/odb/doc/odb-flow.svg
diff --git a/doc/odb-prologue.1 b/odb/doc/odb-prologue.1
index 24e83f4..24e83f4 100644
--- a/doc/odb-prologue.1
+++ b/odb/doc/odb-prologue.1
diff --git a/doc/odb-prologue.xhtml b/odb/doc/odb-prologue.xhtml
index b8cc694..b8cc694 100644
--- a/doc/odb-prologue.xhtml
+++ b/odb/doc/odb-prologue.xhtml
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="&#169; 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&lt;object></b></code>. The ODB runtime uses
+ object and view pointers to return, and, in case of objects, pass and
+ cache dynamically allocated instances of object and view types.
+
+ <p>Except for the raw pointer and the standard smart pointers defined in
+ the <code><b>&lt;memory></b></code> header file, you are expected to
+ include the definition of the default pointer at the beginning of the
+ generated header file. There are two common ways to achieve this: you can
+ either include the necessary header in the file being compiled or you can
+ use the <code><b>--hxx-prologue</b></code> option to add the necessary
+ <code><b>#include</b></code> directive to the generated code.</p></dd>
+
+ <dt><code><b>--session-type</b></code> <code><i>type</i></code></dt>
+ <dd>Use <code><i>type</i></code> as the alternative session type instead
+ of the default <code><b>odb::session</b></code>. This option can be used
+ to specify a custom session implementation to be use by the persistent
+ classes. Note that you will also need to include the definition of the
+ custom session type into the generated header file. This is normally
+ achieved with the <code><b>--hxx-prologue*</b></code> options.</dd>
+
+ <dt><code><b>--profile</b></code>|<code><b>-p</b></code> <code><i>name</i></code></dt>
+ <dd>Specify a profile that should be used during compilation. A profile is
+ an options file. The ODB compiler first looks for a database-specific
+ version with the name constructed by appending the
+ <code><b>-</b></code><code><i>database</i></code><code><b>.options</b></code>
+ suffix to <code><i>name</i></code>, where <code><i>database</i></code> is
+ the database name as specified with the <code><b>--database</b></code>
+ option. If this file is not found, then the ODB compiler looks for a
+ database-independant version with the name constructed by appending just
+ the <code><b>.options</b></code> suffix.
+
+ <p>The profile options files are searched for in the same set of
+ directories as C++ headers included with the <code><b>#include
+ &lt;...></b></code> directive (built-in paths plus those specified with
+ the <code><b>-I</b></code> options). The options file is first searched
+ for in the directory itself and then in its <code><b>odb/</b></code>
+ subdirectory.</p>
+
+ <p>For the format of the options file refer to the
+ <code><b>--options-file</b></code> option below. You can repeat this
+ option to specify more than one profile.</p></dd>
+
+ <dt><code><b>--at-once</b></code></dt>
+ <dd>Generate code for all the input files as well as for all the files
+ that they include at once. The result is a single set of source/schema
+ files that contain all the generated code. If more than one input file is
+ specified together with this option, then the
+ <code><b>--input-name</b></code> option must also be specified in order to
+ provide the base name for the output files. In this case, the directory
+ part of such a base name is used as the location of the combined file.
+ This can be important for the <code><b>#include</b></code> directive
+ resolution.</dd>
+
+ <dt><code><b>--schema</b></code> <code><i>schema</i></code></dt>
+ <dd>Specify a database schema (database namespace) that should be assigned
+ to the persistent classes in the file being compiled. Database schemas are
+ not to be confused with database schema names (schema catalog names) which
+ are specified with the <code><b>--schema-name</b></code> option.</dd>
+
+ <dt><code><b>--export-symbol</b></code> <code><i>symbol</i></code></dt>
+ <dd>Insert <code><i>symbol</i></code> in places where DLL export/import
+ control statements (<code><b>__declspec(dllexport/dllimport)</b></code>)
+ are necessary. See also the <code><b>--extern-symbol</b></code> option
+ below.</dd>
+
+ <dt><code><b>--extern-symbol</b></code> <code><i>symbol</i></code></dt>
+ <dd>If <code><i>symbol</i></code> is defined, insert it in places where a
+ template instantiation must be declared <code><b>extern</b></code>. This
+ option is normally used together with <code><b>--export-symbol</b></code>
+ when both multi-database support and queries are enabled.</dd>
+
+ <dt><code><b>--std</b></code> <code><i>version</i></code></dt>
+ <dd>Specify the C++ standard that should be used during compilation. Valid
+ values are <code><b>c++98</b></code> (default), <code><b>c++11</b></code>,
+ <code><b>c++14</b></code>, <code><b>c++17</b></code>, and
+ <code><b>c++20</b></code>.</dd>
+
+ <dt><code><b>--warn-hard-add</b></code></dt>
+ <dd>Warn about hard-added data members.</dd>
+
+ <dt><code><b>--warn-hard-delete</b></code></dt>
+ <dd>Warn about hard-deleted data members and persistent classes.</dd>
+
+ <dt><code><b>--warn-hard</b></code></dt>
+ <dd>Warn about both hard-added and hard-deleted data members and
+ persistent classes.</dd>
+
+ <dt><code><b>--output-dir</b></code>|<code><b>-o</b></code> <code><i>dir</i></code></dt>
+ <dd>Write the generated files to <code><i>dir</i></code> instead of the
+ current directory.</dd>
+
+ <dt><code><b>--input-name</b></code> <code><i>name</i></code></dt>
+ <dd>Use <code><i>name</i></code> instead of the input file to derive the
+ names of the generated files. If the <code><b>--at-once</b></code> option
+ is specified, then the directory part of <code><i>name</i></code> is used
+ as the location of the combined file. Refer to the
+ <code><b>--at-once</b></code> option for details.</dd>
+
+ <dt><code><b>--changelog</b></code> <code><i>file</i></code></dt>
+ <dd>Read/write changelog from/to <code><i>file</i></code> instead of the
+ default changelog file. The default changelog file name is derived from
+ the input file name and it is placed into the same directory as the input
+ file. Note that the <code><b>--output-dir</b></code> option does not
+ affect the changelog file location. In other words, by default, the
+ changelog file is treated as another input rather than output even though
+ the ODB compiler may modify it. Use the <code><b>--changelog-in</b></code>
+ and <code><b>--changelog-out</b></code> options to specify different input
+ and output chaneglog files.</dd>
+
+ <dt><code><b>--changelog-in</b></code> <code><i>file</i></code></dt>
+ <dd>Read changelog from <code><i>file</i></code> instead of the default
+ changelog file. If this option is specified, then you must also specify
+ the output chanegelog file with <code><b>--changelog-out</b></code>.</dd>
+
+ <dt><code><b>--changelog-out</b></code> <code><i>file</i></code></dt>
+ <dd>Write changelog to <code><i>file</i></code> instead of the default
+ changelog file. If this option is specified, then you must also specify
+ the input chanegelog file with <code><b>--changelog-in</b></code>.</dd>
+
+ <dt><code><b>--changelog-dir</b></code> <code><i>dir</i></code></dt>
+ <dd>Use <code><i>dir</i></code> instead of the input file directory as the
+ changelog file directory. This directory is also added to changelog files
+ specified with the <code><b>--changelog</b></code>,
+ <code><b>--changelog-in</b></code>, and <code><b>--changelog-in</b></code>
+ options unless they are absolute paths.</dd>
+
+ <dt><code><b>--init-changelog</b></code></dt>
+ <dd>Force re-initialization of the changelog even if one exists (all the
+ existing change history will be lost). This option is primarily useful for
+ automated testing.</dd>
+
+ <dt><code><b>--odb-file-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> to construct the names of the generated
+ C++ files. In the single-database mode the default value for this option
+ is <code><b>-odb</b></code>. In the multi-database mode it is
+ <code><b>-odb</b></code> for the files corresponding to the
+ <code><b>common</b></code> database and <code><b>-odb-</b><i>db</i></code>
+ (where <code><i>db</i></code> is the database name) for other
+ databases.</dd>
+
+ <dt><code><b>--sql-file-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> to construct the name of the generated
+ schema SQL file. In the single-database mode by default no suffix is used.
+ In the multi-database mode the default value for this option is
+ <code><b>-</b><i>db</i></code> (where <code><i>db</i></code> is the
+ database name).</dd>
+
+ <dt><code><b>--schema-file-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> to construct the name of the generated
+ schema C++ source file. In the single-database mode the default value for
+ this option is <code><b>-schema</b></code>. In the multi-database mode it
+ is <code><b>-schema-</b><i>db</i></code> (where <code><i>db</i></code> is
+ the database name). See the <code><b>--schema-format</b></code> option for
+ details.</dd>
+
+ <dt><code><b>--changelog-file-suffix</b></code> <code><i>sfx</i></code></dt>
+ <dd>Use <code><i>sfx</i></code> to construct the name of the changelog
+ file. In the single-database mode by default no suffix is used. In the
+ multi-database mode the default value for this option is
+ <code><b>-</b><i>db</i></code> (where <code><i>db</i></code> is the
+ database name).</dd>
+
+ <dt><code><b>--hxx-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> instead of the default
+ <code><b>.hxx</b></code> to construct the name of the generated C++ header
+ file.</dd>
+
+ <dt><code><b>--ixx-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> instead of the default
+ <code><b>.ixx</b></code> to construct the name of the generated C++ inline
+ file.</dd>
+
+ <dt><code><b>--cxx-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> instead of the default
+ <code><b>.cxx</b></code> to construct the name of the generated C++ source
+ file.</dd>
+
+ <dt><code><b>--sql-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> instead of the default
+ <code><b>.sql</b></code> to construct the name of the generated database
+ schema file.</dd>
+
+ <dt><code><b>--changelog-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> instead of the default
+ <code><b>.xml</b></code> to construct the name of the changelog file.</dd>
+
+ <dt><code><b>--hxx-prologue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the beginning of the generated C++
+ header file.</dd>
+
+ <dt><code><b>--ixx-prologue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the beginning of the generated C++
+ inline file.</dd>
+
+ <dt><code><b>--cxx-prologue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the beginning of the generated C++
+ source file.</dd>
+
+ <dt><code><b>--schema-prologue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the beginning of the generated
+ schema C++ source file.</dd>
+
+ <dt><code><b>--sql-prologue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the beginning of the generated
+ database schema file.</dd>
+
+ <dt><code><b>--migration-prologue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the beginning of the generated
+ database migration file.</dd>
+
+ <dt><code><b>--sql-interlude</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> after all the <code><b>DROP</b></code>
+ and before any <code><b>CREATE</b></code> statements in the generated
+ database schema file.</dd>
+
+ <dt><code><b>--hxx-epilogue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the end of the generated C++ header
+ file.</dd>
+
+ <dt><code><b>--ixx-epilogue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the end of the generated C++ inline
+ file.</dd>
+
+ <dt><code><b>--cxx-epilogue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the end of the generated C++ source
+ file.</dd>
+
+ <dt><code><b>--schema-epilogue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the end of the generated schema C++
+ source file.</dd>
+
+ <dt><code><b>--sql-epilogue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the end of the generated database
+ schema file.</dd>
+
+ <dt><code><b>--migration-epilogue</b></code> <code><i>text</i></code></dt>
+ <dd>Insert <code><i>text</i></code> at the end of the generated database
+ migration file.</dd>
+
+ <dt><code><b>--hxx-prologue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the beginning of the
+ generated C++ header file.</dd>
+
+ <dt><code><b>--ixx-prologue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the beginning of the
+ generated C++ inline file.</dd>
+
+ <dt><code><b>--cxx-prologue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the beginning of the
+ generated C++ source file.</dd>
+
+ <dt><code><b>--schema-prologue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the beginning of the
+ generated schema C++ source file.</dd>
+
+ <dt><code><b>--sql-prologue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the beginning of the
+ generated database schema file.</dd>
+
+ <dt><code><b>--migration-prologue-file</b></code> <code><i>f</i></code></dt>
+ <dd>Insert the content of file <code><i>f</i></code> at the beginning of
+ the generated database migration file.</dd>
+
+ <dt><code><b>--sql-interlude-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> after all the
+ <code><b>DROP</b></code> and before any <code><b>CREATE</b></code>
+ statements in the generated database schema file.</dd>
+
+ <dt><code><b>--hxx-epilogue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the end of the
+ generated C++ header file.</dd>
+
+ <dt><code><b>--ixx-epilogue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the end of the
+ generated C++ inline file.</dd>
+
+ <dt><code><b>--cxx-epilogue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the end of the
+ generated C++ source file.</dd>
+
+ <dt><code><b>--schema-epilogue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the end of the
+ generated schema C++ source file.</dd>
+
+ <dt><code><b>--sql-epilogue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Insert the content of <code><i>file</i></code> at the end of the
+ generated database schema file.</dd>
+
+ <dt><code><b>--migration-epilogue-file</b></code> <code><i>f</i></code></dt>
+ <dd>Insert the content of file <code><i>f</i></code> at the end of the
+ generated database migration file.</dd>
+
+ <dt><code><b>--odb-prologue</b></code> <code><i>text</i></code></dt>
+ <dd>Compile <code><i>text</i></code> before the input header file. This
+ option allows you to add additional declarations, such as custom traits
+ specializations, to the ODB compilation process.</dd>
+
+ <dt><code><b>--odb-prologue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Compile <code><i>file</i></code> contents before the input header
+ file. Prologue files are compiled after all the prologue text fragments
+ (<code><b>--odb-prologue</b></code> option).</dd>
+
+ <dt><code><b>--odb-epilogue</b></code> <code><i>text</i></code></dt>
+ <dd>Compile <code><i>text</i></code> after the input header file. This
+ option allows you to add additional declarations, such as custom traits
+ specializations, to the ODB compilation process.</dd>
+
+ <dt><code><b>--odb-epilogue-file</b></code> <code><i>file</i></code></dt>
+ <dd>Compile <code><i>file</i></code> contents after the input header file.
+ Epilogue files are compiled after all the epilogue text fragments
+ (<code><b>--odb-epilogue</b></code> option).</dd>
+
+ <dt><code><b>--table-prefix</b></code> <code><i>prefix</i></code></dt>
+ <dd>Add <code><i>prefix</i></code> to table names and, for databases that
+ have global index and/or foreign key names, to those names as well. The
+ prefix is added to both names that were specified with the <code><b>db
+ table</b></code> and <code><b>db index</b></code> pragmas and those that
+ were automatically derived from class and data member names. If you
+ require a separator, such as an underscore, between the prefix and the
+ name, then you should include it into the prefix value.</dd>
+
+ <dt><code><b>--index-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> instead of the default
+ <code><b>_i</b></code> to construct index names. The suffix is only added
+ to names that were automatically derived from data member names. If you
+ require a separator, such as an underscore, between the name and the
+ suffix, then you should include it into the suffix value.</dd>
+
+ <dt><code><b>--fkey-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> instead of the default
+ <code><b>_fk</b></code> to construct foreign key names. If you require a
+ separator, such as an underscore, between the name and the suffix, then
+ you should include it into the suffix value.</dd>
+
+ <dt><code><b>--sequence-suffix</b></code> <code><i>suffix</i></code></dt>
+ <dd>Use <code><i>suffix</i></code> instead of the default
+ <code><b>_seq</b></code> to construct sequence names. If you require a
+ separator, such as an underscore, between the name and the suffix, then
+ you should include it into the suffix value.</dd>
+
+ <dt><code><b>--sql-name-case</b></code> <code><i>case</i></code></dt>
+ <dd>Convert all automatically-derived SQL names to upper or lower case.
+ Valid values for this option are <code><b>upper</b></code> and
+ <code><b>lower</b></code>.</dd>
+
+ <dt><code><b>--table-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions that
+ is used to transform automatically-derived table names. See the SQL NAME
+ TRANSFORMATIONS section below for details.</dd>
+
+ <dt><code><b>--column-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions that
+ is used to transform automatically-derived column names. See the SQL NAME
+ TRANSFORMATIONS section below for details.</dd>
+
+ <dt><code><b>--index-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions that
+ is used to transform automatically-derived index names. See the SQL NAME
+ TRANSFORMATIONS section below for details.</dd>
+
+ <dt><code><b>--fkey-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions that
+ is used to transform automatically-derived foreign key names. See the SQL
+ NAME TRANSFORMATIONS section below for details.</dd>
+
+ <dt><code><b>--sequence-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions that
+ is used to transform automatically-derived sequence names. See the SQL
+ NAME TRANSFORMATIONS section below for details.</dd>
+
+ <dt><code><b>--statement-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions that
+ is used to transform automatically-derived prepared statement names. See
+ the SQL NAME TRANSFORMATIONS section below for details.</dd>
+
+ <dt><code><b>--sql-name-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions that
+ is used to transform all automatically-derived SQL names. See the SQL NAME
+ TRANSFORMATIONS section below for details.</dd>
+
+ <dt><code><b>--sql-name-regex-trace</b></code></dt>
+ <dd>Trace the process of applying regular expressions specified with the
+ SQL name <code><b>--*-regex</b></code> options. Use this option to find
+ out why your regular expressions don't do what you expected them to
+ do.</dd>
+
+ <dt><code><b>--accessor-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions used
+ to transform data member names to function names when searching for a
+ suitable accessor function. The argument to this option is a Perl-like
+ regular expression in the form
+ <code><b>/</b><i>pattern</i><b>/</b><i>replacement</i><b>/</b></code>. Any
+ character can be used as a delimiter instead of '<code><b>/</b></code>'
+ and the delimiter can be escaped inside <code><i>pattern</i></code> and
+ <code><i>replacement</i></code> with a backslash (<code><b>\</b></code>).
+ You can specify multiple regular expressions by repeating this option.
+
+ <p>All the regular expressions are tried in the order specified and the
+ first expression that produces a suitable accessor function is used. Each
+ expression is tried twice: first with the actual member name and then with
+ the member's <i>public name</i> which is obtained by removing the common
+ member name decorations, such as leading and trailing underscores, the
+ <code><b>m_</b></code> prefix, etc. The ODB compiler also includes a
+ number of built-in expressions for commonly used accessor names, such as
+ <code><b>get_foo</b></code>, <code><b>getFoo</b></code>,
+ <code><b>getfoo</b></code>, and just <code><b>foo</b></code>. The built-in
+ expressions are tried last.</p>
+
+ <p>As an example, the following expression transforms data members with
+ public names in the form <code><b>foo</b></code> to accessor names in the
+ form <code><b>GetFoo</b></code>:</p>
+
+ <p class="code"><code><b>/(.+)/Get\u$1/</b></code></p>
+
+ <p>See also the REGEX AND SHELL QUOTING section below.</p></dd>
+
+ <dt><code><b>--accessor-regex-trace</b></code></dt>
+ <dd>Trace the process of applying regular expressions specified with the
+ <code><b>--accessor-regex</b></code> option. Use this option to find out
+ why your regular expressions don't do what you expected them to do.</dd>
+
+ <dt><code><b>--modifier-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions used
+ to transform data member names to function names when searching for a
+ suitable modifier function. The argument to this option is a Perl-like
+ regular expression in the form
+ <code><b>/</b><i>pattern</i><b>/</b><i>replacement</i><b>/</b></code>. Any
+ character can be used as a delimiter instead of '<code><b>/</b></code>'
+ and the delimiter can be escaped inside <code><i>pattern</i></code> and
+ <code><i>replacement</i></code> with a backslash (<code><b>\</b></code>).
+ You can specify multiple regular expressions by repeating this option.
+
+ <p>All the regular expressions are tried in the order specified and the
+ first expression that produces a suitable modifier function is used. Each
+ expression is tried twice: first with the actual member name and then with
+ the member's <i>public name</i> which is obtained by removing the common
+ member name decorations, such as leading and trailing underscores, the
+ <code><b>m_</b></code> prefix, etc. The ODB compiler also includes a
+ number of built-in expressions for commonly used modifier names, such as
+ <code><b>set_foo</b></code>, <code><b>setFoo</b></code>,
+ <code><b>setfoo</b></code>, and just <code><b>foo</b></code>. The built-in
+ expressions are tried last.</p>
+
+ <p>As an example, the following expression transforms data members with
+ public names in the form <code><b>foo</b></code> to modifier names in the
+ form <code><b>SetFoo</b></code>:</p>
+
+ <p class="code"><code><b>/(.+)/Set\u$1/</b></code></p>
+
+ <p>See also the REGEX AND SHELL QUOTING section below.</p></dd>
+
+ <dt><code><b>--modifier-regex-trace</b></code></dt>
+ <dd>Trace the process of applying regular expressions specified with the
+ <code><b>--modifier-regex</b></code> option. Use this option to find out
+ why your regular expressions don't do what you expected them to do.</dd>
+
+ <dt><code><b>--include-with-brackets</b></code></dt>
+ <dd>Use angle brackets (&lt;>) instead of quotes ("") in the generated
+ <code><b>#include</b></code> directives.</dd>
+
+ <dt><code><b>--include-prefix</b></code> <code><i>prefix</i></code></dt>
+ <dd>Add <code><i>prefix</i></code> to the generated
+ <code><b>#include</b></code> directive paths.</dd>
+
+ <dt><code><b>--include-regex</b></code> <code><i>regex</i></code></dt>
+ <dd>Add <code><i>regex</i></code> to the list of regular expressions used
+ to transform generated <code><b>#include</b></code> directive paths. The
+ argument to this option is a Perl-like regular expression in the form
+ <code><b>/</b><i>pattern</i><b>/</b><i>replacement</i><b>/</b></code>. Any
+ character can be used as a delimiter instead of '<code><b>/</b></code>'
+ and the delimiter can be escaped inside <code><i>pattern</i></code> and
+ <code><i>replacement</i></code> with a backslash (<code><b>\</b></code>).
+ You can specify multiple regular expressions by repeating this option. All
+ the regular expressions are tried in the order specified and the first
+ expression that matches is used.
+
+ <p>As an example, the following expression transforms include paths in the
+ form <code><b>foo/bar-odb.h</b></code> to paths in the form
+ <code><b>foo/generated/bar-odb.h</b></code>:</p>
+
+ <p
+ class="code"><code><b>%foo/(.+)-odb.h%foo/generated/$1-odb.h%</b></code></p>
+
+ <p>See also the REGEX AND SHELL QUOTING section below.</p></dd>
+
+ <dt><code><b>--include-regex-trace</b></code></dt>
+ <dd>Trace the process of applying regular expressions specified with the
+ <code><b>--include-regex</b></code> option. Use this option to find out
+ why your regular expressions don't do what you expected them to do.</dd>
+
+ <dt><code><b>--guard-prefix</b></code> <code><i>prefix</i></code></dt>
+ <dd>Add <code><i>prefix</i></code> to the generated header inclusion
+ guards. The prefix is transformed to upper case and characters that are
+ illegal in a preprocessor macro name are replaced with underscores.</dd>
+
+ <dt><code><b>--show-sloc</b></code></dt>
+ <dd>Print the number of generated physical source lines of code
+ (SLOC).</dd>
+
+ <dt><code><b>--sloc-limit</b></code> <code><i>num</i></code></dt>
+ <dd>Check that the number of generated physical source lines of code
+ (SLOC) does not exceed <code><i>num</i></code>.</dd>
+
+ <dt><code><b>--options-file</b></code> <code><i>file</i></code></dt>
+ <dd>Read additional options from <code><i>file</i></code>. Each option
+ should appear on a separate line optionally followed by space or equal
+ sign (<code><b>=</b></code>) and an option value. Empty lines and lines
+ starting with <code><b>#</b></code> are ignored. Option values can be
+ enclosed in double (<code><b>"</b></code>) or single
+ (<code><b>'</b></code>) quotes to preserve leading and trailing
+ whitespaces as well as to specify empty values. If the value itself
+ contains trailing or leading quotes, enclose it with an extra pair of
+ quotes, for example <code><b>'"x"'</b></code>. Non-leading and
+ non-trailing quotes are interpreted as being part of the option value.
+
+ <p>The semantics of providing options in a file is equivalent to providing
+ the same set of options in the same order on the command line at the point
+ where the <code><b>--options-file</b></code> option is specified except
+ that the shell escaping and quoting is not required. Repeat this option to
+ specify more than one options file.</p></dd>
+
+ <dt><code><b>-x</b></code> <code><i>option</i></code></dt>
+ <dd>Pass <code><i>option</i></code> to the underlying C++ compiler
+ (<code><b>g++</b></code>). The <code><i>option</i></code> value that
+ doesn't start with '<code><b>-</b></code>' is considered the
+ <code><b>g++</b></code> executable name.</dd>
+
+ <dt><code><b>-v</b></code></dt>
+ <dd>Print the commands executed to run the stages of compilation.</dd>
+
+ <dt><code><b>--trace</b></code></dt>
+ <dd>Trace the compilation process.</dd>
+
+ <dt><code><b>--mysql-engine</b></code> <code><i>engine</i></code></dt>
+ <dd>Use <code><i>engine</i></code> instead of the default
+ <code><b>InnoDB</b></code> in the generated database schema file. For more
+ information on the storage engine options see the MySQL documentation. If
+ you would like to use the database-default engine, pass
+ <code><b>default</b></code> as the value for this option.</dd>
+
+ <dt><code><b>--sqlite-override-null</b></code></dt>
+ <dd>Make all columns in the generated database schema allow
+ <code><b>NULL</b></code> values. This is primarily useful in schema
+ migration since SQLite does not support dropping of columns. By making all
+ columns <code><b>NULL</b></code> we can later "delete" them by setting
+ their values to <code><b>NULL</b></code>. Note that this option overrides
+ even the <code><b>not_null</b></code> pragma.</dd>
+
+ <dt><code><b>--sqlite-lax-auto-id</b></code></dt>
+ <dd>Do not force monotonically increasing automatically-assigned object
+ ids. In this mode the generated database schema omits the
+ <code><b>AUTOINCREMENT</b></code> keyword which results in faster object
+ persistence but may lead to automatically-assigned ids not being in a
+ strictly ascending order. Refer to the SQLite documentation for
+ details.</dd>
+
+ <dt><code><b>--pgsql-server-version</b></code> <code><i>ver</i></code></dt>
+ <dd>Specify the minimum PostgreSQL server version with which the generated
+ C++ code and schema will be used. This information is used to enable
+ version-specific optimizations and workarounds in the generated C++ code
+ and schema. The version must be in the
+ <code><i>major</i><b>.</b><i>minor</i></code> form, for example,
+ <code><b>9.1</b></code>. If this option is not specified, then
+ <code><b>7.4</b></code> or later is assumed.</dd>
+
+ <dt><code><b>--oracle-client-version</b></code> <code><i>ver</i></code></dt>
+ <dd>Specify the minimum Oracle client library (OCI) version with which the
+ generated C++ code will be linked. This information is used to enable
+ version-specific optimizations and workarounds in the generated C++ code.
+ The version must be in the <code><i>major</i><b>.</b><i>minor</i></code>
+ form, for example, <code><b>11.2</b></code>. If this option is not
+ specified, then <code><b>10.1</b></code> or later is assumed.</dd>
+
+ <dt><code><b>--oracle-warn-truncation</b></code></dt>
+ <dd>Warn about SQL names that are longer than 30 characters and are
+ therefore truncated. Note that during database schema generation
+ (<code><b>--generate-schema</b></code>) ODB detects when such truncations
+ lead to name conflicts and issues diagnostics even without this option
+ specified.</dd>
+
+ <dt><code><b>--mssql-server-version</b></code> <code><i>ver</i></code></dt>
+ <dd>Specify the minimum SQL Server server version with which the generated
+ C++ code and schema will be used. This information is used to enable
+ version-specific optimizations and workarounds in the generated C++ code
+ and schema. The version must be in the
+ <code><i>major</i><b>.</b><i>minor</i></code> form, for example,
+ <code><b>9.0</b></code> (SQL Server 2005), <code><b>10.5</b></code>
+ (2008R2), or <code><b>11.0</b></code> (2012). If this option is not
+ specified, then <code><b>10.0</b></code> (SQL Server 2008) or later is
+ assumed.</dd>
+
+ <dt><code><b>--mssql-short-limit</b></code> <code><i>size</i></code></dt>
+ <dd>Specify the short data size limit. If a character, national character,
+ or binary data type has a maximum length (in bytes) less than or equal to
+ this limit, then it is treated as <i>short data</i>, otherwise it is
+ <i>long data</i>. For short data ODB pre-allocates an intermediate buffer
+ of the maximum size and binds it directly to a parameter or result column.
+ This way the underlying API (ODBC) can read/write directly from/to this
+ buffer. In the case of long data, the data is read/written in chunks using
+ the <code><b>SQLGetData()</b></code>/<code><b>SQLPutData()</b></code> ODBC
+ functions. While the long data approach reduces the amount of memory used
+ by the application, it may require greater CPU resources. The default
+ short data limit is 1024 bytes. When setting a custom short data limit,
+ make sure that it is sufficiently large so that no object id in the
+ application is treated as long data.</dd>
+ </dl>
+
+ <h1>SQL NAME TRANSFORMATIONS</h1>
+
+ <p>The ODB compiler provides a number of mechanisms for transforming
+ automatically-derived SQL names, such as tables, columns, etc.,
+ to match a specific naming convention. At the higher level, we can
+ add a prefix to global names (tables and, for some databases,
+ indexes and/or foreign keys) with the <code><b>--table-prefix</b></code>
+ option. Similarly, we can specify custom suffixes for
+ automatically-derived
+ index (<code><b>--index-suffix</b></code>; default is <code><b>_i</b></code>),
+ foreign key (<code><b>--fkey-suffix</b></code>; default is <code><b>_fk</b></code>), and
+ sequence (<code><b>--sequence-suffix</b></code>; default is <code><b>_seq</b></code>)
+ names. Finally, we can also convert all the names to upper or lower
+ case with the <code><b>--sql-name-case</b></code> option (valid values
+ are <code><b>upper</b></code> and <code><b>lower</b></code>).</p>
+
+ <p>At the lower level we can specify a set of regular expressions to
+ implement arbitrary transformations of the automatically-derived SQL
+ names. If we want a particular regular expression only to apply to
+ a specific name, for example, table or column, then we use one of the
+ <code><b>--</b><i>kind</i><b>-regex</b></code> options, where
+ <code><i>kind</i></code> can be <code><b>table</b></code>,
+ <code><b>column</b></code>, <code><b>index</b></code>,
+ <code><b>fkey</b></code>, <code><b>sequence</b></code>, or
+ <code><b>statement</b></code>. On the other hand, if we want our
+ regular expressions to apply to all SQL names, then we use the
+ <code><b>--sql-name-regex</b></code> option.</p>
+
+ <p>The interaction between the higher and lower level transformations
+ is as follows. Prefixes and suffixes are added first. Then the
+ regular expression transformations are applied. Finally, if requested,
+ the name is converted to upper or lower case. Note also that all of
+ these transformations except for <code><b>--table-prefix</b></code>
+ only apply to automatically-derived names. In other words, if a table,
+ column, etc., name was explicitly specified with a pragma, then it
+ is used as is, without applying any (except for the table prefix)
+ transformations.</p>
+
+ <p>The value for the <code><b>--*-regex</b></code> options is a Perl-like
+ regular expression in the form
+ <code><b>/</b><i>pattern</i><b>/</b><i>replacement</i><b>/</b></code>.
+ Any character can be used as a delimiter instead of <code><b>/</b></code>
+ and the delimiter can be escaped inside <code><i>pattern</i></code> and
+ <code><i>replacement</i></code> with a backslash (<code><b>\</b></code>).
+ You can also specify multiple regular expressions by repeating these
+ options.</p>
+
+ <p>All the regular expressions are tried in the order specified with the
+ name-specific expressions (for example, <code><b>--table-regex</b></code>)
+ tried first followed by the generic expressions
+ (<code><b>--sql-name-regex</b></code>). The first expression that
+ matches is used.</p>
+
+ <p>As an example, consider a regular expression that transforms a class
+ name in the form <code><b>CFoo</b></code> to a table name in the
+ form <code><b>FOO</b></code>:</p>
+
+ <p><code><b>--table-regex '/C(.+)/\U$1/'</b></code></p>
+
+ <p>As a more interesting example, consider the transformation of class
+ names that follow the upper camel case convention (for example,
+ <code><b>FooBar</b></code>) to table names that follow the
+ underscore-separated, all upper case convention (for example,
+ <code><b>FOO_BAR</b></code>). For this case we have to use
+ separate expressions to handle one-word, two-word, etc.,
+ names:</p>
+
+ <p><code><b>--table-regex '/([A-z][a-z]+)/\U$1/'</b></code></p>
+ <p><code><b>--table-regex '/([A-z][a-z]+)([A-z][a-z]+)/\U$1_$2/'</b></code></p>
+
+ <p>See also the REGEX AND SHELL QUOTING section below.</p>
+
+ <h1>REGEX AND SHELL QUOTING</h1>
+
+ <p>When entering a regular expression argument in the shell
+ command line it is often necessary to use quoting (enclosing
+ the argument in <code><b>"&nbsp;"</b></code> or
+ <code><b>'&nbsp;'</b></code>) in order to prevent the shell
+ from interpreting certain characters, for example, spaces as
+ argument separators and <code><b>$</b></code> as variable
+ expansions.</p>
+
+ <p>Unfortunately it is hard to achieve this in a manner that is
+ portable across POSIX shells, such as those found on
+ GNU/Linux and UNIX, and Windows shell. For example, if you
+ use <code><b>"&nbsp;"</b></code> for quoting you will get a
+ wrong result with POSIX shells if your expression contains
+ <code><b>$</b></code>. The standard way of dealing with this
+ on POSIX systems is to use <code><b>'&nbsp;'</b></code> instead.
+ Unfortunately, Windows shell does not remove <code><b>'&nbsp;'</b></code>
+ from arguments when they are passed to applications. As a result you
+ may have to use <code><b>'&nbsp;'</b></code> for POSIX and
+ <code><b>"&nbsp;"</b></code> for Windows (<code><b>$</b></code> is
+ not treated as a special character on Windows).</p>
+
+ <p>Alternatively, you can save regular expression options into
+ a file, one option per line, and use this file with the
+ <code><b>--options-file</b></code> option. With this approach
+ you don't need to worry about shell quoting.</p>
+
+ <h1>DIAGNOSTICS</h1>
+
+ <p>If the input file is not valid C++, <code><b>odb</b></code>
+ will issue diagnostic messages to STDERR and exit with non-zero exit
+ code.</p>
+
+ <h1>BUGS</h1>
+
+ <p>Send bug reports to the
+ <a href="mailto:odb-users@codesynthesis.com">odb-users@codesynthesis.com</a> mailing list.</p>
+
+ </div>
+ <div id="footer">
+ Copyright &#169; 2009-2024 Code Synthesis.
+
+ <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/gcc.hxx b/odb/gcc.hxx
deleted file mode 100644
index af0e2a0..0000000
--- a/odb/gcc.hxx
+++ /dev/null
@@ -1,195 +0,0 @@
-// file : odb/gcc.hxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#ifndef ODB_GCC_HXX
-#define ODB_GCC_HXX
-
-#include <odb/gcc-fwd.hxx>
-
-// Actually, let's keep it out. With it included we can compile in C++98
-// but not in C++14 (GCC 6 default).
-//
-// #if BUILDING_GCC_MAJOR >= 6
-// # include <safe-ctype.h> // See gcc-fwd.hxx.
-// #endif
-
-// GCC header includes to get the plugin and parse tree declarations.
-// The order is important and doesn't follow any kind of logic.
-//
-
-#include <stdlib.h>
-#include <gmp.h>
-
-#include <cstdlib> // Include before GCC poisons some declarations.
-
-// GCC 4.7 can be built using either C or C++ compiler. From 4.8 it
-// is always built as C++.
-//
-#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
-# define ODB_GCC_PLUGIN_C
-#elif BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 7
-# include <config.h>
-# ifndef ENABLE_BUILD_WITH_CXX
-# define ODB_GCC_PLUGIN_C
-# endif
-#endif
-
-#ifdef ODB_GCC_PLUGIN_C
-extern "C"
-{
-#endif
-
-// GCC's system.h below includes safe-ctype.h which "disables" versions
-// from ctype.h. Well, now it's gonna learn how it feels to be disabled.
-//
-#define SAFE_CTYPE_H
-
-#include <gcc-plugin.h>
-
-#include <config.h>
-#include <system.h>
-#include <coretypes.h>
-#include <tree.h>
-#include <real.h>
-
-#include <cpplib.h>
-#include <cp/cp-tree.h>
-
-#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 5
-# include <c-family/c-common.h>
-# include <c-family/c-pragma.h>
-#else
-# include <c-common.h>
-# include <c-pragma.h>
-#endif
-
-#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 8
-# include <stringpool.h> // get_identifier
-#endif
-
-#include <diagnostic.h>
-#include <output.h>
-
-#ifdef ODB_GCC_PLUGIN_C
-} // extern "C"
-#endif
-
-// Get the value of INTEGER_CST reinterpreted as unsigned.
-//
-inline unsigned long long
-integer_value (tree n)
-{
- unsigned long long val;
-
-#if BUILDING_GCC_MAJOR >= 5
- if (tree_fits_uhwi_p (n))
- val = static_cast<unsigned long long> (tree_to_uhwi (n));
- else
- val = static_cast<unsigned long long> (tree_to_shwi (n));
-#else
- HOST_WIDE_INT hwl (TREE_INT_CST_LOW (n));
- HOST_WIDE_INT hwh (TREE_INT_CST_HIGH (n));
- unsigned short width (HOST_BITS_PER_WIDE_INT);
-
- if (hwh == 0)
- val = static_cast<unsigned long long> (hwl);
- else if (hwh == -1 && hwl != 0)
- val = static_cast<unsigned long long> (hwl);
- else
- val = static_cast<unsigned long long> ((hwh << width) + hwl);
-#endif
-
- return val;
-}
-
-// Since 4.7.0 the location may point inside a macro rather than at
-// the expansion point. We are only really interested in the expansion
-// points so we use the real_source_location() wrapper rather than
-// DECL_SOURCE_LOCATION() to do this at the source.
-//
-inline location_t
-real_source_location (tree n)
-{
- location_t l (DECL_SOURCE_LOCATION (n));
-
-#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 6
- l = linemap_resolve_location (line_table, l, LRK_MACRO_EXPANSION_POINT, 0);
-#endif
-
- return l;
-}
-
-// In 4.9.0 the tree code type was changed from int to enum tree_code.
-// the tree_code_name array is also gone with the get_tree_code_name()
-// function in its place.
-//
-#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 8
-typedef enum tree_code gcc_tree_code_type;
-
-inline const char*
-gcc_tree_code_name (gcc_tree_code_type tc) {return get_tree_code_name (tc);}
-#else
-typedef int gcc_tree_code_type;
-
-inline const char*
-gcc_tree_code_name (gcc_tree_code_type tc) {return tree_code_name[tc];}
-#endif
-
-// Only since GCC 4.7.0.
-//
-#ifndef LOCATION_COLUMN
-#define LOCATION_COLUMN(LOC) (expand_location (LOC).column)
-#endif
-
-#ifndef DECL_SOURCE_COLUMN
-#define DECL_SOURCE_COLUMN(NODE) LOCATION_COLUMN (DECL_SOURCE_LOCATION (NODE))
-#endif
-
-// Only since GCC 4.6.0.
-//
-#ifndef DECL_CHAIN
-#define DECL_CHAIN(x) TREE_CHAIN(x)
-#endif
-
-// In GCC 6 ANON_AGGRNAME_P became anon_aggrname_p().
-// In GCC 10 anon_aggrname_p() became IDENTIFIER_ANON_P.
-//
-#if BUILDING_GCC_MAJOR < 6
-# define IDENTIFIER_ANON_P(X) ANON_AGGRNAME_P(X)
-#elif BUILDING_GCC_MAJOR < 10
-# define IDENTIFIER_ANON_P(X) anon_aggrname_p(X)
-#endif
-
-// In GCC 9:
-//
-// INCLUDED_FROM Became linemap_included_from_linemap().
-// LAST_SOURCE_LINE Was removed apparently as no longer used. Studying
-// the line-map.h diff from 8.3 suggests that the old
-// implementation should still work.
-//
-#if BUILDING_GCC_MAJOR >= 9
-
-inline const line_map_ordinary*
-INCLUDED_FROM (line_maps* set, const line_map_ordinary* map)
-{
- return linemap_included_from_linemap (set, map);
-}
-
-inline source_location
-LAST_SOURCE_LINE_LOCATION (const line_map_ordinary* map)
-{
- return (((map[1].start_location - 1
- - map->start_location)
- & ~((1 << map->m_column_and_range_bits) - 1))
- + map->start_location);
-}
-
-inline linenum_type
-LAST_SOURCE_LINE (const line_map_ordinary* map)
-{
- return SOURCE_LINE (map, LAST_SOURCE_LINE_LOCATION (map));
-}
-
-#endif
-
-#endif // ODB_GCC_HXX
diff --git a/odb/generator.cxx b/odb/generator.cxx
deleted file mode 100644
index 09a971c..0000000
--- a/odb/generator.cxx
+++ /dev/null
@@ -1,1044 +0,0 @@
-// file : odb/generator.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <cctype> // std::toupper, std::is{alpha,upper,lower}
-#include <string>
-#include <memory> // std::unique_ptr
-#include <iomanip>
-#include <fstream>
-#include <sstream>
-#include <iostream>
-
-#include <cutl/fs/auto-remove.hxx>
-
-#include <cutl/compiler/code-stream.hxx>
-#include <cutl/compiler/cxx-indenter.hxx>
-#include <cutl/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>
-#include <odb/generator.hxx>
-
-#include <odb/semantics/relational/model.hxx>
-#include <odb/semantics/relational/changeset.hxx>
-#include <odb/semantics/relational/changelog.hxx>
-
-#include <odb/generate.hxx>
-#include <odb/relational/generate.hxx>
-
-using namespace std;
-using namespace cutl;
-
-using semantics::path;
-typedef vector<string> strings;
-typedef vector<path> paths;
-typedef vector<cutl::shared_ptr<ofstream> > ofstreams;
-
-namespace
-{
- static char const cxx_file_header[] =
- "// -*- C++ -*-\n"
- "//\n"
- "// This file was generated by ODB, object-relational mapping (ORM)\n"
- "// compiler for C++.\n"
- "//\n\n";
-
- static char const sql_file_header[] =
- "/* This file was generated by ODB, object-relational mapping (ORM)\n"
- " * compiler for C++.\n"
- " */\n\n";
-
- void
- open (ifstream& ifs, path const& p)
- {
- ifs.open (p.string ().c_str (), ios_base::in | ios_base::binary);
-
- if (!ifs.is_open ())
- {
- cerr << "error: unable to open '" << p << "' in read mode" << endl;
- throw generator_failed ();
- }
- }
-
- void
- open (ofstream& ofs, path const& p, ios_base::openmode m = ios_base::out)
- {
- ofs.open (p.string ().c_str (), ios_base::out | m);
-
- if (!ofs.is_open ())
- {
- cerr << "error: unable to open '" << p << "' in write mode" << endl;
- throw generator_failed ();
- }
- }
-
- void
- append (ostream& os, strings const& text)
- {
- for (strings::const_iterator i (text.begin ());
- i != text.end (); ++i)
- {
- os << *i << endl;
- }
- }
-
- void
- append (ostream& os, path const& file)
- {
- ifstream ifs;
- open (ifs, file);
-
- // getline() will set the failbit if it failed to extract anything,
- // not even the delimiter and eofbit if it reached eof before seeing
- // the delimiter.
- //
- // We used to just do:
- //
- // os << ifs.rdbuf ();
- //
- // But that has some drawbacks: it won't end with a newline if the file
- // doesn't end with one. There were also some issues with Windows newlines
- // (we ended up doubling them).
- //
- for (string s; getline (ifs, s); )
- os << s << endl;
- }
-
- // Append prologue/interlude/epilogue.
- //
- void
- append_logue (ostream& os,
- database db,
- database_map<vector<string> > const& text,
- database_map<vector<string> > const& file,
- char const* begin_comment,
- char const* end_comment)
- {
- bool t (text.count (db) != 0);
- bool f (file.count (db) != 0);
-
- if (t || f)
- {
- os << begin_comment << endl;
-
- if (t)
- append (os, text[db]);
-
- if (f)
- {
- strings const& fs (file[db]);
-
- for (strings::const_iterator i (fs.begin ());
- i != fs.end (); ++i)
- append (os, path (*i));
- }
-
- os << end_comment << endl
- << endl;
- }
- }
-}
-
-void
-generate (options const& ops,
- features& fts,
- semantics::unit& unit,
- path const& p,
- paths const& inputs)
-{
- namespace sema_rel = semantics::relational;
- using cutl::shared_ptr;
-
- try
- {
- database db (ops.database ()[0]);
- multi_database md (ops.multi_database ());
-
- // First create the database model.
- //
- bool gen_schema (ops.generate_schema () && db != database::common);
-
- shared_ptr<sema_rel::model> model;
-
- if (gen_schema)
- {
- unique_ptr<context> ctx (create_context (cerr, unit, ops, fts, 0));
-
- switch (db)
- {
- case database::mssql:
- case database::mysql:
- case database::oracle:
- case database::pgsql:
- case database::sqlite:
- {
- model = relational::model::generate ();
- break;
- }
- case database::common:
- break;
- }
- }
-
- // Input files.
- //
- path file (ops.input_name ().empty ()
- ? p.leaf ()
- : path (ops.input_name ()).leaf ());
- string base (file.base ().string ());
-
- path in_log_path;
- path log_dir (ops.changelog_dir ().count (db) != 0
- ? ops.changelog_dir ()[db]
- : "");
- if (ops.changelog_in ().count (db) != 0)
- {
- in_log_path = path (ops.changelog_in ()[db]);
-
- if (!log_dir.empty () && !in_log_path.absolute ())
- in_log_path = log_dir / in_log_path;
- }
- else if (ops.changelog ().count (db) != 0)
- {
- in_log_path = path (ops.changelog ()[db]);
-
- if (!in_log_path.absolute () && !log_dir.empty ())
- in_log_path = log_dir / in_log_path;
- }
- else
- {
- string log_name (base + ops.changelog_file_suffix ()[db] +
- ops.changelog_suffix ());
- in_log_path = path (log_name);
-
- if (!log_dir.empty ())
- in_log_path = log_dir / in_log_path;
- else
- in_log_path = p.directory () / in_log_path; // Use input directory.
- }
-
- // Load the old changelog and generate a new one.
- //
- bool gen_changelog (gen_schema && unit.count ("model-version") != 0);
- cutl::shared_ptr<sema_rel::changelog> changelog;
- cutl::shared_ptr<sema_rel::changelog> old_changelog;
- string old_changelog_xml;
-
- path out_log_path;
- if (ops.changelog_out ().count (db))
- {
- out_log_path = path (ops.changelog_out ()[db]);
-
- if (!log_dir.empty () && !out_log_path.absolute ())
- out_log_path = log_dir / out_log_path;
- }
- else
- out_log_path = in_log_path;
-
- if (gen_changelog)
- {
- ifstream log;
-
- // Unless we are forced to re-initialize the changelog, load the
- // old one.
- //
- if (!ops.init_changelog ())
- log.open (in_log_path.string ().c_str (),
- ios_base::in | ios_base::binary);
-
- if (log.is_open ()) // The changelog might not exist.
- {
- try
- {
- // Get the XML into a buffer. We use it to avoid modifying the
- // file when the changelog hasn't changed.
- //
- for (bool first (true); !log.eof (); )
- {
- string line;
- getline (log, line);
-
- if (log.fail ())
- ios_base::failure ("getline");
-
- if (first)
- first = false;
- else
- old_changelog_xml += '\n';
-
- old_changelog_xml += line;
- }
-
- istringstream is (old_changelog_xml);
- is.exceptions (ios_base::badbit | ios_base::failbit);
-
- xml::parser p (is, in_log_path.string ());
- old_changelog.reset (new (shared) sema_rel::changelog (p));
-
- if (old_changelog->database () != db.string ())
- {
- cerr << in_log_path << ": error: wrong database '" <<
- old_changelog->database () << "', expected '" << db <<
- "'" << endl;
- throw generator_failed ();
- }
-
- string sn (ops.schema_name ()[db]);
- if (old_changelog->schema_name () != sn)
- {
- cerr << in_log_path << ": error: wrong schema name '" <<
- old_changelog->schema_name () << "', expected '" << sn <<
- "'" << endl;
- throw generator_failed ();
- }
- }
- catch (const ios_base::failure& e)
- {
- cerr << in_log_path << ": read failure" << endl;
- throw generator_failed ();
- }
- catch (const xml::parsing& e)
- {
- cerr << e.what () << endl;
- throw generator_failed ();
- }
- }
-
- changelog = relational::changelog::generate (
- *model,
- unit.get<model_version> ("model-version"),
- old_changelog.get (),
- in_log_path.string (),
- out_log_path.string (),
- ops);
- }
-
- // Output files.
- //
- fs::auto_removes auto_rm;
-
- string hxx_name (base + ops.odb_file_suffix ()[db] + ops.hxx_suffix ());
- string ixx_name (base + ops.odb_file_suffix ()[db] + ops.ixx_suffix ());
- string cxx_name (base + ops.odb_file_suffix ()[db] + ops.cxx_suffix ());
- string sch_name (base + ops.schema_file_suffix ()[db] + ops.cxx_suffix ());
- string sql_name (base + ops.sql_file_suffix ()[db] + ops.sql_suffix ());
-
- path hxx_path (hxx_name);
- path ixx_path (ixx_name);
- path cxx_path (cxx_name);
- path sch_path (sch_name);
- path sql_path (sql_name);
- paths mig_pre_paths;
- paths mig_post_paths;
-
- bool gen_migration (gen_changelog && !ops.suppress_migration ());
- bool gen_sql_migration (
- gen_migration && ops.schema_format ()[db].count (schema_format::sql));
-
- if (gen_sql_migration)
- {
- for (sema_rel::changelog::contains_changeset_iterator i (
- changelog->contains_changeset_begin ());
- i != changelog->contains_changeset_end (); ++i)
- {
- sema_rel::changeset& cs (i->changeset ());
-
- // Default format: %N[-D%]-%3V-{pre|post}.sql
- //
- string n (base);
-
- if (md != multi_database::disabled)
- n += '-' + db.string ();
-
- ostringstream os;
- os << setfill ('0') << setw (3) << cs.version ();
- n += '-' + os.str ();
-
- mig_pre_paths.push_back (path (n + "-pre" + ops.sql_suffix ()));
- mig_post_paths.push_back (path (n + "-post" + ops.sql_suffix ()));
- }
- }
-
- if (!ops.output_dir ().empty ())
- {
- path dir (ops.output_dir ());
- hxx_path = dir / hxx_path;
- ixx_path = dir / ixx_path;
- cxx_path = dir / cxx_path;
- sch_path = dir / sch_path;
- sql_path = dir / sql_path;
-
- if (gen_sql_migration)
- {
- for (paths::size_type i (0); i < mig_pre_paths.size (); ++i)
- {
- mig_pre_paths[i] = dir / mig_pre_paths[i];
- mig_post_paths[i] = dir / mig_post_paths[i];
- }
- }
- }
-
- //
- //
- bool gen_cxx (!ops.generate_schema_only ());
-
- ofstream hxx;
- if (gen_cxx)
- {
- open (hxx, hxx_path);
- auto_rm.add (hxx_path);
- }
-
- //
- //
- ofstream ixx;
- if (gen_cxx)
- {
- open (ixx, ixx_path);
- auto_rm.add (ixx_path);
- }
-
- //
- //
- ofstream cxx;
- if (gen_cxx && (db != database::common || md == multi_database::dynamic))
- {
- open (cxx, cxx_path);
- auto_rm.add (cxx_path);
- }
-
- //
- //
- bool gen_sep_schema (
- gen_schema &&
- ops.schema_format ()[db].count (schema_format::separate));
-
- ofstream sch;
- if (gen_sep_schema)
- {
- open (sch, sch_path);
- auto_rm.add (sch_path);
- }
-
- //
- //
- bool gen_sql_schema (gen_schema &&
- ops.schema_format ()[db].count (schema_format::sql));
- ofstream sql;
- if (gen_sql_schema)
- {
- open (sql, sql_path);
- auto_rm.add (sql_path);
- }
-
- //
- //
- ofstreams mig_pre, mig_post;
- if (gen_sql_migration)
- {
- for (paths::size_type i (0); i < mig_pre_paths.size (); ++i)
- {
- shared_ptr<ofstream> pre (new (shared) ofstream);
- shared_ptr<ofstream> post (new (shared) ofstream);
-
- open (*pre, mig_pre_paths[i]);
- auto_rm.add (mig_pre_paths[i]);
- mig_pre.push_back (pre);
-
- open (*post, mig_post_paths[i]);
- auto_rm.add (mig_post_paths[i]);
- mig_post.push_back (post);
- }
- }
-
- // Print output file headers.
- //
- if (gen_cxx)
- {
- hxx << cxx_file_header;
- ixx << cxx_file_header;
-
- if (db != database::common)
- cxx << cxx_file_header;
- }
-
- if (gen_sep_schema)
- sch << cxx_file_header;
-
- if (gen_sql_schema)
- sql << sql_file_header;
-
- if (gen_sql_migration)
- {
- for (ofstreams::size_type i (0); i < mig_pre.size (); ++i)
- {
- *mig_pre[i] << sql_file_header;
- *mig_post[i] << sql_file_header;
- }
- }
-
- typedef compiler::ostream_filter<compiler::cxx_indenter, char> ind_filter;
- typedef compiler::ostream_filter<compiler::sloc_counter, char> sloc_filter;
-
- size_t sloc_total (0);
-
- // Include settings.
- //
- string gp (ops.guard_prefix ());
- if (!gp.empty () && gp[gp.size () - 1] != '_')
- gp.append ("_");
-
- // HXX
- //
- if (gen_cxx)
- {
- unique_ptr<context> ctx (
- create_context (hxx, unit, ops, fts, model.get ()));
-
- sloc_filter sloc (ctx->os);
-
- string guard (ctx->make_guard (gp + hxx_name));
-
- hxx << "#ifndef " << guard << endl
- << "#define " << guard << endl
- << endl;
-
- // Copy prologue.
- //
- append_logue (hxx,
- db,
- ops.hxx_prologue (),
- ops.hxx_prologue_file (),
- "// Begin prologue.\n//",
- "//\n// End prologue.");
-
- // Version check.
- //
- hxx << "#include <odb/version.hxx>" << endl
- << endl
- << "#if (ODB_VERSION != " << ODB_VERSION << "UL)" << endl
- << "#error ODB runtime version mismatch" << endl
- << "#endif" << endl
- << endl;
-
- hxx << "#include <odb/pre.hxx>" << endl
- << endl;
-
- // Include main file(s).
- //
- for (paths::const_iterator i (inputs.begin ()); i != inputs.end (); ++i)
- hxx << "#include " <<
- ctx->process_include_path (i->leaf ().string ()) << endl;
-
- hxx << endl;
-
- // There are no -odb.hxx includes if we are generating code for
- // everything.
- //
- if (!ops.at_once ())
- if (include::generate (true))
- hxx << endl;
-
- {
- // We don't want to indent prologues/epilogues.
- //
- ind_filter ind (ctx->os);
-
- switch (db)
- {
- case database::common:
- {
- header::generate ();
- break;
- }
- case database::mssql:
- case database::mysql:
- case database::oracle:
- case database::pgsql:
- case database::sqlite:
- {
- if (md == multi_database::disabled)
- header::generate ();
- else
- {
- string n (base +
- ops.odb_file_suffix ()[database::common] +
- ops.hxx_suffix ());
-
- ctx->os << "#include " << ctx->process_include_path (n) << endl
- << endl;
- }
-
- relational::header::generate ();
- break;
- }
- }
- }
-
- hxx << "#include " << ctx->process_include_path (ixx_name) << endl
- << endl;
-
- hxx << "#include <odb/post.hxx>" << endl
- << endl;
-
- // Copy epilogue.
- //
- append_logue (hxx,
- db,
- ops.hxx_epilogue (),
- ops.hxx_epilogue_file (),
- "// Begin epilogue.\n//",
- "//\n// End epilogue.");
-
- hxx << "#endif // " << guard << endl;
-
- if (ops.show_sloc ())
- cerr << hxx_name << ": " << sloc.stream ().count () << endl;
-
- sloc_total += sloc.stream ().count ();
- }
-
- // IXX
- //
- if (gen_cxx)
- {
- unique_ptr<context> ctx (
- create_context (ixx, unit, ops, fts, model.get ()));
-
- sloc_filter sloc (ctx->os);
-
- // Copy prologue.
- //
- append_logue (ixx,
- db,
- ops.ixx_prologue (),
- ops.ixx_prologue_file (),
- "// Begin prologue.\n//",
- "//\n// End prologue.");
-
- {
- // We don't want to indent prologues/epilogues.
- //
- ind_filter ind (ctx->os);
-
- switch (db)
- {
- case database::common:
- {
- inline_::generate ();
- break;
- }
- case database::mssql:
- case database::mysql:
- case database::oracle:
- case database::pgsql:
- case database::sqlite:
- {
- if (md == multi_database::disabled)
- inline_::generate ();
-
- relational::inline_::generate ();
- break;
- }
- }
- }
-
- // Copy epilogue.
- //
- append_logue (ixx,
- db,
- ops.ixx_epilogue (),
- ops.ixx_epilogue_file (),
- "// Begin epilogue.\n//",
- "//\n// End epilogue.");
-
- if (ops.show_sloc ())
- cerr << ixx_name << ": " << sloc.stream ().count () << endl;
-
- sloc_total += sloc.stream ().count ();
- }
-
- // CXX
- //
- if (gen_cxx && (db != database::common || md == multi_database::dynamic))
- {
- unique_ptr<context> ctx (
- create_context (cxx, unit, ops, fts, model.get ()));
-
- sloc_filter sloc (ctx->os);
-
- // Copy prologue.
- //
- append_logue (cxx,
- db,
- ops.cxx_prologue (),
- ops.cxx_prologue_file (),
- "// Begin prologue.\n//",
- "//\n// End prologue.");
-
- cxx << "#include <odb/pre.hxx>" << endl
- << endl;
-
- // Include query columns implementations for explicit instantiations.
- //
- string impl_guard;
- if (md == multi_database::dynamic && ctx->ext.empty ())
- {
- impl_guard = ctx->make_guard (
- "ODB_" + db.string () + "_QUERY_COLUMNS_DEF");
-
- cxx << "#define " << impl_guard << endl;
- }
-
- cxx << "#include " << ctx->process_include_path (hxx_name) << endl;
-
- // There are no -odb.hxx includes if we are generating code for
- // everything.
- //
- if (!ops.at_once ())
- include::generate (false);
-
- if (!impl_guard.empty ())
- cxx << "#undef " << impl_guard << endl;
-
- cxx << endl;
-
- {
- // We don't want to indent prologues/epilogues.
- //
- ind_filter ind (ctx->os);
-
- switch (db)
- {
- case database::common:
- {
- // Dynamic multi-database support.
- //
- source::generate ();
- break;
- }
- case database::mssql:
- case database::mysql:
- case database::oracle:
- case database::pgsql:
- case database::sqlite:
- {
- relational::source::generate ();
-
- if (gen_schema &&
- ops.schema_format ()[db].count (schema_format::embedded))
- relational::schema::generate_source (changelog.get ());
-
- break;
- }
- }
- }
-
- cxx << "#include <odb/post.hxx>" << endl;
-
- // Copy epilogue.
- //
- append_logue (cxx,
- db,
- ops.cxx_epilogue (),
- ops.cxx_epilogue_file (),
- "// Begin epilogue.\n//",
- "//\n// End epilogue.");
-
- if (ops.show_sloc ())
- cerr << cxx_name << ": " << sloc.stream ().count () << endl;
-
- sloc_total += sloc.stream ().count ();
- }
-
- // SCH
- //
- if (gen_sep_schema)
- {
- unique_ptr<context> ctx (
- create_context (sch, unit, ops, fts, model.get ()));
-
- sloc_filter sloc (ctx->os);
-
- // Copy prologue.
- //
- append_logue (sch,
- db,
- ops.schema_prologue (),
- ops.schema_prologue_file (),
- "// Begin prologue.\n//",
- "//\n// End prologue.");
-
- sch << "#include <odb/pre.hxx>" << endl
- << endl;
-
- sch << "#include <odb/database.hxx>" << endl
- << "#include <odb/schema-catalog-impl.hxx>" << endl
- << endl
- << "#include <odb/details/unused.hxx>" << endl
- << endl;
-
- {
- // We don't want to indent prologues/epilogues.
- //
- ind_filter ind (ctx->os);
-
- switch (db)
- {
- case database::mssql:
- case database::mysql:
- case database::oracle:
- case database::pgsql:
- case database::sqlite:
- {
- relational::schema::generate_source (changelog.get ());
- break;
- }
- case database::common:
- assert (false);
- }
- }
-
- sch << "#include <odb/post.hxx>" << endl;
-
- // Copy epilogue.
- //
- append_logue (sch,
- db,
- ops.schema_epilogue (),
- ops.schema_epilogue_file (),
- "// Begin epilogue.\n//",
- "//\n// End epilogue.");
-
- if (ops.show_sloc ())
- cerr << sch_name << ": " << sloc.stream ().count () << endl;
-
- sloc_total += sloc.stream ().count ();
- }
-
- // SQL
- //
- if (gen_sql_schema)
- {
- unique_ptr<context> ctx (
- create_context (sql, unit, ops, fts, model.get ()));
-
- switch (db)
- {
- case database::mssql:
- case database::mysql:
- case database::oracle:
- case database::pgsql:
- case database::sqlite:
- {
- // Prologue.
- //
- relational::schema::generate_prologue ();
- append_logue (sql,
- db,
- ops.sql_prologue (),
- ops.sql_prologue_file (),
- "/* Begin prologue.\n */",
- "/*\n * End prologue. */");
-
- if (!ops.omit_drop ())
- relational::schema::generate_drop ();
-
- // Interlude.
- //
- append_logue (sql,
- db,
- ops.sql_interlude (),
- ops.sql_interlude_file (),
- "/* Begin interlude.\n */",
- "/*\n * End interlude. */");
-
- if (!ops.omit_create ())
- relational::schema::generate_create ();
-
- // Epilogue.
- //
- append_logue (sql,
- db,
- ops.sql_epilogue (),
- ops.sql_epilogue_file (),
- "/* Begin epilogue.\n */",
- "/*\n * End epilogue. */");
- relational::schema::generate_epilogue ();
-
- break;
- }
- case database::common:
- assert (false);
- }
- }
-
- // MIG
- //
- if (gen_sql_migration)
- {
- for (ofstreams::size_type i (0); i < mig_pre.size (); ++i)
- {
- sema_rel::changeset& cs (
- changelog->contains_changeset_at (i).changeset ());
-
- // pre
- //
- {
- ofstream& mig (*mig_pre[i]);
- unique_ptr<context> ctx (create_context (mig, unit, ops, fts, 0));
-
- switch (db)
- {
- case database::mssql:
- case database::mysql:
- case database::oracle:
- case database::pgsql:
- case database::sqlite:
- {
- // Prologue.
- //
- relational::schema::generate_prologue ();
- append_logue (mig,
- db,
- ops.migration_prologue (),
- ops.migration_prologue_file (),
- "/* Begin prologue.\n */",
- "/*\n * End prologue. */");
-
- relational::schema::generate_migrate_pre (cs);
-
- // Epilogue.
- //
- append_logue (mig,
- db,
- ops.migration_epilogue (),
- ops.migration_epilogue_file (),
- "/* Begin epilogue.\n */",
- "/*\n * End epilogue. */");
- relational::schema::generate_epilogue ();
-
- break;
- }
- case database::common:
- assert (false);
- }
- }
-
- // post
- //
- {
- ofstream& mig (*mig_post[i]);
- unique_ptr<context> ctx (create_context (mig, unit, ops, fts, 0));
-
- switch (db)
- {
- case database::mssql:
- case database::mysql:
- case database::oracle:
- case database::pgsql:
- case database::sqlite:
- {
- // Prologue.
- //
- relational::schema::generate_prologue ();
- append_logue (mig,
- db,
- ops.migration_prologue (),
- ops.migration_prologue_file (),
- "/* Begin prologue.\n */",
- "/*\n * End prologue. */");
-
- relational::schema::generate_migrate_post (cs);
-
- // Epilogue.
- //
- append_logue (mig,
- db,
- ops.migration_epilogue (),
- ops.migration_epilogue_file (),
- "/* Begin epilogue.\n */",
- "/*\n * End epilogue. */");
- relational::schema::generate_epilogue ();
-
- break;
- }
- case database::common:
- assert (false);
- }
- }
- }
- }
-
- // Save the changelog if it has changed.
- //
- if (gen_changelog)
- {
- try
- {
- ostringstream os;
- os.exceptions (ifstream::badbit | ifstream::failbit);
- xml::serializer s (os, out_log_path.string ());
- changelog->serialize (s);
- string const& changelog_xml (os.str ());
-
- if (changelog_xml != old_changelog_xml)
- {
- ofstream log;
- open (log, out_log_path, ios_base::binary);
-
- if (old_changelog == 0)
- auto_rm.add (out_log_path);
-
- log.exceptions (ifstream::badbit | ifstream::failbit);
- log << changelog_xml;
- }
- }
- catch (const ios_base::failure& e)
- {
- cerr << out_log_path << ": write failure" << endl;
- throw generator_failed ();
- }
- catch (const xml::serialization& e)
- {
- cerr << e.what () << endl;
- throw generator_failed ();
- }
- }
-
- // Communicate the sloc count to the driver. This is necessary to
- // correctly handle the total if we are compiling multiple files in
- // one invocation.
- //
- if (ops.show_sloc () || ops.sloc_limit_specified ())
- cout << "odb:sloc:" << sloc_total << endl;
-
- auto_rm.cancel ();
- }
- catch (operation_failed const&)
- {
- // Code generation failed. Diagnostics has already been issued.
- //
- throw generator_failed ();
- }
- catch (semantics::invalid_path const& e)
- {
- cerr << "error: '" << e.path () << "' is not a valid filesystem path"
- << endl;
- throw generator_failed ();
- }
- catch (fs::error const&)
- {
- // Auto-removal of generated files failed. Ignore it.
- //
- throw generator_failed ();
- }
-}
diff --git a/odb/header.cxx b/odb/header.cxx
deleted file mode 100644
index fad28b3..0000000
--- a/odb/header.cxx
+++ /dev/null
@@ -1,900 +0,0 @@
-// file : odb/header.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <odb/common.hxx>
-#include <odb/context.hxx>
-#include <odb/generate.hxx>
-
-using namespace std;
-
-namespace header
-{
- struct class1: traversal::class_, virtual context
- {
- class1 ()
- : typedefs_ (false),
- query_columns_type_ (false, true, false),
- pointer_query_columns_type_ (true, true, false)
- {
- *this >> defines_ >> *this;
- *this >> typedefs_ >> *this;
- }
-
- virtual void
- traverse (type& c)
- {
- class_kind_type ck (class_kind (c));
-
- if (ck == class_other ||
- (!options.at_once () && class_file (c) != unit.file ()))
- return;
-
- names (c);
-
- switch (ck)
- {
- case class_object: traverse_object (c); break;
- case class_view: traverse_view (c); break;
- default: break;
- }
- }
-
- void
- traverse_object (type&);
-
- void
- traverse_view (type&);
-
- private:
- traversal::defines defines_;
- typedefs typedefs_;
-
- instance<query_columns_type> query_columns_type_;
- instance<query_columns_type> pointer_query_columns_type_;
- };
-}
-
-void header::class1::
-traverse_object (type& c)
-{
- using semantics::data_member;
-
- data_member_path* id (id_member (c));
- data_member* idf (id ? id->front () : 0);
- bool auto_id (id && auto_ (*id));
- bool base_id (id && &idf->scope () != &c); // Comes from base.
-
- data_member* opt (context::optimistic (c));
-
- type* poly_root (polymorphic (c));
- bool poly (poly_root != 0);
- bool poly_derived (poly && poly_root != &c);
- type* poly_base (poly_derived ? &polymorphic_base (c) : 0);
- data_member* discriminator (poly ? context::discriminator (*poly_root) : 0);
-
- bool abst (abstract (c));
- bool reuse_abst (abst && !poly);
-
- user_sections& uss (c.get<user_sections> ("user-sections"));
-
- string const& type (class_fq_name (c));
-
- os << "// " << class_name (c) << endl
- << "//" << endl;
-
- // class_traits
- //
- os << "template <>" << endl
- << "struct class_traits< " << type << " >"
- << "{"
- << "static const class_kind kind = class_object;"
- << "};";
-
- // object_traits
- //
- os << "template <>" << endl
- << "class " << exp << "access::object_traits< " << type << " >"
- << "{"
- << "public:" << endl;
-
- // object_type & pointer_type
- //
- os << "typedef " << type << " object_type;"
- << "typedef " << c.get<string> ("object-pointer") << " pointer_type;"
- << "typedef odb::pointer_traits<pointer_type> pointer_traits;"
- << endl;
-
- // polymorphic, root_type, base_type, etc.
- //
- os << "static const bool polymorphic = " << poly << ";"
- << endl;
-
- if (poly)
- {
- os << "typedef " << class_fq_name (*poly_root) << " root_type;";
-
- if (poly_derived)
- {
- os << "typedef " << class_fq_name (*poly_base) << " base_type;"
- << "typedef object_traits<root_type>::discriminator_type " <<
- "discriminator_type;"
- << "typedef polymorphic_concrete_info<root_type> info_type;";
-
- if (abst)
- os << "typedef polymorphic_abstract_info<root_type> " <<
- "abstract_info_type;";
-
- // Calculate our hierarchy depth (number of classes).
- //
- size_t depth (polymorphic_depth (c));
-
- os << endl
- << "static const std::size_t depth = " << depth << "UL;";
- }
- else
- {
- semantics::names* hint;
- semantics::type& t (utype (*discriminator, hint));
-
- os << "typedef " << t.fq_name (hint) << " discriminator_type;"
- << "typedef polymorphic_map<object_type> map_type;"
- << "typedef polymorphic_concrete_info<object_type> info_type;";
-
- if (abst)
- os << "typedef polymorphic_abstract_info<object_type> " <<
- "abstract_info_type;";
-
- os << endl
- << "static const std::size_t depth = 1UL;";
- }
-
- os << endl;
- }
-
- // id_type, version_type, etc.
- //
- if (id != 0)
- {
- if (base_id)
- {
- semantics::class_& b (dynamic_cast<semantics::class_&> (idf->scope ()));
- string const& type (class_fq_name (b));
-
- os << "typedef object_traits< " << type << " >::id_type id_type;";
-
- if (opt != 0)
- os << "typedef object_traits< " << type << " >::version_type " <<
- "version_type;";
-
- os << endl;
-
- if (poly_derived)
- os << "static const bool auto_id = false;";
- else
- os << "static const bool auto_id = object_traits< " << type <<
- " >::auto_id;";
- }
- else
- {
- {
- semantics::names* hint;
- semantics::type& t (utype (*id, hint));
- os << "typedef " << t.fq_name (hint) << " id_type;";
- }
-
- if (opt != 0)
- {
- semantics::names* hint;
- semantics::type& t (utype (*opt, hint));
- os << "typedef " << t.fq_name (hint) << " version_type;";
- }
-
- os << endl
- << "static const bool auto_id = " << auto_id << ";";
- }
-
- os << endl;
- }
- else if (!reuse_abst)
- {
- // Object without id.
- //
- os << "typedef void id_type;"
- << endl
- << "static const bool auto_id = false;"
- << endl;
- }
-
- // abstract
- //
- os << "static const bool abstract = " << abst << ";"
- << endl;
-
- // id()
- //
- if (id != 0 || !reuse_abst)
- {
- // We want to generate a dummy void id() accessor even if this
- // object has no id to help us in the runtime. This way we can
- // write generic code that will work for both void and non-void
- // ids.
- //
- os << "static id_type" << endl
- << "id (const object_type&);"
- << endl;
- }
-
- // version()
- //
- if (opt != 0)
- {
- os << "static version_type" << endl
- << "version (const object_type&);"
- << endl;
- }
-
- // Query.
- //
- if (options.generate_query ())
- {
- // Generate object pointer tags here if we are generating dynamic
- // multi-database support.
- //
- if (multi_dynamic && has_a (c, test_pointer | exclude_base))
- {
- query_tags t;
- t.traverse (c);
- }
- }
-
- // The rest does not apply to reuse-abstract objects.
- //
- if (!reuse_abst)
- {
- // Cache traits typedefs.
- //
- if (id == 0)
- {
- os << "typedef" << endl
- << "no_id_pointer_cache_traits<pointer_type>" << endl
- << "pointer_cache_traits;"
- << endl
- << "typedef" << endl
- << "no_id_reference_cache_traits<object_type>" << endl
- << "reference_cache_traits;"
- << endl;
- }
- else
- {
- char const* obj (poly_derived ? "root_type" : "object_type");
- char const* ptr (poly_derived
- ? "object_traits<root_type>::pointer_type"
- : "pointer_type");
- if (session (c))
- {
- string const& s (options.session_type ());
-
- os << "typedef" << endl
- << "odb::pointer_cache_traits<" << endl
- << " " << ptr << "," << endl
- << " " << s << " >" << endl
- << "pointer_cache_traits;"
- << endl
- << "typedef" << endl
- << "odb::reference_cache_traits<" << endl
- << " " << obj << "," << endl
- << " " << s << " >" << endl
- << "reference_cache_traits;"
- << endl;
- }
- else
- {
- os << "typedef" << endl
- << "no_op_pointer_cache_traits<" << ptr << ">" << endl
- << "pointer_cache_traits;"
- << endl
- << "typedef" << endl
- << "no_op_reference_cache_traits<" << obj << ">" << endl
- << "reference_cache_traits;"
- << endl;
- }
- }
-
- // callback ()
- //
- os << "static void" << endl
- << "callback (database&, object_type&, callback_event);"
- << endl;
-
- os << "static void" << endl
- << "callback (database&, const object_type&, callback_event);"
- << endl;
- }
-
- os << "};";
-
- // The rest only applies to dynamic milti-database support.
- //
- if (!multi_dynamic)
- return;
-
- // pointer_query_columns & query_columns
- //
- if (options.generate_query ())
- {
- // If we don't have object pointers, then also generate
- // query_columns (in this case pointer_query_columns and
- // query_columns are the same and the former inherits from
- // the latter). Otherwise we have to postpone query_columns
- // generation until the second pass to deal with forward-
- // declared objects.
- //
- if (!has_a (c, test_pointer | include_base))
- query_columns_type_->traverse (c);
-
- pointer_query_columns_type_->traverse (c);
- }
-
- // object_traits_impl
- //
- os << "template <>" << endl
- << "class " << exp << "access::object_traits_impl< " << type << ", " <<
- "id_common >:" << endl
- << " public access::object_traits< " << type << " >"
- << "{";
-
- // We don't need to generate anything else for reuse-abstract objects.
- //
- if (reuse_abst)
- {
- os << "};";
- return;
- }
-
- os << "public:" << endl;
-
- if (options.generate_query ())
- {
- // base_traits is needed for query support.
- //
- if (poly_derived)
- os << "typedef object_traits_impl<base_type, id_common> base_traits;"
- << endl;
-
- // query_base_type
- //
- os << "typedef odb::query_base query_base_type;"
- << endl;
- }
-
- // function_table_type
- //
- os << "struct function_table_type"
- << "{";
-
- // persist ()
- //
- os << "void (*persist) (database&, " << (auto_id ? "" : "const ") <<
- "object_type&" << (poly ? ", bool, bool" : "") << ");";
-
- if (id != 0)
- {
- // find (id)
- //
- if (c.default_ctor ())
- os << "pointer_type (*find1) (database&, const id_type&);";
-
- // find (id, obj)
- //
- os << "bool (*find2) (database&, const id_type&, object_type&" <<
- (poly ? ", bool" : "") << ");";
-
- // reload ()
- //
- os << "bool (*reload) (database&, object_type&" <<
- (poly ? ", bool" : "") << ");";
-
- // update ()
- //
- if (!readonly (c) || poly)
- {
- os << "void (*update) (database&, const object_type&" <<
- (poly ? ", bool, bool" : "") << ");";
- }
-
- // erase ()
- //
- os << "void (*erase1) (database&, const id_type&" <<
- (poly ? ", bool, bool" : "") << ");";
-
- os << "void (*erase2) (database&, const object_type&" <<
- (poly ? ", bool, bool" : "") << ");";
-
- // Sections.
- //
- if (uss.count (user_sections::count_total |
- user_sections::count_load |
- (poly ? user_sections::count_load_empty : 0)) != 0)
- os << "bool (*load_section) (connection&, object_type&, section&" <<
- (poly ? ", const info_type*" : "") << ");";
-
- if (uss.count (user_sections::count_total |
- user_sections::count_update |
- (poly ? user_sections::count_update_empty : 0)) != 0)
- os << "bool (*update_section) (connection&, const object_type&, " <<
- "const section&" << (poly ? ", const info_type*" : "") << ");";
- }
-
- if (options.generate_query ())
- {
- if (!options.omit_unprepared ())
- os << "result<object_type> (*query) (database&, const query_base_type&);";
-
- os << "unsigned long long (*erase_query) (database&, " <<
- "const query_base_type&);";
-
- if (options.generate_prepared ())
- {
- os << "odb::details::shared_ptr<prepared_query_impl> " <<
- "(*prepare_query) (connection&, const char*, const query_base_type&);";
-
- os << "odb::details::shared_ptr<result_impl> (*execute_query) ("
- "prepared_query_impl&);";
- }
- }
-
- os << "};" // function_table_type
- << "static const function_table_type* function_table[database_count];"
- << endl;
-
- //
- // Forwarding functions.
- //
-
- // persist ()
- //
- os << "static void" << endl
- << "persist (database&, " << (auto_id ? "" : "const ") << "object_type&);"
- << endl;
-
- if (id != 0)
- {
- // find (id)
- //
- if (c.default_ctor ())
- os << "static pointer_type" << endl
- << "find (database&, const id_type&);"
- << endl;
-
- // find (id, obj)
- //
- os << "static bool" << endl
- << "find (database&, const id_type&, object_type&);"
- << endl;
-
- // reload ()
- //
- os << "static bool" << endl
- << "reload (database&, object_type&);"
- << endl;
-
- // update ()
- //
- if (!readonly (c) || poly)
- {
- os << "static void" << endl
- << "update (database&, const object_type&);"
- << endl;
- }
-
- // erase ()
- //
- os << "static void" << endl
- << "erase (database&, const id_type&);"
- << endl;
-
- os << "static void" << endl
- << "erase (database&, const object_type&);"
- << endl;
-
- // Sections.
- //
- if (uss.count (user_sections::count_total |
- user_sections::count_load |
- (poly ? user_sections::count_load_empty : 0)) != 0)
- os << "static bool" << endl
- << "load (connection&, object_type&, section&);"
- << endl;
-
- if (uss.count (user_sections::count_total |
- user_sections::count_update |
- (poly ? user_sections::count_update_empty : 0)) != 0)
- os << "static bool" << endl
- << "update (connection&, const object_type&, const section&);"
- << endl;
- }
-
- if (options.generate_query ())
- {
- if (!options.omit_unprepared ())
- {
- os << "static result<object_type>" << endl
- << "query (database&, const query_base_type&);"
- << endl;
- }
-
- os << "static unsigned long long" << endl
- << "erase_query (database&, const query_base_type&);"
- << endl;
-
- if (options.generate_prepared ())
- {
- os << "static odb::details::shared_ptr<prepared_query_impl>" << endl
- << "prepare_query (connection&, const char*, const query_base_type&);"
- << endl;
-
- os << "static odb::details::shared_ptr<result_impl>" << endl
- << "execute_query (prepared_query_impl&);"
- << endl;
- }
- }
-
- os << "};"; // object_traits_impl
-}
-
-void header::class1::
-traverse_view (type& c)
-{
- string const& type (class_fq_name (c));
-
- os << "// " << class_name (c) << endl
- << "//" << endl;
-
- // class_traits
- //
- os << "template <>" << endl
- << "struct class_traits< " << type << " >"
- << "{"
- << "static const class_kind kind = class_view;"
- << "};";
-
- // view_traits
- //
- os << "template <>" << endl
- << "class " << exp << "access::view_traits< " << type << " >"
- << "{"
- << "public:" << endl;
-
- // view_type & pointer_type
- //
- os << "typedef " << type << " view_type;"
- << "typedef " << c.get<string> ("object-pointer") << " pointer_type;"
- << endl;
-
- // Generate associated object tags here if we are generating dynamic
- // multi-database support.
- //
- if (multi_dynamic)
- {
- query_tags t;
- t.traverse (c);
- }
-
- // callback ()
- //
- os << "static void" << endl
- << "callback (database&, view_type&, callback_event);"
- << endl;
-
- os << "};";
-
- // The rest only applies to dynamic milti-database support.
- //
- if (!multi_dynamic)
- return;
-
- size_t obj_count (c.get<size_t> ("object-count"));
-
- // view_traits_impl
- //
- os << "template <>" << endl
- << "class " << exp << "access::view_traits_impl< " << type << ", " <<
- "id_common >:" << endl
- << " public access::view_traits< " << type << " >"
- << "{"
- << "public:" << endl;
-
- // query_base_type and query_columns (definition generated by class2).
- //
- os << "typedef odb::query_base query_base_type;"
- << "struct query_columns";
-
- if (obj_count == 0)
- os << "{"
- << "};";
- else
- os << ";"
- << endl;
-
- // function_table_type
- //
- os << "struct function_table_type"
- << "{";
-
- if (!options.omit_unprepared ())
- os << "result<view_type> (*query) (database&, const query_base_type&);"
- << endl;
-
- if (options.generate_prepared ())
- {
- os << "odb::details::shared_ptr<prepared_query_impl> " <<
- "(*prepare_query) (connection&, const char*, const query_base_type&);"
- << endl;
-
- os << "odb::details::shared_ptr<result_impl> (*execute_query) ("
- "prepared_query_impl&);"
- << endl;
- }
-
- os << "};" // function_table_type
- << "static const function_table_type* function_table[database_count];"
- << endl;
-
- //
- // Forwarding functions.
- //
-
- if (!options.omit_unprepared ())
- os << "static result<view_type>" << endl
- << "query (database&, const query_base_type&);"
- << endl;
-
- if (options.generate_prepared ())
- {
- os << "static odb::details::shared_ptr<prepared_query_impl>" << endl
- << "prepare_query (connection&, const char*, const query_base_type&);"
- << endl;
-
- os << "static odb::details::shared_ptr<result_impl>" << endl
- << "execute_query (prepared_query_impl&);"
- << endl;
- }
-
- os << "};";
-}
-
-namespace header
-{
- struct class2: traversal::class_, virtual context
- {
- class2 ()
- : typedefs_ (false),
- query_columns_type_ (false, true, false),
- query_columns_type_inst_ (false, false, true),
- view_query_columns_type_ (true)
- {
- *this >> defines_ >> *this;
- *this >> typedefs_ >> *this;
- }
-
- virtual void
- traverse (type& c)
- {
- class_kind_type ck (class_kind (c));
-
- if (ck == class_other ||
- (!options.at_once () && class_file (c) != unit.file ()))
- return;
-
- names (c);
-
- switch (ck)
- {
- case class_object: traverse_object (c); break;
- case class_view: traverse_view (c); break;
- default: break;
- }
- }
-
- void
- traverse_object (type&);
-
- void
- traverse_view (type&);
-
- private:
- traversal::defines defines_;
- typedefs typedefs_;
-
- instance<query_columns_type> query_columns_type_;
- instance<query_columns_type> query_columns_type_inst_;
- instance<view_query_columns_type> view_query_columns_type_;
- };
-}
-
-void header::class2::
-traverse_object (type& c)
-{
- if (options.generate_query ())
- {
- os << "// " << class_name (c) << endl
- << "//" << endl;
-
- // query_columns
- //
- // If we don't have any pointers, then query_columns is generated
- // in pass 1 (see the comment in class1 for details).
- //
- if (has_a (c, test_pointer | include_base))
- query_columns_type_->traverse (c);
-
- // Generate extern template declarations.
- //
- query_columns_type_inst_->traverse (c);
- }
-
- // Move header comment out of if-block if adding any code here.
-}
-
-void header::class2::
-traverse_view (type& c)
-{
- // query_columns
- //
- if (c.get<size_t> ("object-count") != 0)
- {
- os << "// " << class_name (c) << endl
- << "//" << endl;
-
- view_query_columns_type_->traverse (c);
- }
-
- // Move header comment out of if-block if adding any code here.
-}
-
-namespace header
-{
- void
- generate ()
- {
- context ctx;
- ostream& os (ctx.os);
-
- os << "#include <memory>" << endl
- << "#include <cstddef>" << endl; // std::size_t
-
- if (ctx.features.polymorphic_object)
- os << "#include <string>" << endl; // For discriminator.
-
- if (ctx.options.std () >= cxx_version::cxx11)
- os << "#include <utility>" << endl; // move()
-
- os << endl;
-
- os << "#include <odb/core.hxx>" << endl
- << "#include <odb/traits.hxx>" << endl
- << "#include <odb/callback.hxx>" << endl
- << "#include <odb/wrapper-traits.hxx>" << endl
- << "#include <odb/pointer-traits.hxx>" << endl;
-
-#ifndef ODB_BUILD2
- if (ctx.options.std () == cxx_version::cxx98)
- {
- // In case of a boost TR1 implementation, we cannot distinguish
- // between the boost::shared_ptr and std::tr1::shared_ptr usage since
- // the latter is just a using-declaration for the former. To resolve
- // this we will include TR1 traits if the Boost TR1 header is included.
- //
- if (ctx.features.tr1_pointer)
- {
- os << "#include <odb/tr1/wrapper-traits.hxx>" << endl
- << "#include <odb/tr1/pointer-traits.hxx>" << endl;
- }
- else if (ctx.features.boost_pointer)
- {
- os << "#ifdef BOOST_TR1_MEMORY_HPP_INCLUDED" << endl
- << "# include <odb/tr1/wrapper-traits.hxx>" << endl
- << "# include <odb/tr1/pointer-traits.hxx>" << endl
- << "#endif" << endl;
- }
- }
-#endif
-
- os << "#include <odb/container-traits.hxx>" << endl;
-
- if (ctx.features.session_object)
- {
- if (ctx.options.session_type () == "odb::session")
- os << "#include <odb/session.hxx>" << endl;
-
- os << "#include <odb/cache-traits.hxx>" << endl;
- }
- else
- os << "#include <odb/no-op-cache-traits.hxx>" << endl;
-
- if (ctx.features.polymorphic_object)
- os << "#include <odb/polymorphic-info.hxx>" << endl;
-
- if (ctx.options.generate_query ())
- {
- if (ctx.multi_dynamic)
- os << "#include <odb/query-dynamic.hxx>" << endl;
-
- if (ctx.options.generate_prepared ())
- os << "#include <odb/prepared-query.hxx>" << endl;
-
- os << "#include <odb/result.hxx>" << endl;
-
- if (ctx.features.simple_object)
- os << "#include <odb/simple-object-result.hxx>" << endl;
-
- if (ctx.features.polymorphic_object)
- os << "#include <odb/polymorphic-object-result.hxx>" << endl;
-
- if (ctx.features.no_id_object)
- os << "#include <odb/no-id-object-result.hxx>" << endl;
-
- if (ctx.features.view)
- os << "#include <odb/view-image.hxx>" << endl
- << "#include <odb/view-result.hxx>" << endl;
- }
-
- os << endl
- << "#include <odb/details/unused.hxx>" << endl;
-
- if (ctx.options.generate_query ())
- os << "#include <odb/details/shared-ptr.hxx>" << endl;
-
- os << endl;
-
- os << "namespace odb"
- << "{";
-
- // Generate common code.
- //
- {
- traversal::unit unit;
- traversal::defines unit_defines;
- typedefs unit_typedefs (false);
- traversal::namespace_ ns;
- class1 c;
-
- unit >> unit_defines >> ns;
- unit_defines >> c;
- unit >> unit_typedefs >> c;
-
- traversal::defines ns_defines;
- typedefs ns_typedefs (false);
-
- ns >> ns_defines >> ns;
- ns_defines >> c;
- ns >> ns_typedefs >> c;
-
- unit.dispatch (ctx.unit);
- }
-
- if (ctx.multi_dynamic)
- {
- traversal::unit unit;
- traversal::defines unit_defines;
- typedefs unit_typedefs (false);
- traversal::namespace_ ns;
- class2 c;
-
- unit >> unit_defines >> ns;
- unit_defines >> c;
- unit >> unit_typedefs >> c;
-
- traversal::defines ns_defines;
- typedefs ns_typedefs (false);
-
- ns >> ns_defines >> ns;
- ns_defines >> c;
- ns >> ns_typedefs >> c;
-
- unit.dispatch (ctx.unit);
- }
-
- os << "}";
- }
-}
diff --git a/odb/location.hxx b/odb/location.hxx
deleted file mode 100644
index b16018b..0000000
--- a/odb/location.hxx
+++ /dev/null
@@ -1,25 +0,0 @@
-// file : odb/location.hxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#ifndef ODB_LOCATION_HXX
-#define ODB_LOCATION_HXX
-
-#include <odb/gcc-fwd.hxx>
-
-#include <cstddef>
-#include <cutl/fs/path.hxx>
-
-struct location
-{
- location (location_t);
- location (cutl::fs::path const& f, std::size_t l, std::size_t c)
- : file (f), line (l), column (c)
- {
- }
-
- cutl::fs::path file;
- std::size_t line;
- std::size_t column;
-};
-
-#endif // ODB_LOCATION_HXX
diff --git a/odb/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.cxx b/odb/odb.cxx
deleted file mode 100644
index 225cc21..0000000
--- a/odb/odb.cxx
+++ /dev/null
@@ -1,2032 +0,0 @@
-// file : odb/odb.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <errno.h>
-#include <stdlib.h> // getenv, setenv
-#include <string.h> // strerror, memset
-#include <unistd.h> // stat, close
-#include <sys/types.h> // stat
-#include <sys/stat.h> // stat
-
-// Process.
-//
-#ifndef _WIN32
-# include <unistd.h> // execvp, fork, dup2, pipe, {STDIN,STDERR}_FILENO
-# include <sys/types.h> // waitpid
-# include <sys/wait.h> // waitpid
-#else
-# ifndef WIN32_LEAN_AND_MEAN
-# define WIN32_LEAN_AND_MEAN
-# endif
-# include <windows.h> // CreatePipe, CreateProcess
-# include <io.h> // _open_osfhandle
-# include <fcntl.h> // _O_TEXT
-#endif
-
-#include <string>
-#include <vector>
-#include <cstddef> // size_t
-#include <sstream>
-#include <fstream>
-#include <iostream>
-#include <ext/stdio_filebuf.h>
-
-#include <cutl/fs/path.hxx>
-
-#include <odb/version.hxx>
-#include <odb/options.hxx>
-#include <odb/profile.hxx>
-
-#ifdef HAVE_CONFIG_H
-# include <odb/config.h>
-#endif
-
-using namespace std;
-using cutl::fs::path;
-using cutl::fs::invalid_path;
-
-typedef vector<string> strings;
-typedef vector<path> paths;
-
-//
-// Path manipulation.
-//
-
-// Escape backslashes in the path.
-//
-static string
-escape_path (string const&);
-
-// Search the PATH environment variable for the file.
-//
-static path
-path_search (path const&);
-
-// Driver path with the directory part (search PATH).
-//
-static path
-driver_path (path const& driver);
-
-#ifndef ODB_STATIC_PLUGIN
-static path
-plugin_path (path const& driver, string const& gxx);
-#endif
-
-//
-// Process manipulation.
-//
-struct process_info
-{
-#ifndef _WIN32
- pid_t id;
-#else
- HANDLE id;
-#endif
-
- int out_fd; // Write to this fd to send to the new process' stdin.
- int in_efd; // Read from this fd to receive from the new process' stderr.
- int in_ofd; // Read from this fd to receive from the new process' stdout.
-};
-
-struct process_failure {};
-
-// Start another process using the specified command line. Connect the
-// newly created process' stdin to out_fd. Also if connect_* are true,
-// connect the created process' stdout and stderr to in_*fd. Issue
-// diagnostics and throw process_failure if anything goes wrong. The
-// name argument is the name of the current process for diagnostics.
-//
-static process_info
-start_process (char const* args[],
- char const* name,
- bool connect_stderr = false,
- bool connect_stdout = false);
-
-// Wait for the process to terminate. Return true if the process terminated
-// normally and with the zero exit status. Issue diagnostics and throw
-// process_failure if anything goes wrong. The name argument is the name
-// of the current process for diagnostics.
-//
-static bool
-wait_process (process_info, char const* name);
-
-//
-//
-static string
-encode_plugin_flag (string const& k);
-
-static string
-encode_plugin_option (string const& k, string const& v);
-
-// Extract header search paths from GCC's -v output. May throw the
-// profile_failure, process_failure and invalid_path exceptions. Name
-// is the program name (argv[0]) for diagnostics.
-//
-static paths
-profile_paths (strings const& args, char const* name);
-
-static char const* const db_macro[] =
-{
- "-DODB_DATABASE_COMMON",
- "-DODB_DATABASE_MSSQL",
- "-DODB_DATABASE_MYSQL",
- "-DODB_DATABASE_ORACLE",
- "-DODB_DATABASE_PGSQL",
- "-DODB_DATABASE_SQLITE"
-};
-
-int
-main (int argc, char* argv[])
-{
- ostream& e (cerr);
-
- try
- {
- strings args, plugin_args;
- bool v (false);
-
- // The first argument points to the program name, which is
- // g++ by default.
- //
-#ifdef ODB_GXX_NAME
- path gxx (ODB_GXX_NAME);
-
- if (gxx.empty ())
- {
- e << argv[0] << ": error: embedded g++ compile name is empty" << endl;
- return 1;
- }
-
- // If the g++ name is a relative path (starts with '.'), then use
- // our own path as base.
- //
- if (gxx.string ()[0] == '.')
- {
- path dp (driver_path (path (argv[0])));
- path d (dp.directory ());
-
- if (!d.empty ())
- gxx = d / gxx;
- }
-
- args.push_back (gxx.string ());
-
- // Also modify LD_LIBRARY_PATH to include the lib path.
- //
-#ifndef _WIN32
- {
-#ifdef __APPLE__
- char const name[] = "DYLD_LIBRARY_PATH";
-#else
- char const name[] = "LD_LIBRARY_PATH";
-#endif
-
- string ld_paths;
-
- if (char const* s = getenv (name))
- ld_paths = s;
-
- path d (gxx.directory ());
-
- if (!d.empty ())
- {
- d.complete ();
- d /= path ("..") / path ("lib");
-
- if (ld_paths.empty ())
- ld_paths = d.string ();
- else
- ld_paths = d.string () + path::traits::path_separator + ld_paths;
-
- if (setenv (name, ld_paths.c_str (), 1) != 0)
- {
- e << argv[0] << ": error: unable to update environment" << endl;
- return 1;
- }
- }
- }
-#endif // _WIN32
-
-#else
- args.push_back ("g++");
-#endif // ODB_GXX_NAME
-
- // Default options.
- //
- args.push_back ("-x");
- args.push_back ("c++");
- args.push_back (""); // Reserve space for -std=c++XX.
- args.push_back ("-S");
- args.push_back ("-Wunknown-pragmas");
- args.push_back ("-Wno-deprecated");
- args.push_back (""); // Reserve space for -fplugin=path.
-
- // Parse the default options file if we have one.
- //
- strings def_inc_dirs;
- strings def_defines;
-#ifdef ODB_DEFAULT_OPTIONS_FILE
- {
- path file (ODB_DEFAULT_OPTIONS_FILE);
-
- // If the path is relative, then use the driver's path as a base. If
- // the file is not found in that directory, then also try outer
- // directory (so that we can find /etc if driver is in /usr/bin).
- //
- if (file.relative ())
- {
- bool found (false);
- path dd (driver_path (path (argv[0])).directory ());
-
- for (path d (dd);; d = d.directory ())
- {
- path f (d / file);
- // Check that the file exist without checking for permissions, etc.
- //
- struct stat s;
- if (stat (f.string ().c_str (), &s) == 0 && S_ISREG (s.st_mode))
- {
- file = f;
- found = true;
- break;
- }
-
- if (d.root ())
- break;
- }
-
- if (!found)
- file = dd / file; // For diagnostics.
- }
-
- 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");
-
- bool first_x (true);
-
- while (s.more ())
- {
- string a (s.next ());
- size_t n (a.size ());
-
- // -x
- //
- if (a == "-x")
- {
- if (!s.more () || (a = s.next ()).empty ())
- {
- e << file << ": error: expected argument for the " << a
- << " option" << endl;
- return 1;
- }
-
- if (first_x)
- {
- first_x = false;
-
- // If it doesn't start with '-', then it must be the g++
- // executable name. Update the first argument with it.
- //
- if (a[0] != '-')
- args[0] = a;
- else
- args.push_back (a);
- }
- else
- args.push_back (a);
- }
- // -I
- //
- else if (n > 1 && a[0] == '-' && a[1] == 'I')
- {
- def_inc_dirs.push_back (a);
-
- if (n == 2) // -I /path
- {
- if (!s.more () || (a = s.next ()).empty ())
- {
- e << file << ": error: expected argument for the -I option"
- << endl;
- return 1;
- }
-
- def_inc_dirs.push_back (a);
- }
- }
- // -isystem, -iquote, -idirafter, and -framework (Mac OS X)
- //
- else if (a == "-isystem" ||
- a == "-iquote" ||
- a == "-idirafter" ||
- a == "-framework")
- {
- def_inc_dirs.push_back (a);
-
- if (!s.more () || (a = s.next ()).empty ())
- {
- e << file << ": error: expected argument for the " << a
- << " option" << endl;
- return 1;
- }
-
- def_inc_dirs.push_back (a);
- }
- // -D
- //
- else if (n > 1 && a[0] == '-' && a[1] == 'D')
- {
- def_defines.push_back (a);
-
- if (n == 2) // -D macro
- {
- if (!s.more () || (a = s.next ()).empty ())
- {
- e << file << ": error: expected argument for the -D option"
- << endl;
- return 1;
- }
-
- def_defines.push_back (a);
- }
- }
- // -U
- //
- else if (n > 1 && a[0] == '-' && a[1] == 'U')
- {
- def_defines.push_back (a);
-
- if (n == 2) // -U macro
- {
- if (!s.more () || (a = s.next ()).empty ())
- {
- e << file << ": error: expected argument for the -U option"
- << endl;
- return 1;
- }
-
- def_defines.push_back (a);
- }
- }
- else
- plugin_args.push_back (a);
- }
- }
-#endif
-
- // Add the default preprocessor defines (-D/-U) before the user-supplied
- // ones.
- //
- args.insert (args.end (), def_defines.begin (), def_defines.end ());
-
- // Parse driver options.
- //
- bool first_x (true);
-
- for (int i = 1; i < argc; ++i)
- {
- string a (argv[i]);
- size_t n (a.size ());
-
- // -v
- //
- if (a == "-v")
- {
- v = true;
- args.push_back (a);
- }
- // -x
- //
- else if (a == "-x")
- {
- if (++i == argc || argv[i][0] == '\0')
- {
- e << argv[0] << ": error: expected argument for the -x option" << endl;
- return 1;
- }
-
- a = argv[i];
-
- if (first_x)
- {
- first_x = false;
-
- // If it doesn't start with '-', then it must be the g++
- // executable name. Update the first argument with it.
- //
- if (a[0] != '-')
- args[0] = a;
- else
- args.push_back (a);
- }
- else
- args.push_back (a);
- }
- // -I
- //
- else if (n > 1 && a[0] == '-' && a[1] == 'I')
- {
- args.push_back (a);
-
- if (n == 2) // -I /path
- {
- if (++i == argc || argv[i][0] == '\0')
- {
- e << argv[0] << ": error: expected argument for the -I option"
- << endl;
- return 1;
- }
-
- args.push_back (argv[i]);
- }
- }
- // -isystem, -iquote, -idirafter, and -framework (Mac OS X)
- //
- else if (a == "-isystem" ||
- a == "-iquote" ||
- a == "-idirafter" ||
- a == "-framework")
- {
- args.push_back (a);
-
- if (++i == argc || argv[i][0] == '\0')
- {
- e << argv[0] << ": error: expected argument for the " << a
- << " option" << endl;
- return 1;
- }
-
- args.push_back (argv[i]);
- }
- // -D
- //
- else if (n > 1 && a[0] == '-' && a[1] == 'D')
- {
- args.push_back (a);
-
- if (n == 2) // -D macro
- {
- if (++i == argc || argv[i][0] == '\0')
- {
- e << argv[0] << ": error: expected argument for the -D option"
- << endl;
- return 1;
- }
-
- args.push_back (argv[i]);
- }
- }
- // -U
- //
- else if (n > 1 && a[0] == '-' && a[1] == 'U')
- {
- args.push_back (a);
-
- if (n == 2) // -U macro
- {
- if (++i == argc || argv[i][0] == '\0')
- {
- e << argv[0] << ": error: expected argument for the -U option"
- << endl;
- return 1;
- }
-
- args.push_back (argv[i]);
- }
- }
- // Store everything else in a list so that we can parse it with the
- // cli parser. This is the only reliable way to find out where the
- // options end.
- //
- else
- plugin_args.push_back (a);
- }
-
- // Add the default include directories (-I) after the user-supplied
- // ones.
- //
- args.insert (args.end (), def_inc_dirs.begin (), def_inc_dirs.end ());
-
- // Find the plugin.
- //
- {
-#ifndef ODB_STATIC_PLUGIN
- path plugin (plugin_path (path (argv[0]), args[0]));
-#else
- // Use a dummy name if the plugin is linked into the compiler.
- //
- path plugin ("odb");
-#endif
-
- if (plugin.empty ())
- return 1; // Diagnostics has already been issued.
-
-#ifdef ODB_BUILD2
-#ifdef _WIN32
- // Here is the problem: since the plugin is loaded by GCC (cc1plus.exe
- // to be precise), the DLL assembly magic we have for executables won't
- // help here.
- //
- // To allow executing the ODB compiler in-place we add the odb.exe.dlls/
- // directory to PATH. It is a bit of hack but then DLL assemblies for
- // DLLs is whole new level of insanity that we are unlikely to ever
- // touch.
- //
- // And it turns out we have the same problem in the installed case: if
- // the installation directory is not in PATH, then GCC won't find the
- // DLLs the plugin needs. So we handle both here.
- //
- {
- path d (plugin.directory ());
- d.complete ();
- d.normalize ();
- d /= path ("odb.exe.dlls");
-
- struct stat st;
- if (stat (d.string ().c_str (), &st) != 0 || !S_ISDIR (st.st_mode))
- d = d.directory ();
-
- string v ("PATH=" + d.string ());
-
- if (char const* p = getenv ("PATH"))
- {
- v += ';';
- v += p;
- }
-
- _putenv (v.c_str ());
- }
-#endif
-#endif
-
- args[7] = "-fplugin=" + plugin.string ();
- }
-
- // Parse plugin options. We have to do it twice to get the target
- // database which is needed while loading profiles.
- //
- vector<char*> av;
- av.push_back (argv[0]);
-
- for (strings::iterator i (plugin_args.begin ()), end (plugin_args.end ());
- i != end; ++i)
- {
- av.push_back (const_cast<char*> (i->c_str ()));
- }
-
- int ac (static_cast<int> (av.size ()));
-
- cli::argv_file_scanner::option_info oi[3];
- oi[0].option = "--options-file";
- oi[0].search_func = 0;
- oi[1].option = "-p";
- oi[2].option = "--profile";
-
- vector<database> dbs;
- bool show_sloc;
- size_t sloc_limit;
- {
- oi[1].search_func = &profile_search_ignore;
- oi[2].search_func = &profile_search_ignore;
-
- cli::argv_file_scanner scan (ac, &av[0], oi, 3);
- options ops (scan);
-
- // Handle --build2-metadata (see also buildfile).
- //
- if (ops.build2_metadata_specified ())
- {
- ostream& o (cout);
-
- // Note that the export.metadata variable should be the first non-
- // blank/comment line.
- //
- o << "# build2 buildfile odb" << endl
- << "export.metadata = 1 odb" << endl
- << "odb.name = [string] odb" << endl
- << "odb.version = [string] '" << ODB_COMPILER_VERSION_STR << '\'' << endl
- << "odb.checksum = [string] '" << ODB_COMPILER_VERSION_STR << '\'' << endl;
-
- return 0;
- }
-
- // Handle --version.
- //
- if (ops.version ())
- {
- ostream& o (cout);
-
- o << "ODB object-relational mapping (ORM) compiler for C++ "
- ODB_COMPILER_VERSION_STR << endl;
-
-#ifdef ODB_BUILD2
- o << "Copyright (c) " << ODB_COPYRIGHT << "." << endl;
-#endif
-
- o << "This is free software; see the source for copying conditions. "
- << "There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS "
- << "FOR A PARTICULAR PURPOSE." << endl;
-
- return 0;
- }
-
- // Handle --help.
- //
- if (ops.help ())
- {
- ostream& o (cout);
-
- o << "Usage: " << argv[0] << " [options] file [file ...]" << endl
- << "Options:" << endl;
-
- options::print_usage (cout);
- return 0;
- }
-
- // Check that required options were specifed.
- //
- dbs = ops.database ();
-
- if (dbs.empty ())
- {
- e << argv[0] << ": error: no database specified with the --database "
- << "option" << endl;
- return 1;
- }
-
- if (dbs.size () > 1 && !ops.multi_database_specified ())
- {
- e << argv[0] << ": error: --multi-database option required when " <<
- "multiple databases are specified"<< endl;
- return 1;
- }
-
- show_sloc = ops.show_sloc ();
- sloc_limit = ops.sloc_limit_specified () ? ops.sloc_limit () : 0;
-
- // Translate some ODB options to GCC options.
- //
- switch (ops.std ())
- {
- case cxx_version::cxx98:
- {
- args[3] = "-std=c++98";
- break;
- }
- case cxx_version::cxx11:
- {
- args[3] = "-std=c++0x"; // c++11 was only added in GCC 4.7.0.
- break;
- }
- case cxx_version::cxx14:
- {
- args[3] = "-std=c++1y";
- break;
- }
- case cxx_version::cxx17:
- {
- args[3] = "-std=c++1z";
- break;
- }
- }
- }
-
- // Obtain profile (-I) search paths.
- //
- paths prof_paths (profile_paths (args, argv[0]));
-
- if (v)
- {
- e << "Profile search paths:" << endl;
-
- for (paths::const_iterator i (prof_paths.begin ());
- i != prof_paths.end (); ++i)
- e << " " << *i << endl;
- }
-
- // Pass profile search paths (svc-path option).
- //
- for (paths::const_iterator i (prof_paths.begin ());
- i != prof_paths.end (); ++i)
- {
- args.push_back (encode_plugin_option ("svc-path", i->string ()));
- }
-
- // Add common ODB macros.
- //
- args.push_back ("-DODB_COMPILER");
-
- {
- ostringstream ostr;
- ostr << ODB_COMPILER_VERSION;
- args.push_back ("-DODB_COMPILER_VERSION=" + ostr.str ());
- }
-
- // Compile for each database.
- //
- size_t sloc_total (0);
-
- for (vector<database>::iterator i (dbs.begin ()); i != dbs.end (); ++i)
- {
- database db (*i);
- strings db_args (args);
-
- // Add database-specific ODB macro.
- //
- db_args.push_back (db_macro[db]);
-
- // Second parse.
- //
- profile_data pd (prof_paths, db, argv[0]);
- oi[1].search_func = &profile_search;
- oi[2].search_func = &profile_search;
- oi[1].arg = &pd;
- oi[2].arg = &pd;
-
- cli::argv_file_scanner scan (ac, &av[0], oi, 3);
- options ops (scan);
-
- size_t end (scan.end () - 1); // We have one less in plugin_args.
-
- if (end == plugin_args.size ())
- {
- e << argv[0] << ": error: input file expected" << endl;
- return 1;
- }
-
- // Encode plugin options.
- //
- // Add the database we are compiling for first. More databases
- // could be specified in options files but they will be ignored
- // by the plugin (it only cares about the first).
- //
- db_args.push_back (encode_plugin_option ("database", db.string ()));
-
- cli::options const& desc (options::description ());
- for (size_t i (0); i < end; ++i)
- {
- string k, a (plugin_args[i]);
-
- // Ignore certain options.
- //
- if (a == "--")
- {
- // Ignore the option seperator since GCC doesn't understand it.
- //
- continue;
- }
- else if (a == "-d" || a == "--database")
- {
- // Ignore all other databases.
- //
- i++; // Skip the value.
- continue;
- }
-
- cli::options::const_iterator it (desc.find (a));
-
- if (it == desc.end ())
- {
- e << argv[0] << ": ice: unexpected option '" << a << "'" << endl;
- return 1;
- }
-
- if (a.size () > 2 && a[0] == '-' && a[1] == '-')
- k = string (a, 2); // long format
- else
- k = string (a, 1); // short format
-
- if (it->flag ())
- db_args.push_back (encode_plugin_flag (k));
- else
- {
- // If there are more arguments then we may have a value.
- //
- if (i + 1 == end)
- {
- e << argv[0] << ": ice: expected argument for '" << a << "'"
- << endl;
- return 1;
- }
-
- db_args.push_back (encode_plugin_option (k, plugin_args[++i]));
- }
- }
-
- // Reserve space for and remember the position of the svc-file
- // option.
- //
- size_t svc_file_pos (db_args.size ());
- db_args.push_back ("");
-
- // If compiling multiple input files at once, pass them also with
- // the --svc-file option.
- //
- bool at_once (ops.at_once () && plugin_args.size () - end > 1);
- if (at_once)
- {
- if (ops.input_name ().empty ())
- {
- e << "error: --input-name required when compiling multiple " <<
- "input files at once (--at-once)" << endl;
- return 1;
- }
-
- for (size_t i (end); i < plugin_args.size (); ++i)
- db_args.push_back (
- encode_plugin_option ("svc-file", plugin_args[i]));
- }
-
- // Create an execvp-compatible argument array.
- //
- typedef vector<char const*> cstrings;
- cstrings exec_args;
-
- for (strings::const_iterator i (db_args.begin ()), end (db_args.end ());
- i != end; ++i)
- {
- exec_args.push_back (i->c_str ());
- }
-
- exec_args.push_back ("-"); // Compile stdin.
- exec_args.push_back (0);
-
- // Iterate over the input files and compile each of them.
- //
- for (; end < plugin_args.size (); ++end)
- {
- string name (at_once ? ops.input_name () : plugin_args[end]);
-
- // Set the --svc-file option.
- //
- db_args[svc_file_pos] = encode_plugin_option ("svc-file", name);
- exec_args[svc_file_pos] = db_args[svc_file_pos].c_str ();
-
- //
- //
- ifstream ifs;
-
- if (!at_once)
- {
- ifs.open (name.c_str (), ios_base::in | ios_base::binary);
-
- if (!ifs.is_open ())
- {
- e << name << ": error: unable to open in read mode" << endl;
- return 1;
- }
- }
-
- if (v)
- {
- e << "Compiling " << name << endl;
- for (cstrings::const_iterator i (exec_args.begin ());
- i != exec_args.end (); ++i)
- {
- if (*i != 0)
- e << *i << (*(i + 1) != 0 ? ' ' : '\n');
- }
- }
-
- process_info pi (start_process (&exec_args[0], argv[0], false, true));
-
- {
- __gnu_cxx::stdio_filebuf<char> fb (
- pi.out_fd, ios_base::out | ios_base::binary);
- ostream os (&fb);
-
- if (!at_once)
- {
- // See if we there is a UTF-8 BOM in the input file. If so,
- // then we need to write it before prologues.
- //
- if (ifs.peek () == 0xEF)
- {
- ifs.get ();
- if (ifs.get () != 0xBB || ifs.get () != 0xBF)
- {
- e << name << ": error: invalid UTF-8 BOM sequence" << endl;
- fb.close ();
- wait_process (pi, argv[0]);
- return 1;
- }
-
- os.put (0xEF);
- os.put (0xBB);
- os.put (0xBF);
- }
- }
-
- if (!ops.trace ())
- {
- // Add the standard prologue.
- //
- os << "#line 1 \"<standard-odb-prologue>\"" << endl;
-
- // Make sure ODB compiler and libodb versions are compatible.
- //
- os << "#include <odb/version.hxx>" << endl
- << endl
- << "#if ODB_VERSION != " << ODB_VERSION << endl
- << "# error incompatible ODB compiler and runtime " <<
- "versions" << endl
- << "#endif" << endl
- << endl;
-
- // Include std::string. It is used as a default type for
- // the implicit discriminator member in polymorphism
- // support.
- //
- os << "#include <string>" << endl
- << endl;
-
- // Add ODB compiler metaprogramming tests.
- //
- os << "namespace odb" << endl
- << "{" << endl
- << "namespace compiler" << endl
- << "{" << endl;
-
- // operator< test, used in validator.
- //
- os << "template <typename T>" << endl
- << "bool" << endl
- << "has_lt_operator (const T& x, const T& y)" << endl
- << "{" << endl
- << "bool r (x < y);" << endl
- << "return r;" << endl
- << "}" << endl;
-
- os << "}" << endl
- << "}" << endl;
- }
-
- // Add custom prologue if any.
- //
- // NOTE: if you change the format, you also need to update code
- // in include.cxx
- //
- size_t pro_count (1);
- if (ops.odb_prologue ().count (db) != 0)
- {
- strings const& pro (ops.odb_prologue ()[db]);
- for (size_t i (0); i < pro.size (); ++i, ++pro_count)
- {
- os << "#line 1 \"<odb-prologue-" << pro_count << ">\"" << endl
- << pro[i] << endl;
- }
- }
-
- if (ops.odb_prologue_file ().count (db) != 0)
- {
- strings const& prof (ops.odb_prologue_file ()[db]);
- for (size_t i (0); i < prof.size (); ++i, ++pro_count)
- {
- os << "#line 1 \"<odb-prologue-" << pro_count << ">\""
- << endl;
-
- ifstream ifs (prof[i].c_str (), ios_base::in | ios_base::binary);
-
- if (!ifs.is_open ())
- {
- e << prof[i] << ": error: unable to open in read mode" << endl;
- fb.close ();
- wait_process (pi, argv[0]);
- return 1;
- }
-
- if (!(os << ifs.rdbuf ()))
- {
- e << prof[i] << ": error: io failure" << endl;
- fb.close ();
- wait_process (pi, argv[0]);
- return 1;
- }
-
- os << endl;
- }
- }
-
- if (at_once)
- {
- // Include all the input files (no need to escape).
- //
- os << "#line 1 \"<command-line>\"" << endl;
-
- bool b (ops.include_with_brackets ());
- char op (b ? '<' : '"'), cl (b ? '>' : '"');
-
- for (; end < plugin_args.size (); ++end)
- os << "#include " << op << plugin_args[end] << cl << endl;
- }
- else
- {
- // Write the synthesized translation unit to stdout.
- //
- os << "#line 1 \"" << escape_path (name) << "\"" << endl;
-
- if (!(os << ifs.rdbuf ()))
- {
- e << name << ": error: io failure" << endl;
- fb.close ();
- wait_process (pi, argv[0]);
- return 1;
- }
-
- // Add a new line in case the input file doesn't end with one.
- //
- os << endl;
- }
-
- // Add custom epilogue if any.
- //
- // NOTE: if you change the format, you also need to update code
- // in include.cxx
- //
- size_t epi_count (1);
- if (ops.odb_epilogue ().count (db) != 0)
- {
- strings const& epi (ops.odb_epilogue ()[db]);
- for (size_t i (0); i < epi.size (); ++i, ++epi_count)
- {
- os << "#line 1 \"<odb-epilogue-" << epi_count << ">\"" << endl
- << epi[i] << endl;
- }
- }
-
- if (ops.odb_epilogue_file ().count (db) != 0)
- {
- strings const& epif (ops.odb_epilogue_file ()[db]);
- for (size_t i (0); i < epif.size (); ++i, ++epi_count)
- {
- os << "#line 1 \"<odb-epilogue-" << epi_count << ">\""
- << endl;
-
- ifstream ifs (epif[i].c_str (), ios_base::in | ios_base::binary);
-
- if (!ifs.is_open ())
- {
- e << epif[i] << ": error: unable to open in read mode" << endl;
- fb.close ();
- wait_process (pi, argv[0]);
- return 1;
- }
-
- if (!(os << ifs.rdbuf ()))
- {
- e << epif[i] << ": error: io failure" << endl;
- fb.close ();
- wait_process (pi, argv[0]);
- return 1;
- }
-
- os << endl;
- }
- }
-
- if (!ops.trace ())
- {
- // Add the standard epilogue at the end so that we see all
- // the declarations.
- //
- os << "#line 1 \"<standard-odb-epilogue>\"" << endl;
-
- // Includes for standard smart pointers. The Boost TR1 header
- // may or may not delegate to the GCC implementation. In either
- // case, the necessary declarations will be provided so we don't
- // need to do anything.
- //
- os << "#include <memory>" << endl;
-
- // Standard wrapper traits.
- //
- os << "#include <odb/wrapper-traits.hxx>" << endl;
-
- // Standard pointer traits.
- //
- os << "#include <odb/pointer-traits.hxx>" << endl;
-
- // Standard container traits.
- //
- os << "#include <odb/container-traits.hxx>" << endl;
-
- // TR1 wrapper/pointer traits.
- //
-#ifndef ODB_BUILD2
- if (ops.std () == cxx_version::cxx98)
- os << endl
- << "#ifndef BOOST_TR1_MEMORY_HPP_INCLUDED" << endl
- << "# include <tr1/memory>" << endl
- << "#endif" << endl
- << "#include <odb/tr1/wrapper-traits.hxx>" << endl
- << "#include <odb/tr1/pointer-traits.hxx>" << endl;
-#endif
- }
- }
-
- // Filter the output stream looking for communication from the
- // plugin.
- //
- {
- __gnu_cxx::stdio_filebuf<char> fb (pi.in_ofd, ios_base::in);
- istream is (&fb);
-
- for (bool first (true); !is.eof (); )
- {
- string line;
- getline (is, line);
-
- if (is.fail () && !is.eof ())
- {
- e << argv[0] << ": error: io failure while parsing output"
- << endl;
- wait_process (pi, argv[0]);
- return 1;
- }
-
- if (line.compare (0, 9, "odb:sloc:") == 0)
- {
- if (show_sloc || sloc_limit != 0)
- {
- size_t n;
- istringstream is (string (line, 9, string::npos));
-
- if (!(is >> n && is.eof ()))
- {
- e << argv[0] << ": error: invalid odb:sloc value" << endl;
- wait_process (pi, argv[0]);
- return 1;
- }
-
- sloc_total += n;
- }
-
- continue;
- }
-
- if (first)
- first = false;
- else
- cout << endl;
-
- cout << line;
- }
- }
-
- if (!wait_process (pi, argv[0]))
- return 1;
- } // End input file loop.
- } // End database loop.
-
- // Handle SLOC.
- //
- if (show_sloc)
- e << "total: " << sloc_total << endl;
-
- if (sloc_limit != 0 && sloc_limit < sloc_total)
- {
- e << argv[0] << ": error: SLOC limit of " << sloc_limit << " lines " <<
- "has been exceeded" << endl;
-
- if (!show_sloc)
- e << argv[0] << ": info: use the --show-sloc option to see the "
- << "current total" << endl;
-
- return 1;
- }
- }
- catch (profile_failure const&)
- {
- // Diagnostics has already been issued.
- //
- return 1;
- }
- catch (process_failure const&)
- {
- // Diagnostics has already been issued.
- //
- return 1;
- }
- catch (invalid_path const& ex)
- {
- e << argv[0] << ": error: invalid path '" << ex.path () << "'" << endl;
- return 1;
- }
- catch (cli::exception const& ex)
- {
- e << ex << endl;
- return 1;
- }
-}
-
-static inline string
-encode_plugin_flag (string const& k)
-{
- return "-fplugin-arg-odb-" + k;
-}
-
-static string
-encode_plugin_option (string const& k, string const& cv)
-{
- string o ("-fplugin-arg-odb-");
- o += k;
- o += '=';
-
- if (!cv.empty ())
- {
- // A value cannot contain '='. Encode it as the backspace
- // character.
- //
- string v (cv);
- for (size_t i (0); i < v.size (); ++i)
- if (v[i] == '=')
- v[i] = '\b';
-
- o += v;
- }
-
- return o;
-}
-
-static paths
-profile_paths (strings const& sargs, char const* name)
-{
- // Copy some of the arguments from the passed list. We also need
- // the g++ executable.
- //
- strings args;
-
- args.push_back (sargs[0]);
- args.push_back ("-v");
- args.push_back ("-x");
- args.push_back ("c++");
- args.push_back ("-E");
- args.push_back ("-P");
-
- for (strings::const_iterator i (++sargs.begin ()), end (sargs.end ());
- i != end; ++i)
- {
- string const& a (*i);
-
- // -I
- //
- if (a.size () > 1 && a[0] == '-' && a[1] == 'I')
- {
- args.push_back (a);
-
- if (a.size () == 2) // -I /path
- {
- args.push_back (*(++i));
- }
- }
- // -framework
- //
- else if (a == "-isystem" ||
- a == "-iquote" ||
- a == "-idirafter" ||
- a == "-isysroot" ||
- a == "-framework")
- {
- args.push_back (a);
-
- if (++i == end)
- {
- cerr << name << ": error: expected argument for the " << a
- << " option" << endl;
- throw profile_failure ();
- }
-
- args.push_back (*i);
- }
- // --sysroot
- //
- else if (a.compare (0, 10, "--sysroot=") == 0)
- args.push_back (a);
- // -std
- //
- else if (a.compare (0, 5, "-std=") == 0)
- args.push_back (a);
- }
-
- // Create an execvp-compatible argument array.
- //
- vector<char const*> exec_args;
-
- for (strings::const_iterator i (args.begin ()), end (args.end ());
- i != end; ++i)
- {
- exec_args.push_back (i->c_str ());
- }
-
- exec_args.push_back ("-"); // Compile stdin.
- exec_args.push_back (0);
-
- process_info pi (start_process (&exec_args[0], name, true));
- close (pi.out_fd); // Preprocess empty file.
-
- // Read the output into a temporary string stream. We don't parse
- // it on the fly because we don't know whether it is the data or
- // diagnostics until after the process is terminated and we get
- // the exit code. We also cannot first wait for the exist code
- // and then read the output because the process might get blocked.
- //
- stringstream ss;
- {
- __gnu_cxx::stdio_filebuf<char> fb (pi.in_efd, ios_base::in);
- istream is (&fb);
-
- for (bool first (true); !is.eof (); )
- {
- string line;
- getline (is, line);
-
- if (is.fail () && !is.eof ())
- {
- cerr << name << ": error: "
- << "io failure while parsing profile paths" << endl;
-
- wait_process (pi, name);
- throw profile_failure ();
- }
-
- if (first)
- first = false;
- else
- ss << endl;
-
- ss << line;
- }
- }
-
- if (!wait_process (pi, name))
- {
- // Things didn't go well and ss should contain the diagnostics.
- // In case it is empty, issue our own.
- //
- if (!ss.str ().empty ())
- cerr << ss.rdbuf ();
- else
- cerr << name << ": error: unable to extract profile paths" << endl;
-
- throw profile_failure ();
- }
-
- // Parse the cached output.
- //
- paths r;
- {
- enum
- {
- read_prefix,
- read_path,
- read_suffix
- } state = read_prefix;
-
- while (!ss.eof () && state != read_suffix)
- {
- string line;
- getline (ss, line);
-
- if (ss.fail () && !ss.eof ())
- {
- cerr << name << ": error: "
- << "io failure while parsing profile paths" << endl;
- throw profile_failure ();
- }
-
- switch (state)
- {
- case read_prefix:
- {
- // The English string that we are looking for is "#include <...>
- // search starts here:" but it can be translated. However, all
- // the translations seems to have the "#include" and "<...>"
- // parts, so we can search for those.
- //
- if (line.find ("#include") != string::npos &&
- line.find ("<...>") != string::npos)
- state = read_path;
- break;
- }
- case read_path:
- {
- // The end of the list is terminated with the "End of search
- // list." line, which, again, can be translated. Here we don't
- // have any invariable parts that we can use. Instead, we will
- // rely on the fact that all the paths are space-indented.
- //
- if (!line.empty () && line[0] != ' ')
- state = read_suffix;
- else
- // Paths are indented with a space.
- //
- r.push_back (path (string (line, 1)));
-
- break;
- }
- case read_suffix:
- {
- // We shouldn't get here.
- break;
- }
- }
- }
-
- if (state != read_suffix)
- {
- cerr << name << ": error: unable to parse profile paths" << endl;
- throw profile_failure ();
- }
- }
-
- return r;
-}
-
-//
-// Path manipulation.
-//
-
-static string
-escape_path (string const& p)
-{
- string r;
-
- for (size_t i (0); i < p.size (); ++i)
- {
- if (p[i] == '\\')
- r += "\\\\";
- else
- r += p[i];
- }
-
- return r;
-}
-
-static path
-path_search (path const& f)
-{
- typedef path::traits traits;
-
- // If there is a directory component in the file, then the PATH
- // search does not apply.
- //
- if (!f.directory ().empty ())
- return f;
-
- string paths;
-
- // If there is no PATH in environment then the default search
- // path is the current directory.
- //
- if (char const* s = getenv ("PATH"))
- paths = s;
- else
- paths = traits::path_separator;
-
- // On Windows also check the current directory.
- //
-#ifdef _WIN32
- paths += traits::path_separator;
-#endif
-
- struct stat info;
-
- for (size_t b (0), e (paths.find (traits::path_separator));
- b != string::npos;)
- {
- path p (string (paths, b, e != string::npos ? e - b : e));
-
- // Empty path (i.e., a double colon or a colon at the beginning
- // or end of PATH) means search in the current dirrectory.
- //
- if (p.empty ())
- p = path (".");
-
- path dp (p / f);
-
- // Just check that the file exist without checking for permissions, etc.
- //
- if (stat (dp.string ().c_str (), &info) == 0 && S_ISREG (info.st_mode))
- return dp;
-
- // On Windows also try the path with the .exe extension.
- //
-#ifdef _WIN32
- dp += ".exe";
-
- if (stat (dp.string ().c_str (), &info) == 0 && S_ISREG (info.st_mode))
- return dp;
-#endif
-
- if (e == string::npos)
- b = e;
- else
- {
- b = e + 1;
- e = paths.find (traits::path_separator, b);
- }
- }
-
- return path ();
-}
-
-static path
-driver_path (path const& drv)
-{
- return drv.directory ().empty () ? path_search (drv) : drv;
-}
-
-#ifndef ODB_STATIC_PLUGIN
-static path
-plugin_path (path const& drv,
-#ifdef ODB_GCC_PLUGIN_DIR
- string const& gxx)
-#else
- string const&)
-#endif
-{
-#ifdef _WIN32
- char const plugin_ext[] = ".dll";
-
-// While GCC 8 switched to using .dylib as the plugin extension, there is a
-// bug in the extension stripping code. So for now we use the .so extension
-// everywhere (see also buildfile if changing this).
-//
-//#elif defined(__APPLE__) && defined(ODB_BUILD2)
-// char const plugin_ext[] = ".dylib";
-#else
- char const plugin_ext[] = ".so";
-#endif
-
- // Figure out the plugin base name which is just the driver name (but
- // without the .exe extension on Windows). If the driver name starts with
- // 'lt-', then we are running through the libtool script. Strip this prefix
- // -- the shared object should be in the same directory.
- //
-#ifdef _WIN32
- string b (drv.leaf ().base ().string ());
-#else
- string b (drv.leaf ().string ());
-#endif
-
- bool lt (b.size () > 3 && b[0] == 'l' && b[1] == 't' && b[2] == '-');
- if (lt)
- b = string (b, 3, string::npos);
-
- path dp (driver_path (drv));
-
- if (dp.empty ())
- {
- cerr << drv << ": error: unable to resolve ODB driver path" << endl;
- return path ();
- }
-
- dp = dp.directory ();
- struct stat info;
-
- // Regardless of whether we were given a plugin path, first try
- // the current directory for the .la file. This will make sure
- // running ODB from the build directory works as expected.
- //
- // @@ BUILD2: not going to work for build2 build.
- //
- path pp (dp / path (b + ".la"));
- if (stat (pp.string ().c_str (), &info) == 0)
- {
- pp = dp / path (".libs") / path (b + ".so");
- if (stat (pp.string ().c_str (), &info) == 0)
- return pp;
- }
-
-#ifdef ODB_GCC_PLUGIN_DIR
- // Plugin should be installed into the GCC default plugin directory.
- // Ideally, in this situation, we would simply pass the plugin name and
- // let GCC append the correct directory. Unfortunately, this mechanism
- // was only added in GCC 4.6 so in order to support 4.5 we will have to
- // emulate it ourselves.
- //
- if (!lt)
- {
- //@@ BUILD2: if/when dropping old GCC should just get rid of this.
-#if 1
- // First get the default GCC plugin directory.
- //
- path d;
- vector<char const*> exec_args;
- exec_args.push_back (gxx.c_str ());
- exec_args.push_back ("-print-file-name=plugin");
- exec_args.push_back (0);
-
- process_info pi (
- start_process (
- &exec_args[0], drv.string ().c_str (), false, true));
- close (pi.out_fd);
-
- // Read the path from stdout.
- //
- {
- __gnu_cxx::stdio_filebuf<char> fb (pi.in_ofd, ios_base::in);
- istream is (&fb);
- string line;
- getline (is, line);
- d = path (line);
- }
-
- if (!wait_process (pi, drv.string ().c_str ()))
- return path (); // Assume GCC issued some diagnostics.
-
- if (d.string () == "plugin")
- {
- cerr << drv << ": error: unable to obtain GCC plugin directory" << endl;
- return path ();
- }
-
- // See if the plugin is there.
- //
- pp = d / path (b + plugin_ext);
- if (stat (pp.string ().c_str (), &info) != 0)
- {
- cerr << drv << ": error: no ODB plugin in GCC plugin directory '" <<
- d << "'" << endl;
- return path ();
- }
-
- return pp;
-#else
- return path (b);
-#endif
- }
-#elif defined (ODB_PLUGIN_PATH)
- // If we were given a plugin path, use that unless we are running
- // via libtool.
- //
- if (!lt)
- {
- string rp (ODB_PLUGIN_PATH);
- if (!rp.empty ())
- dp /= path (rp);
-
- pp = dp / path (b + plugin_ext);
-
- if (stat (pp.string ().c_str (), &info) != 0)
- {
- cerr << drv << ": error: no ODB plugin in '" << dp << "'" << endl;
- return path ();
- }
-
- return pp;
- }
-#endif
-
- // Try in the current directory.
- //
- pp = dp / path (b + plugin_ext);
- if (stat (pp.string ().c_str (), &info) != 0)
- {
- cerr << drv << ": error: unable to locate ODB plugin" << endl;
- return path ();
- }
-
- return pp;
-}
-#endif
-
-//
-// Process manipulation.
-//
-
-#ifndef _WIN32
-
-static process_info
-start_process (char const* args[], char const* name, bool err, bool out)
-{
- int out_fd[2];
- int in_efd[2];
- int in_ofd[2];
-
- if (pipe (out_fd) == -1 ||
- (err && pipe (in_efd) == -1) ||
- (out && pipe (in_ofd) == -1))
- {
- char const* err (strerror (errno));
- cerr << name << ": error: " << err << endl;
- throw process_failure ();
- }
-
- pid_t pid (fork ());
-
- if (pid == -1)
- {
- char const* err (strerror (errno));
- cerr << name << ": error: " << err << endl;
- throw process_failure ();
- }
-
- if (pid == 0)
- {
- // Child. Close the write end of the pipe and duplicate the read end
- // to stdin. Then close the original read end descriptors.
- //
- if (close (out_fd[1]) == -1 ||
- dup2 (out_fd[0], STDIN_FILENO) == -1 ||
- close (out_fd[0]) == -1)
- {
- char const* err (strerror (errno));
- cerr << name << ": error: " << err << endl;
- throw process_failure ();
- }
-
- // Do the same for the stderr if requested.
- //
- if (err)
- {
- if (close (in_efd[0]) == -1 ||
- dup2 (in_efd[1], STDERR_FILENO) == -1 ||
- close (in_efd[1]) == -1)
- {
- char const* err (strerror (errno));
- cerr << name << ": error: " << err << endl;
- throw process_failure ();
- }
- }
-
- // Do the same for the stdout if requested.
- //
- if (out)
- {
- if (close (in_ofd[0]) == -1 ||
- dup2 (in_ofd[1], STDOUT_FILENO) == -1 ||
- close (in_ofd[1]) == -1)
- {
- char const* err (strerror (errno));
- cerr << name << ": error: " << err << endl;
- throw process_failure ();
- }
- }
-
- if (execvp (args[0], const_cast<char**> (&args[0])) == -1)
- {
- char const* err (strerror (errno));
- cerr << args[0] << ": error: " << err << endl;
- throw process_failure ();
- }
- }
- else
- {
- // Parent. Close the other ends of the pipes.
- //
- if (close (out_fd[0]) == -1 ||
- (err && close (in_efd[1]) == -1) ||
- (out && close (in_ofd[1]) == -1))
- {
- char const* err (strerror (errno));
- cerr << name << ": error: " << err << endl;
- throw process_failure ();
- }
- }
-
- process_info r;
- r.id = pid;
- r.out_fd = out_fd[1];
- r.in_efd = err ? in_efd[0] : 0;
- r.in_ofd = out ? in_ofd[0] : 0;
- return r;
-}
-
-static bool
-wait_process (process_info pi, char const* name)
-{
- int status;
-
- if (waitpid (pi.id, &status, 0) == -1)
- {
- char const* err (strerror (errno));
- cerr << name << ": error: " << err << endl;
- throw process_failure ();
- }
-
- return WIFEXITED (status) && WEXITSTATUS (status) == 0;
-}
-
-#else // _WIN32
-
-static void
-print_error (char const* name)
-{
- LPTSTR msg;
- DWORD e (GetLastError());
-
- if (!FormatMessage(
- FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS,
- 0,
- e,
- MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR) &msg,
- 0,
- 0))
- {
- cerr << name << ": error: unknown error code " << e << endl;
- return;
- }
-
- cerr << name << ": error: " << msg << endl;
- LocalFree (msg);
-}
-
-static process_info
-start_process (char const* args[], char const* name, bool err, bool out)
-{
- HANDLE out_h[2];
- HANDLE in_eh[2];
- HANDLE in_oh[2];
- SECURITY_ATTRIBUTES sa;
-
- sa.nLength = sizeof (SECURITY_ATTRIBUTES);
- sa.bInheritHandle = true;
- sa.lpSecurityDescriptor = 0;
-
- if (!CreatePipe (&out_h[0], &out_h[1], &sa, 0) ||
- !SetHandleInformation (out_h[1], HANDLE_FLAG_INHERIT, 0))
- {
- print_error (name);
- throw process_failure ();
- }
-
- if (err)
- {
- if (!CreatePipe (&in_eh[0], &in_eh[1], &sa, 0) ||
- !SetHandleInformation (in_eh[0], HANDLE_FLAG_INHERIT, 0))
- {
- print_error (name);
- throw process_failure ();
- }
- }
-
- if (out)
- {
- if (!CreatePipe (&in_oh[0], &in_oh[1], &sa, 0) ||
- !SetHandleInformation (in_oh[0], HANDLE_FLAG_INHERIT, 0))
- {
- print_error (name);
- throw process_failure ();
- }
- }
-
- // Create the process.
- //
- path file (args[0]);
-
- // Do PATH search.
- //
- if (file.directory ().empty ())
- file = path_search (file);
- else if (file.base () == file) // No extension
- file += ".exe"; // Assume .exe.
-
- if (file.empty ())
- {
- cerr << args[0] << ": error: file not found" << endl;
- throw process_failure ();
- }
-
- // Serialize the arguments to string.
- //
- string cmd_line;
-
- for (char const** p (args); *p != 0; ++p)
- {
- if (p != args)
- cmd_line += ' ';
-
- // 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 += '"';
- }
-
- // Prepare other info.
- //
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
-
- memset (&si, 0, sizeof (STARTUPINFO));
- memset (&pi, 0, sizeof (PROCESS_INFORMATION));
-
- si.cb = sizeof(STARTUPINFO);
-
- if (err)
- si.hStdError = in_eh[1];
- else
- si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
-
- if (out)
- si.hStdOutput = in_oh[1];
- else
- si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
-
- si.hStdInput = out_h[0];
- si.dwFlags |= STARTF_USESTDHANDLES;
-
- if (!CreateProcess (
- file.string ().c_str (),
- const_cast<char*> (cmd_line.c_str ()),
- 0, // Process security attributes.
- 0, // Primary thread security attributes.
- true, // Inherit handles.
- 0, // Creation flags.
- 0, // Use our environment.
- 0, // Use our current directory.
- &si,
- &pi))
- {
- print_error (name);
- throw process_failure ();
- }
-
- CloseHandle (pi.hThread);
- CloseHandle (out_h[0]);
-
- if (err)
- CloseHandle (in_eh[1]);
-
- if (out)
- CloseHandle (in_oh[1]);
-
- process_info r;
- r.id = pi.hProcess;
- r.out_fd = _open_osfhandle ((intptr_t) (out_h[1]), 0);
-
- if (r.out_fd == -1)
- {
- cerr << name << ": error: unable to obtain C file handle" << endl;
- throw process_failure ();
- }
-
- if (err)
- {
- // Pass _O_TEXT to get newline translation.
- //
- r.in_efd = _open_osfhandle ((intptr_t) (in_eh[0]), _O_TEXT);
-
- if (r.in_efd == -1)
- {
- cerr << name << ": error: unable to obtain C file handle" << endl;
- throw process_failure ();
- }
- }
- else
- r.in_efd = 0;
-
- if (out)
- {
- // Pass _O_TEXT to get newline translation.
- //
- r.in_ofd = _open_osfhandle ((intptr_t) (in_oh[0]), _O_TEXT);
-
- if (r.in_ofd == -1)
- {
- cerr << name << ": error: unable to obtain C file handle" << endl;
- throw process_failure ();
- }
- }
- else
- r.in_ofd = 0;
-
- return r;
-}
-
-static bool
-wait_process (process_info pi, char const* name)
-{
- DWORD status;
-
- if (WaitForSingleObject (pi.id, INFINITE) != WAIT_OBJECT_0 ||
- !GetExitCodeProcess (pi.id, &status))
- {
- print_error (name);
- throw process_failure ();
- }
-
- CloseHandle (pi.id);
- return status == 0;
-}
-
-#endif // _WIN32
diff --git a/odb/odb/.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/odb/common-query.cxx b/odb/odb/common-query.cxx
new file mode 100644
index 0000000..0b5d063
--- /dev/null
+++ b/odb/odb/common-query.cxx
@@ -0,0 +1,1440 @@
+// file : odb/common-query.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/common-query.hxx>
+
+using namespace std;
+
+// query_utils
+//
+
+string query_utils::
+depth_suffix (size_t d)
+{
+ if (d != 0)
+ {
+ ostringstream os;
+ os << d;
+ return '_' + os.str ();
+ }
+
+ return string ();
+}
+
+// Collect nested (composite) types as generated by query_columns.
+//
+struct query_nested_types: object_columns_base, virtual context
+{
+ query_nested_types (bool ptr): ptr_ (ptr), in_ptr_ (false), depth_ (0) {}
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ // We don't want to traverse bases.
+ //
+ names (c);
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ if (m != 0)
+ {
+ string name (prefix_ + public_name (*m));
+ name += in_ptr_ ? "_column_class" : "_class";
+ name += query_utils::depth_suffix (depth_);
+ name += '_';
+ types.push_back (name);
+
+ depth_++;
+ string p (prefix_);
+ prefix_ = name + "::";
+ object_columns_base::traverse_composite (m, c);
+ prefix_ = p;
+ depth_--;
+ }
+ else
+ object_columns_base::traverse_composite (m, c); // Base
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // The same logic as in query_columns.
+ //
+ if (inverse (m, key_prefix_))
+ return;
+
+ bool poly_ref (m.count ("polymorphic-ref"));
+
+ if (composite_wrapper (utype (*id_member (c))))
+ {
+ if (ptr_ || poly_ref)
+ object_columns_base::traverse_pointer (m, c);
+ else
+ {
+ in_ptr_ = true;
+ object_columns_base::traverse_pointer (m, c);
+ in_ptr_ = false;
+ }
+ }
+ }
+
+public:
+ strings types;
+
+protected:
+ bool ptr_;
+ bool in_ptr_; // True while we are "inside" an object pointer.
+ string prefix_;
+ size_t depth_;
+};
+
+void query_utils::
+inst_query_columns (bool decl,
+ bool ptr,
+ string const& type,
+ string const& alias,
+ semantics::class_& c)
+{
+ inst_header (decl);
+ os << (ptr ? "pointer_" : "") << "query_columns<" << endl
+ << " " << type << "," << endl
+ << " id_" << db << "," << endl
+ << " " << alias << " >;"
+ << endl;
+
+ // If we are generating extern declarations, we also have to generate
+ // them for all the nested (composite) structs. That's what VC++ needs.
+ //
+ if (decl)
+ {
+ query_nested_types t (ptr);
+ t.traverse (c);
+
+ for (strings::iterator i (t.types.begin ()); i != t.types.end (); ++i)
+ {
+ inst_header (decl, true); // Omit export, GCC doesn't like it.
+ os << (ptr ? "pointer_" : "") << "query_columns<" << endl
+ << " " << type << "," << endl
+ << " id_" << db << "," << endl
+ << " " << alias << " >::" << *i << ";"
+ << endl;
+ }
+ }
+}
+
+// query_tags
+//
+
+void query_tags::
+traverse (semantics::class_& c)
+{
+ if (object (c) || composite (c))
+ {
+ object_columns_base::traverse (c);
+ }
+ else if (view (c))
+ {
+ if (c.get<size_t> ("object-count") != 0)
+ {
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ for (view_objects::const_iterator i (objs.begin ());
+ i < objs.end ();
+ ++i)
+ {
+ if (i->kind != view_object::object)
+ continue; // Skip tables.
+
+ if (i->alias.empty ())
+ continue;
+
+ generate (i->alias);
+ }
+ }
+ }
+ // Otherwise it is a transient base (of a composite value).
+
+ if (nl_)
+ os << endl;
+}
+
+void query_tags::
+traverse_object (semantics::class_& c)
+{
+ names (c); // We don't want to traverse bases.
+}
+
+void query_tags::
+traverse_composite (semantics::data_member* m, semantics::class_& c)
+{
+ // Base type.
+ //
+ if (m == 0)
+ {
+ object_columns_base::traverse_composite (m, c);
+ return;
+ }
+
+ // Don't generate an empty struct if we don't have any pointers.
+ //
+ if (!has_a (c, test_pointer))
+ return;
+
+ if (nl_)
+ os << endl;
+
+ os << "struct " << public_name (*m) << "_tag" <<
+ query_utils::depth_suffix (depth_)
+ << "{";
+
+ depth_++;
+ object_columns_base::traverse_composite (m, c);
+ depth_--;
+
+ os << "};";
+
+ nl_ = false;
+}
+
+void query_tags::
+traverse_pointer (semantics::data_member& m, semantics::class_&)
+{
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ generate (public_name (m));
+}
+
+void query_tags::
+generate (string const& name)
+{
+ os << "struct " << name << "_tag;";
+ nl_ = true;
+}
+
+// query_alias_traits
+//
+
+query_alias_traits::
+query_alias_traits (semantics::class_& c, bool decl)
+ : decl_ (decl), depth_ (0)
+{
+ scope_ = "access::";
+ scope_ += (object (c) ? "object_traits_impl" : "view_traits_impl");
+ scope_ += "< " + class_fq_name (c) + ", id_" + db.string () + " >";
+}
+
+void query_alias_traits::
+traverse_object (semantics::class_& c)
+{
+ // We don't want to traverse bases.
+ //
+ names (c);
+}
+
+void query_alias_traits::
+traverse_composite (semantics::data_member* m, semantics::class_& c)
+{
+ // Base type.
+ //
+ if (m == 0)
+ {
+ object_columns_base::traverse_composite (m, c);
+ return;
+ }
+
+ string old_scope (scope_);
+ scope_ += "::" + public_name (*m) + "_tag" +
+ query_utils::depth_suffix (depth_);
+
+ depth_++;
+ object_columns_base::traverse_composite (m, c);
+ depth_--;
+
+ scope_ = old_scope;
+}
+
+void query_alias_traits::
+traverse_pointer (semantics::data_member& m, semantics::class_& c)
+{
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ if (decl_)
+ generate_decl (public_name (m), c);
+ else
+ generate_def (m, c);
+}
+
+void query_alias_traits::
+generate_decl (string const& tag, semantics::class_& c)
+{
+ semantics::class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+ semantics::class_* poly_base (poly_derived ? &polymorphic_base (c) : 0);
+
+ if (poly_derived)
+ generate_decl (tag, *poly_base);
+
+ string const& fq_name (class_fq_name (c));
+
+ os << "template <>" << endl
+ << "struct " << exp << "alias_traits<" << endl
+ << " " << fq_name << "," << endl
+ << " id_" << db << "," << endl
+ << " " << scope_ << "::" << tag << "_tag>"
+ << "{";
+
+ if (poly_derived)
+ os << "typedef alias_traits<" << endl
+ << " " << class_fq_name (*poly_base) << "," << endl
+ << " id_" << db << "," << endl
+ << " " << scope_ << "::" << tag << "_tag>" << endl
+ << "base_traits;"
+ << endl;
+
+ // For dynamic multi-database support also generate common traits
+ // alias. Note that the tag type is the same since they all are
+ // derived from object_traits.
+ //
+ if (db != database::common && multi_dynamic)
+ os << "typedef alias_traits<" << endl
+ << " " << fq_name << "," << endl
+ << " id_common," << endl
+ << " " << scope_ << "::" << tag << "_tag>" << endl
+ << "common_traits;"
+ << endl;
+
+ generate_decl_body (); // Table name, etc.
+
+ os << "};";
+}
+
+void query_alias_traits::
+generate_decl_body ()
+{
+}
+
+void query_alias_traits::
+generate_def (semantics::data_member&, semantics::class_&)
+{
+}
+
+void query_alias_traits::
+generate_def (string const&, semantics::class_&, string const&)
+{
+}
+
+// query_columns_base
+//
+
+query_columns_base::
+query_columns_base (semantics::class_& c, bool decl, bool inst)
+ : decl_ (decl), inst_ (inst), depth_ (0)
+{
+ string const& n (class_fq_name (c));
+
+ if (decl)
+ scope_ = "access::object_traits_impl< " + n + ", id_" +
+ db.string () + " >";
+ else
+ scope_ = "query_columns_base< " + n + ", id_" + db.string () + " >";
+}
+
+void query_columns_base::
+traverse_object (semantics::class_& c)
+{
+ // We don't want to traverse bases.
+ //
+ names (c);
+}
+
+void query_columns_base::
+traverse_composite (semantics::data_member* m, semantics::class_& c)
+{
+ // Base type.
+ //
+ if (m == 0)
+ {
+ object_columns_base::traverse_composite (m, c);
+ return;
+ }
+
+ // Don't generate an empty struct if we don't have any pointers.
+ //
+ if (!has_a (c, test_pointer))
+ return;
+
+ string name (public_name (*m));
+ string dsuffix (query_utils::depth_suffix (depth_));
+
+ if (decl_)
+ {
+ os << "// " << name << endl
+ << "//" << endl
+ << "struct " << name << "_base" << dsuffix << '_'
+ << "{";
+
+ string old_scope (scope_);
+ scope_ += "::" + name + "_tag" + dsuffix;
+
+ depth_++;
+ object_columns_base::traverse_composite (m, c);
+ depth_--;
+
+ scope_ = old_scope;
+
+ os << "};";
+ }
+ else
+ {
+ string old_scope (scope_);
+ scope_ += "::" + name + "_base" + dsuffix + '_';
+
+ depth_++;
+ object_columns_base::traverse_composite (m, c);
+ depth_--;
+
+ scope_ = old_scope;
+ }
+}
+
+void query_columns_base::
+traverse_pointer (semantics::data_member& m, semantics::class_& c)
+{
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ string name (public_name (m));
+ string const& fq_name (class_fq_name (c));
+ bool inv (inverse (m, key_prefix_));
+
+ if (decl_)
+ {
+ os << "// " << name << endl
+ << "//" << endl;
+
+ os << "typedef" << endl
+ << "odb::alias_traits<" << endl
+ << " " << fq_name << "," << endl
+ << " id_" << db << "," << endl
+ << " " << scope_ << "::" << name << "_tag>" << endl
+ << name << "_alias_;"
+ << endl;
+
+ if (inv)
+ {
+ os << "typedef" << endl
+ << "odb::query_pointer<" << endl
+ << " odb::pointer_query_columns<" << endl
+ << " " << fq_name << "," << endl
+ << " id_" << db << "," << endl
+ << " " << name << "_alias_ > >" << endl
+ << name << "_type_ ;"
+ << endl
+ << "static " << const_ << name << "_type_ " << name << ";"
+ << endl;
+ }
+ }
+ else if (inst_)
+ {
+ generate_inst (m, c);
+ }
+ else
+ {
+ // Generate explicit template instantiation directive for the
+ // pointed-to pointer_query_columns.
+ //
+ if (multi_dynamic)
+ generate_inst (m, c);
+
+ if (inv)
+ os << const_ << scope_ << "::" << name << "_type_" << endl
+ << scope_ << "::" << name << ";"
+ << endl;
+ }
+}
+
+void query_columns_base::
+generate_inst (semantics::data_member& m, semantics::class_& c)
+{
+ string name (public_name (m));
+ string const& fq_name (class_fq_name (c));
+
+ string alias (scope_ + "::" + name + "_alias_");
+
+ // Instantiate base [pointer_]query_columns.
+ //
+ {
+ instance<query_columns_base_insts> b (true, inst_, alias, true);
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+
+ // If the pointed-to class has no pointers of its own then
+ // pointer_query_columns just derives from query_columns and
+ // that's what we need to instantiate.
+ //
+ inst_query_columns (inst_,
+ has_a (c, test_pointer | include_base),
+ fq_name,
+ alias,
+ c);
+}
+
+// query_columns
+//
+
+query_columns::
+query_columns (bool decl, bool ptr, semantics::class_& c)
+ : decl_ (decl), ptr_ (ptr), poly_ref_ (false), in_ptr_ (false),
+ fq_name_ (class_fq_name (c)),
+ resue_abstract_ (abstract (c) && !polymorphic (c)),
+ depth_ (0)
+{
+}
+
+void query_columns::
+traverse_object (semantics::class_& c)
+{
+ // We don't want to traverse bases.
+ //
+ names (c);
+}
+
+void query_columns::
+traverse_composite (semantics::data_member* m, semantics::class_& c)
+{
+ // Base type.
+ //
+ if (m == 0)
+ {
+ object_columns_base::traverse_composite (m, c);
+ return;
+ }
+
+ // Use _class_ instead of _type_ to avoid potential clashes between
+ // the class and member names.
+ //
+ string name (public_name (*m));
+ string suffix (in_ptr_ ? "_column_class" : "_class");
+
+ // Add depth to the nested composite to avoid potential name conflicts
+ // in situations like these:
+ //
+ // struct inner { ... };
+ // struct outer { inner value; };
+ // struct object { outer value; }
+ //
+ string dsuffix (query_utils::depth_suffix (depth_));
+ suffix += dsuffix;
+ suffix += '_';
+
+ depth_++;
+
+ if (decl_)
+ {
+ os << "// " << name << endl
+ << "//" << endl
+ << "struct ";
+
+ // For some bizarre reason VC++ needs the export directive for
+ // a type nested in an (exported) template. This appears not
+ // to cause any problems for GCC.
+ //
+ // We only generate the export directive if we are also
+ // explicitly instantiating the query_columns templates.
+ //
+ if (multi_dynamic && !resue_abstract_)
+ os << exp;
+
+ os << name << suffix;
+
+ // Derive from the base in query_columns_base. It contains columns
+ // data for the pointer members.
+ //
+ if (!ptr_ && !poly_ref_ && has_a (c, test_pointer))
+ os << ": " << name << "_base" << dsuffix << '_';
+
+ os << "{";
+
+ if (!const_.empty ())
+ os << name << suffix << " ()" // Need user-defined default c-tor for
+ << "{" // initialization of static const.
+ << "}";
+
+ object_columns_base::traverse_composite (m, c);
+
+ os << "};";
+
+ if (!in_ptr_)
+ os << "static " << const_ << name << suffix << " " << name << ";"
+ << endl;
+ }
+ else
+ {
+ // Handle nested members first.
+ //
+ string old_scope (scope_);
+ scope_ += "::" + name + suffix;
+
+ object_columns_base::traverse_composite (m, c);
+
+ scope_ = old_scope;
+
+ // Composite member. Note that here we don't use suffix for the in-
+ // pointer case because the actual pointer column type derives from
+ // the composite column type (dual interface; see traverse_pointer()
+ // below).
+ //
+ string tmpl (ptr_ ? "pointer_query_columns" : "query_columns");
+ tmpl += "< " + fq_name_ + ", id_" + db.string () + ", A >" + scope_;
+
+ os << "template <typename A>" << endl
+ << const_ << "typename " << tmpl << "::" << name <<
+ (in_ptr_ ? string ("_type_") : suffix) << endl
+ << tmpl << "::" << name << ";"
+ << endl;
+ }
+
+ depth_--;
+}
+
+void query_columns::
+column_ctor (string const&, string const&, string const&)
+{
+}
+
+void query_columns::
+column_common (semantics::data_member& m,
+ string const& type,
+ string const&,
+ string const& suffix)
+{
+ string name (public_name (m));
+
+ if (decl_)
+ {
+ os << "// " << name << endl
+ << "//" << endl;
+
+ os << "typedef odb::query_column< " << type << " > " << name <<
+ suffix << ";"
+ << endl;
+ }
+ else
+ {
+ // Note that here we don't use suffix.
+ //
+ string tmpl (ptr_ ? "pointer_query_columns" : "query_columns");
+ tmpl += "< " + fq_name_ + ", id_" + db.string () + ", A >" + scope_;
+
+ os << "template <typename A>" << endl
+ << const_ << "typename " << tmpl << "::" << name << "_type_" << endl
+ << tmpl << "::" << name << ";"
+ << endl;
+ }
+}
+
+bool query_columns::
+traverse_column (semantics::data_member& m, string const& column, bool)
+{
+ semantics::names* hint;
+ semantics::type* t (&utype (m, hint));
+
+ // Unwrap it if it is a wrapper.
+ //
+ if (semantics::type* wt = wrapper (*t, hint))
+ t = &utype (*wt, hint);
+
+ column_common (m, t->fq_name (hint), column);
+
+ if (decl_)
+ {
+ string name (public_name (m));
+
+ os << "static " << const_ << name << "_type_ " << name << ";"
+ << endl;
+ }
+
+ return true;
+}
+
+void query_columns::
+traverse_pointer (semantics::data_member& m, semantics::class_& c)
+{
+ // If this is for the pointer_query_columns and the member is not
+ // inverse, then create the normal member corresponding to the id
+ // column. This will allow the user to check it for NULL or to
+ // compare ids. In case this is for query_columns, then for the
+ // inverse member everything has been generated in query_columns_base.
+ //
+ if (inverse (m, key_prefix_))
+ return;
+
+ // If we ignore polymorphic references, then a view that uses a custom
+ // join condition based on id will use the id column from the base
+ // table. But the base table hasn't been joined yet. To resolve this
+ // we will generate the id member that points to our column.
+ //
+ poly_ref_ = m.count ("polymorphic-ref");
+
+ string name (public_name (m));
+
+ data_member_path& id (*id_member (c));
+ semantics::names* hint;
+ semantics::type& t (utype (id, hint));
+
+ if (composite_wrapper (t))
+ {
+ // Composite id.
+ //
+
+ // For pointer_query_columns and poly refs generate normal composite
+ // mapping.
+ //
+ if (ptr_ || poly_ref_)
+ object_columns_base::traverse_pointer (m, c);
+ else
+ {
+ // If this is a non-inverse relationship, then make the column have
+ // a dual interface: that of an object pointer and of an id column.
+ // The latter allows the user to, for example, use the is_null()
+ // test in a natural way. For inverse relationships there is no
+ // column and so the column interface is not available.
+ //
+ in_ptr_ = true;
+ object_columns_base::traverse_pointer (m, c);
+ in_ptr_ = false;
+
+ if (decl_)
+ {
+ os << "typedef" << endl
+ << "odb::query_pointer<" << endl
+ << " odb::pointer_query_columns<" << endl
+ << " " << class_fq_name (c) << "," << endl
+ << " id_" << db << "," << endl
+ << " " << name << "_alias_ > >" << endl
+ << name << "_pointer_type_;"
+ << endl;
+
+ os << "struct " << name << "_type_: " <<
+ name << "_pointer_type_, " <<
+ name << "_column_class" << query_utils::depth_suffix (depth_) << '_'
+ << "{";
+
+ if (!const_.empty ())
+ os << name << "_type_ ()" // Need user-defined default c-tor for
+ << "{" // initialization of static const.
+ << "}";
+
+ os << "};";
+
+ os << "static " << const_ << name << "_type_ " << name << ";"
+ << endl;
+ }
+ }
+ }
+ else
+ {
+ // Simple id.
+ //
+ string type (t.fq_name (hint));
+ string col (column_name (m, key_prefix_, default_name_, column_prefix_));
+
+ // For pointer_query_columns and poly refs generate normal column mapping.
+ //
+ if (ptr_ || poly_ref_)
+ column_common (m, type, col);
+ else
+ {
+ // If this is a non-inverse relationship, then make the column have
+ // a dual interface: that of an object pointer and of an id column.
+ // The latter allows the user to, for example, use the is_null()
+ // test in a natural way. For inverse relationships there is no
+ // column and so the column interface is not available.
+ //
+ column_common (m, type, col, "_column_type_");
+
+ if (decl_)
+ {
+ os << "typedef" << endl
+ << "odb::query_pointer<" << endl
+ << " odb::pointer_query_columns<" << endl
+ << " " << class_fq_name (c) << "," << endl
+ << " id_" << db << "," << endl
+ << " " << name << "_alias_ > >" << endl
+ << name << "_pointer_type_;"
+ << endl;
+
+ os << "struct " << name << "_type_: " <<
+ name << "_pointer_type_, " << name << "_column_type_"
+ << "{";
+
+ column_ctor (type, name + "_type_", name + "_column_type_");
+
+ os << "};";
+ }
+ }
+
+ if (decl_)
+ os << "static " << const_ << name << "_type_ " << name << ";"
+ << endl;
+ }
+
+ poly_ref_ = false;
+}
+
+// query_columns_bases
+//
+
+void query_columns_bases::
+traverse (type& c)
+{
+ // Ignore transient bases. Not used for views.
+ //
+ if (!object (c))
+ return;
+
+ if (first_)
+ {
+ os << ":" << endl
+ << " ";
+ first_ = false;
+ }
+ else
+ os << "," << endl
+ << " ";
+
+ os << (ptr_ ? "pointer_query_columns" : "query_columns") << "< " <<
+ class_fq_name (c) << ", id_" << db << ", ";
+
+ // If our base is polymorphic, then it has its own table/alias.
+ //
+ if (polymorphic (c))
+ os << "typename A::base_traits";
+ else
+ os << "A";
+
+ os << " >";
+}
+
+// query_columns_base_aliases
+//
+
+void query_columns_base_aliases::
+traverse (type& c)
+{
+ // Ignore transient bases. Not used for views.
+ //
+ if (!object (c))
+ return;
+
+ string const& name (class_name (c));
+
+ os << "// " << name << endl
+ << "//" << endl
+ << "typedef " << (ptr_ ? "pointer_query_columns" : "query_columns") <<
+ "< " << class_fq_name (c) << ", id_" << db << ", ";
+
+ if (polymorphic (c))
+ os << "typename A::base_traits";
+ else
+ os << "A";
+
+ os << " > " << name << ";"
+ << endl;
+}
+
+// query_columns_base_insts
+//
+
+query_columns_base_insts::
+query_columns_base_insts (bool test_ptr,
+ bool decl,
+ string const& alias,
+ bool poly)
+ : test_ptr_ (test_ptr), decl_ (decl), alias_ (alias), poly_ (poly)
+{
+ *this >> inherits_ >> *this;
+}
+
+query_columns_base_insts::
+query_columns_base_insts (query_columns_base_insts const& x)
+ : context (), // @@ -Wextra
+ test_ptr_ (x.test_ptr_),
+ decl_ (x.decl_),
+ alias_ (x.alias_),
+ poly_ (x.poly_)
+{
+ *this >> inherits_ >> *this;
+}
+
+void query_columns_base_insts::
+traverse (type& c)
+{
+ if (!object (c))
+ return;
+
+ bool poly (polymorphic (c));
+ if (poly && (poly != poly_))
+ return;
+
+ bool ptr (has_a (c, test_pointer | include_base));
+
+ string old_alias;
+ if (poly)
+ {
+ old_alias = alias_;
+ alias_ += "::base_traits";
+ }
+
+ // Instantiate bases recursively.
+ //
+ inherits (c, inherits_);
+
+ inst_query_columns (decl_,
+ test_ptr_ && ptr,
+ class_fq_name (c),
+ alias_,
+ c);
+
+ if (!test_ptr_ && ptr)
+ inst_query_columns (decl_, true, class_fq_name (c), alias_, c);
+
+ if (poly)
+ alias_ = old_alias;
+}
+
+// query_columns_type
+//
+
+void query_columns_type::
+traverse (type& c)
+{
+ string const& type (class_fq_name (c));
+
+ if (ptr_)
+ {
+ os << "template <typename A>" << endl
+ << "struct pointer_query_columns< " << type << ", id_" << db << ", A >";
+
+ // If we don't have pointers (in the whole hierarchy), then
+ // pointer_query_columns and query_columns are the same.
+ //
+ if (!has_a (c, test_pointer | include_base))
+ {
+ os << ":" << endl
+ << " query_columns< " << type << ", id_" << db << ", A >"
+ << "{"
+ << "};";
+ }
+ else
+ {
+ {
+ instance<query_columns_bases> b (ptr_);
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+
+ os << "{";
+
+ {
+ instance<query_columns_base_aliases> b (ptr_);
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+
+ {
+ bool true_ (true);
+ instance<query_columns> t (true_, ptr_, c); //@@ forwarding
+ t->traverse (c);
+ }
+
+ os << "};";
+
+ generate_impl (c);
+ }
+ }
+ else if (decl_)
+ {
+ bool has_ptr (has_a (c, test_pointer | exclude_base));
+
+ if (has_ptr)
+ {
+ // Generate aliases.
+ //
+ {
+ bool true_ (true); //@@ (im)perfect forwarding
+ instance<query_alias_traits> t (c, true_);
+ t->traverse (c);
+ }
+
+ // This class contains everything for inverse pointers and
+ // aliases for non-inverse ones. It doesn't depend on the
+ // table alias (A) template argument.
+ //
+ os << "template <>" << endl
+ << "struct " << exp << "query_columns_base< " << type << ", " <<
+ "id_" << db << " >"
+ << "{";
+
+ bool true_ (true); //@@ (im)perfect forwarding.
+ bool false_ (false);
+ instance<query_columns_base> t (c, true_, false_);
+ t->traverse (c);
+
+ os << "};";
+ }
+
+ os << "template <typename A>" << endl
+ << "struct query_columns< " << type << ", id_" << db << ", A >";
+
+ if (has_ptr)
+ os << ":" << endl
+ << " query_columns_base< " << type << ", id_" << db << " >";
+
+ {
+ instance<query_columns_bases> b (ptr_, !has_ptr);
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+
+ os << "{";
+
+ {
+ instance<query_columns_base_aliases> b (ptr_);
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+
+ {
+ instance<query_columns> t (decl_, ptr_, c);
+ t->traverse (c);
+ }
+
+ os << "};";
+
+ generate_impl (c);
+ }
+ else if (inst_)
+ {
+ // If we have the extern symbol, generate extern template declarations.
+ //
+ // 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)
+ {
+ 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)
+ {
+ bool true_ (true); //@@ (im)perfect forwarding.
+ bool false_ (false);
+
+ instance<query_columns_base> t (c, false_, true_);
+ t->traverse (c);
+ }
+
+ // Don't generate it for reuse-abstract classes.
+ //
+ if (!reuse_abst)
+ generate_inst (c);
+
+ os << "#endif // " << guard << endl
+ << endl;
+ }
+ }
+ }
+ else
+ {
+ bool has_ptr (has_a (c, test_pointer | exclude_base));
+
+ // Generate alias_traits specializations. While the class
+ // is generated even if our base has a pointer, there is
+ // not source code if we don't have pointers ourselves.
+ //
+ if (has_ptr)
+ {
+ bool false_ (false); //@@ (im)perfect forwarding
+ instance<query_alias_traits> t (c, false_);
+ t->traverse (c);
+ }
+
+ // query_columns_base
+ //
+ if (has_ptr)
+ {
+ bool false_ (false); //@@ (im)perfect forwarding.
+ instance<query_columns_base> t (c, false_, false_);
+ t->traverse (c);
+ }
+
+ // Explicit template instantiations. Don't generate it for reuse-
+ // abstract classes.
+ //
+ if (multi_dynamic && (!abstract (c) || polymorphic (c)))
+ generate_inst (c);
+ }
+}
+
+void query_columns_type::
+generate_impl (type& c)
+{
+ string guard;
+
+ // Exclude definitions (they will be explicitly instantiated once in
+ // the source file) unless we have the extern symbol. In this case
+ // the extern template declaration will make sure we don't get
+ // instantiations in multiple places and we will avoid the VC++
+ // warning C4661 (no definition provided).
+ //
+ if (multi_dynamic && ext.empty ())
+ {
+ guard = make_guard ("ODB_" + db.string () + "_QUERY_COLUMNS_DEF");
+
+ os << "#ifdef " << guard << endl
+ << endl;
+ }
+
+ {
+ bool false_ (false);
+ instance<query_columns> t (false_, ptr_, c);
+ t->traverse (c);
+ }
+
+ if (!guard.empty ())
+ os << "#endif // " << guard << endl
+ << endl;
+}
+
+void query_columns_type::
+generate_inst (type& c)
+{
+ string const& type (class_fq_name (c));
+
+ // Explicit template instantiations. Here is what we need to
+ // instantiate:
+ //
+ // 1. Reuse inheritance bases all the way to the ultimate base.
+ // Unlike poly inheritance, reuse inheritance uses the table
+ // alias of the derived type. Note that bases can have object
+ // pointers of their own but their types have already been
+ // instantiated by step 3 below performed for the base object.
+ //
+ // 2. Object pointers. Note that while object pointers cannot have
+ // their own pointers, they can have reuse inheritance bases.
+ //
+ // 3. The query_columns class for the table itself.
+ //
+ // We also need to repeat these steps for pointer_query_columns
+ // since it is used by views.
+ //
+ string alias ("access::object_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ // 1
+ //
+ {
+ instance<query_columns_base_insts> b (false, inst_, alias, false);
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+
+ // 2: Handled by query_columns_base (which is where we generate
+ // all the aliases for object pointers).
+ //
+
+ // 3
+ //
+ inst_query_columns (inst_, false, type, alias, c);
+
+ if (has_a (c, test_pointer | exclude_base))
+ inst_query_columns (inst_, true, type, alias, c);
+}
+
+// view_query_columns_type
+//
+
+void view_query_columns_type::
+traverse (type& c)
+{
+ if (decl_)
+ generate_decl (c);
+ else
+ generate_def (c);
+}
+
+void view_query_columns_type::
+generate_decl (type& c)
+{
+ string const& type (class_fq_name (c));
+ size_t obj_count (c.get<size_t> ("object-count"));
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ // Generate alias_traits specializations.
+ //
+ {
+ bool true_ (true); //@@ (im)perfect forwarding
+ instance<query_alias_traits> at (c, true_);
+
+ for (view_objects::const_iterator i (objs.begin ()); i < objs.end (); ++i)
+ {
+ if (i->kind != view_object::object)
+ continue; // Skip tables.
+
+ if (i->alias.empty ())
+ continue;
+
+ semantics::class_& o (*i->obj);
+ qname const& t (table_name (o));
+
+ // Check that the alias is not the same as the table name
+ // (if this is a polymorphic object, then the alias is just
+ // a prefix).
+ //
+ if (polymorphic (o) || t.qualified () || i->alias != t.uname ())
+ at->generate_decl (i->alias, o);
+ }
+ }
+
+ // If we have the extern symbol, generate extern template declarations.
+ // Do it before query_columns since the inheritance will trigger
+ // instantiation and we won't be able to change visibility (GCC).
+ //
+ // 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)))
+ {
+ 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 // " << guard << endl
+ << endl;
+ }
+
+ // query_columns
+ //
+ os << "struct " << exp << "access::view_traits_impl< " << type << ", " <<
+ "id_" << db << " >::query_columns";
+
+ if (obj_count > 1)
+ {
+ os << "{";
+
+ for (view_objects::const_iterator i (objs.begin ()); i < objs.end (); ++i)
+ {
+ if (i->kind != view_object::object)
+ continue; // Skip tables.
+
+ bool alias (!i->alias.empty ());
+ semantics::class_& o (*i->obj);
+ string const& oname (alias ? i->alias : class_name (o));
+ string const& otype (class_fq_name (o));
+ qname const& table (table_name (o));
+
+ os << "// " << oname << endl
+ << "//" << endl
+ << "typedef" << endl
+ << "odb::pointer_query_columns<" << endl
+ << " " << otype << "," << endl
+ << " id_" << db << "," << endl;
+
+ if (alias && (polymorphic (o) ||
+ table.qualified () ||
+ i->alias != table.uname ()))
+ {
+ os << " odb::alias_traits< " << otype << "," << endl
+ << " id_" << db << "," << endl
+ << " access::view_traits_impl< " << type << ", id_" << db <<
+ " >::" << i->alias << "_tag> >" << endl;
+ }
+ else
+ os << " odb::access::object_traits_impl< " << otype << ", id_" <<
+ db << " > >" << endl;
+
+ os << oname << ";"
+ << endl;
+ }
+
+ os << "};";
+ }
+ else
+ {
+ // For a single object view we generate a shortcut without
+ // an intermediate typedef.
+ //
+ view_object const* vo (0);
+ for (view_objects::const_iterator i (objs.begin ());
+ vo == 0 && i < objs.end ();
+ ++i)
+ {
+ if (i->kind == view_object::object)
+ vo = &*i;
+ }
+
+ bool alias (!vo->alias.empty ());
+ semantics::class_& o (*vo->obj);
+ string const& otype (class_fq_name (o));
+ qname const& table (table_name (o));
+
+ os << ":" << endl
+ << " odb::pointer_query_columns<" << endl
+ << " " << otype << "," << endl
+ << " id_" << db << "," << endl;
+
+ if (alias && (polymorphic (o) ||
+ table.qualified () ||
+ vo->alias != table.uname ()))
+ {
+ os << " odb::alias_traits<" << endl
+ << " " << otype << "," << endl
+ << " id_" << db << "," << endl
+ << " access::view_traits_impl< " << type << ", id_" <<
+ db << " >::" << vo->alias << "_tag> >";
+ }
+ else
+ os << " odb::access::object_traits_impl< " << otype <<
+ ", id_" << db << " > >";
+
+ os << "{"
+ << "};";
+ }
+}
+
+void view_query_columns_type::
+generate_def (type& c)
+{
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ // Generate alias_traits specializations.
+ //
+ {
+ bool false_ (false); //@@ (im)perfect forwarding
+ instance<query_alias_traits> at (c, false_);
+
+ for (view_objects::const_iterator i (objs.begin ());
+ i < objs.end ();
+ ++i)
+ {
+ if (i->kind != view_object::object)
+ continue; // Skip tables.
+
+ if (i->alias.empty ())
+ continue;
+
+ semantics::class_& o (*i->obj);
+ qname const& t (table_name (o));
+
+ // Check that the alias is not the same as the table name
+ // (if this is a polymorphic object, then the alias is just
+ // a prefix).
+ //
+ if (polymorphic (o) || t.qualified () || i->alias != t.uname ())
+ {
+ at->generate_def (i->alias, o, i->alias);
+ }
+ }
+ }
+
+ if (multi_dynamic)
+ generate_inst (c);
+}
+
+void view_query_columns_type::
+generate_inst (type& c)
+{
+ string const& type (class_fq_name (c));
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ string traits ("access::view_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ // Instantiate [pointer_]query_columns.
+ //
+ for (view_objects::const_iterator i (objs.begin ());
+ i < objs.end ();
+ ++i)
+ {
+ if (i->kind != view_object::object)
+ continue; // Skip tables.
+
+ if (i->alias.empty ())
+ continue; // Instantiated by object.
+
+ semantics::class_& o (*i->obj);
+ qname const& t (table_name (o));
+
+ // Check that the alias is not the same as the table name
+ // (if this is a polymorphic object, then the alias is just
+ // a prefix).
+ //
+ if (polymorphic (o) || t.qualified () || i->alias != t.uname ())
+ {
+ string const& otype (class_fq_name (o));
+ string alias ("odb::alias_traits<\n"
+ " " + otype + ",\n"
+ " id_" + db.string () + ",\n"
+ " " + traits + "::" + i->alias + "_tag>");
+
+ // Instantiate base [pointer_]query_columns.
+ //
+ {
+ instance<query_columns_base_insts> b (true, decl_, alias, true);
+ traversal::inherits i (*b);
+ inherits (o, i);
+ }
+
+ // If the pointed-to class has no pointers of its own then
+ // pointer_query_columns just derives from query_columns and
+ // that's what we need to instantiate.
+ //
+ inst_query_columns (decl_,
+ has_a (o, test_pointer | include_base),
+ otype,
+ alias,
+ o);
+ }
+ }
+}
diff --git a/odb/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/odb/context.cxx b/odb/odb/context.cxx
new file mode 100644
index 0000000..f678e64
--- /dev/null
+++ b/odb/odb/context.cxx
@@ -0,0 +1,3389 @@
+// file : odb/context.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <cctype> // std::toupper
+#include <cassert>
+#include <sstream>
+
+#include <odb/context.hxx>
+#include <odb/common.hxx>
+#include <odb/pragma.hxx>
+#include <odb/cxx-lexer.hxx>
+#include <odb/diagnostics.hxx>
+
+#include <odb/relational/mssql/context.hxx>
+#include <odb/relational/mysql/context.hxx>
+#include <odb/relational/oracle/context.hxx>
+#include <odb/relational/pgsql/context.hxx>
+#include <odb/relational/sqlite/context.hxx>
+
+using namespace std;
+
+static inline void
+add_space (string& s)
+{
+ string::size_type n (s.size ());
+ if (n != 0 && s[n - 1] != ' ')
+ s += ' ';
+}
+
+//
+// custom_cxx_type
+//
+string custom_cxx_type::
+translate (string const& val, const cxx_tokens& expr)
+{
+ // Similar to member_access::translate() and a few other places.
+ //
+ string r;
+
+ cxx_tokens_lexer l;
+ l.start (expr);
+
+ string tl;
+ for (cpp_ttype tt (l.next (tl)), ptt (CPP_EOF); tt != CPP_EOF;)
+ {
+ // Try to format the expression to resemble the style of the
+ // generated code.
+ //
+ switch (tt)
+ {
+ case CPP_NOT:
+ {
+ add_space (r);
+ r += '!';
+ break;
+ }
+ case CPP_COMMA:
+ {
+ r += ", ";
+ break;
+ }
+ case CPP_OPEN_PAREN:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD)
+ add_space (r);
+
+ r += '(';
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ r += ')';
+ break;
+ }
+ case CPP_OPEN_SQUARE:
+ {
+ r += '[';
+ break;
+ }
+ case CPP_CLOSE_SQUARE:
+ {
+ r += ']';
+ break;
+ }
+ case CPP_OPEN_BRACE:
+ {
+ add_space (r);
+ r += "{ ";
+ break;
+ }
+ case CPP_CLOSE_BRACE:
+ {
+ add_space (r);
+ r += '}';
+ break;
+ }
+ case CPP_SEMICOLON:
+ {
+ r += ';';
+ break;
+ }
+ case CPP_ELLIPSIS:
+ {
+ add_space (r);
+ r += "...";
+ break;
+ }
+ case CPP_PLUS:
+ case CPP_MINUS:
+ {
+ bool unary (ptt != CPP_NAME &&
+ ptt != CPP_SCOPE &&
+ ptt != CPP_NUMBER &&
+ ptt != CPP_STRING &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_PLUS_PLUS &&
+ ptt != CPP_MINUS_MINUS);
+
+ if (!unary)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+
+ if (!unary)
+ r += ' ';
+ break;
+ }
+ case CPP_PLUS_PLUS:
+ case CPP_MINUS_MINUS:
+ {
+ if (ptt != CPP_NAME &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_CLOSE_SQUARE)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_DEREF:
+ case CPP_DEREF_STAR:
+ case CPP_DOT:
+ case CPP_DOT_STAR:
+ {
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_STRING:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += context::strlit (tl);
+ break;
+ }
+ case CPP_NUMBER:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ break;
+ }
+ case CPP_SCOPE:
+ {
+ // Add space except for a few common cases.
+ //
+ if (ptt != CPP_NAME &&
+ ptt != CPP_OPEN_PAREN &&
+ ptt != CPP_OPEN_SQUARE)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_NAME:
+ {
+ // Start of a name.
+ //
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ break;
+ }
+ case CPP_QUERY:
+ {
+ if (ptt == CPP_OPEN_PAREN)
+ {
+ // Get the next token and see if it is ')'.
+ //
+ ptt = tt;
+ tt = l.next (tl);
+
+ if (tt == CPP_CLOSE_PAREN)
+ r += val;
+ else
+ {
+ add_space (r);
+ r += "? ";
+ }
+ continue; // We have already gotten the next token.
+ }
+ }
+ // Fall through.
+ default:
+ {
+ // Handle CPP_KEYWORD here to avoid a warning (it is not
+ // part of the cpp_ttype enumeration).
+ //
+ if (tt == CPP_KEYWORD)
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ }
+ else
+ {
+ // All the other operators.
+ //
+ add_space (r);
+ r += cxx_lexer::token_spelling[tt];
+ r += ' ';
+ }
+ break;
+ }
+ }
+
+ //
+ // Watch out for the continue statements above if you add any
+ // logic here.
+ //
+
+ ptt = tt;
+ tt = l.next (tl);
+ }
+
+ return r;
+}
+
+
+//
+// view_object
+//
+
+string view_object::
+name () const
+{
+ if (!alias.empty ())
+ return alias;
+
+ return kind == object ? context::class_name (*obj) : tbl_name.string ();
+}
+
+//
+// member_access
+//
+
+bool member_access::
+placeholder () const
+{
+ for (cxx_tokens::const_iterator i (expr.begin ()), e (expr.end ()); i != e;)
+ {
+ if (i->type == CPP_OPEN_PAREN)
+ {
+ if (++i != e && i->type == CPP_QUERY)
+ {
+ if (++i != e && i->type == CPP_CLOSE_PAREN)
+ return true;
+ }
+ }
+ else
+ ++i;
+ }
+
+ return false;
+}
+
+string member_access::
+translate (string const& obj, string const& val, string const& db) const
+{
+ if (empty ())
+ {
+ error (loc) << "non-empty " << kind << " expression required" << endl;
+ throw operation_failed ();
+ }
+
+ // This code is similar to translate_expression() from relations/source.cxx.
+ //
+ string r;
+
+ cxx_tokens_lexer l;
+ l.start (expr);
+
+ string tl;
+ for (cpp_ttype tt (l.next (tl)), ptt (CPP_EOF); tt != CPP_EOF;)
+ {
+ // Try to format the expression to resemble the style of the
+ // generated code.
+ //
+ switch (tt)
+ {
+ case CPP_COMMA:
+ {
+ r += ", ";
+ break;
+ }
+ case CPP_OPEN_PAREN:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD)
+ add_space (r);
+
+ r += '(';
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ r += ')';
+ break;
+ }
+ case CPP_OPEN_SQUARE:
+ {
+ r += '[';
+ break;
+ }
+ case CPP_CLOSE_SQUARE:
+ {
+ r += ']';
+ break;
+ }
+ case CPP_OPEN_BRACE:
+ {
+ add_space (r);
+ r += "{ ";
+ break;
+ }
+ case CPP_CLOSE_BRACE:
+ {
+ add_space (r);
+ r += '}';
+ break;
+ }
+ case CPP_SEMICOLON:
+ {
+ r += ';';
+ break;
+ }
+ case CPP_ELLIPSIS:
+ {
+ add_space (r);
+ r += "...";
+ break;
+ }
+ case CPP_PLUS:
+ case CPP_MINUS:
+ {
+ bool unary (ptt != CPP_NAME &&
+ ptt != CPP_SCOPE &&
+ ptt != CPP_NUMBER &&
+ ptt != CPP_STRING &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_PLUS_PLUS &&
+ ptt != CPP_MINUS_MINUS);
+
+ if (!unary)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+
+ if (!unary)
+ r += ' ';
+ break;
+ }
+ case CPP_PLUS_PLUS:
+ case CPP_MINUS_MINUS:
+ {
+ if (ptt != CPP_NAME &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_CLOSE_SQUARE)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_DEREF:
+ case CPP_DEREF_STAR:
+ case CPP_DOT:
+ case CPP_DOT_STAR:
+ {
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_STRING:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += context::strlit (tl);
+ break;
+ }
+ case CPP_NUMBER:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ break;
+ }
+ case CPP_SCOPE:
+ {
+ // Add space except for a few common cases.
+ //
+ if (ptt != CPP_NAME &&
+ ptt != CPP_OPEN_PAREN &&
+ ptt != CPP_OPEN_SQUARE)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_NAME:
+ {
+ // Start of a name.
+ //
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ break;
+ }
+ case CPP_NOT:
+ case CPP_QUERY:
+ {
+ if (ptt == CPP_OPEN_PAREN)
+ {
+ // Get the next token and see if it is ')'.
+ //
+ ptt = tt;
+ tt = l.next (tl);
+
+ if (tt == CPP_CLOSE_PAREN)
+ {
+ if (ptt == CPP_NOT)
+ {
+ if (db.empty ())
+ {
+ error (loc) << "database instance (!) not available in this "
+ << "context" << endl;
+ throw operation_failed ();
+ }
+
+ r += db;
+ }
+ else
+ r += val;
+ }
+ else
+ {
+ add_space (r);
+ r += (ptt == CPP_NOT ? "!" : "? ");
+ }
+ continue; // We have already gotten the next token.
+ }
+ }
+ // Fall through.
+ default:
+ {
+ // Handle CPP_KEYWORD here to avoid a warning (it is not
+ // part of the cpp_ttype enumeration).
+ //
+ if (tt == CPP_KEYWORD)
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ // Translate 'this'.
+ //
+ r += (tl == "this" ? obj : tl);
+ }
+ else
+ {
+ // All the other operators.
+ //
+ add_space (r);
+ r += cxx_lexer::token_spelling[tt];
+ r += ' ';
+ }
+ break;
+ }
+ }
+
+ //
+ // Watch out for the continue statements above if you add any
+ // logic here.
+ //
+
+ ptt = tt;
+ tt = l.next (tl);
+ }
+
+ return r;
+}
+
+// Sections.
+//
+main_section_type main_section;
+
+bool main_section_type::
+compare (object_section const& s) const
+{
+ main_section_type const* ms (dynamic_cast<main_section_type const*> (&s));
+ return ms != 0 && *this == *ms;
+}
+
+bool user_section::
+compare (object_section const& s) const
+{
+ user_section const* us (dynamic_cast<user_section const*> (&s));
+ return us != 0 && *this == *us;
+}
+
+user_section* user_section::
+total_base () const
+{
+ if (base != 0)
+ {
+ semantics::class_* poly_root (context::polymorphic (*object));
+ if (poly_root != 0 && poly_root != *object)
+ return base;
+ }
+
+ return 0;
+}
+
+size_t user_sections::
+count (unsigned short f) const
+{
+ size_t r (0);
+
+ semantics::class_* poly_root (context::polymorphic (*object));
+ bool poly_derived (poly_root != 0 && poly_root != object);
+
+ if (poly_derived && (f & count_total) != 0)
+ r = context::polymorphic_base (*object).get<user_sections> (
+ "user-sections").count (f);
+
+ for (const_iterator i (begin ()); i != end (); ++i)
+ {
+ // Skip special sections unless we were explicitly asked to count them.
+ //
+ if (i->special == user_section::special_version &&
+ (f & count_special_version) == 0)
+ continue;
+
+ // Skip non-versioned sections if we are only interested in the
+ // versioned ones.
+ //
+ if ((f & count_versioned_only) != 0 &&
+ !context::added (*i->member) && !context::deleted (*i->member))
+ continue;
+
+ bool ovd (i->base != 0 && poly_derived);
+
+ if (i->load != user_section::load_eager)
+ {
+ if (i->load_empty ())
+ {
+ if ((f & count_load_empty) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue; // Count each section only once.
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+ else
+ {
+ if ((f & count_load) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ if (i->update_empty ())
+ {
+ if ((f & count_update_empty) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+ else
+ {
+ if ((f & count_update) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+
+ if (i->optimistic ())
+ {
+ if ((f & count_optimistic) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ return r;
+}
+
+//
+// context
+//
+
+namespace
+{
+ char const* keywords[] =
+ {
+ "NULL",
+ "and",
+ "asm",
+ "auto",
+ "bitand",
+ "bitor",
+ "bool",
+ "break",
+ "case",
+ "catch",
+ "char",
+ "class",
+ "compl",
+ "const",
+ "const_cast",
+ "continue",
+ "default",
+ "delete",
+ "do",
+ "double",
+ "dynamic_cast",
+ "else",
+ "end_eq",
+ "enum",
+ "explicit",
+ "export",
+ "extern",
+ "false",
+ "float",
+ "for",
+ "friend",
+ "goto",
+ "if",
+ "inline",
+ "int",
+ "long",
+ "mutable",
+ "namespace",
+ "new",
+ "not",
+ "not_eq",
+ "operator",
+ "or",
+ "or_eq",
+ "private",
+ "protected",
+ "public",
+ "register",
+ "reinterpret_cast",
+ "return",
+ "short",
+ "signed",
+ "sizeof",
+ "static",
+ "static_cast",
+ "struct",
+ "switch",
+ "template",
+ "this",
+ "throw",
+ "true",
+ "try",
+ "typedef",
+ "typeid",
+ "typename",
+ "union",
+ "unsigned",
+ "using",
+ "virtual",
+ "void",
+ "volatile",
+ "wchar_t",
+ "while",
+ "xor",
+ "xor_eq"
+ };
+}
+
+unique_ptr<context>
+create_context (ostream& os,
+ semantics::unit& unit,
+ options const& ops,
+ features& f,
+ semantics::relational::model* m)
+{
+ unique_ptr<context> r;
+
+ switch (ops.database ()[0])
+ {
+ case database::common:
+ {
+ r.reset (new context (os, unit, ops, f));
+ break;
+ }
+ case database::mssql:
+ {
+ r.reset (new relational::mssql::context (os, unit, ops, f, m));
+ break;
+ }
+ case database::mysql:
+ {
+ r.reset (new relational::mysql::context (os, unit, ops, f, m));
+ break;
+ }
+ case database::oracle:
+ {
+ r.reset (new relational::oracle::context (os, unit, ops, f, m));
+ break;
+ }
+ case database::pgsql:
+ {
+ r.reset (new relational::pgsql::context (os, unit, ops, f, m));
+ break;
+ }
+ case database::sqlite:
+ {
+ r.reset (new relational::sqlite::context (os, unit, ops, f, m));
+ break;
+ }
+ }
+
+ return r;
+}
+
+context::
+~context ()
+{
+ if (current_ == this)
+ current_ = 0;
+}
+
+context::
+context (ostream& os_,
+ semantics::unit& u,
+ options_type const& ops,
+ features_type& f,
+ data_ptr d)
+ : data_ (d ? d : data_ptr (new (shared) data (os_))),
+ extra (data_->extra_),
+ os (data_->os_),
+ unit (u),
+ options (ops),
+ features (f),
+ db (options.database ()[0]),
+ in_comment (data_->in_comment_),
+ exp (data_->exp_),
+ ext (data_->ext_),
+ keyword_set (data_->keyword_set_),
+ include_regex (data_->include_regex_),
+ accessor_regex (data_->accessor_regex_),
+ modifier_regex (data_->modifier_regex_),
+ embedded_schema (
+ ops.generate_schema () &&
+ ops.schema_format ()[db].count (schema_format::embedded)),
+ separate_schema (
+ ops.generate_schema () &&
+ ops.schema_format ()[db].count (schema_format::separate)),
+ multi_static (ops.multi_database () == multi_database::static_),
+ multi_dynamic (ops.multi_database () == multi_database::dynamic),
+ force_versioned (false),
+ top_object (data_->top_object_),
+ cur_object (data_->cur_object_)
+{
+ assert (current_ == 0);
+ current_ = this;
+
+ // Write boolean values as true/false.
+ //
+ os.setf (ios_base::boolalpha);
+
+ // Export control.
+ //
+ if (!ops.export_symbol ()[db].empty ())
+ exp = ops.export_symbol ()[db] + " ";
+
+ ext = ops.extern_symbol ()[db];
+
+ for (size_t i (0); i < sizeof (keywords) / sizeof (char*); ++i)
+ data_->keyword_set_.insert (keywords[i]);
+
+ // SQL name regex.
+ //
+ if (ops.table_regex ().count (db) != 0)
+ {
+ strings const& s (ops.table_regex ()[db]);
+ data_->sql_name_regex_[sql_name_table].assign (s.begin (), s.end ());
+ }
+
+ if (ops.column_regex ().count (db) != 0)
+ {
+ strings const& s (ops.column_regex ()[db]);
+ data_->sql_name_regex_[sql_name_column].assign (s.begin (), s.end ());
+ }
+
+ if (ops.index_regex ().count (db) != 0)
+ {
+ strings const& s (ops.index_regex ()[db]);
+ data_->sql_name_regex_[sql_name_index].assign (s.begin (), s.end ());
+ }
+
+ if (ops.fkey_regex ().count (db) != 0)
+ {
+ strings const& s (ops.fkey_regex ()[db]);
+ data_->sql_name_regex_[sql_name_fkey].assign (s.begin (), s.end ());
+ }
+
+ if (ops.sequence_regex ().count (db) != 0)
+ {
+ strings const& s (ops.sequence_regex ()[db]);
+ data_->sql_name_regex_[sql_name_sequence].assign (s.begin (), s.end ());
+ }
+
+ if (ops.statement_regex ().count (db) != 0)
+ {
+ strings const& s (ops.statement_regex ()[db]);
+ data_->sql_name_regex_[sql_name_statement].assign (s.begin (), s.end ());
+ }
+
+ if (ops.sql_name_regex ().count (db) != 0)
+ {
+ strings const& s (ops.sql_name_regex ()[db]);
+ data_->sql_name_regex_[sql_name_all].assign (s.begin (), s.end ());
+ }
+
+ // Include regex.
+ //
+ for (strings::const_iterator i (ops.include_regex ().begin ());
+ i != ops.include_regex ().end (); ++i)
+ data_->include_regex_.push_back (regexsub (*i));
+
+ // Common accessor/modifier naming variants. Try the user-supplied and
+ // more specific ones first.
+ //
+ for (strings::const_iterator i (ops.accessor_regex ().begin ());
+ i != ops.accessor_regex ().end (); ++i)
+ data_->accessor_regex_.push_back (regexsub (*i));
+
+ data_->accessor_regex_.push_back (regexsub ("/(.+)/get_$1/")); // get_foo
+ data_->accessor_regex_.push_back (regexsub ("/(.+)/get\\u$1/")); // getFoo
+ data_->accessor_regex_.push_back (regexsub ("/(.+)/get$1/")); // getfoo
+ data_->accessor_regex_.push_back (regexsub ("/(.+)/$1/")); // foo
+
+ for (strings::const_iterator i (ops.modifier_regex ().begin ());
+ i != ops.modifier_regex ().end (); ++i)
+ data_->modifier_regex_.push_back (regexsub (*i));
+
+ data_->modifier_regex_.push_back (regexsub ("/(.+)/set_$1/")); // set_foo
+ data_->modifier_regex_.push_back (regexsub ("/(.+)/set\\u$1/")); // setFoo
+ data_->modifier_regex_.push_back (regexsub ("/(.+)/set$1/")); // setfoo
+ data_->modifier_regex_.push_back (regexsub ("/(.+)/$1/")); // foo
+}
+
+context::
+context ()
+ : data_ (current ().data_),
+ extra (current ().extra),
+ os (current ().os),
+ unit (current ().unit),
+ options (current ().options),
+ features (current ().features),
+ db (current ().db),
+ in_comment (current ().in_comment),
+ exp (current ().exp),
+ ext (current ().ext),
+ keyword_set (current ().keyword_set),
+ include_regex (current ().include_regex),
+ accessor_regex (current ().accessor_regex),
+ modifier_regex (current ().modifier_regex),
+ embedded_schema (current ().embedded_schema),
+ separate_schema (current ().separate_schema),
+ multi_static (current ().multi_static),
+ multi_dynamic (current ().multi_dynamic),
+ force_versioned (current ().force_versioned),
+ top_object (current ().top_object),
+ cur_object (current ().cur_object)
+{
+}
+
+context* context::current_;
+
+semantics::data_member* context::
+id (data_member_path const& mp)
+{
+ semantics::data_member* idf (mp.front ());
+
+ if (!id (*idf))
+ return 0;
+
+ // This is for special ids, such as polymorphic-ref, which
+ // don't have "id-member" set (and we want to keep it that
+ // way since it is not really a full-fledged id).
+ //
+ if (idf->get<string> ("id").empty ()) // Not a nested id.
+ return idf;
+
+ const data_member_path& id (
+ *id_member (
+ dynamic_cast<semantics::class_&> (idf->scope ())));
+
+ // Now we need to make sure id is a prefix of mp;
+ //
+ return mp.sub (id) ? idf : 0;
+}
+
+semantics::data_member* context::
+object_pointer (data_member_path const& mp)
+{
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ if (object_pointer (utype (**i)))
+ return *i;
+ }
+
+ return 0;
+}
+
+bool context::
+readonly (data_member_path const& mp, data_member_scope const& ms)
+{
+ assert (mp.size () == ms.size ());
+
+ data_member_scope::const_reverse_iterator si (ms.rbegin ());
+
+ for (data_member_path::const_reverse_iterator pi (mp.rbegin ());
+ pi != mp.rend ();
+ ++pi, ++si)
+ {
+ semantics::data_member& m (**pi);
+
+ if (m.count ("readonly"))
+ return true;
+
+ // Check if any of the classes in the inheritance chain for the
+ // class containing this member are readonly.
+ //
+ class_inheritance_chain const& ic (*si);
+
+ assert (ic.back () == &m.scope ());
+
+ for (class_inheritance_chain::const_reverse_iterator ci (ic.rbegin ());
+ ci != ic.rend ();
+ ++ci)
+ {
+ semantics::class_& c (**ci);
+
+ if (c.count ("readonly"))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool context::
+readonly (semantics::data_member& m)
+{
+ if (m.count ("readonly"))
+ return true;
+
+ // Check if the whole class (object or composite value) is marked
+ // as readonly.
+ //
+ if (m.scope ().count ("readonly"))
+ return true;
+
+ return false;
+}
+
+bool context::
+null (data_member_path const& mp) const
+{
+ // Outer members can override the null-ability of the inner ones. So
+ // start from the most outer member.
+ //
+ for (data_member_path::const_iterator i (mp.begin ()); i != mp.end (); ++i)
+ {
+ if (null (**i))
+ return true;
+ }
+
+ return false;
+}
+
+bool context::
+null (semantics::data_member& m) const
+{
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ if (object_pointer (t))
+ {
+ // By default pointers can be null.
+ //
+ if (m.count ("null"))
+ return true;
+
+ if (!m.count ("not-null"))
+ {
+ if (t.count ("null"))
+ return true;
+
+ if (!t.count ("not-null"))
+ return true;
+ }
+
+ return false;
+ }
+ else
+ {
+ // Everything else by default is not null.
+ //
+ if (m.count ("null"))
+ return true;
+
+ if (!m.count ("not-null"))
+ {
+ if (t.count ("null"))
+ return true;
+
+ if (!t.count ("not-null"))
+ {
+ semantics::type* pt;
+
+ // Check if this type is a wrapper.
+ //
+ if (t.get<bool> ("wrapper"))
+ {
+ // First see if it is null by default.
+ //
+ if (t.get<bool> ("wrapper-null-handler") &&
+ t.get<bool> ("wrapper-null-default"))
+ return true;
+
+ // Otherwise, check the wrapped type.
+ //
+ pt = t.get<semantics::type*> ("wrapper-type");
+ hint = t.get<semantics::names*> ("wrapper-hint");
+ pt = &utype (*pt, hint);
+
+ if (pt->count ("null"))
+ return true;
+
+ if (pt->count ("not-null"))
+ return false;
+ }
+ else
+ pt = &t;
+ }
+ }
+
+ return false;
+ }
+}
+
+bool context::
+null (semantics::data_member& m, string const& kp) const
+{
+ if (kp.empty ())
+ return null (m);
+
+ semantics::type& c (utype (m));
+ semantics::type& t (utype (m, kp));
+
+ if (object_pointer (t))
+ {
+ if (m.count (kp + "-null"))
+ return true;
+
+ if (!m.count (kp + "-not-null"))
+ {
+ if (c.count (kp + "-null"))
+ return true;
+
+ if (!c.count (kp + "-not-null"))
+ {
+ if (t.count ("null"))
+ return true;
+
+ if (!t.count ("not-null"))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+ else
+ {
+ if (m.count (kp + "-null"))
+ return true;
+
+ if (!m.count (kp + "-not-null"))
+ {
+ if (c.count (kp + "-null"))
+ return true;
+
+ if (!c.count (kp + "-not-null"))
+ {
+ if (t.count ("null"))
+ return true;
+
+ if (!t.count ("not-null"))
+ {
+ semantics::type* pt;
+
+ // Check if this type is a wrapper.
+ //
+ if (t.get<bool> ("wrapper"))
+ {
+ // First see if it is null by default.
+ //
+ if (t.get<bool> ("wrapper-null-handler") &&
+ t.get<bool> ("wrapper-null-default"))
+ return true;
+
+ // Otherwise, check the wrapped type.
+ //
+ pt = t.get<semantics::type*> ("wrapper-type");
+ pt = &utype (*pt);
+
+ if (pt->count ("null"))
+ return true;
+
+ if (pt->count ("not-null"))
+ return false;
+ }
+ else
+ pt = &t;
+ }
+ }
+ }
+
+ return false;
+ }
+}
+
+size_t context::
+polymorphic_depth (semantics::class_& c)
+{
+ if (c.count ("polymorphic-depth"))
+ return c.get<size_t> ("polymorphic-depth");
+
+ // Calculate our hierarchy depth (number of classes).
+ //
+ using semantics::class_;
+
+ class_* root (polymorphic (c));
+ assert (root != 0);
+
+ size_t r (1); // One for the root.
+
+ for (class_* b (&c); b != root; b = &polymorphic_base (*b))
+ ++r;
+
+ c.set ("polymorphic-depth", r);
+ return r;
+}
+
+context::class_kind_type context::
+class_kind (semantics::class_& c)
+{
+ if (object (c))
+ return class_object;
+ else if (view (c))
+ return class_view;
+ else if (composite (c))
+ return class_composite;
+ else
+ return class_other;
+}
+
+string context::
+class_name (semantics::class_& c)
+{
+ return c.is_a<semantics::class_instantiation> ()
+ ? c.get<semantics::names*> ("tree-hint")->name ()
+ : c.name ();
+}
+
+string context::
+class_fq_name (semantics::class_& c)
+{
+ return c.is_a<semantics::class_instantiation> ()
+ ? c.fq_name (c.get<semantics::names*> ("tree-hint"))
+ : c.fq_name ();
+}
+
+semantics::scope& context::
+class_scope (semantics::class_& c)
+{
+ return c.is_a<semantics::class_instantiation> ()
+ ? c.get<semantics::names*> ("tree-hint")->scope ()
+ : c.scope ();
+}
+
+semantics::path context::
+class_file (semantics::class_& c)
+{
+ // If we have an explicit definition location, use that.
+ //
+ if (c.count ("definition"))
+ return semantics::path (LOCATION_FILE (c.get<location_t> ("definition")));
+ //
+ // Otherwise, if it is a template instantiation, use the location
+ // of the qualifier.
+ //
+ else if (c.is_a<semantics::class_instantiation> ())
+ return semantics::path (LOCATION_FILE (c.get<location_t> ("location")));
+ else
+ return c.file ();
+}
+
+location_t context::
+class_location (semantics::class_& c)
+{
+ return c.count ("definition")
+ ? c.get<location_t> ("definition")
+ : class_real_location (c);
+}
+
+location_t context::
+class_real_location (semantics::class_& c)
+{
+ return c.is_a<semantics::class_instantiation> ()
+ ? c.get<location_t> ("location")
+ : real_source_location (TYPE_NAME (c.tree_node ()));
+}
+
+string context::
+upcase (string const& s)
+{
+ string r;
+ string::size_type n (s.size ());
+
+ r.reserve (n);
+
+ for (string::size_type i (0); i < n; ++i)
+ r.push_back (toupper (s[i]));
+
+ return r;
+}
+
+void context::
+diverge (streambuf* sb)
+{
+ data_->os_stack_.push (data_->os_.rdbuf ());
+ data_->os_.rdbuf (sb);
+}
+
+void context::
+restore ()
+{
+ data_->os_.rdbuf (data_->os_stack_.top ());
+ data_->os_stack_.pop ();
+}
+
+semantics::type& context::
+utype (semantics::type& t)
+{
+ if (semantics::qualifier* q = dynamic_cast<semantics::qualifier*> (&t))
+ return q->base_type ();
+ else
+ return t;
+}
+
+semantics::type& context::
+utype (semantics::type& t, semantics::names*& hint)
+{
+ if (semantics::qualifier* q = dynamic_cast<semantics::qualifier*> (&t))
+ {
+ hint = q->qualifies ().hint ();
+ return q->base_type ();
+ }
+ else
+ return t;
+}
+
+semantics::type& context::
+utype (semantics::data_member& m,
+ semantics::names*& hint,
+ string const& kp,
+ const custom_cxx_type** translation)
+{
+ semantics::type* t (0);
+
+ if (kp.empty ())
+ {
+ t = &m.type ();
+
+ if (semantics::qualifier* q = dynamic_cast<semantics::qualifier*> (t))
+ {
+ hint = q->qualifies ().hint ();
+ t = &q->base_type ();
+ }
+ else
+ hint = m.belongs ().hint ();
+ }
+ else
+ {
+ if (m.count (kp + "-tree-type"))
+ t = indirect_type (m, kp, hint);
+ else
+ {
+ t = &utype (m);
+
+ // "See through" wrappers.
+ //
+ if (semantics::type* wt = wrapper (*t))
+ t = indirect_type (utype (*wt), kp, hint);
+ else
+ t = indirect_type (*t, kp, hint);
+ }
+ }
+
+ // Do we need to map this type?
+ //
+ // @@ Need to cache the result on the member.
+ //
+ if (translation != 0)
+ *translation = 0;
+
+ for (semantics::scope* s (&m.scope ());; s = &s->scope_ ())
+ {
+ using semantics::namespace_;
+
+ if (namespace_* ns = dynamic_cast<namespace_*> (s))
+ {
+ if (ns->extension ())
+ s = &ns->original ();
+ }
+
+ if (s->count ("custom-cxx-type-map"))
+ {
+ typedef custom_cxx_type_map map;
+
+ map& m (s->get<map> ("custom-cxx-type-map"));
+ map::const_iterator i (m.find (t));
+
+ if (i != m.end ())
+ {
+ hint = i->second->as_hint;
+ t = i->second->as;
+
+ if (translation != 0)
+ *translation = i->second;
+
+ // Currently we only support one level of mapping, but I am
+ // sure someone will want multiple levels.
+ //
+ break;
+ }
+ }
+
+ if (!s->named_p () || s->global_scope ())
+ break;
+ }
+
+ return *t;
+}
+
+bool context::
+const_type (semantics::type& t)
+{
+ if (semantics::qualifier* q = dynamic_cast<semantics::qualifier*> (&t))
+ return q->const_ ();
+
+ return false;
+}
+
+string context::
+type_ref_type (semantics::type& t,
+ semantics::names* hint,
+ bool mc,
+ string const& var,
+ bool decay)
+{
+ using semantics::array;
+ string r;
+
+ // Note that trailing const syntax is used for a reason (consider
+ // t == const foo*). We may also have to decay then top-level array.
+ //
+ array* a;
+ if (decay && (a = dynamic_cast<array*> (&utype (t))) != 0)
+ {
+ semantics::type& bt (a->base_type ());
+ hint = a->contains ().hint ();
+
+ if (bt.is_a<array> ())
+ {
+ // If we need to add/strip const or no name was used in the
+ // declaration, then create an array declaration (e.g., for
+ // char x[2][3] we will have char const (*x)[3]).
+ //
+ if (mc != const_type (t) || hint == 0)
+ return type_val_type (bt, 0, mc, "(*" + var + ")");
+ }
+
+ // Array base type is always cvr-unqualified.
+ //
+ if (mc)
+ r = bt.fq_name (hint) + " const";
+ else
+ r = bt.fq_name (hint);
+
+ r += '*';
+
+ if (!var.empty ())
+ r += ' ' + var;
+ }
+ else
+ {
+ if (mc == const_type (t))
+ r = t.fq_name (hint);
+ else if (mc)
+ r = t.fq_name (hint) + " const";
+ else
+ {
+ semantics::type& ut (utype (t, hint));
+ r = ut.fq_name (hint);
+ }
+
+ r += '&';
+
+ if (!var.empty ())
+ r += ' ' + var;
+ }
+
+ return r;
+}
+
+string context::
+type_val_type (semantics::type& t,
+ semantics::names* hint,
+ bool mc,
+ string const& var)
+{
+ using semantics::array;
+ string r;
+
+ // Arrays are a complicated case. Firstly, we may need to add/strip const
+ // to/from the base type. Secondly, the array dimensions are written after
+ // the variable name. All this is further complicated by multiple dimensions.
+ // Thanks, Dennis!
+ //
+ if (array* a = dynamic_cast<array*> (&utype (t)))
+ {
+ semantics::type& bt (a->base_type ());
+
+ // If we don't need to add/strip const and a name was used in the
+ // declaration, then use that name.
+ //
+ if (mc == const_type (t) && hint != 0)
+ {
+ r = t.fq_name (hint);
+
+ if (!var.empty ())
+ r += ' ' + var;
+ }
+ else
+ {
+ // Otherwise, construct the array declaration.
+ //
+ string v (var);
+ v += '[';
+ ostringstream ostr;
+ ostr << a->size ();
+ v += ostr.str ();
+
+ if (a->size () > 0xFFFFFFFF)
+ v += "ULL";
+ else if (a->size () > 2147483647)
+ v += "U";
+
+ v += ']';
+
+ r = type_val_type (bt, a->contains ().hint (), mc, v);
+ }
+ }
+ else
+ {
+ if (mc == const_type (t))
+ r = t.fq_name (hint);
+ else if (mc)
+ r = t.fq_name (hint) + " const";
+ else
+ {
+ semantics::type& ut (utype (t, hint));
+ r = ut.fq_name (hint);
+ }
+
+ if (!var.empty ())
+ r += ' ' + var;
+ }
+
+ return r;
+}
+
+void context::
+set_member (semantics::data_member& m,
+ const string& obj,
+ const string& val,
+ const string& db,
+ const string& type)
+{
+ member_access& ma (m.get<member_access> ("set"));
+
+ // If this is a custom expression, output the location of where
+ // it came from.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ if (ma.placeholder ())
+ {
+ // Cast the database to the concrete type this code is generated
+ // for. This way the user is free to use either the concrete or
+ // the common.
+ //
+ string d;
+ if (!db.empty ())
+ d = "static_cast<" + context::db.string () + "::database&> (" + db + ")";
+
+ os << ma.translate (obj, val, d) << ";";
+ }
+ else
+ {
+ // If this member is const and we have a synthesized direct access,
+ // then cast away constness. Otherwise, we assume that the user-
+ // provided expression handles this.
+ //
+ bool cast (!type.empty () && ma.direct () && const_member (m));
+ if (cast)
+ os << "const_cast< " << type << "& > (" << endl;
+
+ os << ma.translate (obj);
+
+ if (cast)
+ os << ")";
+
+ os << " = " << val << ";";
+ }
+}
+
+void context::
+inc_member (semantics::data_member& m,
+ const string& obj,
+ const string& gobj,
+ const string& type)
+{
+ member_access& ma (m.get<member_access> ("set"));
+
+ // If this is a custom expression, output the location of where
+ // it came from.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ if (ma.placeholder ())
+ {
+ member_access& gma (m.get<member_access> ("get"));
+
+ if (!gma.synthesized)
+ os << "// From " << location_string (gma.loc, true) << endl;
+
+ os << ma.translate (obj, gma.translate (gobj) + " + 1") << ";";
+ }
+ else
+ {
+ // If this member is const and we have a synthesized direct access,
+ // then cast away constness. Otherwise, we assume that the user-
+ // provided expression handles this.
+ //
+ os << "++";
+
+ bool cast (ma.direct () && const_member (m));
+ if (cast)
+ os << "const_cast< " << type << "& > (" << endl;
+
+ os << ma.translate (obj);
+
+ if (cast)
+ os << ")";
+
+ os << ";";
+ }
+}
+
+void context::
+resolve_data_members (data_member_path& r,
+ semantics::class_& c,
+ const string& name,
+ const location& l,
+ cxx_string_lexer& lex)
+{
+ using semantics::class_;
+ using semantics::data_member;
+
+ // The name was already verified to be syntactically correct so
+ // we don't need to do any extra error checking in this area.
+ //
+ lex.start (name);
+
+ try
+ {
+ string tl;
+ cpp_ttype tt (lex.next (tl));
+
+ data_member& m (c.lookup<data_member> (tl, class_::include_hidden));
+
+ r.push_back (&m);
+
+ if (container (m))
+ return;
+
+ // Resolve nested members if any.
+ //
+ for (tt = lex.next (tl); tt == CPP_DOT; tt = lex.next (tl))
+ {
+ lex.next (tl); // Get CPP_NAME.
+
+ data_member& om (*r.back ());
+
+ // Check that the outer member is composite and also unwrap it while
+ // at it.
+ //
+ class_* comp (composite_wrapper (utype (om)));
+ if (comp == 0)
+ {
+ error (l) << "data member '" << om.name () << "' is not composite"
+ << endl;
+ throw operation_failed ();
+ }
+
+ data_member& nm (
+ comp->lookup<data_member> (tl, class_::include_hidden));
+
+ r.push_back (&nm);
+
+ if (container (nm))
+ return;
+ }
+ }
+ catch (semantics::unresolved const& e)
+ {
+ if (e.type_mismatch)
+ error (l) << "name '" << e.name << "' does not refer to a data member"
+ << endl;
+ else
+ error (l) << "unable to resolve data member '" << e.name << endl;
+
+ throw operation_failed ();
+ }
+ catch (semantics::ambiguous const& e)
+ {
+ error (l) << "data member name '" << e.first.name () << "' is ambiguous"
+ << endl;
+
+ info (e.first.named ().location ()) << "could resolve to " <<
+ "this data member" << endl;
+
+ info (e.second.named ().location ()) << "or could resolve " <<
+ "to this data member" << endl;
+
+ throw operation_failed ();
+ }
+}
+
+bool context::
+composite_ (semantics::class_& c)
+{
+ bool r (c.count ("value") && !c.count ("simple") && !c.count ("container"));
+ c.set ("composite-value", r);
+ return r;
+}
+
+// context::table_prefix
+//
+context::table_prefix::
+table_prefix (semantics::class_& c)
+ : level (1)
+{
+ context& ctx (context::current ());
+
+ ns_schema = ctx.schema (class_scope (c));
+ ns_prefix = ctx.table_name_prefix (class_scope (c));
+ prefix = ctx.table_name (c, &derived);
+ prefix += "_";
+}
+
+void context::table_prefix::
+append (semantics::data_member& m)
+{
+ assert (level > 0);
+
+ context& ctx (context::current ());
+
+ // If a custom table prefix was specified, then ignore the top-level
+ // table prefix (this corresponds to a container directly inside an
+ // object) but keep the schema unless the alternative schema is fully
+ // qualified.
+ //
+ if (m.count ("table"))
+ {
+ qname p, n (m.get<qname> ("table"));
+
+ if (n.fully_qualified ())
+ p = n.qualifier ();
+ else
+ {
+ if (n.qualified ())
+ {
+ p = ns_schema;
+ p.append (n.qualifier ());
+ }
+ else
+ p = prefix.qualifier ();
+ }
+
+ if (level == 1)
+ {
+ p.append (ns_prefix);
+ derived = false;
+ }
+ else
+ p.append (prefix.uname ());
+
+ p += n.uname ();
+ prefix.swap (p);
+ }
+ // Otherwise use the member name and add an underscore unless it is
+ // already there.
+ //
+ else
+ {
+ string name (ctx.public_name_db (m));
+ size_t n (name.size ());
+
+ prefix += name;
+
+ if (n != 0 && name[n - 1] != '_')
+ prefix += "_";
+
+ derived = true;
+ }
+
+ level++;
+}
+
+qname context::
+schema (semantics::scope& s) const
+{
+ if (s.count ("qualified-schema"))
+ return s.get<qname> ("qualified-schema");
+
+ qname r;
+
+ for (semantics::scope* ps (&s);; ps = &ps->scope_ ())
+ {
+ using semantics::namespace_;
+
+ namespace_* ns (dynamic_cast<namespace_*> (ps));
+
+ if (ns == 0) // Some other scope.
+ {
+ if (!ps->named_p ())
+ break;
+
+ continue;
+ }
+
+ if (ns->extension ())
+ ns = &ns->original ();
+
+ bool sf (ns->count ("schema"));
+ bool tf (ns->count ("table"));
+
+ if (tf)
+ {
+ qname n (ns->get<qname> ("table"));
+ tf = n.qualified ();
+
+ // If we have both schema and qualified table prefix, see which
+ // takes precedence based on order.
+ //
+
+ if (tf && sf)
+ {
+ if (ns->get<location_t> ("table-location") >
+ ns->get<location_t> ("schema-location"))
+ sf = false;
+ else
+ tf = false;
+ }
+ }
+
+ if (sf || tf)
+ {
+ qname n (
+ sf
+ ? ns->get<qname> ("schema")
+ : ns->get<qname> ("table").qualifier ());
+ n.append (r);
+ n.swap (r);
+ }
+
+ if (r.fully_qualified () ||
+ ns->global_scope ()) // Note: namespaces always named.
+ break;
+ }
+
+ // If we are still not fully qualified, add the schema that was
+ // specified on the command line.
+ //
+ if (!r.fully_qualified () && options.schema ().count (db) != 0)
+ {
+ qname n (options.schema ()[db]);
+ n.append (r);
+ n.swap (r);
+ }
+
+ s.set ("qualified-schema", r);
+ return r;
+}
+
+string context::
+table_name_prefix (semantics::scope& s) const
+{
+ if (s.count ("table-prefix"))
+ return s.get<string> ("table-prefix");
+
+ string r;
+
+ for (semantics::scope* ps (&s);; ps = &ps->scope_ ())
+ {
+ using semantics::namespace_;
+
+ namespace_* ns (dynamic_cast<namespace_*> (ps));
+
+ if (ns == 0) // Some other scope.
+ {
+ if (!ps->named_p ())
+ break;
+
+ continue;
+ }
+
+ if (ns->extension ())
+ ns = &ns->original ();
+
+ if (ns->count ("table"))
+ {
+ qname n (ns->get<qname> ("table"));
+ r = n.uname () + r;
+ }
+
+ if (ns->global_scope ()) // Note: namespaces always named.
+ break;
+ }
+
+ // Add the prefix that was specified on the command line.
+ //
+ if (options.table_prefix ().count (db) != 0)
+ r = options.table_prefix ()[db] + r;
+
+ s.set ("table-prefix", r);
+ return r;
+}
+
+qname context::
+table_name (semantics::class_& c, bool* pd) const
+{
+ if (c.count ("qualified-table"))
+ return c.get<qname> ("qualified-table");
+
+ qname r;
+ bool sf (c.count ("schema"));
+ bool derived;
+
+ if (c.count ("table"))
+ {
+ r = c.get<qname> ("table");
+
+ if (sf)
+ {
+ // If we have both schema and qualified table, see which takes
+ // precedence based on order. If the table is unqualifed, then
+ // add the schema.
+ //
+ sf = !r.qualified () ||
+ c.get<location_t> ("table-location") <
+ c.get<location_t> ("schema-location");
+ }
+
+ derived = false;
+ }
+ else
+ {
+ r = class_name (c);
+ derived = true;
+ }
+
+ if (sf)
+ {
+ qname n (c.get<qname> ("schema"));
+ n.append (r.uname ());
+ n.swap (r);
+ }
+
+ // Unless we are fully qualified, add any schemas that were
+ // specified on the namespaces and/or with the command line
+ // option.
+ //
+ if (!r.fully_qualified ())
+ {
+ qname n (schema (class_scope (c)));
+ n.append (r);
+ n.swap (r);
+ }
+
+ // Add the table prefix if any.
+ //
+ r.uname () = table_name_prefix (class_scope (c)) + r.uname ();
+
+ if (derived)
+ r.uname () = transform_name (r.uname (), sql_name_table);
+
+ c.set ("qualified-table", r);
+
+ if (pd != 0)
+ *pd = derived;
+
+ return r;
+}
+
+qname context::
+table_name (semantics::class_& obj, data_member_path const& mp) const
+{
+ table_prefix tp (obj);
+
+ if (mp.size () == 1)
+ {
+ // Container directly in the object.
+ //
+ return table_name (*mp.back (), tp);
+ }
+ else
+ {
+ data_member_path::const_iterator i (mp.begin ());
+
+ // The last member is the container.
+ //
+ for (data_member_path::const_iterator e (mp.end () - 1); i != e; ++i)
+ tp.append (**i);
+
+ return table_name (**i, tp);
+ }
+}
+
+// The table prefix passed as the second argument must include the table
+// prefix specified on namespaces and with the --table-prefix option.
+//
+qname context::
+table_name (semantics::data_member& m, table_prefix const& p) const
+{
+ assert (p.level > 0);
+ qname r;
+ string rn;
+ bool derived; // Any of the components in the table name is derived.
+
+ // If a custom table name was specified, then ignore the top-level
+ // table prefix (this corresponds to a container directly inside an
+ // object). If the container table is unqualifed, then we use the
+ // object schema. If it is fully qualified, then we use that name.
+ // Finally, if it is qualified but not fully qualifed, then we
+ // append the object's namespace schema.
+ //
+ if (m.count ("table"))
+ {
+ qname n (m.get<qname> ("table"));
+
+ if (n.fully_qualified ())
+ r = n.qualifier ();
+ else
+ {
+ if (n.qualified ())
+ {
+ r = p.ns_schema;
+ r.append (n.qualifier ());
+ }
+ else
+ r = p.prefix.qualifier ();
+ }
+
+ if (p.level == 1)
+ {
+ rn = p.ns_prefix;
+ derived = false;
+ }
+ else
+ {
+ rn = p.prefix.uname ();
+ derived = p.derived;
+ }
+
+ rn += n.uname ();
+ }
+ else
+ {
+ r = p.prefix.qualifier ();
+ rn = p.prefix.uname () + public_name_db (m);
+ derived = true;
+ }
+
+ if (derived)
+ r.append (transform_name (rn, sql_name_table));
+ else
+ r.append (rn);
+
+ return r;
+}
+
+string context::
+table_options (semantics::class_& c)
+{
+ string r;
+
+ // Accumulate options from class.
+ //
+ // @@ Should we also get them from bases?
+ //
+ // @@ Note for some databases (like SQLite), options are seperated with
+ // comma, not space. Likely the same issue in the column_options().
+ //
+ if (c.count ("options"))
+ {
+ strings const& o (c.get<strings> ("options"));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ return r;
+}
+
+string context::
+table_options (semantics::data_member& m, semantics::type& c)
+{
+ string r;
+
+ // Accumulate options from container and member.
+ //
+ // @@ Currently there is no way to assign options to the container type. If
+ // we use the value specifier, then it ends up being treated as a value
+ // type. To support this we will probably need to invent the container
+ // specifier.
+ //
+ if (c.count ("options"))
+ {
+ strings const& o (c.get<strings> ("options"));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ if (m.count ("options"))
+ {
+ strings const& o (m.get<strings> ("options"));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ return r;
+}
+
+// context::column_prefix
+//
+context::column_prefix::
+column_prefix (data_member_path const& mp, bool l)
+ : derived (false), underscore (false)
+{
+ if (mp.size () < (l ? 1 : 2))
+ return;
+
+ for (data_member_path::const_iterator i (mp.begin ()),
+ e (mp.end () - (l ? 0 : 1)); i != e; ++i)
+ append (**i);
+}
+
+void context::column_prefix::
+append (semantics::data_member& m, string const& kp, string const& dn)
+{
+ bool d;
+ context& ctx (context::current ());
+
+ if (kp.empty ())
+ prefix += ctx.column_name (m, d);
+ else
+ prefix += ctx.column_name (m, kp, dn, d);
+
+ // If the user provided the column prefix, then use it verbatime.
+ // Otherwise, append the underscore, unless it is already there.
+ //
+ underscore = false;
+ if (d)
+ {
+ size_t n (prefix.size ());
+
+ if (n != 0 && prefix[n - 1] != '_')
+ {
+ prefix += '_';
+ underscore = true;
+ }
+ }
+
+ derived = derived || d;
+}
+
+string context::
+column_name (semantics::data_member& m, bool& derived) const
+{
+ derived = !m.count ("column");
+ return derived
+ ? public_name_db (m)
+ : m.get<table_column> ("column").column;
+}
+
+string context::
+column_name (semantics::data_member& m, column_prefix const& cp) const
+{
+ bool d;
+ const string& cn (column_name (m, d));
+ string n (cp.prefix);
+
+ if (cn.empty () && cp.underscore)
+ n.resize (n.size () - 1); // Strip underscore that was auto added.
+
+ n += cn;
+
+ // If any component is derived, then run it through the SQL name regex.
+ //
+ if (d || cp.derived)
+ n = transform_name (n, sql_name_column);
+
+ return n;
+}
+
+string context::
+column_name (semantics::data_member& m,
+ string const& p,
+ string const& d,
+ bool& derived) const
+{
+ if (p.empty () && d.empty ())
+ return column_name (m, derived);
+
+ // A container column name can be specified for the member or for the
+ // container type.
+ //
+ string key (p + "-column");
+ derived = false;
+
+ if (m.count (key))
+ return m.get<string> (key);
+ else
+ {
+ semantics::type& t (utype (m));
+
+ if (t.count (key))
+ return t.get<string> (key);
+ }
+
+ derived = true;
+ return d;
+}
+
+string context::
+column_name (semantics::data_member& m,
+ string const& kp,
+ string const& dn,
+ column_prefix const& cp) const
+{
+ bool d;
+ const string& cn (column_name (m, kp, dn, d));
+ string n (cp.prefix);
+
+ if (cn.empty () && cp.underscore)
+ n.resize (n.size () - 1); // Strip underscore that was auto-added.
+
+ n += cn;
+
+ // If any component is derived, the run it through the SQL name regex.
+ //
+ if (d || cp.derived)
+ n = transform_name (n, sql_name_column);
+
+ return n;
+}
+
+string context::
+column_name (data_member_path const& mp) const
+{
+ return column_name (*mp.back (), column_prefix (mp));
+}
+
+string context::
+column_type (const data_member_path& mp, string const& kp, bool id)
+{
+ if (kp.empty ())
+ {
+ // Return the id type if this member is or is a part of an object id
+ // or pointer to object.
+ //
+ return mp.back ()->get<string> (
+ id || context::id (mp) || object_pointer (mp)
+ ? "column-id-type"
+ : "column-type");
+ }
+ else
+ return indirect_value<string> (*mp.back (), kp + "-column-type");
+}
+
+string context::
+column_type (semantics::data_member& m, string const& kp)
+{
+ return kp.empty ()
+ ? m.get<string> ("column-type")
+ : indirect_value<string> (m, kp + "-column-type");
+}
+
+string context::
+column_options (semantics::data_member& m)
+{
+ // Accumulate options from both type and member.
+ //
+ semantics::type* t (&utype (m));
+
+ if (semantics::class_* p = object_pointer (*t))
+ t = &utype (*id_member (*p));
+
+ if (semantics::type* w = wrapper (*t))
+ t = w;
+
+ string r;
+
+ if (t->count ("options"))
+ {
+ strings const& o (t->get<strings> ("options"));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ if (m.count ("options"))
+ {
+ strings const& o (m.get<strings> ("options"));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ return r;
+}
+
+string context::
+column_options (semantics::data_member& m, string const& kp)
+{
+ if (kp.empty ())
+ return column_options (m);
+
+ string k (kp + "-options");
+
+ // Accumulate options from type, container, and member.
+ //
+ semantics::type& c (utype (m));
+ semantics::type* t (&utype (m, kp));
+
+ if (semantics::class_* p = object_pointer (*t))
+ t = &utype (*id_member (*p));
+
+ if (semantics::type* w = wrapper (*t))
+ t = w;
+
+ string r;
+
+ if (t->count ("options"))
+ {
+ strings const& o (t->get<strings> ("options"));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ if (c.count (k))
+ {
+ strings const& o (c.get<strings> (k));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ if (m.count (k))
+ {
+ strings const& o (m.get<strings> (k));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ return r;
+}
+
+context::type_map_type::const_iterator context::type_map_type::
+find (semantics::type& t, semantics::names* hint)
+{
+ const_iterator e (end ()), i (e);
+
+ // First check the hinted name. This allows us to handle things like
+ // size_t which is nice to map to the same type irrespective of the
+ // actual type. Since this type can be an alias for the one we are
+ // interested in, go into nested hints.
+ //
+ for (; hint != 0 && i == e; hint = hint->hint ())
+ i = base::find (t.fq_name (hint));
+
+ // If the hinted name didn't work, try the primary name (e.g.,
+ // ::std::string) instead of a user typedef (e.g., my_string).
+ //
+ if (i == e)
+ i = base::find (t.fq_name ());
+
+ return i;
+}
+
+string context::
+database_type_impl (semantics::type& t,
+ semantics::names* hint,
+ bool id,
+ bool* null)
+{
+ using semantics::enum_;
+
+ // By default map an enum as its underlying type.
+ //
+ if (enum_* e = dynamic_cast<enum_*> (&t))
+ return database_type_impl (
+ e->underlying_type (), e->underlying_type_hint (), id, null);
+
+ // Built-in type mapping.
+ //
+ type_map_type::const_iterator i (data_->type_map_.find (t, hint));
+ if (i != data_->type_map_.end ())
+ {
+ if (null != 0)
+ *null = i->second.null;
+ return id ? i->second.id_type : i->second.type;
+ }
+
+ return string ();
+}
+
+static string
+public_name_impl (semantics::data_member& m)
+{
+ string s (m.name ());
+ size_t n (s.size ());
+
+ // Do basic processing: remove trailing and leading underscores
+ // as well as the 'm_' prefix.
+ //
+ // @@ What if the resulting names conflict?
+ //
+ size_t b (0), e (n - 1);
+
+ if (n > 2 && s[0] == 'm' && s[1] == '_')
+ b += 2;
+
+ for (; b <= e && s[b] == '_'; b++) ;
+ for (; e >= b && s[e] == '_'; e--) ;
+
+ return b > e ? s : string (s, b, e - b + 1);
+}
+
+string context::
+public_name_db (semantics::data_member& m) const
+{
+ return public_name_impl (m);
+}
+
+string context::
+compose_name (string const& prefix, string const& name)
+{
+ string r (prefix);
+ size_t n (r.size ());
+
+ // Add an underscore unless one is already in the prefix or
+ // the name is empty. Similarly, remove it if it is there but
+ // the name is empty.
+ //
+ if (n != 0)
+ {
+ if (r[n - 1] != '_')
+ {
+ if (!name.empty ())
+ r += '_';
+ }
+ else
+ {
+ if (name.empty ())
+ r.resize (n - 1);
+ }
+ }
+
+ r += name;
+ return r;
+}
+
+string context::
+transform_name (string const& name, sql_name_type type) const
+{
+ string r;
+
+ if (!data_->sql_name_regex_[type].empty () ||
+ !data_->sql_name_regex_[sql_name_all].empty ())
+ {
+ bool t (options.sql_name_regex_trace ());
+
+ if (t)
+ cerr << "name: '" << name << "'" << endl;
+
+ bool found (false);
+
+ // First try the type-specific transformations, if that didn't work,
+ // try common transformations.
+ //
+ for (unsigned short j (0); !found && j < 2; ++j)
+ {
+ regex_mapping const& rm = data_->sql_name_regex_[
+ j == 0 ? type : sql_name_all];
+
+ for (regex_mapping::const_iterator i (rm.begin ()); i != rm.end (); ++i)
+ {
+ if (t)
+ cerr << "try: '" << i->regex () << "' : ";
+
+ if (i->match (name))
+ {
+ r = i->replace (name);
+ found = true;
+
+ if (t)
+ cerr << "'" << r << "' : ";
+ }
+
+ if (t)
+ cerr << (found ? '+' : '-') << endl;
+
+ if (found)
+ break;
+ }
+ }
+
+ if (!found)
+ r = name;
+ }
+ else
+ r = name;
+
+ if (options.sql_name_case ().count (db) != 0)
+ {
+ switch (options.sql_name_case ()[db])
+ {
+ case name_case::upper:
+ {
+ r = data_->sql_name_upper_.replace (r);
+ break;
+ }
+ case name_case::lower:
+ {
+ r = data_->sql_name_lower_.replace (r);
+ break;
+ }
+ }
+ }
+
+ return r;
+}
+
+string context::
+public_name (semantics::data_member& m, bool e) const
+{
+ return e ? escape (public_name_impl (m)) : public_name_impl (m);
+}
+
+string context::
+flat_name (string const& fq)
+{
+ string r;
+ r.reserve (fq.size ());
+
+ for (string::size_type i (0), n (fq.size ()); i < n; ++i)
+ {
+ char c (fq[i]);
+
+ if (c == ':')
+ {
+ if (!r.empty ())
+ r += '_';
+ ++i; // Skip the second ':'.
+ }
+ else
+ r += c;
+ }
+
+ return r;
+}
+
+string context::
+escape (string const& name) const
+{
+ typedef string::size_type size;
+
+ string r;
+ size n (name.size ());
+
+ // In most common cases we will have that many characters.
+ //
+ r.reserve (n);
+
+ for (size i (0); i < n; ++i)
+ {
+ char c (name[i]);
+
+ if (i == 0)
+ {
+ if (!((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ c == '_'))
+ r = (c >= '0' && c <= '9') ? "cxx_" : "cxx";
+ }
+
+ if (!((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') ||
+ c == '_'))
+ r += '_';
+ else
+ r += c;
+ }
+
+ if (r.empty ())
+ r = "cxx";
+
+ // Custom reserved words.
+ //
+ /*
+ reserved_name_map_type::const_iterator i (reserved_name_map.find (r));
+
+ if (i != reserved_name_map.end ())
+ {
+ if (!i->second.empty ())
+ return i->second;
+ else
+ r += L'_';
+ }
+ */
+
+ // Keywords
+ //
+ if (keyword_set.find (r) != keyword_set.end ())
+ {
+ r += '_';
+
+ // Re-run custom words.
+ //
+ /*
+ i = reserved_name_map.find (r);
+
+ if (i != reserved_name_map.end ())
+ {
+ if (!i->second.empty ())
+ return i->second;
+ else
+ r += L'_';
+ }
+ */
+ }
+
+ return r;
+}
+
+string context::
+make_guard (string const& s) const
+{
+ // Split words, e.g., "FooBar" to "Foo_Bar" and convert everything
+ // to upper case.
+ //
+ string r;
+ for (string::size_type i (0), n (s.size ()); i < n - 1; ++i)
+ {
+ char c1 (s[i]);
+ char c2 (s[i + 1]);
+
+ r += toupper (c1);
+
+ if (isalpha (c1) && isalpha (c2) && islower (c1) && isupper (c2))
+ r += "_";
+ }
+ r += toupper (s[s.size () - 1]);
+
+ return escape (r);
+}
+
+static string
+charlit (unsigned int u)
+{
+ string r ("\\x");
+ bool lead (true);
+
+ for (short i (7); i >= 0; --i)
+ {
+ unsigned int x ((u >> (i * 4)) & 0x0F);
+
+ if (lead)
+ {
+ if (x == 0)
+ continue;
+
+ lead = false;
+ }
+
+ r += static_cast<char> (x < 10 ? ('0' + x) : ('A' + x - 10));
+ }
+
+ return r;
+}
+
+static string
+strlit_ascii (string const& str)
+{
+ string r;
+ string::size_type n (str.size ());
+
+ // In most common cases we will have that many chars.
+ //
+ r.reserve (n + 2);
+
+ r += '"';
+
+ bool escape (false);
+
+ for (string::size_type i (0); i < n; ++i)
+ {
+ unsigned int u (static_cast<unsigned int> (str[i]));
+
+ // [128 - ] - unrepresentable
+ // 127 - \x7F
+ // [32 - 126] - as is
+ // [0 - 31] - \X or \xXX
+ //
+
+ if (u < 32 || u == 127)
+ {
+ switch (u)
+ {
+ case '\n':
+ {
+ r += "\\n";
+ break;
+ }
+ case '\t':
+ {
+ r += "\\t";
+ break;
+ }
+ case '\v':
+ {
+ r += "\\v";
+ break;
+ }
+ case '\b':
+ {
+ r += "\\b";
+ break;
+ }
+ case '\r':
+ {
+ r += "\\r";
+ break;
+ }
+ case '\f':
+ {
+ r += "\\f";
+ break;
+ }
+ case '\a':
+ {
+ r += "\\a";
+ break;
+ }
+ default:
+ {
+ r += charlit (u);
+ escape = true;
+ break;
+ }
+ }
+ }
+ else if (u < 127)
+ {
+ if (escape)
+ {
+ // Close and open the string so there are no clashes.
+ //
+ r += '"';
+ r += '"';
+
+ escape = false;
+ }
+
+ switch (u)
+ {
+ case '"':
+ {
+ r += "\\\"";
+ break;
+ }
+ case '\\':
+ {
+ r += "\\\\";
+ break;
+ }
+ default:
+ {
+ r += static_cast<char> (u);
+ break;
+ }
+ }
+ }
+ else
+ {
+ // @@ Unrepresentable character.
+ //
+ r += '?';
+ }
+ }
+
+ r += '"';
+
+ return r;
+}
+
+string context::
+strlit (string const& str)
+{
+ return strlit_ascii (str);
+}
+
+void context::
+inst_header (bool decl, bool omit_exp)
+{
+ if (decl)
+ {
+ if (!ext.empty ())
+ os << ext << " ";
+ else
+ os << "extern ";
+ }
+
+ os << "template struct";
+
+ if (!omit_exp && !exp.empty ())
+ {
+ // If we are generating an explicit instantiation directive rather
+ // than the extern template declaration, then omit the export symbol
+ // if we already have it in the header (i.e., extern symbol specified
+ // and defined). If we don't do that, then we get GCC warnings saying
+ // that the second set of visibility attributes is ignored.
+ //
+ if (!decl && !ext.empty ())
+ os << endl
+ << "#ifndef " << ext << endl
+ << options.export_symbol ()[db] << endl
+ << "#endif" << endl;
+ else
+ os << " " << exp;
+ }
+ else
+ os << " ";
+}
+
+namespace
+{
+ struct column_count_impl: object_members_base
+ {
+ column_count_impl (object_section* section = 0)
+ : object_members_base (false, section)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Object pointers in views require special treatment.
+ //
+ if (view_member (m))
+ {
+ using semantics::class_;
+
+ column_count_type cc;
+
+ if (class_* root = polymorphic (c))
+ {
+ // For a polymorphic class we are going to load all the members
+ // from all the bases (i.e., equivalent to the first statement
+ // in the list of SELECT statements generated for the object).
+ // So our count should be the same as the first value in the
+ // generated column_counts array.
+ //
+ for (class_* b (&c);; b = &polymorphic_base (*b))
+ {
+ column_count_type const& ccb (column_count (*b, section_));
+
+ cc.total += ccb.total - (b != root ? ccb.id : 0);
+ cc.separate_load += ccb.separate_load;
+ cc.soft += ccb.soft;
+
+ if (b == root)
+ break;
+ }
+ }
+ else
+ cc = column_count (c, section_);
+
+ c_.total += cc.total - cc.separate_load;
+
+ if (added (member_path_) != 0 || deleted (member_path_) != 0)
+ c_.soft += cc.total;
+ else
+ c_.soft += cc.soft;
+ }
+ else
+ {
+ size_t t (c_.total);
+
+ object_members_base::traverse_pointer (m, c);
+
+ if (context::inverse (m))
+ {
+ size_t n (c_.total - t);
+
+ c_.inverse += n;
+
+ if (separate_update (member_path_))
+ c_.separate_update -= n;
+ }
+ }
+ }
+
+ virtual void
+ traverse_simple (semantics::data_member& m)
+ {
+ c_.total++;
+
+ bool ro (context::readonly (member_path_, member_scope_));
+
+ if (id ())
+ c_.id++;
+ else if (ro)
+ c_.readonly++;
+ else if (context::version (m))
+ c_.optimistic_managed++;
+
+ // For now discriminator can only be a simple value.
+ //
+ if (discriminator (m))
+ c_.discriminator++;
+
+ {
+ unsigned long long av (added (member_path_));
+ unsigned long long dv (deleted (member_path_));
+
+ // If the addition/deletion version is the same as the section's,
+ // then don't count.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0)
+ c_.added++;
+
+ if (dv != 0)
+ c_.deleted++;
+
+ if (av != 0 || dv != 0)
+ c_.soft++;
+ }
+
+ if (separate_load (member_path_))
+ c_.separate_load++;
+
+ if (separate_update (member_path_) && !ro)
+ c_.separate_update++;
+ }
+
+ context::column_count_type c_;
+ };
+}
+
+context::column_count_type context::
+column_count (semantics::class_& c, object_section* s)
+{
+ if (s == 0)
+ {
+ // Whole class.
+ //
+ if (!c.count ("column-count"))
+ {
+ column_count_impl t;
+ t.traverse (c);
+ c.set ("column-count", t.c_);
+ }
+
+ return c.get<column_count_type> ("column-count");
+ }
+ else
+ {
+ column_count_impl t (s);
+ t.traverse (c);
+ return t.c_;
+ }
+}
+
+namespace
+{
+ struct has_a_impl: object_members_base
+ {
+ has_a_impl (unsigned short flags, object_section* s)
+ : object_members_base ((flags & context::include_base) != 0, s),
+ r_ (0),
+ flags_ (flags)
+ {
+ }
+
+ size_t
+ result () const
+ {
+ return r_;
+ }
+
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section if requested.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ ((flags_ & include_eager_load) != 0 &&
+ *section_ == main_section &&
+ !s.separate_load ());
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_&)
+ {
+ // Ignore polymorphic id references; they are represented as
+ // pointers but are normally handled in a special way.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ // Ignore added/deleted members if so requested.
+ //
+ if (check_soft ())
+ return;
+
+ if (context::is_a (member_path_, member_scope_, flags_))
+ r_++;
+
+ // No need to go inside.
+ }
+
+ virtual void
+ traverse_simple (semantics::data_member&)
+ {
+ // Ignore added/deleted members if so requested.
+ //
+ if (check_soft ())
+ return;
+
+ if (context::is_a (member_path_, member_scope_, flags_))
+ r_++;
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ // Ignore added/deleted members if so requested.
+ //
+ if (check_soft ())
+ return;
+
+ // Ignore versioned containers if so requested.
+ //
+ if ((flags_ & exclude_versioned) != 0 && versioned (m))
+ return;
+
+ // We don't cross the container boundaries (separate table).
+ //
+ unsigned short f (flags_ & (context::test_container |
+ context::test_straight_container |
+ context::test_inverse_container |
+ context::test_readonly_container |
+ context::test_readwrite_container |
+ context::test_smart_container));
+
+ if (context::is_a (member_path_,
+ member_scope_,
+ f,
+ context::container_vt (m),
+ "value"))
+ r_++;
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ if ((flags_ & context::exclude_base) == 0)
+ inherits (c);
+
+ names (c);
+ }
+
+ private:
+ bool
+ check_soft ()
+ {
+ if ((flags_ & exclude_added) != 0 || (flags_ & exclude_deleted) != 0)
+ {
+ unsigned long long av (added (member_path_));
+ unsigned long long dv (deleted (member_path_));
+
+ // If the addition/deletion version is the same as the section's,
+ // then don't exclude.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if ((av != 0 && (flags_ & exclude_added) != 0) ||
+ (dv != 0 && (flags_ & exclude_deleted) != 0))
+ return true;
+ }
+
+ return false;
+ }
+
+ private:
+ size_t r_;
+ unsigned short flags_;
+ };
+}
+
+bool context::
+is_a (data_member_path const& mp,
+ data_member_scope const& ms,
+ unsigned short f,
+ semantics::type& t,
+ string const& kp)
+{
+ bool r (false);
+
+ semantics::data_member& m (*mp.back ());
+
+ if (f & test_pointer)
+ r = r || object_pointer (t);
+
+ if (f & test_eager_pointer)
+ r = r || (object_pointer (t) && !lazy_pointer (t));
+
+ if (f & test_lazy_pointer)
+ r = r || (object_pointer (t) && lazy_pointer (t));
+
+ semantics::type* c;
+ if ((f & (test_container |
+ test_straight_container |
+ test_inverse_container |
+ test_readonly_container |
+ test_readwrite_container |
+ test_smart_container)) != 0 &&
+ (c = container (m)) != 0)
+ {
+ if (f & test_container)
+ r = r || true;
+
+ if (f & test_straight_container)
+ r = r || !inverse (m, kp);
+
+ if (f & test_inverse_container)
+ r = r || inverse (m, kp);
+
+ if (f & test_readonly_container)
+ r = r || readonly (mp, ms);
+
+ if (f & test_readwrite_container)
+ r = r || (!inverse (m, kp) && !readonly (mp, ms));
+
+ if (f & test_smart_container)
+ r = r || (!inverse (m, kp) && !unordered (m) && container_smart (*c));
+ }
+
+ return r;
+}
+
+size_t context::
+has_a (semantics::class_& c, unsigned short flags, object_section* s)
+{
+ has_a_impl impl (flags, s);
+ impl.dispatch (c);
+ return impl.result ();
+}
+
+string context::
+process_include_path (string const& ip, bool prefix, char open)
+{
+ bool t (options.include_regex_trace ());
+ string p (prefix ? options.include_prefix () : string ());
+
+ if (!p.empty () && p[p.size () - 1] != '/')
+ p.append ("/");
+
+ string path (p + ip), r;
+
+ if (t)
+ cerr << "include: '" << path << "'" << endl;
+
+ bool found (false);
+
+ for (regex_mapping::const_iterator i (include_regex.begin ());
+ i != include_regex.end (); ++i)
+ {
+ if (t)
+ cerr << "try: '" << i->regex () << "' : ";
+
+ if (i->match (path))
+ {
+ r = i->replace (path);
+ found = true;
+
+ if (t)
+ cerr << "'" << r << "' : ";
+ }
+
+ if (t)
+ cerr << (found ? '+' : '-') << endl;
+
+ if (found)
+ break;
+ }
+
+ if (!found)
+ r = path;
+
+ // Add brackets or quotes unless the path already has them.
+ //
+ if (!r.empty () && r[0] != '"' && r[0] != '<')
+ {
+ bool b (open == '<' || (open == '\0' && options.include_with_brackets ()));
+ char op (b ? '<' : '"'), cl (b ? '>' : '"');
+ r = op + r + cl;
+ }
+
+ return r;
+}
diff --git a/odb/odb/context.hxx b/odb/odb/context.hxx
new file mode 100644
index 0000000..ec4505b
--- /dev/null
+++ b/odb/odb/context.hxx
@@ -0,0 +1,1941 @@
+// file : odb/context.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_CONTEXT_HXX
+#define ODB_CONTEXT_HXX
+
+#include <odb/gcc-fwd.hxx>
+
+#include <map>
+#include <set>
+#include <list>
+#include <stack>
+#include <vector>
+#include <string>
+#include <memory> // std::unique_ptr
+#include <ostream>
+#include <cstddef> // std::size_t
+#include <iostream>
+
+#include <libcutl/re.hxx>
+#include <libcutl/shared-ptr.hxx>
+
+#include <odb/options.hxx>
+#include <odb/features.hxx>
+#include <odb/location.hxx>
+#include <odb/cxx-token.hxx>
+#include <odb/semantics.hxx>
+#include <odb/semantics/relational/name.hxx>
+#include <odb/semantics/relational/model.hxx>
+#include <odb/traversal.hxx>
+
+using std::endl;
+using std::cerr;
+
+// Regex.
+//
+using cutl::re::regex;
+using cutl::re::regexsub;
+typedef cutl::re::format regex_format;
+
+typedef std::vector<regexsub> regex_mapping;
+
+// Forward-declarations.
+//
+class cxx_string_lexer;
+
+// Generic exception thrown to indicate a failure when diagnostics
+// has already been issued (to stderr).
+//
+class operation_failed {};
+
+// Keep this enum synchronized with the one in libodb/odb/pointer-traits.hxx.
+//
+enum pointer_kind
+{
+ pk_raw,
+ pk_unique,
+ pk_shared,
+ pk_weak
+};
+
+// Keep this enum synchronized with the one in libodb/odb/container-traits.hxx.
+//
+enum container_kind
+{
+ ck_ordered,
+ ck_set,
+ ck_multiset,
+ ck_map,
+ ck_multimap
+};
+
+// The same as class_kind in libodb/odb/traits.hxx.
+//
+enum class_kind
+{
+ class_object,
+ class_view,
+ class_composite,
+ class_other
+};
+
+// Data member path.
+//
+// If it is a direct member of an object, then we will have just
+// one member. However, if this is a member inside a composite
+// value, then we will have a "path" constructed out of members
+// that lead all the way from the object member to the innermost
+// composite value member.
+//
+struct data_member_path: std::vector<semantics::data_member*>
+{
+ data_member_path () {}
+
+ explicit
+ data_member_path (semantics::data_member& m) {push_back (&m);}
+
+ // Return true if this is a sub-path of (or equal to) the
+ // specified path.
+ //
+ bool
+ sub (const data_member_path& p) const
+ {
+ size_t n (p.size ());
+
+ if (n > size ())
+ return false;
+
+ for (size_t i (0); i != n; ++i)
+ if ((*this)[i] != p[i])
+ return false;
+
+ return true;
+ }
+};
+
+// Class inheritance chain, from the most derived to base.
+//
+typedef std::vector<semantics::class_*> class_inheritance_chain;
+
+// A list of inheritance chains for a data member in an object.
+// The first entry in this list would correspond to the object.
+// All subsequent entries, if any, correspond to composite
+// values.
+//
+typedef std::vector<class_inheritance_chain> data_member_scope;
+
+//
+// Semantic graph context types.
+//
+
+// Custom C++ type mapping.
+//
+struct custom_cxx_type
+{
+ custom_cxx_type (): type_node (0), as_node (0) {}
+
+ std::string
+ translate_to (std::string const& v) const {return translate (v, to);}
+
+ std::string
+ translate_from (std::string const& v) const {return translate (v, from);}
+
+ tree type_node;
+ std::string type_name;
+ semantics::type* type;
+ semantics::names* type_hint;
+
+ tree as_node;
+ std::string as_name;
+ semantics::type* as;
+ semantics::names* as_hint;
+
+ cxx_tokens to;
+ bool to_move; // Single (?), so can move.
+
+ cxx_tokens from;
+ bool from_move; // Single (?), so can move.
+
+ location_t loc;
+ tree scope; // Scope for which this mapping is defined.
+
+private:
+ static std::string
+ translate (std::string const&, const cxx_tokens&);
+};
+
+typedef std::vector<custom_cxx_type> custom_cxx_types;
+typedef std::map<semantics::type*, custom_cxx_type*> custom_cxx_type_map;
+
+
+// Object or view pointer.
+//
+struct class_pointer
+{
+ std::string name;
+ tree scope;
+ location_t loc;
+};
+
+//
+//
+struct default_value
+{
+ enum kind_type
+ {
+ reset, // Default value reset.
+ null,
+ boolean, // Literal contains value (true or false).
+ integer, // Integer number. Literal contains sign.
+ floating, // Floating-point number.
+ string, // Literal contains value.
+ enumerator // Literal is the name, enum_value is the tree node.
+ };
+
+ kind_type kind;
+ std::string literal;
+
+ union
+ {
+ tree enum_value;
+ unsigned long long int_value;
+ double float_value;
+ };
+};
+
+// Database potentially-qualified and unqualifed names.
+//
+using semantics::relational::qname;
+using semantics::relational::uname;
+
+// Object or table associated with the view.
+//
+struct view_object
+{
+ // Return a diagnostic name for this association. It is either the
+ // alias, unqualified object name, or string representation of the
+ // table name.
+ //
+ std::string
+ name () const;
+
+ enum kind_type { object, table };
+ enum join_type { left, right, full, inner, cross };
+
+ kind_type kind;
+ join_type join;
+ tree obj_node; // Tree node if kind is object.
+ std::string obj_name; // Name as specified in the pragma if kind is object.
+ qname tbl_name; // Table name if kind is table.
+ std::string alias;
+ tree scope;
+ location_t loc;
+ semantics::class_* obj;
+ semantics::data_member* ptr; // Corresponding object pointer, if any.
+
+ cxx_tokens cond; // Join condition tokens.
+};
+
+typedef std::vector<view_object> view_objects;
+
+// The view_alias_map does not contain entries for tables.
+//
+typedef std::map<std::string, view_object*> view_alias_map;
+typedef std::map<semantics::class_*, view_object*> view_object_map;
+
+// Collection of relationships via which the objects are joined.
+// We need this information to figure out which alias/table
+// names to use for columns corresponding to inverse object
+// pointers inside objects that this view may be loading.
+//
+// The first object is the pointer (i.e., the one containing
+// this data member) while the other is the pointee. In other
+// words, first -> second. We always store the direct (i.e.,
+// non-inverse) side of the relationship. Note also that there
+// could be multiple objects joined using the same relationship.
+//
+typedef
+std::multimap<data_member_path, std::pair<view_object*, view_object*> >
+view_relationship_map;
+
+//
+//
+struct view_query
+{
+ view_query (): distinct (false), for_update (false) {}
+
+ enum kind_type
+ {
+ runtime,
+ complete_select, // SELECT query.
+ complete_execute, // Stored procedure call.
+ condition
+ };
+
+ kind_type kind;
+ std::string literal;
+ cxx_tokens expr;
+ tree scope;
+ location_t loc;
+
+ // Result modifiers (only for condition).
+ //
+ bool distinct; // SELECT DISTINCT
+ bool for_update; // SELECT FOR UPDATE
+};
+
+//
+//
+struct table_column
+{
+ qname table;
+ std::string column;
+ bool expr; // True if column is an expression, and therefore should not
+ // be quoted.
+
+ table_column () {}
+
+ explicit
+ table_column (const std::string& c): column (c), expr (false) {}
+};
+
+//
+//
+struct column_expr_part
+{
+ enum kind_type
+ {
+ literal,
+ reference
+ };
+
+ kind_type kind;
+ std::string value;
+ qname table; // Table name/alias for references.
+ data_member_path member_path; // Path to member for references.
+
+ // Scope and location of this pragma. Used to resolve the member name.
+ //
+ tree scope;
+ location_t loc;
+};
+
+struct column_expr: std::vector<column_expr_part>
+{
+ location_t loc;
+};
+
+//
+//
+struct member_access
+{
+ member_access (const location& l, const char* k, bool s)
+ : loc (l), kind (k), synthesized (s), by_value (false) {}
+
+ // Return true of we have the (?) placeholder.
+ //
+ bool
+ placeholder () const;
+
+ // Return true if this is a synthesized expression that goes
+ // directly for the member.
+ //
+ bool
+ direct () const
+ {
+ return synthesized && expr.size () == 3; // this.member
+ }
+
+ bool
+ empty () const
+ {
+ return expr.empty ();
+ }
+
+ // Issues diagnostics and throws operation_failed if expression is
+ // empty.
+ //
+ std::string
+ translate (std::string const& obj,
+ std::string const& val = std::string (),
+ std::string const& db = std::string ()) const;
+
+ location loc;
+ const char* kind; // accessor/modifier; used for diagnostics.
+ bool synthesized; // If true, then this is a synthesized expression.
+ cxx_tokens expr;
+ bool by_value; // True if accessor returns by value. False doesn't
+ // necessarily mean that it is by reference.
+};
+
+//
+//
+struct model_version
+{
+ unsigned long long base;
+ unsigned long long current;
+ bool open;
+};
+
+// Sections.
+//
+struct object_section
+{
+ virtual bool
+ compare (object_section const&) const = 0;
+
+ virtual bool
+ separate_load () const = 0;
+
+ virtual bool
+ separate_update () const = 0;
+};
+
+inline bool
+operator== (object_section const& x, object_section const& y)
+{
+ return x.compare (y);
+}
+
+inline bool
+operator!= (object_section const& x, object_section const& y)
+{
+ return !x.compare (y);
+}
+
+// Main section.
+//
+struct main_section_type: object_section
+{
+ virtual bool
+ compare (object_section const& s) const;
+
+ virtual bool
+ separate_load () const {return false;}
+
+ virtual bool
+ separate_update () const {return false;}
+};
+
+inline bool
+operator== (main_section_type const&, main_section_type const&)
+{
+ return true; // There is only one main section.
+}
+
+extern main_section_type main_section;
+
+// User-defined section.
+//
+struct user_section: object_section
+{
+ enum load_type
+ {
+ load_eager,
+ load_lazy
+ };
+
+ enum update_type
+ {
+ update_always,
+ update_change,
+ update_manual
+ };
+
+ enum special_type
+ {
+ special_ordinary,
+ special_version // Fake section for optimistic version update.
+ };
+
+ user_section (semantics::data_member& m,
+ semantics::class_& o,
+ std::size_t i,
+ load_type l,
+ update_type u,
+ special_type s = special_ordinary)
+ : member (&m), object (&o), base (0), index (i),
+ load (l), update (u), special (s),
+ total (0), inverse (0), readonly (0), versioned (false),
+ containers (false), readwrite_containers (false),
+ versioned_containers (false), readwrite_versioned_containers (false) {}
+
+ virtual bool
+ compare (object_section const& s) const;
+
+ virtual bool
+ separate_load () const {return load != load_eager;}
+
+ virtual bool
+ separate_update () const
+ {
+ // A separately-loaded section is always separately-updated since
+ // it might not be loaded when update is requested.
+ //
+ return separate_load () || update != update_always;
+ }
+
+ bool
+ load_empty () const;
+
+ bool
+ update_empty () const;
+
+ bool
+ empty () const
+ {
+ return load_empty () && update_empty ();
+ }
+
+ // A section is optimistic if the object that contains it is optimistic.
+ // For polymorphic hierarchies, only sections contained in the root are
+ // considered optimistic.
+ //
+ bool
+ optimistic () const;
+
+ semantics::data_member* member; // Data member of this section.
+ semantics::class_* object; // Object containing this section.
+ user_section* base; // Base of this section.
+ std::size_t index; // Index of this sections.
+
+ load_type load;
+ update_type update;
+ special_type special;
+
+ // Column counts.
+ //
+ std::size_t total;
+ std::size_t inverse;
+ std::size_t readonly;
+
+ bool versioned;
+
+ bool containers;
+ bool readwrite_containers;
+
+ bool versioned_containers;
+ bool readwrite_versioned_containers;
+
+ // Total counts across overrides.
+ //
+ std::size_t
+ total_total () const
+ {
+ user_section* b (total_base ());
+ return total + (b == 0 ? 0 : b->total_total ());
+ }
+
+ std::size_t
+ total_inverse () const
+ {
+ user_section* b (total_base ());
+ return inverse + (b == 0 ? 0 : b->total_inverse ());
+ }
+
+ std::size_t
+ total_readonly () const
+ {
+ user_section* b (total_base ());
+ return readonly + (b == 0 ? 0 : b->total_readonly ());
+ }
+
+ bool
+ total_containers ()
+ {
+ user_section* b (total_base ());
+ return containers || (b != 0 && b->total_containers ());
+ }
+
+ bool
+ total_readwrite_containers ()
+ {
+ user_section* b (total_base ());
+ return readwrite_containers ||
+ (b != 0 && b->total_readwrite_containers ());
+ }
+
+private:
+ user_section*
+ total_base () const;
+};
+
+inline bool
+operator== (user_section const& x, user_section const& y)
+{
+ return x.member == y.member;
+}
+
+// Using list for pointer for element stability (see user_section::base).
+//
+struct user_sections: std::list<user_section>
+{
+ // Count sections that have something to load.
+ //
+ static unsigned short const count_load = 0x01;
+
+ // Count sections that are non-eager but have nothing to load.
+ //
+ static unsigned short const count_load_empty = 0x02;
+
+ // Count sections that have something to update.
+ //
+ static unsigned short const count_update = 0x04;
+
+ // Count sections that have nothing to update.
+ //
+ static unsigned short const count_update_empty = 0x08;
+
+ // Count sections that are optimistic.
+ //
+ static unsigned short const count_optimistic = 0x10;
+
+ // Modifiers:
+ //
+
+ // Don't exclude fake optimistic version update section from the count.
+ //
+ static unsigned short const count_special_version = 0x20;
+
+ // Only count versioned sections.
+ //
+ static unsigned short const count_versioned_only = 0x40;
+
+
+ // Count all sections, but excluding special.
+ //
+ static unsigned short const count_all = count_update |
+ count_update_empty;
+
+ static unsigned short const count_new = 0x1000;
+ static unsigned short const count_override = 0x2000;
+ static unsigned short const count_total = 0x4000;
+
+ std::size_t
+ count (unsigned short flags) const;
+
+ user_sections (semantics::class_& o): object (&o) {};
+ semantics::class_* object;
+};
+
+// Context.
+//
+class context
+{
+public:
+ typedef std::size_t size_t;
+ typedef std::string string;
+ typedef std::vector<string> strings;
+ typedef std::ostream ostream;
+
+ typedef ::options options_type;
+
+ static string
+ upcase (string const&);
+
+public:
+ // Return cvr-unqualified base of the type, or type itself, if it is
+ // not qualified.
+ //
+ static semantics::type&
+ utype (semantics::type&);
+
+ // The same as above, but also returns the name hint for the unqualified
+ // type. If the original type is already unqualified, then the hint
+ // argument is not modified.
+ //
+ static semantics::type&
+ utype (semantics::type&, semantics::names*& hint);
+
+ // The same for a member's type but also do custom C++ type translation.
+ //
+ static semantics::type&
+ utype (semantics::data_member& m, const custom_cxx_type** translation = 0)
+ {
+ semantics::names* hint (0);
+ return utype (m, hint, string (), translation);
+ }
+
+ static semantics::type&
+ utype (semantics::data_member& m,
+ string const& key_prefix,
+ const custom_cxx_type** translation = 0)
+ {
+ semantics::names* hint (0);
+ return utype (m, hint, key_prefix, translation);
+ }
+
+ // In addition to the unqualified type, this version also returns the
+ // name hint for this type. If the member type is already unqualified,
+ // then the hint is from the belongs edge. Otherwise, it is from the
+ // qualifies edge.
+ //
+ static semantics::type&
+ utype (semantics::data_member&,
+ semantics::names*& hint,
+ string const& key_prefix = string (),
+ const custom_cxx_type** translation = 0);
+
+ static semantics::type&
+ utype (const data_member_path& mp, const custom_cxx_type** translation = 0)
+ {
+ return utype (*mp.back (), translation);
+ }
+
+ static semantics::type&
+ utype (const data_member_path& mp,
+ string const& key_prefix,
+ const custom_cxx_type** translation = 0)
+ {
+ return utype (*mp.back (), key_prefix, translation);
+ }
+
+ static semantics::type&
+ utype (const data_member_path& mp,
+ semantics::names*& hint,
+ string const& key_prefix = string (),
+ const custom_cxx_type** translation = 0)
+ {
+ return utype (*mp.back (), hint, key_prefix, translation);
+ }
+
+ // For arrays this function returns true if the (innermost) element
+ // type is const.
+ //
+ static bool
+ const_type (semantics::type&);
+
+ static bool
+ const_member (semantics::data_member& m) {return const_type (m.type ());}
+
+ // Form a reference type for a not mapped, actual member type. If
+ // make_const is true, then add top-level const qualifier, unless
+ // it is already there. If it is false, then strip it if it is
+ // already there. If var is not empty, then embed the variable
+ // name into the type (e.g., char (*v)[3]). If decay_array is
+ // false then don't decay the (top-level) array to a pointer.
+ //
+ static string
+ member_ref_type (semantics::data_member& m,
+ bool make_const,
+ string const& var = "",
+ bool decay_array = true)
+ {
+ return type_ref_type (
+ m.type (), m.belongs ().hint (), make_const, var, decay_array);
+ }
+
+ static string
+ type_ref_type (semantics::type&,
+ semantics::names* hint,
+ bool make_const,
+ string const& var = "",
+ bool decay_array = true);
+
+ // Form a value type for a not mapped, actual member type. If make_const
+ // is true, then add top-level const qualifier, unless it is already
+ // there. If it is false, then strip it if it is already there. If var is
+ // not empty, then embed the variable name into the type (e.g., char v[3]).
+ //
+ static string
+ member_val_type (semantics::data_member& m,
+ bool make_const,
+ string const& var = "")
+ {
+ return type_val_type (m.type (), m.belongs ().hint (), make_const, var);
+ }
+
+ static string
+ type_val_type (semantics::type&,
+ semantics::names* hint,
+ bool make_const,
+ string const& var = "");
+
+ // Member access helpers. Database can be empty. If type is not empty,
+ // then it should be the non-cvr type of the member (e.g., id_type).
+ //
+ void
+ set_member (semantics::data_member& m,
+ const std::string& obj,
+ const std::string& val,
+ const std::string& db,
+ const std::string& type = "");
+
+ void
+ inc_member (semantics::data_member& m,
+ const std::string& sobj, // Set expression object.
+ const std::string& gobj, // Get expression object.
+ const std::string& type = "");
+
+public:
+ // Resolve data member name in the form "a.b.c" to the data member path,
+ // issuing diagnostics and throwing operation_filed in case of an error.
+ // This function stops if it encounters a container leaving lex usable
+ // to continue parsing.
+ //
+ void
+ resolve_data_members (data_member_path& append,
+ semantics::class_& scope,
+ const std::string& name,
+ const location&,
+ cxx_string_lexer&);
+
+ data_member_path
+ resolve_data_members (semantics::class_& scope,
+ const std::string& name,
+ const location& l,
+ cxx_string_lexer& lex)
+ {
+ data_member_path r;
+ resolve_data_members (r, scope, name, l, lex);
+ return r;
+ }
+
+ // Predicates.
+ //
+public:
+ static bool
+ object (semantics::type& t)
+ {
+ return t.count ("object");
+ }
+
+ static bool
+ view (semantics::type& t)
+ {
+ return t.count ("view");
+ }
+
+ // Direct member of a view.
+ //
+ static bool
+ view_member (semantics::data_member& m)
+ {
+ return view (dynamic_cast<semantics::class_&> (m.scope ()));
+ }
+
+ // Check whether the type is a wrapper. Return the wrapped type if
+ // it is a wrapper and NULL otherwise. Note that the returned type
+ // may be cvr-qualified.
+ //
+ static semantics::type*
+ wrapper (semantics::type& t)
+ {
+ return t.count ("wrapper") && t.get<bool> ("wrapper")
+ ? t.get<semantics::type*> ("wrapper-type")
+ : 0;
+ }
+
+ static semantics::type*
+ wrapper (semantics::type& t, semantics::names*& hint)
+ {
+ if (t.count ("wrapper") && t.get<bool> ("wrapper"))
+ {
+ hint = t.get<semantics::names*> ("wrapper-hint");
+ return t.get<semantics::type*> ("wrapper-type");
+ }
+ else
+ return 0;
+ }
+
+ // Composite value type is a class type that was explicitly marked
+ // as value type and there was no database type mapping provided for
+ // it by the user (specifying the database type makes the value type
+ // simple).
+ //
+ static bool
+ composite (semantics::class_& c)
+ {
+ if (c.count ("composite-value"))
+ return c.get<bool> ("composite-value");
+ else
+ return composite_ (c);
+ }
+
+ // Return the class object if this type is a composite value type
+ // and NULL otherwise.
+ //
+ static semantics::class_*
+ composite (semantics::type& t)
+ {
+ semantics::class_* c (dynamic_cast<semantics::class_*> (&t));
+ return c != 0 && composite (*c) ? c : 0;
+ }
+
+ // As above but also "sees through" wrappers.
+ //
+ static semantics::class_*
+ composite_wrapper (semantics::type& t)
+ {
+ if (semantics::class_* c = composite (t))
+ return c;
+ else if (semantics::type* wt = wrapper (t))
+ return composite (utype (*wt));
+ else
+ return 0;
+ }
+
+ // Check if a data member is a container. "Sees through" wrappers and
+ // returns the actual container type or NULL if not a container.
+ //
+ // We require data member as input instead of the type because the
+ // same type (e.g., vector<char>) can be used for both container
+ // and simple value members.
+ //
+ static semantics::type*
+ container (semantics::data_member& m)
+ {
+ // The same type can be used as both a container and a simple value.
+ //
+ if (m.count ("simple"))
+ return 0;
+
+ semantics::type* t (&utype (m));
+
+ if (semantics::type* wt = wrapper (*t))
+ t = &utype (*wt);
+
+ return t->count ("container-kind") ? t : 0;
+ }
+
+ static semantics::class_*
+ object_pointer (semantics::type& t)
+ {
+ return t.get<semantics::class_*> ("element-type", 0);
+ }
+
+ // If this data member is or is part of an object pointer, then
+ // return the member that is the pointer. Otherwise, return 0.
+ //
+ static semantics::data_member*
+ object_pointer (data_member_path const&);
+
+ static semantics::class_*
+ points_to (semantics::data_member& m)
+ {
+ return m.get<semantics::class_*> ("points-to", 0);
+ }
+
+ static bool
+ abstract (semantics::class_& c)
+ {
+ // If a class is abstract in the C++ sense then it is also abstract in
+ // the database sense.
+ //
+ return c.abstract () || c.count ("abstract");
+ }
+
+ static bool
+ session (semantics::class_& c)
+ {
+ return c.get<bool> ("session");
+ }
+
+ static bool
+ transient (semantics::data_member& m)
+ {
+ return m.count ("transient");
+ }
+
+ // Return the deletion version or 0 if not soft-deleted.
+ //
+ static unsigned long long
+ deleted (semantics::class_& c, location_t* l = 0)
+ {
+ unsigned long long v (c.get<unsigned long long> ("deleted", 0));
+
+ if (v != 0 && l != 0)
+ *l = c.get<location_t> ("deleted-location");
+
+ return v;
+ }
+
+ static unsigned long long
+ deleted (semantics::data_member& m, location_t* l = 0)
+ {
+ unsigned long long v (m.get<unsigned long long> ("deleted", 0));
+
+ if (v != 0 && l != 0)
+ *l = m.get<location_t> ("deleted-location");
+
+ return v;
+ }
+
+ static unsigned long long
+ deleted (data_member_path const& mp, location_t* l = 0)
+ {
+ unsigned long long r (0);
+
+ // Find the earliest version since this member was deleted.
+ //
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ unsigned long long v ((*i)->get<unsigned long long> ("deleted", 0));
+ if (v != 0 && (r == 0 || v < r))
+ {
+ r = v;
+
+ if (l != 0)
+ *l = (*i)->get<location_t> ("deleted-location");
+ }
+ }
+
+ return r;
+ }
+
+ static semantics::data_member*
+ deleted_member (data_member_path const& mp)
+ {
+ semantics::data_member* m (0);
+
+ // Find the earliest version since this member was deleted.
+ //
+ unsigned long long r (0);
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ unsigned long long v ((*i)->get<unsigned long long> ("deleted", 0));
+ if (v != 0 && (r == 0 || v < r))
+ {
+ r = v;
+ m = *i;
+ }
+ }
+
+ return m;
+ }
+
+ // Return the addition version or 0 if not soft-added.
+ //
+ static unsigned long long
+ added (semantics::class_& c) // Used for composite only.
+ {
+ return c.get<unsigned long long> ("added", 0);
+ }
+
+ static unsigned long long
+ added (semantics::data_member& m)
+ {
+ return m.get<unsigned long long> ("added", 0);
+ }
+
+ static unsigned long long
+ added (data_member_path const& mp)
+ {
+ unsigned long long r (0);
+
+ // Find the latest version since this member was added.
+ //
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ unsigned long long v ((*i)->get<unsigned long long> ("added", 0));
+ if (v != 0 && v > r)
+ r = v;
+ }
+
+ return r;
+ }
+
+ static semantics::data_member*
+ added_member (data_member_path const& mp)
+ {
+ semantics::data_member* m (0);
+
+ // Find the latest version since this member was added.
+ //
+ unsigned long long r (0);
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ unsigned long long v ((*i)->get<unsigned long long> ("added", 0));
+ if (v != 0 && v > r)
+ {
+ r = v;
+ m = *i;
+ }
+ }
+
+ return m;
+ }
+
+ static bool
+ id (semantics::data_member& m)
+ {
+ return m.count ("id");
+ }
+
+ // If this data member is or is part of an id member, then return
+ // the member that is marked as the id. Otherwise, return 0.
+ //
+ static semantics::data_member*
+ id (data_member_path const&);
+
+ static bool
+ auto_ (semantics::data_member& m)
+ {
+ return id (m) && m.count ("auto");
+ }
+
+ // Must be a path returned by id(). In other words, it assumes
+ // the path is to the id member.
+ //
+ static bool
+ auto_ (data_member_path& mp)
+ {
+ return mp.front ()->count ("auto");
+ }
+
+ // The member scope is used to override readonly status when a readonly
+ // class (object or composite value) inherits from a readwrite base.
+ //
+ static bool
+ readonly (data_member_path const&, data_member_scope const&);
+
+ static bool
+ readonly (semantics::data_member&);
+
+ static bool
+ readonly (semantics::class_& c)
+ {
+ return c.count ("readonly");
+ }
+
+ // Null-able.
+ //
+ bool
+ null (data_member_path const&) const;
+
+ bool
+ null (semantics::data_member&) const;
+
+ bool
+ null (semantics::data_member&, string const& key_prefix) const;
+
+ // Optimistic concurrency.
+ //
+ static semantics::data_member*
+ optimistic (semantics::class_& c)
+ {
+ // Set by the validator.
+ //
+ return c.get<semantics::data_member*> ("optimistic-member", 0);
+ }
+
+ static bool
+ version (semantics::data_member& m)
+ {
+ return m.count ("version");
+ }
+
+ static bool
+ version (const data_member_path& mp)
+ {
+ return mp.size () == 1 && mp.back ()->count ("version");
+ }
+
+ // Polymorphic inheritance. Return root of the hierarchy or NULL if
+ // not polymorphic.
+ //
+ static semantics::class_*
+ polymorphic (semantics::class_& c)
+ {
+ // Set by the validator.
+ //
+ return c.get<semantics::class_*> ("polymorphic-root", 0);
+ }
+
+ static semantics::class_&
+ polymorphic_base (semantics::class_& c)
+ {
+ // Set by the validator.
+ //
+ return *c.get<semantics::class_*> ("polymorphic-base");
+ }
+
+ static size_t
+ polymorphic_depth (semantics::class_&);
+
+ static bool
+ discriminator (semantics::data_member& m)
+ {
+ return m.count ("discriminator");
+ }
+
+ static semantics::data_member*
+ discriminator (semantics::class_& c)
+ {
+ // Set by type processor.
+ //
+ return c.get<semantics::data_member*> ("discriminator", 0);
+ }
+
+ // Model version.
+ //
+ bool
+ versioned () const
+ {
+ return unit.count ("model-version") != 0;
+ }
+
+ model_version const&
+ version () const
+ {
+ return unit.get<model_version> ("model-version");
+ }
+
+ // Versioned object, view, or composite.
+ //
+ static bool
+ versioned (semantics::class_& c)
+ {
+ // Set by processor.
+ //
+ return c.count ("versioned") != 0;
+ }
+
+ // Versioned container.
+ //
+ static bool
+ versioned (semantics::data_member& m)
+ {
+ // Set by processor.
+ //
+ return container (m)->count ("versioned");
+ }
+
+ // Object sections.
+ //
+ static object_section&
+ section (semantics::data_member& m)
+ {
+ object_section* s (m.get<object_section*> ("section", 0));
+ return s == 0 ? main_section : *s;
+ }
+
+ static object_section&
+ section (data_member_path const& mp)
+ {
+ // The direct member of the object specifies the section. If the
+ // path is empty (which can happen, for example, for a container
+ // element), assume it is the main section.
+ //
+ //
+ return mp.empty () ? main_section : section (*mp.front ());
+ }
+
+ // Member belongs to a section that is loaded separately.
+ //
+ static bool
+ separate_load (semantics::data_member& m)
+ {
+ return section (m).separate_load ();
+ }
+
+ static bool
+ separate_load (data_member_path const& mp)
+ {
+ return section (mp).separate_load ();
+ }
+
+ // Member belongs to a section that is updated separately.
+ //
+ static bool
+ separate_update (semantics::data_member& m)
+ {
+ return section (m).separate_update ();
+ }
+
+ static bool
+ separate_update (data_member_path const& mp)
+ {
+ return section (mp).separate_update ();
+ }
+
+ //
+ //
+ typedef ::class_kind class_kind_type;
+
+ static class_kind_type
+ class_kind (semantics::class_&);
+
+ // Return class names. For ordinary classes, this will be the class
+ // name itself. For class template instantiations this will be the
+ // typedef name used in the pragma.
+ //
+ static string
+ class_name (semantics::class_&);
+
+ static string
+ class_fq_name (semantics::class_&);
+
+ // Return class scope. For ordinary classes, this will be the scope
+ // where the class is defined. For class template instantiations this
+ // will be the scope of the typedef name used in the pragma.
+ //
+ static semantics::scope&
+ class_scope (semantics::class_&);
+
+ // Return the class file. For ordinary classes, this will be the file
+ // where the class is defined. For class template instantiations this
+ // will be the file containing the pragma.
+ //
+ static semantics::path
+ class_file (semantics::class_&);
+
+ // Return the location (as location_t; useful for comparison) of
+ // an "ODB class" (i.e., an object, view, or composite value),
+ // taking into account things like definition point overrides,
+ // etc.
+ //
+ location_t
+ class_location (semantics::class_&);
+
+ // Same as above, but returns "real" location, that is, ignoring
+ // the definition point overrides.
+ //
+ location_t
+ class_real_location (semantics::class_&);
+
+ // Database names and types.
+ //
+public:
+ // Schema name for a namespace.
+ //
+ qname
+ schema (semantics::scope&) const;
+
+ // Table name prefix for a namespace.
+ //
+ string
+ table_name_prefix (semantics::scope&) const;
+
+ //
+ //
+ struct table_prefix
+ {
+ table_prefix (): level (0), derived (false) {}
+ table_prefix (semantics::class_&);
+
+ void
+ append (semantics::data_member&);
+
+ qname ns_schema; // Object's namespace schema.
+ string ns_prefix; // Object's namespace table prefix.
+ qname prefix;
+ size_t level;
+ bool derived; // One of the components in the prefix was derived.
+ };
+
+ qname
+ table_name (semantics::class_&, bool* derived = 0) const;
+
+ qname
+ table_name (semantics::class_&, data_member_path const&) const;
+
+ // Table name for the container member. The table prefix passed as the
+ // second argument must include the table prefix specified with the
+ // --table-prefix option.
+ //
+ qname
+ table_name (semantics::data_member&, table_prefix const&) const;
+
+ string
+ table_options (semantics::class_&);
+
+ // Table options for the container member.
+ //
+ string
+ table_options (semantics::data_member&, semantics::type& ct);
+
+ //
+ //
+ struct column_prefix
+ {
+ column_prefix (): derived (false), underscore (false) {}
+
+ column_prefix (semantics::data_member& m,
+ string const& key_prefix = string (),
+ string const& default_name = string ())
+ : derived (false), underscore (false)
+ {
+ append (m, key_prefix, default_name);
+ }
+
+ // If the last argument is true, the prefix will include the last member
+ // in the path.
+ //
+ column_prefix (data_member_path const&, bool last = false);
+
+ bool
+ empty () const {return prefix.empty ();}
+
+ void
+ append (semantics::data_member&,
+ string const& key_prefix = string (),
+ string const& default_name = string ());
+
+ string prefix;
+ bool derived; // One of the components in the prefix was derived.
+ bool underscore; // Trailing underscore was automatically added.
+ };
+
+ string
+ column_name (semantics::data_member&, bool& derived) const;
+
+ string
+ column_name (semantics::data_member&, column_prefix const&) const;
+
+ string
+ column_name (semantics::data_member&,
+ string const& key_prefix,
+ string const& default_name,
+ bool& derived) const;
+
+ string
+ column_name (semantics::data_member&,
+ string const& key_prefix,
+ string const& default_name,
+ column_prefix const&) const;
+
+ string
+ column_name (data_member_path const&) const;
+
+ //
+ //
+ string
+ column_type (const data_member_path&,
+ string const& key_prefix = string (),
+ bool id = false); // Pass true if this type is object id other
+ // than because of the members in the path.
+ string
+ column_type (semantics::data_member&, string const& key_prefix = string ());
+
+ string
+ column_options (semantics::data_member&);
+
+ string
+ column_options (semantics::data_member&, string const& key_prefix);
+
+ // Cleaned-up member name that can be used for database names.
+ //
+ string
+ public_name_db (semantics::data_member&) const;
+
+ // Compose the name by inserting/removing an underscore, as necessary.
+ //
+ static string
+ compose_name (string const& prefix, string const& name);
+
+ // SQL name transformations.
+ //
+ enum sql_name_type
+ {
+ sql_name_all,
+ sql_name_table,
+ sql_name_column,
+ sql_name_index,
+ sql_name_fkey,
+ sql_name_sequence,
+ sql_name_statement,
+ sql_name_count
+ };
+
+ string
+ transform_name (string const& name, sql_name_type) const;
+
+ // C++ names.
+ //
+public:
+ // Cleaned-up and potentially escaped member name that can be used
+ // in public C++ interfaces.
+ //
+ string
+ public_name (semantics::data_member&, bool escape = true) const;
+
+ // "Flatten" fully-qualified C++ name by replacing '::' with '_'
+ // and removing leading '::', if any.
+ //
+ static string
+ flat_name (string const& fqname);
+
+ // Escape C++ keywords, reserved names, and illegal characters.
+ //
+ string
+ escape (string const&) const;
+
+ // Make C++ include guard name by split words, e.g., "FooBar" to
+ // "Foo_Bar" and converting everything to upper case.
+ //
+ string
+ make_guard (string const&) const;
+
+ // Return a string literal that can be used in C++ source code. It
+ // includes "".
+ //
+ static string
+ strlit (string const&);
+
+public:
+ // Generate explicit instantiation headers with all the necessary
+ // extern and export symbols.
+ //
+ void
+ inst_header (bool decl, bool omit_exp = false);
+
+ // Counts and other information.
+ //
+public:
+ struct column_count_type
+ {
+ column_count_type ()
+ : total (0),
+ id (0),
+ inverse (0),
+ readonly (0),
+ optimistic_managed (0),
+ discriminator (0),
+ added (0),
+ deleted (0),
+ soft (0),
+ separate_load (0),
+ separate_update (0)
+ {
+ }
+
+ size_t total;
+ size_t id;
+ size_t inverse;
+ size_t readonly;
+ size_t optimistic_managed;
+ size_t discriminator;
+
+ size_t added; // Soft-added.
+ size_t deleted; // Soft-deleted.
+ size_t soft; // Soft-added/deleted (a column can be both).
+
+ size_t separate_load;
+ size_t separate_update; // Only readwrite.
+ };
+
+ static column_count_type
+ column_count (semantics::class_&, object_section* = 0);
+
+ static data_member_path*
+ id_member (semantics::class_& c)
+ {
+ // Set by the processor. May not be there for reuse-abstract
+ // classes or classes without object id.
+ //
+ return c.count ("id-member") ? &c.get<data_member_path> ("id-member") : 0;
+ }
+
+ // Object pointer information.
+ //
+public:
+ typedef ::pointer_kind pointer_kind_type;
+
+ pointer_kind_type
+ pointer_kind (semantics::type& p)
+ {
+ return p.get<pointer_kind_type> ("pointer-kind");
+ }
+
+ bool
+ lazy_pointer (semantics::type& p)
+ {
+ return p.get<bool> ("pointer-lazy");
+ }
+
+ bool
+ weak_pointer (semantics::type& p)
+ {
+ return pointer_kind (p) == pk_weak;
+ }
+
+ static data_member_path*
+ inverse (semantics::data_member& m)
+ {
+ return object_pointer (utype (m)) && m.count ("inverse")
+ ? &m.get<data_member_path> ("inverse")
+ : 0;
+ }
+
+ data_member_path*
+ inverse (semantics::data_member& m, string const& key_prefix)
+ {
+ if (key_prefix.empty ())
+ return inverse (m);
+
+ if (!object_pointer (utype (m, key_prefix)))
+ return 0;
+
+ string k (key_prefix + "-inverse");
+ return m.count (k) ? &m.get<data_member_path> (k) : 0;
+ }
+
+ // Container information.
+ //
+public:
+ typedef ::container_kind container_kind_type;
+
+ static container_kind_type
+ container_kind (semantics::type& c)
+ {
+ return c.get<container_kind_type> ("container-kind");
+ }
+
+ static bool
+ container_smart (semantics::type& c)
+ {
+ return c.get<bool> ("container-smart");
+ }
+
+ static semantics::type&
+ container_idt (semantics::data_member& m, const custom_cxx_type** trans = 0)
+ {
+ return utype (m, "id", trans);
+ }
+
+ static semantics::type&
+ container_vt (semantics::data_member& m, const custom_cxx_type** trans = 0)
+ {
+ return utype (m, "value", trans);
+ }
+
+ static semantics::type&
+ container_it (semantics::data_member& m, const custom_cxx_type** trans = 0)
+ {
+ return utype (m, "index", trans);
+ }
+
+ static semantics::type&
+ container_kt (semantics::data_member& m, const custom_cxx_type** trans = 0)
+ {
+ return utype (m, "key", trans);
+ }
+
+ static bool
+ unordered (semantics::data_member& m)
+ {
+ if (m.count ("unordered"))
+ return true;
+
+ if (semantics::type* c = container (m))
+ return c->count ("unordered");
+
+ return false;
+ }
+
+ // The 'is a' and 'has a' tests. The has_a() test currently does not
+ // cross the container boundaries.
+ //
+public:
+ static unsigned short const test_pointer = 0x01;
+ static unsigned short const test_eager_pointer = 0x02;
+ static unsigned short const test_lazy_pointer = 0x04;
+ static unsigned short const test_container = 0x08;
+ static unsigned short const test_straight_container = 0x10;
+ static unsigned short const test_inverse_container = 0x20;
+ static unsigned short const test_readonly_container = 0x40;
+ static unsigned short const test_readwrite_container = 0x80;
+ static unsigned short const test_smart_container = 0x100;
+
+ // Exclude versioned containers.
+ //
+ static unsigned short const exclude_versioned = 0x200;
+
+ // Treat eager loaded members as belonging to the main section.
+ // If this flag is specified, then section must be main_section.
+ //
+ static unsigned short const include_eager_load = 0x800;
+
+ // Exclude added/deleted members.
+ //
+ static unsigned short const exclude_added = 0x1000;
+ static unsigned short const exclude_deleted = 0x2000;
+
+ // By default the test goes into bases for non-polymorphic
+ // hierarchies and doesn't go for polymorphic. The following
+ // flags can be used to alter this behavior.
+ //
+ static unsigned short const exclude_base = 0x4000;
+ static unsigned short const include_base = 0x8000;
+
+ bool
+ is_a (data_member_path const& mp,
+ data_member_scope const& ms,
+ unsigned short flags)
+ {
+ return is_a (mp, ms, flags, utype (*mp.back ()), "");
+ }
+
+ bool
+ is_a (data_member_path const&,
+ data_member_scope const&,
+ unsigned short flags,
+ semantics::type&,
+ string const& key_prefix);
+
+ // Return the number of matching entities. Can be used as a just
+ // a bool value (0 means no match).
+ //
+ size_t
+ has_a (semantics::class_&, unsigned short flags, object_section* = 0);
+
+public:
+ // Process include path by adding the prefix, putting it through
+ // the include regex list, and adding opening and closing include
+ // characters ("" or <>) if necessary. The prefix argument indicates
+ // whether the include prefix specified with the --include-prefix
+ // option should be added. The open argument can be used to specify
+ // the opening character. It can have three values: ", <, or \0. In
+ // case of \0, the character is determined based on the value of the
+ // --include-with-bracket option.
+ //
+ string
+ process_include_path (string const&, bool prefix = true, char open = '\0');
+
+ // Diverge output.
+ //
+public:
+ void
+ diverge (std::ostream& os)
+ {
+ diverge (os.rdbuf ());
+ }
+
+ void
+ diverge (std::streambuf* sb);
+
+ void
+ restore ();
+
+ // Implementation details.
+ //
+private:
+ static bool
+ composite_ (semantics::class_&);
+
+ template <typename X>
+ static X
+ indirect_value (semantics::context const& c, string const& key)
+ {
+ typedef X (*func) ();
+ std::type_info const& ti (c.type_info (key));
+
+ if (ti == typeid (func))
+ return c.get<func> (key) ();
+ else
+ return c.get<X> (key);
+ }
+
+ static semantics::type*
+ indirect_type (semantics::context const& c,
+ string const& kp,
+ semantics::names*& hint)
+ {
+ typedef semantics::type* (*func) (semantics::names*&);
+
+ string const tk (kp + "-tree-type");
+ std::type_info const& ti (c.type_info (tk));
+
+ if (ti == typeid (func))
+ return c.get<func> (tk) (hint);
+ else
+ {
+ hint = c.get<semantics::names*> (kp + "-tree-hint");
+ return c.get<semantics::type*> (tk);
+ }
+ }
+
+public:
+ typedef std::set<string> keyword_set_type;
+
+ struct db_type_type
+ {
+ db_type_type () {}
+ db_type_type (string const& t, string const& it, bool n)
+ : type (t), id_type (it), null (n)
+ {
+ }
+
+ string type;
+ string id_type;
+ bool null;
+ };
+
+ struct type_map_type: std::map<string, db_type_type>
+ {
+ typedef std::map<string, db_type_type> base;
+
+ const_iterator
+ find (semantics::type&, semantics::names* hint);
+ };
+
+protected:
+ struct data
+ {
+ virtual
+ ~data () {}
+
+ data (std::ostream& os)
+ : extra_ (0),
+ os_ (os.rdbuf ()),
+ in_comment_ (false),
+ top_object_ (0),
+ cur_object_ (0),
+ sql_name_upper_ ("(.+)", "\\U$1"),
+ sql_name_lower_ ("(.+)", "\\L$1")
+ {
+ }
+
+ public:
+ void* extra_;
+
+ std::ostream os_;
+ std::stack<std::streambuf*> os_stack_;
+
+ bool in_comment_;
+
+ semantics::class_* top_object_;
+ semantics::class_* cur_object_;
+
+ string exp_;
+ string ext_;
+
+ keyword_set_type keyword_set_;
+ type_map_type type_map_;
+
+ regex_mapping sql_name_regex_[sql_name_count];
+ regexsub sql_name_upper_;
+ regexsub sql_name_lower_;
+
+ regex_mapping include_regex_;
+ regex_mapping accessor_regex_;
+ regex_mapping modifier_regex_;
+ };
+
+ typedef cutl::shared_ptr<data> data_ptr;
+ data_ptr data_;
+
+public:
+ typedef ::features features_type;
+
+ void*& extra; // Extra data that may need to be shared by a sub-system.
+
+ std::ostream& os;
+ semantics::unit& unit;
+ options_type const& options;
+ features_type& features;
+ database const db;
+
+ bool& in_comment;
+
+ string& exp; // Export symbol (with trailing space if specified).
+ string& ext; // Extern symbol.
+
+ keyword_set_type const& keyword_set;
+
+ regex_mapping const& include_regex;
+ regex_mapping const& accessor_regex;
+ regex_mapping const& modifier_regex;
+
+ bool embedded_schema;
+ bool separate_schema;
+
+ bool multi_static;
+ bool multi_dynamic;
+
+ bool force_versioned; // Force statement processing for debugging.
+
+ // Outermost object or view currently being traversed.
+ //
+ semantics::class_*& top_object;
+
+ // Object or view currently being traversed. It can be the same as
+ // top_object or it can a base of top_object.
+ //
+ semantics::class_*& cur_object;
+
+ // Per-database customizable functionality.
+ //
+protected:
+ // Return empty string if there is no mapping. The type passed is
+ // already cvr-unqualified. The null out argument indicates whether
+ // the column should allow NULL values by default.
+ //
+ string
+ database_type (semantics::type& t,
+ semantics::names* hint,
+ bool id,
+ bool* null = 0)
+ {
+ return current ().database_type_impl (t, hint, id, null);
+ }
+
+ // The default implementation uses the type map (populated by the database-
+ // specific context implementation) to come up with a mapping.
+ //
+ virtual string
+ database_type_impl (semantics::type&, semantics::names*, bool, bool*);
+
+public:
+ typedef context root_context;
+
+ virtual
+ ~context ();
+ context ();
+ context (std::ostream&,
+ semantics::unit&,
+ options_type const&,
+ features_type&,
+ data_ptr = data_ptr ());
+
+ static context&
+ current ()
+ {
+ return *current_;
+ }
+
+private:
+ static context* current_;
+
+#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
+ context&
+ operator= (context const&) = delete;
+#else
+private:
+ context&
+ operator= (context const&);
+#endif
+};
+
+// Create concrete database context.
+//
+std::unique_ptr<context>
+create_context (std::ostream&,
+ semantics::unit&,
+ options const&,
+ features&,
+ semantics::relational::model*);
+
+// Checks if scope Y names any of X.
+//
+template <typename X, typename Y>
+bool
+has (Y& y)
+{
+ for (semantics::scope::names_iterator i (y.names_begin ()),
+ e (y.names_end ()); i != e; ++i)
+ if (i->named (). template is_a<X> ())
+ return true;
+
+ return false;
+}
+
+#include <odb/context.ixx>
+
+#endif // ODB_CONTEXT_HXX
diff --git a/odb/context.ixx b/odb/odb/context.ixx
index 5018743..5018743 100644
--- a/odb/context.ixx
+++ b/odb/odb/context.ixx
diff --git a/odb/odb/cxx-lexer.cxx b/odb/odb/cxx-lexer.cxx
new file mode 100644
index 0000000..e4e0229
--- /dev/null
+++ b/odb/odb/cxx-lexer.cxx
@@ -0,0 +1,366 @@
+// file : odb/cxx-lexer.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <new> // std::bad_alloc
+#include <cassert>
+#include <iostream>
+
+#include <odb/cxx-lexer.hxx>
+
+using namespace std;
+
+//
+// cxx_lexer
+//
+
+// Token spelling. See cpplib.h for details.
+//
+#define OP(e, s) s ,
+#define TK(e, s) #e ,
+char const* cxx_lexer::token_spelling[N_TTYPES + 1] = { TTYPE_TABLE "KEYWORD"};
+#undef OP
+#undef TK
+
+cxx_lexer::
+~cxx_lexer ()
+{
+}
+
+//
+// cxx_tokens_lexer
+//
+
+void cxx_tokens_lexer::
+start (cxx_tokens const& ts, location_t start_loc)
+{
+ tokens_ = &ts;
+ cur_ = ts.begin ();
+ loc_ = start_loc;
+}
+
+cpp_ttype cxx_tokens_lexer::
+next (std::string& token, tree* node)
+{
+ if (cur_ != tokens_->end ())
+ {
+ loc_ = cur_->loc;
+ token = cur_->literal;
+ if (node != 0)
+ *node = cur_->node;
+ return static_cast<cpp_ttype> (cur_++->type);
+ }
+ else
+ return CPP_EOF;
+}
+
+location_t cxx_tokens_lexer::
+location () const
+{
+ return loc_;
+}
+
+//
+// cxx_pragma_lexer
+//
+
+void cxx_pragma_lexer::
+start ()
+{
+ token_ = &token_data_;
+ type_ = &type_data_;
+}
+
+string cxx_pragma_lexer::
+start (tree& token, cpp_ttype& type)
+{
+ token_ = &token;
+ type_ = &type;
+
+ return translate ();
+}
+
+cpp_ttype cxx_pragma_lexer::
+next (string& token, tree* node)
+{
+ *type_ = pragma_lex (token_);
+
+ // See if this is a keyword using the C++ parser machinery and
+ // the current C++ dialect.
+ //
+ if (*type_ == CPP_NAME &&
+#if BUILDING_GCC_MAJOR >= 8
+ IDENTIFIER_KEYWORD_P (*token_)
+#else
+ C_IS_RESERVED_WORD (*token_)
+#endif
+ )
+ *type_ = CPP_KEYWORD;
+
+ if (node != 0 && node != token_)
+ *node = *token_;
+
+ token = translate ();
+ return *type_;
+}
+
+location_t cxx_pragma_lexer::
+location () const
+{
+ // Starting from GCC 6 the input location seem to require the same
+ // translation as what we do in real_source_location().
+ //
+#if BUILDING_GCC_MAJOR >= 6
+ return linemap_resolve_location (
+ line_table, input_location, LRK_MACRO_EXPANSION_POINT, 0);
+#else
+ return input_location;
+#endif
+}
+
+string cxx_pragma_lexer::
+translate ()
+{
+ string r;
+
+ if (*type_ == CPP_NAME || *type_ == CPP_KEYWORD)
+ r = IDENTIFIER_POINTER (*token_);
+ else if (*type_ == CPP_STRING)
+ r = TREE_STRING_POINTER (*token_);
+
+ return r;
+}
+
+//
+// cxx_string_lexer
+//
+
+// Diagnostics callback.
+//
+extern "C" bool
+cpp_diagnostic_callback (
+ cpp_reader* reader,
+#if BUILDING_GCC_MAJOR >= 9
+ cpp_diagnostic_level level,
+#else
+ int level,
+#endif
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 5
+#if BUILDING_GCC_MAJOR >= 9
+ cpp_warning_reason,
+#else
+ int /*reason*/, // Added in GCC 4.6.0.
+#endif
+#endif
+#if BUILDING_GCC_MAJOR <= 5
+ location_t,
+ unsigned int,
+#else
+ rich_location*,
+#endif
+ char const* msg,
+ va_list *ap)
+{
+ char const* kind (0);
+ switch (level)
+ {
+ case CPP_DL_NOTE:
+ case CPP_DL_WARNING_SYSHDR:
+ case CPP_DL_WARNING:
+ case CPP_DL_PEDWARN:
+ // Ignore these.
+ break;
+ case CPP_DL_ERROR:
+ case CPP_DL_FATAL:
+ kind = "error";
+ break;
+ case CPP_DL_ICE:
+ kind = "ice";
+ break;
+ default:
+ kind = "unknown";
+ break;
+ }
+
+ if (kind != 0)
+ {
+ fprintf (stderr, "%s: ", kind);
+ vfprintf (stderr, msg, *ap);
+ fprintf (stderr, "\n");
+
+ // By resetting the callback we indicate to cxx_string_lexer that there
+ // was an error.
+ //
+#if BUILDING_GCC_MAJOR >= 9
+ cpp_get_callbacks (reader)->diagnostic = 0;
+#else
+ cpp_get_callbacks (reader)->error = 0;
+#endif
+ return true;
+ }
+
+ return false;
+}
+
+cxx_string_lexer::
+cxx_string_lexer ()
+ : reader_ (0)
+{
+#if BUILDING_GCC_MAJOR >= 5
+ linemap_init (&line_map_, UNKNOWN_LOCATION);
+#else
+ linemap_init (&line_map_);
+#endif
+
+#if BUILDING_GCC_MAJOR >= 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
+
+ linemap_add (&line_map_, LC_ENTER, 0, "<memory>", 0);
+
+ reader_ = cpp_create_reader (
+ cxx_dialect == cxx0x // Nothing new for C++14.
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 6
+ ? CLK_CXX11
+#else
+ ? CLK_CXX0X
+#endif
+ : CLK_CXX98,
+ 0,
+ &line_map_);
+
+ if (reader_ == 0)
+ throw bad_alloc ();
+
+ callbacks_ = cpp_get_callbacks (reader_);
+}
+
+cxx_string_lexer::
+~cxx_string_lexer ()
+{
+ if (reader_ != 0)
+ cpp_destroy (reader_);
+
+ // Was removed as "dead code" in GCC 4.7.0.
+ //
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+ linemap_free (&line_map_);
+#endif
+}
+
+void cxx_string_lexer::
+start (string const& data)
+{
+ // The previous lexing session should have popped the buffer.
+ //
+ assert (cpp_get_buffer (reader_) == 0);
+
+#if BUILDING_GCC_MAJOR >= 9
+ callbacks_->diagnostic = &cpp_diagnostic_callback;
+#else
+ callbacks_->error = &cpp_diagnostic_callback;
+#endif
+
+ data_ = data;
+ buf_ = data;
+ buf_ += '\n';
+ loc_ = 0;
+
+ cpp_push_buffer (
+ reader_,
+ reinterpret_cast<unsigned char const*> (buf_.c_str ()),
+ buf_.size (),
+ true);
+}
+
+cpp_ttype cxx_string_lexer::
+next (string& token, tree* node)
+{
+ token.clear ();
+ cpp_token const* t (cpp_get_token (reader_));
+
+ // If there was an error, the callback will be reset to 0. Diagnostics has
+ // already been issued.
+ //
+#if BUILDING_GCC_MAJOR >= 9
+ if (callbacks_->diagnostic == 0)
+#else
+ if (callbacks_->error == 0)
+#endif
+ throw invalid_input ();
+
+ cpp_ttype tt (t->type);
+
+ switch (tt)
+ {
+ case CPP_NAME:
+ {
+ char const* name (
+ reinterpret_cast<char const*> (NODE_NAME (t->val.node.node)));
+
+ // See if this is a keyword using the C++ parser machinery and
+ // the current C++ dialect.
+ //
+ tree id (get_identifier (name));
+
+ if (
+#if BUILDING_GCC_MAJOR >= 8
+ IDENTIFIER_KEYWORD_P (id)
+#else
+ C_IS_RESERVED_WORD (id)
+#endif
+ )
+ tt = CPP_KEYWORD;
+
+ if (node != 0)
+ *node = id;
+
+ token = name;
+ break;
+ }
+ case CPP_STRING:
+ case CPP_NUMBER:
+ {
+ if (node != 0)
+ *node = 0; // Doesn't seem to be available.
+
+ cpp_string const& s (t->val.str);
+ token.assign (reinterpret_cast<char const*> (s.text), s.len);
+ break;
+ }
+ default:
+ {
+ if (tt <= CPP_LAST_PUNCTUATOR)
+ {
+ if (node != 0)
+ *node = 0;
+ token = token_spelling[tt];
+ }
+ else
+ {
+ cerr << "unexpected token '" << token_spelling[tt] << "' in '" <<
+ data_ << "'" << endl;
+ throw invalid_input ();
+ }
+ break;
+ }
+ }
+
+ // Cache the location of this token.
+ //
+ loc_ = t->src_loc;
+
+ return tt;
+}
+
+location_t cxx_string_lexer::
+location () const
+{
+ return loc_;
+}
diff --git a/odb/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/odb/diagnostics.hxx b/odb/odb/diagnostics.hxx
new file mode 100644
index 0000000..46f2272
--- /dev/null
+++ b/odb/odb/diagnostics.hxx
@@ -0,0 +1,96 @@
+// file : odb/diagnostics.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_DIAGNOSTICS_HXX
+#define ODB_DIAGNOSTICS_HXX
+
+#include <odb/gcc-fwd.hxx>
+
+#include <string>
+#include <cstddef>
+#include <iostream>
+
+#include <libcutl/fs/path.hxx>
+
+#include <odb/location.hxx>
+
+using std::endl;
+
+std::ostream&
+error (cutl::fs::path const&, std::size_t line, std::size_t clmn);
+
+std::ostream&
+warn (cutl::fs::path const&, std::size_t line, std::size_t clmn);
+
+std::ostream&
+info (cutl::fs::path const&, std::size_t line, std::size_t clmn);
+
+inline std::ostream&
+error (location const& l)
+{
+ return error (l.file, l.line, l.column);
+}
+
+inline std::ostream&
+warn (location const&l)
+{
+ return warn (l.file, l.line, l.column);
+}
+
+inline std::ostream&
+info (location const&l)
+{
+ return info (l.file, l.line, l.column);
+}
+
+std::ostream&
+error (location_t);
+
+std::ostream&
+warn (location_t);
+
+std::ostream&
+info (location_t);
+
+//
+//
+class cxx_lexer;
+
+std::ostream&
+error (cxx_lexer&);
+
+std::ostream&
+warn (cxx_lexer&);
+
+std::ostream&
+info (cxx_lexer&);
+
+// Location as a string in the "<file>:<line>:<column>" format.
+//
+std::string
+location_string (cutl::fs::path const&,
+ std::size_t line,
+ std::size_t clmn,
+ bool leaf = false);
+
+inline std::string
+location_string (location const& l, bool leaf = false)
+{
+ return location_string (l.file, l.line, l.column, leaf);
+}
+
+std::string
+location_string (location_t, bool leaf = false);
+
+// location_t macro wrappers.
+//
+cutl::fs::path
+location_file (location_t);
+
+std::size_t
+location_line (location_t);
+
+std::size_t
+location_column (location_t);
+
+#endif // ODB_DIAGNOSTICS_HXX
diff --git a/odb/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/odb/gcc.hxx b/odb/odb/gcc.hxx
new file mode 100644
index 0000000..e5fecef
--- /dev/null
+++ b/odb/odb/gcc.hxx
@@ -0,0 +1,215 @@
+// file : odb/gcc.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_GCC_HXX
+#define ODB_GCC_HXX
+
+#include <odb/gcc-fwd.hxx>
+
+// Actually, let's keep it out. With it included we can compile in C++98
+// but not in C++14 (GCC 6 default).
+//
+// #if BUILDING_GCC_MAJOR >= 6
+// # include <safe-ctype.h> // See gcc-fwd.hxx.
+// #endif
+
+// GCC header includes to get the plugin and parse tree declarations.
+// The order is important and doesn't follow any kind of logic.
+//
+
+#include <stdlib.h>
+#include <gmp.h>
+
+#include <cstdlib> // Include before GCC poisons some declarations.
+
+// GCC 4.7 can be built using either C or C++ compiler. From 4.8 it
+// is always built as C++.
+//
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+# define ODB_GCC_PLUGIN_C
+#elif BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 7
+# include <config.h>
+# ifndef ENABLE_BUILD_WITH_CXX
+# define ODB_GCC_PLUGIN_C
+# endif
+#endif
+
+#ifdef ODB_GCC_PLUGIN_C
+extern "C"
+{
+#endif
+
+// GCC's system.h below includes safe-ctype.h which "disables" versions
+// from ctype.h. Well, now it's gonna learn how it feels to be disabled.
+//
+#define SAFE_CTYPE_H
+
+#include <gcc-plugin.h>
+
+#include <config.h>
+#include <system.h>
+#include <coretypes.h>
+#include <tree.h>
+#include <real.h>
+
+#include <cpplib.h>
+#include <cp/cp-tree.h>
+
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 5
+# include <c-family/c-common.h>
+# include <c-family/c-pragma.h>
+#else
+# include <c-common.h>
+# include <c-pragma.h>
+#endif
+
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 8
+# include <stringpool.h> // get_identifier
+#endif
+
+#include <diagnostic.h>
+#include <output.h>
+
+#ifdef ODB_GCC_PLUGIN_C
+} // extern "C"
+#endif
+
+// Get the value of INTEGER_CST reinterpreted as unsigned.
+//
+inline unsigned long long
+integer_value (tree n)
+{
+ unsigned long long val;
+
+#if BUILDING_GCC_MAJOR >= 5
+ if (tree_fits_uhwi_p (n))
+ val = static_cast<unsigned long long> (tree_to_uhwi (n));
+ else
+ val = static_cast<unsigned long long> (tree_to_shwi (n));
+#else
+ HOST_WIDE_INT hwl (TREE_INT_CST_LOW (n));
+ HOST_WIDE_INT hwh (TREE_INT_CST_HIGH (n));
+ unsigned short width (HOST_BITS_PER_WIDE_INT);
+
+ if (hwh == 0)
+ val = static_cast<unsigned long long> (hwl);
+ else if (hwh == -1 && hwl != 0)
+ val = static_cast<unsigned long long> (hwl);
+ else
+ val = static_cast<unsigned long long> ((hwh << width) + hwl);
+#endif
+
+ return val;
+}
+
+// Since 4.7.0 the location may point inside a macro rather than at
+// the expansion point. We are only really interested in the expansion
+// points so we use the real_source_location() wrapper rather than
+// DECL_SOURCE_LOCATION() to do this at the source.
+//
+inline location_t
+real_source_location (tree n)
+{
+ location_t l (DECL_SOURCE_LOCATION (n));
+
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 6
+ l = linemap_resolve_location (line_table, l, LRK_MACRO_EXPANSION_POINT, 0);
+#endif
+
+ return l;
+}
+
+// In 4.9.0 the tree code type was changed from int to enum tree_code.
+// the tree_code_name array is also gone with the get_tree_code_name()
+// function in its place.
+//
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 8
+typedef enum tree_code gcc_tree_code_type;
+
+inline const char*
+gcc_tree_code_name (gcc_tree_code_type tc) {return get_tree_code_name (tc);}
+#else
+typedef int gcc_tree_code_type;
+
+inline const char*
+gcc_tree_code_name (gcc_tree_code_type tc) {return tree_code_name[tc];}
+#endif
+
+// Only since GCC 4.7.0.
+//
+#ifndef LOCATION_COLUMN
+#define LOCATION_COLUMN(LOC) (expand_location (LOC).column)
+#endif
+
+#ifndef DECL_SOURCE_COLUMN
+#define DECL_SOURCE_COLUMN(NODE) LOCATION_COLUMN (DECL_SOURCE_LOCATION (NODE))
+#endif
+
+// Only since GCC 4.6.0.
+//
+#ifndef DECL_CHAIN
+#define DECL_CHAIN(x) TREE_CHAIN(x)
+#endif
+
+// In GCC 6 ANON_AGGRNAME_P became anon_aggrname_p().
+// In GCC 10 anon_aggrname_p() became IDENTIFIER_ANON_P.
+//
+#if BUILDING_GCC_MAJOR < 6
+# define IDENTIFIER_ANON_P(X) ANON_AGGRNAME_P(X)
+#elif BUILDING_GCC_MAJOR < 10
+# define IDENTIFIER_ANON_P(X) anon_aggrname_p(X)
+#endif
+
+// In GCC 9:
+//
+// INCLUDED_FROM Became linemap_included_from_linemap().
+//
+// LAST_SOURCE_LINE Was removed apparently as no longer used. Studying
+// the line-map.h diff from 8.3 suggests that the old
+// implementation should still work.
+//
+#if BUILDING_GCC_MAJOR >= 9
+
+inline const line_map_ordinary*
+INCLUDED_FROM (line_maps* set, const line_map_ordinary* map)
+{
+ return linemap_included_from_linemap (set, map);
+}
+
+inline source_location
+LAST_SOURCE_LINE_LOCATION (const line_map_ordinary* map)
+{
+ return (((map[1].start_location - 1
+ - map->start_location)
+ & ~((1 << map->m_column_and_range_bits) - 1))
+ + map->start_location);
+}
+
+inline linenum_type
+LAST_SOURCE_LINE (const line_map_ordinary* map)
+{
+ return SOURCE_LINE (map, LAST_SOURCE_LINE_LOCATION (map));
+}
+
+#endif
+
+// In GCC 11:
+//
+// lookup_qualified_name() has a new interface.
+//
+// DECL_IS_BUILTIN became DECL_IS_UNDECLARED_BUILTIN.
+//
+#if BUILDING_GCC_MAJOR >= 11
+
+inline tree
+lookup_qualified_name (tree scope, tree name, bool type, bool complain)
+{
+ return lookup_qualified_name (
+ scope, name, (type ? LOOK_want::TYPE : LOOK_want::NORMAL), complain);
+}
+
+#define DECL_IS_BUILTIN(decl) DECL_IS_UNDECLARED_BUILTIN(decl)
+
+#endif
+
+#endif // ODB_GCC_HXX
diff --git a/odb/generate.hxx b/odb/odb/generate.hxx
index b3b9c43..b3b9c43 100644
--- a/odb/generate.hxx
+++ b/odb/odb/generate.hxx
diff --git a/odb/odb/generator.cxx b/odb/odb/generator.cxx
new file mode 100644
index 0000000..f0b92ab
--- /dev/null
+++ b/odb/odb/generator.cxx
@@ -0,0 +1,1044 @@
+// file : odb/generator.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cctype> // std::toupper, std::is{alpha,upper,lower}
+#include <string>
+#include <memory> // std::unique_ptr
+#include <iomanip>
+#include <fstream>
+#include <sstream>
+#include <iostream>
+
+#include <libcutl/fs/auto-remove.hxx>
+
+#include <libcutl/compiler/code-stream.hxx>
+#include <libcutl/compiler/cxx-indenter.hxx>
+#include <libcutl/compiler/sloc-counter.hxx>
+
+#include <libstudxml/parser.hxx>
+#include <libstudxml/serializer.hxx>
+
+#include <odb/version.hxx>
+#include <odb/context.hxx>
+#include <odb/generator.hxx>
+
+#include <odb/semantics/relational/model.hxx>
+#include <odb/semantics/relational/changeset.hxx>
+#include <odb/semantics/relational/changelog.hxx>
+
+#include <odb/generate.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+using namespace cutl;
+
+using semantics::path;
+typedef vector<string> strings;
+typedef vector<path> paths;
+typedef vector<cutl::shared_ptr<ofstream> > ofstreams;
+
+namespace
+{
+ static char const cxx_file_header[] =
+ "// -*- C++ -*-\n"
+ "//\n"
+ "// This file was generated by ODB, object-relational mapping (ORM)\n"
+ "// compiler for C++.\n"
+ "//\n\n";
+
+ static char const sql_file_header[] =
+ "/* This file was generated by ODB, object-relational mapping (ORM)\n"
+ " * compiler for C++.\n"
+ " */\n\n";
+
+ void
+ open (ifstream& ifs, path const& p)
+ {
+ ifs.open (p.string ().c_str (), ios_base::in | ios_base::binary);
+
+ if (!ifs.is_open ())
+ {
+ cerr << "error: unable to open '" << p << "' in read mode" << endl;
+ throw generator_failed ();
+ }
+ }
+
+ void
+ open (ofstream& ofs, path const& p, ios_base::openmode m = ios_base::out)
+ {
+ ofs.open (p.string ().c_str (), ios_base::out | m);
+
+ if (!ofs.is_open ())
+ {
+ cerr << "error: unable to open '" << p << "' in write mode" << endl;
+ throw generator_failed ();
+ }
+ }
+
+ void
+ append (ostream& os, strings const& text)
+ {
+ for (strings::const_iterator i (text.begin ());
+ i != text.end (); ++i)
+ {
+ os << *i << endl;
+ }
+ }
+
+ void
+ append (ostream& os, path const& file)
+ {
+ ifstream ifs;
+ open (ifs, file);
+
+ // getline() will set the failbit if it failed to extract anything,
+ // not even the delimiter and eofbit if it reached eof before seeing
+ // the delimiter.
+ //
+ // We used to just do:
+ //
+ // os << ifs.rdbuf ();
+ //
+ // But that has some drawbacks: it won't end with a newline if the file
+ // doesn't end with one. There were also some issues with Windows newlines
+ // (we ended up doubling them).
+ //
+ for (string s; getline (ifs, s); )
+ os << s << endl;
+ }
+
+ // Append prologue/interlude/epilogue.
+ //
+ void
+ append_logue (ostream& os,
+ database db,
+ database_map<vector<string> > const& text,
+ database_map<vector<string> > const& file,
+ char const* begin_comment,
+ char const* end_comment)
+ {
+ bool t (text.count (db) != 0);
+ bool f (file.count (db) != 0);
+
+ if (t || f)
+ {
+ os << begin_comment << endl;
+
+ if (t)
+ append (os, text[db]);
+
+ if (f)
+ {
+ strings const& fs (file[db]);
+
+ for (strings::const_iterator i (fs.begin ());
+ i != fs.end (); ++i)
+ append (os, path (*i));
+ }
+
+ os << end_comment << endl
+ << endl;
+ }
+ }
+}
+
+void
+generate (options const& ops,
+ features& fts,
+ semantics::unit& unit,
+ path const& p,
+ paths const& inputs)
+{
+ namespace sema_rel = semantics::relational;
+ using cutl::shared_ptr;
+
+ try
+ {
+ database db (ops.database ()[0]);
+ multi_database md (ops.multi_database ());
+
+ // First create the database model.
+ //
+ bool gen_schema (ops.generate_schema () && db != database::common);
+
+ shared_ptr<sema_rel::model> model;
+
+ if (gen_schema)
+ {
+ unique_ptr<context> ctx (create_context (cerr, unit, ops, fts, 0));
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ model = relational::model::generate ();
+ break;
+ }
+ case database::common:
+ break;
+ }
+ }
+
+ // Input files.
+ //
+ path file (ops.input_name ().empty ()
+ ? p.leaf ()
+ : path (ops.input_name ()).leaf ());
+ string base (file.base ().string ());
+
+ path in_log_path;
+ path log_dir (ops.changelog_dir ().count (db) != 0
+ ? ops.changelog_dir ()[db]
+ : "");
+ if (ops.changelog_in ().count (db) != 0)
+ {
+ in_log_path = path (ops.changelog_in ()[db]);
+
+ if (!log_dir.empty () && !in_log_path.absolute ())
+ in_log_path = log_dir / in_log_path;
+ }
+ else if (ops.changelog ().count (db) != 0)
+ {
+ in_log_path = path (ops.changelog ()[db]);
+
+ if (!in_log_path.absolute () && !log_dir.empty ())
+ in_log_path = log_dir / in_log_path;
+ }
+ else
+ {
+ string log_name (base + ops.changelog_file_suffix ()[db] +
+ ops.changelog_suffix ());
+ in_log_path = path (log_name);
+
+ if (!log_dir.empty ())
+ in_log_path = log_dir / in_log_path;
+ else
+ in_log_path = p.directory () / in_log_path; // Use input directory.
+ }
+
+ // Load the old changelog and generate a new one.
+ //
+ bool gen_changelog (gen_schema && unit.count ("model-version") != 0);
+ cutl::shared_ptr<sema_rel::changelog> changelog;
+ cutl::shared_ptr<sema_rel::changelog> old_changelog;
+ string old_changelog_xml;
+
+ path out_log_path;
+ if (ops.changelog_out ().count (db))
+ {
+ out_log_path = path (ops.changelog_out ()[db]);
+
+ if (!log_dir.empty () && !out_log_path.absolute ())
+ out_log_path = log_dir / out_log_path;
+ }
+ else
+ out_log_path = in_log_path;
+
+ if (gen_changelog)
+ {
+ ifstream log;
+
+ // Unless we are forced to re-initialize the changelog, load the
+ // old one.
+ //
+ if (!ops.init_changelog ())
+ log.open (in_log_path.string ().c_str (),
+ ios_base::in | ios_base::binary);
+
+ if (log.is_open ()) // The changelog might not exist.
+ {
+ try
+ {
+ // Get the XML into a buffer. We use it to avoid modifying the
+ // file when the changelog hasn't changed.
+ //
+ for (bool first (true); !log.eof (); )
+ {
+ string line;
+ getline (log, line);
+
+ if (log.fail ())
+ ios_base::failure ("getline");
+
+ if (first)
+ first = false;
+ else
+ old_changelog_xml += '\n';
+
+ old_changelog_xml += line;
+ }
+
+ istringstream is (old_changelog_xml);
+ is.exceptions (ios_base::badbit | ios_base::failbit);
+
+ xml::parser p (is, in_log_path.string ());
+ old_changelog.reset (new (shared) sema_rel::changelog (p));
+
+ if (old_changelog->database () != db.string ())
+ {
+ cerr << in_log_path << ": error: wrong database '" <<
+ old_changelog->database () << "', expected '" << db <<
+ "'" << endl;
+ throw generator_failed ();
+ }
+
+ string sn (ops.schema_name ()[db]);
+ if (old_changelog->schema_name () != sn)
+ {
+ cerr << in_log_path << ": error: wrong schema name '" <<
+ old_changelog->schema_name () << "', expected '" << sn <<
+ "'" << endl;
+ throw generator_failed ();
+ }
+ }
+ catch (const ios_base::failure& e)
+ {
+ cerr << in_log_path << ": read failure" << endl;
+ throw generator_failed ();
+ }
+ catch (const xml::parsing& e)
+ {
+ cerr << e.what () << endl;
+ throw generator_failed ();
+ }
+ }
+
+ changelog = relational::changelog::generate (
+ *model,
+ unit.get<model_version> ("model-version"),
+ old_changelog.get (),
+ in_log_path.string (),
+ out_log_path.string (),
+ ops);
+ }
+
+ // Output files.
+ //
+ fs::auto_removes auto_rm;
+
+ string hxx_name (base + ops.odb_file_suffix ()[db] + ops.hxx_suffix ());
+ string ixx_name (base + ops.odb_file_suffix ()[db] + ops.ixx_suffix ());
+ string cxx_name (base + ops.odb_file_suffix ()[db] + ops.cxx_suffix ());
+ string sch_name (base + ops.schema_file_suffix ()[db] + ops.cxx_suffix ());
+ string sql_name (base + ops.sql_file_suffix ()[db] + ops.sql_suffix ());
+
+ path hxx_path (hxx_name);
+ path ixx_path (ixx_name);
+ path cxx_path (cxx_name);
+ path sch_path (sch_name);
+ path sql_path (sql_name);
+ paths mig_pre_paths;
+ paths mig_post_paths;
+
+ bool gen_migration (gen_changelog && !ops.suppress_migration ());
+ bool gen_sql_migration (
+ gen_migration && ops.schema_format ()[db].count (schema_format::sql));
+
+ if (gen_sql_migration)
+ {
+ for (sema_rel::changelog::contains_changeset_iterator i (
+ changelog->contains_changeset_begin ());
+ i != changelog->contains_changeset_end (); ++i)
+ {
+ sema_rel::changeset& cs (i->changeset ());
+
+ // Default format: %N[-D%]-%3V-{pre|post}.sql
+ //
+ string n (base);
+
+ if (md != multi_database::disabled)
+ n += '-' + db.string ();
+
+ ostringstream os;
+ os << setfill ('0') << setw (3) << cs.version ();
+ n += '-' + os.str ();
+
+ mig_pre_paths.push_back (path (n + "-pre" + ops.sql_suffix ()));
+ mig_post_paths.push_back (path (n + "-post" + ops.sql_suffix ()));
+ }
+ }
+
+ if (!ops.output_dir ().empty ())
+ {
+ path dir (ops.output_dir ());
+ hxx_path = dir / hxx_path;
+ ixx_path = dir / ixx_path;
+ cxx_path = dir / cxx_path;
+ sch_path = dir / sch_path;
+ sql_path = dir / sql_path;
+
+ if (gen_sql_migration)
+ {
+ for (paths::size_type i (0); i < mig_pre_paths.size (); ++i)
+ {
+ mig_pre_paths[i] = dir / mig_pre_paths[i];
+ mig_post_paths[i] = dir / mig_post_paths[i];
+ }
+ }
+ }
+
+ //
+ //
+ bool gen_cxx (!ops.generate_schema_only ());
+
+ ofstream hxx;
+ if (gen_cxx)
+ {
+ open (hxx, hxx_path);
+ auto_rm.add (hxx_path);
+ }
+
+ //
+ //
+ ofstream ixx;
+ if (gen_cxx)
+ {
+ open (ixx, ixx_path);
+ auto_rm.add (ixx_path);
+ }
+
+ //
+ //
+ ofstream cxx;
+ if (gen_cxx && (db != database::common || md == multi_database::dynamic))
+ {
+ open (cxx, cxx_path);
+ auto_rm.add (cxx_path);
+ }
+
+ //
+ //
+ bool gen_sep_schema (
+ gen_schema &&
+ ops.schema_format ()[db].count (schema_format::separate));
+
+ ofstream sch;
+ if (gen_sep_schema)
+ {
+ open (sch, sch_path);
+ auto_rm.add (sch_path);
+ }
+
+ //
+ //
+ bool gen_sql_schema (gen_schema &&
+ ops.schema_format ()[db].count (schema_format::sql));
+ ofstream sql;
+ if (gen_sql_schema)
+ {
+ open (sql, sql_path);
+ auto_rm.add (sql_path);
+ }
+
+ //
+ //
+ ofstreams mig_pre, mig_post;
+ if (gen_sql_migration)
+ {
+ for (paths::size_type i (0); i < mig_pre_paths.size (); ++i)
+ {
+ shared_ptr<ofstream> pre (new (shared) ofstream);
+ shared_ptr<ofstream> post (new (shared) ofstream);
+
+ open (*pre, mig_pre_paths[i]);
+ auto_rm.add (mig_pre_paths[i]);
+ mig_pre.push_back (pre);
+
+ open (*post, mig_post_paths[i]);
+ auto_rm.add (mig_post_paths[i]);
+ mig_post.push_back (post);
+ }
+ }
+
+ // Print output file headers.
+ //
+ if (gen_cxx)
+ {
+ hxx << cxx_file_header;
+ ixx << cxx_file_header;
+
+ if (db != database::common)
+ cxx << cxx_file_header;
+ }
+
+ if (gen_sep_schema)
+ sch << cxx_file_header;
+
+ if (gen_sql_schema)
+ sql << sql_file_header;
+
+ if (gen_sql_migration)
+ {
+ for (ofstreams::size_type i (0); i < mig_pre.size (); ++i)
+ {
+ *mig_pre[i] << sql_file_header;
+ *mig_post[i] << sql_file_header;
+ }
+ }
+
+ typedef compiler::ostream_filter<compiler::cxx_indenter, char> ind_filter;
+ typedef compiler::ostream_filter<compiler::sloc_counter, char> sloc_filter;
+
+ size_t sloc_total (0);
+
+ // Include settings.
+ //
+ string gp (ops.guard_prefix ());
+ if (!gp.empty () && gp[gp.size () - 1] != '_')
+ gp.append ("_");
+
+ // HXX
+ //
+ if (gen_cxx)
+ {
+ unique_ptr<context> ctx (
+ create_context (hxx, unit, ops, fts, model.get ()));
+
+ sloc_filter sloc (ctx->os);
+
+ string guard (ctx->make_guard (gp + hxx_name));
+
+ hxx << "#ifndef " << guard << endl
+ << "#define " << guard << endl
+ << endl;
+
+ // Copy prologue.
+ //
+ append_logue (hxx,
+ db,
+ ops.hxx_prologue (),
+ ops.hxx_prologue_file (),
+ "// Begin prologue.\n//",
+ "//\n// End prologue.");
+
+ // Version check (see similar check in odb.cxx for background).
+ //
+ hxx << "#include <odb/version.hxx>" << endl
+ << 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;
+
+ hxx << "#include <odb/pre.hxx>" << endl
+ << endl;
+
+ // Include main file(s).
+ //
+ for (paths::const_iterator i (inputs.begin ()); i != inputs.end (); ++i)
+ hxx << "#include " <<
+ ctx->process_include_path (i->leaf ().string ()) << endl;
+
+ hxx << endl;
+
+ // There are no -odb.hxx includes if we are generating code for
+ // everything.
+ //
+ if (!ops.at_once ())
+ if (include::generate (true))
+ hxx << endl;
+
+ {
+ // We don't want to indent prologues/epilogues.
+ //
+ ind_filter ind (ctx->os);
+
+ switch (db)
+ {
+ case database::common:
+ {
+ header::generate ();
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ if (md == multi_database::disabled)
+ header::generate ();
+ else
+ {
+ string n (base +
+ ops.odb_file_suffix ()[database::common] +
+ ops.hxx_suffix ());
+
+ ctx->os << "#include " << ctx->process_include_path (n) << endl
+ << endl;
+ }
+
+ relational::header::generate ();
+ break;
+ }
+ }
+ }
+
+ hxx << "#include " << ctx->process_include_path (ixx_name) << endl
+ << endl;
+
+ hxx << "#include <odb/post.hxx>" << endl
+ << endl;
+
+ // Copy epilogue.
+ //
+ append_logue (hxx,
+ db,
+ ops.hxx_epilogue (),
+ ops.hxx_epilogue_file (),
+ "// Begin epilogue.\n//",
+ "//\n// End epilogue.");
+
+ hxx << "#endif // " << guard << endl;
+
+ if (ops.show_sloc ())
+ cerr << hxx_name << ": " << sloc.stream ().count () << endl;
+
+ sloc_total += sloc.stream ().count ();
+ }
+
+ // IXX
+ //
+ if (gen_cxx)
+ {
+ unique_ptr<context> ctx (
+ create_context (ixx, unit, ops, fts, model.get ()));
+
+ sloc_filter sloc (ctx->os);
+
+ // Copy prologue.
+ //
+ append_logue (ixx,
+ db,
+ ops.ixx_prologue (),
+ ops.ixx_prologue_file (),
+ "// Begin prologue.\n//",
+ "//\n// End prologue.");
+
+ {
+ // We don't want to indent prologues/epilogues.
+ //
+ ind_filter ind (ctx->os);
+
+ switch (db)
+ {
+ case database::common:
+ {
+ inline_::generate ();
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ if (md == multi_database::disabled)
+ inline_::generate ();
+
+ relational::inline_::generate ();
+ break;
+ }
+ }
+ }
+
+ // Copy epilogue.
+ //
+ append_logue (ixx,
+ db,
+ ops.ixx_epilogue (),
+ ops.ixx_epilogue_file (),
+ "// Begin epilogue.\n//",
+ "//\n// End epilogue.");
+
+ if (ops.show_sloc ())
+ cerr << ixx_name << ": " << sloc.stream ().count () << endl;
+
+ sloc_total += sloc.stream ().count ();
+ }
+
+ // CXX
+ //
+ if (gen_cxx && (db != database::common || md == multi_database::dynamic))
+ {
+ unique_ptr<context> ctx (
+ create_context (cxx, unit, ops, fts, model.get ()));
+
+ sloc_filter sloc (ctx->os);
+
+ // Copy prologue.
+ //
+ append_logue (cxx,
+ db,
+ ops.cxx_prologue (),
+ ops.cxx_prologue_file (),
+ "// Begin prologue.\n//",
+ "//\n// End prologue.");
+
+ cxx << "#include <odb/pre.hxx>" << endl
+ << endl;
+
+ // Include query columns implementations for explicit instantiations.
+ //
+ string impl_guard;
+ if (md == multi_database::dynamic && ctx->ext.empty ())
+ {
+ impl_guard = ctx->make_guard (
+ "ODB_" + db.string () + "_QUERY_COLUMNS_DEF");
+
+ cxx << "#define " << impl_guard << endl;
+ }
+
+ cxx << "#include " << ctx->process_include_path (hxx_name) << endl;
+
+ // There are no -odb.hxx includes if we are generating code for
+ // everything.
+ //
+ if (!ops.at_once ())
+ include::generate (false);
+
+ if (!impl_guard.empty ())
+ cxx << "#undef " << impl_guard << endl;
+
+ cxx << endl;
+
+ {
+ // We don't want to indent prologues/epilogues.
+ //
+ ind_filter ind (ctx->os);
+
+ switch (db)
+ {
+ case database::common:
+ {
+ // Dynamic multi-database support.
+ //
+ source::generate ();
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ relational::source::generate ();
+
+ if (gen_schema &&
+ ops.schema_format ()[db].count (schema_format::embedded))
+ relational::schema::generate_source (changelog.get ());
+
+ break;
+ }
+ }
+ }
+
+ cxx << "#include <odb/post.hxx>" << endl;
+
+ // Copy epilogue.
+ //
+ append_logue (cxx,
+ db,
+ ops.cxx_epilogue (),
+ ops.cxx_epilogue_file (),
+ "// Begin epilogue.\n//",
+ "//\n// End epilogue.");
+
+ if (ops.show_sloc ())
+ cerr << cxx_name << ": " << sloc.stream ().count () << endl;
+
+ sloc_total += sloc.stream ().count ();
+ }
+
+ // SCH
+ //
+ if (gen_sep_schema)
+ {
+ unique_ptr<context> ctx (
+ create_context (sch, unit, ops, fts, model.get ()));
+
+ sloc_filter sloc (ctx->os);
+
+ // Copy prologue.
+ //
+ append_logue (sch,
+ db,
+ ops.schema_prologue (),
+ ops.schema_prologue_file (),
+ "// Begin prologue.\n//",
+ "//\n// End prologue.");
+
+ sch << "#include <odb/pre.hxx>" << endl
+ << endl;
+
+ sch << "#include <odb/database.hxx>" << endl
+ << "#include <odb/schema-catalog-impl.hxx>" << endl
+ << endl
+ << "#include <odb/details/unused.hxx>" << endl
+ << endl;
+
+ {
+ // We don't want to indent prologues/epilogues.
+ //
+ ind_filter ind (ctx->os);
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ relational::schema::generate_source (changelog.get ());
+ break;
+ }
+ case database::common:
+ assert (false);
+ }
+ }
+
+ sch << "#include <odb/post.hxx>" << endl;
+
+ // Copy epilogue.
+ //
+ append_logue (sch,
+ db,
+ ops.schema_epilogue (),
+ ops.schema_epilogue_file (),
+ "// Begin epilogue.\n//",
+ "//\n// End epilogue.");
+
+ if (ops.show_sloc ())
+ cerr << sch_name << ": " << sloc.stream ().count () << endl;
+
+ sloc_total += sloc.stream ().count ();
+ }
+
+ // SQL
+ //
+ if (gen_sql_schema)
+ {
+ unique_ptr<context> ctx (
+ create_context (sql, unit, ops, fts, model.get ()));
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ // Prologue.
+ //
+ relational::schema::generate_prologue ();
+ append_logue (sql,
+ db,
+ ops.sql_prologue (),
+ ops.sql_prologue_file (),
+ "/* Begin prologue.\n */",
+ "/*\n * End prologue. */");
+
+ if (!ops.omit_drop ())
+ relational::schema::generate_drop ();
+
+ // Interlude.
+ //
+ append_logue (sql,
+ db,
+ ops.sql_interlude (),
+ ops.sql_interlude_file (),
+ "/* Begin interlude.\n */",
+ "/*\n * End interlude. */");
+
+ if (!ops.omit_create ())
+ relational::schema::generate_create ();
+
+ // Epilogue.
+ //
+ append_logue (sql,
+ db,
+ ops.sql_epilogue (),
+ ops.sql_epilogue_file (),
+ "/* Begin epilogue.\n */",
+ "/*\n * End epilogue. */");
+ relational::schema::generate_epilogue ();
+
+ break;
+ }
+ case database::common:
+ assert (false);
+ }
+ }
+
+ // MIG
+ //
+ if (gen_sql_migration)
+ {
+ for (ofstreams::size_type i (0); i < mig_pre.size (); ++i)
+ {
+ sema_rel::changeset& cs (
+ changelog->contains_changeset_at (i).changeset ());
+
+ // pre
+ //
+ {
+ ofstream& mig (*mig_pre[i]);
+ unique_ptr<context> ctx (create_context (mig, unit, ops, fts, 0));
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ // Prologue.
+ //
+ relational::schema::generate_prologue ();
+ append_logue (mig,
+ db,
+ ops.migration_prologue (),
+ ops.migration_prologue_file (),
+ "/* Begin prologue.\n */",
+ "/*\n * End prologue. */");
+
+ relational::schema::generate_migrate_pre (cs);
+
+ // Epilogue.
+ //
+ append_logue (mig,
+ db,
+ ops.migration_epilogue (),
+ ops.migration_epilogue_file (),
+ "/* Begin epilogue.\n */",
+ "/*\n * End epilogue. */");
+ relational::schema::generate_epilogue ();
+
+ break;
+ }
+ case database::common:
+ assert (false);
+ }
+ }
+
+ // post
+ //
+ {
+ ofstream& mig (*mig_post[i]);
+ unique_ptr<context> ctx (create_context (mig, unit, ops, fts, 0));
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ // Prologue.
+ //
+ relational::schema::generate_prologue ();
+ append_logue (mig,
+ db,
+ ops.migration_prologue (),
+ ops.migration_prologue_file (),
+ "/* Begin prologue.\n */",
+ "/*\n * End prologue. */");
+
+ relational::schema::generate_migrate_post (cs);
+
+ // Epilogue.
+ //
+ append_logue (mig,
+ db,
+ ops.migration_epilogue (),
+ ops.migration_epilogue_file (),
+ "/* Begin epilogue.\n */",
+ "/*\n * End epilogue. */");
+ relational::schema::generate_epilogue ();
+
+ break;
+ }
+ case database::common:
+ assert (false);
+ }
+ }
+ }
+ }
+
+ // Save the changelog if it has changed.
+ //
+ if (gen_changelog)
+ {
+ try
+ {
+ ostringstream os;
+ os.exceptions (ifstream::badbit | ifstream::failbit);
+ xml::serializer s (os, out_log_path.string ());
+ changelog->serialize (s);
+ string const& changelog_xml (os.str ());
+
+ if (changelog_xml != old_changelog_xml)
+ {
+ ofstream log;
+ open (log, out_log_path, ios_base::binary);
+
+ if (old_changelog == 0)
+ auto_rm.add (out_log_path);
+
+ log.exceptions (ifstream::badbit | ifstream::failbit);
+ log << changelog_xml;
+ }
+ }
+ catch (const ios_base::failure& e)
+ {
+ cerr << out_log_path << ": write failure" << endl;
+ throw generator_failed ();
+ }
+ catch (const xml::serialization& e)
+ {
+ cerr << e.what () << endl;
+ throw generator_failed ();
+ }
+ }
+
+ // Communicate the sloc count to the driver. This is necessary to
+ // correctly handle the total if we are compiling multiple files in
+ // one invocation.
+ //
+ if (ops.show_sloc () || ops.sloc_limit_specified ())
+ cout << "odb:sloc:" << sloc_total << endl;
+
+ auto_rm.cancel ();
+ }
+ catch (operation_failed const&)
+ {
+ // Code generation failed. Diagnostics has already been issued.
+ //
+ throw generator_failed ();
+ }
+ catch (semantics::invalid_path const& e)
+ {
+ cerr << "error: '" << e.path () << "' is not a valid filesystem path"
+ << endl;
+ throw generator_failed ();
+ }
+ catch (fs::error const&)
+ {
+ // Auto-removal of generated files failed. Ignore it.
+ //
+ throw generator_failed ();
+ }
+}
diff --git a/odb/generator.hxx b/odb/odb/generator.hxx
index 205043b..205043b 100644
--- a/odb/generator.hxx
+++ b/odb/odb/generator.hxx
diff --git a/odb/odb/header.cxx b/odb/odb/header.cxx
new file mode 100644
index 0000000..dacdd1d
--- /dev/null
+++ b/odb/odb/header.cxx
@@ -0,0 +1,902 @@
+// file : odb/header.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/common.hxx>
+#include <odb/context.hxx>
+#include <odb/generate.hxx>
+
+using namespace std;
+
+namespace header
+{
+ struct class1: traversal::class_, virtual context
+ {
+ class1 ()
+ : typedefs_ (false),
+ query_columns_type_ (false, true, false),
+ pointer_query_columns_type_ (true, true, false)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ default: break;
+ }
+ }
+
+ void
+ traverse_object (type&);
+
+ void
+ traverse_view (type&);
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<query_columns_type> query_columns_type_;
+ instance<query_columns_type> pointer_query_columns_type_;
+ };
+}
+
+void header::class1::
+traverse_object (type& c)
+{
+ using semantics::data_member;
+
+ data_member_path* id (id_member (c));
+ data_member* idf (id ? id->front () : 0);
+ bool auto_id (id && auto_ (*id));
+ bool base_id (id && &idf->scope () != &c); // Comes from base.
+
+ data_member* opt (context::optimistic (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+ type* poly_base (poly_derived ? &polymorphic_base (c) : 0);
+ data_member* discriminator (poly ? context::discriminator (*poly_root) : 0);
+
+ bool abst (abstract (c));
+ bool reuse_abst (abst && !poly);
+
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ string const& type (class_fq_name (c));
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ // class_traits
+ //
+ os << "template <>" << endl
+ << "struct class_traits< " << type << " >"
+ << "{"
+ << "static const class_kind kind = class_object;"
+ << "};";
+
+ // object_traits
+ //
+ os << "template <>" << endl
+ << "class " << exp << "access::object_traits< " << type << " >"
+ << "{"
+ << "public:" << endl;
+
+ // object_type & pointer_type
+ //
+ os << "typedef " << type << " object_type;"
+ << "typedef " << c.get<string> ("object-pointer") << " pointer_type;"
+ << "typedef odb::pointer_traits<pointer_type> pointer_traits;"
+ << endl;
+
+ // polymorphic, root_type, base_type, etc.
+ //
+ os << "static const bool polymorphic = " << poly << ";"
+ << endl;
+
+ if (poly)
+ {
+ os << "typedef " << class_fq_name (*poly_root) << " root_type;";
+
+ if (poly_derived)
+ {
+ os << "typedef " << class_fq_name (*poly_base) << " base_type;"
+ << "typedef object_traits<root_type>::discriminator_type " <<
+ "discriminator_type;"
+ << "typedef polymorphic_concrete_info<root_type> info_type;";
+
+ if (abst)
+ os << "typedef polymorphic_abstract_info<root_type> " <<
+ "abstract_info_type;";
+
+ // Calculate our hierarchy depth (number of classes).
+ //
+ size_t depth (polymorphic_depth (c));
+
+ os << endl
+ << "static const std::size_t depth = " << depth << "UL;";
+ }
+ else
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (*discriminator, hint));
+
+ os << "typedef " << t.fq_name (hint) << " discriminator_type;"
+ << "typedef polymorphic_map<object_type> map_type;"
+ << "typedef polymorphic_concrete_info<object_type> info_type;";
+
+ if (abst)
+ os << "typedef polymorphic_abstract_info<object_type> " <<
+ "abstract_info_type;";
+
+ os << endl
+ << "static const std::size_t depth = 1UL;";
+ }
+
+ os << endl;
+ }
+
+ // id_type, version_type, etc.
+ //
+ if (id != 0)
+ {
+ if (base_id)
+ {
+ semantics::class_& b (dynamic_cast<semantics::class_&> (idf->scope ()));
+ string const& type (class_fq_name (b));
+
+ os << "typedef object_traits< " << type << " >::id_type id_type;";
+
+ if (opt != 0)
+ os << "typedef object_traits< " << type << " >::version_type " <<
+ "version_type;";
+
+ os << endl;
+
+ if (poly_derived)
+ os << "static const bool auto_id = false;";
+ else
+ os << "static const bool auto_id = object_traits< " << type <<
+ " >::auto_id;";
+ }
+ else
+ {
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (*id, hint));
+ os << "typedef " << t.fq_name (hint) << " id_type;";
+ }
+
+ if (opt != 0)
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (*opt, hint));
+ os << "typedef " << t.fq_name (hint) << " version_type;";
+ }
+
+ os << endl
+ << "static const bool auto_id = " << auto_id << ";";
+ }
+
+ os << endl;
+ }
+ else if (!reuse_abst)
+ {
+ // Object without id.
+ //
+ os << "typedef void id_type;"
+ << endl
+ << "static const bool auto_id = false;"
+ << endl;
+ }
+
+ // abstract
+ //
+ os << "static const bool abstract = " << abst << ";"
+ << endl;
+
+ // id()
+ //
+ if (id != 0 || !reuse_abst)
+ {
+ // We want to generate a dummy void id() accessor even if this
+ // object has no id to help us in the runtime. This way we can
+ // write generic code that will work for both void and non-void
+ // ids.
+ //
+ os << "static id_type" << endl
+ << "id (const object_type&);"
+ << endl;
+ }
+
+ // version()
+ //
+ if (opt != 0)
+ {
+ os << "static version_type" << endl
+ << "version (const object_type&);"
+ << endl;
+ }
+
+ // Query.
+ //
+ if (options.generate_query ())
+ {
+ // Generate object pointer tags here if we are generating dynamic
+ // multi-database support.
+ //
+ if (multi_dynamic && has_a (c, test_pointer | exclude_base))
+ {
+ query_tags t;
+ t.traverse (c);
+ }
+ }
+
+ // The rest does not apply to reuse-abstract objects.
+ //
+ if (!reuse_abst)
+ {
+ // Cache traits typedefs.
+ //
+ if (id == 0)
+ {
+ os << "typedef" << endl
+ << "no_id_pointer_cache_traits<pointer_type>" << endl
+ << "pointer_cache_traits;"
+ << endl
+ << "typedef" << endl
+ << "no_id_reference_cache_traits<object_type>" << endl
+ << "reference_cache_traits;"
+ << endl;
+ }
+ else
+ {
+ char const* obj (poly_derived ? "root_type" : "object_type");
+ char const* ptr (poly_derived
+ ? "object_traits<root_type>::pointer_type"
+ : "pointer_type");
+ if (session (c))
+ {
+ string const& s (options.session_type ());
+
+ os << "typedef" << endl
+ << "odb::pointer_cache_traits<" << endl
+ << " " << ptr << "," << endl
+ << " " << s << " >" << endl
+ << "pointer_cache_traits;"
+ << endl
+ << "typedef" << endl
+ << "odb::reference_cache_traits<" << endl
+ << " " << obj << "," << endl
+ << " " << s << " >" << endl
+ << "reference_cache_traits;"
+ << endl;
+ }
+ else
+ {
+ os << "typedef" << endl
+ << "no_op_pointer_cache_traits<" << ptr << ">" << endl
+ << "pointer_cache_traits;"
+ << endl
+ << "typedef" << endl
+ << "no_op_reference_cache_traits<" << obj << ">" << endl
+ << "reference_cache_traits;"
+ << endl;
+ }
+ }
+
+ // callback ()
+ //
+ os << "static void" << endl
+ << "callback (database&, object_type&, callback_event);"
+ << endl;
+
+ os << "static void" << endl
+ << "callback (database&, const object_type&, callback_event);"
+ << endl;
+ }
+
+ os << "};";
+
+ // The rest only applies to dynamic milti-database support.
+ //
+ if (!multi_dynamic)
+ return;
+
+ // pointer_query_columns & query_columns
+ //
+ if (options.generate_query ())
+ {
+ // If we don't have object pointers, then also generate
+ // query_columns (in this case pointer_query_columns and
+ // query_columns are the same and the former inherits from
+ // the latter). Otherwise we have to postpone query_columns
+ // generation until the second pass to deal with forward-
+ // declared objects.
+ //
+ if (!has_a (c, test_pointer | include_base))
+ query_columns_type_->traverse (c);
+
+ pointer_query_columns_type_->traverse (c);
+ }
+
+ // object_traits_impl
+ //
+ os << "template <>" << endl
+ << "class " << exp << "access::object_traits_impl< " << type << ", " <<
+ "id_common >:" << endl
+ << " public access::object_traits< " << type << " >"
+ << "{";
+
+ // We don't need to generate anything else for reuse-abstract objects.
+ //
+ if (reuse_abst)
+ {
+ os << "};";
+ return;
+ }
+
+ os << "public:" << endl;
+
+ if (options.generate_query ())
+ {
+ // base_traits is needed for query support.
+ //
+ if (poly_derived)
+ os << "typedef object_traits_impl<base_type, id_common> base_traits;"
+ << endl;
+
+ // query_base_type
+ //
+ os << "typedef odb::query_base query_base_type;"
+ << endl;
+ }
+
+ // function_table_type
+ //
+ os << "struct function_table_type"
+ << "{";
+
+ // persist ()
+ //
+ os << "void (*persist) (database&, " << (auto_id ? "" : "const ") <<
+ "object_type&" << (poly ? ", bool, bool" : "") << ");";
+
+ if (id != 0)
+ {
+ // find (id)
+ //
+ if (c.default_ctor ())
+ os << "pointer_type (*find1) (database&, const id_type&);";
+
+ // find (id, obj)
+ //
+ os << "bool (*find2) (database&, const id_type&, object_type&" <<
+ (poly ? ", bool" : "") << ");";
+
+ // reload ()
+ //
+ os << "bool (*reload) (database&, object_type&" <<
+ (poly ? ", bool" : "") << ");";
+
+ // update ()
+ //
+ if (!readonly (c) || poly)
+ {
+ os << "void (*update) (database&, const object_type&" <<
+ (poly ? ", bool, bool" : "") << ");";
+ }
+
+ // erase ()
+ //
+ os << "void (*erase1) (database&, const id_type&" <<
+ (poly ? ", bool, bool" : "") << ");";
+
+ os << "void (*erase2) (database&, const object_type&" <<
+ (poly ? ", bool, bool" : "") << ");";
+
+ // Sections.
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ os << "bool (*load_section) (connection&, object_type&, section&" <<
+ (poly ? ", const info_type*" : "") << ");";
+
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ os << "bool (*update_section) (connection&, const object_type&, " <<
+ "const section&" << (poly ? ", const info_type*" : "") << ");";
+ }
+
+ if (options.generate_query ())
+ {
+ if (!options.omit_unprepared ())
+ os << "result<object_type> (*query) (database&, const query_base_type&);";
+
+ os << "unsigned long long (*erase_query) (database&, " <<
+ "const query_base_type&);";
+
+ if (options.generate_prepared ())
+ {
+ os << "odb::details::shared_ptr<prepared_query_impl> " <<
+ "(*prepare_query) (connection&, const char*, const query_base_type&);";
+
+ os << "odb::details::shared_ptr<result_impl> (*execute_query) ("
+ "prepared_query_impl&);";
+ }
+ }
+
+ os << "};" // function_table_type
+ << "static const function_table_type* function_table[database_count];"
+ << endl;
+
+ //
+ // Forwarding functions.
+ //
+
+ // persist ()
+ //
+ os << "static void" << endl
+ << "persist (database&, " << (auto_id ? "" : "const ") << "object_type&);"
+ << endl;
+
+ if (id != 0)
+ {
+ // find (id)
+ //
+ if (c.default_ctor ())
+ os << "static pointer_type" << endl
+ << "find (database&, const id_type&);"
+ << endl;
+
+ // find (id, obj)
+ //
+ os << "static bool" << endl
+ << "find (database&, const id_type&, object_type&);"
+ << endl;
+
+ // reload ()
+ //
+ os << "static bool" << endl
+ << "reload (database&, object_type&);"
+ << endl;
+
+ // update ()
+ //
+ if (!readonly (c) || poly)
+ {
+ os << "static void" << endl
+ << "update (database&, const object_type&);"
+ << endl;
+ }
+
+ // erase ()
+ //
+ os << "static void" << endl
+ << "erase (database&, const id_type&);"
+ << endl;
+
+ os << "static void" << endl
+ << "erase (database&, const object_type&);"
+ << endl;
+
+ // Sections.
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ os << "static bool" << endl
+ << "load (connection&, object_type&, section&);"
+ << endl;
+
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ os << "static bool" << endl
+ << "update (connection&, const object_type&, const section&);"
+ << endl;
+ }
+
+ if (options.generate_query ())
+ {
+ if (!options.omit_unprepared ())
+ {
+ os << "static result<object_type>" << endl
+ << "query (database&, const query_base_type&);"
+ << endl;
+ }
+
+ os << "static unsigned long long" << endl
+ << "erase_query (database&, const query_base_type&);"
+ << endl;
+
+ if (options.generate_prepared ())
+ {
+ os << "static odb::details::shared_ptr<prepared_query_impl>" << endl
+ << "prepare_query (connection&, const char*, const query_base_type&);"
+ << endl;
+
+ os << "static odb::details::shared_ptr<result_impl>" << endl
+ << "execute_query (prepared_query_impl&);"
+ << endl;
+ }
+ }
+
+ os << "};"; // object_traits_impl
+}
+
+void header::class1::
+traverse_view (type& c)
+{
+ string const& type (class_fq_name (c));
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ // class_traits
+ //
+ os << "template <>" << endl
+ << "struct class_traits< " << type << " >"
+ << "{"
+ << "static const class_kind kind = class_view;"
+ << "};";
+
+ // view_traits
+ //
+ os << "template <>" << endl
+ << "class " << exp << "access::view_traits< " << type << " >"
+ << "{"
+ << "public:" << endl;
+
+ // view_type & pointer_type
+ //
+ os << "typedef " << type << " view_type;"
+ << "typedef " << c.get<string> ("object-pointer") << " pointer_type;"
+ << endl;
+
+ // Generate associated object tags here if we are generating dynamic
+ // multi-database support.
+ //
+ if (multi_dynamic)
+ {
+ query_tags t;
+ t.traverse (c);
+ }
+
+ // callback ()
+ //
+ os << "static void" << endl
+ << "callback (database&, view_type&, callback_event);"
+ << endl;
+
+ os << "};";
+
+ // The rest only applies to dynamic milti-database support.
+ //
+ if (!multi_dynamic)
+ return;
+
+ size_t obj_count (c.get<size_t> ("object-count"));
+
+ // view_traits_impl
+ //
+ os << "template <>" << endl
+ << "class " << exp << "access::view_traits_impl< " << type << ", " <<
+ "id_common >:" << endl
+ << " public access::view_traits< " << type << " >"
+ << "{"
+ << "public:" << endl;
+
+ // query_base_type and query_columns (definition generated by class2).
+ //
+ os << "typedef odb::query_base query_base_type;"
+ << "struct query_columns";
+
+ if (obj_count == 0)
+ os << "{"
+ << "};";
+ else
+ os << ";"
+ << endl;
+
+ // function_table_type
+ //
+ os << "struct function_table_type"
+ << "{";
+
+ if (!options.omit_unprepared ())
+ os << "result<view_type> (*query) (database&, const query_base_type&);"
+ << endl;
+
+ if (options.generate_prepared ())
+ {
+ os << "odb::details::shared_ptr<prepared_query_impl> " <<
+ "(*prepare_query) (connection&, const char*, const query_base_type&);"
+ << endl;
+
+ os << "odb::details::shared_ptr<result_impl> (*execute_query) ("
+ "prepared_query_impl&);"
+ << endl;
+ }
+
+ os << "};" // function_table_type
+ << "static const function_table_type* function_table[database_count];"
+ << endl;
+
+ //
+ // Forwarding functions.
+ //
+
+ if (!options.omit_unprepared ())
+ os << "static result<view_type>" << endl
+ << "query (database&, const query_base_type&);"
+ << endl;
+
+ if (options.generate_prepared ())
+ {
+ os << "static odb::details::shared_ptr<prepared_query_impl>" << endl
+ << "prepare_query (connection&, const char*, const query_base_type&);"
+ << endl;
+
+ os << "static odb::details::shared_ptr<result_impl>" << endl
+ << "execute_query (prepared_query_impl&);"
+ << endl;
+ }
+
+ os << "};";
+}
+
+namespace header
+{
+ struct class2: traversal::class_, virtual context
+ {
+ class2 ()
+ : typedefs_ (false),
+ query_columns_type_ (false, true, false),
+ query_columns_type_inst_ (false, false, true),
+ view_query_columns_type_ (true)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ default: break;
+ }
+ }
+
+ void
+ traverse_object (type&);
+
+ void
+ traverse_view (type&);
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<query_columns_type> query_columns_type_;
+ instance<query_columns_type> query_columns_type_inst_;
+ instance<view_query_columns_type> view_query_columns_type_;
+ };
+}
+
+void header::class2::
+traverse_object (type& c)
+{
+ if (options.generate_query ())
+ {
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ // query_columns
+ //
+ // If we don't have any pointers, then query_columns is generated
+ // in pass 1 (see the comment in class1 for details).
+ //
+ if (has_a (c, test_pointer | include_base))
+ query_columns_type_->traverse (c);
+
+ // Generate extern template declarations.
+ //
+ query_columns_type_inst_->traverse (c);
+ }
+
+ // Move header comment out of if-block if adding any code here.
+}
+
+void header::class2::
+traverse_view (type& c)
+{
+ // query_columns
+ //
+ if (c.get<size_t> ("object-count") != 0)
+ {
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ view_query_columns_type_->traverse (c);
+ }
+
+ // Move header comment out of if-block if adding any code here.
+}
+
+namespace header
+{
+ void
+ generate ()
+ {
+ context ctx;
+ ostream& os (ctx.os);
+
+ os << "#include <memory>" << endl
+ << "#include <cstddef>" << endl; // std::size_t
+
+ if (ctx.features.polymorphic_object)
+ os << "#include <string>" << endl; // For discriminator.
+
+ if (ctx.options.std () >= cxx_version::cxx11)
+ os << "#include <utility>" << endl; // move()
+
+ os << endl;
+
+ os << "#include <odb/core.hxx>" << endl
+ << "#include <odb/traits.hxx>" << endl
+ << "#include <odb/callback.hxx>" << endl
+ << "#include <odb/wrapper-traits.hxx>" << endl
+ << "#include <odb/pointer-traits.hxx>" << endl;
+
+ // @@ 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
+ // between the boost::shared_ptr and std::tr1::shared_ptr usage since
+ // the latter is just a using-declaration for the former. To resolve
+ // this we will include TR1 traits if the Boost TR1 header is included.
+ //
+ if (ctx.features.tr1_pointer)
+ {
+ os << "#include <odb/tr1/wrapper-traits.hxx>" << endl
+ << "#include <odb/tr1/pointer-traits.hxx>" << endl;
+ }
+ else if (ctx.features.boost_pointer)
+ {
+ os << "#ifdef BOOST_TR1_MEMORY_HPP_INCLUDED" << endl
+ << "# include <odb/tr1/wrapper-traits.hxx>" << endl
+ << "# include <odb/tr1/pointer-traits.hxx>" << endl
+ << "#endif" << endl;
+ }
+ }
+#endif
+
+ os << "#include <odb/container-traits.hxx>" << endl;
+
+ if (ctx.features.session_object)
+ {
+ if (ctx.options.session_type () == "odb::session")
+ os << "#include <odb/session.hxx>" << endl;
+
+ os << "#include <odb/cache-traits.hxx>" << endl;
+ }
+ else
+ os << "#include <odb/no-op-cache-traits.hxx>" << endl;
+
+ if (ctx.features.polymorphic_object)
+ os << "#include <odb/polymorphic-info.hxx>" << endl;
+
+ if (ctx.options.generate_query ())
+ {
+ if (ctx.multi_dynamic)
+ os << "#include <odb/query-dynamic.hxx>" << endl;
+
+ if (ctx.options.generate_prepared ())
+ os << "#include <odb/prepared-query.hxx>" << endl;
+
+ os << "#include <odb/result.hxx>" << endl;
+
+ if (ctx.features.simple_object)
+ os << "#include <odb/simple-object-result.hxx>" << endl;
+
+ if (ctx.features.polymorphic_object)
+ os << "#include <odb/polymorphic-object-result.hxx>" << endl;
+
+ if (ctx.features.no_id_object)
+ os << "#include <odb/no-id-object-result.hxx>" << endl;
+
+ if (ctx.features.view)
+ os << "#include <odb/view-image.hxx>" << endl
+ << "#include <odb/view-result.hxx>" << endl;
+ }
+
+ os << endl
+ << "#include <odb/details/unused.hxx>" << endl;
+
+ if (ctx.options.generate_query ())
+ os << "#include <odb/details/shared-ptr.hxx>" << endl;
+
+ os << endl;
+
+ os << "namespace odb"
+ << "{";
+
+ // Generate common code.
+ //
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ class1 c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx.unit);
+ }
+
+ if (ctx.multi_dynamic)
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ class2 c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx.unit);
+ }
+
+ os << "}";
+ }
+}
diff --git a/odb/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/odb/location.hxx b/odb/odb/location.hxx
new file mode 100644
index 0000000..cc59196
--- /dev/null
+++ b/odb/odb/location.hxx
@@ -0,0 +1,25 @@
+// file : odb/location.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_LOCATION_HXX
+#define ODB_LOCATION_HXX
+
+#include <odb/gcc-fwd.hxx>
+
+#include <cstddef>
+#include <libcutl/fs/path.hxx>
+
+struct location
+{
+ location (location_t);
+ location (cutl::fs::path const& f, std::size_t l, std::size_t c)
+ : file (f), line (l), column (c)
+ {
+ }
+
+ cutl::fs::path file;
+ std::size_t line;
+ std::size_t column;
+};
+
+#endif // ODB_LOCATION_HXX
diff --git a/odb/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/odb.cxx b/odb/odb/odb.cxx
new file mode 100644
index 0000000..9899262
--- /dev/null
+++ b/odb/odb/odb.cxx
@@ -0,0 +1,2188 @@
+// file : odb/odb.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <errno.h>
+#include <stdlib.h> // getenv, setenv
+#include <string.h> // strerror, memset
+#include <unistd.h> // stat, close
+#include <sys/types.h> // stat
+#include <sys/stat.h> // stat
+
+// Process.
+//
+#ifndef _WIN32
+# include <unistd.h> // execvp, fork, dup2, pipe, {STDIN,STDERR}_FILENO
+# include <sys/types.h> // waitpid
+# include <sys/wait.h> // waitpid
+#else
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include <windows.h> // CreatePipe, CreateProcess, GetTemp*, MAX_PATH
+# include <io.h> // _open_osfhandle
+# include <fcntl.h> // _O_TEXT
+#endif
+
+#include <string>
+#include <vector>
+#include <cstddef> // size_t
+#include <sstream>
+#include <fstream>
+#include <iostream>
+#include <ext/stdio_filebuf.h>
+
+#include <libcutl/fs/path.hxx>
+#include <libcutl/fs/auto-remove.hxx>
+
+#include <odb/version.hxx>
+#include <odb/options.hxx>
+#include <odb/profile.hxx>
+
+#ifdef HAVE_CONFIG_H
+# include <odb/config.h>
+#endif
+
+using namespace std;
+using cutl::fs::path;
+using cutl::fs::invalid_path;
+using cutl::fs::auto_remove;
+
+typedef vector<string> strings;
+typedef vector<path> paths;
+
+//
+// Path manipulation.
+//
+
+// Escape backslashes in the path.
+//
+static string
+escape_path (string const&);
+
+// Search the PATH environment variable for the file.
+//
+static path
+path_search (path const&);
+
+// Driver path with the directory part (search PATH).
+//
+static path
+driver_path (path const& driver);
+
+#ifndef ODB_STATIC_PLUGIN
+static path
+plugin_path (path const& driver, string const& gxx);
+#endif
+
+//
+// Process manipulation.
+//
+struct process_info
+{
+#ifndef _WIN32
+ pid_t id;
+#else
+ HANDLE id;
+#endif
+
+ int out_fd; // Write to this fd to send to the new process' stdin.
+ int in_efd; // Read from this fd to receive from the new process' stderr.
+ int in_ofd; // Read from this fd to receive from the new process' stdout.
+};
+
+struct process_failure {};
+
+#ifdef _WIN32
+// Deal with Windows command line length limit.
+//
+static auto_remove
+fixup_cmd_line (vector<const char*>& args,
+ size_t start,
+ const char* name,
+ string& arg);
+#endif
+
+// Start another process using the specified command line. Connect the
+// newly created process' stdin to out_fd. Also if connect_* are true,
+// connect the created process' stdout and stderr to in_*fd. Issue
+// diagnostics and throw process_failure if anything goes wrong. The
+// name argument is the name of the current process for diagnostics.
+//
+static process_info
+start_process (char const* args[],
+ char const* name,
+ bool connect_stderr = false,
+ bool connect_stdout = false);
+
+// Wait for the process to terminate. Return true if the process terminated
+// normally and with the zero exit status. Issue diagnostics and throw
+// process_failure if anything goes wrong. The name argument is the name
+// of the current process for diagnostics.
+//
+static bool
+wait_process (process_info, char const* name);
+
+//
+//
+static string
+encode_plugin_flag (string const& k);
+
+static string
+encode_plugin_option (string const& k, string const& v);
+
+// Extract header search paths from GCC's -v output. May throw the
+// profile_failure, process_failure and invalid_path exceptions. Name
+// is the program name (argv[0]) for diagnostics.
+//
+static paths
+profile_paths (strings const& args, char const* name);
+
+static char const* const db_macro[] =
+{
+ "-DODB_DATABASE_COMMON",
+ "-DODB_DATABASE_MSSQL",
+ "-DODB_DATABASE_MYSQL",
+ "-DODB_DATABASE_ORACLE",
+ "-DODB_DATABASE_PGSQL",
+ "-DODB_DATABASE_SQLITE"
+};
+
+int
+main (int argc, char* argv[])
+{
+ ostream& e (cerr);
+
+ try
+ {
+ strings args, plugin_args;
+ bool v (false);
+
+ // The first argument points to the program name, which is
+ // g++ by default.
+ //
+#ifdef ODB_GXX_NAME
+ path gxx (ODB_GXX_NAME);
+
+ if (gxx.empty ())
+ {
+ e << argv[0] << ": error: embedded g++ compile name is empty" << endl;
+ return 1;
+ }
+
+ // If the g++ name is a relative path (starts with '.'), then use
+ // our own path as base.
+ //
+ if (gxx.string ()[0] == '.')
+ {
+ path dp (driver_path (path (argv[0])));
+ path d (dp.directory ());
+
+ if (!d.empty ())
+ gxx = d / gxx;
+ }
+
+ args.push_back (gxx.string ());
+
+ // Also modify LD_LIBRARY_PATH to include the lib path.
+ //
+#ifndef _WIN32
+ {
+#ifdef __APPLE__
+ char const name[] = "DYLD_LIBRARY_PATH";
+#else
+ char const name[] = "LD_LIBRARY_PATH";
+#endif
+
+ string ld_paths;
+
+ if (char const* s = getenv (name))
+ ld_paths = s;
+
+ path d (gxx.directory ());
+
+ if (!d.empty ())
+ {
+ d.complete ();
+ d /= path ("..") / path ("lib");
+
+ if (ld_paths.empty ())
+ ld_paths = d.string ();
+ else
+ ld_paths = d.string () + path::traits::path_separator + ld_paths;
+
+ if (setenv (name, ld_paths.c_str (), 1) != 0)
+ {
+ e << argv[0] << ": error: unable to update environment" << endl;
+ return 1;
+ }
+ }
+ }
+#endif // _WIN32
+
+#else
+ args.push_back ("g++");
+#endif // ODB_GXX_NAME
+
+ // Default options.
+ //
+ args.push_back ("-x");
+ args.push_back ("c++");
+ args.push_back (""); // Reserve space for -std=c++XX.
+ args.push_back ("-S");
+ args.push_back ("-Wunknown-pragmas");
+ args.push_back ("-Wno-deprecated");
+ args.push_back (""); // Reserve space for -fplugin=path.
+
+ // Parse the default options file if we have one.
+ //
+ strings def_inc_dirs;
+ strings def_defines;
+#ifdef ODB_DEFAULT_OPTIONS_FILE
+ {
+ path file (ODB_DEFAULT_OPTIONS_FILE);
+
+ // If the path is relative, then use the driver's path as a base. If
+ // the file is not found in that directory, then also try outer
+ // directory (so that we can find /etc if driver is in /usr/bin).
+ //
+ if (file.relative ())
+ {
+ bool found (false);
+ path dd (driver_path (path (argv[0])).directory ());
+
+ for (path d (dd);; d = d.directory ())
+ {
+ path f (d / file);
+ // Check that the file exist without checking for permissions, etc.
+ //
+ struct stat s;
+ if (stat (f.string ().c_str (), &s) == 0 && S_ISREG (s.st_mode))
+ {
+ file = f;
+ found = true;
+ break;
+ }
+
+ if (d.root ())
+ break;
+ }
+
+ if (!found)
+ file = dd / file; // For diagnostics.
+ }
+
+ cli::argv_file_scanner s (file.string ());
+
+ bool first_x (true);
+
+ while (s.more ())
+ {
+ string a (s.next ());
+ size_t n (a.size ());
+
+ // -x
+ //
+ if (a == "-x")
+ {
+ if (!s.more () || (a = s.next ()).empty ())
+ {
+ e << file << ": error: expected argument for the " << a
+ << " option" << endl;
+ return 1;
+ }
+
+ if (first_x)
+ {
+ first_x = false;
+
+ // If it doesn't start with '-', then it must be the g++
+ // executable name. Update the first argument with it.
+ //
+ if (a[0] != '-')
+ args[0] = a;
+ else
+ args.push_back (a);
+ }
+ else
+ args.push_back (a);
+ }
+ // -I
+ //
+ else if (n > 1 && a[0] == '-' && a[1] == 'I')
+ {
+ def_inc_dirs.push_back (a);
+
+ if (n == 2) // -I /path
+ {
+ if (!s.more () || (a = s.next ()).empty ())
+ {
+ e << file << ": error: expected argument for the -I option"
+ << endl;
+ return 1;
+ }
+
+ def_inc_dirs.push_back (a);
+ }
+ }
+ // -isystem, -iquote, -idirafter, and -framework (Mac OS X)
+ //
+ else if (a == "-isystem" ||
+ a == "-iquote" ||
+ a == "-idirafter" ||
+ a == "-framework")
+ {
+ def_inc_dirs.push_back (a);
+
+ if (!s.more () || (a = s.next ()).empty ())
+ {
+ e << file << ": error: expected argument for the " << a
+ << " option" << endl;
+ return 1;
+ }
+
+ def_inc_dirs.push_back (a);
+ }
+ // -D
+ //
+ else if (n > 1 && a[0] == '-' && a[1] == 'D')
+ {
+ def_defines.push_back (a);
+
+ if (n == 2) // -D macro
+ {
+ if (!s.more () || (a = s.next ()).empty ())
+ {
+ e << file << ": error: expected argument for the -D option"
+ << endl;
+ return 1;
+ }
+
+ def_defines.push_back (a);
+ }
+ }
+ // -U
+ //
+ else if (n > 1 && a[0] == '-' && a[1] == 'U')
+ {
+ def_defines.push_back (a);
+
+ if (n == 2) // -U macro
+ {
+ if (!s.more () || (a = s.next ()).empty ())
+ {
+ e << file << ": error: expected argument for the -U option"
+ << endl;
+ return 1;
+ }
+
+ def_defines.push_back (a);
+ }
+ }
+ else
+ plugin_args.push_back (a);
+ }
+ }
+#endif
+
+ // Add the default preprocessor defines (-D/-U) before the user-supplied
+ // ones.
+ //
+ args.insert (args.end (), def_defines.begin (), def_defines.end ());
+
+ // Parse driver options.
+ //
+ // We scan expanding --options-file in order to allow specifying ad hoc
+ // options (-I, etc) in options files.
+ //
+ bool first_x (true);
+
+ for (cli::argv_file_scanner scan (argc, argv, "--options-file");
+ scan.more (); )
+ {
+ string a (scan.next ());
+ size_t n (a.size ());
+
+ // -v
+ //
+ if (a == "-v")
+ {
+ v = true;
+ args.push_back (a);
+ }
+ // -x
+ //
+ else if (a == "-x")
+ {
+ const char* v;
+ if (!scan.more () || (v = scan.next ())[0] == '\0')
+ {
+ e << argv[0] << ": error: expected argument for the -x option"
+ << endl;
+ return 1;
+ }
+
+ if (first_x)
+ {
+ first_x = false;
+
+ // If it doesn't start with '-', then it must be the g++
+ // executable name. Update the first argument with it.
+ //
+ if (v[0] != '-')
+ args[0] = v;
+ else
+ args.push_back (v);
+ }
+ else
+ args.push_back (v);
+ }
+ // -I
+ //
+ else if (n > 1 && a[0] == '-' && a[1] == 'I')
+ {
+ args.push_back (a);
+
+ if (n == 2) // -I /path
+ {
+ const char* v;
+ if (!scan.more () || (v = scan.next ())[0] == '\0')
+ {
+ e << argv[0] << ": error: expected argument for the -I option"
+ << endl;
+ return 1;
+ }
+
+ args.push_back (v);
+ }
+ }
+ // -isystem, -iquote, -idirafter, and -framework (Mac OS X)
+ //
+ else if (a == "-isystem" ||
+ a == "-iquote" ||
+ a == "-idirafter" ||
+ a == "-framework")
+ {
+ args.push_back (a);
+
+ const char* v;
+ if (!scan.more () || (v = scan.next ())[0] == '\0')
+ {
+ e << argv[0] << ": error: expected argument for the " << a
+ << " option" << endl;
+ return 1;
+ }
+
+ args.push_back (v);
+ }
+ // -D
+ //
+ else if (n > 1 && a[0] == '-' && a[1] == 'D')
+ {
+ args.push_back (a);
+
+ if (n == 2) // -D macro
+ {
+ const char* v;
+ if (!scan.more () || (v = scan.next ())[0] == '\0')
+ {
+ e << argv[0] << ": error: expected argument for the -D option"
+ << endl;
+ return 1;
+ }
+
+ args.push_back (v);
+ }
+ }
+ // -U
+ //
+ else if (n > 1 && a[0] == '-' && a[1] == 'U')
+ {
+ args.push_back (a);
+
+ if (n == 2) // -U macro
+ {
+ const char* v;
+ if (!scan.more () || (v = scan.next ())[0] == '\0')
+ {
+ e << argv[0] << ": error: expected argument for the -U option"
+ << endl;
+ return 1;
+ }
+
+ args.push_back (v);
+ }
+ }
+ // Store everything else in a list so that we can parse it with the
+ // cli parser. This is the only reliable way to find out where the
+ // options end.
+ //
+ else
+ plugin_args.push_back (a);
+ }
+
+ // Add the default include directories (-I) after the user-supplied
+ // ones.
+ //
+ args.insert (args.end (), def_inc_dirs.begin (), def_inc_dirs.end ());
+
+ // Find the plugin.
+ //
+ {
+#ifndef ODB_STATIC_PLUGIN
+ path plugin (plugin_path (path (argv[0]), args[0]));
+#else
+ // Use a dummy name if the plugin is linked into the compiler.
+ //
+ path plugin ("odb");
+#endif
+
+ if (plugin.empty ())
+ return 1; // Diagnostics has already been issued.
+
+#ifdef _WIN32
+ // Here is the problem: since the plugin is loaded by GCC (cc1plus.exe
+ // to be precise), the DLL assembly magic we have for executables won't
+ // help here.
+ //
+ // To allow executing the ODB compiler in-place we add the odb.exe.dlls/
+ // directory to PATH. It is a bit of hack but then DLL assemblies for
+ // DLLs is whole new level of insanity that we are unlikely to ever
+ // touch.
+ //
+ // And it turns out we have the same problem in the installed case: if
+ // the installation directory is not in PATH, then GCC won't find the
+ // DLLs the plugin needs. So we handle both here.
+ //
+ {
+ path d (plugin.directory ());
+ d.complete ();
+ d.normalize ();
+ d /= path ("odb.exe.dlls");
+
+ struct stat st;
+ if (stat (d.string ().c_str (), &st) != 0 || !S_ISDIR (st.st_mode))
+ d = d.directory ();
+
+ string v ("PATH=" + d.string ());
+
+ if (char const* p = getenv ("PATH"))
+ {
+ v += ';';
+ v += p;
+ }
+
+ _putenv (v.c_str ());
+ }
+#endif
+
+ args[7] = "-fplugin=" + plugin.string ();
+ }
+
+ // Parse plugin options. We have to do it twice to get the target
+ // database which is needed while loading profiles.
+ //
+ vector<char*> av;
+ av.push_back (argv[0]);
+
+ for (strings::iterator i (plugin_args.begin ()), end (plugin_args.end ());
+ i != end; ++i)
+ {
+ av.push_back (const_cast<char*> (i->c_str ()));
+ }
+
+ int ac (static_cast<int> (av.size ()));
+
+ cli::argv_file_scanner::option_info oi[3];
+ oi[0].option = "--options-file"; // Keep in case profile uses it.
+ oi[0].search_func = 0;
+ oi[1].option = "-p";
+ oi[2].option = "--profile";
+
+ vector<database> dbs;
+ bool show_sloc;
+ size_t sloc_limit;
+ {
+ oi[1].search_func = &profile_search_ignore;
+ oi[2].search_func = &profile_search_ignore;
+
+ cli::argv_file_scanner scan (ac, &av[0], oi, 3);
+ options ops (scan);
+
+ // Handle --build2-metadata (see also buildfile).
+ //
+ if (ops.build2_metadata_specified ())
+ {
+ ostream& o (cout);
+
+ // Note that the export.metadata variable should be the first non-
+ // blank/comment line.
+ //
+ o << "# build2 buildfile odb" << endl
+ << "export.metadata = 1 odb" << endl
+ << "odb.name = [string] odb" << endl
+ << "odb.version = [string] '" << ODB_COMPILER_VERSION_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;
+ }
+
+ // Handle --version.
+ //
+ if (ops.version ())
+ {
+ ostream& o (cout);
+
+ o << "ODB object-relational mapping (ORM) compiler for C++ "
+ 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;
+
+ return 0;
+ }
+
+ // Handle --help.
+ //
+ if (ops.help ())
+ {
+ ostream& o (cout);
+
+ o << "Usage: " << argv[0] << " [options] file [file ...]" << endl
+ << "Options:" << endl;
+
+ options::print_usage (cout);
+ return 0;
+ }
+
+ // Check that required options were specifed.
+ //
+ dbs = ops.database ();
+
+ if (dbs.empty ())
+ {
+ e << argv[0] << ": error: no database specified with the --database "
+ << "option" << endl;
+ return 1;
+ }
+
+ if (dbs.size () > 1 && !ops.multi_database_specified ())
+ {
+ e << argv[0] << ": error: --multi-database option required when " <<
+ "multiple databases are specified"<< endl;
+ return 1;
+ }
+
+ show_sloc = ops.show_sloc ();
+ sloc_limit = ops.sloc_limit_specified () ? ops.sloc_limit () : 0;
+
+ // Translate some ODB options to GCC options.
+ //
+ switch (ops.std ())
+ {
+ case cxx_version::cxx98:
+ {
+ args[3] = "-std=c++98";
+ break;
+ }
+ case cxx_version::cxx11:
+ {
+ args[3] = "-std=c++0x"; // c++11 was only added in GCC 4.7.0.
+ break;
+ }
+ case cxx_version::cxx14:
+ {
+ args[3] = "-std=c++1y";
+ break;
+ }
+ case cxx_version::cxx17:
+ {
+ args[3] = "-std=c++1z";
+ break;
+ }
+ case cxx_version::cxx20:
+ {
+ args[3] = "-std=c++2a";
+ break;
+ }
+ }
+ }
+
+ // Obtain profile (-I) search paths.
+ //
+ paths prof_paths (profile_paths (args, argv[0]));
+
+ if (v)
+ {
+ e << "Profile search paths:" << endl;
+
+ for (paths::const_iterator i (prof_paths.begin ());
+ i != prof_paths.end (); ++i)
+ e << " " << *i << endl;
+ }
+
+ // Pass profile search paths (svc-path option).
+ //
+ for (paths::const_iterator i (prof_paths.begin ());
+ i != prof_paths.end (); ++i)
+ {
+ args.push_back (encode_plugin_option ("svc-path", i->string ()));
+ }
+
+ // Add common ODB macros.
+ //
+ args.push_back ("-DODB_COMPILER");
+
+ {
+ ostringstream ostr;
+ ostr << ODB_COMPILER_VERSION_OLD;
+ args.push_back ("-DODB_COMPILER_VERSION=" + ostr.str ());
+ }
+
+ // Compile for each database.
+ //
+ size_t sloc_total (0);
+
+ for (vector<database>::iterator i (dbs.begin ()); i != dbs.end (); ++i)
+ {
+ database db (*i);
+ strings db_args (args);
+
+ // Add database-specific ODB macro.
+ //
+ db_args.push_back (db_macro[db]);
+
+ // Second parse.
+ //
+ profile_data pd (prof_paths, db, argv[0]);
+ oi[1].search_func = &profile_search;
+ oi[2].search_func = &profile_search;
+ oi[1].arg = &pd;
+ oi[2].arg = &pd;
+
+ cli::argv_file_scanner scan (ac, &av[0], oi, 3);
+ options ops (scan);
+
+ size_t end (scan.end () - 1); // We have one less in plugin_args.
+
+ if (end == plugin_args.size ())
+ {
+ e << argv[0] << ": error: input file expected" << endl;
+ return 1;
+ }
+
+ // Encode plugin options.
+ //
+ // Add the database we are compiling for first. More databases
+ // could be specified in options files but they will be ignored
+ // by the plugin (it only cares about the first).
+ //
+ db_args.push_back (encode_plugin_option ("database", db.string ()));
+
+ cli::options const& desc (options::description ());
+ for (size_t i (0); i < end; ++i)
+ {
+ string k, a (plugin_args[i]);
+
+ // Ignore certain options.
+ //
+ if (a == "--")
+ {
+ // Ignore the option seperator since GCC doesn't understand it.
+ //
+ continue;
+ }
+ else if (a == "-d" || a == "--database")
+ {
+ // Ignore all other databases.
+ //
+ i++; // Skip the value.
+ continue;
+ }
+
+ cli::options::const_iterator it (desc.find (a));
+
+ if (it == desc.end ())
+ {
+ e << argv[0] << ": ice: unexpected option '" << a << "'" << endl;
+ return 1;
+ }
+
+ if (a.size () > 2 && a[0] == '-' && a[1] == '-')
+ k = string (a, 2); // long format
+ else
+ k = string (a, 1); // short format
+
+ if (it->flag ())
+ db_args.push_back (encode_plugin_flag (k));
+ else
+ {
+ // If there are more arguments then we may have a value.
+ //
+ if (i + 1 == end)
+ {
+ e << argv[0] << ": ice: expected argument for '" << a << "'"
+ << endl;
+ return 1;
+ }
+
+ db_args.push_back (encode_plugin_option (k, plugin_args[++i]));
+ }
+ }
+
+ // Reserve space for and remember the position of the svc-file
+ // option.
+ //
+ size_t svc_file_pos (db_args.size ());
+ db_args.push_back ("");
+
+ // If compiling multiple input files at once, pass them also with
+ // the --svc-file option.
+ //
+ bool at_once (ops.at_once () && plugin_args.size () - end > 1);
+ if (at_once)
+ {
+ if (ops.input_name ().empty ())
+ {
+ e << "error: --input-name required when compiling multiple " <<
+ "input files at once (--at-once)" << endl;
+ return 1;
+ }
+
+ for (size_t i (end); i < plugin_args.size (); ++i)
+ db_args.push_back (
+ encode_plugin_option ("svc-file", plugin_args[i]));
+ }
+
+ // Create an execvp-compatible argument array.
+ //
+ typedef vector<char const*> cstrings;
+ cstrings exec_args;
+
+ for (strings::const_iterator i (db_args.begin ()), end (db_args.end ());
+ i != end; ++i)
+ {
+ exec_args.push_back (i->c_str ());
+ }
+
+ exec_args.push_back ("-"); // Compile stdin.
+ exec_args.push_back (0);
+
+ // Iterate over the input files and compile each of them.
+ //
+ for (; end < plugin_args.size (); ++end)
+ {
+ string name (at_once ? ops.input_name () : plugin_args[end]);
+
+ // Set the --svc-file option.
+ //
+ db_args[svc_file_pos] = encode_plugin_option ("svc-file", name);
+ exec_args[svc_file_pos] = db_args[svc_file_pos].c_str ();
+
+ //
+ //
+ ifstream ifs;
+
+ if (!at_once)
+ {
+ ifs.open (name.c_str (), ios_base::in | ios_base::binary);
+
+ if (!ifs.is_open ())
+ {
+ e << name << ": error: unable to open in read mode" << endl;
+ return 1;
+ }
+ }
+
+ if (v)
+ {
+ e << "Compiling " << name << endl;
+ for (cstrings::const_iterator i (exec_args.begin ());
+ i != exec_args.end (); ++i)
+ {
+ if (*i != 0)
+ e << *i << (*(i + 1) != 0 ? ' ' : '\n');
+ }
+ }
+
+ // Deal with Windows command line length limit.
+ //
+#ifdef _WIN32
+ string ops_file_arg;
+ auto_remove opt_file_rm (
+ fixup_cmd_line (exec_args, 1, argv[0], ops_file_arg));
+#endif
+
+ process_info pi (start_process (&exec_args[0], argv[0], false, true));
+
+ {
+ __gnu_cxx::stdio_filebuf<char> fb (
+ pi.out_fd, ios_base::out | ios_base::binary);
+ ostream os (&fb);
+
+ if (!at_once)
+ {
+ // See if we there is a UTF-8 BOM in the input file. If so,
+ // then we need to write it before prologues.
+ //
+ if (ifs.peek () == 0xEF)
+ {
+ ifs.get ();
+ if (ifs.get () != 0xBB || ifs.get () != 0xBF)
+ {
+ e << name << ": error: invalid UTF-8 BOM sequence" << endl;
+ fb.close ();
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ os.put (0xEF);
+ os.put (0xBB);
+ os.put (0xBF);
+ }
+ }
+
+ if (!ops.trace ())
+ {
+ // Add the standard prologue.
+ //
+ os << "#line 1 \"<standard-odb-prologue>\"" << endl;
+
+ // Make sure ODB compiler and libodb versions are compatible.
+ //
+ // 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 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
+ << endl;
+
+ // Include std::string. It is used as a default type for
+ // the implicit discriminator member in polymorphism
+ // support.
+ //
+ os << "#include <string>" << endl
+ << endl;
+
+ // Add ODB compiler metaprogramming tests.
+ //
+ os << "namespace odb" << endl
+ << "{" << endl
+ << "namespace compiler" << endl
+ << "{" << endl;
+
+ // operator< test, used in validator.
+ //
+ // 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
+ << "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;
+ }
+
+ // Add custom prologue if any.
+ //
+ // NOTE: if you change the format, you also need to update code
+ // in include.cxx
+ //
+ size_t pro_count (1);
+ if (ops.odb_prologue ().count (db) != 0)
+ {
+ strings const& pro (ops.odb_prologue ()[db]);
+ for (size_t i (0); i < pro.size (); ++i, ++pro_count)
+ {
+ os << "#line 1 \"<odb-prologue-" << pro_count << ">\"" << endl
+ << pro[i] << endl;
+ }
+ }
+
+ if (ops.odb_prologue_file ().count (db) != 0)
+ {
+ strings const& prof (ops.odb_prologue_file ()[db]);
+ for (size_t i (0); i < prof.size (); ++i, ++pro_count)
+ {
+ os << "#line 1 \"<odb-prologue-" << pro_count << ">\""
+ << endl;
+
+ ifstream ifs (prof[i].c_str (), ios_base::in | ios_base::binary);
+
+ if (!ifs.is_open ())
+ {
+ e << prof[i] << ": error: unable to open in read mode" << endl;
+ fb.close ();
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ if (!(os << ifs.rdbuf ()))
+ {
+ e << prof[i] << ": error: io failure" << endl;
+ fb.close ();
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ os << endl;
+ }
+ }
+
+ if (at_once)
+ {
+ // Include all the input files (no need to escape).
+ //
+ os << "#line 1 \"<command-line>\"" << endl;
+
+ bool b (ops.include_with_brackets ());
+ char op (b ? '<' : '"'), cl (b ? '>' : '"');
+
+ for (; end < plugin_args.size (); ++end)
+ os << "#include " << op << plugin_args[end] << cl << endl;
+ }
+ else
+ {
+ // Write the synthesized translation unit to stdout.
+ //
+ os << "#line 1 \"" << escape_path (name) << "\"" << endl;
+
+ if (!(os << ifs.rdbuf ()))
+ {
+ e << name << ": error: io failure" << endl;
+ fb.close ();
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ // Add a new line in case the input file doesn't end with one.
+ //
+ os << endl;
+ }
+
+ // Add custom epilogue if any.
+ //
+ // NOTE: if you change the format, you also need to update code
+ // in include.cxx
+ //
+ size_t epi_count (1);
+ if (ops.odb_epilogue ().count (db) != 0)
+ {
+ strings const& epi (ops.odb_epilogue ()[db]);
+ for (size_t i (0); i < epi.size (); ++i, ++epi_count)
+ {
+ os << "#line 1 \"<odb-epilogue-" << epi_count << ">\"" << endl
+ << epi[i] << endl;
+ }
+ }
+
+ if (ops.odb_epilogue_file ().count (db) != 0)
+ {
+ strings const& epif (ops.odb_epilogue_file ()[db]);
+ for (size_t i (0); i < epif.size (); ++i, ++epi_count)
+ {
+ os << "#line 1 \"<odb-epilogue-" << epi_count << ">\""
+ << endl;
+
+ ifstream ifs (epif[i].c_str (), ios_base::in | ios_base::binary);
+
+ if (!ifs.is_open ())
+ {
+ e << epif[i] << ": error: unable to open in read mode" << endl;
+ fb.close ();
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ if (!(os << ifs.rdbuf ()))
+ {
+ e << epif[i] << ": error: io failure" << endl;
+ fb.close ();
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ os << endl;
+ }
+ }
+
+ if (!ops.trace ())
+ {
+ // Add the standard epilogue at the end so that we see all
+ // the declarations.
+ //
+ os << "#line 1 \"<standard-odb-epilogue>\"" << endl;
+
+ // Includes for standard smart pointers. The Boost TR1 header
+ // may or may not delegate to the GCC implementation. In either
+ // case, the necessary declarations will be provided so we don't
+ // need to do anything.
+ //
+ os << "#include <memory>" << endl;
+
+ // Standard wrapper traits.
+ //
+ os << "#include <odb/wrapper-traits.hxx>" << endl;
+
+ // Standard pointer traits.
+ //
+ os << "#include <odb/pointer-traits.hxx>" << endl;
+
+ // Standard container traits.
+ //
+ os << "#include <odb/container-traits.hxx>" << endl;
+
+ // TR1 wrapper/pointer traits.
+ //
+ // @@ TMP: drop after 2.5.0.
+#if 0
+ if (ops.std () == cxx_version::cxx98)
+ os << endl
+ << "#ifndef BOOST_TR1_MEMORY_HPP_INCLUDED" << endl
+ << "# include <tr1/memory>" << endl
+ << "#endif" << endl
+ << "#include <odb/tr1/wrapper-traits.hxx>" << endl
+ << "#include <odb/tr1/pointer-traits.hxx>" << endl;
+#endif
+ }
+ }
+
+ // Filter the output stream looking for communication from the
+ // plugin.
+ //
+ {
+ __gnu_cxx::stdio_filebuf<char> fb (pi.in_ofd, ios_base::in);
+ istream is (&fb);
+
+ for (bool first (true); !is.eof (); )
+ {
+ string line;
+ getline (is, line);
+
+ if (is.fail () && !is.eof ())
+ {
+ e << argv[0] << ": error: io failure while parsing output"
+ << endl;
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ if (line.compare (0, 9, "odb:sloc:") == 0)
+ {
+ if (show_sloc || sloc_limit != 0)
+ {
+ size_t n;
+ istringstream is (string (line, 9, string::npos));
+
+ if (!(is >> n && is.eof ()))
+ {
+ e << argv[0] << ": error: invalid odb:sloc value" << endl;
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ sloc_total += n;
+ }
+
+ continue;
+ }
+
+ if (first)
+ first = false;
+ else
+ cout << endl;
+
+ cout << line;
+ }
+ }
+
+ if (!wait_process (pi, argv[0]))
+ return 1;
+ } // End input file loop.
+ } // End database loop.
+
+ // Handle SLOC.
+ //
+ if (show_sloc)
+ e << "total: " << sloc_total << endl;
+
+ if (sloc_limit != 0 && sloc_limit < sloc_total)
+ {
+ e << argv[0] << ": error: SLOC limit of " << sloc_limit << " lines " <<
+ "has been exceeded" << endl;
+
+ if (!show_sloc)
+ e << argv[0] << ": info: use the --show-sloc option to see the "
+ << "current total" << endl;
+
+ return 1;
+ }
+ }
+ catch (profile_failure const&)
+ {
+ // Diagnostics has already been issued.
+ //
+ return 1;
+ }
+ catch (process_failure const&)
+ {
+ // Diagnostics has already been issued.
+ //
+ return 1;
+ }
+ catch (invalid_path const& ex)
+ {
+ e << argv[0] << ": error: invalid path '" << ex.path () << "'" << endl;
+ return 1;
+ }
+ catch (cli::exception const& ex)
+ {
+ e << ex << endl;
+ return 1;
+ }
+}
+
+static inline string
+encode_plugin_flag (string const& k)
+{
+ return "-fplugin-arg-odb-" + k;
+}
+
+static string
+encode_plugin_option (string const& k, string const& cv)
+{
+ string o ("-fplugin-arg-odb-");
+ o += k;
+ o += '=';
+
+ if (!cv.empty ())
+ {
+ // A value cannot contain '='. Encode it as the backspace
+ // character.
+ //
+ string v (cv);
+ for (size_t i (0); i < v.size (); ++i)
+ if (v[i] == '=')
+ v[i] = '\b';
+
+ o += v;
+ }
+
+ return o;
+}
+
+static paths
+profile_paths (strings const& sargs, char const* name)
+{
+ // Copy some of the arguments from the passed list. We also need
+ // the g++ executable.
+ //
+ strings args;
+
+ args.push_back (sargs[0]);
+ args.push_back ("-v");
+ args.push_back ("-x");
+ args.push_back ("c++");
+ args.push_back ("-E");
+ args.push_back ("-P");
+
+ for (strings::const_iterator i (++sargs.begin ()), end (sargs.end ());
+ i != end; ++i)
+ {
+ string const& a (*i);
+
+ // -I
+ //
+ if (a.size () > 1 && a[0] == '-' && a[1] == 'I')
+ {
+ args.push_back (a);
+
+ if (a.size () == 2) // -I /path
+ {
+ args.push_back (*(++i));
+ }
+ }
+ // -framework
+ //
+ else if (a == "-isystem" ||
+ a == "-iquote" ||
+ a == "-idirafter" ||
+ a == "-isysroot" ||
+ a == "-framework")
+ {
+ args.push_back (a);
+
+ if (++i == end)
+ {
+ cerr << name << ": error: expected argument for the " << a
+ << " option" << endl;
+ throw profile_failure ();
+ }
+
+ args.push_back (*i);
+ }
+ // --sysroot
+ //
+ else if (a.compare (0, 10, "--sysroot=") == 0)
+ args.push_back (a);
+ // -std
+ //
+ else if (a.compare (0, 5, "-std=") == 0)
+ args.push_back (a);
+ }
+
+ // Create an execvp-compatible argument array.
+ //
+ vector<char const*> exec_args;
+
+ for (strings::const_iterator i (args.begin ()), end (args.end ());
+ i != end; ++i)
+ {
+ exec_args.push_back (i->c_str ());
+ }
+
+ exec_args.push_back ("-"); // Compile stdin.
+ exec_args.push_back (0);
+
+#ifdef _WIN32
+ string ops_file_arg;
+ auto_remove opt_file_rm (fixup_cmd_line (exec_args, 1, name, ops_file_arg));
+#endif
+
+ process_info pi (start_process (&exec_args[0], name, true));
+ close (pi.out_fd); // Preprocess empty file.
+
+ // Read the output into a temporary string stream. We don't parse
+ // it on the fly because we don't know whether it is the data or
+ // diagnostics until after the process is terminated and we get
+ // the exit code. We also cannot first wait for the exist code
+ // and then read the output because the process might get blocked.
+ //
+ stringstream ss;
+ {
+ __gnu_cxx::stdio_filebuf<char> fb (pi.in_efd, ios_base::in);
+ istream is (&fb);
+
+ for (bool first (true); !is.eof (); )
+ {
+ string line;
+ getline (is, line);
+
+ if (is.fail () && !is.eof ())
+ {
+ cerr << name << ": error: "
+ << "io failure while parsing profile paths" << endl;
+
+ wait_process (pi, name);
+ throw profile_failure ();
+ }
+
+ if (first)
+ first = false;
+ else
+ ss << endl;
+
+ ss << line;
+ }
+ }
+
+ if (!wait_process (pi, name))
+ {
+ // Things didn't go well and ss should contain the diagnostics.
+ // In case it is empty, issue our own.
+ //
+ if (!ss.str ().empty ())
+ cerr << ss.rdbuf ();
+ else
+ cerr << name << ": error: unable to extract profile paths" << endl;
+
+ throw profile_failure ();
+ }
+
+ // Parse the cached output.
+ //
+ paths r;
+ {
+ enum
+ {
+ read_prefix,
+ read_path,
+ read_suffix
+ } state = read_prefix;
+
+ while (!ss.eof () && state != read_suffix)
+ {
+ string line;
+ getline (ss, line);
+
+ if (ss.fail () && !ss.eof ())
+ {
+ cerr << name << ": error: "
+ << "io failure while parsing profile paths" << endl;
+ throw profile_failure ();
+ }
+
+ switch (state)
+ {
+ case read_prefix:
+ {
+ // The English string that we are looking for is "#include <...>
+ // search starts here:" but it can be translated. However, all
+ // the translations seems to have the "#include" and "<...>"
+ // parts, so we can search for those.
+ //
+ if (line.find ("#include") != string::npos &&
+ line.find ("<...>") != string::npos)
+ state = read_path;
+ break;
+ }
+ case read_path:
+ {
+ // The end of the list is terminated with the "End of search
+ // list." line, which, again, can be translated. Here we don't
+ // have any invariable parts that we can use. Instead, we will
+ // rely on the fact that all the paths are space-indented.
+ //
+ if (!line.empty () && line[0] != ' ')
+ state = read_suffix;
+ else
+ // Paths are indented with a space.
+ //
+ r.push_back (path (string (line, 1)));
+
+ break;
+ }
+ case read_suffix:
+ {
+ // We shouldn't get here.
+ break;
+ }
+ }
+ }
+
+ if (state != read_suffix)
+ {
+ cerr << name << ": error: unable to parse profile paths" << endl;
+ throw profile_failure ();
+ }
+ }
+
+ return r;
+}
+
+//
+// Path manipulation.
+//
+
+static string
+escape_path (string const& p)
+{
+ string r;
+
+ for (size_t i (0); i < p.size (); ++i)
+ {
+ if (p[i] == '\\')
+ r += "\\\\";
+ else
+ r += p[i];
+ }
+
+ return r;
+}
+
+static path
+path_search (path const& f)
+{
+ typedef path::traits traits;
+
+ // If there is a directory component in the file, then the PATH
+ // search does not apply.
+ //
+ if (!f.directory ().empty ())
+ return f;
+
+ string paths;
+
+ // If there is no PATH in environment then the default search
+ // path is the current directory.
+ //
+ if (char const* s = getenv ("PATH"))
+ paths = s;
+ else
+ paths = traits::path_separator;
+
+ // On Windows also check the current directory.
+ //
+#ifdef _WIN32
+ paths += traits::path_separator;
+#endif
+
+ struct stat info;
+
+ for (size_t b (0), e (paths.find (traits::path_separator));
+ b != string::npos;)
+ {
+ path p (string (paths, b, e != string::npos ? e - b : e));
+
+ // Empty path (i.e., a double colon or a colon at the beginning
+ // or end of PATH) means search in the current dirrectory.
+ //
+ if (p.empty ())
+ p = path (".");
+
+ path dp (p / f);
+
+ // Just check that the file exist without checking for permissions, etc.
+ //
+ if (stat (dp.string ().c_str (), &info) == 0 && S_ISREG (info.st_mode))
+ return dp;
+
+ // On Windows also try the path with the .exe extension.
+ //
+#ifdef _WIN32
+ dp += ".exe";
+
+ if (stat (dp.string ().c_str (), &info) == 0 && S_ISREG (info.st_mode))
+ return dp;
+#endif
+
+ if (e == string::npos)
+ b = e;
+ else
+ {
+ b = e + 1;
+ e = paths.find (traits::path_separator, b);
+ }
+ }
+
+ return path ();
+}
+
+static path
+driver_path (path const& drv)
+{
+ return drv.directory ().empty () ? path_search (drv) : drv;
+}
+
+#ifndef ODB_STATIC_PLUGIN
+static path
+plugin_path (path const& drv,
+#ifdef ODB_GCC_PLUGIN_DIR
+ string const& gxx)
+#else
+ string const&)
+#endif
+{
+#ifdef _WIN32
+ char const plugin_ext[] = ".dll";
+
+// While GCC 8 switched to using .dylib as the plugin extension, there is a
+// bug in the extension stripping code. So for now we use the .so extension
+// everywhere (see also buildfile if changing this).
+//
+//#elif defined(__APPLE__)
+// 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).
+ //
+#ifdef _WIN32
+ string b (drv.leaf ().base ().string ());
+#else
+ string b (drv.leaf ().string ());
+#endif
+
+ path dp (driver_path (drv));
+
+ if (dp.empty ())
+ {
+ cerr << drv << ": error: unable to resolve ODB driver path" << endl;
+ return path ();
+ }
+
+ dp = dp.directory ();
+ struct stat info;
+
+ path pp;
+
+#ifdef ODB_GCC_PLUGIN_DIR
+ // Plugin should be installed into the GCC default plugin directory.
+ // Ideally, in this situation, we would simply pass the plugin name and
+ // let GCC append the correct directory. Unfortunately, this mechanism
+ // was only added in GCC 4.6 so in order to support 4.5 we will have to
+ // emulate it ourselves.
+ //
+ //@@ TMP: drop this after 2.5.0 since we no longer support GCC < 5.
+ //
+ {
+#if 1
+ // First get the default GCC plugin directory.
+ //
+ path d;
+ vector<char const*> exec_args;
+ exec_args.push_back (gxx.c_str ());
+ exec_args.push_back ("-print-file-name=plugin");
+ exec_args.push_back (0);
+
+ process_info pi (
+ start_process (
+ &exec_args[0], drv.string ().c_str (), false, true));
+ close (pi.out_fd);
+
+ // Read the path from stdout.
+ //
+ {
+ __gnu_cxx::stdio_filebuf<char> fb (pi.in_ofd, ios_base::in);
+ istream is (&fb);
+ string line;
+ getline (is, line);
+ d = path (line);
+ }
+
+ if (!wait_process (pi, drv.string ().c_str ()))
+ return path (); // Assume GCC issued some diagnostics.
+
+ if (d.string () == "plugin")
+ {
+ cerr << drv << ": error: unable to obtain GCC plugin directory" << endl;
+ return path ();
+ }
+
+ // See if the plugin is there.
+ //
+ pp = d / path (b + plugin_ext);
+ if (stat (pp.string ().c_str (), &info) != 0)
+ {
+ cerr << drv << ": error: no ODB plugin in GCC plugin directory '" <<
+ d << "'" << endl;
+ return path ();
+ }
+
+ return pp;
+#else
+ return path (b);
+#endif
+ }
+#elif defined (ODB_PLUGIN_PATH)
+ // If we were given a plugin path, use that.
+ //
+ {
+ string rp (ODB_PLUGIN_PATH);
+ if (!rp.empty ())
+ dp /= path (rp);
+
+ pp = dp / path (b + plugin_ext);
+
+ if (stat (pp.string ().c_str (), &info) != 0)
+ {
+ cerr << drv << ": error: no ODB plugin in '" << dp << "'" << endl;
+ return path ();
+ }
+
+ return pp;
+ }
+#endif
+
+ // Try in the current directory.
+ //
+ pp = dp / path (b + plugin_ext);
+ if (stat (pp.string ().c_str (), &info) != 0)
+ {
+ cerr << drv << ": error: unable to locate ODB plugin" << endl;
+ return path ();
+ }
+
+ return pp;
+}
+#endif
+
+//
+// Process manipulation.
+//
+
+#ifndef _WIN32
+
+static process_info
+start_process (char const* args[], char const* name, bool err, bool out)
+{
+ int out_fd[2];
+ int in_efd[2];
+ int in_ofd[2];
+
+ if (pipe (out_fd) == -1 ||
+ (err && pipe (in_efd) == -1) ||
+ (out && pipe (in_ofd) == -1))
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+
+ pid_t pid (fork ());
+
+ if (pid == -1)
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+
+ if (pid == 0)
+ {
+ // Child. Close the write end of the pipe and duplicate the read end
+ // to stdin. Then close the original read end descriptors.
+ //
+ if (close (out_fd[1]) == -1 ||
+ dup2 (out_fd[0], STDIN_FILENO) == -1 ||
+ close (out_fd[0]) == -1)
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+
+ // Do the same for the stderr if requested.
+ //
+ if (err)
+ {
+ if (close (in_efd[0]) == -1 ||
+ dup2 (in_efd[1], STDERR_FILENO) == -1 ||
+ close (in_efd[1]) == -1)
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+ }
+
+ // Do the same for the stdout if requested.
+ //
+ if (out)
+ {
+ if (close (in_ofd[0]) == -1 ||
+ dup2 (in_ofd[1], STDOUT_FILENO) == -1 ||
+ close (in_ofd[1]) == -1)
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+ }
+
+ if (execvp (args[0], const_cast<char**> (&args[0])) == -1)
+ {
+ char const* err (strerror (errno));
+ cerr << args[0] << ": error: " << err << endl;
+ throw process_failure ();
+ }
+ }
+ else
+ {
+ // Parent. Close the other ends of the pipes.
+ //
+ if (close (out_fd[0]) == -1 ||
+ (err && close (in_efd[1]) == -1) ||
+ (out && close (in_ofd[1]) == -1))
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+ }
+
+ process_info r;
+ r.id = pid;
+ r.out_fd = out_fd[1];
+ r.in_efd = err ? in_efd[0] : 0;
+ r.in_ofd = out ? in_ofd[0] : 0;
+ return r;
+}
+
+static bool
+wait_process (process_info pi, char const* name)
+{
+ int status;
+
+ if (waitpid (pi.id, &status, 0) == -1)
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+
+ return WIFEXITED (status) && WEXITSTATUS (status) == 0;
+}
+
+#else // _WIN32
+
+static void
+print_error (char const* name)
+{
+ LPTSTR msg;
+ DWORD e (GetLastError());
+
+ if (!FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ 0,
+ e,
+ MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &msg,
+ 0,
+ 0))
+ {
+ cerr << name << ": error: unknown error code " << e << endl;
+ return;
+ }
+
+ cerr << name << ": error: " << msg << endl;
+ LocalFree (msg);
+}
+
+// On Windows we need to protect command line arguments with spaces using
+// quotes. Since there could be actual quotes in the value, we need to escape
+// them.
+//
+static void
+append_quoted (string& cmd_line, const char* ca)
+{
+ string a (ca);
+ bool quote (a.find (' ') != string::npos);
+
+ if (quote)
+ cmd_line += '"';
+
+ for (size_t i (0); i < a.size (); ++i)
+ {
+ if (a[i] == '"')
+ cmd_line += "\\\"";
+ else
+ cmd_line += a[i];
+ }
+
+ if (quote)
+ cmd_line += '"';
+}
+
+// Deal with Windows command line length limit.
+//
+// The best approach seems to be passing the command line in an "options file"
+// ("response file" in Microsoft's terminology).
+//
+static auto_remove
+fixup_cmd_line (vector<const char*>& args,
+ size_t start,
+ const char* name,
+ string& arg)
+{
+ // Calculate the would-be command line length similar to how start_process()
+ // implementation does it.
+ //
+ size_t n (0);
+ string s;
+ for (const char* a: args)
+ {
+ if (a != nullptr)
+ {
+ if (n != 0)
+ n++; // For the space separator.
+
+ s.clear ();
+ append_quoted (s, a);
+ n += s.size ();
+ }
+ }
+
+ if (n <= 32766) // 32768 - "Unicode terminating null character".
+ return auto_remove ();
+
+ // Create the temporary file.
+ //
+ char d[MAX_PATH + 1], p[MAX_PATH + 1];
+ if (GetTempPathA (sizeof (d), d) == 0 ||
+ GetTempFileNameA (d, "odb-options-", 0, p) == 0)
+ {
+ print_error (name);
+ throw process_failure ();
+ }
+
+ auto_remove rm = auto_remove (path (p));
+ try
+ {
+ ofstream ofs (p);
+ if (!ofs.is_open ())
+ {
+ cerr << name << ": error: unable to open '" << p << "' in write mode"
+ << endl;
+ throw process_failure ();
+ }
+
+ ofs.exceptions (ios_base::badbit | ios_base::failbit);
+
+ // Write the arguments to file.
+ //
+ // The format is a space-separated list of potentially-quoted arguments
+ // with support for backslash-escaping.
+ //
+ string b;
+ for (size_t i (start), n (args.size () - 1); i != n; ++i)
+ {
+ const char* a (args[i]);
+
+ // We will most likely have backslashes so just do it.
+ //
+ {
+ for (b.clear (); *a != '\0'; ++a)
+ {
+ if (*a != '\\')
+ b += *a;
+ else
+ b += "\\\\";
+ }
+
+ a = b.c_str ();
+ }
+
+ s.clear ();
+ append_quoted (s, a);
+ ofs << (i != start ? " " : "") << s;
+ }
+
+ ofs << '\n';
+ ofs.close ();
+ }
+ catch (const ios_base::failure&)
+ {
+ cerr << name << ": error: unable to write to '" << p << "'" << endl;
+ throw process_failure ();
+ }
+
+ // Rewrite the command line.
+ //
+ arg = string ("@") + p;
+ args.resize (start);
+ args.push_back (arg.c_str());
+ args.push_back (nullptr);
+
+ return rm;
+}
+
+static process_info
+start_process (char const* args[], char const* name, bool err, bool out)
+{
+ HANDLE out_h[2];
+ HANDLE in_eh[2];
+ HANDLE in_oh[2];
+ SECURITY_ATTRIBUTES sa;
+
+ sa.nLength = sizeof (SECURITY_ATTRIBUTES);
+ sa.bInheritHandle = true;
+ sa.lpSecurityDescriptor = 0;
+
+ if (!CreatePipe (&out_h[0], &out_h[1], &sa, 0) ||
+ !SetHandleInformation (out_h[1], HANDLE_FLAG_INHERIT, 0))
+ {
+ print_error (name);
+ throw process_failure ();
+ }
+
+ if (err)
+ {
+ if (!CreatePipe (&in_eh[0], &in_eh[1], &sa, 0) ||
+ !SetHandleInformation (in_eh[0], HANDLE_FLAG_INHERIT, 0))
+ {
+ print_error (name);
+ throw process_failure ();
+ }
+ }
+
+ if (out)
+ {
+ if (!CreatePipe (&in_oh[0], &in_oh[1], &sa, 0) ||
+ !SetHandleInformation (in_oh[0], HANDLE_FLAG_INHERIT, 0))
+ {
+ print_error (name);
+ throw process_failure ();
+ }
+ }
+
+ // Create the process.
+ //
+ path file (args[0]);
+
+ // Do PATH search.
+ //
+ if (file.directory ().empty ())
+ file = path_search (file);
+ else if (file.base () == file) // No extension
+ file += ".exe"; // Assume .exe.
+
+ if (file.empty ())
+ {
+ cerr << args[0] << ": error: file not found" << endl;
+ throw process_failure ();
+ }
+
+ // Serialize the arguments to string.
+ //
+ string cmd_line;
+
+ for (char const** p (args); *p != 0; ++p)
+ {
+ if (p != args)
+ cmd_line += ' ';
+
+ append_quoted (cmd_line, *p);
+ }
+
+ // Prepare other info.
+ //
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ memset (&si, 0, sizeof (STARTUPINFO));
+ memset (&pi, 0, sizeof (PROCESS_INFORMATION));
+
+ si.cb = sizeof(STARTUPINFO);
+
+ if (err)
+ si.hStdError = in_eh[1];
+ else
+ si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
+
+ if (out)
+ si.hStdOutput = in_oh[1];
+ else
+ si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
+
+ si.hStdInput = out_h[0];
+ si.dwFlags |= STARTF_USESTDHANDLES;
+
+ if (!CreateProcess (
+ file.string ().c_str (),
+ const_cast<char*> (cmd_line.c_str ()),
+ 0, // Process security attributes.
+ 0, // Primary thread security attributes.
+ true, // Inherit handles.
+ 0, // Creation flags.
+ 0, // Use our environment.
+ 0, // Use our current directory.
+ &si,
+ &pi))
+ {
+ print_error (name);
+ throw process_failure ();
+ }
+
+ CloseHandle (pi.hThread);
+ CloseHandle (out_h[0]);
+
+ if (err)
+ CloseHandle (in_eh[1]);
+
+ if (out)
+ CloseHandle (in_oh[1]);
+
+ process_info r;
+ r.id = pi.hProcess;
+ r.out_fd = _open_osfhandle ((intptr_t) (out_h[1]), 0);
+
+ if (r.out_fd == -1)
+ {
+ cerr << name << ": error: unable to obtain C file handle" << endl;
+ throw process_failure ();
+ }
+
+ if (err)
+ {
+ // Pass _O_TEXT to get newline translation.
+ //
+ r.in_efd = _open_osfhandle ((intptr_t) (in_eh[0]), _O_TEXT);
+
+ if (r.in_efd == -1)
+ {
+ cerr << name << ": error: unable to obtain C file handle" << endl;
+ throw process_failure ();
+ }
+ }
+ else
+ r.in_efd = 0;
+
+ if (out)
+ {
+ // Pass _O_TEXT to get newline translation.
+ //
+ r.in_ofd = _open_osfhandle ((intptr_t) (in_oh[0]), _O_TEXT);
+
+ if (r.in_ofd == -1)
+ {
+ cerr << name << ": error: unable to obtain C file handle" << endl;
+ throw process_failure ();
+ }
+ }
+ else
+ r.in_ofd = 0;
+
+ return r;
+}
+
+static bool
+wait_process (process_info pi, char const* name)
+{
+ DWORD status;
+
+ if (WaitForSingleObject (pi.id, INFINITE) != WAIT_OBJECT_0 ||
+ !GetExitCodeProcess (pi.id, &status))
+ {
+ print_error (name);
+ throw process_failure ();
+ }
+
+ CloseHandle (pi.id);
+ return status == 0;
+}
+
+#endif // _WIN32
diff --git a/odb/odb/option-functions.cxx b/odb/odb/option-functions.cxx
new file mode 100644
index 0000000..7eda934
--- /dev/null
+++ b/odb/odb/option-functions.cxx
@@ -0,0 +1,132 @@
+// file : odb/option-functions.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <set>
+#include <utility> // std::make_pair()
+
+#include <odb/option-functions.hxx>
+
+using namespace std;
+
+void
+process_options (options& o)
+{
+ database db (o.database ()[0]);
+
+ // If --generate-schema-only was specified, then set --generate-schema
+ // as well.
+ //
+ if (o.generate_schema_only ())
+ o.generate_schema (true);
+
+ // If --warn-hard was specified, then set both --warn-hard-{add,delete}.
+ //
+ if (o.warn_hard ())
+ {
+ o.warn_hard_add (true);
+ o.warn_hard_delete (true);
+ }
+
+ // Set the default schema format depending on the database.
+ //
+ if (o.generate_schema () && o.schema_format ()[db].empty ())
+ {
+ set<schema_format>& f (o.schema_format ()[db]);
+
+ switch (db)
+ {
+ case database::common:
+ {
+ break; // No schema for common.
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ {
+ f.insert (schema_format::sql);
+ break;
+ }
+ case database::sqlite:
+ {
+ f.insert (schema_format::embedded);
+ break;
+ }
+ }
+ }
+
+ // Set default --schema-version-table value.
+ //
+ if (o.schema_version_table ().count (db) == 0)
+ o.schema_version_table ()[db] = "schema_version";
+
+ // Set default --schema-name value.
+ //
+ if (o.schema_name ().count (db) == 0)
+ o.schema_name ()[db] = "";
+
+ // Set default --fkeys-deferrable-mode value.
+ //
+ if (o.fkeys_deferrable_mode ().count (db) == 0)
+ o.fkeys_deferrable_mode ()[db] = deferrable::deferred;
+
+ // Set default --{export,extern}-symbol values.
+ //
+ if (o.export_symbol ().count (db) == 0)
+ o.export_symbol ()[db] = "";
+
+ if (o.extern_symbol ().count (db) == 0)
+ o.extern_symbol ()[db] = "";
+
+ // Set default --*-file-suffix values.
+ //
+ {
+ database cm (database::common);
+
+ o.odb_file_suffix ().insert (make_pair (cm, string ("-odb")));
+ o.sql_file_suffix ().insert (make_pair (cm, string ("")));
+ o.schema_file_suffix ().insert (make_pair (cm, string ("-schema")));
+ o.changelog_file_suffix ().insert (make_pair (cm, string ("")));
+ }
+
+ if (o.multi_database () == multi_database::disabled)
+ {
+ o.odb_file_suffix ().insert (make_pair (db, string ("-odb")));
+ o.sql_file_suffix ().insert (make_pair (db, string ("")));
+ o.schema_file_suffix ().insert (make_pair (db, string ("-schema")));
+ o.changelog_file_suffix ().insert (make_pair (db, string ("")));
+ }
+ else
+ {
+ o.odb_file_suffix ().insert (make_pair (db, "-odb-" + db.string ()));
+ o.sql_file_suffix ().insert (make_pair (db, "-" + db.string ()));
+ o.schema_file_suffix ().insert (make_pair (db, "-schema-" + db.string ()));
+ o.changelog_file_suffix ().insert (make_pair (db, '-' + db.string ()));
+ }
+
+ // Set default --default-database value.
+ //
+ if (!o.default_database_specified ())
+ {
+ switch (o.multi_database ())
+ {
+ case multi_database::disabled:
+ {
+ o.default_database (db);
+ o.default_database_specified (true);
+ break;
+ }
+ case multi_database::dynamic:
+ {
+ o.default_database (database::common);
+ o.default_database_specified (true);
+ break;
+ }
+ case multi_database::static_:
+ {
+ // No default database unless explicitly specified.
+ break;
+ }
+ }
+ }
+}
diff --git a/odb/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/odb/option-types.cxx b/odb/odb/option-types.cxx
new file mode 100644
index 0000000..c4a030b
--- /dev/null
+++ b/odb/odb/option-types.cxx
@@ -0,0 +1,352 @@
+// file : odb/option-types.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <istream>
+#include <ostream>
+#include <algorithm> // std::lower_bound
+
+#include <odb/option-types.hxx>
+
+using namespace std;
+
+//
+// cxx_version
+//
+
+static const char* cxx_version_[] =
+{
+ "c++98",
+ "c++11",
+ "c++14",
+ "c++17",
+ "c++20"
+};
+
+string cxx_version::
+string () const
+{
+ return cxx_version_[v_];
+}
+
+istream&
+operator>> (istream& is, cxx_version& v)
+{
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ {
+ if (s == "c++98")
+ v = cxx_version::cxx98;
+ else if (s == "c++11")
+ v = cxx_version::cxx11;
+ else if (s == "c++14")
+ v = cxx_version::cxx14;
+ else if (s == "c++17")
+ v = cxx_version::cxx17;
+ else if (s == "c++20")
+ v = cxx_version::cxx20;
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+//
+// database
+//
+
+static const char* database_[] =
+{
+ "common",
+ "mssql",
+ "mysql",
+ "oracle",
+ "pgsql",
+ "sqlite"
+};
+
+static const char* database_name_[] =
+{
+ "Common Interface",
+ "SQL Server",
+ "MySQL",
+ "Oracle",
+ "PostgreSQL",
+ "SQLite"
+};
+
+string database::
+string () const
+{
+ return database_[v_];
+}
+
+string database::
+name () const
+{
+ return database_name_[v_];
+}
+
+istream&
+operator>> (istream& is, database& db)
+{
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ {
+ const char** e (database_ + sizeof (database_) / sizeof (char*));
+ const char** i (lower_bound (database_, e, s));
+
+ if (i != e && *i == s)
+ db = database::value (i - database_);
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+ostream&
+operator<< (ostream& os, database db)
+{
+ return os << db.string ();
+}
+
+//
+// multi_database
+//
+
+static const char* multi_database_[] =
+{
+ "dynamic",
+ "static",
+ "disabled"
+};
+
+string multi_database::
+string () const
+{
+ return multi_database_[v_];
+}
+
+istream&
+operator>> (istream& is, multi_database& db)
+{
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ {
+ const char** e (
+ multi_database_ + sizeof (multi_database_) / sizeof (char*) - 1);
+ const char** i (lower_bound (multi_database_, e, s));
+
+ if (i != e && *i == s)
+ db = multi_database::value (i - multi_database_);
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+ostream&
+operator<< (ostream& os, multi_database db)
+{
+ return os << db.string ();
+}
+
+//
+// schema_format
+//
+
+static const char* schema_format_[] =
+{
+ "embedded",
+ "separate",
+ "sql"
+};
+
+string schema_format::
+string () const
+{
+ return schema_format_[v_];
+}
+
+istream&
+operator>> (istream& is, schema_format& sf)
+{
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ {
+ const char** e (schema_format_ + sizeof (schema_format_) / sizeof (char*));
+ const char** i (lower_bound (schema_format_, e, s));
+
+ if (i != e && *i == s)
+ sf = schema_format::value (i - schema_format_);
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+ostream&
+operator<< (ostream& os, schema_format sf)
+{
+ return os << sf.string ();
+}
+
+//
+// name_case
+//
+
+istream&
+operator>> (istream& is, name_case& v)
+{
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ {
+ if (s == "upper")
+ v = name_case::upper;
+ else if (s == "lower")
+ v = name_case::lower;
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+//
+// pgsql_version
+//
+
+istream&
+operator>> (istream& is, pgsql_version& v)
+{
+ unsigned short major, minor;
+
+ // Extract the major version.
+ //
+ is >> major;
+
+ if (!is.fail ())
+ {
+ // Extract the decimal point.
+ //
+ char p;
+ is >> p;
+
+ if (!is.fail () && p == '.')
+ {
+ // Extract the minor version.
+ //
+ is >> minor;
+
+ if (!is.fail ())
+ v = pgsql_version (major, minor);
+ }
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+ostream&
+operator<< (ostream& os, pgsql_version v)
+{
+ return os << v.ver_major () << '.' << v.ver_minor ();
+}
+
+//
+// oracle_version
+//
+
+istream&
+operator>> (istream& is, oracle_version& v)
+{
+ unsigned short major, minor;
+
+ // Extract the major version.
+ //
+ is >> major;
+
+ if (!is.fail ())
+ {
+ // Extract the decimal point.
+ //
+ char p;
+ is >> p;
+
+ if (!is.fail () && p == '.')
+ {
+ // Extract the minor version.
+ //
+ is >> minor;
+
+ if (!is.fail ())
+ v = oracle_version (major, minor);
+ }
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+ostream&
+operator<< (ostream& os, oracle_version v)
+{
+ return os << v.ver_major () << '.' << v.ver_minor ();
+}
+
+//
+// mssql_version
+//
+
+istream&
+operator>> (istream& is, mssql_version& v)
+{
+ unsigned short major, minor;
+
+ // Extract the major version.
+ //
+ is >> major;
+
+ if (!is.fail ())
+ {
+ // Extract the decimal point.
+ //
+ char p;
+ is >> p;
+
+ if (!is.fail () && p == '.')
+ {
+ // Extract the minor version.
+ //
+ is >> minor;
+
+ if (!is.fail ())
+ v = mssql_version (major, minor);
+ }
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+ostream&
+operator<< (ostream& os, mssql_version v)
+{
+ return os << v.ver_major () << '.' << v.ver_minor ();
+}
diff --git a/odb/odb/option-types.hxx b/odb/odb/option-types.hxx
new file mode 100644
index 0000000..869fc83
--- /dev/null
+++ b/odb/odb/option-types.hxx
@@ -0,0 +1,391 @@
+// file : odb/option-types.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_OPTION_TYPES_HXX
+#define ODB_OPTION_TYPES_HXX
+
+#include <map>
+#include <iosfwd>
+#include <string>
+#include <cassert>
+
+#include <odb/semantics/relational/name.hxx>
+#include <odb/semantics/relational/deferrable.hxx>
+
+using semantics::relational::qname;
+using semantics::relational::deferrable;
+
+struct cxx_version
+{
+ enum value
+ {
+ cxx98,
+ cxx11,
+ cxx14,
+ cxx17,
+ cxx20
+ };
+
+ cxx_version (value v = value (0)) : v_ (v) {}
+ operator value () const {return v_;}
+
+ std::string
+ string () const;
+
+private:
+ value v_;
+};
+
+std::istream&
+operator>> (std::istream&, cxx_version&);
+
+//
+//
+struct database
+{
+ enum value
+ {
+ // Keep in alphabetic order.
+ //
+ common,
+ mssql,
+ mysql,
+ oracle,
+ pgsql,
+ sqlite
+ };
+
+ database (value v = value (0)) : v_ (v) {}
+ operator value () const {return v_;}
+
+ std::string
+ string () const;
+
+ // Full name (e.g., PostgreSQL).
+ //
+ std::string
+ name () const;
+
+private:
+ value v_;
+};
+
+std::istream&
+operator>> (std::istream&, database&);
+
+std::ostream&
+operator<< (std::ostream&, database);
+
+//
+//
+template <typename V>
+struct database_map: std::map<database, V>
+{
+ typedef std::map<database, V> base_type;
+
+ using base_type::operator[];
+
+ V const&
+ operator[] (database const& k) const
+ {
+ typename base_type::const_iterator i (this->find (k));
+ assert (i != this->end ());
+ return i->second;
+ }
+};
+
+//
+//
+struct multi_database
+{
+ enum value
+ {
+ // Keep in alphabetic order.
+ //
+ dynamic,
+ static_,
+ disabled // Special value.
+ };
+
+ multi_database (value v = disabled) : v_ (v) {}
+ operator value () const {return v_;}
+
+ std::string
+ string () const;
+
+private:
+ value v_;
+};
+
+std::istream&
+operator>> (std::istream&, multi_database&);
+
+std::ostream&
+operator<< (std::ostream&, multi_database);
+
+//
+//
+struct schema_format
+{
+ enum value
+ {
+ // Keep in alphabetic order.
+ //
+ embedded,
+ separate,
+ sql
+ };
+
+ schema_format (value v = value (0)) : v_ (v) {}
+ operator value () const {return v_;}
+
+ std::string
+ string () const;
+
+private:
+ value v_;
+};
+
+std::istream&
+operator>> (std::istream&, schema_format&);
+
+std::ostream&
+operator<< (std::ostream&, schema_format);
+
+//
+//
+struct name_case
+{
+ enum value
+ {
+ upper,
+ lower
+ };
+
+ name_case (value v = value (0)) : v_ (v) {}
+ operator value () const {return v_;}
+
+private:
+ value v_;
+};
+
+std::istream&
+operator>> (std::istream&, name_case&);
+
+//
+//
+struct pgsql_version
+{
+ pgsql_version (unsigned short major, unsigned short minor)
+ : major_ (major), minor_ (minor)
+ {
+ }
+
+ unsigned short
+ ver_major () const
+ {
+ return major_;
+ }
+
+ unsigned short
+ ver_minor () const
+ {
+ return minor_;
+ }
+
+private:
+ unsigned short major_;
+ unsigned short minor_;
+};
+
+inline bool
+operator== (const pgsql_version& x, const pgsql_version& y)
+{
+ return x.ver_major () == y.ver_major ();
+}
+
+inline bool
+operator!= (const pgsql_version& x, const pgsql_version& y)
+{
+ return !(x == y);
+}
+
+inline bool
+operator< (const pgsql_version& x, const pgsql_version& y)
+{
+ return x.ver_major () < y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () < y.ver_minor ());
+}
+
+inline bool
+operator> (const pgsql_version& x, const pgsql_version& y)
+{
+ return x.ver_major () > y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () > y.ver_minor ());
+}
+
+inline bool
+operator<= (const pgsql_version& x, const pgsql_version& y)
+{
+ return !(x > y);
+}
+
+inline bool
+operator>= (const pgsql_version& x, const pgsql_version& y)
+{
+ return !(x < y);
+}
+
+std::istream&
+operator>> (std::istream&, pgsql_version&);
+
+std::ostream&
+operator<< (std::ostream&, pgsql_version);
+
+//
+//
+struct oracle_version
+{
+ oracle_version (unsigned short major, unsigned short minor)
+ : major_ (major), minor_ (minor)
+ {
+ }
+
+ unsigned short
+ ver_major () const
+ {
+ return major_;
+ }
+
+ unsigned short
+ ver_minor () const
+ {
+ return minor_;
+ }
+
+private:
+ unsigned short major_;
+ unsigned short minor_;
+};
+
+inline bool
+operator== (const oracle_version& x, const oracle_version& y)
+{
+ return x.ver_major () == y.ver_major ();
+}
+
+inline bool
+operator!= (const oracle_version& x, const oracle_version& y)
+{
+ return !(x == y);
+}
+
+inline bool
+operator< (const oracle_version& x, const oracle_version& y)
+{
+ return x.ver_major () < y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () < y.ver_minor ());
+}
+
+inline bool
+operator> (const oracle_version& x, const oracle_version& y)
+{
+ return x.ver_major () > y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () > y.ver_minor ());
+}
+
+inline bool
+operator<= (const oracle_version& x, const oracle_version& y)
+{
+ return !(x > y);
+}
+
+inline bool
+operator>= (const oracle_version& x, const oracle_version& y)
+{
+ return !(x < y);
+}
+
+std::istream&
+operator>> (std::istream&, oracle_version&);
+
+std::ostream&
+operator<< (std::ostream&, oracle_version);
+
+//
+//
+struct mssql_version
+{
+ mssql_version (unsigned short major, unsigned short minor)
+ : major_ (major), minor_ (minor)
+ {
+ }
+
+ unsigned short
+ ver_major () const
+ {
+ return major_;
+ }
+
+ unsigned short
+ ver_minor () const
+ {
+ return minor_;
+ }
+
+private:
+ unsigned short major_;
+ unsigned short minor_;
+};
+
+inline bool
+operator== (const mssql_version& x, const mssql_version& y)
+{
+ return x.ver_major () == y.ver_major ();
+}
+
+inline bool
+operator!= (const mssql_version& x, const mssql_version& y)
+{
+ return !(x == y);
+}
+
+inline bool
+operator< (const mssql_version& x, const mssql_version& y)
+{
+ return x.ver_major () < y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () < y.ver_minor ());
+}
+
+inline bool
+operator> (const mssql_version& x, const mssql_version& y)
+{
+ return x.ver_major () > y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () > y.ver_minor ());
+}
+
+inline bool
+operator<= (const mssql_version& x, const mssql_version& y)
+{
+ return !(x > y);
+}
+
+inline bool
+operator>= (const mssql_version& x, const mssql_version& y)
+{
+ return !(x < y);
+}
+
+std::istream&
+operator>> (std::istream&, mssql_version&);
+
+std::ostream&
+operator<< (std::ostream&, mssql_version);
+
+#endif // ODB_OPTION_TYPES_HXX
diff --git a/odb/odb/options.cli b/odb/odb/options.cli
new file mode 100644
index 0000000..17ee438
--- /dev/null
+++ b/odb/odb/options.cli
@@ -0,0 +1,1086 @@
+// file : odb/options.cli
+// license : GNU GPL v3; see accompanying LICENSE file
+
+include <set>;
+include <vector>;
+include <string>;
+include <cstddef>;
+include <cstdint>;
+
+include <odb/option-types.hxx>;
+
+class options
+{
+ //
+ // Wrapper options. These are not passed to the plugin.
+ //
+
+ std::uint64_t --build2-metadata; // Leave undocumented/hidden.
+
+ bool --help {"Print usage information and exit."};
+ bool --version {"Print version and exit."};
+
+ //
+ // C++ preprocessor options. Also not passed to the plugin.
+ //
+ std::vector<std::string> -I
+ {
+ "<dir>",
+ "Add <dir> to the beginning of the list of directories to be searched
+ for included header files."
+ };
+
+ std::vector<std::string> -D
+ {
+ "<name>[=<def>]",
+ "Define macro <name> with definition <def>. If definition is omitted,
+ define <name> to be 1."
+ };
+
+ std::vector<std::string> -U
+ {
+ "<name>",
+ "Cancel any previous definitions of macro <name>, either built-in or
+ provided with the \cb{-D} option."
+ };
+
+ //
+ // Plugin options.
+ //
+ std::vector< ::database> --database | -d
+ {
+ "<db>",
+ "Generate code for the <db> database. Valid values are \cb{mssql},
+ \cb{mysql}, \cb{oracle}, \cb{pgsql}, \cb{sqlite}, and \cb{common}
+ (multi-database mode only)."
+ };
+
+ ::multi_database --multi-database | -m = ::multi_database::disabled
+ {
+ "<type>",
+ "Enable multi-database support and specify its type. Valid values
+ for this option are \cb{static} and \cb{dynamic}.
+
+ In the multi-database mode, options that determine the kind (for
+ example, \cb{--schema-format}), names (for example,
+ \cb{--odb-file-suffix}), or content (for example, prologue and
+ epilogue options) of the output files can be prefixed with the
+ database name followed by a colon, for example, \cb{mysql:value}.
+ This restricts the value of such an option to only apply to
+ generated files corresponding to this database."
+ };
+
+ ::database --default-database
+ {
+ "<db>",
+ "When static multi-database support is used, specify the database that
+ should be made the default. When dynamic multi-database support is used,
+ \cb{common} is always made the default database."
+ };
+
+ bool --generate-query | -q
+ {
+ "Generate query support code. Without this support you cannot use views
+ and can only load objects via their ids."
+ };
+
+ bool --generate-prepared
+ {
+ "Generate prepared query execution support code."
+ };
+
+ bool --omit-unprepared
+ {
+ "Omit un-prepared (once-off) query execution support code."
+ };
+
+ bool --generate-session | -e
+ {
+ "Generate session support code. With this option session support will
+ be enabled by default for all the persistent classes except those for
+ which it was explicitly disabled using the \cb{db session} pragma."
+ };
+
+ bool --generate-schema | -s
+ {
+ "Generate the database schema. The database schema contains SQL
+ statements that create database tables necessary to store persistent
+ classes defined in the file being compiled. Note that by applying
+ this schema, all the existing information stored in such tables will
+ be lost.
+
+ Depending on the database being used (\cb{--database} option), the
+ schema is generated either as a standalone SQL file or embedded into
+ the generated C++ code. By default the SQL file is generated for
+ the MySQL, PostgreSQL, Oracle, and Microsoft SQL Server databases
+ and the schema is embedded into the C++ code for the SQLite database.
+ Use the \cb{--schema-format} option to alter the default schema format.
+
+ If database schema evolution support is enabled (that is, the object
+ model version is specified), then this option also triggers the
+ generation of database schema migration statements, again either as
+ standalong SQL files or embedded into the generated C++ code. You can
+ suppress the generation of schema migration statements by specifying
+ the \cb{--suppress-migration} option."
+ };
+
+ bool --generate-schema-only
+ {
+ "Generate only the database schema. Note that this option is only valid
+ when generating schema as a standalone SQL file (see \cb{--schema-format}
+ for details)."
+ };
+
+ bool --suppress-migration
+ {
+ "Suppress the generation of database schema migration statements."
+ };
+
+ bool --suppress-schema-version
+ {
+ "Suppress the generation of schema version table. If you specify this
+ option then you are also expected to manually specify the database
+ schema version and migration state at runtime using the
+ \cb{odb::database::schema_version()} function."
+ };
+
+ database_map<qname> --schema-version-table
+ {
+ "<name>",
+ "Specify the alternative schema version table name instead of the default
+ \cb{schema_version}. If you specify this option then you are also
+ expected to manually specify the schema version table name at runtime
+ using the \cb{odb::database::schema_version_table()} function. The table
+ name can be qualified."
+ };
+
+ database_map<std::set< ::schema_format> > --schema-format
+ {
+ "<format>",
+ "Generate the database schema in the specified format. Pass \cb{sql} as
+ <format> to generate the database schema as a standalone SQL file or
+ pass \cb{embedded} to embed the schema into the generated C++ code.
+ The \cb{separate} value is similar to \cb{embedded} except the schema
+ creation code is generated into a separate C++ file (\cb{name-schema.cxx}
+ by default). This value is primarily useful if you want to place the
+ schema creation functionality into a separate program or library.
+ Repeat this option to generate the same database schema in multiple
+ formats."
+ };
+
+ bool --omit-drop
+ {
+ "Omit \cb{DROP} statements from the generated database schema."
+ };
+
+ bool --omit-create
+ {
+ "Omit \cb{CREATE} statements from the generated database schema."
+ };
+
+ database_map<std::string> --schema-name
+ {
+ "<name>",
+ "Use <name> as the database schema name. Schema names are primarily
+ used to distinguish between multiple embedded schemas in the schema
+ catalog. They are not to be confused with database schemas (database
+ namespaces) which are specified with the \cb{--schema} option. If
+ this option is not specified, the empty name, which is the default
+ schema name, is used."
+ };
+
+ database_map<deferrable> --fkeys-deferrable-mode
+ {
+ "<m>",
+ "Use constraint checking mode <m> in foreign keys generated for object
+ relationships. Valid values for this option are \cb{not_deferrable},
+ \cb{immediate}, and \cb{deferred} (default). MySQL and SQL Server do
+ not support deferrable foreign keys and for these databases such keys
+ are generated commented out. Other foreign keys generated by the ODB
+ compiler (such as the ones used to support containers and polymorphic
+ hierarchies) are always generated as not deferrable.
+
+ Note also that if you use either \cb{not_deferrable} or \cb{immediate}
+ mode, then the order in which you persist, update, and erase objects
+ within a transaction becomes important."
+ };
+
+ std::string --default-pointer = "*"
+ {
+ "<ptr>",
+ "Use <ptr> as the default pointer for persistent objects and views.
+ Objects and views that do not have a pointer assigned with the
+ \cb{db pointer} pragma will use this pointer by default. The value
+ of this option can be '\cb{*}' which denotes the raw pointer and is
+ the default, or qualified name of a smart pointer class template,
+ for example, \cb{std::shared_ptr}. In the latter case, the ODB compiler
+ constructs the object or view pointer by adding a single template
+ argument of the object or view type to the qualified name, for example
+ \cb{std::shared_ptr<object>}. The ODB runtime uses object and view
+ pointers to return, and, in case of objects, pass and cache
+ dynamically allocated instances of object and view types.
+
+ Except for the raw pointer and the standard smart pointers defined
+ in the \cb{<memory>} header file, you are expected to include the
+ definition of the default pointer at the beginning of the generated
+ header file. There are two common ways to achieve this: you can either
+ include the necessary header in the file being compiled or you can use
+ the \cb{--hxx-prologue} option to add the necessary \cb{#include}
+ directive to the generated code."
+ };
+
+ std::string --session-type = "odb::session"
+ {
+ "<type>",
+ "Use <type> as the alternative session type instead of the default
+ \cb{odb::session}. This option can be used to specify a custom
+ session implementation to be use by the persistent classes. Note
+ that you will also need to include the definition of the custom
+ session type into the generated header file. This is normally
+ achieved with the \cb{--hxx-prologue*} options."
+ };
+
+ // The following option is "fake" in that it is actually handled by
+ // argv_file_scanner. We have it here to get the documentation.
+ //
+ std::string --profile | -p
+ {
+ "<name>",
+ "Specify a profile that should be used during compilation. A
+ profile is an options file. The ODB compiler first looks for
+ a database-specific version with the name constructed by appending
+ the \cb{-}\ci{database}\cb{.options} suffix to <name>, where
+ \ci{database} is the database name as specified with the
+ \cb{--database} option. If this file is not found, then the
+ ODB compiler looks for a database-independant version with the
+ name constructed by appending just the \cb{.options} suffix.
+
+ The profile options files are searched for in the same set of
+ directories as C++ headers included with the \cb{#include <...>}
+ directive (built-in paths plus those specified with the \cb{-I}
+ options). The options file is first searched for in the directory
+ itself and then in its \cb{odb/} subdirectory.
+
+ For the format of the options file refer to the \cb{--options-file}
+ option below. You can repeat this option to specify more than one
+ profile."
+ };
+
+ bool --at-once
+ {
+ "Generate code for all the input files as well as for all the files that
+ they include at once. The result is a single set of source/schema files
+ that contain all the generated code. If more than one input file is
+ specified together with this option, then the \cb{--input-name} option
+ must also be specified in order to provide the base name for the output
+ files. In this case, the directory part of such a base name is used as
+ the location of the combined file. This can be important for the
+ \cb{#include} directive resolution."
+ };
+
+ database_map<qname> --schema
+ {
+ "<schema>",
+ "Specify a database schema (database namespace) that should be
+ assigned to the persistent classes in the file being compiled.
+ Database schemas are not to be confused with database schema
+ names (schema catalog names) which are specified with the
+ \cb{--schema-name} option."
+ };
+
+ // Export control.
+ //
+ database_map<std::string> --export-symbol
+ {
+ "<symbol>",
+ "Insert <symbol> in places where DLL export/import control statements
+ (\cb{__declspec(dllexport/dllimport)}) are necessary. See also the
+ \cb{--extern-symbol} option below."
+ };
+
+ database_map<std::string> --extern-symbol
+ {
+ "<symbol>",
+ "If <symbol> is defined, insert it in places where a template
+ instantiation must be declared \cb{extern}. This option is normally
+ used together with \cb{--export-symbol} when both multi-database
+ support and queries are enabled."
+ };
+
+ // Language.
+ //
+ // @@ TODO: perhaps we should switch to latest to match how we build
+ // runtime by default?
+ //
+ cxx_version --std = cxx_version::cxx98
+ {
+ "<version>",
+ "Specify the C++ standard that should be used during compilation.
+ Valid values are \cb{c++98} (default), \cb{c++11}, \cb{c++14},
+ \cb{c++17}, and \cb{c++20}."
+ };
+
+ // Diagnostics.
+ //
+ bool --warn-hard-add
+ {
+ "Warn about hard-added data members."
+ };
+
+ bool --warn-hard-delete
+ {
+ "Warn about hard-deleted data members and persistent classes."
+ };
+
+ bool --warn-hard
+ {
+ "Warn about both hard-added and hard-deleted data members and
+ persistent classes."
+ };
+
+ // Output.
+ //
+ std::string --output-dir | -o
+ {
+ "<dir>",
+ "Write the generated files to <dir> instead of the current directory."
+ };
+
+ std::string --input-name
+ {
+ "<name>",
+ "Use <name> instead of the input file to derive the names of the
+ generated files. If the \cb{--at-once} option is specified, then
+ the directory part of <name> is used as the location of the
+ combined file. Refer to the \cb{--at-once} option for details."
+ };
+
+ database_map<std::string> --changelog
+ {
+ "<file>",
+ "Read/write changelog from/to <file> instead of the default changelog
+ file. The default changelog file name is derived from the input file
+ name and it is placed into the same directory as the input file. Note
+ that the \cb{--output-dir} option does not affect the changelog file
+ location. In other words, by default, the changelog file is treated
+ as another input rather than output even though the ODB compiler may
+ modify it. Use the \cb{--changelog-in} and \cb{--changelog-out}
+ options to specify different input and output chaneglog files."
+ };
+
+ database_map<std::string> --changelog-in
+ {
+ "<file>",
+ "Read changelog from <file> instead of the default changelog file. If
+ this option is specified, then you must also specify the output
+ chanegelog file with \cb{--changelog-out}."
+ };
+
+ database_map<std::string> --changelog-out
+ {
+ "<file>",
+ "Write changelog to <file> instead of the default changelog file. If
+ this option is specified, then you must also specify the input
+ chanegelog file with \cb{--changelog-in}."
+ };
+
+ database_map<std::string> --changelog-dir
+ {
+ "<dir>",
+ "Use <dir> instead of the input file directory as the changelog file
+ directory. This directory is also added to changelog files specified
+ with the \cb{--changelog}, \cb{--changelog-in}, and \cb{--changelog-in}
+ options unless they are absolute paths."
+ };
+
+ bool --init-changelog
+ {
+ "Force re-initialization of the changelog even if one exists (all the
+ existing change history will be lost). This option is primarily useful
+ for automated testing."
+ };
+
+ database_map<std::string> --odb-file-suffix
+ {
+ "<suffix>",
+ "Use <suffix> to construct the names of the generated C++ files. In
+ the single-database mode the default value for this option is \cb{-odb}.
+ In the multi-database mode it is \cb{-odb} for the files corresponding
+ to the \cb{common} database and \c{\b{-odb-}\i{db}} (where \ci{db} is
+ the database name) for other databases."
+ };
+
+ database_map<std::string> --sql-file-suffix
+ {
+ "<suffix>",
+ "Use <suffix> to construct the name of the generated schema SQL file.
+ In the single-database mode by default no suffix is used. In the
+ multi-database mode the default value for this option is
+ \c{\b{-}\i{db}} (where \ci{db} is the database name)."
+ };
+
+ database_map<std::string> --schema-file-suffix
+ {
+ "<suffix>",
+ "Use <suffix> to construct the name of the generated schema C++ source
+ file. In the single-database mode the default value for this option is
+ \cb{-schema}. In the multi-database mode it is \c{\b{-schema-}\i{db}}
+ (where \ci{db} is the database name). See the \cb{--schema-format}
+ option for details."
+ };
+
+ database_map<std::string> --changelog-file-suffix
+ {
+ "<sfx>",
+ "Use <sfx> to construct the name of the changelog file. In the
+ single-database mode by default no suffix is used. In the
+ multi-database mode the default value for this option is
+ \c{\b{-}\i{db}} (where \ci{db} is the database name)."
+ };
+
+ std::string --hxx-suffix = ".hxx"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.hxx} to construct the name of
+ the generated C++ header file."
+ };
+
+ std::string --ixx-suffix = ".ixx"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.ixx} to construct the name of
+ the generated C++ inline file."
+ };
+
+ std::string --cxx-suffix = ".cxx"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.cxx} to construct the name of
+ the generated C++ source file."
+ };
+
+ std::string --sql-suffix = ".sql"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.sql} to construct the name of
+ the generated database schema file."
+ };
+
+ std::string --changelog-suffix = ".xml"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.xml} to construct the name of
+ the changelog file."
+ };
+
+ // Prologues.
+ //
+ database_map<std::vector<std::string> > --hxx-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated C++ header file."
+ };
+
+ database_map<std::vector<std::string> > --ixx-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated C++ inline file."
+ };
+
+ database_map<std::vector<std::string> > --cxx-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated C++ source file."
+ };
+
+ database_map<std::vector<std::string> > --schema-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated schema C++ source file."
+ };
+
+ database_map<std::vector<std::string> > --sql-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated database schema file."
+ };
+
+ database_map<std::vector<std::string> > --migration-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated database migration file."
+ };
+
+ // Interludes.
+ //
+ database_map<std::vector<std::string> > --sql-interlude
+ {
+ "<text>",
+ "Insert <text> after all the \cb{DROP} and before any \cb{CREATE}
+ statements in the generated database schema file."
+ };
+
+ // Epilogues.
+ //
+ database_map<std::vector<std::string> > --hxx-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated C++ header file."
+ };
+
+ database_map<std::vector<std::string> > --ixx-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated C++ inline file."
+ };
+
+ database_map<std::vector<std::string> > --cxx-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated C++ source file."
+ };
+
+ database_map<std::vector<std::string> > --schema-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated schema C++ source file."
+ };
+
+ database_map<std::vector<std::string> > --sql-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated database schema file."
+ };
+
+ database_map<std::vector<std::string> > --migration-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated database migration file."
+ };
+
+ // Prologue files.
+ //
+ database_map<std::vector<std::string> > --hxx-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated C++
+ header file."
+ };
+
+ database_map<std::vector<std::string> > --ixx-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated C++
+ inline file."
+ };
+
+ database_map<std::vector<std::string> > --cxx-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated C++
+ source file."
+ };
+
+ database_map<std::vector<std::string> > --schema-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated schema
+ C++ source file."
+ };
+
+ database_map<std::vector<std::string> > --sql-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated
+ database schema file."
+ };
+
+ database_map<std::vector<std::string> > --migration-prologue-file
+ {
+ "<f>",
+ "Insert the content of file <f> at the beginning of the generated database
+ migration file."
+ };
+
+ // Interlude files.
+ //
+ database_map<std::vector<std::string> > --sql-interlude-file
+ {
+ "<file>",
+ "Insert the content of <file> after all the \cb{DROP} and before any
+ \cb{CREATE} statements in the generated database schema file."
+ };
+
+ // Epilogue files.
+ //
+ database_map<std::vector<std::string> > --hxx-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated C++ header
+ file."
+ };
+
+ database_map<std::vector<std::string> > --ixx-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated C++ inline
+ file."
+ };
+
+ database_map<std::vector<std::string> > --cxx-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated C++ source
+ file."
+ };
+
+ database_map<std::vector<std::string> > --schema-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated schema C++
+ source file."
+ };
+
+ database_map<std::vector<std::string> > --sql-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated database
+ schema file."
+ };
+
+ database_map<std::vector<std::string> > --migration-epilogue-file
+ {
+ "<f>",
+ "Insert the content of file <f> at the end of the generated database
+ migration file."
+ };
+
+ // ODB compilation prologue/epilogue.
+ //
+ database_map<std::vector<std::string> > --odb-prologue
+ {
+ "<text>",
+ "Compile <text> before the input header file. This option allows you
+ to add additional declarations, such as custom traits specializations,
+ to the ODB compilation process."
+ };
+
+ database_map<std::vector<std::string> > --odb-prologue-file
+ {
+ "<file>",
+ "Compile <file> contents before the input header file. Prologue files
+ are compiled after all the prologue text fragments (\cb{--odb-prologue}
+ option)."
+ };
+
+ database_map<std::vector<std::string> > --odb-epilogue
+ {
+ "<text>",
+ "Compile <text> after the input header file. This option allows you
+ to add additional declarations, such as custom traits specializations,
+ to the ODB compilation process."
+ };
+
+ database_map<std::vector<std::string> > --odb-epilogue-file
+ {
+ "<file>",
+ "Compile <file> contents after the input header file. Epilogue files
+ are compiled after all the epilogue text fragments (\cb{--odb-epilogue}
+ option)."
+ };
+
+ // SQL names.
+ //
+ database_map<std::string> --table-prefix
+ {
+ "<prefix>",
+ "Add <prefix> to table names and, for databases that have global index
+ and/or foreign key names, to those names as well. The prefix is added to
+ both names that were specified with the \cb{db table} and \cb{db index}
+ pragmas and those that were automatically derived from class and data
+ member names. If you require a separator, such as an underscore,
+ between the prefix and the name, then you should include it into the
+ prefix value."
+ };
+
+ database_map<std::string> --index-suffix
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{_i} to construct index names.
+ The suffix is only added to names that were automatically derived from
+ data member names. If you require a separator, such as an underscore,
+ between the name and the suffix, then you should include it into the
+ suffix value."
+ };
+
+ database_map<std::string> --fkey-suffix
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{_fk} to construct foreign key
+ names. If you require a separator, such as an underscore, between the
+ name and the suffix, then you should include it into the suffix value."
+ };
+
+ database_map<std::string> --sequence-suffix
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{_seq} to construct sequence
+ names. If you require a separator, such as an underscore, between the
+ name and the suffix, then you should include it into the suffix value."
+ };
+
+ database_map<name_case> --sql-name-case
+ {
+ "<case>",
+ "Convert all automatically-derived SQL names to upper or lower case.
+ Valid values for this option are \cb{upper} and \cb{lower}."
+ };
+
+ database_map<std::vector<std::string> > --table-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions that is used to
+ transform automatically-derived table names. See the SQL NAME
+ TRANSFORMATIONS section below for details."
+ };
+
+ database_map<std::vector<std::string> > --column-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions that is used to
+ transform automatically-derived column names. See the SQL NAME
+ TRANSFORMATIONS section below for details."
+ };
+
+ database_map<std::vector<std::string> > --index-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions that is used to
+ transform automatically-derived index names. See the SQL NAME
+ TRANSFORMATIONS section below for details."
+ };
+
+ database_map<std::vector<std::string> > --fkey-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions that is used to
+ transform automatically-derived foreign key names. See the SQL NAME
+ TRANSFORMATIONS section below for details."
+ };
+
+ database_map<std::vector<std::string> > --sequence-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions that is used to
+ transform automatically-derived sequence names. See the SQL NAME
+ TRANSFORMATIONS section below for details."
+ };
+
+ database_map<std::vector<std::string> > --statement-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions that is used to
+ transform automatically-derived prepared statement names. See
+ the SQL NAME TRANSFORMATIONS section below for details."
+ };
+
+ database_map<std::vector<std::string> > --sql-name-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions that is used to
+ transform all automatically-derived SQL names. See the SQL NAME
+ TRANSFORMATIONS section below for details."
+ };
+
+ bool --sql-name-regex-trace
+ {
+ "Trace the process of applying regular expressions specified with the
+ SQL name \cb{--*-regex} options. Use this option to find out why your
+ regular expressions don't do what you expected them to do."
+ };
+
+ // Accessor/modifier options.
+ //
+ std::vector<std::string> --accessor-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions used to transform
+ data member names to function names when searching for a suitable
+ accessor function. The argument to this option is a Perl-like regular
+ expression in the form \c{\b{/}\i{pattern}\b{/}\i{replacement}\b{/}}.
+ Any character can be used as a delimiter instead of '\cb{/}' and the
+ delimiter can be escaped inside \ci{pattern} and \ci{replacement}
+ with a backslash (\cb{\\}). You can specify multiple regular
+ expressions by repeating this option.
+
+ All the regular expressions are tried in the order specified and
+ the first expression that produces a suitable accessor function is
+ used. Each expression is tried twice: first with the actual member
+ name and then with the member's \i{public name} which is obtained by
+ removing the common member name decorations, such as leading and
+ trailing underscores, the \cb{m_} prefix, etc. The ODB compiler also
+ includes a number of built-in expressions for commonly used accessor
+ names, such as \cb{get_foo}, \cb{getFoo}, \cb{getfoo}, and just
+ \cb{foo}. The built-in expressions are tried last.
+
+ As an example, the following expression transforms data members with
+ public names in the form \cb{foo} to accessor names in the form
+ \cb{GetFoo}:
+
+ \cb{/(.+)/Get\\u$1/}
+
+ See also the REGEX AND SHELL QUOTING section below."
+ };
+
+ bool --accessor-regex-trace
+ {
+ "Trace the process of applying regular expressions specified with the
+ \cb{--accessor-regex} option. Use this option to find out why your
+ regular expressions don't do what you expected them to do."
+ };
+
+ std::vector<std::string> --modifier-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions used to transform
+ data member names to function names when searching for a suitable
+ modifier function. The argument to this option is a Perl-like regular
+ expression in the form \c{\b{/}\i{pattern}\b{/}\i{replacement}\b{/}}.
+ Any character can be used as a delimiter instead of '\cb{/}' and the
+ delimiter can be escaped inside \ci{pattern} and \ci{replacement}
+ with a backslash (\cb{\\}). You can specify multiple regular
+ expressions by repeating this option.
+
+ All the regular expressions are tried in the order specified and
+ the first expression that produces a suitable modifier function is
+ used. Each expression is tried twice: first with the actual member
+ name and then with the member's \i{public name} which is obtained by
+ removing the common member name decorations, such as leading and
+ trailing underscores, the \cb{m_} prefix, etc. The ODB compiler also
+ includes a number of built-in expressions for commonly used modifier
+ names, such as \cb{set_foo}, \cb{setFoo}, \cb{setfoo}, and just
+ \cb{foo}. The built-in expressions are tried last.
+
+ As an example, the following expression transforms data members with
+ public names in the form \cb{foo} to modifier names in the form
+ \cb{SetFoo}:
+
+ \cb{/(.+)/Set\\u$1/}
+
+ See also the REGEX AND SHELL QUOTING section below."
+ };
+
+ bool --modifier-regex-trace
+ {
+ "Trace the process of applying regular expressions specified with the
+ \cb{--modifier-regex} option. Use this option to find out why your
+ regular expressions don't do what you expected them to do."
+ };
+
+ // Include options.
+ //
+ bool --include-with-brackets
+ {
+ "Use angle brackets (<>) instead of quotes (\"\") in the generated
+ \cb{#include} directives."
+ };
+
+ std::string --include-prefix
+ {
+ "<prefix>",
+ "Add <prefix> to the generated \cb{#include} directive paths."
+ };
+
+ std::vector<std::string> --include-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions used to transform
+ generated \cb{#include} directive paths. The argument to this option
+ is a Perl-like regular expression in the form
+ \c{\b{/}\i{pattern}\b{/}\i{replacement}\b{/}}. Any character can be
+ used as a delimiter instead of '\cb{/}' and the delimiter can be escaped
+ inside \ci{pattern} and \ci{replacement} with a backslash (\cb{\\}).
+ You can specify multiple regular expressions by repeating this option.
+ All the regular expressions are tried in the order specified and the
+ first expression that matches is used.
+
+ As an example, the following expression transforms include paths in
+ the form \cb{foo/bar-odb.h} to paths in the form
+ \cb{foo/generated/bar-odb.h}:
+
+ \cb{%foo/(.+)-odb.h%foo/generated/$1-odb.h%}
+
+ See also the REGEX AND SHELL QUOTING section below."
+ };
+
+ bool --include-regex-trace
+ {
+ "Trace the process of applying regular expressions specified with the
+ \cb{--include-regex} option. Use this option to find out why your
+ regular expressions don't do what you expected them to do."
+ };
+
+ std::string --guard-prefix
+ {
+ "<prefix>",
+ "Add <prefix> to the generated header inclusion guards. The prefix is
+ transformed to upper case and characters that are illegal in a
+ preprocessor macro name are replaced with underscores."
+ };
+
+ bool --show-sloc
+ {
+ "Print the number of generated physical source lines of code (SLOC)."
+ };
+
+ std::size_t --sloc-limit
+ {
+ "<num>",
+ "Check that the number of generated physical source lines of code (SLOC)
+ does not exceed <num>."
+ };
+
+ // The following option is "fake" in that it is actually handled by
+ // argv_file_scanner. We have it here to get the documentation.
+ //
+ std::string --options-file
+ {
+ "<file>",
+ "Read additional options from <file>. Each option should appear on a
+ separate line optionally followed by space or equal sign (\cb{=}) and an
+ option value. Empty lines and lines starting with \cb{#} are ignored.
+ Option values can be enclosed in double (\cb{\"}) or single (\cb{'})
+ quotes to preserve leading and trailing whitespaces as well as to specify
+ empty values. If the value itself contains trailing or leading quotes,
+ enclose it with an extra pair of quotes, for example \cb{'\"x\"'}.
+ Non-leading and non-trailing quotes are interpreted as being part of the
+ option value.
+
+ The semantics of providing options in a file is equivalent to providing
+ the same set of options in the same order on the command line at the
+ point where the \cb{--options-file} option is specified except that
+ the shell escaping and quoting is not required. Repeat this option
+ to specify more than one options file."
+ };
+
+ std::vector<std::string> -x
+ {
+ "<option>",
+ "Pass <option> to the underlying C++ compiler (\cb{g++}). The <option>
+ value that doesn't start with '\cb{-}' is considered the \cb{g++}
+ executable name."
+ };
+
+ bool -v {"Print the commands executed to run the stages of compilation."};
+
+ bool --trace {"Trace the compilation process."};
+
+ //
+ // MySQL-specific options.
+ //
+
+ std::string --mysql-engine = "InnoDB"
+ {
+ "<engine>",
+ "Use <engine> instead of the default \cb{InnoDB} in the generated
+ database schema file. For more information on the storage engine
+ options see the MySQL documentation. If you would like to use the
+ database-default engine, pass \cb{default} as the value for this
+ option."
+ };
+
+ //
+ // SQLite-specific options.
+ //
+
+ bool --sqlite-override-null
+ {
+ "Make all columns in the generated database schema allow \cb{NULL}
+ values. This is primarily useful in schema migration since SQLite
+ does not support dropping of columns. By making all columns \cb{NULL}
+ we can later \"delete\" them by setting their values to \cb{NULL}.
+ Note that this option overrides even the \cb{not_null} pragma."
+ };
+
+ bool --sqlite-lax-auto-id
+ {
+ "Do not force monotonically increasing automatically-assigned
+ object ids. In this mode the generated database schema omits the
+ \cb{AUTOINCREMENT} keyword which results in faster object persistence
+ but may lead to automatically-assigned ids not being in a strictly
+ ascending order. Refer to the SQLite documentation for details."
+ };
+
+ //
+ // PostgreSQL-specific options.
+ //
+
+ ::pgsql_version --pgsql-server-version (7, 4)
+ {
+ "<ver>",
+ "Specify the minimum PostgreSQL server version with which the generated
+ C++ code and schema will be used. This information is used to enable
+ version-specific optimizations and workarounds in the generated C++
+ code and schema. The version must be in the \c{\i{major}\b{.}\i{minor}}
+ form, for example, \cb{9.1}. If this option is not specified, then
+ \cb{7.4} or later is assumed."
+ };
+
+ //
+ // Oracle-specific options.
+ //
+
+ ::oracle_version --oracle-client-version (10, 1)
+ {
+ "<ver>",
+ "Specify the minimum Oracle client library (OCI) version with which the
+ generated C++ code will be linked. This information is used to enable
+ version-specific optimizations and workarounds in the generated C++
+ code. The version must be in the \c{\i{major}\b{.}\i{minor}} form,
+ for example, \cb{11.2}. If this option is not specified, then
+ \cb{10.1} or later is assumed."
+ };
+
+ bool --oracle-warn-truncation
+ {
+ "Warn about SQL names that are longer than 30 characters and are
+ therefore truncated. Note that during database schema generation
+ (\cb{--generate-schema}) ODB detects when such truncations lead
+ to name conflicts and issues diagnostics even without this option
+ specified."
+ };
+
+ //
+ // SQL Server-specific options.
+ //
+
+ ::mssql_version --mssql-server-version (10, 0)
+ {
+ "<ver>",
+ "Specify the minimum SQL Server server version with which the generated
+ C++ code and schema will be used. This information is used to enable
+ version-specific optimizations and workarounds in the generated C++
+ code and schema. The version must be in the \c{\i{major}\b{.}\i{minor}}
+ form, for example, \cb{9.0} (SQL Server 2005), \cb{10.5} (2008R2), or
+ \cb{11.0} (2012). If this option is not specified, then \cb{10.0} (SQL
+ Server 2008) or later is assumed."
+ };
+
+ unsigned int --mssql-short-limit = 1024
+ {
+ "<size>",
+ "Specify the short data size limit. If a character, national character, or
+ binary data type has a maximum length (in bytes) less than or equal to
+ this limit, then it is treated as \i{short data}, otherwise it is \i{long
+ data}. For short data ODB pre-allocates an intermediate buffer of
+ the maximum size and binds it directly to a parameter or result
+ column. This way the underlying API (ODBC) can read/write directly
+ from/to this buffer. In the case of long data, the data is read/written
+ in chunks using the \cb{SQLGetData()}/\cb{SQLPutData()} ODBC functions.
+ While the long data approach reduces the amount of memory used by the
+ application, it may require greater CPU resources. The default short
+ data limit is 1024 bytes. When setting a custom short data limit, make
+ sure that it is sufficiently large so that no object id in the
+ application is treated as long data."
+ };
+};
diff --git a/odb/odb/parser.cxx b/odb/odb/parser.cxx
new file mode 100644
index 0000000..c026808
--- /dev/null
+++ b/odb/odb/parser.cxx
@@ -0,0 +1,2403 @@
+// file : odb/parser.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx> // Keep it first.
+
+#include <set>
+#include <map>
+#include <vector>
+#include <string>
+#include <cassert>
+#include <sstream>
+#include <iostream>
+
+#include <odb/diagnostics.hxx>
+#include <odb/parser.hxx>
+#include <odb/semantics.hxx>
+
+using namespace std;
+using namespace semantics;
+
+class parser::impl
+{
+public:
+ typedef parser::failed failed;
+
+ impl (options const&, loc_pragmas&, ns_loc_pragmas&, decl_pragmas&);
+
+ unique_ptr<unit>
+ parse (tree global_scope, path const& main_file);
+
+private:
+ typedef semantics::access access;
+
+ // Extended GCC tree declaration that is either a GCC tree
+ // declaration, a virtual declaration, or a pragma. If it is
+ // a pragma, then the assoc flag indicated whether this pragma
+ // has been associated with a declaration. Otherwise, the assoc
+ // flag indicates whether pragmas have been associated with this
+ // declaration (we use this to ignore certain declarations for
+ // pragma association purposes, e.g., the anonymous type in
+ // struct {...} m_).
+ //
+ struct tree_decl
+ {
+ tree decl;
+ virt_declaration const* vdecl;
+ pragma const* prag;
+ mutable bool assoc; // Allow modification via std::set iterator.
+
+ tree_decl (tree d): decl (d), vdecl (0), prag (0), assoc (false) {}
+ tree_decl (virt_declaration const& d)
+ : decl (0), vdecl (&d), prag (0), assoc (false) {}
+ tree_decl (pragma const& p)
+ : decl (0), vdecl (0), prag (&p), assoc (false) {}
+
+ bool
+ operator< (tree_decl const& y) const;
+ };
+
+ typedef multiset<tree_decl> decl_set;
+
+private:
+ void
+ collect (tree ns);
+
+ void
+ emit ();
+
+ // Emit a type declaration. This is either a named class-type definition/
+ // declaration or a typedef. In the former case the function returns the
+ // newly created type node. In the latter case it returns 0.
+ //
+ type*
+ emit_type_decl (tree);
+
+ // Emit a template declaration.
+ //
+ void
+ emit_template_decl (tree);
+
+ class_template&
+ emit_class_template (tree, bool stub = false);
+
+ union_template&
+ emit_union_template (tree, bool stub = false);
+
+ template <typename T>
+ T&
+ emit_class (tree, path const& f, size_t l, size_t c, bool stub = false);
+
+ template <typename T>
+ T&
+ emit_union (tree, path const& f, size_t l, size_t c, bool stub = false);
+
+ // Access is not used when creating a stub.
+ //
+ enum_&
+ emit_enum (tree,
+ access,
+ path const& f,
+ size_t l,
+ size_t c,
+ bool stub = false);
+
+ // Create new or find existing semantic graph type.
+ //
+ type&
+ emit_type (tree, access, path const& f, size_t l, size_t c);
+
+ type&
+ create_type (tree, access, path const& f, size_t l, size_t c);
+
+ string
+ emit_type_name (tree, bool direct = true);
+
+
+ //
+ // Pragma handling.
+ //
+ void
+ add_pragma (node&, pragma const&);
+
+ // Process positioned and named pragmas.
+ //
+ void
+ process_pragmas (declaration const&,
+ node&,
+ string const& name,
+ decl_set::const_iterator begin,
+ decl_set::const_iterator cur,
+ decl_set::const_iterator end);
+
+ // Process named pragmas only.
+ //
+ void
+ process_named_pragmas (declaration const&, node&);
+
+ void
+ diagnose_unassoc_pragmas (decl_set const&);
+
+ // Return declaration's fully-qualified scope name (e.g., ::foo::bar).
+ //
+ string
+ fq_scope (tree);
+
+ // Return declaration's access.
+ //
+ access
+ decl_access (tree decl)
+ {
+ // Note that TREE_PUBLIC() returns something other than what we need.
+ //
+ if (TREE_PRIVATE (decl))
+ return access::private_;
+
+ if (TREE_PROTECTED (decl))
+ return access::protected_;
+
+ return access::public_;
+ }
+
+ //
+ //
+ template <typename T>
+ void
+ define_fund (tree);
+
+private:
+ options const& ops_;
+ loc_pragmas& loc_pragmas_;
+ ns_loc_pragmas& ns_loc_pragmas_;
+ decl_pragmas& decl_pragmas_;
+
+ bool trace;
+ ostream& ts;
+
+ unit* unit_;
+ scope* scope_;
+ vector<scope*> class_scopes_; // Current hierarchy of class-like scopes.
+ size_t error_;
+
+ decl_set decls_;
+
+ typedef map<location_t, tree> decl_map;
+ decl_map all_decls_;
+};
+
+bool parser::impl::tree_decl::
+operator< (tree_decl const& y) const
+{
+ location_t xl, yl;
+ int xb (0), yb (0);
+
+ if (decl != 0)
+ xl = real_source_location (decl);
+ else if (vdecl != 0)
+ {
+ xl = vdecl->ord;
+ xb = vdecl->ord_bias;
+ }
+ else
+ xl = prag->loc;
+
+ if (y.decl != 0)
+ yl = real_source_location (y.decl);
+ else if (y.vdecl != 0)
+ {
+ yl = y.vdecl->ord;
+ yb = y.vdecl->ord_bias;
+ }
+ else
+ yl = y.prag->loc;
+
+ // If both are virtual and at the same location, use the definition
+ // location to order them.
+ //
+ if (vdecl != 0 && y.vdecl != 0 && xl == yl && xb == yb)
+ {
+ xl = vdecl->loc;
+ yl = y.vdecl->loc;
+ }
+
+ return xl < yl || (xl == yl && xb < yb);
+}
+
+//
+// Function templates.
+//
+
+template <typename T>
+void parser::impl::
+define_fund (tree t)
+{
+ t = TYPE_MAIN_VARIANT (t);
+ char const* name (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (t))));
+
+ T& node (unit_->new_fund_node<T> (t));
+ unit_->new_edge<defines> (*scope_, node, name);
+ unit_->insert (t, node);
+
+ process_named_pragmas (t, node);
+}
+
+template <typename T>
+T& parser::impl::
+emit_class (tree c, path const& file, size_t line, size_t clmn, bool stub)
+{
+ c = TYPE_MAIN_VARIANT (c);
+
+ // See if there is a stub already for this type.
+ //
+ T* c_node (0);
+
+ if (node* n = unit_->find (c))
+ {
+ c_node = &dynamic_cast<T&> (*n);
+ }
+ else
+ {
+ c_node = &unit_->new_node<T> (file, line, clmn, c);
+ unit_->insert (c, *c_node);
+ }
+
+ if (stub || !COMPLETE_TYPE_P (c))
+ return *c_node;
+
+ // Note: "include" the base classes into the class scope (see comment for
+ // self-typedefs in emit_type_decl()).
+ //
+ class_scopes_.push_back (c_node);
+
+ // Traverse base information.
+ //
+ tree bis (TYPE_BINFO (c));
+ size_t n (bis ? BINFO_N_BASE_BINFOS (bis) : 0);
+
+ for (size_t i (0); i < n; i++)
+ {
+ tree bi (BINFO_BASE_BINFO (bis, i));
+ access a (access::public_);
+
+ if (BINFO_BASE_ACCESSES (bis))
+ {
+ tree ac (BINFO_BASE_ACCESS (bis, i));
+
+ if (ac == NULL_TREE || ac == access_public_node)
+ {
+ a = access::public_;
+ }
+ else if (ac == access_protected_node)
+ {
+ a = access::protected_;
+ }
+ else
+ {
+ assert (ac == access_private_node);
+ a = access::private_;
+ }
+ }
+
+ bool virt (BINFO_VIRTUAL_P (bi));
+ tree base (TYPE_MAIN_VARIANT (BINFO_TYPE (bi)));
+
+ // Find the corresponding graph node. If we cannot find one then
+ // the base is a template instantiation since an ordinary class
+ // has to be defined (complete) in order to be a base.
+ //
+ class_* b_node (0);
+ string name;
+
+ if (node* n = unit_->find (base))
+ {
+ b_node = &dynamic_cast<class_&> (*n);
+
+ if (trace)
+ name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (base)));
+ }
+ else
+ {
+ // Use public access for a template instantiation in the inheritance
+ // declaration.
+ //
+ b_node = &dynamic_cast<class_&> (
+ emit_type (base, access::public_, file, line, clmn));
+
+ if (trace)
+ name = emit_type_name (base);
+ }
+
+ unit_->new_edge<inherits> (*c_node, *b_node, a, virt);
+
+ if (trace)
+ ts << "\t" << a.string () << (virt ? " virtual" : "") << " base "
+ << name << " (" << static_cast<type*> (b_node) << ")" << endl;
+ }
+
+ // Collect member declarations so that we can traverse them in
+ // the source code order.
+ //
+ decl_set decls;
+
+ for (tree d (TYPE_FIELDS (c)); d != NULL_TREE; d = TREE_CHAIN (d))
+ {
+ switch (TREE_CODE (d))
+ {
+ case TYPE_DECL:
+ {
+ if (!DECL_SELF_REFERENCE_P (d))
+ decls.insert (d);
+ break;
+ }
+ case TEMPLATE_DECL:
+ {
+ if (DECL_CLASS_TEMPLATE_P (d))
+ decls.insert (d);
+ break;
+ }
+ case FIELD_DECL:
+ {
+ if (!DECL_ARTIFICIAL (d))
+ decls.insert (d);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Add virtual declarations if any.
+ //
+ {
+ virt_declarations::const_iterator i (virt_declarations_.find (c));
+
+ if (i != virt_declarations_.end ())
+ decls.insert (i->second.begin (), i->second.end ());
+ }
+
+ // Add location pragmas if any.
+ //
+ {
+ loc_pragmas::const_iterator i (loc_pragmas_.find (c));
+
+ if (i != loc_pragmas_.end ())
+ decls.insert (i->second.begin (), i->second.end ());
+ }
+
+ scope* prev_scope (scope_);
+ scope_ = c_node;
+
+ for (decl_set::const_iterator b (decls.begin ()), i (b), e (decls.end ());
+ i != e; ++i)
+ {
+ // Skip pragmas.
+ //
+ if (i->prag != 0)
+ continue;
+
+ // Handle virtual declarations.
+ //
+ if (i->vdecl != 0)
+ {
+ virt_declaration const& vd (*i->vdecl);
+
+ switch (vd.tree_code)
+ {
+ case FIELD_DECL:
+ {
+ // First check that it doesn't conflict with any of the real
+ // data members defined in this class.
+ //
+ tree d (
+ lookup_qualified_name (
+ c, get_identifier (vd.name.c_str ()), false, false));
+
+ if (d != error_mark_node && TREE_CODE (d) == FIELD_DECL)
+ {
+ error (vd.loc) << "virtual data member declaration '" << vd.name
+ << "' conflicts with a previous declaration"
+ << endl;
+
+ location_t l (real_source_location (d));
+ info (l) << "'" << vd.name << "' was previously declared here"
+ << endl;
+
+ throw failed ();
+ }
+
+ path file (LOCATION_FILE (vd.loc));
+ size_t line (LOCATION_LINE (vd.loc));
+ size_t clmn (LOCATION_COLUMN (vd.loc));
+
+ access a (access::public_);
+
+ type& type_node (emit_type (vd.type, a, file, line, clmn));
+ data_member& member_node (
+ unit_->new_node<data_member> (file, line, clmn, tree (0)));
+
+ unit_->new_edge<names> (*c_node, member_node, vd.name, a);
+ belongs& edge (unit_->new_edge<belongs> (member_node, type_node));
+
+ // See if there is a name hint for this type.
+ //
+ if (names* hint = unit_->find_hint (vd.type))
+ edge.hint (*hint);
+
+ // Process pragmas that may be associated with this field.
+ //
+ process_pragmas (vd, member_node, vd.name, b, i, e);
+ break;
+ }
+ default:
+ {
+ assert (false);
+ break;
+ }
+ }
+ continue;
+ }
+
+ tree d (i->decl);
+
+ switch (TREE_CODE (d))
+ {
+ case TYPE_DECL:
+ {
+ type* n (emit_type_decl (d));
+
+ // If this is a named class-type definition, then handle
+ // the pragmas.
+ //
+ if (n != 0)
+ process_pragmas (n->tree_node (), *n, n->name (), b, i, e);
+
+ break;
+ }
+ case TEMPLATE_DECL:
+ {
+ emit_template_decl (d);
+ break;
+ }
+ case FIELD_DECL:
+ {
+ // If this is a bit-field then TREE_TYPE may be a modified type
+ // with lesser precision. In this case, DECL_BIT_FIELD_TYPE
+ // will be the type that was original specified. Use that type
+ // for now. Furthermore, bitfields can be anonymous, which we
+ // ignore.
+ //
+ //
+ bool bf (DECL_C_BIT_FIELD (d));
+
+ if (bf && DECL_NAME (d) == 0)
+ break;
+
+ // Another case where we can have NULL name is anonymous struct
+ // or union extension, for example:
+ //
+ // struct s
+ // {
+ // union
+ // {
+ // int a;
+ // int b;
+ // };
+ // };
+ //
+ // GCC appears to create a fake member for such a struct/union
+ // without any name. Ignore such members for now.
+ //
+ if (DECL_NAME (d) == 0)
+ break;
+
+ tree t (bf ? DECL_BIT_FIELD_TYPE (d) : TREE_TYPE (d));
+
+ char const* name (IDENTIFIER_POINTER (DECL_NAME (d)));
+
+ path file (DECL_SOURCE_FILE (d));
+ size_t line (DECL_SOURCE_LINE (d));
+ size_t clmn (DECL_SOURCE_COLUMN (d));
+
+ access a (decl_access (d));
+
+ type& type_node (emit_type (t, a, file, line, clmn));
+ data_member& member_node (
+ unit_->new_node<data_member> (file, line, clmn, d));
+ unit_->insert (d, member_node);
+
+ unit_->new_edge<names> (*c_node, member_node, name, a);
+ belongs& edge (unit_->new_edge<belongs> (member_node, type_node));
+
+ // See if there is a name hint for this type.
+ //
+ if (names* hint = unit_->find_hint (t))
+ edge.hint (*hint);
+
+ if (trace)
+ {
+ string type_name (emit_type_name (t));
+
+ ts << "\t" << a.string () << " data member " << type_name
+ << " (" << &type_node << ") " << name << " at "
+ << file << ":" << line << endl;
+ }
+
+ // Process pragmas that may be associated with this field.
+ //
+ process_pragmas (d, member_node, name, b, i, e);
+
+ break;
+ }
+ default:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ // Diagnose any position pragmas that haven't been associated.
+ //
+ diagnose_unassoc_pragmas (decls);
+
+ scope_ = prev_scope;
+ class_scopes_.pop_back ();
+
+ return *c_node;
+}
+
+template <typename T>
+T& parser::impl::
+emit_union (tree u, path const& file, size_t line, size_t clmn, bool stub)
+{
+ u = TYPE_MAIN_VARIANT (u);
+
+ // See if there is a stub already for this type.
+ //
+ T* u_node (0);
+
+ if (node* n = unit_->find (u))
+ {
+ u_node = &dynamic_cast<T&> (*n);
+ }
+ else
+ {
+ u_node = &unit_->new_node<T> (file, line, clmn, u);
+ unit_->insert (u, *u_node);
+ }
+
+ if (stub || !COMPLETE_TYPE_P (u))
+ return *u_node;
+
+ class_scopes_.push_back (u_node);
+
+ // Collect member declarations so that we can traverse them in
+ // the source code order.
+ //
+ decl_set decls;
+
+ for (tree d (TYPE_FIELDS (u)); d != NULL_TREE ; d = TREE_CHAIN (d))
+ {
+ switch (TREE_CODE (d))
+ {
+ case TYPE_DECL:
+ {
+ if (!DECL_SELF_REFERENCE_P (d))
+ decls.insert (d);
+ break;
+ }
+ case TEMPLATE_DECL:
+ {
+ if (DECL_CLASS_TEMPLATE_P (d))
+ decls.insert (d);
+ break;
+ }
+ case FIELD_DECL:
+ {
+ if (!DECL_ARTIFICIAL (d))
+ decls.insert (d);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Add location pragmas if any.
+ //
+ {
+ loc_pragmas::const_iterator i (loc_pragmas_.find (u));
+
+ if (i != loc_pragmas_.end ())
+ decls.insert (i->second.begin (), i->second.end ());
+ }
+
+ scope* prev_scope (scope_);
+ scope_ = u_node;
+
+ for (decl_set::const_iterator b (decls.begin ()), i (b), e (decls.end ());
+ i != e; ++i)
+ {
+ // Skip pragmas.
+ //
+ if (i->prag)
+ continue;
+
+ tree d (i->decl);
+
+ switch (TREE_CODE (d))
+ {
+ case TYPE_DECL:
+ {
+ type* n (emit_type_decl (d));
+
+ // If this is a named class-type definition, then handle
+ // the pragmas.
+ //
+ if (n != 0)
+ process_pragmas (n->tree_node (), *n, n->name (), b, i, e);
+
+ break;
+ }
+ case TEMPLATE_DECL:
+ {
+ emit_template_decl (d);
+ break;
+ }
+ case FIELD_DECL:
+ {
+ // We can have NULL name in case of anonymous struct or union
+ // extension, for example:
+ //
+ // union s
+ // {
+ // struct
+ // {
+ // int a;
+ // int b;
+ // };
+ // int c;
+ // };
+ //
+ // GCC appears to create a fake member for such a struct/union
+ // without any name. Ignore such members for now.
+ //
+ if (DECL_NAME (d) == 0)
+ break;
+
+ tree t (TREE_TYPE (d));
+ char const* name (IDENTIFIER_POINTER (DECL_NAME (d)));
+
+ path file (DECL_SOURCE_FILE (d));
+ size_t line (DECL_SOURCE_LINE (d));
+ size_t clmn (DECL_SOURCE_COLUMN (d));
+
+ access a (decl_access (d));
+
+ type& type_node (emit_type (t, a, file, line, clmn));
+ data_member& member_node (
+ unit_->new_node<data_member> (file, line, clmn, d));
+ unit_->insert (d, member_node);
+
+ unit_->new_edge<names> (*u_node, member_node, name, a);
+ belongs& edge (unit_->new_edge<belongs> (member_node, type_node));
+
+ // See if there is a name hint for this type.
+ //
+ if (names* hint = unit_->find_hint (t))
+ edge.hint (*hint);
+
+ if (trace)
+ {
+ string type_name (emit_type_name (t));
+
+ ts << "\t" << a.string () << " union member " << type_name
+ << " (" << &type_node << ") " << name << " at "
+ << file << ":" << line << endl;
+ }
+
+ // Process pragmas that may be associated with this field.
+ //
+ process_pragmas (d, member_node, name, b, i, e);
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Diagnose any position pragmas that haven't been associated.
+ //
+ diagnose_unassoc_pragmas (decls);
+
+ scope_ = prev_scope;
+ class_scopes_.pop_back ();
+ return *u_node;
+}
+
+//
+// Functions.
+//
+
+parser::impl::
+impl (options const& ops,
+ loc_pragmas & lp,
+ ns_loc_pragmas& nslp,
+ decl_pragmas& dp)
+ : ops_ (ops),
+ loc_pragmas_ (lp),
+ ns_loc_pragmas_ (nslp),
+ decl_pragmas_ (dp),
+ trace (ops.trace ()),
+ ts (cerr)
+{
+}
+
+unique_ptr<unit> parser::impl::
+parse (tree global_scope, path const& main_file)
+{
+ unique_ptr<unit> u (new unit (main_file));
+ u->insert (global_namespace, *u);
+ process_named_pragmas (global_namespace, *u);
+
+ unit_ = u.get ();
+ scope_ = unit_;
+ error_ = 0;
+
+ // Define fundamental types.
+ //
+ define_fund<fund_void> (void_type_node);
+ define_fund<fund_bool> (boolean_type_node);
+ define_fund<fund_char> (char_type_node);
+ define_fund<fund_wchar> (wchar_type_node);
+
+ if (ops_.std () >= cxx_version::cxx11)
+ {
+ define_fund<fund_char16> (char16_type_node);
+ define_fund<fund_char32> (char32_type_node);
+ }
+
+ define_fund<fund_signed_char> (signed_char_type_node);
+ define_fund<fund_unsigned_char> (unsigned_char_type_node);
+ define_fund<fund_short> (short_integer_type_node);
+ define_fund<fund_unsigned_short> (short_unsigned_type_node);
+ define_fund<fund_int> (integer_type_node);
+ define_fund<fund_unsigned_int> (unsigned_type_node);
+ define_fund<fund_long> (long_integer_type_node);
+ define_fund<fund_unsigned_long> (long_unsigned_type_node);
+ define_fund<fund_long_long> (long_long_integer_type_node);
+ define_fund<fund_unsigned_long_long> (long_long_unsigned_type_node);
+ define_fund<fund_float> (float_type_node);
+ define_fund<fund_double> (double_type_node);
+ define_fund<fund_long_double> (long_double_type_node);
+
+ // First collect all the namespace-level declarations we are
+ // interested in in the line-decl map so that they appear in
+ // the source code order.
+ //
+ collect (global_scope);
+
+ // Add namespace-level position pragmas if any.
+ //
+ {
+ loc_pragmas::const_iterator i (loc_pragmas_.find (global_namespace));
+
+ if (i != loc_pragmas_.end ())
+ decls_.insert (i->second.begin (), i->second.end ());
+ }
+
+ // Convert position namespace pragmas to name pragmas.
+ //
+ for (ns_loc_pragmas::const_iterator i (ns_loc_pragmas_.begin ());
+ i != ns_loc_pragmas_.end (); ++i)
+ {
+ pragma const& p (i->pragma);
+
+ decl_map::const_iterator j (all_decls_.lower_bound (p.loc));
+
+ if (j == all_decls_.end ())
+ {
+ error (p.loc)
+ << "db pragma '" << p.pragma_name << "' is not associated with a "
+ << "namespace declaration" << endl;
+ error_++;
+ continue;
+ }
+
+ // Find the "namespace difference" between this declaration and
+ // the pragma's namespace. The outermost namespace in the result
+ // is what we are looking for.
+ //
+ tree ns (0);
+
+ for (tree prev (j->second), scope (CP_DECL_CONTEXT (prev));;
+ scope = CP_DECL_CONTEXT (scope))
+ {
+ if (scope == i->ns)
+ {
+ ns = prev;
+ break;
+ }
+
+ if (scope == global_namespace)
+ break;
+
+ prev = scope;
+ }
+
+ if (ns == 0 || TREE_CODE (ns) != NAMESPACE_DECL)
+ {
+ error (p.loc)
+ << "db pragma '" << p.pragma_name << "' is not associated with a "
+ << "namespace declaration" << endl;
+ error_++;
+ continue;
+ }
+
+ pragma_set& s (decl_pragmas_[ns]);
+ pragma_set::iterator it (s.find (p.context_name));
+
+ // Make sure we override only if this pragma came after the one
+ // already in the set.
+ //
+ if (it == s.end () || it->second.loc <= p.loc)
+ s.insert (p);
+ }
+
+ // Construct the semantic graph.
+ //
+ if (error_ == 0)
+ emit ();
+
+ if (error_ > 0)
+ throw failed ();
+
+ return u;
+}
+
+void parser::impl::
+collect (tree ns)
+{
+ cp_binding_level* level = NAMESPACE_LEVEL (ns);
+ tree decl = level->names;
+
+ // Collect declarations.
+ //
+ for (; decl != NULL_TREE; decl = TREE_CHAIN (decl))
+ {
+ all_decls_[real_source_location (decl)] = decl;
+
+ if (DECL_IS_BUILTIN (decl))
+ continue;
+
+ switch (TREE_CODE (decl))
+ {
+ case TYPE_DECL:
+ {
+ // Skip special type declarations.
+ //
+ if (DECL_NAME (decl) == NULL_TREE)
+ continue;
+
+ tree type (TREE_TYPE (decl));
+ if (LAMBDA_TYPE_P (type))
+ continue;
+
+ decls_.insert (decl);
+ break;
+ }
+ case TEMPLATE_DECL:
+ {
+ if (DECL_CLASS_TEMPLATE_P (decl))
+ decls_.insert (decl);
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Traverse namespaces.
+ //
+ for (
+#if BUILDING_GCC_MAJOR >= 8
+ decl = level->names;
+#else
+ decl = level->namespaces;
+#endif
+ decl != NULL_TREE;
+ decl = TREE_CHAIN (decl))
+ {
+#if BUILDING_GCC_MAJOR >= 8
+ // Now namespaces are interleaved with other declarations. In fact, we
+ // could probably collect everything in a single pass.
+ //
+ if (TREE_CODE (decl) != NAMESPACE_DECL)
+ continue;
+#endif
+
+ // Ignore namespace aliases.
+ //
+ if (DECL_NAMESPACE_ALIAS (decl))
+ continue;
+
+ if (!DECL_IS_BUILTIN (decl) || DECL_NAMESPACE_STD_P (decl))
+ {
+ tree dn (DECL_NAME (decl));
+
+ if (trace)
+ {
+ char const* name (dn ? IDENTIFIER_POINTER (dn) : "<anonymous>");
+
+ ts << "namespace " << name << " at "
+ << DECL_SOURCE_FILE (decl) << ":"
+ << DECL_SOURCE_LINE (decl) << endl;
+ }
+
+ // Skip anonymous namespaces (there could be nothing of interest to us
+ // inside but they wreck havoc with our attempts to sort declarations
+ // into namespaces).
+ //
+ if (dn != 0)
+ collect (decl);
+ }
+ }
+}
+
+void parser::impl::
+emit ()
+{
+ for (decl_set::const_iterator b (decls_.begin ()), i (b),
+ e (decls_.end ()); i != e; ++i)
+ {
+ // Skip pragmas.
+ //
+ if (i->prag)
+ continue;
+
+ tree decl (i->decl);
+
+ // Get this declaration's namespace and unwind our scope until
+ // we find a common prefix of namespaces.
+ //
+ string pfx;
+ string ns (fq_scope (decl));
+
+ for (pfx = scope_->fq_name (); !pfx.empty (); pfx = scope_->fq_name ())
+ {
+ size_t n (pfx.size ());
+
+ // Make sure we handle cases like ns="::foobar", pfx="::foo".
+ //
+ if (ns.compare (0, n, pfx) == 0 && (ns.size () == n || ns[n - 1] == ':'))
+ break;
+
+ if (trace)
+ ts << "closing namespace " << scope_->name () << endl;
+
+ scope_ = &scope_->scope_ ();
+ }
+
+ // Build the rest of the namespace hierarchy for this declaration.
+ //
+ if (ns != pfx)
+ {
+ path f (DECL_SOURCE_FILE (decl));
+ size_t l (DECL_SOURCE_LINE (decl));
+ size_t c (DECL_SOURCE_COLUMN (decl));
+
+ for (size_t b (pfx.size () + 2), e (ns.find ("::", b));
+ b != string::npos;)
+ {
+ string n (ns, b, e == string::npos ? e : e - b);
+
+ if (trace)
+ ts << "opening namespace " << n << " for "
+ << DECL_SOURCE_FILE (decl) << ":"
+ << DECL_SOURCE_LINE (decl) << endl;
+
+ // Use the declarations's file, line, and column as an
+ // approximation for this namespace origin. Also resolve
+ // the tree node for this namespace.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ tree tree_node (
+ get_namespace_binding (
+ scope_->tree_node (), get_identifier (n.c_str ())));
+#else
+ tree tree_node (
+ namespace_binding (
+ get_identifier (n.c_str ()), scope_->tree_node ()));
+#endif
+
+ namespace_& node (unit_->new_node<namespace_> (f, l, c, tree_node));
+ unit_->new_edge<defines> (*scope_, node, n);
+
+ if (namespace_* orig =
+ dynamic_cast<namespace_*> (unit_->find (tree_node)))
+ {
+ // This is an extension.
+ //
+ node.original (*orig);
+ }
+ else
+ {
+ // This is the original. Add it to the map and process any
+ // pragmas it might have (at this stage namespaces can only
+ // have name pragmas).
+ //
+ unit_->insert (tree_node, node);
+ process_named_pragmas (tree_node, node);
+ }
+
+ scope_ = &node;
+
+ if (e == string::npos)
+ b = e;
+ else
+ {
+ b = e + 2;
+ e = ns.find ("::", b);
+ }
+ }
+ }
+
+ switch (TREE_CODE (decl))
+ {
+ case TYPE_DECL:
+ {
+ type* n (emit_type_decl (decl));
+
+ // If this is a named class-type definition, then handle
+ // the pragmas.
+ //
+ if (n != 0)
+ process_pragmas (n->tree_node (), *n, n->name (), b, i, e);
+
+ break;
+ }
+ case TEMPLATE_DECL:
+ {
+ emit_template_decl (decl);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ assert (class_scopes_.empty ());
+ }
+
+ // Diagnose any position pragmas that haven't been associated.
+ //
+ diagnose_unassoc_pragmas (decls_);
+}
+
+type* parser::impl::
+emit_type_decl (tree decl)
+{
+ tree t (TREE_TYPE (decl));
+ gcc_tree_code_type tc (TREE_CODE (t));
+
+ tree decl_name (DECL_NAME (decl));
+ char const* name (IDENTIFIER_POINTER (decl_name));
+
+ if (DECL_ARTIFICIAL (decl) &&
+ (tc == RECORD_TYPE || tc == UNION_TYPE || tc == ENUMERAL_TYPE))
+ {
+ // If we have an anonymous class typedef, use the user-
+ // supplied name instead of the synthesized one. ARM
+ // says that in typedef struct {} S; S becomes struct's
+ // name.
+ //
+ if (IDENTIFIER_ANON_P (decl_name))
+ {
+ tree d (TYPE_NAME (t));
+
+ if (d != NULL_TREE &&
+ !DECL_ARTIFICIAL (d) &&
+ DECL_NAME (d) != NULL_TREE &&
+ !IDENTIFIER_ANON_P (DECL_NAME (d)))
+ {
+ decl = d;
+ decl_name = DECL_NAME (decl);
+ name = IDENTIFIER_POINTER (decl_name);
+ }
+ else
+ {
+ // This type has only the synthesized name which means that
+ // it is either typedef'ed as a derived type or it is used
+ // to declare a varibale or similar. The first case will be
+ // covered by the typedef handling code below. The second
+ // case will be covere by emit_type().
+ //
+ return 0;
+ }
+ }
+
+ path file (DECL_SOURCE_FILE (decl));
+ size_t line (DECL_SOURCE_LINE (decl));
+ size_t clmn (DECL_SOURCE_COLUMN (decl));
+
+ type* node (0);
+
+ // Pointers to member functions are represented as record
+ // types. Detect and handle this case.
+ //
+ if (TYPE_PTRMEMFUNC_P (t))
+ {
+ t = TYPE_MAIN_VARIANT (t);
+ node = &unit_->new_node<unsupported_type> (
+ file, line, clmn, t, "pointer_to_member_function_type");
+ unit_->insert (t, *node);
+ }
+ else
+ {
+
+ if (trace)
+ ts << "start " << gcc_tree_code_name(tc) << " " << name
+ << " at " << file << ":" << line << endl;
+
+ switch (tc)
+ {
+ case RECORD_TYPE:
+ {
+ node = &emit_class<class_> (t, file, line, clmn);
+ break;
+ }
+ case UNION_TYPE:
+ {
+ node = &emit_union<union_> (t, file, line, clmn);
+ break;
+ }
+ case ENUMERAL_TYPE:
+ {
+ node = &emit_enum (t, decl_access (decl), file, line, clmn);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (trace)
+ ts << "end " << gcc_tree_code_name(tc) << " " << name
+ << " (" << node << ") at "
+ << DECL_SOURCE_FILE (decl) << ":"
+ << DECL_SOURCE_LINE (decl) << endl;
+ }
+
+ if (COMPLETE_TYPE_P (t))
+ unit_->new_edge<defines> (*scope_, *node, name);
+ else
+ unit_->new_edge<declares> (*scope_, *node, name);
+
+ return node;
+ }
+ else
+ {
+ // Normal typedef. We need to detect and ignore the anonymous
+ // class typedef case described above since we already used
+ // this name to define the class.
+ //
+ if ((tc == RECORD_TYPE || tc == UNION_TYPE || tc == ENUMERAL_TYPE) &&
+ TYPE_NAME (TYPE_MAIN_VARIANT (t)) == decl)
+ return 0;
+
+ path f (DECL_SOURCE_FILE (decl));
+ size_t l (DECL_SOURCE_LINE (decl));
+ size_t c (DECL_SOURCE_COLUMN (decl));
+
+ type& node (emit_type (t, decl_access (decl), f, l, c));
+
+ // Omit inner self-typedefs (e.g., a class typedefs itself in its own
+ // scope). Such aliases don't buy us anything (in particular, they cannot
+ // be used to form an fq-name) but they do cause scoping cycles if this
+ // name happens to be used to find outer scope (see scope::scope_()).
+ // Note that this means we can now have class template instantiations that
+ // are not named and therefore don't belong to any scope.
+ //
+ // Note that emit_type() might still enter this decl as a hint. It's fuzzy
+ // whether this is harmless or not.
+ //
+ // Note also that using the normal scope hierarchy does not work in more
+ // complex cases where templates cross-self-typedef. So instead we now use
+ // a special-purpose mechanism (class_scopes_). Note for this to work
+ // correctly (hopefully), the class should be "in scope" for its bases.
+ // Here is a representative examples (inspired by code in Eigen):
+ //
+ // template <typename M>
+ // struct PlainObjectBase
+ // {
+ // typedef M Self;
+ // };
+ //
+ // template <typename T, int X, int Y>
+ // struct Matrix: PlainObjectBase<Matrix<T, X, Y>>
+ // {
+ // typedef PlainObjectBase<Matrix> Base;
+ // typedef Matrix Self;
+ // };
+ //
+ // typedef Matrix<double, 3, 1> Vector3d;
+ //
+ // Here we want both Self's (but not Base) to be skipped.
+ //
+ if (scope* s = dynamic_cast<scope*> (&node))
+ {
+ for (auto i (class_scopes_.rbegin ()); i != class_scopes_.rend (); ++i)
+ {
+ if (s == *i)
+ {
+ if (trace)
+ {
+ string s (emit_type_name (t, false));
+
+ ts << "omitting inner self-typedef " << s << " (" << &node
+ << ") -> " << name << " at " << f << ":" << l << endl;
+ }
+ return 0;
+ }
+ }
+ }
+
+ typedefs& edge (unit_->new_edge<typedefs> (*scope_, node, name));
+
+ // Find our hint.
+ //
+ if (tree ot = DECL_ORIGINAL_TYPE (decl))
+ {
+ if (names* hint = unit_->find_hint (ot))
+ edge.hint (*hint);
+ }
+
+ // Add this edge to the hint map. It may already be there if we
+ // are handling something like this:
+ //
+ // typedef foo bar;
+ // typedef bar foo;
+ //
+ // GCC also appears to re-purpose a node for another name (not
+ // sure if its a bug or a feature), so use the latest seen name.
+ //
+ unit_->insert_hint (t, edge);
+
+ if (trace)
+ {
+ string s (emit_type_name (t, false));
+
+ ts << "typedef " << s << " (" << &node << ") -> " << name
+ << " at " << f << ":" << l << endl;
+ }
+
+ return 0;
+ }
+}
+
+void parser::impl::
+emit_template_decl (tree decl)
+{
+ // Currently we only handle class/union templates.
+ //
+ tree t (TREE_TYPE (DECL_TEMPLATE_RESULT (decl)));
+ gcc_tree_code_type tc (TREE_CODE (t));
+
+ if (trace)
+ {
+ ts << gcc_tree_code_name(tc) << " template (" << decl << ") "
+ << IDENTIFIER_POINTER (DECL_NAME (decl)) << " (" << t << ") at "
+ << DECL_SOURCE_FILE (decl) << ":"
+ << DECL_SOURCE_LINE (decl) << endl;
+
+ ts << "specializations:" << endl;
+
+ for (tree s (DECL_TEMPLATE_SPECIALIZATIONS (decl));
+ s != NULL_TREE; s = TREE_CHAIN (s))
+ {
+ tree t (TREE_TYPE (s));
+ tree d (TYPE_NAME (t));
+
+ ts << "\tspecialization " << t << " at "
+ << DECL_SOURCE_FILE (d) << ":"
+ << DECL_SOURCE_LINE (d) << endl;
+ }
+
+ ts << "instantiations:" << endl;
+
+ for (tree i (DECL_TEMPLATE_INSTANTIATIONS (decl));
+ i != NULL_TREE; i = TREE_CHAIN (i))
+ {
+ tree t (TREE_VALUE (i));
+ tree d (TYPE_NAME (t));
+
+ ts << "\tinstantiation " << t << " at "
+ << DECL_SOURCE_FILE (d) << ":"
+ << DECL_SOURCE_LINE (d) << endl;
+ }
+ }
+
+ char const* name (IDENTIFIER_POINTER (DECL_NAME (decl)));
+
+ if (trace)
+ ts << "start " << gcc_tree_code_name(tc) << " template " << name << " at "
+ << DECL_SOURCE_FILE (decl) << ":"
+ << DECL_SOURCE_LINE (decl) << endl;
+
+ type_template* t_node (0);
+
+ if (tc == RECORD_TYPE)
+ t_node = &emit_class_template (decl);
+ else
+ t_node = &emit_union_template (decl);
+
+ if (COMPLETE_TYPE_P (t))
+ unit_->new_edge<defines> (*scope_, *t_node, name);
+ else
+ unit_->new_edge<declares> (*scope_, *t_node, name);
+
+ if (trace)
+ ts << "end " << gcc_tree_code_name(tc) << " template " << name
+ << " (" << t_node << ") at "
+ << DECL_SOURCE_FILE (decl) << ":"
+ << DECL_SOURCE_LINE (decl) << endl;
+}
+
+class_template& parser::impl::
+emit_class_template (tree t, bool stub)
+{
+ // See if there is a stub already for this template.
+ //
+ class_template* ct_node (0);
+ tree c (TREE_TYPE (DECL_TEMPLATE_RESULT (t)));
+
+ if (node* n = unit_->find (t))
+ {
+ ct_node = &dynamic_cast<class_template&> (*n);
+ }
+ else
+ {
+ path f (DECL_SOURCE_FILE (t));
+ size_t ln (DECL_SOURCE_LINE (t));
+ size_t cl (DECL_SOURCE_COLUMN (t));
+
+ ct_node = &unit_->new_node<class_template> (f, ln, cl, c);
+ unit_->insert (t, *ct_node);
+ }
+
+ if (stub || !COMPLETE_TYPE_P (c))
+ return *ct_node;
+
+ class_scopes_.push_back (ct_node);
+
+ // Collect member declarations so that we can traverse them in
+ // the source code order. For now we are only interested in
+ // nested class template declarations.
+ //
+ decl_set decls;
+
+ for (tree d (TYPE_FIELDS (c)); d != NULL_TREE ; d = TREE_CHAIN (d))
+ {
+ switch (TREE_CODE (d))
+ {
+ case TEMPLATE_DECL:
+ {
+ if (DECL_CLASS_TEMPLATE_P (d))
+ decls.insert (d);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ scope* prev_scope (scope_);
+ scope_ = ct_node;
+
+ for (decl_set::const_iterator i (decls.begin ()), e (decls.end ());
+ i != e; ++i)
+ {
+ // Skip pragmas.
+ //
+ if (i->prag)
+ continue;
+
+ tree d (i->decl);
+
+ switch (TREE_CODE (d))
+ {
+ case TEMPLATE_DECL:
+ {
+ emit_template_decl (d);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Diagnose any position pragmas that haven't been associated.
+ //
+ diagnose_unassoc_pragmas (decls);
+
+ scope_ = prev_scope;
+ class_scopes_.pop_back ();
+ return *ct_node;
+}
+
+union_template& parser::impl::
+emit_union_template (tree t, bool stub)
+{
+ // See if there is a stub already for this template.
+ //
+ union_template* ut_node (0);
+ tree u (TREE_TYPE (DECL_TEMPLATE_RESULT (t)));
+
+ if (node* n = unit_->find (t))
+ {
+ ut_node = &dynamic_cast<union_template&> (*n);
+ }
+ else
+ {
+ path f (DECL_SOURCE_FILE (t));
+ size_t l (DECL_SOURCE_LINE (t));
+ size_t c (DECL_SOURCE_COLUMN (t));
+
+ ut_node = &unit_->new_node<union_template> (f, l, c, u);
+ unit_->insert (t, *ut_node);
+ }
+
+ if (stub || !COMPLETE_TYPE_P (u))
+ return *ut_node;
+
+ class_scopes_.push_back (ut_node);
+
+ // Collect member declarations so that we can traverse them in
+ // the source code order. For now we are only interested in
+ // nested class template declarations.
+ //
+ decl_set decls;
+
+ for (tree d (TYPE_FIELDS (u)); d != NULL_TREE ; d = TREE_CHAIN (d))
+ {
+ switch (TREE_CODE (d))
+ {
+ case TEMPLATE_DECL:
+ {
+ if (DECL_CLASS_TEMPLATE_P (d))
+ decls.insert (d);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ scope* prev_scope (scope_);
+ scope_ = ut_node;
+
+ for (decl_set::const_iterator i (decls.begin ()), e (decls.end ());
+ i != e; ++i)
+ {
+ // Skip pragmas.
+ //
+ if (i->prag)
+ continue;
+
+ tree d (i->decl);
+
+ switch (TREE_CODE (d))
+ {
+ case TEMPLATE_DECL:
+ {
+ emit_template_decl (d);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Diagnose any position pragmas that haven't been associated.
+ //
+ diagnose_unassoc_pragmas (decls);
+
+ scope_ = prev_scope;
+ class_scopes_.pop_back ();
+ return *ut_node;
+}
+
+enum_& parser::impl::
+emit_enum (tree e,
+ access access,
+ path const& file,
+ size_t line,
+ size_t clmn,
+ bool stub)
+{
+ e = TYPE_MAIN_VARIANT (e);
+
+ // See if there is a stub already for this type.
+ //
+ enum_* e_node (0);
+
+ if (node* n = unit_->find (e))
+ e_node = &dynamic_cast<enum_&> (*n);
+ else
+ {
+ e_node = &unit_->new_node<enum_> (file, line, clmn, e);
+
+ // Set the underlying type even for incomplete (forward-declared) enums.
+ //
+ tree ut (ENUM_UNDERLYING_TYPE (e));
+ names* hint (unit_->find_hint (ut));
+ integral_type* un = dynamic_cast<integral_type*> (
+ unit_->find (TYPE_MAIN_VARIANT (ut)));
+
+ // For "old" enums GCC creates a distinct type node and the only way to
+ // get to one of the known integrals is via its name.
+ //
+ if (un == 0)
+ {
+ ut = TREE_TYPE (TYPE_NAME (ut));
+ un = dynamic_cast<integral_type*> (unit_->find (TYPE_MAIN_VARIANT (ut)));
+ }
+
+ underlies& edge (unit_->new_edge<underlies> (*un, *e_node));
+
+ if (hint != 0)
+ edge.hint (*hint);
+
+ unit_->insert (e, *e_node);
+ }
+
+ if (stub || !COMPLETE_TYPE_P (e))
+ return *e_node;
+
+ // Traverse enumerators.
+ //
+ for (tree er (TYPE_VALUES (e)); er != NULL_TREE ; er = TREE_CHAIN (er))
+ {
+ char const* name (IDENTIFIER_POINTER (TREE_PURPOSE (er)));
+ tree decl (TREE_VALUE (er));
+ tree tval (DECL_INITIAL (decl));
+
+ unsigned long long val (integer_value (tval));
+
+ // There doesn't seem to be a way to get the proper position for
+ // each enumerator.
+ //
+ enumerator& er_node = unit_->new_node<enumerator> (
+ file, line, clmn, er, val);
+ unit_->new_edge<enumerates> (*e_node, er_node);
+ unit_->insert (decl, er_node);
+
+ // In C++11 the enumerators are always available in the enum's
+ // scope, even for old enums.
+ //
+ if (ops_.std () >= cxx_version::cxx11)
+ unit_->new_edge<names> (*e_node, er_node, name, access::public_);
+
+ // Inject enumerators into the outer scope unless this is an
+ // enum class.
+ //
+ if (UNSCOPED_ENUM_P (e))
+ unit_->new_edge<names> (*scope_, er_node, name, access);
+
+ if (trace)
+ ts << "\tenumerator " << name << " at " << file << ":" << line << endl;
+ }
+
+ return *e_node;
+}
+
+type& parser::impl::
+emit_type (tree t,
+ access access,
+ path const& file,
+ size_t line,
+ size_t clmn)
+{
+ tree mv (TYPE_MAIN_VARIANT (t));
+
+ if (trace)
+ {
+ ts << gcc_tree_code_name(TREE_CODE (t)) << " " << t
+ << " main " << mv << endl;
+
+ for (tree v (TYPE_MAIN_VARIANT (t)); v != 0; v = TYPE_NEXT_VARIANT (v))
+ ts << "\tvariant " << v << " " << CP_TYPE_CONST_P (v) << endl;
+ }
+
+ node* n (unit_->find (mv));
+
+ type& r (n != 0
+ ? dynamic_cast<type&> (*n)
+ : create_type (t, access, file, line, clmn));
+
+ if (trace && n != 0)
+ ts << "found node " << &r << " for type " << mv << endl;
+
+ if (cp_type_quals (t) == TYPE_UNQUALIFIED)
+ {
+ unit_->insert (t, r); // Add this variant to the map.
+ return r;
+ }
+
+ // See if this type already has this variant.
+ //
+ bool qc (CP_TYPE_CONST_P (t));
+ bool qv (CP_TYPE_VOLATILE_P (t));
+ bool qr (CP_TYPE_RESTRICT_P (t));
+
+ for (type::qualified_iterator i (r.qualified_begin ());
+ i != r.qualified_end (); ++i)
+ {
+ qualifier& q (i->qualifier ());
+
+ if (q.const_ () == qc && q.volatile_ () == qv && q.restrict_ () == qr)
+ {
+ if (trace)
+ ts << "found qualifier variant " << &q << endl;
+
+ unit_->insert (t, q); // Add this variant to the map.
+ return q;
+ }
+ }
+
+ // No such variant yet. Create a new one. Qualified types are not
+ // unique in the tree so don't add this node to the map.
+ //
+ qualifier& q (unit_->new_node<qualifier> (file, line, clmn, t, qc, qv, qr));
+ qualifies& e (unit_->new_edge<qualifies> (q, r));
+ unit_->insert (t, q);
+
+ // See if there is a name hint for this type.
+ //
+ // If TREE_TYPE (TYPE_NAME (t)) != t then we have an inline qualifier,
+ // as in:
+ //
+ // const foo x;
+ //
+ // If they are equal, then there are two possible cases. The first is
+ // when we have a qualifier typedef, as in:
+ //
+ // typedef const foo cfoo;
+ // cfoo x;
+ //
+ // The second is when we have a qualifier typedef but what is actually
+ // used in the declaration is an inline qualifier, as in:
+ //
+ // typedef const foo cfoo;
+ // const foo x;
+ //
+ // Unfortunately, in GCC, these two cases are indistinguishable. In
+ // certain cases this can lead to a wrong hint being used for the base
+ // type, for example:
+ //
+ // typedef foo my_foo;
+ // typedef foo foo_t;
+ // typedef const foo_t cfoo;
+ //
+ // const my_foo x;
+ //
+ // Above, the hint will be foo_t while it should be my_foo.
+ //
+ tree bt (0);
+
+ if (tree decl = TYPE_NAME (t))
+ {
+ bt = TREE_TYPE (decl);
+
+ if (t == bt)
+ {
+ // A const type can be named only with a typedef. Get the
+ // original type.
+ //
+ tree ot (DECL_ORIGINAL_TYPE (decl));
+
+ // And chase it one more time to get rid of the qualification.
+ //
+ decl = TYPE_NAME (ot);
+ bt = decl != 0 ? TREE_TYPE (decl) : 0;
+ }
+ }
+
+ if (bt != 0)
+ {
+ if (names* hint = unit_->find_hint (bt))
+ e.hint (*hint);
+ }
+
+ process_named_pragmas (t, q);
+
+ return q;
+}
+
+type& parser::impl::
+create_type (tree t,
+ access access,
+ path const& file,
+ size_t line,
+ size_t clmn)
+{
+ type* r (0);
+ gcc_tree_code_type tc (TREE_CODE (t));
+
+ switch (tc)
+ {
+ //
+ // User-defined types.
+ //
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ {
+ t = TYPE_MAIN_VARIANT (t);
+ tree ti (TYPE_TEMPLATE_INFO (t));
+
+ if (ti == NULL_TREE)
+ {
+ // Ordinary class. There are two situations which can lead
+ // here. First is when we have an anonymous class that is
+ // part of the declaration, for example:
+ //
+ // typedef const struct {...} s;
+ //
+ // The second situation is a named class definition which
+ // we haven't parsed yet. In this case we are going to
+ // create a "stub" class node which will be processed and
+ // filled in later.
+ //
+
+ // Pointers to member functions are represented as record
+ // types. Detect and handle this case.
+ //
+ if (TYPE_PTRMEMFUNC_P (t))
+ {
+ r = &unit_->new_node<unsupported_type> (
+ file, line, clmn, t, "pointer_to_member_function_type");
+ unit_->insert (t, *r);
+ }
+ else
+ {
+ tree d (TYPE_NAME (t));
+
+ if (trace)
+ ts << "start anon/stub " << gcc_tree_code_name(tc) << " at "
+ << file << ":" << line << endl;
+
+ if (d == NULL_TREE || IDENTIFIER_ANON_P (DECL_NAME (d)))
+ {
+ if (tc == RECORD_TYPE)
+ r = &emit_class<class_> (t, file, line, clmn);
+ else
+ r = &emit_union<union_> (t, file, line, clmn);
+ }
+ else
+ {
+ // Use the "defining" declaration's file, line, and column
+ // information to create the stub.
+ //
+ path f (DECL_SOURCE_FILE (d));
+ size_t l (DECL_SOURCE_LINE (d));
+ size_t c (DECL_SOURCE_COLUMN (d));
+
+ if (tc == RECORD_TYPE)
+ r = &emit_class<class_> (t, f, l, c, true);
+ else
+ r = &emit_union<union_> (t, f, l, c, true);
+ }
+
+ if (trace)
+ ts << "end anon/stub " << gcc_tree_code_name(tc) << " (" << r << ")"
+ << " at " << file << ":" << line << endl;
+ }
+ }
+ else
+ {
+ // Template instantiation.
+ //
+ tree decl (TI_TEMPLATE (ti)); // DECL_TEMPLATE
+
+ // Get to the most general template declaration.
+ //
+ while (DECL_TEMPLATE_INFO (decl))
+ decl = DECL_TI_TEMPLATE (decl);
+
+ type_template* t_node (0);
+
+ // Find the template node or create a stub if none exist.
+ //
+ if (node* n = unit_->find (decl))
+ t_node = &dynamic_cast<type_template&> (*n);
+ else
+ {
+ if (trace)
+ ts << "start stub " << gcc_tree_code_name(tc) << " template for ("
+ << decl << ") at " << file << ":" << line << endl;
+
+ if (tc == RECORD_TYPE)
+ t_node = &emit_class_template (decl, true);
+ else
+ t_node = &emit_union_template (decl, true);
+
+ if (trace)
+ ts << "end stub " << gcc_tree_code_name(tc) << " template ("
+ << t_node << ") at " << file << ":" << line << endl;
+ }
+
+ if (trace)
+ ts << "start " << gcc_tree_code_name(tc) << " instantiation ("
+ << t << ") for template (" << t_node << ")"
+ << " at " << file << ":" << line << endl;
+
+ type_instantiation* i_node (0);
+
+ if (tc == RECORD_TYPE)
+ i_node = &emit_class<class_instantiation> (t, file, line, clmn);
+ else
+ i_node = &emit_union<union_instantiation> (t, file, line, clmn);
+
+ if (trace)
+ ts << "end " << gcc_tree_code_name(tc) << " instantiation ("
+ << static_cast<type*> (i_node) << ")"
+ << " at " << file << ":" << line << endl;
+
+ unit_->new_edge<instantiates> (*i_node, *t_node);
+ process_named_pragmas (t, *i_node);
+ r = i_node;
+ }
+
+ break;
+ }
+ case ENUMERAL_TYPE:
+ {
+ // The same logic as in the "ordinary class" case above
+ // applies here.
+ //
+
+ t = TYPE_MAIN_VARIANT (t);
+ tree d (TYPE_NAME (t));
+
+ if (trace)
+ ts << "start anon/stub " << gcc_tree_code_name(tc) << " at "
+ << file << ":" << line << endl;
+
+ if (d == NULL_TREE || IDENTIFIER_ANON_P (DECL_NAME (d)))
+ {
+ r = &emit_enum (t, access, file, line, clmn);
+ }
+ else
+ {
+ // Use the "defining" declaration's file, line, and column
+ // information to create the stub.
+ //
+ path f (DECL_SOURCE_FILE (d));
+ size_t l (DECL_SOURCE_LINE (d));
+ size_t c (DECL_SOURCE_COLUMN (d));
+
+ r = &emit_enum (t, access, f, l, c, true);
+ }
+
+ if (trace)
+ ts << "end anon/stub " << gcc_tree_code_name(tc) << " (" << r << ")"
+ << " at " << file << ":" << line << endl;
+
+ break;
+ }
+
+ //
+ // Derived types.
+ //
+
+ case ARRAY_TYPE:
+ {
+ unsigned long long size (0);
+
+ if (tree index = TYPE_DOMAIN (t))
+ {
+ tree max (TYPE_MAX_VALUE (index));
+
+ if (TREE_CODE (max) == INTEGER_CST)
+ {
+ size = integer_value (max);
+
+ // The docs say that TYPE_DOMAIN will be NULL if the
+ // array doesn't specify bounds. In reality, it is
+ // set to ~0.
+ //
+ if (size == ~(unsigned long long) (0))
+ size = 0;
+
+ size++; // Convert max index to size.
+ }
+ else
+ {
+ error (file, line, clmn)
+ << "non-integer array index " <<
+ gcc_tree_code_name(TREE_CODE (max)) << endl;
+
+ throw failed ();
+ }
+ }
+
+ // In GCC tree a const array has both the array type itself and the
+ // element type marked as const. This doesn't bode well with our
+ // semantic graph model where we have a separate type node for
+ // qualifiers. To fix this, we are going to strip the const
+ // qualification from the element type and only preserve it in
+ // the array type. In other words, we view it as "constant array"
+ // rather than "array of constant elements".
+ //
+ using semantics::array; // vs std::array.
+
+ tree bt (TREE_TYPE (t));
+ tree bt_mv (TYPE_MAIN_VARIANT (bt));
+ type& bt_node (emit_type (bt_mv, access::public_, file, line, clmn));
+ t = TYPE_MAIN_VARIANT (t);
+ array& a (unit_->new_node<array> (file, line, clmn, t, size));
+ unit_->insert (t, a);
+ contains& edge (unit_->new_edge<contains> (a, bt_node));
+
+ // See if there is a name hint for the base type.
+ //
+ if (names* hint = unit_->find_hint (
+ cp_type_quals (bt) == TYPE_UNQUALIFIED ? bt : bt_mv))
+ edge.hint (*hint);
+
+ process_named_pragmas (t, a);
+ r = &a;
+ break;
+ }
+ case REFERENCE_TYPE:
+ {
+ tree bt (TREE_TYPE (t));
+ type& bt_node (emit_type (bt, access::public_, file, line, clmn));
+ t = TYPE_MAIN_VARIANT (t);
+ reference& ref (unit_->new_node<reference> (file, line, clmn, t));
+ unit_->insert (t, ref);
+ references& edge (unit_->new_edge<references> (ref, bt_node));
+
+ // See if there is a name hint for the base type.
+ //
+ if (names* hint = unit_->find_hint (bt))
+ edge.hint (*hint);
+
+ process_named_pragmas (t, ref);
+ r = &ref;
+ break;
+ }
+ case POINTER_TYPE:
+ {
+ if (!TYPE_PTRMEM_P (t))
+ {
+ tree bt (TREE_TYPE (t));
+ type& bt_node (emit_type (bt, access::public_, file, line, clmn));
+ t = TYPE_MAIN_VARIANT (t);
+ pointer& p (unit_->new_node<pointer> (file, line, clmn, t));
+ unit_->insert (t, p);
+ points& edge (unit_->new_edge<points> (p, bt_node));
+
+ // See if there is a name hint for the base type.
+ //
+ if (names* hint = unit_->find_hint (bt))
+ edge.hint (*hint);
+
+ process_named_pragmas (t, p);
+ r = &p;
+ }
+ else
+ {
+ t = TYPE_MAIN_VARIANT (t);
+ r = &unit_->new_node<unsupported_type> (
+ file, line, clmn, t, "pointer_to_data_member_type");
+ unit_->insert (t, *r);
+
+ if (trace)
+ ts << "unsupported pointer_to_data_member_type (" << r << ")"
+ << " at " << file << ":" << line << endl;
+ }
+ break;
+ }
+ default:
+ {
+ t = TYPE_MAIN_VARIANT (t);
+ r = &unit_->new_node<unsupported_type> (
+ file, line, clmn, t, gcc_tree_code_name(tc));
+ unit_->insert (t, *r);
+
+ if (trace)
+ ts << "unsupported " << gcc_tree_code_name(tc) << " (" << r << ")"
+ << " at " << file << ":" << line << endl;
+
+ break;
+ }
+ }
+
+ return *r;
+}
+
+string parser::impl::
+emit_type_name (tree type, bool direct)
+{
+ // First see if there is a "direct" name for this type.
+ //
+ if (direct)
+ {
+ if (tree decl = TYPE_NAME (type))
+ {
+ tree t (TREE_TYPE (decl));
+
+ if (t != 0 && same_type_p (type, t))
+ return IDENTIFIER_POINTER (DECL_NAME (decl));
+ }
+ }
+
+ string r;
+
+ if (CP_TYPE_CONST_P (type))
+ r += " const";
+
+ if (CP_TYPE_VOLATILE_P (type))
+ r += " volatile";
+
+ if (CP_TYPE_RESTRICT_P (type))
+ r += " __restrict";
+
+ gcc_tree_code_type tc (TREE_CODE (type));
+
+ switch (tc)
+ {
+ //
+ // User-defined types.
+ //
+
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ {
+ tree ti (TYPE_TEMPLATE_INFO (type));
+
+ if (ti == NULL_TREE)
+ {
+ // Ordinary class.
+ //
+ type = TYPE_MAIN_VARIANT (type);
+
+ // Pointers to member functions are represented as record
+ // types and don't have names, not even the synthesized ones.
+ //
+ if (TYPE_PTRMEMFUNC_P (type))
+ r = "<pointer-to-member-function>" + r;
+ else
+ {
+ tree name (TYPE_NAME (type));
+ r = IDENTIFIER_POINTER (DECL_NAME (name)) + r;
+ }
+ }
+ else
+ {
+ // Template instantiation.
+ //
+ tree decl (TI_TEMPLATE (ti)); // DECL_TEMPLATE
+ string id (IDENTIFIER_POINTER (DECL_NAME (decl)));
+
+ id += '<';
+
+ tree args (INNERMOST_TEMPLATE_ARGS (TI_ARGS (ti)));
+
+ for (size_t i (0), n (TREE_VEC_LENGTH (args)); i < n ; ++i)
+ {
+ tree a (TREE_VEC_ELT (args, i));
+
+ if (i != 0)
+ id += ", ";
+
+ // Assume integer and type-only arguments.
+ //
+ if (TREE_CODE (a) == INTEGER_CST)
+ id += to_string (integer_value (a));
+ else
+ id += emit_type_name (a);
+ }
+
+ id += '>';
+
+ r = id + r;
+ }
+
+ break;
+ }
+
+ case ENUMERAL_TYPE:
+ {
+ type = TYPE_MAIN_VARIANT (type);
+ tree decl (TYPE_NAME (type));
+ r = IDENTIFIER_POINTER (DECL_NAME (decl)) + r;
+ break;
+ }
+
+ //
+ // Derived types.
+ //
+
+ case ARRAY_TYPE:
+ {
+ unsigned long long size (0);
+
+ if (tree index = TYPE_DOMAIN (type))
+ {
+ tree max (TYPE_MAX_VALUE (index));
+
+ if (TREE_CODE (max) == INTEGER_CST)
+ {
+ size = integer_value (max);
+
+ // Same as above.
+ //
+ if (size == ~(unsigned long long) (0))
+ size = 0;
+
+ size++;
+ }
+ else
+ {
+ // Non-integer array index which we do not support. The
+ // error has been/will be issued in emit_type.
+ //
+ }
+ }
+
+ tree t (TREE_TYPE (type));
+
+ if (size != 0)
+ {
+ ostringstream ostr;
+ ostr << size;
+ r = emit_type_name (t) + "[" + ostr.str () + "]" + r;
+ }
+ else
+ r = emit_type_name (t) + "[]" + r;
+
+ break;
+ }
+ case REFERENCE_TYPE:
+ {
+ tree t (TREE_TYPE (type));
+ r = emit_type_name (t) + "&" + r;
+ break;
+ }
+ case POINTER_TYPE:
+ {
+ if (!TYPE_PTRMEM_P (type))
+ {
+ tree t (TREE_TYPE (type));
+ r = emit_type_name (t) + "*" + r;
+ }
+ else
+ r = "<pointer_to_member_type>";
+
+ break;
+ }
+
+ //
+ // Fundamental types.
+ //
+
+ case VOID_TYPE:
+ case REAL_TYPE:
+ case BOOLEAN_TYPE:
+ case INTEGER_TYPE:
+ {
+ type = TYPE_MAIN_VARIANT (type);
+ tree decl (TYPE_NAME (type));
+ r = IDENTIFIER_POINTER (DECL_NAME (decl)) + r;
+ break;
+ }
+ default:
+ {
+ r = "<" + string (gcc_tree_code_name(tc)) + ">";
+ break;
+ }
+ }
+
+ return r;
+}
+
+void parser::impl::
+process_pragmas (declaration const& decl,
+ node& node,
+ string const& name,
+ decl_set::const_iterator begin,
+ decl_set::const_iterator cur,
+ decl_set::const_iterator /*end*/)
+{
+ // First process the position pragmas by iterating backwards until
+ // we get to the preceding non-pragma declaration that has been
+ // associated.
+ //
+ pragma_set prags;
+
+ if (cur != begin)
+ {
+ decl_set::const_iterator i (cur);
+ for (--i; i != begin && (i->prag != 0 || !i->assoc); --i) ;
+
+ for (; i != cur; ++i)
+ {
+ if (i->prag == 0) // Skip declarations.
+ continue;
+
+ assert (!i->assoc);
+
+ if (i->prag->check (decl, name, i->prag->pragma_name, i->prag->loc))
+ prags.insert (*i->prag);
+ else
+ error_++; // Diagnostic has already been issued.
+
+ i->assoc = true; // Mark this pragma as associated.
+ }
+
+ cur->assoc = true; // Mark the declaration as associated.
+ }
+
+ // Now see if there are any named pragmas for this declaration. By
+ // doing this after handling the position pragmas we ensure correct
+ // overriding.
+ //
+ {
+ decl_pragmas::const_iterator i (decl_pragmas_.find (decl));
+
+ if (i != decl_pragmas_.end ())
+ prags.insert (i->second.begin (), i->second.end ());
+ }
+
+ // Finally, copy the resulting pragma set to context.
+ //
+ for (pragma_set::iterator i (prags.begin ()); i != prags.end (); ++i)
+ add_pragma (node, i->second);
+}
+
+void parser::impl::
+process_named_pragmas (declaration const& decl, node& node)
+{
+ pragma_set prags;
+
+ decl_pragmas::const_iterator i (decl_pragmas_.find (decl));
+
+ if (i != decl_pragmas_.end ())
+ prags.insert (i->second.begin (), i->second.end ());
+
+ // Copy the resulting pragma set to context.
+ //
+ for (pragma_set::iterator i (prags.begin ()); i != prags.end (); ++i)
+ add_pragma (node, i->second);
+}
+
+void parser::impl::
+add_pragma (node& n, pragma const& p)
+{
+ if (trace)
+ ts << "\t\t pragma " << p.pragma_name << endl;
+
+ string const& k (p.context_name);
+
+ if (p.add == 0)
+ {
+ n.set (k, p.value);
+ n.set (k + "-location", p.loc);
+ }
+ else
+ p.add (n, k, p.value, p.loc);
+}
+
+void parser::impl::
+diagnose_unassoc_pragmas (decl_set const& decls)
+{
+ for (decl_set::const_iterator i (decls.begin ()), e (decls.end ());
+ i != e; ++i)
+ {
+ if (i->prag && !i->assoc)
+ {
+ pragma const& p (*i->prag);
+ error (p.loc)
+ << "db pragma '" << p.pragma_name << "' is not associated with a "
+ << "declaration" << endl;
+ error_++;
+ }
+ }
+}
+
+string parser::impl::
+fq_scope (tree decl)
+{
+ string s, tmp;
+
+ for (tree scope (CP_DECL_CONTEXT (decl));
+ scope != global_namespace;)
+ {
+ tree prev (CP_DECL_CONTEXT (scope));
+
+ // If this is an inline namespace, pretend it doesn't exist.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ if (!is_nested_namespace (prev, scope, true))
+#else
+ if (!is_associated_namespace (prev, scope))
+#endif
+ {
+ tree n = DECL_NAME (scope);
+
+ tmp = "::";
+ tmp += (n != NULL_TREE ? IDENTIFIER_POINTER (n) : "");
+ tmp += s;
+ s.swap (tmp);
+ }
+
+ scope = prev;
+ }
+
+ return s;
+}
+
+//
+// parser
+//
+
+parser::
+~parser ()
+{
+ // Needs parser::impl definition.
+}
+
+parser::
+parser (options const& ops,
+ loc_pragmas& lp,
+ ns_loc_pragmas& nslp,
+ decl_pragmas& dp)
+ : impl_ (new impl (ops, lp, nslp, dp))
+{
+}
+
+unique_ptr<unit> parser::
+parse (tree global_scope, path const& main_file)
+{
+ return impl_->parse (global_scope, main_file);
+}
diff --git a/odb/parser.hxx b/odb/odb/parser.hxx
index b26c8f1..b26c8f1 100644
--- a/odb/parser.hxx
+++ b/odb/odb/parser.hxx
diff --git a/odb/odb/plugin.cxx b/odb/odb/plugin.cxx
new file mode 100644
index 0000000..c065a8a
--- /dev/null
+++ b/odb/odb/plugin.cxx
@@ -0,0 +1,458 @@
+// file : odb/plugin.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx> // Keep it first.
+
+#include <unistd.h> // stat()
+#include <sys/types.h> // stat
+#include <sys/stat.h> // stat
+
+#include <memory> // std::unique_ptr
+#include <string>
+#include <vector>
+#include <cstring> // std::strcpy, std::strstr
+#include <cassert>
+#include <iostream>
+
+#include <libcutl/re.hxx>
+#include <libcutl/fs/path.hxx>
+
+#include <odb/pragma.hxx>
+#include <odb/parser.hxx>
+#include <odb/options.hxx>
+#include <odb/option-functions.hxx>
+#include <odb/features.hxx>
+#include <odb/profile.hxx>
+#include <odb/version.hxx>
+#include <odb/validator.hxx>
+#include <odb/processor.hxx>
+#include <odb/generator.hxx>
+#include <odb/semantics/unit.hxx>
+
+using namespace std;
+using namespace semantics;
+
+using cutl::fs::path;
+using cutl::fs::invalid_path;
+
+typedef vector<path> paths;
+
+#if defined(_WIN32) && !defined(ODB_STATIC_PLUGIN)
+__declspec(dllexport)
+#endif
+int plugin_is_GPL_compatible;
+
+unique_ptr<options const> options_;
+paths profile_paths_;
+path file_; // File being compiled.
+paths inputs_; // List of input files in at-once mode or just file_.
+
+bool (*cpp_diagnostic_prev) (
+ cpp_reader*,
+#if BUILDING_GCC_MAJOR >= 9
+ cpp_diagnostic_level,
+ cpp_warning_reason,
+#else
+ int,
+ int,
+#endif
+#if BUILDING_GCC_MAJOR >= 6
+ rich_location*,
+#else
+ location_t,
+ unsigned int,
+#endif
+ const char*,
+ va_list*);
+
+static bool
+cpp_diagnostic_filter (cpp_reader* r,
+#if BUILDING_GCC_MAJOR >= 9
+ cpp_diagnostic_level level,
+ cpp_warning_reason reason,
+#else
+ int level,
+ int reason,
+#endif
+#if BUILDING_GCC_MAJOR >= 6
+ rich_location* l,
+#else
+ location_t l,
+ unsigned int column_override,
+#endif
+ const char* msg,
+ va_list* ap)
+{
+ // #pragma once in the main file. Note that the message that we get is
+ // potentially translated so we search for the substring (there is
+ // currently only one warning in libcpp that mentions #pragma once).
+ // Unfortunately, some translations translate the 'once' word (e.g,
+ // #pragma uno; I wonder if one can actually specify it like that in
+ // the source code). Oh, well, there is only so much we can do.
+ //
+ if (strstr (msg, "#pragma once") != 0)
+ return true;
+
+ return cpp_diagnostic_prev (
+ r,
+ level,
+ reason,
+#if BUILDING_GCC_MAJOR >= 6
+ l,
+#else
+ l,
+ column_override,
+#endif
+ msg,
+ ap);
+}
+
+// A prefix of the _cpp_file struct. This struct is not part of the
+// public interface so we have to resort to this technique (based on
+// libcpp/files.c).
+//
+struct cpp_file_prefix
+{
+ char const* name;
+ char const* path;
+ char const* pchname;
+ char const* dir_name;
+ _cpp_file* next_file;
+ const uchar* buffer;
+ const uchar* buffer_start;
+ const cpp_hashnode *cmacro;
+ cpp_dir *dir;
+ struct stat st;
+};
+
+extern "C" void
+start_unit_callback (void*, void*)
+{
+ // Set the preprocessor error callback to filter out useless diagnostics.
+ //
+ cpp_callbacks* cb (cpp_get_callbacks (parse_in));
+
+#if BUILDING_GCC_MAJOR >= 9
+ cpp_diagnostic_prev = cb->diagnostic;
+ cb->diagnostic = &cpp_diagnostic_filter;
+#else
+ cpp_diagnostic_prev = cb->error;
+ cb->error = &cpp_diagnostic_filter;
+#endif
+
+ if (cpp_diagnostic_prev == 0)
+ {
+ cerr << "ice: expected cpp diagnostic callback to be set" << endl;
+ exit (1);
+ }
+
+ // Set the directory of the main file (stdin) to that of the orginal
+ // file so that relative inclusion works. Also adjust the path and
+ // re-stat the file so that #pragma once works.
+ //
+ cpp_buffer* b (cpp_get_buffer (parse_in));
+ _cpp_file* f (cpp_get_file (b));
+ cpp_dir* d (cpp_get_dir (f));
+ char const* p (cpp_get_path (f));
+
+ cpp_file_prefix* fp (reinterpret_cast<cpp_file_prefix*> (f));
+
+ // Perform some sanity checks.
+ //
+ if (p != 0 && *p == '\0' // The path should be empty (stdin).
+ && cpp_get_prev (b) == 0 // This is the only buffer (main file).
+ && fp->path == p // Our prefix corresponds to the actual type.
+ && fp->dir == d // Our prefix corresponds to the actual type.
+ && fp->dir_name == 0) // The directory part hasn't been initialized.
+ {
+ // The dir_name is initialized by libcpp lazily so we can preemptively
+ // set it to what we need.
+ //
+ path d (file_.directory ());
+ char* s;
+
+ if (d.empty ())
+ {
+ s = XNEWVEC (char, 1);
+ *s = '\0';
+ }
+ else
+ {
+ size_t n (d.string ().size ());
+ s = XNEWVEC (char, n + 2);
+ strcpy (s, d.string ().c_str ());
+ s[n] = path::traits::directory_separator;
+ s[n + 1] = '\0';
+ }
+
+ fp->dir_name = s;
+
+ // Unless we are in the at-once mode (where input files are actually
+ // #include'ed into the synthesized stdin), pretend that we are the
+ // actual input file. This is necessary for the #pragma once to work.
+ //
+ // All this relies on the way things are implemented in libcpp. In
+ // particular, the #pragma once code first checks if the mtime and
+ // size of files match (that's why we need stat() below). If they
+ // do, then it goes ahead and compares their contents. To re-load
+ // the contents of the file libcpp uses the path (that's why we
+ // need to adjust that as well).
+ //
+ if (inputs_.size () == 1)
+ {
+ string const& f (file_.string ());
+
+ XDELETEVEC (fp->path);
+ size_t n (f.size ());
+ char* p (XNEWVEC (char, n + 1));
+ strcpy (p, f.c_str ());
+ p[n] = '\0';
+ fp->path = p;
+
+ // This call shouldn't fail since we've just opened it in the driver.
+ stat (fp->path, &fp->st);
+ }
+ }
+ else
+ {
+ cerr << "ice: unable to initialize main file directory" << endl;
+ exit (1);
+ }
+}
+
+extern "C" void
+gate_callback (void*, void*)
+{
+ // If there were errors during compilation, let GCC handle the
+ // exit.
+ //
+ if (errorcount || sorrycount)
+ return;
+
+ int r (0);
+
+ try
+ {
+ // Post process pragmas.
+ //
+ post_process_pragmas ();
+
+ // Parse the GCC tree to semantic graph.
+ //
+ parser p (*options_, loc_pragmas_, ns_loc_pragmas_, decl_pragmas_);
+ unique_ptr<unit> u (p.parse (global_namespace, file_));
+
+ features f;
+
+ // Process, pass 1.
+ //
+ process (*options_, f, *u, file_, 1);
+
+ // Validate, pass 1.
+ //
+ validate (*options_, f, *u, file_, 1);
+
+ // Process, pass 2.
+ //
+ process (*options_, f, *u, file_, 2);
+
+ // Validate, pass 2.
+ //
+ validate (*options_, f, *u, file_, 2);
+
+ // Generate.
+ //
+ generate (*options_, f, *u, file_, inputs_);
+ }
+ catch (cutl::re::format const& e)
+ {
+ cerr << "error: invalid regex: '" << e.regex () << "': " <<
+ e.description () << endl;
+ r = 1;
+ }
+ catch (pragmas_failed const&)
+ {
+ // Diagnostics has aready been issued.
+ //
+ r = 1;
+ }
+ catch (parser::failed const&)
+ {
+ // Diagnostics has aready been issued.
+ //
+ r = 1;
+ }
+ catch (validator_failed const&)
+ {
+ // Diagnostics has aready been issued.
+ //
+ r = 1;
+ }
+ catch (processor_failed const&)
+ {
+ // Diagnostics has aready been issued.
+ //
+ r = 1;
+ }
+ catch (generator_failed const&)
+ {
+ // Diagnostics has aready been issued.
+ //
+ r = 1;
+ }
+
+ exit (r);
+}
+
+static char const* const odb_version = ODB_COMPILER_VERSION_STR;
+
+typedef vector<string> strings;
+
+extern "C"
+#if defined(_WIN32) && !defined(ODB_STATIC_PLUGIN)
+__declspec(dllexport)
+#endif
+int
+plugin_init (plugin_name_args* plugin_info, plugin_gcc_version*)
+{
+ int r (0);
+ plugin_info->version = odb_version;
+
+ try
+ {
+ // Parse options.
+ //
+ {
+ strings argv_str;
+ argv_str.push_back (plugin_info->base_name);
+
+ for (int i (0); i < plugin_info->argc; ++i)
+ {
+ plugin_argument& a (plugin_info->argv[i]);
+
+ // A value cannot contain '=' so it is passed as the backspace
+ // character.
+ //
+ string v (a.value != 0 ? a.value : "");
+ for (size_t i (0); i < v.size (); ++i)
+ if (v[i] == '\b')
+ v[i] = '=';
+
+ // Handle service options.
+ //
+ if (strcmp (a.key, "svc-path") == 0)
+ {
+ profile_paths_.push_back (path (v));
+ continue;
+ }
+
+ if (strcmp (a.key, "svc-file") == 0)
+ {
+ // First is the main file. Subsequent are inputs in the at-once
+ // mode.
+ //
+ if (file_.empty ())
+ file_ = path (v);
+ else
+ inputs_.push_back (path (v));
+
+ continue;
+ }
+
+ string opt (strlen (a.key) > 1 ? "--" : "-");
+ opt += a.key;
+
+ argv_str.push_back (opt);
+
+ if (a.value != 0)
+ argv_str.push_back (v);
+ }
+
+ vector<char*> argv;
+ for (strings::iterator i (argv_str.begin ()); i != argv_str.end (); ++i)
+ argv.push_back (const_cast<char*> (i->c_str ()));
+
+ int argc (static_cast<int> (argv.size ()));
+
+ if (inputs_.empty ())
+ inputs_.push_back (file_);
+
+ // Two-phase options parsing, similar to the driver.
+ //
+ cli::argv_file_scanner::option_info oi[3];
+ oi[0].option = "--options-file";
+ oi[0].search_func = 0;
+ oi[1].option = "-p";
+ oi[2].option = "--profile";
+
+ database db;
+ {
+ oi[1].search_func = &profile_search_ignore;
+ oi[2].search_func = &profile_search_ignore;
+
+ cli::argv_file_scanner scan (argc, &argv[0], oi, 3);
+ options ops (scan);
+ assert (ops.database_specified ());
+ db = ops.database ()[0];
+ }
+
+ profile_data pd (profile_paths_, db, "odb plugin");
+ oi[1].search_func = &profile_search;
+ oi[2].search_func = &profile_search;
+ oi[1].arg = &pd;
+ oi[2].arg = &pd;
+
+ cli::argv_file_scanner scan (argc, &argv[0], oi, 3);
+ unique_ptr<options> ops (
+ new options (scan, cli::unknown_mode::fail, cli::unknown_mode::fail));
+
+ // Process options.
+ //
+ process_options (*ops);
+
+ options_ = move (ops);
+ pragma_db_ = db;
+ pragma_multi_ = options_->multi_database ();
+ }
+
+ if (options_->trace ())
+ cerr << "starting plugin " << plugin_info->base_name << endl;
+
+ // Disable assembly output. GCC doesn't define HOST_BIT_BUCKET
+ // correctly for MinGW (it still used /dev/null which fails to
+ // open).
+ //
+#ifdef _WIN32
+ asm_file_name = "nul";
+#else
+ asm_file_name = HOST_BIT_BUCKET;
+#endif
+
+ // Register callbacks.
+ //
+ register_callback (plugin_info->base_name,
+ PLUGIN_PRAGMAS,
+ register_odb_pragmas,
+ 0);
+
+ register_callback (plugin_info->base_name,
+ PLUGIN_START_UNIT,
+ start_unit_callback,
+ 0);
+
+ register_callback (plugin_info->base_name,
+ PLUGIN_OVERRIDE_GATE,
+ &gate_callback,
+ 0);
+ }
+ catch (cli::exception const& ex)
+ {
+ cerr << ex << endl;
+ r = 1;
+ }
+
+ if (r != 0)
+ exit (r);
+
+ return r;
+}
diff --git a/odb/odb/pragma.cxx b/odb/odb/pragma.cxx
new file mode 100644
index 0000000..6668733
--- /dev/null
+++ b/odb/odb/pragma.cxx
@@ -0,0 +1,4442 @@
+// file : odb/pragma.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <cctype> // std::isalnum
+#include <limits>
+#include <vector>
+#include <sstream>
+
+#include <odb/diagnostics.hxx>
+#include <odb/lookup.hxx>
+#include <odb/pragma.hxx>
+#include <odb/cxx-token.hxx>
+#include <odb/cxx-lexer.hxx>
+
+#include <odb/context.hxx>
+#include <odb/relational/context.hxx>
+
+using namespace std;
+using namespace cutl;
+
+using container::any;
+
+virt_declarations virt_declarations_;
+loc_pragmas loc_pragmas_;
+decl_pragmas decl_pragmas_;
+ns_loc_pragmas ns_loc_pragmas_;
+
+database pragma_db_;
+multi_database pragma_multi_;
+
+template <typename X>
+void
+accumulate (compiler::context& ctx, string const& k, any const& v, location_t)
+{
+ // Empty values are used to indicate that this pragma must be ignored.
+ //
+ if (v.empty ())
+ return;
+
+ typedef vector<X> container;
+
+ container& c (ctx.count (k)
+ ? ctx.get<container> (k)
+ : ctx.set (k, container ()));
+
+ c.push_back (v.value<X> ());
+}
+
+// Parse a qualified string. It can be in one of the following formats:
+//
+// "foo.bar.baz" (can have empty leading component, e.g., ".bar.baz")
+// "foo"."bar"."baz" (can have empty leading component, e.g., ""."bar"."baz")
+// ."foo.bar" (used to specify unqualifed names with periods)
+//
+// Empty leading components means fully qualified (similar to ::foo in C++).
+//
+static bool
+parse_qname (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ string const& p, // Pragma name for diagnostic.
+ qname& name,
+ bool* expr = 0, // If specified, detect an expression
+ string* expr_str = 0) // and store it here.
+{
+ assert (tt == CPP_STRING || tt == CPP_DOT);
+
+ // Handle the special case of unqualified name which can contain periods.
+ //
+ if (tt == CPP_DOT)
+ {
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_STRING)
+ {
+ error (l) << "name expected after '.' in db pragma " << p << endl;
+ return false;
+ }
+
+ name = tl;
+ tt = l.next (tl, &tn);
+ return true;
+ }
+
+ name.clear ();
+ string str (tl);
+
+ // See what comes after the string.
+ //
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_DOT)
+ {
+ name.append (str);
+
+ for (; tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_STRING)
+ {
+ error (l) << "name expected after '.' in db pragma " << p << endl;
+ return false;
+ }
+
+ name.append (tl);
+ }
+
+ return true;
+ }
+
+ if (expr != 0 && tt == CPP_PLUS)
+ {
+ *expr = true;
+ *expr_str = str;
+ return true;
+ }
+
+ // Scan the string looking for '.' as well as for non-identifier
+ // characters if we need to detect expressions.
+ //
+ string::size_type prev (string::npos);
+
+ for (size_t i (0); i < str.size (); ++i)
+ {
+ char c (str[i]);
+
+ if (c == '.')
+ {
+ if (prev == string::npos)
+ name.append (string (str, 0, i));
+ else
+ name.append (string (str, prev + 1, i - prev - 1));
+
+ prev = i;
+ }
+ else if (expr != 0 && !(isalnum (c) || c == '_'))
+ {
+ *expr = true;
+ *expr_str = str;
+ return true;
+ }
+ }
+
+ if (prev == string::npos)
+ name.append (str);
+ else
+ name.append (string (str, prev + 1, string::npos));
+
+ return true;
+}
+
+static bool
+parse_expression (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ cxx_tokens& ts,
+ string const& prag)
+{
+ // Keep reading tokens until we see a mis-matching ')' or ',' while
+ // keeping track of the '()' and '{}' balance.
+ //
+ size_t p_balance (0), b_balance (0);
+
+ for (; tt != CPP_EOF; tt = l.next (tl, &tn))
+ {
+ bool done (false);
+ cxx_token ct (l.location (), tt);
+
+ switch (tt)
+ {
+ case CPP_OPEN_BRACE:
+ {
+ b_balance++;
+ break;
+ }
+ case CPP_CLOSE_BRACE:
+ {
+ b_balance--;
+ break;
+ }
+ case CPP_OPEN_PAREN:
+ {
+ p_balance++;
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ if (p_balance == 0 && b_balance == 0)
+ done = true;
+ else
+ p_balance--;
+ break;
+ }
+ case CPP_COMMA:
+ {
+ if (p_balance == 0 && b_balance == 0)
+ done = true;
+ else
+ break;
+ }
+ case CPP_STRING:
+ {
+ ct.literal = tl;
+ break;
+ }
+ case CPP_NAME:
+ //case CPP_KEYWORD: see default:
+ {
+ ct.literal = tl;
+ break;
+ }
+ case CPP_NUMBER:
+ {
+ switch (TREE_CODE (tn))
+ {
+ case INTEGER_CST:
+ {
+ tree type (TREE_TYPE (tn));
+ unsigned long long v (integer_value (tn));
+
+ ostringstream os;
+ os << v;
+
+ if (type == long_long_integer_type_node)
+ os << "LL";
+ else if (type == long_long_unsigned_type_node)
+ os << "ULL";
+ else if (type == long_integer_type_node)
+ os << "L";
+ else if (type == long_unsigned_type_node)
+ os << "UL";
+ else if (
+ TYPE_UNSIGNED (type) &&
+ TYPE_PRECISION (type) >= TYPE_PRECISION (integer_type_node))
+ os << "U";
+
+ ct.literal = os.str ();
+ break;
+ }
+ case REAL_CST:
+ {
+ tree type (TREE_TYPE (tn));
+ REAL_VALUE_TYPE val (TREE_REAL_CST (tn));
+
+ // This is the best we can do. val cannot be INF or NaN.
+ //
+ char tmp[256];
+ real_to_decimal (tmp, &val, sizeof (tmp), 0, true);
+ istringstream is (tmp);
+ ostringstream os;
+
+ if (type == float_type_node)
+ {
+ float f;
+ is >> f;
+ os << f << 'F';
+ }
+ else
+ {
+ double d;
+ is >> d;
+ os << d;
+ }
+
+ ct.literal = os.str ();
+ break;
+ }
+ default:
+ {
+ error (l) << "unexpected numeric constant in db pragma "
+ << prag << endl;
+ return false;
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ // CPP_KEYWORD is not in the cpp_ttype enumeration.
+ //
+ if (tt == CPP_KEYWORD)
+ ct.literal = tl;
+
+ break;
+ }
+ }
+
+ if (done)
+ break;
+
+ // We don't store the tree node in ct since we converted numbers to
+ // string literals.
+ //
+ ts.push_back (ct);
+ }
+
+ return true;
+}
+
+static string
+parse_scoped_name (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ string const& prag)
+{
+ try
+ {
+ return lookup::parse_scoped_name (l, tt, tl, tn);
+ }
+ catch (lookup::invalid_name const&)
+ {
+ error (l) << "invalid name in db pragma " << prag << endl;
+ return "";
+ }
+}
+
+static tree
+resolve_scoped_name (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ tree start_scope,
+ string& name,
+ bool is_type,
+ string const& prag,
+ bool trailing_scope = false,
+ cpp_ttype* prev_tt = 0)
+{
+ try
+ {
+ cpp_ttype ptt; // Not used.
+ tree r (
+ lookup::resolve_scoped_name (
+ l, tt, tl, tn, ptt, start_scope, name, is_type, trailing_scope));
+
+ if (prev_tt != 0)
+ *prev_tt = ptt;
+
+ return r;
+ }
+ catch (lookup::invalid_name const&)
+ {
+ error (l) << "invalid name in db pragma " << prag << endl;
+ return 0;
+ }
+ catch (lookup::unable_to_resolve const& e)
+ {
+ if (e.last ())
+ error (l) << "unable to resolve " << (is_type ? "type " : "") << "name "
+ << "'" << e.name () << "' in db pragma " << prag << endl;
+ else
+ error (l) << "unable to resolve name '" << e.name () << "' in db pragma "
+ << prag << endl;
+
+ return 0;
+ }
+}
+
+// Resolve a data member in the specified scope taking into account virtual
+// member declarations.
+//
+declaration
+resolve_data_member (tree scope,
+ const cxx_tokens& name,
+ string& decl_name, // Note: appended to.
+ string const& prag)
+{
+ declaration decl;
+
+ if (name.size () == 1 && name.back ().type == CPP_NAME)
+ {
+ virt_declarations::iterator i (virt_declarations_.find (scope));
+
+ if (i != virt_declarations_.end ())
+ {
+ string const& n (name.back ().literal);
+
+ virt_declaration_set::const_iterator j (i->second.find (n, FIELD_DECL));
+
+ if (j != i->second.end ())
+ {
+ decl = declaration (*j);
+ decl_name += n;
+ }
+ }
+ }
+
+ if (!decl)
+ {
+ cxx_tokens_lexer l;
+ l.start (name);
+
+ tree tn;
+ string tl;
+ cpp_ttype tt (l.next (tl));
+
+ tree d (resolve_scoped_name (
+ l, tt, tl, tn, scope, decl_name, false, prag));
+
+ if (d == 0)
+ return decl; // Diagnostics has already been issued.
+
+ decl = declaration (d);
+ }
+
+ return decl;
+}
+
+static bool
+check_spec_decl_type (declaration const& d,
+ string const& name,
+ string const& p,
+ location_t l)
+{
+ gcc_tree_code_type tc (d.tree_code ());
+ bool type (TREE_CODE_CLASS (tc) == tcc_type);
+
+ if (p == "no_id")
+ {
+ // No_id can be used on objects only.
+ //
+ if (tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a class" << endl;
+ return false;
+ }
+ }
+ else if (p == "id" ||
+ p == "auto" ||
+ p == "column" ||
+ p == "inverse" ||
+ p == "on_delete" ||
+ p == "points_to" ||
+ p == "section" ||
+ p == "load" ||
+ p == "update" ||
+ p == "version" ||
+ p == "index" ||
+ p == "unique" ||
+ p == "get" ||
+ p == "set" ||
+ p == "access")
+ {
+ if (tc != FIELD_DECL)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "transient")
+ {
+ // Transient can be used for both data members and classes (object,
+ // view, or composite value).
+ //
+ if (tc != FIELD_DECL && tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member or class" << endl;
+ return false;
+ }
+ }
+ else if (p == "added")
+ {
+ // Added can be used for data members only.
+ //
+ if (tc != FIELD_DECL)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "deleted")
+ {
+ // Deleted can be used for both data members and classes (object,
+ // view of composite value).
+ //
+ if (tc != FIELD_DECL && tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member or class" << endl;
+ return false;
+ }
+ }
+ else if (p == "readonly")
+ {
+ // Readonly can be used for both data members and classes (object or
+ // composite value).
+ //
+ if (tc != FIELD_DECL && tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member or class" << endl;
+ return false;
+ }
+ }
+ else if (p == "abstract" ||
+ p == "callback" ||
+ p == "query" ||
+ p == "object" ||
+ p == "optimistic" ||
+ p == "polymorphic" ||
+ p == "definition" ||
+ p == "sectionable" ||
+ p == "bulk")
+ {
+ if (tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a class" << endl;
+ return false;
+ }
+ }
+ else if (p == "pointer")
+ {
+ // Table can be used for namespaces and classes (object or view).
+ //
+ if (tc != NAMESPACE_DECL && tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a class" << endl;
+ return false;
+ }
+ }
+ else if (p == "table")
+ {
+ // Table can be used for namespaces, members (container), and types
+ // (container, object, or view).
+ //
+ if (tc != NAMESPACE_DECL && tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a namespace, type, or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "session")
+ {
+ // Session can be used only for namespaces and objects.
+ //
+ if (tc != NAMESPACE_DECL && tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a namespace or class" << endl;
+ return false;
+ }
+ }
+ else if (p == "schema")
+ {
+ // For now schema can be used only for namespaces and objects.
+ //
+ if (tc != NAMESPACE_DECL && tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a namespace or class" << endl;
+ return false;
+ }
+ }
+ else if (p == "type" ||
+ p == "id_type" ||
+ p == "value_type" ||
+ p == "index_type" ||
+ p == "key_type")
+ {
+ // Type can be used for both members and types.
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "default")
+ {
+ // Default can be used for both members and types.
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "value_column" ||
+ p == "index_column" ||
+ p == "key_column" ||
+ p == "id_column")
+ {
+ // Container columns can be used for both members (container) and
+ // types (container).
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "options" ||
+ p == "value_options" ||
+ p == "index_options" ||
+ p == "key_options" ||
+ p == "id_options")
+ {
+ // Options can be used for both members and types.
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "null" ||
+ p == "not_null" ||
+ p == "key_null" ||
+ p == "key_not_null" ||
+ p == "value_null" ||
+ p == "value_not_null")
+ {
+ // Null pragmas can be used for both members and types (values,
+ // containers, and pointers).
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "unordered")
+ {
+ // Unordered can be used for both members (container) and
+ // types (container).
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "virtual")
+ {
+ // Virtual is specified for a member.
+ //
+ if (tc != FIELD_DECL)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "simple" ||
+ p == "container")
+ {
+ // Apply to both members and types.
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return false;
+ }
+
+ return true;
+}
+
+static void
+add_pragma (pragma const& prag, declaration const& decl, bool ns)
+{
+ if (decl)
+ decl_pragmas_[decl].insert (prag);
+ else
+ {
+ tree scope (current_scope ());
+
+ if (!ns)
+ {
+ if (!CLASS_TYPE_P (scope))
+ scope = global_namespace;
+
+ loc_pragmas_[scope].push_back (prag);
+ }
+ else
+ ns_loc_pragmas_.push_back (ns_loc_pragma (scope, prag));
+ }
+}
+
+static void
+handle_pragma (cxx_lexer& l,
+ string db,
+ string p,
+ string const& qualifier,
+ any& qualifier_value,
+ declaration const& decl,
+ string const& decl_name,
+ bool ns) // True if this is a position namespace pragma.
+{
+ cpp_ttype tt;
+ string tl;
+ tree tn;
+
+ // See if there is a database prefix. The db argument may already
+ // contain it.
+ //
+ if (db.empty () &&
+ (p == "mysql" ||
+ p == "sqlite" ||
+ p == "pgsql" ||
+ p == "oracle" ||
+ p == "mssql"))
+ {
+ tt = l.next (tl);
+
+ if (tt != CPP_COLON)
+ {
+ error (l) << "':' expected after database prefix " << p << endl;
+ return;
+ }
+
+ // Specifier prefix.
+ //
+ db = p;
+ tt = l.next (p);
+
+ if (tt != CPP_NAME && tt != CPP_KEYWORD)
+ {
+ error (l) << "expected specifier after databas prefix " << db << endl;
+ return;
+ }
+ }
+
+ string name (p); // Pragma name.
+ any val; // Pragma value.
+ pragma::add_func adder (0); // Custom context adder.
+ location_t loc (l.location ()); // Pragma location.
+
+ if (qualifier == "model")
+ {
+ // version(unsigned long long base,
+ // unsigned long long current,
+ // open|closed)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ assert (decl == global_namespace);
+
+ if (p == "version")
+ {
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ model_version v;
+
+ // base
+ //
+ if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
+ {
+ error (l) << "unsigned integer expected as base version" << endl;
+ return;
+ }
+
+ v.base = integer_value (tn);
+
+ if (v.base == 0)
+ {
+ error (l) << "base version cannot be zero" << endl;
+ return;
+ }
+
+ // current
+ //
+ if (l.next (tl, &tn) != CPP_COMMA)
+ {
+ error (l) << "current version expected after base version" << endl;
+ return;
+ }
+
+ if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
+ {
+ error (l) << "unsigned integer expected as current version" << endl;
+ return;
+ }
+
+ v.current = integer_value (tn);
+
+ if (v.current == 0)
+ {
+ error (l) << "current version cannot be zero" << endl;
+ return;
+ }
+
+ if (v.base > v.current)
+ {
+ error (l) << "current version should be greater than or equal to " <<
+ "base version" << endl;
+ return;
+ }
+
+ // open|closed
+ //
+ tt = l.next (tl, &tn);
+ if (tt == CPP_COMMA)
+ {
+ if (l.next (tl, &tn) != CPP_NAME || (tl != "open" && tl != "closed"))
+ {
+ error (l) << "open or closed expected after current version" << endl;
+ return;
+ }
+
+ v.open = (tl == "open");
+ tt = l.next (tl, &tn);
+ }
+ else
+ v.open = true;
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ name = "model-version";
+ val = v;
+ tt = l.next (tl, &tn);
+ }
+ else
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return;
+ }
+ }
+ else if (qualifier == "map")
+ {
+ // type("<regex>") | type(<name>)
+ // as("<subst>") | as(<name>)
+ // to("<subst>") | to(<expr>)
+ // from("<subst>") | from(<expr>)
+ //
+
+ if (p != "type" &&
+ p != "as" &&
+ p != "to" &&
+ p != "from")
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return;
+ }
+
+ // Make sure we've got the correct declaration type.
+ //
+ assert (decl == global_namespace);
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (qualifier_value.type_info () == typeid (custom_cxx_type))
+ {
+ // C++ type mapping.
+ //
+ custom_cxx_type& ct (qualifier_value.value<custom_cxx_type> ());
+
+ if (p == "type" || p == "as")
+ {
+ // Can be built-in type (e.g., bool).
+ //
+ if (tt == CPP_NAME || tt == CPP_KEYWORD || tt == CPP_SCOPE)
+ {
+ string name;
+ tree decl (
+ resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), name, true, p));
+
+ if (decl == 0)
+ return; // Diagnostics has already been issued.
+
+ if (TREE_CODE (decl) != TYPE_DECL)
+ {
+ error (loc) << "name '" << name << "' in db pragma "
+ << p << " does not refer to a type" << endl;
+ return;
+ }
+
+ (p == "type" ? ct.type_node : ct.as_node) = TREE_TYPE (decl);
+ (p == "type" ? ct.type_name : ct.as_name) = name;
+ }
+ else
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+ }
+ else if (p == "to" || p == "from")
+ {
+ if (tt != CPP_CLOSE_PAREN) // Empty expression is ok.
+ {
+ if (!parse_expression (
+ l, tt, tl, tn, (p == "to" ? ct.to : ct.from), p))
+ return; // Diagnostics has already been issued.
+ }
+ }
+ }
+ else
+ {
+ using relational::custom_db_type;
+
+ // Database type mapping.
+ //
+ custom_db_type& ct (qualifier_value.value<custom_db_type> ());
+
+ if (p == "type")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "type name regex expected in db pragma " << p << endl;
+ return;
+ }
+
+ try
+ {
+ // Make it case-insensitive.
+ //
+ ct.type.assign (tl, true);
+ }
+ catch (regex_format const& e)
+ {
+ error (l) << "invalid regex: '" << e.regex () << "' in db pragma "
+ << p << ": " << e.description () << endl;
+ return;
+ }
+ }
+ else if (p == "as")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+
+ ct.as = tl;
+ }
+ else if (p == "to")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "expression expected in db pragma " << p << endl;
+ return;
+ }
+
+ ct.to = tl;
+ }
+ else if (p == "from")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "expression expected in db pragma " << p << endl;
+ return;
+ }
+
+ ct.from = tl;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ name.clear (); // We don't need to add anything for this pragma.
+ tt = l.next (tl, &tn);
+ }
+ else if (qualifier == "index")
+ {
+ // unique
+ // type("<type>")
+ // method("<method>")
+ // options("<options>")
+ // member(<name>[, "<options>"])
+ // members(<name>[, <name>...])
+ //
+
+ if (p != "unique" &&
+ p != "type" &&
+ p != "method" &&
+ p != "options" &&
+ p != "member" &&
+ p != "members")
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return;
+ }
+
+ using relational::index;
+ index& in (qualifier_value.value<index> ());
+
+ if (p == "unique")
+ in.type = "UNIQUE";
+ else
+ {
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (p == "type")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "index type expected in db pragma " << p << endl;
+ return;
+ }
+
+ in.type = tl;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "method")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "index method expected in db pragma " << p << endl;
+ return;
+ }
+
+ in.method = tl;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "options")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "index options expected in db pragma " << p << endl;
+ return;
+ }
+
+ in.options = tl;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "member")
+ {
+ if (tt != CPP_NAME)
+ {
+ error (l) << "data member name expected in db pragma " << p << endl;
+ return;
+ }
+
+ index::member m;
+ m.loc = loc;
+ m.name = tl;
+
+ tt = l.next (tl, &tn);
+
+ // Parse nested members if any.
+ //
+ for (; tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "name expected after '.' in db pragma " << p << endl;
+ return;
+ }
+
+ m.name += '.';
+ m.name += tl;
+ }
+
+ // Parse member options, if any.
+ //
+ if (tt == CPP_COMMA)
+ {
+ if (l.next (tl, &tn) != CPP_STRING)
+ {
+ error (l) << "index member options expected in db pragma " << p
+ << endl;
+ return;
+ }
+
+ m.options = tl;
+ tt = l.next (tl, &tn);
+ }
+
+ in.members.push_back (m);
+ }
+ else if (p == "members")
+ {
+ for (;;)
+ {
+ if (tt != CPP_NAME)
+ {
+ error (l) << "data member name expected in db pragma " << p
+ << endl;
+ return;
+ }
+
+ index::member m;
+ m.loc = l.location ();
+ m.name = tl;
+
+ tt = l.next (tl, &tn);
+
+ // Parse nested members if any.
+ //
+ for (; tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "name expected after '.' in db pragma " << p
+ << endl;
+ return;
+ }
+
+ m.name += '.';
+ m.name += tl;
+ }
+
+ in.members.push_back (m);
+
+ if (tt == CPP_COMMA)
+ tt = l.next (tl, &tn);
+ else
+ break;
+ }
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+ }
+
+ name.clear (); // We don't need to add anything for this pragma.
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "index" ||
+ p == "unique")
+ {
+ // index
+ // unique
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "get" ||
+ p == "set" ||
+ p == "access")
+ {
+ // get(name|expr)
+ // set(name|expr)
+ // access(name|expr)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ member_access ma (loc, p == "set" ? "modifier" : "accessor", false);
+
+ tt = l.next (tl, &tn);
+ if (tt != CPP_CLOSE_PAREN) // Empty expression are ok.
+ {
+ if (!parse_expression (l, tt, tl, tn, ma.expr, p))
+ return; // Diagnostics has already been issued.
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+ }
+
+ val = ma;
+
+ // Convert access to the get/set pair.
+ //
+ if (p == "access")
+ {
+ if (db.empty () || db == pragma_db_.string ())
+ add_pragma (
+ pragma (p, "get", val, loc, &check_spec_decl_type, 0), decl, ns);
+
+ ma.kind = "modifier";
+ val = ma;
+ name = "set";
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "table")
+ {
+ // table (<name>)
+ // table (<name> [= "<alias>"] [type] [: "<cond>"] (view only)
+ //
+ // <name> := "name" | "name.name" | "name"."name"
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_STRING && tt != CPP_DOT)
+ {
+ error (l) << "table name expected in db pragma " << p << endl;
+ return;
+ }
+
+ // The table specifier is used for both objects and views. In case
+ // of an object, the context values is just a string. In case of a
+ // view, the context value is a view_object entry. The problem is
+ // that at this stage we don't know whether we are dealing with an
+ // object or a view. To resolve this in a somewhat hackish way, we
+ // are going to create both a string and a view_object entry.
+ //
+ view_object vo;
+ vo.kind = view_object::table;
+
+ if (!parse_qname (l, tt, tl, tn, p, vo.tbl_name))
+ return; // Diagnostics has already been issued.
+
+ if (tt == CPP_EQ)
+ {
+ // We have an alias.
+ //
+ if (l.next (tl, &tn) != CPP_STRING)
+ {
+ error (l) << "table alias expected after '=' in db pragma " << p
+ << endl;
+ return;
+ }
+
+ vo.alias = tl;
+ tt = l.next (tl, &tn);
+ }
+
+ if (tt == CPP_NAME)
+ {
+ // We have a JOIN type.
+ //
+ if (tl == "left")
+ vo.join = view_object::left;
+ else if (tl == "right")
+ vo.join = view_object::right;
+ else if (tl == "full")
+ vo.join = view_object::full;
+ else if (tl == "inner")
+ vo.join = view_object::inner;
+ else if (tl == "cross")
+ vo.join = view_object::cross;
+ else
+ {
+ error (l) << "unknown join type '" << tl << "'" << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else
+ vo.join = view_object::left;
+
+ if (tt == CPP_COLON)
+ {
+ // We have a condition.
+ //
+ if (vo.join == view_object::cross)
+ {
+ error (l)
+ << "no join condition can be specified for a cross join" << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (!parse_expression (l, tt, tl, tn, vo.cond, p))
+ return; // Diagnostics has already been issued.
+
+ if (vo.cond.empty ())
+ {
+ error (l) << "join condition expected after ':' in db pragma " << p
+ << endl;
+ return;
+ }
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ // Add the "table" pragma.
+ //
+ if (vo.alias.empty () && vo.cond.empty ())
+ {
+ if (db.empty () || db == pragma_db_.string ())
+ {
+ add_pragma (
+ pragma (p, name, vo.tbl_name, loc, &check_spec_decl_type, 0),
+ decl,
+ ns);
+ }
+ }
+
+ vo.scope = current_scope ();
+ vo.loc = loc;
+ val = vo;
+ name = "objects";
+ adder = &accumulate<view_object>;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "session")
+ {
+ // session
+ // session (true|false)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_KEYWORD || (tl != "true" && tl != "false"))
+ {
+ error (l) << "true or false expected in db pragma " << p << endl;
+ return;
+ }
+
+ val = (tl == "true");
+
+ tt = l.next (tl, &tn);
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ }
+ else if (p == "schema")
+ {
+ // schema (<name>)
+ //
+ // <name> := "name" | "name.name" | "name"."name"
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_STRING && tt != CPP_DOT)
+ {
+ error (l) << "table name expected in db pragma " << p << endl;
+ return;
+ }
+
+ qname s;
+ if (!parse_qname (l, tt, tl, tn, p, s))
+ return; // Diagnostics has already been issued.
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ val = s;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "pointer")
+ {
+ // pointer (qname)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ class_pointer cp;
+ size_t pb (0);
+ bool punc (false);
+
+ for (tt = l.next (tl, &tn);
+ tt != CPP_EOF && (tt != CPP_CLOSE_PAREN || pb != 0);
+ tt = l.next (tl, &tn))
+ {
+ if (punc && tt > CPP_LAST_PUNCTUATOR)
+ cp.name += ' ';
+
+ punc = false;
+
+ if (tt == CPP_OPEN_PAREN)
+ pb++;
+ else if (tt == CPP_CLOSE_PAREN)
+ pb--;
+
+ // @@ Need to handle literals, at least integer.
+ //
+ switch (tt)
+ {
+ case CPP_LESS:
+ {
+ cp.name += "< ";
+ break;
+ }
+ case CPP_GREATER:
+ {
+ cp.name += " >";
+ break;
+ }
+ case CPP_COMMA:
+ {
+ cp.name += ", ";
+ break;
+ }
+ case CPP_NAME:
+ // case CPP_KEYWORD: // see default:
+ {
+ cp.name += tl;
+ punc = true;
+ break;
+ }
+ default:
+ {
+ if (tt == CPP_KEYWORD)
+ {
+ cp.name += tl;
+ punc = true;
+ }
+ else if (tt <= CPP_LAST_PUNCTUATOR)
+ cp.name += cxx_lexer::token_spelling[tt];
+ else
+ {
+ error (l) << "unexpected token '" << cxx_lexer::token_spelling[tt]
+ << "' in db pragma " << p << endl;
+ return;
+ }
+ break;
+ }
+ }
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ if (cp.name.empty ())
+ {
+ error (l) << "expected pointer name in db pragma " << p << endl;
+ return;
+ }
+
+ cp.scope = current_scope ();
+ cp.loc = loc;
+ val = cp;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "abstract")
+ {
+ // abstract
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "optimistic")
+ {
+ // optimistic
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "polymorphic")
+ {
+ // polymorphic
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "definition")
+ {
+ // definition
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ val = l.location ();
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "sectionable")
+ {
+ // sectionable
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "callback")
+ {
+ // callback (name)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME)
+ {
+ error (l) << "member function name expected in db pragma " << p << endl;
+ return;
+ }
+
+ val = tl;
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "bulk")
+ {
+ // bulk (batch-size)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
+ {
+ error (l) << "unsigned integer expected as batch size" << endl;
+ return;
+ }
+
+ unsigned long long b (integer_value (tn));
+
+ if (b == 0 || b == 1)
+ {
+ error (l) << "batch size has to be greater than 1" << endl;
+ return;
+ }
+
+ val = b;
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "query")
+ {
+ // query ()
+ // query ("statement")
+ // query (expr)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ view_query vq;
+
+ bool s (false);
+ string str;
+
+ if (tt == CPP_STRING)
+ {
+ s = true;
+ str = tl;
+ tt = l.next (tl, &tn);
+ }
+
+ if (tt == CPP_CLOSE_PAREN)
+ {
+ if (s)
+ vq.literal = str;
+ else
+ {
+ // Empty query() pragma indicates that the statement will be
+ // provided at runtime. Encode this case as empty literal
+ // and expression.
+ //
+ }
+ }
+ else
+ {
+ // Expression.
+ //
+ if (s)
+ vq.expr.push_back (cxx_token (0, CPP_STRING, str));
+
+ if (!parse_expression (l, tt, tl, tn, vq.expr, p))
+ return; // Diagnostics has already been issued.
+ }
+
+ // Disallow query(, distinct).
+ //
+ if (tt == CPP_COMMA && vq.expr.empty ())
+ {
+ error (l) << "query expression expected in db pragma " << p << endl;
+ return;
+ }
+
+ // The query expression can be omitted with the modifier in its
+ // place. Handle this case.
+ //
+ if (vq.expr.size () == 1 && vq.expr.front ().type == CPP_NAME)
+ {
+ string const& n (vq.expr.front ().literal);
+
+ if (n == "distinct")
+ vq.distinct = true;
+ else if (n == "for_update")
+ vq.for_update = true;
+
+ if (vq.distinct || vq.for_update)
+ vq.expr.clear ();
+ }
+
+ if (tt == CPP_COMMA)
+ {
+ do
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "result modifier expected in db pragma " << p << endl;
+ return;
+ }
+
+ if (tl == "distinct")
+ vq.distinct = true;
+ else if (tl == "for_update")
+ vq.for_update = true;
+ else
+ {
+ error (l) << "unknown result modifier '" << tl << "'" << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ } while (tt == CPP_COMMA);
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ vq.scope = current_scope ();
+ vq.loc = loc;
+ val = vq;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "object")
+ {
+ // object (fq-name [ = name] [type] [: expr])
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME && tt != CPP_SCOPE)
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+
+ view_object vo;
+ vo.kind = view_object::object;
+ vo.obj_node = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), vo.obj_name, true, p);
+
+ if (vo.obj_node == 0)
+ return; // Diagnostics has already been issued.
+
+ // Get the actual type if this is a TYPE_DECL. Also get the main variant.
+ //
+ if (TREE_CODE (vo.obj_node) == TYPE_DECL)
+ vo.obj_node = TREE_TYPE (vo.obj_node);
+
+ if (TYPE_P (vo.obj_node)) // Can be a template.
+ vo.obj_node = TYPE_MAIN_VARIANT (vo.obj_node);
+
+ if (tt == CPP_EQ)
+ {
+ // We have an alias.
+ //
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "alias name expected after '=' in db pragma " << p
+ << endl;
+ return;
+ }
+
+ vo.alias = tl;
+ tt = l.next (tl, &tn);
+ }
+
+ if (tt == CPP_NAME)
+ {
+ // We have a JOIN type.
+ //
+ if (tl == "left")
+ vo.join = view_object::left;
+ else if (tl == "right")
+ vo.join = view_object::right;
+ else if (tl == "full")
+ vo.join = view_object::full;
+ else if (tl == "inner")
+ vo.join = view_object::inner;
+ else if (tl == "cross")
+ vo.join = view_object::cross;
+ else
+ {
+ error (l) << "unknown join type '" << tl << "'" << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else
+ vo.join = view_object::left;
+
+ if (tt == CPP_COLON)
+ {
+ // We have a condition.
+ //
+ if (vo.join == view_object::cross)
+ {
+ error (l)
+ << "no join condition can be specified for a cross join" << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (!parse_expression (l, tt, tl, tn, vo.cond, p))
+ return; // Diagnostics has already been issued.
+
+ if (vo.cond.empty ())
+ {
+ error (l) << "join condition expected after ':' in db pragma " << p
+ << endl;
+ return;
+ }
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ vo.scope = current_scope ();
+ vo.loc = loc;
+ val = vo;
+ name = "objects"; // Change the context entry name.
+ adder = &accumulate<view_object>;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "id")
+ {
+ // id[(member-path)]
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ string name;
+
+ tt = l.next (tl, &tn);
+ if (tt == CPP_OPEN_PAREN)
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "data member name expected in db pragma " << p
+ << endl;
+ return;
+ }
+
+ name = tl;
+
+ for (tt = l.next (tl, &tn); tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "name expected after '.' in db pragma " << p << endl;
+ return;
+ }
+
+ name += '.';
+ name += tl;
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+
+ val = name;
+ }
+ else if (p == "no_id")
+ {
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ name = "id";
+ val = false;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "auto")
+ {
+ // auto
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "column")
+ {
+ // column ("<name>")
+ // column ("<name>.<name>") (view only)
+ // column ("<name>"."<name>") (view only)
+ // column (fq-name) (view only)
+ // column (expr) (view only)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ bool expr (false);
+ string expr_str;
+ if (tt == CPP_STRING || tt == CPP_DOT)
+ {
+ qname qn;
+
+ if (!parse_qname (l, tt, tl, tn, p, qn, &expr, &expr_str))
+ return; // Diagnostics has already been issued.
+
+ if (tt == CPP_CLOSE_PAREN)
+ {
+ table_column tc;
+ tc.expr = expr;
+
+ if (expr)
+ tc.column = expr_str;
+ else
+ {
+ tc.table = qn.qualifier ();
+ tc.column = qn.uname ();
+ }
+
+ val = tc;
+ }
+ else if (!expr)
+ {
+ error (l) << "column name, expression, or data member name expected "
+ << "in db pragma " << p << endl;
+ return;
+ }
+ }
+
+ if (val.empty ())
+ {
+ // We have an expression.
+ //
+ column_expr e;
+
+ if (expr)
+ {
+ e.push_back (column_expr_part ());
+ e.back ().kind = column_expr_part::literal;
+ e.back ().value = expr_str;
+
+ if (tt != CPP_PLUS)
+ {
+ error (l) << "'+' or ')' expected in db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+
+ for (;;)
+ {
+ if (tt == CPP_STRING)
+ {
+ e.push_back (column_expr_part ());
+ e.back ().kind = column_expr_part::literal;
+ e.back ().value = tl;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (tt == CPP_NAME || tt == CPP_SCOPE)
+ {
+ string name (parse_scoped_name (l, tt, tl, tn, p));
+
+ if (name.empty ())
+ return; // Diagnostics has already been issued.
+
+ // Resolve nested members if any.
+ //
+ for (; tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "name expected after '.' in db pragma " << p
+ << endl;
+ return;
+ }
+
+ name += '.';
+ name += tl;
+ }
+
+ e.push_back (column_expr_part ());
+ e.back ().kind = column_expr_part::reference;
+ e.back ().value = name;
+ e.back ().scope = current_scope ();
+ e.back ().loc = loc;
+ }
+ else
+ {
+ error (l) << "column name, expression, or data member name expected "
+ << "in db pragma " << p << endl;
+ return;
+ }
+
+ if (tt == CPP_PLUS)
+ tt = l.next (tl, &tn);
+ else if (tt == CPP_CLOSE_PAREN)
+ break;
+ else
+ {
+ error (l) << "'+' or ')' expected in db pragma " << p << endl;
+ return;
+ }
+ }
+
+ e.loc = loc;
+ val = e;
+ name = "column-expr";
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "value_column" ||
+ p == "index_column" ||
+ p == "key_column" ||
+ p == "id_column")
+ {
+ // value_column ("<name>")
+ // index_column ("<name>")
+ // key_column ("<name>")
+ // id_column ("<name>")
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_STRING)
+ {
+ error (l) << "column name expected in db pragma " << p << endl;
+ return;
+ }
+
+ val = tl;
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "options" ||
+ p == "value_options" ||
+ p == "index_options" ||
+ p == "key_options" ||
+ p == "id_options")
+ {
+ // options (["<name>"])
+ // value_options (["<name>"])
+ // index_options (["<name>"])
+ // key_options (["<name>"])
+ // id_options (["<name>"])
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_STRING)
+ {
+ // Ignore empty options strings. Internally, we use them to
+ // indicate options reset (see below).
+ //
+ if (!tl.empty ())
+ val = tl;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (tt == CPP_CLOSE_PAREN)
+ {
+ // Empty options specifier signals options reset. Encode it as an
+ // empty string.
+ //
+ val = string ();
+ }
+ else
+ {
+ error (l) << "options string expected in db pragma " << p << endl;
+ return;
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ adder = &accumulate<string>;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "type" ||
+ p == "id_type" ||
+ p == "value_type" ||
+ p == "index_type" ||
+ p == "key_type")
+ {
+ // type ("<type>")
+ // id_type ("<type>")
+ // value_type ("<type>")
+ // index_type ("<type>")
+ // key_type ("<type>")
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_STRING)
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+
+ val = tl;
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "null" ||
+ p == "not_null" ||
+ p == "key_null" ||
+ p == "key_not_null" ||
+ p == "value_null" ||
+ p == "value_not_null")
+ {
+ // null
+ // not_null
+ // key_null
+ // key_not_null
+ // value_null
+ // value_not_null
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "default")
+ {
+ // default () (<empty>)
+ // default (null) (n)
+ // default (true|false) (t|f)
+ // default ([+|-]<number>) (-|+)
+ // default ("string") (s)
+ // default (<enumerator>) (e)
+ //
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ default_value dv;
+
+ switch (tt)
+ {
+ case CPP_CLOSE_PAREN:
+ {
+ // Default value reset.
+ //
+ dv.kind = default_value::reset;
+ break;
+ }
+ case CPP_STRING:
+ {
+ dv.kind = default_value::string;
+ dv.literal = tl;
+ tt = l.next (tl, &tn);
+ break;
+ }
+ case CPP_NAME:
+ {
+ // This can be null or an enumerator name.
+ //
+ if (tl == "null")
+ {
+ dv.kind = default_value::null;
+ tt = l.next (tl, &tn);
+ break;
+ }
+ }
+ // Fall through.
+ case CPP_SCOPE:
+ {
+ // We have a potentially scopped enumerator name.
+ //
+ dv.enum_value = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), dv.literal, false, p);
+
+ if (dv.enum_value == 0)
+ return; // Diagnostics has already been issued.
+
+ dv.kind = default_value::enumerator;
+ break;
+ }
+ case CPP_MINUS:
+ case CPP_PLUS:
+ {
+ if (tt == CPP_MINUS)
+ dv.literal = "-";
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NUMBER)
+ {
+ error (l) << "expected numeric constant after '"
+ << (tt == CPP_MINUS ? "-" : "+") << "' in db pragma "
+ << p << endl;
+ return;
+ }
+ }
+ // Fall through.
+ case CPP_NUMBER:
+ {
+ switch (TREE_CODE (tn))
+ {
+ case INTEGER_CST:
+ {
+ dv.int_value = integer_value (tn);
+ dv.kind = default_value::integer;
+ break;
+ }
+ case REAL_CST:
+ {
+ REAL_VALUE_TYPE d (TREE_REAL_CST (tn));
+
+ if (REAL_VALUE_ISINF (d))
+ dv.float_value = numeric_limits<double>::infinity ();
+ else if (REAL_VALUE_ISNAN (d))
+ dv.float_value = numeric_limits<double>::quiet_NaN ();
+ else
+ {
+ char tmp[256];
+ real_to_decimal (tmp, &d, sizeof (tmp), 0, true);
+ istringstream is (tmp);
+ is >> dv.float_value;
+ }
+
+ if (dv.literal == "-")
+ dv.float_value = -dv.float_value;
+
+ dv.kind = default_value::floating;
+ break;
+ }
+ default:
+ {
+ error (l) << "unexpected numeric constant in db pragma " << p
+ << endl;
+ return;
+ }
+ }
+
+ tt = l.next (tl, &tn);
+ break;
+ }
+ default:
+ {
+ // This can be the true or false keyword.
+ //
+ if (tt == CPP_KEYWORD && (tl == "true" || tl == "false"))
+ {
+ dv.kind = default_value::boolean;
+ dv.literal = tl;
+ tt = l.next (tl, &tn);
+ }
+ else
+ {
+ error (l) << "unexpected expression in db pragma " << p << endl;
+ return;
+ }
+
+ break;
+ }
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ val = dv;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "inverse")
+ {
+ // inverse (name)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME)
+ {
+ error (l) << "member name expected in db pragma " << p << endl;
+ return;
+ }
+
+ string name (tl);
+
+ tt = l.next (tl, &tn);
+
+ // Parse nested members if any.
+ //
+ for (; tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "name expected after '.' in db pragma " << p << endl;
+ return;
+ }
+
+ name += '.';
+ name += tl;
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ val = name;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "on_delete")
+ {
+ // on_delete (cascade|set_null)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ if (l.next (tl, &tn) != CPP_NAME || (tl != "cascade" && tl != "set_null"))
+ {
+ error (l) << "cascade or set_null expected after '('" << endl;
+ return;
+ }
+
+ using semantics::relational::foreign_key;
+ val = (tl == "cascade" ? foreign_key::cascade : foreign_key::set_null);
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "points_to")
+ {
+ // points_to(<fq-name>)
+ //
+
+ tree type;
+ string type_name;
+
+ string p (tl);
+ location_t loc (l.location ());
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_NAME || tt == CPP_SCOPE)
+ {
+ type = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), type_name, true, p);
+
+ if (type == 0)
+ return; // Diagnostics has already been issued.
+
+ // Get the actual type if this is a TYPE_DECL. Also get the main
+ // variant.
+ //
+ if (TREE_CODE (type) == TYPE_DECL)
+ type = TREE_TYPE (type);
+
+ if (TYPE_P (type)) // Can be a template.
+ type = TYPE_MAIN_VARIANT (type);
+
+ if (TREE_CODE (type) != RECORD_TYPE)
+ {
+ error (loc) << "name '" << type_name << "' in db pragma " << p
+ << " does not refer to a class" << endl;
+ return;
+ }
+
+ val = type;
+ }
+ else
+ {
+ error (l) << "class name expected in db pragma " << p << endl;
+ return;
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "section")
+ {
+ // section (name)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME)
+ {
+ error (l) << "member name expected in db pragma " << p << endl;
+ return;
+ }
+
+ name = "section-member";
+ val = tl;
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "load")
+ {
+ // load (eager|lazy)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME || (tl != "eager" && tl != "lazy"))
+ {
+ error (l) << "eager or lazy expected in db pragma " << p << endl;
+ return;
+ }
+
+ name = "section-load";
+ val = (tl == "eager"
+ ? user_section::load_eager
+ : user_section::load_lazy);
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "update")
+ {
+ // update (always|change|manual)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME ||
+ (tl != "always" && tl != "change" && tl != "manual"))
+ {
+ error (l) << "always, change, or manual expected in db pragma " <<
+ p << endl;
+ return;
+ }
+
+ name = "section-update";
+
+ if (tl == "always")
+ val = user_section::update_always;
+ else if (tl == "change")
+ val = user_section::update_change;
+ else
+ val = user_section::update_manual;
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "unordered")
+ {
+ // unordered
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "readonly")
+ {
+ // readonly
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "transient")
+ {
+ // transient
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "added" || p == "deleted")
+ {
+ // added (unsigned long long version)
+ // deleted (unsigned long long version)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ char const* n (p == "added" ? "addition" : "deletion");
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
+ {
+ error (l) << "unsigned integer expected as " << n << " version" << endl;
+ return;
+ }
+
+ unsigned long long v (integer_value (tn));
+
+ if (v == 0)
+ {
+ error (l) << n << " version cannot be zero" << endl;
+ return;
+ }
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ val = v;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "version")
+ {
+ // version
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "virtual")
+ {
+ // Stray virtual specifier (i.e., without explicit member name).
+ //
+ error (l) << "virtual member declaration requires name" << endl;
+ return;
+ }
+ else if (p == "before" || p == "after")
+ {
+ // Stray before/after specifier (i.e., without preceding virtual).
+ //
+ error (l) << p << " specifier must follow virtual" << endl;
+ return;
+ }
+ else
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return;
+ }
+
+ // Add the pragma unless was indicated otherwise.
+ //
+ if (!name.empty () && (db.empty () || db == pragma_db_.string ()))
+ {
+ // If the value is not specified and we don't use a custom adder,
+ // then make it bool (flag).
+ //
+ if (adder == 0 && val.empty ())
+ val = true;
+
+ // Convert '_' to '-' in the context name so we get foo-bar instead
+ // of foo_bar (that's the convention used).
+ //
+ for (size_t i (0); i < name.size (); ++i)
+ if (name[i] == '_')
+ name[i] = '-';
+
+ // Record this pragma.
+ //
+ add_pragma (
+ pragma (p, name, val, loc, &check_spec_decl_type, adder), decl, ns);
+ }
+
+ // Mark the type or member as simple value or container, depending
+ // on the pragma. For static multi-database support we only do it
+ // if the pragma applies to this database since in this case we can
+ // have different mappings for different databases (e.g., composite
+ // in one and simple in another). For dynamic multi-database support
+ // we do this regardless of the database since here the mapping
+ // should the consistent.
+ //
+ // @@ Did we add new simple value or container pragmas and forgot to
+ // account for them here?
+ //
+ if ((qualifier == "value" || qualifier == "member") &&
+ (pragma_multi_ == multi_database::dynamic || db.empty () ||
+ db == pragma_db_.string ()))
+ {
+ // We assume a data member is simple only if the database type was
+ // specified explicitly.
+ //
+ if (name == "type" ||
+ name == "id-type" ||
+ (qualifier == "value" &&
+ (name == "null" ||
+ name == "not-null" ||
+ name == "default" ||
+ name == "options")))
+ {
+ add_pragma (pragma (p, "simple", true, loc, &check_spec_decl_type, 0),
+ decl,
+ false);
+ }
+ else if (name == "table" ||
+ name == "value-type" ||
+ name == "index-type" ||
+ name == "key-type" ||
+
+ name == "key-null" ||
+ name == "key-not-null" ||
+ name == "value-null" ||
+ name == "value-not-null" ||
+
+ name == "value-column" ||
+ name == "index-column" ||
+ name == "key-column" ||
+ name == "index-column" ||
+
+ name == "value-options" ||
+ name == "index-options" ||
+ name == "key-options" ||
+ name == "index-options" ||
+
+ name == "unordered")
+ {
+ add_pragma (pragma (p, "container", true, loc, &check_spec_decl_type, 0),
+ decl,
+ false);
+ }
+ }
+
+ // See if there are any more pragmas.
+ //
+ if (tt == CPP_NAME || tt == CPP_KEYWORD)
+ {
+ handle_pragma (l,
+ "",
+ tl,
+ qualifier,
+ qualifier_value,
+ decl,
+ decl_name,
+ ns);
+ }
+ else if (tt != CPP_EOF)
+ error (l) << "unexpected text after " << p << " in db pragma" << endl;
+}
+
+//
+// Qualifiers.
+//
+
+static bool
+check_qual_decl_type (declaration const& d,
+ string const& name,
+ string const& p,
+ location_t l)
+{
+ gcc_tree_code_type tc (d.tree_code ());
+ bool type (TREE_CODE_CLASS (tc) == tcc_type);
+
+ if (p == "model" ||
+ p == "map")
+ {
+ assert (d == global_namespace);
+ }
+ else if (p == "index")
+ {
+ if (tc != RECORD_TYPE)
+ {
+ // For an index, name is not empty only if the class name was
+ // specified explicitly. Otherwise, the index definition scope
+ // is assumed.
+ //
+ if (name.empty ())
+ {
+ error (l) << "db pragma " << p << " outside of a class scope" << endl;
+ info (l) << "use the db pragma " << p << "(<class-name>) syntax "
+ << " instead" << endl;
+ }
+ else
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a class" << endl;
+ return false;
+ }
+ }
+ else if (p == "namespace")
+ {
+ if (tc != NAMESPACE_DECL)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a namespace" << endl;
+ return false;
+ }
+ }
+ else if (p == "object" ||
+ p == "view")
+ {
+ if (tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a class" << endl;
+ return false;
+ }
+ }
+ else if (p == "value")
+ {
+ if (!type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type" << endl;
+ return false;
+ }
+ }
+ else if (p == "member")
+ {
+ if (tc != FIELD_DECL)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member" << endl;
+ return false;
+ }
+ }
+ else
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return false;
+ }
+
+ return true;
+}
+
+static void
+add_qual_entry (compiler::context& ctx,
+ string const& k,
+ any const& v,
+ location_t l)
+{
+ // Store the TYPE_DECL node that was referred to in the pragma. This
+ // can be used later as a name hint in case the type is a template
+ // instantiation. Also store the pragma location which is used as
+ // the "definition point" for this instantiation.
+ //
+ ctx.set ("tree-node", v);
+ ctx.set ("location", l);
+
+ ctx.set (k, true);
+}
+
+static void
+handle_pragma_qualifier (cxx_lexer& l, string p)
+{
+ cpp_ttype tt;
+ string tl;
+ tree tn;
+
+ declaration decl;
+ tree orig_decl (0); // Original declarations as used in the pragma.
+ string decl_name;
+
+ // Check for a database prefix.
+ //
+ string db;
+
+ if (p == "mysql" ||
+ p == "sqlite" ||
+ p == "pgsql" ||
+ p == "oracle" ||
+ p == "mssql")
+ {
+ tt = l.next (tl);
+
+ if (tt == CPP_COLON)
+ {
+ // Specifier prefix.
+ //
+ db = p;
+ tt = l.next (p);
+ }
+ else
+ {
+ // Qualifier prefix. Ignore the rest if the databases don't match.
+ //
+ if (p != pragma_db_.string ())
+ return;
+
+ p = tl;
+ }
+
+ if (tt != CPP_NAME && tt != CPP_KEYWORD)
+ {
+ error (l) << "expected specifier after db " << db << " pragma" << endl;
+ return;
+ }
+
+ // Make sure a qualifier prefix is not used before a specifier.
+ //
+ if (!db.empty () &&
+ (p == "model" ||
+ p == "map" ||
+ p == "namespace" ||
+ p == "object" ||
+ p == "view" ||
+ p == "value" ||
+ p == "member"))
+ {
+ error (l) << "specifier prefix '" << db << ":' used before qualifier " <<
+ p << endl;
+ return;
+ }
+ }
+
+ //
+ //
+ string name (p); // Pragma name.
+ any val; // Pragma value.
+ location_t loc (l.location ()); // Pragma location.
+ pragma::add_func adder (0); // Custom context adder.
+ bool ns (false); // Namespace location pragma.
+
+ cxx_tokens saved_tokens; // Saved token sequence to be replayed.
+
+ // Pragma qualifiers.
+ //
+ if (p == "model")
+ {
+ orig_decl = global_namespace;
+ decl = declaration (orig_decl);
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "map")
+ {
+ // map type("<regex>") as("<subst>") [to("<subst>")] [from("<subst>")]
+ // map type(<c++-type>) as(<c++-type>) [to(<expr>)] [from(<expr>)]
+ //
+
+ // The thing that determines whether this is a database or C++ type
+ // mapping is what we have inside 'type'. So to handle this we are
+ // going to pre-scan the pragma looking for 'type' and saving the
+ // tokens. Once we determine what this is, we replay the saved
+ // tokens to actually parse them.
+ //
+
+ // Determine what this is by scanning the pragma until we see
+ // the 'type' qualifier or EOF.
+ //
+ bool db (true);
+
+ bool done (false);
+ size_t balance (0);
+ for (tt = l.next (tl, &tn);
+ !(done && balance == 0) && tt != CPP_EOF;
+ tt = l.next (tl, &tn))
+ {
+ saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+
+ switch (tt)
+ {
+ case CPP_OPEN_PAREN:
+ {
+ balance++;
+ continue;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ if (balance > 0)
+ balance--;
+ else
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+ continue;
+ }
+ case CPP_NAME:
+ {
+ if (balance == 0 && tl == "type")
+ break;
+
+ continue;
+ }
+ default:
+ continue;
+ }
+
+ tt = l.next (tl, &tn);
+ saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ balance++;
+
+ tt = l.next (tl, &tn);
+ saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+
+ db = (tt == CPP_STRING);
+ }
+
+ done = true; // Scan until the closing ')'.
+ }
+
+ if (balance != 0)
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+
+ orig_decl = global_namespace;
+ decl = declaration (orig_decl);
+
+ if (db)
+ {
+ using relational::custom_db_type;
+
+ custom_db_type ct;
+ ct.loc = loc;
+ val = ct;
+ name = "custom-db-types";
+ adder = &accumulate<custom_db_type>;
+ }
+ else
+ {
+ custom_cxx_type ct;
+ ct.loc = loc;
+ ct.scope = current_scope ();
+ val = ct;
+ name = "custom-cxx-types";
+ adder = &accumulate<custom_cxx_type>;
+ }
+ }
+ else if (p == "index")
+ {
+ // Index can be both a qualifier and a specifier. Things are complicated
+ // by the fact that when it is a specifier, it belongs to a member which
+ // means that the actual qualifier ('member') can be omitted. So we need
+ // to distinguish between cases like these:
+ //
+ // #pragma db index type("INTEGER") // specifier
+ // #pragma db index type("UNIQUE") member(foo_) // qualifier
+ //
+ // The thing that determines whether this is a qualifier or a specifier
+ // is the presence of the 'member' or 'members' specifier. So to handle
+ // this we are going to pre-scan the pragma looking for 'member' or
+ // 'members' and saving the tokens. Once we determine what this is,
+ // we replay the saved tokens to actually parse them.
+ //
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_OPEN_PAREN)
+ {
+ // Determine what this is by scanning the pragma until we see
+ // the 'member' qualifier or EOF.
+ //
+ bool qual (false);
+ size_t balance (0);
+
+ for (; tt != CPP_EOF; tt = l.next (tl, &tn))
+ {
+ switch (tt)
+ {
+ case CPP_OPEN_PAREN:
+ {
+ balance++;
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ if (balance > 0)
+ balance--;
+ else
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+ break;
+ }
+ case CPP_NAME:
+ {
+ if (balance == 0 && (tl == "member" || tl == "members"))
+ qual = true;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (qual)
+ break;
+
+ saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+ }
+
+ if (balance != 0)
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+
+ if (qual)
+ {
+ // This is a qualifer. The saved tokens sequence contains tokens
+ // until the first 'member' or 'members' specifier. So we will
+ // first need to re-play these tokens and then continue parsing
+ // as if we just saw the 'member' or 'members' specifier. The
+ // token type (tt) and token literal (tl) variables should contain
+ // the correct values.
+ //
+
+ // Also check that no specifier prefix was used for this qualifer.
+ //
+ if (!db.empty ())
+ {
+ error (loc) << "specifier prefix '" << db << ":' used before " <<
+ "qualifier index" << endl;
+ return;
+ }
+
+ orig_decl = current_scope ();
+ decl = declaration (orig_decl);
+ }
+ else
+ {
+ // This is a specifier. The saved tokens sequence contains all the
+ // tokens in this pragma until EOF.
+ //
+ cxx_tokens_lexer l;
+ l.start (saved_tokens, loc);
+ handle_pragma (
+ l, db, "index", "member", val, declaration (), "", false);
+ return;
+ }
+ }
+
+ relational::index in;
+ in.loc = loc;
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ // Specifier with the class fq-name, index name, or both.
+ //
+ // index(<fq-name>)
+ // index("<name>")
+ // index(<fq-name>::"<name>")
+ //
+ tt = l.next (tl, &tn);
+
+ // Resolve class name, if any.
+ //
+ if (tt == CPP_NAME || tt == CPP_SCOPE)
+ {
+ cpp_ttype ptt;
+ orig_decl = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), decl_name, true, p, true, &ptt);
+
+ if (orig_decl == 0)
+ return; // Diagnostics has already been issued.
+
+ // Get the actual type if this is a TYPE_DECL. Also get the main
+ // variant.
+ //
+ if (TREE_CODE (orig_decl) == TYPE_DECL)
+ orig_decl = TREE_TYPE (orig_decl);
+
+ if (TYPE_P (orig_decl)) // Can be a template.
+ decl = declaration (TYPE_MAIN_VARIANT (orig_decl));
+ else
+ decl = declaration (orig_decl);
+
+ if (tt == CPP_STRING && ptt != CPP_SCOPE)
+ {
+ error (l) << "'::' expected before index name in db pragma " << p
+ << endl;
+ return;
+ }
+
+ if (tt != CPP_STRING && ptt == CPP_SCOPE)
+ {
+ error (l) << "index name expected after '::' in db pragma " << p
+ << endl;
+ return;
+ }
+ }
+ else
+ {
+ orig_decl = current_scope ();
+ decl = declaration (orig_decl);
+ }
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (!check_qual_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (tt == CPP_STRING)
+ {
+ in.name = tl;
+ tt = l.next (tl, &tn);
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+
+ val = in;
+ adder = &accumulate<relational::index>;
+ }
+ else if (p == "namespace")
+ {
+ // namespace [(<identifier>)]
+ // namespace () (refers to global namespace)
+ //
+
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_NAME || tt == CPP_SCOPE)
+ {
+ orig_decl = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), decl_name, false, p);
+
+ if (orig_decl == 0)
+ return; // Diagnostics has already been issued.
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (!check_qual_decl_type (orig_decl, decl_name, p, loc))
+ return;
+
+ // Resolve namespace aliases if any.
+ //
+ orig_decl = ORIGINAL_NAMESPACE (orig_decl);
+ decl = declaration (orig_decl);
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (tt == CPP_CLOSE_PAREN)
+ {
+ orig_decl = global_namespace;
+ decl = declaration (orig_decl);
+ tt = l.next (tl, &tn);
+ }
+ else
+ {
+ error (l) << "data member name expected in db pragma " << p << endl;
+ return;
+ }
+ }
+ else
+ {
+ // Make sure we are in a namespace scope.
+ //
+ if (TREE_CODE (current_scope ()) != NAMESPACE_DECL)
+ {
+ error (l) << "db pragma " << p << " is not in a namespace scope"
+ << endl;
+ return;
+ }
+
+ ns = true;
+ }
+ }
+ else if (p == "object" ||
+ p == "view" ||
+ p == "value")
+ {
+ // object [(<identifier>)]
+ // view [(<identifier>)]
+ // value [(<identifier>)]
+ //
+
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ tt = l.next (tl, &tn);
+
+ // Can be built-in type (e.g., bool).
+ //
+ if (tt == CPP_NAME || tt == CPP_KEYWORD || tt == CPP_SCOPE)
+ {
+ orig_decl = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), decl_name, true, p);
+
+ if (orig_decl == 0)
+ return; // Diagnostics has already been issued.
+
+ // Get the actual type if this is a TYPE_DECL. Also get the main
+ // variant.
+ //
+ if (TREE_CODE (orig_decl) == TYPE_DECL)
+ orig_decl = TREE_TYPE (orig_decl);
+
+ if (TYPE_P (orig_decl)) // Can be a template.
+ decl = declaration (TYPE_MAIN_VARIANT (orig_decl));
+ else
+ decl = declaration (orig_decl);
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (!check_qual_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+ }
+ }
+ else if (p == "member")
+ {
+ // member [(<identifier>)]
+ //
+
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME && tt != CPP_SCOPE)
+ {
+ error (l) << "data member name expected in db pragma " << p << endl;
+ return;
+ }
+
+ // We need to see if this is a virtual data member declaration. Also,
+ // if it is not, then the name can still refer to one so we need to
+ // take extra steps to handle that. But first, we save the name and
+ // look for the 'virtual' specifier.
+ //
+ cxx_tokens name_tokens;
+ for (; tt != CPP_CLOSE_PAREN && tt != CPP_EOF; tt = l.next (tl, &tn))
+ name_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ // Now scan the remainder of the pragma looking for the 'virtual'
+ // keyword and saving the tokens in between for later.
+ //
+ bool virt (false);
+ size_t balance (0);
+ for (tt = l.next (tl, &tn); tt != CPP_EOF; tt = l.next (tl, &tn))
+ {
+ switch (tt)
+ {
+ case CPP_OPEN_PAREN:
+ {
+ balance++;
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ if (balance > 0)
+ balance--;
+ else
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+ break;
+ }
+ default:
+ {
+ if (balance == 0 && tt == CPP_KEYWORD && tl == "virtual")
+ virt = true;
+ break;
+ }
+ }
+
+ if (virt)
+ break;
+
+ saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+ }
+
+ if (balance != 0)
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+
+ // Regardless of whether this is a virtual member declaration or a
+ // reference, resolve its scope name (if one is specified), which
+ // should be a class. We will need it in both cases.
+ //
+ tree scope;
+ if (name_tokens.size () > 2) // scope::name
+ {
+ size_t n (name_tokens.size ());
+
+ if (name_tokens[n - 2].type != CPP_SCOPE ||
+ name_tokens[n - 1].type != CPP_NAME)
+ {
+ error (l) << "invalid name in db pragma " << p << endl;
+ return;
+ }
+
+ cxx_tokens scope_tokens (1, name_tokens.back ());
+ name_tokens.pop_back (); // ::
+ name_tokens.pop_back (); // name
+ name_tokens.swap (scope_tokens);
+
+ cxx_tokens_lexer l;
+ l.start (scope_tokens);
+
+ tree tn;
+ string tl;
+ cpp_ttype tt (l.next (tl));
+
+ scope = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), decl_name, true, p);
+
+ if (scope == 0)
+ return; // Diagnostics has already been issued.
+
+ scope = TYPE_MAIN_VARIANT (TREE_TYPE (scope));
+
+ if (tt != CPP_EOF)
+ {
+ error (l) << "invalid name in db pragma " << p << endl;
+ return;
+ }
+
+ decl_name += "::";
+ }
+ else
+ scope = current_scope ();
+
+ if (virt)
+ {
+ // Should be a single name.
+ //
+ if (name_tokens.size () > 1 || name_tokens.back ().type != CPP_NAME)
+ {
+ location_t l (name_tokens.back ().loc);
+ error (l) << "invalid name in db pragma " << p << endl;
+ return;
+ }
+ string const& name (name_tokens.back ().literal);
+
+ // Parse the remainder of the virtual specifier.
+ //
+ // virtual(<fq-name>)
+ //
+ tree type;
+ string type_name;
+ location_t ord (loc);
+ int ord_bias (0);
+ {
+ string p (tl);
+ location_t loc (l.location ());
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ // Can be built-in type (e.g., bool).
+ //
+ if (tt == CPP_NAME || tt == CPP_KEYWORD || tt == CPP_SCOPE)
+ {
+ type = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), type_name, true, p);
+
+ if (type == 0)
+ return; // Diagnostics has already been issued.
+
+ if (TREE_CODE (type) != TYPE_DECL)
+ {
+ error (loc) << "name '" << type_name << "' in db pragma "
+ << p << " does not refer to a type" << endl;
+ return;
+ }
+
+ type = TREE_TYPE (type);
+ }
+ else
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ // See if we have before/after specifiers.
+ //
+ if (tt == CPP_NAME && tl == "before")
+ {
+ // before[(<member>)]
+ //
+ // Before without the member name means first.
+ //
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "')' member name expected in db pragma before"
+ << endl;
+ }
+
+ string dn;
+ cxx_tokens ts (1, cxx_token (l.location (), CPP_NAME, tl));
+ declaration d (resolve_data_member (scope, ts, dn, "before"));
+
+ if (!d)
+ return; // Diagnostics has already been issued.
+
+ if (d.virt)
+ {
+ ord = d.decl.virt->ord;
+ ord_bias = d.decl.virt->ord_bias - 1;
+ }
+ else
+ {
+ ord = real_source_location (d.decl.real);
+ ord_bias = -1;
+ }
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma before"
+ << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else
+ ord = 0;
+ }
+
+ if (tt == CPP_NAME && tl == "after")
+ {
+ // after[(<member>)]
+ //
+ // Before without the member name means last.
+ //
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "')' member name expected in db pragma after"
+ << endl;
+ }
+
+ string dn;
+ cxx_tokens ts (1, cxx_token (l.location (), CPP_NAME, tl));
+ declaration d (resolve_data_member (scope, ts, dn, "after"));
+
+ if (!d)
+ return; // Diagnostics has already been issued.
+
+ if (d.virt)
+ {
+ ord = d.decl.virt->ord;
+ ord_bias = d.decl.virt->ord_bias + 1;
+ }
+ else
+ {
+ ord = real_source_location (d.decl.real);
+ ord_bias = 1;
+ }
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma after"
+ << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else
+ ord = ~location_t (0);
+ }
+ }
+
+ pair<virt_declaration_set::const_iterator, bool> r (
+ virt_declarations_[scope].insert (
+ virt_declaration (loc, ord, ord_bias, name, FIELD_DECL, type)));
+
+ if (!r.second)
+ {
+ error (loc) << "virtual data member declaration '" << name
+ << "' conflicts with a previous declaration" << endl;
+
+ info (r.first->loc) << "'" << name << "' was previously "
+ << "declared here" << endl;
+ return;
+ }
+
+ decl_name += name;
+ decl = declaration (*r.first);
+
+ // Mark it as virtual using the standard pragma machinery.
+ //
+ add_pragma (
+ pragma ("virtual", "virtual", true, loc, &check_spec_decl_type, 0),
+ decl,
+ false);
+ }
+ else
+ {
+ // Not a virtual member declaration.
+ //
+ decl = resolve_data_member (scope, name_tokens, decl_name, p);
+
+ if (!decl)
+ return; // Diagnostics has already been issued.
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (!check_qual_decl_type (decl, decl_name, p, loc))
+ return;
+ }
+ }
+ }
+ //
+ // The member qualifier can be omitted so we need to also handle all
+ // the member pragmas here.
+ //
+ else if (p == "id" ||
+ p == "auto" ||
+ p == "unique" ||
+ p == "get" ||
+ p == "set" ||
+ p == "access" ||
+ p == "column" ||
+ p == "value_column" ||
+ p == "index_column" ||
+ p == "key_column" ||
+ p == "id_column" ||
+ p == "options" ||
+ p == "value_options" ||
+ p == "index_options" ||
+ p == "key_options" ||
+ p == "id_options" ||
+ p == "type" ||
+ p == "id_type" ||
+ p == "value_type" ||
+ p == "index_type" ||
+ p == "key_type" ||
+ p == "table" ||
+ p == "null" ||
+ p == "not_null" ||
+ p == "key_null" ||
+ p == "key_not_null" ||
+ p == "value_null" ||
+ p == "value_not_null" ||
+ p == "default" ||
+ p == "section" ||
+ p == "load" ||
+ p == "update" ||
+ p == "inverse" ||
+ p == "on_delete" ||
+ p == "points_to" ||
+ p == "unordered" ||
+ p == "readonly" ||
+ p == "transient" ||
+ p == "added" ||
+ p == "deleted" ||
+ p == "version" ||
+ p == "virtual")
+ {
+ handle_pragma (l, db, p, "member", val, declaration (), "", false);
+ return;
+ }
+ else
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return;
+ }
+
+ // Record this pragma. Delay this until after we process the
+ // specifiers for value (see comment below for the reason).
+ //
+ if (adder == 0)
+ val = orig_decl;
+
+ pragma prag (p,
+ name, // For now no need to translate '_' to '-'.
+ val,
+ loc,
+ &check_qual_decl_type,
+ adder != 0 ? adder : &add_qual_entry);
+
+ tree scope;
+ if (!decl)
+ {
+ scope = current_scope ();
+
+ if (!ns && !CLASS_TYPE_P (scope))
+ scope = global_namespace;
+ }
+
+ any* pval;
+ if (p != "value")
+ {
+ if (decl)
+ pval = &decl_pragmas_[decl].insert (prag).value;
+ else
+ {
+ if (!ns)
+ {
+ pragma_list& pl (loc_pragmas_[scope]);
+ pl.push_back (prag);
+ pval = &pl.back ().value;
+ }
+ else
+ {
+ ns_loc_pragmas_.push_back (ns_loc_pragma (scope, prag));
+ pval = &ns_loc_pragmas_.back ().pragma.value;
+ }
+ }
+ }
+ else
+ pval = &val;
+
+ // See if there are any saved tokens to replay.
+ //
+ if (!saved_tokens.empty ())
+ {
+ cxx_tokens_lexer l;
+ l.start (saved_tokens);
+
+ string tl;
+ cpp_ttype tt (l.next (tl));
+
+ if (tt == CPP_NAME || tt == CPP_KEYWORD)
+ {
+ handle_pragma (l, "", tl, p, *pval, decl, decl_name, ns);
+
+ if (errorcount != 0) // Avoid parsing the rest if there was an error.
+ return;
+ }
+ else if (tt != CPP_EOF)
+ {
+ error (l) << "unexpected text after " << p << " in db pragma" << endl;
+ return;
+ }
+ }
+
+ size_t count (0);
+ if (tt == CPP_NAME || tt == CPP_KEYWORD)
+ {
+ if (decl)
+ count = decl_pragmas_[decl].size ();
+ else
+ count = loc_pragmas_[scope].size ();
+
+ handle_pragma (l, "", tl, p, *pval, decl, decl_name, ns);
+ }
+ else if (tt != CPP_EOF)
+ {
+ error (l) << "unexpected text after " << p << " in db pragma" << endl;
+ return;
+ }
+
+ // Record the value pragma. Here things are complicated by the fact
+ // that we use the value pragma by itself to signify a composite value
+ // type declaration. Consider this example:
+ //
+ // #pragma db value pgsql:type("POINT")
+ // class point {...};
+ //
+ // Should this class be considered composite value type in other
+ // databases (because that's what would happen by default)? Probably
+ // not. So to overcome this we are going to detect and ignore cases
+ // where (a) some specifiers followed the value qualifier but (b)
+ // none of them are for the database that we are compiling for.
+ //
+ if (p == "value")
+ {
+ if (decl)
+ {
+ pragma_set& ps (decl_pragmas_[decl]);
+
+ if (tt == CPP_EOF || ps.size () > count)
+ ps.insert (prag);
+ }
+ else
+ {
+ pragma_list& pl (loc_pragmas_[scope]);
+
+ if (tt == CPP_EOF || pl.size () > count)
+ pl.push_back (prag);
+ }
+ }
+}
+
+/*
+extern "C" void
+handle_pragma_db_mysql (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "mysql");
+}
+
+extern "C" void
+handle_pragma_db_sqlite (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "sqlite");
+}
+
+extern "C" void
+handle_pragma_db_pgsql (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "pgsql");
+}
+
+extern "C" void
+handle_pragma_db_oracle (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "oracle");
+}
+
+extern "C" void
+handle_pragma_db_mssql (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "mssql");
+}
+
+extern "C" void
+handle_pragma_db_model (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "model");
+}
+
+extern "C" void
+handle_pragma_db_map (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "map");
+}
+
+extern "C" void
+handle_pragma_db_namespace (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "namespace");
+}
+
+extern "C" void
+handle_pragma_db_object (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "object");
+}
+
+extern "C" void
+handle_pragma_db_view (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "view");
+}
+
+extern "C" void
+handle_pragma_db_value (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "value");
+}
+
+extern "C" void
+handle_pragma_db_member (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "member");
+}
+
+extern "C" void
+handle_pragma_db_id (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "id");
+}
+
+extern "C" void
+handle_pragma_db_auto (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "auto");
+}
+
+extern "C" void
+handle_pragma_db_column (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "column");
+}
+
+extern "C" void
+handle_pragma_db_vcolumn (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "value_column");
+}
+
+extern "C" void
+handle_pragma_db_icolumn (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "index_column");
+}
+
+extern "C" void
+handle_pragma_db_kcolumn (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "key_column");
+}
+
+extern "C" void
+handle_pragma_db_idcolumn (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "id_column");
+}
+
+extern "C" void
+handle_pragma_db_options (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "options");
+}
+
+extern "C" void
+handle_pragma_db_voptions (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "value_options");
+}
+
+extern "C" void
+handle_pragma_db_ioptions (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "index_options");
+}
+
+extern "C" void
+handle_pragma_db_koptions (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "key_options");
+}
+
+extern "C" void
+handle_pragma_db_idoptions (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "id_options");
+}
+
+extern "C" void
+handle_pragma_db_type (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "type");
+}
+
+extern "C" void
+handle_pragma_db_id_type (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "id_type");
+}
+
+extern "C" void
+handle_pragma_db_vtype (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "value_type");
+}
+
+extern "C" void
+handle_pragma_db_itype (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "index_type");
+}
+
+extern "C" void
+handle_pragma_db_ktype (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "key_type");
+}
+
+extern "C" void
+handle_pragma_db_table (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "table");
+}
+
+extern "C" void
+handle_pragma_db_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "null");
+}
+
+extern "C" void
+handle_pragma_db_not_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "not_null");
+}
+
+extern "C" void
+handle_pragma_db_value_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "value_null");
+}
+
+extern "C" void
+handle_pragma_db_value_not_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "value_not_null");
+}
+
+extern "C" void
+handle_pragma_db_key_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "key_null");
+}
+
+extern "C" void
+handle_pragma_db_key_not_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "key_not_null");
+}
+
+extern "C" void
+handle_pragma_db_default (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "default");
+}
+
+extern "C" void
+handle_pragma_db_section (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "section");
+}
+
+extern "C" void
+handle_pragma_db_load (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "load");
+}
+
+extern "C" void
+handle_pragma_db_update (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "update");
+}
+
+extern "C" void
+handle_pragma_db_inverse (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "inverse");
+}
+
+extern "C" void
+handle_pragma_db_on_delete (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "on_delete");
+}
+
+extern "C" void
+handle_pragma_db_points_to (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "points_to");
+}
+
+extern "C" void
+handle_pragma_db_unordered (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "unordered");
+}
+
+extern "C" void
+handle_pragma_db_readonly (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "readonly");
+}
+
+extern "C" void
+handle_pragma_db_transient (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "transient");
+}
+
+extern "C" void
+handle_pragma_db_added (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "added");
+}
+
+extern "C" void
+handle_pragma_db_deleted (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "deleted");
+}
+
+extern "C" void
+handle_pragma_db_version (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "version");
+}
+
+extern "C" void
+handle_pragma_db_virtual (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "virtual");
+}
+*/
+
+extern "C" void
+handle_pragma_db (cpp_reader*)
+{
+ cxx_pragma_lexer l;
+ l.start ();
+
+ string tl;
+ cpp_ttype tt (l.next (tl));
+
+ if (tt != CPP_NAME && tt != CPP_KEYWORD)
+ {
+ error (l) << "expected specifier after db pragma" << endl;
+ return;
+ }
+
+ handle_pragma_qualifier (l, tl);
+}
+
+extern "C" void
+register_odb_pragmas (void*, void*)
+{
+ // GCC has a limited number of pragma slots and we have exhausted them.
+ // A workaround is to make 'db' a pragma rather than a namespace. This
+ // way we only have one pragma but the drawback of this approach is the
+ // fact that the specifier or qualifier name will now be macro-expanded
+ // (though this happens anyway if we have multiple specifiers in a single
+ // pragma). Once the GCC folks fix this, we can go back to the namespace
+ // approach.
+ //
+ c_register_pragma_with_expansion (0, "db", handle_pragma_db);
+
+ /*
+ c_register_pragma_with_expansion ("db", "mysql", handle_pragma_db_mysql);
+ c_register_pragma_with_expansion ("db", "sqlite", handle_pragma_db_sqlite);
+ c_register_pragma_with_expansion ("db", "pgsql", handle_pragma_db_pgsql);
+ c_register_pragma_with_expansion ("db", "oracle", handle_pragma_db_oracle);
+ c_register_pragma_with_expansion ("db", "mssql", handle_pragma_db_mssql);
+ c_register_pragma_with_expansion ("db", "model", handle_pragma_db_model);
+ c_register_pragma_with_expansion ("db", "map", handle_pragma_db_map);
+ c_register_pragma_with_expansion ("db", "namespace", handle_pragma_db_namespace);
+ c_register_pragma_with_expansion ("db", "object", handle_pragma_db_object);
+ c_register_pragma_with_expansion ("db", "view", handle_pragma_db_view);
+ c_register_pragma_with_expansion ("db", "value", handle_pragma_db_value);
+ c_register_pragma_with_expansion ("db", "member", handle_pragma_db_member);
+ c_register_pragma_with_expansion ("db", "id", handle_pragma_db_id);
+ c_register_pragma_with_expansion ("db", "auto", handle_pragma_db_auto);
+ c_register_pragma_with_expansion ("db", "column", handle_pragma_db_column);
+ c_register_pragma_with_expansion ("db", "value_column", handle_pragma_db_vcolumn);
+ c_register_pragma_with_expansion ("db", "index_column", handle_pragma_db_icolumn);
+ c_register_pragma_with_expansion ("db", "key_column", handle_pragma_db_kcolumn);
+ c_register_pragma_with_expansion ("db", "id_column", handle_pragma_db_idcolumn);
+ c_register_pragma_with_expansion ("db", "options", handle_pragma_db_options);
+ c_register_pragma_with_expansion ("db", "value_options", handle_pragma_db_voptions);
+ c_register_pragma_with_expansion ("db", "index_options", handle_pragma_db_ioptions);
+ c_register_pragma_with_expansion ("db", "key_options", handle_pragma_db_koptions);
+ c_register_pragma_with_expansion ("db", "id_options", handle_pragma_db_idoptions);
+ c_register_pragma_with_expansion ("db", "type", handle_pragma_db_type);
+ c_register_pragma_with_expansion ("db", "id_type", handle_pragma_db_id_type);
+ c_register_pragma_with_expansion ("db", "value_type", handle_pragma_db_vtype);
+ c_register_pragma_with_expansion ("db", "index_type", handle_pragma_db_itype);
+ c_register_pragma_with_expansion ("db", "key_type", handle_pragma_db_ktype);
+ c_register_pragma_with_expansion ("db", "table", handle_pragma_db_table);
+ c_register_pragma_with_expansion ("db", "null", handle_pragma_db_null);
+ c_register_pragma_with_expansion ("db", "not_null", handle_pragma_db_not_null);
+ c_register_pragma_with_expansion ("db", "key_null", handle_pragma_db_key_null);
+ c_register_pragma_with_expansion ("db", "key_not_null", handle_pragma_db_key_not_null);
+ c_register_pragma_with_expansion ("db", "value_null", handle_pragma_db_value_null);
+ c_register_pragma_with_expansion ("db", "value_not_null", handle_pragma_db_value_not_null);
+ c_register_pragma_with_expansion ("db", "default", handle_pragma_db_default);
+ c_register_pragma_with_expansion ("db", "section", handle_pragma_db_section);
+ c_register_pragma_with_expansion ("db", "load", handle_pragma_db_load);
+ c_register_pragma_with_expansion ("db", "update", handle_pragma_db_update);
+ c_register_pragma_with_expansion ("db", "inverse", handle_pragma_db_inverse);
+ c_register_pragma_with_expansion ("db", "on_delete", handle_pragma_db_on_delete);
+ c_register_pragma_with_expansion ("db", "points_to", handle_pragma_db_points_to);
+ c_register_pragma_with_expansion ("db", "unordered", handle_pragma_db_unordered);
+ c_register_pragma_with_expansion ("db", "readonly", handle_pragma_db_readonly);
+ c_register_pragma_with_expansion ("db", "transient", handle_pragma_db_transient);
+ c_register_pragma_with_expansion ("db", "added", handle_pragma_db_added);
+ c_register_pragma_with_expansion ("db", "deleted", handle_pragma_db_deleted);
+ c_register_pragma_with_expansion ("db", "version", handle_pragma_db_version);
+ c_register_pragma_with_expansion ("db", "virtual", handle_pragma_db_virtual);
+ */
+}
+
+void
+post_process_pragmas ()
+{
+ // Make sure object, view, and composite class template instantiations
+ // are fully instantiated.
+ //
+ for (decl_pragmas::iterator i (decl_pragmas_.begin ()),
+ e (decl_pragmas_.end ()); i != e; ++i)
+ {
+ if (i->first.virt)
+ continue;
+
+ tree type (i->first.decl.real);
+
+ if (!(CLASS_TYPE_P (type) && CLASSTYPE_TEMPLATE_INSTANTIATION (type)))
+ continue;
+
+ // Check whether this is an object, view, or composite value type.
+ //
+ pragma const* p (0);
+ string diag_name;
+
+ for (pragma_set::iterator j (i->second.begin ()), e (i->second.end ());
+ j != e; ++j)
+ {
+ string const& name (j->second.context_name);
+
+ if (name == "object")
+ {
+ p = &j->second;
+ diag_name = "persistent object";
+ break;
+ }
+ else if (name == "view")
+ {
+ p = &j->second;
+ diag_name = "view";
+ break;
+ }
+ else if (name == "value")
+ {
+ p = &j->second;
+ diag_name = "composite value";
+ break;
+ }
+ // We don't want to instantiate simple values since they may be
+ // incomplete.
+ //
+ else if (name == "simple" || name == "container")
+ {
+ p = 0;
+ break;
+ }
+ }
+
+ if (p == 0)
+ continue;
+
+ // Make sure it is instantiated.
+ //
+ tree decl (TYPE_NAME (p->value.value<tree> ()));
+ location_t loc (real_source_location (decl));
+
+ // Reset input location so that we get nice diagnostics in case
+ // of an error.
+ //
+ input_location = loc;
+
+ if (instantiate_class_template (type) == error_mark_node ||
+ errorcount != 0 ||
+ !COMPLETE_TYPE_P (type))
+ {
+ error (loc) << "unable to instantiate " << diag_name << " class template"
+ << endl;
+ throw pragmas_failed ();
+ }
+ }
+}
diff --git a/odb/odb/pragma.hxx b/odb/odb/pragma.hxx
new file mode 100644
index 0000000..0d4d3f1
--- /dev/null
+++ b/odb/odb/pragma.hxx
@@ -0,0 +1,287 @@
+// file : odb/pragma.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_PRAGMA_HXX
+#define ODB_PRAGMA_HXX
+
+#include <odb/gcc.hxx>
+
+#include <map>
+#include <set>
+#include <vector>
+#include <string>
+
+#include <odb/option-types.hxx> // database
+
+#include <libcutl/container/any.hxx>
+#include <libcutl/container/multi-index.hxx>
+#include <libcutl/compiler/context.hxx>
+
+struct virt_declaration
+{
+ virt_declaration (location_t l,
+ location_t o,
+ int ob,
+ std::string const& n,
+ gcc_tree_code_type tc,
+ tree t)
+ : loc (l), ord (o), ord_bias (ob), name (n), tree_code (tc), type (t) {}
+
+ location_t loc;
+ location_t ord; // Ordering location for before/after support.
+ int ord_bias; // Ordering bias for the same locations.
+ std::string name;
+ gcc_tree_code_type tree_code;
+ tree type; // Declaration's type.
+};
+
+// Note that we consider virtual declarations with the same name but
+// different tree codes unequal. If that is too loose, then the
+// inserting code must do additional checks.
+//
+struct virt_declaration_set
+{
+ typedef cutl::container::key<std::string, gcc_tree_code_type> key;
+ typedef std::map<key, virt_declaration> map;
+ typedef cutl::container::map_const_iterator<map> const_iterator;
+
+ std::pair<const_iterator, bool>
+ insert (const virt_declaration& v)
+ {
+ std::pair<map::iterator, bool> r (
+ map_.insert (map::value_type (key (v.name, v.tree_code), v)));
+
+ const_iterator i (r.first);
+
+ if (r.second)
+ r.first->first.assign (i->name, i->tree_code);
+
+ return std::make_pair (i, r.second);
+ }
+
+ const_iterator
+ find (std::string const& name, gcc_tree_code_type tree_code) const
+ {
+ return map_.find (key (name, tree_code));
+ }
+
+ const_iterator begin () const {return map_.begin ();}
+ const_iterator end () const {return map_.end ();}
+
+private:
+ map map_;
+};
+
+// Map of scopes (e.g., class, namespace) to sets of virtual declarations.
+//
+typedef std::map<tree, virt_declaration_set> virt_declarations;
+extern virt_declarations virt_declarations_;
+
+// Real or virtual declaration. If it is real, then it is a pointer to
+// the GCC tree node. Otherwise, it is a pointer to virt_declaration
+// from virt_declarations_ above.
+//
+struct declaration
+{
+ declaration (): virt (false) {decl.real = 0;}
+ declaration (tree d): virt (false) {decl.real = d;}
+ declaration (virt_declaration const& d): virt (true) {decl.virt = &d;}
+
+ bool virt;
+
+ union
+ {
+ tree real;
+ virt_declaration const* virt;
+ } decl;
+
+ gcc_tree_code_type
+ tree_code () const
+ {
+ return (virt ? decl.virt->tree_code : TREE_CODE (decl.real));
+ }
+
+ typedef bool declaration::*bool_convertible;
+ operator bool_convertible () const
+ {
+ return ptr () == 0 ? 0 : &declaration::virt;
+ }
+
+public:
+ bool
+ operator== (declaration const& x) const
+ {
+ return virt == x.virt && ptr () == x.ptr ();
+ }
+
+ bool
+ operator!= (declaration const& x) const
+ {
+ return !(*this == x);
+ }
+
+ bool
+ operator< (declaration const& x) const
+ {
+ return virt < x.virt || (virt == x.virt && ptr () < x.ptr ());
+ }
+
+public:
+ void const*
+ ptr () const
+ {
+ return virt
+ ? static_cast<void const*> (decl.virt)
+ : static_cast<void const*> (decl.real);
+ }
+};
+
+inline bool
+operator== (declaration const& x, tree y)
+{
+ return !x.virt && x.decl.real == y;
+}
+
+inline bool
+operator== (tree x, declaration const& y)
+{
+ return !y.virt && y.decl.real == x;
+}
+
+struct pragma
+{
+ // Check that the pragma is applicable to the declaration. Return true
+ // on success, complain and return false otherwise.
+ //
+ typedef bool (*check_func) (declaration const& decl,
+ std::string const& decl_name,
+ std::string const& prag_name,
+ location_t);
+
+ // Add the pragma value to the context.
+ //
+ typedef void (*add_func) (cutl::compiler::context&,
+ std::string const& key,
+ cutl::container::any const& value,
+ location_t);
+
+ pragma (std::string const& pn,
+ std::string const& cn,
+ cutl::container::any const& v,
+ location_t l,
+ check_func c,
+ add_func a)
+ : pragma_name (pn),
+ context_name (cn),
+ value (v),
+ loc (l),
+ check (c),
+ add (a)
+ {
+ }
+
+ std::string pragma_name; // Actual pragma name for diagnostics.
+ std::string context_name; // Context entry name.
+ cutl::container::any value;
+ location_t loc;
+ check_func check;
+ add_func add;
+};
+
+typedef std::vector<pragma> pragma_list;
+
+// A set of pragmas. Insertion of a pragma with the same name and no
+// custom add function overrides the old value.
+//
+struct pragma_set: std::multimap<std::string, pragma>
+{
+ typedef std::multimap<std::string, pragma> base;
+
+ pragma&
+ insert (pragma const& p)
+ {
+ std::string const& n (p.context_name);
+ std::pair<iterator, iterator> r (equal_range (n));
+
+ iterator i (end ());
+
+ if (p.add == 0)
+ {
+ if (r.first != r.second)
+ {
+ i = r.first;
+ assert (++r.first == r.second);
+
+ i->second = p;
+ }
+ }
+ else if (r.first != r.second)
+ assert ((--r.second)->second.loc <= p.loc);
+
+ if (i == end ())
+ i = base::insert (base::value_type (n, p));
+
+ return i->second;
+ }
+
+ void
+ insert (const_iterator begin, const_iterator end)
+ {
+ for (; begin != end; ++begin)
+ insert (begin->second);
+ }
+
+ // Return the last pragma in the equal range which (by construction) has the
+ // location greater or equal to all the other pragmas in this range.
+ //
+ iterator
+ find (std::string const& n)
+ {
+ return equal_range (n).second;
+ }
+};
+
+
+// Position pragmas inside a class or namespace. The key for the
+// namespace case is the global_namespace node.
+//
+typedef std::map<tree, pragma_list> loc_pragmas;
+extern loc_pragmas loc_pragmas_;
+
+// Position pragmas for namespaces. Because re-opened namespaces do
+// not have any representation in the GCC tree, these are handled in
+// a special way. They are stored as a list of pragmas and their outer
+// namespaces.
+//
+struct ns_loc_pragma
+{
+ typedef ::pragma pragma_type;
+ ns_loc_pragma (tree n, pragma_type const& p): ns (n), pragma (p) {}
+
+ tree ns;
+ pragma_type pragma;
+};
+
+typedef std::vector<ns_loc_pragma> ns_loc_pragmas;
+extern ns_loc_pragmas ns_loc_pragmas_;
+
+// Pragmas associated with specific declarations (real or virtual).
+//
+typedef std::map<declaration, pragma_set> decl_pragmas;
+extern decl_pragmas decl_pragmas_;
+
+// Database we are generating code for as well as multi-database support.
+// Used to ignore database-specific pragmas.
+//
+extern database pragma_db_;
+extern multi_database pragma_multi_;
+
+extern "C" void
+register_odb_pragmas (void*, void*);
+
+struct pragmas_failed {};
+
+void
+post_process_pragmas ();
+
+#endif // ODB_PRAGMA_HXX
diff --git a/odb/odb/pregenerated/odb/options.cxx b/odb/odb/pregenerated/odb/options.cxx
new file mode 100644
index 0000000..da22570
--- /dev/null
+++ b/odb/odb/pregenerated/odb/options.cxx
@@ -0,0 +1,4034 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+#include <odb/option-parsers.hxx>
+//
+// End prologue.
+
+#include <odb/options.hxx>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <utility>
+#include <ostream>
+#include <sstream>
+#include <cstring>
+#include <fstream>
+
+namespace cli
+{
+ // unknown_option
+ //
+ unknown_option::
+ ~unknown_option () throw ()
+ {
+ }
+
+ void unknown_option::
+ print (::std::ostream& os) const
+ {
+ os << "unknown option '" << option ().c_str () << "'";
+ }
+
+ const char* unknown_option::
+ what () const throw ()
+ {
+ return "unknown option";
+ }
+
+ // unknown_argument
+ //
+ unknown_argument::
+ ~unknown_argument () throw ()
+ {
+ }
+
+ void unknown_argument::
+ print (::std::ostream& os) const
+ {
+ os << "unknown argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unknown_argument::
+ what () const throw ()
+ {
+ return "unknown argument";
+ }
+
+ // missing_value
+ //
+ missing_value::
+ ~missing_value () throw ()
+ {
+ }
+
+ void missing_value::
+ print (::std::ostream& os) const
+ {
+ os << "missing value for option '" << option ().c_str () << "'";
+ }
+
+ const char* missing_value::
+ what () const throw ()
+ {
+ return "missing option value";
+ }
+
+ // invalid_value
+ //
+ invalid_value::
+ ~invalid_value () throw ()
+ {
+ }
+
+ void invalid_value::
+ print (::std::ostream& os) const
+ {
+ os << "invalid value '" << value ().c_str () << "' for option '"
+ << option ().c_str () << "'";
+
+ if (!message ().empty ())
+ os << ": " << message ().c_str ();
+ }
+
+ const char* invalid_value::
+ what () const throw ()
+ {
+ return "invalid option value";
+ }
+
+ // eos_reached
+ //
+ void eos_reached::
+ print (::std::ostream& os) const
+ {
+ os << what ();
+ }
+
+ const char* eos_reached::
+ what () const throw ()
+ {
+ return "end of argument stream reached";
+ }
+
+ // file_io_failure
+ //
+ file_io_failure::
+ ~file_io_failure () throw ()
+ {
+ }
+
+ void file_io_failure::
+ print (::std::ostream& os) const
+ {
+ os << "unable to open file '" << file ().c_str () << "' or read failure";
+ }
+
+ const char* file_io_failure::
+ what () const throw ()
+ {
+ return "unable to open file or read failure";
+ }
+
+ // unmatched_quote
+ //
+ unmatched_quote::
+ ~unmatched_quote () throw ()
+ {
+ }
+
+ void unmatched_quote::
+ print (::std::ostream& os) const
+ {
+ os << "unmatched quote in argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unmatched_quote::
+ what () const throw ()
+ {
+ return "unmatched quote";
+ }
+
+ // scanner
+ //
+ scanner::
+ ~scanner ()
+ {
+ }
+
+ // argv_scanner
+ //
+ bool argv_scanner::
+ more ()
+ {
+ return i_ < argc_;
+ }
+
+ const char* argv_scanner::
+ peek ()
+ {
+ if (i_ < argc_)
+ return argv_[i_];
+ else
+ throw eos_reached ();
+ }
+
+ const char* argv_scanner::
+ next ()
+ {
+ if (i_ < argc_)
+ {
+ const char* r (argv_[i_]);
+
+ if (erase_)
+ {
+ for (int i (i_ + 1); i < argc_; ++i)
+ argv_[i - 1] = argv_[i];
+
+ --argc_;
+ argv_[argc_] = 0;
+ }
+ else
+ ++i_;
+
+ ++start_position_;
+ return r;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ void argv_scanner::
+ skip ()
+ {
+ if (i_ < argc_)
+ {
+ ++i_;
+ ++start_position_;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ std::size_t argv_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ // argv_file_scanner
+ //
+ int argv_file_scanner::zero_argc_ = 0;
+ std::string argv_file_scanner::empty_string_;
+
+ bool argv_file_scanner::
+ more ()
+ {
+ if (!args_.empty ())
+ return true;
+
+ while (base::more ())
+ {
+ // See if the next argument is the file option.
+ //
+ const char* a (base::peek ());
+ const option_info* oi = 0;
+ const char* ov = 0;
+
+ if (!skip_)
+ {
+ if ((oi = find (a)) != 0)
+ {
+ base::next ();
+
+ if (!base::more ())
+ throw missing_value (a);
+
+ ov = base::next ();
+ }
+ else if (std::strncmp (a, "-", 1) == 0)
+ {
+ if ((ov = std::strchr (a, '=')) != 0)
+ {
+ std::string o (a, 0, ov - a);
+ if ((oi = find (o.c_str ())) != 0)
+ {
+ base::next ();
+ ++ov;
+ }
+ }
+ }
+ }
+
+ if (oi != 0)
+ {
+ if (oi->search_func != 0)
+ {
+ std::string f (oi->search_func (ov, oi->arg));
+
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ load (ov);
+
+ if (!args_.empty ())
+ return true;
+ }
+ else
+ {
+ if (!skip_)
+ skip_ = (std::strcmp (a, "--") == 0);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ const char* argv_file_scanner::
+ peek ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? base::peek () : args_.front ().value.c_str ();
+ }
+
+ const std::string& argv_file_scanner::
+ peek_file ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? empty_string_ : *args_.front ().file;
+ }
+
+ std::size_t argv_file_scanner::
+ peek_line ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? 0 : args_.front ().line;
+ }
+
+ const char* argv_file_scanner::
+ next ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::next ();
+ else
+ {
+ hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value);
+ args_.pop_front ();
+ ++start_position_;
+ return hold_[i_].c_str ();
+ }
+ }
+
+ void argv_file_scanner::
+ skip ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::skip ();
+ else
+ {
+ args_.pop_front ();
+ ++start_position_;
+ }
+ }
+
+ const argv_file_scanner::option_info* argv_file_scanner::
+ find (const char* a) const
+ {
+ for (std::size_t i (0); i < options_count_; ++i)
+ if (std::strcmp (a, options_[i].option) == 0)
+ return &options_[i];
+
+ return 0;
+ }
+
+ std::size_t argv_file_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ void argv_file_scanner::
+ load (const std::string& file)
+ {
+ using namespace std;
+
+ ifstream is (file.c_str ());
+
+ if (!is.is_open ())
+ throw file_io_failure (file);
+
+ files_.push_back (file);
+
+ arg a;
+ a.file = &*files_.rbegin ();
+
+ for (a.line = 1; !is.eof (); ++a.line)
+ {
+ string line;
+ getline (is, line);
+
+ if (is.fail () && !is.eof ())
+ throw file_io_failure (file);
+
+ string::size_type n (line.size ());
+
+ // Trim the line from leading and trailing whitespaces.
+ //
+ if (n != 0)
+ {
+ const char* f (line.c_str ());
+ const char* l (f + n);
+
+ const char* of (f);
+ while (f < l && (*f == ' ' || *f == '\t' || *f == '\r'))
+ ++f;
+
+ --l;
+
+ const char* ol (l);
+ while (l > f && (*l == ' ' || *l == '\t' || *l == '\r'))
+ --l;
+
+ if (f != of || l != ol)
+ line = f <= l ? string (f, l - f + 1) : string ();
+ }
+
+ // Ignore empty lines, those that start with #.
+ //
+ if (line.empty () || line[0] == '#')
+ continue;
+
+ string::size_type p (string::npos);
+ if (line.compare (0, 1, "-") == 0)
+ {
+ p = line.find (' ');
+
+ string::size_type q (line.find ('='));
+ if (q != string::npos && q < p)
+ p = q;
+ }
+
+ string s1;
+ if (p != string::npos)
+ {
+ s1.assign (line, 0, p);
+
+ // Skip leading whitespaces in the argument.
+ //
+ if (line[p] == '=')
+ ++p;
+ else
+ {
+ n = line.size ();
+ for (++p; p < n; ++p)
+ {
+ char c (line[p]);
+ if (c != ' ' && c != '\t' && c != '\r')
+ break;
+ }
+ }
+ }
+ else if (!skip_)
+ skip_ = (line == "--");
+
+ string s2 (line, p != string::npos ? p : 0);
+
+ // If the string (which is an option value or argument) is
+ // wrapped in quotes, remove them.
+ //
+ n = s2.size ();
+ char cf (s2[0]), cl (s2[n - 1]);
+
+ if (cf == '"' || cf == '\'' || cl == '"' || cl == '\'')
+ {
+ if (n == 1 || cf != cl)
+ throw unmatched_quote (s2);
+
+ s2 = string (s2, 1, n - 2);
+ }
+
+ if (!s1.empty ())
+ {
+ // See if this is another file option.
+ //
+ const option_info* oi;
+ if (!skip_ && (oi = find (s1.c_str ())))
+ {
+ if (s2.empty ())
+ throw missing_value (oi->option);
+
+ if (oi->search_func != 0)
+ {
+ string f (oi->search_func (s2.c_str (), oi->arg));
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ {
+ // If the path of the file being parsed is not simple and the
+ // path of the file that needs to be loaded is relative, then
+ // complete the latter using the former as a base.
+ //
+#ifndef _WIN32
+ string::size_type p (file.find_last_of ('/'));
+ bool c (p != string::npos && s2[0] != '/');
+#else
+ string::size_type p (file.find_last_of ("/\\"));
+ bool c (p != string::npos && s2[1] != ':');
+#endif
+ if (c)
+ s2.insert (0, file, 0, p + 1);
+
+ load (s2);
+ }
+
+ continue;
+ }
+
+ a.value = s1;
+ args_.push_back (a);
+ }
+
+ a.value = s2;
+ args_.push_back (a);
+ }
+ }
+
+ void options::
+ push_back (const option& o)
+ {
+ container_type::size_type n (size ());
+ container_type::push_back (o);
+ map_[o.name ()] = n;
+
+ for (option_names::const_iterator i (o.aliases ().begin ());
+ i != o.aliases ().end (); ++i)
+ map_[*i] = n;
+ }
+
+ template <typename X>
+ struct parser
+ {
+ static void
+ parse (X& x, bool& xs, scanner& s)
+ {
+ using namespace std;
+
+ const char* o (s.next ());
+ if (s.more ())
+ {
+ string v (s.next ());
+ istringstream is (v);
+ if (!(is >> x && is.peek () == istringstream::traits_type::eof ()))
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<bool>
+ {
+ static void
+ parse (bool& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ const char* v (s.next ());
+
+ if (std::strcmp (v, "1") == 0 ||
+ std::strcmp (v, "true") == 0 ||
+ std::strcmp (v, "TRUE") == 0 ||
+ std::strcmp (v, "True") == 0)
+ x = true;
+ else if (std::strcmp (v, "0") == 0 ||
+ std::strcmp (v, "false") == 0 ||
+ std::strcmp (v, "FALSE") == 0 ||
+ std::strcmp (v, "False") == 0)
+ x = false;
+ else
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<std::string>
+ {
+ static void
+ parse (std::string& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ x = s.next ();
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X>
+ struct parser<std::pair<X, std::size_t> >
+ {
+ static void
+ parse (std::pair<X, std::size_t>& x, bool& xs, scanner& s)
+ {
+ x.second = s.position ();
+ parser<X>::parse (x.first, xs, s);
+ }
+ };
+
+ template <typename X>
+ struct parser<std::vector<X> >
+ {
+ static void
+ parse (std::vector<X>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.push_back (x);
+ xs = true;
+ }
+ };
+
+ template <typename X, typename C>
+ struct parser<std::set<X, C> >
+ {
+ static void
+ parse (std::set<X, C>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.insert (x);
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::map<K, V, C> >
+ {
+ static void
+ parse (std::map<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m[k] = v;
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::multimap<K, V, C> >
+ {
+ static void
+ parse (std::multimap<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m.insert (typename std::multimap<K, V, C>::value_type (k, v));
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X, typename T, T X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, s);
+ }
+
+ template <typename X, bool X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ s.next ();
+ x.*M = true;
+ }
+
+ template <typename X, typename T, T X::*M, bool X::*S>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, x.*S, s);
+ }
+}
+
+#include <map>
+
+// options
+//
+
+options::
+options ()
+: build2_metadata_ (),
+ build2_metadata_specified_ (false),
+ help_ (),
+ version_ (),
+ I_ (),
+ I_specified_ (false),
+ D_ (),
+ D_specified_ (false),
+ U_ (),
+ U_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ multi_database_ (::multi_database::disabled),
+ multi_database_specified_ (false),
+ default_database_ (),
+ default_database_specified_ (false),
+ generate_query_ (),
+ generate_prepared_ (),
+ omit_unprepared_ (),
+ generate_session_ (),
+ generate_schema_ (),
+ generate_schema_only_ (),
+ suppress_migration_ (),
+ suppress_schema_version_ (),
+ schema_version_table_ (),
+ schema_version_table_specified_ (false),
+ schema_format_ (),
+ schema_format_specified_ (false),
+ omit_drop_ (),
+ omit_create_ (),
+ schema_name_ (),
+ schema_name_specified_ (false),
+ fkeys_deferrable_mode_ (),
+ fkeys_deferrable_mode_specified_ (false),
+ default_pointer_ ("*"),
+ default_pointer_specified_ (false),
+ session_type_ ("odb::session"),
+ session_type_specified_ (false),
+ profile_ (),
+ profile_specified_ (false),
+ at_once_ (),
+ schema_ (),
+ schema_specified_ (false),
+ export_symbol_ (),
+ export_symbol_specified_ (false),
+ extern_symbol_ (),
+ extern_symbol_specified_ (false),
+ std_ (cxx_version::cxx98),
+ std_specified_ (false),
+ warn_hard_add_ (),
+ warn_hard_delete_ (),
+ warn_hard_ (),
+ output_dir_ (),
+ output_dir_specified_ (false),
+ input_name_ (),
+ input_name_specified_ (false),
+ changelog_ (),
+ changelog_specified_ (false),
+ changelog_in_ (),
+ changelog_in_specified_ (false),
+ changelog_out_ (),
+ changelog_out_specified_ (false),
+ changelog_dir_ (),
+ changelog_dir_specified_ (false),
+ init_changelog_ (),
+ odb_file_suffix_ (),
+ odb_file_suffix_specified_ (false),
+ sql_file_suffix_ (),
+ sql_file_suffix_specified_ (false),
+ schema_file_suffix_ (),
+ schema_file_suffix_specified_ (false),
+ changelog_file_suffix_ (),
+ changelog_file_suffix_specified_ (false),
+ hxx_suffix_ (".hxx"),
+ hxx_suffix_specified_ (false),
+ ixx_suffix_ (".ixx"),
+ ixx_suffix_specified_ (false),
+ cxx_suffix_ (".cxx"),
+ cxx_suffix_specified_ (false),
+ sql_suffix_ (".sql"),
+ sql_suffix_specified_ (false),
+ changelog_suffix_ (".xml"),
+ changelog_suffix_specified_ (false),
+ hxx_prologue_ (),
+ hxx_prologue_specified_ (false),
+ ixx_prologue_ (),
+ ixx_prologue_specified_ (false),
+ cxx_prologue_ (),
+ cxx_prologue_specified_ (false),
+ schema_prologue_ (),
+ schema_prologue_specified_ (false),
+ sql_prologue_ (),
+ sql_prologue_specified_ (false),
+ migration_prologue_ (),
+ migration_prologue_specified_ (false),
+ sql_interlude_ (),
+ sql_interlude_specified_ (false),
+ hxx_epilogue_ (),
+ hxx_epilogue_specified_ (false),
+ ixx_epilogue_ (),
+ ixx_epilogue_specified_ (false),
+ cxx_epilogue_ (),
+ cxx_epilogue_specified_ (false),
+ schema_epilogue_ (),
+ schema_epilogue_specified_ (false),
+ sql_epilogue_ (),
+ sql_epilogue_specified_ (false),
+ migration_epilogue_ (),
+ migration_epilogue_specified_ (false),
+ hxx_prologue_file_ (),
+ hxx_prologue_file_specified_ (false),
+ ixx_prologue_file_ (),
+ ixx_prologue_file_specified_ (false),
+ cxx_prologue_file_ (),
+ cxx_prologue_file_specified_ (false),
+ schema_prologue_file_ (),
+ schema_prologue_file_specified_ (false),
+ sql_prologue_file_ (),
+ sql_prologue_file_specified_ (false),
+ migration_prologue_file_ (),
+ migration_prologue_file_specified_ (false),
+ sql_interlude_file_ (),
+ sql_interlude_file_specified_ (false),
+ hxx_epilogue_file_ (),
+ hxx_epilogue_file_specified_ (false),
+ ixx_epilogue_file_ (),
+ ixx_epilogue_file_specified_ (false),
+ cxx_epilogue_file_ (),
+ cxx_epilogue_file_specified_ (false),
+ schema_epilogue_file_ (),
+ schema_epilogue_file_specified_ (false),
+ sql_epilogue_file_ (),
+ sql_epilogue_file_specified_ (false),
+ migration_epilogue_file_ (),
+ migration_epilogue_file_specified_ (false),
+ odb_prologue_ (),
+ odb_prologue_specified_ (false),
+ odb_prologue_file_ (),
+ odb_prologue_file_specified_ (false),
+ odb_epilogue_ (),
+ odb_epilogue_specified_ (false),
+ odb_epilogue_file_ (),
+ odb_epilogue_file_specified_ (false),
+ table_prefix_ (),
+ table_prefix_specified_ (false),
+ index_suffix_ (),
+ index_suffix_specified_ (false),
+ fkey_suffix_ (),
+ fkey_suffix_specified_ (false),
+ sequence_suffix_ (),
+ sequence_suffix_specified_ (false),
+ sql_name_case_ (),
+ sql_name_case_specified_ (false),
+ table_regex_ (),
+ table_regex_specified_ (false),
+ column_regex_ (),
+ column_regex_specified_ (false),
+ index_regex_ (),
+ index_regex_specified_ (false),
+ fkey_regex_ (),
+ fkey_regex_specified_ (false),
+ sequence_regex_ (),
+ sequence_regex_specified_ (false),
+ statement_regex_ (),
+ statement_regex_specified_ (false),
+ sql_name_regex_ (),
+ sql_name_regex_specified_ (false),
+ sql_name_regex_trace_ (),
+ accessor_regex_ (),
+ accessor_regex_specified_ (false),
+ accessor_regex_trace_ (),
+ modifier_regex_ (),
+ modifier_regex_specified_ (false),
+ modifier_regex_trace_ (),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ include_prefix_specified_ (false),
+ include_regex_ (),
+ include_regex_specified_ (false),
+ include_regex_trace_ (),
+ guard_prefix_ (),
+ guard_prefix_specified_ (false),
+ show_sloc_ (),
+ sloc_limit_ (),
+ sloc_limit_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false),
+ x_ (),
+ x_specified_ (false),
+ v_ (),
+ trace_ (),
+ mysql_engine_ ("InnoDB"),
+ mysql_engine_specified_ (false),
+ sqlite_override_null_ (),
+ sqlite_lax_auto_id_ (),
+ pgsql_server_version_ (7, 4),
+ pgsql_server_version_specified_ (false),
+ oracle_client_version_ (10, 1),
+ oracle_client_version_specified_ (false),
+ oracle_warn_truncation_ (),
+ mssql_server_version_ (10, 0),
+ mssql_server_version_specified_ (false),
+ mssql_short_limit_ (1024),
+ mssql_short_limit_specified_ (false)
+{
+}
+
+options::
+options (int& argc,
+ char** argv,
+ bool erase,
+ ::cli::unknown_mode opt,
+ ::cli::unknown_mode arg)
+: build2_metadata_ (),
+ build2_metadata_specified_ (false),
+ help_ (),
+ version_ (),
+ I_ (),
+ I_specified_ (false),
+ D_ (),
+ D_specified_ (false),
+ U_ (),
+ U_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ multi_database_ (::multi_database::disabled),
+ multi_database_specified_ (false),
+ default_database_ (),
+ default_database_specified_ (false),
+ generate_query_ (),
+ generate_prepared_ (),
+ omit_unprepared_ (),
+ generate_session_ (),
+ generate_schema_ (),
+ generate_schema_only_ (),
+ suppress_migration_ (),
+ suppress_schema_version_ (),
+ schema_version_table_ (),
+ schema_version_table_specified_ (false),
+ schema_format_ (),
+ schema_format_specified_ (false),
+ omit_drop_ (),
+ omit_create_ (),
+ schema_name_ (),
+ schema_name_specified_ (false),
+ fkeys_deferrable_mode_ (),
+ fkeys_deferrable_mode_specified_ (false),
+ default_pointer_ ("*"),
+ default_pointer_specified_ (false),
+ session_type_ ("odb::session"),
+ session_type_specified_ (false),
+ profile_ (),
+ profile_specified_ (false),
+ at_once_ (),
+ schema_ (),
+ schema_specified_ (false),
+ export_symbol_ (),
+ export_symbol_specified_ (false),
+ extern_symbol_ (),
+ extern_symbol_specified_ (false),
+ std_ (cxx_version::cxx98),
+ std_specified_ (false),
+ warn_hard_add_ (),
+ warn_hard_delete_ (),
+ warn_hard_ (),
+ output_dir_ (),
+ output_dir_specified_ (false),
+ input_name_ (),
+ input_name_specified_ (false),
+ changelog_ (),
+ changelog_specified_ (false),
+ changelog_in_ (),
+ changelog_in_specified_ (false),
+ changelog_out_ (),
+ changelog_out_specified_ (false),
+ changelog_dir_ (),
+ changelog_dir_specified_ (false),
+ init_changelog_ (),
+ odb_file_suffix_ (),
+ odb_file_suffix_specified_ (false),
+ sql_file_suffix_ (),
+ sql_file_suffix_specified_ (false),
+ schema_file_suffix_ (),
+ schema_file_suffix_specified_ (false),
+ changelog_file_suffix_ (),
+ changelog_file_suffix_specified_ (false),
+ hxx_suffix_ (".hxx"),
+ hxx_suffix_specified_ (false),
+ ixx_suffix_ (".ixx"),
+ ixx_suffix_specified_ (false),
+ cxx_suffix_ (".cxx"),
+ cxx_suffix_specified_ (false),
+ sql_suffix_ (".sql"),
+ sql_suffix_specified_ (false),
+ changelog_suffix_ (".xml"),
+ changelog_suffix_specified_ (false),
+ hxx_prologue_ (),
+ hxx_prologue_specified_ (false),
+ ixx_prologue_ (),
+ ixx_prologue_specified_ (false),
+ cxx_prologue_ (),
+ cxx_prologue_specified_ (false),
+ schema_prologue_ (),
+ schema_prologue_specified_ (false),
+ sql_prologue_ (),
+ sql_prologue_specified_ (false),
+ migration_prologue_ (),
+ migration_prologue_specified_ (false),
+ sql_interlude_ (),
+ sql_interlude_specified_ (false),
+ hxx_epilogue_ (),
+ hxx_epilogue_specified_ (false),
+ ixx_epilogue_ (),
+ ixx_epilogue_specified_ (false),
+ cxx_epilogue_ (),
+ cxx_epilogue_specified_ (false),
+ schema_epilogue_ (),
+ schema_epilogue_specified_ (false),
+ sql_epilogue_ (),
+ sql_epilogue_specified_ (false),
+ migration_epilogue_ (),
+ migration_epilogue_specified_ (false),
+ hxx_prologue_file_ (),
+ hxx_prologue_file_specified_ (false),
+ ixx_prologue_file_ (),
+ ixx_prologue_file_specified_ (false),
+ cxx_prologue_file_ (),
+ cxx_prologue_file_specified_ (false),
+ schema_prologue_file_ (),
+ schema_prologue_file_specified_ (false),
+ sql_prologue_file_ (),
+ sql_prologue_file_specified_ (false),
+ migration_prologue_file_ (),
+ migration_prologue_file_specified_ (false),
+ sql_interlude_file_ (),
+ sql_interlude_file_specified_ (false),
+ hxx_epilogue_file_ (),
+ hxx_epilogue_file_specified_ (false),
+ ixx_epilogue_file_ (),
+ ixx_epilogue_file_specified_ (false),
+ cxx_epilogue_file_ (),
+ cxx_epilogue_file_specified_ (false),
+ schema_epilogue_file_ (),
+ schema_epilogue_file_specified_ (false),
+ sql_epilogue_file_ (),
+ sql_epilogue_file_specified_ (false),
+ migration_epilogue_file_ (),
+ migration_epilogue_file_specified_ (false),
+ odb_prologue_ (),
+ odb_prologue_specified_ (false),
+ odb_prologue_file_ (),
+ odb_prologue_file_specified_ (false),
+ odb_epilogue_ (),
+ odb_epilogue_specified_ (false),
+ odb_epilogue_file_ (),
+ odb_epilogue_file_specified_ (false),
+ table_prefix_ (),
+ table_prefix_specified_ (false),
+ index_suffix_ (),
+ index_suffix_specified_ (false),
+ fkey_suffix_ (),
+ fkey_suffix_specified_ (false),
+ sequence_suffix_ (),
+ sequence_suffix_specified_ (false),
+ sql_name_case_ (),
+ sql_name_case_specified_ (false),
+ table_regex_ (),
+ table_regex_specified_ (false),
+ column_regex_ (),
+ column_regex_specified_ (false),
+ index_regex_ (),
+ index_regex_specified_ (false),
+ fkey_regex_ (),
+ fkey_regex_specified_ (false),
+ sequence_regex_ (),
+ sequence_regex_specified_ (false),
+ statement_regex_ (),
+ statement_regex_specified_ (false),
+ sql_name_regex_ (),
+ sql_name_regex_specified_ (false),
+ sql_name_regex_trace_ (),
+ accessor_regex_ (),
+ accessor_regex_specified_ (false),
+ accessor_regex_trace_ (),
+ modifier_regex_ (),
+ modifier_regex_specified_ (false),
+ modifier_regex_trace_ (),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ include_prefix_specified_ (false),
+ include_regex_ (),
+ include_regex_specified_ (false),
+ include_regex_trace_ (),
+ guard_prefix_ (),
+ guard_prefix_specified_ (false),
+ show_sloc_ (),
+ sloc_limit_ (),
+ sloc_limit_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false),
+ x_ (),
+ x_specified_ (false),
+ v_ (),
+ trace_ (),
+ mysql_engine_ ("InnoDB"),
+ mysql_engine_specified_ (false),
+ sqlite_override_null_ (),
+ sqlite_lax_auto_id_ (),
+ pgsql_server_version_ (7, 4),
+ pgsql_server_version_specified_ (false),
+ oracle_client_version_ (10, 1),
+ oracle_client_version_specified_ (false),
+ oracle_warn_truncation_ (),
+ mssql_server_version_ (10, 0),
+ mssql_server_version_specified_ (false),
+ mssql_short_limit_ (1024),
+ mssql_short_limit_specified_ (false)
+{
+ ::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+}
+
+options::
+options (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ ::cli::unknown_mode opt,
+ ::cli::unknown_mode arg)
+: build2_metadata_ (),
+ build2_metadata_specified_ (false),
+ help_ (),
+ version_ (),
+ I_ (),
+ I_specified_ (false),
+ D_ (),
+ D_specified_ (false),
+ U_ (),
+ U_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ multi_database_ (::multi_database::disabled),
+ multi_database_specified_ (false),
+ default_database_ (),
+ default_database_specified_ (false),
+ generate_query_ (),
+ generate_prepared_ (),
+ omit_unprepared_ (),
+ generate_session_ (),
+ generate_schema_ (),
+ generate_schema_only_ (),
+ suppress_migration_ (),
+ suppress_schema_version_ (),
+ schema_version_table_ (),
+ schema_version_table_specified_ (false),
+ schema_format_ (),
+ schema_format_specified_ (false),
+ omit_drop_ (),
+ omit_create_ (),
+ schema_name_ (),
+ schema_name_specified_ (false),
+ fkeys_deferrable_mode_ (),
+ fkeys_deferrable_mode_specified_ (false),
+ default_pointer_ ("*"),
+ default_pointer_specified_ (false),
+ session_type_ ("odb::session"),
+ session_type_specified_ (false),
+ profile_ (),
+ profile_specified_ (false),
+ at_once_ (),
+ schema_ (),
+ schema_specified_ (false),
+ export_symbol_ (),
+ export_symbol_specified_ (false),
+ extern_symbol_ (),
+ extern_symbol_specified_ (false),
+ std_ (cxx_version::cxx98),
+ std_specified_ (false),
+ warn_hard_add_ (),
+ warn_hard_delete_ (),
+ warn_hard_ (),
+ output_dir_ (),
+ output_dir_specified_ (false),
+ input_name_ (),
+ input_name_specified_ (false),
+ changelog_ (),
+ changelog_specified_ (false),
+ changelog_in_ (),
+ changelog_in_specified_ (false),
+ changelog_out_ (),
+ changelog_out_specified_ (false),
+ changelog_dir_ (),
+ changelog_dir_specified_ (false),
+ init_changelog_ (),
+ odb_file_suffix_ (),
+ odb_file_suffix_specified_ (false),
+ sql_file_suffix_ (),
+ sql_file_suffix_specified_ (false),
+ schema_file_suffix_ (),
+ schema_file_suffix_specified_ (false),
+ changelog_file_suffix_ (),
+ changelog_file_suffix_specified_ (false),
+ hxx_suffix_ (".hxx"),
+ hxx_suffix_specified_ (false),
+ ixx_suffix_ (".ixx"),
+ ixx_suffix_specified_ (false),
+ cxx_suffix_ (".cxx"),
+ cxx_suffix_specified_ (false),
+ sql_suffix_ (".sql"),
+ sql_suffix_specified_ (false),
+ changelog_suffix_ (".xml"),
+ changelog_suffix_specified_ (false),
+ hxx_prologue_ (),
+ hxx_prologue_specified_ (false),
+ ixx_prologue_ (),
+ ixx_prologue_specified_ (false),
+ cxx_prologue_ (),
+ cxx_prologue_specified_ (false),
+ schema_prologue_ (),
+ schema_prologue_specified_ (false),
+ sql_prologue_ (),
+ sql_prologue_specified_ (false),
+ migration_prologue_ (),
+ migration_prologue_specified_ (false),
+ sql_interlude_ (),
+ sql_interlude_specified_ (false),
+ hxx_epilogue_ (),
+ hxx_epilogue_specified_ (false),
+ ixx_epilogue_ (),
+ ixx_epilogue_specified_ (false),
+ cxx_epilogue_ (),
+ cxx_epilogue_specified_ (false),
+ schema_epilogue_ (),
+ schema_epilogue_specified_ (false),
+ sql_epilogue_ (),
+ sql_epilogue_specified_ (false),
+ migration_epilogue_ (),
+ migration_epilogue_specified_ (false),
+ hxx_prologue_file_ (),
+ hxx_prologue_file_specified_ (false),
+ ixx_prologue_file_ (),
+ ixx_prologue_file_specified_ (false),
+ cxx_prologue_file_ (),
+ cxx_prologue_file_specified_ (false),
+ schema_prologue_file_ (),
+ schema_prologue_file_specified_ (false),
+ sql_prologue_file_ (),
+ sql_prologue_file_specified_ (false),
+ migration_prologue_file_ (),
+ migration_prologue_file_specified_ (false),
+ sql_interlude_file_ (),
+ sql_interlude_file_specified_ (false),
+ hxx_epilogue_file_ (),
+ hxx_epilogue_file_specified_ (false),
+ ixx_epilogue_file_ (),
+ ixx_epilogue_file_specified_ (false),
+ cxx_epilogue_file_ (),
+ cxx_epilogue_file_specified_ (false),
+ schema_epilogue_file_ (),
+ schema_epilogue_file_specified_ (false),
+ sql_epilogue_file_ (),
+ sql_epilogue_file_specified_ (false),
+ migration_epilogue_file_ (),
+ migration_epilogue_file_specified_ (false),
+ odb_prologue_ (),
+ odb_prologue_specified_ (false),
+ odb_prologue_file_ (),
+ odb_prologue_file_specified_ (false),
+ odb_epilogue_ (),
+ odb_epilogue_specified_ (false),
+ odb_epilogue_file_ (),
+ odb_epilogue_file_specified_ (false),
+ table_prefix_ (),
+ table_prefix_specified_ (false),
+ index_suffix_ (),
+ index_suffix_specified_ (false),
+ fkey_suffix_ (),
+ fkey_suffix_specified_ (false),
+ sequence_suffix_ (),
+ sequence_suffix_specified_ (false),
+ sql_name_case_ (),
+ sql_name_case_specified_ (false),
+ table_regex_ (),
+ table_regex_specified_ (false),
+ column_regex_ (),
+ column_regex_specified_ (false),
+ index_regex_ (),
+ index_regex_specified_ (false),
+ fkey_regex_ (),
+ fkey_regex_specified_ (false),
+ sequence_regex_ (),
+ sequence_regex_specified_ (false),
+ statement_regex_ (),
+ statement_regex_specified_ (false),
+ sql_name_regex_ (),
+ sql_name_regex_specified_ (false),
+ sql_name_regex_trace_ (),
+ accessor_regex_ (),
+ accessor_regex_specified_ (false),
+ accessor_regex_trace_ (),
+ modifier_regex_ (),
+ modifier_regex_specified_ (false),
+ modifier_regex_trace_ (),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ include_prefix_specified_ (false),
+ include_regex_ (),
+ include_regex_specified_ (false),
+ include_regex_trace_ (),
+ guard_prefix_ (),
+ guard_prefix_specified_ (false),
+ show_sloc_ (),
+ sloc_limit_ (),
+ sloc_limit_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false),
+ x_ (),
+ x_specified_ (false),
+ v_ (),
+ trace_ (),
+ mysql_engine_ ("InnoDB"),
+ mysql_engine_specified_ (false),
+ sqlite_override_null_ (),
+ sqlite_lax_auto_id_ (),
+ pgsql_server_version_ (7, 4),
+ pgsql_server_version_specified_ (false),
+ oracle_client_version_ (10, 1),
+ oracle_client_version_specified_ (false),
+ oracle_warn_truncation_ (),
+ mssql_server_version_ (10, 0),
+ mssql_server_version_specified_ (false),
+ mssql_short_limit_ (1024),
+ mssql_short_limit_specified_ (false)
+{
+ ::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+}
+
+options::
+options (int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::cli::unknown_mode opt,
+ ::cli::unknown_mode arg)
+: build2_metadata_ (),
+ build2_metadata_specified_ (false),
+ help_ (),
+ version_ (),
+ I_ (),
+ I_specified_ (false),
+ D_ (),
+ D_specified_ (false),
+ U_ (),
+ U_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ multi_database_ (::multi_database::disabled),
+ multi_database_specified_ (false),
+ default_database_ (),
+ default_database_specified_ (false),
+ generate_query_ (),
+ generate_prepared_ (),
+ omit_unprepared_ (),
+ generate_session_ (),
+ generate_schema_ (),
+ generate_schema_only_ (),
+ suppress_migration_ (),
+ suppress_schema_version_ (),
+ schema_version_table_ (),
+ schema_version_table_specified_ (false),
+ schema_format_ (),
+ schema_format_specified_ (false),
+ omit_drop_ (),
+ omit_create_ (),
+ schema_name_ (),
+ schema_name_specified_ (false),
+ fkeys_deferrable_mode_ (),
+ fkeys_deferrable_mode_specified_ (false),
+ default_pointer_ ("*"),
+ default_pointer_specified_ (false),
+ session_type_ ("odb::session"),
+ session_type_specified_ (false),
+ profile_ (),
+ profile_specified_ (false),
+ at_once_ (),
+ schema_ (),
+ schema_specified_ (false),
+ export_symbol_ (),
+ export_symbol_specified_ (false),
+ extern_symbol_ (),
+ extern_symbol_specified_ (false),
+ std_ (cxx_version::cxx98),
+ std_specified_ (false),
+ warn_hard_add_ (),
+ warn_hard_delete_ (),
+ warn_hard_ (),
+ output_dir_ (),
+ output_dir_specified_ (false),
+ input_name_ (),
+ input_name_specified_ (false),
+ changelog_ (),
+ changelog_specified_ (false),
+ changelog_in_ (),
+ changelog_in_specified_ (false),
+ changelog_out_ (),
+ changelog_out_specified_ (false),
+ changelog_dir_ (),
+ changelog_dir_specified_ (false),
+ init_changelog_ (),
+ odb_file_suffix_ (),
+ odb_file_suffix_specified_ (false),
+ sql_file_suffix_ (),
+ sql_file_suffix_specified_ (false),
+ schema_file_suffix_ (),
+ schema_file_suffix_specified_ (false),
+ changelog_file_suffix_ (),
+ changelog_file_suffix_specified_ (false),
+ hxx_suffix_ (".hxx"),
+ hxx_suffix_specified_ (false),
+ ixx_suffix_ (".ixx"),
+ ixx_suffix_specified_ (false),
+ cxx_suffix_ (".cxx"),
+ cxx_suffix_specified_ (false),
+ sql_suffix_ (".sql"),
+ sql_suffix_specified_ (false),
+ changelog_suffix_ (".xml"),
+ changelog_suffix_specified_ (false),
+ hxx_prologue_ (),
+ hxx_prologue_specified_ (false),
+ ixx_prologue_ (),
+ ixx_prologue_specified_ (false),
+ cxx_prologue_ (),
+ cxx_prologue_specified_ (false),
+ schema_prologue_ (),
+ schema_prologue_specified_ (false),
+ sql_prologue_ (),
+ sql_prologue_specified_ (false),
+ migration_prologue_ (),
+ migration_prologue_specified_ (false),
+ sql_interlude_ (),
+ sql_interlude_specified_ (false),
+ hxx_epilogue_ (),
+ hxx_epilogue_specified_ (false),
+ ixx_epilogue_ (),
+ ixx_epilogue_specified_ (false),
+ cxx_epilogue_ (),
+ cxx_epilogue_specified_ (false),
+ schema_epilogue_ (),
+ schema_epilogue_specified_ (false),
+ sql_epilogue_ (),
+ sql_epilogue_specified_ (false),
+ migration_epilogue_ (),
+ migration_epilogue_specified_ (false),
+ hxx_prologue_file_ (),
+ hxx_prologue_file_specified_ (false),
+ ixx_prologue_file_ (),
+ ixx_prologue_file_specified_ (false),
+ cxx_prologue_file_ (),
+ cxx_prologue_file_specified_ (false),
+ schema_prologue_file_ (),
+ schema_prologue_file_specified_ (false),
+ sql_prologue_file_ (),
+ sql_prologue_file_specified_ (false),
+ migration_prologue_file_ (),
+ migration_prologue_file_specified_ (false),
+ sql_interlude_file_ (),
+ sql_interlude_file_specified_ (false),
+ hxx_epilogue_file_ (),
+ hxx_epilogue_file_specified_ (false),
+ ixx_epilogue_file_ (),
+ ixx_epilogue_file_specified_ (false),
+ cxx_epilogue_file_ (),
+ cxx_epilogue_file_specified_ (false),
+ schema_epilogue_file_ (),
+ schema_epilogue_file_specified_ (false),
+ sql_epilogue_file_ (),
+ sql_epilogue_file_specified_ (false),
+ migration_epilogue_file_ (),
+ migration_epilogue_file_specified_ (false),
+ odb_prologue_ (),
+ odb_prologue_specified_ (false),
+ odb_prologue_file_ (),
+ odb_prologue_file_specified_ (false),
+ odb_epilogue_ (),
+ odb_epilogue_specified_ (false),
+ odb_epilogue_file_ (),
+ odb_epilogue_file_specified_ (false),
+ table_prefix_ (),
+ table_prefix_specified_ (false),
+ index_suffix_ (),
+ index_suffix_specified_ (false),
+ fkey_suffix_ (),
+ fkey_suffix_specified_ (false),
+ sequence_suffix_ (),
+ sequence_suffix_specified_ (false),
+ sql_name_case_ (),
+ sql_name_case_specified_ (false),
+ table_regex_ (),
+ table_regex_specified_ (false),
+ column_regex_ (),
+ column_regex_specified_ (false),
+ index_regex_ (),
+ index_regex_specified_ (false),
+ fkey_regex_ (),
+ fkey_regex_specified_ (false),
+ sequence_regex_ (),
+ sequence_regex_specified_ (false),
+ statement_regex_ (),
+ statement_regex_specified_ (false),
+ sql_name_regex_ (),
+ sql_name_regex_specified_ (false),
+ sql_name_regex_trace_ (),
+ accessor_regex_ (),
+ accessor_regex_specified_ (false),
+ accessor_regex_trace_ (),
+ modifier_regex_ (),
+ modifier_regex_specified_ (false),
+ modifier_regex_trace_ (),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ include_prefix_specified_ (false),
+ include_regex_ (),
+ include_regex_specified_ (false),
+ include_regex_trace_ (),
+ guard_prefix_ (),
+ guard_prefix_specified_ (false),
+ show_sloc_ (),
+ sloc_limit_ (),
+ sloc_limit_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false),
+ x_ (),
+ x_specified_ (false),
+ v_ (),
+ trace_ (),
+ mysql_engine_ ("InnoDB"),
+ mysql_engine_specified_ (false),
+ sqlite_override_null_ (),
+ sqlite_lax_auto_id_ (),
+ pgsql_server_version_ (7, 4),
+ pgsql_server_version_specified_ (false),
+ oracle_client_version_ (10, 1),
+ oracle_client_version_specified_ (false),
+ oracle_warn_truncation_ (),
+ mssql_server_version_ (10, 0),
+ mssql_server_version_specified_ (false),
+ mssql_short_limit_ (1024),
+ mssql_short_limit_specified_ (false)
+{
+ ::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+}
+
+options::
+options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::cli::unknown_mode opt,
+ ::cli::unknown_mode arg)
+: build2_metadata_ (),
+ build2_metadata_specified_ (false),
+ help_ (),
+ version_ (),
+ I_ (),
+ I_specified_ (false),
+ D_ (),
+ D_specified_ (false),
+ U_ (),
+ U_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ multi_database_ (::multi_database::disabled),
+ multi_database_specified_ (false),
+ default_database_ (),
+ default_database_specified_ (false),
+ generate_query_ (),
+ generate_prepared_ (),
+ omit_unprepared_ (),
+ generate_session_ (),
+ generate_schema_ (),
+ generate_schema_only_ (),
+ suppress_migration_ (),
+ suppress_schema_version_ (),
+ schema_version_table_ (),
+ schema_version_table_specified_ (false),
+ schema_format_ (),
+ schema_format_specified_ (false),
+ omit_drop_ (),
+ omit_create_ (),
+ schema_name_ (),
+ schema_name_specified_ (false),
+ fkeys_deferrable_mode_ (),
+ fkeys_deferrable_mode_specified_ (false),
+ default_pointer_ ("*"),
+ default_pointer_specified_ (false),
+ session_type_ ("odb::session"),
+ session_type_specified_ (false),
+ profile_ (),
+ profile_specified_ (false),
+ at_once_ (),
+ schema_ (),
+ schema_specified_ (false),
+ export_symbol_ (),
+ export_symbol_specified_ (false),
+ extern_symbol_ (),
+ extern_symbol_specified_ (false),
+ std_ (cxx_version::cxx98),
+ std_specified_ (false),
+ warn_hard_add_ (),
+ warn_hard_delete_ (),
+ warn_hard_ (),
+ output_dir_ (),
+ output_dir_specified_ (false),
+ input_name_ (),
+ input_name_specified_ (false),
+ changelog_ (),
+ changelog_specified_ (false),
+ changelog_in_ (),
+ changelog_in_specified_ (false),
+ changelog_out_ (),
+ changelog_out_specified_ (false),
+ changelog_dir_ (),
+ changelog_dir_specified_ (false),
+ init_changelog_ (),
+ odb_file_suffix_ (),
+ odb_file_suffix_specified_ (false),
+ sql_file_suffix_ (),
+ sql_file_suffix_specified_ (false),
+ schema_file_suffix_ (),
+ schema_file_suffix_specified_ (false),
+ changelog_file_suffix_ (),
+ changelog_file_suffix_specified_ (false),
+ hxx_suffix_ (".hxx"),
+ hxx_suffix_specified_ (false),
+ ixx_suffix_ (".ixx"),
+ ixx_suffix_specified_ (false),
+ cxx_suffix_ (".cxx"),
+ cxx_suffix_specified_ (false),
+ sql_suffix_ (".sql"),
+ sql_suffix_specified_ (false),
+ changelog_suffix_ (".xml"),
+ changelog_suffix_specified_ (false),
+ hxx_prologue_ (),
+ hxx_prologue_specified_ (false),
+ ixx_prologue_ (),
+ ixx_prologue_specified_ (false),
+ cxx_prologue_ (),
+ cxx_prologue_specified_ (false),
+ schema_prologue_ (),
+ schema_prologue_specified_ (false),
+ sql_prologue_ (),
+ sql_prologue_specified_ (false),
+ migration_prologue_ (),
+ migration_prologue_specified_ (false),
+ sql_interlude_ (),
+ sql_interlude_specified_ (false),
+ hxx_epilogue_ (),
+ hxx_epilogue_specified_ (false),
+ ixx_epilogue_ (),
+ ixx_epilogue_specified_ (false),
+ cxx_epilogue_ (),
+ cxx_epilogue_specified_ (false),
+ schema_epilogue_ (),
+ schema_epilogue_specified_ (false),
+ sql_epilogue_ (),
+ sql_epilogue_specified_ (false),
+ migration_epilogue_ (),
+ migration_epilogue_specified_ (false),
+ hxx_prologue_file_ (),
+ hxx_prologue_file_specified_ (false),
+ ixx_prologue_file_ (),
+ ixx_prologue_file_specified_ (false),
+ cxx_prologue_file_ (),
+ cxx_prologue_file_specified_ (false),
+ schema_prologue_file_ (),
+ schema_prologue_file_specified_ (false),
+ sql_prologue_file_ (),
+ sql_prologue_file_specified_ (false),
+ migration_prologue_file_ (),
+ migration_prologue_file_specified_ (false),
+ sql_interlude_file_ (),
+ sql_interlude_file_specified_ (false),
+ hxx_epilogue_file_ (),
+ hxx_epilogue_file_specified_ (false),
+ ixx_epilogue_file_ (),
+ ixx_epilogue_file_specified_ (false),
+ cxx_epilogue_file_ (),
+ cxx_epilogue_file_specified_ (false),
+ schema_epilogue_file_ (),
+ schema_epilogue_file_specified_ (false),
+ sql_epilogue_file_ (),
+ sql_epilogue_file_specified_ (false),
+ migration_epilogue_file_ (),
+ migration_epilogue_file_specified_ (false),
+ odb_prologue_ (),
+ odb_prologue_specified_ (false),
+ odb_prologue_file_ (),
+ odb_prologue_file_specified_ (false),
+ odb_epilogue_ (),
+ odb_epilogue_specified_ (false),
+ odb_epilogue_file_ (),
+ odb_epilogue_file_specified_ (false),
+ table_prefix_ (),
+ table_prefix_specified_ (false),
+ index_suffix_ (),
+ index_suffix_specified_ (false),
+ fkey_suffix_ (),
+ fkey_suffix_specified_ (false),
+ sequence_suffix_ (),
+ sequence_suffix_specified_ (false),
+ sql_name_case_ (),
+ sql_name_case_specified_ (false),
+ table_regex_ (),
+ table_regex_specified_ (false),
+ column_regex_ (),
+ column_regex_specified_ (false),
+ index_regex_ (),
+ index_regex_specified_ (false),
+ fkey_regex_ (),
+ fkey_regex_specified_ (false),
+ sequence_regex_ (),
+ sequence_regex_specified_ (false),
+ statement_regex_ (),
+ statement_regex_specified_ (false),
+ sql_name_regex_ (),
+ sql_name_regex_specified_ (false),
+ sql_name_regex_trace_ (),
+ accessor_regex_ (),
+ accessor_regex_specified_ (false),
+ accessor_regex_trace_ (),
+ modifier_regex_ (),
+ modifier_regex_specified_ (false),
+ modifier_regex_trace_ (),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ include_prefix_specified_ (false),
+ include_regex_ (),
+ include_regex_specified_ (false),
+ include_regex_trace_ (),
+ guard_prefix_ (),
+ guard_prefix_specified_ (false),
+ show_sloc_ (),
+ sloc_limit_ (),
+ sloc_limit_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false),
+ x_ (),
+ x_specified_ (false),
+ v_ (),
+ trace_ (),
+ mysql_engine_ ("InnoDB"),
+ mysql_engine_specified_ (false),
+ sqlite_override_null_ (),
+ sqlite_lax_auto_id_ (),
+ pgsql_server_version_ (7, 4),
+ pgsql_server_version_specified_ (false),
+ oracle_client_version_ (10, 1),
+ oracle_client_version_specified_ (false),
+ oracle_warn_truncation_ (),
+ mssql_server_version_ (10, 0),
+ mssql_server_version_specified_ (false),
+ mssql_short_limit_ (1024),
+ mssql_short_limit_specified_ (false)
+{
+ ::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+}
+
+options::
+options (::cli::scanner& s,
+ ::cli::unknown_mode opt,
+ ::cli::unknown_mode arg)
+: build2_metadata_ (),
+ build2_metadata_specified_ (false),
+ help_ (),
+ version_ (),
+ I_ (),
+ I_specified_ (false),
+ D_ (),
+ D_specified_ (false),
+ U_ (),
+ U_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ multi_database_ (::multi_database::disabled),
+ multi_database_specified_ (false),
+ default_database_ (),
+ default_database_specified_ (false),
+ generate_query_ (),
+ generate_prepared_ (),
+ omit_unprepared_ (),
+ generate_session_ (),
+ generate_schema_ (),
+ generate_schema_only_ (),
+ suppress_migration_ (),
+ suppress_schema_version_ (),
+ schema_version_table_ (),
+ schema_version_table_specified_ (false),
+ schema_format_ (),
+ schema_format_specified_ (false),
+ omit_drop_ (),
+ omit_create_ (),
+ schema_name_ (),
+ schema_name_specified_ (false),
+ fkeys_deferrable_mode_ (),
+ fkeys_deferrable_mode_specified_ (false),
+ default_pointer_ ("*"),
+ default_pointer_specified_ (false),
+ session_type_ ("odb::session"),
+ session_type_specified_ (false),
+ profile_ (),
+ profile_specified_ (false),
+ at_once_ (),
+ schema_ (),
+ schema_specified_ (false),
+ export_symbol_ (),
+ export_symbol_specified_ (false),
+ extern_symbol_ (),
+ extern_symbol_specified_ (false),
+ std_ (cxx_version::cxx98),
+ std_specified_ (false),
+ warn_hard_add_ (),
+ warn_hard_delete_ (),
+ warn_hard_ (),
+ output_dir_ (),
+ output_dir_specified_ (false),
+ input_name_ (),
+ input_name_specified_ (false),
+ changelog_ (),
+ changelog_specified_ (false),
+ changelog_in_ (),
+ changelog_in_specified_ (false),
+ changelog_out_ (),
+ changelog_out_specified_ (false),
+ changelog_dir_ (),
+ changelog_dir_specified_ (false),
+ init_changelog_ (),
+ odb_file_suffix_ (),
+ odb_file_suffix_specified_ (false),
+ sql_file_suffix_ (),
+ sql_file_suffix_specified_ (false),
+ schema_file_suffix_ (),
+ schema_file_suffix_specified_ (false),
+ changelog_file_suffix_ (),
+ changelog_file_suffix_specified_ (false),
+ hxx_suffix_ (".hxx"),
+ hxx_suffix_specified_ (false),
+ ixx_suffix_ (".ixx"),
+ ixx_suffix_specified_ (false),
+ cxx_suffix_ (".cxx"),
+ cxx_suffix_specified_ (false),
+ sql_suffix_ (".sql"),
+ sql_suffix_specified_ (false),
+ changelog_suffix_ (".xml"),
+ changelog_suffix_specified_ (false),
+ hxx_prologue_ (),
+ hxx_prologue_specified_ (false),
+ ixx_prologue_ (),
+ ixx_prologue_specified_ (false),
+ cxx_prologue_ (),
+ cxx_prologue_specified_ (false),
+ schema_prologue_ (),
+ schema_prologue_specified_ (false),
+ sql_prologue_ (),
+ sql_prologue_specified_ (false),
+ migration_prologue_ (),
+ migration_prologue_specified_ (false),
+ sql_interlude_ (),
+ sql_interlude_specified_ (false),
+ hxx_epilogue_ (),
+ hxx_epilogue_specified_ (false),
+ ixx_epilogue_ (),
+ ixx_epilogue_specified_ (false),
+ cxx_epilogue_ (),
+ cxx_epilogue_specified_ (false),
+ schema_epilogue_ (),
+ schema_epilogue_specified_ (false),
+ sql_epilogue_ (),
+ sql_epilogue_specified_ (false),
+ migration_epilogue_ (),
+ migration_epilogue_specified_ (false),
+ hxx_prologue_file_ (),
+ hxx_prologue_file_specified_ (false),
+ ixx_prologue_file_ (),
+ ixx_prologue_file_specified_ (false),
+ cxx_prologue_file_ (),
+ cxx_prologue_file_specified_ (false),
+ schema_prologue_file_ (),
+ schema_prologue_file_specified_ (false),
+ sql_prologue_file_ (),
+ sql_prologue_file_specified_ (false),
+ migration_prologue_file_ (),
+ migration_prologue_file_specified_ (false),
+ sql_interlude_file_ (),
+ sql_interlude_file_specified_ (false),
+ hxx_epilogue_file_ (),
+ hxx_epilogue_file_specified_ (false),
+ ixx_epilogue_file_ (),
+ ixx_epilogue_file_specified_ (false),
+ cxx_epilogue_file_ (),
+ cxx_epilogue_file_specified_ (false),
+ schema_epilogue_file_ (),
+ schema_epilogue_file_specified_ (false),
+ sql_epilogue_file_ (),
+ sql_epilogue_file_specified_ (false),
+ migration_epilogue_file_ (),
+ migration_epilogue_file_specified_ (false),
+ odb_prologue_ (),
+ odb_prologue_specified_ (false),
+ odb_prologue_file_ (),
+ odb_prologue_file_specified_ (false),
+ odb_epilogue_ (),
+ odb_epilogue_specified_ (false),
+ odb_epilogue_file_ (),
+ odb_epilogue_file_specified_ (false),
+ table_prefix_ (),
+ table_prefix_specified_ (false),
+ index_suffix_ (),
+ index_suffix_specified_ (false),
+ fkey_suffix_ (),
+ fkey_suffix_specified_ (false),
+ sequence_suffix_ (),
+ sequence_suffix_specified_ (false),
+ sql_name_case_ (),
+ sql_name_case_specified_ (false),
+ table_regex_ (),
+ table_regex_specified_ (false),
+ column_regex_ (),
+ column_regex_specified_ (false),
+ index_regex_ (),
+ index_regex_specified_ (false),
+ fkey_regex_ (),
+ fkey_regex_specified_ (false),
+ sequence_regex_ (),
+ sequence_regex_specified_ (false),
+ statement_regex_ (),
+ statement_regex_specified_ (false),
+ sql_name_regex_ (),
+ sql_name_regex_specified_ (false),
+ sql_name_regex_trace_ (),
+ accessor_regex_ (),
+ accessor_regex_specified_ (false),
+ accessor_regex_trace_ (),
+ modifier_regex_ (),
+ modifier_regex_specified_ (false),
+ modifier_regex_trace_ (),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ include_prefix_specified_ (false),
+ include_regex_ (),
+ include_regex_specified_ (false),
+ include_regex_trace_ (),
+ guard_prefix_ (),
+ guard_prefix_specified_ (false),
+ show_sloc_ (),
+ sloc_limit_ (),
+ sloc_limit_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false),
+ x_ (),
+ x_specified_ (false),
+ v_ (),
+ trace_ (),
+ mysql_engine_ ("InnoDB"),
+ mysql_engine_specified_ (false),
+ sqlite_override_null_ (),
+ sqlite_lax_auto_id_ (),
+ pgsql_server_version_ (7, 4),
+ pgsql_server_version_specified_ (false),
+ oracle_client_version_ (10, 1),
+ oracle_client_version_specified_ (false),
+ oracle_warn_truncation_ (),
+ mssql_server_version_ (10, 0),
+ mssql_server_version_specified_ (false),
+ mssql_short_limit_ (1024),
+ mssql_short_limit_specified_ (false)
+{
+ _parse (s, opt, arg);
+}
+
+::cli::usage_para options::
+print_usage (::std::ostream& os, ::cli::usage_para p)
+{
+ CLI_POTENTIALLY_UNUSED (os);
+
+ if (p == ::cli::usage_para::text)
+ os << ::std::endl;
+
+ os << "--help Print usage information and exit." << ::std::endl;
+
+ os << "--version Print version and exit." << ::std::endl;
+
+ os << "-I <dir> Add <dir> to the beginning of the list of" << ::std::endl
+ << " directories to be searched for included header" << ::std::endl
+ << " files." << ::std::endl;
+
+ os << "-D <name>[=<def>] Define macro <name> with definition <def>." << ::std::endl;
+
+ os << "-U <name> Cancel any previous definitions of macro <name>," << ::std::endl
+ << " either built-in or provided with the -D option." << ::std::endl;
+
+ os << "--database|-d <db> Generate code for the <db> database." << ::std::endl;
+
+ os << "--multi-database|-m <type> Enable multi-database support and specify its" << ::std::endl
+ << " type." << ::std::endl;
+
+ os << "--default-database <db> When static multi-database support is used," << ::std::endl
+ << " specify the database that should be made the" << ::std::endl
+ << " default." << ::std::endl;
+
+ os << "--generate-query|-q Generate query support code." << ::std::endl;
+
+ os << "--generate-prepared Generate prepared query execution support code." << ::std::endl;
+
+ os << "--omit-unprepared Omit un-prepared (once-off) query execution" << ::std::endl
+ << " support code." << ::std::endl;
+
+ os << "--generate-session|-e Generate session support code." << ::std::endl;
+
+ os << "--generate-schema|-s Generate the database schema." << ::std::endl;
+
+ os << "--generate-schema-only Generate only the database schema." << ::std::endl;
+
+ os << "--suppress-migration Suppress the generation of database schema" << ::std::endl
+ << " migration statements." << ::std::endl;
+
+ os << "--suppress-schema-version Suppress the generation of schema version table." << ::std::endl;
+
+ os << "--schema-version-table <name> Specify the alternative schema version table name" << ::std::endl
+ << " instead of the default schema_version." << ::std::endl;
+
+ os << "--schema-format <format> Generate the database schema in the specified" << ::std::endl
+ << " format." << ::std::endl;
+
+ os << "--omit-drop Omit DROP statements from the generated database" << ::std::endl
+ << " schema." << ::std::endl;
+
+ os << "--omit-create Omit CREATE statements from the generated" << ::std::endl
+ << " database schema." << ::std::endl;
+
+ os << "--schema-name <name> Use <name> as the database schema name." << ::std::endl;
+
+ os << "--fkeys-deferrable-mode <m> Use constraint checking mode <m> in foreign keys" << ::std::endl
+ << " generated for object relationships." << ::std::endl;
+
+ os << "--default-pointer <ptr> Use <ptr> as the default pointer for persistent" << ::std::endl
+ << " objects and views." << ::std::endl;
+
+ os << "--session-type <type> Use <type> as the alternative session type" << ::std::endl
+ << " instead of the default odb::session." << ::std::endl;
+
+ os << "--profile|-p <name> Specify a profile that should be used during" << ::std::endl
+ << " compilation." << ::std::endl;
+
+ os << "--at-once Generate code for all the input files as well as" << ::std::endl
+ << " for all the files that they include at once." << ::std::endl;
+
+ os << "--schema <schema> Specify a database schema (database namespace)" << ::std::endl
+ << " that should be assigned to the persistent classes" << ::std::endl
+ << " in the file being compiled." << ::std::endl;
+
+ os << "--export-symbol <symbol> Insert <symbol> in places where DLL export/import" << ::std::endl
+ << " control statements" << ::std::endl
+ << " (__declspec(dllexport/dllimport)) are necessary." << ::std::endl;
+
+ os << "--extern-symbol <symbol> If <symbol> is defined, insert it in places where" << ::std::endl
+ << " a template instantiation must be declared extern." << ::std::endl;
+
+ os << "--std <version> Specify the C++ standard that should be used" << ::std::endl
+ << " during compilation." << ::std::endl;
+
+ os << "--warn-hard-add Warn about hard-added data members." << ::std::endl;
+
+ os << "--warn-hard-delete Warn about hard-deleted data members and" << ::std::endl
+ << " persistent classes." << ::std::endl;
+
+ os << "--warn-hard Warn about both hard-added and hard-deleted data" << ::std::endl
+ << " members and persistent classes." << ::std::endl;
+
+ os << "--output-dir|-o <dir> Write the generated files to <dir> instead of the" << ::std::endl
+ << " current directory." << ::std::endl;
+
+ os << "--input-name <name> Use <name> instead of the input file to derive" << ::std::endl
+ << " the names of the generated files." << ::std::endl;
+
+ os << "--changelog <file> Read/write changelog from/to <file> instead of" << ::std::endl
+ << " the default changelog file." << ::std::endl;
+
+ os << "--changelog-in <file> Read changelog from <file> instead of the default" << ::std::endl
+ << " changelog file." << ::std::endl;
+
+ os << "--changelog-out <file> Write changelog to <file> instead of the default" << ::std::endl
+ << " changelog file." << ::std::endl;
+
+ os << "--changelog-dir <dir> Use <dir> instead of the input file directory as" << ::std::endl
+ << " the changelog file directory." << ::std::endl;
+
+ os << "--init-changelog Force re-initialization of the changelog even if" << ::std::endl
+ << " one exists (all the existing change history will" << ::std::endl
+ << " be lost)." << ::std::endl;
+
+ os << "--odb-file-suffix <suffix> Use <suffix> to construct the names of the" << ::std::endl
+ << " generated C++ files." << ::std::endl;
+
+ os << "--sql-file-suffix <suffix> Use <suffix> to construct the name of the" << ::std::endl
+ << " generated schema SQL file." << ::std::endl;
+
+ os << "--schema-file-suffix <suffix> Use <suffix> to construct the name of the" << ::std::endl
+ << " generated schema C++ source file." << ::std::endl;
+
+ os << "--changelog-file-suffix <sfx> Use <sfx> to construct the name of the changelog" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--hxx-suffix <suffix> Use <suffix> instead of the default .hxx to" << ::std::endl
+ << " construct the name of the generated C++ header" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--ixx-suffix <suffix> Use <suffix> instead of the default .ixx to" << ::std::endl
+ << " construct the name of the generated C++ inline" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--cxx-suffix <suffix> Use <suffix> instead of the default .cxx to" << ::std::endl
+ << " construct the name of the generated C++ source" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--sql-suffix <suffix> Use <suffix> instead of the default .sql to" << ::std::endl
+ << " construct the name of the generated database" << ::std::endl
+ << " schema file." << ::std::endl;
+
+ os << "--changelog-suffix <suffix> Use <suffix> instead of the default .xml to" << ::std::endl
+ << " construct the name of the changelog file." << ::std::endl;
+
+ os << "--hxx-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " C++ header file." << ::std::endl;
+
+ os << "--ixx-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " C++ inline file." << ::std::endl;
+
+ os << "--cxx-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " C++ source file." << ::std::endl;
+
+ os << "--schema-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " schema C++ source file." << ::std::endl;
+
+ os << "--sql-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " database schema file." << ::std::endl;
+
+ os << "--migration-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " database migration file." << ::std::endl;
+
+ os << "--sql-interlude <text> Insert <text> after all the DROP and before any" << ::std::endl
+ << " CREATE statements in the generated database" << ::std::endl
+ << " schema file." << ::std::endl;
+
+ os << "--hxx-epilogue <text> Insert <text> at the end of the generated C++" << ::std::endl
+ << " header file." << ::std::endl;
+
+ os << "--ixx-epilogue <text> Insert <text> at the end of the generated C++" << ::std::endl
+ << " inline file." << ::std::endl;
+
+ os << "--cxx-epilogue <text> Insert <text> at the end of the generated C++" << ::std::endl
+ << " source file." << ::std::endl;
+
+ os << "--schema-epilogue <text> Insert <text> at the end of the generated schema" << ::std::endl
+ << " C++ source file." << ::std::endl;
+
+ os << "--sql-epilogue <text> Insert <text> at the end of the generated" << ::std::endl
+ << " database schema file." << ::std::endl;
+
+ os << "--migration-epilogue <text> Insert <text> at the end of the generated" << ::std::endl
+ << " database migration file." << ::std::endl;
+
+ os << "--hxx-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated C++ header file." << ::std::endl;
+
+ os << "--ixx-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated C++ inline file." << ::std::endl;
+
+ os << "--cxx-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated C++ source file." << ::std::endl;
+
+ os << "--schema-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated schema C++ source file." << ::std::endl;
+
+ os << "--sql-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated database schema file." << ::std::endl;
+
+ os << "--migration-prologue-file <f> Insert the content of file <f> at the beginning" << ::std::endl
+ << " of the generated database migration file." << ::std::endl;
+
+ os << "--sql-interlude-file <file> Insert the content of <file> after all the DROP" << ::std::endl
+ << " and before any CREATE statements in the generated" << ::std::endl
+ << " database schema file." << ::std::endl;
+
+ os << "--hxx-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated C++ header file." << ::std::endl;
+
+ os << "--ixx-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated C++ inline file." << ::std::endl;
+
+ os << "--cxx-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated C++ source file." << ::std::endl;
+
+ os << "--schema-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated schema C++ source file." << ::std::endl;
+
+ os << "--sql-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated database schema file." << ::std::endl;
+
+ os << "--migration-epilogue-file <f> Insert the content of file <f> at the end of the" << ::std::endl
+ << " generated database migration file." << ::std::endl;
+
+ os << "--odb-prologue <text> Compile <text> before the input header file." << ::std::endl;
+
+ os << "--odb-prologue-file <file> Compile <file> contents before the input header" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--odb-epilogue <text> Compile <text> after the input header file." << ::std::endl;
+
+ os << "--odb-epilogue-file <file> Compile <file> contents after the input header" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--table-prefix <prefix> Add <prefix> to table names and, for databases" << ::std::endl
+ << " that have global index and/or foreign key names," << ::std::endl
+ << " to those names as well." << ::std::endl;
+
+ os << "--index-suffix <suffix> Use <suffix> instead of the default _i to" << ::std::endl
+ << " construct index names." << ::std::endl;
+
+ os << "--fkey-suffix <suffix> Use <suffix> instead of the default _fk to" << ::std::endl
+ << " construct foreign key names." << ::std::endl;
+
+ os << "--sequence-suffix <suffix> Use <suffix> instead of the default _seq to" << ::std::endl
+ << " construct sequence names." << ::std::endl;
+
+ os << "--sql-name-case <case> Convert all automatically-derived SQL names to" << ::std::endl
+ << " upper or lower case." << ::std::endl;
+
+ os << "--table-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform automatically-derived" << ::std::endl
+ << " table names." << ::std::endl;
+
+ os << "--column-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform automatically-derived" << ::std::endl
+ << " column names." << ::std::endl;
+
+ os << "--index-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform automatically-derived" << ::std::endl
+ << " index names." << ::std::endl;
+
+ os << "--fkey-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform automatically-derived" << ::std::endl
+ << " foreign key names." << ::std::endl;
+
+ os << "--sequence-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform automatically-derived" << ::std::endl
+ << " sequence names." << ::std::endl;
+
+ os << "--statement-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform automatically-derived" << ::std::endl
+ << " prepared statement names." << ::std::endl;
+
+ os << "--sql-name-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform all" << ::std::endl
+ << " automatically-derived SQL names." << ::std::endl;
+
+ os << "--sql-name-regex-trace Trace the process of applying regular expressions" << ::std::endl
+ << " specified with the SQL name --*-regex options." << ::std::endl;
+
+ os << "--accessor-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " used to transform data member names to function" << ::std::endl
+ << " names when searching for a suitable accessor" << ::std::endl
+ << " function." << ::std::endl;
+
+ os << "--accessor-regex-trace Trace the process of applying regular expressions" << ::std::endl
+ << " specified with the --accessor-regex option." << ::std::endl;
+
+ os << "--modifier-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " used to transform data member names to function" << ::std::endl
+ << " names when searching for a suitable modifier" << ::std::endl
+ << " function." << ::std::endl;
+
+ os << "--modifier-regex-trace Trace the process of applying regular expressions" << ::std::endl
+ << " specified with the --modifier-regex option." << ::std::endl;
+
+ os << "--include-with-brackets Use angle brackets (<>) instead of quotes (\"\") in" << ::std::endl
+ << " the generated #include directives." << ::std::endl;
+
+ os << "--include-prefix <prefix> Add <prefix> to the generated #include directive" << ::std::endl
+ << " paths." << ::std::endl;
+
+ os << "--include-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " used to transform generated #include directive" << ::std::endl
+ << " paths." << ::std::endl;
+
+ os << "--include-regex-trace Trace the process of applying regular expressions" << ::std::endl
+ << " specified with the --include-regex option." << ::std::endl;
+
+ os << "--guard-prefix <prefix> Add <prefix> to the generated header inclusion" << ::std::endl
+ << " guards." << ::std::endl;
+
+ os << "--show-sloc Print the number of generated physical source" << ::std::endl
+ << " lines of code (SLOC)." << ::std::endl;
+
+ os << "--sloc-limit <num> Check that the number of generated physical" << ::std::endl
+ << " source lines of code (SLOC) does not exceed" << ::std::endl
+ << " <num>." << ::std::endl;
+
+ os << "--options-file <file> Read additional options from <file>." << ::std::endl;
+
+ os << "-x <option> Pass <option> to the underlying C++ compiler" << ::std::endl
+ << " (g++)." << ::std::endl;
+
+ os << "-v Print the commands executed to run the stages of" << ::std::endl
+ << " compilation." << ::std::endl;
+
+ os << "--trace Trace the compilation process." << ::std::endl;
+
+ os << "--mysql-engine <engine> Use <engine> instead of the default InnoDB in the" << ::std::endl
+ << " generated database schema file." << ::std::endl;
+
+ os << "--sqlite-override-null Make all columns in the generated database schema" << ::std::endl
+ << " allow NULL values." << ::std::endl;
+
+ os << "--sqlite-lax-auto-id Do not force monotonically increasing" << ::std::endl
+ << " automatically-assigned object ids." << ::std::endl;
+
+ os << "--pgsql-server-version <ver> Specify the minimum PostgreSQL server version" << ::std::endl
+ << " with which the generated C++ code and schema will" << ::std::endl
+ << " be used." << ::std::endl;
+
+ os << "--oracle-client-version <ver> Specify the minimum Oracle client library (OCI)" << ::std::endl
+ << " version with which the generated C++ code will be" << ::std::endl
+ << " linked." << ::std::endl;
+
+ os << "--oracle-warn-truncation Warn about SQL names that are longer than 30" << ::std::endl
+ << " characters and are therefore truncated." << ::std::endl;
+
+ os << "--mssql-server-version <ver> Specify the minimum SQL Server server version" << ::std::endl
+ << " with which the generated C++ code and schema will" << ::std::endl
+ << " be used." << ::std::endl;
+
+ os << "--mssql-short-limit <size> Specify the short data size limit." << ::std::endl;
+
+ p = ::cli::usage_para::option;
+
+ return p;
+}
+
+struct _cli_options_desc_type: ::cli::options
+{
+ _cli_options_desc_type ()
+ {
+ ::options::fill (*this);
+ }
+};
+
+static _cli_options_desc_type _cli_options_desc_;
+
+void options::
+fill (::cli::options& os)
+{
+ // --build2-metadata
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--build2-metadata", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --help
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--help", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --version
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--version", a, true, dv);
+ os.push_back (o);
+ }
+
+ // -I
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("-I", a, false, dv);
+ os.push_back (o);
+ }
+
+ // -D
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("-D", a, false, dv);
+ os.push_back (o);
+ }
+
+ // -U
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("-U", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --database
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-d");
+ std::string dv;
+ ::cli::option o ("--database", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --multi-database
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-m");
+ std::string dv;
+ ::cli::option o ("--multi-database", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --default-database
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--default-database", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --generate-query
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-q");
+ std::string dv;
+ ::cli::option o ("--generate-query", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --generate-prepared
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--generate-prepared", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --omit-unprepared
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--omit-unprepared", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --generate-session
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-e");
+ std::string dv;
+ ::cli::option o ("--generate-session", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --generate-schema
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-s");
+ std::string dv;
+ ::cli::option o ("--generate-schema", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --generate-schema-only
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--generate-schema-only", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --suppress-migration
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--suppress-migration", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --suppress-schema-version
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--suppress-schema-version", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --schema-version-table
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-version-table", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --schema-format
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-format", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --omit-drop
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--omit-drop", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --omit-create
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--omit-create", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --schema-name
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-name", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --fkeys-deferrable-mode
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--fkeys-deferrable-mode", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --default-pointer
+ //
+ {
+ ::cli::option_names a;
+ std::string dv ("*");
+ ::cli::option o ("--default-pointer", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --session-type
+ //
+ {
+ ::cli::option_names a;
+ std::string dv ("odb::session");
+ ::cli::option o ("--session-type", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --profile
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-p");
+ std::string dv;
+ ::cli::option o ("--profile", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --at-once
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--at-once", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --schema
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --export-symbol
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--export-symbol", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --extern-symbol
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--extern-symbol", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --std
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--std", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --warn-hard-add
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--warn-hard-add", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --warn-hard-delete
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--warn-hard-delete", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --warn-hard
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--warn-hard", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --output-dir
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-o");
+ std::string dv;
+ ::cli::option o ("--output-dir", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --input-name
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--input-name", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --changelog
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--changelog", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --changelog-in
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--changelog-in", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --changelog-out
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--changelog-out", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --changelog-dir
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--changelog-dir", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --init-changelog
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--init-changelog", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --odb-file-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--odb-file-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-file-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-file-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --schema-file-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-file-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --changelog-file-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--changelog-file-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --hxx-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv (".hxx");
+ ::cli::option o ("--hxx-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --ixx-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv (".ixx");
+ ::cli::option o ("--ixx-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --cxx-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv (".cxx");
+ ::cli::option o ("--cxx-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv (".sql");
+ ::cli::option o ("--sql-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --changelog-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv (".xml");
+ ::cli::option o ("--changelog-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --hxx-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--hxx-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --ixx-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--ixx-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --cxx-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--cxx-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --schema-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --migration-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--migration-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-interlude
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-interlude", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --hxx-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--hxx-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --ixx-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--ixx-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --cxx-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--cxx-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --schema-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --migration-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--migration-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --hxx-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--hxx-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --ixx-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--ixx-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --cxx-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--cxx-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --schema-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --migration-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--migration-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-interlude-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-interlude-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --hxx-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--hxx-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --ixx-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--ixx-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --cxx-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--cxx-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --schema-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --migration-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--migration-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --odb-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--odb-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --odb-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--odb-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --odb-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--odb-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --odb-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--odb-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --table-prefix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--table-prefix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --index-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--index-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --fkey-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--fkey-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sequence-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sequence-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-name-case
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-name-case", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --table-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--table-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --column-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--column-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --index-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--index-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --fkey-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--fkey-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sequence-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sequence-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --statement-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--statement-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-name-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-name-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-name-regex-trace
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-name-regex-trace", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --accessor-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--accessor-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --accessor-regex-trace
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--accessor-regex-trace", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --modifier-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--modifier-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --modifier-regex-trace
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--modifier-regex-trace", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --include-with-brackets
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--include-with-brackets", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --include-prefix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--include-prefix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --include-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--include-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --include-regex-trace
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--include-regex-trace", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --guard-prefix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--guard-prefix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --show-sloc
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--show-sloc", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --sloc-limit
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sloc-limit", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --options-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--options-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // -x
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("-x", a, false, dv);
+ os.push_back (o);
+ }
+
+ // -v
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("-v", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --trace
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--trace", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --mysql-engine
+ //
+ {
+ ::cli::option_names a;
+ std::string dv ("InnoDB");
+ ::cli::option o ("--mysql-engine", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sqlite-override-null
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sqlite-override-null", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --sqlite-lax-auto-id
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sqlite-lax-auto-id", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --pgsql-server-version
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--pgsql-server-version", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --oracle-client-version
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--oracle-client-version", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --oracle-warn-truncation
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--oracle-warn-truncation", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --mssql-server-version
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--mssql-server-version", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --mssql-short-limit
+ //
+ {
+ ::cli::option_names a;
+ std::string dv ("1024");
+ ::cli::option o ("--mssql-short-limit", a, false, dv);
+ os.push_back (o);
+ }
+}
+
+const ::cli::options& options::
+description ()
+{
+ return _cli_options_desc_;
+}
+
+typedef
+std::map<std::string, void (*) (options&, ::cli::scanner&)>
+_cli_options_map;
+
+static _cli_options_map _cli_options_map_;
+
+struct _cli_options_map_init
+{
+ _cli_options_map_init ()
+ {
+ _cli_options_map_["--build2-metadata"] =
+ &::cli::thunk< options, std::uint64_t, &options::build2_metadata_,
+ &options::build2_metadata_specified_ >;
+ _cli_options_map_["--help"] =
+ &::cli::thunk< options, &options::help_ >;
+ _cli_options_map_["--version"] =
+ &::cli::thunk< options, &options::version_ >;
+ _cli_options_map_["-I"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::I_,
+ &options::I_specified_ >;
+ _cli_options_map_["-D"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::D_,
+ &options::D_specified_ >;
+ _cli_options_map_["-U"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::U_,
+ &options::U_specified_ >;
+ _cli_options_map_["--database"] =
+ &::cli::thunk< options, std::vector< ::database>, &options::database_,
+ &options::database_specified_ >;
+ _cli_options_map_["-d"] =
+ &::cli::thunk< options, std::vector< ::database>, &options::database_,
+ &options::database_specified_ >;
+ _cli_options_map_["--multi-database"] =
+ &::cli::thunk< options, ::multi_database, &options::multi_database_,
+ &options::multi_database_specified_ >;
+ _cli_options_map_["-m"] =
+ &::cli::thunk< options, ::multi_database, &options::multi_database_,
+ &options::multi_database_specified_ >;
+ _cli_options_map_["--default-database"] =
+ &::cli::thunk< options, ::database, &options::default_database_,
+ &options::default_database_specified_ >;
+ _cli_options_map_["--generate-query"] =
+ &::cli::thunk< options, &options::generate_query_ >;
+ _cli_options_map_["-q"] =
+ &::cli::thunk< options, &options::generate_query_ >;
+ _cli_options_map_["--generate-prepared"] =
+ &::cli::thunk< options, &options::generate_prepared_ >;
+ _cli_options_map_["--omit-unprepared"] =
+ &::cli::thunk< options, &options::omit_unprepared_ >;
+ _cli_options_map_["--generate-session"] =
+ &::cli::thunk< options, &options::generate_session_ >;
+ _cli_options_map_["-e"] =
+ &::cli::thunk< options, &options::generate_session_ >;
+ _cli_options_map_["--generate-schema"] =
+ &::cli::thunk< options, &options::generate_schema_ >;
+ _cli_options_map_["-s"] =
+ &::cli::thunk< options, &options::generate_schema_ >;
+ _cli_options_map_["--generate-schema-only"] =
+ &::cli::thunk< options, &options::generate_schema_only_ >;
+ _cli_options_map_["--suppress-migration"] =
+ &::cli::thunk< options, &options::suppress_migration_ >;
+ _cli_options_map_["--suppress-schema-version"] =
+ &::cli::thunk< options, &options::suppress_schema_version_ >;
+ _cli_options_map_["--schema-version-table"] =
+ &::cli::thunk< options, database_map<qname>, &options::schema_version_table_,
+ &options::schema_version_table_specified_ >;
+ _cli_options_map_["--schema-format"] =
+ &::cli::thunk< options, database_map<std::set< ::schema_format> >, &options::schema_format_,
+ &options::schema_format_specified_ >;
+ _cli_options_map_["--omit-drop"] =
+ &::cli::thunk< options, &options::omit_drop_ >;
+ _cli_options_map_["--omit-create"] =
+ &::cli::thunk< options, &options::omit_create_ >;
+ _cli_options_map_["--schema-name"] =
+ &::cli::thunk< options, database_map<std::string>, &options::schema_name_,
+ &options::schema_name_specified_ >;
+ _cli_options_map_["--fkeys-deferrable-mode"] =
+ &::cli::thunk< options, database_map<deferrable>, &options::fkeys_deferrable_mode_,
+ &options::fkeys_deferrable_mode_specified_ >;
+ _cli_options_map_["--default-pointer"] =
+ &::cli::thunk< options, std::string, &options::default_pointer_,
+ &options::default_pointer_specified_ >;
+ _cli_options_map_["--session-type"] =
+ &::cli::thunk< options, std::string, &options::session_type_,
+ &options::session_type_specified_ >;
+ _cli_options_map_["--profile"] =
+ &::cli::thunk< options, std::string, &options::profile_,
+ &options::profile_specified_ >;
+ _cli_options_map_["-p"] =
+ &::cli::thunk< options, std::string, &options::profile_,
+ &options::profile_specified_ >;
+ _cli_options_map_["--at-once"] =
+ &::cli::thunk< options, &options::at_once_ >;
+ _cli_options_map_["--schema"] =
+ &::cli::thunk< options, database_map<qname>, &options::schema_,
+ &options::schema_specified_ >;
+ _cli_options_map_["--export-symbol"] =
+ &::cli::thunk< options, database_map<std::string>, &options::export_symbol_,
+ &options::export_symbol_specified_ >;
+ _cli_options_map_["--extern-symbol"] =
+ &::cli::thunk< options, database_map<std::string>, &options::extern_symbol_,
+ &options::extern_symbol_specified_ >;
+ _cli_options_map_["--std"] =
+ &::cli::thunk< options, cxx_version, &options::std_,
+ &options::std_specified_ >;
+ _cli_options_map_["--warn-hard-add"] =
+ &::cli::thunk< options, &options::warn_hard_add_ >;
+ _cli_options_map_["--warn-hard-delete"] =
+ &::cli::thunk< options, &options::warn_hard_delete_ >;
+ _cli_options_map_["--warn-hard"] =
+ &::cli::thunk< options, &options::warn_hard_ >;
+ _cli_options_map_["--output-dir"] =
+ &::cli::thunk< options, std::string, &options::output_dir_,
+ &options::output_dir_specified_ >;
+ _cli_options_map_["-o"] =
+ &::cli::thunk< options, std::string, &options::output_dir_,
+ &options::output_dir_specified_ >;
+ _cli_options_map_["--input-name"] =
+ &::cli::thunk< options, std::string, &options::input_name_,
+ &options::input_name_specified_ >;
+ _cli_options_map_["--changelog"] =
+ &::cli::thunk< options, database_map<std::string>, &options::changelog_,
+ &options::changelog_specified_ >;
+ _cli_options_map_["--changelog-in"] =
+ &::cli::thunk< options, database_map<std::string>, &options::changelog_in_,
+ &options::changelog_in_specified_ >;
+ _cli_options_map_["--changelog-out"] =
+ &::cli::thunk< options, database_map<std::string>, &options::changelog_out_,
+ &options::changelog_out_specified_ >;
+ _cli_options_map_["--changelog-dir"] =
+ &::cli::thunk< options, database_map<std::string>, &options::changelog_dir_,
+ &options::changelog_dir_specified_ >;
+ _cli_options_map_["--init-changelog"] =
+ &::cli::thunk< options, &options::init_changelog_ >;
+ _cli_options_map_["--odb-file-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::odb_file_suffix_,
+ &options::odb_file_suffix_specified_ >;
+ _cli_options_map_["--sql-file-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::sql_file_suffix_,
+ &options::sql_file_suffix_specified_ >;
+ _cli_options_map_["--schema-file-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::schema_file_suffix_,
+ &options::schema_file_suffix_specified_ >;
+ _cli_options_map_["--changelog-file-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::changelog_file_suffix_,
+ &options::changelog_file_suffix_specified_ >;
+ _cli_options_map_["--hxx-suffix"] =
+ &::cli::thunk< options, std::string, &options::hxx_suffix_,
+ &options::hxx_suffix_specified_ >;
+ _cli_options_map_["--ixx-suffix"] =
+ &::cli::thunk< options, std::string, &options::ixx_suffix_,
+ &options::ixx_suffix_specified_ >;
+ _cli_options_map_["--cxx-suffix"] =
+ &::cli::thunk< options, std::string, &options::cxx_suffix_,
+ &options::cxx_suffix_specified_ >;
+ _cli_options_map_["--sql-suffix"] =
+ &::cli::thunk< options, std::string, &options::sql_suffix_,
+ &options::sql_suffix_specified_ >;
+ _cli_options_map_["--changelog-suffix"] =
+ &::cli::thunk< options, std::string, &options::changelog_suffix_,
+ &options::changelog_suffix_specified_ >;
+ _cli_options_map_["--hxx-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::hxx_prologue_,
+ &options::hxx_prologue_specified_ >;
+ _cli_options_map_["--ixx-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::ixx_prologue_,
+ &options::ixx_prologue_specified_ >;
+ _cli_options_map_["--cxx-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::cxx_prologue_,
+ &options::cxx_prologue_specified_ >;
+ _cli_options_map_["--schema-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::schema_prologue_,
+ &options::schema_prologue_specified_ >;
+ _cli_options_map_["--sql-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_prologue_,
+ &options::sql_prologue_specified_ >;
+ _cli_options_map_["--migration-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::migration_prologue_,
+ &options::migration_prologue_specified_ >;
+ _cli_options_map_["--sql-interlude"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_interlude_,
+ &options::sql_interlude_specified_ >;
+ _cli_options_map_["--hxx-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::hxx_epilogue_,
+ &options::hxx_epilogue_specified_ >;
+ _cli_options_map_["--ixx-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::ixx_epilogue_,
+ &options::ixx_epilogue_specified_ >;
+ _cli_options_map_["--cxx-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::cxx_epilogue_,
+ &options::cxx_epilogue_specified_ >;
+ _cli_options_map_["--schema-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::schema_epilogue_,
+ &options::schema_epilogue_specified_ >;
+ _cli_options_map_["--sql-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_epilogue_,
+ &options::sql_epilogue_specified_ >;
+ _cli_options_map_["--migration-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::migration_epilogue_,
+ &options::migration_epilogue_specified_ >;
+ _cli_options_map_["--hxx-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::hxx_prologue_file_,
+ &options::hxx_prologue_file_specified_ >;
+ _cli_options_map_["--ixx-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::ixx_prologue_file_,
+ &options::ixx_prologue_file_specified_ >;
+ _cli_options_map_["--cxx-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::cxx_prologue_file_,
+ &options::cxx_prologue_file_specified_ >;
+ _cli_options_map_["--schema-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::schema_prologue_file_,
+ &options::schema_prologue_file_specified_ >;
+ _cli_options_map_["--sql-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_prologue_file_,
+ &options::sql_prologue_file_specified_ >;
+ _cli_options_map_["--migration-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::migration_prologue_file_,
+ &options::migration_prologue_file_specified_ >;
+ _cli_options_map_["--sql-interlude-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_interlude_file_,
+ &options::sql_interlude_file_specified_ >;
+ _cli_options_map_["--hxx-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::hxx_epilogue_file_,
+ &options::hxx_epilogue_file_specified_ >;
+ _cli_options_map_["--ixx-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::ixx_epilogue_file_,
+ &options::ixx_epilogue_file_specified_ >;
+ _cli_options_map_["--cxx-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::cxx_epilogue_file_,
+ &options::cxx_epilogue_file_specified_ >;
+ _cli_options_map_["--schema-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::schema_epilogue_file_,
+ &options::schema_epilogue_file_specified_ >;
+ _cli_options_map_["--sql-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_epilogue_file_,
+ &options::sql_epilogue_file_specified_ >;
+ _cli_options_map_["--migration-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::migration_epilogue_file_,
+ &options::migration_epilogue_file_specified_ >;
+ _cli_options_map_["--odb-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::odb_prologue_,
+ &options::odb_prologue_specified_ >;
+ _cli_options_map_["--odb-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::odb_prologue_file_,
+ &options::odb_prologue_file_specified_ >;
+ _cli_options_map_["--odb-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::odb_epilogue_,
+ &options::odb_epilogue_specified_ >;
+ _cli_options_map_["--odb-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::odb_epilogue_file_,
+ &options::odb_epilogue_file_specified_ >;
+ _cli_options_map_["--table-prefix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::table_prefix_,
+ &options::table_prefix_specified_ >;
+ _cli_options_map_["--index-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::index_suffix_,
+ &options::index_suffix_specified_ >;
+ _cli_options_map_["--fkey-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::fkey_suffix_,
+ &options::fkey_suffix_specified_ >;
+ _cli_options_map_["--sequence-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::sequence_suffix_,
+ &options::sequence_suffix_specified_ >;
+ _cli_options_map_["--sql-name-case"] =
+ &::cli::thunk< options, database_map<name_case>, &options::sql_name_case_,
+ &options::sql_name_case_specified_ >;
+ _cli_options_map_["--table-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::table_regex_,
+ &options::table_regex_specified_ >;
+ _cli_options_map_["--column-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::column_regex_,
+ &options::column_regex_specified_ >;
+ _cli_options_map_["--index-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::index_regex_,
+ &options::index_regex_specified_ >;
+ _cli_options_map_["--fkey-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::fkey_regex_,
+ &options::fkey_regex_specified_ >;
+ _cli_options_map_["--sequence-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sequence_regex_,
+ &options::sequence_regex_specified_ >;
+ _cli_options_map_["--statement-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::statement_regex_,
+ &options::statement_regex_specified_ >;
+ _cli_options_map_["--sql-name-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_name_regex_,
+ &options::sql_name_regex_specified_ >;
+ _cli_options_map_["--sql-name-regex-trace"] =
+ &::cli::thunk< options, &options::sql_name_regex_trace_ >;
+ _cli_options_map_["--accessor-regex"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::accessor_regex_,
+ &options::accessor_regex_specified_ >;
+ _cli_options_map_["--accessor-regex-trace"] =
+ &::cli::thunk< options, &options::accessor_regex_trace_ >;
+ _cli_options_map_["--modifier-regex"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::modifier_regex_,
+ &options::modifier_regex_specified_ >;
+ _cli_options_map_["--modifier-regex-trace"] =
+ &::cli::thunk< options, &options::modifier_regex_trace_ >;
+ _cli_options_map_["--include-with-brackets"] =
+ &::cli::thunk< options, &options::include_with_brackets_ >;
+ _cli_options_map_["--include-prefix"] =
+ &::cli::thunk< options, std::string, &options::include_prefix_,
+ &options::include_prefix_specified_ >;
+ _cli_options_map_["--include-regex"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::include_regex_,
+ &options::include_regex_specified_ >;
+ _cli_options_map_["--include-regex-trace"] =
+ &::cli::thunk< options, &options::include_regex_trace_ >;
+ _cli_options_map_["--guard-prefix"] =
+ &::cli::thunk< options, std::string, &options::guard_prefix_,
+ &options::guard_prefix_specified_ >;
+ _cli_options_map_["--show-sloc"] =
+ &::cli::thunk< options, &options::show_sloc_ >;
+ _cli_options_map_["--sloc-limit"] =
+ &::cli::thunk< options, std::size_t, &options::sloc_limit_,
+ &options::sloc_limit_specified_ >;
+ _cli_options_map_["--options-file"] =
+ &::cli::thunk< options, std::string, &options::options_file_,
+ &options::options_file_specified_ >;
+ _cli_options_map_["-x"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::x_,
+ &options::x_specified_ >;
+ _cli_options_map_["-v"] =
+ &::cli::thunk< options, &options::v_ >;
+ _cli_options_map_["--trace"] =
+ &::cli::thunk< options, &options::trace_ >;
+ _cli_options_map_["--mysql-engine"] =
+ &::cli::thunk< options, std::string, &options::mysql_engine_,
+ &options::mysql_engine_specified_ >;
+ _cli_options_map_["--sqlite-override-null"] =
+ &::cli::thunk< options, &options::sqlite_override_null_ >;
+ _cli_options_map_["--sqlite-lax-auto-id"] =
+ &::cli::thunk< options, &options::sqlite_lax_auto_id_ >;
+ _cli_options_map_["--pgsql-server-version"] =
+ &::cli::thunk< options, ::pgsql_version, &options::pgsql_server_version_,
+ &options::pgsql_server_version_specified_ >;
+ _cli_options_map_["--oracle-client-version"] =
+ &::cli::thunk< options, ::oracle_version, &options::oracle_client_version_,
+ &options::oracle_client_version_specified_ >;
+ _cli_options_map_["--oracle-warn-truncation"] =
+ &::cli::thunk< options, &options::oracle_warn_truncation_ >;
+ _cli_options_map_["--mssql-server-version"] =
+ &::cli::thunk< options, ::mssql_version, &options::mssql_server_version_,
+ &options::mssql_server_version_specified_ >;
+ _cli_options_map_["--mssql-short-limit"] =
+ &::cli::thunk< options, unsigned int, &options::mssql_short_limit_,
+ &options::mssql_short_limit_specified_ >;
+ }
+};
+
+static _cli_options_map_init _cli_options_map_init_;
+
+bool options::
+_parse (const char* o, ::cli::scanner& s)
+{
+ _cli_options_map::const_iterator i (_cli_options_map_.find (o));
+
+ if (i != _cli_options_map_.end ())
+ {
+ (*(i->second)) (*this, s);
+ return true;
+ }
+
+ return false;
+}
+
+bool options::
+_parse (::cli::scanner& s,
+ ::cli::unknown_mode opt_mode,
+ ::cli::unknown_mode arg_mode)
+{
+ // Can't skip combined flags (--no-combined-flags).
+ //
+ assert (opt_mode != ::cli::unknown_mode::skip);
+
+ bool r = false;
+ bool opt = true;
+
+ while (s.more ())
+ {
+ const char* o = s.peek ();
+
+ if (std::strcmp (o, "--") == 0)
+ {
+ opt = false;
+ s.skip ();
+ r = true;
+ continue;
+ }
+
+ if (opt)
+ {
+ if (_parse (o, s))
+ {
+ r = true;
+ continue;
+ }
+
+ if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0')
+ {
+ // Handle combined option values.
+ //
+ std::string co;
+ if (const char* v = std::strchr (o, '='))
+ {
+ co.assign (o, 0, v - o);
+ ++v;
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (co.c_str ()),
+ const_cast<char*> (v)
+ };
+
+ ::cli::argv_scanner ns (0, ac, av);
+
+ if (_parse (co.c_str (), ns))
+ {
+ // Parsed the option but not its value?
+ //
+ if (ns.end () != 2)
+ throw ::cli::invalid_value (co, v);
+
+ s.next ();
+ r = true;
+ continue;
+ }
+ else
+ {
+ // Set the unknown option and fall through.
+ //
+ o = co.c_str ();
+ }
+ }
+
+ // Handle combined flags.
+ //
+ char cf[3];
+ {
+ const char* p = o + 1;
+ for (; *p != '\0'; ++p)
+ {
+ if (!((*p >= 'a' && *p <= 'z') ||
+ (*p >= 'A' && *p <= 'Z') ||
+ (*p >= '0' && *p <= '9')))
+ break;
+ }
+
+ if (*p == '\0')
+ {
+ for (p = o + 1; *p != '\0'; ++p)
+ {
+ std::strcpy (cf, "-");
+ cf[1] = *p;
+ cf[2] = '\0';
+
+ int ac (1);
+ char* av[] =
+ {
+ cf
+ };
+
+ ::cli::argv_scanner ns (0, ac, av);
+
+ if (!_parse (cf, ns))
+ break;
+ }
+
+ if (*p == '\0')
+ {
+ // All handled.
+ //
+ s.next ();
+ r = true;
+ continue;
+ }
+ else
+ {
+ // Set the unknown option and fall through.
+ //
+ o = cf;
+ }
+ }
+ }
+
+ switch (opt_mode)
+ {
+ case ::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::cli::unknown_mode::fail:
+ {
+ throw ::cli::unknown_option (o);
+ }
+ }
+
+ break;
+ }
+ }
+
+ switch (arg_mode)
+ {
+ case ::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::cli::unknown_mode::fail:
+ {
+ throw ::cli::unknown_argument (o);
+ }
+ }
+
+ break;
+ }
+
+ return r;
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
diff --git a/odb/odb/pregenerated/odb/options.hxx b/odb/odb/pregenerated/odb/options.hxx
new file mode 100644
index 0000000..74406a0
--- /dev/null
+++ b/odb/odb/pregenerated/odb/options.hxx
@@ -0,0 +1,2340 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+#ifndef ODB_OPTIONS_HXX
+#define ODB_OPTIONS_HXX
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <list>
+#include <deque>
+#include <map>
+#include <vector>
+#include <iosfwd>
+#include <string>
+#include <cstddef>
+#include <exception>
+
+#ifndef CLI_POTENTIALLY_UNUSED
+# if defined(_MSC_VER) || defined(__xlC__)
+# define CLI_POTENTIALLY_UNUSED(x) (void*)&x
+# else
+# define CLI_POTENTIALLY_UNUSED(x) (void)x
+# endif
+#endif
+
+namespace cli
+{
+ class usage_para
+ {
+ public:
+ enum value
+ {
+ none,
+ text,
+ option
+ };
+
+ usage_para (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ class unknown_mode
+ {
+ public:
+ enum value
+ {
+ skip,
+ stop,
+ fail
+ };
+
+ unknown_mode (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ // Exceptions.
+ //
+
+ class exception: public std::exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const = 0;
+ };
+
+ ::std::ostream&
+ operator<< (::std::ostream&, const exception&);
+
+ class unknown_option: public exception
+ {
+ public:
+ virtual
+ ~unknown_option () throw ();
+
+ unknown_option (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class unknown_argument: public exception
+ {
+ public:
+ virtual
+ ~unknown_argument () throw ();
+
+ unknown_argument (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ class missing_value: public exception
+ {
+ public:
+ virtual
+ ~missing_value () throw ();
+
+ missing_value (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class invalid_value: public exception
+ {
+ public:
+ virtual
+ ~invalid_value () throw ();
+
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message = std::string ());
+
+ const std::string&
+ option () const;
+
+ const std::string&
+ value () const;
+
+ const std::string&
+ message () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ std::string value_;
+ std::string message_;
+ };
+
+ class eos_reached: public exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class file_io_failure: public exception
+ {
+ public:
+ virtual
+ ~file_io_failure () throw ();
+
+ file_io_failure (const std::string& file);
+
+ const std::string&
+ file () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string file_;
+ };
+
+ class unmatched_quote: public exception
+ {
+ public:
+ virtual
+ ~unmatched_quote () throw ();
+
+ unmatched_quote (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ // Command line argument scanner interface.
+ //
+ // The values returned by next() are guaranteed to be valid
+ // for the two previous arguments up until a call to a third
+ // peek() or next().
+ //
+ // The position() function returns a monotonically-increasing
+ // number which, if stored, can later be used to determine the
+ // relative position of the argument returned by the following
+ // call to next(). Note that if multiple scanners are used to
+ // extract arguments from multiple sources, then the end
+ // position of the previous scanner should be used as the
+ // start position of the next.
+ //
+ class scanner
+ {
+ public:
+ virtual
+ ~scanner ();
+
+ virtual bool
+ more () = 0;
+
+ virtual const char*
+ peek () = 0;
+
+ virtual const char*
+ next () = 0;
+
+ virtual void
+ skip () = 0;
+
+ virtual std::size_t
+ position () = 0;
+ };
+
+ class argv_scanner: public scanner
+ {
+ public:
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ int
+ end () const;
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ protected:
+ std::size_t start_position_;
+ int i_;
+ int& argc_;
+ char** argv_;
+ bool erase_;
+ };
+
+ class argv_file_scanner: public argv_scanner
+ {
+ public:
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t start_position = 0);
+
+ struct option_info
+ {
+ // If search_func is not NULL, it is called, with the arg
+ // value as the second argument, to locate the options file.
+ // If it returns an empty string, then the file is ignored.
+ //
+ const char* option;
+ std::string (*search_func) (const char*, void* arg);
+ void* arg;
+ };
+
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const option_info* options = 0,
+ std::size_t options_count = 0,
+ std::size_t start_position = 0);
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ // Return the file path if the peeked at argument came from a file and
+ // the empty string otherwise. The reference is guaranteed to be valid
+ // till the end of the scanner lifetime.
+ //
+ const std::string&
+ peek_file ();
+
+ // Return the 1-based line number if the peeked at argument came from
+ // a file and zero otherwise.
+ //
+ std::size_t
+ peek_line ();
+
+ private:
+ const option_info*
+ find (const char*) const;
+
+ void
+ load (const std::string& file);
+
+ typedef argv_scanner base;
+
+ const std::string option_;
+ option_info option_info_;
+ const option_info* options_;
+ std::size_t options_count_;
+
+ struct arg
+ {
+ std::string value;
+ const std::string* file;
+ std::size_t line;
+ };
+
+ std::deque<arg> args_;
+ std::list<std::string> files_;
+
+ // Circular buffer of two arguments.
+ //
+ std::string hold_[2];
+ std::size_t i_;
+
+ bool skip_;
+
+ static int zero_argc_;
+ static std::string empty_string_;
+ };
+
+ typedef std::vector<std::string> option_names;
+
+ class option
+ {
+ public:
+
+ const std::string&
+ name () const;
+
+ const option_names&
+ aliases () const;
+
+ bool
+ flag () const;
+
+ const std::string&
+ default_value () const;
+
+ public:option ();
+ option (const std::string& name,
+ const option_names& aliases,
+ bool flag,
+ const std::string& default_value);
+
+ private:
+ std::string name_;
+ option_names aliases_;
+ bool flag_;
+ std::string default_value_;
+ };
+
+ class options: public std::vector<option>
+ {
+ public:
+ typedef std::vector<option> container_type;
+
+ container_type::const_iterator
+ find (const std::string& name) const;
+
+ void
+ push_back (const option&);
+ private:
+ typedef std::map<std::string, container_type::size_type> map_type;
+ map_type map_;
+ };
+
+ template <typename X>
+ struct parser;
+}
+
+#include <set>
+
+#include <vector>
+
+#include <string>
+
+#include <cstddef>
+
+#include <cstdint>
+
+#include <odb/option-types.hxx>
+
+class options
+{
+ public:
+ options ();
+
+ options (int& argc,
+ char** argv,
+ bool erase = false,
+ ::cli::unknown_mode option = ::cli::unknown_mode::fail,
+ ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ ::cli::unknown_mode option = ::cli::unknown_mode::fail,
+ ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
+
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::cli::unknown_mode option = ::cli::unknown_mode::fail,
+ ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::cli::unknown_mode option = ::cli::unknown_mode::fail,
+ ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
+
+ options (::cli::scanner&,
+ ::cli::unknown_mode option = ::cli::unknown_mode::fail,
+ ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
+
+ // Option accessors and modifiers.
+ //
+ const std::uint64_t&
+ build2_metadata () const;
+
+ std::uint64_t&
+ build2_metadata ();
+
+ void
+ build2_metadata (const std::uint64_t&);
+
+ bool
+ build2_metadata_specified () const;
+
+ void
+ build2_metadata_specified (bool);
+
+ const bool&
+ help () const;
+
+ bool&
+ help ();
+
+ void
+ help (const bool&);
+
+ const bool&
+ version () const;
+
+ bool&
+ version ();
+
+ void
+ version (const bool&);
+
+ const std::vector<std::string>&
+ I () const;
+
+ std::vector<std::string>&
+ I ();
+
+ void
+ I (const std::vector<std::string>&);
+
+ bool
+ I_specified () const;
+
+ void
+ I_specified (bool);
+
+ const std::vector<std::string>&
+ D () const;
+
+ std::vector<std::string>&
+ D ();
+
+ void
+ D (const std::vector<std::string>&);
+
+ bool
+ D_specified () const;
+
+ void
+ D_specified (bool);
+
+ const std::vector<std::string>&
+ U () const;
+
+ std::vector<std::string>&
+ U ();
+
+ void
+ U (const std::vector<std::string>&);
+
+ bool
+ U_specified () const;
+
+ void
+ U_specified (bool);
+
+ const std::vector< ::database>&
+ database () const;
+
+ std::vector< ::database>&
+ database ();
+
+ void
+ database (const std::vector< ::database>&);
+
+ bool
+ database_specified () const;
+
+ void
+ database_specified (bool);
+
+ const ::multi_database&
+ multi_database () const;
+
+ ::multi_database&
+ multi_database ();
+
+ void
+ multi_database (const ::multi_database&);
+
+ bool
+ multi_database_specified () const;
+
+ void
+ multi_database_specified (bool);
+
+ const ::database&
+ default_database () const;
+
+ ::database&
+ default_database ();
+
+ void
+ default_database (const ::database&);
+
+ bool
+ default_database_specified () const;
+
+ void
+ default_database_specified (bool);
+
+ const bool&
+ generate_query () const;
+
+ bool&
+ generate_query ();
+
+ void
+ generate_query (const bool&);
+
+ const bool&
+ generate_prepared () const;
+
+ bool&
+ generate_prepared ();
+
+ void
+ generate_prepared (const bool&);
+
+ const bool&
+ omit_unprepared () const;
+
+ bool&
+ omit_unprepared ();
+
+ void
+ omit_unprepared (const bool&);
+
+ const bool&
+ generate_session () const;
+
+ bool&
+ generate_session ();
+
+ void
+ generate_session (const bool&);
+
+ const bool&
+ generate_schema () const;
+
+ bool&
+ generate_schema ();
+
+ void
+ generate_schema (const bool&);
+
+ const bool&
+ generate_schema_only () const;
+
+ bool&
+ generate_schema_only ();
+
+ void
+ generate_schema_only (const bool&);
+
+ const bool&
+ suppress_migration () const;
+
+ bool&
+ suppress_migration ();
+
+ void
+ suppress_migration (const bool&);
+
+ const bool&
+ suppress_schema_version () const;
+
+ bool&
+ suppress_schema_version ();
+
+ void
+ suppress_schema_version (const bool&);
+
+ const database_map<qname>&
+ schema_version_table () const;
+
+ database_map<qname>&
+ schema_version_table ();
+
+ void
+ schema_version_table (const database_map<qname>&);
+
+ bool
+ schema_version_table_specified () const;
+
+ void
+ schema_version_table_specified (bool);
+
+ const database_map<std::set< ::schema_format> >&
+ schema_format () const;
+
+ database_map<std::set< ::schema_format> >&
+ schema_format ();
+
+ void
+ schema_format (const database_map<std::set< ::schema_format> >&);
+
+ bool
+ schema_format_specified () const;
+
+ void
+ schema_format_specified (bool);
+
+ const bool&
+ omit_drop () const;
+
+ bool&
+ omit_drop ();
+
+ void
+ omit_drop (const bool&);
+
+ const bool&
+ omit_create () const;
+
+ bool&
+ omit_create ();
+
+ void
+ omit_create (const bool&);
+
+ const database_map<std::string>&
+ schema_name () const;
+
+ database_map<std::string>&
+ schema_name ();
+
+ void
+ schema_name (const database_map<std::string>&);
+
+ bool
+ schema_name_specified () const;
+
+ void
+ schema_name_specified (bool);
+
+ const database_map<deferrable>&
+ fkeys_deferrable_mode () const;
+
+ database_map<deferrable>&
+ fkeys_deferrable_mode ();
+
+ void
+ fkeys_deferrable_mode (const database_map<deferrable>&);
+
+ bool
+ fkeys_deferrable_mode_specified () const;
+
+ void
+ fkeys_deferrable_mode_specified (bool);
+
+ const std::string&
+ default_pointer () const;
+
+ std::string&
+ default_pointer ();
+
+ void
+ default_pointer (const std::string&);
+
+ bool
+ default_pointer_specified () const;
+
+ void
+ default_pointer_specified (bool);
+
+ const std::string&
+ session_type () const;
+
+ std::string&
+ session_type ();
+
+ void
+ session_type (const std::string&);
+
+ bool
+ session_type_specified () const;
+
+ void
+ session_type_specified (bool);
+
+ const std::string&
+ profile () const;
+
+ std::string&
+ profile ();
+
+ void
+ profile (const std::string&);
+
+ bool
+ profile_specified () const;
+
+ void
+ profile_specified (bool);
+
+ const bool&
+ at_once () const;
+
+ bool&
+ at_once ();
+
+ void
+ at_once (const bool&);
+
+ const database_map<qname>&
+ schema () const;
+
+ database_map<qname>&
+ schema ();
+
+ void
+ schema (const database_map<qname>&);
+
+ bool
+ schema_specified () const;
+
+ void
+ schema_specified (bool);
+
+ const database_map<std::string>&
+ export_symbol () const;
+
+ database_map<std::string>&
+ export_symbol ();
+
+ void
+ export_symbol (const database_map<std::string>&);
+
+ bool
+ export_symbol_specified () const;
+
+ void
+ export_symbol_specified (bool);
+
+ const database_map<std::string>&
+ extern_symbol () const;
+
+ database_map<std::string>&
+ extern_symbol ();
+
+ void
+ extern_symbol (const database_map<std::string>&);
+
+ bool
+ extern_symbol_specified () const;
+
+ void
+ extern_symbol_specified (bool);
+
+ const cxx_version&
+ std () const;
+
+ cxx_version&
+ std ();
+
+ void
+ std (const cxx_version&);
+
+ bool
+ std_specified () const;
+
+ void
+ std_specified (bool);
+
+ const bool&
+ warn_hard_add () const;
+
+ bool&
+ warn_hard_add ();
+
+ void
+ warn_hard_add (const bool&);
+
+ const bool&
+ warn_hard_delete () const;
+
+ bool&
+ warn_hard_delete ();
+
+ void
+ warn_hard_delete (const bool&);
+
+ const bool&
+ warn_hard () const;
+
+ bool&
+ warn_hard ();
+
+ void
+ warn_hard (const bool&);
+
+ const std::string&
+ output_dir () const;
+
+ std::string&
+ output_dir ();
+
+ void
+ output_dir (const std::string&);
+
+ bool
+ output_dir_specified () const;
+
+ void
+ output_dir_specified (bool);
+
+ const std::string&
+ input_name () const;
+
+ std::string&
+ input_name ();
+
+ void
+ input_name (const std::string&);
+
+ bool
+ input_name_specified () const;
+
+ void
+ input_name_specified (bool);
+
+ const database_map<std::string>&
+ changelog () const;
+
+ database_map<std::string>&
+ changelog ();
+
+ void
+ changelog (const database_map<std::string>&);
+
+ bool
+ changelog_specified () const;
+
+ void
+ changelog_specified (bool);
+
+ const database_map<std::string>&
+ changelog_in () const;
+
+ database_map<std::string>&
+ changelog_in ();
+
+ void
+ changelog_in (const database_map<std::string>&);
+
+ bool
+ changelog_in_specified () const;
+
+ void
+ changelog_in_specified (bool);
+
+ const database_map<std::string>&
+ changelog_out () const;
+
+ database_map<std::string>&
+ changelog_out ();
+
+ void
+ changelog_out (const database_map<std::string>&);
+
+ bool
+ changelog_out_specified () const;
+
+ void
+ changelog_out_specified (bool);
+
+ const database_map<std::string>&
+ changelog_dir () const;
+
+ database_map<std::string>&
+ changelog_dir ();
+
+ void
+ changelog_dir (const database_map<std::string>&);
+
+ bool
+ changelog_dir_specified () const;
+
+ void
+ changelog_dir_specified (bool);
+
+ const bool&
+ init_changelog () const;
+
+ bool&
+ init_changelog ();
+
+ void
+ init_changelog (const bool&);
+
+ const database_map<std::string>&
+ odb_file_suffix () const;
+
+ database_map<std::string>&
+ odb_file_suffix ();
+
+ void
+ odb_file_suffix (const database_map<std::string>&);
+
+ bool
+ odb_file_suffix_specified () const;
+
+ void
+ odb_file_suffix_specified (bool);
+
+ const database_map<std::string>&
+ sql_file_suffix () const;
+
+ database_map<std::string>&
+ sql_file_suffix ();
+
+ void
+ sql_file_suffix (const database_map<std::string>&);
+
+ bool
+ sql_file_suffix_specified () const;
+
+ void
+ sql_file_suffix_specified (bool);
+
+ const database_map<std::string>&
+ schema_file_suffix () const;
+
+ database_map<std::string>&
+ schema_file_suffix ();
+
+ void
+ schema_file_suffix (const database_map<std::string>&);
+
+ bool
+ schema_file_suffix_specified () const;
+
+ void
+ schema_file_suffix_specified (bool);
+
+ const database_map<std::string>&
+ changelog_file_suffix () const;
+
+ database_map<std::string>&
+ changelog_file_suffix ();
+
+ void
+ changelog_file_suffix (const database_map<std::string>&);
+
+ bool
+ changelog_file_suffix_specified () const;
+
+ void
+ changelog_file_suffix_specified (bool);
+
+ const std::string&
+ hxx_suffix () const;
+
+ std::string&
+ hxx_suffix ();
+
+ void
+ hxx_suffix (const std::string&);
+
+ bool
+ hxx_suffix_specified () const;
+
+ void
+ hxx_suffix_specified (bool);
+
+ const std::string&
+ ixx_suffix () const;
+
+ std::string&
+ ixx_suffix ();
+
+ void
+ ixx_suffix (const std::string&);
+
+ bool
+ ixx_suffix_specified () const;
+
+ void
+ ixx_suffix_specified (bool);
+
+ const std::string&
+ cxx_suffix () const;
+
+ std::string&
+ cxx_suffix ();
+
+ void
+ cxx_suffix (const std::string&);
+
+ bool
+ cxx_suffix_specified () const;
+
+ void
+ cxx_suffix_specified (bool);
+
+ const std::string&
+ sql_suffix () const;
+
+ std::string&
+ sql_suffix ();
+
+ void
+ sql_suffix (const std::string&);
+
+ bool
+ sql_suffix_specified () const;
+
+ void
+ sql_suffix_specified (bool);
+
+ const std::string&
+ changelog_suffix () const;
+
+ std::string&
+ changelog_suffix ();
+
+ void
+ changelog_suffix (const std::string&);
+
+ bool
+ changelog_suffix_specified () const;
+
+ void
+ changelog_suffix_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ hxx_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ hxx_prologue ();
+
+ void
+ hxx_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ hxx_prologue_specified () const;
+
+ void
+ hxx_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ ixx_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ ixx_prologue ();
+
+ void
+ ixx_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ ixx_prologue_specified () const;
+
+ void
+ ixx_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ cxx_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ cxx_prologue ();
+
+ void
+ cxx_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ cxx_prologue_specified () const;
+
+ void
+ cxx_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ schema_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ schema_prologue ();
+
+ void
+ schema_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ schema_prologue_specified () const;
+
+ void
+ schema_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ sql_prologue ();
+
+ void
+ sql_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_prologue_specified () const;
+
+ void
+ sql_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ migration_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ migration_prologue ();
+
+ void
+ migration_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ migration_prologue_specified () const;
+
+ void
+ migration_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_interlude () const;
+
+ database_map<std::vector<std::string> >&
+ sql_interlude ();
+
+ void
+ sql_interlude (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_interlude_specified () const;
+
+ void
+ sql_interlude_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ hxx_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ hxx_epilogue ();
+
+ void
+ hxx_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ hxx_epilogue_specified () const;
+
+ void
+ hxx_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ ixx_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ ixx_epilogue ();
+
+ void
+ ixx_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ ixx_epilogue_specified () const;
+
+ void
+ ixx_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ cxx_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ cxx_epilogue ();
+
+ void
+ cxx_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ cxx_epilogue_specified () const;
+
+ void
+ cxx_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ schema_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ schema_epilogue ();
+
+ void
+ schema_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ schema_epilogue_specified () const;
+
+ void
+ schema_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ sql_epilogue ();
+
+ void
+ sql_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_epilogue_specified () const;
+
+ void
+ sql_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ migration_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ migration_epilogue ();
+
+ void
+ migration_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ migration_epilogue_specified () const;
+
+ void
+ migration_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ hxx_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ hxx_prologue_file ();
+
+ void
+ hxx_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ hxx_prologue_file_specified () const;
+
+ void
+ hxx_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ ixx_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ ixx_prologue_file ();
+
+ void
+ ixx_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ ixx_prologue_file_specified () const;
+
+ void
+ ixx_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ cxx_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ cxx_prologue_file ();
+
+ void
+ cxx_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ cxx_prologue_file_specified () const;
+
+ void
+ cxx_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ schema_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ schema_prologue_file ();
+
+ void
+ schema_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ schema_prologue_file_specified () const;
+
+ void
+ schema_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ sql_prologue_file ();
+
+ void
+ sql_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_prologue_file_specified () const;
+
+ void
+ sql_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ migration_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ migration_prologue_file ();
+
+ void
+ migration_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ migration_prologue_file_specified () const;
+
+ void
+ migration_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_interlude_file () const;
+
+ database_map<std::vector<std::string> >&
+ sql_interlude_file ();
+
+ void
+ sql_interlude_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_interlude_file_specified () const;
+
+ void
+ sql_interlude_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ hxx_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ hxx_epilogue_file ();
+
+ void
+ hxx_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ hxx_epilogue_file_specified () const;
+
+ void
+ hxx_epilogue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ ixx_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ ixx_epilogue_file ();
+
+ void
+ ixx_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ ixx_epilogue_file_specified () const;
+
+ void
+ ixx_epilogue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ cxx_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ cxx_epilogue_file ();
+
+ void
+ cxx_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ cxx_epilogue_file_specified () const;
+
+ void
+ cxx_epilogue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ schema_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ schema_epilogue_file ();
+
+ void
+ schema_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ schema_epilogue_file_specified () const;
+
+ void
+ schema_epilogue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ sql_epilogue_file ();
+
+ void
+ sql_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_epilogue_file_specified () const;
+
+ void
+ sql_epilogue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ migration_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ migration_epilogue_file ();
+
+ void
+ migration_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ migration_epilogue_file_specified () const;
+
+ void
+ migration_epilogue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ odb_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ odb_prologue ();
+
+ void
+ odb_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ odb_prologue_specified () const;
+
+ void
+ odb_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ odb_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ odb_prologue_file ();
+
+ void
+ odb_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ odb_prologue_file_specified () const;
+
+ void
+ odb_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ odb_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ odb_epilogue ();
+
+ void
+ odb_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ odb_epilogue_specified () const;
+
+ void
+ odb_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ odb_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ odb_epilogue_file ();
+
+ void
+ odb_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ odb_epilogue_file_specified () const;
+
+ void
+ odb_epilogue_file_specified (bool);
+
+ const database_map<std::string>&
+ table_prefix () const;
+
+ database_map<std::string>&
+ table_prefix ();
+
+ void
+ table_prefix (const database_map<std::string>&);
+
+ bool
+ table_prefix_specified () const;
+
+ void
+ table_prefix_specified (bool);
+
+ const database_map<std::string>&
+ index_suffix () const;
+
+ database_map<std::string>&
+ index_suffix ();
+
+ void
+ index_suffix (const database_map<std::string>&);
+
+ bool
+ index_suffix_specified () const;
+
+ void
+ index_suffix_specified (bool);
+
+ const database_map<std::string>&
+ fkey_suffix () const;
+
+ database_map<std::string>&
+ fkey_suffix ();
+
+ void
+ fkey_suffix (const database_map<std::string>&);
+
+ bool
+ fkey_suffix_specified () const;
+
+ void
+ fkey_suffix_specified (bool);
+
+ const database_map<std::string>&
+ sequence_suffix () const;
+
+ database_map<std::string>&
+ sequence_suffix ();
+
+ void
+ sequence_suffix (const database_map<std::string>&);
+
+ bool
+ sequence_suffix_specified () const;
+
+ void
+ sequence_suffix_specified (bool);
+
+ const database_map<name_case>&
+ sql_name_case () const;
+
+ database_map<name_case>&
+ sql_name_case ();
+
+ void
+ sql_name_case (const database_map<name_case>&);
+
+ bool
+ sql_name_case_specified () const;
+
+ void
+ sql_name_case_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ table_regex () const;
+
+ database_map<std::vector<std::string> >&
+ table_regex ();
+
+ void
+ table_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ table_regex_specified () const;
+
+ void
+ table_regex_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ column_regex () const;
+
+ database_map<std::vector<std::string> >&
+ column_regex ();
+
+ void
+ column_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ column_regex_specified () const;
+
+ void
+ column_regex_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ index_regex () const;
+
+ database_map<std::vector<std::string> >&
+ index_regex ();
+
+ void
+ index_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ index_regex_specified () const;
+
+ void
+ index_regex_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ fkey_regex () const;
+
+ database_map<std::vector<std::string> >&
+ fkey_regex ();
+
+ void
+ fkey_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ fkey_regex_specified () const;
+
+ void
+ fkey_regex_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sequence_regex () const;
+
+ database_map<std::vector<std::string> >&
+ sequence_regex ();
+
+ void
+ sequence_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ sequence_regex_specified () const;
+
+ void
+ sequence_regex_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ statement_regex () const;
+
+ database_map<std::vector<std::string> >&
+ statement_regex ();
+
+ void
+ statement_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ statement_regex_specified () const;
+
+ void
+ statement_regex_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_name_regex () const;
+
+ database_map<std::vector<std::string> >&
+ sql_name_regex ();
+
+ void
+ sql_name_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_name_regex_specified () const;
+
+ void
+ sql_name_regex_specified (bool);
+
+ const bool&
+ sql_name_regex_trace () const;
+
+ bool&
+ sql_name_regex_trace ();
+
+ void
+ sql_name_regex_trace (const bool&);
+
+ const std::vector<std::string>&
+ accessor_regex () const;
+
+ std::vector<std::string>&
+ accessor_regex ();
+
+ void
+ accessor_regex (const std::vector<std::string>&);
+
+ bool
+ accessor_regex_specified () const;
+
+ void
+ accessor_regex_specified (bool);
+
+ const bool&
+ accessor_regex_trace () const;
+
+ bool&
+ accessor_regex_trace ();
+
+ void
+ accessor_regex_trace (const bool&);
+
+ const std::vector<std::string>&
+ modifier_regex () const;
+
+ std::vector<std::string>&
+ modifier_regex ();
+
+ void
+ modifier_regex (const std::vector<std::string>&);
+
+ bool
+ modifier_regex_specified () const;
+
+ void
+ modifier_regex_specified (bool);
+
+ const bool&
+ modifier_regex_trace () const;
+
+ bool&
+ modifier_regex_trace ();
+
+ void
+ modifier_regex_trace (const bool&);
+
+ const bool&
+ include_with_brackets () const;
+
+ bool&
+ include_with_brackets ();
+
+ void
+ include_with_brackets (const bool&);
+
+ const std::string&
+ include_prefix () const;
+
+ std::string&
+ include_prefix ();
+
+ void
+ include_prefix (const std::string&);
+
+ bool
+ include_prefix_specified () const;
+
+ void
+ include_prefix_specified (bool);
+
+ const std::vector<std::string>&
+ include_regex () const;
+
+ std::vector<std::string>&
+ include_regex ();
+
+ void
+ include_regex (const std::vector<std::string>&);
+
+ bool
+ include_regex_specified () const;
+
+ void
+ include_regex_specified (bool);
+
+ const bool&
+ include_regex_trace () const;
+
+ bool&
+ include_regex_trace ();
+
+ void
+ include_regex_trace (const bool&);
+
+ const std::string&
+ guard_prefix () const;
+
+ std::string&
+ guard_prefix ();
+
+ void
+ guard_prefix (const std::string&);
+
+ bool
+ guard_prefix_specified () const;
+
+ void
+ guard_prefix_specified (bool);
+
+ const bool&
+ show_sloc () const;
+
+ bool&
+ show_sloc ();
+
+ void
+ show_sloc (const bool&);
+
+ const std::size_t&
+ sloc_limit () const;
+
+ std::size_t&
+ sloc_limit ();
+
+ void
+ sloc_limit (const std::size_t&);
+
+ bool
+ sloc_limit_specified () const;
+
+ void
+ sloc_limit_specified (bool);
+
+ const std::string&
+ options_file () const;
+
+ std::string&
+ options_file ();
+
+ void
+ options_file (const std::string&);
+
+ bool
+ options_file_specified () const;
+
+ void
+ options_file_specified (bool);
+
+ const std::vector<std::string>&
+ x () const;
+
+ std::vector<std::string>&
+ x ();
+
+ void
+ x (const std::vector<std::string>&);
+
+ bool
+ x_specified () const;
+
+ void
+ x_specified (bool);
+
+ const bool&
+ v () const;
+
+ bool&
+ v ();
+
+ void
+ v (const bool&);
+
+ const bool&
+ trace () const;
+
+ bool&
+ trace ();
+
+ void
+ trace (const bool&);
+
+ const std::string&
+ mysql_engine () const;
+
+ std::string&
+ mysql_engine ();
+
+ void
+ mysql_engine (const std::string&);
+
+ bool
+ mysql_engine_specified () const;
+
+ void
+ mysql_engine_specified (bool);
+
+ const bool&
+ sqlite_override_null () const;
+
+ bool&
+ sqlite_override_null ();
+
+ void
+ sqlite_override_null (const bool&);
+
+ const bool&
+ sqlite_lax_auto_id () const;
+
+ bool&
+ sqlite_lax_auto_id ();
+
+ void
+ sqlite_lax_auto_id (const bool&);
+
+ const ::pgsql_version&
+ pgsql_server_version () const;
+
+ ::pgsql_version&
+ pgsql_server_version ();
+
+ void
+ pgsql_server_version (const ::pgsql_version&);
+
+ bool
+ pgsql_server_version_specified () const;
+
+ void
+ pgsql_server_version_specified (bool);
+
+ const ::oracle_version&
+ oracle_client_version () const;
+
+ ::oracle_version&
+ oracle_client_version ();
+
+ void
+ oracle_client_version (const ::oracle_version&);
+
+ bool
+ oracle_client_version_specified () const;
+
+ void
+ oracle_client_version_specified (bool);
+
+ const bool&
+ oracle_warn_truncation () const;
+
+ bool&
+ oracle_warn_truncation ();
+
+ void
+ oracle_warn_truncation (const bool&);
+
+ const ::mssql_version&
+ mssql_server_version () const;
+
+ ::mssql_version&
+ mssql_server_version ();
+
+ void
+ mssql_server_version (const ::mssql_version&);
+
+ bool
+ mssql_server_version_specified () const;
+
+ void
+ mssql_server_version_specified (bool);
+
+ const unsigned int&
+ mssql_short_limit () const;
+
+ unsigned int&
+ mssql_short_limit ();
+
+ void
+ mssql_short_limit (const unsigned int&);
+
+ bool
+ mssql_short_limit_specified () const;
+
+ void
+ mssql_short_limit_specified (bool);
+
+ // Print usage information.
+ //
+ static ::cli::usage_para
+ print_usage (::std::ostream&,
+ ::cli::usage_para = ::cli::usage_para::none);
+
+ // Option description.
+ //
+ static const ::cli::options&
+ description ();
+
+ // Implementation details.
+ //
+ protected:
+ friend struct _cli_options_desc_type;
+
+ static void
+ fill (::cli::options&);
+
+ bool
+ _parse (const char*, ::cli::scanner&);
+
+ private:
+ bool
+ _parse (::cli::scanner&,
+ ::cli::unknown_mode option,
+ ::cli::unknown_mode argument);
+
+ public:
+ std::uint64_t build2_metadata_;
+ bool build2_metadata_specified_;
+ bool help_;
+ bool version_;
+ std::vector<std::string> I_;
+ bool I_specified_;
+ std::vector<std::string> D_;
+ bool D_specified_;
+ std::vector<std::string> U_;
+ bool U_specified_;
+ std::vector< ::database> database_;
+ bool database_specified_;
+ ::multi_database multi_database_;
+ bool multi_database_specified_;
+ ::database default_database_;
+ bool default_database_specified_;
+ bool generate_query_;
+ bool generate_prepared_;
+ bool omit_unprepared_;
+ bool generate_session_;
+ bool generate_schema_;
+ bool generate_schema_only_;
+ bool suppress_migration_;
+ bool suppress_schema_version_;
+ database_map<qname> schema_version_table_;
+ bool schema_version_table_specified_;
+ database_map<std::set< ::schema_format> > schema_format_;
+ bool schema_format_specified_;
+ bool omit_drop_;
+ bool omit_create_;
+ database_map<std::string> schema_name_;
+ bool schema_name_specified_;
+ database_map<deferrable> fkeys_deferrable_mode_;
+ bool fkeys_deferrable_mode_specified_;
+ std::string default_pointer_;
+ bool default_pointer_specified_;
+ std::string session_type_;
+ bool session_type_specified_;
+ std::string profile_;
+ bool profile_specified_;
+ bool at_once_;
+ database_map<qname> schema_;
+ bool schema_specified_;
+ database_map<std::string> export_symbol_;
+ bool export_symbol_specified_;
+ database_map<std::string> extern_symbol_;
+ bool extern_symbol_specified_;
+ cxx_version std_;
+ bool std_specified_;
+ bool warn_hard_add_;
+ bool warn_hard_delete_;
+ bool warn_hard_;
+ std::string output_dir_;
+ bool output_dir_specified_;
+ std::string input_name_;
+ bool input_name_specified_;
+ database_map<std::string> changelog_;
+ bool changelog_specified_;
+ database_map<std::string> changelog_in_;
+ bool changelog_in_specified_;
+ database_map<std::string> changelog_out_;
+ bool changelog_out_specified_;
+ database_map<std::string> changelog_dir_;
+ bool changelog_dir_specified_;
+ bool init_changelog_;
+ database_map<std::string> odb_file_suffix_;
+ bool odb_file_suffix_specified_;
+ database_map<std::string> sql_file_suffix_;
+ bool sql_file_suffix_specified_;
+ database_map<std::string> schema_file_suffix_;
+ bool schema_file_suffix_specified_;
+ database_map<std::string> changelog_file_suffix_;
+ bool changelog_file_suffix_specified_;
+ std::string hxx_suffix_;
+ bool hxx_suffix_specified_;
+ std::string ixx_suffix_;
+ bool ixx_suffix_specified_;
+ std::string cxx_suffix_;
+ bool cxx_suffix_specified_;
+ std::string sql_suffix_;
+ bool sql_suffix_specified_;
+ std::string changelog_suffix_;
+ bool changelog_suffix_specified_;
+ database_map<std::vector<std::string> > hxx_prologue_;
+ bool hxx_prologue_specified_;
+ database_map<std::vector<std::string> > ixx_prologue_;
+ bool ixx_prologue_specified_;
+ database_map<std::vector<std::string> > cxx_prologue_;
+ bool cxx_prologue_specified_;
+ database_map<std::vector<std::string> > schema_prologue_;
+ bool schema_prologue_specified_;
+ database_map<std::vector<std::string> > sql_prologue_;
+ bool sql_prologue_specified_;
+ database_map<std::vector<std::string> > migration_prologue_;
+ bool migration_prologue_specified_;
+ database_map<std::vector<std::string> > sql_interlude_;
+ bool sql_interlude_specified_;
+ database_map<std::vector<std::string> > hxx_epilogue_;
+ bool hxx_epilogue_specified_;
+ database_map<std::vector<std::string> > ixx_epilogue_;
+ bool ixx_epilogue_specified_;
+ database_map<std::vector<std::string> > cxx_epilogue_;
+ bool cxx_epilogue_specified_;
+ database_map<std::vector<std::string> > schema_epilogue_;
+ bool schema_epilogue_specified_;
+ database_map<std::vector<std::string> > sql_epilogue_;
+ bool sql_epilogue_specified_;
+ database_map<std::vector<std::string> > migration_epilogue_;
+ bool migration_epilogue_specified_;
+ database_map<std::vector<std::string> > hxx_prologue_file_;
+ bool hxx_prologue_file_specified_;
+ database_map<std::vector<std::string> > ixx_prologue_file_;
+ bool ixx_prologue_file_specified_;
+ database_map<std::vector<std::string> > cxx_prologue_file_;
+ bool cxx_prologue_file_specified_;
+ database_map<std::vector<std::string> > schema_prologue_file_;
+ bool schema_prologue_file_specified_;
+ database_map<std::vector<std::string> > sql_prologue_file_;
+ bool sql_prologue_file_specified_;
+ database_map<std::vector<std::string> > migration_prologue_file_;
+ bool migration_prologue_file_specified_;
+ database_map<std::vector<std::string> > sql_interlude_file_;
+ bool sql_interlude_file_specified_;
+ database_map<std::vector<std::string> > hxx_epilogue_file_;
+ bool hxx_epilogue_file_specified_;
+ database_map<std::vector<std::string> > ixx_epilogue_file_;
+ bool ixx_epilogue_file_specified_;
+ database_map<std::vector<std::string> > cxx_epilogue_file_;
+ bool cxx_epilogue_file_specified_;
+ database_map<std::vector<std::string> > schema_epilogue_file_;
+ bool schema_epilogue_file_specified_;
+ database_map<std::vector<std::string> > sql_epilogue_file_;
+ bool sql_epilogue_file_specified_;
+ database_map<std::vector<std::string> > migration_epilogue_file_;
+ bool migration_epilogue_file_specified_;
+ database_map<std::vector<std::string> > odb_prologue_;
+ bool odb_prologue_specified_;
+ database_map<std::vector<std::string> > odb_prologue_file_;
+ bool odb_prologue_file_specified_;
+ database_map<std::vector<std::string> > odb_epilogue_;
+ bool odb_epilogue_specified_;
+ database_map<std::vector<std::string> > odb_epilogue_file_;
+ bool odb_epilogue_file_specified_;
+ database_map<std::string> table_prefix_;
+ bool table_prefix_specified_;
+ database_map<std::string> index_suffix_;
+ bool index_suffix_specified_;
+ database_map<std::string> fkey_suffix_;
+ bool fkey_suffix_specified_;
+ database_map<std::string> sequence_suffix_;
+ bool sequence_suffix_specified_;
+ database_map<name_case> sql_name_case_;
+ bool sql_name_case_specified_;
+ database_map<std::vector<std::string> > table_regex_;
+ bool table_regex_specified_;
+ database_map<std::vector<std::string> > column_regex_;
+ bool column_regex_specified_;
+ database_map<std::vector<std::string> > index_regex_;
+ bool index_regex_specified_;
+ database_map<std::vector<std::string> > fkey_regex_;
+ bool fkey_regex_specified_;
+ database_map<std::vector<std::string> > sequence_regex_;
+ bool sequence_regex_specified_;
+ database_map<std::vector<std::string> > statement_regex_;
+ bool statement_regex_specified_;
+ database_map<std::vector<std::string> > sql_name_regex_;
+ bool sql_name_regex_specified_;
+ bool sql_name_regex_trace_;
+ std::vector<std::string> accessor_regex_;
+ bool accessor_regex_specified_;
+ bool accessor_regex_trace_;
+ std::vector<std::string> modifier_regex_;
+ bool modifier_regex_specified_;
+ bool modifier_regex_trace_;
+ bool include_with_brackets_;
+ std::string include_prefix_;
+ bool include_prefix_specified_;
+ std::vector<std::string> include_regex_;
+ bool include_regex_specified_;
+ bool include_regex_trace_;
+ std::string guard_prefix_;
+ bool guard_prefix_specified_;
+ bool show_sloc_;
+ std::size_t sloc_limit_;
+ bool sloc_limit_specified_;
+ std::string options_file_;
+ bool options_file_specified_;
+ std::vector<std::string> x_;
+ bool x_specified_;
+ bool v_;
+ bool trace_;
+ std::string mysql_engine_;
+ bool mysql_engine_specified_;
+ bool sqlite_override_null_;
+ bool sqlite_lax_auto_id_;
+ ::pgsql_version pgsql_server_version_;
+ bool pgsql_server_version_specified_;
+ ::oracle_version oracle_client_version_;
+ bool oracle_client_version_specified_;
+ bool oracle_warn_truncation_;
+ ::mssql_version mssql_server_version_;
+ bool mssql_server_version_specified_;
+ unsigned int mssql_short_limit_;
+ bool mssql_short_limit_specified_;
+};
+
+#include <odb/options.ixx>
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
+#endif // ODB_OPTIONS_HXX
diff --git a/odb/odb/pregenerated/odb/options.ixx b/odb/odb/pregenerated/odb/options.ixx
new file mode 100644
index 0000000..9a78a2e
--- /dev/null
+++ b/odb/odb/pregenerated/odb/options.ixx
@@ -0,0 +1,3471 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <cassert>
+
+namespace cli
+{
+ // usage_para
+ //
+ inline usage_para::
+ usage_para (value v)
+ : v_ (v)
+ {
+ }
+
+ // unknown_mode
+ //
+ inline unknown_mode::
+ unknown_mode (value v)
+ : v_ (v)
+ {
+ }
+
+ // exception
+ //
+ inline ::std::ostream&
+ operator<< (::std::ostream& os, const exception& e)
+ {
+ e.print (os);
+ return os;
+ }
+
+ // unknown_option
+ //
+ inline unknown_option::
+ unknown_option (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& unknown_option::
+ option () const
+ {
+ return option_;
+ }
+
+ // unknown_argument
+ //
+ inline unknown_argument::
+ unknown_argument (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unknown_argument::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // missing_value
+ //
+ inline missing_value::
+ missing_value (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& missing_value::
+ option () const
+ {
+ return option_;
+ }
+
+ // invalid_value
+ //
+ inline invalid_value::
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message)
+ : option_ (option),
+ value_ (value),
+ message_ (message)
+ {
+ }
+
+ inline const std::string& invalid_value::
+ option () const
+ {
+ return option_;
+ }
+
+ inline const std::string& invalid_value::
+ value () const
+ {
+ return value_;
+ }
+
+ inline const std::string& invalid_value::
+ message () const
+ {
+ return message_;
+ }
+
+ // file_io_failure
+ //
+ inline file_io_failure::
+ file_io_failure (const std::string& file)
+ : file_ (file)
+ {
+ }
+
+ inline const std::string& file_io_failure::
+ file () const
+ {
+ return file_;
+ }
+
+ // unmatched_quote
+ //
+ inline unmatched_quote::
+ unmatched_quote (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unmatched_quote::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // argv_scanner
+ //
+ inline argv_scanner::
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + 1),
+ i_ (1),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline argv_scanner::
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + static_cast<std::size_t> (start)),
+ i_ (start),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline int argv_scanner::
+ end () const
+ {
+ return i_;
+ }
+
+ // argv_file_scanner
+ //
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+
+ load (file);
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const option_info* options,
+ std::size_t options_count,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ load (file);
+ }
+
+ inline const std::string& option::
+ name () const
+ {
+ return name_;
+ }
+
+ inline const option_names& option::
+ aliases () const
+ {
+ return aliases_;
+ }
+
+ inline bool option::
+ flag () const
+ {
+ return flag_;
+ }
+
+ inline const std::string& option::
+ default_value () const
+ {
+ return default_value_;
+ }
+
+ inline option::
+ option ()
+ {
+ }
+
+ inline option::
+ option (const std::string& n,
+ const option_names& a,
+ bool f,
+ const std::string& dv)
+ : name_ (n), aliases_ (a), flag_ (f), default_value_ (dv)
+ {
+ }
+
+ inline options::container_type::const_iterator options::
+ find (const std::string& name) const
+ {
+ map_type::const_iterator i (map_.find (name));
+ return i != map_.end () ? begin () + i->second : end ();
+ }
+}
+
+// options
+//
+
+inline const std::uint64_t& options::
+build2_metadata () const
+{
+ return this->build2_metadata_;
+}
+
+inline std::uint64_t& options::
+build2_metadata ()
+{
+ return this->build2_metadata_;
+}
+
+inline void options::
+build2_metadata (const std::uint64_t& x)
+{
+ this->build2_metadata_ = x;
+}
+
+inline bool options::
+build2_metadata_specified () const
+{
+ return this->build2_metadata_specified_;
+}
+
+inline void options::
+build2_metadata_specified (bool x)
+{
+ this->build2_metadata_specified_ = x;
+}
+
+inline const bool& options::
+help () const
+{
+ return this->help_;
+}
+
+inline bool& options::
+help ()
+{
+ return this->help_;
+}
+
+inline void options::
+help (const bool& x)
+{
+ this->help_ = x;
+}
+
+inline const bool& options::
+version () const
+{
+ return this->version_;
+}
+
+inline bool& options::
+version ()
+{
+ return this->version_;
+}
+
+inline void options::
+version (const bool& x)
+{
+ this->version_ = x;
+}
+
+inline const std::vector<std::string>& options::
+I () const
+{
+ return this->I_;
+}
+
+inline std::vector<std::string>& options::
+I ()
+{
+ return this->I_;
+}
+
+inline void options::
+I (const std::vector<std::string>& x)
+{
+ this->I_ = x;
+}
+
+inline bool options::
+I_specified () const
+{
+ return this->I_specified_;
+}
+
+inline void options::
+I_specified (bool x)
+{
+ this->I_specified_ = x;
+}
+
+inline const std::vector<std::string>& options::
+D () const
+{
+ return this->D_;
+}
+
+inline std::vector<std::string>& options::
+D ()
+{
+ return this->D_;
+}
+
+inline void options::
+D (const std::vector<std::string>& x)
+{
+ this->D_ = x;
+}
+
+inline bool options::
+D_specified () const
+{
+ return this->D_specified_;
+}
+
+inline void options::
+D_specified (bool x)
+{
+ this->D_specified_ = x;
+}
+
+inline const std::vector<std::string>& options::
+U () const
+{
+ return this->U_;
+}
+
+inline std::vector<std::string>& options::
+U ()
+{
+ return this->U_;
+}
+
+inline void options::
+U (const std::vector<std::string>& x)
+{
+ this->U_ = x;
+}
+
+inline bool options::
+U_specified () const
+{
+ return this->U_specified_;
+}
+
+inline void options::
+U_specified (bool x)
+{
+ this->U_specified_ = x;
+}
+
+inline const std::vector< ::database>& options::
+database () const
+{
+ return this->database_;
+}
+
+inline std::vector< ::database>& options::
+database ()
+{
+ return this->database_;
+}
+
+inline void options::
+database (const std::vector< ::database>& x)
+{
+ this->database_ = x;
+}
+
+inline bool options::
+database_specified () const
+{
+ return this->database_specified_;
+}
+
+inline void options::
+database_specified (bool x)
+{
+ this->database_specified_ = x;
+}
+
+inline const ::multi_database& options::
+multi_database () const
+{
+ return this->multi_database_;
+}
+
+inline ::multi_database& options::
+multi_database ()
+{
+ return this->multi_database_;
+}
+
+inline void options::
+multi_database (const ::multi_database& x)
+{
+ this->multi_database_ = x;
+}
+
+inline bool options::
+multi_database_specified () const
+{
+ return this->multi_database_specified_;
+}
+
+inline void options::
+multi_database_specified (bool x)
+{
+ this->multi_database_specified_ = x;
+}
+
+inline const ::database& options::
+default_database () const
+{
+ return this->default_database_;
+}
+
+inline ::database& options::
+default_database ()
+{
+ return this->default_database_;
+}
+
+inline void options::
+default_database (const ::database& x)
+{
+ this->default_database_ = x;
+}
+
+inline bool options::
+default_database_specified () const
+{
+ return this->default_database_specified_;
+}
+
+inline void options::
+default_database_specified (bool x)
+{
+ this->default_database_specified_ = x;
+}
+
+inline const bool& options::
+generate_query () const
+{
+ return this->generate_query_;
+}
+
+inline bool& options::
+generate_query ()
+{
+ return this->generate_query_;
+}
+
+inline void options::
+generate_query (const bool& x)
+{
+ this->generate_query_ = x;
+}
+
+inline const bool& options::
+generate_prepared () const
+{
+ return this->generate_prepared_;
+}
+
+inline bool& options::
+generate_prepared ()
+{
+ return this->generate_prepared_;
+}
+
+inline void options::
+generate_prepared (const bool& x)
+{
+ this->generate_prepared_ = x;
+}
+
+inline const bool& options::
+omit_unprepared () const
+{
+ return this->omit_unprepared_;
+}
+
+inline bool& options::
+omit_unprepared ()
+{
+ return this->omit_unprepared_;
+}
+
+inline void options::
+omit_unprepared (const bool& x)
+{
+ this->omit_unprepared_ = x;
+}
+
+inline const bool& options::
+generate_session () const
+{
+ return this->generate_session_;
+}
+
+inline bool& options::
+generate_session ()
+{
+ return this->generate_session_;
+}
+
+inline void options::
+generate_session (const bool& x)
+{
+ this->generate_session_ = x;
+}
+
+inline const bool& options::
+generate_schema () const
+{
+ return this->generate_schema_;
+}
+
+inline bool& options::
+generate_schema ()
+{
+ return this->generate_schema_;
+}
+
+inline void options::
+generate_schema (const bool& x)
+{
+ this->generate_schema_ = x;
+}
+
+inline const bool& options::
+generate_schema_only () const
+{
+ return this->generate_schema_only_;
+}
+
+inline bool& options::
+generate_schema_only ()
+{
+ return this->generate_schema_only_;
+}
+
+inline void options::
+generate_schema_only (const bool& x)
+{
+ this->generate_schema_only_ = x;
+}
+
+inline const bool& options::
+suppress_migration () const
+{
+ return this->suppress_migration_;
+}
+
+inline bool& options::
+suppress_migration ()
+{
+ return this->suppress_migration_;
+}
+
+inline void options::
+suppress_migration (const bool& x)
+{
+ this->suppress_migration_ = x;
+}
+
+inline const bool& options::
+suppress_schema_version () const
+{
+ return this->suppress_schema_version_;
+}
+
+inline bool& options::
+suppress_schema_version ()
+{
+ return this->suppress_schema_version_;
+}
+
+inline void options::
+suppress_schema_version (const bool& x)
+{
+ this->suppress_schema_version_ = x;
+}
+
+inline const database_map<qname>& options::
+schema_version_table () const
+{
+ return this->schema_version_table_;
+}
+
+inline database_map<qname>& options::
+schema_version_table ()
+{
+ return this->schema_version_table_;
+}
+
+inline void options::
+schema_version_table (const database_map<qname>& x)
+{
+ this->schema_version_table_ = x;
+}
+
+inline bool options::
+schema_version_table_specified () const
+{
+ return this->schema_version_table_specified_;
+}
+
+inline void options::
+schema_version_table_specified (bool x)
+{
+ this->schema_version_table_specified_ = x;
+}
+
+inline const database_map<std::set< ::schema_format> >& options::
+schema_format () const
+{
+ return this->schema_format_;
+}
+
+inline database_map<std::set< ::schema_format> >& options::
+schema_format ()
+{
+ return this->schema_format_;
+}
+
+inline void options::
+schema_format (const database_map<std::set< ::schema_format> >& x)
+{
+ this->schema_format_ = x;
+}
+
+inline bool options::
+schema_format_specified () const
+{
+ return this->schema_format_specified_;
+}
+
+inline void options::
+schema_format_specified (bool x)
+{
+ this->schema_format_specified_ = x;
+}
+
+inline const bool& options::
+omit_drop () const
+{
+ return this->omit_drop_;
+}
+
+inline bool& options::
+omit_drop ()
+{
+ return this->omit_drop_;
+}
+
+inline void options::
+omit_drop (const bool& x)
+{
+ this->omit_drop_ = x;
+}
+
+inline const bool& options::
+omit_create () const
+{
+ return this->omit_create_;
+}
+
+inline bool& options::
+omit_create ()
+{
+ return this->omit_create_;
+}
+
+inline void options::
+omit_create (const bool& x)
+{
+ this->omit_create_ = x;
+}
+
+inline const database_map<std::string>& options::
+schema_name () const
+{
+ return this->schema_name_;
+}
+
+inline database_map<std::string>& options::
+schema_name ()
+{
+ return this->schema_name_;
+}
+
+inline void options::
+schema_name (const database_map<std::string>& x)
+{
+ this->schema_name_ = x;
+}
+
+inline bool options::
+schema_name_specified () const
+{
+ return this->schema_name_specified_;
+}
+
+inline void options::
+schema_name_specified (bool x)
+{
+ this->schema_name_specified_ = x;
+}
+
+inline const database_map<deferrable>& options::
+fkeys_deferrable_mode () const
+{
+ return this->fkeys_deferrable_mode_;
+}
+
+inline database_map<deferrable>& options::
+fkeys_deferrable_mode ()
+{
+ return this->fkeys_deferrable_mode_;
+}
+
+inline void options::
+fkeys_deferrable_mode (const database_map<deferrable>& x)
+{
+ this->fkeys_deferrable_mode_ = x;
+}
+
+inline bool options::
+fkeys_deferrable_mode_specified () const
+{
+ return this->fkeys_deferrable_mode_specified_;
+}
+
+inline void options::
+fkeys_deferrable_mode_specified (bool x)
+{
+ this->fkeys_deferrable_mode_specified_ = x;
+}
+
+inline const std::string& options::
+default_pointer () const
+{
+ return this->default_pointer_;
+}
+
+inline std::string& options::
+default_pointer ()
+{
+ return this->default_pointer_;
+}
+
+inline void options::
+default_pointer (const std::string& x)
+{
+ this->default_pointer_ = x;
+}
+
+inline bool options::
+default_pointer_specified () const
+{
+ return this->default_pointer_specified_;
+}
+
+inline void options::
+default_pointer_specified (bool x)
+{
+ this->default_pointer_specified_ = x;
+}
+
+inline const std::string& options::
+session_type () const
+{
+ return this->session_type_;
+}
+
+inline std::string& options::
+session_type ()
+{
+ return this->session_type_;
+}
+
+inline void options::
+session_type (const std::string& x)
+{
+ this->session_type_ = x;
+}
+
+inline bool options::
+session_type_specified () const
+{
+ return this->session_type_specified_;
+}
+
+inline void options::
+session_type_specified (bool x)
+{
+ this->session_type_specified_ = x;
+}
+
+inline const std::string& options::
+profile () const
+{
+ return this->profile_;
+}
+
+inline std::string& options::
+profile ()
+{
+ return this->profile_;
+}
+
+inline void options::
+profile (const std::string& x)
+{
+ this->profile_ = x;
+}
+
+inline bool options::
+profile_specified () const
+{
+ return this->profile_specified_;
+}
+
+inline void options::
+profile_specified (bool x)
+{
+ this->profile_specified_ = x;
+}
+
+inline const bool& options::
+at_once () const
+{
+ return this->at_once_;
+}
+
+inline bool& options::
+at_once ()
+{
+ return this->at_once_;
+}
+
+inline void options::
+at_once (const bool& x)
+{
+ this->at_once_ = x;
+}
+
+inline const database_map<qname>& options::
+schema () const
+{
+ return this->schema_;
+}
+
+inline database_map<qname>& options::
+schema ()
+{
+ return this->schema_;
+}
+
+inline void options::
+schema (const database_map<qname>& x)
+{
+ this->schema_ = x;
+}
+
+inline bool options::
+schema_specified () const
+{
+ return this->schema_specified_;
+}
+
+inline void options::
+schema_specified (bool x)
+{
+ this->schema_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+export_symbol () const
+{
+ return this->export_symbol_;
+}
+
+inline database_map<std::string>& options::
+export_symbol ()
+{
+ return this->export_symbol_;
+}
+
+inline void options::
+export_symbol (const database_map<std::string>& x)
+{
+ this->export_symbol_ = x;
+}
+
+inline bool options::
+export_symbol_specified () const
+{
+ return this->export_symbol_specified_;
+}
+
+inline void options::
+export_symbol_specified (bool x)
+{
+ this->export_symbol_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+extern_symbol () const
+{
+ return this->extern_symbol_;
+}
+
+inline database_map<std::string>& options::
+extern_symbol ()
+{
+ return this->extern_symbol_;
+}
+
+inline void options::
+extern_symbol (const database_map<std::string>& x)
+{
+ this->extern_symbol_ = x;
+}
+
+inline bool options::
+extern_symbol_specified () const
+{
+ return this->extern_symbol_specified_;
+}
+
+inline void options::
+extern_symbol_specified (bool x)
+{
+ this->extern_symbol_specified_ = x;
+}
+
+inline const cxx_version& options::
+std () const
+{
+ return this->std_;
+}
+
+inline cxx_version& options::
+std ()
+{
+ return this->std_;
+}
+
+inline void options::
+std (const cxx_version& x)
+{
+ this->std_ = x;
+}
+
+inline bool options::
+std_specified () const
+{
+ return this->std_specified_;
+}
+
+inline void options::
+std_specified (bool x)
+{
+ this->std_specified_ = x;
+}
+
+inline const bool& options::
+warn_hard_add () const
+{
+ return this->warn_hard_add_;
+}
+
+inline bool& options::
+warn_hard_add ()
+{
+ return this->warn_hard_add_;
+}
+
+inline void options::
+warn_hard_add (const bool& x)
+{
+ this->warn_hard_add_ = x;
+}
+
+inline const bool& options::
+warn_hard_delete () const
+{
+ return this->warn_hard_delete_;
+}
+
+inline bool& options::
+warn_hard_delete ()
+{
+ return this->warn_hard_delete_;
+}
+
+inline void options::
+warn_hard_delete (const bool& x)
+{
+ this->warn_hard_delete_ = x;
+}
+
+inline const bool& options::
+warn_hard () const
+{
+ return this->warn_hard_;
+}
+
+inline bool& options::
+warn_hard ()
+{
+ return this->warn_hard_;
+}
+
+inline void options::
+warn_hard (const bool& x)
+{
+ this->warn_hard_ = x;
+}
+
+inline const std::string& options::
+output_dir () const
+{
+ return this->output_dir_;
+}
+
+inline std::string& options::
+output_dir ()
+{
+ return this->output_dir_;
+}
+
+inline void options::
+output_dir (const std::string& x)
+{
+ this->output_dir_ = x;
+}
+
+inline bool options::
+output_dir_specified () const
+{
+ return this->output_dir_specified_;
+}
+
+inline void options::
+output_dir_specified (bool x)
+{
+ this->output_dir_specified_ = x;
+}
+
+inline const std::string& options::
+input_name () const
+{
+ return this->input_name_;
+}
+
+inline std::string& options::
+input_name ()
+{
+ return this->input_name_;
+}
+
+inline void options::
+input_name (const std::string& x)
+{
+ this->input_name_ = x;
+}
+
+inline bool options::
+input_name_specified () const
+{
+ return this->input_name_specified_;
+}
+
+inline void options::
+input_name_specified (bool x)
+{
+ this->input_name_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+changelog () const
+{
+ return this->changelog_;
+}
+
+inline database_map<std::string>& options::
+changelog ()
+{
+ return this->changelog_;
+}
+
+inline void options::
+changelog (const database_map<std::string>& x)
+{
+ this->changelog_ = x;
+}
+
+inline bool options::
+changelog_specified () const
+{
+ return this->changelog_specified_;
+}
+
+inline void options::
+changelog_specified (bool x)
+{
+ this->changelog_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+changelog_in () const
+{
+ return this->changelog_in_;
+}
+
+inline database_map<std::string>& options::
+changelog_in ()
+{
+ return this->changelog_in_;
+}
+
+inline void options::
+changelog_in (const database_map<std::string>& x)
+{
+ this->changelog_in_ = x;
+}
+
+inline bool options::
+changelog_in_specified () const
+{
+ return this->changelog_in_specified_;
+}
+
+inline void options::
+changelog_in_specified (bool x)
+{
+ this->changelog_in_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+changelog_out () const
+{
+ return this->changelog_out_;
+}
+
+inline database_map<std::string>& options::
+changelog_out ()
+{
+ return this->changelog_out_;
+}
+
+inline void options::
+changelog_out (const database_map<std::string>& x)
+{
+ this->changelog_out_ = x;
+}
+
+inline bool options::
+changelog_out_specified () const
+{
+ return this->changelog_out_specified_;
+}
+
+inline void options::
+changelog_out_specified (bool x)
+{
+ this->changelog_out_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+changelog_dir () const
+{
+ return this->changelog_dir_;
+}
+
+inline database_map<std::string>& options::
+changelog_dir ()
+{
+ return this->changelog_dir_;
+}
+
+inline void options::
+changelog_dir (const database_map<std::string>& x)
+{
+ this->changelog_dir_ = x;
+}
+
+inline bool options::
+changelog_dir_specified () const
+{
+ return this->changelog_dir_specified_;
+}
+
+inline void options::
+changelog_dir_specified (bool x)
+{
+ this->changelog_dir_specified_ = x;
+}
+
+inline const bool& options::
+init_changelog () const
+{
+ return this->init_changelog_;
+}
+
+inline bool& options::
+init_changelog ()
+{
+ return this->init_changelog_;
+}
+
+inline void options::
+init_changelog (const bool& x)
+{
+ this->init_changelog_ = x;
+}
+
+inline const database_map<std::string>& options::
+odb_file_suffix () const
+{
+ return this->odb_file_suffix_;
+}
+
+inline database_map<std::string>& options::
+odb_file_suffix ()
+{
+ return this->odb_file_suffix_;
+}
+
+inline void options::
+odb_file_suffix (const database_map<std::string>& x)
+{
+ this->odb_file_suffix_ = x;
+}
+
+inline bool options::
+odb_file_suffix_specified () const
+{
+ return this->odb_file_suffix_specified_;
+}
+
+inline void options::
+odb_file_suffix_specified (bool x)
+{
+ this->odb_file_suffix_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+sql_file_suffix () const
+{
+ return this->sql_file_suffix_;
+}
+
+inline database_map<std::string>& options::
+sql_file_suffix ()
+{
+ return this->sql_file_suffix_;
+}
+
+inline void options::
+sql_file_suffix (const database_map<std::string>& x)
+{
+ this->sql_file_suffix_ = x;
+}
+
+inline bool options::
+sql_file_suffix_specified () const
+{
+ return this->sql_file_suffix_specified_;
+}
+
+inline void options::
+sql_file_suffix_specified (bool x)
+{
+ this->sql_file_suffix_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+schema_file_suffix () const
+{
+ return this->schema_file_suffix_;
+}
+
+inline database_map<std::string>& options::
+schema_file_suffix ()
+{
+ return this->schema_file_suffix_;
+}
+
+inline void options::
+schema_file_suffix (const database_map<std::string>& x)
+{
+ this->schema_file_suffix_ = x;
+}
+
+inline bool options::
+schema_file_suffix_specified () const
+{
+ return this->schema_file_suffix_specified_;
+}
+
+inline void options::
+schema_file_suffix_specified (bool x)
+{
+ this->schema_file_suffix_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+changelog_file_suffix () const
+{
+ return this->changelog_file_suffix_;
+}
+
+inline database_map<std::string>& options::
+changelog_file_suffix ()
+{
+ return this->changelog_file_suffix_;
+}
+
+inline void options::
+changelog_file_suffix (const database_map<std::string>& x)
+{
+ this->changelog_file_suffix_ = x;
+}
+
+inline bool options::
+changelog_file_suffix_specified () const
+{
+ return this->changelog_file_suffix_specified_;
+}
+
+inline void options::
+changelog_file_suffix_specified (bool x)
+{
+ this->changelog_file_suffix_specified_ = x;
+}
+
+inline const std::string& options::
+hxx_suffix () const
+{
+ return this->hxx_suffix_;
+}
+
+inline std::string& options::
+hxx_suffix ()
+{
+ return this->hxx_suffix_;
+}
+
+inline void options::
+hxx_suffix (const std::string& x)
+{
+ this->hxx_suffix_ = x;
+}
+
+inline bool options::
+hxx_suffix_specified () const
+{
+ return this->hxx_suffix_specified_;
+}
+
+inline void options::
+hxx_suffix_specified (bool x)
+{
+ this->hxx_suffix_specified_ = x;
+}
+
+inline const std::string& options::
+ixx_suffix () const
+{
+ return this->ixx_suffix_;
+}
+
+inline std::string& options::
+ixx_suffix ()
+{
+ return this->ixx_suffix_;
+}
+
+inline void options::
+ixx_suffix (const std::string& x)
+{
+ this->ixx_suffix_ = x;
+}
+
+inline bool options::
+ixx_suffix_specified () const
+{
+ return this->ixx_suffix_specified_;
+}
+
+inline void options::
+ixx_suffix_specified (bool x)
+{
+ this->ixx_suffix_specified_ = x;
+}
+
+inline const std::string& options::
+cxx_suffix () const
+{
+ return this->cxx_suffix_;
+}
+
+inline std::string& options::
+cxx_suffix ()
+{
+ return this->cxx_suffix_;
+}
+
+inline void options::
+cxx_suffix (const std::string& x)
+{
+ this->cxx_suffix_ = x;
+}
+
+inline bool options::
+cxx_suffix_specified () const
+{
+ return this->cxx_suffix_specified_;
+}
+
+inline void options::
+cxx_suffix_specified (bool x)
+{
+ this->cxx_suffix_specified_ = x;
+}
+
+inline const std::string& options::
+sql_suffix () const
+{
+ return this->sql_suffix_;
+}
+
+inline std::string& options::
+sql_suffix ()
+{
+ return this->sql_suffix_;
+}
+
+inline void options::
+sql_suffix (const std::string& x)
+{
+ this->sql_suffix_ = x;
+}
+
+inline bool options::
+sql_suffix_specified () const
+{
+ return this->sql_suffix_specified_;
+}
+
+inline void options::
+sql_suffix_specified (bool x)
+{
+ this->sql_suffix_specified_ = x;
+}
+
+inline const std::string& options::
+changelog_suffix () const
+{
+ return this->changelog_suffix_;
+}
+
+inline std::string& options::
+changelog_suffix ()
+{
+ return this->changelog_suffix_;
+}
+
+inline void options::
+changelog_suffix (const std::string& x)
+{
+ this->changelog_suffix_ = x;
+}
+
+inline bool options::
+changelog_suffix_specified () const
+{
+ return this->changelog_suffix_specified_;
+}
+
+inline void options::
+changelog_suffix_specified (bool x)
+{
+ this->changelog_suffix_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+hxx_prologue () const
+{
+ return this->hxx_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+hxx_prologue ()
+{
+ return this->hxx_prologue_;
+}
+
+inline void options::
+hxx_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->hxx_prologue_ = x;
+}
+
+inline bool options::
+hxx_prologue_specified () const
+{
+ return this->hxx_prologue_specified_;
+}
+
+inline void options::
+hxx_prologue_specified (bool x)
+{
+ this->hxx_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+ixx_prologue () const
+{
+ return this->ixx_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+ixx_prologue ()
+{
+ return this->ixx_prologue_;
+}
+
+inline void options::
+ixx_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->ixx_prologue_ = x;
+}
+
+inline bool options::
+ixx_prologue_specified () const
+{
+ return this->ixx_prologue_specified_;
+}
+
+inline void options::
+ixx_prologue_specified (bool x)
+{
+ this->ixx_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+cxx_prologue () const
+{
+ return this->cxx_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+cxx_prologue ()
+{
+ return this->cxx_prologue_;
+}
+
+inline void options::
+cxx_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->cxx_prologue_ = x;
+}
+
+inline bool options::
+cxx_prologue_specified () const
+{
+ return this->cxx_prologue_specified_;
+}
+
+inline void options::
+cxx_prologue_specified (bool x)
+{
+ this->cxx_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+schema_prologue () const
+{
+ return this->schema_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+schema_prologue ()
+{
+ return this->schema_prologue_;
+}
+
+inline void options::
+schema_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->schema_prologue_ = x;
+}
+
+inline bool options::
+schema_prologue_specified () const
+{
+ return this->schema_prologue_specified_;
+}
+
+inline void options::
+schema_prologue_specified (bool x)
+{
+ this->schema_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_prologue () const
+{
+ return this->sql_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_prologue ()
+{
+ return this->sql_prologue_;
+}
+
+inline void options::
+sql_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_prologue_ = x;
+}
+
+inline bool options::
+sql_prologue_specified () const
+{
+ return this->sql_prologue_specified_;
+}
+
+inline void options::
+sql_prologue_specified (bool x)
+{
+ this->sql_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+migration_prologue () const
+{
+ return this->migration_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+migration_prologue ()
+{
+ return this->migration_prologue_;
+}
+
+inline void options::
+migration_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->migration_prologue_ = x;
+}
+
+inline bool options::
+migration_prologue_specified () const
+{
+ return this->migration_prologue_specified_;
+}
+
+inline void options::
+migration_prologue_specified (bool x)
+{
+ this->migration_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_interlude () const
+{
+ return this->sql_interlude_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_interlude ()
+{
+ return this->sql_interlude_;
+}
+
+inline void options::
+sql_interlude (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_interlude_ = x;
+}
+
+inline bool options::
+sql_interlude_specified () const
+{
+ return this->sql_interlude_specified_;
+}
+
+inline void options::
+sql_interlude_specified (bool x)
+{
+ this->sql_interlude_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+hxx_epilogue () const
+{
+ return this->hxx_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+hxx_epilogue ()
+{
+ return this->hxx_epilogue_;
+}
+
+inline void options::
+hxx_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->hxx_epilogue_ = x;
+}
+
+inline bool options::
+hxx_epilogue_specified () const
+{
+ return this->hxx_epilogue_specified_;
+}
+
+inline void options::
+hxx_epilogue_specified (bool x)
+{
+ this->hxx_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+ixx_epilogue () const
+{
+ return this->ixx_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+ixx_epilogue ()
+{
+ return this->ixx_epilogue_;
+}
+
+inline void options::
+ixx_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->ixx_epilogue_ = x;
+}
+
+inline bool options::
+ixx_epilogue_specified () const
+{
+ return this->ixx_epilogue_specified_;
+}
+
+inline void options::
+ixx_epilogue_specified (bool x)
+{
+ this->ixx_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+cxx_epilogue () const
+{
+ return this->cxx_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+cxx_epilogue ()
+{
+ return this->cxx_epilogue_;
+}
+
+inline void options::
+cxx_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->cxx_epilogue_ = x;
+}
+
+inline bool options::
+cxx_epilogue_specified () const
+{
+ return this->cxx_epilogue_specified_;
+}
+
+inline void options::
+cxx_epilogue_specified (bool x)
+{
+ this->cxx_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+schema_epilogue () const
+{
+ return this->schema_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+schema_epilogue ()
+{
+ return this->schema_epilogue_;
+}
+
+inline void options::
+schema_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->schema_epilogue_ = x;
+}
+
+inline bool options::
+schema_epilogue_specified () const
+{
+ return this->schema_epilogue_specified_;
+}
+
+inline void options::
+schema_epilogue_specified (bool x)
+{
+ this->schema_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_epilogue () const
+{
+ return this->sql_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_epilogue ()
+{
+ return this->sql_epilogue_;
+}
+
+inline void options::
+sql_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_epilogue_ = x;
+}
+
+inline bool options::
+sql_epilogue_specified () const
+{
+ return this->sql_epilogue_specified_;
+}
+
+inline void options::
+sql_epilogue_specified (bool x)
+{
+ this->sql_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+migration_epilogue () const
+{
+ return this->migration_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+migration_epilogue ()
+{
+ return this->migration_epilogue_;
+}
+
+inline void options::
+migration_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->migration_epilogue_ = x;
+}
+
+inline bool options::
+migration_epilogue_specified () const
+{
+ return this->migration_epilogue_specified_;
+}
+
+inline void options::
+migration_epilogue_specified (bool x)
+{
+ this->migration_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+hxx_prologue_file () const
+{
+ return this->hxx_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+hxx_prologue_file ()
+{
+ return this->hxx_prologue_file_;
+}
+
+inline void options::
+hxx_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->hxx_prologue_file_ = x;
+}
+
+inline bool options::
+hxx_prologue_file_specified () const
+{
+ return this->hxx_prologue_file_specified_;
+}
+
+inline void options::
+hxx_prologue_file_specified (bool x)
+{
+ this->hxx_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+ixx_prologue_file () const
+{
+ return this->ixx_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+ixx_prologue_file ()
+{
+ return this->ixx_prologue_file_;
+}
+
+inline void options::
+ixx_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->ixx_prologue_file_ = x;
+}
+
+inline bool options::
+ixx_prologue_file_specified () const
+{
+ return this->ixx_prologue_file_specified_;
+}
+
+inline void options::
+ixx_prologue_file_specified (bool x)
+{
+ this->ixx_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+cxx_prologue_file () const
+{
+ return this->cxx_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+cxx_prologue_file ()
+{
+ return this->cxx_prologue_file_;
+}
+
+inline void options::
+cxx_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->cxx_prologue_file_ = x;
+}
+
+inline bool options::
+cxx_prologue_file_specified () const
+{
+ return this->cxx_prologue_file_specified_;
+}
+
+inline void options::
+cxx_prologue_file_specified (bool x)
+{
+ this->cxx_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+schema_prologue_file () const
+{
+ return this->schema_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+schema_prologue_file ()
+{
+ return this->schema_prologue_file_;
+}
+
+inline void options::
+schema_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->schema_prologue_file_ = x;
+}
+
+inline bool options::
+schema_prologue_file_specified () const
+{
+ return this->schema_prologue_file_specified_;
+}
+
+inline void options::
+schema_prologue_file_specified (bool x)
+{
+ this->schema_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_prologue_file () const
+{
+ return this->sql_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_prologue_file ()
+{
+ return this->sql_prologue_file_;
+}
+
+inline void options::
+sql_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_prologue_file_ = x;
+}
+
+inline bool options::
+sql_prologue_file_specified () const
+{
+ return this->sql_prologue_file_specified_;
+}
+
+inline void options::
+sql_prologue_file_specified (bool x)
+{
+ this->sql_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+migration_prologue_file () const
+{
+ return this->migration_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+migration_prologue_file ()
+{
+ return this->migration_prologue_file_;
+}
+
+inline void options::
+migration_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->migration_prologue_file_ = x;
+}
+
+inline bool options::
+migration_prologue_file_specified () const
+{
+ return this->migration_prologue_file_specified_;
+}
+
+inline void options::
+migration_prologue_file_specified (bool x)
+{
+ this->migration_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_interlude_file () const
+{
+ return this->sql_interlude_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_interlude_file ()
+{
+ return this->sql_interlude_file_;
+}
+
+inline void options::
+sql_interlude_file (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_interlude_file_ = x;
+}
+
+inline bool options::
+sql_interlude_file_specified () const
+{
+ return this->sql_interlude_file_specified_;
+}
+
+inline void options::
+sql_interlude_file_specified (bool x)
+{
+ this->sql_interlude_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+hxx_epilogue_file () const
+{
+ return this->hxx_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+hxx_epilogue_file ()
+{
+ return this->hxx_epilogue_file_;
+}
+
+inline void options::
+hxx_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->hxx_epilogue_file_ = x;
+}
+
+inline bool options::
+hxx_epilogue_file_specified () const
+{
+ return this->hxx_epilogue_file_specified_;
+}
+
+inline void options::
+hxx_epilogue_file_specified (bool x)
+{
+ this->hxx_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+ixx_epilogue_file () const
+{
+ return this->ixx_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+ixx_epilogue_file ()
+{
+ return this->ixx_epilogue_file_;
+}
+
+inline void options::
+ixx_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->ixx_epilogue_file_ = x;
+}
+
+inline bool options::
+ixx_epilogue_file_specified () const
+{
+ return this->ixx_epilogue_file_specified_;
+}
+
+inline void options::
+ixx_epilogue_file_specified (bool x)
+{
+ this->ixx_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+cxx_epilogue_file () const
+{
+ return this->cxx_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+cxx_epilogue_file ()
+{
+ return this->cxx_epilogue_file_;
+}
+
+inline void options::
+cxx_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->cxx_epilogue_file_ = x;
+}
+
+inline bool options::
+cxx_epilogue_file_specified () const
+{
+ return this->cxx_epilogue_file_specified_;
+}
+
+inline void options::
+cxx_epilogue_file_specified (bool x)
+{
+ this->cxx_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+schema_epilogue_file () const
+{
+ return this->schema_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+schema_epilogue_file ()
+{
+ return this->schema_epilogue_file_;
+}
+
+inline void options::
+schema_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->schema_epilogue_file_ = x;
+}
+
+inline bool options::
+schema_epilogue_file_specified () const
+{
+ return this->schema_epilogue_file_specified_;
+}
+
+inline void options::
+schema_epilogue_file_specified (bool x)
+{
+ this->schema_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_epilogue_file () const
+{
+ return this->sql_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_epilogue_file ()
+{
+ return this->sql_epilogue_file_;
+}
+
+inline void options::
+sql_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_epilogue_file_ = x;
+}
+
+inline bool options::
+sql_epilogue_file_specified () const
+{
+ return this->sql_epilogue_file_specified_;
+}
+
+inline void options::
+sql_epilogue_file_specified (bool x)
+{
+ this->sql_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+migration_epilogue_file () const
+{
+ return this->migration_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+migration_epilogue_file ()
+{
+ return this->migration_epilogue_file_;
+}
+
+inline void options::
+migration_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->migration_epilogue_file_ = x;
+}
+
+inline bool options::
+migration_epilogue_file_specified () const
+{
+ return this->migration_epilogue_file_specified_;
+}
+
+inline void options::
+migration_epilogue_file_specified (bool x)
+{
+ this->migration_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+odb_prologue () const
+{
+ return this->odb_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+odb_prologue ()
+{
+ return this->odb_prologue_;
+}
+
+inline void options::
+odb_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->odb_prologue_ = x;
+}
+
+inline bool options::
+odb_prologue_specified () const
+{
+ return this->odb_prologue_specified_;
+}
+
+inline void options::
+odb_prologue_specified (bool x)
+{
+ this->odb_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+odb_prologue_file () const
+{
+ return this->odb_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+odb_prologue_file ()
+{
+ return this->odb_prologue_file_;
+}
+
+inline void options::
+odb_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->odb_prologue_file_ = x;
+}
+
+inline bool options::
+odb_prologue_file_specified () const
+{
+ return this->odb_prologue_file_specified_;
+}
+
+inline void options::
+odb_prologue_file_specified (bool x)
+{
+ this->odb_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+odb_epilogue () const
+{
+ return this->odb_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+odb_epilogue ()
+{
+ return this->odb_epilogue_;
+}
+
+inline void options::
+odb_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->odb_epilogue_ = x;
+}
+
+inline bool options::
+odb_epilogue_specified () const
+{
+ return this->odb_epilogue_specified_;
+}
+
+inline void options::
+odb_epilogue_specified (bool x)
+{
+ this->odb_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+odb_epilogue_file () const
+{
+ return this->odb_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+odb_epilogue_file ()
+{
+ return this->odb_epilogue_file_;
+}
+
+inline void options::
+odb_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->odb_epilogue_file_ = x;
+}
+
+inline bool options::
+odb_epilogue_file_specified () const
+{
+ return this->odb_epilogue_file_specified_;
+}
+
+inline void options::
+odb_epilogue_file_specified (bool x)
+{
+ this->odb_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+table_prefix () const
+{
+ return this->table_prefix_;
+}
+
+inline database_map<std::string>& options::
+table_prefix ()
+{
+ return this->table_prefix_;
+}
+
+inline void options::
+table_prefix (const database_map<std::string>& x)
+{
+ this->table_prefix_ = x;
+}
+
+inline bool options::
+table_prefix_specified () const
+{
+ return this->table_prefix_specified_;
+}
+
+inline void options::
+table_prefix_specified (bool x)
+{
+ this->table_prefix_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+index_suffix () const
+{
+ return this->index_suffix_;
+}
+
+inline database_map<std::string>& options::
+index_suffix ()
+{
+ return this->index_suffix_;
+}
+
+inline void options::
+index_suffix (const database_map<std::string>& x)
+{
+ this->index_suffix_ = x;
+}
+
+inline bool options::
+index_suffix_specified () const
+{
+ return this->index_suffix_specified_;
+}
+
+inline void options::
+index_suffix_specified (bool x)
+{
+ this->index_suffix_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+fkey_suffix () const
+{
+ return this->fkey_suffix_;
+}
+
+inline database_map<std::string>& options::
+fkey_suffix ()
+{
+ return this->fkey_suffix_;
+}
+
+inline void options::
+fkey_suffix (const database_map<std::string>& x)
+{
+ this->fkey_suffix_ = x;
+}
+
+inline bool options::
+fkey_suffix_specified () const
+{
+ return this->fkey_suffix_specified_;
+}
+
+inline void options::
+fkey_suffix_specified (bool x)
+{
+ this->fkey_suffix_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+sequence_suffix () const
+{
+ return this->sequence_suffix_;
+}
+
+inline database_map<std::string>& options::
+sequence_suffix ()
+{
+ return this->sequence_suffix_;
+}
+
+inline void options::
+sequence_suffix (const database_map<std::string>& x)
+{
+ this->sequence_suffix_ = x;
+}
+
+inline bool options::
+sequence_suffix_specified () const
+{
+ return this->sequence_suffix_specified_;
+}
+
+inline void options::
+sequence_suffix_specified (bool x)
+{
+ this->sequence_suffix_specified_ = x;
+}
+
+inline const database_map<name_case>& options::
+sql_name_case () const
+{
+ return this->sql_name_case_;
+}
+
+inline database_map<name_case>& options::
+sql_name_case ()
+{
+ return this->sql_name_case_;
+}
+
+inline void options::
+sql_name_case (const database_map<name_case>& x)
+{
+ this->sql_name_case_ = x;
+}
+
+inline bool options::
+sql_name_case_specified () const
+{
+ return this->sql_name_case_specified_;
+}
+
+inline void options::
+sql_name_case_specified (bool x)
+{
+ this->sql_name_case_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+table_regex () const
+{
+ return this->table_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+table_regex ()
+{
+ return this->table_regex_;
+}
+
+inline void options::
+table_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->table_regex_ = x;
+}
+
+inline bool options::
+table_regex_specified () const
+{
+ return this->table_regex_specified_;
+}
+
+inline void options::
+table_regex_specified (bool x)
+{
+ this->table_regex_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+column_regex () const
+{
+ return this->column_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+column_regex ()
+{
+ return this->column_regex_;
+}
+
+inline void options::
+column_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->column_regex_ = x;
+}
+
+inline bool options::
+column_regex_specified () const
+{
+ return this->column_regex_specified_;
+}
+
+inline void options::
+column_regex_specified (bool x)
+{
+ this->column_regex_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+index_regex () const
+{
+ return this->index_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+index_regex ()
+{
+ return this->index_regex_;
+}
+
+inline void options::
+index_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->index_regex_ = x;
+}
+
+inline bool options::
+index_regex_specified () const
+{
+ return this->index_regex_specified_;
+}
+
+inline void options::
+index_regex_specified (bool x)
+{
+ this->index_regex_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+fkey_regex () const
+{
+ return this->fkey_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+fkey_regex ()
+{
+ return this->fkey_regex_;
+}
+
+inline void options::
+fkey_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->fkey_regex_ = x;
+}
+
+inline bool options::
+fkey_regex_specified () const
+{
+ return this->fkey_regex_specified_;
+}
+
+inline void options::
+fkey_regex_specified (bool x)
+{
+ this->fkey_regex_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sequence_regex () const
+{
+ return this->sequence_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sequence_regex ()
+{
+ return this->sequence_regex_;
+}
+
+inline void options::
+sequence_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->sequence_regex_ = x;
+}
+
+inline bool options::
+sequence_regex_specified () const
+{
+ return this->sequence_regex_specified_;
+}
+
+inline void options::
+sequence_regex_specified (bool x)
+{
+ this->sequence_regex_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+statement_regex () const
+{
+ return this->statement_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+statement_regex ()
+{
+ return this->statement_regex_;
+}
+
+inline void options::
+statement_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->statement_regex_ = x;
+}
+
+inline bool options::
+statement_regex_specified () const
+{
+ return this->statement_regex_specified_;
+}
+
+inline void options::
+statement_regex_specified (bool x)
+{
+ this->statement_regex_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_name_regex () const
+{
+ return this->sql_name_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_name_regex ()
+{
+ return this->sql_name_regex_;
+}
+
+inline void options::
+sql_name_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_name_regex_ = x;
+}
+
+inline bool options::
+sql_name_regex_specified () const
+{
+ return this->sql_name_regex_specified_;
+}
+
+inline void options::
+sql_name_regex_specified (bool x)
+{
+ this->sql_name_regex_specified_ = x;
+}
+
+inline const bool& options::
+sql_name_regex_trace () const
+{
+ return this->sql_name_regex_trace_;
+}
+
+inline bool& options::
+sql_name_regex_trace ()
+{
+ return this->sql_name_regex_trace_;
+}
+
+inline void options::
+sql_name_regex_trace (const bool& x)
+{
+ this->sql_name_regex_trace_ = x;
+}
+
+inline const std::vector<std::string>& options::
+accessor_regex () const
+{
+ return this->accessor_regex_;
+}
+
+inline std::vector<std::string>& options::
+accessor_regex ()
+{
+ return this->accessor_regex_;
+}
+
+inline void options::
+accessor_regex (const std::vector<std::string>& x)
+{
+ this->accessor_regex_ = x;
+}
+
+inline bool options::
+accessor_regex_specified () const
+{
+ return this->accessor_regex_specified_;
+}
+
+inline void options::
+accessor_regex_specified (bool x)
+{
+ this->accessor_regex_specified_ = x;
+}
+
+inline const bool& options::
+accessor_regex_trace () const
+{
+ return this->accessor_regex_trace_;
+}
+
+inline bool& options::
+accessor_regex_trace ()
+{
+ return this->accessor_regex_trace_;
+}
+
+inline void options::
+accessor_regex_trace (const bool& x)
+{
+ this->accessor_regex_trace_ = x;
+}
+
+inline const std::vector<std::string>& options::
+modifier_regex () const
+{
+ return this->modifier_regex_;
+}
+
+inline std::vector<std::string>& options::
+modifier_regex ()
+{
+ return this->modifier_regex_;
+}
+
+inline void options::
+modifier_regex (const std::vector<std::string>& x)
+{
+ this->modifier_regex_ = x;
+}
+
+inline bool options::
+modifier_regex_specified () const
+{
+ return this->modifier_regex_specified_;
+}
+
+inline void options::
+modifier_regex_specified (bool x)
+{
+ this->modifier_regex_specified_ = x;
+}
+
+inline const bool& options::
+modifier_regex_trace () const
+{
+ return this->modifier_regex_trace_;
+}
+
+inline bool& options::
+modifier_regex_trace ()
+{
+ return this->modifier_regex_trace_;
+}
+
+inline void options::
+modifier_regex_trace (const bool& x)
+{
+ this->modifier_regex_trace_ = x;
+}
+
+inline const bool& options::
+include_with_brackets () const
+{
+ return this->include_with_brackets_;
+}
+
+inline bool& options::
+include_with_brackets ()
+{
+ return this->include_with_brackets_;
+}
+
+inline void options::
+include_with_brackets (const bool& x)
+{
+ this->include_with_brackets_ = x;
+}
+
+inline const std::string& options::
+include_prefix () const
+{
+ return this->include_prefix_;
+}
+
+inline std::string& options::
+include_prefix ()
+{
+ return this->include_prefix_;
+}
+
+inline void options::
+include_prefix (const std::string& x)
+{
+ this->include_prefix_ = x;
+}
+
+inline bool options::
+include_prefix_specified () const
+{
+ return this->include_prefix_specified_;
+}
+
+inline void options::
+include_prefix_specified (bool x)
+{
+ this->include_prefix_specified_ = x;
+}
+
+inline const std::vector<std::string>& options::
+include_regex () const
+{
+ return this->include_regex_;
+}
+
+inline std::vector<std::string>& options::
+include_regex ()
+{
+ return this->include_regex_;
+}
+
+inline void options::
+include_regex (const std::vector<std::string>& x)
+{
+ this->include_regex_ = x;
+}
+
+inline bool options::
+include_regex_specified () const
+{
+ return this->include_regex_specified_;
+}
+
+inline void options::
+include_regex_specified (bool x)
+{
+ this->include_regex_specified_ = x;
+}
+
+inline const bool& options::
+include_regex_trace () const
+{
+ return this->include_regex_trace_;
+}
+
+inline bool& options::
+include_regex_trace ()
+{
+ return this->include_regex_trace_;
+}
+
+inline void options::
+include_regex_trace (const bool& x)
+{
+ this->include_regex_trace_ = x;
+}
+
+inline const std::string& options::
+guard_prefix () const
+{
+ return this->guard_prefix_;
+}
+
+inline std::string& options::
+guard_prefix ()
+{
+ return this->guard_prefix_;
+}
+
+inline void options::
+guard_prefix (const std::string& x)
+{
+ this->guard_prefix_ = x;
+}
+
+inline bool options::
+guard_prefix_specified () const
+{
+ return this->guard_prefix_specified_;
+}
+
+inline void options::
+guard_prefix_specified (bool x)
+{
+ this->guard_prefix_specified_ = x;
+}
+
+inline const bool& options::
+show_sloc () const
+{
+ return this->show_sloc_;
+}
+
+inline bool& options::
+show_sloc ()
+{
+ return this->show_sloc_;
+}
+
+inline void options::
+show_sloc (const bool& x)
+{
+ this->show_sloc_ = x;
+}
+
+inline const std::size_t& options::
+sloc_limit () const
+{
+ return this->sloc_limit_;
+}
+
+inline std::size_t& options::
+sloc_limit ()
+{
+ return this->sloc_limit_;
+}
+
+inline void options::
+sloc_limit (const std::size_t& x)
+{
+ this->sloc_limit_ = x;
+}
+
+inline bool options::
+sloc_limit_specified () const
+{
+ return this->sloc_limit_specified_;
+}
+
+inline void options::
+sloc_limit_specified (bool x)
+{
+ this->sloc_limit_specified_ = x;
+}
+
+inline const std::string& options::
+options_file () const
+{
+ return this->options_file_;
+}
+
+inline std::string& options::
+options_file ()
+{
+ return this->options_file_;
+}
+
+inline void options::
+options_file (const std::string& x)
+{
+ this->options_file_ = x;
+}
+
+inline bool options::
+options_file_specified () const
+{
+ return this->options_file_specified_;
+}
+
+inline void options::
+options_file_specified (bool x)
+{
+ this->options_file_specified_ = x;
+}
+
+inline const std::vector<std::string>& options::
+x () const
+{
+ return this->x_;
+}
+
+inline std::vector<std::string>& options::
+x ()
+{
+ return this->x_;
+}
+
+inline void options::
+x (const std::vector<std::string>& x)
+{
+ this->x_ = x;
+}
+
+inline bool options::
+x_specified () const
+{
+ return this->x_specified_;
+}
+
+inline void options::
+x_specified (bool x)
+{
+ this->x_specified_ = x;
+}
+
+inline const bool& options::
+v () const
+{
+ return this->v_;
+}
+
+inline bool& options::
+v ()
+{
+ return this->v_;
+}
+
+inline void options::
+v (const bool& x)
+{
+ this->v_ = x;
+}
+
+inline const bool& options::
+trace () const
+{
+ return this->trace_;
+}
+
+inline bool& options::
+trace ()
+{
+ return this->trace_;
+}
+
+inline void options::
+trace (const bool& x)
+{
+ this->trace_ = x;
+}
+
+inline const std::string& options::
+mysql_engine () const
+{
+ return this->mysql_engine_;
+}
+
+inline std::string& options::
+mysql_engine ()
+{
+ return this->mysql_engine_;
+}
+
+inline void options::
+mysql_engine (const std::string& x)
+{
+ this->mysql_engine_ = x;
+}
+
+inline bool options::
+mysql_engine_specified () const
+{
+ return this->mysql_engine_specified_;
+}
+
+inline void options::
+mysql_engine_specified (bool x)
+{
+ this->mysql_engine_specified_ = x;
+}
+
+inline const bool& options::
+sqlite_override_null () const
+{
+ return this->sqlite_override_null_;
+}
+
+inline bool& options::
+sqlite_override_null ()
+{
+ return this->sqlite_override_null_;
+}
+
+inline void options::
+sqlite_override_null (const bool& x)
+{
+ this->sqlite_override_null_ = x;
+}
+
+inline const bool& options::
+sqlite_lax_auto_id () const
+{
+ return this->sqlite_lax_auto_id_;
+}
+
+inline bool& options::
+sqlite_lax_auto_id ()
+{
+ return this->sqlite_lax_auto_id_;
+}
+
+inline void options::
+sqlite_lax_auto_id (const bool& x)
+{
+ this->sqlite_lax_auto_id_ = x;
+}
+
+inline const ::pgsql_version& options::
+pgsql_server_version () const
+{
+ return this->pgsql_server_version_;
+}
+
+inline ::pgsql_version& options::
+pgsql_server_version ()
+{
+ return this->pgsql_server_version_;
+}
+
+inline void options::
+pgsql_server_version (const ::pgsql_version& x)
+{
+ this->pgsql_server_version_ = x;
+}
+
+inline bool options::
+pgsql_server_version_specified () const
+{
+ return this->pgsql_server_version_specified_;
+}
+
+inline void options::
+pgsql_server_version_specified (bool x)
+{
+ this->pgsql_server_version_specified_ = x;
+}
+
+inline const ::oracle_version& options::
+oracle_client_version () const
+{
+ return this->oracle_client_version_;
+}
+
+inline ::oracle_version& options::
+oracle_client_version ()
+{
+ return this->oracle_client_version_;
+}
+
+inline void options::
+oracle_client_version (const ::oracle_version& x)
+{
+ this->oracle_client_version_ = x;
+}
+
+inline bool options::
+oracle_client_version_specified () const
+{
+ return this->oracle_client_version_specified_;
+}
+
+inline void options::
+oracle_client_version_specified (bool x)
+{
+ this->oracle_client_version_specified_ = x;
+}
+
+inline const bool& options::
+oracle_warn_truncation () const
+{
+ return this->oracle_warn_truncation_;
+}
+
+inline bool& options::
+oracle_warn_truncation ()
+{
+ return this->oracle_warn_truncation_;
+}
+
+inline void options::
+oracle_warn_truncation (const bool& x)
+{
+ this->oracle_warn_truncation_ = x;
+}
+
+inline const ::mssql_version& options::
+mssql_server_version () const
+{
+ return this->mssql_server_version_;
+}
+
+inline ::mssql_version& options::
+mssql_server_version ()
+{
+ return this->mssql_server_version_;
+}
+
+inline void options::
+mssql_server_version (const ::mssql_version& x)
+{
+ this->mssql_server_version_ = x;
+}
+
+inline bool options::
+mssql_server_version_specified () const
+{
+ return this->mssql_server_version_specified_;
+}
+
+inline void options::
+mssql_server_version_specified (bool x)
+{
+ this->mssql_server_version_specified_ = x;
+}
+
+inline const unsigned int& options::
+mssql_short_limit () const
+{
+ return this->mssql_short_limit_;
+}
+
+inline unsigned int& options::
+mssql_short_limit ()
+{
+ return this->mssql_short_limit_;
+}
+
+inline void options::
+mssql_short_limit (const unsigned int& x)
+{
+ this->mssql_short_limit_ = x;
+}
+
+inline bool options::
+mssql_short_limit_specified () const
+{
+ return this->mssql_short_limit_specified_;
+}
+
+inline void options::
+mssql_short_limit_specified (bool x)
+{
+ this->mssql_short_limit_specified_ = x;
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
diff --git a/odb/odb/processor.cxx b/odb/odb/processor.cxx
new file mode 100644
index 0000000..fb129fa
--- /dev/null
+++ b/odb/odb/processor.cxx
@@ -0,0 +1,3267 @@
+// file : odb/processor.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <iostream>
+#include <algorithm> // std::find
+
+#include <odb/common.hxx>
+#include <odb/lookup.hxx>
+#include <odb/context.hxx>
+#include <odb/cxx-lexer.hxx>
+#include <odb/processor.hxx>
+#include <odb/diagnostics.hxx>
+
+#include <odb/relational/processor.hxx>
+
+using namespace std;
+
+namespace
+{
+ // Find name hint for this type decl.
+ //
+ static semantics::names*
+ find_hint (semantics::unit& u, tree decl)
+ {
+ semantics::names* r (0);
+
+ for (tree ot (DECL_ORIGINAL_TYPE (decl));
+ ot != 0;
+ ot = decl ? DECL_ORIGINAL_TYPE (decl) : 0)
+ {
+ if ((r = u.find_hint (ot)))
+ break;
+
+ decl = TYPE_NAME (ot);
+ }
+
+ return r;
+ }
+
+ // Indirect (dynamic) context values.
+ //
+ static semantics::type*
+ id_tree_type (semantics::names*& hint)
+ {
+ context& c (context::current ());
+ data_member_path& id (*context::id_member (*c.top_object));
+ return &c.utype (id, hint);
+ }
+
+ struct data_member: traversal::data_member, context
+ {
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ if (transient (m))
+ return;
+
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ // See if this member is a section.
+ //
+ if (t.fq_name () == "::odb::section")
+ {
+ using semantics::class_;
+
+ class_& c (dynamic_cast<class_&> (m.scope ()));
+ class_* poly_root (polymorphic (c));
+ semantics::data_member* opt (optimistic (c));
+
+ // If we have sections in a polymorphic optimistic hierarchy,
+ // then the version member should be in the root.
+ //
+ if (poly_root == &c && opt != 0 && &opt->scope () != &c)
+ {
+ error (m.location ()) << "version must be a direct data member " <<
+ "of a class that contains sections" << endl;
+ info (opt->location ()) << "version member is declared here" << endl;
+ throw operation_failed ();
+ }
+
+ process_user_section (m, c);
+
+ // We don't need a modifier but the accessor should be by-reference.
+ //
+ process_access (m, "get");
+
+ member_access& ma (m.get<member_access> ("get"));
+ if (ma.by_value)
+ {
+ error (ma.loc) << "accessor returning a value cannot be used "
+ << "for a section" << endl;
+ info (ma.loc) << "accessor returning a const reference is required"
+ << endl;
+ info (m.location ()) << "data member is defined here" << endl;
+ throw operation_failed ();
+ }
+
+ // Mark this member as transient since we don't store it in the
+ // database.
+ //
+ m.set ("transient", true);
+
+ features.section = true;
+ return;
+ }
+ else
+ {
+ process_access (m, "get");
+ process_access (m, "set");
+ }
+
+ // See if this member belongs to a section.
+ //
+ if (m.count ("section-member") != 0)
+ process_section_member (m);
+
+ // We don't need to do any further processing for common if we
+ // are generating static multi-database code.
+ //
+ if (multi_static && options.database ()[0] == database::common)
+ return;
+
+ // Handle wrappers.
+ //
+ semantics::type* wt (0), *qwt (0);
+ semantics::names* whint (0);
+ if (process_wrapper (t))
+ {
+ qwt = t.get<semantics::type*> ("wrapper-type");
+ whint = t.get<semantics::names*> ("wrapper-hint");
+ wt = &utype (*qwt, whint);
+ }
+
+ // If the type is const and the member is not id, version, or
+ // inverse, then mark it as readonly. In case of a wrapper,
+ // both the wrapper type and the wrapped type must be const.
+ // To see why, consider these possibilities:
+ //
+ // unique_ptr<const T> - can modify by setting a new pointer
+ // const unique_ptr<T> - can modify by changing the pointed-to value
+ //
+ if (const_member (m) && !(id (m) || version (m) || m.count ("inverse")))
+ {
+ if (qwt == 0 || const_type (*qwt))
+ m.set ("readonly", true);
+ }
+
+ process_points_to (m);
+
+ if (composite_wrapper (t))
+ return;
+
+ // Process object pointer. The resulting column will be a simple
+ // or composite value.
+ //
+ if (process_object_pointer (m, t))
+ return;
+
+ // Before checking if this is a container, check if this member
+ // or its type were deduced to be a simple value based on the
+ // pragmas. This is necessary because a container member (e.g.,
+ // vector<char>) can be "overridden" into a simple value (e.g.,
+ // BLOB) with a pragma.
+ //
+ if (m.count ("simple") ||
+ t.count ("simple") ||
+ (wt != 0 && wt->count ("simple")))
+ return;
+
+ process_container (m, (wt != 0 ? *wt : t));
+ }
+
+ //
+ // Process member access expressions.
+ //
+
+ enum found_type
+ {
+ found_none,
+ found_some, // Found something but keep looking for a better one.
+ found_best
+ };
+
+ // Check if a function is a suitable accessor for this member.
+ //
+ found_type
+ check_accessor (semantics::data_member& m,
+ tree f,
+ string const& n,
+ member_access& ma,
+ bool strict)
+ {
+ // Must be const.
+ //
+ if (!DECL_CONST_MEMFUNC_P (f))
+ return found_none;
+
+ // Accessor is a function with no arguments (other than 'this').
+ //
+ if (FUNCTION_FIRST_USER_PARMTYPE (f) != void_list_node)
+ return found_none;
+
+ // Note that to get the return type we have to use
+ // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as
+ // suggested in the documentation.
+ //
+ tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f))));
+ gcc_tree_code_type tc (TREE_CODE (r));
+
+ // In the strict mode make sure the function returns for non-array
+ // types a value or a (const) reference to the member type and for
+ // array types a (const) pointer to element type. In the lax mode
+ // we just check that the return value is not void.
+ //
+ if (strict)
+ {
+ semantics::type& t (utype (m));
+ semantics::array* ar (dynamic_cast<semantics::array*> (&t));
+
+ if (ar != 0 && tc != POINTER_TYPE)
+ return found_none;
+
+ tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (r) : r);
+ tree bt_mv (TYPE_MAIN_VARIANT (bt));
+
+ if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv)
+ return found_none;
+ }
+ else if (r == void_type_node)
+ return found_none;
+
+ cxx_tokens& e (ma.expr);
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+
+ // See if it returns by value.
+ //
+ ma.by_value = (tc != REFERENCE_TYPE && tc != POINTER_TYPE);
+
+ return found_best;
+ }
+
+ // Check if a function is a suitable modifier for this member.
+ //
+ found_type
+ check_modifier (semantics::data_member& m,
+ tree f,
+ string const& n,
+ member_access& ma,
+ bool strict)
+ {
+ tree a (FUNCTION_FIRST_USER_PARMTYPE (f));
+
+ // For a modifier, it can either be a function that returns a non-
+ // const reference (or non-const pointer, in case the member is an
+ // array) or a by-value modifier that sets a new value. If both are
+ // available, we prefer the former for efficiency.
+ //
+ cxx_tokens& e (ma.expr);
+ semantics::type& t (utype (m));
+ semantics::array* ar (dynamic_cast<semantics::array*> (&t));
+
+ if (a == void_list_node)
+ {
+ // Note that to get the return type we have to use
+ // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as
+ // suggested in the documentation.
+ //
+ tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f))));
+ gcc_tree_code_type tc (TREE_CODE (r));
+
+ // By-reference modifier. Should return a reference or a pointer.
+ //
+ if (tc != (ar != 0 ? POINTER_TYPE : REFERENCE_TYPE))
+ return found_none;
+
+ // The base type should not be const and, in strict mode, should
+ // match the member type.
+ //
+ tree bt (TREE_TYPE (r));
+
+ if (CP_TYPE_CONST_P (bt))
+ return found_none;
+
+ tree bt_mv (TYPE_MAIN_VARIANT (bt));
+
+ if (strict && (ar != 0 ? ar->base_type () : t).tree_node () != bt_mv)
+ return found_none;
+
+ e.clear (); // Could contain by value modifier.
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+
+ return found_best;
+ }
+ // Otherwise look for a by value modifier, which is a function
+ // with a single argument.
+ //
+ else if (TREE_CHAIN (a) == void_list_node)
+ {
+ // In the lax mode any function with a single argument works
+ // for us. And we don't care what it returns.
+ //
+ if (strict)
+ {
+ // In the strict mode make sure the argument matches the
+ // member. This is exactly the same logic as in accessor
+ // with regards to arrays, references, etc.
+ //
+ tree at (TREE_VALUE (a));
+ gcc_tree_code_type tc (TREE_CODE (at));
+
+ if (ar != 0 && tc != POINTER_TYPE)
+ return found_none;
+
+ tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (at) : at);
+ tree bt_mv (TYPE_MAIN_VARIANT (bt));
+
+ if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv)
+ return found_none;
+ }
+
+ if (e.empty ())
+ {
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_QUERY));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+
+ // Continue searching in case there is version that returns a
+ // non-const reference which we prefer for efficiency.
+ //
+ return found_some;
+ }
+ else
+ return found_none; // We didn't find anything better.
+ }
+
+ return found_none;
+ }
+
+ void
+ process_access (semantics::data_member& m, std::string const& k)
+ {
+ bool virt (m.count ("virtual"));
+
+ // Ignore certain special virtual members.
+ //
+ if (virt && (m.count ("polymorphic-ref") || m.count ("discriminator")))
+ return;
+
+ char const* kind (k == "get" ? "accessor" : "modifier");
+ semantics::class_& c (dynamic_cast<semantics::class_&> (m.scope ()));
+
+ // If we don't have an access expression, try to come up with
+ // one.
+ //
+ if (!m.count (k))
+ {
+ found_type found (found_none);
+ semantics::access const& a (m.named ().access ());
+ member_access& ma (
+ m.set (
+ k, member_access (m.location (), kind, true)));
+
+ // If this member is not virtual and is either public or if we
+ // are a friend of this class, then go for the member directly.
+ //
+ if (!virt && (a == semantics::access::public_ ||
+ c.get<bool> ("friend")))
+ {
+ ma.expr.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ ma.expr.push_back (cxx_token (0, CPP_DOT));
+ ma.expr.push_back (cxx_token (0, CPP_NAME, m.name ()));
+ found = found_best;
+ }
+
+ // Otherwise, try to find a suitable accessor/modifier.
+ //
+
+ // First try the original name. If that doesn't produce anything,
+ // then try the public name.
+ //
+ bool t (k == "get"
+ ? options.accessor_regex_trace ()
+ : options.modifier_regex_trace ());
+ regex_mapping const& re (
+ k == "get" ? accessor_regex : modifier_regex);
+
+ for (unsigned short j (0); found != found_best && j != 2; ++j)
+ {
+ string b (j == 0 ? m.name () : public_name (m, false));
+
+ // Skip the second pass if original and public names are the same.
+ //
+ if (j == 1 && b == m.name ())
+ continue;
+
+ if (t)
+ cerr << kind << (j == 0 ? " original" : " public")
+ << " name '" << b << "'" << endl;
+
+ for (regex_mapping::const_iterator i (re.begin ());
+ found != found_best && i != re.end ();
+ ++i)
+ {
+ if (t)
+ cerr << "try: '" << i->regex () << "' : ";
+
+ if (!i->match (b))
+ {
+ if (t)
+ cerr << '-' << endl;
+ continue;
+ }
+
+ string n (i->replace (b));
+
+ if (t)
+ cerr << "'" << n << "' : ";
+
+ tree decl (
+ lookup_qualified_name (
+ c.tree_node (), get_identifier (n.c_str ()), false, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != BASELINK)
+ {
+ if (t)
+ cerr << '-' << endl;
+ continue;
+ }
+
+ // OVL_* macros work for both FUNCTION_DECL and OVERLOAD.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ for (ovl_iterator i (BASELINK_FUNCTIONS (decl)); i; ++i)
+#else
+ for (tree o (BASELINK_FUNCTIONS (decl)); o != 0; o = OVL_NEXT (o))
+#endif
+ {
+#if BUILDING_GCC_MAJOR >= 8
+ tree f (*i);
+#else
+ tree f (OVL_CURRENT (o));
+#endif
+ // We are only interested in public non-static member
+ // functions. Note that TREE_PUBLIC() returns something
+ // other than what we need.
+ //
+ if (
+#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"
+ ? check_accessor (m, f, n, ma, true)
+ : check_modifier (m, f, n, ma, true));
+
+ if (r != found_none)
+ {
+ // Update the location of the access expression to point
+ // to this function.
+ //
+ ma.loc = location (real_source_location (f));
+ found = r;
+ }
+ }
+
+ if (t)
+ cerr << (found != found_none ? '+' : '-') << endl;
+ }
+ }
+
+ // If that didn't work then the generated code won't be able
+ // to access this member.
+ //
+ if (found == found_none)
+ {
+ location const& l (m.location ());
+
+ if (virt)
+ {
+ error (l) << "no suitable " << kind << " function could be "
+ << "automatically found for virtual data member '"
+ << m.name () << "'" << endl;
+
+ info (l) << "use '#pragma db " << k << "' to explicitly "
+ << "specify the " << kind << " function or "
+ << "expression" << endl;
+ }
+ else
+ {
+ error (l) << "data member '" << m.name () << "' is "
+ << a.string () << " and no suitable " << kind
+ << " function could be automatically found" << endl;
+
+ info (l) << "consider making class 'odb::access' a friend of "
+ << "class '" << class_name (c) << "'" << endl;
+
+ info (l) << "or use '#pragma db " << k << "' to explicitly "
+ << "specify the " << kind << " function or "
+ << "expression" << endl;
+ }
+
+ throw operation_failed ();
+ }
+ }
+
+ member_access& ma (m.get<member_access> (k));
+
+ if (ma.empty ())
+ return;
+
+ cxx_tokens& e (ma.expr);
+
+ // If it is just a name, resolve it and convert to an appropriate
+ // expression.
+ //
+ if (e.size () == 1 && e.back ().type == CPP_NAME)
+ {
+ string n (e.back ().literal);
+ e.clear ();
+
+ tree decl (
+ lookup_qualified_name (
+ c.tree_node (), get_identifier (n.c_str ()), false, false));
+
+ if (decl == error_mark_node)
+ {
+ error (ma.loc) << "unable to resolve data member or function "
+ << "name '" << n << "'" << endl;
+ throw operation_failed ();
+ }
+
+ switch (TREE_CODE (decl))
+ {
+ case FIELD_DECL:
+ {
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ break;
+ }
+ case BASELINK:
+ {
+ // OVL_* macros work for both FUNCTION_DECL and OVERLOAD.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ for (ovl_iterator i (BASELINK_FUNCTIONS (decl)); i; ++i)
+#else
+ for (tree o (BASELINK_FUNCTIONS (decl)); o != 0; o = OVL_NEXT (o))
+#endif
+ {
+#if BUILDING_GCC_MAJOR >= 8
+ tree f (*i);
+#else
+ tree f (OVL_CURRENT (o));
+#endif
+ // We are only interested in non-static member functions.
+ //
+ if (
+#if BUILDING_GCC_MAJOR >= 14
+ !DECL_OBJECT_MEMBER_FUNCTION_P (f)
+#else
+ !DECL_NONSTATIC_MEMBER_FUNCTION_P (f)
+#endif
+ )
+ continue;
+
+ if ((k == "get"
+ ? check_accessor (m, f, n, ma, false)
+ : check_modifier (m, f, n, ma, false)) == found_best)
+ break;
+ }
+
+ if (e.empty ())
+ {
+ error (ma.loc) << "unable to find suitable " << kind
+ << " function '" << n << "'" << endl;
+ throw operation_failed ();
+ }
+ break;
+ }
+ default:
+ {
+ error (ma.loc) << "name '" << n << "' does not refer to a data "
+ << "member or function" << endl;
+ throw operation_failed ();
+ }
+ }
+ }
+
+ // If there is no 'this' keyword, then add it as a prefix.
+ //
+ {
+ bool t (false);
+ for (cxx_tokens::iterator i (e.begin ()); i != e.end (); ++i)
+ {
+ if (i->type == CPP_KEYWORD && i->literal == "this")
+ {
+ t = true;
+ break;
+ }
+ }
+
+ if (!t)
+ {
+ e.insert (e.begin (), cxx_token (0, CPP_DOT));
+ e.insert (e.begin (), cxx_token (0, CPP_KEYWORD, "this"));
+ }
+ }
+
+ // Check that there is no placeholder in the accessor expression.
+ //
+ if (k == "get" && ma.placeholder ())
+ {
+ error (ma.loc) << "(?) placeholder in the accessor expression"
+ << endl;
+ throw operation_failed ();
+ }
+
+ // Check that the member type is default-constructible if we
+ // have a by value modifier.
+ //
+ if (k == "set" && ma.placeholder ())
+ {
+ semantics::class_* c (dynamic_cast<semantics::class_*> (&utype (m)));
+
+ // Assume all other types are default-constructible.
+ //
+ if (c != 0)
+ {
+ // If this type is a class template instantiation, then make
+ // sure it is instantiated. While types used in real members
+ // will be instantiated, this is not necessarily the case for
+ // virtual members. Without the instantiation we won't be able
+ // to detect whether the type has the default ctor.
+ //
+ // It would have been cleaner to do it in post_process_pragmas()
+ // but there we don't yet know whether we need the default ctor.
+ // And it is a good idea not to require instantiability unless
+ // we really need it.
+ //
+ tree type (c->tree_node ());
+
+ if (!COMPLETE_TYPE_P (type) &&
+ CLASSTYPE_TEMPLATE_INSTANTIATION (type))
+ {
+ // Reset input location so that we get nice diagnostics in
+ // case of an error. Use the location of the virtual pragma.
+ //
+ location_t loc (m.get<location_t> ("virtual-location"));
+ input_location = loc;
+
+ if (instantiate_class_template (type) == error_mark_node ||
+ errorcount != 0 ||
+ !COMPLETE_TYPE_P (type))
+ {
+ error (loc) << "unable to instantiate virtual data member " <<
+ "type" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ if (!c->default_ctor ())
+ {
+ error (ma.loc) << "modifier expression requires member type " <<
+ "to be default-constructible" << endl;
+ throw operation_failed ();
+ }
+ }
+ }
+ }
+
+ //
+ // Process section.
+ //
+
+ user_section&
+ process_user_section (semantics::data_member& m, semantics::class_& c)
+ {
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ user_section::load_type l (
+ m.get ("section-load", user_section::load_eager));
+
+ user_section::update_type u (
+ m.get ("section-update", user_section::update_always));
+
+ if (l == user_section::load_eager && u == user_section::update_always)
+ {
+ location const& l (m.location ());
+
+ error (l) << "eager-loaded, always-updated section is pointless"
+ << endl;
+
+ info (l) << "use '#pragma db load' and/or '#pragma db update' to "
+ "specify an alternative loading and/or updating strategy" << endl;
+
+ info (l) << "or remove the section altogether" << endl;
+
+ throw operation_failed ();
+ }
+
+ size_t n (uss.count (user_sections::count_total |
+ user_sections::count_all |
+ user_sections::count_special_version));
+ user_section us (m, c, n, l, u);
+
+ // We may already have seen this section (e.g., forward reference
+ // from a member of this section).
+ //
+ user_sections::iterator i (find (uss.begin (), uss.end (), us));
+
+ if (i != uss.end ())
+ return *i;
+
+ // If we are adding a new section to an optimistic class with
+ // version in a base, make sure the base is sectionable.
+ //
+ semantics::data_member* opt (optimistic (c));
+ if (opt != 0 && &opt->scope () != &c)
+ {
+ semantics::class_* poly_root (polymorphic (c));
+ semantics::node* base (poly_root ? poly_root : &opt->scope ());
+
+ if (!base->count ("sectionable"))
+ {
+ error (m.location ()) << "adding new section to a derived class " <<
+ "in an optimistic hierarchy requires sectionable base class" <<
+ endl;
+
+ info (base->location ()) << "use '#pragma db object sectionable' " <<
+ "to make the base class of this hierarchy sectionable" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ uss.push_back (us);
+ return uss.back ();
+ }
+
+ void
+ process_section_member (semantics::data_member& m)
+ {
+ using semantics::class_;
+ using semantics::data_member;
+
+ string name (m.get<string> ("section-member"));
+ location_t loc (m.get<location_t> ("section-member-location"));
+ class_& c (dynamic_cast<class_&> (m.scope ()));
+
+ class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ try
+ {
+ data_member& us (c.lookup<data_member> (name, class_::include_hidden));
+
+ // Make sure we are referencing a section.
+ //
+ if (utype (us).fq_name () != "::odb::section")
+ {
+ error (loc) << "data member '" << name << "' in '#pragma db " <<
+ "section' is not of the odb::section type" << endl;
+ throw operation_failed ();
+ }
+
+ // If the section is in the base, handle polymorphic inheritance.
+ //
+ class_& b (dynamic_cast<class_&> (us.scope ()));
+ user_section* s (0);
+
+ if (&c != &b && poly_derived)
+ {
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ // This is a section override. See if we have already handled
+ // this section.
+ //
+ for (user_sections::iterator i (uss.begin ());
+ s == 0 && i != uss.end ();
+ ++i)
+ {
+ if (i->member == &us)
+ s = &*i;
+ }
+
+ // Otherwise, find and copy the nearest override in the base.
+ // The result should be a chain of overrides leading all the
+ // way to the original section.
+ //
+ if (s == 0)
+ {
+ for (class_* b (&polymorphic_base (c));;
+ b = &polymorphic_base (*b))
+ {
+ user_sections& buss (b->get<user_sections> ("user-sections"));
+
+ for (user_sections::iterator i (buss.begin ());
+ s == 0 && i != buss.end ();
+ ++i)
+ {
+ if (i->member == &us)
+ {
+ uss.push_back (*i);
+ uss.back ().object = &c;
+ uss.back ().base = &*i;
+ s = &uss.back ();
+ }
+ }
+
+ if (s != 0)
+ break;
+
+ assert (b != poly_root); // We should have found it by now.
+ }
+ }
+ }
+ else
+ s = &process_user_section (us, c);
+
+ // Mark the member as added/deleted if the section is added/deleted.
+ // Also check that the version ordering is correct.
+ //
+ if (unsigned long long sav = added (*s->member))
+ {
+ location_t sl (s->member->get<location_t> ("added-location"));
+
+ if (unsigned long long mav = added (m))
+ {
+ location_t ml (m.get<location_t> ("added-location"));
+
+ if (mav < sav)
+ {
+ error (ml) << "member addition version is less than the " <<
+ "section addition version" << endl;
+ info (sl) << "section addition version is specified here" <<
+ endl;
+ throw operation_failed ();
+ }
+
+ if (mav == sav)
+ {
+ error (ml) << "member addition version is the same as " <<
+ "section addition version" << endl;
+ info (sl) << "section addition version is specified here" <<
+ endl;
+ info (ml) << "delete this pragma" << endl;
+ throw operation_failed ();
+ }
+ }
+ else
+ {
+ m.set ("added", sav);
+ m.set ("added-location", sl);
+ }
+ }
+
+ if (unsigned long long sdv = deleted (*s->member))
+ {
+ location_t sl (s->member->get<location_t> ("deleted-location"));
+
+ if (unsigned long long mdv = deleted (m))
+ {
+ location_t ml (m.get<location_t> ("deleted-location"));
+
+ if (mdv > sdv)
+ {
+ error (ml) << "member deletion version is greater than the " <<
+ "section deletion version" << endl;
+ info (sl) << "section deletion version is specified here" <<
+ endl;
+ throw operation_failed ();
+ }
+
+ if (mdv == sdv)
+ {
+ error (ml) << "member deletion version is the same as " <<
+ "section deletion version" << endl;
+ info (sl) << "section deletion version is specified here" <<
+ endl;
+ info (ml) << "delete this pragma" << endl;
+ throw operation_failed ();
+ }
+ }
+ else
+ {
+ m.set ("deleted", sdv);
+ m.set ("deleted-location", sl);
+ }
+ }
+
+ // Insert as object_section.
+ //
+ m.set ("section", static_cast<object_section*> (s));
+ }
+ catch (semantics::unresolved const& e)
+ {
+ if (e.type_mismatch)
+ error (loc) << "name '" << name << "' in '#pragma db section' " <<
+ "does not refer to a data member" << endl;
+ else
+ error (loc) << "unable to resolve data member '" << name << "' " <<
+ "specified with '#pragma db section'" << endl;
+
+ throw operation_failed ();
+ }
+ catch (semantics::ambiguous const& e)
+ {
+ error (loc) << "data member name '" << name << "' specified " <<
+ "with '#pragma db section' is ambiguous" << endl;
+
+ info (e.first.named ().location ()) << "could resolve to this " <<
+ "data member" << endl;
+
+ info (e.second.named ().location ()) << "or could resolve to " <<
+ "this data member" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ //
+ // Process wrapper.
+ //
+
+ bool
+ process_wrapper (semantics::type& t)
+ {
+ if (t.count ("wrapper"))
+ return t.get<bool> ("wrapper");
+
+ // Check this type with wrapper_traits.
+ //
+ tree inst (instantiate_template (wrapper_traits_, t.tree_node ()));
+
+ if (inst == 0)
+ {
+ t.set ("wrapper", false);
+ return false;
+ }
+
+ // @@ This points to the primary template, not the specialization.
+ //
+ tree decl (TYPE_NAME (inst));
+
+ string f (DECL_SOURCE_FILE (decl));
+ size_t l (DECL_SOURCE_LINE (decl));
+ size_t c (DECL_SOURCE_COLUMN (decl));
+
+ // Get the wrapped type.
+ //
+ try
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("wrapped_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ throw operation_failed ();
+
+ // The wrapped_type alias is a typedef in an instantiation
+ // that we just instantiated dynamically. As a result there
+ // is no semantic graph edges corresponding to this typedef
+ // since we haven't parsed it yet (unless it was instantiated
+ // explicitly by the user; see below). So to get the tree node
+ // that can actually be resolved to the graph node, we use
+ // the source type of this typedef.
+ //
+ tree type (DECL_ORIGINAL_TYPE (decl));
+
+ bool qc (CP_TYPE_CONST_P (type));
+ bool qv (CP_TYPE_VOLATILE_P (type));
+ bool qr (CP_TYPE_RESTRICT_P (type));
+
+ type = TYPE_MAIN_VARIANT (type);
+ semantics::type* wt (
+ dynamic_cast<semantics::type*> (unit.find (type)));
+
+ // Object pointers and wrappers often use the same smart
+ // pointers so check if the wrapped type is an object.
+ //
+ if (object (*wt))
+ {
+ t.set ("wrapper", false);
+ return false;
+ }
+
+ if (qc || qv || qr)
+ {
+ for (semantics::type::qualified_iterator i (wt->qualified_begin ());
+ i != wt->qualified_end (); ++i)
+ {
+ semantics::qualifier& q (i->qualifier ());
+
+ if (q.const_ () == qc &&
+ q.volatile_ () == qv &&
+ q.restrict_ () == qr)
+ {
+ wt = &q;
+ break;
+ }
+ }
+ }
+
+ // Find the hint.
+ //
+ // If we can't find any, then try to fallback to the wrapped_type
+ // alias inside wrapper_traits. This requires an explicit
+ // wrapper_traits instantiation (see above).
+ //
+ semantics::names* wh (find_hint (unit, decl));
+
+ if (wh == nullptr)
+ wh = unit.find_hint (TREE_TYPE (decl));
+
+ t.set ("wrapper-type", wt);
+ t.set ("wrapper-hint", wh);
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "wrapper_traits specialization does not define the "
+ << "wrapped_type type" << endl;
+ throw;
+ }
+
+ // Get the null_handler flag.
+ //
+ bool null_handler (false);
+
+ try
+ {
+ tree nh (
+ lookup_qualified_name (
+ inst, get_identifier ("null_handler"), false, false));
+
+ if (nh == error_mark_node || TREE_CODE (nh) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (nh) &&
+ !DECL_TEMPLATE_INSTANTIATED (nh) &&
+ !DECL_EXPLICIT_INSTANTIATION (nh))
+ instantiate_decl (nh, false, false);
+
+ tree init (DECL_INITIAL (nh));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ null_handler = static_cast<bool> (integer_value (init));
+ t.set ("wrapper-null-handler", null_handler);
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "wrapper_traits specialization does not define the "
+ << "null_handler constant" << endl;
+ throw;
+ }
+
+ // Get the null_default flag.
+ //
+ if (null_handler)
+ {
+ try
+ {
+ tree nh (
+ lookup_qualified_name (
+ inst, get_identifier ("null_default"), false, false));
+
+ if (nh == error_mark_node || TREE_CODE (nh) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (nh) &&
+ !DECL_TEMPLATE_INSTANTIATED (nh) &&
+ !DECL_EXPLICIT_INSTANTIATION (nh))
+ instantiate_decl (nh, false, false);
+
+ tree init (DECL_INITIAL (nh));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ t.set ("wrapper-null-default",
+ static_cast<bool> (integer_value (init)));
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "wrapper_traits specialization does not define the "
+ << "null_default constant" << endl;
+ throw;
+ }
+ }
+
+ // Check if the wrapper is a TR1 template instantiation.
+ //
+ if (tree ti = TYPE_TEMPLATE_INFO (t.tree_node ()))
+ {
+ tree decl (TI_TEMPLATE (ti)); // DECL_TEMPLATE
+
+ // Get to the most general template declaration.
+ //
+ while (DECL_TEMPLATE_INFO (decl))
+ decl = DECL_TI_TEMPLATE (decl);
+
+ bool& tr1 (features.tr1_pointer);
+ bool& boost (features.boost_pointer);
+
+ string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER));
+
+ // In case of a boost TR1 implementation, we cannot distinguish
+ // between the boost:: and std::tr1:: usage since the latter is
+ // just a using-declaration for the former.
+ //
+ tr1 = tr1
+ || n.compare (0, 8, "std::tr1") == 0
+ || n.compare (0, 10, "::std::tr1") == 0;
+
+ boost = boost
+ || n.compare (0, 17, "boost::shared_ptr") == 0
+ || n.compare (0, 19, "::boost::shared_ptr") == 0;
+ }
+
+ t.set ("wrapper", true);
+ return true;
+ }
+
+ //
+ // Process object pointer.
+ //
+
+ semantics::class_*
+ process_object_pointer (semantics::data_member& m,
+ semantics::type& t,
+ string const& kp = string ())
+ {
+ using semantics::class_;
+ using semantics::data_member;
+
+ class_* c (0);
+
+ // The overall idea is as follows: try to instantiate the pointer
+ // traits class template. If we are successeful, then get the
+ // element type and see if it is an object.
+ //
+ if (t.count ("element-type"))
+ c = t.get<class_*> ("element-type");
+ else
+ {
+ tree inst (instantiate_template (pointer_traits_, t.tree_node ()));
+
+ if (inst == 0)
+ return 0;
+
+ // @@ This points to the primary template, not the specialization.
+ //
+ tree decl (TYPE_NAME (inst));
+
+ string fl (DECL_SOURCE_FILE (decl));
+ size_t ln (DECL_SOURCE_LINE (decl));
+ size_t cl (DECL_SOURCE_COLUMN (decl));
+
+ // Get the element type.
+ //
+ tree tn (0);
+ try
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("element_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ throw operation_failed ();
+
+ tn = TYPE_MAIN_VARIANT (TREE_TYPE (decl));
+
+ // Check if the pointer is a TR1 template instantiation.
+ //
+ if (tree ti = TYPE_TEMPLATE_INFO (t.tree_node ()))
+ {
+ decl = TI_TEMPLATE (ti); // DECL_TEMPLATE
+
+ // Get to the most general template declaration.
+ //
+ while (DECL_TEMPLATE_INFO (decl))
+ decl = DECL_TI_TEMPLATE (decl);
+
+ bool& tr1 (features.tr1_pointer);
+ bool& boost (features.boost_pointer);
+
+ string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER));
+
+ // In case of a boost TR1 implementation, we cannot distinguish
+ // between the boost:: and std::tr1:: usage since the latter is
+ // just a using-declaration for the former.
+ //
+ tr1 = tr1
+ || n.compare (0, 8, "std::tr1") == 0
+ || n.compare (0, 10, "::std::tr1") == 0;
+
+ boost = boost
+ || n.compare (0, 17, "boost::shared_ptr") == 0
+ || n.compare (0, 19, "::boost::shared_ptr") == 0;
+ }
+ }
+ catch (operation_failed const&)
+ {
+ os << fl << ":" << ln << ":" << cl << ": error: pointer_traits "
+ << "specialization does not define the 'element_type' type"
+ << endl;
+ throw;
+ }
+
+ c = dynamic_cast<class_*> (unit.find (tn));
+
+ if (c == 0 || !object (*c))
+ return 0;
+
+ t.set ("element-type", c);
+
+ // Determine the pointer kind.
+ //
+ try
+ {
+ tree kind (
+ lookup_qualified_name (
+ inst, get_identifier ("kind"), false, false));
+
+ if (kind == error_mark_node || TREE_CODE (kind) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (kind) &&
+ !DECL_TEMPLATE_INSTANTIATED (kind) &&
+ !DECL_EXPLICIT_INSTANTIATION (kind))
+ instantiate_decl (kind, false, false);
+
+ tree init (DECL_INITIAL (kind));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ pointer_kind_type pk = static_cast<pointer_kind_type> (
+ integer_value (init));
+ t.set ("pointer-kind", pk);
+ }
+ catch (operation_failed const&)
+ {
+ os << fl << ":" << ln << ":" << cl << ": error: pointer_traits "
+ << "specialization does not define the 'kind' constant" << endl;
+ throw;
+ }
+
+ // Get the lazy flag.
+ //
+ try
+ {
+ tree lazy (
+ lookup_qualified_name (
+ inst, get_identifier ("lazy"), false, false));
+
+ if (lazy == error_mark_node || TREE_CODE (lazy) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (lazy) &&
+ !DECL_TEMPLATE_INSTANTIATED (lazy) &&
+ !DECL_EXPLICIT_INSTANTIATION (lazy))
+ instantiate_decl (lazy, false, false);
+
+ tree init (DECL_INITIAL (lazy));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ t.set ("pointer-lazy", static_cast<bool> (integer_value (init)));
+ }
+ catch (operation_failed const&)
+ {
+ os << fl << ":" << ln << ":" << cl << ": error: pointer_traits "
+ << "specialization does not define the 'kind' constant" << endl;
+ throw;
+ }
+ }
+
+ // See if this is the inverse side of a bidirectional relationship.
+ // If so, then resolve the member path and cache it in the context.
+ //
+ if (m.count ("inverse"))
+ {
+ string name (m.get<string> ("inverse"));
+ location_t l (m.get<location_t> ("inverse-location"));
+ data_member_path mp (resolve_data_members (*c, name, l, lex_));
+
+ {
+ string tl;
+ data_member& m (*mp.back ());
+
+ if (container (m) && lex_.next (tl) != CPP_EOF)
+ {
+ error (l) << "unexpect name after container member " <<
+ m.name () << " in '#pragma db inverse'" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ // Validate each member.
+ //
+ for (data_member_path::iterator i (mp.begin ()); i != mp.end (); ++i)
+ {
+ data_member& im (**i);
+ const string& n (im.name ());
+
+ if (im.count ("transient"))
+ {
+ error (l) << "data member '" << n << "' specified with " <<
+ "'#pragma db inverse' is transient" << endl;
+ info (im.location ()) << "data member '" << n << "' is " <<
+ "defined here" << endl;
+ throw operation_failed ();
+ }
+
+ if (im.count ("inverse") || im.count ("value-inverse"))
+ {
+ error (l) << "data member '" << n << "' specified with " <<
+ "'#pragma db inverse' is itself inverse" << endl;
+ info (im.location ()) << "data member '" << n << "' is " <<
+ "defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ // @@ Would be good to check that the other end is actually
+ // an object pointer/points_to and points to the correct
+ // object. But the other class may not have been processed
+ // yet. Need to do in validator, pass 2.
+ //
+ m.remove ("inverse");
+ m.set (kp + (kp.empty () ? "": "-") + "inverse", mp);
+ }
+
+ return c;
+ }
+
+ //
+ // Process points-to pragma.
+ //
+
+ void
+ process_points_to (semantics::data_member& m,
+ string const& /*kp*/ = string ())
+ {
+ if (!m.count ("points-to"))
+ return;
+
+ using semantics::class_;
+
+ tree t (m.get<tree> ("points-to"));
+ location_t l (m.get<location_t> ("points-to-location"));
+
+ class_* c (dynamic_cast<class_*> (unit.find (t)));
+
+ if (c == 0 || !object (*c))
+ {
+ error (l) << "name specified with '#pragma db points_to' does "
+ << "not refer to an object" << endl;
+ throw operation_failed ();
+ }
+
+ m.remove ("points-to");
+ m.set (/*kp + (kp.empty () ? "": "-") + */"points-to", c);
+ }
+
+ //
+ // Process container.
+ //
+
+ void
+ process_container_value (semantics::type& t,
+ semantics::data_member& m,
+ string const& prefix,
+ bool obj_ptr)
+ {
+ if (composite_wrapper (t))
+ return;
+
+ if (obj_ptr)
+ process_object_pointer (m, t, prefix);
+ }
+
+ bool
+ process_container (semantics::data_member& m, semantics::type& t)
+ {
+ // The overall idea is as follows: try to instantiate the container
+ // traits class template. If we are successeful, then this is a
+ // container type and we can extract the various information from
+ // the instantiation. Otherwise, this is not a container.
+ //
+ location ml (m.location ());
+
+ container_kind_type ck;
+ bool smart;
+ semantics::type* vt (0);
+ semantics::type* it (0);
+ semantics::type* kt (0);
+
+ semantics::names* vh (0);
+ semantics::names* ih (0);
+ semantics::names* kh (0);
+
+ if (t.count ("container-kind"))
+ {
+ ck = t.get<container_kind_type> ("container-kind");
+ smart = t.get<bool> ("container-smart");
+
+ vt = &utype (m, vh, "value");
+
+ if (ck == ck_ordered)
+ it = &utype (m, ih, "index");
+
+ if (ck == ck_map || ck == ck_multimap)
+ kt = &utype (m, kh, "key");
+ }
+ else
+ {
+ tree inst (instantiate_template (container_traits_, t.tree_node ()));
+
+ if (inst == 0)
+ return false;
+
+ // @@ This points to the primary template, not the specialization.
+ //
+ tree decl (TYPE_NAME (inst));
+
+ string f (DECL_SOURCE_FILE (decl));
+ size_t l (DECL_SOURCE_LINE (decl));
+ size_t c (DECL_SOURCE_COLUMN (decl));
+
+ // Determine the container kind.
+ //
+ try
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("kind"), false, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (decl) &&
+ !DECL_TEMPLATE_INSTANTIATED (decl) &&
+ !DECL_EXPLICIT_INSTANTIATION (decl))
+ instantiate_decl (decl, false, false);
+
+ tree init (DECL_INITIAL (decl));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ ck = static_cast<container_kind_type> (integer_value (init));
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "container kind constant" << endl;
+
+ throw;
+ }
+
+ t.set ("container-kind", ck);
+
+ // See if it is a smart container.
+ //
+ try
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("smart"), false, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (decl) &&
+ !DECL_TEMPLATE_INSTANTIATED (decl) &&
+ !DECL_EXPLICIT_INSTANTIATION (decl))
+ instantiate_decl (decl, false, false);
+
+ tree init (DECL_INITIAL (decl));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ smart = static_cast<bool> (integer_value (init));
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "'smart' constant" << endl;
+ throw;
+ }
+
+ // For now we only support ordered smart containers.
+ //
+ if (smart && ck != ck_ordered)
+ {
+ os << f << ":" << l << ":" << c << ": error: only ordered smart " <<
+ "containers are currently supported" << endl;
+ throw operation_failed ();
+ }
+
+ t.set ("container-smart", smart);
+
+ // Mark id column as not null.
+ //
+ t.set ("id-not-null", true);
+
+ // Get the value type.
+ //
+ {
+ tree decl (lookup_qualified_name (
+ inst, get_identifier ("value_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "value_type type" << endl;
+
+ throw operation_failed ();
+ }
+
+ tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
+
+ if (semantics::node* n = unit.find (type))
+ vt = &dynamic_cast<semantics::type&> (*n);
+ else
+ {
+ error (ml) << "container value type is not instantiated" << endl;
+ info (ml) << "use typedef/using to instantiate" << endl;
+ throw operation_failed ();
+ }
+
+ // Find the hint.
+ //
+ vh = find_hint (unit, decl);
+ }
+
+
+ t.set ("value-tree-type", vt);
+ t.set ("value-tree-hint", vh);
+ vt = &utype (m, vh, "value"); // Map.
+
+ // Issue a warning if we are relaxing null-ness in the container
+ // type.
+ //
+ if (t.count ("value-null") && vt->count ("not-null"))
+ {
+ os << t.file () << ":" << t.line () << ":" << t.column () << ":"
+ << " warning: container value declared null while its type "
+ << "is declared not null" << endl;
+ }
+
+ // Get the index type for ordered containers.
+ //
+ if (ck == ck_ordered)
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("index_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "index_type type" << endl;
+ throw operation_failed ();
+ }
+
+ tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
+
+ if (semantics::node* n = unit.find (type))
+ it = &dynamic_cast<semantics::type&> (*n);
+ else
+ {
+ error (ml) << "container index type is not instantiated" << endl;
+ info (ml) << "use typedef/using to instantiate" << endl;
+ throw operation_failed ();
+ }
+
+ // Find the hint.
+ //
+ ih = find_hint (unit, decl);
+
+ t.set ("index-not-null", true);
+ t.set ("index-tree-type", it);
+ t.set ("index-tree-hint", ih);
+ it = &utype (m, ih, "index"); // Map.
+ }
+
+ // Get the key type for maps.
+ //
+ if (ck == ck_map || ck == ck_multimap)
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("key_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "key_type type" << endl;
+ throw operation_failed ();
+ }
+
+ tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
+
+ if (semantics::node* n = unit.find (type))
+ kt = &dynamic_cast<semantics::type&> (*n);
+ else
+ {
+ error (ml) << "container key type is not instantiated" << endl;
+ info (ml) << "use typedef/using to instantiate" << endl;
+ throw operation_failed ();
+ }
+
+ // Find the hint.
+ //
+ kh = find_hint (unit, decl);
+
+ t.set ("key-tree-type", kt);
+ t.set ("key-tree-hint", kh);
+ kt = &utype (m, kh, "key"); // Map.
+
+ // Issue a warning if we are relaxing null-ness in the container
+ // type.
+ //
+ if (t.count ("key-null") && kt->count ("not-null"))
+ {
+ os << t.file () << ":" << t.line () << ":" << t.column () << ":"
+ << " warning: container key declared null while its type "
+ << "is declared not null" << endl;
+ }
+ }
+
+ // Determine if container value/index/key types are wrappers.
+ //
+ process_wrapper (*vt);
+
+ if (it != 0)
+ process_wrapper (*it);
+
+ if (kt != 0)
+ process_wrapper (*kt);
+
+ // Check if we are versioned. For now we are not allowing for
+ // soft-add/delete in container keys (might be used in WHERE,
+ // primary key).
+ //
+ {
+ semantics::class_* comp (0);
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ comp = composite_wrapper (*vt);
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ comp = composite_wrapper (*kt);
+ if (comp == 0 || column_count (*comp).soft == 0)
+ {
+ comp = composite_wrapper (*vt);
+ break;
+ }
+
+ error (ml) << "map key type cannot have soft-added/deleted " <<
+ "data members" << endl;
+ info (kt->location ()) << "key type is defined here" << endl;
+ throw operation_failed ();
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ comp = composite_wrapper (*vt);
+ if (comp == 0 || column_count (*comp).soft == 0)
+ {
+ comp = 0;
+ break;
+ }
+
+ error (ml) << "set value type cannot have soft-added/deleted " <<
+ "data members" << endl;
+ info (vt->location ()) << "value type is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ if (force_versioned || (comp != 0 && column_count (*comp).soft != 0))
+ t.set ("versioned", true);
+ }
+ }
+
+ // Process member data.
+ //
+ m.set ("id-tree-type", &id_tree_type);
+
+ // Has to be first to handle inverse.
+ //
+ process_container_value (*vt, m, "value", true);
+
+ if (it != 0)
+ process_container_value (*it, m, "index", false);
+
+ if (kt != 0)
+ process_container_value (*kt, m, "key", true);
+
+ // A map cannot be an inverse container.
+ //
+ if (m.count ("value-inverse") && (ck == ck_map || ck == ck_multimap))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: inverse container cannot be a map" << endl;
+ throw operation_failed ();
+ }
+
+ // If this is an inverse side of a bidirectional object relationship
+ // and it is an ordered container, mark it as unordred since there is
+ // no concept of order in this construct.
+ //
+ if (ck == ck_ordered && m.count ("value-inverse"))
+ m.set ("unordered", true);
+
+ // Issue an error if we have a non-inverse smart unordered container.
+ //
+ if (smart && ck == ck_ordered && unordered (m) &&
+ !m.count ("value-inverse"))
+ {
+ error (ml) << "smart ordered container cannot be unordered" << endl;
+ throw operation_failed ();
+ }
+
+ // Issue a warning if we are relaxing null-ness in the member.
+ //
+ if (m.count ("value-null") &&
+ (t.count ("value-not-null") || vt->count ("not-null")))
+ {
+ warn (ml) << "container value declared null while the container "
+ << "type or value type declares it as not null" << endl;
+ }
+
+ if (ck == ck_map || ck == ck_multimap)
+ {
+ if (m.count ("key-null") &&
+ (t.count ("key-not-null") || kt->count ("not-null")))
+ {
+ warn (ml) << "container key declared null while the container "
+ << "type or key type declares it as not null" << endl;
+ }
+ }
+
+ return true;
+ }
+
+ //
+ // Implementation details (c-tor, helpers).
+ //
+
+ data_member ()
+ {
+ // Find the odb namespace.
+ //
+ tree odb = lookup_qualified_name (
+ global_namespace, get_identifier ("odb"), false, false);
+
+ if (odb == error_mark_node)
+ {
+ os << unit.file () << ": error: unable to resolve odb namespace"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ // Find wrapper traits.
+ //
+ wrapper_traits_ = lookup_qualified_name (
+ odb, get_identifier ("wrapper_traits"), true, false);
+
+ if (wrapper_traits_ == error_mark_node ||
+ !DECL_CLASS_TEMPLATE_P (wrapper_traits_))
+ {
+ os << unit.file () << ": error: unable to resolve wrapper_traits "
+ << "in the odb namespace" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Find pointer traits.
+ //
+ pointer_traits_ = lookup_qualified_name (
+ odb, get_identifier ("pointer_traits"), true, false);
+
+ if (pointer_traits_ == error_mark_node ||
+ !DECL_CLASS_TEMPLATE_P (pointer_traits_))
+ {
+ os << unit.file () << ": error: unable to resolve pointer_traits "
+ << "in the odb namespace" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Find the access class.
+ //
+ tree access = lookup_qualified_name (
+ odb, get_identifier ("access"), true, false);
+
+ if (access == error_mark_node)
+ {
+ os << unit.file () << ": error: unable to resolve access class"
+ << "in the odb namespace" << endl;
+
+ throw operation_failed ();
+ }
+
+ access = TREE_TYPE (access);
+
+ // Find container_traits.
+ //
+ container_traits_ = lookup_qualified_name (
+ access, get_identifier ("container_traits"), true, false);
+
+ if (container_traits_ == error_mark_node ||
+ !DECL_CLASS_TEMPLATE_P (container_traits_))
+ {
+ os << unit.file () << ": error: unable to resolve container_traits "
+ << "in the odb namespace" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ static tree
+ instantiate_template (tree t, tree arg)
+ {
+ tree args (make_tree_vec (1));
+ TREE_VEC_ELT (args, 0) = arg;
+
+ // This step should succeed regardles of whether there is a
+ // specialization for this type.
+ //
+ tree inst (
+ lookup_template_class (t, args, 0, 0, 0, tf_warning_or_error));
+
+ if (inst == error_mark_node)
+ {
+ // Diagnostics has already been issued by lookup_template_class.
+ //
+ throw operation_failed ();
+ }
+
+ inst = TYPE_MAIN_VARIANT (inst);
+
+ // The instantiation may already be complete if it matches a
+ // (complete) specialization or was used before.
+ //
+ if (!COMPLETE_TYPE_P (inst))
+ inst = instantiate_class_template (inst);
+
+ // If we cannot instantiate this type, assume there is no suitable
+ // specialization for it.
+ //
+ if (inst == error_mark_node || !COMPLETE_TYPE_P (inst))
+ return 0;
+
+ return inst;
+ }
+
+ private:
+ tree wrapper_traits_;
+ tree pointer_traits_;
+ tree container_traits_;
+
+ cxx_string_lexer lex_;
+ };
+
+ struct view_data_member: traversal::data_member, context
+ {
+ view_data_member (semantics::class_& c)
+ : view_ (c),
+ amap_ (c.get<view_alias_map> ("alias-map")),
+ omap_ (c.get<view_object_map> ("object-map")) {}
+
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ using semantics::data_member;
+
+ if (transient (m))
+ return;
+
+ semantics::type& t (utype (m));
+
+ if (semantics::class_* c = object_pointer (t))
+ {
+ location const& l (m.location ());
+
+ if (lazy_pointer (t))
+ {
+ error (l) << "lazy object pointer in view" << endl;
+ throw operation_failed ();
+ }
+
+ // Find the corresponding associated object. First see if this
+ // data member name matches any aliases.
+ //
+ view_alias_map::iterator i (amap_.find (m.name ()));
+
+ if (i == amap_.end ())
+ i = amap_.find (public_name (m, false));
+
+ view_object* vo (0);
+
+ if (i != amap_.end ())
+ {
+ vo = i->second;
+
+ if (vo->obj != c) // @@ Poly base/derived difference?
+ {
+ error (l) << "different pointed-to and associated objects" << endl;
+ info (vo->loc) << "associated object is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+ else
+ {
+ // If there is no alias match, try the object type.
+ //
+ view_object_map::iterator i (omap_.find (c));
+
+ if (i == omap_.end ())
+ {
+ error (l) << "unable to find associated object for object "
+ << "pointer" << endl;
+ info (l) << "use associated object alias as this data member "
+ << "name" << endl;
+ throw operation_failed ();
+ }
+
+ vo = i->second;
+ }
+
+ if (vo->ptr != 0)
+ {
+ location const& l2 (vo->ptr->location ());
+
+ error (l) << "associated object is already loaded via another "
+ << "object pointer" << endl;
+ info (l2) << "the other data member is defined here" << endl;
+ info (l2) << "use associated object alias as this data member "
+ << "name to load a different object" << endl;
+
+ throw operation_failed ();
+ }
+
+ vo->ptr = &m;
+ m.set ("view-object", vo);
+ }
+ }
+
+ private:
+ semantics::class_& view_;
+ view_alias_map& amap_;
+ view_object_map& omap_;
+ };
+
+ // Figure out the "summary" added/deleted version for a composite
+ // value type.
+ //
+ struct summary_version: object_members_base
+ {
+ summary_version (): av (0), dv (0), a_ (true), d_ (true) {}
+
+ virtual void
+ traverse_simple (semantics::data_member&)
+ {
+ if (a_)
+ {
+ if (unsigned long long v = added (member_path_))
+ {
+ if (av == 0 || av < v)
+ av = v;
+ }
+ else
+ {
+ av = 0;
+ a_ = false;
+ }
+ }
+
+ if (d_)
+ {
+ if (unsigned long long v = deleted (member_path_))
+ {
+ if (dv == 0 || dv > v)
+ dv = v;
+ }
+ else
+ {
+ dv = 0;
+ d_ = false;
+ }
+ }
+ }
+
+ public:
+ unsigned long long av;
+ unsigned long long dv;
+
+ bool a_;
+ bool d_;
+ };
+
+ struct class_: traversal::class_, context
+ {
+ class_ ()
+ : typedefs_ (true),
+ std_string_ (0),
+ std_string_hint_ (0),
+ access_ (0)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+
+ member_names_ >> member_;
+
+ // Resolve the std::string type node.
+ //
+ using semantics::scope;
+
+ for (scope::names_iterator_pair ip (unit.find ("std"));
+ ip.first != ip.second; ++ip.first)
+ {
+ if (scope* ns = dynamic_cast<scope*> (&ip.first->named ()))
+ {
+ scope::names_iterator_pair jp (ns->find ("string"));
+
+ if (jp.first != jp.second)
+ {
+ std_string_ = dynamic_cast<semantics::type*> (
+ &jp.first->named ());
+ std_string_hint_ = &*jp.first;
+ break;
+ }
+ }
+ }
+
+ assert (std_string_ != 0); // No std::string?
+
+ // Resolve odb::access, if any.
+ //
+ tree odb = lookup_qualified_name (
+ global_namespace, get_identifier ("odb"), false, false);
+
+ if (odb != error_mark_node)
+ {
+ access_ = lookup_qualified_name (
+ odb, get_identifier ("access"), true, false);
+
+ access_ = (access_ != error_mark_node ? TREE_TYPE (access_) : 0);
+ }
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type k (class_kind (c));
+
+ if (k == class_other)
+ return;
+
+ names (c); // Process nested classes.
+
+ // Check if odb::access is a friend of this class.
+ //
+ c.set ("friend", access_ != 0 && is_friend (c.tree_node (), access_));
+
+ // Assign pointer.
+ //
+ if (k == class_object || k == class_view)
+ assign_pointer (c);
+
+ if (k == class_object)
+ traverse_object_pre (c);
+ else if (k == class_view)
+ traverse_view_pre (c);
+
+ names (c, member_names_);
+
+ if (k == class_object)
+ traverse_object_post (c);
+ else if (k == class_view)
+ traverse_view_post (c);
+ else if (k == class_composite)
+ traverse_composite_post (c);
+ }
+
+ //
+ // Object.
+ //
+
+ virtual void
+ traverse_object_pre (type& c)
+ {
+ using semantics::class_;
+ using semantics::data_member;
+
+ class_* poly_root (polymorphic (c));
+
+ // Sections.
+ //
+ user_sections& uss (c.set ("user-sections", user_sections (c)));
+
+ // Copy sections from reuse bases. For polymorphic classes, sections
+ // are overridden.
+ //
+ if (poly_root == 0 || poly_root == &c)
+ {
+ for (type::inherits_iterator i (c.inherits_begin ());
+ i != c.inherits_end (); ++i)
+ {
+ type& b (i->base ());
+
+ if (object (b))
+ {
+ user_sections& buss (b.get<user_sections> ("user-sections"));
+
+ for (user_sections::iterator j (buss.begin ());
+ j != buss.end ();
+ ++j)
+ {
+ // Don't copy the special version update section.
+ //
+ if (j->special == user_section::special_version)
+ continue;
+
+ uss.push_back (*j);
+ uss.back ().object = &c;
+ uss.back ().base = &*j;
+ }
+ }
+ }
+ }
+
+ // Determine whether it is a session object.
+ //
+ if (!c.count ("session"))
+ {
+ // If this is a derived class in a polymorphic hierarchy,
+ // then it should have the same session value as the root.
+ //
+ if (poly_root != 0 && poly_root != &c)
+ c.set ("session", session (*poly_root));
+ else
+ {
+ // See if any of the namespaces containing this class specify
+ // the session value.
+ //
+ bool found (false);
+ for (semantics::scope* s (&class_scope (c));; s = &s->scope_ ())
+ {
+ using semantics::namespace_;
+
+ namespace_* ns (dynamic_cast<namespace_*> (s));
+
+ if (ns == 0) // Some other scope.
+ {
+ if (!s->named_p ())
+ break;
+
+ continue;
+ }
+
+ if (ns->extension ())
+ ns = &ns->original ();
+
+ if (ns->count ("session"))
+ {
+ c.set ("session", ns->get<bool> ("session"));
+ found = true;
+ break;
+ }
+
+ if (ns->global_scope ()) // Note: namespaces always named.
+ break;
+ }
+
+ // If still not found, then use the default value.
+ //
+ if (!found)
+ c.set ("session", options.generate_session ());
+ }
+ }
+
+ if (session (c))
+ features.session_object = true;
+
+ if (poly_root != 0)
+ {
+ using namespace semantics;
+ using semantics::data_member;
+
+ data_member_path& id (*id_member (*poly_root));
+ data_member* idm (id.front ());
+
+ if (poly_root != &c)
+ {
+ // If we are a derived class in the polymorphic persistent
+ // class hierarchy, then add a synthesized virtual pointer
+ // member that points back to the root.
+ //
+ semantics::class_& base (polymorphic_base (c));
+
+ if (&base != poly_root)
+ idm = &dynamic_cast<data_member&> (base.names_begin ()->named ());
+
+ path const& f (idm->file ());
+ size_t l (idm->line ()), col (idm->column ());
+
+ semantics::data_member& m (
+ unit.new_node<semantics::data_member> (f, l, col, tree (0)));
+ m.set ("virtual", true);
+
+ // Make it the first member in the class. This is important:
+ // we rely on the corrensponding foreign key to be first.
+ //
+ node_position<type, scope::names_iterator> np (c, c.names_end ());
+ unit.new_edge<semantics::names> (
+ np, m, idm->name (), access::public_);
+
+ // Use the raw pointer as this member's type.
+ //
+ if (!base.pointed_p ())
+ {
+ // Create the pointer type in the graph. The pointer node
+ // in GCC seems to always be present, even if not explicitly
+ // used in the translation unit.
+ //
+ tree t (base.tree_node ());
+ tree ptr (TYPE_POINTER_TO (t));
+ assert (ptr != 0);
+ ptr = TYPE_MAIN_VARIANT (ptr);
+ pointer& p (unit.new_node<pointer> (f, l, col, ptr));
+ unit.insert (ptr, p);
+ unit.new_edge<points> (p, base);
+ assert (base.pointed_p ());
+ }
+
+ unit.new_edge<belongs> (m, base.pointed ().pointer ());
+
+ // Mark it as a special kind of id.
+ //
+ m.set ("id", string ());
+ m.set ("polymorphic-ref", true);
+
+ // Make sure we also use the same column name as the root.
+ //
+ if (composite_wrapper (utype (id)))
+ m.set ("column", table_column (column_prefix (id, true).prefix));
+ else
+ m.set ("column", table_column (column_name (id)));
+ }
+ else
+ {
+ // If we are a root of the polymorphic persistent class hierarchy,
+ // then add a synthesized virtual member for the discriminator.
+ // Use the location of the polymorphic pragma as the location of
+ // this member.
+ //
+ location_t loc (c.get<location_t> ("polymorphic-location"));
+ semantics::data_member& m (
+ unit.new_node<semantics::data_member> (
+ path (LOCATION_FILE (loc)),
+ LOCATION_LINE (loc),
+ LOCATION_COLUMN (loc),
+ tree (0)));
+ m.set ("virtual", true);
+
+ // Insert it after the id member (or first if this id comes
+ // from reuse-base).
+ //
+ node_position<type, scope::names_iterator> np (
+ c, c.find (idm->named ()));
+ unit.new_edge<semantics::names> (
+ np, m, "typeid_", access::public_);
+
+ belongs& edge (unit.new_edge<belongs> (m, *std_string_));
+ edge.hint (*std_string_hint_);
+
+ m.set ("readonly", true);
+ m.set ("discriminator", true);
+
+ c.set ("discriminator", &m);
+ }
+ }
+ }
+
+ virtual void
+ traverse_object_post (type& c)
+ {
+ semantics::class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ semantics::data_member* opt (optimistic (c));
+
+ // Figure out if we are versioned. We are versioned if we have
+ // soft-added/deleted columns ourselves or our poly-base is
+ // versioned.
+ //
+ if (force_versioned ||
+ column_count (c).soft != 0 ||
+ (poly_derived && polymorphic_base (c).count ("versioned")))
+ c.set ("versioned", true);
+
+ // Sections.
+ //
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ // See if we need to add a special fake section for version update.
+ //
+ if (c.count ("sectionable"))
+ {
+ uss.push_back (
+ user_section (*opt,
+ c,
+ uss.count (user_sections::count_total |
+ user_sections::count_all |
+ user_sections::count_special_version),
+ user_section::load_lazy,
+ user_section::update_manual,
+ user_section::special_version));
+
+ // If we are a root of a polymorphic hierarchy and the version is in
+ // a reuse-base, then we need to make sure that base is sectionable
+ // and derive from its special version update section.
+ //
+ semantics::node& opt_base (opt->scope ());
+ if (poly_root == &c && &opt_base != &c)
+ {
+ if (!opt_base.count ("sectionable"))
+ {
+ location_t l (c.get<location_t> ("sectionable-location"));
+
+ error (l) << "reuse base class of a sectionable polymorphic " <<
+ "root class must be sectionable" << endl;
+
+ info (opt_base.location ()) << "use '#pragma db object " <<
+ "sectionable' to make the base class of this hierarchy " <<
+ "sectionable" << endl;
+
+ throw operation_failed ();
+ }
+
+ uss.back ().base =
+ &opt_base.get<user_sections> ("user-sections").back ();
+ }
+ }
+
+ // Calculate column counts for sections.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ column_count_type cc (column_count (c, &*i));
+ i->total = cc.total;
+ i->inverse = cc.inverse;
+ i->readonly = cc.readonly;
+
+ // Figure out if we are versioned. We are versioned if we have
+ // soft-added/deleted columns ourselves or our poly-base is
+ // versioned.
+ //
+ if (force_versioned || cc.soft != 0 ||
+ (poly_derived && i->base != 0 && i->base->versioned))
+ i->versioned = true;
+
+ if (size_t n = has_a (c, test_container, &*i))
+ {
+ i->containers = true;
+ i->versioned_containers =
+ n != has_a (c,
+ test_container |
+ exclude_deleted | exclude_added | exclude_versioned,
+ &*i);
+
+ if ((n = has_a (c, test_readwrite_container, &*i)))
+ {
+ i->readwrite_containers = true;
+ i->readwrite_versioned_containers =
+ n != has_a (c,
+ test_readwrite_container |
+ exclude_deleted | exclude_added | exclude_versioned,
+ &*i);
+ }
+ }
+ }
+ }
+
+ //
+ // View.
+ //
+
+ virtual void
+ traverse_view_pre (type& c)
+ {
+ // Resolve referenced objects from tree nodes to semantic graph
+ // nodes. Also populate maps and compute counts.
+ //
+ view_alias_map& amap (c.set ("alias-map", view_alias_map ()));
+ view_object_map& omap (c.set ("object-map", view_object_map ()));
+
+ size_t& obj_count (c.set ("object-count", size_t (0)));
+ size_t& tbl_count (c.set ("table-count", size_t (0)));
+
+ if (c.count ("objects"))
+ {
+ using semantics::class_;
+
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ if (i->kind != view_object::object)
+ {
+ tbl_count++;
+ continue;
+ }
+ else
+ obj_count++;
+
+ tree n (TYPE_MAIN_VARIANT (i->obj_node));
+
+ if (TREE_CODE (n) != RECORD_TYPE)
+ {
+ error (i->loc) << "name '" << i->obj_name << "' in db pragma " <<
+ "object does not name a class" << endl;
+
+ throw operation_failed ();
+ }
+
+ class_& o (dynamic_cast<class_&> (*unit.find (n)));
+
+ if (!object (o))
+ {
+ error (i->loc) << "name '" << i->obj_name << "' in db pragma " <<
+ "object does not name a persistent class" << endl;
+
+ info (o.location ()) << "class '" << i->obj_name << "' is " <<
+ "defined here" << endl;
+
+ throw operation_failed ();
+ }
+
+ i->obj = &o;
+ i->ptr = 0; // Nothing yet.
+
+ if (i->alias.empty ())
+ {
+ if (!omap.insert (view_object_map::value_type (&o, &*i)).second)
+ {
+ error (i->loc) << "persistent class '" << i->obj_name <<
+ "' is used in the view more than once" << endl;
+
+ error (omap[&o]->loc) << "previously used here" << endl;
+
+ info (i->loc) << "use the alias clause to assign it a " <<
+ "different name" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Also add the bases of a polymorphic object.
+ //
+ class_* poly_root (polymorphic (o));
+
+ if (poly_root != 0 && poly_root != &o)
+ {
+ for (class_* b (&polymorphic_base (o));;
+ b = &polymorphic_base (*b))
+ {
+ if (!omap.insert (view_object_map::value_type (b, &*i)).second)
+ {
+ error (i->loc) << "base class '" << class_name (*b) <<
+ "' is used in the view more than once" << endl;
+
+ error (omap[b]->loc) << "previously used here" << endl;
+
+ info (i->loc) << "use the alias clause to assign it a " <<
+ "different name" << endl;
+
+ throw operation_failed ();
+ }
+
+ if (b == poly_root)
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (!amap.insert (
+ view_alias_map::value_type (i->alias, &*i)).second)
+ {
+ error (i->loc) << "alias '" << i->alias << "' is used in " <<
+ "the view more than once" << endl;
+
+ throw operation_failed ();
+ }
+ }
+ }
+ }
+ }
+
+ virtual void
+ traverse_view_post (type& c)
+ {
+ // Handle data members.
+ //
+ {
+ view_data_member t (c);
+ traversal::names n (t);
+ names (c, n);
+ }
+
+ // Figure out if we are versioned. Forced versioning is handled
+ // in relational/processing.
+ //
+ if (column_count (c).soft != 0)
+ c.set ("versioned", true);
+ }
+
+ //
+ // Composite.
+ //
+
+ virtual void
+ traverse_composite_post (type& c)
+ {
+ // Figure out if we are versioned.
+ //
+ if (force_versioned || column_count (c).soft != 0)
+ {
+ c.set ("versioned", true);
+
+ // See if we are "summarily" added/deleted, that is, all the
+ // columns are added/deleted. Note: this does not include
+ // containers.
+ //
+ summary_version sv;
+ sv.traverse (c);
+
+ // Note: there are no locations.
+ //
+ if (sv.av != 0)
+ c.set ("added", sv.av);
+
+ if (sv.dv != 0)
+ c.set ("deleted", sv.dv);
+ }
+ }
+
+ //
+ // Assign object/view pointer.
+ //
+
+ void
+ assign_pointer (type& c)
+ {
+ location_t loc (0); // Pragma location, or 0 if not used.
+
+ try
+ {
+ bool raw;
+ string ptr;
+ string const& type (class_fq_name (c));
+
+ tree decl (0); // Resolved template node.
+ string decl_name; // User-provided template name.
+ tree resolve_scope (0); // Scope in which we resolve names.
+
+ class_pointer const* cp (0);
+ bool cp_template (false);
+
+ if (c.count ("pointer"))
+ {
+ cp = &c.get<class_pointer> ("pointer");
+ }
+ // If we are a derived type in polymorphic hierarchy, then use
+ // our root's pointer type by default.
+ //
+ else if (semantics::class_* r = polymorphic (c))
+ {
+ if (&c != r && r->count ("pointer-template"))
+ cp = r->get<class_pointer const*> ("pointer-template");
+ }
+
+ if (cp != 0)
+ {
+ string const& p (cp->name);
+
+ if (p == "*")
+ {
+ raw = true;
+ ptr = type + "*";
+ cp_template = true;
+ }
+ else if (p[p.size () - 1] == '*')
+ {
+ raw = true;
+ ptr = p;
+ }
+ else if (p.find ('<') != string::npos)
+ {
+ // Template-id.
+ //
+ raw = false; // Fair to assume not raw, though technically can be.
+ ptr = p;
+ decl_name.assign (p, 0, p.find ('<'));
+ }
+ else
+ {
+ // This is not a template-id. Resolve it and see if it is a
+ // template or a type.
+ //
+ decl = resolve_name (p, cp->scope, true);
+ gcc_tree_code_type tc (TREE_CODE (decl));
+
+ if (tc == TYPE_DECL)
+ {
+ raw = (TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE);
+ ptr = p;
+
+ // This can be a typedef'ed alias for a TR1 template-id.
+ //
+ if (tree ti = TYPE_TEMPLATE_INFO (TREE_TYPE (decl)))
+ {
+ decl = TI_TEMPLATE (ti); // DECL_TEMPLATE
+
+ // Get to the most general template declaration.
+ //
+ while (DECL_TEMPLATE_INFO (decl))
+ decl = DECL_TI_TEMPLATE (decl);
+ }
+ else
+ decl = 0; // Not a template.
+ }
+ else if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl))
+ {
+ raw = false;
+ ptr = p + "< " + type + " >";
+ decl_name = p;
+ cp_template = true;
+ }
+ else
+ {
+ error (cp->loc)
+ << "name '" << p << "' specified with db pragma pointer "
+ << "does not name a type or a template" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ // Resolve scope is the scope of the pragma.
+ //
+ resolve_scope = cp->scope;
+ loc = cp->loc;
+ }
+ else
+ {
+ // See if any of the namespaces containing this class specify
+ // a pointer.
+ //
+ for (semantics::scope* s (&class_scope (c));; s = &s->scope_ ())
+ {
+ using semantics::namespace_;
+
+ namespace_* ns (dynamic_cast<namespace_*> (s));
+
+ if (ns == 0) // Some other scope.
+ {
+ if (!s->named_p ())
+ break;
+
+ continue;
+ }
+
+ if (ns->extension ())
+ ns = &ns->original ();
+
+ if (!ns->count ("pointer"))
+ {
+ if (ns->global_scope ()) // Note: namespace always named.
+ break;
+ else
+ continue;
+ }
+
+ cp = &ns->get<class_pointer> ("pointer");
+ string const& p (cp->name);
+
+ // Namespace-specified pointer can only be '*' or are template.
+ //
+ if (p == "*")
+ {
+ raw = true;
+ ptr = type + "*";
+ }
+ else if (p[p.size () - 1] == '*')
+ {
+ error (cp->loc)
+ << "name '" << p << "' specified with db pragma pointer "
+ << "at namespace level cannot be a raw pointer" << endl;
+ }
+ else if (p.find ('<') != string::npos)
+ {
+ error (cp->loc)
+ << "name '" << p << "' specified with db pragma pointer "
+ << "at namespace level cannot be a template-id" << endl;
+ }
+ else
+ {
+ // Resolve this name and make sure it is a template.
+ //
+ decl = resolve_name (p, cp->scope, true);
+ gcc_tree_code_type tc (TREE_CODE (decl));
+
+ if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl))
+ {
+ raw = false;
+ ptr = p + "< " + type + " >";
+ decl_name = p;
+ }
+ else
+ {
+ error (cp->loc)
+ << "name '" << p << "' specified with db pragma pointer "
+ << "does not name a template" << endl;
+ }
+ }
+
+ if (ptr.empty ())
+ throw operation_failed ();
+
+ cp_template = true;
+
+ // Resolve scope is the scope of the pragma.
+ //
+ resolve_scope = cp->scope;
+ loc = cp->loc;
+ break;
+ }
+
+ // Use the default pointer.
+ //
+ if (ptr.empty ())
+ {
+ string const& p (options.default_pointer ());
+
+ if (p == "*")
+ {
+ raw = true;
+ ptr = type + "*";
+ }
+ else
+ {
+ raw = false;
+ ptr = p + "< " + type + " >";
+ decl_name = p;
+ }
+
+ // Resolve scope is the scope of the class.
+ //
+ resolve_scope = class_scope (c).tree_node ();
+ }
+ }
+
+ // If this class is a root of a polymorphic hierarchy, then cache
+ // the pointer template so that we can use it for derived classes.
+ //
+ if (cp != 0 && cp_template && polymorphic (c) == &c)
+ c.set ("pointer-template", cp);
+
+ // Check if we are using TR1.
+ //
+ if (decl != 0 || !decl_name.empty ())
+ {
+ bool& tr1 (features.tr1_pointer);
+ bool& boost (features.boost_pointer);
+
+ // First check the user-supplied name.
+ //
+ tr1 = tr1
+ || decl_name.compare (0, 8, "std::tr1") == 0
+ || decl_name.compare (0, 10, "::std::tr1") == 0;
+
+ // If there was no match, also resolve the name since it can be
+ // a using-declaration for a TR1 template.
+ //
+ if (!tr1)
+ {
+ if (decl == 0)
+ decl = resolve_name (decl_name, resolve_scope, false);
+
+ if (TREE_CODE (decl) != TEMPLATE_DECL || !
+ DECL_CLASS_TEMPLATE_P (decl))
+ {
+ // This is only checked for the --default-pointer option.
+ //
+ error (c.file (), c.line (), c.column ())
+ << "name '" << decl_name << "' specified with the "
+ << "--default-pointer option does not name a class "
+ << "template" << endl;
+
+ throw operation_failed ();
+ }
+
+ string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER));
+
+ // In case of a boost TR1 implementation, we cannot distinguish
+ // between the boost:: and std::tr1:: usage since the latter is
+ // just a using-declaration for the former.
+ //
+ tr1 = tr1
+ || n.compare (0, 8, "std::tr1") == 0
+ || n.compare (0, 10, "::std::tr1") == 0;
+
+ boost = boost
+ || n.compare (0, 17, "boost::shared_ptr") == 0
+ || n.compare (0, 19, "::boost::shared_ptr") == 0;
+ }
+ }
+
+ // Fully-qualify all the unqualified components of the name.
+ //
+ try
+ {
+ lex_.start (ptr);
+ ptr.clear ();
+
+ string t;
+ bool punc (false);
+ bool scoped (false);
+
+ for (cpp_ttype tt (lex_.next (t));
+ tt != CPP_EOF;
+ tt = lex_.next (t))
+ {
+ if (punc && tt > CPP_LAST_PUNCTUATOR)
+ ptr += ' ';
+
+ punc = false;
+
+ switch (static_cast<unsigned> (tt))
+ {
+ case CPP_LESS:
+ {
+ ptr += "< ";
+ break;
+ }
+ case CPP_GREATER:
+ {
+ ptr += " >";
+ break;
+ }
+ case CPP_COMMA:
+ {
+ ptr += ", ";
+ break;
+ }
+ case CPP_NAME:
+ {
+ // If the name was not preceeded with '::', look it
+ // up in the pragmas's scope and add the qualifer.
+ //
+ if (!scoped)
+ {
+ tree decl (resolve_name (t, resolve_scope, false));
+ tree scope (CP_DECL_CONTEXT (decl));
+
+ // If this is an inline namespace, skip it until we get
+ // to the non-inline one.
+ //
+ while (scope != global_namespace)
+ {
+ tree prev (CP_DECL_CONTEXT (scope));
+
+#if BUILDING_GCC_MAJOR >= 8
+ if (!is_nested_namespace (prev, scope, true))
+#else
+ if (!is_associated_namespace (prev, scope))
+#endif
+ break;
+
+ scope = prev;
+ }
+
+ if (scope != global_namespace)
+ {
+ ptr += "::";
+ ptr += decl_as_string (scope, TFF_PLAIN_IDENTIFIER);
+ }
+
+ ptr += "::";
+ }
+
+ ptr += t;
+ punc = true;
+ break;
+ }
+ case CPP_KEYWORD:
+ case CPP_NUMBER:
+ {
+ ptr += t;
+ punc = true;
+ break;
+ }
+ default:
+ {
+ ptr += t;
+ break;
+ }
+ }
+
+ scoped = (tt == CPP_SCOPE);
+ }
+ }
+ catch (cxx_lexer::invalid_input const&)
+ {
+ throw operation_failed ();
+ }
+
+ c.set ("object-pointer", ptr);
+ c.set ("object-pointer-raw", raw);
+ }
+ catch (invalid_name const& ex)
+ {
+ if (loc != 0)
+ error (loc)
+ << "name '" << ex.name () << "' specified with db pragma "
+ << "pointer is invalid" << endl;
+ else
+ error (c.file (), c.line (), c.column ())
+ << "name '" << ex.name () << "' specified with the "
+ << "--default-pointer option is invalid" << endl;
+
+
+ throw operation_failed ();
+ }
+ catch (unable_to_resolve const& ex)
+ {
+ if (loc != 0)
+ error (loc)
+ << "unable to resolve name '" << ex.name () << "' specified "
+ << "with db pragma pointer" << endl;
+ else
+ error (c.file (), c.line (), c.column ())
+ << "unable to resolve name '" << ex.name () << "' specified "
+ << "with the --default-pointer option" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ private:
+ struct invalid_name
+ {
+ invalid_name (string const& n): name_ (n) {}
+
+ string const&
+ name () const {return name_;}
+
+ private:
+ string name_;
+ };
+
+ typedef lookup::unable_to_resolve unable_to_resolve;
+
+ tree
+ resolve_name (string const& qn, tree scope, bool is_type)
+ {
+ try
+ {
+ string tl;
+ tree tn;
+ cpp_ttype tt, ptt;
+
+ nlex_.start (qn);
+ tt = nlex_.next (tl, &tn);
+
+ string name;
+ return lookup::resolve_scoped_name (
+ nlex_, tt, tl, tn, ptt, scope, name, is_type);
+ }
+ catch (cxx_lexer::invalid_input const&)
+ {
+ throw invalid_name (qn);
+ }
+ catch (lookup::invalid_name const&)
+ {
+ throw invalid_name (qn);
+ }
+ }
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ data_member member_;
+ traversal::names member_names_;
+
+ cxx_string_lexer lex_;
+ cxx_string_lexer nlex_; // Nested lexer.
+
+ semantics::type* std_string_;
+ semantics::names* std_string_hint_;
+
+ tree access_; // odb::access node.
+ };
+
+ static bool
+ check_to_from (const cxx_tokens& ex, const char* c, location_t l)
+ {
+ // Make sure we have one and only one placeholder (?).
+ //
+ bool r (false), m (true);
+
+ for (cxx_tokens::const_iterator i (ex.begin ()), e (ex.end ()); i != e;)
+ {
+ if (i->type == CPP_OPEN_PAREN)
+ {
+ if (++i != e && i->type == CPP_QUERY)
+ {
+ if (++i != e && i->type == CPP_CLOSE_PAREN)
+ {
+ if (r)
+ m = false; // Multiple (?), can't move.
+ else
+ r = true;
+ }
+ }
+ }
+ else
+ ++i;
+ }
+
+ if (!r)
+ {
+ error (l) << "no '(?)' expression in the '" << c << "' clause "
+ << "of db pragma map" << endl;
+
+ throw operation_failed ();
+ }
+
+ return m;
+ }
+}
+
+static void
+process1 (semantics::unit& u)
+{
+ // Process custom C++ type mapping.
+ //
+
+ // Create an empty list if we don't have one. This makes the
+ // rest of the code simpler.
+ //
+ if (!u.count ("custom-cxx-types"))
+ u.set ("custom-cxx-types", custom_cxx_types ());
+
+ custom_cxx_types & cts (u.get<custom_cxx_types> ("custom-cxx-types"));
+
+ for (custom_cxx_types::iterator i (cts.begin ()); i != cts.end (); ++i)
+ {
+ custom_cxx_type& ct (*i);
+
+ // type
+ //
+ if (ct.type_node == 0)
+ {
+ error (ct.loc) << "'type' clause expected in db pragma map" << endl;
+ throw operation_failed ();
+ }
+
+ ct.type = dynamic_cast<semantics::type*> (
+ u.find (TYPE_MAIN_VARIANT (ct.type_node)));
+ ct.type_hint = u.find_hint (ct.type_node);
+
+ // as
+ //
+ if (ct.as_node == 0)
+ {
+ error (ct.loc) << "'as' clause expected in db pragma map" << endl;
+ throw operation_failed ();
+ }
+
+ ct.as = dynamic_cast<semantics::type*> (
+ u.find (TYPE_MAIN_VARIANT (ct.as_node)));
+ ct.as_hint = u.find_hint (ct.as_node);
+
+ // to
+ //
+ {
+ cxx_tokens& e (ct.to);
+
+ if (e.empty ())
+ {
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_QUERY));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+ ct.to_move = true;
+ }
+ else
+ ct.to_move = check_to_from (e, "to", ct.loc);
+ }
+
+ // from
+ //
+ {
+ cxx_tokens& e (ct.from);
+
+ if (e.empty ())
+ {
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_QUERY));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+ ct.from_move = true;
+ }
+ else
+ ct.from_move = check_to_from (e, "from", ct.loc);
+ }
+
+ // Resolve mapping scope.
+ //
+ semantics::scope* s (dynamic_cast<semantics::scope*> (u.find (ct.scope)));
+ if (s == 0)
+ {
+ error (ct.loc) << "unable to resolve db pragma map scope" << endl;
+ throw operation_failed ();
+ }
+
+ if (semantics::namespace_* ns = dynamic_cast<semantics::namespace_*> (s))
+ {
+ if (ns->extension ())
+ s = &ns->original ();
+ }
+
+ // Enter into the map.
+ //
+ if (!s->count ("custom-cxx-type-map"))
+ s->set ("custom-cxx-type-map", custom_cxx_type_map ());
+
+ s->get<custom_cxx_type_map> ("custom-cxx-type-map")[ct.type] = &ct;
+ }
+}
+
+static void
+process2 (options const& ops, features& f, semantics::unit& u)
+{
+ unique_ptr<context> ctx (create_context (cerr, u, ops, f, 0));
+
+ // Common processing.
+ //
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (true);
+ traversal::namespace_ ns;
+ class_ c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (true);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx->unit);
+ }
+
+ // Database-specific processing.
+ //
+ switch (ops.database ()[0])
+ {
+ case database::common:
+ {
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ relational::process ();
+ break;
+ }
+ }
+}
+
+void
+process (options const& ops,
+ features& f,
+ semantics::unit& u,
+ semantics::path const&,
+ unsigned short pass)
+{
+ try
+ {
+ if (pass == 1)
+ process1 (u);
+ else if (pass == 2)
+ process2 (ops, f, u);
+ }
+ catch (operation_failed const&)
+ {
+ // Processing failed. Diagnostics has already been issued.
+ //
+ throw processor_failed ();
+ }
+}
diff --git a/odb/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/odb/profile.hxx b/odb/odb/profile.hxx
new file mode 100644
index 0000000..b6e8e53
--- /dev/null
+++ b/odb/odb/profile.hxx
@@ -0,0 +1,39 @@
+// file : odb/profile.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_PROFILE_HXX
+#define ODB_PROFILE_HXX
+
+#include <set>
+#include <vector>
+#include <string>
+
+#include <libcutl/fs/path.hxx>
+
+#include <odb/option-types.hxx>
+
+struct profile_data
+{
+ typedef cutl::fs::path path;
+ typedef std::vector<path> paths;
+
+ profile_data (paths const& p, database d, char const* n)
+ : search_paths (p), db (d), name (n)
+ {
+ }
+
+ paths const& search_paths;
+ database db;
+ char const* name;
+ std::set<path> loaded;
+};
+
+struct profile_failure {};
+
+std::string
+profile_search (char const* profile, void* arg);
+
+std::string
+profile_search_ignore (char const* profile, void* arg);
+
+#endif // ODB_PROFILE_HXX
diff --git a/odb/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/odb/relational/generate.hxx b/odb/odb/relational/generate.hxx
new file mode 100644
index 0000000..e597fb8
--- /dev/null
+++ b/odb/odb/relational/generate.hxx
@@ -0,0 +1,81 @@
+// file : odb/relational/generate.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_GENERATE_HXX
+#define ODB_RELATIONAL_GENERATE_HXX
+
+#include <string>
+#include <libcutl/shared-ptr.hxx>
+
+#include <odb/context.hxx>
+#include <odb/semantics/relational/model.hxx>
+#include <odb/semantics/relational/changeset.hxx>
+#include <odb/semantics/relational/changelog.hxx>
+
+namespace relational
+{
+ namespace header
+ {
+ void
+ generate ();
+ }
+
+ namespace inline_
+ {
+ void
+ generate ();
+ }
+
+ namespace source
+ {
+ void
+ generate ();
+ }
+
+ namespace model
+ {
+ cutl::shared_ptr<semantics::relational::model>
+ generate ();
+ }
+
+ namespace changelog
+ {
+ // Returns NULL if the changelog is unchanged.
+ //
+ cutl::shared_ptr<semantics::relational::changelog>
+ generate (semantics::relational::model&,
+ model_version const&,
+ semantics::relational::changelog* old, // Can be NULL.
+ std::string const& in_name,
+ std::string const& out_name,
+ options const&);
+ }
+
+ namespace schema
+ {
+ void
+ generate_prologue ();
+
+ void
+ generate_epilogue ();
+
+ void
+ generate_drop ();
+
+ void
+ generate_create ();
+
+ void
+ generate_migrate_pre (semantics::relational::changeset&);
+
+ void
+ generate_migrate_post (semantics::relational::changeset&);
+
+ // Generate embedded schema.
+ //
+ void
+ generate_source (semantics::relational::changelog*);
+ }
+}
+
+#endif // ODB_RELATIONAL_GENERATE_HXX
diff --git a/odb/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/odb/relational/header.hxx b/odb/odb/relational/header.hxx
new file mode 100644
index 0000000..964aff2
--- /dev/null
+++ b/odb/odb/relational/header.hxx
@@ -0,0 +1,1466 @@
+// file : odb/relational/header.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_HEADER_HXX
+#define ODB_RELATIONAL_HEADER_HXX
+
+#include <odb/relational/context.hxx>
+#include <odb/relational/common.hxx>
+
+namespace relational
+{
+ namespace header
+ {
+ //
+ // image_type
+ //
+
+ struct image_member: virtual member_base
+ {
+ typedef image_member base;
+
+ image_member (string const& var = string ())
+ : member_base (var, 0, 0, string (), string ()) {}
+
+ image_member (string const& var,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, ct, fq_type, key_prefix) {}
+ };
+
+ template <typename T>
+ struct image_member_impl: image_member, virtual member_base_impl<T>
+ {
+ typedef image_member_impl base_impl;
+
+ image_member_impl (base const& x)
+ : member_base::base (x), // virtual base
+ base (x),
+ member_image_type_ (base::type_override_,
+ base::custom_override_,
+ base::fq_type_override_,
+ base::key_prefix_)
+ {
+ }
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ using member_base_impl<T>::container;
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi))
+ return false;
+
+ image_type = member_image_type_->image_type (mi.m);
+
+ if (var_override_.empty ())
+ os << "// " << mi.m.name () << endl
+ << "//" << endl;
+
+ return true;
+ }
+
+ virtual void
+ traverse_pointer (member_info& mi)
+ {
+ // Object pointers in views require special treatment.
+ //
+ if (view_member (mi.m))
+ {
+ using semantics::class_;
+
+ class_& c (*mi.ptr);
+ class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ if (poly_derived)
+ // Use a helper to create a complete chain of images all
+ // the way to the root (see libodb/odb/view-image.hxx).
+ //
+ os << "view_object_image<" << endl
+ << " " << class_fq_name (c) << "," << endl
+ << " " << class_fq_name (*poly_root) << "," << endl
+ << " id_" << db << " >";
+ else
+ os << "object_traits_impl< " << class_fq_name (c) << ", " <<
+ "id_" << db << " >::image_type";
+
+ os << " " << mi.var << "value;"
+ << endl;
+ }
+ else
+ member_base_impl<T>::traverse_pointer (mi);
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << endl;
+ }
+
+ protected:
+ string image_type;
+ instance<member_image_type> member_image_type_;
+ };
+
+ struct image_base: traversal::class_, virtual context
+ {
+ typedef image_base base;
+
+ image_base (): first_ (true) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ bool obj (object (c));
+
+ // Ignore transient bases. Not used for views.
+ //
+ if (!(obj || composite (c)))
+ return;
+
+ if (first_)
+ {
+ os << ": ";
+ first_ = false;
+ }
+ else
+ {
+ os << "," << endl
+ << " ";
+ }
+
+ string const& type (class_fq_name (c));
+
+ if (obj)
+ os << "object_traits_impl< " << type << ", id_" << db <<
+ " >::image_type";
+ else
+ os << "composite_value_traits< " << type << ", id_" << db <<
+ " >::image_type";
+ }
+
+ private:
+ bool first_;
+ };
+
+ struct image_type: traversal::class_, virtual context
+ {
+ typedef image_type base;
+
+ image_type ()
+ {
+ *this >> names_member_ >> member_;
+ }
+
+ image_type (image_type const&)
+ : root_context (), context () //@@ -Wextra
+ {
+ *this >> names_member_ >> member_;
+ }
+
+ virtual void
+ image_extra (type&)
+ {
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ os << "struct image_type";
+
+ if (!view (c))
+ {
+ // Don't go into the base if we are a derived type in a
+ // polymorphic hierarchy.
+ //
+ if (!poly_derived)
+ {
+ instance<image_base> b;
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+ }
+
+ os << "{";
+
+ if (poly_derived)
+ os << "base_traits::image_type* base;"
+ << endl;
+
+ names (c);
+
+ // We don't need a version if this is a composite value type
+ // or reuse-abstract object.
+ //
+ if (!(composite (c) || (abstract (c) && !polymorphic (c))))
+ os << "std::size_t version;"
+ << endl;
+
+ image_extra (c);
+
+ os << "};";
+ }
+
+ private:
+ instance<image_member> member_;
+ traversal::names names_member_;
+ };
+
+ // Member-specific traits types for container members.
+ //
+ struct container_traits: object_members_base, virtual context
+ {
+ typedef container_traits base;
+
+ container_traits (semantics::class_& c)
+ : object_members_base (true, false, false), c_ (c)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&)
+ {
+ // We don't want to traverse composite id.
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ if (object (c_))
+ object_members_base::traverse_composite (m, c);
+ else
+ {
+ // If we are generating traits for a composite value type, then
+ // we don't want to go into its bases or it composite members.
+ //
+ if (m == 0 && &c == &c_)
+ names (c);
+ }
+ }
+
+ virtual void
+ container_public_extra_pre (semantics::data_member&, semantics::type&)
+ {
+ }
+
+ virtual void
+ container_public_extra_post (semantics::data_member&, semantics::type&)
+ {
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type& c)
+ {
+ using semantics::type;
+ using semantics::class_;
+
+ // Figure out if this member is from a base object or composite
+ // value and if it's from an object, whether it is reuse-abstract.
+ //
+ bool base, reuse_abst;
+
+ if (object (c_))
+ {
+ base = cur_object != &c_ ||
+ !object (dynamic_cast<type&> (m.scope ()));
+ reuse_abst = abstract (c_) && !polymorphic (c_);
+ }
+ else
+ {
+ base = false; // We don't go into bases.
+ reuse_abst = true; // Always abstract.
+ }
+
+ container_kind_type ck (container_kind (c));
+
+ const custom_cxx_type* vct (0);
+ const custom_cxx_type* ict (0);
+ const custom_cxx_type* kct (0);
+
+ type& vt (container_vt (m, &vct));
+ type* it (0);
+ type* kt (0);
+
+ bool ordered (false);
+ bool inverse (context::inverse (m, "value"));
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ {
+ it = &container_it (m, &ict);
+ ordered = true;
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ kt = &container_kt (m, &kct);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ bool smart (!inverse &&
+ (ck != ck_ordered || ordered) &&
+ container_smart (c));
+
+ string name (flat_prefix_ + public_name (m) + "_traits");
+
+ // Figure out column counts.
+ //
+ size_t id_columns, value_columns, data_columns, cond_columns;
+ bool versioned (context::versioned (m));
+
+ if (!reuse_abst)
+ {
+ type& idt (container_idt (m));
+
+ if (class_* idc = composite_wrapper (idt))
+ id_columns = column_count (*idc).total;
+ else
+ id_columns = 1;
+
+ data_columns = cond_columns = id_columns;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ // Add one for the index.
+ //
+ if (ordered)
+ {
+ data_columns++;
+
+ if (smart)
+ cond_columns++;
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ // Add some for the key.
+ //
+ size_t n;
+
+ class_* ptr (object_pointer (*kt));
+ semantics::type& t (ptr == 0 ? *kt : utype (*id_member (*ptr)));
+
+ if (class_* comp = composite_wrapper (t))
+ n = column_count (*comp).total;
+ else
+ n = 1;
+
+ data_columns += n;
+
+ // Key is not currently used (see also bind()).
+ //
+ // cond_columns += n;
+
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ // Not currently used (see also bind())
+ //
+ // Value is also a key.
+ //
+ // class_* ptr (object_pointer (vt));
+ // semantics::type& t (ptr == 0 ? vt : utype (*id_member (*ptr)));
+ //
+ // if (class_* comp = composite_wrapper (t))
+ // cond_columns += column_count (*comp).total;
+ // else
+ // cond_columns++;
+ //
+ break;
+ }
+ }
+
+ {
+ class_* ptr (object_pointer (vt));
+ semantics::type& t (ptr == 0 ? vt : utype (*id_member (*ptr)));
+
+ if (class_* comp = composite_wrapper (t))
+ value_columns = column_count (*comp).total;
+ else
+ value_columns = 1;
+
+ data_columns += value_columns;
+ }
+
+ // Store column counts for the source generator.
+ //
+ m.set ("id-column-count", id_columns);
+ m.set ("value-column-count", value_columns);
+ m.set ("cond-column-count", cond_columns);
+ m.set ("data-column-count", data_columns);
+ }
+
+ os << "// " << m.name () << endl
+ << "//" << endl
+ << "struct " << exp << name;
+
+ if (base)
+ {
+ semantics::class_& b (dynamic_cast<semantics::class_&> (m.scope ()));
+ string const& type (class_fq_name (b));
+
+ if (object (b))
+ os << ": access::object_traits_impl< " << type << ", id_" <<
+ db << " >::" << name;
+ else
+ os << ": access::composite_value_traits< " << type << ", id_" <<
+ db << " >::" << public_name (m) << "_traits"; // No prefix_.
+ }
+
+ os << "{";
+
+ container_public_extra_pre (m, c);
+
+ if (!reuse_abst)
+ {
+ // column_count
+ //
+ os << "static const std::size_t id_column_count = " <<
+ id_columns << "UL;";
+
+ if (smart)
+ os << "static const std::size_t value_column_count = " <<
+ value_columns << "UL;"
+ << "static const std::size_t cond_column_count = " <<
+ cond_columns << "UL;";
+
+ os << "static const std::size_t data_column_count = " <<
+ data_columns << "UL;"
+ << endl;
+
+ os << "static const bool versioned = " << versioned << ";"
+ << endl;
+
+ // Statements.
+ //
+ os << "static const char insert_statement[];"
+ << "static const char select_statement[];";
+
+ if (smart)
+ os << "static const char update_statement[];";
+
+ os << "static const char delete_statement[];"
+ << endl;
+ }
+
+ if (base)
+ {
+ container_public_extra_post (m, c);
+ os << "};";
+
+ return;
+ }
+
+ // container_type
+ // container_traits
+ // index_type
+ // key_type
+ // value_type
+ //
+ os << "typedef ";
+
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ if (semantics::type* wt = wrapper (t))
+ {
+ // Use the hint from the wrapper unless the wrapped type is
+ // qualified. In this case use the hint for the unqualified
+ // type.
+ //
+ hint = t.get<semantics::names*> ("wrapper-hint");
+ utype (*wt, hint);
+
+ os << c.fq_name (hint);
+ }
+ else
+ // t and c are the same.
+ //
+ os << t.fq_name (hint);
+ }
+
+ os << " container_type;";
+
+ os << "typedef" << endl
+ << "odb::access::container_traits<container_type>" << endl
+ << "container_traits_type;";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "typedef container_traits_type::index_type index_type;";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "typedef container_traits_type::key_type key_type;";
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << "typedef container_traits_type::value_type value_type;"
+ << endl;
+
+ // functions_type
+ //
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "typedef " << (smart ? "smart_" : "") <<
+ "ordered_functions<index_type, value_type> functions_type;";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "typedef map_functions<key_type, value_type> " <<
+ "functions_type;";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "typedef set_functions<value_type> functions_type;";
+ break;
+ }
+ }
+
+ os << "typedef " << db << "::" << (smart ? "smart_" : "")
+ << "container_statements< " << name << " > statements_type;"
+ << endl;
+
+ // cond_image_type (object id is taken from the object image).
+ //
+ // For dumb containers we use the id binding directly.
+ //
+ if (smart)
+ {
+ os << "struct cond_image_type"
+ << "{";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<image_member> im (
+ "index_", *it, ict, "index_type", "index");
+ im->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<image_member> im ("key_", *kt, kct, "key_type", "key");
+ im->traverse (m);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "// value" << endl
+ << "//" << endl;
+ instance<image_member> im (
+ "value_", vt, vct, "value_type", "value");
+ im->traverse (m);
+ break;
+ }
+ }
+
+ os << "std::size_t version;"
+ << "};";
+ }
+
+ // data_image_type (object id is taken from the object image)
+ //
+ os << "struct data_image_type"
+ << "{";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<image_member> im (
+ "index_", *it, ict, "index_type", "index");
+ im->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<image_member> im ("key_", *kt, kct, "key_type", "key");
+ im->traverse (m);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << "// value" << endl
+ << "//" << endl;
+ instance<image_member> im ("value_", vt, vct, "value_type", "value");
+ im->traverse (m);
+
+ os << "std::size_t version;"
+ << "};";
+
+ // bind (cond_image)
+ //
+ if (smart)
+ os << "static void" << endl
+ << "bind (" << bind_vector << "," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "cond_image_type&);"
+ << endl;
+
+ // bind (data_image)
+ //
+ os << "static void" << endl
+ << "bind (" << bind_vector << "," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "data_image_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ // bind (cond_image, data_image) (update)
+ //
+ if (smart)
+ {
+ os << "static void" << endl
+ << "bind (" << bind_vector << "," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "cond_image_type&," << endl
+ << "data_image_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ os << "static void" << endl
+ << "grow (data_image_type&," << endl
+ << truncated_vector;
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // init (data_image)
+ //
+ if (!inverse)
+ {
+ os << "static void" << endl
+ << "init (data_image_type&," << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "index_type*," << endl;
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "const key_type*," << endl;
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "const value_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // init (cond_image)
+ //
+ if (smart)
+ {
+ os << "static void" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "init (cond_image_type&, index_type);";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ // os << "init (data_image_type&, const key_type&);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ // os << "init (data_image_type&, const value_type&);";
+ break;
+ }
+ }
+
+ os << endl;
+ }
+
+ // init (data)
+ //
+ os << "static void" << endl
+ << "init (";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "index_type&," << endl;
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "key_type&," << endl;
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "value_type&," << endl;
+ os << "const data_image_type&," << endl
+ << "database*";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ // insert
+ //
+ os << "static void" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "insert (index_type, const value_type&, void*);";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "insert (const key_type&, const value_type&, void*);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "insert (const value_type&, void*);";
+ break;
+ }
+ }
+
+ os << endl;
+
+ // select
+ //
+ os << "static bool" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "select (index_type&, value_type&, void*);";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "select (key_type&, value_type&, void*);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "select (value_type&, void*);";
+ break;
+ }
+ }
+
+ os << endl;
+
+ // update
+ //
+ if (smart)
+ {
+ os << "static void" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "update (index_type, const value_type&, void*);";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ //os << "update (const key_type&, const value_type&, void*);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ //os << "update (const value_type&, const value_type&, void*);";
+ break;
+ }
+ }
+
+ os << endl;
+ }
+
+ // delete_
+ //
+ os << "static void" << endl
+ << "delete_ (";
+
+ if (smart)
+ {
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "index_type, ";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+ }
+
+ os << "void*);"
+ << endl;
+
+ // persist
+ //
+ if (!inverse)
+ {
+ os << "static void" << endl
+ << "persist (const container_type&," << endl
+ << "statements_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // load
+ //
+ os << "static void" << endl
+ << "load (container_type&," << endl
+ << "statements_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ // update
+ //
+ if (!(inverse || readonly (member_path_, member_scope_)))
+ {
+ os << "static void" << endl
+ << "update (const container_type&," << endl
+ << "statements_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // erase
+ //
+ if (!inverse)
+ {
+ os << "static void" << endl
+ << "erase (";
+
+ if (smart)
+ os << "const container_type*, ";
+
+ os << "statements_type&);"
+ << endl;
+ }
+
+ container_public_extra_post (m, c);
+
+ os << "};";
+ }
+
+ protected:
+ semantics::class_& c_;
+ };
+
+ //
+ //
+ struct section_traits: virtual context
+ {
+ typedef section_traits base;
+
+ section_traits (semantics::class_& c): c_ (c) {}
+
+ virtual void
+ section_public_extra_pre (user_section&)
+ {
+ }
+
+ virtual void
+ section_public_extra_post (user_section&)
+ {
+ }
+
+ virtual void
+ traverse (user_section& s)
+ {
+ semantics::class_* poly_root (polymorphic (c_));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c_);
+
+ semantics::data_member* opt (optimistic (c_));
+
+ // Treat the special version update sections as abstract in reuse
+ // inheritance.
+ //
+ bool reuse_abst (!poly &&
+ (abstract (c_) ||
+ s.special == user_section::special_version));
+
+ bool load (s.total != 0 && s.separate_load ());
+ bool load_con (s.containers && s.separate_load ());
+ bool load_opt (s.optimistic () && s.separate_load ());
+
+ bool update (s.total != s.inverse + s.readonly); // Always separate.
+ bool update_con (s.readwrite_containers);
+ bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
+
+ // Don't generate anything for empty sections.
+ //
+ if (!(load || load_con || load_opt ||
+ update || update_con || update_opt))
+ return;
+
+ // If we are adding a new section to a derived class in an optimistic
+ // hierarchy, then pretend it inherits from the special version update
+ // section.
+ //
+ user_section* rs (0);
+ if (opt != 0)
+ {
+ // Skip overrides and get to the new section if polymorphic.
+ //
+ for (rs = &s; poly && rs->base != 0; rs = rs->base) ;
+
+ if (rs != 0)
+ {
+ if (rs->object != &opt->scope ())
+ rs->base = &(poly ? poly_root : &opt->scope ())->
+ get<user_sections> ("user-sections").back ();
+ else
+ rs = 0;
+ }
+ }
+
+ string name (public_name (*s.member) + "_traits");
+
+ os << "// " << s.member->name () << endl
+ << "//" << endl
+ << "struct " << exp << name
+ << "{";
+
+ os << "typedef object_traits_impl<object_type, id_" << db <<
+ ">::image_type image_type;"
+ << "typedef object_traits_impl<object_type, id_" << db <<
+ ">::id_image_type id_image_type;"
+ << endl;
+
+ section_public_extra_pre (s);
+
+ // bind (id, image_type)
+ //
+ // If id is NULL, then id is ignored (select). Otherwise, it is
+ // copied at the end (update).
+ //
+ if (load || load_opt || update || update_opt)
+ {
+ os << "static std::size_t" << endl
+ << "bind (" << bind_vector << "," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "image_type&," << endl
+ << db << "::statement_kind";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // grow ()
+ //
+ // We have to have out own version because the truncated vector
+ // will have different number of elements.
+ //
+ if (generate_grow && (load || load_opt))
+ {
+ os << "static bool" << endl
+ << "grow (image_type&," << endl
+ << truncated_vector;
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // init (object, image)
+ //
+ if (load)
+ {
+ os << "static void" << endl
+ << "init (object_type&," << endl
+ << "const image_type&," << endl
+ << "database*";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // init (image, object)
+ //
+ if (update)
+ {
+ os << "static " << (generate_grow ? "bool" : "void") << endl
+ << "init (image_type&," << endl
+ << "const object_type&";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // The rest does not apply to reuse-abstract sections.
+ //
+ if (reuse_abst)
+ {
+ section_public_extra_post (s);
+ os << "};";
+ return;
+ }
+
+ // column_count
+ //
+ column_count_type const& cc (column_count (poly ? *poly_root : c_));
+
+ // Generate load and update column counts even when they are zero so
+ // that we can instantiate section_statements.
+ //
+ os << "static const std::size_t id_column_count = " << cc.id << "UL;";
+
+ os << "static const std::size_t managed_optimistic_load_column_count" <<
+ " = " << cc.optimistic_managed << "UL;"
+ << "static const std::size_t load_column_count = " <<
+ (load ? s.total_total () : 0) << "UL;";
+
+ os << "static const std::size_t managed_optimistic_update_column_count" <<
+ " = " << (poly_derived ? 0 : cc.optimistic_managed) << "UL;"
+ << "static const std::size_t update_column_count = " <<
+ (update ? s.total - s.inverse - s.readonly : 0) << "UL;"
+ << endl;
+
+ os << "static const bool versioned = " << s.versioned << ";"
+ << endl;
+
+ // Statements.
+ //
+ if (load || load_opt)
+ os << "static const char select_statement[];"
+ << endl;
+
+ if (update || update_opt)
+ os << "static const char update_statement[];"
+ << endl;
+
+ // Section statements.
+ //
+ if (load || load_opt || update || update_opt)
+ os << "typedef " << db << "::section_statements< object_type, " <<
+ name << " > statements_type;"
+ << endl;
+
+ // We pass statement cache instead of just statements because
+ // we may also need statements for containers.
+ //
+
+ // load ()
+ //
+ if (load || load_opt || load_con)
+ os << "static void" << endl
+ << "load (extra_statement_cache_type&, object_type&" <<
+ (poly ? ", bool top = true" : "") << ");"
+ << endl;
+
+ // update ()
+ //
+ if (update || update_opt || update_con)
+ os << "static void" << endl
+ << "update (extra_statement_cache_type&, const object_type&" <<
+ (poly_derived && s.base != 0 ? ", bool base = true" : "") << ");"
+ << endl;
+
+ section_public_extra_post (s);
+
+ os << "};";
+
+ if (rs != 0)
+ rs->base = 0;
+ }
+
+ protected:
+ semantics::class_& c_;
+ };
+
+ // First pass over objects, views, and composites. Some code must be
+ // split into two parts to deal with yet undefined types.
+ //
+ struct class1: traversal::class_, virtual context
+ {
+ typedef class1 base;
+
+ class1 ()
+ : typedefs_ (false),
+ id_image_member_ ("id_"),
+ version_image_member_ ("version_"),
+ discriminator_image_member_ ("discriminator_"),
+ query_columns_type_ (false, true, false),
+ pointer_query_columns_type_ (true, true, false)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ class1 (class1 const&)
+ : root_context (), //@@ -Wextra
+ context (),
+ typedefs_ (false),
+ id_image_member_ ("id_"),
+ version_image_member_ ("version_"),
+ discriminator_image_member_ ("discriminator_"),
+ query_columns_type_ (false, true, false),
+ pointer_query_columns_type_ (true, true, false)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ case class_composite: traverse_composite (c); break;
+ default: break;
+ }
+ }
+
+ virtual void
+ object_public_extra_pre (type&)
+ {
+ }
+
+ virtual void
+ object_public_extra_post (type&)
+ {
+ }
+
+ virtual void
+ traverse_object (type&);
+
+ virtual void
+ view_public_extra_pre (type&)
+ {
+ }
+
+ virtual void
+ view_public_extra_post (type&)
+ {
+ }
+
+ virtual void
+ traverse_view (type&);
+
+ virtual void
+ traverse_composite (type&);
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<image_type> image_type_;
+ instance<image_member> id_image_member_;
+ instance<image_member> version_image_member_;
+ instance<image_member> discriminator_image_member_;
+
+ instance<query_columns_type> query_columns_type_;
+ instance<query_columns_type> pointer_query_columns_type_;
+ };
+
+ // Second pass over objects, views, and composites.
+ //
+ struct class2: traversal::class_, virtual context
+ {
+ typedef class2 base;
+
+ class2 ()
+ : typedefs_ (false),
+ query_columns_type_ (false, true, false),
+ query_columns_type_inst_ (false, false, true),
+ view_query_columns_type_ (true)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ class2 (class2 const&)
+ : root_context (), //@@ -Wextra
+ context (),
+ typedefs_ (false),
+ query_columns_type_ (false, true, false),
+ query_columns_type_inst_ (false, false, true),
+ view_query_columns_type_ (true)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ case class_composite: traverse_composite (c); break;
+ default: break;
+ }
+ }
+
+ virtual void
+ traverse_object (type& c)
+ {
+ if (options.generate_query ())
+ {
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ // query_columns
+ //
+ // If we don't have any pointers, then query_columns is generated
+ // in pass 1 (see the comment in class1 for details).
+ //
+ if (has_a (c, test_pointer | include_base))
+ query_columns_type_->traverse (c);
+
+ // Generate extern template declarations.
+ //
+ if (multi_dynamic)
+ query_columns_type_inst_->traverse (c);
+ }
+
+ // Move header comment out of if-block if adding any code here.
+ }
+
+ virtual void
+ traverse_view (type& c)
+ {
+ // query_columns
+ //
+ if (c.get<size_t> ("object-count") != 0)
+ {
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ view_query_columns_type_->traverse (c);
+ }
+
+ // Move header comment out of if-block if adding any code here.
+ }
+
+ virtual void
+ traverse_composite (type&)
+ {
+ }
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<query_columns_type> query_columns_type_;
+ instance<query_columns_type> query_columns_type_inst_;
+ instance<view_query_columns_type> view_query_columns_type_;
+ };
+
+ struct include: virtual context
+ {
+ typedef include base;
+
+ virtual void
+ generate ()
+ {
+ os << "#include <odb/details/buffer.hxx>" << endl
+ << endl;
+
+ os << "#include <odb/" << db << "/version.hxx>" << endl
+ << "#include <odb/" << db << "/forward.hxx>" << endl
+ << "#include <odb/" << db << "/binding.hxx>" << endl
+ << "#include <odb/" << db << "/" << db << "-types.hxx>" << endl;
+
+ if (options.generate_query ())
+ {
+ os << "#include <odb/" << db << "/query.hxx>" << endl;
+
+ if (multi_dynamic)
+ os << "#include <odb/" << db << "/query-dynamic.hxx>" << endl;
+ }
+
+ os << endl;
+ }
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_HEADER_HXX
diff --git a/odb/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/odb/relational/inline.hxx b/odb/odb/relational/inline.hxx
new file mode 100644
index 0000000..a609cc1
--- /dev/null
+++ b/odb/odb/relational/inline.hxx
@@ -0,0 +1,693 @@
+// file : odb/relational/inline.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_INLINE_HXX
+#define ODB_RELATIONAL_INLINE_HXX
+
+#include <odb/diagnostics.hxx>
+#include <odb/relational/context.hxx>
+#include <odb/relational/common.hxx>
+
+namespace relational
+{
+ namespace inline_
+ {
+ //
+ // get/set null (composite value only)
+ //
+
+ struct null_member: virtual member_base
+ {
+ typedef null_member base;
+
+ null_member (bool get)
+ : member_base (0, 0, string (), string ()), get_ (get) {}
+
+ protected:
+ bool get_;
+ };
+
+ template <typename T>
+ struct null_member_impl: null_member, virtual member_base_impl<T>
+ {
+ typedef null_member_impl base_impl;
+
+ null_member_impl (base const& x): base (x) {}
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")"
+ << "{";
+ }
+
+ // If the whole value type is readonly, then set will never be
+ // called with sk == statement_update.
+ //
+ if (!get_ && !readonly (*context::top_object))
+ {
+ semantics::class_* c;
+
+ if (readonly (mi.m) || ((c = composite (mi.t)) && readonly (*c)))
+ os << "if (sk == statement_insert)" << endl;
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ if (added (mi.m) || deleted (mi.m))
+ os << "}";
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ string traits ("composite_value_traits< " + mi.fq_type () + ", id_" +
+ db.string () + " >");
+
+ if (get_)
+ os << "r = r && " << traits << "::get_null (" <<
+ "i." << mi.var << "value";
+ else
+ os << traits << "::set_null (i." << mi.var << "value, sk";
+
+ if (versioned (*composite (mi.t)))
+ os << ", svm";
+
+ os << ");";
+ }
+ };
+
+ struct null_base: traversal::class_, virtual context
+ {
+ typedef null_base base;
+
+ null_base (bool get): get_ (get) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ // Ignore transient bases.
+ //
+ if (!composite (c))
+ return;
+
+ string traits ("composite_value_traits< " + class_fq_name (c) +
+ ", id_" + db.string () + " >");
+
+ // If the derived value type is readonly, then set will never be
+ // called with sk == statement_update.
+ //
+ if (!get_ && readonly (c) && !readonly (*context::top_object))
+ os << "if (sk == statement_insert)" << endl;
+
+ if (get_)
+ os << "r = r && " << traits << "::get_null (i";
+ else
+ os << traits << "::set_null (i, sk";
+
+ if (versioned (c))
+ os << ", svm";
+
+ os << ");";
+ }
+
+ protected:
+ bool get_;
+ };
+
+ //
+ //
+ struct class_: traversal::class_, virtual context
+ {
+ typedef class_ base;
+
+ class_ ()
+ : typedefs_ (false),
+ get_null_base_ (true),
+ get_null_member_ (true),
+ set_null_base_ (false),
+ set_null_member_ (false)
+ {
+ init ();
+ }
+
+ class_ (class_ const&)
+ : root_context (), //@@ -Wextra
+ context (),
+ typedefs_ (false),
+ get_null_base_ (true),
+ get_null_member_ (true),
+ set_null_base_ (false),
+ set_null_member_ (false)
+ {
+ init ();
+ }
+
+ void
+ init ()
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+
+ get_null_base_inherits_ >> get_null_base_;
+ get_null_member_names_ >> get_null_member_;
+
+ set_null_base_inherits_ >> set_null_base_;
+ set_null_member_names_ >> set_null_member_;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ context::top_object = context::cur_object = &c;
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ case class_composite: traverse_composite (c); break;
+ default: break;
+ }
+
+ context::top_object = context::cur_object = 0;
+ }
+
+ virtual void
+ object_extra (type&)
+ {
+ }
+
+ virtual void
+ traverse_object (type& c)
+ {
+ using semantics::data_member;
+
+ data_member_path* id (id_member (c));
+ data_member* idf (id ? id->front () : 0);
+ bool auto_id (id && auto_ (*id));
+ bool base_id (id && &idf->scope () != &c); // Comes from base.
+ data_member* optimistic (context::optimistic (c));
+
+ // Base class that contains the object id and version for optimistic
+ // concurrency.
+ //
+ type* base (base_id ? dynamic_cast<type*> (&idf->scope ()) : 0);
+
+ type* poly_root (context::polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ bool abst (abstract (c));
+ bool reuse_abst (abst && !poly);
+
+ bool versioned (context::versioned (c));
+
+ string const& type (class_fq_name (c));
+ string traits ("access::object_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ object_extra (c);
+
+ if (id != 0 && base_id)
+ {
+ if (!poly_derived)
+ {
+ // id (id_image_type)
+ //
+ if (auto_id)
+ {
+ os << "inline" << endl
+ << traits << "::id_type" << endl
+ << traits << "::" << endl
+ << "id (const id_image_type& i)"
+ << "{"
+ << "return object_traits_impl< " << class_fq_name (*base) <<
+ ", id_" << db << " >::id (i);"
+ << "}";
+ }
+
+ // id (image_type)
+ //
+ if (options.generate_query ())
+ {
+ os << "inline" << endl
+ << traits << "::id_type" << endl
+ << traits << "::" << endl
+ << "id (const image_type& i)"
+ << "{"
+ << "return object_traits_impl< " << class_fq_name (*base) <<
+ ", id_" << db << " >::id (i);"
+ << "}";
+ }
+
+ // version (image_type)
+ //
+ if (optimistic != 0)
+ {
+ os << "inline" << endl
+ << traits << "::version_type" << endl
+ << traits << "::" << endl
+ << "version (const image_type& i)"
+ << "{"
+ << "return object_traits_impl< " << class_fq_name (*base) <<
+ ", id_" << db << " >::version (i);"
+ << "}";
+ }
+ }
+
+ // bind (id_image_type)
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "bind (" << bind_vector << " b, id_image_type& i" <<
+ (optimistic != 0 ? ", bool bv" : "") << ")"
+ << "{"
+ << "object_traits_impl< " << class_fq_name (*base) << ", id_" <<
+ db << " >::bind (b, i" << (optimistic != 0 ? ", bv" : "") << ");"
+ << "}";
+
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "init (id_image_type& i, const id_type& id" <<
+ (optimistic != 0 ? ", const version_type* v" : "") << ")"
+ << "{"
+ << "object_traits_impl< " << class_fq_name (*base) << ", id_" <<
+ db << " >::init (i, id" << (optimistic != 0 ? ", v" : "") << ");"
+ << "}";
+ }
+
+ if (poly_derived)
+ {
+ size_t depth (polymorphic_depth (c));
+
+ // check_version
+ //
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "check_version (const std::size_t* v, const image_type& i)"
+ << "{"
+ << "return ";
+
+ string image ("i.");
+ for (size_t i (0); i < depth; ++i)
+ {
+ os << (i == 0 ? "" : " ||") << endl
+ << " v[" << i << "UL] != " << image << "version";
+
+ image += "base->";
+ }
+
+ os << ";"
+ << "}";
+
+ // update_version
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "update_version (std::size_t* v, const image_type& i, " <<
+ db << "::binding* b)"
+ << "{";
+
+ image = "i.";
+ for (size_t i (0); i < depth; ++i)
+ {
+ os << "v[" << i << "UL] = " << image << "version;";
+ image += "base->";
+ }
+
+ // A poly-abstract class always has only one entry in the
+ // bindings array.
+ //
+ if (abst)
+ os << "b[0].version++;";
+ else
+ for (size_t i (0); i < depth; ++i)
+ os << "b[" << i << "UL].version++;";
+
+ os << "}";
+ }
+
+ // The rest does not apply to reuse-abstract objects.
+ //
+ if (reuse_abst)
+ return;
+
+ // erase (object_type)
+ //
+ if (id != 0 && !poly && optimistic == 0 &&
+ !has_a (c, test_smart_container))
+ {
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "erase (database& db, const object_type& obj)"
+ << "{"
+ << "callback (db, obj, callback_event::pre_erase);"
+ << "erase (db, id (obj));";
+
+ // Note that we don't reset sections since the object is now
+ // transient and the state of a section in a transient object
+ // is undefined.
+
+ os << "callback (db, obj, callback_event::post_erase);"
+ << "}";
+ }
+
+ // load (section) [thunk version; poly_derived is true]
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0 &&
+ uss.count (user_sections::count_new |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) == 0)
+ {
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "load (connection& conn, object_type& obj, section& s, " <<
+ "const info_type* pi)"
+ << "{"
+ << "return base_traits::load (conn, obj, s, pi);"
+ << "}";
+ }
+
+ // update (section) [thunk version; poly_derived is true]
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0 &&
+ uss.count (user_sections::count_new |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) == 0)
+ {
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "update (connection& conn, const object_type& obj, " <<
+ "const section& s, const info_type* pi)"
+ << "{"
+ << "return base_traits::update (conn, obj, s, pi);"
+ << "}";
+ }
+
+ // load_()
+ //
+ if (id != 0 &&
+ !(poly_derived ||
+ has_a (c, test_container | include_eager_load, &main_section) ||
+ uss.count (user_sections::count_new |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0))
+ {
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "load_ (statements_type& sts," << endl
+ << "object_type& obj," << endl
+ << "bool";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (sts);"
+ << "ODB_POTENTIALLY_UNUSED (obj);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl;
+
+ // Mark eager sections as loaded.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ data_member& m (*i->member);
+
+ // If the section is soft- added or deleted, check the version.
+ // We can only end up here if the object itself is versioned
+ // (simple value section).
+ //
+ unsigned long long av (added (m));
+ unsigned long long dv (deleted (m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")" << endl;
+ }
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << ma.translate ("obj") << ".reset (true, false);"
+ << endl;
+ }
+
+ os << "}";
+ }
+
+ if (poly && need_image_clone && options.generate_query ())
+ {
+ // root_image ()
+ //
+ os << "inline" << endl
+ << traits << "::root_traits::image_type&" << endl
+ << traits << "::" << endl
+ << "root_image (image_type& i)"
+ << "{";
+
+ if (poly_derived)
+ os << "return base_traits::root_image (*i.base);";
+ else
+ os << "return i;";
+
+ os << "}";
+
+ // clone_image ()
+ //
+ os << "inline" << endl
+ << traits << "::image_type*" << endl
+ << traits << "::" << endl
+ << "clone_image (image_type& i)"
+ << "{";
+
+ if (poly_derived)
+ os << "details::unique_ptr<base_traits::image_type> p (" << endl
+ << "base_traits::clone_image (*i.base));"
+ << "image_type* c (new image_type (i));"
+ << "c->base = p.release ();"
+ << "return c;";
+ else
+ os << "return new image_type (i);";
+
+ os << "}";
+
+ // copy_image ()
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "copy_image (image_type& d, image_type& s)"
+ << "{";
+
+ if (poly_derived)
+ os << "base_traits::image_type* b (d.base);"
+ << "base_traits::copy_image (*b, *s.base);"
+ << "d = s;" // Overwrites the base pointer.
+ << "d.base = b;";
+ else
+ os << "d = s;";
+
+ os << "}";
+
+ // free_image ()
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "free_image (image_type* i)"
+ << "{";
+
+ if (poly_derived)
+ os << "base_traits::free_image (i->base);";
+
+ os << "delete i;"
+ << "}";
+ }
+ }
+
+ virtual void
+ view_extra (type&)
+ {
+ }
+
+ virtual void
+ traverse_view (type& c)
+ {
+ string const& type (class_fq_name (c));
+ string traits ("access::view_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ view_extra (c);
+ }
+
+ virtual void
+ traverse_composite (type& c)
+ {
+ bool versioned (context::versioned (c));
+
+ string const& type (class_fq_name (c));
+ string traits ("access::composite_value_traits< " + type + ", id_" +
+ db.string () + " >");
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ if (!has_a (c, test_container))
+ {
+ // get_null (image)
+ //
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "get_null (const image_type& i";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);"
+ << endl;
+
+ os << "bool r (true);";
+
+ inherits (c, get_null_base_inherits_);
+ names (c, get_null_member_names_);
+
+ os << "return r;"
+ << "}";
+
+ // set_null (image)
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "set_null (image_type& i," << endl
+ << db << "::statement_kind sk";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (sk);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl;
+
+ inherits (c, set_null_base_inherits_);
+ names (c, set_null_member_names_);
+
+ os << "}";
+ }
+ }
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<null_base> get_null_base_;
+ traversal::inherits get_null_base_inherits_;
+ instance<null_member> get_null_member_;
+ traversal::names get_null_member_names_;
+
+ instance<null_base> set_null_base_;
+ traversal::inherits set_null_base_inherits_;
+ instance<null_member> set_null_member_;
+ traversal::names set_null_member_names_;
+ };
+
+ struct include: virtual context
+ {
+ typedef include base;
+
+ virtual void
+ generate ()
+ {
+ if (versioned ())
+ os << "#include <odb/schema-version.hxx>" << endl
+ << endl;
+
+ if (features.polymorphic_object && options.generate_query ())
+ os << "#include <odb/details/unique-ptr.hxx>" << endl
+ << endl;
+ }
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_INLINE_HXX
diff --git a/odb/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/odb/relational/model.hxx b/odb/odb/relational/model.hxx
new file mode 100644
index 0000000..fdfa8fd
--- /dev/null
+++ b/odb/odb/relational/model.hxx
@@ -0,0 +1,868 @@
+// file : odb/relational/model.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_MODEL_HXX
+#define ODB_RELATIONAL_MODEL_HXX
+
+#include <map>
+#include <set>
+#include <cassert>
+#include <sstream>
+
+#include <odb/semantics/relational.hxx>
+
+#include <odb/relational/common.hxx>
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ namespace model
+ {
+ typedef std::set<qname> tables;
+ typedef std::map<qname, semantics::node*> deleted_table_map;
+ typedef std::map<uname, semantics::data_member*> deleted_column_map;
+
+ struct object_columns: object_columns_base, virtual context
+ {
+ typedef object_columns base;
+
+ object_columns (sema_rel::model& model,
+ sema_rel::table& table,
+ bool object)
+ : model_ (model),
+ table_ (table),
+ object_ (object),
+ pkey_ (0),
+ id_override_ (false)
+ {
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ if (context::top_object != &c)
+ {
+ // We are in one of the bases. Set the id_prefix to its
+ // (unqualified) name.
+ //
+ string t (id_prefix_);
+ id_prefix_ = class_name (c) + "::";
+ object_columns_base::traverse_object (c);
+ id_prefix_ = t;
+ }
+ else
+ object_columns_base::traverse_object (c);
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ string t (id_prefix_);
+
+ if (m != 0)
+ // Member of a composite type. Add the data member to id_prefix.
+ //
+ if (!id_override_)
+ id_prefix_ += m->name () + ".";
+ else
+ id_override_ = false;
+ else
+ // Composite base. Add its unqualified name to id_prefix.
+ //
+ id_prefix_ += class_name (c) + "::";
+
+ object_columns_base::traverse_composite (m, c);
+
+ id_prefix_ = t;
+ }
+
+ virtual void
+ traverse (semantics::data_member& m,
+ semantics::type& t,
+ string const& kp,
+ string const& dn,
+ semantics::class_* to = 0)
+ {
+ // This overrides the member name for a composite container value
+ // or key.
+ //
+ if (!kp.empty ())
+ {
+ semantics::class_* c (object_pointer (t));
+ if (composite_wrapper (c == 0 ? t : utype (*id_member (*c))))
+ {
+ id_prefix_ = kp + ".";
+ id_override_ = true;
+ }
+ }
+
+ object_columns_base::traverse (m, t, kp, dn, to);
+ }
+
+ using object_columns_base::traverse;
+
+ virtual bool
+ traverse_column (semantics::data_member& m, string const& name, bool)
+ {
+ if (semantics::data_member* m = deleted_member (member_path_))
+ {
+ table_.get<deleted_column_map> ("deleted-map")[name] = m;
+ return false;
+ }
+
+ string col_id (id_prefix_ +
+ (key_prefix_.empty () ? m.name () : key_prefix_));
+
+ sema_rel::column& c (
+ model_.new_node<sema_rel::column> (col_id, type (m), null (m)));
+ c.set ("cxx-location", m.location ());
+ c.set ("member-path", member_path_);
+ model_.new_edge<sema_rel::unames> (table_, c, name);
+
+ // An id member cannot have a default value.
+ //
+ if (!object_columns_base::id ())
+ {
+ string const& d (default_ (m));
+
+ if (!d.empty ())
+ c.default_ (d);
+ }
+
+ // If we have options, add them.
+ //
+ string const& o (column_options (m, key_prefix_));
+
+ if (!o.empty ())
+ c.options (o);
+
+ constraints (m, name, col_id, c);
+ return true;
+ }
+
+ virtual string
+ type (semantics::data_member&)
+ {
+ return object_columns_base::column_type ();
+ }
+
+ virtual bool
+ null (semantics::data_member&)
+ {
+ return !object_columns_base::id () && object_columns_base::null ();
+ }
+
+ virtual string
+ default_null (semantics::data_member&)
+ {
+ return "NULL";
+ }
+
+ virtual string
+ default_bool (semantics::data_member&, bool v)
+ {
+ // Most databases do not support boolean literals. Those that
+ // do should override this.
+ //
+ return (v ? "1" : "0");
+ }
+
+ virtual string
+ default_integer (semantics::data_member&, unsigned long long v, bool neg)
+ {
+ std::ostringstream ostr;
+ ostr << (neg ? "-" : "") << v;
+ return ostr.str ();
+ }
+
+ virtual string
+ default_float (semantics::data_member&, double v)
+ {
+ std::ostringstream ostr;
+ ostr << v;
+ return ostr.str ();
+ }
+
+ virtual string
+ default_string (semantics::data_member&, string const& v)
+ {
+ return quote_string (v);
+ }
+
+ virtual string
+ default_enum (semantics::data_member&,
+ tree /*enumerator*/,
+ string const& /*name*/)
+ {
+ // Has to be implemented by the database-specific override.
+ //
+ assert (false);
+ return string ();
+ }
+
+ virtual void
+ primary_key (sema_rel::primary_key&)
+ {
+ }
+
+ virtual void
+ constraints (semantics::data_member& m,
+ string const& /* name */,
+ string const& /* id */,
+ sema_rel::column& c)
+ {
+ if (object_)
+ {
+ if (semantics::data_member* idm = id ())
+ {
+ if (pkey_ == 0)
+ {
+ pkey_ = &model_.new_node<sema_rel::primary_key> (
+ m.count ("auto"));
+ pkey_->set ("cxx-location", idm->location ());
+
+ // In most databases the primary key constraint can be
+ // manipulated without an explicit name. So we use the special
+ // empty name for primary keys in order not to clash with
+ // columns and other constraints. If the target database does
+ // not support unnamed primary key manipulation, then the
+ // database-specific code will have to come up with a suitable
+ // name.
+ //
+ model_.new_edge<sema_rel::unames> (table_, *pkey_, "");
+ primary_key (*pkey_);
+ }
+
+ model_.new_edge<sema_rel::contains> (*pkey_, c);
+ }
+ }
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore inverse object pointers.
+ //
+ if (inverse (m, key_prefix_))
+ return;
+
+ if (deleted (member_path_))
+ {
+ // Still traverse it as columns so that we can populate the
+ // deleted map.
+ //
+ object_columns_base::traverse_pointer (m, c);
+ return;
+ }
+
+ // Get the position of the last column.
+ //
+ sema_rel::table::names_iterator i (table_.names_end ());
+
+ while (i != table_.names_begin ())
+ {
+ --i;
+ if (i->nameable ().is_a<sema_rel::column> ())
+ break;
+ }
+
+ // Traverse the object pointer as columns.
+ //
+ object_columns_base::traverse_pointer (m, c);
+
+ // Get to the first column that we have added.
+ //
+ if (i != table_.names_end ())
+ ++i; // Next column.
+ else
+ i = table_.names_begin ();
+
+ foreign_key (m, c, i);
+ }
+
+ virtual void
+ traverse_points_to (semantics::data_member& m, semantics::class_& c)
+ {
+ if (deleted (member_path_))
+ {
+ // Still traverse it as columns so that we can populate the
+ // deleted map.
+ //
+ object_columns_base::traverse_points_to (m, c);
+ return;
+ }
+
+ // Get the position of the last column.
+ //
+ sema_rel::table::names_iterator i (table_.names_end ());
+
+ while (i != table_.names_begin ())
+ {
+ --i;
+ if (i->nameable ().is_a<sema_rel::column> ())
+ break;
+ }
+
+ // Traverse the data member as columns.
+ //
+ object_columns_base::traverse_points_to (m, c);
+
+ // Get to the first column that we have added.
+ //
+ if (i != table_.names_end ())
+ ++i; // Next column.
+ else
+ i = table_.names_begin ();
+
+ foreign_key (m, c, i);
+ }
+
+ virtual void
+ foreign_key (semantics::data_member& m,
+ semantics::class_& c,
+ sema_rel::table::names_iterator i)
+ {
+ using sema_rel::column;
+ using sema_rel::foreign_key;
+
+ string id (id_prefix_ +
+ (key_prefix_.empty () ? m.name () : key_prefix_));
+
+ deferrable def (
+ m.get<deferrable> ("deferrable",
+ options.fkeys_deferrable_mode ()[db]));
+
+ foreign_key::action_type on_delete (
+ m.get<foreign_key::action_type> (
+ "on-delete", foreign_key::no_action));
+
+ foreign_key& fk (
+ model_.new_node<foreign_key> (id, table_name (c), def, on_delete));
+
+ fk.set ("cxx-location", m.location ());
+
+ bool simple;
+
+ // Get referenced columns.
+ //
+ {
+ data_member_path& id (*id_member (c));
+
+ instance<object_columns_list> ocl;
+ ocl->traverse (id);
+
+ for (object_columns_list::iterator i (ocl->begin ());
+ i != ocl->end (); ++i)
+ fk.referenced_columns ().push_back (i->name);
+
+ simple = (fk.referenced_columns ().size () == 1);
+ }
+
+ // Get referencing columns.
+ //
+ for (; i != table_.names_end (); ++i)
+ {
+ if (column* c = dynamic_cast<column*> (&i->nameable ()))
+ model_.new_edge<sema_rel::contains> (fk, *c);
+ else
+ break;
+ }
+
+ // Derive the constraint name. Generally, we want it to be based
+ // on the column name. This is straightforward for single-column
+ // references. In case of a composite id, we will need to use the
+ // column prefix which is based on the data member name, unless
+ // overridden by the user. In the latter case the prefix can be
+ // empty, in which case we will just fall back on the member's
+ // public name.
+ //
+ string name;
+
+ if (simple)
+ name = fk.contains_begin ()->column ().name ();
+ else
+ {
+ string p (column_prefix (m, key_prefix_, default_name_).prefix);
+
+ if (p.empty ())
+ p = public_name_db (m);
+ else if (p[p.size () - 1] == '_')
+ p.resize (p.size () - 1); // Remove trailing underscore.
+
+ name = column_prefix_.prefix + p;
+ }
+
+ model_.new_edge<sema_rel::unames> (
+ table_, fk, fkey_name (table_.name (), name));
+ }
+
+ protected:
+ string
+ default_ (semantics::data_member&);
+
+ protected:
+ sema_rel::model& model_;
+ sema_rel::table& table_;
+ bool object_;
+ sema_rel::primary_key* pkey_;
+ string id_prefix_;
+ bool id_override_;
+ };
+
+ struct object_indexes: traversal::class_, virtual context
+ {
+ typedef object_indexes base;
+
+ object_indexes (sema_rel::model& model, sema_rel::table& table)
+ : model_ (model), table_ (table)
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ object_indexes (object_indexes const& x)
+ : root_context (), context (), //@@ -Wextra
+ model_ (x.model_), table_ (x.table_)
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ if (!object (c)) // Ignore transient bases.
+ return;
+
+ // Polymorphic bases get their own tables.
+ //
+ if (!polymorphic (c))
+ inherits (c);
+
+ indexes& ins (c.get<indexes> ("index"));
+
+ for (indexes::iterator i (ins.begin ()); i != ins.end (); ++i)
+ {
+ // Using index name as its id.
+ //
+ sema_rel::index& in (
+ model_.new_node<sema_rel::index> (
+ i->name, i->type, i->method, i->options));
+ in.set ("cxx-location", location (i->loc));
+ model_.new_edge<sema_rel::unames> (table_, in, i->name);
+
+ for (index::members_type::iterator j (i->members.begin ());
+ j != i->members.end (); ++j)
+ {
+ using sema_rel::column;
+
+ index::member& im (*j);
+
+ semantics::type* t (&utype (*im.path.back ()));
+
+ if (semantics::class_* ptr = object_pointer (*t))
+ t = &utype (*id_member (*ptr));
+
+ if (type* comp = composite_wrapper (*t))
+ {
+ // Composite value. Get the list of the columns. Note that
+ // the column prefix needs to contain all the components.
+ //
+ instance<object_columns_list> ocl (
+ column_prefix (im.path, true));
+ ocl->traverse (*comp);
+
+ for (object_columns_list::iterator i (ocl->begin ());
+ i != ocl->end (); ++i)
+ {
+ column* c (table_.find<column> (i->name));
+ assert (c != 0);
+ model_.new_edge<sema_rel::contains> (in, *c, im.options);
+ }
+ }
+ else
+ {
+ // Simple value. Get the column name and look it up in the
+ // table.
+ //
+ column* c (table_.find<column> (column_name (im.path)));
+ assert (c != 0);
+ model_.new_edge<sema_rel::contains> (in, *c, im.options);
+ }
+ }
+ }
+ }
+
+ private:
+ sema_rel::model& model_;
+ sema_rel::table& table_;
+
+ traversal::inherits inherits_;
+ };
+
+ struct member_create: object_members_base, virtual context
+ {
+ typedef member_create base;
+
+ member_create (sema_rel::model& model)
+ : object_members_base (false, true, false), model_ (model)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&)
+ {
+ // We don't want to traverse composite id.
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ if (context::top_object != &c)
+ {
+ // We are in one of the bases. Set the id_prefix to its
+ // (unqualified) name.
+ //
+ string t (id_prefix_);
+ id_prefix_ = class_name (c) + "::";
+ object_members_base::traverse_object (c);
+ id_prefix_ = t;
+ }
+ else
+ {
+ // Top-level object. Set its id as a prefix.
+ //
+ id_prefix_ = string (class_fq_name (c), 2) + "::";
+ object_members_base::traverse_object (c);
+ }
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ string t (id_prefix_);
+
+ if (m != 0)
+ // Member of a composite type. Add the data member to id_prefix.
+ //
+ id_prefix_ += m->name () + ".";
+ else
+ // Composite base. Add its unqualified name to id_prefix.
+ //
+ id_prefix_ += class_name (c) + "::";
+
+ object_members_base::traverse_composite (m, c);
+
+ id_prefix_ = t;
+ }
+
+ virtual string
+ table_options (semantics::data_member& m, semantics::type& ct)
+ {
+ return context::table_options (m, ct);
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type& ct)
+ {
+ using semantics::type;
+ using semantics::data_member;
+
+ using sema_rel::column;
+
+ // Ignore inverse containers of object pointers.
+ //
+ if (inverse (m, "value"))
+ return;
+
+ container_kind_type ck (container_kind (ct));
+ qname const& name (table_name (m, table_prefix_));
+
+ // Ignore deleted container members.
+ //
+ if (semantics::data_member* m = deleted_member (member_path_))
+ {
+ model_.get<deleted_table_map> ("deleted-map")[name] = m;
+ return;
+ }
+
+ // Add the [] decorator to distinguish this id from non-container
+ // ids (we don't want to ever end up comparing, for example, an
+ // object table to a container table).
+ //
+ string id (id_prefix_ + m.name () + "[]");
+
+ sema_rel::table& t (model_.new_node<sema_rel::table> (id));
+ t.set ("cxx-location", m.location ());
+ t.set ("member-path", member_path_);
+ t.set ("deleted-map", deleted_column_map ());
+ model_.new_edge<sema_rel::qnames> (model_, t, name);
+
+ t.options (table_options (m, ct));
+ t.extra ()["kind"] = "container";
+
+ // object_id
+ //
+ {
+ bool f (false); //@@ (im)persfect forwarding.
+ instance<object_columns> oc (model_, t, f);
+ oc->traverse (m, container_idt (m), "id", "object_id");
+ }
+
+ // Foreign key and index for the object id. Keep this foreign
+ // key first since we reply on this information to lookup the
+ // corresponding object table.
+ //
+ {
+ // Derive the name prefix. See the comment for the other foreign
+ // key code above.
+ //
+ // Note also that id_name can be a column prefix (if id is
+ // composite), in which case it can be empty. In this case
+ // we just fallback on the default name.
+ //
+ // Finally, this is a top-level column, so there is no column
+ // prefix.
+ //
+ string id_name (
+ column_name (m, "id", "object_id", column_prefix ()));
+
+ if (id_name.empty ())
+ id_name = "object_id";
+
+ // Foreign key.
+ //
+ sema_rel::foreign_key& fk (
+ model_.new_node<sema_rel::foreign_key> (
+ id + ".id",
+ table_name (*context::top_object),
+ sema_rel::deferrable::not_deferrable,
+ sema_rel::foreign_key::cascade));
+ fk.set ("cxx-location", m.location ());
+ model_.new_edge<sema_rel::unames> (
+ t, fk, fkey_name (t.name (), id_name));
+
+ // Get referenced columns.
+ //
+ {
+ data_member_path& id (*id_member (*context::top_object));
+
+ instance<object_columns_list> ocl;
+ ocl->traverse (id);
+
+ for (object_columns_list::iterator i (ocl->begin ());
+ i != ocl->end (); ++i)
+ fk.referenced_columns ().push_back (i->name);
+ }
+
+ // All the columns we have in this table so far are for the
+ // object id. Add them to the foreign key.
+ //
+ for (sema_rel::table::names_iterator i (t.names_begin ());
+ i != t.names_end ();
+ ++i)
+ {
+ if (column* c = dynamic_cast<column*> (&i->nameable ()))
+ model_.new_edge<sema_rel::contains> (fk, *c);
+ }
+
+ // Index. See if we have a custom index.
+ //
+ index* sin (m.count ("id-index") ? &m.get<index> ("id-index") : 0);
+ sema_rel::index* in (0);
+
+ if (sin != 0)
+ {
+ in = &model_.new_node<sema_rel::index> (
+ id + ".id", sin->type, sin->method, sin->options);
+ in->set ("cxx-location", location (sin->loc));
+ }
+ else
+ {
+ in = &model_.new_node<sema_rel::index> (id + ".id");
+ in->set ("cxx-location", m.location ());
+ }
+
+ model_.new_edge<sema_rel::unames> (
+ t,
+ *in,
+ sin != 0 && !sin->name.empty ()
+ ? sin->name
+ : index_name (name, id_name));
+
+ // All the columns we have in this table so far are for the
+ // object id. Add them to the index.
+ //
+ for (sema_rel::table::names_iterator i (t.names_begin ());
+ i != t.names_end ();
+ ++i)
+ {
+ if (column* c = dynamic_cast<column*> (&i->nameable ()))
+ model_.new_edge<sema_rel::contains> (
+ *in, *c, (sin != 0 ? sin->members.back ().options : ""));
+ }
+ }
+
+ // index (simple value)
+ //
+ bool ordered (ck == ck_ordered && !unordered (m));
+ if (ordered)
+ {
+ // Column.
+ //
+ {
+ bool f (false); //@@ (im)persfect forwarding.
+ instance<object_columns> oc (model_, t, f);
+ oc->traverse (m, container_it (m), "index", "index");
+ }
+
+ // This is a simple value so the name cannot be empty. It is
+ // also a top-level column, so there is no column prefix.
+ //
+ string col (column_name (m, "index", "index", column_prefix ()));
+
+ // Index. See if we have a custom index.
+ //
+ index* sin (m.count ("index-index")
+ ? &m.get<index> ("index-index")
+ : 0);
+ sema_rel::index* in (0);
+
+ if (sin != 0)
+ {
+ in = &model_.new_node<sema_rel::index> (
+ id + ".index", sin->type, sin->method, sin->options);
+ in->set ("cxx-location", location (sin->loc));
+ }
+ else
+ {
+ in = &model_.new_node<sema_rel::index> (id + ".index");
+ in->set ("cxx-location", m.location ());
+ }
+
+ model_.new_edge<sema_rel::unames> (
+ t,
+ *in,
+ sin != 0 && !sin->name.empty ()
+ ? sin->name
+ : index_name (name, col));
+
+ column* c (t.find<column> (col));
+ assert (c != 0);
+
+ model_.new_edge<sema_rel::contains> (
+ *in,
+ *c,
+ (sin != 0 ? sin->members.back ().options : ""));
+ }
+
+ // key
+ //
+ if (ck == ck_map || ck == ck_multimap)
+ {
+ bool f (false); //@@ (im)persfect forwarding.
+ instance<object_columns> oc (model_, t, f);
+ oc->traverse (m, container_kt (m), "key", "key");
+ }
+
+ // value
+ //
+ {
+ bool f (false); //@@ (im)persfect forwarding.
+ instance<object_columns> oc (model_, t, f);
+ oc->traverse (m, container_vt (m), "value", "value");
+ }
+ }
+
+ protected:
+ sema_rel::model& model_;
+ string id_prefix_;
+ };
+
+ struct class_: traversal::class_, virtual context
+ {
+ typedef class_ base;
+
+ class_ (sema_rel::model& model): model_ (model) {}
+
+ virtual string
+ table_options (type& c)
+ {
+ return context::table_options (c);
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ if (!options.at_once () && class_file (c) != unit.file ())
+ return;
+
+ if (!object (c))
+ return;
+
+ semantics::class_* poly (polymorphic (c));
+
+ if (abstract (c) && poly == 0)
+ return;
+
+ qname const& name (table_name (c));
+
+ // If the table with this name was already seen, assume the
+ // user knows what they are doing and skip it.
+ //
+ if (tables_.count (name))
+ return;
+
+ if (deleted (c))
+ {
+ model_.get<deleted_table_map> ("deleted-map")[name] = &c;
+ return;
+ }
+
+ string id (class_fq_name (c), 2); // Remove leading '::'.
+
+ sema_rel::table& t (model_.new_node<sema_rel::table> (id));
+ t.set ("cxx-location", c.location ());
+ t.set ("class", &c);
+ t.set ("deleted-map", deleted_column_map ());
+ model_.new_edge<sema_rel::qnames> (model_, t, name);
+
+ t.options (table_options (c));
+
+ t.extra ()["kind"] =(poly == 0
+ ? "object"
+ : (poly == &c
+ ? "polymorphic root object"
+ : "polymorphic derived object"));
+
+ // Add columns.
+ //
+ {
+ bool tr (true); //@@ (im)persfect forwarding.
+ instance<object_columns> oc (model_, t, tr);
+ oc->traverse (c);
+ }
+
+ // Add indexes.
+ //
+ {
+ instance<object_indexes> oi (model_, t);
+ oi->traverse (c);
+ }
+
+ tables_.insert (name);
+
+ // Create tables for members.
+ //
+ {
+ instance<member_create> mc (model_);
+ mc->traverse (c);
+ }
+ }
+
+ protected:
+ sema_rel::model& model_;
+ tables tables_;
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_MODEL_HXX
diff --git a/odb/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/odb/relational/mysql/model.cxx b/odb/odb/relational/mysql/model.cxx
new file mode 100644
index 0000000..17ed4c0
--- /dev/null
+++ b/odb/odb/relational/mysql/model.cxx
@@ -0,0 +1,161 @@
+// file : odb/relational/mysql/model.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/relational/model.hxx>
+#include <odb/relational/mysql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mysql
+ {
+ namespace model
+ {
+ namespace relational = relational::model;
+
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x): base (x) {}
+
+ virtual string
+ default_bool (semantics::data_member&, bool v)
+ {
+ // MySQL has TRUE and FALSE as just aliases for 1 and 0. Still
+ // use them for self-documentation.
+ //
+ return v ? "TRUE" : "FALSE";
+ }
+
+ virtual string
+ default_enum (semantics::data_member& m, tree en, string const& name)
+ {
+ // Make sure the column is mapped to an ENUM or integer type.
+ //
+ sql_type const& t (parse_sql_type (column_type (), m, false));
+
+ switch (t.type)
+ {
+ case sql_type::ENUM:
+ case sql_type::TINYINT:
+ case sql_type::SMALLINT:
+ case sql_type::MEDIUMINT:
+ case sql_type::INT:
+ case sql_type::BIGINT:
+ break;
+ default:
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: column with default value specified as C++ "
+ << "enumerator must map to MySQL ENUM or integer type"
+ << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ using semantics::enum_;
+ using semantics::enumerator;
+
+ enumerator& er (dynamic_cast<enumerator&> (*unit.find (en)));
+ enum_& e (er.enum_ ());
+
+ if (t.type == sql_type::ENUM)
+ {
+ // Assuming the enumerators in the C++ enum and MySQL ENUM are
+ // in the same order, calculate the poistion of the C++
+ // enumerator and use that as an index in the MySQL ENUM.
+ //
+ size_t pos (0);
+
+ for (enum_::enumerates_iterator i (e.enumerates_begin ()),
+ end (e.enumerates_end ()); i != end; ++i)
+ {
+ if (&i->enumerator () == &er)
+ break;
+
+ pos++;
+ }
+
+ if (pos < t.enumerators.size ())
+ return t.enumerators[pos];
+ else
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: unable to map C++ enumerator '" << name
+ << "' to MySQL ENUM value" << endl;
+
+ throw operation_failed ();
+ }
+ }
+ else
+ {
+ ostringstream ostr;
+
+ if (e.unsigned_ ())
+ ostr << er.value ();
+ else
+ ostr << static_cast<long long> (er.value ());
+
+ return ostr.str ();
+ }
+ }
+ };
+ entry<object_columns> object_columns_;
+
+ struct member_create: relational::member_create, context
+ {
+ member_create (base const& x): base (x) {}
+
+ virtual string
+ table_options (semantics::data_member& m, semantics::type& c)
+ {
+ string r (relational::member_create::table_options (m, c));
+
+ string const& engine (options.mysql_engine ());
+ if (engine != "default")
+ {
+ // Note: MySQL table options can be separated with spaces.
+ //
+ if (!r.empty ())
+ r += ' ';
+
+ r += "ENGINE=";
+ r += engine;
+ }
+
+ return r;
+ }
+ };
+ entry<member_create> member_create_;
+
+ struct class_: relational::class_, context
+ {
+ class_ (base const& x): base (x) {}
+
+ virtual string
+ table_options (type& c)
+ {
+ string r (relational::class_::table_options (c));
+
+ string const& engine (options.mysql_engine ());
+ if (engine != "default")
+ {
+ // Note: MySQL table options can be separated with spaces.
+ //
+ if (!r.empty ())
+ r += ' ';
+
+ r += "ENGINE=";
+ r += engine;
+ }
+
+ return r;
+ }
+ };
+ entry<class_> class__;
+ }
+ }
+}
diff --git a/odb/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/odb/relational/pgsql/context.cxx b/odb/odb/relational/pgsql/context.cxx
new file mode 100644
index 0000000..7f99f5d
--- /dev/null
+++ b/odb/odb/relational/pgsql/context.cxx
@@ -0,0 +1,786 @@
+// file : odb/relational/pgsql/context.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/diagnostics.hxx>
+
+#include <odb/sql-token.hxx>
+#include <odb/sql-lexer.hxx>
+
+#include <odb/relational/pgsql/context.hxx>
+#include <odb/relational/pgsql/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace
+ {
+ struct type_map_entry
+ {
+ char const* const cxx_type;
+ char const* const db_type;
+ char const* const db_id_type;
+ bool const null;
+ };
+
+ type_map_entry type_map[] =
+ {
+ {"bool", "BOOLEAN", 0, false},
+
+ {"char", "CHAR(1)", 0, false},
+ {"signed char", "SMALLINT", 0, false},
+ {"unsigned char", "SMALLINT", 0, false},
+
+ {"short int", "SMALLINT", 0, false},
+ {"short unsigned int", "SMALLINT", 0, false},
+
+ {"int", "INTEGER", 0, false},
+ {"unsigned int", "INTEGER", 0, false},
+
+ {"long int", "BIGINT", 0, false},
+ {"long unsigned int", "BIGINT", 0, false},
+
+ {"long long int", "BIGINT", 0, false},
+ {"long long unsigned int", "BIGINT", 0, false},
+
+ {"float", "REAL", 0, false},
+ {"double", "DOUBLE PRECISION", 0, false},
+
+ {"::std::string", "TEXT", 0, false},
+
+ {"::size_t", "BIGINT", 0, false},
+ {"::std::size_t", "BIGINT", 0, false}
+ };
+ }
+
+ context* context::current_;
+
+ context::
+ ~context ()
+ {
+ if (current_ == this)
+ current_ = 0;
+ }
+
+ context::
+ context (ostream& os,
+ semantics::unit& u,
+ options_type const& ops,
+ features_type& f,
+ sema_rel::model* m)
+ : root_context (os, u, ops, f, data_ptr (new (shared) data (os))),
+ base_context (static_cast<data*> (root_context::data_.get ()), m),
+ data_ (static_cast<data*> (base_context::data_))
+ {
+ assert (current_ == 0);
+ current_ = this;
+
+ generate_grow = true;
+ need_alias_as = true;
+ insert_send_auto_id = false;
+ delay_freeing_statement_result = false;
+ need_image_clone = false;
+ generate_bulk = true;
+ global_index = true;
+ global_fkey = false;
+ data_->bind_vector_ = "pgsql::bind*";
+ data_->truncated_vector_ = "bool*";
+
+ // Populate the C++ type to DB type map.
+ //
+ for (size_t i (0); i < sizeof (type_map) / sizeof (type_map_entry); ++i)
+ {
+ type_map_entry const& e (type_map[i]);
+
+ type_map_type::value_type v (
+ e.cxx_type,
+ db_type_type (
+ e.db_type, e.db_id_type ? e.db_id_type : e.db_type, e.null));
+
+ data_->type_map_.insert (v);
+ }
+ }
+
+ context::
+ context ()
+ : data_ (current ().data_)
+ {
+ }
+
+ namespace
+ {
+ struct has_grow: traversal::class_
+ {
+ has_grow (bool& r, user_section* s)
+ : r_ (r), section_ (s)
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ // Ignore transient bases.
+ //
+ if (!(context::object (c) || context::composite (c)))
+ return;
+
+ if (section_ == 0 && c.count ("pgsql-grow"))
+ r_ = c.get<bool> ("pgsql-grow");
+ else
+ {
+ // r_ should be false.
+ //
+ inherits (c);
+
+ if (!r_)
+ names (c);
+
+ if (section_ == 0)
+ c.set ("pgsql-grow", r_);
+ }
+ }
+
+ private:
+ bool& r_;
+ user_section* section_;
+ traversal::inherits inherits_;
+ };
+
+ struct has_grow_member: member_base
+ {
+ has_grow_member (bool& r, user_section* section = 0)
+ : relational::member_base (0, 0, string (), string (), section),
+ r_ (r) {}
+
+ has_grow_member (bool& r,
+ user_section* section,
+ semantics::type* t,
+ const custom_cxx_type* ct,
+ string const& key_prefix = string ())
+ : relational::member_base (t, ct, string (), key_prefix, section),
+ r_ (r) {}
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ // If we have a key prefix (container), then it can't be in a
+ // section (while mi.m can). The same for top-level -- if we got
+ // called, then we shouldn't ignore it.
+ //
+ return !key_prefix_.empty () || top_level_ ||
+ (section_ == 0 && !separate_load (mi.m)) ||
+ (section_ != 0 && *section_ == section (mi.m));
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ // By calling grow() instead of recursing, we reset any overrides.
+ // We also don't pass section since they don't apply inside
+ // composites.
+ //
+ r_ = r_ || context::grow (dynamic_cast<semantics::class_&> (mi.t));
+ }
+
+ virtual void
+ traverse_numeric (member_info&)
+ {
+ r_ = true;
+ }
+
+ virtual void
+ traverse_string (member_info&)
+ {
+ r_ = true;
+ }
+
+ virtual void
+ traverse_varbit (member_info&)
+ {
+ r_ = true;
+ }
+
+ private:
+ bool& r_;
+ };
+ }
+
+ bool context::
+ grow_impl (semantics::class_& c, user_section* section)
+ {
+ if (section == 0 && c.count ("pgsql-grow"))
+ return c.get<bool> ("pgsql-grow");
+
+ bool r (false);
+ has_grow ct (r, section);
+ has_grow_member mt (r, section);
+ traversal::names names;
+ ct >> names >> mt;
+ ct.traverse (c);
+ return r;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member& m)
+ {
+ bool r (false);
+ has_grow_member mt (r);
+ mt.traverse (m, true);
+ return r;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member& m,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& kp)
+ {
+ bool r (false);
+ has_grow_member mt (r, 0, &t, ct, kp);
+ mt.traverse (m, true);
+ return r;
+ }
+
+ string const& context::
+ convert_expr (string const& sqlt, semantics::data_member& m, bool to)
+ {
+ sql_type const& t (parse_sql_type (sqlt, m));
+ return to ? t.to : t.from;
+ }
+
+ string context::
+ quote_id_impl (qname const& id) const
+ {
+ string r;
+
+ bool f (true);
+ for (qname::iterator i (id.begin ()); i < id.end (); ++i)
+ {
+ if (i->empty ())
+ continue;
+
+ // Warn if the name is greater than the (NAMEDATALEN - 1) limit,
+ // which is 63 in the default PG build.
+ //
+ if (i->size () > 63)
+ {
+ cerr << "warning: SQL name '" << *i << "' is longer than "
+ << "the default PostgreSQL name limit of 63 characters "
+ << "and may be truncated" << endl;
+
+ cerr << "info: consider shortening it using #pragma db "
+ << "table/column/index or --*-regex options" << endl;
+ }
+
+ if (f)
+ f = false;
+ else
+ r += '.';
+
+ r += '"';
+ r += *i;
+ r += '"';
+ }
+
+ return r;
+ }
+
+ string context::
+ statement_name (string const& type, string const& name, semantics::node& n)
+ {
+ // Put the type first so that in the case of truncation it
+ // remains thus lowering the chance of a clash.
+ //
+ string r (type);
+ r += '_';
+ r += name;
+
+ r = transform_name (r, sql_name_statement);
+
+ // Warn if the name is greater than the (NAMEDATALEN - 1) limit,
+ // which is 63 in the default PG build.
+ //
+ // Note that we have to do it in addition to the above since this
+ // name doesn't go through quote_id().
+ //
+ if (r.size () > 63)
+ {
+ location const& l (n.location ());
+
+ warn (l) << "prepared statement name '" << r << "' is longer than "
+ << "the default PostgreSQL name limit of 63 characters "
+ << "and may be truncated" << endl;
+
+ info (l) << "consider shortening the corresponding namespace "
+ << "name, class name, or data member name" << endl;
+
+ info (l) << "or shortening the statement name itself using the "
+ << "--statement-regex option" << endl;
+ }
+
+ return r;
+ }
+
+ string context::
+ database_type_impl (semantics::type& t,
+ semantics::names* hint,
+ bool id,
+ bool* null)
+ {
+ string r (base_context::database_type_impl (t, hint, id, null));
+
+ if (!r.empty ())
+ return r;
+
+ using semantics::array;
+
+ // char[N] mapping.
+ //
+ if (array* a = dynamic_cast<array*> (&t))
+ {
+ semantics::type& bt (a->base_type ());
+ if (bt.is_a<semantics::fund_char> ())
+ {
+ unsigned long long n (a->size ());
+
+ if (n == 0)
+ return r;
+ else if (n == 1)
+ r = "CHAR(";
+ else
+ {
+ r = "VARCHAR(";
+ n--;
+ }
+
+ ostringstream ostr;
+ ostr << n;
+ r += ostr.str ();
+ r += ')';
+ }
+ }
+
+ return r;
+ }
+
+ //
+ // SQL type parsing.
+ //
+
+ sql_type const& context::
+ parse_sql_type (string const& t, semantics::data_member& m, bool custom)
+ {
+ // If this proves to be too expensive, we can maintain a cache of
+ // parsed types across contexts.
+ //
+ data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t));
+
+ if (i != data_->sql_type_cache_.end ()
+ && (custom ? i->second.custom_cached : i->second.straight_cached))
+ {
+ return (custom ? i->second.custom : i->second.straight);
+ }
+ else
+ {
+ try
+ {
+ sql_type st (
+ parse_sql_type (
+ t,
+ custom ? &unit.get<custom_db_types> ("custom-db-types") : 0));
+
+ if (custom)
+ return data_->sql_type_cache_[t].cache_custom (st);
+ else
+ return data_->sql_type_cache_[t].cache_straight (st);
+ }
+ catch (invalid_sql_type const& e)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: " << e.message () << endl;
+
+ throw operation_failed ();
+ }
+ }
+ }
+
+ inline sql_type
+ error (bool fail, string const& m)
+ {
+ if (!fail)
+ return sql_type ();
+ else
+ throw context::invalid_sql_type (m);
+ }
+
+ sql_type context::
+ parse_sql_type (string sqlt, custom_db_types const* ct)
+ {
+ try
+ {
+ sql_type r;
+
+ // First run the type through the custom mapping, if requested.
+ //
+ if (ct != 0)
+ {
+ for (custom_db_types::const_iterator i (ct->begin ());
+ i != ct->end (); ++i)
+ {
+ custom_db_type const& t (*i);
+
+ if (t.type.match (sqlt))
+ {
+ r.to = t.type.replace (sqlt, t.to);
+ r.from = t.type.replace (sqlt, t.from);
+ sqlt = t.type.replace (sqlt, t.as);
+ break;
+ }
+ }
+ }
+
+ sql_lexer l (sqlt);
+
+ // While most type names use single identifier, there are
+ // a couple of exceptions to this rule:
+ //
+ // BIT VARYING (VARBIT)
+ // CHARACTER VARYING (VARRCHAR)
+ // DOUBLE PRECISION (DOUBLE)
+ // TIME WITH TIME ZONE (not currently supported)
+ // TIMESTAMP WITH TIME ZONE (not currently supported)
+ //
+
+ enum state
+ {
+ parse_prefix,
+ parse_name,
+ parse_range,
+ parse_suffix,
+ parse_done
+ };
+
+ state s (parse_prefix);
+ string prefix;
+ bool flt (false);
+
+ for (sql_token t (l.next ());
+ s != parse_done && t.type () != sql_token::t_eos;
+ t = l.next ())
+ {
+ sql_token::token_type tt (t.type ());
+
+ switch (s)
+ {
+ case parse_prefix:
+ {
+ if (tt == sql_token::t_identifier)
+ {
+ string const& id (context::upcase (t.identifier ()));
+
+ if (id == "BIT" ||
+ id == "CHARACTER" ||
+ id == "DOUBLE")
+ {
+ prefix = id;
+ s = parse_name;
+ continue;
+ }
+ }
+
+ s = parse_name;
+ }
+ // Fall through.
+ case parse_name:
+ {
+ if (tt == sql_token::t_identifier)
+ {
+ bool match (true);
+ string const& id (context::upcase (t.identifier ()));
+
+ //
+ // Numeric types.
+ //
+ if (id == "BOOL" || id == "BOOLEAN")
+ {
+ r.type = sql_type::BOOLEAN;
+ }
+ else if (id == "SMALLINT" || id == "INT2")
+ {
+ r.type = sql_type::SMALLINT;
+ }
+ else if (id == "INT" ||
+ id == "INTEGER" ||
+ id == "INT4")
+ {
+ r.type = sql_type::INTEGER;
+ }
+ else if (id == "BIGINT")
+ {
+ r.type = sql_type::BIGINT;
+ }
+ else if (id == "REAL" || id == "FLOAT4")
+ {
+ r.type = sql_type::REAL;
+ }
+ else if ((id == "PRECISION" && prefix == "DOUBLE") ||
+ id == "FLOAT8")
+ {
+ r.type = sql_type::DOUBLE;
+ }
+ else if (id == "FLOAT")
+ {
+ // Assign a type only once we know the precision of the
+ // float.
+ //
+ flt = true;
+ }
+ else if (id == "NUMERIC" || id == "DECIMAL")
+ {
+ r.type = sql_type::NUMERIC;
+ }
+ //
+ // Date-time types.
+ //
+ else if (id == "DATE")
+ {
+ r.type = sql_type::DATE;
+ }
+ else if (id == "TIME")
+ {
+ r.type = sql_type::TIME;
+ }
+ else if (id == "TIMETZ")
+ {
+ return error (ct, "PostgreSQL time zones are not currently "
+ "supported");
+ }
+ else if (id == "TIMESTAMP")
+ {
+ r.type = sql_type::TIMESTAMP;
+ }
+ else if (id == "TIMESTAMPTZ")
+ {
+ return error (ct, "PostgreSQL time zones are not currently "
+ "supported");
+ }
+ //
+ // String and binary types.
+ //
+ else if (id == "CHAR")
+ {
+ r.type = sql_type::CHAR;
+ }
+ else if (id == "VARCHAR")
+ {
+ r.type = sql_type::VARCHAR;
+ }
+ else if (id == "TEXT")
+ {
+ r.type = sql_type::TEXT;
+ }
+ else if (id == "VARYING")
+ {
+ if (prefix == "BIT")
+ r.type = sql_type::VARBIT;
+ else if (prefix == "CHARACTER")
+ r.type = sql_type::VARCHAR;
+ }
+ else if (id == "BYTEA")
+ {
+ r.type = sql_type::BYTEA;
+ }
+ else if (id == "VARBIT")
+ {
+ r.type = sql_type::VARBIT;
+ }
+ //
+ // Other types.
+ //
+ else if (id == "UUID")
+ {
+ r.type = sql_type::UUID;
+ }
+ else
+ match = false;
+
+ if (match)
+ {
+ s = parse_range;
+ continue;
+ }
+ }
+
+ // Some prefixes can also be type names if not followed
+ // by the actual type name.
+ //
+ if (!prefix.empty ())
+ {
+ if (prefix == "BIT")
+ {
+ r.type = sql_type::BIT;
+ }
+ else if (prefix == "CHARACTER")
+ {
+ r.type = sql_type::CHAR;
+ }
+ }
+
+ if (r.type == sql_type::invalid)
+ {
+ return error (
+ ct,
+ tt == sql_token::t_identifier
+ ? "unknown PostgreSQL type '" + t.identifier () + "'"
+ : "expected PostgreSQL type name");
+ }
+
+ s = parse_range;
+ }
+ // Fall through.
+ case parse_range:
+ {
+ if (t.punctuation () == sql_token::p_lparen)
+ {
+ t = l.next ();
+
+ if (t.type () != sql_token::t_int_lit)
+ {
+ return error (ct, "integer range expected in PostgreSQL "
+ "type declaration");
+ }
+
+ unsigned int v;
+ istringstream is (t.literal ());
+
+ if (!(is >> v && is.eof ()))
+ {
+ return error (ct, "invalid range value '" + t.literal () +
+ "' in PostgreSQL type declaration");
+ }
+
+ r.range = true;
+ r.range_value = v;
+
+ t = l.next ();
+
+ if (t.punctuation () == sql_token::p_comma)
+ {
+ // We have the second range value. Skip it.
+ //
+ l.next ();
+ t = l.next ();
+ }
+
+ if (t.punctuation () != sql_token::p_rparen)
+ {
+ return error (ct, "expected ')' in PostgreSQL type "
+ "declaration");
+ }
+
+ s = parse_suffix;
+ continue;
+ }
+
+ s = parse_suffix;
+ }
+ // Fall through.
+ case parse_suffix:
+ {
+ if (r.type == sql_type::TIME || r.type == sql_type::TIMESTAMP)
+ {
+ string const& id1 (context::upcase (t.identifier ()));
+
+ if (id1 == "WITH")
+ {
+ t = l.next ();
+ tt = t.type ();
+
+ if (tt == sql_token::t_identifier)
+ {
+ string const& id2 (context::upcase (t.identifier ()));
+
+ if (id2 == "TIME")
+ {
+ t = l.next ();
+ tt = t.type ();
+
+ if (tt == sql_token::t_identifier)
+ {
+ string const& id3 (context::upcase (t.identifier ()));
+
+ if (id3 == "ZONE")
+ {
+ // This code shall not fall through.
+ //
+ return error (ct, "PostgreSQL time zones are not "
+ "currently supported");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return error (
+ ct,
+ tt == sql_token::t_identifier
+ ? "unknown PostgreSQL type '" + t.identifier () + "'"
+ : "unknown PostgreSQL type");
+ }
+ case parse_done:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ if (s == parse_name && !prefix.empty ())
+ {
+ // Some prefixes can also be type names if not followed
+ // by the actual type name.
+ //
+ if (prefix == "BIT")
+ {
+ r.type = sql_type::BIT;
+ }
+ else if (prefix == "CHARACTER")
+ {
+ r.type = sql_type::CHAR;
+ }
+ }
+
+ if (flt)
+ {
+ r.type = r.range && r.range_value < 25 ?
+ sql_type::REAL :
+ sql_type::DOUBLE;
+ }
+
+ if (r.type == sql_type::invalid)
+ return error (ct, "incomplete PostgreSQL type declaration");
+
+ // If range is omitted for CHAR or BIT types, it defaults to 1.
+ //
+ if ((r.type == sql_type::CHAR || r.type == sql_type::BIT) && !r.range)
+ {
+ r.range = true;
+ r.range_value = 1;
+ }
+
+ return r;
+ }
+ catch (sql_lexer::invalid_input const& e)
+ {
+ return error (ct, "invalid PostgreSQL type declaration: " + e.message);
+ }
+ }
+ }
+}
diff --git a/odb/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/odb/relational/pgsql/header.cxx b/odb/odb/relational/pgsql/header.cxx
new file mode 100644
index 0000000..c3efc3e
--- /dev/null
+++ b/odb/odb/relational/pgsql/header.cxx
@@ -0,0 +1,285 @@
+// file : odb/relational/pgsql/header.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/header.hxx>
+
+#include <odb/relational/pgsql/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace header
+ {
+ namespace relational = relational::header;
+
+ struct class1: relational::class1
+ {
+ class1 (base const& x): base (x) {}
+
+ virtual void
+ object_public_extra_post (type& c)
+ {
+ bool abst (abstract (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ if (abst && !poly)
+ return;
+
+ data_member_path* id (id_member (c));
+ semantics::data_member* optimistic (context::optimistic (c));
+
+ column_count_type const& cc (column_count (c));
+
+ size_t update_columns (
+ cc.total - cc.id - cc.inverse - cc.readonly - cc.separate_update);
+
+ // Statement names.
+ //
+ os << "static const char persist_statement_name[];";
+
+ if (id != 0)
+ {
+ if (poly_derived)
+ os << "static const char* const find_statement_names[" <<
+ (abst ? "1" : "depth") << "];";
+ else
+ os << "static const char find_statement_name[];";
+
+ if (poly && !poly_derived)
+ os << "static const char find_discriminator_statement_name[];";
+
+ if (update_columns != 0)
+ os << "static const char update_statement_name[];";
+
+ os << "static const char erase_statement_name[];";
+
+ if (optimistic != 0)
+ os << "static const char optimistic_erase_statement_name[];";
+ }
+
+ // Query statement name.
+ //
+ if (options.generate_query ())
+ os << "static const char query_statement_name[];"
+ << "static const char erase_query_statement_name[];";
+
+ os << endl;
+
+ // Statement types.
+ //
+ os << "static const unsigned int persist_statement_types[];";
+
+ if (id != 0)
+ {
+ os << "static const unsigned int find_statement_types[];";
+
+ if (update_columns != 0)
+ os << "static const unsigned int update_statement_types[];";
+
+ if (optimistic != 0)
+ os << "static const unsigned int " <<
+ "optimistic_erase_statement_types[];";
+ }
+
+ os << endl;
+
+ if (poly_derived)
+ return;
+
+ // Bulk operations batch size.
+ //
+ {
+ unsigned long long b (c.count ("bulk")
+ ? c.get<unsigned long long> ("bulk")
+ : 1);
+
+ os << "static const std::size_t batch = " << b << "UL;"
+ << endl;
+ }
+ }
+
+ virtual void
+ view_public_extra_post (type&)
+ {
+ // Statement names.
+ //
+ os << "static const char query_statement_name[];"
+ << endl;
+ }
+ };
+ entry<class1> class1_entry_;
+
+ struct container_traits: relational::container_traits, context
+ {
+ container_traits (base const& x): base (x) {}
+
+ virtual void
+ container_public_extra_pre (semantics::data_member& m,
+ semantics::type& t)
+ {
+ if (!object (c_) || (abstract (c_) && !polymorphic (c_)))
+ return;
+
+ bool smart (!inverse (m, "value") && !unordered (m) &&
+ container_smart (t));
+
+ // Container statement names.
+ //
+ os << "static const char select_name[];"
+ << "static const char insert_name[];";
+
+ if (smart)
+ os << "static const char update_name[];";
+
+ os << "static const char delete_name[];"
+ << endl;
+
+ // Container statement types.
+ //
+ os << "static const unsigned int insert_types[];";
+
+ if (smart)
+ os << "static const unsigned int update_types[];"
+ << "static const unsigned int delete_types[];";
+
+ os << endl;
+ }
+ };
+ entry<container_traits> container_traits_;
+
+ struct section_traits: relational::section_traits, context
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ section_public_extra_post (user_section& s)
+ {
+ semantics::class_* poly_root (polymorphic (c_));
+ bool poly (poly_root != 0);
+
+ if (!poly && (abstract (c_) ||
+ s.special == user_section::special_version))
+ return;
+
+ bool load (s.total != 0 && s.separate_load ());
+ bool load_opt (s.optimistic () && s.separate_load ());
+
+ bool update (s.total != s.inverse + s.readonly); // Always separate.
+ bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
+
+ // Statement names.
+ //
+ if (load || load_opt)
+ os << "static const char select_name[];"
+ << endl;
+
+ if (update || update_opt)
+ os << "static const char update_name[];"
+ << endl;
+
+ // Statement types.
+ //
+ if (update || update_opt)
+ os << "static const unsigned int update_types[];";
+ }
+ };
+ entry<section_traits> section_traits_;
+
+ struct image_member: relational::image_member_impl<sql_type>,
+ member_base
+ {
+ image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ // Exchanged as strings. Can have up to 1000 digits not counting
+ // '-' and '.'.
+ //
+
+ os << image_type << " " << mi.var << "value;"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Additional 4 bytes at the beginning of the array specify
+ // the number of significant bits in the image. This number
+ // is stored in network byte order.
+ //
+ unsigned int n (4 + mi.st->range / 8 + (mi.st->range % 8 ? 1 : 0));
+
+ os << "unsigned char " << mi.var << "value[" << n << "];"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_uuid (member_info& mi)
+ {
+ // UUID is a 16-byte sequence.
+ //
+ os << "unsigned char " << mi.var << "value[16];"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+ };
+ entry<image_member> image_member_;
+ }
+ }
+}
diff --git a/odb/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/odb/relational/pgsql/source.cxx b/odb/odb/relational/pgsql/source.cxx
new file mode 100644
index 0000000..b881e48
--- /dev/null
+++ b/odb/odb/relational/pgsql/source.cxx
@@ -0,0 +1,1140 @@
+// file : odb/relational/pgsql/source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/relational/source.hxx>
+
+#include <odb/relational/pgsql/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace source
+ {
+ namespace relational = relational::source;
+
+ struct query_parameters: relational::query_parameters
+ {
+ query_parameters (base const& x): base (x), i_ (0) {}
+
+ virtual string
+ next (semantics::data_member&, const string&, const string&)
+ {
+ ostringstream ss;
+ ss << "$" << ++i_;
+
+ return ss.str ();
+ }
+
+ virtual string
+ auto_id (semantics::data_member&, const string&, const string&)
+ {
+ return "DEFAULT";
+ }
+
+ private:
+ size_t i_;
+ };
+ entry<query_parameters> query_parameters_;
+
+ namespace
+ {
+ const char* integer_buffer_types[] =
+ {
+ "pgsql::bind::boolean_",
+ "pgsql::bind::smallint",
+ "pgsql::bind::integer",
+ "pgsql::bind::bigint"
+ };
+
+ const char* float_buffer_types[] =
+ {
+ "pgsql::bind::real",
+ "pgsql::bind::double_"
+ };
+
+ const char* char_bin_buffer_types[] =
+ {
+ "pgsql::bind::text", // CHAR
+ "pgsql::bind::text", // VARCHAR
+ "pgsql::bind::text", // TEXT
+ "pgsql::bind::bytea" // BYTEA
+ };
+
+ const char* date_time_buffer_types[] =
+ {
+ "pgsql::bind::date",
+ "pgsql::bind::time",
+ "pgsql::bind::timestamp"
+ };
+
+ const char* oids[] =
+ {
+ "pgsql::bool_oid", // BOOLEAN
+ "pgsql::int2_oid", // SMALLINT
+ "pgsql::int4_oid", // INTEGER
+ "pgsql::int8_oid", // BIGINT
+ "pgsql::float4_oid", // REAL
+ "pgsql::float8_oid", // DOUBLE
+ "pgsql::numeric_oid", // NUMERIC
+ "pgsql::date_oid", // DATE
+ "pgsql::time_oid", // TIME
+ "pgsql::timestamp_oid", // TIMESTAMP
+ "pgsql::text_oid", // CHAR
+ "pgsql::text_oid", // VARCHAR
+ "pgsql::text_oid", // TEXT
+ "pgsql::bytea_oid", // BYTEA
+ "pgsql::bit_oid", // BIT
+ "pgsql::varbit_oid", // VARBIT
+ "pgsql::uuid_oid" // UUID
+ };
+ }
+
+ struct statement_oids: object_columns_base, context
+ {
+ statement_oids (statement_kind sk,
+ bool first = true,
+ object_section* section = 0)
+ : object_columns_base (first, column_prefix (), section), sk_ (sk)
+ {
+ }
+
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section for
+ // SELECT statements.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ (sk_ == statement_select &&
+ *section_ == main_section &&
+ !s.separate_load ());
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore certain columns depending on what kind statement we are
+ // generating. See object_columns in common source generator for
+ // details.
+ //
+ if (!(inverse (m, key_prefix_) && sk_ != statement_select))
+ object_columns_base::traverse_pointer (m, c);
+ }
+
+ virtual bool
+ traverse_column (semantics::data_member& m,
+ string const&,
+ bool first)
+ {
+ // Ignore certain columns depending on what kind statement we are
+ // generating. See object_columns in common source generator for
+ // details.
+ //
+ if (id ())
+ {
+ if (sk_ == statement_update ||
+ (sk_ == statement_insert && auto_ (m)))
+ return false;
+ }
+
+ if (sk_ == statement_update &&
+ readonly (member_path_, member_scope_))
+ return false;
+
+ if ((sk_ == statement_insert || sk_ == statement_update) &&
+ version (m))
+ return false;
+
+ if (!first)
+ os << ',' << endl;
+
+ os << oids[parse_sql_type (column_type (), m).type];
+
+ return true;
+ }
+
+ private:
+ statement_kind sk_;
+ };
+
+ //
+ // bind
+ //
+
+ struct bind_member: relational::bind_member_impl<sql_type>,
+ member_base
+ {
+ bind_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << b << ".type = " <<
+ integer_buffer_types[mi.st->type - sql_type::BOOLEAN] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << b << ".type = " <<
+ float_buffer_types[mi.st->type - sql_type::REAL] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ os << b << ".type = pgsql::bind::numeric;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data_ptr ();"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ();"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << b << ".type = " <<
+ date_time_buffer_types[mi.st->type - sql_type::DATE] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << b << ".type = " <<
+ char_bin_buffer_types[mi.st->type - sql_type::CHAR] << ";"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data_ptr ();"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ();"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ os << b << ".type = pgsql::bind::bit;"
+ << b << ".buffer = " << arg << "." << mi.var << "value;"
+ << b << ".capacity = sizeof (" << arg << "." << mi.var << "value);"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ os << b << ".type = pgsql::bind::varbit;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data_ptr ();"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ();"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_uuid (member_info& mi)
+ {
+ os << b << ".type = pgsql::bind::uuid;"
+ << b << ".buffer = " << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+ };
+ entry<bind_member> bind_member_;
+
+ //
+ // grow
+ //
+
+ struct grow_member: relational::grow_member_impl<sql_type>,
+ member_base
+ {
+ grow_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_date_time (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_bit (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_uuid (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+ };
+ entry<grow_member> grow_member_;
+
+ //
+ // init image
+ //
+
+ struct init_image_member: relational::init_image_member_impl<sql_type>,
+ member_base
+ {
+ init_image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ set_null (member_info& mi)
+ {
+ os << "i." << mi.var << "null = true;";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ // @@ Optimization: can remove growth check if buffer is fixed.
+ //
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = size;"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = size;"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "sizeof (i." << mi.var << "value)," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = size;";
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = size;"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_uuid (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+ };
+ entry<init_image_member> init_image_member_;
+
+ //
+ // init value
+ //
+
+ struct init_value_member: relational::init_value_member_impl<sql_type>,
+ member_base
+ {
+ init_value_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ get_null (string const& var) const
+ {
+ os << "i." << var << "null";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Presented as byte.
+ //
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ // Presented as bytea.
+ //
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_uuid (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+ };
+ entry<init_value_member> init_value_member_;
+
+ struct class_: relational::class_, context
+ {
+ class_ (base const& x): base (x) {}
+
+ virtual string
+ persist_statement_extra (type& c,
+ relational::query_parameters&,
+ persist_position p)
+ {
+ string r;
+
+ if (p == persist_after_values)
+ {
+ data_member_path* id (id_member (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ // Top-level auto id.
+ //
+ if (id != 0 && !poly_derived && auto_ (*id))
+ r = "RETURNING " +
+ convert_from (column_qname (*id), *id->back ());
+ }
+
+ return r;
+ }
+
+ virtual void
+ object_extra (type& c)
+ {
+ bool abst (abstract (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ if (abst && !poly)
+ return;
+
+ data_member_path* id (id_member (c));
+ semantics::data_member* optimistic (context::optimistic (c));
+
+ column_count_type const& cc (column_count (c));
+
+ size_t update_columns (
+ cc.total - cc.id - cc.inverse - cc.readonly - cc.separate_update);
+
+ string const& n (class_fq_name (c));
+ string const& fn (flat_name (n));
+ string traits ("access::object_traits_impl< " + n + ", id_pgsql >");
+
+ os << "const char " << traits << "::" << endl
+ << "persist_statement_name[] = " <<
+ strlit (statement_name ("persist", fn, c)) << ";"
+ << endl;
+
+ if (id != 0)
+ {
+ if (poly_derived)
+ {
+ os << "const char* const " << traits << "::" << endl
+ << "find_statement_names[] ="
+ << "{";
+
+ for (size_t i (0), n (abst ? 1 : polymorphic_depth (c));
+ i < n;
+ ++i)
+ {
+ if (i != 0)
+ os << "," << endl;
+
+ ostringstream ostr;
+ ostr << "find_" << i;
+ os << strlit (statement_name (ostr.str (), fn, c));
+ }
+
+ os << "};";
+ }
+ else
+ os << "const char " << traits << "::" << endl
+ << "find_statement_name[] = " <<
+ strlit (statement_name ("find", fn, c)) << ";"
+ << endl;
+
+ if (poly && !poly_derived)
+ os << "const char " << traits << "::" << endl
+ << "find_discriminator_statement_name[] = " <<
+ strlit (statement_name ("find_discriminator", fn, c)) << ";"
+ << endl;
+
+ if (update_columns != 0)
+ os << "const char " << traits << "::" << endl
+ << "update_statement_name[] = " <<
+ strlit (statement_name ("update", fn, c)) << ";"
+ << endl;
+
+ os << "const char " << traits << "::" << endl
+ << "erase_statement_name[] = " <<
+ strlit (statement_name ("erase", fn, c)) << ";"
+ << endl;
+
+ if (optimistic != 0)
+ os << "const char " << traits << "::" << endl
+ << "optimistic_erase_statement_name[] = " <<
+ strlit (statement_name ("erase_optimistic", fn, c)) << ";"
+ << endl;
+ }
+
+ // Query statement name.
+ //
+ if (options.generate_query ())
+ {
+ os << "const char " << traits << "::" << endl
+ << "query_statement_name[] = " <<
+ strlit (statement_name ("query", fn, c)) << ";"
+ << endl
+ << "const char " << traits << "::" << endl
+ << "erase_query_statement_name[] = " <<
+ strlit (statement_name ("erase_query", fn, c)) << ";"
+ << endl;
+ }
+
+ // Statement types.
+ //
+
+ // persist_statement_types.
+ //
+ {
+ os << "const unsigned int " << traits << "::" << endl
+ << "persist_statement_types[] ="
+ << "{";
+
+ statement_oids st (statement_insert);
+ st.traverse (c);
+
+ // Empty array is not portable. So add a dummy member if we
+ // are not sending anything with the insert statement.
+ //
+ if (cc.total == cc.inverse + cc.optimistic_managed +
+ (id != 0 && !poly_derived && auto_ (*id) ? cc.id : 0))
+ os << "0";
+
+ os << "};";
+ }
+
+ // find_statement_types.
+ //
+ if (id != 0)
+ {
+ os << "const unsigned int " << traits << "::" << endl
+ << "find_statement_types[] ="
+ << "{";
+
+ statement_oids st (statement_select, true);
+ st.traverse (*id);
+
+ os << "};";
+ }
+
+ // update_statement_types.
+ //
+ if (id != 0 && update_columns != 0)
+ {
+ os << "const unsigned int " << traits << "::" << endl
+ << "update_statement_types[] ="
+ << "{";
+
+ {
+ statement_oids st (statement_update, true, &main_section);
+ st.traverse (c);
+ }
+
+ // Not the same as update_columns.
+ //
+ bool first (cc.total == cc.id + cc.inverse + cc.readonly +
+ cc.separate_update + cc.optimistic_managed);
+
+ statement_oids st (statement_where, first);
+ st.traverse (*id);
+
+ if (optimistic != 0)
+ st.traverse (*optimistic);
+
+ os << "};";
+ }
+
+ if (id != 0 && optimistic != 0)
+ {
+ os << "const unsigned int " << traits << "::" << endl
+ << "optimistic_erase_statement_types[] ="
+ << "{";
+
+ statement_oids st (statement_where);
+ st.traverse (*id);
+ st.traverse (*optimistic);
+
+ os << "};";
+ }
+ }
+
+ virtual void
+ extra_statement_cache_extra_args (bool c, bool s)
+ {
+ bool u (c || s);
+
+ os << "," << endl
+ << db << "::native_binding&" << (u ? " idn" : "") << "," << endl
+ << "const unsigned int*" << (u ? " idt" : "");
+ }
+
+ virtual void
+ view_extra (type& c)
+ {
+ string const& n (class_fq_name (c));
+ string const& fn (flat_name (n));
+ string traits ("access::view_traits_impl< " + n + ", id_pgsql >");
+
+ os << "const char " << traits << "::" << endl
+ << "query_statement_name[] = " <<
+ strlit (statement_name ("query", fn, c)) << ";"
+ << endl;
+ }
+
+ virtual void
+ object_query_statement_ctor_args (type&,
+ string const& q,
+ bool process,
+ bool prep)
+ {
+ os << "sts.connection ()," << endl;
+
+ if (prep)
+ os << "n," << endl;
+ else
+ os << "query_statement_name," << endl;
+
+ os << "text," << endl
+ << process << "," << endl // Process.
+ << "true," << endl // Optimize.
+ << q << ".parameter_types ()," << endl
+ << q << ".parameter_count ()," << endl
+ << q << ".parameters_binding ()," << endl
+ << "imb";
+ }
+
+ virtual void
+ object_erase_query_statement_ctor_args (type&)
+ {
+ os << "conn," << endl
+ << "erase_query_statement_name," << endl
+ << "text," << endl
+ << "q.parameter_types ()," << endl
+ << "q.parameter_count ()," << endl
+ << "q.parameters_binding ()";
+ }
+
+ virtual void
+ view_query_statement_ctor_args (type&,
+ string const& q,
+ bool process,
+ bool prep)
+ {
+ os << "sts.connection ()," << endl;
+
+ if (prep)
+ os << "n," << endl;
+ else
+ os << "query_statement_name," << endl;
+
+ os << q << ".clause ()," << endl
+ << process << "," << endl // Process.
+ << "true," << endl // Optimize.
+ << q << ".parameter_types ()," << endl
+ << q << ".parameter_count ()," << endl
+ << q << ".parameters_binding ()," << endl
+ << "imb";
+ }
+
+ virtual void
+ post_query_ (type&, bool once_off)
+ {
+ if (once_off)
+ os << "st->deallocate ();";
+ }
+ };
+ entry<class_> class_entry_;
+
+ struct container_traits : relational::container_traits, context
+ {
+ container_traits (base const& x): base (x) {}
+
+ virtual void
+ container_extra (semantics::data_member& m, semantics::type& t)
+ {
+ if (!object (c_) || (abstract (c_) && !polymorphic (c_)))
+ return;
+
+ container_kind_type ck (container_kind (t));
+
+ string const& pn (public_name (m));
+ string scope (scope_ + "::" + flat_prefix_ + pn + "_traits");
+
+ data_member_path* imp (inverse (m, "value"));
+ bool inv (imp != 0);
+
+ bool smart (!inv && !unordered (m) && container_smart (t));
+
+ // Statment names.
+ //
+
+ // Prefix top-object name to avoid conflicts with inherited
+ // member statement names.
+ //
+ string fn (
+ flat_name (
+ class_fq_name (*top_object) + "_" + flat_prefix_ + pn));
+
+ os << "const char " << scope << "::" << endl
+ << "select_name[] = " <<
+ strlit (statement_name ("select", fn, m)) << ";"
+ << endl
+ << "const char " << scope << "::" << endl
+ << "insert_name[] = " <<
+ strlit (statement_name ("insert", fn, m)) << ";"
+ << endl;
+
+ if (smart)
+ os << "const char " << scope << "::" << endl
+ << "update_name[] = " <<
+ strlit (statement_name ("update", fn, m)) << ";"
+ << endl;
+
+ os << "const char " << scope << "::" << endl
+ << "delete_name[] = " <<
+ strlit (statement_name ("delete", fn, m)) << ";"
+ << endl;
+
+ // Statement types.
+ //
+
+ semantics::type& vt (container_vt (m));
+ semantics::type& idt (container_idt (m));
+
+ // insert statement types.
+ //
+ {
+ os << "const unsigned int " << scope << "::" << endl
+ << "insert_types[] ="
+ << "{";
+
+ if (!inv)
+ {
+ statement_oids so (statement_insert);
+
+ so.traverse (m, idt, "id", "object_id");
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ so.traverse (m, container_it (m), "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ so.traverse (m, container_kt (m), "key", "key");
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ so.traverse (m, vt, "value", "value");
+ }
+ else
+ // MSVC does not allow zero length arrays or uninitialized
+ // non-extern const values.
+ //
+ os << "0";
+
+ os << "};";
+ }
+
+ // update statement types.
+ //
+ if (smart)
+ {
+ os << "const unsigned int " << scope << "::" << endl
+ << "update_types[] ="
+ << "{";
+
+ {
+ // Use insert instead of update to include read-only members.
+ //
+ statement_oids so (statement_insert);
+ so.traverse (m, vt, "value", "value");
+ }
+
+ statement_oids so (statement_where, false);
+ so.traverse (m, idt, "id", "object_id");
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ so.traverse (m, container_it (m), "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ //so.traverse (m, container_kt (t), "key", "key");
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ //so.traverse (m, vt, "value", "value");
+ break;
+ }
+ }
+
+ os << "};";
+ }
+
+ // delete statement types.
+ //
+ if (smart)
+ {
+ os << "const unsigned int " << scope << "::" << endl
+ << "delete_types[] ="
+ << "{";
+
+ statement_oids so (statement_where);
+ so.traverse (m, idt, "id", "object_id");
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ so.traverse (m, container_it (m), "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ //so.traverse (m, container_kt (t), "key", "key");
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ //so.traverse (m, vt, "value", "value");
+ break;
+ }
+ }
+
+ os << "};";
+ }
+ }
+ };
+ entry<container_traits> container_traits_;
+
+ struct section_traits : relational::section_traits, context
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ section_extra (user_section& s)
+ {
+ semantics::class_* poly_root (polymorphic (c_));
+ bool poly (poly_root != 0);
+
+ if (!poly && (abstract (c_) ||
+ s.special == user_section::special_version))
+ return;
+
+ semantics::data_member* opt (optimistic (c_));
+
+ bool load (s.total != 0 && s.separate_load ());
+ bool load_opt (s.optimistic () && s.separate_load ());
+
+ bool update (s.total != s.inverse + s.readonly); // Always separate.
+ bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
+
+ string name (public_name (*s.member));
+ string scope (scope_ + "::" + name + "_traits");
+
+ // Statment names.
+ //
+
+ // Prefix object name to avoid conflicts with inherited member
+ // statement names.
+ //
+ string fn (flat_name (class_fq_name (c_) + "_" + name));
+
+ if (load || load_opt)
+ os << "const char " << scope << "::" << endl
+ << "select_name[] = " <<
+ strlit (statement_name ("select", fn, *s.member)) << ";"
+ << endl;
+
+ if (update || update_opt)
+ os << "const char " << scope << "::" << endl
+ << "update_name[] = " <<
+ strlit (statement_name ("update", fn, *s.member)) << ";"
+ << endl;
+
+ // Statement types.
+ //
+ if (update || update_opt)
+ {
+ os << "const unsigned int " << scope << "::" << endl
+ << "update_types[] ="
+ << "{";
+
+ {
+ statement_oids st (statement_update, true, &s);
+ st.traverse (c_);
+ }
+
+ statement_oids st (statement_where, !update);
+ st.traverse (*id_member (c_));
+
+ if (s.optimistic ()) // Note: not update_opt.
+ st.traverse (*opt);
+
+ os << "};";
+ }
+ }
+ };
+ entry<section_traits> section_traits_;
+
+ struct container_cache_init_members:
+ relational::container_cache_init_members
+ {
+ container_cache_init_members (base const& x): base (x) {}
+
+ virtual void
+ extra_members ()
+ {
+ os << ", idn, idt";
+ }
+ };
+ entry<container_cache_init_members> container_cache_init_members_;
+
+ struct section_cache_init_members:
+ relational::section_cache_init_members
+ {
+ section_cache_init_members (base const& x): base (x) {}
+
+ virtual void
+ extra_members ()
+ {
+ os << ", idn, idt";
+ }
+ };
+ entry<section_cache_init_members> section_cache_init_members_;
+ }
+ }
+}
diff --git a/odb/odb/relational/processor.cxx b/odb/odb/relational/processor.cxx
new file mode 100644
index 0000000..0f60359
--- /dev/null
+++ b/odb/odb/relational/processor.cxx
@@ -0,0 +1,1564 @@
+// file : odb/relational/processor.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <vector>
+#include <algorithm>
+
+#include <odb/diagnostics.hxx>
+#include <odb/lookup.hxx>
+#include <odb/cxx-lexer.hxx>
+#include <odb/common.hxx>
+
+#include <odb/relational/context.hxx>
+#include <odb/relational/processor.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace
+ {
+ // Indirect (dynamic) context values.
+ //
+ static string
+ id_column_type ()
+ {
+ context& c (context::current ());
+ data_member_path& id (*context::id_member (*c.top_object));
+ return id.back ()->get<string> ("column-id-type");
+ }
+
+ struct data_member: traversal::data_member, context
+ {
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ if (transient (m))
+ return;
+
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ semantics::type* wt;
+ semantics::names* whint (0);
+ if ((wt = wrapper (t, whint)))
+ wt = &utype (*wt, whint);
+
+ // Determine the member kind.
+ //
+ enum {simple, composite, container, unknown} kind (unknown);
+
+ // See if this is a composite value type.
+ //
+ if (composite_wrapper (t))
+ kind = composite;
+
+ // If not, see if it is a simple value.
+ //
+ if (kind == unknown)
+ {
+ string type, id_type;
+
+ if (m.count ("id-type"))
+ id_type = m.get<string> ("id-type");
+
+ if (m.count ("type"))
+ {
+ type = m.get<string> ("type");
+
+ if (id_type.empty ())
+ id_type = type;
+ }
+
+ if (semantics::class_* c = object_pointer (t))
+ {
+ // An object pointer in view doesn't really have a "column"
+ // so pretend that it has already been handled.
+ //
+ if (view_member (m))
+ kind = simple;
+ else
+ {
+ // This is an object pointer. The column type is the pointed-to
+ // object id type.
+ //
+ semantics::data_member& id (*id_member (*c)->back ());
+
+ semantics::names* idhint;
+ semantics::type& idt (utype (id, idhint));
+
+ // The id type can be a composite value type.
+ //
+ if (composite_wrapper (idt))
+ kind = composite;
+ else
+ {
+ semantics::type* wt;
+ semantics::names* whint (0);
+ if ((wt = wrapper (idt, whint)))
+ wt = &utype (*wt, whint);
+
+ if (type.empty () && id.count ("id-type"))
+ type = id.get<string> ("id-type");
+
+ if (type.empty () && id.count ("type"))
+ type = id.get<string> ("type");
+
+ // The rest should be identical to the code for the id_type in
+ // the else block.
+ //
+ if (type.empty () && idt.count ("id-type"))
+ type = idt.get<string> ("id-type");
+
+ if (type.empty () && wt != 0 && wt->count ("id-type"))
+ type = wt->get<string> ("id-type");
+
+ if (type.empty () && idt.count ("type"))
+ type = idt.get<string> ("type");
+
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("type");
+
+ if (type.empty ())
+ type = database_type (idt, idhint, true);
+
+ if (type.empty () && wt != 0)
+ type = database_type (*wt, whint, true);
+
+ id_type = type;
+ }
+ }
+ }
+ else
+ {
+ if (id_type.empty () && t.count ("id-type"))
+ id_type = t.get<string> ("id-type");
+
+ if (id_type.empty () && wt != 0 && wt->count ("id-type"))
+ id_type = wt->get<string> ("id-type");
+
+ if (type.empty () && t.count ("type"))
+ type = t.get<string> ("type");
+
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("type");
+
+ if (id_type.empty ())
+ id_type = type;
+
+ if (id_type.empty ())
+ id_type = database_type (t, hint, true);
+
+ if (id_type.empty () && wt != 0)
+ id_type = database_type (*wt, whint, true);
+
+ bool null (false);
+ if (type.empty ())
+ type = database_type (t, hint, false, &null);
+
+ if (type.empty () && wt != 0)
+ type = database_type (*wt, whint, false, &null);
+
+ // Use id mapping for discriminators.
+ //
+ if (id (m) || discriminator (m))
+ type = id_type;
+ // Allow NULL if requested by the default mapping.
+ //
+ else if (null && !m.count ("not-null"))
+ m.set ("null", true);
+ }
+
+ if (kind == unknown && !type.empty ())
+ {
+ m.set ("column-type", type);
+ m.set ("column-id-type", id_type);
+
+ // Issue a warning if we are relaxing null-ness.
+ //
+ if (m.count ("null") && t.count ("not-null"))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " warning: data member declared null while its type is "
+ << "declared not null" << endl;
+ }
+
+ kind = simple;
+ }
+ }
+
+ // If not a simple value, see if this is a container.
+ //
+ if (kind == unknown && context::container (m))
+ {
+ process_container (m, (wt != 0 ? *wt : t));
+ kind = container;
+ }
+
+ // If it is none of the above then we have an error.
+ //
+ if (kind == unknown)
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: unable to map C++ type '" << t.fq_name (hint)
+ << "' used in data member '" << m.name () << "' to a "
+ << db.name () << " database type" << endl;
+
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " info: use '#pragma db type' to specify the database type"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ if (m.count ("polymorphic-ref"))
+ {
+ // Copy the column name from the root's id member, if specified.
+ //
+ {
+ semantics::class_& r (*object_pointer (t));
+ semantics::data_member& id (*id_member (r)->front ());
+
+ if (id.count ("column"))
+ m.set ("column", id.get<table_column> ("column"));
+ }
+
+ m.set ("not-null", true);
+ m.set ("deferrable",
+ sema_rel::deferrable (sema_rel::deferrable::not_deferrable));
+ m.set ("on-delete", sema_rel::foreign_key::cascade);
+ }
+
+ process_index (m);
+ }
+
+ // Convert index/unique specifiers to the index entry in the object.
+ //
+ void
+ process_index (semantics::data_member& m)
+ {
+ bool ip (m.count ("index"));
+ bool up (m.count ("unique"));
+
+ if (ip || up)
+ {
+ using semantics::class_;
+ class_& c (dynamic_cast<class_&> (m.scope ()));
+
+ indexes& ins (c.count ("index")
+ ? c.get<indexes> ("index")
+ : c.set ("index", indexes ()));
+
+ index in;
+ in.loc = m.get<location_t> (
+ ip ? "index-location" : "unique-location");
+
+ if (up)
+ in.type = "UNIQUE";
+
+ index::member im;
+ im.loc = in.loc;
+ im.name = m.name ();
+ im.path.push_back (&m);
+ in.members.push_back (im);
+
+ // Insert it in the location order.
+ //
+ ins.insert (
+ lower_bound (ins.begin (), ins.end (), in, index_comparator ()),
+ in);
+ }
+ }
+
+ void
+ process_container_value (semantics::type& t,
+ semantics::names* hint,
+ semantics::data_member& m,
+ string const& prefix,
+ bool obj_ptr)
+ {
+ if (composite_wrapper (t))
+ return;
+
+ semantics::names* wh (0);
+ semantics::type* wt (wrapper (t, wh));
+
+ string type;
+ semantics::type& ct (utype (m));
+
+ // Custom mapping can come from these places (listed in the order
+ // of priority): member, container type, value type. To complicate
+ // things a bit, for object references, it can also come from the
+ // member and value type of the id member.
+ //
+ if (m.count (prefix + "-type"))
+ type = m.get<string> (prefix + "-type");
+
+ if (type.empty () && ct.count (prefix + "-type"))
+ type = ct.get<string> (prefix + "-type");
+
+ semantics::class_* c;
+ if (obj_ptr && (c = object_pointer (t)))
+ {
+ // This is an object pointer. The column type is the pointed-to
+ // object id type.
+ //
+ semantics::data_member& id (*id_member (*c)->back ());
+
+ semantics::names* idhint;
+ semantics::type& idt (utype (id, idhint));
+
+ // Nothing to do if this is a composite value type.
+ //
+ if (composite_wrapper (idt))
+ return;
+
+ semantics::type* wt (0);
+ semantics::names* whint (0);
+ if ((wt = wrapper (idt, whint)))
+ wt = &utype (*wt, whint);
+
+ if (type.empty () && id.count ("id-type"))
+ type = id.get<string> ("id-type");
+
+ if (type.empty () && id.count ("type"))
+ type = id.get<string> ("type");
+
+ // The rest of the code is identical to the else block except here
+ // we have to check for "id-type" before checking for "type".
+ //
+
+ if (type.empty () && idt.count ("id-type"))
+ type = idt.get<string> ("id-type");
+
+ if (type.empty () && wt != 0 && wt->count ("id-type"))
+ type = wt->get<string> ("id-type");
+
+ if (type.empty () && idt.count ("type"))
+ type = idt.get<string> ("type");
+
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("type");
+
+ if (type.empty ())
+ type = database_type (idt, idhint, true);
+
+ if (type.empty () && wt != 0)
+ type = database_type (*wt, whint, true);
+ }
+ else
+ {
+ if (type.empty () && t.count ("type"))
+ type = t.get<string> ("type");
+
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("type");
+
+ bool null (false);
+ if (type.empty ())
+ type = database_type (t, hint, false, &null);
+
+ if (type.empty () && wt != 0)
+ type = database_type (*wt, wh, false, &null);
+
+ // Allow NULL if requested by the default mapping.
+ //
+ if (null && !m.count (prefix + "-not-null"))
+ m.set (prefix + "-null", true);
+ }
+
+ if (!type.empty ())
+ {
+ m.set (prefix + "-column-type", type);
+ m.set (prefix + "-column-id-type", type);
+ return;
+ }
+
+ // We do not support nested containers so skip that test.
+ //
+
+ // If it is none of the above then we have an error.
+ //
+ string fq_type (t.fq_anonymous () ? "<anonymous>" : t.fq_name ());
+
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: unable to map C++ type '" << fq_type << "' used in "
+ << "data member '" << m.name () << "' to a " << db.name ()
+ << " database type" << endl;
+
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " info: use '#pragma db " << prefix << "_type' to specify the "
+ << "database type" << endl;
+
+ throw operation_failed ();
+ }
+
+ void
+ process_container (semantics::data_member& m, semantics::type& t)
+ {
+ container_kind_type ck (t.get<container_kind_type> ("container-kind"));
+
+ semantics::names* vh (0);
+ semantics::names* ih (0);
+ semantics::names* kh (0);
+
+ semantics::type* vt (&utype (m, vh, "value"));
+ semantics::type* it (ck == ck_ordered ? &utype (m, ih, "index") : 0);
+ semantics::type* kt (ck == ck_map || ck == ck_multimap
+ ? &utype (m, kh, "key")
+ : 0);
+
+ // Process member data.
+ //
+ m.set ("id-column-type", &id_column_type);
+
+ process_container_value (*vt, vh, m, "value", true);
+
+ if (it != 0)
+ process_container_value (*it, ih, m, "index", false);
+
+ if (kt != 0)
+ process_container_value (*kt, kh, m, "key", true);
+ }
+ };
+
+ //
+ //
+ struct view_data_member: traversal::data_member, context
+ {
+ view_data_member (semantics::class_& c)
+ : view_ (c),
+ query_ (c.get<view_query> ("query")),
+ amap_ (c.get<view_alias_map> ("alias-map")),
+ omap_ (c.get<view_object_map> ("object-map"))
+ {
+ }
+
+ struct assoc_member
+ {
+ semantics::data_member* m;
+ view_object* vo;
+ };
+
+ typedef vector<assoc_member> assoc_members;
+
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ using semantics::data_member;
+
+ if (transient (m))
+ return;
+
+ semantics::type& t (utype (m));
+
+ // Object pointers are associated with objects.
+ //
+ if (object_pointer (t))
+ return;
+
+ data_member* src_m (0); // Source member.
+
+ // Resolve member references in column expressions.
+ //
+ if (m.count ("column"))
+ {
+ // Column literal.
+ //
+ if (query_.kind != view_query::condition)
+ {
+ warn (m.get<location_t> ("column-location"))
+ << "db pragma column ignored in a view with "
+ << (query_.kind == view_query::runtime ? "runtime" : "complete")
+ << " query" << endl;
+ }
+
+ return;
+ }
+ else if (m.count ("column-expr"))
+ {
+ column_expr& e (m.get<column_expr> ("column-expr"));
+
+ if (query_.kind != view_query::condition)
+ {
+ warn (e.loc)
+ << "db pragma column ignored in a view with "
+ << (query_.kind == view_query::runtime ? "runtime" : "complete")
+ << " query" << endl;
+ return;
+ }
+
+ for (column_expr::iterator i (e.begin ()); i != e.end (); ++i)
+ {
+ // This code is quite similar to translate_expression in the
+ // source generator.
+ //
+ try
+ {
+ using semantics::scope;
+ using semantics::class_;
+
+ if (i->kind != column_expr_part::reference)
+ continue;
+
+ lex_.start (i->value);
+
+ string tl;
+ tree tn;
+ cpp_ttype tt (lex_.next (tl, &tn));
+
+ data_member* m (0);
+ view_object* vo (0);
+
+ // Check if this is an alias.
+ //
+ if (tt == CPP_NAME)
+ {
+ view_alias_map::iterator j (amap_.find (tl));
+
+ if (j != amap_.end ())
+ {
+ vo = j->second;
+
+ // Skip '::'.
+ //
+ if (lex_.next (tl, &tn) != CPP_SCOPE)
+ {
+ error (i->loc) << "member name expected after an alias " <<
+ "in db pragma column" << endl;
+ throw operation_failed ();
+ }
+
+ if (lex_.next (tl, &tn) != CPP_NAME)
+ throw lookup::invalid_name ();
+
+ m = &vo->obj->lookup<data_member> (
+ tl, scope::include_hidden);
+
+ tt = lex_.next (tl, &tn);
+ }
+ }
+
+ // If it is not an alias, do the normal lookup.
+ //
+ if (vo == 0)
+ {
+ // Also get the object type. We need to do it so that
+ // we can get the correct (derived) table name (the
+ // member itself can come from a base class).
+ //
+ scope* s;
+ string name;
+ cpp_ttype ptt; // Not used.
+ m = &lookup::resolve_scoped_name<data_member> (
+ lex_, tt, tl, tn, ptt,
+ dynamic_cast<scope&> (*unit.find (i->scope)),
+ name,
+ false,
+ &s);
+
+ view_object_map::iterator j (
+ omap_.find (dynamic_cast<class_*> (s)));
+
+ if (j == omap_.end ())
+ {
+ error (i->loc) << "name '" << name << "' in db pragma " <<
+ "column does not refer to a data member of a " <<
+ "persistent class that is used in this view" << endl;
+ throw operation_failed ();
+ }
+
+ vo = j->second;
+ }
+
+ i->member_path.push_back (m);
+
+ // Figure out the table name/alias for this member.
+ //
+ if (class_* root = polymorphic (*vo->obj))
+ {
+ // If the object is polymorphic, then figure out which of the
+ // bases this member comes from and use the corresponding
+ // table.
+ //
+ class_* c (&static_cast<class_&> (m->scope ()));
+
+ // If this member's class is not polymorphic (root uses reuse
+ // inheritance), then use the root table.
+ //
+ if (!polymorphic (*c))
+ c = root;
+
+ // In a polymorphic hierarchy we have several tables and the
+ // provided alias is used as a prefix together with the table
+ // name to form the actual alias.
+ //
+ qname const& t (table_name (*c));
+
+ if (vo->alias.empty ())
+ i->table = t;
+ else
+ i->table = qname (vo->alias + "_" + t.uname ());
+ }
+ else
+ i->table = vo->alias.empty ()
+ ? table_name (*vo->obj)
+ : qname (vo->alias);
+
+ // Finally, resolve nested members if any.
+ //
+ for (; tt == CPP_DOT; tt = lex_.next (tl, &tn))
+ {
+ lex_.next (tl, &tn); // Get CPP_NAME.
+
+ // Check that the outer member is composite and also
+ // unwrap it while at it.
+ //
+ class_* comp (composite_wrapper (utype (*m)));
+ if (comp == 0)
+ {
+ error (i->loc) << "data member '" << m->name () << "' " <<
+ "specified in db pragma column is not composite" << endl;
+ throw operation_failed ();
+ }
+
+ m = &comp->lookup<data_member> (tl, class_::include_hidden);
+ i->member_path.push_back (m);
+ }
+
+ // If the expression is just this reference, then we have
+ // a source member.
+ //
+ if (e.size () == 1)
+ src_m = m;
+ }
+ catch (lookup::invalid_name const&)
+ {
+ error (i->loc) << "invalid name in db pragma column" << endl;
+ throw operation_failed ();
+ }
+ catch (semantics::unresolved const& e)
+ {
+ if (e.type_mismatch)
+ error (i->loc) << "name '" << e.name << "' in db pragma " <<
+ "column does not refer to a data member" << endl;
+ else
+ error (i->loc) << "unable to resolve data member '" <<
+ e.name << "' specified with db pragma column" << endl;
+
+ throw operation_failed ();
+ }
+ catch (semantics::ambiguous const& e)
+ {
+ error (i->loc) << "data member name '" << e.first.name () <<
+ "' specified with db pragma column is ambiguous" << endl;
+
+ info (e.first.named ().location ()) << "could resolve to " <<
+ "this data member" << endl;
+
+ info (e.second.named ().location ()) << "or could resolve " <<
+ "to this data member" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ // Check that the source member is not transient or inverse. Also
+ // check that the C++ types are the same (sans cvr-qualification
+ // and wrapping) and issue a warning if they differ. In rare cases
+ // where this is not a mistake, the user can use a phony expression
+ // (e.g., "" + person:name) to disable the warning. Note that in
+ // this case there will be no type pragma copying, which is probably
+ // ok seeing that the C++ types are different.
+ //
+ //
+ if (src_m != 0)
+ {
+ string reason;
+
+ if (transient (*src_m))
+ reason = "transient";
+ else if (inverse (*src_m))
+ reason = "inverse";
+
+ if (!reason.empty ())
+ {
+ error (e.loc)
+ << "object data member '" << src_m->name () << "' specified "
+ << "in db pragma column is " << reason << endl;
+ throw operation_failed ();
+ }
+
+ if (!member_resolver::check_types (utype (*src_m), utype (m)))
+ {
+ warn (e.loc)
+ << "object data member '" << src_m->name () << "' specified "
+ << "in db pragma column has a different type compared to the "
+ << "view data member" << endl;
+
+ info (src_m->file (), src_m->line (), src_m->column ())
+ << "object data member is defined here" << endl;
+
+ info (m.file (), m.line (), m.column ())
+ << "view data member is defined here" << endl;
+ }
+ }
+ }
+ // This member has no column information. If we are generating our
+ // own query, try to find a member with the same (or similar) name
+ // in one of the associated objects.
+ //
+ else if (query_.kind == view_query::condition)
+ {
+ view_objects& objs (view_.get<view_objects> ("objects"));
+
+ assoc_members exact_members, pub_members;
+ member_resolver resolver (exact_members, pub_members, m);
+
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ if (i->kind == view_object::object)
+ resolver.traverse (*i);
+ }
+
+ assoc_members& members (
+ !exact_members.empty () ? exact_members : pub_members);
+
+ // Issue diagnostics if we didn't find any or found more
+ // than one.
+ //
+ if (members.empty ())
+ {
+ error (m.file (), m.line (), m.column ())
+ << "unable to find a corresponding data member for '"
+ << m.name () << "' in any of the associated objects" << endl;
+
+ info (m.file (), m.line (), m.column ())
+ << "use db pragma column to specify the corresponding data "
+ << "member or column name" << endl;
+
+ throw operation_failed ();
+ }
+ else if (members.size () > 1)
+ {
+ error (m.file (), m.line (), m.column ())
+ << "corresponding data member for '" << m.name () << "' is "
+ << "ambiguous" << endl;
+
+ info (m.file (), m.line (), m.column ())
+ << "candidates are:" << endl;
+
+ for (assoc_members::const_iterator i (members.begin ());
+ i != members.end ();
+ ++i)
+ {
+ info (i->m->file (), i->m->line (), i->m->column ())
+ << " '" << i->m->name () << "' in object '"
+ << i->vo->name () << "'" << endl;
+ }
+
+ info (m.file (), m.line (), m.column ())
+ << "use db pragma column to resolve this ambiguity" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Synthesize the column expression for this member.
+ //
+ assoc_member const& am (members.back ());
+
+ column_expr& e (m.set ("column-expr", column_expr ()));
+ e.push_back (column_expr_part ());
+ column_expr_part& ep (e.back ());
+
+ ep.kind = column_expr_part::reference;
+
+
+ // If this object is polymorphic, then figure out which of the
+ // bases this member comes from and use the corresponding table.
+ //
+ using semantics::class_;
+
+ if (class_* root = polymorphic (*am.vo->obj))
+ {
+ class_* c (&static_cast<class_&> (am.m->scope ()));
+
+ // If this member's class is not polymorphic (root uses reuse
+ // inheritance), then use the root table.
+ //
+ if (!polymorphic (*c))
+ c = root;
+
+ // In a polymorphic hierarchy we have several tables and the
+ // provided alias is used as a prefix together with the table
+ // name to form the actual alias.
+ //
+ qname const& t (table_name (*c));
+
+ if (am.vo->alias.empty ())
+ ep.table = t;
+ else
+ ep.table = qname (am.vo->alias + "_" + t.uname ());
+ }
+ else
+ ep.table = am.vo->alias.empty ()
+ ? table_name (*am.vo->obj)
+ : qname (am.vo->alias);
+
+ ep.member_path.push_back (am.m);
+
+ src_m = am.m;
+ }
+
+ // If we have the source member and don't have the type pragma of
+ // our own, but the source member does, then copy the columnt type
+ // over. In case the source member is a pointer, also check the id
+ // member.
+ //
+ if (src_m != 0 && !m.count ("type"))
+ {
+ if (src_m->count ("type"))
+ m.set ("column-type", src_m->get<string> ("column-type"));
+ else if (semantics::class_* c = object_pointer (utype (*src_m)))
+ {
+ semantics::data_member& id (*id_member (*c)->back ());
+
+ if (id.count ("type"))
+ m.set ("column-type", id.get<string> ("column-type"));
+ }
+ }
+
+ // Check the return statements above if you add any extra logic
+ // here.
+ }
+
+ struct member_resolver: traversal::class_
+ {
+ member_resolver (assoc_members& members,
+ assoc_members& pub_members,
+ semantics::data_member& m)
+ : member_ (members, pub_members, m)
+ {
+ *this >> names_ >> member_;
+ *this >> inherits_ >> *this;
+ }
+
+ void
+ traverse (view_object& vo)
+ {
+ member_.vo_ = &vo;
+
+ // First look for an exact match.
+ //
+ {
+ member_.exact_ = true;
+ member_.found_ = false;
+ traverse (*vo.obj);
+ }
+
+ // If we didn't find an exact match, then look for a public
+ // name match.
+ //
+ if (!member_.found_)
+ {
+ member_.exact_ = false;
+ traverse (*vo.obj);
+ }
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ if (!object (c))
+ return; // Ignore transient bases.
+
+ names (c);
+
+ // If we already found a match in one of the derived classes,
+ // don't go into bases to get the standard "hiding" behavior.
+ //
+ if (!member_.found_)
+ inherits (c);
+ }
+
+ public:
+ static bool
+ check_types (semantics::type& ot, semantics::type& vt)
+ {
+ using semantics::type;
+
+ // Require that the types be the same sans the wrapping and
+ // cvr-qualification. If the object member type is a pointer,
+ // use the id type of the pointed-to object.
+ //
+ type* t1;
+
+ if (semantics::class_* c = object_pointer (ot))
+ t1 = &utype (*id_member (*c));
+ else
+ t1 = &ot;
+
+ type* t2 (&vt);
+
+ if (type* wt1 = context::wrapper (*t1))
+ t1 = &utype (*wt1);
+
+ if (type* wt2 = context::wrapper (*t2))
+ t2 = &utype (*wt2);
+
+ if (t1 != t2)
+ return false;
+
+ return true;
+ }
+
+ private:
+ struct data_member: traversal::data_member
+ {
+ data_member (assoc_members& members,
+ assoc_members& pub_members,
+ semantics::data_member& m)
+ : members_ (members),
+ pub_members_ (pub_members),
+ name_ (m.name ()),
+ pub_name_ (context::current ().public_name (m)),
+ type_ (utype (m))
+ {
+ }
+
+ virtual void
+ traverse (type& m)
+ {
+ if (exact_)
+ {
+ if (name_ == m.name () && check (m))
+ {
+ assoc_member am;
+ am.m = &m;
+ am.vo = vo_;
+ members_.push_back (am);
+ found_ = true;
+ }
+ }
+ else
+ {
+ if (pub_name_ == context::current ().public_name (m) &&
+ check (m))
+ {
+ assoc_member am;
+ am.m = &m;
+ am.vo = vo_;
+ pub_members_.push_back (am);
+ found_ = true;
+ }
+ }
+ }
+
+ bool
+ check (semantics::data_member& m)
+ {
+ // Make sure that the found node can possibly match.
+ //
+ if (context::transient (m) ||
+ context::inverse (m) ||
+ m.count ("polymorphic-ref"))
+ return false;
+
+ return check_types (utype (m), type_);
+ }
+
+ assoc_members& members_;
+ assoc_members& pub_members_;
+
+ string name_;
+ string pub_name_;
+ semantics::type& type_;
+
+ view_object* vo_;
+ bool exact_;
+ bool found_;
+ };
+
+ traversal::names names_;
+ data_member member_;
+ traversal::inherits inherits_;
+ };
+
+ private:
+ semantics::class_& view_;
+ view_query& query_;
+ view_alias_map& amap_;
+ view_object_map& omap_;
+ cxx_string_lexer lex_;
+ };
+
+ struct class_: traversal::class_, context
+ {
+ class_ ()
+ : typedefs_ (true)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+
+ member_names_ >> member_;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type k (class_kind (c));
+
+ if (k == class_other)
+ return;
+
+ names (c); // Process nested classes.
+ names (c, member_names_);
+
+ if (k == class_object)
+ traverse_object (c);
+ else if (k == class_view)
+ traverse_view (c);
+ }
+
+ //
+ // Object.
+ //
+
+ virtual void
+ traverse_object (type& c)
+ {
+ // Remove the bulk pragma if this database doesn't support bulk
+ // operations.
+ //
+ if (c.count ("bulk") && !generate_bulk)
+ c.remove ("bulk");
+
+ // Process indexes. Here we need to do two things: resolve member
+ // names to member paths and assign names to unnamed indexes. We
+ // are also going to handle the special container indexes.
+ //
+ indexes& ins (c.count ("index")
+ ? c.get<indexes> ("index")
+ : c.set ("index", indexes ()));
+
+ for (indexes::iterator i (ins.begin ()); i != ins.end ();)
+ {
+ index& in (*i);
+
+ // This should never happen since a db index pragma without
+ // the member specifier will be treated as a member pragma.
+ //
+ assert (!in.members.empty ());
+
+ // First resolve member names.
+ //
+ index::members_type::iterator j (in.members.begin ());
+ for (; j != in.members.end (); ++j)
+ {
+ index::member& im (*j);
+
+ if (!im.path.empty ())
+ continue; // Already resolved.
+
+ im.path = resolve_data_members (c, im.name, im.loc, lex_);
+
+ if (container (*im.path.back ()))
+ break;
+ }
+
+ // Add the table prefix if this database has global index names.
+ //
+ if (!in.name.empty () && global_index)
+ in.name = table_name_prefix (class_scope (c)) + in.name;
+
+ // Handle container indexes.
+ //
+ if (j != in.members.end ())
+ {
+ // Do some sanity checks.
+ //
+ if (in.members.size () != 1)
+ {
+ error (in.loc) << "multiple data members specified for a "
+ << "container index" << endl;
+ throw operation_failed ();
+ }
+
+ string tl;
+ if (lex_.next (tl) != CPP_DOT || lex_.next (tl) != CPP_NAME ||
+ (tl != "id" && tl != "index"))
+ {
+ error (j->loc) << ".id or .index special member expected in a "
+ << "container index" << endl;
+ throw operation_failed ();
+ }
+
+ string n (tl);
+
+ if (lex_.next (tl) != CPP_EOF)
+ {
+ error (j->loc) << "unexpected text after ." << n << " in "
+ << "db pragma member" << endl;
+ throw operation_failed ();
+ }
+
+ // Move this index to the container member.
+ //
+ j->path.back ()->set (n + "-index", *i);
+ i = ins.erase (i);
+ continue;
+ }
+
+ // Now assign the name if the index is unnamed. We have to
+ // add table name as a prefix here since there is not way
+ // to distinguish between user-assigned and auto-derived
+ // names in the model.
+ //
+ if (in.name.empty ())
+ {
+ // Make sure there is only one member.
+ //
+ if (in.members.size () > 1)
+ {
+ error (in.loc) << "unnamed index with more than one data "
+ << "member" << endl;
+ throw operation_failed ();
+ }
+
+ // Generally, we want the index name to be based on the column
+ // name. This is straightforward for single-column members. In
+ // case of a composite member, we will need to use the column
+ // prefix which is based on the data member name, unless
+ // overridden by the user. In the latter case the prefix can
+ // be empty, in which case we will just fall back on the
+ // member's public name.
+ //
+ string n (column_prefix (in.members.front ().path, true).prefix);
+
+ if (n.empty ())
+ n = public_name_db (*in.members.front ().path.back ());
+ else if (n[n.size () - 1] == '_')
+ n.resize (n.size () - 1); // Remove trailing underscore.
+
+ in.name = index_name (table_name (c), n);
+ }
+
+ ++i;
+ }
+ }
+
+ //
+ // View.
+ //
+
+ struct relationship
+ {
+ semantics::data_member* member;
+ string name;
+ view_object* pointer;
+ view_object* pointee;
+ };
+
+ typedef vector<relationship> relationships;
+
+ virtual void
+ traverse_view (type& c)
+ {
+ bool has_q (c.count ("query"));
+ bool has_o (c.count ("objects"));
+
+ // Determine the kind of query template we've got.
+ //
+ view_query& vq (has_q
+ ? c.get<view_query> ("query")
+ : c.set ("query", view_query ()));
+ if (has_q)
+ {
+ if (!vq.literal.empty ())
+ {
+ string q (upcase (vq.literal));
+
+ //@@ We need to recognize database-specific list of prefixes. For
+ // example, PG has WITH. Alternatively (or in addition) we could
+ // do the same comment trick (e.g., /*SELECT*/ to treat it as a
+ // SELECT-like queiry).
+ //
+ if (q.compare (0, 7, "SELECT ") == 0)
+ vq.kind = view_query::complete_select;
+ else if (q.compare (0, 5, "EXEC ") == 0 ||
+ q.compare (0, 5, "CALL ") == 0 ||
+ q.compare (0, 8, "EXECUTE ") == 0)
+ vq.kind = view_query::complete_execute;
+ //
+ // Hint for databases that use SELECT for stored procedure
+ // calls (e.g., PostgreSQL).
+ //
+ else if (q.compare (0, 8, "/*CALL*/") == 0)
+ {
+ vq.literal = string (vq.literal, q[8] == ' ' ? 9 : 8);
+ vq.kind = view_query::complete_execute;
+ }
+ else
+ vq.kind = view_query::condition;
+ }
+ else if (!vq.expr.empty ())
+ {
+ // If the first token in the expression is a string and
+ // it starts with "SELECT " or is equal to "SELECT" or
+ // one of the stored procedure call keywords, then we
+ // have a complete query.
+ //
+ if (vq.expr.front ().type == CPP_STRING)
+ {
+ string q (upcase (vq.expr.front ().literal));
+
+ if (q.compare (0, 7, "SELECT ") == 0 || q == "SELECT")
+ vq.kind = view_query::complete_select;
+ else if (q.compare (0, 5, "EXEC ") == 0 || q == "EXEC" ||
+ q.compare (0, 5, "CALL ") == 0 || q == "CALL" ||
+ q.compare (0, 8, "EXECUTE ") == 0 || q == "EXECUTE")
+ vq.kind = view_query::complete_execute;
+ else if (q.compare (0, 8, "/*CALL*/") == 0)
+ {
+ vq.expr.front ().literal =
+ string (vq.expr.front ().literal, q[8] == ' ' ? 9 : 8);
+ vq.kind = view_query::complete_execute;
+ }
+ else
+ vq.kind = view_query::condition;
+ }
+ else
+ vq.kind = view_query::condition;
+ }
+ else
+ vq.kind = (vq.distinct || vq.for_update)
+ ? view_query::condition // The query(distinct) case.
+ : view_query::runtime;
+ }
+ else
+ vq.kind = has_o ? view_query::condition : view_query::runtime;
+
+ if ((vq.distinct || vq.for_update) && vq.kind != view_query::condition)
+ {
+ error (vq.loc)
+ << "result modifier specified for "
+ << (vq.kind == view_query::runtime ? "runtime" : "native")
+ << " query" << endl;
+
+ throw operation_failed ();
+ }
+
+ // We cannot have an incomplete query if there are not objects
+ // to derive the rest from.
+ //
+ if (vq.kind == view_query::condition && !has_o)
+ {
+ error (c.file (), c.line (), c.column ())
+ << "view '" << class_fq_name (c) << "' has an incomplete query "
+ << "template and no associated objects" << endl;
+
+ info (c.file (), c.line (), c.column ())
+ << "use db pragma query to provide a complete query template"
+ << endl;
+
+ info (c.file (), c.line (), c.column ())
+ << "or use db pragma object to associate one or more objects "
+ << "with the view"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ // Process join conditions.
+ //
+ if (has_o)
+ {
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ if (i == objs.begin () && i->join != view_object::left)
+ {
+ error (i->loc)
+ << "no join type can be specified for the first associated "
+ << (i->kind == view_object::object ? "object" : "table")
+ << endl;
+ throw operation_failed ();
+ }
+
+ if (i->kind != view_object::object)
+ {
+ // Make sure we have join conditions for tables unless it
+ // is the first entry.
+ //
+ if (i != objs.begin () && i->cond.empty ())
+ {
+ error (i->loc)
+ << "missing join condition in db pragma table" << endl;
+
+ throw operation_failed ();
+ }
+
+ continue;
+ }
+
+ // If we have to generate the query and there was no JOIN
+ // condition specified by the user, try to come up with one
+ // automatically based on object relationships. CROSS JOIN
+ // has no condition.
+ //
+ if (vq.kind == view_query::condition &&
+ i->cond.empty () &&
+ i != objs.begin () &&
+ i->join != view_object::cross)
+ {
+ relationships rs;
+
+ // Check objects specified prior to this one for any
+ // relationships. We don't examine objects that were
+ // specified after this one because that would require
+ // rearranging the JOIN order.
+ //
+ for (view_objects::iterator j (objs.begin ()); j != i; ++j)
+ {
+ if (j->kind != view_object::object)
+ continue; // Skip tables.
+
+ // First see if any of the objects that were specified
+ // prior to this object point to it.
+ //
+ {
+ relationship_resolver r (rs, *i, true);
+ r.traverse (*j);
+ }
+
+ // Now see if this object points to any of the objects
+ // specified prior to it.
+ //
+ {
+ relationship_resolver r (rs, *j, false);
+ r.traverse (*i);
+ }
+ }
+
+ // Issue diagnostics if we didn't find any or found more
+ // than one.
+ //
+ if (rs.empty ())
+ {
+ error (i->loc)
+ << "unable to find an object relationship involving "
+ << "object '" << i->name () << "' and any of the previously "
+ << "associated objects" << endl;
+
+ info (i->loc)
+ << "use the join condition clause in db pragma object "
+ << "to specify a custom join condition" << endl;
+
+ throw operation_failed ();
+ }
+ else if (rs.size () > 1)
+ {
+ error (i->loc)
+ << "object relationship for object '" << i->name () << "' "
+ << "is ambiguous" << endl;
+
+ info (i->loc)
+ << "candidates are:" << endl;
+
+ for (relationships::const_iterator j (rs.begin ());
+ j != rs.end ();
+ ++j)
+ {
+ semantics::data_member& m (*j->member);
+
+ info (m.file (), m.line (), m.column ())
+ << " '" << j->name << "' "
+ << "in object '" << j->pointer->name () << "' "
+ << "pointing to '" << j->pointee->name () << "'"
+ << endl;
+ }
+
+ info (i->loc)
+ << "use the join condition clause in db pragma object "
+ << "to resolve this ambiguity" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Synthesize the condition.
+ //
+ relationship const& r (rs.back ());
+
+ string name (r.pointer->alias.empty ()
+ ? class_fq_name (*r.pointer->obj)
+ : r.pointer->alias);
+ name += "::";
+ name += r.name;
+
+ lex_.start (name);
+
+ string t;
+ for (cpp_ttype tt (lex_.next (t));
+ tt != CPP_EOF;
+ tt = lex_.next (t))
+ {
+ i->cond.push_back (cxx_token (lex_.location (), tt, t));
+ }
+ }
+ }
+ }
+
+ // Handle forced versioning. When versioning is forced, ignore
+ // it for native views.
+ //
+ if (force_versioned && vq.kind == view_query::condition)
+ c.set ("versioned", true);
+
+ // Handle data members.
+ //
+ {
+ view_data_member t (c);
+ traversal::names n (t);
+ names (c, n);
+ }
+ }
+
+ struct relationship_resolver: object_members_base
+ {
+ relationship_resolver (relationships& rs,
+ view_object& pointee,
+ bool forward)
+ // Look in polymorphic bases only for previously-associated
+ // objects since backward pointers from bases will result in
+ // the pathological case (we will have to join the base table
+ // first, which means we will get both bases and derived objects
+ // instead of just derived).
+ //
+ : object_members_base (false, false, true, forward),
+ relationships_ (rs),
+ // Ignore self-references if we are looking for backward
+ // pointers since they were already added to the list in
+ // the previous pass.
+ //
+ self_pointer_ (forward),
+ pointer_ (0),
+ pointee_ (pointee)
+ {
+ }
+
+ void
+ traverse (view_object& pointer)
+ {
+ pointer_ = &pointer;
+ object_members_base::traverse (*pointer.obj);
+ }
+
+ using object_members_base::traverse; // Unhide.
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ // Ignore inverse sides of the same relationship to avoid
+ // phony conflicts caused by the direct side that will end
+ // up in the relationship list as well. Unless the inverse
+ // member is in the polymorphic base in which case we will
+ // miss it since we don't examine inside poly bases on the
+ // backwards scan (see above).
+ //
+ if (data_member_path* imp = inverse (m))
+ {
+ if (&imp->front ()->scope () == &c) // Direct member.
+ return;
+ }
+
+ // Ignore self-pointers if requested.
+ //
+ if (!self_pointer_ && pointer_->obj == &c)
+ return;
+
+ if (pointee_.obj == &c)
+ {
+ relationships_.push_back (relationship ());
+ relationships_.back ().member = &m;
+ relationships_.back ().name = member_prefix_ + m.name ();
+ relationships_.back ().pointer = pointer_;
+ relationships_.back ().pointee = &pointee_;
+ }
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ if (semantics::class_* c =
+ object_pointer (context::container_vt (m)))
+ {
+ if (inverse (m, "value"))
+ return;
+
+ // Ignore self-pointers if requested.
+ //
+ if (!self_pointer_ && pointer_->obj == c)
+ return;
+
+ if (pointee_.obj == c)
+ {
+ relationships_.push_back (relationship ());
+ relationships_.back ().member = &m;
+ relationships_.back ().name = member_prefix_ + m.name ();
+ relationships_.back ().pointer = pointer_;
+ relationships_.back ().pointee = &pointee_;
+ }
+ }
+ }
+
+ private:
+ relationships& relationships_;
+ bool self_pointer_;
+ view_object* pointer_;
+ view_object& pointee_;
+ };
+
+ private:
+ cxx_string_lexer lex_;
+
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ data_member member_;
+ traversal::names member_names_;
+ };
+ }
+
+ void
+ process ()
+ {
+ context ctx;
+
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (true);
+ traversal::namespace_ ns;
+ class_ c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (true);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx.unit);
+ }
+}
diff --git a/odb/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/odb/relational/schema.hxx b/odb/odb/relational/schema.hxx
new file mode 100644
index 0000000..cd975b7
--- /dev/null
+++ b/odb/odb/relational/schema.hxx
@@ -0,0 +1,1606 @@
+// file : odb/relational/schema.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_SCHEMA_HXX
+#define ODB_RELATIONAL_SCHEMA_HXX
+
+#include <set>
+#include <vector>
+#include <cassert>
+
+#include <odb/emitter.hxx>
+#include <odb/relational/common.hxx>
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ namespace schema
+ {
+ typedef std::set<qname> table_set;
+
+ struct common: virtual context
+ {
+ typedef ::emitter emitter_type;
+
+ common (emitter_type& e, ostream& os, schema_format f)
+ : e_ (e), os_ (os), format_ (f) {}
+
+ void
+ pre_statement ()
+ {
+ e_.pre ();
+ diverge (os_);
+ }
+
+ void
+ post_statement ()
+ {
+ restore ();
+ e_.post ();
+ }
+
+ emitter_type&
+ emitter () const
+ {
+ return e_;
+ }
+
+ ostream&
+ stream () const
+ {
+ return os_;
+ }
+
+ public:
+ // Find an entity corresponding to the drop node in alter_table.
+ //
+ template <typename T, typename D>
+ T&
+ find (D& d)
+ {
+ using sema_rel::model;
+ using sema_rel::changeset;
+ using sema_rel::table;
+ using sema_rel::alter_table;
+
+ alter_table& at (dynamic_cast<alter_table&> (d.scope ()));
+ changeset& cs (dynamic_cast<changeset&> (at.scope ()));
+ model& bm (cs.base_model ());
+ table* bt (bm.find<table> (at.name ()));
+ assert (bt != 0);
+ T* b (bt->find<T> (d.name ()));
+ assert (b != 0);
+ return *b;
+ }
+
+ protected:
+ emitter_type& e_;
+ ostream& os_;
+ schema_format format_;
+ };
+
+ //
+ // Drop.
+ //
+
+ // Only used in migration.
+ //
+ struct drop_column: trav_rel::drop_column, common
+ {
+ typedef drop_column base;
+
+ drop_column (common const& c, bool* first = 0)
+ : common (c),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ drop_column (drop_column const& c)
+ : root_context (), // @@ -Wextra
+ context (),
+ common (c),
+ first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
+ first_data_ (c.first_data_)
+ {
+ }
+
+ virtual void
+ drop_header ()
+ {
+ // By default ADD COLUMN though some databases use just ADD.
+ //
+ os << "DROP COLUMN ";
+ }
+
+ virtual void
+ traverse (sema_rel::drop_column& dc)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl
+ << " ";
+ drop_header ();
+ os << quote_id (dc.name ());
+ }
+
+ protected:
+ bool& first_;
+ bool first_data_;
+ };
+
+ // Normally only used in migration but some databases use it as a
+ // base to also drop foreign keys in schema.
+ //
+ struct drop_foreign_key: trav_rel::foreign_key,
+ trav_rel::drop_foreign_key,
+ trav_rel::add_foreign_key, // Override.
+ common
+ {
+ typedef drop_foreign_key base;
+
+ // Schema constructor.
+ //
+ drop_foreign_key (common const& c, table_set& dropped, bool* first = 0)
+ : common (c),
+ dropped_ (&dropped),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ // Migration constructor.
+ //
+ drop_foreign_key (common const& c, bool* first = 0)
+ : common (c),
+ dropped_ (0),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ drop_foreign_key (drop_foreign_key const& c)
+ : root_context (), // @@ -Wextra
+ context (),
+ common (c),
+ dropped_ (c.dropped_),
+ first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
+ first_data_ (c.first_data_)
+ {
+ }
+
+ virtual void
+ drop_header ()
+ {
+ os << "DROP CONSTRAINT ";
+ }
+
+ virtual void
+ traverse (sema_rel::foreign_key& fk)
+ {
+ // If the table which we reference is droped before us, then
+ // we need to drop the constraint first. Similarly, if the
+ // referenced table is not part if this model, then assume
+ // it is dropped before us. In migration we always do this
+ // first.
+ //
+ sema_rel::table& t (dynamic_cast<sema_rel::table&> (fk.scope ()));
+
+ if (dropped_ != 0)
+ {
+ sema_rel::qname const& rt (fk.referenced_table ());
+ sema_rel::model& m (dynamic_cast<sema_rel::model&> (t.scope ()));
+
+ if (dropped_->find (rt) == dropped_->end () &&
+ m.find (rt) != m.names_end ())
+ return;
+ }
+
+ drop (t, fk);
+ }
+
+ virtual void
+ drop (sema_rel::table& t, sema_rel::foreign_key& fk)
+ {
+ // When generating schema we would need to check if the key exists.
+ // So this implementation will need to be customize on the per-
+ // database level.
+ //
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (t.name ()) << endl
+ << " ";
+ drop_header ();
+ os << quote_id (fk.name ()) << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ traverse (sema_rel::drop_foreign_key& dfk)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl;
+ drop (dfk);
+ }
+
+ virtual void
+ drop (sema_rel::drop_foreign_key& dfk)
+ {
+ os << " ";
+ drop_header ();
+ os << quote_id (dfk.name ());
+ }
+
+ protected:
+ table_set* dropped_;
+ bool& first_;
+ bool first_data_;
+ };
+
+ // Currently only used in migration.
+ //
+ struct drop_index: trav_rel::drop_index, common
+ {
+ typedef drop_index base;
+
+ enum index_type {unique, non_unique, all};
+
+ drop_index (common const& c, index_type t = all)
+ : common (c), type_ (t) {}
+
+ virtual void
+ traverse (sema_rel::drop_index& di)
+ {
+ // Find the index we are dropping in the base model.
+ //
+ traverse (find<sema_rel::index> (di));
+ }
+
+ virtual void
+ traverse (sema_rel::index& in)
+ {
+ if (type_ == unique &&
+ in.type ().find ("UNIQUE") == string::npos &&
+ in.type ().find ("unique") == string::npos)
+ return;
+
+ if (type_ == non_unique && (
+ in.type ().find ("UNIQUE") != string::npos ||
+ in.type ().find ("unique") != string::npos))
+ return;
+
+ pre_statement ();
+ drop (in);
+ post_statement ();
+ }
+
+ virtual string
+ name (sema_rel::index& in)
+ {
+ return quote_id (in.name ());
+ }
+
+ virtual void
+ drop (sema_rel::index& in)
+ {
+ os << "DROP INDEX " << name (in) << endl;
+ }
+
+ protected:
+ index_type type_;
+ };
+
+ struct drop_table: trav_rel::table,
+ trav_rel::drop_table,
+ trav_rel::add_table, // Override.
+ trav_rel::alter_table, // Override.
+ common
+ {
+ typedef drop_table base;
+
+ drop_table (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f) {}
+
+ virtual void
+ drop (sema_rel::table& t, bool migration)
+ {
+ pre_statement ();
+ os << "DROP TABLE " << (migration ? "" : "IF EXISTS ") <<
+ quote_id (t.name ()) << endl;
+ post_statement ();
+ }
+
+ virtual void
+ delete_ (sema_rel::qname const& rtable,
+ sema_rel::qname const& dtable,
+ sema_rel::primary_key& rkey,
+ sema_rel::primary_key& dkey)
+ {
+ pre_statement ();
+
+ // This might not be the most efficient way for every database.
+ //
+ os << "DELETE FROM " << quote_id (rtable) << endl
+ << " WHERE EXISTS (SELECT 1 FROM " << quote_id (dtable) << endl
+ << " WHERE ";
+
+ for (size_t i (0); i != rkey.contains_size (); ++i)
+ {
+ if (i != 0)
+ os << endl
+ << " AND ";
+
+ os << quote_id (rtable) << "." <<
+ quote_id (rkey.contains_at (i).column ().name ()) << " = " <<
+ quote_id (dtable) << "." <<
+ quote_id (dkey.contains_at (i).column ().name ());
+ }
+
+ os << ")" << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ traverse (sema_rel::table& t, bool migration)
+ {
+ // By default drop foreign keys referencing tables that would
+ // have already been dropped on the first pass.
+ //
+ if (pass_ == 1)
+ {
+ // Drop constraints. In migration this is always done on pass 1.
+ //
+ if (migration)
+ {
+ instance<drop_foreign_key> dfk (*this);
+ trav_rel::unames n (*dfk);
+ names (t, n);
+ }
+ else
+ {
+ dropped_.insert (t.name ()); // Add it before to cover self-refs.
+
+ instance<drop_foreign_key> dfk (*this, dropped_);
+ trav_rel::unames n (*dfk);
+ names (t, n);
+ }
+ }
+ else
+ {
+ if (migration && t.extra ()["kind"] == "polymorphic derived object")
+ {
+ // If we are dropping a polymorphic derived object, then we
+ // also have to clean the base tables. Note that this won't
+ // trigger cascade deletion since we have dropped all the
+ // keys on pass 1. But we still need to do this in the base
+ // to root order in order not to trigger other cascades.
+ //
+ using sema_rel::model;
+ using sema_rel::table;
+ using sema_rel::primary_key;
+ using sema_rel::foreign_key;
+
+ model& m (dynamic_cast<model&> (t.scope ()));
+
+ table* p (&t);
+ do
+ {
+ // The polymorphic link is the first primary key.
+ //
+ for (table::names_iterator i (p->names_begin ());
+ i != p->names_end (); ++i)
+ {
+ if (foreign_key* fk = dynamic_cast<foreign_key*> (
+ &i->nameable ()))
+ {
+ p = m.find<table> (fk->referenced_table ());
+ assert (p != 0); // Base table should be there.
+ break;
+ }
+ }
+
+ primary_key& rkey (*p->find<primary_key> (""));
+ primary_key& dkey (*t.find<primary_key> (""));
+ assert (rkey.contains_size () == dkey.contains_size ());
+ delete_ (p->name (), t.name (), rkey, dkey);
+ }
+ while (p->extra ()["kind"] != "polymorphic root object");
+ }
+
+ drop (t, migration);
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::table& t)
+ {
+ traverse (t, false);
+ }
+
+ virtual void
+ traverse (sema_rel::drop_table& dt)
+ {
+ using sema_rel::model;
+ using sema_rel::changeset;
+ using sema_rel::table;
+
+ // Find the table we are dropping in the base model.
+ //
+ changeset& cs (dynamic_cast<changeset&> (dt.scope ()));
+ model& bm (cs.base_model ());
+ table* t (bm.find<table> (dt.name ()));
+ assert (t != 0);
+ traverse (*t, true);
+ }
+
+ using add_table::traverse; // Unhide.
+ using alter_table::traverse; // Unhide.
+
+ using table::names;
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ table_set dropped_;
+ };
+
+ struct drop_model: trav_rel::model, common
+ {
+ typedef drop_model base;
+
+ drop_model (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f)
+ {
+ }
+
+ virtual void
+ traverse (sema_rel::model& m)
+ {
+ // Traverse named entities in the reverse order. This way we
+ // drop them in the order opposite to creating.
+ //
+ for (sema_rel::model::names_iterator begin (m.names_begin ()),
+ end (m.names_end ()); begin != end;)
+ dispatch (*--end);
+ }
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ };
+
+ //
+ // Create.
+ //
+
+ struct create_column: trav_rel::column,
+ trav_rel::add_column,
+ trav_rel::alter_column, // Override.
+ common
+ {
+ typedef create_column base;
+
+ create_column (common const& c,
+ bool override_null = true,
+ bool* first = 0)
+ : common (c),
+ override_null_ (override_null),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ create_column (create_column const& c)
+ : root_context (), // @@ -Wextra
+ context (),
+ common (c),
+ override_null_ (c.override_null_),
+ first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
+ first_data_ (c.first_data_)
+ {
+ }
+
+ virtual void
+ traverse (sema_rel::column& c)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl
+ << " ";
+ create (c);
+ }
+
+ virtual void
+ add_header ()
+ {
+ // By default ADD COLUMN though some databases use just ADD.
+ //
+ os << "ADD COLUMN ";
+ }
+
+ virtual void
+ traverse (sema_rel::add_column& ac)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl
+ << " ";
+ add_header ();
+ create (ac);
+ }
+
+ virtual void
+ create (sema_rel::column& c)
+ {
+ using sema_rel::column;
+
+ // See if this column is (part of) a primary key.
+ //
+ sema_rel::primary_key* pk (0);
+
+ for (column::contained_iterator i (c.contained_begin ());
+ i != c.contained_end ();
+ ++i)
+ {
+ if ((pk = dynamic_cast<sema_rel::primary_key*> (&i->key ())))
+ break;
+ }
+
+ os << quote_id (c.name ()) << " ";
+
+ type (c, pk != 0 && pk->auto_ ());
+ constraints (c, pk);
+
+ if (!c.options ().empty ())
+ os << " " << c.options ();
+ }
+
+ virtual void
+ constraints (sema_rel::column& c, sema_rel::primary_key* pk)
+ {
+ null (c);
+
+ if (!c.default_ ().empty ())
+ os << " DEFAULT " << c.default_ ();
+
+ // If this is a single-column primary key, generate it inline.
+ //
+ if (pk != 0 && pk->contains_size () == 1)
+ primary_key ();
+
+ if (pk != 0 && pk->auto_ ())
+ auto_ (*pk);
+ }
+
+ virtual void
+ type (sema_rel::column& c, bool /*auto*/)
+ {
+ os << c.type ();
+ }
+
+ virtual void
+ null (sema_rel::column& c)
+ {
+ bool n (c.null ());
+
+ // If we are adding a new column that doesn't allow NULL nor has
+ // a default value, add it as NULL. Later, after migration, we
+ // will convert it to NOT NULL.
+ //
+ if (override_null_ && c.is_a<sema_rel::add_column> () &&
+ !n && c.default_ ().empty ())
+ n = true;
+
+ // Specify both cases explicitly for better readability,
+ // especially in ALTER COLUMN clauses.
+ //
+ os << (n ? " NULL" : " NOT NULL");
+ }
+
+ virtual void
+ primary_key ()
+ {
+ os << " PRIMARY KEY";
+ }
+
+ virtual void
+ auto_ (sema_rel::primary_key&)
+ {
+ }
+
+ protected:
+ bool override_null_; // Override NOT NULL in add_column.
+ bool& first_;
+ bool first_data_;
+ bool add_;
+ };
+
+ struct create_primary_key: trav_rel::primary_key, common
+ {
+ typedef create_primary_key base;
+
+ create_primary_key (common const& c): common (c) {}
+
+ virtual void
+ traverse (sema_rel::primary_key& pk)
+ {
+ // Single-column primary keys are generated inline in the
+ // column declaration.
+ //
+ if (pk.contains_size () == 1)
+ return;
+
+ // We will always follow a column.
+ //
+ os << "," << endl;
+
+ create (pk);
+ }
+
+ virtual void
+ create (sema_rel::primary_key& pk)
+ {
+ using sema_rel::primary_key;
+
+ // By default we create unnamed primary key constraint.
+ //
+
+ os << " PRIMARY KEY (";
+
+ for (primary_key::contains_iterator i (pk.contains_begin ());
+ i != pk.contains_end ();
+ ++i)
+ {
+ if (i != pk.contains_begin ())
+ os << "," << endl
+ << " ";
+
+ os << quote_id (i->column ().name ());
+ }
+
+ os << ")";
+ }
+ };
+
+ struct create_foreign_key: trav_rel::foreign_key,
+ trav_rel::add_foreign_key,
+ common
+ {
+ typedef create_foreign_key base;
+
+ // Schema constructor, pass 1.
+ //
+ create_foreign_key (common const& c, table_set& created, bool* first = 0)
+ : common (c),
+ created_ (&created),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ // Schema constructor, pass 2 and migration constructor.
+ //
+ create_foreign_key (common const& c, bool* first = 0)
+ : common (c),
+ created_ (0),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ create_foreign_key (create_foreign_key const& c)
+ : root_context (), // @@ -Wextra
+ context (),
+ common (c),
+ created_ (c.created_),
+ first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
+ first_data_ (c.first_data_)
+ {
+ }
+
+ virtual void
+ traverse (sema_rel::foreign_key& fk)
+ {
+ if (created_ != 0)
+ {
+ // Pass 1.
+ //
+ // If the referenced table has already been defined, do the
+ // foreign key definition in the table definition. Otherwise
+ // postpone it until pass 2 where we do it via ALTER TABLE.
+ //
+ if (created_->find (fk.referenced_table ()) != created_->end ())
+ {
+ traverse_create (fk);
+ fk.set (db.string () + "-fk-defined", true); // Mark it as defined.
+ }
+ }
+ else
+ {
+ // Pass 2.
+ //
+ if (!fk.count (db.string () + "-fk-defined"))
+ traverse_add (fk);
+ }
+ }
+
+ virtual void
+ traverse_create (sema_rel::foreign_key& fk)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl
+ << " CONSTRAINT ";
+ create (fk);
+ }
+
+ virtual void
+ traverse_add (sema_rel::foreign_key& fk)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl;
+ add (fk);
+ }
+
+ virtual void
+ traverse (sema_rel::add_foreign_key& afk)
+ {
+ traverse_add (afk);
+ }
+
+ virtual void
+ add_header ()
+ {
+ os << "ADD CONSTRAINT ";
+ }
+
+ virtual void
+ add (sema_rel::foreign_key& fk)
+ {
+ os << " ";
+ add_header ();
+ create (fk);
+ }
+
+ virtual void
+ create (sema_rel::foreign_key& fk)
+ {
+ using sema_rel::foreign_key;
+
+ os << name (fk) << endl
+ << " FOREIGN KEY (";
+
+ for (foreign_key::contains_iterator i (fk.contains_begin ());
+ i != fk.contains_end ();
+ ++i)
+ {
+ if (i != fk.contains_begin ())
+ os << "," << endl
+ << " ";
+
+ os << quote_id (i->column ().name ());
+ }
+
+ string tn (table_name (fk));
+ string tn_pad (tn.size (), ' ');
+
+ os << ")" << endl
+ << " REFERENCES " << tn << " (";
+
+ foreign_key::columns const& refs (fk.referenced_columns ());
+ for (foreign_key::columns::const_iterator i (refs.begin ());
+ i != refs.end ();
+ ++i)
+ {
+ if (i != refs.begin ())
+ os << "," << endl
+ << " " << tn_pad;
+
+ os << quote_id (*i);
+ }
+
+ os << ")";
+
+ if (fk.on_delete () != foreign_key::no_action)
+ on_delete (fk.on_delete ());
+
+ if (!fk.not_deferrable ())
+ deferrable (fk.deferrable ());
+ }
+
+ virtual string
+ name (sema_rel::foreign_key& fk)
+ {
+ return quote_id (fk.name ());
+ }
+
+ virtual string
+ table_name (sema_rel::foreign_key& fk)
+ {
+ return quote_id (fk.referenced_table ());
+ }
+
+ virtual void
+ on_delete (sema_rel::foreign_key::action_type a)
+ {
+ using sema_rel::foreign_key;
+
+ switch (a)
+ {
+ case foreign_key::no_action:
+ break;
+ case foreign_key::cascade:
+ {
+ os << endl
+ << " ON DELETE CASCADE";
+ break;
+ }
+ case foreign_key::set_null:
+ {
+ os << endl
+ << " ON DELETE SET NULL";
+ break;
+ }
+ }
+ }
+
+ virtual void
+ deferrable (sema_rel::deferrable d)
+ {
+ os << endl
+ << " DEFERRABLE INITIALLY " << d;
+ }
+
+ protected:
+ table_set* created_;
+ bool& first_;
+ bool first_data_;
+ };
+
+ struct create_index: trav_rel::index, common
+ {
+ typedef create_index base;
+
+ enum index_type {unique, non_unique, all};
+
+ create_index (common const& c, index_type t = all)
+ : common (c), type_ (t) {}
+
+ virtual void
+ traverse (sema_rel::index& in)
+ {
+ if (type_ == unique &&
+ in.type ().find ("UNIQUE") == string::npos &&
+ in.type ().find ("unique") == string::npos)
+ return;
+
+ if (type_ == non_unique && (
+ in.type ().find ("UNIQUE") != string::npos ||
+ in.type ().find ("unique") != string::npos))
+ return;
+
+ pre_statement ();
+ create (in);
+ post_statement ();
+ }
+
+ virtual string
+ name (sema_rel::index& in)
+ {
+ return quote_id (in.name ());
+ }
+
+ virtual string
+ table_name (sema_rel::index& in)
+ {
+ return quote_id (static_cast<sema_rel::table&> (in.scope ()).name ());
+ }
+
+ virtual void
+ columns (sema_rel::index& in)
+ {
+ using sema_rel::index;
+
+ for (index::contains_iterator i (in.contains_begin ());
+ i != in.contains_end ();
+ ++i)
+ {
+ if (in.contains_size () > 1)
+ {
+ if (i != in.contains_begin ())
+ os << ",";
+
+ os << endl
+ << " ";
+ }
+
+ os << quote_id (i->column ().name ());
+
+ if (!i->options ().empty ())
+ os << ' ' << i->options ();
+ }
+ }
+
+ virtual void
+ create (sema_rel::index& in)
+ {
+ // Default implementation that ignores the method.
+ //
+ os << "CREATE ";
+
+ if (!in.type ().empty ())
+ os << in.type () << ' ';
+
+ os << "INDEX " << name (in) << endl
+ << " ON " << table_name (in) << " (";
+
+ columns (in);
+
+ os << ")" << endl;
+
+ if (!in.options ().empty ())
+ os << ' ' << in.options () << endl;
+ }
+
+ protected:
+ index_type type_;
+ };
+
+ struct create_table: trav_rel::table,
+ trav_rel::alter_table, // Override.
+ common
+ {
+ typedef create_table base;
+
+ using trav_rel::table::names;
+
+ create_table (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f) {}
+
+ virtual void
+ create_pre (sema_rel::qname const& table)
+ {
+ os << "CREATE TABLE " << quote_id (table) << " (";
+ }
+
+ virtual void
+ create_post (sema_rel::table& t)
+ {
+ os << ")" << endl;
+
+ if (!t.options ().empty ())
+ os << " " << t.options () << endl;
+ }
+
+ virtual void
+ create (sema_rel::table& t)
+ {
+ pre_statement ();
+ create_pre (t.name ());
+
+ instance<create_column> c (*this);
+ instance<create_primary_key> pk (*this);
+
+ // We will always follow a column, so set first to false.
+ //
+ bool f (false); // (Im)perfect forwarding.
+ bool* pf (&f); // (Im)perfect forwarding.
+ instance<create_foreign_key> fk (*this, created_, pf);
+
+ trav_rel::unames n;
+ n >> c;
+ n >> pk;
+ n >> fk;
+
+ names (t, n);
+
+ create_post (t);
+ post_statement ();
+
+ // Create indexes.
+ //
+ {
+ instance<create_index> in (*this);
+ trav_rel::unames n (*in);
+ names (t, n);
+ }
+ }
+
+ // See if there are any undefined foreign keys that we need to
+ // add with ALTER TABLE.
+ //
+ bool
+ check_undefined_fk (sema_rel::table& t)
+ {
+ for (sema_rel::table::names_iterator i (t.names_begin ());
+ i != t.names_end (); ++i)
+ {
+ if (i->nameable ().is_a<sema_rel::foreign_key> () &&
+ !i->nameable ().count (db.string () + "-fk-defined"))
+ return true;
+ }
+ return false;
+ }
+
+ virtual void
+ traverse (sema_rel::table& t)
+ {
+ // By default add foreign keys referencing tables that haven't
+ // yet been defined on the second pass.
+ //
+ if (pass_ == 1)
+ {
+ // In migration we always add foreign keys on pass 2.
+ //
+ if (!t.is_a<sema_rel::add_table> ())
+ created_.insert (t.name ()); // Add it before to cover self-refs.
+
+ create (t);
+ }
+ else
+ {
+ // Add undefined foreign keys.
+ //
+ if (check_undefined_fk (t))
+ {
+ pre_statement ();
+ os << "ALTER TABLE " << quote_id (t.name ());
+
+ instance<create_foreign_key> cfk (*this);
+ trav_rel::unames n (*cfk);
+ names (t, n);
+ os << endl;
+
+ post_statement ();
+ }
+ }
+ }
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ table_set created_;
+ };
+
+ struct create_model: trav_rel::model, common
+ {
+ typedef create_model base;
+
+ create_model (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f) {}
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ };
+
+ //
+ // Alter.
+ //
+
+ struct alter_column: trav_rel::alter_column,
+ trav_rel::add_column,
+ common
+ {
+ typedef alter_column base;
+
+ alter_column (common const& c, bool pre, bool* first = 0)
+ : common (c),
+ pre_ (pre),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true),
+ fl_ (false),
+ def_ (c, fl_)
+ {
+ }
+
+ alter_column (alter_column const& c)
+ : root_context (), // @@ -Wextra
+ context (),
+ common (c),
+ pre_ (c.pre_),
+ first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
+ first_data_ (c.first_data_),
+ fl_ (false),
+ def_ (c, fl_)
+ {
+ }
+
+ virtual void
+ alter_header ()
+ {
+ os << "ALTER COLUMN ";
+ }
+
+ virtual void
+ alter (sema_rel::column& c)
+ {
+ // By default use the whole definition.
+ //
+ def_->create (c);
+ }
+
+ virtual void
+ traverse (sema_rel::column& c)
+ {
+ // Relax (NULL) in pre and tighten (NOT NULL) in post.
+ //
+ if (pre_ != c.null ())
+ return;
+
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl
+ << " ";
+ alter_header ();
+ alter (c);
+ }
+
+ virtual void
+ traverse (sema_rel::alter_column& ac)
+ {
+ assert (ac.null_altered ());
+ traverse (static_cast<sema_rel::column&> (ac));
+ }
+
+ virtual void
+ traverse (sema_rel::add_column& ac)
+ {
+ // We initially add NOT NULL columns without default values as
+ // NULL. Now, after the migration, we convert them to NOT NULL.
+ //
+ if (!ac.null () && ac.default_ ().empty ())
+ traverse (static_cast<sema_rel::column&> (ac));
+ }
+
+ protected:
+ bool pre_;
+ bool& first_;
+ bool first_data_;
+ bool fl_; // (Im)perfect forwarding.
+ instance<create_column> def_;
+ };
+
+ struct alter_table_common: trav_rel::alter_table, common
+ {
+ alter_table_common (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f) {}
+
+ template <typename T>
+ T*
+ check (sema_rel::alter_table& at)
+ {
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ if (T* x = dynamic_cast<T*> (&i->nameable ()))
+ return x;
+ }
+ return 0;
+ }
+
+ sema_rel::column*
+ check_alter_column_null (sema_rel::alter_table& at, bool v)
+ {
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ using sema_rel::add_column;
+ using sema_rel::alter_column;
+
+ if (alter_column* ac = dynamic_cast<alter_column*> (&i->nameable ()))
+ {
+ if (ac->null_altered () && ac->null () == v)
+ return ac;
+ }
+
+ // If we are testing for NOT NULL, also look for new columns that
+ // we initially add as NULL and later convert to NOT NULL.
+ //
+ if (!v)
+ {
+ if (add_column* ac = dynamic_cast<add_column*> (&i->nameable ()))
+ {
+ if (!ac->null () && ac->default_ ().empty ())
+ return ac;
+ }
+ }
+ }
+ return 0;
+ }
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ };
+
+ struct alter_table_pre: alter_table_common
+ {
+ typedef alter_table_pre base;
+
+ alter_table_pre (emitter_type& e, ostream& os, schema_format f)
+ : alter_table_common (e, os, f) {}
+
+ // Check if there will be any clauses in ALTER TABLE.
+ //
+ using alter_table_common::check;
+
+ virtual bool
+ check (sema_rel::alter_table& at)
+ {
+ // If changing the below test, make sure to also update tests
+ // in database-specific code.
+ //
+ return
+ check<sema_rel::drop_foreign_key> (at) ||
+ check<sema_rel::add_column> (at) ||
+ check_alter_column_null (at, true);
+ }
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ // By default we generate all the alterations in a single ALTER TABLE
+ // statement. Quite a few databases don't support this.
+ //
+ pre_statement ();
+ os << "ALTER TABLE " << quote_id (at.name ());
+
+ bool f (true); // Shared first flag.
+ bool* pf (&f); // (Im)perfect forwarding.
+ bool tl (true); // (Im)perfect forwarding.
+ instance<create_column> cc (*this, tl, pf);
+ instance<alter_column> ac (*this, tl, pf);
+ instance<drop_foreign_key> dfk (*this, pf);
+ trav_rel::unames n;
+ n >> cc;
+ n >> ac;
+ n >> dfk;
+ names (at, n);
+ os << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ traverse (sema_rel::alter_table& at)
+ {
+ if (pass_ == 1)
+ {
+ // Drop unique indexes.
+ //
+ {
+ drop_index::index_type it (drop_index::unique);
+ instance<drop_index> in (*this, it);
+ trav_rel::unames n (*in);
+ names (at, n);
+ }
+
+ if (check (at))
+ alter (at);
+ }
+ else
+ {
+ // Add non-unique indexes.
+ //
+ {
+ create_index::index_type it (create_index::non_unique);
+ instance<create_index> in (*this, it);
+ trav_rel::unames n (*in);
+ names (at, n);
+ }
+ }
+ }
+ };
+
+ struct changeset_pre: trav_rel::changeset, common
+ {
+ typedef changeset_pre base;
+
+ changeset_pre (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f) {}
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ };
+
+ struct alter_table_post: alter_table_common
+ {
+ typedef alter_table_post base;
+
+ alter_table_post (emitter_type& e, ostream& os, schema_format f)
+ : alter_table_common (e, os, f) {}
+
+ // Check if there will be any clauses in ALTER TABLE.
+ //
+ using alter_table_common::check;
+
+ virtual bool
+ check (sema_rel::alter_table& at)
+ {
+ // If changing the below test, make sure to also update tests
+ // in database-specific code.
+ //
+ return
+ check<sema_rel::add_foreign_key> (at) ||
+ check<sema_rel::drop_column> (at) ||
+ check_alter_column_null (at, false);
+ }
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ // By default we generate all the alterations in a single ALTER TABLE
+ // statement. Quite a few databases don't support this.
+ //
+ pre_statement ();
+ os << "ALTER TABLE " << quote_id (at.name ());
+
+ bool f (true); // Shared first flag.
+ bool* pf (&f); // (Im)perfect forwarding.
+ bool fl (false); // (Im)perfect forwarding.
+ instance<drop_column> dc (*this, pf);
+ instance<alter_column> ac (*this, fl, pf);
+ instance<create_foreign_key> fk (*this, pf);
+
+ trav_rel::unames n;
+ n >> dc;
+ n >> ac;
+ n >> fk;
+ names (at, n);
+ os << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ traverse (sema_rel::alter_table& at)
+ {
+ if (pass_ == 1)
+ {
+ // Drop non-unique indexes.
+ //
+ {
+ drop_index::index_type it (drop_index::non_unique);
+ instance<drop_index> in (*this, it);
+ trav_rel::unames n (*in);
+ names (at, n);
+ }
+ }
+ else
+ {
+ if (check (at))
+ alter (at);
+
+ // Add unique indexes.
+ //
+ {
+ create_index::index_type it (create_index::unique);
+ instance<create_index> in (*this, it);
+ trav_rel::unames n (*in);
+ names (at, n);
+ }
+ }
+ }
+ };
+
+ struct changeset_post: trav_rel::changeset, common
+ {
+ typedef changeset_post base;
+
+ changeset_post (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f) {}
+
+ virtual void
+ traverse (sema_rel::changeset& m)
+ {
+ // Traverse named entities in the reverse order. This way we
+ // drop them in the order opposite to creating.
+ //
+ for (sema_rel::changeset::names_iterator begin (m.names_begin ()),
+ end (m.names_end ()); begin != end;)
+ dispatch (*--end);
+ }
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ };
+
+ //
+ // Schema version table.
+ //
+
+ struct version_table: common
+ {
+ typedef version_table base;
+
+ version_table (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f),
+ table_ (options.schema_version_table ()[db]),
+ qt_ (quote_id (table_)),
+ qs_ (quote_string (options.schema_name ()[db])),
+ qn_ (quote_id ("name")),
+ qv_ (quote_id ("version")),
+ qm_ (quote_id ("migration"))
+ {
+ }
+
+ // Create the version table if it doesn't exist.
+ //
+ virtual void
+ create_table () {}
+
+ // Remove the version entry. Called after the DROP statements.
+ //
+ virtual void
+ drop ()
+ {
+ pre_statement ();
+
+ os << "DELETE FROM " << qt_ << endl
+ << " WHERE " << qn_ << " = " << qs_ << endl;
+
+ post_statement ();
+ }
+
+ // Set the version. Called after the CREATE statements.
+ //
+ virtual void
+ create (sema_rel::version) {}
+
+ // Set the version and migration state to true. Called after
+ // the pre migration statements.
+ //
+ virtual void
+ migrate_pre (sema_rel::version v)
+ {
+ pre_statement ();
+
+ os << "UPDATE " << qt_ << endl
+ << " SET " << qv_ << " = " << v << ", " << qm_ << " = 1" << endl
+ << " WHERE " << qn_ << " = " << qs_ << endl;
+
+ post_statement ();
+ }
+
+ // Set migration state to false. Called after the post migration
+ // statements.
+ //
+ virtual void
+ migrate_post ()
+ {
+ pre_statement ();
+
+ os << "UPDATE " << qt_ << endl
+ << " SET " << qm_ << " = 0" << endl
+ << " WHERE " << qn_ << " = " << qs_ << endl;
+
+ post_statement ();
+ }
+
+ protected:
+ sema_rel::qname table_;
+ string qt_; // Quoted table.
+ string qs_; // Quoted schema name string.
+ string qn_; // Quoted name column.
+ string qv_; // Quoted version column.
+ string qm_; // Quoted migration column.
+ };
+
+ //
+ // SQL output.
+ //
+
+ struct sql_emitter: emitter, virtual context
+ {
+ typedef sql_emitter base;
+
+ virtual void
+ pre ()
+ {
+ first_ = true;
+ }
+
+ virtual void
+ line (const std::string& l)
+ {
+ if (first_ && !l.empty ())
+ first_ = false;
+ else
+ os << endl;
+
+ os << l;
+ }
+
+ virtual void
+ post ()
+ {
+ if (!first_) // Ignore empty statements.
+ os << ';' << endl
+ << endl;
+ }
+
+ protected:
+ bool first_;
+ };
+
+ struct sql_file: virtual context
+ {
+ typedef sql_file base;
+
+ virtual void
+ prologue ()
+ {
+ }
+
+ virtual void
+ epilogue ()
+ {
+ }
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_SCHEMA_HXX
diff --git a/odb/odb/relational/source.cxx b/odb/odb/relational/source.cxx
new file mode 100644
index 0000000..abc0a46
--- /dev/null
+++ b/odb/odb/relational/source.cxx
@@ -0,0 +1,6345 @@
+// file : odb/relational/source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <map>
+
+#include <odb/gcc.hxx>
+
+#include <odb/lookup.hxx>
+#include <odb/cxx-lexer.hxx>
+
+#include <odb/relational/source.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+void relational::source::class_::
+traverse_object (type& c)
+{
+ using semantics::data_member;
+
+ data_member_path* id (id_member (c));
+ data_member* idf (id ? id->front () : 0);
+ data_member* idb (id ? id->back () : 0);
+ bool auto_id (id && auto_ (*id));
+ bool base_id (id && &idf->scope () != &c); // Comes from base.
+
+ data_member* opt (optimistic (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+ type* poly_base (poly_derived ? &polymorphic_base (c) : 0);
+ size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1);
+ data_member* discriminator (poly ? context::discriminator (*poly_root) : 0);
+
+ bool abst (abstract (c));
+ bool reuse_abst (abst && !poly);
+ bool readonly (context::readonly (c));
+
+ bool grow (false);
+ bool grow_id (false);
+
+ if (generate_grow)
+ {
+ grow = context::grow (c);
+ grow_id = (id ? context::grow (*idb) : false) ||
+ (opt ? context::grow (*opt) : false);
+ }
+
+ column_count_type const& cc (column_count (c));
+ bool versioned (context::versioned (c));
+
+ // Schema name as a string literal or empty.
+ //
+ string schema_name (options.schema_name ()[db]);
+ if (!schema_name.empty ())
+ schema_name = strlit (schema_name);
+
+ string const& type (class_fq_name (c));
+ string traits ("access::object_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+ user_sections* buss (poly_base != 0
+ ? &poly_base->get<user_sections> ("user-sections")
+ : 0);
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ object_extra (c);
+
+ //
+ // Query (abstract and concrete).
+ //
+
+ // query_columns
+ //
+ if (options.generate_query ())
+ query_columns_type_->traverse (c);
+
+ // Statement cache (definition).
+ //
+ if (!reuse_abst && id != 0)
+ {
+ bool sections (false);
+ bool containers (has_a (c, test_container));
+
+ os << "struct " << traits << "::extra_statement_cache_type"
+ << "{";
+
+ instance<container_cache_members> cm;
+ cm->traverse (c);
+
+ if (containers)
+ os << endl;
+
+ instance<section_cache_members> sm;
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip the special version update section in reuse inheritance (we
+ // always treat it as abstract).
+ //
+ if (i->special == user_section::special_version && !poly)
+ continue;
+
+ // Generate an entry for a readonly section in optimistic
+ // polymorphic root since it can be overridden and we may
+ // need to update the version.
+ //
+ if (!i->empty () || (poly && i->optimistic ()))
+ {
+ sm->traverse (*i);
+ sections = true;
+ }
+ }
+
+ if (sections)
+ os << endl;
+
+ os << "extra_statement_cache_type (" << endl
+ << db << "::connection&" << (containers || sections ? " c" : "") <<
+ "," << endl
+ << "image_type&" << (sections ? " im" : "") << "," << endl
+ << "id_image_type&" << (sections ? " idim" : "") << "," << endl
+ << db << "::binding&" << (containers || sections ? " id" : "") <<
+ "," << endl
+ << db << "::binding&" << (sections ? " idv" : "");
+
+ extra_statement_cache_extra_args (containers, sections);
+
+ os << ")";
+
+ instance<container_cache_init_members> cim;
+ cim->traverse (c);
+
+ instance<section_cache_init_members> sim (!containers);
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ if (i->special == user_section::special_version && !poly)
+ continue;
+
+ if (!i->empty () || (poly && i->optimistic ()))
+ sim->traverse (*i);
+ }
+
+ os << "{"
+ << "}"
+ << "};";
+ }
+
+ //
+ // Containers (abstract and concrete).
+ //
+
+ {
+ instance<container_traits> t (c);
+ t->traverse (c);
+ }
+
+ //
+ // Sections (abstract and concrete).
+ //
+
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ instance<section_traits> t (c);
+ t->traverse (*i);
+ }
+
+ //
+ // Functions (abstract and concrete).
+ //
+
+ // id(), version()
+ //
+ if (!poly_derived && id != 0 && !base_id)
+ {
+ // id (id_image_type)
+ //
+ if (auto_id)
+ {
+ os << traits << "::id_type" << endl
+ << traits << "::" << endl
+ << "id (const id_image_type& i)"
+ << "{"
+ << db << "::database* db (0);"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl
+ << "id_type id;";
+ init_id_value_member_id_image_->traverse (*idb);
+ os << "return id;"
+ << "}";
+ }
+
+ // id (image)
+ //
+ if (options.generate_query ())
+ {
+ os << traits << "::id_type" << endl
+ << traits << "::" << endl
+ << "id (const image_type& i)"
+ << "{"
+ << db << "::database* db (0);"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl
+ << "id_type id;";
+
+ // Handle nested id.
+ //
+ if (id->size () > 1)
+ {
+ string var;
+
+ for (data_member_path::const_iterator i (id->begin ());
+ i != id->end ();
+ ++i)
+ {
+ // The same logic as in member_base.
+ //
+ if (!var.empty ())
+ var += "value."; // Composite.
+
+ string const& name ((*i)->name ());
+ var += name;
+
+ if (name[name.size () - 1] != '_')
+ var += '_';
+ }
+
+ instance<init_value_member> t ("id", var);
+ t->traverse (*idb);
+ }
+ else
+ init_id_value_member_->traverse (*idb);
+
+ os << "return id;"
+ << "}";
+ }
+
+ // version (image)
+ //
+ if (opt != 0)
+ {
+ os << traits << "::version_type" << endl
+ << traits << "::" << endl
+ << "version (const image_type& i)"
+ << "{"
+ << "version_type v;";
+ init_version_value_member_->traverse (*opt);
+ os << "return v;"
+ << "}";
+ }
+ }
+
+ // discriminator(image)
+ //
+ if (poly && !poly_derived)
+ {
+ os << traits << "::discriminator_type" << endl
+ << traits << "::" << endl
+ << "discriminator (const image_type& i)"
+ << "{"
+ << db << "::database* db (0);"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl
+ << "discriminator_type d;";
+ init_discriminator_value_member_->traverse (*discriminator);
+ os << "return d;"
+ << "}";
+ }
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ os << "bool " << traits << "::" << endl
+ << "grow (image_type& i," << endl
+ << truncated_vector << " t";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ if (poly_derived)
+ os << "," << endl
+ << "std::size_t d";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (t);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "bool grew (false);"
+ << endl;
+
+ index_ = 0;
+
+ if (poly_derived)
+ {
+ // Select column count for this class.
+ //
+ size_t cols (cc.total - cc.id);
+
+ os << "// " << class_name (*poly_base) << " base" << endl
+ << "//" << endl
+ << "if (--d != 0)"
+ << "{"
+ << "if (base_traits::grow (*i.base, " <<
+ "t + " << cols << "UL" <<
+ (context::versioned (*poly_base) ? ", svm" : "") <<
+ (poly_base != poly_root ? ", d" : "") << "))" << endl
+ << "i.base->version++;"
+ << "}";
+ }
+ else
+ inherits (c, grow_base_inherits_);
+
+ names (c, grow_member_names_);
+
+ os << "return grew;"
+ << "}";
+ }
+
+ // bind (image_type)
+ //
+ os << "void " << traits << "::" << endl
+ << "bind (" << bind_vector << " b," << endl;
+
+ // If we are a derived type in a polymorphic hierarchy, then
+ // we get the external id binding.
+ //
+ if (poly_derived)
+ os << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl;
+
+ os << "image_type& i," << endl
+ << db << "::statement_kind sk";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (sk);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (readonly)
+ os << "assert (sk != statement_update);"
+ << endl;
+
+ os << "std::size_t n (0);"
+ << endl;
+
+ if (poly_derived)
+ {
+ // The id reference comes first in the insert statement.
+ //
+ os << "// " << idf->name () << endl
+ << "//" << endl
+ << "if (sk == statement_insert)"
+ << "{"
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
+ << "}";
+ }
+ else
+ inherits (c, bind_base_inherits_);
+
+ names (c, bind_member_names_);
+
+ if (poly_derived)
+ {
+ // The id reference comes last in the update statement.
+ //
+ if (!readonly)
+ os << "// " << idf->name () << endl
+ << "//" << endl
+ << "if (sk == statement_update)"
+ << "{"
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
+ << "}";
+
+ // Bind the image chain for the select statement. Seeing that
+ // this is the last statement in the function, we don't care
+ // about updating n.
+ //
+ os << "// " << class_name (*poly_base) << " base" << endl
+ << "//" << endl
+ << "if (sk == statement_select)" << endl
+ << "base_traits::bind (b + n, ";
+
+ if (poly_base != poly_root)
+ os << "id, id_size, ";
+
+ os << "*i.base, sk" <<
+ (context::versioned (*poly_base) ? ", svm" : "") << ");";
+ }
+
+ os << "}";
+
+ // bind (id_image_type)
+ //
+ if (!poly_derived && id != 0 && !base_id)
+ {
+ os << "void " << traits << "::" << endl
+ << "bind (" << bind_vector << " b, id_image_type& i" <<
+ (opt != 0 ? ", bool bv" : "") << ")"
+ << "{"
+ << "std::size_t n (0);";
+
+ if (composite_wrapper (utype (*id)))
+ os << db << "::statement_kind sk (" << db << "::statement_select);";
+
+ bind_id_member_->traverse (*idb);
+
+ if (opt != 0)
+ {
+ os << "if (bv)"
+ << "{"
+ << "n += " << column_count (c).id << ";"
+ << endl;
+
+ bind_version_member_->traverse (*opt);
+ os << "}";
+ }
+
+ os << "}";
+ }
+
+ // init (image, object)
+ //
+ os << (generate_grow ? "bool " : "void ") << traits << "::" << endl
+ << "init (image_type& i," << endl
+ << "const object_type& o," << endl
+ << db << "::statement_kind sk";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << "ODB_POTENTIALLY_UNUSED (sk);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (readonly)
+ os << "assert (sk != statement_update);"
+ << endl;
+
+ init_image_pre (c);
+
+ if (generate_grow)
+ os << "bool grew (false);"
+ << endl;
+
+ if (!poly_derived)
+ inherits (c, init_image_base_inherits_);
+
+ names (c, init_image_member_names_);
+
+ if (generate_grow)
+ os << "return grew;";
+
+ os << "}";
+
+ // init (object, image)
+ //
+ os << "void " << traits << "::" << endl
+ << "init (object_type& o," << endl
+ << "const image_type& i," << endl
+ << "database* db";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ if (poly_derived)
+ os << "," << endl
+ << "std::size_t d";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl;
+
+ if (poly_derived)
+ {
+ os << "// " << class_name (*poly_base) << " base" << endl
+ << "//" << endl
+ << "if (--d != 0)" << endl
+ << "base_traits::init (o, *i.base, db" <<
+ (context::versioned (*poly_base) ? ", svm" : "") <<
+ (poly_base != poly_root ? ", d" : "") << ");"
+ << endl;
+ }
+ else
+ inherits (c, init_value_base_inherits_);
+
+ names (c, init_value_member_names_);
+
+ os << "}";
+
+ // init (id_image, id)
+ //
+ if (id != 0 && !base_id)
+ {
+ os << "void " << traits << "::" << endl
+ << "init (id_image_type& i, const id_type& id" <<
+ (opt != 0 ? ", const version_type* v" : "") << ")"
+ << "{";
+
+ if (grow_id)
+ os << "bool grew (false);";
+
+ if (composite_wrapper (utype (*id)))
+ os << db << "::statement_kind sk (" << db << "::statement_select);";
+
+ init_id_image_member_->traverse (*idb);
+
+ if (opt != 0)
+ {
+ // Here we rely on the fact that init_image_member
+ // always wraps the statements in a block.
+ //
+ os << "if (v != 0)";
+ init_version_image_member_->traverse (*opt);
+ }
+
+ if (grow_id)
+ os << "if (grew)" << endl
+ << "i.version++;";
+
+ os << "}";
+ }
+
+ // The rest does not apply to reuse-abstract objects.
+ //
+ if (reuse_abst)
+ return;
+
+ //
+ // Containers (concrete).
+ //
+
+ //
+ // Sections (concrete).
+ //
+
+ // Polymorphic map.
+ //
+ if (poly)
+ {
+ if (!poly_derived)
+ os << traits << "::map_type*" << endl
+ << traits << "::map;"
+ << endl;
+
+ // Sections. Do we have any new sections or overrides?
+ //
+ bool sections (
+ uss.count (user_sections::count_new |
+ user_sections::count_override |
+ user_sections::count_load |
+ user_sections::count_update |
+ user_sections::count_special_version |
+ (poly ? user_sections::count_optimistic : 0)) != 0);
+ if (sections)
+ {
+ string const& fn (flat_name (type));
+
+ size_t n (uss.count (user_sections::count_total |
+ user_sections::count_all |
+ user_sections::count_special_version));
+
+ map<size_t, user_section*> m;
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ m[i->index] = &*i;
+
+ os << "static const "<< traits << "::" << (abst ? "abstract_" : "") <<
+ "info_type::section_functions" << endl
+ << "section_functions_for_" << fn << "[] ="
+ << "{";
+
+ for (size_t i (0); i != n; ++i)
+ {
+ os << (i != 0 ? "," : "") << "{";
+
+ map<size_t, user_section*>::iterator j (m.find (i));
+
+ if (j != m.end ())
+ {
+ user_section& s (*j->second);
+ string n (public_name (*s.member));
+
+ // load
+ //
+ if (s.load_empty ())
+ os << "0";
+ else
+ os << "&odb::section_load_impl< " << type << ", id_" << db <<
+ ", " << traits << "::" << n << "_traits >";
+
+ os << "," << endl;
+
+ // update
+ //
+ // Generate an entry for a readonly section in optimistic
+ // polymorphic root since it can be overridden and we may
+ // need to update the version.
+ //
+ if (s.update_empty () && !(poly && s.optimistic ()))
+ os << "0";
+ else
+ os << "&odb::section_update_impl< " << type << ", id_" << db <<
+ ", " << traits << "::" << n << "_traits >";
+ }
+ else
+ os << "0," << endl
+ << "0";
+
+ os << "}";
+ }
+
+ os << "};";
+
+ os << "static const "<< traits << "::" << (abst ? "abstract_" : "") <<
+ "info_type::section_list" << endl
+ << "section_list_for_" << fn << " ="
+ << "{"
+ << n << "UL," << endl
+ << "section_functions_for_" << fn
+ << "};";
+ }
+
+ //
+ //
+ os << "const " << traits << "::" << (abst ? "abstract_" : "") <<
+ "info_type" << endl
+ << traits << "::info (" << endl
+ << "typeid (" << type << ")," << endl;
+
+ if (poly_derived)
+ os << "&object_traits_impl< " << class_fq_name (*poly_base) <<
+ ", id_" << db << " >::info," << endl;
+ else
+ os << "0," << endl;
+
+ // Sections.
+ //
+ if (sections)
+ os << "&section_list_for_" << flat_name (type);
+ else
+ os << "0";
+
+ if (!abst)
+ {
+ os << "," << endl
+ << strlit (string (type, 2, string::npos)) << "," << endl
+ << "&odb::create_impl< " << type << " >," << endl
+ << "&odb::dispatch_impl< " << type << ", id_" << db << " >," << endl;
+
+ if (poly_derived)
+ os << "&statements_type::delayed_loader";
+ else
+ os << "0";
+ }
+
+ os << ");"
+ << endl;
+
+ if (!abst)
+ os << "static const " << traits << "::entry_type" << endl
+ << "polymorphic_entry_for_" << flat_name (type) << ";"
+ << endl;
+ }
+
+ //
+ // Statements.
+ //
+ bool update_columns (
+ cc.total != cc.id + cc.inverse + cc.readonly + cc.separate_update);
+
+ qname table (table_name (c));
+ string qtable (quote_id (table));
+
+ // persist_statement
+ //
+ {
+ string sep (versioned ? "\n" : " ");
+
+ statement_columns sc;
+ {
+ statement_kind sk (statement_insert); // Imperfect forwarding.
+ instance<object_columns> ct (sk, sc);
+ ct->traverse (c);
+ process_statement_columns (sc, statement_insert, versioned);
+ }
+
+ bool dv (sc.empty ()); // The DEFAULT VALUES syntax.
+
+ os << "const char " << traits << "::persist_statement[] =" << endl
+ << strlit ("INSERT INTO " + qtable + sep) << endl;
+
+ for (statement_columns::const_iterator b (sc.begin ()), i (b),
+ e (sc.end ()); i != e;)
+ {
+ string s;
+
+ if (i == b)
+ s += '(';
+ s += i->column;
+ s += (++i != e ? ',' : ')');
+ s += sep;
+
+ os << strlit (s) << endl;
+ }
+
+ instance<query_parameters> qp (statement_insert, table);
+
+ string extra (persist_statement_extra (c, *qp, persist_after_columns));
+
+ if (!extra.empty ())
+ os << strlit (extra + sep) << endl;
+
+ string values;
+ if (!dv)
+ {
+ os << strlit ("VALUES" + sep) << endl;
+
+ values += '(';
+ instance<persist_statement_params> pt (values, *qp, sep);
+ pt->traverse (c);
+ values += ')';
+ }
+ else
+ values = "DEFAULT VALUES";
+
+ extra = persist_statement_extra (c, *qp, persist_after_values);
+
+ if (!extra.empty ())
+ values += sep;
+
+ os << strlit (values);
+
+ if (!extra.empty ())
+ os << endl
+ << strlit (extra);
+
+ os << ";"
+ << endl;
+ }
+
+ // Index of the first empty SELECT statement in poly-derived
+ // statement list. All subsequent statements are also empty.
+ // The first statement can never be empty (contains id and
+ // type_id).
+ //
+ size_t empty_depth (0);
+
+ if (id != 0)
+ {
+ string sep (versioned ? "\n" : " ");
+
+ instance<object_columns_list> id_cols;
+ id_cols->traverse (*id);
+
+ std::vector<size_t> find_column_counts (abst ? 1 : poly_depth);
+
+ // find_statement
+ //
+ if (poly_derived)
+ os << "const char* const " << traits << "::find_statements[] ="
+ << "{";
+ else
+ os << "const char " << traits << "::find_statement[] =" << endl;
+
+ for (size_t d (poly_depth); d != 0;)
+ {
+ statement_columns sc;
+ {
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ object_section* s (&main_section); // Imperfect forwarding.
+ instance<object_columns> t (qtable, sk, sc, d, s);
+ t->traverse (c);
+ process_statement_columns (sc, statement_select, versioned);
+ find_column_counts[poly_depth - d] = sc.size ();
+ }
+
+ if (sc.size () != 0)
+ {
+ os << strlit ("SELECT" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ os << strlit ("FROM " + qtable + sep) << endl;
+
+ if (d != 1)
+ {
+ bool f (false); // @@ (im)perfect forwarding
+ size_t d1 (d - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (c, f, d1);
+ j->traverse (polymorphic_base (c));
+
+ for (strings::const_iterator i (j->begin ()); i != j->end (); ++i)
+ os << strlit (*i + sep) << endl;
+ }
+
+ {
+ // For the find statement, don't join section members.
+ //
+ bool f (false); // @@ (im)perfect forwarding
+ object_section* s (&main_section); // @@ (im)perfect forwarding
+ instance<object_joins> j (c, f, d, s);
+ j->traverse (c);
+
+ for (strings::const_iterator i (j->begin ()); i != j->end (); ++i)
+ os << strlit (*i + sep) << endl;
+ }
+
+ string where ("WHERE ");
+ instance<query_parameters> qp (statement_select, table);
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += qtable + "." + quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ os << strlit (where);
+ }
+ else
+ os << strlit (""); // Empty SELECT statement.
+
+ if (abst)
+ break;
+
+ if (--d != 0)
+ os << "," << endl
+ << endl;
+ }
+
+ if (poly_derived)
+ os << "};";
+ else
+ os << ";"
+ << endl;
+
+ // find_column_counts
+ //
+ if (poly_derived)
+ {
+ os << "const std::size_t " << traits << "::find_column_counts[] ="
+ << "{";
+
+ for (std::vector<size_t>::iterator b (find_column_counts.begin ()),
+ i (b), e (find_column_counts.end ()); i != e;)
+ {
+ os << *i << "UL";
+
+ if (*i == 0 && empty_depth == 0)
+ empty_depth = i - b;
+
+ if (++i != e)
+ os << ',' << endl;
+ }
+
+ os << "};";
+ }
+
+ // find_discriminator_statement
+ //
+ if (poly && !poly_derived)
+ {
+ statement_columns sc;
+ {
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ instance<object_columns> t (qtable, sk, sc);
+ t->traverse (*discriminator);
+
+ if (opt != 0)
+ t->traverse (*opt);
+
+ process_statement_columns (sc, statement_select, false);
+ }
+
+ os << "const char " << traits << "::" << endl
+ << "find_discriminator_statement[] =" << endl
+ << strlit ("SELECT ") << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? ", " : " ")) << endl;
+ }
+
+ os << strlit ("FROM " + qtable + " ") << endl;
+
+ string where ("WHERE ");
+ instance<query_parameters> qp (statement_select, table);
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += qtable + "." + quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+
+ // update_statement
+ //
+ if (update_columns)
+ {
+ string sep (versioned ? "\n" : " ");
+
+ instance<query_parameters> qp (statement_update, table);
+
+ statement_columns sc;
+ {
+ query_parameters* p (qp.get ()); // Imperfect forwarding.
+ statement_kind sk (statement_update); // Imperfect forwarding.
+ object_section* s (&main_section); // Imperfect forwarding.
+ instance<object_columns> t (sk, sc, p, s);
+ t->traverse (c);
+ process_statement_columns (sc, statement_update, versioned);
+ }
+
+ os << "const char " << traits << "::update_statement[] =" << endl
+ << strlit ("UPDATE " + qtable + sep) << endl
+ << strlit ("SET" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ // This didn't work out: cannot change the identity column.
+ //
+ //if (sc.empty ())
+ //{
+ // // We can end up with nothing to set if we need to "touch" a row
+ // // in order to increment its optimistic concurrency version. In
+ // // this case just do a dummy assignment based on the id column.
+ // //
+ // string const& c (quote_id (id_cols->begin ()->name));
+ // os << strlit (c + "=" + c) << endl;
+ //}
+
+ string extra (update_statement_extra (c));
+
+ if (!extra.empty ())
+ os << strlit (extra + sep) << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ // Top-level version column.
+ //
+ if (opt != 0 && !poly_derived)
+ {
+ string name (column_qname (*opt, column_prefix ()));
+ string type (column_type (*opt));
+
+ where += " AND " + name + "=" +
+ convert_to (qp->next (*opt, name, type), type, *opt);
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+
+ // erase_statement
+ //
+ {
+ instance<query_parameters> qp (statement_delete, table);
+ os << "const char " << traits << "::erase_statement[] =" << endl
+ << strlit ("DELETE FROM " + qtable + " ") << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+
+ if (opt != 0 && !poly_derived)
+ {
+ instance<query_parameters> qp (statement_delete, table);
+
+ os << "const char " << traits << "::optimistic_erase_statement[] " <<
+ "=" << endl
+ << strlit ("DELETE FROM " + qtable + " ") << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ // Top-level version column.
+ //
+ string name (column_qname (*opt, column_prefix ()));
+ string type (column_type (*opt));
+
+ where += " AND " + name + "=" +
+ convert_to (qp->next (*opt, name, type), type, *opt);
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+ }
+
+ // query_statement
+ //
+ bool query_optimize (false);
+ if (options.generate_query ())
+ {
+ // query_statement
+ //
+ strings joins;
+ if (poly_depth != 1)
+ {
+ bool t (true); //@@ (im)perfect forwarding
+ size_t d (poly_depth - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (c, t, d);
+ j->traverse (polymorphic_base (c));
+ joins = j->joins;
+ }
+
+ if (id != 0)
+ {
+ // For the query statement we also join section members so that they
+ // can be used in the WHERE clause.
+ //
+ bool t (true); //@@ (im)perfect forwarding
+ instance<object_joins> j (c, t, poly_depth);
+ j->traverse (c);
+ joins.insert (joins.end (), j->begin (), j->end ());
+ }
+
+ query_optimize = !joins.empty ();
+
+ statement_columns sc;
+ {
+ statement_kind sk (statement_select); //@@ Imperfect forwarding.
+ object_section* s (&main_section); //@@ Imperfect forwarding.
+ instance<object_columns> oc (qtable, sk, sc, poly_depth, s);
+ oc->traverse (c);
+ process_statement_columns (
+ sc, statement_select, versioned || query_optimize);
+ }
+
+ string sep (versioned || query_optimize ? "\n" : " ");
+
+ os << "const char " << traits << "::query_statement[] =" << endl
+ << strlit ("SELECT" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ string prev ("FROM " + qtable);
+
+ for (strings::const_iterator i (joins.begin ()); i != joins.end (); ++i)
+ {
+ os << strlit (prev + sep) << endl;
+ prev = *i;
+ }
+
+ os << strlit (prev) << ";"
+ << endl;
+
+ // erase_query_statement
+ //
+ os << "const char " << traits << "::erase_query_statement[] =" << endl
+ << strlit ("DELETE FROM " + qtable) << ";"
+ << endl;
+
+ // table_name
+ //
+ os << "const char " << traits << "::table_name[] =" << endl
+ << strlit (qtable) << ";" // Use quoted name.
+ << endl;
+ }
+
+ // persist ()
+ //
+ size_t persist_containers (has_a (c, test_straight_container));
+ bool persist_versioned_containers (
+ persist_containers >
+ has_a (c,
+ test_straight_container |
+ exclude_deleted | exclude_added | exclude_versioned));
+
+ os << "void " << traits << "::" << endl
+ << "persist (database& db, " << (auto_id ? "" : "const ") <<
+ "object_type& obj";
+
+ if (poly)
+ os << ", bool top, bool dyn";
+
+ os << ")"
+ << "{";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (top);"
+ << endl;
+
+ os << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ os << "if (dyn)" << endl
+ << "{"
+ << "const std::type_info& t (typeid (obj));"
+ << endl
+ << "if (t != info.type)"
+ << "{"
+ << "const info_type& pi (root_traits::map->find (t));"
+ << "pi.dispatch (info_type::call_persist, db, &obj, 0);"
+ << "return;"
+ << "}"
+ << "}";
+
+ // If we are database-poly-abstract but not C++-abstract, then make
+ // sure we are not trying to persist an instance of an abstract class.
+ //
+ if (abst && !c.abstract ())
+ os << "if (top)" << endl
+ << "throw abstract_class ();"
+ << endl;
+
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned ||
+ persist_versioned_containers ||
+ uss.count (user_sections::count_new |
+ user_sections::count_all |
+ user_sections::count_versioned_only) != 0)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ // Call callback (pre_persist).
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "callback (db," << endl
+ << (auto_id ? "static_cast<const object_type&> (obj)," : "obj,") << endl
+ << "callback_event::pre_persist);"
+ << endl;
+ }
+
+ // Call our base if we are a derived type in a polymorphic
+ // hierarchy.
+ //
+ if (poly_derived)
+ os << "base_traits::persist (db, obj, false, false);"
+ << endl;
+
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.insert_image_binding ());";
+
+ if (poly_derived)
+ os << "const binding& idb (sts.id_image_binding ());";
+
+ os << endl;
+
+ if (generate_grow)
+ os << "if (";
+
+ os << "init (im, obj, statement_insert" << (versioned ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << ")" << endl
+ << "im.version++";
+
+ os << ";"
+ << endl;
+
+ if (!poly_derived && auto_id && insert_send_auto_id)
+ {
+ string const& n (idf->name ());
+ string var ("im." + n + (n[n.size () - 1] == '_' ? "" : "_"));
+ init_auto_id (*idf, var); // idf == idb, since auto
+ }
+
+ os << "if (";
+
+ if (poly_derived)
+ os << "idb.version != sts.insert_id_binding_version () ||" << endl;
+
+ os << "im.version != sts.insert_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, ";
+
+ if (poly_derived)
+ os << "idb.bind, idb.count, ";
+
+ os << "im, statement_insert" << (versioned ? ", svm" : "") << ");";
+
+ if (poly_derived)
+ os << "sts.insert_id_binding_version (idb.version);";
+
+ os << "sts.insert_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+
+ // Bind id image since that's where the returned id and/or version will
+ // be extracted.
+ //
+ if (!poly_derived)
+ {
+ bool bv (opt != 0 && optimistic_insert_bind_version (*opt));
+ if (bv || auto_id)
+ {
+ os << "{"
+ << "id_image_type& i (sts.id_image ());"
+ << "binding& b (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || b.version == 0)"
+ << "{"
+ << "bind (b.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "b.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}"
+ << "}";
+ }
+ }
+
+ os << "insert_statement& st (sts.persist_statement ());"
+ << "if (!st.execute ())" << endl
+ << "throw object_already_persistent ();"
+ << endl;
+
+ if (!poly_derived && auto_id) // idf == idb since auto
+ {
+ set_member (*idf, "obj", "id (sts.id_image ())", "db", "id_type");
+ os << endl;
+ }
+
+ // Set the optimistic concurrency version in the object member.
+ //
+ if (opt != 0 && !poly_derived)
+ {
+ // If we don't have auto id, then obj is a const reference.
+ //
+ set_member (*opt,
+ (auto_id ? "obj" : "const_cast<object_type&> (obj)"),
+ optimistic_version_init (*opt),
+ "", // No database.
+ "version_type");
+ os << endl;
+ }
+
+ // Initialize id_image and binding if we are a root of a polymorphic
+ // hierarchy or if we have non-inverse containers.
+ //
+ if (!poly_derived && (poly || persist_containers))
+ {
+ // If this is a polymorphic root without containers, then we only
+ // need to do this if we are not a top-level call. If we are poly-
+ // abstract, then top will always be false.
+ //
+ if (poly && !persist_containers && !abst)
+ os << "if (!top)"
+ << "{";
+
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, id (obj));"
+ << endl
+ << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+
+ if (poly && !persist_containers && !abst)
+ os << "}";
+ }
+
+ if (persist_containers)
+ {
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
+ << endl;
+
+ instance<container_calls> t (container_calls::persist_call);
+ t->traverse (c);
+ }
+
+ // Reset sections: loaded, unchanged.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ // Skip overridden sections; they have been reset by the base.
+ //
+ if (i->base != 0 && poly_derived)
+ continue;
+
+ data_member& m (*i->member);
+
+ // If the section is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (m));
+ unsigned long long dv (deleted (m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")" << endl;
+ }
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << ma.translate ("obj") << ".reset (true, false);"
+ << endl;
+ }
+
+ // Call callback (post_persist).
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "callback (db," << endl
+ << (auto_id ? "static_cast<const object_type&> (obj)," : "obj,") << endl
+ << "callback_event::post_persist);";
+ }
+
+ os << "}";
+
+ // persist() bulk
+ //
+ if (c.count ("bulk-persist"))
+ {
+ os << "void " << traits << "::" << endl
+ << "persist (database& db," << endl
+ << (auto_id ? "" : "const ") << "object_type** objs," << endl
+ << "std::size_t n," << endl
+ << "multiple_exceptions& mex)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned ||
+ uss.count (user_sections::count_new |
+ user_sections::count_all |
+ user_sections::count_versioned_only) != 0)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ os << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "const object_type& obj (*objs[i]);"
+ << "callback (db, obj, callback_event::pre_persist);"
+ //@@ assumption: generate_grow is false or it only affects select (like
+ // in pgsql) so all we have to do is to increment image
+ // version if it grew.
+ //@@ assumption: insert_send_auto_id is false
+ << "image_type& im (sts.image (i));";
+
+ if (generate_grow)
+ os << "if (";
+
+ os << "init (im, obj, statement_insert" << (versioned ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << " && i == 0)" << endl
+ << "im.version++";
+
+ os << ";"
+ << "}";
+
+ //@@ assumption: generate_grow: as above
+ os << "binding& imb (sts.insert_image_binding ());"
+ << "if (imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, sts.image (), statement_insert" <<
+ (versioned ? ", svm" : "") << ");"
+ << "imb.version++;"
+ << "}";
+
+ // Bind id image since that's where the returned id and/or version will
+ // be extracted.
+ //
+ bool bv (opt != 0 && optimistic_insert_bind_version (*opt));
+ if (bv || auto_id)
+ {
+ os << "binding& idb (sts.id_image_binding ());"
+ //@@ assumption: generate_grow: as above
+ << "if (idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, sts.id_image ());"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+ }
+
+ os << "insert_statement& st (sts.persist_statement ());"
+ << "n = st.execute (n, mex);" // Actual number of rows attempted.
+ << endl;
+
+ os << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "bool r (st.result (i));" // Sets current in mex.
+ << endl
+ << "if (mex[i] != 0)" << endl // Pending exception for this position.
+ << "continue;"
+ << endl
+ << "if (!r)"
+ << "{"
+ << "mex.insert (i, object_already_persistent ());"
+ << "continue;"
+ << "}"
+ << "if (mex.fatal ())" << endl // Don't do any extra work.
+ << "continue;"
+ << endl
+ << (auto_id ? "" : "const ") << "object_type& obj (*objs[i]);"
+ << endl;
+
+ // Extract auto id.
+ //
+ if (auto_id) // idb == idf, since auto
+ {
+ set_member (*idf, "obj", "id (sts.id_image (i))", "db", "id_type");
+ os << endl;
+ }
+
+ // Set the optimistic concurrency version.
+ //
+ if (opt != 0)
+ {
+ // If we don't have auto id, then obj is a const reference.
+ //
+ set_member (*opt,
+ (auto_id ? "obj" : "const_cast<object_type&> (obj)"),
+ optimistic_version_init (*opt, true),
+ "", // No database.
+ "version_type");
+ os << endl;
+ }
+
+ // Reset sections: loaded, unchanged.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ data_member& m (*i->member);
+
+ // If the section is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (m));
+ unsigned long long dv (deleted (m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")" << endl;
+ }
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << ma.translate ("obj") << ".reset (true, false);"
+ << endl;
+ }
+
+ os << "callback (db," << endl
+ << (auto_id ? "static_cast<const object_type&> (obj)," : "obj,") << endl
+ << "callback_event::post_persist);"
+ << "}" // for
+ << "}"; // persist ()
+ }
+
+ // update ()
+ //
+ if (id != 0 && (!readonly || poly))
+ {
+ size_t update_containers (
+ has_a (c, test_readwrite_container, &main_section));
+
+ bool update_versioned_containers (
+ update_containers >
+ has_a (c,
+ test_readwrite_container |
+ exclude_deleted | exclude_added | exclude_versioned,
+ &main_section));
+
+ // See if we have any sections that we might have to update.
+ //
+ bool sections (false);
+ bool versioned_sections (false);
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // This test will automatically skip the special version update section.
+ //
+ if (i->update_empty () || i->update == user_section::update_manual)
+ continue;
+
+ sections = true;
+
+ if (added (*i->member) || deleted (*i->member))
+ versioned_sections = true;
+
+ // For change-updated sections, also check if we have any
+ // versioned smart containers.
+ //
+ if (i->update != user_section::update_change)
+ continue;
+
+ if (has_a (c, test_smart_container, &*i) !=
+ has_a (c, test_smart_container | exclude_deleted | exclude_added, &*i))
+ versioned_sections = true;
+ }
+
+ os << "void " << traits << "::" << endl
+ << "update (database& db, const object_type& obj";
+
+ if (poly)
+ os << ", bool top, bool dyn";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (top);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << "using " << db << "::update_statement;"
+ << endl;
+
+ if (poly)
+ os << "if (dyn)" << endl
+ << "{"
+ << "const std::type_info& t (typeid (obj));"
+ << endl
+ << "if (t != info.type)"
+ << "{"
+ << "const info_type& pi (root_traits::map->find (t));"
+ << "pi.dispatch (info_type::call_update, db, &obj, 0);"
+ << "return;"
+ << "}"
+ << "}";
+
+ // If we are database-poly-abstract but not C++-abstract, then make
+ // sure we are not trying to update an instance of an abstract class.
+ //
+ if (abst && !c.abstract ())
+ os << "if (top)" << endl
+ << "throw abstract_class ();"
+ << endl;
+
+ // If we are readonly, then there is nothing else to do.
+ //
+ if (!readonly)
+ {
+ // Call callback (pre_update).
+ //
+ if (!abst) // If we are poly-abstract then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "callback (db, obj, callback_event::pre_update);"
+ << endl;
+ }
+
+ bool sts (false);
+
+ if ((versioned && update_columns) ||
+ update_versioned_containers ||
+ versioned_sections)
+ {
+ sts = true;
+ os << db << "::transaction& tr (" << db <<
+ "::transaction::current ());"
+ << db << "::connection& conn (tr.connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));"
+ << endl;
+ }
+
+ // If we have change-updated sections that contain change-tracking
+ // containers, then mark such sections as changed if any of the
+ // containers were changed. Do this before calling the base so that
+ // we account for the whole hierarchy before we actually start
+ // updating any sections (important for optimistic concurrency
+ // version).
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ user_section& s (*i);
+
+ if (s.update != user_section::update_change)
+ continue;
+
+ if (!has_a (c, test_smart_container, &s))
+ continue;
+
+ data_member& m (*s.member);
+
+ os << "// " << m.name () << endl
+ << "//" << endl;
+
+ // If the section is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (m));
+ unsigned long long dv (deleted (m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")";
+ }
+
+ os << "{";
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ // VC++ cannot grok the constructor syntax.
+ //
+ os << "const odb::section& s = " << ma.translate ("obj") << ";"
+ << endl;
+
+ os << "if (";
+
+ // Unless we are always loaded, test for being loaded.
+ //
+ if (s.load != user_section::load_eager)
+ os << "s.loaded () && ";
+
+ // And for not already being changed.
+ //
+ os << "!s.changed ())"
+ << "{";
+
+ instance<container_calls> t (container_calls::section_call, &s);
+ t->traverse (c);
+
+ os << "}" // if
+ << "}";
+ }
+
+ if (poly_derived)
+ {
+ bool readonly_base (context::readonly (*poly_base));
+
+ if (!sts && (readonly_base ||
+ update_columns ||
+ update_containers ||
+ sections))
+ {
+ os << db << "::transaction& tr (" << db <<
+ "::transaction::current ());"
+ << db << "::connection& conn (tr.connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+ }
+
+ // Unless our base is readonly, call it first.
+ //
+ if (!readonly_base)
+ {
+ os << "base_traits::update (db, obj, false, false);"
+ << endl;
+ }
+ else
+ {
+ // Otherwise, we have to initialize the id image ourselves. If
+ // we don't have any columns, containers or sections to update,
+ // then we only have to do it if this is not a top-level call.
+ // If we are abstract, then top is always false.
+ //
+ if (!update_columns && !update_containers && !sections && !abst)
+ os << "if (!top)";
+
+ os << "{"
+ << "id_image_type& i (sts.id_image ());"
+ << "init (i, id (obj));"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;"
+ // Optimistic poly base cannot be readonly.
+ << "}"
+ << "}";
+ }
+
+ if (update_columns)
+ {
+ // Initialize the object image.
+ //
+ os << "image_type& im (sts.image ());";
+
+ if (generate_grow)
+ os << "if (";
+
+ os << "init (im, obj, statement_update" <<
+ (versioned ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << ")" << endl
+ << "im.version++";
+
+ os << ";"
+ << endl;
+
+ os << "const binding& idb (sts.id_image_binding ());"
+ << "binding& imb (sts.update_image_binding ());"
+ << "if (idb.version != sts.update_id_binding_version () ||" << endl
+ << "im.version != sts.update_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, idb.bind, idb.count, im, statement_update" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.update_id_binding_version (idb.version);"
+ << "sts.update_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+
+ os << "update_statement& st (sts.update_statement ());"
+ << "if (";
+
+ if (versioned)
+ os << "!st.empty () && ";
+
+ os << "st.execute () == 0)" << endl
+ << "throw object_not_persistent ();"
+ << endl;
+ }
+
+ // Otherwise, nothing else to do here if we don't have any columns
+ // to update.
+ }
+ else if (update_columns)
+ {
+ if (!sts)
+ os << db << "::transaction& tr (" << db <<
+ "::transaction::current ());"
+ << db << "::connection& conn (tr.connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+
+ // Initialize id image.
+ //
+ if (opt != 0)
+ os << "const version_type& v (version (obj));";
+
+ os << "id_image_type& idi (sts.id_image ());"
+ << "init (idi, id (obj)" << (opt != 0 ? ", &v" : "") << ");"
+ << endl;
+
+ // Initialize object image.
+ //
+ os << "image_type& im (sts.image ());";
+
+ if (generate_grow)
+ os << "if (";
+
+ os << "init (im, obj, statement_update" <<
+ (versioned ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << ")" << endl
+ << "im.version++";
+
+ os << ";"
+ << endl;
+
+ // The update binding is bound to two images (object and id)
+ // so we have to track both versions.
+ //
+ os << "bool u (false);" // Avoid incrementing version twice.
+ << "binding& imb (sts.update_image_binding ());"
+ << "if (im.version != sts.update_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, statement_update" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.update_image_version (im.version);"
+ << "imb.version++;"
+ << "u = true;"
+ << "}";
+
+ // To update the id part of the update binding we have to do
+ // it indirectly via the id binding, which just points to the
+ // suffix of the update bind array (see object_statements).
+ //
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (idi.version != sts.update_id_image_version () ||" << endl
+ << "idb.version == 0)"
+ << "{"
+ // If the id binding is up-to-date, then that means update
+ // binding is too and we just need to update the versions.
+ //
+ << "if (idi.version != sts.id_image_version () ||" << endl
+ << "idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, idi);"
+ // Update the id binding versions since we may use them later
+ // to update containers.
+ //
+ << "sts.id_image_version (idi.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}"
+ << "sts.update_id_image_version (idi.version);"
+ << endl
+ << "if (!u)" << endl
+ << "imb.version++;"
+ << "}";
+
+ os << "update_statement& st (sts.update_statement ());"
+ << "if (";
+
+ if (versioned)
+ os << "!st.empty () && ";
+
+ os << "st.execute () == 0)" << endl;
+
+ if (opt == 0)
+ os << "throw object_not_persistent ();";
+ else
+ os << "throw object_changed ();";
+
+ os << endl;
+ }
+ else if (poly || update_containers || sections)
+ {
+ // We don't have any columns to update but we may still need
+ // to initialize the id image if we are polymorphic or have
+ // any containers or sections.
+ //
+ if (!sts)
+ os << db << "::transaction& tr (" << db <<
+ "::transaction::current ());"
+ << db << "::connection& conn (tr.connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+
+ // The same rationale as a couple of screens up.
+ //
+ if (poly && !update_containers && !sections && !abst)
+ os << "if (!top)";
+
+ os << "{"
+ << "id_image_type& i (sts.id_image ());"
+ << "init (i, id (obj));"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;"
+ // Cannot be optimistic.
+ << "}"
+ << "}";
+ }
+
+ if (update_containers || sections)
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
+ << endl;
+
+ if (update_containers)
+ {
+ instance<container_calls> t (container_calls::update_call,
+ &main_section);
+ t->traverse (c);
+ }
+
+ // Update the optimistic concurrency version in the object member.
+ // Do it before updating sections since they will update it as well.
+ // The same code as in section_traits.
+ //
+ if (opt != 0 && !poly_derived)
+ {
+ // Object is passed as const reference so we need to cast away
+ // constness.
+ //
+ const char* obj ("const_cast<object_type&> (obj)");
+ string inc (optimistic_version_increment (*opt));
+
+ if (inc == "1")
+ inc_member (*opt, obj, "obj", "version_type");
+ else
+ set_member (*opt, obj, inc, "", "version_type");
+
+ os << endl;
+ }
+
+ // Update sections that are loaded and need updating.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ if (i->update_empty () || i->update == user_section::update_manual)
+ continue;
+
+ // Here is the deal: for polymorphic section overrides, we could
+ // have used the same approach as in load_() where the class that
+ // defines the section data member initiates the section update.
+ // However, if we used this approach, then we would get what can
+ // be called "interleaved" update statements. That is, a section
+ // update would update the section data in "derived" tables before
+ // updating the main section in these derived tables. While this
+ // does not feel right, it can also cause more real problems if,
+ // for example, there are constraints or some such. Generally,
+ // we always want to update the main section data first for each
+ // table in the hierarchy.
+ //
+ // So what we are going to do instead is call section traits
+ // update at each override point (and indicate to it not to
+ // call base). One tricky aspect is who is going to reset the
+ // changed state. We have to do it at the top since otherwise
+ // overrides won't know the section has changed. Finding out
+ // whether we are the final override (in section terms, not
+ // object terms) is tricky.
+ //
+ data_member& m (*i->member);
+
+ // If the section is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (m));
+ unsigned long long dv (deleted (m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")"
+ << "{";
+ }
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << "if (";
+
+ // Unless we are always loaded, test for being loaded.
+ //
+ string sep;
+ if (i->load != user_section::load_eager)
+ {
+ os << ma.translate ("obj") << ".loaded ()";
+ sep = " && ";
+ }
+
+ // If change-updated section, check that it has changed.
+ //
+ if (i->update == user_section::update_change)
+ os << sep << ma.translate ("obj") << ".changed ()";
+
+ os << ")"
+ << "{";
+
+ // Re-initialize the id+ver image with new version which may
+ // have changed. Here we take a bit of a shortcut and not
+ // re-bind the image since we know only version may have
+ // changed and that is always an integer (cannot grow).
+ //
+ if (opt != 0)
+ {
+ os << "const version_type& v (version (obj));"
+ << "init (sts.id_image (), id (obj), &v);"
+ << endl;
+ }
+
+ os << public_name (m) << "_traits::update (esc, obj";
+
+ if (poly_derived && i->base != 0)
+ {
+ // Normally we don't want to call base (base object's update()
+ // would have called it; see comment above). There is one
+ // exception, however, and that is a readonly base section
+ // in optimistic class. In this case, base object's update()
+ // won't call it (it is readonly) but it needs to be called
+ // in order to increment the version.
+ //
+ bool base (false);
+ for (user_section* b (i->base); !base && b != 0; b = b->base)
+ {
+ if (b->total != b->inverse + b->readonly ||
+ b->readwrite_containers)
+ break; // We have a readwrite base.
+ else if (b->optimistic ())
+ base = true; // We have a readonly optimistic root.
+ }
+
+ os << ", " << base;
+ }
+
+ os << ");";
+
+ // Clear the change flag and arm the transaction rollback callback.
+ //
+ if (i->update == user_section::update_change)
+ {
+ // If polymorphic, do it only if we are the final override.
+ //
+ if (poly)
+ os << "if (root_traits::map->find (typeid (obj))." <<
+ "final_section_update (info, " << i->index << "UL))" << endl;
+
+ os << ma.translate ("obj") << ".reset (true, false, &tr);";
+ }
+
+ os << "}";
+
+ if (av != 0 || dv != 0)
+ os << "}";
+ }
+
+ // Call callback (post_update).
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ os << "callback (db, obj, callback_event::post_update);"
+ << "pointer_cache_traits::update (db, obj);";
+
+ if (poly)
+ os << "}";
+ }
+ } // readonly
+
+ os << "}";
+ }
+
+ // update () bulk
+ //
+ if (id != 0 && !readonly && c.count ("bulk-update"))
+ {
+ os << "void " << traits << "::" << endl
+ << "update (database& db," << endl
+ << "const object_type** objs," << endl
+ << "std::size_t n," << endl
+ << "multiple_exceptions& mex)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using " << db << "::update_statement;"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "const object_type& obj (*objs[i]);"
+ << "callback (db, obj, callback_event::pre_update);";
+
+ if (opt != 0)
+ os << "const version_type& v (version (obj));";
+
+ os << "init (sts.id_image (i), id (obj)" << (opt != 0 ? ", &v" : "") << ");";
+
+ //@@ assumption: generate_grow is false or it only affects select (like
+ // in pgsql) so all we have to do is to increment image
+ // version if it grew.
+
+ os << "image_type& im (sts.image (i));";
+
+ if (generate_grow)
+ os << "if (";
+
+ os << "init (im, obj, statement_update" << (versioned ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << " && i == 0)" << endl
+ << "im.version++";
+
+ os << ";"
+ << "}";
+
+ // Update bindings.
+ //
+ os << "binding& idb (sts.id_image_binding ());"
+ << "binding& imb (sts.update_image_binding ());"
+ << endl;
+
+ //@@ assumption: generate_grow: as above
+ //
+ os << "bool u (false);" // Avoid incrementing version twice.
+ << "if (imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, sts.image (), statement_update" <<
+ (versioned ? ", svm" : "") << ");"
+ << "imb.version++;"
+ << "u = true;"
+ << "}";
+
+ //@@ assumption: generate_grow: as above
+ //
+ os << "if (idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, sts.id_image ());"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << endl
+ << "if (!u)" << endl
+ << "imb.version++;"
+ << "}";
+
+ // We don't need the versioned check here since the statement cannot
+ // be empty; otherwise it would have been an empty object which is
+ // illegal.
+ //
+ os << "update_statement& st (sts.update_statement ());"
+ << "n = st.execute (n, mex);"; // Actual number of rows attempted.
+
+ os << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "unsigned long long r (st.result (i));" // Also sets current in mex.
+ << endl
+ << "if (mex[i] != 0)" << endl // Pending exception from result().
+ << "continue;"
+ << endl
+ << "if (r != 1)"
+ << "{"
+ << "mex.insert (i," << endl
+ //@@ assumption: result_unknown
+ << "(r == update_statement::result_unknown)," << endl
+ << (opt == 0 ? "object_not_persistent" : "object_changed") << " ());"
+ << "continue;"
+ << "}"
+ << "if (mex.fatal ())" << endl // Don't do any extra work.
+ << "continue;"
+ << endl
+ << "const object_type& obj (*objs[i]);";
+
+ // Update the optimistic concurrency version in the object member.
+ //
+ if (opt != 0)
+ {
+ // Object is passed as const reference so we need to cast away
+ // constness.
+ //
+ const char* obj ("const_cast<object_type&> (obj)");
+ string inc (optimistic_version_increment (*opt));
+
+ if (inc == "1")
+ inc_member (*opt, obj, "obj", "version_type");
+ else
+ set_member (*opt, obj, inc, "", "version_type");
+
+ os << endl;
+ }
+
+ os << "callback (db, obj, callback_event::post_update);"
+ << "pointer_cache_traits::update (db, obj);"
+ << "}" // for
+ << "}"; // update()
+ }
+
+ // erase (id)
+ //
+ size_t erase_containers (has_a (c, test_straight_container));
+ bool erase_versioned_containers (
+ erase_containers >
+ has_a (c, test_straight_container | exclude_deleted | exclude_added));
+
+ if (id != 0)
+ {
+ os << "void " << traits << "::" << endl
+ << "erase (database& db, const id_type& id";
+
+ if (poly)
+ os << ", bool top, bool dyn";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";";
+
+ if (poly)
+ os << endl
+ << "ODB_POTENTIALLY_UNUSED (top);";
+
+ os << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+
+ // Get the discriminator and determine the dynamic type of
+ // this object.
+ //
+ if (poly)
+ {
+ os << "if (dyn)" << endl
+ << "{"
+ << "discriminator_type d;"
+ << "root_traits::discriminator_ (sts.root_statements (), id, &d);";
+
+ if (!abst)
+ os << endl
+ << "if (d != info.discriminator)"
+ << "{";
+
+ os << "const info_type& pi (root_traits::map->find (d));"
+ << endl
+ // Check that the dynamic type is derived from the static type.
+ //
+ << "if (!pi.derived (info))" << endl
+ << "throw object_not_persistent ();"
+ << endl
+ << "pi.dispatch (info_type::call_erase, db, 0, &id);"
+ << "return;";
+
+ if (!abst)
+ os << "}";
+
+ os << "}";
+ }
+
+ // Initialize id image.
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, id);"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+
+ if (poly)
+ os << "}";
+ }
+
+ // Erase containers first so that there are no reference
+ // violations (we don't want to rely on ON DELETE CASCADE
+ // here since in case of a custom schema, it might not be
+ // there).
+ //
+ if (erase_containers)
+ {
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());";
+
+ if (erase_versioned_containers)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ instance<container_calls> t (container_calls::erase_id_call);
+ t->traverse (c);
+ }
+
+ os << "if (sts.erase_statement ().execute () != 1)" << endl
+ << "throw object_not_persistent ();"
+ << endl;
+
+ if (poly_derived)
+ {
+ // Call our base last (we erase polymorphic objects from base
+ // to derived in order not to trigger cascading deletes).
+ //
+ os << "base_traits::erase (db, id, false, false);"
+ << endl;
+ }
+
+ // Remove from the object cache.
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "pointer_cache_traits::erase (db, id);";
+ }
+
+ os << "}";
+ }
+
+ // erase (id) bulk
+ //
+ if (id != 0 && c.count ("bulk-erase"))
+ {
+ os << "std::size_t " << traits << "::" << endl
+ << "erase (database& db," << endl
+ << "const id_type** ids," << endl
+ << "std::size_t n," << endl
+ << "multiple_exceptions& mex)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl
+ << "for (std::size_t i (0); i != n; ++i)" << endl
+ << "init (sts.id_image (i), *ids[i]);"
+ << endl
+ << "binding& idb (sts.id_image_binding ());"
+ //@@ assumption: generate_grow is false or it only affects select (like
+ // in pgsql).
+ << "if (idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, sts.id_image ());"
+ << "idb.version++;"
+ << (opt != 0 ? "sts.optimistic_id_image_binding ().version++;" : "")
+ << "}"
+ << "delete_statement& st (sts.erase_statement ());"
+ << "n = st.execute (n, mex);" // Set to actual number of rows attempted.
+ << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "unsigned long long r (st.result (i));" // Sets current in mex.
+ << endl
+ << "if (mex[i] != 0)" << endl // Pending exception from result().
+ << "continue;"
+ << endl
+ << "if (r != 1)"
+ << "{"
+ << "mex.insert (i," << endl
+ //@@ assumption: result_unknown
+ << "(r == delete_statement::result_unknown)," << endl
+ << "object_not_persistent ());"
+ << "continue;"
+ << "}"
+ << "if (mex.fatal ())" << endl // Don't do any extra work.
+ << "continue;"
+ << "pointer_cache_traits::erase (db, *ids[i]);"
+ << "}" // for
+ << "return n;"
+ << "}"; // erase()
+ }
+
+ // erase (object)
+ //
+ bool erase_smart_containers (has_a (c, test_smart_container));
+
+ if (id != 0 && (poly || opt != 0 || erase_smart_containers))
+ {
+ os << "void " << traits << "::" << endl
+ << "erase (database& db, const object_type& obj";
+
+ if (poly)
+ os << ", bool top, bool dyn";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (top);";
+
+ os << endl;
+
+ if (poly)
+ os << "if (dyn)" << endl
+ << "{"
+ << "const std::type_info& t (typeid (obj));"
+ << endl
+ << "if (t != info.type)"
+ << "{"
+ << "const info_type& pi (root_traits::map->find (t));"
+ << "pi.dispatch (info_type::call_erase, db, &obj, 0);"
+ << "return;"
+ << "}"
+ << "}";
+
+ // If we are database-poly-abstract but not C++-abstract, then make
+ // sure we are not trying to erase an instance of an abstract class.
+ //
+ if (abst && !c.abstract ())
+ os << "if (top)" << endl
+ << "throw abstract_class ();"
+ << endl;
+
+ if (opt != 0 || erase_smart_containers)
+ {
+ string rsts (poly_derived ? "rsts" : "sts");
+
+ os << "using namespace " << db << ";"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (poly_derived)
+ os << endl
+ << "root_statements_type& rsts (sts.root_statements ());"
+ << "ODB_POTENTIALLY_UNUSED (rsts);";
+
+ os << endl;
+
+ // Call callback (pre_erase).
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "callback (db, obj, callback_event::pre_erase);"
+ << endl;
+ }
+
+ if (!abst || erase_containers)
+ {
+ os << "const id_type& id (object_traits_impl::id (obj));"
+ << endl;
+ }
+
+ // Smart containers case.
+ //
+ if (opt == 0)
+ {
+ // Initialize id image.
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ os << "id_image_type& i (" << rsts << ".id_image ());"
+ << "init (i, id);"
+ << endl;
+
+ os << "binding& idb (" << rsts << ".id_image_binding ());"
+ << "if (i.version != " << rsts << ".id_image_version () || " <<
+ "idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << rsts << ".id_image_version (i.version);"
+ << "idb.version++;"
+ // Cannot be optimistic.
+ << "}";
+
+ if (poly)
+ os << "}";
+ }
+
+ // Erase containers first so that there are no reference
+ // violations (we don't want to rely on ON DELETE CASCADE
+ // here since in case of a custom schema, it might not be
+ // there).
+ //
+
+ if (erase_containers)
+ {
+ os << "extra_statement_cache_type& esc (" <<
+ "sts.extra_statement_cache ());";
+
+ if (erase_versioned_containers)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ instance<container_calls> t (container_calls::erase_obj_call);
+ t->traverse (c);
+ }
+
+ os << "if (sts.erase_statement ().execute () != 1)" << endl
+ << "throw object_not_persistent ();"
+ << endl;
+ }
+ // Optimistic case.
+ //
+ else
+ {
+ // Initialize id + managed column image.
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ os << "const version_type& v (version (obj));"
+ << "id_image_type& i (" << rsts << ".id_image ());"
+ << "init (i, id, &v);"
+ << endl;
+
+ os << "binding& idb (" << rsts << ".id_image_binding ());"
+ << "if (i.version != " << rsts << ".id_image_version () ||" << endl
+ << "idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << rsts << ".id_image_version (i.version);"
+ << "idb.version++;"
+ << rsts << ".optimistic_id_image_binding ().version++;"
+ << "}";
+
+ if (poly)
+ os << "}"; // if (top)
+ }
+
+ // If this is a derived type in a polymorphic hierarchy, then we
+ // need to check the version (stored in root) before we go ahead
+ // and start deleting things. Also use the same code for root with
+ // containers since it is more efficient than the find_() method
+ // below.
+ //
+ bool svm (false);
+ if (poly_derived || (poly && erase_containers))
+ {
+ // Only do the check in the top-level call.
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ os << "if (top)"
+ << "{"
+ << "version_type v;"
+ << "root_traits::discriminator_ (" << rsts << ", id, 0, &v);"
+ << endl;
+
+ os << "if (v != version (obj))" << endl
+ << "throw object_changed ();"
+ << "}";
+ }
+ }
+ else if (erase_containers)
+ {
+ // Things get complicated here: we don't want to trash the
+ // containers and then find out that the versions don't match
+ // and we therefore cannot delete the object. After all, there
+ // is no guarantee that the user will abort the transaction.
+ // In fact, a perfectly reasonable scenario is to reload the
+ // object, re-check the condition, decide not to delete the
+ // object, and then commit the transaction.
+ //
+ // There doesn't seem to be anything better than first making
+ // sure we can delete the object, then deleting the container
+ // data, and then deleting the object. To check that we can
+ // delete the object we are going to use find_() and then
+ // compare the versions. A special-purpose SELECT query would
+ // have been more efficient but it would complicated and bloat
+ // things significantly.
+ //
+
+ if (versioned)
+ {
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));"
+ << endl;
+ svm = true;
+ }
+
+ os << "if (!find_ (sts, &id" <<
+ (versioned ? ", svm" : "") << "))" << endl
+ << "throw object_changed ();"
+ << endl;
+
+ if (delay_freeing_statement_result)
+ os << "sts.find_statement ().free_result ();"
+ << endl;
+
+ os << "if (version (sts.image ()) != version (obj))" << endl
+ << "throw object_changed ();"
+ << endl;
+ }
+
+ // Erase containers first so that there are no reference
+ // violations (we don't want to rely on ON DELETE CASCADE
+ // here since in case of a custom schema, it might not be
+ // there).
+ //
+ if (erase_containers)
+ {
+ os << "extra_statement_cache_type& esc (" <<
+ "sts.extra_statement_cache ());";
+
+ if (erase_versioned_containers && !svm)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ instance<container_calls> t (container_calls::erase_obj_call);
+ t->traverse (c);
+ }
+
+ const char* st (
+ poly_derived ? "erase_statement" : "optimistic_erase_statement");
+
+ os << "if (sts." << st << " ().execute () != 1)" << endl
+ << "throw object_changed ();"
+ << endl;
+ }
+
+ if (poly_derived)
+ {
+ // Call our base last (we erase polymorphic objects from base
+ // to derived in order not to trigger cascading deletes).
+ //
+ os << "base_traits::erase (db, obj, false, false);"
+ << endl;
+ }
+
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ // Note that we don't reset sections since the object is now
+ // transient and the state of a section in a transient object
+ // is undefined.
+
+ // Remove from the object cache.
+ //
+ os << "pointer_cache_traits::erase (db, id);";
+
+ // Call callback (post_erase).
+ //
+ os << "callback (db, obj, callback_event::post_erase);";
+
+ if (poly)
+ os << "}";
+ }
+ }
+ else
+ {
+ os << "callback (db, obj, callback_event::pre_erase);"
+ << "erase (db, id (obj), true, false);"
+ << "callback (db, obj, callback_event::post_erase);";
+ }
+
+ os << "}";
+ }
+
+ // erase (object) bulk
+ //
+ if (id != 0 && c.count ("bulk-erase"))
+ {
+ os << "void " << traits << "::" << endl
+ << "erase (database& db," << endl
+ << "const object_type** objs," << endl
+ << "std::size_t n," << endl
+ << "multiple_exceptions& mex)"
+ << "{";
+
+ // In non-optimistic case delegate to erase(id).
+ //
+ if (opt == 0)
+ {
+ os << "id_type a[batch];"
+ << "const id_type* p[batch];"
+ << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "const object_type& obj (*objs[i]);"
+ << "callback (db, obj, callback_event::pre_erase);"
+ << "a[i] = id (obj);"
+ << "p[i] = a + i;"
+ << "}"
+ << "n = erase (db, p, n, mex);"
+ << endl
+ << "if (mex.fatal ())" << endl // Don't do any extra work.
+ << "return;"
+ << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "if (mex[i] == 0)" << endl // No pending exception.
+ << "callback (db, *objs[i], callback_event::post_erase);"
+ << "}"; // for
+ }
+ else
+ {
+ os << "using namespace " << db << ";"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "const object_type& obj (*objs[i]);"
+ << "callback (db, obj, callback_event::pre_erase);"
+ << "const version_type& v (version (obj));"
+ << "init (sts.id_image (i), id (obj), &v);"
+ << "}";
+
+ os << "binding& idb (sts.id_image_binding ());"
+ //@@ assumption: generate_grow is false or it only affects select
+ // (like in pgsql).
+ << "if (idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, sts.id_image ());"
+ << "idb.version++;"
+ << "sts.optimistic_id_image_binding ().version++;"
+ << "}"
+ << "delete_statement& st (sts.optimistic_erase_statement ());"
+ << "n = st.execute (n, mex);" // Set to actual number of attempted.
+ << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "unsigned long long r (st.result (i));" // Sets current in mex.
+ << endl
+ << "if (mex[i] != 0)" << endl // Pending exception from result().
+ << "continue;"
+ << endl
+ << "if (r != 1)"
+ << "{"
+ << "mex.insert (i," << endl
+ //@@ assumption: result_unknown
+ << "(r == delete_statement::result_unknown)," << endl
+ << "object_changed ());"
+ << "continue;"
+ << "}"
+ << "if (mex.fatal ())" << endl // Don't do any extra work.
+ << "continue;"
+ << endl
+ << "const object_type& obj (*objs[i]);"
+ << "pointer_cache_traits::erase (db, id (obj));"
+ << "callback (db, obj, callback_event::post_erase);"
+ << "}"; // for
+ }
+
+ os << "}"; // erase()
+ }
+
+ // find (id)
+ //
+ if (id != 0 && c.default_ctor ())
+ {
+ string rsts (poly_derived ? "rsts" : "sts");
+
+ os << traits << "::pointer_type" << endl
+ << traits << "::" << endl
+ << "find (database& db, const id_type& id)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ // First check the session.
+ //
+ os << "{";
+
+ if (poly_derived)
+ os << "root_traits::pointer_type rp (pointer_cache_traits::find (" <<
+ "db, id));"
+ << endl
+ << "if (!root_traits::pointer_traits::null_ptr (rp))" << endl
+ << "return" << endl
+ << " root_traits::pointer_traits::dynamic_pointer_cast<" <<
+ "object_type> (rp);";
+ else
+ os << "pointer_type p (pointer_cache_traits::find (db, id));"
+ << endl
+ << "if (!pointer_traits::null_ptr (p))" << endl
+ << "return p;";
+
+ os << "}";
+
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ if (poly_derived)
+ os << "root_statements_type& rsts (sts.root_statements ());";
+
+ os << endl
+ << "statements_type::auto_lock l (" << rsts << ");";
+
+ if (delay_freeing_statement_result)
+ os << "auto_result ar;";
+
+ if (poly)
+ os << "root_traits::discriminator_type d;";
+
+ os << endl
+ << "if (l.locked ())"
+ << "{"
+ << "if (!find_ (sts, &id" << (versioned ? ", svm" : "") << "))" << endl
+ << "return pointer_type ();";
+
+ if (delay_freeing_statement_result)
+ os << endl
+ << "ar.set (sts.find_statement (" << (poly_derived ? "depth" : "") <<
+ "));";
+
+ if (poly)
+ os << "d = root_traits::discriminator (" << rsts << ".image ());";
+
+ os << "}";
+
+ if (poly)
+ {
+ // If statements are locked, then get the discriminator by
+ // executing a special SELECT statement. We need it to be
+ // able to create an object of the correct dynamic type
+ // which will be loaded later.
+ //
+ os << "else" << endl
+ << "root_traits::discriminator_ (" << rsts << ", id, &d);"
+ << endl;
+
+ if (abst)
+ os << "const info_type& pi (root_traits::map->find (d));"
+ << endl;
+ else
+ os << "const info_type& pi (" << endl
+ << "d == info.discriminator ? info : root_traits::map->find (d));"
+ << endl;
+ }
+
+ // Create the object.
+ //
+ if (poly_derived)
+ {
+ os << "root_traits::pointer_type rp (pi.create ());"
+ << "pointer_type p (" << endl
+ << "root_traits::pointer_traits::static_pointer_cast<object_type> " <<
+ "(rp));"
+ << "pointer_traits::guard pg (p);"
+ << endl;
+
+ // Insert it as a root pointer (for non-unique pointers, rp should
+ // still be valid and for unique pointers this is a no-op).
+ //
+ os << "pointer_cache_traits::insert_guard ig (" << endl
+ << "pointer_cache_traits::insert (db, id, rp));"
+ << endl;
+ }
+ else
+ {
+ if (poly)
+ os << "pointer_type p (pi.create ());";
+ else
+ os << "pointer_type p (" << endl
+ << "access::object_factory<object_type, pointer_type>::create ());";
+
+ os << "pointer_traits::guard pg (p);"
+ << endl;
+
+ os << "pointer_cache_traits::insert_guard ig (" << endl
+ << "pointer_cache_traits::insert (db, id, p));"
+ << endl;
+ }
+
+ os << "object_type& obj (pointer_traits::get_ref (p));"
+ << endl
+ << "if (l.locked ())"
+ << "{"
+ << "select_statement& st (sts.find_statement (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << "ODB_POTENTIALLY_UNUSED (st);"
+ << endl;
+
+ if (poly)
+ os << "callback_event ce (callback_event::pre_load);"
+ << "pi.dispatch (info_type::call_callback, db, &obj, &ce);";
+ else
+ os << "callback (db, obj, callback_event::pre_load);";
+
+ os << "init (obj, sts.image (), &db" << (versioned ? ", svm" : "") << ");";
+
+ init_value_extra ();
+
+ if (delay_freeing_statement_result)
+ os << "ar.free ();";
+
+ os << "load_ (sts, obj, false" << (versioned ? ", svm" : "") << ");";
+
+ if (poly)
+ // Load the dynamic part of the object unless static and dynamic
+ // types are the same.
+ //
+ os << endl
+ << "if (&pi != &info)"
+ << "{"
+ << "std::size_t d (depth);"
+ << "pi.dispatch (info_type::call_load, db, &obj, &d);"
+ << "}";
+
+ os << rsts << ".load_delayed (" << (versioned ? "&svm" : "0") << ");"
+ << "l.unlock ();";
+
+ if (poly)
+ os << "ce = callback_event::post_load;"
+ << "pi.dispatch (info_type::call_callback, db, &obj, &ce);";
+ else
+ os << "callback (db, obj, callback_event::post_load);";
+
+ os << "pointer_cache_traits::load (ig.position ());"
+ << "}"
+ << "else" << endl
+ << rsts << ".delay_load (id, obj, ig.position ()" <<
+ (poly ? ", pi.delayed_loader" : "") << ");"
+ << endl;
+
+ os << "ig.release ();"
+ << "pg.release ();"
+ << "return p;"
+ << "}";
+ }
+
+ // find (id, obj)
+ //
+ if (id != 0)
+ {
+ string rsts (poly_derived ? "rsts" : "sts");
+
+ os << "bool " << traits << "::" << endl
+ << "find (database& db, const id_type& id, object_type& obj";
+
+ if (poly)
+ os << ", bool dyn";
+
+ os << ")"
+ << "{";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (dyn);"
+ << endl;
+
+ os << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ {
+ if (!abst)
+ os << "if (dyn)" << endl
+ << "{";
+
+ os << "const std::type_info& t (typeid (obj));";
+
+ if (!abst)
+ os << endl
+ << "if (t != info.type)"
+ << "{";
+
+ os << "const info_type& pi (root_traits::map->find (t));"
+ << "return pi.dispatch (info_type::call_find, db, &obj, &id);";
+
+ if (!abst)
+ os << "}"
+ << "}";
+ }
+
+ if (!abst)
+ {
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ if (poly_derived)
+ os << "root_statements_type& rsts (sts.root_statements ());";
+
+ // This can only be top-level call so auto_lock must succeed.
+ //
+ os << endl
+ << "statements_type::auto_lock l (" << rsts << ");"
+ << "assert (l.locked ()) /* Must be a top-level call. */;"
+ << endl;
+
+ os << "if (!find_ (sts, &id" <<
+ (versioned ? ", svm" : "") << "))" << endl
+ << "return false;"
+ << endl;
+
+ os << "select_statement& st (sts.find_statement (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << "ODB_POTENTIALLY_UNUSED (st);"
+ << endl;
+
+ if (delay_freeing_statement_result)
+ os << "auto_result ar (st);";
+
+ os << "reference_cache_traits::position_type pos (" << endl
+ << "reference_cache_traits::insert (db, id, obj));"
+ << "reference_cache_traits::insert_guard ig (pos);"
+ << endl
+ << "callback (db, obj, callback_event::pre_load);"
+ << "init (obj, sts.image (), &db" <<
+ (versioned ? ", svm" : "") << ");";
+
+ init_value_extra ();
+
+ if (delay_freeing_statement_result)
+ os << "ar.free ();";
+
+ os << "load_ (sts, obj, false" << (versioned ? ", svm" : "") << ");"
+ << rsts << ".load_delayed (" << (versioned ? "&svm" : "0") << ");"
+ << "l.unlock ();"
+ << "callback (db, obj, callback_event::post_load);"
+ << "reference_cache_traits::load (pos);"
+ << "ig.release ();"
+ << "return true;";
+ }
+
+ os << "}";
+ }
+
+ // reload ()
+ //
+ if (id != 0)
+ {
+ string rsts (poly_derived ? "rsts" : "sts");
+
+ // This implementation is almost exactly the same as find(id, obj)
+ // above except that it doesn't interract with the object cache and,
+ // in case of optimistic concurrency, checks if the object actually
+ // needs to be reloaded.
+ //
+ os << "bool " << traits << "::" << endl
+ << "reload (database& db, object_type& obj";
+
+ if (poly)
+ os << ", bool dyn";
+
+ os << ")"
+ << "{";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (dyn);"
+ << endl;
+
+ os << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ {
+ if (!abst)
+ os << "if (dyn)" << endl
+ << "{";
+
+ os << "const std::type_info& t (typeid (obj));";
+
+ if (!abst)
+ os << endl
+ << "if (t != info.type)"
+ << "{";
+
+ os << "const info_type& pi (root_traits::map->find (t));"
+ << "return pi.dispatch (info_type::call_reload, db, &obj, 0);";
+
+ if (!abst)
+ os << "}"
+ << "}";
+ }
+
+ if (!abst)
+ {
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ if (poly_derived)
+ os << "root_statements_type& rsts (sts.root_statements ());";
+
+ // This can only be top-level call so auto_lock must succeed.
+ //
+ os << endl
+ << "statements_type::auto_lock l (" << rsts << ");"
+ << "assert (l.locked ()) /* Must be a top-level call. */;"
+ << endl;
+
+ os << "const id_type& id (object_traits_impl::id (obj));"
+ << "if (!find_ (sts, &id" <<
+ (versioned ? ", svm" : "") << "))" << endl
+ << "return false;"
+ << endl;
+
+ os << "select_statement& st (sts.find_statement (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << "ODB_POTENTIALLY_UNUSED (st);"
+ << endl;
+
+ if (delay_freeing_statement_result)
+ os << "auto_result ar (st);"
+ << endl;
+
+ if (opt != 0)
+ {
+ const char* tr (poly_derived ? "root_traits::" : "");
+
+ os << "if (" << tr << "version (" << rsts << ".image ()) == " <<
+ tr << "version (obj))" << endl
+ << "return true;"
+ << endl;
+ }
+
+ os << "callback (db, obj, callback_event::pre_load);"
+ << "init (obj, sts.image (), &db" <<
+ (versioned ? ", svm" : "") << ");";
+
+ init_value_extra ();
+
+ if (delay_freeing_statement_result)
+ os << "ar.free ();";
+
+ os << "load_ (sts, obj, true" << (versioned ? ", svm" : "") << ");"
+ << rsts << ".load_delayed (" << (versioned ? "&svm" : "0") << ");"
+ << "l.unlock ();"
+ << "callback (db, obj, callback_event::post_load);"
+ << "return true;";
+ }
+
+ os << "}";
+ }
+
+ // load (section) [non-thunk version]
+ //
+ if (uss.count (user_sections::count_new |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ {
+ string rsts (poly_derived ? "rsts" : "sts");
+
+ os << "bool " << traits << "::" << endl
+ << "load (connection& conn, object_type& obj, section& s" <<
+ (poly ? ", const info_type* pi" : "") << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ {
+ // Resolve type information if we are doing indirect calls.
+ //
+ os << "bool top (pi == 0);" // Top-level call.
+ << "if (top)"
+ << "{"
+ << "const std::type_info& t (typeid (obj));";
+
+ if (!abst)
+ os << endl
+ << "if (t == info.type)" << endl
+ << "pi = &info;"
+ << "else" << endl;
+
+ os << "pi = &root_traits::map->find (t);"
+ << "}";
+ }
+
+ // Lock the statements for the object itself. We need to do this since we
+ // are using the id image and loading of object pointers can overwrite it.
+ //
+ os << db << "::connection& c (static_cast<" << db <<
+ "::connection&> (conn));"
+ << "statements_type& sts (c.statement_cache ()." <<
+ "find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ if (poly_derived)
+ os << "root_statements_type& rsts (sts.root_statements ());";
+
+ os << endl
+ << "statements_type::auto_lock l (" << rsts << ");";
+
+ // It this is a top-level call, then auto_lock must succeed.
+ //
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "assert (l.locked ()) /* Must be a top-level call. */;"
+ << endl;
+
+ os << "bool r (false);"
+ << endl;
+
+ // If our poly-base has load sections, then call the base version
+ // first. Besides checking for sections, it will also initialize
+ // the id image.
+ //
+ if (poly_derived &&
+ buss->count (user_sections::count_total |
+ user_sections::count_load |
+ user_sections::count_load_empty) != 0)
+ {
+ os << "if (base_traits::load (conn, obj, s, pi))" << endl
+ << "r = true;"
+ << endl;
+ }
+ else
+ {
+ // Initialize id image (all this is equivalent to using rsts).
+ //
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, id (obj));"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+ }
+
+ // Resolve extra statements if we are doing direct calls.
+ //
+ if (!poly)
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
+ << endl;
+
+ // Dispatch.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ if (i->load == user_section::load_eager)
+ continue;
+
+ // Overridden sections are handled by the base.
+ //
+ if (poly_derived && i->base != 0)
+ continue;
+
+ data_member& m (*i->member);
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << "if (!r && &s == &" << ma.translate ("obj") << ")"
+ << "{";
+
+ if (!poly)
+ os << public_name (m) << "_traits::load (esc, obj);";
+ else
+ {
+ // If this is an empty section, then there may not be any
+ // overrides.
+ //
+ os << "info_type::section_load sl (" <<
+ "pi->find_section_load (" << i->index << "UL));";
+
+ if (i->load_empty ())
+ os << "if (sl != 0)" << endl;
+
+ os << "sl (conn, obj, true);";
+ }
+
+ os << "r = true;"
+ << "}";
+ }
+
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ os << rsts << ".load_delayed (" << (versioned ? "&svm" : "0") << ");"
+ << "l.unlock ();";
+
+ if (poly)
+ os << "}";
+
+ os << "return r;"
+ << "}";
+ }
+
+ // update (section) [non-thunk version]
+ //
+ if (uss.count (user_sections::count_new |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ {
+ os << "bool " << traits << "::" << endl
+ << "update (connection& conn, const object_type& obj, " <<
+ "const section& s" << (poly ? ", const info_type* pi" : "") << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ {
+ // Resolve type information if we are doing indirect calls.
+ //
+ os << "if (pi == 0)" // Top-level call.
+ << "{"
+ << "const std::type_info& t (typeid (obj));";
+
+ if (!abst)
+ os << endl
+ << "if (t == info.type)" << endl
+ << "pi = &info;"
+ << "else" << endl;
+
+ os << "pi = &root_traits::map->find (t);"
+ << "}";
+ }
+
+ // If our poly-base has update sections, then call the base version
+ // first. Besides checking for sections, it will also initialize the
+ // id image.
+ //
+ if (poly_derived &&
+ buss->count (user_sections::count_total |
+ user_sections::count_update |
+ user_sections::count_update_empty) != 0)
+ {
+ os << "if (base_traits::update (conn, obj, s, pi))" << endl
+ << "return true;"
+ << endl;
+ }
+ else
+ {
+ // Resolve extra statements if we are doing direct calls.
+ //
+ os << db << "::connection& c (static_cast<" << db <<
+ "::connection&> (conn));"
+ << "statements_type& sts (c.statement_cache ()." <<
+ "find_object<object_type> ());";
+
+ if (!poly)
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());";
+
+ os << endl;
+
+ // Initialize id image. This is not necessarily the root of the
+ // polymorphic hierarchy.
+ //
+ if (opt != 0)
+ os << "const version_type& v (version (obj));";
+
+ os << "id_image_type& i (sts.id_image ());";
+
+ os << "init (i, id (obj)" << (opt != 0 ? ", &v" : "") << ");"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+ }
+
+ // Dispatch.
+ //
+ bool e (false);
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ // Overridden sections are handled by the base.
+ //
+ if (poly_derived && i->base != 0)
+ continue;
+
+ // Skip read-only sections. Polymorphic sections are always
+ // (potentially) read-write.
+ //
+ if (!poly && i->update_empty ())
+ continue;
+
+ data_member& m (*i->member);
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ if (e)
+ os << "else ";
+ else
+ e = true;
+
+ os << "if (&s == &" << ma.translate ("obj") << ")";
+
+ if (!poly)
+ os << public_name (m) << "_traits::update (esc, obj);";
+ else
+ {
+ // If this is a readonly section, then there may not be any
+ // overrides.
+ //
+ os << "{"
+ << "info_type::section_update su (" <<
+ "pi->find_section_update (" << i->index << "UL));";
+
+ if (i->update_empty ())
+ {
+ // For optimistic section, also check that we are not the
+ // final override, since otherwise we will increment the
+ // version without updating anything.
+ //
+ if (i->optimistic ())
+ os << "if (su != 0 && su != info.sections->functions[" <<
+ i->index << "UL].update)" << endl;
+ else
+ os << "if (su != 0)" << endl;
+ }
+
+ os << "su (conn, obj);"
+ << "}";
+ }
+ }
+
+ os << "else" << endl
+ << "return false;"
+ << endl
+ << "return true;"
+ << "}";
+ }
+
+ // find_ ()
+ //
+ if (id != 0)
+ {
+ os << "bool " << traits << "::" << endl
+ << "find_ (statements_type& sts," << endl
+ << "const id_type* id";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ if (poly_derived && !abst)
+ os << "," << endl
+ << "std::size_t d";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ // Initialize id image.
+ //
+ if (poly_derived && !abst)
+ os << "if (d == depth)"
+ << "{";
+
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, *id);"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+
+ if (poly_derived && !abst)
+ os << "}";
+
+ // Rebind data image.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.select_image_binding (" <<
+ (poly_derived ? (abst ? "depth" : "d") : "") << "));"
+ << endl;
+
+ if (poly_derived)
+ {
+ os << "if (imb.version == 0 ||" << endl
+ << "check_version (sts.select_image_versions (), im))"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "update_version (sts.select_image_versions ()," << endl
+ << "im," << endl
+ << "sts.select_image_bindings ());"
+ << "}";
+ }
+ else
+ {
+ os << "if (im.version != sts.select_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+ }
+
+ os << "select_statement& st (sts.find_statement (" <<
+ (poly_derived ? (abst ? "depth" : "d") : "") << "));"
+ << endl;
+
+ // The dynamic loader SELECT statement can be dynamically empty.
+ //
+ if (versioned && poly_derived && !abst)
+ os << "if (st.empty ())" << endl
+ << "return true;"
+ << endl;
+
+ os << "st.execute ();"
+ << "auto_result ar (st);"
+ << "select_statement::result r (st.fetch ());"
+ << endl;
+
+ if (grow)
+ {
+ os << "if (r == select_statement::truncated)"
+ << "{"
+ << "if (grow (im, sts.select_image_truncated ()" <<
+ (versioned ? ", svm" : "") <<
+ (poly_derived ? (abst ? ", depth" : ", d") : "") << "))" << endl
+ << "im.version++;"
+ << endl;
+
+ if (poly_derived)
+ {
+ os << "if (check_version (sts.select_image_versions (), im))"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "update_version (sts.select_image_versions ()," << endl
+ << "im," << endl
+ << "sts.select_image_bindings ());"
+ << "st.refetch ();"
+ << "}";
+ }
+ else
+ {
+ os << "if (im.version != sts.select_image_version ())"
+ << "{"
+ << "bind (imb.bind, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (im.version);"
+ << "imb.version++;"
+ << "st.refetch ();"
+ << "}";
+ }
+
+ os << "}";
+ }
+
+ // If we are delaying, only free the result if it is empty.
+ //
+ if (delay_freeing_statement_result)
+ os << "if (r != select_statement::no_data)"
+ << "{"
+ << "ar.release ();"
+ << "return true;"
+ << "}"
+ << "else" << endl
+ << "return false;";
+ else
+ os << "return r != select_statement::no_data;";
+
+ os << "}";
+ }
+
+ // load_()
+ //
+ // Load containers, reset/reload sections.
+ //
+ size_t load_containers (
+ has_a (c, test_container | include_eager_load, &main_section));
+
+ if (poly_derived ||
+ load_containers ||
+ uss.count (user_sections::count_new |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ {
+ bool load_versioned_containers (
+ load_containers >
+ has_a (c,
+ test_container | include_eager_load |
+ exclude_deleted | exclude_added | exclude_versioned,
+ &main_section));
+
+ os << "void " << traits << "::" << endl
+ << "load_ (statements_type& sts," << endl
+ << "object_type& obj," << endl
+ << "bool reload";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ if (poly_derived)
+ os << "," << endl
+ << "std::size_t d";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (reload);";
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+ os << endl;
+
+ if (poly_derived)
+ os << "if (--d != 0)" << endl
+ << "base_traits::load_ (sts.base_statements (), obj, reload" <<
+ (context::versioned (*poly_base) ? ", svm" : "") <<
+ (poly_base != poly_root ? ", d" : "") << ");"
+ << endl;
+
+ if (load_containers ||
+ (!poly && uss.count (user_sections::count_new |
+ user_sections::count_load) != 0))
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
+ << endl;
+
+ if (!versioned && (
+ load_versioned_containers ||
+ uss.count (user_sections::count_new |
+ user_sections::count_all |
+ user_sections::count_versioned_only) != 0))
+ {
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));"
+ << endl;
+ }
+
+ if (load_containers)
+ {
+ instance<container_calls> t (container_calls::load_call, &main_section);
+ t->traverse (c);
+ }
+
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ // Skip overridden sections; they are handled by the base.
+ //
+ if (i->base != 0 && poly_derived)
+ continue;
+
+ data_member& m (*i->member);
+
+ // If the section is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (m));
+ unsigned long long dv (deleted (m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")"
+ << "{";
+ }
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ if (i->load == user_section::load_eager)
+ {
+ // Mark an eager section as loaded.
+ //
+ os << ma.translate ("obj") << ".reset (true, false);"
+ << endl;
+ continue;
+ }
+
+ os << "if (reload)"
+ << "{"
+ // Reload sections that have been loaded, clear change state.
+ //
+ << "if (" << ma.translate ("obj") << ".loaded ())"
+ << "{";
+
+ if (!poly)
+ os << public_name (m) << "_traits::load (esc, obj);";
+ else
+ {
+ os << "info_type::section_load sl (" << endl
+ << "root_traits::map->find (typeid (obj)).find_section_load (" <<
+ i->index << "UL));";
+
+ if (i->load_empty ())
+ os << "if (sl != 0)" << endl;
+
+ os << "sl (sts.connection (), obj, true);";
+ }
+
+ os << ma.translate ("obj") << ".reset (true, false);"
+ << "}"
+ << "}"
+ << "else" << endl
+ // Reset to unloaded, unchanged state.
+ << ma.translate ("obj") << ".reset ();";
+
+ if (av != 0 || dv != 0)
+ os << "}";
+ else
+ os << endl;
+ }
+
+ os << "}";
+ }
+
+ // load_()
+ //
+ // Load the dynamic part of the object. We don't need it if we are
+ // poly-abstract.
+ //
+ if (poly_derived && !abst)
+ {
+ os << "void " << traits << "::" << endl
+ << "load_ (database& db, root_type& r, std::size_t d)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "object_type& obj (static_cast<object_type&> (r));"
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl
+ << "d = depth - d;" // Convert to distance from derived.
+ << endl;
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));"
+ << endl;
+
+ // Avoid trying to execute an empty SELECT statement.
+ //
+ if (empty_depth != 0)
+ os << "if (d > " << (poly_depth - empty_depth) << "UL)"
+ << "{";
+
+ os << "if (!find_ (sts, 0" << (versioned ? ", svm" : "") << ", d))" << endl
+ << "throw object_not_persistent ();" // Database inconsistency.
+ << endl;
+
+ os << "select_statement& st (sts.find_statement (d));"
+ << "ODB_POTENTIALLY_UNUSED (st);"
+ << endl;
+
+ // The resulting SELECT statement may end up being dynamically empty.
+ //
+ if (versioned)
+ os << "if (!st.empty ())"
+ << "{";
+
+ if (delay_freeing_statement_result)
+ os << "auto_result ar (st);";
+
+ os << "init (obj, sts.image (), &db" << (versioned ? ", svm" : "") <<
+ ", d);";
+
+ init_value_extra ();
+
+ if (delay_freeing_statement_result)
+ os << "ar.free ();";
+
+ if (versioned)
+ os << "}";
+
+ if (empty_depth != 0)
+ os << "}";
+
+ os << "load_ (sts, obj, false, " << (versioned ? "svm, " : "") << "d);"
+ << "}";
+ }
+
+ // discriminator_ ()
+ //
+ if (poly && !poly_derived)
+ {
+ os << "void " << traits << "::" << endl
+ << "discriminator_ (statements_type& sts," << endl
+ << "const id_type& id," << endl
+ << "discriminator_type* pd";
+
+ if (opt != 0)
+ os << "," << endl
+ << "version_type* pv";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ // Initialize id image.
+ //
+ os << "id_image_type& idi (sts.discriminator_id_image ());"
+ << "init (idi, id);"
+ << endl;
+
+ os << "binding& idb (sts.discriminator_id_image_binding ());"
+ << "if (idi.version != sts.discriminator_id_image_version () ||" << endl
+ << "idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, idi" << (opt != 0 ? ", false" : "") << ");"
+ << "sts.discriminator_id_image_version (idi.version);"
+ << "idb.version++;"
+ << "}";
+
+ // Rebind data image.
+ //
+ os << "discriminator_image_type& i (sts.discriminator_image ());"
+ << "binding& imb (sts.discriminator_image_binding ());"
+ << endl
+ << "if (i.version != sts.discriminator_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ // Generate bind code inline. For now discriminator is simple
+ // value so we don't need statement kind (sk).
+ //
+ << bind_vector << " b (imb.bind);"
+ << "std::size_t n (0);"
+ << "{";
+ bind_discriminator_member_->traverse (*discriminator);
+ os << "}";
+
+ if (opt != 0)
+ {
+ os << "n++;" // For now discriminator is a simple value.
+ << "{";
+ bind_version_member_->traverse (*opt);
+ os << "}";
+ }
+
+ os << "sts.discriminator_image_version (i.version);"
+ << "imb.version++;"
+ << "}";
+
+ os << "{"
+ << "select_statement& st (sts.find_discriminator_statement ());"
+ << "st.execute ();"
+ << "auto_result ar (st);"
+ << "select_statement::result r (st.fetch ());"
+ << endl
+ << "if (r == select_statement::no_data)"
+ << "{";
+
+ if (opt != 0)
+ os << "if (pv != 0)" << endl
+ << "throw object_changed ();"
+ << "else" << endl;
+
+ os << "throw object_not_persistent ();"
+ << "}";
+
+ if (generate_grow &&
+ (context::grow (*discriminator) ||
+ (opt != 0 && context::grow (*opt))))
+ {
+ os << "else if (r == select_statement::truncated)"
+ << "{";
+
+ // Generate grow code inline.
+ //
+ os << "bool grew (false);"
+ << truncated_vector << " t (sts.discriminator_image_truncated ());"
+ << endl;
+
+ index_ = 0;
+ grow_discriminator_member_->traverse (*discriminator);
+
+ if (opt != 0)
+ grow_version_member_->traverse (*opt);
+
+ os << "if (grew)" << endl
+ << "i.version++;"
+ << endl;
+
+ os << "if (i.version != sts.discriminator_image_version ())"
+ << "{"
+ // Generate bind code inline. The same code as above.
+ //
+ << bind_vector << " b (imb.bind);"
+ << "std::size_t n (0);"
+ << "{";
+ bind_discriminator_member_->traverse (*discriminator);
+ os << "}";
+
+ if (opt != 0)
+ {
+ os << "n++;" // For now discriminator is a simple value.
+ << "{";
+ bind_version_member_->traverse (*opt);
+ os << "}";
+ }
+
+ os << "sts.discriminator_image_version (i.version);"
+ << "imb.version++;"
+ << "st.refetch ();"
+ << "}"
+ << "}";
+ }
+
+ // Discriminator cannot be long data (no streaming).
+ //
+ os << "}";
+
+ // Initialize value inline instead of generating a separate
+ // init() function. For now discriminator is simple value so
+ // we don't need the database (db).
+ //
+ os << "if (pd != 0)"
+ << "{"
+ << "discriminator_type& d (*pd);";
+ init_named_discriminator_value_member_->traverse (*discriminator);
+ os << "}";
+
+ if (opt != 0)
+ {
+ os << "if (pv != 0)"
+ << "{"
+ << "version_type& v (*pv);";
+ init_named_version_value_member_->traverse (*opt);
+ os << "}";
+ }
+
+ os << "}";
+ }
+
+ if (options.generate_query ())
+ {
+ char const* result_type;
+ if (poly)
+ result_type = "polymorphic_object_result_impl<object_type>";
+ else if (id != 0)
+ result_type = "object_result_impl<object_type>";
+ else
+ result_type = "no_id_object_result_impl<object_type>";
+
+ string sep (versioned || query_optimize ? "\n" : " ");
+
+ // Unprepared.
+ //
+ if (!options.omit_unprepared ())
+ {
+ // query ()
+ //
+ os << "result< " << traits << "::object_type >" << endl
+ << traits << "::" << endl
+ << "query (database& db, const query_base_type& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl;
+
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << endl
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ // Rebind the image if necessary.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.select_image_binding (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << endl;
+
+ if (poly_derived)
+ {
+ os << "if (imb.version == 0 ||" << endl
+ << "check_version (sts.select_image_versions (), im))"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "update_version (sts.select_image_versions ()," << endl
+ << "im," << endl
+ << "sts.select_image_bindings ());"
+ << "}";
+ }
+ else
+ {
+ os << "if (im.version != sts.select_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+ }
+
+ os << "std::string text (query_statement);"
+ << "if (!q.empty ())"
+ << "{"
+ << "text += " << strlit (sep) << ";"
+ << "text += q.clause ();"
+ << "}";
+
+ os << "q.init_parameters ();"
+ << "shared_ptr<select_statement> st (" << endl
+ << "new (shared) select_statement (" << endl;
+ object_query_statement_ctor_args (
+ c, "q", versioned || query_optimize, false);
+ os << "));" << endl
+ << "st->execute ();";
+
+ post_query_ (c, true);
+
+ os << endl
+ << "shared_ptr< odb::" << result_type << " > r (" << endl
+ << "new (shared) " << db << "::" << result_type << " (" << endl
+ << "q, st, sts, " << (versioned ? "&svm" : "0") << "));"
+ << endl
+ << "return result<object_type> (r);"
+ << "}";
+
+ // query(odb::query_base)
+ //
+ if (multi_dynamic)
+ os << "result< " << traits << "::object_type >" << endl
+ << traits << "::" << endl
+ << "query (database& db, const odb::query_base& q)"
+ << "{"
+ << "return query (db, query_base_type (q));"
+ << "}";
+ }
+
+ // erase_query
+ //
+ os << "unsigned long long " << traits << "::" << endl
+ << "erase_query (database& db, const query_base_type& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << endl
+ << "std::string text (erase_query_statement);"
+ << "if (!q.empty ())"
+ << "{"
+ << "text += ' ';"
+ << "text += q.clause ();"
+ << "}"
+ << "q.init_parameters ();"
+ << "delete_statement st (" << endl;
+ object_erase_query_statement_ctor_args (c);
+ os << ");"
+ << endl
+ << "return st.execute ();"
+ << "}";
+
+ // erase_query(odb::query_base)
+ //
+ if (multi_dynamic)
+ os << "unsigned long long " << traits << "::" << endl
+ << "erase_query (database& db, const odb::query_base& q)"
+ << "{"
+ << "return erase_query (db, query_base_type (q));"
+ << "}";
+
+ // Prepared. Very similar to unprepared but has some annoying variations
+ // that make it difficult to factor out something common.
+ //
+ if (options.generate_prepared ())
+ {
+ // prepare_query
+ //
+ os << "odb::details::shared_ptr<prepared_query_impl>" << endl
+ << traits << "::" << endl
+ << "prepare_query (connection& c, const char* n, " <<
+ "const query_base_type& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl;
+
+ os << db << "::connection& conn (" << endl
+ << "static_cast<" << db << "::connection&> (c));"
+ << endl
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ // Rebind the image if necessary.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.select_image_binding (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << endl;
+
+ if (poly_derived)
+ {
+ os << "if (imb.version == 0 ||" << endl
+ << "check_version (sts.select_image_versions (), im))"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "update_version (sts.select_image_versions ()," << endl
+ << "im," << endl
+ << "sts.select_image_bindings ());"
+ << "}";
+ }
+ else
+ {
+ os << "if (im.version != sts.select_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+ }
+
+ os << "std::string text (query_statement);"
+ << "if (!q.empty ())"
+ << "{"
+ << "text += " << strlit (sep) << ";"
+ << "text += q.clause ();"
+ << "}";
+
+ os << "shared_ptr<" << db << "::prepared_query_impl> r (" << endl
+ << "new (shared) " << db << "::prepared_query_impl (conn));"
+ << "r->name = n;"
+ << "r->execute = &execute_query;"
+ << "r->query = q;"
+ << "r->stmt.reset (" << endl
+ << "new (shared) select_statement (" << endl;
+ object_query_statement_ctor_args (
+ c, "r->query", versioned || query_optimize, true);
+ os << "));"
+ << endl
+ << "return r;"
+ << "}";
+
+ // prepare_query(odb::query_base)
+ //
+ if (multi_dynamic)
+ os << "odb::details::shared_ptr<prepared_query_impl>" << endl
+ << traits << "::" << endl
+ << "prepare_query (connection& c, const char* n, " <<
+ "const odb::query_base& q)"
+ << "{"
+ << "return prepare_query (c, n, query_base_type (q));"
+ << "}";
+
+ // execute_query
+ //
+ os << "odb::details::shared_ptr<result_impl>" << endl
+ << traits << "::" << endl
+ << "execute_query (prepared_query_impl& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl
+ << db << "::prepared_query_impl& pq (" << endl
+ << "static_cast<" << db << "::prepared_query_impl&> (q));"
+ << "shared_ptr<select_statement> st (" << endl
+ << "odb::details::inc_ref (" << endl
+ << "static_cast<select_statement*> (pq.stmt.get ())));"
+ << endl;
+
+ os << db << "::transaction& tr (" << db << "::transaction::current ());"
+ << "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 (q.verify_connection (tr));"
+ << endl
+ << "statements_type& sts (" << endl
+ << "st->connection ().statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ // Rebind the image if necessary.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.select_image_binding (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << endl;
+
+ if (poly_derived)
+ {
+ os << "if (imb.version == 0 ||" << endl
+ << "check_version (sts.select_image_versions (), im))"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "update_version (sts.select_image_versions ()," << endl
+ << "im," << endl
+ << "sts.select_image_bindings ());"
+ << "}";
+ }
+ else
+ {
+ os << "if (im.version != sts.select_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+ }
+
+ os << "pq.query.init_parameters ();"
+ << "st->execute ();";
+ post_query_ (c, false);
+
+ os << endl
+ << "return shared_ptr<result_impl> (" << endl
+ << "new (shared) " << db << "::" << result_type << " (" << endl
+ << "pq.query, st, sts, " << (versioned ? "&svm" : "0") << "));"
+ << "}";
+ }
+ }
+
+ // Generate function table registration for dynamic multi-database
+ // support.
+ //
+ if (multi_dynamic)
+ {
+ string fn (flat_name (type));
+ string dt ("access::object_traits_impl< " + type + ", id_common >");
+
+ os << "static const" << endl
+ << dt << "::" << endl
+ << "function_table_type function_table_" << fn << "_ ="
+ << "{";
+
+ // persist ()
+ //
+ os << "&" << traits << "::persist";
+
+ if (id != 0)
+ {
+ // find (id)
+ //
+ if (c.default_ctor ())
+ os << "," << endl
+ << "&" << traits << "::find";
+
+ // find (id, obj)
+ //
+ os << "," << endl
+ << "&" << traits << "::find";
+
+ // reload ()
+ //
+ os << "," << endl
+ << "&" << traits << "::reload";
+
+ // update ()
+ //
+ if (!readonly || poly)
+ os << "," << endl
+ << "&" << traits << "::update";
+
+ // erase ()
+ //
+ os << "," << endl
+ << "&" << traits << "::erase";
+
+ os << "," << endl
+ << "&" << traits << "::erase";
+
+ // Sections.
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ os << "," << endl
+ << "&" << traits << "::load";
+
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ os << "," << endl
+ << "&" << traits << "::update";
+ }
+
+ if (options.generate_query ())
+ {
+ if (!options.omit_unprepared ())
+ os << "," << endl
+ << "&" << traits << "::query";
+
+ os << "," << endl
+ << "&" << traits << "::erase_query";
+
+ if (options.generate_prepared ())
+ {
+ os << "," << endl
+ << "&" << traits << "::prepare_query";
+
+ os << "," << endl
+ << "&" << traits << "::execute_query";
+ }
+ }
+
+ os << "};";
+
+ os << "static const object_function_table_entry< " << type << ", " <<
+ "id_" << db << " >" << endl
+ << "function_table_entry_" << fn << "_ (" << endl
+ << "&function_table_" << fn << "_);"
+ << endl;
+ }
+}
+
+void relational::source::class_::
+traverse_view (type& c)
+{
+ view_query& vq (c.get<view_query> ("query"));
+
+ // Only process the view query if it is versioned since the query text
+ // (e.g., in the native view) must be structured. We also shouldn't try
+ // to optimize JOINs (since the objects are JOINed explicitly by the
+ // user), unless we are adding poly-base/derived JOINs.
+ //
+ bool versioned (context::versioned (c));
+ bool query_optimize (false);
+
+ string const& type (class_fq_name (c));
+ string traits ("access::view_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ size_t columns (column_count (c).total);
+
+ // Schema name as a string literal or empty.
+ //
+ string schema_name (options.schema_name ()[db]);
+ if (!schema_name.empty ())
+ schema_name = strlit (schema_name);
+
+ // Generate the from-list. Also collect relationships via which
+ // the objects are joined.
+ //
+ strings from;
+ view_relationship_map rel_map;
+
+ if (vq.kind == view_query::condition)
+ {
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ bool first (i == objs.begin ());
+ string l;
+
+ //
+ // Tables.
+ //
+
+ if (i->kind == view_object::table)
+ {
+ if (first)
+ {
+ l = "FROM ";
+ l += quote_id (i->tbl_name);
+
+ if (!i->alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (i->alias);
+
+ l += from_trailer (c);
+
+ from.push_back (l);
+ continue;
+ }
+
+ l = join_syntax (*i);
+ l += ' ';
+ l += quote_id (i->tbl_name);
+
+ if (!i->alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (i->alias);
+
+ if (i->join == view_object::cross) // No ON condition for CROSS JOIN.
+ {
+ from.push_back (l);
+ continue;
+ }
+
+ semantics::scope& scope (
+ dynamic_cast<semantics::scope&> (*unit.find (i->scope)));
+
+ expression e (
+ translate_expression (
+ c, i->cond, scope, i->loc, "table"));
+
+ if (e.kind != expression::literal)
+ {
+ error (i->loc) << "invalid join condition in db pragma " <<
+ "table" << endl;
+ throw operation_failed ();
+ }
+
+ l += " ON";
+
+ // Add the pragma location for easier error tracking.
+ //
+ from.push_back (l);
+ from.push_back ("// From " + location_string (i->loc, true));
+ from.push_back (e.value);
+ continue;
+ }
+
+ //
+ // Objects.
+ //
+ semantics::class_& o (*i->obj);
+
+ bool poly (polymorphic (o));
+ size_t poly_depth (poly ? polymorphic_depth (o) : 1);
+
+ string alias (i->alias);
+
+ // For polymorphic objects, alias is just a prefix.
+ //
+ if (poly && !alias.empty ())
+ alias += "_" + table_name (o).uname ();
+
+ // First object.
+ //
+ if (first)
+ {
+ l = "FROM ";
+ l += table_qname (o);
+
+ if (!alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (alias);
+
+ l += from_trailer (c);
+
+ from.push_back (l);
+
+ if (poly_depth != 1)
+ {
+ bool t (true); //@@ (im)perfect forwarding
+ size_t d (poly_depth - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (o, t, d, i->alias);
+ j->traverse (polymorphic_base (o));
+
+ from.insert (from.end (), j->begin (), j->end ());
+ query_optimize = query_optimize || !j->joins.empty ();
+ }
+ continue;
+ }
+
+ semantics::scope& scope (
+ dynamic_cast<semantics::scope&> (*unit.find (i->scope)));
+
+ expression e (i->join == view_object::cross
+ ? expression ("") // Dummy literal expression.
+ : translate_expression (
+ c, i->cond, scope, i->loc, "object"));
+
+ // Literal expression.
+ //
+ if (e.kind == expression::literal)
+ {
+ l = join_syntax (*i);
+ l += ' ';
+ l += table_qname (o);
+
+ if (!alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (alias);
+
+ if (i->join == view_object::cross) // No ON condition for CROSS JOIN.
+ {
+ from.push_back (l);
+ continue;
+ }
+
+ l += " ON";
+
+ // Add the pragma location for easier error tracking.
+ //
+ from.push_back (l);
+ from.push_back ("// From " + location_string (i->loc, true));
+ from.push_back (e.value);
+
+ if (poly_depth != 1)
+ {
+ bool t (true); //@@ (im)perfect forwarding
+ size_t d (poly_depth - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (o, t, d, i->alias);
+ j->traverse (polymorphic_base (o));
+
+ from.insert (from.end (), j->begin (), j->end ());
+ query_optimize = query_optimize || !j->joins.empty ();
+ }
+ continue;
+ }
+
+ // We have an object relationship (pointer) for which we need
+ // to come up with the corresponding JOIN condition. If this
+ // is a to-many relationship, then we first need to JOIN the
+ // container table. This code is similar to object_joins.
+ //
+ // Note that this cannot be CROSS JOIN; we've handled that case
+ // above.
+ //
+ using semantics::data_member;
+
+ data_member& m (*e.member_path.back ());
+ data_member_path* imp (inverse (m));
+
+ // Resolve the pointed-to object to view_object and do
+ // some sanity checks while at it.
+ //
+ semantics::class_* c (0);
+
+ if (container (m))
+ c = object_pointer (container_vt (m));
+ else
+ c = object_pointer (utype (m));
+
+ view_object* vo (0);
+
+ // Check if the pointed-to object has been previously associated
+ // with this view and is unambiguous. A pointer to ourselves is
+ // always assumed to point to this association.
+ //
+ if (&o == c)
+ vo = &*i;
+ else
+ {
+ bool ambig (false);
+
+ for (view_objects::iterator j (objs.begin ()); j != i; ++j)
+ {
+ if (j->obj != c)
+ continue;
+
+ if (vo == 0)
+ {
+ vo = &*j;
+ continue;
+ }
+
+ // If it is the first ambiguous object, issue the
+ // error.
+ //
+ if (!ambig)
+ {
+ error (i->loc) << "pointed-to object '" << class_name (*c) <<
+ "' is ambiguous" << endl;
+ info (i->loc) << "candidates are:" << endl;
+ info (vo->loc) << " '" << vo->name () << "'" << endl;
+ ambig = true;
+ }
+
+ info (j->loc) << " '" << j->name () << "'" << endl;
+ }
+
+ if (ambig)
+ {
+ info (i->loc) << "use the other side of the relationship " <<
+ "or full join condition clause in db pragma object to " <<
+ "resolve this ambiguity" << endl;
+ throw operation_failed ();
+ }
+
+ if (vo == 0)
+ {
+ error (i->loc) << "pointed-to object '" << class_name (*c) <<
+ "' specified in the join condition has not been " <<
+ "previously associated with this view" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ // JOIN relationship points to us:
+ // vo - us
+ // e.vo - other side
+ // e.member_path - in other side
+ //
+ // JOIN relationship points to other side:
+ // vo - other side
+ // e.vo - us
+ // e.member_path - in us
+ //
+ if (imp == 0)
+ rel_map.insert (make_pair (e.member_path, make_pair (e.vo, vo)));
+ else
+ rel_map.insert (make_pair (*imp, make_pair (vo, e.vo)));
+
+ // Left and right-hand side table names.
+ //
+ qname lt;
+ {
+ using semantics::class_;
+
+ class_& o (*e.vo->obj);
+ string const& a (e.vo->alias);
+
+ if (class_* root = polymorphic (o))
+ {
+ // If the object is polymorphic, then figure out which of the
+ // bases this member comes from and use the corresponding
+ // table.
+ //
+ class_* c (
+ &static_cast<class_&> (
+ e.member_path.front ()->scope ()));
+
+ // If this member's class is not polymorphic (root uses reuse
+ // inheritance), then use the root table.
+ //
+ if (!polymorphic (*c))
+ c = root;
+
+ qname const& t (table_name (*c));
+
+ if (a.empty ())
+ lt = t;
+ else
+ lt = qname (a + "_" + t.uname ());
+ }
+ else
+ lt = a.empty () ? table_name (o) : qname (a);
+ }
+
+ qname rt;
+ {
+ qname t (table_name (*vo->obj));
+ string const& a (vo->alias);
+ rt = a.empty ()
+ ? t
+ : qname (polymorphic (*vo->obj) ? a + "_" + t.uname () : a);
+ }
+
+ // First join the container table if necessary.
+ //
+ semantics::type* cont (container (imp != 0 ? *imp->back () : m));
+
+ string ct; // Container table.
+ if (cont != 0)
+ {
+ l = join_syntax (*i);
+ l += ' ';
+
+ // The same relationship can be used by multiple associated
+ // objects. So if the object that contains this container has
+ // an alias, then also construct one for the table that we
+ // are joining.
+ //
+ {
+ using semantics::class_;
+
+ qname t;
+
+ // In a polymorphic hierarchy the member can be in a base (see
+ // above).
+ //
+ if (class_* root = polymorphic (imp != 0 ? *vo->obj : *e.vo->obj))
+ {
+ class_* c;
+ if (imp == 0)
+ {
+ c = &static_cast<class_&> (e.member_path.front ()->scope ());
+
+ if (!polymorphic (*c))
+ c = root;
+
+ t = table_name (*c, e.member_path);
+ }
+ else
+ {
+ c = &static_cast<class_&> (imp->front ()->scope ());
+
+ if (!polymorphic (*c))
+ c = root;
+
+ t = table_name (*c, *imp);
+ }
+ }
+ else
+ t = imp != 0
+ ? table_name (*vo->obj, *imp)
+ : table_name (*e.vo->obj, e.member_path);
+
+ // The tricky part is to figure out which view_object, vo
+ // or e.vo we should use. We want to use the alias from the
+ // object that actually contains this container. The following
+ // might not make intuitive sense, but it has been verified
+ // with the truth table.
+ //
+ string const& a (imp != 0 ? vo->alias : e.vo->alias);
+
+ if (a.empty ())
+ {
+ ct = quote_id (t);
+ l += ct;
+ }
+ else
+ {
+ ct = quote_id (a + '_' + t.uname ());
+ l += quote_id (t);
+ l += (need_alias_as ? " AS " : " ") + ct;
+ }
+ }
+
+ l += " ON";
+ from.push_back (l);
+
+ // If we are the pointed-to object, then we have to turn
+ // things around. This is necessary to have the proper
+ // JOIN order. There seems to be a pattern there but it
+ // is not yet intuitively clear what it means.
+ //
+ instance<object_columns_list> c_cols; // Container columns.
+ instance<object_columns_list> o_cols; // Object columns.
+
+ qname* ot; // Object table (either lt or rt).
+
+ if (imp != 0)
+ {
+ semantics::data_member& imb (*imp->back ());
+
+ if (&o == c)
+ {
+ // container.value = pointer.id
+ //
+ data_member_path& id (*id_member (*e.vo->obj));
+
+ c_cols->traverse (imb, utype (id), "value", "value");
+ o_cols->traverse (id);
+ ot = &lt;
+ }
+ else
+ {
+ // container.id = pointed-to.id
+ //
+ data_member_path& id (*id_member (*vo->obj));
+
+ c_cols->traverse (imb, utype (id), "id", "object_id", vo->obj);
+ o_cols->traverse (id);
+ ot = &rt;
+ }
+ }
+ else
+ {
+ if (&o == c)
+ {
+ // container.id = pointer.id
+ //
+ data_member_path& id (*id_member (*e.vo->obj));
+
+ c_cols->traverse (
+ m, utype (id), "id", "object_id", e.vo->obj);
+ o_cols->traverse (id);
+ ot = &lt;
+ }
+ else
+ {
+ // container.value = pointed-to.id
+ //
+ data_member_path& id (*id_member (*vo->obj));
+
+ c_cols->traverse (m, utype (id), "value", "value");
+ o_cols->traverse (id);
+ ot = &rt;
+ }
+ }
+
+ for (object_columns_list::iterator b (c_cols->begin ()), i (b),
+ j (o_cols->begin ()); i != c_cols->end (); ++i, ++j)
+ {
+ l.clear ();
+
+ if (i != b)
+ l += "AND ";
+
+ l += ct;
+ l += '.';
+ l += quote_id (i->name);
+ l += '=';
+ l += quote_id (*ot);
+ l += '.';
+ l += quote_id (j->name);
+
+ from.push_back (strlit (l));
+ }
+ }
+
+ // If we have already joined the container with the desired
+ // join type, then use LEFT JOIN to join the object to the
+ // container. This is the right thing to do since an entry
+ // in the container can only point (either via id or value)
+ // to a single object.
+ //
+ l = (cont == 0 ? join_syntax (*i) : "LEFT JOIN");
+ l += ' ';
+
+ l += table_qname (o);
+
+ if (!alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (alias);
+
+ l += " ON";
+ from.push_back (l);
+
+ if (cont != 0)
+ {
+ instance<object_columns_list> c_cols; // Container columns.
+ instance<object_columns_list> o_cols; // Object columns.
+
+ qname* ot; // Object table (either lt or rt).
+
+ if (imp != 0)
+ {
+ semantics::data_member& imb (*imp->back ());
+
+ if (&o == c)
+ {
+ // container.id = pointed-to.id
+ //
+ data_member_path& id (*id_member (*vo->obj));
+
+ c_cols->traverse (imb, utype (id), "id", "object_id", vo->obj);
+ o_cols->traverse (id);
+ ot = &rt;
+ }
+ else
+ {
+ // container.value = pointer.id
+ //
+ data_member_path& id (*id_member (*e.vo->obj));
+
+ c_cols->traverse (imb, utype (id), "value", "value");
+ o_cols->traverse (id);
+ ot = &lt;
+ }
+ }
+ else
+ {
+ if (&o == c)
+ {
+ // container.value = pointed-to.id
+ //
+ data_member_path& id (*id_member (*vo->obj));
+
+ c_cols->traverse (m, utype (id), "value", "value");
+ o_cols->traverse (id);
+ ot = &rt;
+ }
+ else
+ {
+ // container.id = pointer.id
+ //
+ data_member_path& id (*id_member (*e.vo->obj));
+
+ c_cols->traverse (m, utype (id), "id", "object_id", e.vo->obj);
+ o_cols->traverse (id);
+ ot = &lt;
+ }
+ }
+
+ for (object_columns_list::iterator b (c_cols->begin ()), i (b),
+ j (o_cols->begin ()); i != c_cols->end (); ++i, ++j)
+ {
+ l.clear ();
+
+ if (i != b)
+ l += "AND ";
+
+ l += ct;
+ l += '.';
+ l += quote_id (i->name);
+ l += '=';
+ l += quote_id (*ot);
+ l += '.';
+ l += quote_id (j->name);
+
+ from.push_back (strlit (l));
+ }
+ }
+ else
+ {
+ instance<object_columns_list> l_cols;
+ instance<object_columns_list> r_cols;
+
+ if (imp != 0)
+ {
+ // our.id = pointed-to.pointer
+ //
+ l_cols->traverse (*id_member (*e.vo->obj));
+ r_cols->traverse (*imp->back (), column_prefix (*imp));
+ }
+ else
+ {
+ // our.pointer = pointed-to.id
+ //
+ l_cols->traverse (*e.member_path.back (),
+ column_prefix (e.member_path));
+ r_cols->traverse (*id_member (*vo->obj));
+ }
+
+ for (object_columns_list::iterator b (l_cols->begin ()), i (b),
+ j (r_cols->begin ()); i != l_cols->end (); ++i, ++j)
+ {
+ l.clear ();
+
+ if (i != b)
+ l += "AND ";
+
+ l += quote_id (lt);
+ l += '.';
+ l += quote_id (i->name);
+ l += '=';
+ l += quote_id (rt);
+ l += '.';
+ l += quote_id (j->name);
+
+ from.push_back (strlit (l));
+ }
+ }
+
+ if (poly_depth != 1)
+ {
+ bool t (true); //@@ (im)perfect forwarding
+ size_t d (poly_depth - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (o, t, d, i->alias);
+ j->traverse (polymorphic_base (o));
+
+ from.insert (from.end (), j->begin (), j->end ());
+ query_optimize = query_optimize || !j->joins.empty ();
+ }
+ } // End JOIN-generating for-loop.
+ }
+
+ // Check that pointed-to objects inside objects that we are loading
+ // have session support enabled (required to have a shared copy).
+ // Also see if we need to throw if there is no session.
+ //
+ bool need_session (false);
+ if (vq.kind == view_query::condition)
+ {
+ view_objects& objs (c.get<view_objects> ("objects"));
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ if (i->kind != view_object::object || i->ptr == 0)
+ continue; // Not an object or not loaded.
+
+ instance<view_object_check> t (*i, rel_map);
+ t->traverse (*i->obj);
+ need_session = need_session || t->session_;
+ }
+ }
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ view_extra (c);
+
+ // query_columns
+ //
+ if (c.get<size_t> ("object-count") != 0)
+ view_query_columns_type_->traverse (c);
+
+ //
+ // Functions.
+ //
+
+ // grow ()
+ //
+ if (generate_grow && columns != 0)
+ {
+ os << "bool " << traits << "::" << endl
+ << "grow (image_type& i," << endl
+ << truncated_vector << " t";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (t);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "bool grew (false);"
+ << endl;
+
+ index_ = 0;
+ names (c, grow_member_names_);
+
+ os << "return grew;"
+ << "}";
+ }
+
+ // bind (image_type)
+ //
+ if (columns != 0)
+ {
+ os << "void " << traits << "::" << endl
+ << "bind (" << bind_vector << " b," << endl
+ << "image_type& i";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);"
+ << endl;
+
+ os << "using namespace " << db << ";"
+ << endl
+ << db << "::statement_kind sk (statement_select);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl
+ << "std::size_t n (0);"
+ << endl;
+
+ names (c, bind_member_names_);
+
+ os << "}";
+ }
+
+ // init (view, image)
+ //
+ if (columns != 0)
+ {
+ os << "void " << traits << "::" << endl
+ << "init (view_type& o," << endl
+ << "const image_type& i," << endl
+ << "database* db";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl;
+
+ if (need_session)
+ os << "if (!" << options.session_type () << "::_has_cache ())" << endl
+ << "throw session_required ();"
+ << endl;
+
+ // Note: db must be not NULL in order to load pointers.
+ //
+ if (has_a (c, test_pointer))
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (*db));"
+ << endl;
+
+ names (c, init_view_pointer_member_pre_names_);
+ names (c, init_value_member_names_);
+ names (c, init_view_pointer_member_post_names_);
+
+ os << "}";
+ }
+
+ // query_statement()
+ //
+ if (vq.kind != view_query::runtime)
+ {
+ os << traits << "::query_base_type" << endl
+ << traits << "::" << endl
+ << "query_statement (const query_base_type& q)"
+ << "{";
+
+ if (vq.kind == view_query::complete_select ||
+ vq.kind == view_query::complete_execute)
+ {
+ os << "query_base_type r (" << endl;
+
+ bool ph (false);
+ bool pred (vq.kind == view_query::complete_select);
+
+ if (!vq.literal.empty ())
+ {
+ // See if we have the '(?)' placeholder.
+ //
+ // @@ Ideally we would need to make sure we don't match
+ // this inside strings and quoted identifier. So the
+ // proper way to handle this would be to tokenize the
+ // statement using sql_lexer, once it is complete enough.
+ //
+ string::size_type p (vq.literal.find ("(?)"));
+
+ if (p != string::npos)
+ {
+ ph = true;
+ // For the SELECT query we keep the parenthesis in (?) and
+ // also handle the case where the query expression is empty.
+ //
+ if (pred)
+ os << strlit (string (vq.literal, 0, p + 1)) << " +" << endl
+ << "(q.empty () ? query_base_type::true_expr : q) +" << endl
+ << strlit (string (vq.literal, p + 2));
+ else
+ {
+ os << strlit (string (vq.literal, 0, p)) << " + q";
+
+ p += 3;
+ if (p != vq.literal.size ())
+ os << " + " << strlit (string (vq.literal, p));
+ }
+ }
+ else
+ os << strlit (vq.literal);
+ }
+ else
+ {
+ semantics::scope& scope (
+ dynamic_cast<semantics::scope&> (*unit.find (vq.scope)));
+
+ // Output the pragma location for easier error tracking.
+ //
+ os << "// From " << location_string (vq.loc, true) << endl
+ << translate_expression (
+ c, vq.expr, scope, vq.loc, "query", &ph, pred).value;
+ }
+
+ os << ");";
+
+ // If there was no placeholder, add the query condition
+ // at the end.
+ //
+ if (!ph)
+ os << endl
+ << "if (!q.empty ())"
+ << "{"
+ << "r += " << strlit (versioned ? "\n" : " ") << ";"
+ << "r += q.clause_prefix ();"
+ << "r += q;"
+ << "}";
+ }
+ else // vq.kind == view_query::condition
+ {
+ // Use the from-list generated above.
+ //
+ statement_columns sc;
+ {
+ instance<view_columns> t (sc, from, rel_map);
+ t->traverse (c);
+ process_statement_columns (
+ sc, statement_select, versioned || query_optimize);
+ }
+
+ string sep (versioned || query_optimize ? "\n" : " ");
+
+ os << "query_base_type r (" << endl
+ << strlit ((vq.distinct ? "SELECT DISTINCT" : "SELECT") + sep);
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << endl
+ << strlit (c + (++i != e ? "," : "") + sep);
+ }
+
+ os << ");"
+ << endl;
+
+ // It is much easier to add the separator at the beginning of the
+ // next line since the JOIN condition may not be a string literal.
+ //
+ for (strings::const_iterator i (from.begin ()); i != from.end (); ++i)
+ {
+ if (i->compare (0, 5, "FROM ") == 0)
+ os << "r += " << strlit (*i) << ";";
+ else if (i->compare (0, 3, "// ") == 0)
+ os << *i << endl;
+ else
+ {
+ // See if this is a JOIN. The exact spelling is database-dependent,
+ // but we know there is the JOIN word in there somewhere and before
+ // it we should only have keywords and spaces.
+ //
+ size_t p (i->find ("JOIN "));
+ if (p != string::npos)
+ {
+ // Make sure before it we only have A-Z and spaces.
+ //
+ for (char c; p != 0; --p)
+ {
+ c = (*i)[p - 1];
+ if ((c < 'A' || c > 'Z') && c != ' ')
+ break;
+ }
+
+ if (p == 0)
+ os << endl
+ << "r += " << strlit (sep + *i) << ";";
+ }
+
+ // Something else, assume already a string literal.
+ //
+ if (p != 0)
+ os << "r += " << *i << ";";
+ }
+ }
+
+ // Generate the query condition.
+ //
+ if (!vq.literal.empty () || !vq.expr.empty ())
+ {
+ os << endl
+ << "query_base_type c (" << endl;
+
+ bool ph (false);
+
+ if (!vq.literal.empty ())
+ {
+ // See if we have the '(?)' placeholder.
+ //
+ // @@ Ideally we would need to make sure we don't match
+ // this inside strings and quoted identifier. So the
+ // proper way to handle this would be to tokenize the
+ // statement using sql_lexer, once it is complete enough.
+ //
+ string::size_type p (vq.literal.find ("(?)"));
+
+ if (p != string::npos)
+ {
+ ph = true;
+ os << strlit (string (vq.literal, 0, p + 1))<< " +" << endl
+ << "(q.empty () ? query_base_type::true_expr : q) +" << endl
+ << strlit (string (vq.literal, p + 2));
+ }
+ else
+ os << strlit (vq.literal);
+
+ os << ");";
+ }
+ else
+ {
+ semantics::scope& scope (
+ dynamic_cast<semantics::scope&> (*unit.find (vq.scope)));
+
+ // Output the pragma location for easier error tracking.
+ //
+ os << "// From " << location_string (vq.loc, true) << endl
+ << translate_expression (
+ c, vq.expr, scope, vq.loc, "query", &ph).value;
+
+ os << ");";
+
+ // Optimize the query if it had a placeholder. This gets
+ // rid of useless clauses like WHERE TRUE.
+ //
+ if (ph)
+ os << endl
+ << "c.optimize ();";
+ }
+
+ if (!ph)
+ os << endl
+ << "c += q;";
+
+ os << endl
+ << "if (!c.empty ())"
+ << "{"
+ << "r += " << strlit (sep) << ";"
+ << "r += c.clause_prefix ();"
+ << "r += c;"
+ << "}";
+
+ string st (select_trailer (c));
+ if (!st.empty ())
+ {
+ os << "r += " << strlit (sep) << ";"
+ << "r += " << strlit (st) << ";";
+ }
+ }
+ else
+ {
+ os << endl
+ << "if (!q.empty ())"
+ << "{"
+ << "r += " << strlit (sep) << ";"
+ << "r += q.clause_prefix ();"
+ << "r += q;"
+ << "}";
+ }
+ }
+
+ os << "return r;"
+ << "}";
+ }
+
+ // Unprepared.
+ //
+ if (!options.omit_unprepared ())
+ {
+ os << "result< " << traits << "::view_type >" << endl
+ << traits << "::" << endl
+ << "query (database& db, const query_base_type& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl;
+
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_view<view_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl
+ << "image_type& im (sts.image ());"
+ << "binding& imb (sts.image_binding ());"
+ << endl
+ << "if (im.version != sts.image_version () || imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im" << (versioned ? ", svm" : "") << ");"
+ << "sts.image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+
+ if (vq.kind == view_query::runtime)
+ os << "const query_base_type& qs (q);";
+ else
+ os << "const query_base_type& qs (query_statement (q));";
+
+ os << "qs.init_parameters ();"
+ << "shared_ptr<select_statement> st (" << endl
+ << "new (shared) select_statement (" << endl;
+ view_query_statement_ctor_args (
+ c, "qs", versioned || query_optimize, false);
+ os << "));" << endl
+ << "st->execute ();";
+
+ post_query_ (c, true);
+
+ os << endl
+ << "shared_ptr< odb::view_result_impl<view_type> > r (" << endl
+ << "new (shared) " << db << "::view_result_impl<view_type> (" << endl
+ << "qs, st, sts, " << (versioned ? "&svm" : "0") << "));"
+ << endl
+ << "return result<view_type> (r);"
+ << "}";
+
+ // query(odb::query_base)
+ //
+ if (multi_dynamic)
+ os << "result< " << traits << "::view_type >" << endl
+ << traits << "::" << endl
+ << "query (database& db, const odb::query_base& q)"
+ << "{"
+ << "return query (db, query_base_type (q));"
+ << "}";
+ }
+
+ // Prepared. Very similar to unprepared but has some annoying variations
+ // that make it difficult to factor out something common.
+ //
+ if (options.generate_prepared ())
+ {
+ // prepare_query
+ //
+ os << "odb::details::shared_ptr<prepared_query_impl>" << endl
+ << traits << "::" << endl
+ << "prepare_query (connection& c, const char* n, " <<
+ "const query_base_type& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl;
+
+ os << db << "::connection& conn (" << endl
+ << "static_cast<" << db << "::connection&> (c));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_view<view_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ // Rebind the image if necessary.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.image_binding ());"
+ << endl
+ << "if (im.version != sts.image_version () || imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im" << (versioned ? ", svm" : "") << ");"
+ << "sts.image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+
+ os << "shared_ptr<" << db << "::prepared_query_impl> r (" << endl
+ << "new (shared) " << db << "::prepared_query_impl (conn));"
+ << "r->name = n;"
+ << "r->execute = &execute_query;";
+
+ if (vq.kind == view_query::runtime)
+ os << "r->query = q;";
+ else
+ os << "r->query = query_statement (q);";
+
+ os << "r->stmt.reset (" << endl
+ << "new (shared) select_statement (" << endl;
+ view_query_statement_ctor_args (
+ c, "r->query", versioned || query_optimize, true);
+ os << "));"
+ << endl
+ << "return r;"
+ << "}";
+
+ // prepare_query(odb::query_base)
+ //
+ if (multi_dynamic)
+ os << "odb::details::shared_ptr<prepared_query_impl>" << endl
+ << traits << "::" << endl
+ << "prepare_query (connection& c, const char* n, " <<
+ "const odb::query_base& q)"
+ << "{"
+ << "return prepare_query (c, n, query_base_type (q));"
+ << "}";
+
+ // execute_query
+ //
+ os << "odb::details::shared_ptr<result_impl>" << endl
+ << traits << "::" << endl
+ << "execute_query (prepared_query_impl& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl
+ << db << "::prepared_query_impl& pq (" << endl
+ << "static_cast<" << db << "::prepared_query_impl&> (q));"
+ << "shared_ptr<select_statement> st (" << endl
+ << "odb::details::inc_ref (" << endl
+ << "static_cast<select_statement*> (pq.stmt.get ())));"
+ << endl;
+
+ os << db << "::transaction& tr (" << db << "::transaction::current ());"
+ << "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 (q.verify_connection (tr));"
+ << endl
+ << "statements_type& sts (" << endl
+ << "st->connection ().statement_cache ().find_view<view_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ // Rebind the image if necessary.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.image_binding ());"
+ << endl
+ << "if (im.version != sts.image_version () || imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im" << (versioned ? ", svm" : "") << ");"
+ << "sts.image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+
+ os << "pq.query.init_parameters ();"
+ << "st->execute ();";
+
+ post_query_ (c, false);
+
+ os << endl
+ << "return shared_ptr<result_impl> (" << endl
+ << "new (shared) " << db << "::view_result_impl<view_type> (" << endl
+ << "pq.query, st, sts, " << (versioned ? "&svm" : "0") << "));"
+ << "}";
+ }
+
+ // Generate function table registration for dynamic multi-database
+ // support.
+ //
+ if (multi_dynamic)
+ {
+ string fn (flat_name (type));
+ string dt ("access::view_traits_impl< " + type + ", id_common >");
+
+ os << "static const" << endl
+ << dt << "::" << endl
+ << "function_table_type function_table_" << fn << "_ ="
+ << "{";
+
+ if (!options.omit_unprepared ())
+ os << "&" << traits << "::query";
+
+ if (options.generate_prepared ())
+ {
+ if (!options.omit_unprepared ())
+ os << "," << endl;
+
+ os << "&" << traits << "::prepare_query" << "," << endl
+ << "&" << traits << "::execute_query";
+ }
+
+ os << "};";
+
+ os << "static const view_function_table_entry< " << type << ", " <<
+ "id_" << db << " >" << endl
+ << "function_table_entry_" << fn << "_ (" << endl
+ << "&function_table_" << fn << "_);"
+ << endl;
+ }
+}
+
+namespace relational
+{
+ namespace source
+ {
+ static inline void
+ add_space (string& s)
+ {
+ string::size_type n (s.size ());
+ if (n != 0 && s[n - 1] != ' ')
+ s += ' ';
+ }
+
+ static string
+ translate_name_trailer (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ cpp_ttype& ptt)
+ {
+ string r;
+
+ for (; tt != CPP_EOF; ptt = tt, tt = l.next (tl, &tn))
+ {
+ bool done (false);
+
+ switch (tt)
+ {
+ case CPP_SCOPE:
+ case CPP_DOT:
+ {
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ default:
+ {
+ // Handle CPP_KEYWORD here to avoid a warning (it is not
+ // part of the cpp_ttype enumeration).
+ //
+ if (tt == CPP_NAME || tt == CPP_KEYWORD)
+ {
+ // For names like 'foo::template bar'.
+ //
+ if (ptt == CPP_NAME || ptt == CPP_KEYWORD)
+ r += ' ';
+
+ r += tl;
+ }
+ else
+ done = true;
+
+ break;
+ }
+ }
+
+ if (done)
+ break;
+ }
+
+ return r;
+ }
+
+ static class_::expression
+ translate_name (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ cpp_ttype& ptt,
+ semantics::scope& start_scope,
+ location_t loc,
+ string const& prag,
+ bool check_ptr,
+ view_alias_map const& amap,
+ view_object_map const& omap)
+ {
+ using semantics::scope;
+ using semantics::data_member;
+ typedef class_::expression expression;
+
+ bool multi_obj ((amap.size () + omap.size ()) > 1);
+
+ bool fail (false);
+ string name;
+ string r ("query_columns");
+ context& ctx (context::current ());
+
+ // This code is quite similar to view_data_members in the type
+ // processor.
+ //
+ try
+ {
+ data_member* m (0);
+ view_object* vo (0);
+
+ // Check if this is an alias.
+ //
+ if (tt == CPP_NAME)
+ {
+ view_alias_map::const_iterator i (amap.find (tl));
+
+ if (i != amap.end ())
+ {
+ if (multi_obj)
+ {
+ r += "::";
+ r += i->first;
+ }
+
+ vo = i->second;
+ fail = true; // This must be a data member.
+
+ // Skip '::'.
+ //
+ ptt = tt;
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_SCOPE)
+ {
+ error (loc) << "member name expected after an alias in db " <<
+ "pragma " << prag << endl;
+ throw operation_failed ();
+ }
+
+ ptt = tt;
+ if (l.next (tl, &tn) != CPP_NAME)
+ throw lookup::invalid_name ();
+
+ m = &vo->obj->lookup<data_member> (tl, scope::include_hidden);
+
+ tt = l.next (tl, &tn);
+ }
+ }
+
+ // If it is not an alias, do the normal lookup.
+ //
+ if (vo == 0)
+ {
+ // Also get the object type. We need to do it so that
+ // we can get the correct (derived) object name (the
+ // member itself can come from a base class).
+ //
+ scope* s;
+ cpp_ttype ptt; // Not used.
+ m = &lookup::resolve_scoped_name<data_member> (
+ l, tt, tl, tn, ptt,
+ start_scope,
+ name,
+ false,
+ &s);
+
+ view_object_map::const_iterator i (
+ omap.find (dynamic_cast<semantics::class_*> (s)));
+
+ if (i == omap.end ())
+ {
+ // Not an object associated with this view. Assume it
+ // is some other valid name.
+ //
+ return expression (
+ name + translate_name_trailer (l, tt, tl, tn, ptt));
+ }
+
+ vo = i->second;
+
+ if (multi_obj)
+ {
+ r += "::";
+ r += context::class_name (*vo->obj);
+ }
+ }
+
+ expression e (vo);
+ r += "::";
+ r += ctx.public_name (*m);
+
+ // Assemble the member path if we may need to return a pointer
+ // expression.
+ //
+ if (check_ptr)
+ e.member_path.push_back (m);
+
+ fail = true; // Now we definitely fail if anything goes wrong.
+
+ // Finally, resolve nested members if any.
+ //
+ for (; tt == CPP_DOT; ptt = tt, tt = l.next (tl, &tn))
+ {
+ // Check if this member is actually of a composite value type.
+ // This is to handle expressions like "object::member.is_null ()"
+ // correctly. The remaining issue here is that in the future
+ // is_null()/is_not_null() will be valid for composite values
+ // as well.
+ //
+ semantics::class_* comp (
+ context::composite_wrapper (context::utype (*m)));
+ if (comp == 0)
+ break;
+
+ ptt = tt;
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME)
+ {
+ error (loc) << "name expected after '.' in db pragma " <<
+ prag << endl;
+ throw operation_failed ();
+ }
+
+ m = &comp->lookup<data_member> (tl, scope::include_hidden);
+
+ r += '.';
+ r += ctx.public_name (*m);
+
+ if (check_ptr)
+ e.member_path.push_back (m);
+ }
+
+ // If requested, check if this member is a pointer. We only do this
+ // if there is nothing after this name.
+ //
+ if (check_ptr && tt == CPP_EOF)
+ {
+ using semantics::type;
+
+ type* t;
+
+ if (context::container (*m))
+ t = &context::container_vt (*m);
+ else
+ t = &context::utype (*m);
+
+ if (context::object_pointer (*t))
+ return e;
+ }
+
+ // Read the remainder of the expression (e.g., '.is_null ()') if
+ // the member is not composite and we bailed out from the above
+ // loop.
+ //
+ if (tt == CPP_DOT)
+ r += translate_name_trailer (l, tt, tl, tn, ptt);
+
+ return expression (r);
+ }
+ catch (lookup::invalid_name const&)
+ {
+ if (!fail)
+ return expression (
+ name + translate_name_trailer (l, tt, tl, tn, ptt));
+
+ error (loc) << "invalid name in db pragma " << prag << endl;
+ throw operation_failed ();
+ }
+ catch (semantics::unresolved const& e)
+ {
+ if (!fail)
+ return expression (
+ name + translate_name_trailer (l, tt, tl, tn, ptt));
+
+ if (e.type_mismatch)
+ error (loc) << "name '" << e.name << "' in db pragma " << prag <<
+ " does not refer to a data member" << endl;
+ else
+ error (loc) << "unable to resolve data member '" << e.name <<
+ "' specified with db pragma " << prag << endl;
+
+ throw operation_failed ();
+ }
+ catch (semantics::ambiguous const& e)
+ {
+ error (loc) << "data member name '" << e.first.name () << "' " <<
+ "specified with db pragma " << prag << " is ambiguous" << endl;
+
+ info (e.first.named ().location ()) << "could resolve to this " <<
+ "data member" << endl;
+
+ info (e.second.named ().location ()) << "or could resolve to this " <<
+ "data member" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ class_::expression class_::
+ translate_expression (type& c,
+ cxx_tokens const& ts,
+ semantics::scope& scope,
+ location_t loc,
+ string const& prag,
+ bool* placeholder,
+ bool predicate)
+ {
+ // This code is similar to translate() from context.cxx.
+ //
+
+ // The overall idea is as folows: read in tokens and add them
+ // to the string. If a token starts a name, try to resolve it
+ // to an object member (taking into account aliases). If this
+ // was successful, translate it to the query column reference.
+ // Otherwise, output it as is.
+ //
+ // If the placeholder argument is not NULL, then we need to
+ // detect the special '(?)' token sequence and replace it
+ // with the query variable ('q').
+ //
+ expression e ("");
+ string& r (e.value);
+
+ view_alias_map const& amap (c.get<view_alias_map> ("alias-map"));
+ view_object_map const& omap (c.get<view_object_map> ("object-map"));
+
+ cxx_tokens_lexer l;
+ l.start (ts);
+
+ tree tn;
+ string tl;
+ for (cpp_ttype tt (l.next (tl, &tn)), ptt (CPP_EOF); tt != CPP_EOF;)
+ {
+ // Try to format the expression to resemble the style of the
+ // generated code.
+ //
+ switch (tt)
+ {
+ case CPP_NOT:
+ {
+ add_space (r);
+ r += '!';
+ break;
+ }
+ case CPP_COMMA:
+ {
+ r += ", ";
+ break;
+ }
+ case CPP_OPEN_PAREN:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD)
+ add_space (r);
+
+ r += '(';
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ r += ')';
+ break;
+ }
+ case CPP_OPEN_SQUARE:
+ {
+ r += '[';
+ break;
+ }
+ case CPP_CLOSE_SQUARE:
+ {
+ r += ']';
+ break;
+ }
+ case CPP_OPEN_BRACE:
+ {
+ add_space (r);
+ r += "{ ";
+ break;
+ }
+ case CPP_CLOSE_BRACE:
+ {
+ add_space (r);
+ r += '}';
+ break;
+ }
+ case CPP_SEMICOLON:
+ {
+ r += ';';
+ break;
+ }
+ case CPP_ELLIPSIS:
+ {
+ add_space (r);
+ r += "...";
+ break;
+ }
+ case CPP_PLUS:
+ case CPP_MINUS:
+ {
+ bool unary (ptt != CPP_NAME &&
+ ptt != CPP_SCOPE &&
+ ptt != CPP_NUMBER &&
+ ptt != CPP_STRING &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_PLUS_PLUS &&
+ ptt != CPP_MINUS_MINUS);
+
+ if (!unary)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+
+ if (!unary)
+ r += ' ';
+ break;
+ }
+ case CPP_PLUS_PLUS:
+ case CPP_MINUS_MINUS:
+ {
+ if (ptt != CPP_NAME &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_CLOSE_SQUARE)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_DEREF:
+ case CPP_DEREF_STAR:
+ case CPP_DOT:
+ case CPP_DOT_STAR:
+ {
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_STRING:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += strlit (tl);
+ break;
+ }
+ case CPP_NUMBER:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ break;
+ }
+ case CPP_SCOPE:
+ case CPP_NAME:
+ {
+ // Start of a name.
+ //
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ // Check if this is a pointer expression.
+ //
+ // If r is not empty, then it means this is not just the
+ // name. If placeholder is not 0, then we are translating
+ // a query expression, not a join condition.
+ //
+ expression e (
+ translate_name (
+ l, tt, tl, tn, ptt,
+ scope, loc, prag,
+ r.empty () && placeholder == 0, amap, omap));
+
+ if (e.kind == expression::literal)
+ r += e.value;
+ else
+ return e;
+
+ continue; // We have already extracted the next token.
+ }
+ case CPP_QUERY:
+ {
+ if (placeholder != 0 && !*placeholder)
+ {
+ if (ptt == CPP_OPEN_PAREN)
+ {
+ // Get the next token and see if it is ')'.
+ //
+ ptt = tt;
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_CLOSE_PAREN)
+ {
+ *placeholder = true;
+
+ // Predicate is true if this is a SELECT statement clause.
+ // Otherwise it is a stored procedure parameters.
+ //
+ if (predicate)
+ r += "q.empty () ? query_base_type::true_expr : q";
+ else
+ {
+ r.resize (r.size () - 1); // Remove opening paren.
+ r += "q";
+ break; // Skip the closing paren as well.
+ }
+ }
+ else
+ {
+ // The same as in the default case.
+ //
+ add_space (r);
+ r += "? ";
+ }
+ continue; // We have already gotten the next token.
+ }
+ }
+ }
+ // Fall through.
+ default:
+ {
+ // Handle CPP_KEYWORD here to avoid a warning (it is not
+ // part of the cpp_ttype enumeration).
+ //
+ if (tt == CPP_KEYWORD)
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ }
+ else
+ {
+ // All the other operators.
+ //
+ add_space (r);
+ r += cxx_lexer::token_spelling[tt];
+ r += ' ';
+ }
+ break;
+ }
+ }
+
+ //
+ // Watch out for the continue statements above if you add any
+ // logic here.
+ //
+
+ ptt = tt;
+ tt = l.next (tl, &tn);
+ }
+
+ return e;
+ }
+
+ void
+ generate ()
+ {
+ context ctx;
+ ostream& os (ctx.os);
+
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ instance<class_> c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ instance<include> i;
+ i->generate ();
+
+ os << "namespace odb"
+ << "{";
+
+ unit.dispatch (ctx.unit);
+
+ os << "}";
+ }
+ }
+}
diff --git a/odb/odb/relational/source.hxx b/odb/odb/relational/source.hxx
new file mode 100644
index 0000000..ba6b2be
--- /dev/null
+++ b/odb/odb/relational/source.hxx
@@ -0,0 +1,7158 @@
+// file : odb/relational/source.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_SOURCE_HXX
+#define ODB_RELATIONAL_SOURCE_HXX
+
+#include <map>
+#include <set>
+#include <list>
+#include <vector>
+#include <sstream>
+
+#include <odb/diagnostics.hxx>
+
+#include <odb/relational/context.hxx>
+#include <odb/relational/common.hxx>
+#include <odb/relational/schema.hxx>
+
+namespace relational
+{
+ namespace source
+ {
+ // Column literal in a statement (e.g., in select-list, etc).
+ //
+ struct statement_column
+ {
+ statement_column (): member (0) {}
+ statement_column (std::string const& tbl,
+ std::string const& col,
+ std::string const& t,
+ semantics::data_member& m,
+ std::string const& kp = "")
+ : table (tbl), column (col), type (t), member (&m), key_prefix (kp)
+ {
+ }
+
+ std::string table; // Schema-qualifed and quoted table name.
+ std::string column; // Table-qualifed and quoted column expr.
+ std::string type; // Column SQL type.
+ semantics::data_member* member;
+ std::string key_prefix;
+ };
+
+ typedef std::list<statement_column> statement_columns;
+
+ // Query parameter generator. A new instance is created for each
+ // query, so the customized version can have a counter to implement,
+ // for example, numbered parameters (e.g., $1, $2, etc). The auto_id()
+ // function is called instead of next() for the automatically-assigned
+ // object id member when generating the persist statement. If empty
+ // string is returned, then parameter is ignored.
+ //
+ struct query_parameters: virtual context
+ {
+ typedef query_parameters base;
+
+ query_parameters (statement_kind sk, qname const& table)
+ : sk_ (sk), table_ (table) {}
+
+ virtual string
+ next (semantics::data_member&,
+ const std::string& /*column*/, // Table qualified and quoted.
+ const std::string& /*sqlt*/)
+ {
+ return "?";
+ }
+
+ virtual string
+ auto_id (semantics::data_member& m,
+ const std::string& column,
+ const std::string& sqlt)
+ {
+ return next (m, column, sqlt);
+ }
+
+ string
+ next (const object_columns_list::column& c)
+ {
+ return next (*c.member, quote_id (c.name), c.type);
+ }
+
+ string
+ next (const statement_column& c)
+ {
+ return next (*c.member, c.column, c.type);
+ }
+
+ protected:
+ statement_kind sk_;
+ qname table_;
+ };
+
+ struct object_columns: object_columns_base, virtual context
+ {
+ typedef object_columns base;
+
+ // If provided, used to resolve table/alias names for inverse
+ // pointed-to and base objects. Returns qualified name.
+ //
+ struct table_name_resolver
+ {
+ virtual string
+ resolve_pointer (semantics::data_member&) const = 0;
+
+ virtual string
+ resolve_base (semantics::class_&) const = 0;
+ };
+
+ object_columns (statement_kind sk,
+ statement_columns& sc,
+ query_parameters* param = 0,
+ object_section* section = 0)
+ : object_columns_base (true, true, section),
+ sk_ (sk),
+ ro_ (true),
+ sc_ (sc),
+ param_ (param),
+ table_name_resolver_ (0),
+ depth_ (1)
+ {
+ }
+
+ object_columns (statement_kind sk,
+ bool ignore_ro,
+ statement_columns& sc,
+ query_parameters* param)
+ : object_columns_base (true, true, 0),
+ sk_ (sk),
+ ro_ (ignore_ro),
+ sc_ (sc),
+ param_ (param),
+ table_name_resolver_ (0),
+ depth_ (1)
+ {
+ }
+
+ object_columns (std::string const& table_qname,
+ statement_kind sk,
+ statement_columns& sc,
+ size_t depth = 1,
+ object_section* section = 0,
+ table_name_resolver* tnr = 0)
+ : object_columns_base (true, true, section),
+ sk_ (sk),
+ ro_ (true),
+ sc_ (sc),
+ param_ (0),
+ table_name_ (table_qname),
+ table_name_resolver_ (tnr),
+ depth_ (depth)
+ {
+ }
+
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section for
+ // SELECT statements. Also include optimistic version into
+ // section's SELECT and UPDATE statements.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ (sk_ == statement_select &&
+ *section_ == main_section &&
+ !s.separate_load ()) ||
+ (version (mp) &&
+ (sk_ == statement_update || sk_ == statement_select));
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ // If we are generating a select statement and this is a derived
+ // type in a polymorphic hierarchy, then we need to include base
+ // columns, but do it in reverse order as well as switch the table
+ // name (base columns come from different tables).
+ //
+ semantics::class_* poly_root (polymorphic (c));
+ if (poly_root != 0 && poly_root != &c)
+ {
+ names (c);
+
+ if (sk_ == statement_select && --depth_ != 0)
+ {
+ semantics::class_& b (polymorphic_base (c));
+
+ table_name_ = table_name_resolver_ != 0
+ ? table_name_resolver_->resolve_base (b)
+ : table_qname (b);
+
+ inherits (c);
+ }
+ }
+ else
+ object_columns_base::traverse_object (c);
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore polymorphic id references for select statements.
+ //
+ if (sk_ == statement_select && m.count ("polymorphic-ref"))
+ return;
+
+ data_member_path* imp (inverse (m, key_prefix_));
+
+ // Ignore certain columns depending on what kind of statement we are
+ // generating. Columns corresponding to the inverse members are
+ // only present in the select statements.
+ //
+ if (imp != 0 && sk_ != statement_select)
+ return;
+
+ // Inverse object pointers come from a joined table.
+ //
+ if (imp != 0)
+ {
+ bool poly (polymorphic (c) != 0);
+ semantics::data_member& imf (*imp->front ());
+ semantics::data_member& imb (*imp->back ());
+
+ // In a polymorphic hierarchy the inverse member can be in
+ // the base class, in which case we should use that table.
+ //
+ semantics::class_& imc (
+ poly ? dynamic_cast<semantics::class_&> (imf.scope ()) : c);
+
+ data_member_path& id (*id_member (imc));
+ semantics::type& idt (utype (id));
+
+ if (container (imb))
+ {
+ // This container is a direct member of the class so the table
+ // prefix is just the class table name. We don't assign join
+ // aliases for container tables so use the actual table name.
+ // Note that the if(!table_name_.empty ()) test may look wrong
+ // at first but it is not; if table_name_ is empty then we are
+ // generating a container table where we don't qualify columns
+ // with tables.
+ //
+ string table;
+
+ if (!table_name_.empty ())
+ {
+ if (table_name_resolver_ != 0)
+ table = table_name_resolver_->resolve_pointer (m);
+ else
+ table = table_qname (imc, *imp);
+ }
+
+ instance<object_columns> oc (table, sk_, sc_);
+ oc->traverse (imb, idt, "id", "object_id", &imc);
+ }
+ else
+ {
+ // Use the join alias instead of the actual table name unless we
+ // are handling a container. Generally, we want the join alias
+ // to be based on the column name. This is straightforward for
+ // single-column references. In case of a composite id, we will
+ // need to use the column prefix which is based on the data
+ // member name, unless overridden by the user. In the latter
+ // case the prefix can be empty, in which case we will just
+ // fall back on the member's public name. Note that the
+ // if(!table_name_.empty ()) test may look wrong at first but
+ // it is not; if table_name_ is empty then we are generating a
+ // container table where we don't qualify columns with tables.
+ //
+ string alias;
+
+ if (!table_name_.empty ())
+ {
+ if (table_name_resolver_ != 0)
+ alias = table_name_resolver_->resolve_pointer (m);
+ else
+ {
+ string n;
+
+ if (composite_wrapper (idt))
+ {
+ n = column_prefix (m, key_prefix_, default_name_).prefix;
+
+ if (n.empty ())
+ n = public_name_db (m);
+ else if (n[n.size () - 1] == '_')
+ n.resize (n.size () - 1); // Remove trailing underscore.
+ }
+ else
+ {
+ bool dummy;
+ n = column_name (m, key_prefix_, default_name_, dummy);
+ }
+
+ alias = column_prefix_.prefix + n;
+
+ if (poly)
+ {
+ qname const& table (table_name (imc));
+ alias = quote_id (alias + "_" + table.uname ());
+ }
+ else
+ alias = quote_id (alias);
+ }
+ }
+
+ instance<object_columns> oc (alias, sk_, sc_);
+ oc->traverse (id);
+ }
+ }
+ else
+ object_columns_base::traverse_pointer (m, c);
+ }
+
+ virtual bool
+ traverse_column (semantics::data_member& m, string const& name, bool)
+ {
+ // Ignore certain columns depending on what kind statement we are
+ // generating. Id and readonly columns are not present in the update
+ // statements.
+ //
+ if ((id () || readonly (member_path_, member_scope_)) &&
+ sk_ == statement_update && ro_)
+ return false;
+
+ return column (m, table_name_, quote_id (name));
+ }
+
+ virtual bool
+ column (semantics::data_member& m,
+ string const& table,
+ string const& column)
+ {
+ string r;
+
+ if (!table.empty ())
+ {
+ r += table; // Already quoted.
+ r += '.';
+ }
+
+ r += column; // Already quoted.
+
+ string const& sqlt (column_type ());
+
+ // Version column (optimistic concurrency) requires special
+ // handling in the UPDATE statement.
+ //
+ //
+ if (sk_ == statement_update && version (m))
+ {
+ r += "=" + r + "+1";
+ }
+ else if (param_ != 0)
+ {
+ r += '=';
+ r += convert_to (param_->next (m, column, sqlt), sqlt, m);
+ }
+ else if (sk_ == statement_select)
+ r = convert_from (r, sqlt, m);
+
+ sc_.push_back (statement_column (table, r, sqlt, m, key_prefix_));
+ return true;
+ }
+
+ protected:
+ statement_kind sk_;
+ bool ro_;
+ statement_columns& sc_;
+ query_parameters* param_;
+ string table_name_;
+ table_name_resolver* table_name_resolver_;
+ size_t depth_;
+ };
+
+ struct view_columns: object_columns_base,
+ object_columns::table_name_resolver,
+ virtual context
+ {
+ typedef view_columns base;
+
+ view_columns (statement_columns& sc,
+ strings& from,
+ const view_relationship_map& rm)
+ : sc_ (sc), from_ (from), rel_map_ (rm), in_composite_ (false) {}
+
+ // Implementation of table_name_resolver for object_columns.
+ //
+ virtual string
+ resolve_pointer (semantics::data_member& m) const
+ {
+ view_object& us (*ptr_->get<view_object*> ("view-object"));
+
+ data_member_path& imp (*inverse (m));
+ semantics::data_member& imf (*imp.front ());
+ semantics::data_member& imb (*imp.back ());
+
+ using semantics::class_;
+ typedef view_relationship_map::const_iterator iterator;
+
+ std::pair<iterator, iterator> r (rel_map_.equal_range (imp));
+
+ for (; r.first != r.second; ++r.first)
+ {
+ if (r.first->second.second != &us) // Not our associated.
+ continue;
+
+ view_object& vo (*r.first->second.first); // First because inverse.
+
+ // Derive the table name the same way as the JOIN code.
+ //
+ class_* c (vo.obj);
+ if (class_* root = polymorphic (*c))
+ {
+ // Can be in base.
+ //
+ c = &static_cast<class_&> (imf.scope ());
+
+ if (!polymorphic (*c))
+ c = root;
+ }
+
+ string const& a (vo.alias);
+
+ if (container (imb))
+ {
+ // If this is a container, then object_columns will use the
+ // column from the container table, not from the object table
+ // (which, strictly speaking, might not have been JOIN'ed).
+ //
+ qname t (table_name (*c, imp));
+ return a.empty ()
+ ? quote_id (t)
+ : quote_id (a + '_' + t.uname ());
+ }
+ else
+ {
+ qname t;
+ if (a.empty ())
+ t = table_name (*c);
+ else
+ {
+ if (polymorphic (*c))
+ t = qname (a + "_" + table_name (*c).uname ());
+ else
+ t = qname (a);
+ }
+ return quote_id (t);
+ }
+ }
+
+ // So there is no associated object for this column. The initial
+ // plan was to complain and ask the user to explicitly associate
+ // the object. This is not a bad plan except for one thing: if
+ // the direct side of the relationship is a container, then
+ // associating that object explicitly will result in both the
+ // container table and the object table being JOIN'ed. But we
+ // only need the container table (for the object id) So we will
+ // be joining a table for nothing, which is not very clean. So
+ // the alternative, and more difficult, plan is to go ahead and
+ // add the necessary JOIN's automatically.
+ //
+ // This code follows the normal JOIN generation code.
+ //
+ class_* o (object_pointer (utype (m)));
+ if (class_* root = polymorphic (*o))
+ {
+ o = &static_cast<class_&> (imf.scope ());
+
+ if (!polymorphic (*o))
+ o = root;
+ }
+
+ string const& a (us.alias);
+ string lt (a.empty () ? table_qname (*us.obj) : quote_id (a));
+ string rt;
+ qname ct (container (imb) ? table_name (*o, imp) : table_name (*o));
+
+ string l ("LEFT JOIN ");
+
+ if (a.empty ())
+ {
+ rt = quote_id (ct);
+ l += rt;
+ }
+ else
+ {
+ // The same relationship can be used by multiple associated
+ // objects. So if we have an alias, then also construct one
+ // for the table that we are joining.
+ //
+ rt = quote_id (a + '_' + ct.uname ());
+ l += quote_id (ct);
+ l += (need_alias_as ? " AS " : " ") + rt;
+ }
+
+ l += " ON";
+ from_.push_back (l);
+
+ instance<object_columns_list> l_cols; // Our id columns.
+ instance<object_columns_list> r_cols; // Other side id columns.
+
+ data_member_path& id (*id_member (*us.obj));
+
+ l_cols->traverse (id);
+
+ if (container (imb))
+ r_cols->traverse (imb, utype (id), "value", "value");
+ else
+ r_cols->traverse (imb, column_prefix (imp));
+
+ for (object_columns_list::iterator b (l_cols->begin ()), i (b),
+ j (r_cols->begin ()); i != l_cols->end (); ++i, ++j)
+ {
+ l.clear ();
+
+ if (i != b)
+ l += "AND ";
+
+ l += lt;
+ l += '.';
+ l += quote_id (i->name);
+ l += '=';
+ l += rt;
+ l += '.';
+ l += quote_id (j->name);
+
+ from_.push_back (strlit (l));
+ }
+
+ return rt;
+
+ /*
+ // The alternative implementation:
+ //
+ location const& l1 (m.location ());
+ location const& l2 (ptr_->location ());
+
+ string n1 (class_name (*object_pointer (utype (m))));
+ string n2 (class_name (*object_pointer (utype (*ptr_))));
+
+ error (l1) << "object '" << n1 << "' pointed-to by the inverse "
+ << "data member in object '" << n2 << "' must be "
+ << "explicitly associated with the view" << endl;
+
+ info (l2) << "view data member that loads '" << n2 << "' is "
+ << "defined here" << endl;
+
+ throw operation_failed ();
+ */
+ }
+
+ virtual string
+ resolve_base (semantics::class_& b) const
+ {
+ view_object& vo (*ptr_->get<view_object*> ("view-object"));
+
+ qname t (vo.alias.empty ()
+ ? table_name (b)
+ : qname (vo.alias + "_" + table_name (b).uname ()));
+
+ return quote_id (t);
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+ size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1);
+
+ view_object& vo (*m.get<view_object*> ("view-object"));
+ string const& a (vo.alias);
+
+ qname t;
+ if (a.empty ())
+ t = table_name (c);
+ else
+ {
+ if (poly)
+ t = qname (a + "_" + table_name (c).uname ());
+ else
+ t = qname (a);
+ }
+ string qt (quote_id (t));
+
+ ptr_ = &m;
+
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ object_section* s (&main_section); // Imperfect forwarding.
+ instance<object_columns> oc (qt, sk, sc_, poly_depth, s, this);
+ oc->traverse (c);
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* pm, semantics::class_& c)
+ {
+ if (in_composite_)
+ {
+ object_columns_base::traverse_composite (pm, c);
+ return;
+ }
+
+ // Override the column prerix.
+ //
+ semantics::data_member& m (*pm);
+
+ // If we have literal column specified, use that.
+ //
+ if (m.count ("column"))
+ {
+ table_column const& tc (m.get<table_column> ("column"));
+
+ if (!tc.table.empty ())
+ table_prefix_ = tc.table;
+
+ column_prefix_ = object_columns_base::column_prefix (m);
+ }
+ // Otherwise, see if there is a column expression. For composite
+ // members in a view, this should be a single reference.
+ //
+ else if (m.count ("column-expr"))
+ {
+ column_expr const& e (m.get<column_expr> ("column-expr"));
+
+ if (e.size () > 1)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: column expression specified for a data member "
+ << "of a composite value type" << endl;
+
+ throw operation_failed ();
+ }
+
+ data_member_path const& mp (e.back ().member_path);
+
+ if (mp.size () > 1)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: invalid data member in db pragma column"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ table_prefix_ = e.back ().table;
+ column_prefix_ = object_columns_base::column_prefix (*mp.back ());
+ }
+ else
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: no column prefix provided for a view data member"
+ << endl;
+
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": info: use db pragma column to specify the column prefix"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ in_composite_ = true;
+ object_columns_base::traverse_composite (pm, c);
+ in_composite_ = false;
+ }
+
+ virtual bool
+ traverse_column (semantics::data_member& m, string const& name, bool)
+ {
+ string tbl;
+ string col;
+
+ // If we are inside a composite value, use the standard
+ // column name machinery.
+ //
+ if (in_composite_)
+ {
+ if (!table_prefix_.empty ())
+ {
+ tbl = quote_id (table_prefix_);
+ col += tbl;
+ col += '.';
+ }
+
+ col += quote_id (name);
+ }
+ // If we have literal column specified, use that.
+ //
+ else if (m.count ("column"))
+ {
+ table_column const& tc (m.get<table_column> ("column"));
+
+ if (!tc.expr)
+ {
+ if (!tc.table.empty ())
+ {
+ tbl = quote_id (tc.table);
+ col += tbl;
+ col += '.';
+ }
+
+ col += quote_id (tc.column);
+ }
+ else
+ col += tc.column;
+ }
+ // Otherwise, see if there is a column expression.
+ //
+ else if (m.count ("column-expr"))
+ {
+ column_expr const& e (m.get<column_expr> ("column-expr"));
+
+ for (column_expr::const_iterator i (e.begin ()); i != e.end (); ++i)
+ {
+ switch (i->kind)
+ {
+ case column_expr_part::literal:
+ {
+ col += i->value;
+ break;
+ }
+ case column_expr_part::reference:
+ {
+ tbl = quote_id (i->table);
+ col += tbl;
+ col += '.';
+ col += column_qname (i->member_path);
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: no column name provided for a view data member"
+ << endl;
+
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": info: use db pragma column to specify the column name"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ return column (m, tbl, col);
+ }
+
+ // The column argument is a qualified and quoted column or
+ // expression.
+ //
+ virtual bool
+ column (semantics::data_member& m,
+ string const& table,
+ string const& column)
+ {
+ string const& sqlt (column_type ());
+ sc_.push_back (
+ statement_column (
+ table, convert_from (column, sqlt, m), sqlt, m));
+ return true;
+ }
+
+ protected:
+ statement_columns& sc_;
+ strings& from_;
+ const view_relationship_map& rel_map_;
+
+ bool in_composite_;
+ qname table_prefix_; // Table corresponding to column_prefix_;
+
+ // Set to the current pointer data member that we are traversing.
+ //
+ semantics::data_member* ptr_;
+ };
+
+ struct polymorphic_object_joins: object_columns_base, virtual context
+ {
+ typedef polymorphic_object_joins base;
+
+ polymorphic_object_joins (semantics::class_& obj,
+ bool query,
+ size_t depth,
+ string const& alias = "",
+ user_section* section = 0)
+ : object_columns_base (true, true),
+ obj_ (obj),
+ query_ (query),
+ depth_ (depth),
+ section_ (section),
+ alias_ (alias)
+ {
+ // Get the table and id columns.
+ //
+ table_ = alias_.empty ()
+ ? table_qname (obj_)
+ : quote_id (alias_ + "_" + table_name (obj_).uname ());
+
+ cols_->traverse (*id_member (obj_));
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ // If section is specified, skip bases that don't add anything
+ // to load.
+ //
+ bool skip (false), stop (false);
+ if (section_ != 0)
+ {
+ skip = true;
+
+ if (section_->object == &c)
+ {
+ user_section& s (*section_);
+
+ if (s.total != 0 || s.optimistic ())
+ skip = false;
+
+ section_ = s.base; // Move to the next base.
+
+ if (section_ == 0)
+ stop = true; // Stop at this base if there are no more overrides.
+ }
+ }
+ // Skip intermediates that don't add any data members.
+ //
+ else if (!query_)
+ {
+ column_count_type const& cc (column_count (c));
+ if (cc.total == cc.id + cc.separate_load)
+ skip = true;
+ }
+
+ if (!skip)
+ {
+ std::ostringstream cond;
+
+ qname table (table_name (c));
+ string alias (alias_.empty ()
+ ? quote_id (table)
+ : quote_id (alias_ + "_" + table.uname ()));
+
+ for (object_columns_list::iterator b (cols_->begin ()), i (b);
+ i != cols_->end ();
+ ++i)
+ {
+ if (i != b)
+ cond << " AND ";
+
+ string qn (quote_id (i->name));
+ cond << alias << '.' << qn << '=' << table_ << '.' << qn;
+ }
+
+ string line ("LEFT JOIN " + quote_id (table));
+
+ if (!alias_.empty ())
+ line += (need_alias_as ? " AS " : " ") + alias;
+
+ line += " ON " + cond.str ();
+
+ joins.push_back (line);
+ }
+
+ if (!stop && --depth_ != 0)
+ inherits (c);
+ }
+
+ public:
+ strings joins;
+
+ strings::const_iterator
+ begin () const {return joins.begin ();}
+
+ strings::const_iterator
+ end () const {return joins.end ();}
+
+ private:
+ semantics::class_& obj_;
+ bool query_;
+ size_t depth_;
+ user_section* section_;
+ string alias_;
+ string table_;
+ instance<object_columns_list> cols_;
+ };
+
+ struct object_joins: object_columns_base, virtual context
+ {
+ typedef object_joins base;
+
+ //@@ context::{cur,top}_object; might have to be created every time.
+ //
+ object_joins (semantics::class_& scope,
+ bool query,
+ size_t depth,
+ object_section* section = 0)
+ : object_columns_base (true, true, section),
+ query_ (query),
+ depth_ (depth),
+ table_ (table_qname (scope)),
+ id_ (*id_member (scope))
+ {
+ id_cols_->traverse (id_);
+ }
+
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ (*section_ == main_section && !s.separate_load ());
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ // If this is a derived type in a polymorphic hierarchy, then we
+ // need to include base joins, but do it in reverse order as well
+ // as switch the table name (base columns come from different
+ // tables).
+ //
+ semantics::class_* poly_root (polymorphic (c));
+ if (poly_root != 0 && poly_root != &c)
+ {
+ names (c);
+
+ if (query_ || --depth_ != 0)
+ {
+ table_ = table_qname (polymorphic_base (c));
+ inherits (c);
+ }
+ }
+ else
+ object_columns_base::traverse_object (c);
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore polymorphic id references; they are joined by
+ // polymorphic_object_joins in a special way.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ string t, a, dt, da;
+ std::ostringstream cond, dcond; // @@ diversion?
+
+ // Derive table alias for this member. Generally, we want the
+ // alias to be based on the column name. This is straightforward
+ // for single-column references. In case of a composite id, we
+ // will need to use the column prefix which is based on the data
+ // member name, unless overridden by the user. In the latter
+ // case the prefix can be empty, in which case we will just
+ // fall back on the member's public name.
+ //
+ string alias;
+ {
+ string n;
+
+ if (composite_wrapper (utype (*id_member (c))))
+ {
+ n = column_prefix (m, key_prefix_, default_name_).prefix;
+
+ if (n.empty ())
+ n = public_name_db (m);
+ else if (n[n.size () - 1] == '_')
+ n.resize (n.size () - 1); // Remove trailing underscore.
+ }
+ else
+ {
+ bool dummy;
+ n = column_name (m, key_prefix_, default_name_, dummy);
+ }
+
+ alias = column_prefix_.prefix + n;
+ }
+
+ semantics::class_* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+
+ semantics::class_* joined_obj (0);
+
+ if (data_member_path* imp = inverse (m, key_prefix_))
+ {
+ semantics::data_member& imf (*imp->front ());
+ semantics::data_member& imb (*imp->back ());
+
+ // In a polymorphic hierarchy the inverse member can be in
+ // the base class, in which case we should use that table.
+ //
+ semantics::class_& imc (
+ poly ? dynamic_cast<semantics::class_&> (imf.scope ()) : c);
+
+ if (container (imb))
+ {
+ // This container is a direct member of the class so the table
+ // prefix is just the class table name.
+ //
+ t = table_qname (imc, *imp);
+
+ // Container's value is our id.
+ //
+ instance<object_columns_list> id_cols;
+ id_cols->traverse (imb, utype (id_), "value", "value");
+
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b),
+ j (id_cols_->begin ()); i != id_cols->end (); ++i, ++j)
+ {
+
+ if (i != b)
+ cond << " AND ";
+
+ cond << t << '.' << quote_id (i->name) << '=' <<
+ table_ << '.' << quote_id (j->name);
+ }
+
+ // Add the join for the object itself so that we are able to
+ // use it in the WHERE clause.
+ //
+ if (query_)
+ {
+ // Here we can use the most derived class instead of the
+ // one containing the inverse member.
+ //
+ qname const& table (table_name (c));
+
+ dt = quote_id (table);
+ da = quote_id (poly ? alias + "_" + table.uname () : alias);
+
+ data_member_path& id (*id_member (c));
+
+ instance<object_columns_list> oid_cols, cid_cols;
+ oid_cols->traverse (id);
+ cid_cols->traverse (imb, utype (id), "id", "object_id", &c);
+
+ for (object_columns_list::iterator b (cid_cols->begin ()), i (b),
+ j (oid_cols->begin ()); i != cid_cols->end (); ++i, ++j)
+ {
+
+ if (i != b)
+ dcond << " AND ";
+
+ dcond << da << '.' << quote_id (j->name) << '=' <<
+ t << '.' << quote_id (i->name);
+ }
+
+ joined_obj = &c;
+ }
+ }
+ else
+ {
+ qname const& table (table_name (imc));
+
+ t = quote_id (table);
+ a = quote_id (poly ? alias + "_" + table.uname () : alias);
+
+ instance<object_columns_list> id_cols;
+ id_cols->traverse (imb, column_prefix (*imp));
+
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b),
+ j (id_cols_->begin ()); i != id_cols->end (); ++i, ++j)
+ {
+ if (i != b)
+ cond << " AND ";
+
+ cond << a << '.' << quote_id (i->name) << '=' <<
+ table_ << '.' << quote_id (j->name);
+ }
+
+ // If we are generating query, JOIN base/derived classes so
+ // that we can use their data in the WHERE clause.
+ //
+ if (query_)
+ joined_obj = &imc;
+ }
+ }
+ else if (query_)
+ {
+ // We need the join to be able to use the referenced object
+ // in the WHERE clause.
+ //
+ qname const& table (table_name (c));
+
+ t = quote_id (table);
+ a = quote_id (poly ? alias + "_" + table.uname () : alias);
+
+ instance<object_columns_list> oid_cols (column_prefix_);
+ oid_cols->traverse (m);
+
+ instance<object_columns_list> pid_cols;
+ pid_cols->traverse (*id_member (c));
+
+ for (object_columns_list::iterator b (pid_cols->begin ()), i (b),
+ j (oid_cols->begin ()); i != pid_cols->end (); ++i, ++j)
+ {
+
+ if (i != b)
+ cond << " AND ";
+
+ cond << a << '.' << quote_id (i->name) << '=' <<
+ table_ << '.' << quote_id (j->name);
+ }
+
+ joined_obj = &c;
+ }
+
+ if (!t.empty ())
+ {
+ string line ("LEFT JOIN ");
+ line += t;
+
+ if (!a.empty ())
+ line += (need_alias_as ? " AS " : " ") + a;
+
+ line += " ON ";
+ line += cond.str ();
+
+ joins.push_back (line);
+ }
+
+ // Add dependent join (i.e., an object table join via the
+ // container table).
+ //
+ if (!dt.empty ())
+ {
+ string line ("LEFT JOIN ");
+ line += dt;
+
+ if (!da.empty ())
+ line += (need_alias_as ? " AS " : " ") + da;
+
+ line += " ON ";
+ line += dcond.str ();
+
+ joins.push_back (line);
+ }
+
+ // If we joined the object that is part of a polymorphic type
+ // hierarchy, then we may need join its bases as well as its
+ // derived types. This is only done for queries.
+ //
+ if (joined_obj != 0 && poly)
+ {
+ size_t depth (polymorphic_depth (*joined_obj));
+
+ // Join "up" (derived).
+ //
+ if (joined_obj != &c)
+ {
+ bool t (true); //@@ (im)perfect forward.
+ size_t d (polymorphic_depth (c) - depth); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (*joined_obj, t, d, alias);
+ j->traverse (c);
+ joins.insert (joins.end (), j->joins.begin (), j->joins.end ());
+ }
+
+ // Join "down" (base).
+ //
+ if (joined_obj != poly_root)
+ {
+ bool t (true); //@@ (im)perfect forward.
+ size_t d (depth - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (*joined_obj, t, d, alias);
+ j->traverse (polymorphic_base (*joined_obj));
+ joins.insert (joins.end (), j->joins.begin (), j->joins.end ());
+ }
+ }
+ }
+
+ public:
+ strings joins;
+
+ strings::const_iterator
+ begin () const {return joins.begin ();}
+
+ strings::const_iterator
+ end () const {return joins.end ();}
+
+ private:
+ bool query_;
+ size_t depth_;
+ string table_;
+ data_member_path& id_;
+ instance<object_columns_list> id_cols_;
+ };
+
+ // Check that eager object pointers in the objects that a view loads
+ // can be loaded from the cache (i.e., they have session support
+ // enabled).
+ //
+ struct view_object_check: object_members_base
+ {
+ typedef view_object_check base;
+
+ view_object_check (view_object& vo, view_relationship_map& rm)
+ : object_members_base (false, &main_section),
+ session_ (false), vo_ (vo), rel_map_ (rm) {}
+
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ // Include eager loaded members into the main section.
+ //
+ object_section& s (section (mp));
+ return *section_ == s || !s.separate_load ();
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ check (m, inverse (m), utype (m), c);
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ semantics::type& vt (container_vt (m));
+
+ if (semantics::class_* cvt = composite_wrapper (vt))
+ {
+ // Check this composite value for any pointers.
+ //
+ instance<view_object_check> t (vo_, rel_map_);
+ t->traverse (*cvt);
+
+ session_ = session_ || t->session_;
+ }
+ else if (semantics::class_* c = object_pointer (vt))
+ check (m, inverse (m, "value"), vt, *c);
+ }
+
+ void
+ check (semantics::data_member& m,
+ data_member_path* imp,
+ semantics::type& pt, // Pointer type.
+ semantics::class_& c)
+ {
+ // We don't care about lazy pointers.
+ //
+ if (lazy_pointer (pt))
+ return;
+
+ // First check the pointed-to object recursively.
+ //
+ if (!c.count ("view-object-check-seen"))
+ {
+ c.set ("view-object-check-seen", true);
+ instance<view_object_check> t (vo_, rel_map_);
+ t->traverse (c);
+
+ // We may come again for another view.
+ //
+ c.remove ("view-object-check-seen");
+
+ session_ = session_ || t->session_;
+ }
+
+ // See if the pointed-to object in this relationship is loaded
+ // by this view.
+ //
+ typedef view_relationship_map::const_iterator iterator;
+
+ std::pair<iterator, iterator> r (
+ rel_map_.equal_range (imp != 0 ? *imp : member_path_));
+
+ if (r.first == r.second)
+ return; // This relationship does not figure in the view.
+
+ view_object& vo (*(imp != 0
+ ? r.first->second.first
+ : r.first->second.second));
+
+ assert (vo.obj == &c); // Got the above right?
+
+ if (vo.ptr == 0)
+ return; // This object is not loaded by the view.
+
+ // The pointed-to object in this relationship is loaded
+ // by the view. The hard question, of course, is whether
+ // it has anything to do with us. We assume it does.
+ //
+ if (!session (c))
+ {
+ // Always direct data member.
+ //
+ semantics::class_& v (
+ dynamic_cast<semantics::class_&> (vo.ptr->scope ()));
+
+ location const& l1 (c.location ());
+ location const& l2 (m.location ());
+ location const& l3 (vo_.ptr->location ());
+ location const& l4 (vo.ptr->location ());
+
+ string on (class_name (c));
+ string vn (class_name (v));
+
+ error (l1) << "object '" << on << "' has session support disabled "
+ << "but may be loaded by view '" << vn << "' via "
+ << "several data members" << endl;
+
+ info (l2) << "indirectly via this data member..." << endl;
+ info (l3) << "...as a result of this object load" << endl;
+ info (l4) << "and directly as a result of this load" << endl;
+ info (l1) << "session support is required to only load one copy "
+ << "of the object" << endl;
+ info (l1) << "and don't forget to create a session instance when "
+ << "using this view" << endl;
+
+ throw operation_failed ();
+ }
+
+ session_ = true;
+ }
+
+ bool session_;
+
+ private:
+ view_object& vo_;
+ view_relationship_map& rel_map_;
+ };
+
+ //
+ // bind
+ //
+
+ struct bind_member: virtual member_base
+ {
+ typedef bind_member base;
+
+ // NULL section means we are generating object bind().
+ //
+ bind_member (string const& var = string (),
+ string const& arg = string (),
+ object_section* section = 0)
+ : member_base (var, 0, 0, string (), string (), section),
+ arg_override_ (arg) {}
+
+ bind_member (string const& var,
+ string const& arg,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, ct, fq_type, key_prefix),
+ arg_override_ (arg) {}
+
+ protected:
+ string arg_override_;
+ };
+
+ template <typename T>
+ struct bind_member_impl: bind_member, virtual member_base_impl<T>
+ {
+ typedef bind_member_impl base_impl;
+
+ bind_member_impl (base const& x): base (x) {}
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ using member_base_impl<T>::container;
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi))
+ return false;
+
+ // Treat version as present in every section.
+ //
+ if (section_ != 0 && !version (mi.m) && *section_ != section (mi.m))
+ return false;
+
+ // Ignore polymorphic id references; they are bound in a special
+ // way.
+ //
+ if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
+ return false;
+
+ std::ostringstream ostr;
+ ostr << "b[n]";
+ b = ostr.str ();
+
+ arg = arg_override_.empty () ? string ("i") : arg_override_;
+
+ if (var_override_.empty ())
+ {
+ // Ignore inverse, separately-loaded members in the main
+ // section (nothing to persist).
+ //
+ if (section_ == 0 && separate_load (mi.m) && inverse (mi.m))
+ return false;
+
+ semantics::class_* comp (composite (mi.t));
+
+ os << "// " << mi.m.name () << endl
+ << "//" << endl;
+
+ // Order of these tests is important.
+ //
+ if (!insert_send_auto_id && auto_ (mi.m))
+ os << "if (sk != statement_insert && sk != statement_update)"
+ << "{";
+ else if (section_ == 0 && separate_load (mi.m))
+ os << "if (sk == statement_insert)"
+ << "{";
+ else if (inverse (mi.m, key_prefix_) || version (mi.m))
+ os << "if (sk == statement_select)"
+ << "{";
+ // If the whole class is readonly, then we will never be
+ // called with sk == statement_update.
+ //
+ else if (!readonly (*context::top_object))
+ {
+ if (id (mi.m) ||
+ readonly (mi.m) ||
+ (comp != 0 && readonly (*comp)) ||
+ (section_ == 0 && separate_update (mi.m)))
+ os << "if (sk != statement_update)"
+ << "{";
+ }
+
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ // If this is a composite member, see if it is summarily
+ // added/deleted.
+ //
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ // If the addition/deletion version is the same as the section's,
+ // then we don't need the test.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")"
+ << "{";
+ }
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ if (var_override_.empty ())
+ {
+ semantics::class_* comp (composite (mi.t));
+
+ // We need to increment the index even if we skipped this
+ // member due to the schema version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ os << "}";
+
+ if (mi.ptr != 0 && view_member (mi.m))
+ {
+ // See column_count_impl for details on what's going on here.
+ //
+ column_count_type cc;
+ if (semantics::class_* root = polymorphic (*mi.ptr))
+ {
+ for (semantics::class_* b (mi.ptr);; b = &polymorphic_base (*b))
+ {
+ column_count_type const& ccb (column_count (*b));
+
+ cc.total += ccb.total - (b != root ? ccb.id : 0);
+ cc.separate_load += ccb.separate_load;
+
+ if (b == root)
+ break;
+ }
+ }
+ else
+ cc = column_count (*mi.ptr);
+
+ os << "n += " << cc.total - cc.separate_load << "UL;";
+ }
+ else if (comp != 0)
+ {
+ bool ro (readonly (*comp));
+ column_count_type const& cc (column_count (*comp));
+
+ os << "n += " << cc.total << "UL";
+
+ // select = total
+ // insert = total - inverse
+ // update = total - inverse - readonly
+ //
+ if (cc.inverse != 0 || (!ro && cc.readonly != 0))
+ {
+ os << " - (" << endl
+ << "sk == statement_select ? 0 : ";
+
+ if (cc.inverse != 0)
+ os << cc.inverse << "UL";
+
+ if (!ro && cc.readonly != 0)
+ {
+ if (cc.inverse != 0)
+ os << " + ";
+
+ os << "(" << endl
+ << "sk == statement_insert ? 0 : " <<
+ cc.readonly << "UL)";
+ }
+
+ os << ")";
+ }
+
+ os << ";";
+ }
+ else
+ os << "n++;";
+
+ bool block (false);
+
+ // The same logic as in pre().
+ //
+ if (!insert_send_auto_id && auto_ (mi.m))
+ block = true;
+ else if (section_ == 0 && separate_load (mi.m))
+ block = true;
+ else if (inverse (mi.m, key_prefix_) || version (mi.m))
+ block = true;
+ else if (!readonly (*context::top_object))
+ {
+ semantics::class_* c;
+
+ if (id (mi.m) ||
+ readonly (mi.m) ||
+ ((c = composite (mi.t)) && readonly (*c)) ||
+ (section_ == 0 && separate_update (mi.m)))
+ block = true;
+ }
+
+ if (block)
+ os << "}";
+ else
+ os << endl;
+ }
+ }
+
+ virtual void
+ traverse_pointer (member_info& mi)
+ {
+ // Object pointers in views require special treatment.
+ //
+ if (view_member (mi.m))
+ {
+ semantics::class_& c (*mi.ptr);
+ semantics::class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ os << "object_traits_impl< " << class_fq_name (c) << ", id_" <<
+ db << " >::bind (" << endl
+ << "b + n, " << (poly_derived ? "0, 0, " : "") << arg << "." <<
+ mi.var << "value, sk" << (versioned (c) ? ", svm" : "") << ");";
+ }
+ else
+ member_base_impl<T>::traverse_pointer (mi);
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ os << "composite_value_traits< " << mi.fq_type () << ", id_" <<
+ db << " >::bind (" << endl
+ << "b + n, " << arg << "." << mi.var << "value, sk" <<
+ (versioned (*composite (mi.t)) ? ", svm" : "") << ");";
+ }
+
+ protected:
+ string b;
+ string arg;
+ };
+
+ struct bind_base: traversal::class_, virtual context
+ {
+ typedef bind_base base;
+
+ virtual void
+ traverse (type& c)
+ {
+ bool obj (object (c));
+
+ // Ignore transient bases. Not used for views.
+ //
+ if (!(obj || composite (c)))
+ return;
+
+ os << "// " << class_name (c) << " base" << endl
+ << "//" << endl;
+
+ // If the derived class is readonly, then we will never be
+ // called with sk == statement_update.
+ //
+ bool ro (readonly (c));
+ bool check (ro && !readonly (*context::top_object));
+
+ if (check)
+ os << "if (sk != statement_update)"
+ << "{";
+
+ if (obj)
+ os << "object_traits_impl< ";
+ else
+ os << "composite_value_traits< ";
+
+ os << class_fq_name (c) << ", id_" << db << " >::bind (b + n, i, sk" <<
+ (versioned (c) ? ", svm" : "") << ");";
+
+ column_count_type const& cc (column_count (c));
+
+ os << "n += ";
+
+ // select = total - separate_load
+ // insert = total - inverse - optimistic_managed - id(auto & !sending)
+ // update = total - inverse - optimistic_managed - id - readonly -
+ // separate_update
+ //
+ size_t select (cc.total - cc.separate_load);
+ size_t insert (cc.total - cc.inverse - cc.optimistic_managed);
+ size_t update (insert - cc.id - cc.readonly - cc.separate_update);
+
+ data_member_path* id;
+ if (!insert_send_auto_id && (id = id_member (c)) != 0 && auto_ (*id))
+ insert -= cc.id;
+
+ if (select == insert && insert == update)
+ os << select << "UL;";
+ else if (select != insert && insert == update)
+ os << "sk == statement_select ? " << select << "UL : " <<
+ insert << "UL;";
+ else if (select == insert && insert != update)
+ os << "sk == statement_update ? " << update << "UL : " <<
+ select << "UL;";
+ else
+ os << "sk == statement_select ? " << select << "UL : " <<
+ "sk == statement_insert ? " << insert << "UL : " <<
+ update << "UL;";
+
+ if (check)
+ os << "}";
+ else
+ os << endl;
+ }
+ };
+
+ //
+ // grow
+ //
+
+ struct grow_member: virtual member_base
+ {
+ typedef grow_member base;
+
+ grow_member (size_t& index,
+ string const& var = string (),
+ user_section* section = 0)
+ : member_base (var, 0, 0, string (), string (), section),
+ index_ (index) {}
+
+ grow_member (size_t& index,
+ string const& var,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, ct, fq_type, key_prefix), index_ (index) {}
+
+ protected:
+ size_t& index_;
+ };
+
+ template <typename T>
+ struct grow_member_impl: grow_member, virtual member_base_impl<T>
+ {
+ typedef grow_member_impl base_impl;
+
+ grow_member_impl (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ using member_base_impl<T>::container;
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi))
+ return false;
+
+ // If we have a key prefix (container), then it can't be in a section
+ // (while mi.m can). The same for top-level -- if we got called, then
+ // we shouldn't ignore it. (Same logic as in has_grow_member).
+ //
+ if (!(!key_prefix_.empty () || top_level_ ||
+ (section_ == 0 && !separate_load (mi.m)) ||
+ (section_ != 0 && *section_ == section (mi.m))))
+ return false;
+
+ // Ignore polymorphic id references; they are not returned by
+ // the select statement.
+ //
+ if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
+ return false;
+
+ std::ostringstream ostr;
+ ostr << "t[" << index_ << "UL]";
+ e = ostr.str ();
+
+ if (var_override_.empty ())
+ {
+ os << "// " << mi.m.name () << endl
+ << "//" << endl;
+
+ semantics::class_* comp (composite (mi.t));
+
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ // If this is a composite member, see if it is summarily
+ // added/deleted.
+ //
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ // If the addition/deletion version is the same as the section's,
+ // then we don't need the test.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")"
+ << "{";
+ }
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ semantics::class_* comp (composite (mi.t));
+
+ if (var_override_.empty ())
+ {
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ os << "}";
+ }
+
+ if (mi.ptr != 0 && view_member (mi.m))
+ {
+ // See column_count_impl for details on what's going on here.
+ //
+ column_count_type cc;
+ if (semantics::class_* root = polymorphic (*mi.ptr))
+ {
+ for (semantics::class_* b (mi.ptr);; b = &polymorphic_base (*b))
+ {
+ column_count_type const& ccb (column_count (*b));
+
+ cc.total += ccb.total - (b != root ? ccb.id : 0);
+ cc.separate_load += ccb.separate_load;
+
+ if (b == root)
+ break;
+ }
+ }
+ else
+ cc = column_count (*mi.ptr);
+
+ index_ += cc.total - cc.separate_load;
+ }
+ else if (comp != 0)
+ index_ += column_count (*comp).total;
+ else
+ index_++;
+ }
+
+ virtual void
+ traverse_pointer (member_info& mi)
+ {
+ // Object pointers in views require special treatment. They
+ // can only be immediate members of the view class.
+ //
+ if (view_member (mi.m))
+ {
+ semantics::class_& c (*mi.ptr);
+
+ os << "if (object_traits_impl< " << class_fq_name (c) <<
+ ", id_" << db << " >::grow (" << endl
+ << "i." << mi.var << "value, t + " << index_ << "UL" <<
+ (versioned (c) ? ", svm" : "") << "))" << endl
+ << "grew = true;"
+ << endl;
+ }
+ else
+ member_base_impl<T>::traverse_pointer (mi);
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ semantics::class_& c (*composite (mi.t));
+
+ os << "if (composite_value_traits< " << mi.fq_type () <<
+ ", id_" << db << " >::grow (" << endl
+ << "i." << mi.var << "value, t + " << index_ << "UL" <<
+ (versioned (c) ? ", svm" : "") << "))" << endl
+ << "grew = true;"
+ << endl;
+ }
+
+ protected:
+ string e;
+ };
+
+ struct grow_base: traversal::class_, virtual context
+ {
+ typedef grow_base base;
+
+ grow_base (size_t& index): index_ (index) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ bool obj (object (c));
+
+ // Ignore transient bases. Not used for views.
+ //
+ if (!(obj || composite (c)))
+ return;
+
+ os << "// " << class_name (c) << " base" << endl
+ << "//" << endl;
+
+ os << "if (";
+
+ if (obj)
+ os << "object_traits_impl< ";
+ else
+ os << "composite_value_traits< ";
+
+ os << class_fq_name (c) << ", id_" << db << " >::grow (" << endl
+ << "i, t + " << index_ << "UL" <<
+ (versioned (c) ? ", svm" : "") << "))" << endl
+ << "grew = true;"
+ << endl;
+
+ index_ += column_count (c).total;
+ }
+
+ protected:
+ size_t& index_;
+ };
+
+ //
+ // init image
+ //
+
+ struct init_image_member: virtual member_base
+ {
+ typedef init_image_member base;
+
+ init_image_member (string const& var = string (),
+ string const& member = string (),
+ user_section* section = 0)
+ : member_base (var, 0, 0, string (), string (), section),
+ member_override_ (member)
+ {
+ }
+
+ init_image_member (string const& var,
+ string const& member,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, ct, fq_type, key_prefix),
+ member_override_ (member)
+ {
+ }
+
+ protected:
+ string member_override_;
+ };
+
+ template <typename T>
+ struct init_image_member_impl: init_image_member,
+ virtual member_base_impl<T>
+ {
+ typedef init_image_member_impl base_impl;
+
+ init_image_member_impl (base const& x)
+ : base (x),
+ member_database_type_id_ (base::type_override_,
+ base::custom_override_,
+ base::fq_type_override_,
+ base::key_prefix_)
+ {
+ }
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ using member_base_impl<T>::container;
+
+ virtual void
+ set_null (member_info&) = 0;
+
+ virtual void
+ check_accessor (member_info&, member_access&) {}
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ // Ignore containers (they get their own table) and inverse
+ // object pointers (they are not present in this binding).
+ //
+ if (container (mi) || inverse (mi.m, key_prefix_))
+ return false;
+
+ if (section_ != 0 && *section_ != section (mi.m))
+ return false;
+
+ // Ignore polymorphic id references; they are initialized in a
+ // special way.
+ //
+ if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
+ return false;
+
+ semantics::class_* comp (composite (mi.t));
+
+ if (!member_override_.empty ())
+ {
+ member = member_override_;
+ os << "{";
+ }
+ else
+ {
+ // If we are generating standard init() and this member
+ // contains version, ignore it.
+ //
+ if (version (mi.m))
+ return false;
+
+ // If we don't send auto id in INSERT statement, ignore this
+ // member altogether (we never send auto id in UPDATE).
+ //
+ if (!insert_send_auto_id && auto_ (mi.m))
+ return false;
+
+ os << "// " << mi.m.name () << endl
+ << "//" << endl;
+
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ // If this is a composite member, see if it is summarily
+ // added/deleted.
+ //
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ // If the addition/deletion version is the same as the section's,
+ // then we don't need the test.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")"
+ << "{";
+ }
+
+ // If the whole class is readonly, then we will never be
+ // called with sk == statement_update.
+ //
+ if (!readonly (*context::top_object))
+ {
+ if (id (mi.m) ||
+ readonly (mi.m) ||
+ (section_ == 0 && separate_update (mi.m)) ||
+ (comp != 0 && readonly (*comp))) // Can't be id.
+ {
+ // If we are generating section init(), then sk can only be
+ // statement_update.
+ //
+ if (section_ == 0)
+ os << "if (sk == statement_insert)";
+ }
+ }
+
+ os << "{";
+
+ if (discriminator (mi.m))
+ member = "di.discriminator";
+ else
+ {
+ // Get the member using the accessor expression.
+ //
+ member_access& ma (mi.m.template get<member_access> ("get"));
+
+ // Make sure this kind of member can be accessed with this
+ // kind of accessor (database-specific, e.g., streaming).
+ //
+ if (comp == 0)
+ check_accessor (mi, ma);
+
+ // If this is not a synthesized expression, then output
+ // its location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ // Use the original type to form the const reference. VC++
+ // cannot grok the constructor syntax.
+ //
+ os << member_ref_type (mi.m, true, "v") << " =" << endl
+ << " " << ma.translate ("o") << ";"
+ << endl;
+
+ member = "v";
+ }
+ }
+
+ // Translate.
+ //
+ if (mi.ct != 0)
+ {
+ os << "// From " << location_string (mi.ct->loc, true) << endl
+ << type_ref_type (*mi.ct->as, mi.ct->as_hint, true, "vt") <<
+ " =" << endl
+ << " " << mi.ct->translate_to (member) << ";"
+ << endl;
+
+ member = "vt";
+ }
+
+ // If this is a wrapped composite value, then we need to "unwrap"
+ // it. If this is a NULL wrapper, then we also need to handle that.
+ // For simple values this is taken care of by the value_traits
+ // specializations.
+ //
+ if (mi.wrapper != 0 && comp != 0)
+ {
+ // The wrapper type, not the wrapped type.
+ //
+ string const& wt (mi.fq_type (false));
+
+ // If this is a NULL wrapper and the member can be NULL, then
+ // we need to handle the NULL value.
+ //
+ if (null (mi.m, key_prefix_) &&
+ mi.wrapper->template get<bool> ("wrapper-null-handler"))
+ {
+ os << "if (wrapper_traits< " << wt << " >::get_null (" <<
+ member << "))" << endl
+ << "composite_value_traits< " << mi.fq_type () << ", id_" <<
+ db << " >::set_null (" << endl
+ << "i." << mi.var << "value, sk" <<
+ (versioned (*comp) ? ", svm" : "") << ");"
+ << "else"
+ << "{";
+ }
+
+ os << "const" << mi.fq_type () << "& vw = " << endl
+ << " wrapper_traits< " + wt + " >::get_ref (" + member + ");"
+ << endl;
+
+ member = "vw";
+ }
+
+ if (discriminator (mi.m))
+ os << "const info_type& di (map->find (typeid (o)));"
+ << endl;
+
+ if (mi.ptr != 0)
+ {
+ // When handling a pointer, mi.t is the id type of the referenced
+ // object.
+ //
+ semantics::type& pt (utype (mi.m, key_prefix_));
+
+ type = "obj_traits::id_type";
+
+ // Handle NULL pointers and extract the id.
+ //
+ os << "typedef object_traits< " << class_fq_name (*mi.ptr) <<
+ " > obj_traits;";
+
+ if (weak_pointer (pt))
+ {
+ os << "typedef odb::pointer_traits< " << mi.ptr_fq_type () <<
+ " > wptr_traits;"
+ << "typedef odb::pointer_traits< wptr_traits::" <<
+ "strong_pointer_type > ptr_traits;"
+ << endl
+ << "wptr_traits::strong_pointer_type sp (" <<
+ "wptr_traits::lock (" << member << "));";
+
+ member = "sp";
+ }
+ else
+ os << "typedef odb::pointer_traits< " << mi.ptr_fq_type () <<
+ " > ptr_traits;"
+ << endl;
+
+ os << "bool is_null (ptr_traits::null_ptr (" << member << "));"
+ << "if (!is_null)"
+ << "{"
+ << "const " << type << "& ptr_id (" << endl;
+
+ if (lazy_pointer (pt))
+ os << "ptr_traits::object_id< ptr_traits::element_type > (" <<
+ member << ")";
+ else
+ os << "obj_traits::id (ptr_traits::get_ref (" << member << "))";
+
+ os << ");"
+ << endl;
+
+ member = "ptr_id";
+ }
+ else if (comp != 0)
+ type = mi.fq_type ();
+ else
+ {
+ type = mi.fq_type ();
+
+ // Indicate to the value_traits whether this column can be NULL.
+ //
+ os << "bool is_null (" << null (mi.m, key_prefix_) << ");";
+ }
+
+ if (comp != 0)
+ traits = "composite_value_traits< " + type + ", id_" +
+ db.string () + " >";
+ else
+ {
+ db_type_id = member_database_type_id_->database_type_id (mi.m);
+ traits = db.string () + "::value_traits<\n "
+ + type + ",\n "
+ + db_type_id + " >";
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ semantics::class_* comp (composite (mi.t));
+
+ if (mi.ptr != 0)
+ {
+ os << "}"
+ << "else" << endl;
+
+ if (!null (mi.m, key_prefix_))
+ os << "throw null_pointer ();";
+ else if (comp != 0)
+ os << traits << "::set_null (i." << mi.var << "value, sk" <<
+ (versioned (*comp) ? ", svm" : "") << ");";
+ else
+ set_null (mi);
+ }
+
+ if (mi.wrapper != 0 && comp != 0)
+ {
+ if (null (mi.m, key_prefix_) &&
+ mi.wrapper->template get<bool> ("wrapper-null-handler"))
+ os << "}";
+ }
+
+ os << "}";
+
+ if (member_override_.empty ())
+ {
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ os << "}";
+ }
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ bool grow (generate_grow &&
+ context::grow (mi.m, mi.t, mi.ct, key_prefix_));
+
+ if (grow)
+ os << "if (";
+
+ os << traits << "::init (" << endl
+ << "i." << mi.var << "value," << endl
+ << member << "," << endl
+ << "sk";
+
+ if (versioned (*composite (mi.t)))
+ os << "," << endl
+ << "svm";
+
+ os << ")";
+
+ if (grow)
+ os << ")" << endl
+ << "grew = true";
+
+ os << ";";
+ }
+
+ protected:
+ string type;
+ string db_type_id;
+ string member;
+ string traits;
+
+ instance<member_database_type_id> member_database_type_id_;
+ };
+
+ struct init_image_base: traversal::class_, virtual context
+ {
+ typedef init_image_base base;
+
+ virtual void
+ traverse (type& c)
+ {
+ bool obj (object (c));
+
+ // Ignore transient bases. Not used for views.
+ //
+ if (!(obj || composite (c)))
+ return;
+
+ os << "// " << class_name (c) << " base" << endl
+ << "//" << endl;
+
+ // If the derived class is readonly, then we will never be
+ // called with sk == statement_update.
+ //
+ bool check (readonly (c) && !readonly (*context::top_object));
+
+ if (check)
+ os << "if (sk != statement_update)"
+ << "{";
+
+ if (generate_grow)
+ os << "if (";
+
+ if (obj)
+ os << "object_traits_impl< ";
+ else
+ os << "composite_value_traits< ";
+
+ os << class_fq_name (c) << ", id_" << db << " >::init (i, o, sk" <<
+ (versioned (c) ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << ")" << endl
+ << "grew = true";
+
+ os << ";";
+
+ if (check)
+ os << "}";
+ else
+ os << endl;
+ }
+ };
+
+ //
+ // init value
+ //
+
+ struct init_value_member: virtual member_base
+ {
+ typedef init_value_member base;
+
+ init_value_member (string const& member = string (),
+ string const& var = string (),
+ bool ignore_implicit_discriminator = true,
+ user_section* section = 0)
+ : member_base (var, 0, 0, string (), string (), section),
+ member_override_ (member),
+ ignore_implicit_discriminator_ (ignore_implicit_discriminator)
+ {
+ }
+
+ init_value_member (string const& var,
+ string const& member,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, ct, fq_type, key_prefix),
+ member_override_ (member),
+ ignore_implicit_discriminator_ (true)
+ {
+ }
+
+ virtual void
+ get_null (string const& /*var*/) const {};
+
+ protected:
+ string member_override_;
+ bool ignore_implicit_discriminator_;
+ };
+
+ template <typename T>
+ struct init_value_member_impl: init_value_member,
+ virtual member_base_impl<T>
+ {
+ typedef init_value_member_impl base_impl;
+
+ init_value_member_impl (base const& x)
+ : base (x),
+ member_database_type_id_ (base::type_override_,
+ base::custom_override_,
+ base::fq_type_override_,
+ base::key_prefix_)
+ {
+ }
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ using member_base_impl<T>::container;
+
+ virtual void
+ get_null (string const& var) const = 0;
+
+ virtual void
+ check_modifier (member_info&, member_access&) {}
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi))
+ return false;
+
+ if (section_ != 0 && *section_ != section (mi.m))
+ return false;
+
+ // Ignore polymorphic id references; they are initialized in a
+ // special way.
+ //
+ if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
+ return false;
+
+ // Ignore implicit discriminators.
+ //
+ if (ignore_implicit_discriminator_ && discriminator (mi.m))
+ return false;
+
+ semantics::class_* comp (composite (mi.t));
+
+ if (!member_override_.empty ())
+ {
+ os << "{";
+ member = member_override_;
+ }
+ else
+ {
+ // Ignore separately loaded members.
+ //
+ if (section_ == 0 && separate_load (mi.m))
+ return false;
+
+ os << "// " << mi.m.name () << endl
+ << "//" << endl;
+
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ // If this is a composite member, see if it is summarily
+ // added/deleted.
+ //
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ // If the addition/deletion version is the same as the section's,
+ // then we don't need the test.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")";
+ }
+
+ os << "{";
+
+ if (mi.ptr != 0 && view_member (mi.m))
+ return true; // That's enough for the object pointer in view.
+
+ // Set the member using the modifier expression.
+ //
+ member_access& ma (mi.m.template get<member_access> ("set"));
+
+ // Make sure this kind of member can be modified with this
+ // kind of accessor (database-specific, e.g., streaming).
+ //
+ if (comp == 0)
+ check_modifier (mi, ma);
+
+ // If this is not a synthesized expression, then output
+ // its location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ // See if we are modifying via a reference or proper modifier.
+ //
+ if (ma.placeholder ())
+ os << member_val_type (mi.m, false, "v") << ";"
+ << endl;
+ else
+ {
+ // Use the original type to form the reference. VC++ cannot
+ // grok the constructor syntax.
+ //
+ os << member_ref_type (mi.m, false, "v") << " =" << endl
+ << " ";
+
+ // If this member is const and we have a synthesized direct
+ // access, then cast away constness. Otherwise, we assume
+ // that the user-provided expression handles this.
+ //
+ bool cast (mi.cq && ma.direct ());
+ if (cast)
+ os << "const_cast< " << member_ref_type (mi.m, false) <<
+ " > (" << endl;
+
+ os << ma.translate ("o");
+
+ if (cast)
+ os << ")";
+
+ os << ";"
+ << endl;
+ }
+
+ member = "v";
+ }
+
+ // Translate.
+ //
+ if (mi.ct != 0)
+ {
+ os << type_val_type (*mi.ct->as, mi.ct->as_hint, false, "vt") << ";"
+ << endl;
+
+ translate_member = member;
+ member = "vt";
+ }
+
+ // If this is a wrapped composite value, then we need to "unwrap" it.
+ // If this is a NULL wrapper, then we also need to handle that. For
+ // simple values this is taken care of by the value_traits
+ // specializations.
+ //
+ if (mi.wrapper != 0 && comp != 0)
+ {
+ // The wrapper type, not the wrapped type.
+ //
+ string const& wt (mi.fq_type (false));
+
+ // If this is a NULL wrapper and the member can be NULL, then
+ // we need to handle the NULL value.
+ //
+ if (null (mi.m, key_prefix_) &&
+ mi.wrapper->template get<bool> ("wrapper-null-handler"))
+ {
+ os << "if (composite_value_traits< " << mi.fq_type () <<
+ ", id_" << db << " >::get_null (" << endl
+ << "i." << mi.var << "value" <<
+ (versioned (*comp) ? ", svm" : "") << "))" << endl
+ << "wrapper_traits< " << wt << " >::set_null (" << member + ");"
+ << "else"
+ << "{";
+ }
+
+ os << mi.fq_type () << "& vw =" << endl
+ << " wrapper_traits< " + wt + " >::set_ref (" + member + ");"
+ << endl;
+
+ wrap_member = member;
+ member = "vw";
+ }
+
+ if (mi.ptr != 0)
+ {
+ type = "obj_traits::id_type";
+
+ // Handle NULL pointers and extract the id.
+ //
+ os << "typedef object_traits< " << class_fq_name (*mi.ptr) <<
+ " > obj_traits;"
+ << "typedef odb::pointer_traits< " << mi.ptr_fq_type () <<
+ " > ptr_traits;"
+ << endl;
+
+ os << "if (";
+
+ if (comp != 0)
+ os << "composite_value_traits< " << type << ", id_" << db <<
+ " >::get_null (" << endl
+ << "i." << mi.var << "value" <<
+ (versioned (*comp) ? ", svm" : "") << ")";
+ else
+ get_null (mi.var);
+
+ os << ")" << endl;
+
+ // Don't throw null_pointer if we can't have NULLs and the pointer
+ // is NULL since this can be useful during migration. Instead, we
+ // rely on the database enforcing this.
+ //
+ os << member << " = ptr_traits::pointer_type ();";
+
+ os << "else"
+ << "{";
+
+ os << type << " ptr_id;";
+
+ member = "ptr_id";
+ }
+ else
+ type = mi.fq_type ();
+
+ if (comp != 0)
+ traits = "composite_value_traits< " + type + ", id_" +
+ db.string () + " >";
+ else
+ {
+ db_type_id = member_database_type_id_->database_type_id (mi.m);
+ traits = db.string () + "::value_traits<\n "
+ + type + ",\n "
+ + db_type_id + " >";
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ if (mi.ptr != 0)
+ {
+ if (view_member (mi.m))
+ {
+ // The object pointer in view doesn't need any of this.
+ os << "}";
+ return;
+ }
+
+ // Restore the member variable name.
+ //
+ member = member_override_.empty () ? "v" : member_override_;
+
+ // When handling a pointer, mi.t is the id type of the referenced
+ // object.
+ //
+ semantics::type& pt (utype (mi.m, key_prefix_));
+
+ if (lazy_pointer (pt))
+ os << member << " = ptr_traits::pointer_type (" << endl
+ << "*static_cast<" << db << "::database*> (db), ptr_id);";
+ else
+ {
+ os << "// If a compiler error points to the line below, then" << endl
+ << "// it most likely means that a pointer used in a member" << endl
+ << "// cannot be initialized from an object pointer." << endl
+ << "//" << endl
+ << member << " = ptr_traits::pointer_type (" << endl
+ << "static_cast<" << db << "::database*> (db)->load<" << endl
+ << " obj_traits::object_type > (ptr_id));";
+
+ // If we are loading into an eager weak pointer, make sure there
+ // is someone else holding a strong pointer to it (normally a
+ // session). Otherwise, the object will be loaded and immediately
+ // deleted. Besides not making much sense, this also breaks the
+ // delayed loading machinery which expects the object to be around
+ // at least until the top-level load() returns.
+ //
+ if (weak_pointer (pt))
+ {
+ os << endl
+ << "if (odb::pointer_traits<" <<
+ "ptr_traits::strong_pointer_type>::null_ptr (" << endl
+ << "ptr_traits::lock (" << member << ")))" << endl
+ << "throw session_required ();";
+ }
+ }
+
+ os << "}";
+ }
+
+ // Wrap back (so to speak).
+ //
+ if (mi.wrapper != 0 && composite (mi.t) != 0)
+ {
+ if (null (mi.m, key_prefix_) &&
+ mi.wrapper->template get<bool> ("wrapper-null-handler"))
+ os << "}";
+
+ member = wrap_member;
+ }
+
+ // Untranslate.
+ //
+ if (mi.ct != 0)
+ {
+ //@@ Use move() in C++11? Or not.
+ //
+ os << "// From " << location_string (mi.ct->loc, true) << endl
+ << translate_member << " = " <<
+ mi.ct->translate_from (member) << ";";
+
+ member = translate_member;
+ }
+
+ // Call the modifier if we are using a proper one.
+ //
+ if (member_override_.empty ())
+ {
+ member_access& ma (mi.m.template get<member_access> ("set"));
+
+ if (ma.placeholder ())
+ {
+ // If this is not a synthesized expression, then output its
+ // location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << ma.translate (
+ "o", "v", "*static_cast<" + db.string () + "::database*> (db)")
+ << ";";
+ }
+ }
+
+ os << "}";
+ }
+
+ virtual void
+ traverse_pointer (member_info& mi)
+ {
+ // Object pointers in views require special treatment.
+ //
+ if (view_member (mi.m))
+ {
+ // This is the middle part. The pre and post parts are generated
+ // by init_view_pointer_member below.
+ //
+ using semantics::class_;
+
+ class_& c (*mi.ptr);
+ class_* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ string o_tp (mi.var + "object_type");
+ string o_tr (mi.var + "object_traits");
+ string r_tr (poly_derived ? mi.var + "root_traits" : o_tr);
+ string i_tp (mi.var + "info_type");
+
+ string id (mi.var + "id");
+ string o (mi.var + "o");
+ string pi (mi.var + "pi"); // Polymorphic type info.
+
+ // If load_() will be loading containers or the rest of the
+ // polymorphic object, then we need to perform several extra
+ // things. We need to initialize the id image in the object
+ // statements. We also have to lock the statements so that
+ // nobody messes up this id image.
+ //
+ bool init_id (
+ poly ||
+ has_a (c, test_container | include_eager_load, &main_section));
+
+ bool versioned (context::versioned (c));
+
+ os << "if (" << o << " != 0)"
+ << "{";
+
+ if (poly)
+ os << "callback_event ce (callback_event::pre_load);"
+ << pi << "->dispatch (" << i_tp << "::call_callback, " <<
+ "*db, " << o << ", &ce);";
+ else
+ os << o_tr << "::callback (*db, *" << o <<
+ ", callback_event::pre_load);";
+
+ os << o_tr << "::init (*" << o << ", i." << mi.var << "value, db" <<
+ (versioned ? ", svm" : "") << ");";
+
+ // Call load_() to load the rest of the object (containers, etc).
+ //
+ if (id_member (poly ? *poly_root : c) != 0)
+ {
+ const char* s (poly_derived ? "osts" : "sts");
+
+ os << o_tr << "::statements_type& " << s << " (" << endl
+ << "conn.statement_cache ().find_object<" << o_tp << "> ());";
+
+ if (poly_derived)
+ os << r_tr << "::statements_type& sts (osts.root_statements ());";
+
+ if (init_id)
+ {
+ // This can only be top-level call so lock must succeed.
+ //
+ os << r_tr << "::statements_type::auto_lock l (sts);"
+ << "assert (l.locked ()) /* Must be a top-level call. */;"
+ << endl
+ << r_tr << "::id_image_type& i (sts.id_image ());"
+ << r_tr << "::init (i, " << id << ");"
+ << db << "::binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || " <<
+ "idb.version == 0)"
+ << "{"
+ << r_tr << "::bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (optimistic (poly ? *poly_root : c) != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+ }
+
+ os << o_tr << "::load_ (" << s << ", *" << o << ", false" <<
+ (versioned ? ", svm" : "") << ");";
+
+ // Load the dynamic part of the object unless static and dynamic
+ // types are the same.
+ //
+ if (poly)
+ os << endl
+ << "if (" << pi << " != &" << o_tr << "::info)"
+ << "{"
+ << "std::size_t d (" << o_tr << "::depth);"
+ << pi << "->dispatch (" << i_tp << "::call_load, *db, " <<
+ o << ", &d);"
+ << "}";
+
+ if (init_id)
+ os << "sts.load_delayed (" << (versioned ? "&svm" : "0") << ");"
+ << "l.unlock ();";
+ }
+
+ os << "}";
+ }
+ else
+ member_base_impl<T>::traverse_pointer (mi);
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ os << traits << "::init (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "db";
+
+ if (versioned (*composite (mi.t)))
+ os << "," << endl
+ << "svm";
+
+ os << ");"
+ << endl;
+ }
+
+ protected:
+ string type;
+ string db_type_id;
+ string traits;
+ string member;
+ string translate_member; // Untranslated member.
+ string wrap_member; // Wrapped member.
+
+ instance<member_database_type_id> member_database_type_id_;
+ };
+
+ // This class generates the pre and post parts. The middle part is
+ // generated by init_value_member above.
+ //
+ struct init_view_pointer_member: virtual member_base,
+ member_base_impl<bool> // Dummy SQL type.
+ {
+ typedef init_view_pointer_member base;
+
+ init_view_pointer_member (bool pre, init_value_member const& ivm)
+ : member_base (0, 0, string (), string (), 0),
+ pre_ (pre), init_value_member_ (ivm) {}
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ // Only interested in object pointers inside views.
+ //
+ return mi.ptr != 0 && view_member (mi.m);
+ }
+
+ virtual void
+ traverse_pointer (member_info& mi)
+ {
+ using semantics::class_;
+
+ class_& c (*mi.ptr);
+ bool abst (abstract (c));
+ class_* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+ size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1);
+
+ data_member_path* idm (id_member (poly ? *poly_root : c));
+
+ os << "// " << mi.m.name () << (pre_ ? " pre" : " post") << endl
+ << "//" << endl;
+
+ string o_tp (mi.var + "object_type");
+ string o_tr (mi.var + "object_traits");
+ string r_tr (poly_derived ? mi.var + "root_traits" : o_tr);
+ string i_tp (mi.var + "info_type");
+ string p_tp (mi.var + "pointer_type");
+ string p_tr (mi.var + "pointer_traits");
+ string c_tr (mi.var + "cache_traits");
+
+ string id (mi.var + "id"); // Object id.
+ string p (mi.var + "p"); // Object pointer.
+ string pg (mi.var + "pg"); // Pointer guard.
+ string ig (mi.var + "ig"); // Cache insert guard.
+ string o (mi.var + "o"); // Object.
+ string pi (mi.var + "pi"); // Polymorphic type info.
+
+ bool op_raw (c.get<bool> ("object-pointer-raw"));
+ bool mp_raw (utype (mi.m).is_a<semantics::pointer> ());
+
+ // Output aliases and variables before any schema version if-
+ // blocks since we need to be able to access them across all
+ // three parts.
+ //
+ if (pre_)
+ {
+ os << "typedef " << class_fq_name (c) << " " << o_tp << ";"
+ << "typedef object_traits_impl<" << o_tp << ", id_" << db <<
+ "> " << o_tr << ";";
+
+ if (poly_derived)
+ os << "typedef " << o_tr << "::root_traits " << r_tr << ";";
+
+ if (poly)
+ os << "typedef " << r_tr << "::info_type " << i_tp << ";";
+
+ os << "typedef " << r_tr << "::pointer_type " << p_tp << ";"
+ << "typedef " << r_tr << "::pointer_traits " << p_tr << ";";
+ if (idm != 0)
+ os << "typedef " << r_tr << "::pointer_cache_traits " <<
+ c_tr << ";";
+ os << endl;
+
+ if (idm != 0)
+ os << r_tr << "::id_type " << id << ";";
+ os << p_tp << " " << p << (op_raw ? " = 0" : "") << ";" // VC++
+ << p_tr << "::guard " << pg << ";";
+ if (idm != 0)
+ os << c_tr << "::insert_guard " << ig << ";";
+ os << o_tp << "* " << o << " (0);";
+
+ if (poly)
+ os << "const " << i_tp << "* " << pi << " = 0;"; // VC++
+
+ os << endl;
+ }
+
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")";
+ }
+
+ os << "{";
+
+ if (pre_)
+ {
+ string id_im;
+ if (idm != 0)
+ {
+ // Check for NULL.
+ //
+ string id_var;
+ {
+ id_im = mi.var + "value";
+
+ // In a polymorphic class, the id is in the root image.
+ //
+ for (size_t i (0); i < poly_depth - 1; ++i)
+ id_im += (i == 0 ? ".base" : "->base");
+
+ string n;
+ for (data_member_path::const_iterator i (idm->begin ());
+ i != idm->end ();
+ ++i)
+ {
+ // The same logic as in member_base.
+ //
+ if (!n.empty ())
+ n += "value."; // Composite.
+
+ string const& name ((*i)->name ());
+ n += name;
+
+ if (n[n.size () - 1] != '_')
+ n += '_';
+ }
+
+ id_var = id_im + (poly_derived ? "->" : ".") + n;
+ id_im = (poly_derived ? "*i." : "i.") + id_im;
+ }
+
+ os << "if (";
+
+ if (semantics::class_* comp = composite (mi.t))
+ os << "!composite_value_traits< " << o_tr << "::id_type, id_" <<
+ db << " >::get_null (" << endl
+ << "i." << id_var << "value" <<
+ (versioned (*comp) ? ", svm" : "") << ")";
+ else
+ {
+ os << "!(";
+ init_value_member_.get_null (id_var);
+ os << ")";
+ }
+
+ os << ")"
+ << "{";
+
+ // Check cache.
+ //
+ os << id << " = " << r_tr << "::id (" << id_im << ");"
+ << p << " = " << c_tr << "::find (*db, " << id << ");"
+ << endl;
+
+ os << "if (" << p_tr << "::null_ptr (" << p << "))"
+ << "{";
+ }
+
+ // To support by-value object loading, we are going to load
+ // into an existing instance if the pointer is already not
+ // NULL. To limit the potential misuse (especially when it
+ // comes to sessions), we are going to limit this support
+ // only to raw pointers. Furthermore, we will only insert
+ // such an object into the cache if its object pointer is
+ // also raw.
+ //
+ if (mp_raw && !poly)
+ {
+ // Get the member using the accessor expression.
+ //
+ member_access& ma (mi.m.get<member_access> ("get"));
+
+ // If this is not a synthesized expression, then output
+ // its location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ // Use the original type to form the const reference. VC++
+ // cannot grok the constructor syntax.
+ //
+ os << member_ref_type (mi.m, true, "m") << " =" << endl
+ << " " << ma.translate ("o") << ";"
+ << endl;
+
+ os << "if (m != 0)"
+ << "{";
+
+ if (op_raw)
+ os << ig << ".reset (" << c_tr << "::insert (*db, " << id <<
+ ", m));";
+
+ os << o << " = m;"
+ << "}"
+ << "else"
+ << "{";
+ }
+
+ if (poly)
+ {
+ os << r_tr << "::discriminator_type d (" << endl
+ << r_tr << "::discriminator (" << id_im << "));";
+
+ if (abst)
+ os << pi << " = &" << r_tr << "::map->find (d);";
+ else
+ os << pi << " = (d == " << o_tr << "::info.discriminator" << endl
+ << "? &" << o_tr << "::info" << endl
+ << ": &" << r_tr << "::map->find (d));";
+
+ os << p << " = " << pi << "->create ();";
+ }
+ else
+ os << p << " = object_factory<" << o_tp << ", " << p_tp <<
+ ">::create ();";
+
+ os << pg << ".reset (" << p << ");";
+ if (idm != 0)
+ os << ig << ".reset (" << c_tr << "::insert (*db, " << id <<
+ ", " << p << "));";
+
+ if (poly_derived)
+ os << o << " = static_cast<" << o_tp << "*> (" << p_tr <<
+ "::get_ptr (" << p << "));";
+ else
+ os << o << " = " << p_tr << "::get_ptr (" << p << ");";
+
+ if (mp_raw && !poly)
+ os << "}";
+
+ if (idm != 0)
+ os << "}" // Cache.
+ << "}"; // NULL.
+ }
+ else
+ {
+ os << "if (" << o << " != 0)"
+ << "{";
+
+ if (poly)
+ os << "callback_event ce (callback_event::post_load);"
+ << pi << "->dispatch (" << i_tp << "::call_callback, " <<
+ "*db, " << o << ", &ce);";
+ else
+ os << o_tr << "::callback (*db, *" << o <<
+ ", callback_event::post_load);";
+
+ if (idm != 0)
+ {
+ if (mp_raw && !op_raw && !poly)
+ os << "if (!" << p_tr << "::null_ptr (" << p << "))"
+ << "{";
+
+ os << c_tr << "::load (" << ig << ".position ());"
+ << ig << ".release ();";
+
+ if (mp_raw && !op_raw && !poly)
+ os << "}";
+ }
+
+ os << pg << ".release ();";
+
+ os << "}";
+
+ // If member pointer is not raw, then result is in p.
+ // If both member and object are raw, then result is in o.
+ // If member is raw but object is not, then result is in
+ // p if it is not NULL, and in o (either NULL or the same
+ // as the member value) otherwise.
+ //
+ member_access& ma (mi.m.get<member_access> ("set"));
+
+ if (ma.empty () && !poly)
+ {
+ // It is ok to have empty modifier expression as long as
+ // the member pointer is raw. This is the by-value load
+ // and the user is not interested in learning whether the
+ // object is NULL.
+ //
+ if (!mp_raw)
+ {
+ error (ma.loc) << "non-empty modifier expression required " <<
+ "for loading an object via a smart pointer" << endl;
+ throw operation_failed ();
+ }
+
+ os << "// Empty modifier expression was specified for this\n"
+ << "// object so make sure we have actually loaded the\n"
+ << "// data into the existing instance rather than, say,\n"
+ << "// finding the object in the cache or creating a new one.\n"
+ << "//\n"
+ << "assert (" << p_tr << "::null_ptr (" << p << "));";
+ }
+ else
+ {
+ if (!(mp_raw && op_raw) || poly)
+ {
+ string r (options.std () >= cxx_version::cxx11
+ ? "std::move (" + p + ")"
+ : p);
+
+ if (poly_derived)
+ // This pointer could have come from cache, so use dynamic
+ // cast.
+ //
+ r = p_tr + "::dynamic_pointer_cast<" + o_tp + "> (\n" +
+ r + ")";
+
+ // Unless the pointer is raw, explicitly construct the
+ // smart pointer from the object pointer so that we get
+ // the behavior similar to calling database::load() (in
+ // both cases we are the "ownership end-points"; unless
+ // the object was already in the session before loading
+ // this view (in which case using raw pointers as object
+ // pointers is a really stupid idea), this logic will do
+ // the right thing and what the user most likely expects.
+ //
+ if (!mp_raw)
+ r = member_val_type (mi.m, false) + " (\n" + r + ")";
+
+ if (mp_raw && !op_raw)
+ os << "if (!" << p_tr << "::null_ptr (" << p << "))" << endl;
+
+ os << "// If a compiler error points to the line below, then\n"
+ << "// it most likely means that a pointer used in view\n"
+ << "// member cannot be initialized from an object pointer.\n"
+ << "//" << endl;
+
+ set_member (mi.m, "o", r, "db");
+ }
+
+ if (mp_raw && !poly)
+ {
+ if (!op_raw)
+ os << "else" << endl; // NULL p
+
+ set_member (mi.m, "o", o, "db");
+ }
+ }
+ }
+
+ os << "}";
+ }
+
+ virtual bool const&
+ member_sql_type (semantics::data_member&) {return pre_;};
+
+ protected:
+ bool pre_;
+ init_value_member const& init_value_member_;
+ };
+
+ struct init_value_base: traversal::class_, virtual context
+ {
+ typedef init_value_base base;
+
+ virtual void
+ traverse (type& c)
+ {
+ bool obj (object (c));
+
+ // Ignore transient bases. Not used for views.
+ //
+ if (!(obj || composite (c)))
+ return;
+
+ os << "// " << class_name (c) << " base" << endl
+ << "//" << endl;
+
+ if (obj)
+ os << "object_traits_impl< ";
+ else
+ os << "composite_value_traits< ";
+
+ os << class_fq_name (c) << ", id_" << db << " >::init (o, i, db" <<
+ (versioned (c) ? ", svm" : "") << ");"
+ << endl;
+ }
+ };
+
+ // Member-specific traits types for container members.
+ //
+ struct container_traits: object_members_base, virtual context
+ {
+ typedef container_traits base;
+
+ container_traits (semantics::class_& c)
+ : object_members_base (
+ true,
+ object (c), // Only build table prefix for objects.
+ false),
+ c_ (c)
+ {
+ scope_ = object (c)
+ ? "access::object_traits_impl< "
+ : "access::composite_value_traits< ";
+
+ scope_ += class_fq_name (c) + ", id_" + db.string () + " >";
+ }
+
+ // Unless the database system can execute several interleaving
+ // statements, cache the result set.
+ //
+ virtual void
+ cache_result (string const& statement)
+ {
+ os << statement << ".cache ();";
+ }
+
+ // Additional code that need to be executed following the call to
+ // init_value.
+ //
+ virtual void
+ init_value_extra ()
+ {
+ }
+
+ virtual void
+ process_statement_columns (statement_columns&,
+ statement_kind,
+ bool /*dynamic*/)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&)
+ {
+ // We don't want to traverse composite id.
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ if (object (c_))
+ object_members_base::traverse_composite (m, c);
+ else
+ {
+ // If we are generating traits for a composite value type, then
+ // we don't want to go into its bases or it composite members.
+ //
+ if (m == 0 && &c == &c_)
+ names (c);
+ }
+ }
+
+ virtual void
+ container_extra (semantics::data_member&, semantics::type&)
+ {
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type& t)
+ {
+ using semantics::type;
+
+ // Figure out if this member is from a base object or composite
+ // value and if it's from an object, whether it is reuse-abstract.
+ //
+ bool base, reuse_abst;
+
+ if (object (c_))
+ {
+ base = cur_object != &c_ ||
+ !object (dynamic_cast<type&> (m.scope ()));
+ reuse_abst = abstract (c_) && !polymorphic (c_);
+ }
+ else
+ {
+ base = false; // We don't go into bases.
+ reuse_abst = true; // Always abstract.
+ }
+
+ container_kind_type ck (container_kind (t));
+
+ const custom_cxx_type* vct (0);
+ const custom_cxx_type* ict (0);
+ const custom_cxx_type* kct (0);
+
+ type& vt (container_vt (m, &vct));
+ type* it (0);
+ type* kt (0);
+
+ data_member_path* imp (context::inverse (m, "value"));
+
+ bool ordered (false);
+ bool inverse (imp != 0);
+ bool grow (false);
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ {
+ it = &container_it (m, &ict);
+ ordered = true;
+
+ if (generate_grow)
+ grow = grow || context::grow (m, *it, ict, "index");
+ }
+
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ kt = &container_kt (m, &kct);
+
+ if (generate_grow)
+ grow = grow || context::grow (m, *kt, kct, "key");
+
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ bool smart (!inverse &&
+ (ck != ck_ordered || ordered) &&
+ container_smart (t));
+
+ if (generate_grow)
+ grow = grow || context::grow (m, vt, vct, "value");
+
+ bool eager_ptr (is_a (member_path_,
+ member_scope_,
+ test_eager_pointer,
+ vt,
+ "value"));
+ if (!eager_ptr)
+ {
+ if (semantics::class_* cvt = composite_wrapper (vt))
+ eager_ptr = has_a (*cvt, test_eager_pointer);
+ }
+
+ bool versioned (context::versioned (m));
+
+ string name (flat_prefix_ + public_name (m) + "_traits");
+ string scope (scope_ + "::" + name);
+
+ os << "// " << m.name () << endl
+ << "//" << endl
+ << endl;
+
+ container_extra (m, t);
+
+ //
+ // Statements.
+ //
+ if (!reuse_abst)
+ {
+ string sep (versioned ? "\n" : " ");
+
+ semantics::type& idt (container_idt (m));
+
+ qname table (table_name (m, table_prefix_));
+ string qtable (quote_id (table));
+ instance<object_columns_list> id_cols;
+ instance<object_columns_list> ik_cols; // index/key columns
+
+ if (smart)
+ {
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ ik_cols->traverse (m, *it, "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+ }
+
+ // select_statement
+ //
+ os << "const char " << scope << "::" << endl
+ << "select_statement[] =" << endl;
+
+ if (inverse)
+ {
+ semantics::class_* c (object_pointer (vt));
+ semantics::data_member& imf (*imp->front ());
+ semantics::data_member& imb (*imp->back ());
+
+ // In a polymorphic hierarchy the inverse member can be in
+ // the base class, in which case we should use that class
+ // for the table name, etc.
+ //
+ if (polymorphic (*c))
+ c = &dynamic_cast<semantics::class_&> (imf.scope ());
+
+ data_member_path& inv_id (*id_member (*c));
+
+ qname inv_table; // Other table name.
+ string inv_qtable;
+ instance<object_columns_list> inv_id_cols; // Other id column.
+ instance<object_columns_list> inv_fid_cols; // Other foreign id
+ // column (ref to us).
+ statement_columns sc;
+
+ if (container (imb))
+ {
+ // many(i)-to-many
+ //
+
+ // This other container is a direct member of the class so the
+ // table prefix is just the class table name.
+ //
+ inv_table = table_name (*c, *imp);
+ inv_qtable = quote_id (inv_table);
+
+ inv_id_cols->traverse (imb, utype (inv_id), "id", "object_id", c);
+ inv_fid_cols->traverse (imb, idt, "value", "value");
+
+ for (object_columns_list::iterator i (inv_id_cols->begin ());
+ i != inv_id_cols->end (); ++i)
+ {
+ // If this is a simple id, then pass the "id" key prefix. If
+ // it is a composite id, then the members have no prefix.
+ //
+ sc.push_back (
+ statement_column (
+ inv_qtable,
+ convert_from (inv_qtable + "." + quote_id (i->name),
+ i->type,
+ *i->member),
+ i->type,
+ *i->member,
+ inv_id_cols->size () == 1 ? "id" : ""));
+ }
+ }
+ else
+ {
+ // many(i)-to-one
+ //
+ inv_table = table_name (*c);
+ inv_qtable = quote_id (inv_table);
+
+ inv_id_cols->traverse (inv_id);
+ inv_fid_cols->traverse (imb, column_prefix (*imp));
+
+ for (object_columns_list::iterator i (inv_id_cols->begin ());
+ i != inv_id_cols->end (); ++i)
+ {
+ sc.push_back (
+ statement_column (
+ inv_qtable,
+ convert_from (inv_qtable + "." + quote_id (i->name),
+ i->type,
+ *i->member),
+ i->type,
+ *i->member));
+ }
+ }
+
+ process_statement_columns (sc, statement_select, versioned);
+
+ os << strlit ("SELECT" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ instance<query_parameters> qp (statement_select, inv_table);
+ os << strlit ("FROM " + inv_qtable + sep) << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (inv_fid_cols->begin ()),
+ i (b); i != inv_fid_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += inv_qtable + "." + quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+ os << strlit (where);
+ }
+ else
+ {
+ id_cols->traverse (m, idt, "id", "object_id");
+
+ statement_columns sc;
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ instance<object_columns> t (qtable, sk, sc);
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ t->traverse (m, *it, "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ t->traverse (m, *kt, "key", "key");
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ t->traverse (m, vt, "value", "value");
+
+ process_statement_columns (sc, statement_select, versioned);
+
+ os << strlit ("SELECT" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ instance<query_parameters> qp (statement_select, table);
+ os << strlit ("FROM " + qtable + sep) << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += qtable + "." + quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ if (ordered)
+ {
+ // Top-level column.
+ //
+ string const& col (
+ column_qname (m, "index", "index", column_prefix ()));
+
+ where += " ORDER BY " + qtable + "." + col;
+ }
+
+ os << strlit (where);
+ }
+
+ os << ";"
+ << endl;
+
+ // insert_statement
+ //
+ os << "const char " << scope << "::" << endl
+ << "insert_statement[] =" << endl;
+
+ if (inverse)
+ os << strlit ("") << ";"
+ << endl;
+ else
+ {
+ statement_columns sc;
+ statement_kind sk (statement_insert); // Imperfect forwarding.
+ instance<object_columns> t (sk, sc);
+
+ t->traverse (m, idt, "id", "object_id");
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ t->traverse (m, *it, "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ t->traverse (m, *kt, "key", "key");
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ t->traverse (m, vt, "value", "value");
+
+ process_statement_columns (sc, statement_insert, versioned);
+
+ os << strlit ("INSERT INTO " + qtable + sep) << endl;
+
+ for (statement_columns::const_iterator b (sc.begin ()), i (b),
+ e (sc.end ()); i != e;)
+ {
+ string s;
+
+ if (i == b)
+ s += '(';
+ s += i->column;
+ s += (++i != e ? ',' : ')');
+ s += sep;
+
+ os << strlit (s) << endl;
+ }
+
+ os << strlit ("VALUES" + sep) << endl;
+
+ string values ("(");
+ instance<query_parameters> qp (statement_insert, table);
+ for (statement_columns::const_iterator b (sc.begin ()), i (b),
+ e (sc.end ()); i != e; ++i)
+ {
+ if (i != b)
+ {
+ values += ',';
+ values += sep;
+ }
+
+ values += convert_to (qp->next (*i), i->type, *i->member);
+ }
+ values += ')';
+
+ os << strlit (values) << ";"
+ << endl;
+ }
+
+ // update_statement
+ //
+ if (smart)
+ {
+ os << "const char " << scope << "::" << endl
+ << "update_statement[] =" << endl
+ << strlit ("UPDATE " + qtable + sep) << endl
+ << strlit ("SET" + sep) << endl;
+
+ instance<query_parameters> qp (statement_update, table);
+ statement_columns sc;
+ {
+ bool f (false); // Imperfect forwarding.
+ query_parameters* p (qp.get ()); // Imperfect forwarding.
+ statement_kind sk (statement_update); // Imperfect forwarding.
+ instance<object_columns> t (sk, f, sc, p);
+ t->traverse (m, vt, "value", "value");
+ process_statement_columns (sc, statement_update, versioned);
+ }
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ for (object_columns_list::iterator b (ik_cols->begin ()), i (b);
+ i != ik_cols->end (); ++i)
+ {
+ where += " AND " + quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+
+ // delete_statement
+ //
+ os << "const char " << scope << "::" << endl
+ << "delete_statement[] =" << endl;
+
+ if (inverse)
+ os << strlit ("") << ";"
+ << endl;
+ else
+ {
+ instance<query_parameters> qp (statement_delete, table);
+
+ os << strlit ("DELETE FROM " + qtable + " ") << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ if (smart)
+ {
+ for (object_columns_list::iterator b (ik_cols->begin ()), i (b);
+ i != ik_cols->end (); ++i)
+ {
+ where += " AND " + quote_id (i->name) +
+ (ck == ck_ordered ? ">=" : "=") +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+ }
+
+ if (base)
+ return;
+
+ //
+ // Functions.
+ //
+
+ // bind (cond_image_type)
+ //
+ if (smart)
+ {
+ os << "void " << scope << "::" << endl
+ << "bind (" << bind_vector << " b," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "cond_image_type& c)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "statement_kind sk (statement_select);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl
+ << "std::size_t n (0);"
+ << endl;
+
+ os << "// object_id" << endl
+ << "//" << endl
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
+ << endl;
+
+ // We don't need to update the bind index since this is the
+ // last element.
+ //
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "index_", "c", *it, ict, "index_type", "index");
+ bm->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "key_", "c", *kt, kct, "key_type", "key");
+ bm->traverse (m);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "// value" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "value_", "c", vt, vct, "value_type", "value");
+ bm->traverse (m);
+ break;
+ }
+ }
+ os << "}";
+ }
+
+ // bind (data_image_type)
+ //
+ {
+ os << "void " << scope << "::" << endl
+ << "bind (" << bind_vector << " b," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "data_image_type& d";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ // In the case of containers, insert and select column sets are
+ // the same since we can't have inverse members as container
+ // elements.
+ //
+ << "statement_kind sk (statement_select);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl
+ << "size_t n (0);"
+ << endl;
+
+ os << "// object_id" << endl
+ << "//" << endl
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
+ << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "index_", "d", *it, ict, "index_type", "index");
+ bm->traverse (m);
+ os << "n++;" // Simple value.
+ << endl;
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "key_", "d", *kt, kct, "key_type", "key");
+ bm->traverse (m);
+
+ if (semantics::class_* c = composite_wrapper (*kt))
+ os << "n += " << column_count (*c).total << "UL;"
+ << endl;
+ else
+ os << "n++;"
+ << endl;
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ // We don't need to update the bind index since this is the
+ // last element.
+ //
+ os << "// value" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "value_", "d", vt, vct, "value_type", "value");
+ bm->traverse (m);
+
+ os << "}";
+ }
+
+ // bind (cond_image, data_image) (update)
+ //
+ if (smart)
+ {
+ os << "void " << scope << "::" << endl
+ << "bind (" << bind_vector << " b," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "cond_image_type& c," << endl
+ << "data_image_type& d";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ // Use insert instead of update to include read-only members.
+ //
+ << "statement_kind sk (statement_insert);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl
+ << "std::size_t n (0);"
+ << endl;
+
+ os << "// value" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "value_", "d", vt, vct, "value_type", "value");
+ bm->traverse (m);
+
+ if (semantics::class_* c = composite_wrapper (vt))
+ os << "n += " << column_count (*c).total << "UL;"
+ << endl;
+ else
+ os << "n++;"
+ << endl;
+
+ os << "// object_id" << endl
+ << "//" << endl
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
+ << endl;
+
+ // We don't need to update the bind index since this is the
+ // last element.
+ //
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "index_", "c", *it, ict, "index_type", "index");
+ bm->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "key_", "c", *kt, kct, "key_type", "key");
+ bm->traverse (m);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "// value" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "value_", "c", vt, vct, "value_type", "value");
+ bm->traverse (m);
+ break;
+ }
+ }
+ os << "}";
+ }
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ size_t index (0);
+
+ os << "void " << scope << "::" << endl
+ << "grow (data_image_type& i," << endl
+ << truncated_vector << " t";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "bool grew (false);"
+ << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<grow_member> gm (
+ index, "index_", *it, ict, "index_type", "index");
+ gm->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<grow_member> gm (
+ index, "key_", *kt, kct, "key_type", "key");
+ gm->traverse (m);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << "// value" << endl
+ << "//" << endl;
+ instance<grow_member> gm (
+ index, "value_", vt, vct, "value_type", "value");
+ gm->traverse (m);
+
+ os << "if (grew)" << endl
+ << "i.version++;"
+ << "}";
+ }
+
+ // init (data_image)
+ //
+ if (!inverse)
+ {
+ os << "void " << scope << "::" << endl
+ << "init (data_image_type& i," << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "index_type* j," << endl;
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "const key_type* k," << endl;
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "const value_type& v";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "statement_kind sk (statement_insert);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl;
+
+ if (generate_grow)
+ os << "bool grew (false);"
+ << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl
+ << "if (j != 0)";
+
+ instance<init_image_member> im (
+ "index_", "*j", *it, ict, "index_type", "index");
+ im->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl
+ << "if (k != 0)";
+
+ instance<init_image_member> im (
+ "key_", "*k", *kt, kct, "key_type", "key");
+ im->traverse (m);
+
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << "// value" << endl
+ << "//" << endl;
+ {
+ instance<init_image_member> im (
+ "value_", "v", vt, vct, "value_type", "value");
+ im->traverse (m);
+ }
+
+ if (generate_grow)
+ os << "if (grew)" << endl
+ << "i.version++;";
+
+ os << "}";
+ }
+
+ // init (cond_image)
+ //
+ if (smart)
+ {
+ os << "void " << scope << "::" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "init (cond_image_type& i, index_type j)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "statement_kind sk (statement_select);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl;
+
+ instance<init_image_member> im (
+ "index_", "j", *it, ict, "index_type", "index");
+ im->traverse (m);
+
+ os << "}";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ // Need to handle growth.
+ //
+ // os << "init (data_image_type&, const key_type&);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ // Need to handle growth.
+ //
+ // os << "init (data_image_type&, const value_type&);";
+ break;
+ }
+ }
+
+ os << endl;
+ }
+
+ // init (data)
+ //
+ os << "void " << scope << "::" << endl
+ << "init (";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "index_type& j," << endl;
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "key_type& k," << endl;
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "value_type& v," << endl;
+ os << "const data_image_type& i," << endl
+ << "database* db";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+
+ instance<init_value_member> im (
+ "index_", "j", *it, ict, "index_type", "index");
+ im->traverse (m);
+ }
+
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+
+ instance<init_value_member> im (
+ "key_", "k", *kt, kct, "key_type", "key");
+ im->traverse (m);
+
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "// value" << endl
+ << "//" << endl;
+ {
+ // If the value is an object pointer, pass the id type as a
+ // type override.
+ //
+ instance<init_value_member> im (
+ "value_", "v", vt, vct, "value_type", "value");
+ im->traverse (m);
+ }
+ os << "}";
+
+ // insert
+ //
+ {
+ string ia, ka, va, da;
+
+ if (!inverse)
+ {
+ ia = ordered ? " i" : "";
+ ka = " k";
+ va = " v";
+ da = " d";
+ }
+
+ os << "void " << scope << "::" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "insert (index_type" << ia << ", " <<
+ "const value_type&" << va << ", " <<
+ "void*" << da << ")";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "insert (const key_type&" << ka << ", " <<
+ "const value_type&" << va << ", " <<
+ "void*" << da << ")";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "insert (const value_type&" << va << ", " <<
+ "void*" << da << ")";
+ break;
+ }
+ }
+
+ os << "{";
+
+ if (!inverse)
+ {
+ os << "using namespace " << db << ";"
+ << endl
+ << "statements_type& sts (*static_cast< statements_type* > (d));"
+ << "data_image_type& di (sts.data_image ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration ());";
+
+ os << endl
+ << "init (di, ";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "&i, ";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "&k, ";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "v" << (versioned ? ", svm" : "") << ");";
+
+ os << endl
+ << "if (sts.data_binding_test_version ())"
+ << "{"
+ << "const binding& id (sts.id_binding ());"
+ << "bind (sts.data_bind (), id.bind, id.count, di" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.data_binding_update_version ();"
+ << "}"
+ << "if (!sts.insert_statement ().execute ())" << endl
+ << "throw object_already_persistent ();";
+ }
+
+ os << "}";
+ }
+
+ // update
+ //
+ if (smart)
+ {
+ os << "void " << scope << "::" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "update (index_type i, const value_type& v, void* d)";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << "{";
+
+ os << "using namespace " << db << ";"
+ << endl
+ << "statements_type& sts (*static_cast< statements_type* > (d));"
+ << "cond_image_type& ci (sts.cond_image ());"
+ << "data_image_type& di (sts.data_image ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration ());";
+
+ os << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "init (ci, i);";
+ os << "init (di, 0, v" << (versioned ? ", svm" : "") << ");";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ //os << "init (di, 0, v);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ //os << "init (di, v);";
+ break;
+ }
+ }
+
+ os << endl
+ << "if (sts.update_binding_test_version ())"
+ << "{"
+ << "const binding& id (sts.id_binding ());"
+ << "bind (sts.update_bind (), id.bind, id.count, ci, di" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.update_binding_update_version ();"
+ << "}";
+
+ os << "if (sts.update_statement ().execute () == 0)" << endl
+ << "throw object_not_persistent ();"
+ << "}";
+ }
+
+ // select
+ //
+ os << "bool " << scope << "::" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "select (index_type&" << (ordered ? " i" : "") <<
+ ", value_type& v, void* d)";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "select (key_type& k, value_type& v, void* d)";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "select (value_type& v, void* d)";
+ break;
+ }
+ }
+
+ os << "{"
+ << "using namespace " << db << ";"
+ << "using " << db << "::select_statement;" // Conflicts.
+ << endl
+ << "statements_type& sts (*static_cast< statements_type* > (d));"
+ << "data_image_type& di (sts.data_image ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration ());";
+
+ os << endl
+ << "init (";
+
+ // Extract current element.
+ //
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "i, ";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "k, ";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "v, di, &sts.connection ().database ()" <<
+ (versioned ? ", svm" : "") << ");"
+ << endl;
+
+ init_value_extra ();
+
+ // If we are loading an eager pointer, then the call to init
+ // above executes other statements which potentially could
+ // change the image, including the id.
+ //
+ if (eager_ptr)
+ {
+ os << "if (sts.data_binding_test_version ())"
+ << "{"
+ << "const binding& id (sts.id_binding ());"
+ << "bind (sts.data_bind (), id.bind, id.count, di" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.data_binding_update_version ();"
+ << "}";
+ }
+
+ // Fetch next.
+ //
+ os << "select_statement& st (sts.select_statement ());"
+ << "select_statement::result r (st.fetch ());";
+
+ if (grow)
+ os << endl
+ << "if (r == select_statement::truncated)"
+ << "{"
+ << "grow (di, sts.select_image_truncated ()" <<
+ (versioned ? ", svm" : "") << ");"
+ << endl
+ << "if (sts.data_binding_test_version ())"
+ << "{"
+ // Id cannot change.
+ //
+ << "bind (sts.data_bind (), 0, sts.id_binding ().count, di" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.data_binding_update_version ();"
+ << "st.refetch ();"
+ << "}"
+ << "}";
+
+ os << "return r != select_statement::no_data;"
+ << "}";
+
+ // delete_
+ //
+ os << "void " << scope << "::" << endl
+ << "delete_ (";
+
+ if (smart)
+ {
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "index_type i, ";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+ }
+
+ os << "void*" << (inverse ? "" : " d") << ")"
+ << "{";
+
+ if (!inverse)
+ {
+ os << "using namespace " << db << ";"
+ << endl
+ << "statements_type& sts (*static_cast< statements_type* > (d));";
+
+ if (smart)
+ {
+ os << "cond_image_type& ci (sts.cond_image ());"
+ << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "init (ci, i);";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << endl
+ << "if (sts.cond_binding_test_version ())"
+ << "{"
+ << "const binding& id (sts.id_binding ());"
+ << "bind (sts.cond_bind (), id.bind, id.count, ci);"
+ << "sts.cond_binding_update_version ();"
+ << "}";
+ }
+
+ os << "sts.delete_statement ().execute ();";
+ }
+
+ os << "}";
+
+ // persist
+ //
+ if (!inverse)
+ {
+ os << "void " << scope << "::" << endl
+ << "persist (const container_type& c," << endl
+ << "statements_type& sts";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "functions_type& fs (sts.functions ());";
+
+ if (versioned)
+ os << "sts.version_migration (svm);";
+
+ if (!smart && ck == ck_ordered)
+ os << "fs.ordered_ = " << ordered << ";";
+
+ os << "container_traits_type::persist (c, fs);"
+ << "}";
+ }
+
+ // load
+ //
+ os << "void " << scope << "::" << endl
+ << "load (container_type& c," << endl
+ << "statements_type& sts";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using " << db << "::select_statement;" // Conflicts.
+ << endl
+ << "const binding& id (sts.id_binding ());"
+ << endl
+ << "if (sts.data_binding_test_version ())"
+ << "{"
+ << "bind (sts.data_bind (), id.bind, id.count, sts.data_image ()" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.data_binding_update_version ();"
+ << "}"
+ // We use the id binding directly so no need to check cond binding.
+ //
+ << "select_statement& st (sts.select_statement ());"
+ << "st.execute ();"
+ << "auto_result ar (st);";
+
+ // If we are loading eager object pointers, we may need to cache
+ // the result since we will be loading other objects.
+ //
+ if (eager_ptr)
+ cache_result ("st");
+
+ os << "select_statement::result r (st.fetch ());";
+
+ if (grow)
+ os << endl
+ << "if (r == select_statement::truncated)"
+ << "{"
+ << "data_image_type& di (sts.data_image ());"
+ << "grow (di, sts.select_image_truncated ()" <<
+ (versioned ? ", svm" : "") << ");"
+ << endl
+ << "if (sts.data_binding_test_version ())"
+ << "{"
+ // Id cannot change.
+ //
+ << "bind (sts.data_bind (), 0, id.count, di" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.data_binding_update_version ();"
+ << "st.refetch ();"
+ << "}"
+ << "}";
+
+ os << "bool more (r != select_statement::no_data);"
+ << endl
+ << "functions_type& fs (sts.functions ());";
+
+ if (versioned)
+ os << "sts.version_migration (svm);";
+
+ if (!smart && ck == ck_ordered)
+ os << "fs.ordered_ = " << ordered << ";";
+
+ os << "container_traits_type::load (c, more, fs);"
+ << "}";
+
+ // update
+ //
+ if (!(inverse || readonly (member_path_, member_scope_)))
+ {
+ os << "void " << scope << "::" << endl
+ << "update (const container_type& c," << endl
+ << "statements_type& sts";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "functions_type& fs (sts.functions ());";
+
+ if (versioned)
+ os << "sts.version_migration (svm);";
+
+ if (!smart && ck == ck_ordered)
+ os << "fs.ordered_ = " << ordered << ";";
+
+ os << "container_traits_type::update (c, fs);"
+ << "}";
+ }
+
+ // erase
+ //
+ if (!inverse)
+ {
+ os << "void " << scope << "::" << endl
+ << "erase (";
+
+ if (smart)
+ os << "const container_type* c, ";
+
+ os << "statements_type& sts)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "functions_type& fs (sts.functions ());";
+
+ if (!smart && ck == ck_ordered)
+ os << "fs.ordered_ = " << ordered << ";";
+
+ os << "container_traits_type::erase (" << (smart ? "c, " : "") << "fs);"
+ << "}";
+ }
+ }
+
+ protected:
+ string scope_;
+ semantics::class_& c_;
+ };
+
+ // Extra statement cache members for containers.
+ //
+ struct container_cache_members: object_members_base, virtual context
+ {
+ typedef container_cache_members base;
+
+ container_cache_members ()
+ : object_members_base (true, false, false)
+ {
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type& c)
+ {
+ bool smart (!context::inverse (m, "value") &&
+ !unordered (m) &&
+ container_smart (c));
+
+ string traits (flat_prefix_ + public_name (m) + "_traits");
+
+ os << db << "::" << (smart ? "smart_" : "") <<
+ "container_statements_impl< " << traits << " > " <<
+ flat_prefix_ << m.name () << ";";
+ }
+ };
+
+ struct container_cache_init_members: object_members_base, virtual context
+ {
+ typedef container_cache_init_members base;
+
+ container_cache_init_members ()
+ : object_members_base (true, false, false), first_ (true)
+ {
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ if (first_)
+ {
+ os << endl
+ << ": ";
+ first_ = false;
+ }
+ else
+ os << "," << endl
+ << " ";
+
+ os << flat_prefix_ << m.name () << " (c, id";
+ extra_members ();
+ os << ")";
+ }
+
+ virtual void
+ extra_members () {}
+
+ protected:
+ bool first_;
+ };
+
+ // Extra statement cache members for sections.
+ //
+ struct section_cache_members: virtual context
+ {
+ typedef section_cache_members base;
+
+ virtual void
+ traverse (user_section& s)
+ {
+ string traits (public_name (*s.member) + "_traits");
+
+ os << db << "::" << "section_statements< " <<
+ class_fq_name (*s.object) << ", " << traits << " > " <<
+ s.member->name () << ";";
+ }
+ };
+
+ struct section_cache_init_members: virtual context
+ {
+ typedef section_cache_init_members base;
+
+ section_cache_init_members (bool first): first_ (first) {}
+
+ virtual void
+ traverse (user_section& s)
+ {
+ if (first_)
+ {
+ os << endl
+ << ": ";
+ first_ = false;
+ }
+ else
+ os << "," << endl
+ << " ";
+
+ os << s.member->name () << " (c, im, idim, id, idv";
+ extra_members ();
+ os << ")";
+ }
+
+ virtual void
+ extra_members () {}
+
+ protected:
+ bool first_;
+ };
+
+ // Calls for container members.
+ //
+ struct container_calls: object_members_base, virtual context
+ {
+ typedef container_calls base;
+
+ enum call_type
+ {
+ persist_call,
+ load_call,
+ update_call,
+ erase_obj_call,
+ erase_id_call,
+ section_call
+ };
+
+ container_calls (call_type call, object_section* section = 0)
+ : object_members_base (true, false, true, false, section),
+ call_ (call),
+ obj_prefix_ ("obj"),
+ by_value_ (0)
+ {
+ }
+
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section for
+ // load calls.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ (call_ == load_call &&
+ *section_ == main_section &&
+ !s.separate_load ());
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&)
+ {
+ // We don't want to traverse composite id.
+ }
+
+ virtual void
+ traverse_composite_wrapper (semantics::data_member* m,
+ semantics::class_& c,
+ semantics::type* w)
+ {
+ if (m == 0 ||
+ call_ == erase_id_call ||
+ (call_ == load_call && by_value_ != 0))
+ {
+ object_members_base::traverse_composite (m, c);
+ return;
+ }
+
+ // Get this member using the accessor expression.
+ //
+ member_access& ma (
+ m->get<member_access> (call_ == load_call ? "set" : "get"));
+
+ // We don't support by-value modifiers for composite values
+ // with containers. However, at this point we don't know
+ // whether this composite value has any containers. So we
+ // are just going to set a flag that can be checked in
+ // traverse_container() below.
+ //
+ if (call_ == load_call && ma.placeholder ())
+ {
+ by_value_ = &ma;
+ object_members_base::traverse_composite (m, c);
+ by_value_ = 0;
+ return;
+ }
+
+ // We also don't support by-value accessors is there is a
+ // smart container inside (which, again, we don't know at
+ // this point). So keep track of such first instance.
+ //
+ member_access* old_by_value (by_value_);
+ if (call_ != load_call && ma.by_value && by_value_ == 0)
+ by_value_ = &ma;
+
+ string old_op (obj_prefix_);
+ string old_f (from_);
+ obj_prefix_.clear ();
+
+ // If this member is const and we have a synthesized direct
+ // access, then cast away constness. Otherwise, we assume
+ // that the user-provided expression handles this.
+ //
+ bool cast (call_ == load_call && ma.direct () && const_member (*m));
+ if (cast)
+ obj_prefix_ = "const_cast< " + member_ref_type (*m, false) +
+ " > (\n";
+
+ obj_prefix_ += ma.translate (old_op);
+
+ if (cast)
+ obj_prefix_ += ")";
+
+ // If this is not a synthesized expression, then store its
+ // location which we will output later for easier error
+ // tracking.
+ //
+ if (!ma.synthesized)
+ from_ += "// From " + location_string (ma.loc, true) + "\n";
+
+ // If this is a wrapped composite value, then we need to "unwrap" it.
+ //
+ if (w != 0)
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (*m, hint));
+
+ // Because we cannot have nested containers, member type should
+ // be the same as w.
+ //
+ assert (&t == w);
+
+ obj_prefix_ = "wrapper_traits< " + t.fq_name (hint) + " >::" +
+ (call_ == load_call ? "set_ref" : "get_ref") +
+ " (\n" + obj_prefix_ + ")";
+ }
+
+ object_members_base::traverse_composite (m, c);
+ from_ = old_f;
+ obj_prefix_ = old_op;
+ by_value_ = old_by_value;
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type& c)
+ {
+ using semantics::type;
+
+ bool inverse (context::inverse (m, "value"));
+ bool smart (!inverse && !unordered (m) && container_smart (c));
+ bool versioned (context::versioned (m));
+
+ // In certain cases we don't need to do anything.
+ //
+ if ((call_ != load_call && inverse) ||
+ (call_ == section_call && !smart) ||
+ (call_ == update_call && readonly (member_path_, member_scope_)))
+ return;
+
+ string const& name (m.name ());
+ string sts_name (flat_prefix_ + name);
+ string traits (flat_prefix_ + public_name (m) + "_traits");
+
+ os << "// " << member_prefix_ << m.name () << endl
+ << "//" << endl;
+
+ // Get this member using the accessor expression.
+ //
+ string var;
+ member_access& ma (
+ m.get<member_access> (call_ == load_call ? "set" : "get"));
+
+ // We don't support by-value modifiers for composite values
+ // with containers.
+ //
+ if (call_ == load_call && by_value_ != 0)
+ {
+ error (by_value_->loc) << "by-value modification of a composite "
+ << "value with container is not supported"
+ << endl;
+ info (m.location ()) << "container member is defined here" << endl;
+ throw operation_failed ();
+ }
+
+ // We don't support by-value accessors for smart containers.
+ //
+ if (call_ != load_call && smart)
+ {
+ if (by_value_ != 0)
+ {
+ error (by_value_->loc) << "by-value access to a composite value "
+ << "with smart container is not supported"
+ << endl;
+ info (m.location ()) << "container member is defined here" << endl;
+ throw operation_failed ();
+ }
+
+ if (ma.by_value)
+ {
+ error (ma.loc) << "by-value access to a smart container is not "
+ << "supported" << endl;
+ info (m.location ()) << "container member is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (member_path_));
+ unsigned long long dv (deleted (member_path_));
+
+ // If the addition/deletion version is the same as the section's,
+ // then we don't need the test.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")" << endl;
+ }
+
+ os << "{";
+
+ if (call_ != erase_id_call && (call_ != erase_obj_call || smart))
+ {
+ // See if we are modifying via a reference or proper modifier.
+ //
+ if (call_ == load_call && ma.placeholder ())
+ os << member_val_type (m, false, "v") << ";"
+ << endl;
+ else
+ {
+ // Note: this case is for both access and modification.
+ //
+
+ // Output stored locations, if any.
+ //
+ os << from_;
+
+ // If this is not a synthesized expression, then output its
+ // location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ // Note that here we don't decay arrays.
+ //
+ const string& ref_type (
+ member_ref_type (m, call_ != load_call, "v", false /* decay */));
+
+ // VC++ cannot grok the constructor syntax.
+ //
+ os << ref_type << " =" << endl
+ << " ";
+
+ // If this member is const and we have a synthesized direct
+ // access, then cast away constness. Otherwise, we assume
+ // that the user-provided expression handles this.
+ //
+ bool cast (call_ == load_call && ma.direct () && const_member (m));
+ if (cast)
+ os << "const_cast< " << member_ref_type (m, false, "", false) <<
+ " > (" << endl;
+
+ os << ma.translate (obj_prefix_);
+
+ if (cast)
+ os << ")";
+
+ os << ";"
+ << endl;
+ }
+
+ var = "v";
+
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ // If this is a wrapped container, then we need to "unwrap" it.
+ //
+ if (wrapper (t))
+ {
+ var = "wrapper_traits< " + t.fq_name (hint) + " >::" +
+ (call_ == load_call ? "set_ref" : "get_ref") + " (" + var + ")";
+ }
+ }
+
+ switch (call_)
+ {
+ case persist_call:
+ {
+ os << traits << "::persist (" << endl
+ << var << "," << endl
+ << "esc." << sts_name;
+
+ if (versioned)
+ os << "," << endl
+ << "svm";
+
+ os << ");";
+ break;
+ }
+ case load_call:
+ {
+ os << traits << "::load (" << endl
+ << var << "," << endl
+ << "esc." << sts_name;
+
+ if (versioned)
+ os << "," << endl
+ << "svm";
+
+ os << ");";
+ break;
+ }
+ case update_call:
+ {
+ os << traits << "::update (" << endl
+ << var << "," << endl
+ << "esc." << sts_name;
+
+ if (versioned)
+ os << "," << endl
+ << "svm";
+
+ os << ");";
+ break;
+ }
+ case erase_obj_call:
+ {
+ os << traits << "::erase (" << endl;
+
+ if (smart)
+ os << "&" << var << "," << endl;
+
+ os << "esc." << sts_name << ");"
+ << endl;
+ break;
+ }
+ case erase_id_call:
+ {
+ os << traits << "::erase (" << endl;
+
+ if (smart)
+ os << "0," << endl;
+
+ os << "esc." << sts_name << ");"
+ << endl;
+ break;
+ }
+ case section_call:
+ {
+ os << "if (" << traits << "::container_traits_type::changed (" <<
+ var << "))" << endl
+ << "s.reset (true, true);"; // loaded, changed
+ break;
+ }
+ }
+
+ if (call_ == load_call)
+ {
+ // Call the modifier if we are using a proper one.
+ //
+ if (ma.placeholder ())
+ {
+ os << endl
+ << from_;
+
+ // If this is not a synthesized expression, then output its
+ // location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << ma.translate (
+ obj_prefix_, "v", "static_cast<" + db.string () +
+ "::database&> (db)") << ";";
+ }
+ }
+
+ os << "}";
+ }
+
+ protected:
+ call_type call_;
+ string obj_prefix_;
+ string from_;
+ member_access* by_value_;
+ };
+
+ //
+ //
+ struct section_traits: traversal::class_, virtual context
+ {
+ typedef section_traits base;
+
+ section_traits (semantics::class_& c)
+ : c_ (c),
+ scope_ ("access::object_traits_impl< " + class_fq_name (c) +
+ ", id_" + db.string () + " >")
+ {
+ }
+
+ // Additional code that need to be executed following the call to
+ // init_value().
+ //
+ virtual void
+ init_value_extra ()
+ {
+ }
+
+ virtual void
+ process_statement_columns (statement_columns&,
+ statement_kind,
+ bool /*dynamic*/)
+ {
+ }
+
+ virtual void
+ section_extra (user_section&)
+ {
+ }
+
+ // Returning "1" means increment by one.
+ //
+ virtual string
+ optimistic_version_increment (semantics::data_member&)
+ {
+ return "1";
+ }
+
+ virtual string
+ update_statement_extra (user_section&)
+ {
+ return "";
+ }
+
+ virtual void
+ traverse (user_section& s)
+ {
+ using semantics::class_;
+ using semantics::data_member;
+
+ data_member& m (*s.member);
+
+ class_* poly_root (polymorphic (c_));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c_);
+ class_* poly_base (poly_derived ? &polymorphic_base (c_) : 0);
+
+ data_member* opt (optimistic (c_));
+
+ // Treat the special version update sections as abstract in reuse
+ // inheritance.
+ //
+ bool reuse_abst (!poly &&
+ (abstract (c_) ||
+ s.special == user_section::special_version));
+
+ bool load (s.total != 0 && s.separate_load ());
+ bool load_con (s.containers && s.separate_load ());
+ bool load_opt (s.optimistic () && s.separate_load ());
+
+ bool update (s.total != s.inverse + s.readonly); // Always separate.
+ bool update_con (s.readwrite_containers);
+ bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
+
+ // Don't generate anything for empty sections.
+ //
+ if (!(load || load_con || load_opt ||
+ update || update_con || update_opt))
+ return;
+
+ // If we are adding a new section to a derived class in an optimistic
+ // polymorphic hierarchy, then pretend it inherits from the special
+ // version update section.
+ //
+ user_section* rs (0);
+ if (opt != 0)
+ {
+ // Skip overrides and get to the new section if polymorphic.
+ //
+ for (rs = &s; poly && rs->base != 0; rs = rs->base) ;
+
+ if (rs != 0)
+ {
+ if (rs->object != &opt->scope ())
+ rs->base = &(poly ? poly_root : &opt->scope ())->
+ get<user_sections> ("user-sections").back ();
+ else
+ rs = 0;
+ }
+ }
+
+ string name (public_name (m) + "_traits");
+ string scope (scope_ + "::" + name);
+
+ os << "// " << m.name () << endl
+ << "//" << endl
+ << endl;
+
+ // bind (id, image_type)
+ //
+ if (load || load_opt || update || update_opt)
+ {
+ os << "std::size_t " << scope << "::" << endl
+ << "bind (" << bind_vector << " b," << endl
+ << "const " << bind_vector << (reuse_abst ? "," : " id,") << endl
+ << "std::size_t" << (reuse_abst ? "," : " id_size,") << endl
+ << "image_type& i," << endl
+ << db << "::statement_kind sk";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (sk);";
+
+ if (s.versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl
+ << "std::size_t n (0);"
+ << endl;
+
+ // Bind reuse base. It is always first and we never ask it
+ // to bind id(+ver).
+ //
+ if (s.base != 0 && !poly_derived)
+ {
+ user_section& b (*s.base);
+
+ bool load (b.total != 0 && b.separate_load ());
+ bool load_opt (b.optimistic () && b.separate_load ());
+
+ bool update (b.total != b.inverse + b.readonly);
+
+ if (load || load_opt || update)
+ os << "// " << class_name (*b.object) << endl
+ << "//" << endl
+ << "n += object_traits_impl< " << class_fq_name (*b.object) <<
+ ", id_" << db << " >::" << public_name (*b.member) <<
+ "_traits::bind (" << endl
+ << "b, 0, 0, i, sk" << (b.versioned ? ", svm" : "") << ");"
+ << endl;
+ }
+
+ // Bind members.
+ //
+ {
+ instance<bind_member> bm ("", "", &s);
+ traversal::names n (*bm);
+ names (c_, n);
+ }
+
+ // Bind polymorphic image chain for the select statement.
+ //
+ if (s.base != 0 && poly_derived && s.separate_load ())
+ {
+ // Find the next base that has something to load, if any.
+ //
+ user_section* b (s.base);
+ string acc (".base");
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ if (b->total != 0 || b->optimistic ())
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ acc += "->base";
+ }
+
+ if (b != 0)
+ os << "// " << class_name (*b->object) << endl
+ << "//" << endl
+ << "if (sk == statement_select)" << endl
+ << "n += object_traits_impl< " << class_fq_name (*b->object) <<
+ ", id_" << db << " >::" << public_name (*b->member) <<
+ "_traits::bind (" << endl
+ << "b + n, 0, 0, *i" << acc << ", sk" <<
+ (b->versioned ? ", svm" : "") << ");"
+ << endl;
+ }
+
+ if (!reuse_abst)
+ os << "// object_id" << endl
+ << "//" << endl
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
+ << endl;
+
+ os << "return n;"
+ << "}";
+ }
+
+ // grow ()
+ //
+ if (generate_grow && (load || load_opt))
+ {
+ os << "bool " << scope << "::" << endl
+ << "grow (image_type& i," << endl
+ << truncated_vector << " t";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (t);";
+
+ if (s.versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "bool grew (false);"
+ << endl;
+
+ size_t index (0);
+
+ if (s.base != 0 && !poly_derived)
+ {
+ user_section& b (*s.base);
+
+ bool load (b.total != 0);
+ bool load_opt (b.optimistic ());
+
+ if (load || load_opt)
+ {
+ os << "// " << class_name (*b.object) << endl
+ << "//" << endl
+ << "grew = object_traits_impl< " << class_fq_name (*b.object) <<
+ ", id_" << db << " >::" << public_name (*b.member) <<
+ "_traits::grow (i, t" << (b.versioned ? ", svm" : "") << ");"
+ << endl;
+
+ index += b.total + (load_opt ? 1 : 0);
+ }
+ }
+
+ {
+ user_section* ps (&s);
+ instance<grow_member> gm (index, "", ps);
+ traversal::names n (*gm);
+ names (c_, n);
+ }
+
+ // Grow polymorphic image chain.
+ //
+ if (s.base != 0 && poly_derived)
+ {
+ // Find the next base that has something to load, if any.
+ //
+ user_section* b (s.base);
+ string acc (".base");
+ size_t cols;
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ cols = b->total + (b->optimistic () ? 1 : 0);
+ if (cols != 0)
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ acc += "->base";
+ }
+
+ if (b != 0)
+ os << "// " << class_name (*b->object) << endl
+ << "//" << endl
+ << "if (object_traits_impl< " << class_fq_name (*b->object) <<
+ ", id_" << db << " >::" << public_name (*b->member) <<
+ "_traits::grow (" << endl
+ << "*i" << acc << ", t + " << cols << "UL" <<
+ (b->versioned ? ", svm" : "") << "))" << endl
+ << "i" << acc << "->version++;"
+ << endl;
+ }
+
+ os << "return grew;" << endl
+ << "}";
+ }
+
+ // init (object, image)
+ //
+ if (load)
+ {
+ os << "void " << scope << "::" << endl
+ << "init (object_type& o," << endl
+ << "const image_type& i," << endl
+ << "database* db";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (s.versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl;
+
+ if (s.base != 0)
+ {
+ if (!poly_derived)
+ {
+ user_section& b (*s.base);
+
+ bool load (b.total != 0);
+
+ if (load)
+ os << "// " << class_name (*b.object) << endl
+ << "//" << endl
+ << "object_traits_impl< " << class_fq_name (*b.object) <<
+ ", id_" << db << " >::" << public_name (*b.member) <<
+ "_traits::init (o, i, db" <<
+ (b.versioned ? ", svm" : "") << ");"
+ << endl;
+ }
+ else
+ {
+ // Find the next base that has something to load, if any.
+ //
+ user_section* b (s.base);
+ string acc (".base");
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ if (b->total != 0)
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ acc += "->base";
+ }
+
+ if (b != 0)
+ os << "// " << class_name (*b->object) << endl
+ << "//" << endl
+ << "object_traits_impl< " << class_fq_name (*b->object) <<
+ ", id_" << db << " >::" << public_name (*b->member) <<
+ "_traits::init (" << endl
+ << "o, *i" << acc << ", db" <<
+ (b->versioned ? ", svm" : "") << ");"
+ << endl;
+ }
+ }
+
+ {
+ instance<init_value_member> iv ("", "", true, &s);
+ traversal::names n (*iv);
+ names (c_, n);
+ }
+
+ os << "}";
+ }
+
+ // init (image, object)
+ //
+ if (update)
+ {
+ os << (generate_grow ? "bool " : "void ") << scope << "::" << endl
+ << "init (image_type& i," << endl
+ << "const object_type& o";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{";
+
+ if (s.versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);"
+ << endl;
+
+ os << "using namespace " << db << ";"
+ << endl
+ << "statement_kind sk (statement_insert);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl;
+
+ // There is no call to init_image_pre() here (which calls the
+ // copy callback for some databases) since we are not going to
+ // touch any of the members that were loaded by query.
+
+ if (generate_grow)
+ os << "bool grew (false);"
+ << endl;
+
+ if (s.base != 0 && !poly_derived)
+ {
+ user_section& b (*s.base);
+
+ bool update (b.total != b.inverse + b.readonly);
+
+ if (update)
+ os << "// " << class_name (*b.object) << endl
+ << "//" << endl
+ << (generate_grow ? "grew = " : "") <<
+ "object_traits_impl< " << class_fq_name (*b.object) <<
+ ", id_" << db << " >::" << public_name (*b.member) <<
+ "_traits::init (i, o" << (b.versioned ? ", svm" : "") << ");"
+ << endl;
+ }
+
+ {
+ instance<init_image_member> ii ("", "", &s);
+ traversal::names n (*ii);
+ names (c_, n);
+ }
+
+ if (generate_grow)
+ os << "return grew;";
+
+ os << "}";
+ }
+
+ // The rest does not apply to reuse-abstract sections.
+ //
+ if (reuse_abst)
+ {
+ section_extra (s);
+ return;
+ }
+
+ string sep (s.versioned ? "\n" : " ");
+
+ // Schema name as a string literal or empty.
+ //
+ string schema_name (options.schema_name ()[db]);
+ if (!schema_name.empty ())
+ schema_name = strlit (schema_name);
+
+ // Statements.
+ //
+ qname table (table_name (c_));
+ string qtable (quote_id (table));
+
+ instance<object_columns_list> id_cols;
+ id_cols->traverse (*id_member (c_));
+
+ // select_statement
+ //
+ if (load || load_opt)
+ {
+ size_t depth (poly_derived ? polymorphic_depth (c_) : 1);
+
+ statement_columns sc;
+ {
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ object_section* ps (&s); // Imperfect forwarding.
+ instance<object_columns> t (qtable, sk, sc, depth, ps);
+ t->traverse (c_);
+ process_statement_columns (sc, statement_select, s.versioned);
+ }
+
+ os << "const char " << scope << "::" << endl
+ << "select_statement[] =" << endl
+ << strlit ("SELECT" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ os << strlit ("FROM " + qtable + sep) << endl;
+
+ // Join polymorphic bases.
+ //
+ if (depth != 1 && s.base != 0)
+ {
+ bool f (false); //@@ (im)perfect forwarding
+ size_t d (depth - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (c_, f, d, "", s.base);
+ j->traverse (*poly_base);
+
+ for (strings::const_iterator i (j->begin ()); i != j->end (); ++i)
+ os << strlit (*i + sep) << endl;
+ }
+
+ // Join tables of inverse members belonging to this section.
+ //
+ {
+ bool f (false); // @@ (im)perfect forwarding
+ object_section* ps (&s); // @@ (im)perfect forwarding
+ instance<object_joins> j (c_, f, depth, ps);
+ j->traverse (c_);
+
+ for (strings::const_iterator i (j->begin ()); i != j->end (); ++i)
+ os << strlit (*i + sep) << endl;
+ }
+
+ string where ("WHERE ");
+ instance<query_parameters> qp (statement_select, table);
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += qtable + "." + quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+
+ // update_statement
+ //
+ if (update || update_opt)
+ {
+ instance<query_parameters> qp (statement_update, table);
+
+ statement_columns sc;
+ {
+ query_parameters* p (qp.get ()); // Imperfect forwarding.
+ statement_kind sk (statement_update); // Imperfect forwarding.
+ object_section* ps (&s); // Imperfect forwarding.
+ instance<object_columns> t (sk, sc, p, ps);
+ t->traverse (c_);
+ process_statement_columns (sc, statement_update, s.versioned);
+ }
+
+ os << "const char " << scope << "::" << endl
+ << "update_statement[] =" << endl
+ << strlit ("UPDATE " + qtable + sep) << endl
+ << strlit ("SET" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ // This didn't work out: cannot change the identity column.
+ //
+ //if (sc.empty ())
+ //{
+ // // We can end up with nothing to set if we need to "touch" a row
+ // // in order to increment its optimistic concurrency version. In
+ // // this case just do a dummy assignment based on the id column.
+ // //
+ // string const& c (quote_id (id_cols->begin ()->name));
+ // os << strlit (c + "=" + c) << endl;
+ //}
+
+ string extra (update_statement_extra (s));
+
+ if (!extra.empty ())
+ os << strlit (extra + sep) << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ if (s.optimistic ()) // Note: not update_opt.
+ {
+ string name (column_qname (*opt, column_prefix ()));
+ string type (column_type (*opt));
+
+ where += " AND " + name + "=" +
+ convert_to (qp->next (*opt, name, type), type, *opt);
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+
+ // load ()
+ //
+ if (load || load_opt || load_con)
+ {
+ os << "void " << scope << "::" << endl
+ << "load (extra_statement_cache_type& esc, object_type& obj" <<
+ (poly ? ", bool top" : "") << ")"
+ << "{";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (top);"
+ << endl;
+
+ if (s.versioned || s.versioned_containers)
+ os << "const schema_version_migration& svm (" << endl
+ << "esc." << m.name () << ".version_migration (" <<
+ schema_name << "));"
+ << endl;
+
+ // Load values, if any.
+ //
+ if (load || load_opt)
+ {
+ // The SELECT statement for the top override loads all the
+ // values.
+ //
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ // Note that we don't use delayed load machinery here. While
+ // a section can definitely contain self-referencing pointers,
+ // loading such a pointer won't mess up the data members in the
+ // image that we care about. It also holds true for streaming
+ // result, since the bindings are different.
+
+ os << "using namespace " << db << ";"
+ << "using " << db << "::select_statement;" // Conflicts.
+ << endl
+ << "statements_type& sts (esc." << m.name () << ");"
+ << endl
+ << "image_type& im (sts.image ());"
+ << "binding& imb (sts.select_image_binding ());"
+ << endl;
+
+ // For the polymorphic case, instead of storing an array of
+ // versions as we do for objects, we will add all the versions
+ // up and use that as a cumulative image chain version. If you
+ // meditate a bit on that, you will realize that it will work
+ // (hint: versions can only increase).
+ //
+ string ver;
+ string ver_decl;
+
+ if (s.base != 0 && poly_derived)
+ {
+ ver = "imv";
+ ver_decl = "std::size_t imv (im.version";
+
+ user_section* b (s.base);
+ string acc ("im.base");
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ if (b->total != 0 || b->optimistic ())
+ ver_decl += " +\n" + acc + "->version";
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ acc += "->base";
+ }
+
+ ver_decl += ")";
+
+ os << ver_decl << ";"
+ << endl;
+ }
+ else
+ ver = "im.version";
+
+ os << "if (" << ver << " != sts.select_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (s.versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (" << ver << ");"
+ << "imb.version++;"
+ << "}";
+
+ // Id binding is assumed initialized and bound.
+ //
+ os << "select_statement& st (sts.select_statement ());";
+
+ // The statement can be dynamically empty.
+ //
+ if (s.versioned)
+ os << "if (!st.empty ())"
+ << "{";
+
+ os << "st.execute ();"
+ << "auto_result ar (st);"
+ << "select_statement::result r (st.fetch ());"
+ << endl;
+
+ os << "if (r == select_statement::no_data)" << endl
+ << "throw object_not_persistent ();"
+ << endl;
+
+ if (grow (c_, &s))
+ {
+ os << "if (r == select_statement::truncated)"
+ << "{"
+ << "if (grow (im, sts.select_image_truncated ()" <<
+ (s.versioned ? ", svm" : "") << "))" << endl
+ << "im.version++;"
+ << endl;
+
+ // The same logic as above.
+ //
+ if (s.base != 0 && poly_derived)
+ os << ver_decl << ";"
+ << endl;
+
+ os << "if (" << ver << " != sts.select_image_version ())"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (s.versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (" << ver << ");"
+ << "imb.version++;"
+ << "st.refetch ();"
+ << "}"
+ << "}";
+ }
+
+ if (opt != 0) // Not load_opt, we do it in poly-derived as well.
+ {
+ os << "if (";
+
+ if (poly_derived)
+ {
+ os << "root_traits::version (*im.base";
+ for (class_* b (poly_base);
+ b != poly_root;
+ b = &polymorphic_base (*b))
+ os << "->base";
+ os << ")";
+ }
+ else
+ os << "version (im)";
+
+ os << " != " << (poly_derived ? "root_traits::" : "") <<
+ "version (obj))" << endl
+ << "throw object_changed ();"
+ << endl;
+ }
+
+ if (load)
+ {
+ os << "init (obj, im, &sts.connection ().database ()" <<
+ (s.versioned ? ", svm" : "") << ");";
+ init_value_extra (); // Stream results, etc.
+ os << endl;
+ }
+
+ if (s.versioned)
+ os << "}"; // if (!st.empty ())
+
+ if (poly)
+ os << "}"; // if (top)
+ }
+
+ // Call base to load its containers, if this is an override.
+ //
+ if (poly_derived && s.base != 0)
+ {
+ user_section* b (s.base);
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ // If we don't have any values of our own but out base
+ // does, then allow it to load them.
+ //
+ if (b->containers ||
+ (!load && (b->total != 0 || b->optimistic ())))
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ }
+
+ // This one is tricky: ideally we would do a direct call to
+ // the base's load() (which may not be our immediate base,
+ // BTW) but there is no easy way to resolve base's extra
+ // statements from ours. So, instead, we are going to go
+ // via the dispatch machinery which requires a connection
+ // rather than statements. Not the most efficient way but
+ // simple.
+
+ // Find the "previous" override by starting the search from
+ // our base.
+ //
+ if (b != 0)
+ {
+ // Note that here we are using the base section index to
+ // handle the special version update base.
+ //
+ os << "info.base->find_section_load (" << b->index << "UL) (" <<
+ "esc." << m.name () << ".connection (), obj, " <<
+ // If we don't have any values of our own, then allow the
+ // base load its.
+ //
+ (load ? "false" : "top") << ");"
+ << endl;
+ }
+ }
+
+ // Load our containers, if any.
+ //
+ if (s.containers)
+ {
+ instance<container_calls> t (container_calls::load_call, &s);
+ t->traverse (c_);
+ }
+
+ os << "}";
+ }
+
+ // update ()
+ //
+ if (update || update_opt || update_con)
+ {
+ os << "void " << scope << "::" << endl
+ << "update (extra_statement_cache_type& esc, " <<
+ "const object_type& obj" <<
+ (poly_derived && s.base != 0 ? ", bool base" : "") << ")"
+ << "{";
+
+ // Call base if this is an override.
+ //
+ if (poly_derived && s.base != 0)
+ {
+ user_section* b (s.base);
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ if (b->total != b->inverse + b->readonly ||
+ b->readwrite_containers ||
+ (poly && b->optimistic ()))
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ }
+
+ // The same (tricky) logic as in load(). Note that here we are
+ // using the base section index to handle the special version
+ // update base.
+ //
+ if (b != 0)
+ os << "if (base)" << endl
+ << "info.base->find_section_update (" << b->index <<
+ "UL) (esc." << m.name () << ".connection (), obj);"
+ << endl;
+ else
+ os << "ODB_POTENTIALLY_UNUSED (base);"
+ << endl;
+ }
+
+ if (s.versioned || s.readwrite_versioned_containers)
+ os << "const schema_version_migration& svm (" << endl
+ << "esc." << m.name () << ".version_migration (" <<
+ schema_name << "));"
+ << endl;
+
+ // Update values, if any.
+ //
+ if (update || update_opt)
+ {
+ os << "using namespace " << db << ";"
+ << "using " << db << "::update_statement;" // Conflicts.
+ << endl
+ << "statements_type& sts (esc." << m.name () << ");"
+ << endl
+ << "image_type& im (sts.image ());"
+ << "const binding& id (sts.idv_binding ());" // id+version
+ << "binding& imb (sts.update_image_binding ());"
+ << endl;
+
+ if (update)
+ {
+ if (generate_grow)
+ os << "if (";
+
+ os << "init (im, obj" << (s.versioned ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << ")" << endl
+ << "im.version++";
+
+ os << ";"
+ << endl;
+ }
+
+ os << "if (im.version != sts.update_image_version () ||" << endl
+ << "id.version != sts.update_id_binding_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, id.bind, id.count, im, statement_update" <<
+ (s.versioned ? ", svm" : "") << ");"
+ << "sts.update_image_version (im.version);"
+ << "sts.update_id_binding_version (id.version);"
+ << "imb.version++;"
+ << "}";
+
+ os << "update_statement& st (sts.update_statement ());"
+ << "if (";
+
+ if (s.versioned)
+ os << "!st.empty () && ";
+
+ os << "st.execute () == 0)" << endl;
+
+ if (opt == 0)
+ os << "throw object_not_persistent ();";
+ else
+ os << "throw object_changed ();";
+
+ os << endl;
+ }
+
+ // Update readwrite containers if any.
+ //
+ if (s.readwrite_containers)
+ {
+ instance<container_calls> t (container_calls::update_call, &s);
+ t->traverse (c_);
+ }
+
+ // Update the optimistic concurrency version in the object member.
+ // Very similar code to object.
+ //
+ if (s.optimistic ()) // Note: not update_opt.
+ {
+ // Object is passed as const reference so we need to cast away
+ // constness.
+ //
+ const char* obj ("const_cast<object_type&> (obj)");
+ string inc (optimistic_version_increment (*opt));
+
+ if (inc == "1")
+ inc_member (*opt, obj, "obj", "version_type");
+ else
+ set_member (*opt, obj, inc, "", "version_type");
+ }
+
+ os << "}";
+ }
+
+ section_extra (s);
+
+ if (rs != 0)
+ rs->base = 0;
+ }
+
+ using class_::traverse; // Unhide.
+
+ protected:
+ semantics::class_& c_;
+ string scope_;
+ };
+
+ // Output a list of parameters for the persist statement.
+ //
+ struct persist_statement_params: object_columns_base, virtual context
+ {
+ typedef persist_statement_params base;
+
+ persist_statement_params (string& params,
+ query_parameters& qp,
+ const string& sep)
+ : params_ (params), qp_ (qp), sep_ (sep)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ if (!inverse (m, key_prefix_))
+ object_columns_base::traverse_pointer (m, c);
+ }
+
+ virtual bool
+ traverse_column (semantics::data_member& m,
+ string const& name,
+ bool first)
+ {
+ string p;
+
+ if (version (m))
+ p = version_value (m);
+ else
+ {
+ const string& qname (quote_id (name));
+ const string& type (column_type ());
+
+ p = auto_ (m) // Only simple, direct id can be auto.
+ ? qp_.auto_id (m, qname, type)
+ : qp_.next (m, qname, type);
+ }
+
+ if (!p.empty ())
+ {
+ if (!first)
+ {
+ params_ += ',';
+ params_ += sep_;
+ }
+
+ params_ += (p != "DEFAULT" ? convert_to (p, column_type (), m) : p);
+ }
+
+ return !p.empty ();
+ }
+
+ virtual string
+ version_value (semantics::data_member&)
+ {
+ return "1";
+ }
+
+ private:
+ string& params_;
+ query_parameters& qp_;
+ const string& sep_;
+ };
+
+ //
+ //
+ struct class_: traversal::class_, virtual context
+ {
+ typedef class_ base;
+
+ class_ ()
+ : typedefs_ (false),
+ query_columns_type_ (false, false, false),
+ view_query_columns_type_ (false),
+ index_ (0),
+ grow_base_ (index_),
+ grow_member_ (index_),
+ grow_version_member_ (index_, "version_"),
+ grow_discriminator_member_ (index_, "discriminator_"),
+ bind_id_member_ ("id_"),
+ bind_version_member_ ("version_"),
+ bind_discriminator_member_ ("discriminator_"),
+ init_id_image_member_ ("id_", "id"),
+ init_version_image_member_ ("version_", "(*v)"),
+ init_view_pointer_member_pre_ (true, *init_value_member_),
+ init_view_pointer_member_post_ (false, *init_value_member_),
+ init_id_value_member_ ("id"),
+ init_id_value_member_id_image_ ("id", "id_"),
+ init_version_value_member_ ("v"),
+ init_named_version_value_member_ ("v", "version_"),
+ init_discriminator_value_member_ ("d", "", false),
+ init_named_discriminator_value_member_ (
+ "d", "discriminator_", false)
+ {
+ init ();
+ }
+
+ class_ (class_ const&)
+ : root_context (), //@@ -Wextra
+ context (),
+ typedefs_ (false),
+ query_columns_type_ (false, false, false),
+ view_query_columns_type_ (false),
+ index_ (0),
+ grow_base_ (index_),
+ grow_member_ (index_),
+ grow_version_member_ (index_, "version_"),
+ grow_discriminator_member_ (index_, "discriminator_"),
+ bind_id_member_ ("id_"),
+ bind_version_member_ ("version_"),
+ bind_discriminator_member_ ("discriminator_"),
+ init_id_image_member_ ("id_", "id"),
+ init_version_image_member_ ("version_", "(*v)"),
+ init_view_pointer_member_pre_ (true, *init_value_member_),
+ init_view_pointer_member_post_ (false, *init_value_member_),
+ init_id_value_member_ ("id"),
+ init_id_value_member_id_image_ ("id", "id_"),
+ init_version_value_member_ ("v"),
+ init_named_version_value_member_ ("v", "version_"),
+ init_discriminator_value_member_ ("d", "", false),
+ init_named_discriminator_value_member_ (
+ "d", "discriminator_", false)
+ {
+ init ();
+ }
+
+ void
+ init ()
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+
+ if (generate_grow)
+ {
+ grow_base_inherits_ >> grow_base_;
+ grow_member_names_ >> grow_member_;
+ }
+
+ bind_base_inherits_ >> bind_base_;
+ bind_member_names_ >> bind_member_;
+
+ init_image_base_inherits_ >> init_image_base_;
+ init_image_member_names_ >> init_image_member_;
+
+ init_value_base_inherits_ >> init_value_base_;
+ init_value_member_names_ >> init_value_member_;
+
+ init_view_pointer_member_pre_names_ >> init_view_pointer_member_pre_;
+ init_view_pointer_member_post_names_ >> init_view_pointer_member_post_;
+ }
+
+ virtual void
+ init_auto_id (semantics::data_member&, // id member
+ string const&) // image variable prefix
+ {
+ if (insert_send_auto_id)
+ assert (false);
+ }
+
+ virtual void
+ init_image_pre (type&)
+ {
+ }
+
+ virtual void
+ init_value_extra ()
+ {
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ context::top_object = context::cur_object = &c;
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ case class_composite: traverse_composite (c); break;
+ default: break;
+ }
+
+ context::top_object = context::cur_object = 0;
+ }
+
+ //
+ // statements
+ //
+
+ enum persist_position
+ {
+ persist_after_columns,
+ persist_after_values
+ };
+
+ virtual string
+ persist_statement_extra (type&, query_parameters&, persist_position)
+ {
+ return "";
+ }
+
+ virtual string
+ update_statement_extra (type&)
+ {
+ return "";
+ }
+
+ //
+ // common
+ //
+
+ virtual void
+ post_query_ (type&, bool /*once_off*/)
+ {
+ }
+
+ virtual void
+ process_statement_columns (statement_columns&,
+ statement_kind,
+ bool /*dynamic*/)
+ {
+ }
+
+ //
+ // object
+ //
+
+ virtual void
+ object_extra (type&) {}
+
+ virtual void
+ extra_statement_cache_extra_args (bool /*containers*/,
+ bool /*sections*/) {}
+
+ virtual void
+ object_query_statement_ctor_args (type&,
+ std::string const& q,
+ bool process,
+ bool /*prepared*/)
+ {
+ os << "conn," << endl
+ << "text," << endl
+ << process << "," << endl // Process.
+ << "true," << endl // Optimize.
+ << q << ".parameters_binding ()," << endl
+ << "imb";
+ }
+
+ virtual void
+ object_erase_query_statement_ctor_args (type&)
+ {
+ os << "conn," << endl
+ << "text," << endl
+ << "q.parameters_binding ()";
+ }
+
+ virtual string
+ optimistic_version_init (semantics::data_member&, bool /*index*/ = false)
+ {
+ return "1";
+ }
+
+ // Returning "1" means increment by one.
+ //
+ virtual string
+ optimistic_version_increment (semantics::data_member&,
+ bool /*index*/ = false)
+ {
+ return "1";
+ }
+
+ virtual bool
+ optimistic_insert_bind_version (semantics::data_member&)
+ {
+ return false;
+ }
+
+ virtual void
+ traverse_object (type& c);
+
+ //
+ // view
+ //
+
+ virtual void
+ view_extra (type&)
+ {
+ }
+
+ virtual void
+ view_query_statement_ctor_args (type&,
+ string const& q,
+ bool process,
+ bool /*prepared*/)
+ {
+ os << "conn," << endl
+ << q << ".clause ()," << endl
+ << process << "," << endl // Process.
+ << "true," << endl // Optimize.
+ << q << ".parameters_binding ()," << endl
+ << "imb";
+ }
+
+ virtual string
+ from_trailer (type&) { return "";}
+
+ virtual string
+ select_trailer (type& c)
+ {
+ return c.get<view_query> ("query").for_update ? "FOR UPDATE" : "";
+ }
+
+ virtual string
+ join_syntax (view_object const& vo)
+ {
+ const char* r (0);
+
+ switch (vo.join)
+ {
+ case view_object::left: r = "LEFT JOIN"; break;
+ case view_object::right: r = "RIGHT JOIN"; break;
+ case view_object::full: r = "FULL JOIN"; break;
+ case view_object::inner: r = "INNER JOIN"; break;
+ case view_object::cross: r = "CROSS JOIN"; break;
+ }
+
+ return r;
+ }
+
+ virtual void
+ traverse_view (type& c);
+
+ struct expression
+ {
+ explicit
+ expression (std::string const& v): kind (literal), value (v) {}
+ expression (view_object* vo): kind (pointer), vo (vo) {}
+
+ enum kind_type {literal, pointer};
+
+ kind_type kind;
+ std::string value;
+ data_member_path member_path;
+ view_object* vo;
+ };
+
+ expression
+ translate_expression (type& c,
+ cxx_tokens const&,
+ semantics::scope& start_scope,
+ location_t loc,
+ string const& prag,
+ bool* placeholder = 0,
+ bool predicate = true);
+ //
+ // composite
+ //
+
+ virtual void
+ traverse_composite (type& c)
+ {
+ bool versioned (context::versioned (c));
+
+ string const& type (class_fq_name (c));
+ string traits ("access::composite_value_traits< " + type + ", id_" +
+ db.string () + " >");
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ // Containers.
+ //
+ {
+ instance<container_traits> t (c);
+ t->traverse (c);
+ }
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ os << "bool " << traits << "::" << endl
+ << "grow (image_type& i," << endl
+ << truncated_vector << " t";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (t);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "bool grew (false);"
+ << endl;
+
+ index_ = 0;
+ inherits (c, grow_base_inherits_);
+ names (c, grow_member_names_);
+
+ os << "return grew;"
+ << "}";
+ }
+
+ // bind (image_type)
+ //
+ os << "void " << traits << "::" << endl
+ << "bind (" << bind_vector << " b," << endl
+ << "image_type& i," << endl
+ << db << "::statement_kind sk";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (b);"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (sk);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (readonly (c))
+ os << "assert (sk != statement_update);"
+ << endl;
+
+ os << "std::size_t n (0);"
+ << "ODB_POTENTIALLY_UNUSED (n);"
+ << endl;
+
+ inherits (c, bind_base_inherits_);
+ names (c, bind_member_names_);
+
+ os << "}";
+
+ // init (image, value)
+ //
+ os << (generate_grow ? "bool " : "void ") << traits << "::" << endl
+ << "init (image_type& i," << endl
+ << "const value_type& o," << endl
+ << db << "::statement_kind sk";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << "ODB_POTENTIALLY_UNUSED (sk);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (readonly (c))
+ os << "assert (sk != statement_update);"
+ << endl;
+
+ if (generate_grow)
+ os << "bool grew (false);"
+ << endl;
+
+ inherits (c, init_image_base_inherits_);
+ names (c, init_image_member_names_);
+
+ if (generate_grow)
+ os << "return grew;";
+
+ os << "}";
+
+ // init (value, image)
+ //
+ os << "void " << traits << "::" << endl
+ << "init (value_type& o," << endl
+ << "const image_type& i," << endl
+ << "database* db";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl;
+
+ inherits (c, init_value_base_inherits_);
+ names (c, init_value_member_names_);
+
+ os << "}";
+ }
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<query_columns_type> query_columns_type_;
+ instance<view_query_columns_type> view_query_columns_type_;
+
+ size_t index_;
+ instance<grow_base> grow_base_;
+ traversal::inherits grow_base_inherits_;
+ instance<grow_member> grow_member_;
+ traversal::names grow_member_names_;
+ instance<grow_member> grow_version_member_;
+ instance<grow_member> grow_discriminator_member_;
+
+
+ instance<bind_base> bind_base_;
+ traversal::inherits bind_base_inherits_;
+ instance<bind_member> bind_member_;
+ traversal::names bind_member_names_;
+ instance<bind_member> bind_id_member_;
+ instance<bind_member> bind_version_member_;
+ instance<bind_member> bind_discriminator_member_;
+
+ instance<init_image_base> init_image_base_;
+ traversal::inherits init_image_base_inherits_;
+ instance<init_image_member> init_image_member_;
+ traversal::names init_image_member_names_;
+
+ instance<init_image_member> init_id_image_member_;
+ instance<init_image_member> init_version_image_member_;
+
+ instance<init_value_base> init_value_base_;
+ traversal::inherits init_value_base_inherits_;
+ instance<init_value_member> init_value_member_;
+ traversal::names init_value_member_names_;
+
+ instance<init_view_pointer_member> init_view_pointer_member_pre_;
+ instance<init_view_pointer_member> init_view_pointer_member_post_;
+ traversal::names init_view_pointer_member_pre_names_;
+ traversal::names init_view_pointer_member_post_names_;
+
+ instance<init_value_member> init_id_value_member_;
+ instance<init_value_member> init_id_value_member_id_image_;
+ instance<init_value_member> init_version_value_member_;
+ instance<init_value_member> init_named_version_value_member_;
+ instance<init_value_member> init_discriminator_value_member_;
+ instance<init_value_member> init_named_discriminator_value_member_;
+ };
+
+ struct include: virtual context
+ {
+ typedef include base;
+
+ virtual void
+ generate ()
+ {
+ extra_pre ();
+
+ os << "#include <cassert>" << endl
+ << "#include <cstring> // std::memcpy" << endl;
+
+ if (features.polymorphic_object)
+ os << "#include <typeinfo>" << endl;
+
+ os << endl;
+
+ if (features.polymorphic_object)
+ os << "#include <odb/polymorphic-map.hxx>" << endl;
+
+ if (embedded_schema)
+ os << "#include <odb/schema-catalog-impl.hxx>" << endl;
+
+ if (multi_dynamic)
+ os << "#include <odb/function-table.hxx>" << endl;
+
+ os << endl;
+
+ os << "#include <odb/" << db << "/traits.hxx>" << endl
+ << "#include <odb/" << db << "/database.hxx>" << endl
+ << "#include <odb/" << db << "/transaction.hxx>" << endl
+ << "#include <odb/" << db << "/connection.hxx>" << endl
+ << "#include <odb/" << db << "/statement.hxx>" << endl
+ << "#include <odb/" << db << "/statement-cache.hxx>" << endl;
+
+ if (features.simple_object)
+ os << "#include <odb/" << db << "/simple-object-statements.hxx>" << endl;
+
+ if (features.polymorphic_object)
+ os << "#include <odb/" << db << "/polymorphic-object-statements.hxx>" << endl;
+
+ if (features.no_id_object)
+ os << "#include <odb/" << db << "/no-id-object-statements.hxx>" << endl;
+
+ if (features.view)
+ os << "#include <odb/" << db << "/view-statements.hxx>" << endl;
+
+ if (features.section)
+ os << "#include <odb/" << db << "/section-statements.hxx>" << endl;
+
+ os << "#include <odb/" << db << "/container-statements.hxx>" << endl
+ << "#include <odb/" << db << "/exceptions.hxx>" << endl;
+
+ if (options.generate_query ())
+ {
+ if (options.generate_prepared ())
+ os << "#include <odb/" << db << "/prepared-query.hxx>" << endl;
+
+ if (features.simple_object)
+ os << "#include <odb/" << db << "/simple-object-result.hxx>" << endl;
+
+ if (features.polymorphic_object)
+ os << "#include <odb/" << db << "/polymorphic-object-result.hxx>" << endl;
+
+ if (features.no_id_object)
+ os << "#include <odb/" << db << "/no-id-object-result.hxx>" << endl;
+
+ if (features.view)
+ os << "#include <odb/" << db << "/view-result.hxx>" << endl;
+ }
+
+ extra_post ();
+
+ os << endl;
+ }
+
+ virtual void
+ extra_pre ()
+ {
+ }
+
+ virtual void
+ extra_post ()
+ {
+ }
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_SOURCE_HXX
diff --git a/odb/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/odb/relational/sqlite/source.cxx b/odb/odb/relational/sqlite/source.cxx
new file mode 100644
index 0000000..5a4b9d3
--- /dev/null
+++ b/odb/odb/relational/sqlite/source.cxx
@@ -0,0 +1,471 @@
+// file : odb/relational/sqlite/source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/source.hxx>
+
+#include <odb/relational/sqlite/common.hxx>
+#include <odb/relational/sqlite/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace sqlite
+ {
+ namespace source
+ {
+ namespace relational = relational::source;
+
+ struct query_parameters: relational::query_parameters, context
+ {
+ query_parameters (base const& x): base (x) {}
+
+ virtual string
+ next (semantics::data_member& m,
+ const string& column,
+ const string& sqlt)
+ {
+ // Handle stream columns. Specifically, we somehow need to
+ // pass the column name to the code that runs in the
+ // statement. So what we are going to do is encode it
+ // in the parameter name.
+ //
+ if (sk_ == statement_insert || sk_ == statement_update)
+ {
+ const sql_type& t (parse_sql_type (sqlt, m, false));
+ if (t.stream)
+ {
+ // The column name is quoted.
+ //
+ string r (column);
+ r[0] = '$'; // Replace leading '"'.
+ r.resize (r.size () - 1); // Remove trailing '"'.
+
+ // Verify it only contains allowed characters.
+ //
+ for (size_t i (1); i != r.size (); ++i)
+ {
+ char c (r[i]);
+ if (c != '_' &&
+ (c < '0' || c > '9') &&
+ (c < 'a' || c > 'z') &&
+ (c < 'A' || c > 'Z'))
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: unsupported character '" << c << "' in "
+ << sqlt << " column name " << column << endl;
+
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": info: STREAM column can contain alpha-numeric "
+ << "characters plus '_'" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ // For TEXT columns, since we use the *_bind_zeroblob()
+ // function (there is no *_bind_zerotext()), the value
+ // that will be stored is BLOB, not TEXT, unless we
+ // explicitly CAST it. The user better make sure the
+ // encoding of raw TEXT data they are going to write
+ // matches the database encoding.
+ //
+ if (t.type == sql_type::TEXT)
+ r = "CAST(" + r + " AS TEXT)";
+
+ return r;
+ }
+ }
+
+ return "?";
+ }
+ };
+ entry<query_parameters> query_parameters_;
+
+ //
+ // bind
+ //
+
+ struct bind_member: relational::bind_member_impl<sql_type>,
+ member_base
+ {
+ bind_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << b << ".type = sqlite::bind::integer;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_real (member_info& mi)
+ {
+ os << b << ".type = sqlite::bind::real;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_text (member_info& mi)
+ {
+ os << b << ".type = sqlite::image_traits<" << endl
+ << " " << mi.fq_type () << "," << endl
+ << " sqlite::id_text>::bind_value;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ();"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_blob (member_info& mi)
+ {
+ os << b << ".type = sqlite::bind::blob;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ();"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_stream (member_info& mi)
+ {
+ os << b << ".type = sqlite::bind::stream;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+ };
+ entry<bind_member> bind_member_;
+
+ //
+ // grow
+ //
+
+ struct grow_member: relational::grow_member_impl<sql_type>,
+ member_base
+ {
+ grow_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ os << e << " = false;"
+ << endl;
+ }
+
+ virtual void
+ traverse_real (member_info&)
+ {
+ os << e << " = false;"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_stream (member_info&)
+ {
+ os << e << " = false;"
+ << endl;
+ }
+ };
+ entry<grow_member> grow_member_;
+
+ //
+ // init image
+ //
+
+ struct init_image_member: relational::init_image_member_impl<sql_type>,
+ member_base
+ {
+ init_image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ set_null (member_info& mi)
+ {
+ os << "i." << mi.var << "null = true;";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_real (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_stream (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+ };
+ entry<init_image_member> init_image_member_;
+
+ //
+ // init value
+ //
+
+ struct init_value_member: relational::init_value_member_impl<sql_type>,
+ member_base
+ {
+ init_value_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ get_null (string const& var) const
+ {
+ os << "i." << var << "null";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_real (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_stream (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+ };
+ entry<init_value_member> init_value_member_;
+
+ struct statement_columns_common: context
+ {
+ void
+ process (relational::statement_columns& cs, statement_kind sk)
+ {
+ using relational::statement_columns;
+
+ // For SELECT statements, add _ROWID_ "follow-up" column to
+ // each stream column. The reason we need both, and not just
+ // ROWID is the NULL value. Let's hope that SELECT'ing a BLOB
+ // but not actually reading it with sqlite3_result_blob() is
+ // as fast as not SELECT'ing it.
+ //
+ if (sk != statement_select)
+ return;
+
+ for (statement_columns::iterator i (cs.begin ());
+ i != cs.end (); ++i)
+ {
+ if (parse_sql_type (i->type, *i->member).stream)
+ {
+ // Column is already table-qualified and quoted. Do some
+ // surgery to replace it with _ROWID_. That is, we want to
+ // transform "table"."column" to "table"."_ROWID_".
+ //
+ string c (i->column);
+ string::size_type n (c.size ()), p (c.rfind ('"', n - 2));
+ assert (p != string::npos);
+ string as (c, p + 1, n - p - 2);
+ c.resize (p);
+ c += "\"_ROWID_\"";
+
+ // We are going to pack this "tightly", without any newlines,
+ // so that the statement processing code treats them as a
+ // single column.
+ //
+ i->column += ',';
+ i->column += c;
+ }
+ }
+ }
+ };
+
+ struct container_traits: relational::container_traits,
+ statement_columns_common
+ {
+ container_traits (base const& x): base (x) {}
+
+ virtual void
+ cache_result (string const&)
+ {
+ // Caching is not necessary since SQLite can execute several
+ // interleaving statements.
+ //
+ }
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk,
+ bool)
+ {
+ statement_columns_common::process (cols, sk);
+ }
+ };
+ entry<container_traits> container_traits_;
+
+ struct section_traits: relational::section_traits,
+ statement_columns_common
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk,
+ bool)
+ {
+ statement_columns_common::process (cols, sk);
+ }
+ };
+ entry<section_traits> section_traits_;
+
+ struct class_: relational::class_, statement_columns_common
+ {
+ class_ (base const& x): base (x) {}
+
+ virtual void
+ init_auto_id (semantics::data_member& m, string const& im)
+ {
+ // Don't set the id value to NULL if this is a nullable wrapper.
+ // This will allow the user to control whether the value is auto or
+ // manually assigned by using something like this:
+ //
+ // #pragma db auto
+ // odb::nullable<int64_t> id;
+ //
+ semantics::type& t (utype (m));
+ if (wrapper (t) && t.template get<bool> ("wrapper-null-handler"))
+ return;
+
+ os << im << "null = true;"
+ << endl;
+ }
+
+ virtual string
+ select_trailer (type&)
+ {
+ // SQLite has not support for FOR UPDATE and since this is an
+ // optimization, we simply ignore it.
+ //
+ return "";
+ }
+
+ virtual string
+ join_syntax (view_object const& vo)
+ {
+ const char* n (0);
+
+ if (vo.join == view_object::full)
+ n = "FULL OUTER JOIN";
+ else if (vo.join == view_object::right)
+ n = "RIGHT OUTER JOIN";
+
+ if (n != 0)
+ {
+ error (vo.loc) << n << " is not supported by SQLite" << endl;
+ throw operation_failed ();
+ }
+
+ return base::join_syntax (vo);
+ }
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk,
+ bool)
+ {
+ statement_columns_common::process (cols, sk);
+ }
+ };
+ entry<class_> class_entry_;
+ }
+ }
+}
diff --git a/odb/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/odb/semantics/class-template.cxx b/odb/odb/semantics/class-template.cxx
new file mode 100644
index 0000000..f8bbca4
--- /dev/null
+++ b/odb/odb/semantics/class-template.cxx
@@ -0,0 +1,54 @@
+// file : odb/semantics/class-template.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/class-template.hxx>
+
+namespace semantics
+{
+ class_template::
+ class_template (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ class_instantiation::
+ class_instantiation (path const& file,
+ size_t line,
+ size_t column,
+ tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // class_template
+ //
+ {
+ type_info ti (typeid (class_template));
+ ti.add_base (typeid (type_template));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+
+ // class_instantiation
+ //
+ {
+ type_info ti (typeid (class_instantiation));
+ ti.add_base (typeid (class_));
+ ti.add_base (typeid (type_instantiation));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/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/odb/semantics/class.cxx b/odb/odb/semantics/class.cxx
new file mode 100644
index 0000000..97cf088
--- /dev/null
+++ b/odb/odb/semantics/class.cxx
@@ -0,0 +1,175 @@
+// file : odb/semantics/class.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx> // TYPE_HAS_DEFAULT_CONSTRUCTOR
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/class.hxx>
+
+namespace semantics
+{
+ inherits::
+ inherits (access_type access, bool virt)
+ : virt_ (virt), access_ (access)
+ {
+ }
+
+ class_::
+ class_ (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ bool class_::
+ default_ctor () const
+ {
+ tree t (tree_node ());
+
+ // TYPE_HAS_DEFAULT_CONSTRUCTOR() returns true if we have a deleted
+ // default ctor. locate_ctor(), on the other hand, returns NULL_TREE in
+ // this case.
+ //
+ if (TYPE_HAS_DEFAULT_CONSTRUCTOR (t))
+ {
+#if BUILDING_GCC_MAJOR >= 8
+
+ // Work around GCC bug 86441. Essentially, we should not trigger an
+ // instantiation or completion of the default ctor. As a result, we will
+ // assume that if we have a lazy default ctor, it is not implicitly
+ // deleted.
+ //
+ if (CLASSTYPE_LAZY_DEFAULT_CTOR (t))
+ return true;
+
+ for (ovl_iterator i (CLASSTYPE_CONSTRUCTORS (t)); i; ++i)
+ {
+ tree f (*i);
+
+ if (TREE_CODE (f) == FUNCTION_DECL && DECL_DELETED_FN (f))
+ continue;
+
+ if (default_ctor_p (f))
+ return true;
+ }
+#else
+ return locate_ctor (t) != NULL_TREE;
+#endif
+
+ }
+
+ return false;
+ }
+
+ bool class_::
+ complete () const
+ {
+ return COMPLETE_TYPE_P (tree_node ());
+ }
+
+ bool class_::
+ abstract () const
+ {
+ return CLASSTYPE_PURE_VIRTUALS (tree_node ());
+ }
+
+ names* class_::
+ lookup (string const& name,
+ type_id const& ti,
+ unsigned int flags,
+ bool* ph) const
+ {
+ bool h (false);
+ bool& rh (ph != 0 ? *ph : h);
+
+ names* r (scope::lookup (name, ti, flags | exclude_outer, &rh));
+
+ if (r != 0)
+ return r;
+
+ // If we found a name but the types didn't match, then bail out
+ // unless we want hidden names.
+ //
+ if (rh && (flags & include_hidden) == 0)
+ return 0;
+
+ // Look in the base classes unless requested not to. For the name
+ // lookup purposes, bases can be viewed as a parallel set of outer
+ // scopes that are searched after the class scope and before any
+ // real outer scope. Interestingly, outer scopes of bases are not
+ // considered during this lookup, only their bases.
+ //
+ if ((flags & exclude_base) == 0)
+ {
+ // Being hidden in one base doesn't mean it is also hidden in the
+ // other. Normally that would be an ambiguous lookup, but we use
+ // relaxed rules.
+ //
+ bool any_h (false); // Indicates whether any base hides the name.
+
+ for (inherits_iterator i (inherits_begin ()); i != inherits_end (); ++i)
+ {
+ bool h (false); // Indicates whether this base hides the name.
+ names* br (i->base ().lookup (name, ti, flags | exclude_outer, &h));
+ any_h = any_h || h;
+
+ if (br != 0)
+ {
+ if (r != 0)
+ throw ambiguous (*r, *br);
+
+ r = br;
+
+ if (h)
+ rh = true;
+ }
+ }
+
+ if (r != 0)
+ return r;
+
+ if (any_h)
+ {
+ rh = true;
+ if ((flags & include_hidden) == 0)
+ return 0;
+ }
+ }
+
+ // Look in the outer scope unless requested not to.
+ //
+ if ((flags & exclude_outer) == 0)
+ return scope ().lookup (name, ti, flags, &rh);
+
+ return 0;
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // inherits
+ //
+ {
+ type_info ti (typeid (inherits));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // class_
+ //
+ {
+ type_info ti (typeid (class_));
+ ti.add_base (typeid (type));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/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/odb/semantics/derived.cxx b/odb/odb/semantics/derived.cxx
new file mode 100644
index 0000000..771ad21
--- /dev/null
+++ b/odb/odb/semantics/derived.cxx
@@ -0,0 +1,254 @@
+// file : odb/semantics/derived.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/derived.hxx>
+
+using namespace std;
+
+namespace semantics
+{
+ qualifies::
+ qualifies ()
+ : hint_ (0)
+ {
+ }
+
+ qualifier::
+ qualifier (path const& file,
+ size_t line,
+ size_t column,
+ tree tn,
+ bool c,
+ bool v,
+ bool r)
+ : node (file, line, column, tn), c_ (c), v_ (v), r_ (r)
+ {
+ }
+
+ string qualifier::
+ fq_name (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ return nameable::fq_name (hint);
+
+ // GCC type_as_string() for some reason cannot correctly print names
+ // like 'const std::string'. Instead it prints 'const string'.
+ //
+ type& bt (base_type ());
+
+ // Use the trailing qualifier syntax so that we don't get bogged down
+ // in stuff like 'const const foo*'. We also have to handle arrays in
+ // a special way since char[16] const is not a legal syntax.
+ //
+ string q;
+ if (c_)
+ q += " const";
+
+ if (v_)
+ q += " volatile";
+
+ if (r_)
+ q += " __restrict";
+
+ hint = qualifies ().hint ();
+
+ if (array* a = dynamic_cast<array*> (&bt))
+ return a->fq_name (hint, q);
+ else
+ return bt.fq_name (hint) + q;
+ }
+
+ points::
+ points ()
+ : hint_ (0)
+ {
+ }
+
+ pointer::
+ pointer (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ string pointer::
+ fq_name (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ return nameable::fq_name (hint);
+
+ // GCC type_as_string() for some reason cannot correctly print names
+ // like 'const std::string*'. Instead it prints 'const string*'.
+ //
+ string r (base_type ().fq_name (points ().hint ()));
+ r += '*';
+ return r;
+ }
+
+ references::
+ references ()
+ : hint_ (0)
+ {
+ }
+
+ reference::
+ reference (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ string reference::
+ fq_name (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ return nameable::fq_name (hint);
+
+ // GCC type_as_string() for some reason cannot correctly print names
+ // like 'const std::string&'. Instead it prints 'const string&'.
+ //
+ string r (base_type ().fq_name (points ().hint ()));
+ r += '&';
+ return r;
+ }
+
+ contains::
+ contains ()
+ : hint_ (0)
+ {
+ }
+
+ array::
+ array (path const& file,
+ size_t line,
+ size_t column,
+ tree tn,
+ unsigned long long size)
+ : node (file, line, column, tn), size_ (size)
+ {
+ }
+
+ string array::
+ fq_name (names* hint) const
+ {
+ // GCC type_as_string() for some reason cannot correctly print names
+ // like 'const std::string[123]'. Instead it prints 'const string[123]'.
+ //
+ string t;
+ return fq_name (hint, t);
+ }
+
+ string array::
+ fq_name (names* hint, string& t) const
+ {
+ if (hint != 0 || defined_ != 0)
+ return nameable::fq_name (hint) + t;
+
+ t += '[';
+ ostringstream ostr;
+ ostr << size ();
+ t += ostr.str ();
+
+ if (size () > 0xFFFFFFFF)
+ t += "ULL";
+ else if (size () > 2147483647)
+ t += "U";
+
+ t += ']';
+
+ type& bt (base_type ());
+ hint = contains ().hint ();
+ array* a;
+
+ if (hint != 0 || (a = dynamic_cast<array*> (&bt)) == 0)
+ return bt.fq_name (hint) + t;
+ else
+ return a->fq_name (0, t);
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // derived_type
+ //
+ {
+ type_info ti (typeid (derived_type));
+ ti.add_base (typeid (type));
+ insert (ti);
+ }
+
+ // qualifies
+ //
+ {
+ type_info ti (typeid (qualifies));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // qualifier
+ //
+ {
+ type_info ti (typeid (qualifier));
+ ti.add_base (typeid (derived_type));
+ insert (ti);
+ }
+
+ // points
+ //
+ {
+ type_info ti (typeid (points));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // pointer
+ //
+ {
+ type_info ti (typeid (pointer));
+ ti.add_base (typeid (derived_type));
+ insert (ti);
+ }
+
+ // references
+ //
+ {
+ type_info ti (typeid (references));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // reference
+ //
+ {
+ type_info ti (typeid (reference));
+ ti.add_base (typeid (derived_type));
+ insert (ti);
+ }
+
+ // contains
+ //
+ {
+ type_info ti (typeid (contains));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // array
+ //
+ {
+ type_info ti (typeid (array));
+ ti.add_base (typeid (derived_type));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/derived.hxx b/odb/odb/semantics/derived.hxx
new file mode 100644
index 0000000..e58ec9f
--- /dev/null
+++ b/odb/odb/semantics/derived.hxx
@@ -0,0 +1,440 @@
+// file : odb/semantics/derived.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_DERIVED_HXX
+#define ODB_SEMANTICS_DERIVED_HXX
+
+#include <odb/semantics/elements.hxx>
+
+namespace semantics
+{
+ //
+ // Derived types (cvr-qualifiers, pointer, reference, and array).
+ //
+
+ class derived_type: public type
+ {
+ public:
+ virtual type&
+ base_type () const = 0;
+ };
+
+ //
+ //
+ class qualifier;
+
+ class qualifies: public edge
+ {
+ public:
+ typedef semantics::type type_type;
+ typedef semantics::qualifier qualifier_type;
+
+ type_type&
+ type () const
+ {
+ return *type_;
+ }
+
+ qualifier_type&
+ qualifier () const
+ {
+ return *qualifier_;
+ }
+
+ // Name hint of the base type.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ qualifies ();
+
+ void
+ set_left_node (qualifier_type& n)
+ {
+ qualifier_ = &n;
+ }
+
+ void
+ set_right_node (type_type& n)
+ {
+ type_ = &n;
+ }
+
+ protected:
+ type_type* type_;
+ qualifier_type* qualifier_;
+ names* hint_;
+ };
+
+ class qualifier: public derived_type
+ {
+ public:
+ typedef semantics::qualifies qualifies_type;
+
+ bool
+ const_ () const
+ {
+ return c_;
+ }
+
+ bool
+ volatile_ () const
+ {
+ return v_;
+ }
+
+ bool
+ restrict_ () const
+ {
+ return r_;
+ }
+
+ virtual type&
+ base_type () const
+ {
+ return qualifies_->type ();
+ }
+
+ qualifies_type&
+ qualifies () const
+ {
+ return *qualifies_;
+ }
+
+ public:
+ virtual string
+ fq_name (names*) const;
+
+ public:
+ qualifier (path const&,
+ size_t line,
+ size_t column,
+ tree,
+ bool c,
+ bool v,
+ bool r);
+
+ void
+ add_edge_left (qualifies_type& e)
+ {
+ qualifies_ = &e;
+ }
+
+ private:
+ bool c_, v_, r_;
+ qualifies_type* qualifies_;
+ };
+
+ //
+ //
+ class pointer;
+
+ class points: public edge
+ {
+ public:
+ typedef semantics::type type_type;
+ typedef semantics::pointer pointer_type;
+
+ type_type&
+ type () const
+ {
+ return *type_;
+ }
+
+ pointer_type&
+ pointer () const
+ {
+ return *pointer_;
+ }
+
+ // Name hint of the base type.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ points ();
+
+ void
+ set_left_node (pointer_type& n)
+ {
+ pointer_ = &n;
+ }
+
+ void
+ set_right_node (type_type& n)
+ {
+ type_ = &n;
+ }
+
+ protected:
+ type_type* type_;
+ pointer_type* pointer_;
+ names* hint_;
+ };
+
+ class pointer: public derived_type
+ {
+ public:
+ typedef semantics::points points_type;
+
+ virtual type&
+ base_type () const
+ {
+ return points_->type ();
+ }
+
+ points_type&
+ points () const
+ {
+ return *points_;
+ }
+
+ public:
+ virtual string
+ fq_name (names*) const;
+
+ public:
+ pointer (path const&, size_t line, size_t column, tree);
+
+ void
+ add_edge_left (points_type& e)
+ {
+ points_ = &e;
+ }
+
+ private:
+ points_type* points_;
+ };
+
+
+ //
+ //
+ class reference;
+
+ class references: public edge
+ {
+ public:
+ typedef semantics::type type_type;
+ typedef semantics::reference reference_type;
+
+ type_type&
+ type () const
+ {
+ return *type_;
+ }
+
+ reference_type&
+ reference () const
+ {
+ return *reference_;
+ }
+
+ // Name hint of the base type.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ references ();
+
+ void
+ set_left_node (reference_type& n)
+ {
+ reference_ = &n;
+ }
+
+ void
+ set_right_node (type_type& n)
+ {
+ type_ = &n;
+ }
+
+ protected:
+ type_type* type_;
+ reference_type* reference_;
+ names* hint_;
+ };
+
+ class reference: public derived_type
+ {
+ public:
+ typedef semantics::references references_type;
+
+ virtual type&
+ base_type () const
+ {
+ return references_->type ();
+ }
+
+ references_type&
+ references () const
+ {
+ return *references_;
+ }
+
+ public:
+ virtual string
+ fq_name (names*) const;
+
+ public:
+ reference (path const&, size_t line, size_t column, tree);
+
+ void
+ add_edge_left (references_type& e)
+ {
+ references_ = &e;
+ }
+
+ private:
+ references_type* references_;
+ };
+
+
+ //
+ //
+ class array;
+
+ class contains: public edge
+ {
+ public:
+ typedef semantics::type type_type;
+ typedef semantics::array array_type;
+
+ type_type&
+ type () const
+ {
+ return *type_;
+ }
+
+ array_type&
+ array () const
+ {
+ return *array_;
+ }
+
+ // Name hint of the base type.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ contains ();
+
+ void
+ set_left_node (array_type& n)
+ {
+ array_ = &n;
+ }
+
+ void
+ set_right_node (type_type& n)
+ {
+ type_ = &n;
+ }
+
+ protected:
+ type_type* type_;
+ array_type* array_;
+ names* hint_;
+ };
+
+ class array: public derived_type
+ {
+ public:
+ typedef semantics::contains contains_type;
+
+ // Return the number of elements in the array or 0 if it is not
+ // specified (e.g., int[]).
+ //
+ unsigned long long
+ size () const
+ {
+ return size_;
+ }
+
+ virtual type&
+ base_type () const
+ {
+ return contains_->type ();
+ }
+
+ contains_type&
+ contains () const
+ {
+ return *contains_;
+ }
+
+ public:
+ virtual string
+ fq_name (names*) const;
+
+ private:
+ friend class qualifier;
+
+ string
+ fq_name (names*, string& trailer) const;
+
+ using derived_type::fq_name; // Unhide.
+
+ public:
+ array (path const&,
+ size_t line,
+ size_t column,
+ tree,
+ unsigned long long size);
+
+ void
+ add_edge_left (contains_type& e)
+ {
+ contains_ = &e;
+ }
+
+ private:
+ contains_type* contains_;
+ unsigned long long size_;
+ };
+}
+
+#endif // ODB_SEMANTICS_DERIVED_HXX
diff --git a/odb/odb/semantics/elements.cxx b/odb/odb/semantics/elements.cxx
new file mode 100644
index 0000000..b5793d0
--- /dev/null
+++ b/odb/odb/semantics/elements.cxx
@@ -0,0 +1,619 @@
+// file : odb/semantics/elements.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/cxx-lexer.hxx>
+
+#include <odb/semantics/elements.hxx>
+#include <odb/semantics/namespace.hxx>
+#include <odb/semantics/unit.hxx>
+
+using namespace std;
+
+namespace semantics
+{
+ // access
+ //
+ static char const* access_str[] = {"public", "protected", "private"};
+
+ char const* access::
+ string () const
+ {
+ return access_str[value_];
+ }
+
+ //
+ //
+ node::
+ node (path const& file, size_t line, size_t column, tree tn)
+ : tree_node_ (tn), loc_ (file, line, column)
+ {
+ }
+
+ node::
+ node ()
+ : loc_ (0)
+ {
+ // GCC plugin machinery #define's abort as a macro.
+ //
+ // std::abort ();
+ abort ();
+ }
+
+ // nameable
+ //
+
+ bool nameable::
+ in_scope (scope_type& s)
+ {
+ for (scope_type* p (&scope ());; p = &p->scope_ ())
+ {
+ //@@ Need to handle namespace extensions.
+ //
+ if (p == &s)
+ return true;
+
+ if (!p->named_p () || p->global_scope ())
+ break;
+ }
+
+ return false;
+ }
+
+ bool nameable::
+ anonymous_ () const
+ {
+ tree n (tree_node ());
+
+ if (TYPE_P (n))
+ {
+ tree name (0);
+
+ if (tree decl = TYPE_NAME (n))
+ name = DECL_NAME (decl);
+
+ return name != 0 && IDENTIFIER_ANON_P (name);
+ }
+
+ return true;
+ }
+
+ bool nameable::
+ fq_anonymous_ (scope_entry const* prev) const
+ {
+ scope_entry scope (this, prev);
+
+ // Nameable is fq-anonymous if all the paths to the global scope
+ // have at least one anonymous link.
+ //
+ if (defined_ != 0 || !named_.empty ())
+ {
+ if (named ().global_scope ())
+ return false;
+
+ if (defined_ != 0)
+ {
+ nameable const& s (defined_->scope ());
+
+ if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
+ return false;
+ }
+
+ for (names_list::const_iterator i (named_.begin ()), e (named_.end ());
+ i != e; ++i)
+ {
+ nameable const& s ((*i)->scope ());
+
+ if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
+ return false;
+ }
+ }
+
+ // If we can get a literal name for this type node, then it is not
+ // anonymous as long as its scope is not anonymous.
+ //
+ tree type (tree_node ());
+
+ if (TYPE_P (type))
+ {
+ tree name (0);
+
+ if (tree decl = TYPE_NAME (type))
+ {
+ name = DECL_NAME (decl);
+ if (name != 0 && IDENTIFIER_ANON_P (name))
+ return true;
+
+ tree s (CP_DECL_CONTEXT (decl));
+
+ gcc_tree_code_type tc (TREE_CODE (s));
+
+ if (tc == TYPE_DECL)
+ s = TREE_TYPE (s);
+ else if (tc == NAMESPACE_DECL)
+ {
+ // "Unwind" any inline namespaces since they are not in
+ // semantic grapth.
+ //
+ while (s != global_namespace)
+ {
+ tree prev (CP_DECL_CONTEXT (s));
+
+#if BUILDING_GCC_MAJOR >= 8
+ if (!is_nested_namespace (prev, s, true))
+#else
+ if (!is_associated_namespace (prev, s))
+#endif
+ break;
+
+ s = prev;
+ }
+ }
+
+ if (nameable* n = dynamic_cast<nameable*> (unit ().find (s)))
+ return scope.find (n) || n->fq_anonymous_ (&scope);
+ }
+ else
+ return false; // Assume this is a derived type (e.g., pointer).
+ }
+
+ return true;
+ }
+
+ bool nameable::
+ fq_anonymous (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ {
+ names& n (hint ? *hint : *defined_);
+
+ if (n.global_scope ())
+ return false;
+
+ return n.scope ().fq_anonymous ();
+ }
+ else
+ return fq_anonymous ();
+ }
+
+ static string
+ qualify_names (string const& n, bool qualify_first)
+ {
+ // @@ Creating a lexer for each call is a bad idea. Need
+ // to cache it somewhere.
+ //
+ cxx_string_lexer l;
+ l.start (n);
+
+ string r, t;
+ bool punc (false);
+ bool scoped (false);
+
+ // Names returned by GCC's type_as_string() (on which this function
+ // is called) include inline namespaces (e.g., std::__cxx11::string).
+ // So, besides fully-qualifying names, this function also needs to get
+ // rid of those. The idea is to resolve names as we lex them, skipping
+ // inline namespaces and stopping once we reach something other than a
+ // namespace.
+ //
+ tree ns (global_namespace);
+ tree id;
+
+ for (cpp_ttype tt = l.next (t, &id); tt != CPP_EOF; tt = l.next (t, &id))
+ {
+ if (punc && tt > CPP_LAST_PUNCTUATOR)
+ r += ' ';
+
+ punc = false;
+ tree new_ns (global_namespace); // By default, revert to global.
+
+ switch (static_cast<unsigned> (tt))
+ {
+ case CPP_LESS:
+ {
+ r += "< ";
+ break;
+ }
+ case CPP_GREATER:
+ {
+ r += " >";
+ break;
+ }
+ case CPP_COMMA:
+ {
+ r += ", ";
+ break;
+ }
+ case CPP_NAME:
+ {
+ // Check if this is a namespace and, if so, whether it is
+ // inline.
+ //
+ if (ns != 0)
+ {
+ new_ns = lookup_qualified_name (ns, id, false, false);
+
+ if (new_ns == error_mark_node ||
+ TREE_CODE (new_ns) != NAMESPACE_DECL)
+ new_ns = 0; // Not a namespace, stop resolving.
+ else
+ {
+ // Check if this is an inline namespace and skip it if so.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ if (is_nested_namespace (ns, new_ns, true))
+#else
+ if (is_associated_namespace (ns, new_ns))
+#endif
+ {
+ // Skip also the following scope operator. Strictly speaking
+ // there could be none (i.e., this is a name of an inline
+ // namespace) but we only use this function to print names
+ // of anonymous types.
+ //
+ assert (l.next (t) == CPP_SCOPE);
+ continue;
+ }
+ }
+ }
+ else
+ new_ns = 0; // Keep it disabled until we hit a new name.
+
+ // If the name was not preceeded with '::', qualify it.
+ //
+ if (!scoped)
+ {
+ if (!qualify_first)
+ qualify_first = true;
+ else
+ r += "::";
+ }
+
+ r += t;
+ punc = true;
+ break;
+ }
+ case CPP_KEYWORD:
+ case CPP_NUMBER:
+ {
+ r += t;
+ punc = true;
+ break;
+ }
+ case CPP_SCOPE:
+ {
+ new_ns = ns; // Don't change the namespace.
+ }
+ // Fall through.
+ default:
+ {
+ r += t;
+ break;
+ }
+ }
+
+ scoped = (tt == CPP_SCOPE);
+ ns = new_ns;
+ }
+
+ return r;
+ }
+
+ string nameable::
+ name_ () const
+ {
+ tree n (tree_node ());
+
+ if (!TYPE_P (n))
+ return "<anonymous>";
+
+ // @@ Doing this once and caching the result is probably a
+ // good idea.
+ //
+ return qualify_names (
+ type_as_string (n, TFF_PLAIN_IDENTIFIER | TFF_UNQUALIFIED_NAME), false);
+ }
+
+ string nameable::
+ fq_name () const
+ {
+ return fq_name_ (0);
+ }
+
+ string nameable::
+ fq_name_ (scope_entry const* prev) const
+ {
+ // @@ Doing this once and caching the result is probably a
+ // good idea.
+ //
+ scope_entry scope (this, prev);
+
+ if (named_p () && named ().global_scope ())
+ return "";
+
+ if (defined_ != 0)
+ {
+ nameable const& s (defined_->scope ());
+
+ if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
+ return s.fq_name_ (&scope) + "::" + name ();
+ }
+
+ for (names_list::const_iterator i (named_.begin ()), e (named_.end ());
+ i != e; ++i)
+ {
+ nameable const& s ((*i)->scope ());
+
+ if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
+ return s.fq_name_ (&scope) + "::" + name ();
+ }
+
+ tree n (tree_node ());
+
+ if (!TYPE_P (n))
+ return "<anonymous>";
+
+ return qualify_names (type_as_string (n, TFF_PLAIN_IDENTIFIER), true);
+ }
+
+ string nameable::
+ fq_name (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ {
+ names& n (hint ? *hint : *defined_);
+
+ if (n.global_scope ())
+ return "";
+
+ return n.scope ().fq_name () + "::" + n.name ();
+ }
+ else
+ {
+ // Since there was no hint, prefer the literal name over the names
+ // edges.
+ //
+ tree n (tree_node ());
+
+ if (TYPE_P (n))
+ return qualify_names (type_as_string (n, TFF_PLAIN_IDENTIFIER), true);
+
+ // Last resort is to call the other version of fq_name which will
+ // check the names edges.
+ //
+ return fq_name ();
+ }
+ }
+
+ // scope
+ //
+
+ scope::names_iterator_pair scope::
+ find (string const& name) const
+ {
+ names_map::const_iterator i (names_map_.find (name));
+
+ if (i == names_map_.end ())
+ return names_iterator_pair (names_.end (), names_.end ());
+ else
+ return names_iterator_pair (i->second.begin (), i->second.end ());
+ }
+
+ scope::names_iterator scope::
+ find (names& e)
+ {
+ list_iterator_map::iterator i (iterator_map_.find (&e));
+ return i != iterator_map_.end () ? i->second : names_.end ();
+ }
+
+ static bool
+ is_base (type_id const& b, compiler::type_info const& d)
+ {
+ using compiler::type_info;
+
+ for (type_info::base_iterator i (d.begin_base ());
+ i != d.end_base (); ++i)
+ {
+ type_info const& ti (i->type_info ());
+
+ if (b == ti.type_id () || is_base (b, ti))
+ return true;
+ }
+
+ return false;
+ }
+
+ names* scope::
+ lookup (string const& name,
+ type_id const& ti,
+ unsigned int flags,
+ bool* hidden) const
+ {
+ names_iterator_pair p (find (name));
+ names* r (0);
+
+ for (names_const_iterator i (p.first); i != p.second; ++i)
+ {
+ type_id const& xti (typeid (i->named ()));
+
+ // If types are equal, then we found a match. Also check if ti is
+ // a base type of xti.
+ //
+ if (xti == ti || is_base (ti, compiler::lookup (xti)))
+ {
+ if (r != 0)
+ {
+ // If both are namespaces, then the one is just an extension
+ // of the other.
+ //
+ if (!(r->named ().is_a<namespace_> () &&
+ i->named ().is_a<namespace_> ()))
+ throw ambiguous (*r, *i);
+ }
+ else
+ r = &*i;
+ }
+ }
+
+ if (r != 0)
+ return r;
+
+ // If we found a name but the types didn't match, then bail out
+ // unless we want hidden names.
+ //
+ if (p.first != p.second)
+ {
+ if (hidden != 0)
+ *hidden = true;
+
+ if ((flags & include_hidden) == 0)
+ return 0;
+ }
+
+ // Look in the outer scope unless requested not to or if this is
+ // the global scope.
+ //
+ if ((flags & exclude_outer) == 0 && named_p () && !global_scope ())
+ return scope ().lookup (name, ti, flags, hidden);
+
+ return 0;
+ }
+
+ void scope::
+ add_edge_left (names& e)
+ {
+ names_list::iterator i (names_.insert (names_.end (), &e));
+ iterator_map_[&e] = i;
+ names_map_[e.name ()].push_back (&e);
+ }
+
+ void scope::
+ add_edge_left (names& e, names_iterator after)
+ {
+ names_list::iterator i;
+
+ if (after.base () == names_.end ())
+ i = names_.insert (names_.begin (), &e);
+ else
+ {
+ names_list::iterator j (after.base ());
+ i = names_.insert (++j, &e);
+ }
+
+ iterator_map_[&e] = i;
+ names_map_[e.name ()].push_back (&e);
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // node
+ //
+ insert (type_info (typeid (node)));
+
+ // edge
+ //
+ insert (type_info (typeid (edge)));
+
+ // names
+ //
+ {
+ type_info ti (typeid (names));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // declares
+ //
+ {
+ type_info ti (typeid (declares));
+ ti.add_base (typeid (names));
+ insert (ti);
+ }
+
+ // defines
+ //
+ {
+ type_info ti (typeid (defines));
+ ti.add_base (typeid (declares));
+ insert (ti);
+ }
+
+ // typedefs
+ //
+ {
+ type_info ti (typeid (typedefs));
+ ti.add_base (typeid (declares));
+ insert (ti);
+ }
+
+ // nameable
+ //
+ {
+ type_info ti (typeid (nameable));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ // scope
+ //
+ {
+ type_info ti (typeid (scope));
+ ti.add_base (typeid (nameable));
+ insert (ti);
+ }
+
+ // type
+ //
+ {
+ type_info ti (typeid (type));
+ ti.add_base (typeid (nameable));
+ insert (ti);
+ }
+
+ // belongs
+ //
+ {
+ type_info ti (typeid (belongs));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // instance
+ //
+ {
+ type_info ti (typeid (instance));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ // data_member
+ //
+ {
+ type_info ti (typeid (data_member));
+ ti.add_base (typeid (nameable));
+ ti.add_base (typeid (instance));
+ insert (ti);
+ }
+
+ // unsupported_type
+ //
+ {
+ type_info ti (typeid (unsupported_type));
+ ti.add_base (typeid (type));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/elements.hxx b/odb/odb/semantics/elements.hxx
new file mode 100644
index 0000000..699a1be
--- /dev/null
+++ b/odb/odb/semantics/elements.hxx
@@ -0,0 +1,841 @@
+// file : odb/semantics/elements.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_ELEMENTS_HXX
+#define ODB_SEMANTICS_ELEMENTS_HXX
+
+#include <map>
+#include <list>
+#include <vector>
+#include <string>
+#include <cstdlib> // std::abort
+#include <cstddef> // std::size_t
+#include <utility> // std::pair
+#include <cassert>
+
+#include <libcutl/fs/path.hxx>
+#include <libcutl/container/graph.hxx>
+#include <libcutl/container/pointer-iterator.hxx>
+#include <libcutl/compiler/type-id.hxx>
+#include <libcutl/compiler/context.hxx>
+
+#include <odb/gcc-fwd.hxx>
+#include <odb/location.hxx>
+
+namespace semantics
+{
+ using namespace cutl;
+
+ using std::size_t;
+ using std::string;
+
+ using container::graph;
+ using container::pointer_iterator;
+
+ using compiler::type_id;
+ using compiler::context;
+
+ //
+ //
+ using fs::path;
+ using fs::invalid_path;
+
+ //
+ //
+ class access
+ {
+ public:
+ enum value { public_, protected_, private_ };
+
+ access (value v)
+ : value_ (v)
+ {
+ }
+
+ operator value () const
+ {
+ return value_;
+ }
+
+ char const* string () const;
+
+ private:
+ value value_;
+ };
+
+ //
+ //
+ class node;
+ class edge;
+ class unit;
+
+ // Support for inserting edges at specified positions.
+ //
+ template <typename N, typename I>
+ struct node_position
+ {
+ node_position (N& node, I pos)
+ : node_ (node), pos_ (pos)
+ {
+ }
+
+ operator N& () const
+ {
+ return node_;
+ }
+
+ template <typename E>
+ void
+ add_edge_left (E& e)
+ {
+ node_.add_edge_left (e, pos_);
+ }
+
+ template <typename E>
+ void
+ add_edge_right (E& e)
+ {
+ node_.add_edge_right (e, pos_);
+ }
+
+ private:
+ N& node_;
+ I pos_;
+ };
+
+ //
+ //
+ class edge: public context
+ {
+ public:
+ virtual
+ ~edge () {}
+
+ public:
+ template <typename X>
+ X*
+ is_a () {return dynamic_cast<X*> (this);}
+
+ template <typename X>
+ const X*
+ is_a () const {return dynamic_cast<const X*> (this);}
+ };
+
+ //
+ //
+ class node: public context
+ {
+ public:
+ virtual
+ ~node () {}
+
+ public:
+ tree
+ tree_node () const
+ {
+ return tree_node_;
+ }
+
+ public:
+ typedef ::location location_type;
+
+ path const&
+ file () const
+ {
+ return loc_.file;
+ }
+
+ size_t
+ line () const
+ {
+ return loc_.line;
+ }
+
+ size_t
+ column () const
+ {
+ return loc_.column;
+ }
+
+ location_type const&
+ location () const
+ {
+ return loc_;
+ }
+
+ public:
+ template <typename X>
+ X*
+ is_a () {return dynamic_cast<X*> (this);}
+
+ template <typename X>
+ const X*
+ is_a () const {return dynamic_cast<const X*> (this);}
+
+ public:
+ node (path const& file, size_t line, size_t column, tree);
+
+ // Sink functions that allow extensions in the form of one-way
+ // edges.
+ //
+ void
+ add_edge_left (edge&) {}
+
+ void
+ add_edge_right (edge&) {}
+
+ protected:
+ // For virtual inheritance. Should never be actually called.
+ //
+ node ();
+
+ protected:
+ typedef semantics::unit unit_type;
+
+ unit_type const&
+ unit () const
+ {
+ return *unit_;
+ }
+
+ unit_type&
+ unit ()
+ {
+ return *unit_;
+ }
+
+ private:
+ friend class semantics::unit;
+
+ void
+ unit (unit_type& u)
+ {
+ unit_ = &u;
+ }
+
+ private:
+ tree tree_node_;
+ unit_type* unit_;
+
+ location_type loc_;
+ };
+
+ //
+ //
+ class scope;
+ class nameable;
+
+
+ //
+ //
+ class names: public edge
+ {
+ public:
+ typedef semantics::scope scope_type;
+ typedef semantics::access access_type;
+
+ string const&
+ name () const
+ {
+ return name_;
+ }
+
+ scope_type&
+ scope () const
+ {
+ return *scope_;
+ }
+
+ // Return true if the entity that this edge names is a global scope.
+ // In this case calling scope() is undefined behavior.
+ //
+ bool
+ global_scope () const
+ {
+ return scope_ == 0;
+ }
+
+ nameable&
+ named () const
+ {
+ return *named_;
+ }
+
+ access_type
+ access () const
+ {
+ return access_;
+ }
+
+ // Names edge in terms of which this edge was defined. Can be NULL.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ names (string const& name, access_type access = access_type::public_)
+ : name_ (name), access_ (access), hint_ (0)
+ {
+ }
+
+ void
+ set_left_node (scope_type& n)
+ {
+ scope_ = &n;
+ }
+
+ void
+ set_right_node (nameable& n)
+ {
+ named_ = &n;
+ }
+
+ protected:
+ scope_type* scope_;
+ nameable* named_;
+ string name_;
+ access_type access_;
+ names* hint_;
+ };
+
+ //
+ // Declarations and definitions.
+ //
+
+ class declares: public names
+ {
+ public:
+ declares (string const& name, access_type access = access_type::public_)
+ : names (name, access)
+ {
+ }
+ };
+
+ class defines: public declares
+ {
+ public:
+ defines (string const& name, access_type access = access_type::public_)
+ : declares (name, access)
+ {
+ }
+ };
+
+ class type;
+ class typedefs: public declares
+ {
+ public:
+ typedef semantics::type type_type;
+
+ type_type&
+ type () const;
+
+ public:
+ typedefs (string const& name, access_type access = access_type::public_)
+ : declares (name, access)
+ {
+ }
+ };
+
+ //
+ //
+ class nameable: public virtual node
+ {
+ typedef std::vector<names*> names_list;
+
+ public:
+ typedef semantics::scope scope_type;
+
+ // Return true if this type is unnamed and no literal name, such as
+ // template-id or derived type declarator, can be used instead.
+ //
+ bool
+ anonymous () const
+ {
+ if (defined_ != 0 || !named_.empty ())
+ return false;
+
+ return anonymous_ ();
+ }
+
+ // Return true if the node itself or any of the scopes up to the
+ // global scope is anonymous. For a named class nested in an unnamed
+ // class, anonymous() will return false and fq_anonymous() will
+ // return true.
+ //
+ bool
+ fq_anonymous () const
+ {
+ return fq_anonymous_ (0);
+ }
+
+ // As above but use the hint to select the first outer scope. If
+ // hint is 0, use the defines edge.
+ //
+ bool
+ fq_anonymous (names* hint) const;
+
+ // Return the node's unqualifed name. If the node has a name, then
+ // return it, preferring the defines edge. Otherwise, return a
+ // literal name, e.g., template-id or a derived type declarator.
+ // Finally, if the type is anonymous, return <anonymous> string.
+ //
+ string
+ name () const
+ {
+ if (defined_ != 0)
+ return defined_->name ();
+
+ if (!named_.empty ())
+ return named_[0]->name ();
+
+ return name_ ();
+ }
+
+ // Return the node's fully-qualifed name.
+ //
+ virtual string
+ fq_name () const;
+
+ // As above but use the hint to select the first outer scope. If hint
+ // is 0, use the defines edge.
+ //
+ virtual string
+ fq_name (names* hint) const;
+
+ // Return true if the type is named.
+ //
+ bool
+ named_p () const
+ {
+ return defined_ != 0 || !named_.empty ();
+ }
+
+ scope_type&
+ scope () const
+ {
+ return named ().scope ();
+ }
+
+ names&
+ named () const
+ {
+ return defined_ != 0 ? *defined_ : *named_[0];
+ }
+
+ bool
+ in_scope (scope_type&);
+
+ public:
+ nameable ()
+ : defined_ (0)
+ {
+ }
+
+ void
+ add_edge_right (defines& e)
+ {
+ assert (defined_ == 0);
+ defined_ = &e;
+ }
+
+ void
+ add_edge_right (names& e)
+ {
+ named_.push_back (&e);
+ }
+
+ using node::add_edge_right;
+
+ protected:
+ // We need to keep the scope we have seen in the fq_* function
+ // family in order to detect names that are inside the node
+ // and which would otherwise lead to infinite recursion. Here
+ // is the canonical example:
+ //
+ // template <typename X>
+ // class c
+ // {
+ // typedef c this_type;
+ // };
+ //
+ struct scope_entry
+ {
+ scope_entry (nameable const* e, scope_entry const* p)
+ : entry_ (e), prev_ (p)
+ {
+ }
+
+ bool
+ find (nameable const* n) const
+ {
+ for (scope_entry const* i (this); i != 0; i = i->prev_)
+ if (i->entry_ == n)
+ return true;
+
+ return false;
+ }
+
+ private:
+ nameable const* entry_;
+ scope_entry const* prev_;
+ };
+
+ bool
+ anonymous_ () const;
+
+ bool
+ fq_anonymous_ (scope_entry const*) const;
+
+ string
+ name_ () const;
+
+ string
+ fq_name_ (scope_entry const*) const;
+
+ protected:
+ defines* defined_;
+ names_list named_;
+ };
+
+
+ // Ambiguous name lookup exception.
+ //
+ struct ambiguous
+ {
+ ambiguous (names& f, names& s): first (f), second (s) {}
+ names& first;
+ names& second;
+ };
+
+ // Unresolved name lookup exception.
+ //
+ struct unresolved
+ {
+ unresolved (string const& n, bool tm): name (n), type_mismatch (tm) {}
+ string name;
+ bool type_mismatch; // True if the name resolved but types didn't match.
+ };
+
+ //
+ //
+ class scope: public virtual nameable
+ {
+ protected:
+ typedef std::list<names*> names_list;
+ typedef std::map<names*, names_list::iterator> list_iterator_map;
+ typedef std::map<string, names_list> names_map;
+
+ public:
+ typedef pointer_iterator<names_list::iterator> names_iterator;
+ typedef pointer_iterator<names_list::const_iterator> names_const_iterator;
+
+ typedef
+ std::pair<names_const_iterator, names_const_iterator>
+ names_iterator_pair;
+
+ public:
+ bool
+ global_scope () const
+ {
+ return named ().global_scope ();
+ }
+
+ scope&
+ scope_ () const
+ {
+ return nameable::scope ();
+ }
+
+ names_iterator
+ names_begin ()
+ {
+ return names_.begin ();
+ }
+
+ names_iterator
+ names_end ()
+ {
+ return names_.end ();
+ }
+
+ names_const_iterator
+ names_begin () const
+ {
+ return names_.begin ();
+ }
+
+ names_const_iterator
+ names_end () const
+ {
+ return names_.end ();
+ }
+
+ // Find a name in this scope.
+ //
+ public:
+ virtual names_iterator_pair
+ find (string const& name) const;
+
+ names_iterator
+ find (names&);
+
+ // Lookup a name of the specified type in this scope and, if not
+ // found, in outer scopes.
+ //
+ public:
+ static unsigned int const exclude_outer = 0x01; // Exclude outer scopes.
+ static unsigned int const include_hidden = 0x02; // Include hidden names.
+
+ virtual names*
+ lookup (string const& name,
+ type_id const&,
+ unsigned int flags = 0,
+ bool* hidden = 0) const;
+
+ template <typename T>
+ T&
+ lookup (string const& name, unsigned int flags = 0) const
+ {
+ bool hidden (false);
+
+ if (names* n = lookup (name, typeid (T), flags, &hidden))
+ return dynamic_cast<T&> (n->named ());
+
+ throw unresolved (name, hidden);
+ }
+
+ public:
+ scope (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ void
+ add_edge_left (names&);
+
+ void
+ add_edge_left (names&, names_iterator after);
+
+ using nameable::add_edge_right;
+
+ protected:
+ scope ()
+ {
+ }
+
+ private:
+ names_list names_;
+ list_iterator_map iterator_map_;
+ names_map names_map_;
+ };
+
+ //
+ //
+ class points;
+
+ class belongs;
+ class qualifies;
+
+ class type: public virtual nameable
+ {
+ typedef std::vector<qualifies*> qualified;
+
+ public:
+ typedef pointer_iterator<qualified::const_iterator> qualified_iterator;
+
+ qualified_iterator
+ qualified_begin () const
+ {
+ return qualified_.begin ();
+ }
+
+ qualified_iterator
+ qualified_end () const
+ {
+ return qualified_.end ();
+ }
+
+ public:
+ bool
+ pointed_p () const {return pointed_ != 0;}
+
+ points&
+ pointed () const {return *pointed_;}
+
+ public:
+ type (): pointed_ (0) {}
+
+ void
+ add_edge_right (belongs&)
+ {
+ }
+
+ void
+ add_edge_right (qualifies& e)
+ {
+ qualified_.push_back (&e);
+ }
+
+ void
+ add_edge_right (points& e)
+ {
+ pointed_ = &e;
+ }
+
+ using nameable::add_edge_right;
+
+ private:
+ qualified qualified_;
+ points* pointed_;
+ };
+
+ //
+ //
+ class instance;
+
+ class belongs: public edge
+ {
+ public:
+ typedef semantics::type type_type;
+ typedef semantics::instance instance_type;
+
+ type_type&
+ type () const
+ {
+ return *type_;
+ }
+
+ instance_type&
+ instance () const
+ {
+ return *instance_;
+ }
+
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ belongs ()
+ : hint_ (0)
+ {
+ }
+
+ void
+ set_left_node (instance_type& n)
+ {
+ instance_ = &n;
+ }
+
+ void
+ set_right_node (type_type& n)
+ {
+ type_ = &n;
+ }
+
+ private:
+ type_type* type_;
+ instance_type* instance_;
+ names* hint_;
+ };
+
+ //
+ //
+ class instance: public virtual node
+ {
+ public:
+ typedef semantics::type type_type;
+ typedef semantics::belongs belongs_type;
+
+ type_type&
+ type () const
+ {
+ return belongs_->type ();
+ }
+
+ belongs_type&
+ belongs () const
+ {
+ return *belongs_;
+ }
+
+ public:
+ void
+ add_edge_left (belongs_type& e)
+ {
+ belongs_ = &e;
+ }
+
+ protected:
+ instance ()
+ {
+ }
+
+ private:
+ belongs_type* belongs_;
+ };
+
+ // Data member for class and union types.
+ //
+ class data_member: public nameable, public instance
+ {
+ public:
+ data_member (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ protected:
+ data_member ()
+ {
+ }
+ };
+
+ // Unsupported type.
+ //
+ class unsupported_type: public type
+ {
+ public:
+ string const&
+ type_name () const
+ {
+ return type_name_;
+ }
+
+ public:
+ unsupported_type (path const& file,
+ size_t line,
+ size_t column,
+ tree tn,
+ string const& type_name)
+ : node (file, line, column, tn), type_name_ (type_name)
+ {
+ }
+
+ private:
+ string const type_name_;
+ };
+}
+
+#include <odb/semantics/elements.ixx>
+
+#endif // ODB_SEMANTICS_ELEMENTS_HXX
diff --git a/odb/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/odb/semantics/enum.cxx b/odb/odb/semantics/enum.cxx
new file mode 100644
index 0000000..6432986
--- /dev/null
+++ b/odb/odb/semantics/enum.cxx
@@ -0,0 +1,85 @@
+// file : odb/semantics/enum.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/enum.hxx>
+
+namespace semantics
+{
+ enumerates::
+ enumerates ()
+ {
+ }
+
+ enumerator::
+ enumerator (path const& file,
+ size_t line,
+ size_t column,
+ tree tn,
+ unsigned long long value)
+ : node (file, line, column, tn), value_ (value)
+ {
+ }
+
+ underlies::
+ underlies ()
+ : type_ (0), enum__ (0), hint_ (0)
+ {
+ }
+
+ enum_::
+ enum_ (path const& file,
+ size_t line,
+ size_t column,
+ tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // enumerates
+ //
+ {
+ type_info ti (typeid (enumerates));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // enumerator
+ //
+ {
+ type_info ti (typeid (enumerator));
+ ti.add_base (typeid (nameable));
+ ti.add_base (typeid (instance));
+ insert (ti);
+ }
+
+ // underlies
+ //
+ {
+ type_info ti (typeid (underlies));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // enum_
+ //
+ {
+ type_info ti (typeid (enum_));
+ ti.add_base (typeid (type));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/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/odb/semantics/fundamental.cxx b/odb/odb/semantics/fundamental.cxx
new file mode 100644
index 0000000..ed4a67f
--- /dev/null
+++ b/odb/odb/semantics/fundamental.cxx
@@ -0,0 +1,222 @@
+// file : odb/semantics/fundamental.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/fundamental.hxx>
+
+namespace semantics
+{
+ string fund_type::
+ fq_name () const
+ {
+ return name ();
+ }
+
+ string fund_type::
+ fq_name (names* hint) const
+ {
+ if (hint == 0)
+ return name ();
+
+ return type::fq_name (hint);
+ }
+
+ // char
+ //
+ bool fund_char::
+ unsigned_ () const
+ {
+ return TYPE_UNSIGNED (tree_node ()) != 0;
+ }
+
+ // wchar_t
+ //
+ bool fund_wchar::
+ unsigned_ () const
+ {
+ return TYPE_UNSIGNED (tree_node ()) != 0;
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // fund_type
+ //
+ {
+ type_info ti (typeid (fund_type));
+ ti.add_base (typeid (type));
+ insert (ti);
+ }
+
+ // fund_void
+ //
+ {
+ type_info ti (typeid (fund_void));
+ ti.add_base (typeid (fund_type));
+ insert (ti);
+ }
+
+ // integral_type
+ //
+ {
+ type_info ti (typeid (integral_type));
+ ti.add_base (typeid (fund_type));
+ insert (ti);
+ }
+
+ // fund_bool
+ //
+ {
+ type_info ti (typeid (fund_bool));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_char
+ //
+ {
+ type_info ti (typeid (fund_char));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_wchar
+ //
+ {
+ type_info ti (typeid (fund_wchar));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_char16
+ //
+ {
+ type_info ti (typeid (fund_char16));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_char32
+ //
+ {
+ type_info ti (typeid (fund_char32));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_signed_char
+ //
+ {
+ type_info ti (typeid (fund_signed_char));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_unsigned_char
+ //
+ {
+ type_info ti (typeid (fund_unsigned_char));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_short
+ //
+ {
+ type_info ti (typeid (fund_short));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_unsigned_short
+ //
+ {
+ type_info ti (typeid (fund_unsigned_short));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_int
+ //
+ {
+ type_info ti (typeid (fund_int));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_unsigned_int
+ //
+ {
+ type_info ti (typeid (fund_unsigned_int));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_long
+ //
+ {
+ type_info ti (typeid (fund_long));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_unsigned_long
+ //
+ {
+ type_info ti (typeid (fund_unsigned_long));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_long_long
+ //
+ {
+ type_info ti (typeid (fund_long_long));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_unsigned_long_long
+ //
+ {
+ type_info ti (typeid (fund_unsigned_long_long));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_float
+ //
+ {
+ type_info ti (typeid (fund_float));
+ ti.add_base (typeid (fund_type));
+ insert (ti);
+ }
+
+ // fund_double
+ //
+ {
+ type_info ti (typeid (fund_double));
+ ti.add_base (typeid (fund_type));
+ insert (ti);
+ }
+
+ // fund_long_double
+ //
+ {
+ type_info ti (typeid (fund_long_double));
+ ti.add_base (typeid (fund_type));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/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/odb/semantics/namespace.cxx b/odb/odb/semantics/namespace.cxx
new file mode 100644
index 0000000..d9be903
--- /dev/null
+++ b/odb/odb/semantics/namespace.cxx
@@ -0,0 +1,107 @@
+// file : odb/semantics/namespace.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/namespace.hxx>
+
+namespace semantics
+{
+ namespace_::
+ namespace_ (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn), original_ (0)
+ {
+ }
+
+ namespace_::
+ namespace_ ()
+ : original_ (0)
+ {
+ }
+
+ names* namespace_::
+ lookup (string const& name,
+ type_id const& ti,
+ unsigned int flags,
+ bool* hidden) const
+ {
+ if (original_ != 0)
+ return original_->lookup (name, ti, flags, hidden);
+
+ // Being hidden in one namespace doesn't mean it is also hidden in
+ // the other. Normally that would be an ambiguous lookup, but we use
+ // relaxed rules.
+ //
+ bool h (false); // Indicates whether this namespace hides the name.
+ bool any_h (false); // Indicates whether any namespace hides the name.
+
+ names* r (scope::lookup (name, ti, flags | exclude_outer, &h));
+ any_h = any_h || h;
+
+ if (r != 0 && h && hidden != 0)
+ *hidden = true;
+
+ for (extensions_iterator i (extensions_begin ());
+ i != extensions_end ();
+ ++i)
+ {
+ h = false;
+ names* er ((*i)->scope::lookup (name, ti, flags | exclude_outer, &h));
+ any_h = any_h || h;
+
+ if (er != 0)
+ {
+ if (r != 0)
+ {
+ // If both are namespaces, then the one is just an extension
+ // of the other.
+ //
+ if (!(r->named ().is_a<namespace_> () &&
+ er->named ().is_a<namespace_> ()))
+ throw ambiguous (*r, *er);
+ }
+ else
+ r = er;
+
+ if (h && hidden != 0)
+ *hidden = true;
+ }
+ }
+
+ if (r != 0)
+ return r;
+
+ if (any_h)
+ {
+ if (hidden != 0)
+ *hidden = true;
+
+ if ((flags & include_hidden) == 0)
+ return 0;
+ }
+
+ // Look in the outer scope unless requested not to or if this is
+ // the global scope.
+ //
+ if ((flags & exclude_outer) == 0 && !global_scope ())
+ return scope ().lookup (name, ti, flags, hidden);
+
+ return 0;
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ type_info ti (typeid (namespace_));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+ } init_;
+ }
+}
diff --git a/odb/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/odb/semantics/relational/changelog.cxx b/odb/odb/semantics/relational/changelog.cxx
new file mode 100644
index 0000000..353497f
--- /dev/null
+++ b/odb/odb/semantics/relational/changelog.cxx
@@ -0,0 +1,187 @@
+// file : odb/semantics/relational/changelog.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <vector>
+#include <sstream>
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/changelog.hxx>
+#include <odb/semantics/relational/model.hxx>
+#include <odb/semantics/relational/changeset.hxx>
+
+using namespace std;
+
+namespace semantics
+{
+ namespace relational
+ {
+ changelog::
+ changelog (xml::parser& p)
+ : contains_model_ (0)
+ {
+ using namespace xml;
+
+ p.next_expect (parser::start_element, xmlns, "changelog");
+ p.content (content::complex);
+
+ if (p.attribute<unsigned int> ("version") != 1)
+ throw parsing (p, "unsupported changelog format version");
+
+ database_ = p.attribute ("database");
+ schema_name_ = p.attribute ("schema-name", "");
+
+ // Because things are stored in the reverse order, first save the
+ // changesets as XML chunks and then re-parse them in the reverse
+ // order. We have to do it this way so that we can do lookups along
+ // the alters edges.
+ //
+ typedef vector<string> changesets;
+ changesets cs;
+
+ for (parser::event_type e (p.peek ());
+ e == parser::start_element;
+ e = p.peek ())
+ {
+ if (p.qname () != xml::qname (xmlns, "changeset"))
+ break; // Not our elements.
+
+ ostringstream os;
+ os.exceptions (ios_base::badbit | ios_base::failbit);
+ serializer s (os, "changeset", 0); // No pretty-printing.
+ size_t depth (0);
+
+ do
+ {
+ switch (p.next ())
+ {
+ case parser::start_element:
+ {
+ s.start_element (p.qname ());
+
+ if (depth == 0)
+ s.namespace_decl (xmlns, "");
+
+ typedef parser::attribute_map_type attr_map;
+ attr_map const& am (p.attribute_map ());
+
+ for (attr_map::const_iterator i (am.begin ());
+ i != am.end (); ++i)
+ s.attribute (i->first, i->second.value);
+
+ depth++;
+ break;
+ }
+ case parser::end_element:
+ {
+ depth--;
+ s.end_element ();
+ break;
+ }
+ case parser::characters:
+ {
+ s.characters (p.value ());
+ break;
+ }
+ default:
+ {
+ depth = 0;
+ break;
+ }
+ }
+ } while (depth != 0);
+
+ cs.push_back (os.str ());
+ }
+
+ // Get the model.
+ //
+ p.next_expect (parser::start_element, xmlns, "model");
+ model_type& m (new_node<model_type> (p, *this));
+ new_edge<contains_model_type> (*this, m);
+ p.next_expect (parser::end_element);
+
+ // Re-parse the changesets in reverse order.
+ //
+ qscope* base (&m);
+ for (changesets::reverse_iterator i (cs.rbegin ()); i != cs.rend (); ++i)
+ {
+ istringstream is (*i);
+ is.exceptions (ios_base::badbit | ios_base::failbit);
+ parser ip (is, p.input_name ());
+
+ ip.next_expect (parser::start_element, xmlns, "changeset");
+
+ changeset& c (new_node<changeset> (ip, *base, *this));
+ new_edge<contains_changeset> (*this, c);
+ base = &c;
+
+ ip.next_expect (parser::end_element);
+ }
+
+ p.next_expect (parser::end_element);
+ }
+
+ void changelog::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "changelog");
+ s.namespace_decl (xmlns, "");
+ s.attribute ("database", database_);
+ if (!schema_name_.empty ())
+ s.attribute ("schema-name", schema_name_);
+ s.attribute ("version", 1); // Format version.
+
+ // For better readability serialize things in reverse order so that
+ // the most recent changeset appears first.
+ //
+ for (contains_changeset_list::const_reverse_iterator i (
+ contains_changeset_.rbegin ());
+ i != contains_changeset_.rend (); ++i)
+ {
+ (*i)->changeset ().serialize (s);
+ s.characters ("\n");
+ }
+
+ model ().serialize (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // contains_model
+ //
+ {
+ type_info ti (typeid (contains_model));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // contains_changeset
+ //
+ {
+ type_info ti (typeid (contains_changeset));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // changelog
+ //
+ {
+ type_info ti (typeid (changelog));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/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/odb/semantics/relational/changeset.cxx b/odb/odb/semantics/relational/changeset.cxx
new file mode 100644
index 0000000..b044a0c
--- /dev/null
+++ b/odb/odb/semantics/relational/changeset.cxx
@@ -0,0 +1,56 @@
+// file : odb/semantics/relational/changeset.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/changeset.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ changeset::
+ changeset (changeset const& c, qscope& b, graph& g)
+ : qscope (c, &b, g),
+ version_ (c.version_),
+ alters_model_ (0)
+ {
+ }
+
+ changeset::
+ changeset (xml::parser& p, qscope& b, graph& g)
+ : qscope (p, &b, g),
+ version_ (p.attribute<version_type> ("version")),
+ alters_model_ (0)
+ {
+ }
+
+ void changeset::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "changeset");
+ s.attribute ("version", version_);
+ qscope::serialize_content (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ {
+ type_info ti (typeid (changeset));
+ ti.add_base (typeid (qscope));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/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/odb/semantics/relational/column.cxx b/odb/odb/semantics/relational/column.cxx
new file mode 100644
index 0000000..9d4d6e5
--- /dev/null
+++ b/odb/odb/semantics/relational/column.cxx
@@ -0,0 +1,201 @@
+// file : odb/semantics/relational/column.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/column.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ // column
+ //
+ column::
+ column (column const& c, uscope&, graph& g)
+ : unameable (c, g),
+ type_ (c.type_),
+ null_ (c.null_),
+ default__ (c.default__),
+ options_ (c.options_)
+ {
+ }
+
+ column::
+ column (xml::parser& p, uscope&, graph& g)
+ : unameable (p, g),
+ type_ (p.attribute ("type", string ())),
+ null_ (p.attribute<bool> ("null")),
+ default__ (p.attribute ("default", string ())),
+ options_ (p.attribute ("options", string ()))
+ {
+ p.content (xml::content::empty);
+ }
+
+ column& column::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<column> (*this, s, g);
+ }
+
+ void column::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "column");
+ serialize_attributes (s);
+ s.end_element ();
+ }
+
+ void column::
+ serialize_attributes (xml::serializer& s) const
+ {
+ unameable::serialize_attributes (s);
+
+ s.attribute ("type", type ());
+ s.attribute ("null", null ()); // Output even if false.
+
+ if (!default_ ().empty ())
+ s.attribute ("default", default_ ());
+
+ if (!options ().empty ())
+ s.attribute ("options", options ());
+ }
+
+ // add_column
+ //
+ add_column& add_column::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<add_column> (*this, s, g);
+ }
+
+ void add_column::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "add-column");
+ column::serialize_attributes (s);
+ s.end_element ();
+ }
+
+ // drop_column
+ //
+ drop_column::
+ drop_column (xml::parser& p, uscope&, graph& g)
+ : unameable (p, g)
+ {
+ p.content (xml::content::empty);
+ }
+
+ drop_column& drop_column::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<drop_column> (*this, s, g);
+ }
+
+ void drop_column::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "drop-column");
+ unameable::serialize_attributes (s);
+ s.end_element ();
+ }
+
+ // alter_column
+ //
+ alter_column::
+ alter_column (alter_column const& ac, uscope& s, graph& g)
+ : column (ac, s, g),
+ alters_ (0),
+ null_altered_ (ac.null_altered_)
+ {
+ column* b (s.lookup<column, drop_column> (ac.name ()));
+ assert (b != 0);
+ g.new_edge<alters> (*this, *b);
+ }
+
+ alter_column::
+ alter_column (xml::parser& p, uscope& s, graph& g)
+ : column (p, s, g),
+ alters_ (0),
+ null_altered_ (p.attribute_present ("null"))
+ {
+ name_type n (p.attribute<name_type> ("name"));
+ column* b (s.lookup<column, drop_column> (n));
+ assert (b != 0);
+ g.new_edge<alters> (*this, *b);
+ }
+
+ alter_column& alter_column::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<alter_column> (*this, s, g);
+ }
+
+ void alter_column::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "alter-column");
+
+ // Here we override the standard column logic.
+ //
+ unameable::serialize_attributes (s);
+
+ if (null_altered_)
+ s.attribute ("null", null_);
+
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ unameable::parser_map& m (unameable::parser_map_);
+
+ m["column"] = &unameable::parser_impl<column>;
+ m["add-column"] = &unameable::parser_impl<add_column>;
+ m["drop-column"] = &unameable::parser_impl<drop_column>;
+ m["alter-column"] = &unameable::parser_impl<alter_column>;
+
+ using compiler::type_info;
+
+ // column
+ //
+ {
+ type_info ti (typeid (column));
+ ti.add_base (typeid (unameable));
+ insert (ti);
+ }
+
+ // add_column
+ //
+ {
+ type_info ti (typeid (add_column));
+ ti.add_base (typeid (column));
+ insert (ti);
+ }
+
+ // drop_column
+ //
+ {
+ type_info ti (typeid (drop_column));
+ ti.add_base (typeid (unameable));
+ insert (ti);
+ }
+
+ // alter_column
+ //
+ {
+ type_info ti (typeid (alter_column));
+ ti.add_base (typeid (column));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/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/odb/semantics/relational/elements.cxx b/odb/odb/semantics/relational/elements.cxx
new file mode 100644
index 0000000..de1878a
--- /dev/null
+++ b/odb/odb/semantics/relational/elements.cxx
@@ -0,0 +1,179 @@
+// file : odb/semantics/relational/elements.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/elements.hxx>
+#include <odb/semantics/relational/column.hxx>
+#include <odb/semantics/relational/primary-key.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ string const xmlns = "http://www.codesynthesis.com/xmlns/odb/changelog";
+
+ // duplicate_name
+ //
+ template <>
+ duplicate_name::
+ duplicate_name (uscope& s, unameable& o, unameable& d)
+ : scope (s), orig (o), dup (d), name (o.name ())
+ {
+ }
+
+ template <>
+ duplicate_name::
+ duplicate_name (qscope& s, qnameable& o, qnameable& d)
+ : scope (s), orig (o), dup (d), name (o.name ().string ())
+ {
+ }
+
+ // scope<uname>
+ //
+ template <>
+ void scope<uname>::
+ add_edge_left (names_type& e)
+ {
+ nameable_type& n (e.nameable ());
+ name_type const& name (e.name ());
+
+ typename names_map::iterator i (names_map_.find (name));
+
+ if (i == names_map_.end ())
+ {
+ typename names_list::iterator i;
+
+ // We want the order to be add/alter columns first, then the
+ // primary key, then other keys, and finnally drop columns.
+ //
+ if (n.is_a<column> () ||
+ n.is_a<add_column> () ||
+ n.is_a<alter_column> ())
+ {
+ i = names_.insert (first_key_, &e);
+ }
+ else if (!n.is_a<drop_column> ())
+ {
+ if (n.is_a<primary_key> ())
+ first_key_ = i = names_.insert (
+ first_key_ != names_.end () ? first_key_ : first_drop_column_,
+ &e);
+ else
+ {
+ i = names_.insert (first_drop_column_, &e);
+
+ if (first_key_ == names_.end ())
+ first_key_ = i;
+ }
+ }
+ else
+ {
+ i = names_.insert (names_.end (), &e);
+
+ if (first_drop_column_ == names_.end ())
+ first_drop_column_ = i;
+ }
+
+ names_map_[name] = i;
+ iterator_map_[&e] = i;
+ }
+ else
+ throw duplicate_name (*this, (*i->second)->nameable (), n);
+ }
+
+ template <>
+ void scope<uname>::
+ remove_edge_left (names_type& e)
+ {
+ typename names_iterator_map::iterator i (iterator_map_.find (&e));
+ assert (i != iterator_map_.end ());
+
+ // If we are removing the first key, then move to the next key (or
+ // the end which means there are no keys).
+ //
+ if (first_key_ == i->second)
+ first_key_++;
+
+ // The same for the first drop column.
+ //
+ if (first_drop_column_ == i->second)
+ first_drop_column_++;
+
+ names_.erase (i->second);
+ names_map_.erase (e.name ());
+ iterator_map_.erase (i);
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // node
+ //
+ insert (type_info (typeid (node)));
+
+ // edge
+ //
+ insert (type_info (typeid (edge)));
+
+ // alters
+ //
+ {
+ type_info ti (typeid (alters));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // names
+ //
+ {
+ type_info ti (typeid (unames));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ {
+ type_info ti (typeid (qnames));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // nameable
+ //
+ {
+ type_info ti (typeid (unameable));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ {
+ type_info ti (typeid (qnameable));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ // scope
+ //
+ {
+ type_info ti (typeid (uscope));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ {
+ type_info ti (typeid (qscope));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/elements.hxx b/odb/odb/semantics/relational/elements.hxx
new file mode 100644
index 0000000..4036942
--- /dev/null
+++ b/odb/odb/semantics/relational/elements.hxx
@@ -0,0 +1,462 @@
+// file : odb/semantics/relational/elements.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_ELEMENTS_HXX
+#define ODB_SEMANTICS_RELATIONAL_ELEMENTS_HXX
+
+#include <map>
+#include <list>
+#include <vector>
+#include <string>
+#include <cassert>
+
+#include <libcutl/container/graph.hxx>
+#include <libcutl/container/pointer-iterator.hxx>
+#include <libcutl/compiler/context.hxx>
+
+#include <libstudxml/parser.hxx>
+#include <libstudxml/serializer.hxx>
+
+#include <odb/semantics/relational/name.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ using namespace cutl;
+
+ using std::string;
+
+ using container::pointer_iterator;
+ using compiler::context;
+
+ typedef unsigned long long version;
+
+ //
+ //
+ extern string const xmlns;
+
+ //
+ //
+ class node;
+ class edge;
+
+ typedef container::graph<node, edge> graph;
+
+ //
+ //
+ class edge: public context
+ {
+ public:
+ template <typename X>
+ bool
+ is_a () const
+ {
+ return dynamic_cast<X const*> (this) != 0;
+ }
+
+ public:
+ virtual
+ ~edge () {}
+ };
+
+ //
+ //
+ class node: public context
+ {
+ // Return name of the node.
+ //
+ public:
+ virtual string
+ kind () const = 0;
+
+ public:
+ template <typename X>
+ bool
+ is_a () const
+ {
+ return dynamic_cast<X const*> (this) != 0;
+ }
+
+ public:
+ virtual
+ ~node () {}
+
+ // XML serialization.
+ //
+ virtual void
+ serialize (xml::serializer&) const = 0;
+
+ // Sink functions that allow extensions in the form of one-way
+ // edges.
+ //
+ void
+ add_edge_right (edge&) {}
+
+ void
+ remove_edge_right (edge&) {}
+ };
+
+ //
+ //
+ class alters: public edge
+ {
+ public:
+ node&
+ base () const {return *base_;}
+
+ node&
+ modifier () const {return *modifier_;}
+
+ public:
+ alters () : base_ (0), modifier_ (0) {}
+
+ void
+ set_left_node (node& m)
+ {
+ assert (modifier_ == 0);
+ modifier_ = &m;
+ }
+
+ void
+ set_right_node (node& b)
+ {
+ assert (base_ == 0);
+ base_ = &b;
+ }
+
+ void
+ clear_left_node (node& m)
+ {
+ assert (modifier_ == &m);
+ modifier_ = 0;
+ }
+
+ void
+ clear_right_node (node& b)
+ {
+ assert (base_ == &b);
+ base_ = 0;
+ }
+
+ protected:
+ node* base_;
+ node* modifier_;
+ };
+
+ //
+ //
+ template <typename N>
+ class scope;
+
+ template <typename N>
+ class nameable;
+
+ //
+ //
+ template <typename N>
+ class names: public edge
+ {
+ public:
+ typedef N name_type;
+ typedef relational::scope<N> scope_type;
+ typedef relational::nameable<N> nameable_type;
+
+ name_type const&
+ name () const
+ {
+ return name_;
+ }
+
+ scope_type&
+ scope () const
+ {
+ return *scope_;
+ }
+
+ nameable_type&
+ nameable () const
+ {
+ return *nameable_;
+ }
+
+ public:
+ names (name_type const& name): name_ (name) {}
+
+ void
+ set_left_node (scope_type& n)
+ {
+ scope_ = &n;
+ }
+
+ void
+ set_right_node (nameable_type& n)
+ {
+ nameable_ = &n;
+ }
+
+ void
+ clear_left_node (scope_type& n)
+ {
+ assert (scope_ == &n);
+ scope_ = 0;
+ }
+
+ void
+ clear_right_node (nameable_type& n)
+ {
+ assert (nameable_ == &n);
+ nameable_ = 0;
+ }
+
+ protected:
+ name_type name_;
+ scope_type* scope_;
+ nameable_type* nameable_;
+ };
+
+ typedef names<uname> unames;
+ typedef names<qname> qnames;
+
+ //
+ //
+ template <typename N>
+ class nameable: public virtual node
+ {
+ public:
+ typedef N name_type;
+ typedef relational::names<N> names_type;
+ typedef relational::scope<N> scope_type;
+
+ name_type const&
+ name () const {return named_->name ();}
+
+ scope_type&
+ scope () const {return named ().scope ();}
+
+ names_type&
+ named () const {return *named_;}
+
+ string const&
+ id () const {return id_;}
+
+ public:
+ // Id identifies the C++ node (e.g., a class or a data member) that
+ // this model node corresponds to. The ids are not necessarily unique
+ // (e.g., there can be a foreign key and an index with the same id that
+ // correspond to a container member). However, in any given scope, the
+ // {id,typeid} must be unique. This becomes important when we try to
+ // find correspondance between nodes during model diff'ing.
+ //
+ nameable (string const& id): id_ (id), named_ (0) {}
+
+ virtual nameable&
+ clone (scope_type&, graph&) const = 0;
+
+ // Virtual because we call it via nameable interface (e.g., in copy).
+ //
+ virtual void
+ add_edge_right (names_type& e)
+ {
+ assert (named_ == 0);
+ named_ = &e;
+ }
+
+ virtual void
+ remove_edge_right (names_type& e)
+ {
+ assert (named_ == &e);
+ named_ = 0;
+ }
+
+ using node::add_edge_right;
+ using node::remove_edge_right;
+
+ protected:
+ nameable (nameable const&, graph& g);
+ nameable (xml::parser&, graph& g);
+
+ void
+ serialize_attributes (xml::serializer&) const;
+
+ public:
+ typedef void (*parser_func) (xml::parser&, scope_type&, graph&);
+ typedef std::map<std::string, parser_func> parser_map;
+ static parser_map parser_map_;
+
+ template <typename T>
+ static void
+ parser_impl (xml::parser&, scope_type&, graph&);
+
+ private:
+ string id_;
+ names_type* named_;
+ };
+
+ typedef nameable<uname> unameable;
+ typedef nameable<qname> qnameable;
+
+
+ //
+ //
+ struct duplicate_name
+ {
+ template <typename N>
+ duplicate_name (relational::scope<N>&,
+ relational::nameable<N>& orig,
+ relational::nameable<N>& dup);
+
+ node& scope;
+ node& orig;
+ node& dup;
+
+ string name;
+ };
+
+ template <typename N>
+ class scope: public virtual node
+ {
+ protected:
+ typedef N name_type;
+ typedef relational::names<N> names_type;
+ typedef relational::nameable<N> nameable_type;
+
+ typedef std::list<names_type*> names_list;
+ typedef std::map<name_type, typename names_list::iterator> names_map;
+ typedef
+ std::map<names_type const*, typename names_list::iterator>
+ names_iterator_map;
+
+ public:
+ typedef pointer_iterator<typename names_list::iterator> names_iterator;
+ typedef
+ pointer_iterator<typename names_list::const_iterator>
+ names_const_iterator;
+
+ public:
+ // Iteration.
+ //
+ names_iterator
+ names_begin ()
+ {
+ return names_.begin ();
+ }
+
+ names_iterator
+ names_end ()
+ {
+ return names_.end ();
+ }
+
+ names_const_iterator
+ names_begin () const
+ {
+ return names_.begin ();
+ }
+
+ names_const_iterator
+ names_end () const
+ {
+ return names_.end ();
+ }
+
+ bool
+ names_empty () const
+ {
+ return names_.empty ();
+ }
+
+ // Find (this scope only).
+ //
+ template <typename T>
+ T*
+ find (name_type const&);
+
+ names_iterator
+ find (name_type const&);
+
+ names_const_iterator
+ find (name_type const&) const;
+
+ names_iterator
+ find (names_type const&);
+
+ names_const_iterator
+ find (names_type const&) const;
+
+ // Lookup in this and all altered scopes until we find what we are
+ // looking for or hit a stop node of type S (e.g., drop_*).
+ //
+ template <typename T, typename S>
+ T*
+ lookup (name_type const&);
+
+ public:
+ scope*
+ base () const
+ {
+ return alters_ != 0 ? &dynamic_cast<scope&> (alters_->base ()) : 0;
+ }
+
+ public:
+ scope ()
+ : first_key_ (names_.end ()),
+ first_drop_column_ (names_.end ()),
+ alters_ (0) {}
+
+ // Virtual because we call it via scope interface (e.g., in copy).
+ //
+ virtual void
+ add_edge_left (alters& a)
+ {
+ assert (alters_ == 0);
+ alters_ = &a;
+ }
+
+ virtual void
+ remove_edge_left (alters& a)
+ {
+ assert (alters_ == &a);
+ alters_ = 0;
+ }
+
+ virtual void
+ add_edge_left (names_type&);
+
+ virtual void
+ remove_edge_left (names_type&);
+
+ protected:
+ scope (scope const&, scope* base, graph&);
+ scope (xml::parser&, scope* base, graph&);
+
+ void
+ serialize_content (xml::serializer&) const;
+
+ protected:
+ names_list names_;
+ names_map names_map_;
+ names_iterator_map iterator_map_;
+
+ typename names_list::iterator first_key_;
+ typename names_list::iterator first_drop_column_;
+
+ alters* alters_;
+ };
+
+ template <>
+ void scope<uname>::
+ add_edge_left (names_type&);
+
+ template <>
+ void scope<uname>::
+ remove_edge_left (names_type&);
+
+ typedef scope<uname> uscope;
+ typedef scope<qname> qscope;
+ }
+}
+
+#include <odb/semantics/relational/elements.txx>
+
+#endif // ODB_SEMANTICS_RELATIONAL_ELEMENTS_HXX
diff --git a/odb/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/odb/semantics/relational/foreign-key.cxx b/odb/odb/semantics/relational/foreign-key.cxx
new file mode 100644
index 0000000..0357d95
--- /dev/null
+++ b/odb/odb/semantics/relational/foreign-key.cxx
@@ -0,0 +1,218 @@
+// file : odb/semantics/relational/foreign-key.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <ostream>
+#include <istream>
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/foreign-key.hxx>
+
+using namespace std;
+
+namespace semantics
+{
+ namespace relational
+ {
+ static const char* action_str[] = {"NO ACTION", "CASCADE", "SET NULL"};
+
+ ostream&
+ operator<< (ostream& os, foreign_key::action_type v)
+ {
+ return os << action_str[v];
+ }
+
+ istream&
+ operator>> (istream& is, foreign_key::action_type& v)
+ {
+ string s;
+ getline (is, s);
+
+ if (!is.eof ())
+ is.setstate (istream::failbit);
+
+ if (!is.fail ())
+ {
+ if (s == "NO ACTION")
+ v = foreign_key::no_action;
+ else if (s == "CASCADE")
+ v = foreign_key::cascade;
+ else if (s == "SET NULL")
+ v = foreign_key::set_null;
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+ }
+
+ foreign_key::
+ foreign_key (foreign_key const& k, uscope& s, graph& g)
+ : key (k, s, g),
+ referenced_table_ (k.referenced_table_),
+ referenced_columns_ (k.referenced_columns_),
+ deferrable_ (k.deferrable_),
+ on_delete_ (k.on_delete_)
+ {
+ }
+
+ foreign_key::
+ foreign_key (xml::parser& p, uscope& s, graph& g)
+ : key (p, s, g),
+ deferrable_ (p.attribute ("deferrable", deferrable_type ())),
+ on_delete_ (p.attribute ("on-delete", no_action))
+ {
+ using namespace xml;
+
+ p.next_expect (parser::start_element, xmlns, "references");
+ referenced_table_ = p.attribute<qname> ("table");
+ p.content (content::complex);
+
+ for (parser::event_type e (p.peek ());
+ e == parser::start_element;
+ e = p.peek ())
+ {
+ if (p.qname () != xml::qname (xmlns, "column"))
+ break; // Not our elements.
+
+ p.next ();
+ referenced_columns_.push_back (p.attribute<uname> ("name"));
+ p.content (content::empty);
+ p.next_expect (parser::end_element);
+ }
+
+ p.next_expect (parser::end_element);
+ }
+
+ foreign_key& foreign_key::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<foreign_key> (*this, s, g);
+ }
+
+ void foreign_key::
+ serialize_attributes (xml::serializer& s) const
+ {
+ key::serialize_attributes (s);
+
+ if (deferrable () != deferrable_type::not_deferrable)
+ s.attribute ("deferrable", deferrable ());
+
+ if (on_delete () != no_action)
+ s.attribute ("on-delete", on_delete ());
+ }
+
+ void foreign_key::
+ serialize_content (xml::serializer& s) const
+ {
+ key::serialize_content (s);
+
+ // Referenced columns.
+ //
+ s.start_element (xmlns, "references");
+ s.attribute ("table", referenced_table ());
+
+ for (columns::const_iterator i (referenced_columns_.begin ());
+ i != referenced_columns_.end (); ++i)
+ {
+ s.start_element (xmlns, "column");
+ s.attribute ("name", *i);
+ s.end_element ();
+ }
+
+ s.end_element (); // references
+ }
+
+ void foreign_key::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "foreign-key");
+ serialize_attributes (s);
+ serialize_content (s);
+ s.end_element (); // foreign-key
+ }
+
+ // add_foreign_key
+ //
+ add_foreign_key& add_foreign_key::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<add_foreign_key> (*this, s, g);
+ }
+
+ void add_foreign_key::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "add-foreign-key");
+ foreign_key::serialize_attributes (s);
+ foreign_key::serialize_content (s);
+ s.end_element ();
+ }
+
+ // drop_foreign_key
+ //
+ drop_foreign_key::
+ drop_foreign_key (xml::parser& p, uscope&, graph& g)
+ : unameable (p, g)
+ {
+ p.content (xml::content::empty);
+ }
+
+ drop_foreign_key& drop_foreign_key::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<drop_foreign_key> (*this, s, g);
+ }
+
+ void drop_foreign_key::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "drop-foreign-key");
+ unameable::serialize_attributes (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ unameable::parser_map& m (unameable::parser_map_);
+
+ m["foreign-key"] = &unameable::parser_impl<foreign_key>;
+ m["add-foreign-key"] = &unameable::parser_impl<add_foreign_key>;
+ m["drop-foreign-key"] = &unameable::parser_impl<drop_foreign_key>;
+
+ using compiler::type_info;
+
+ // foreign_key
+ //
+ {
+ type_info ti (typeid (foreign_key));
+ ti.add_base (typeid (key));
+ insert (ti);
+ }
+
+ // add_foreign_key
+ //
+ {
+ type_info ti (typeid (add_foreign_key));
+ ti.add_base (typeid (foreign_key));
+ insert (ti);
+ }
+
+ // drop_foreign_key
+ //
+ {
+ type_info ti (typeid (drop_foreign_key));
+ ti.add_base (typeid (unameable));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/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/odb/semantics/relational/index.cxx b/odb/odb/semantics/relational/index.cxx
new file mode 100644
index 0000000..2329f3a
--- /dev/null
+++ b/odb/odb/semantics/relational/index.cxx
@@ -0,0 +1,145 @@
+// file : odb/semantics/relational/index.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/index.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ // index
+ //
+ index::
+ index (index const& i, uscope& s, graph& g)
+ : key (i, s, g),
+ type_ (i.type_),
+ method_ (i.method_),
+ options_ (i.options_)
+ {
+ }
+
+ index::
+ index (xml::parser& p, uscope& s, graph& g)
+ : key (p, s, g),
+ type_ (p.attribute ("type", string ())),
+ method_ (p.attribute ("method", string ())),
+ options_ (p.attribute ("options", string ()))
+ {
+ }
+
+ index& index::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<index> (*this, s, g);
+ }
+
+ void index::
+ serialize_attributes (xml::serializer& s) const
+ {
+ key::serialize_attributes (s);
+
+ if (!type ().empty ())
+ s.attribute ("type", type ());
+
+ if (!method ().empty ())
+ s.attribute ("method", method ());
+
+ if (!options ().empty ())
+ s.attribute ("options", options ());
+ }
+
+ void index::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "index");
+ serialize_attributes (s);
+ key::serialize_content (s);
+ s.end_element ();
+ }
+
+ // add_index
+ //
+ add_index& add_index::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<add_index> (*this, s, g);
+ }
+
+ void add_index::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "add-index");
+ index::serialize_attributes (s);
+ index::serialize_content (s);
+ s.end_element ();
+ }
+
+ // drop_index
+ //
+ drop_index::
+ drop_index (xml::parser& p, uscope&, graph& g)
+ : unameable (p, g)
+ {
+ p.content (xml::content::empty);
+ }
+
+ drop_index& drop_index::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<drop_index> (*this, s, g);
+ }
+
+ void drop_index::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "drop-index");
+ unameable::serialize_attributes (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ unameable::parser_map& m (unameable::parser_map_);
+
+ m["index"] = &unameable::parser_impl<index>;
+ m["add-index"] = &unameable::parser_impl<add_index>;
+ m["drop-index"] = &unameable::parser_impl<drop_index>;
+
+ using compiler::type_info;
+
+ // index
+ //
+ {
+ type_info ti (typeid (index));
+ ti.add_base (typeid (key));
+ insert (ti);
+ }
+
+ // add_index
+ //
+ {
+ type_info ti (typeid (add_index));
+ ti.add_base (typeid (index));
+ insert (ti);
+ }
+
+ // drop_index
+ //
+ {
+ type_info ti (typeid (drop_index));
+ ti.add_base (typeid (unameable));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/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/odb/semantics/relational/key.cxx b/odb/odb/semantics/relational/key.cxx
new file mode 100644
index 0000000..3511618
--- /dev/null
+++ b/odb/odb/semantics/relational/key.cxx
@@ -0,0 +1,97 @@
+// file : odb/semantics/relational/key.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/key.hxx>
+#include <odb/semantics/relational/column.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ key::
+ key (key const& k, uscope& s, graph& g)
+ : unameable (k, g)
+ {
+ for (contains_iterator i (k.contains_begin ());
+ i != k.contains_end (); ++i)
+ {
+ column* c (s.lookup<column, drop_column> (i->column ().name ()));
+ assert (c != 0);
+ g.new_edge<contains> (*this, *c, i->options ());
+ }
+ }
+
+ key::
+ key (xml::parser& p, uscope& s, graph& g)
+ : unameable (p, g)
+ {
+ using namespace xml;
+ p.content (content::complex);
+
+ for (parser::event_type e (p.peek ());
+ e == parser::start_element;
+ e = p.peek ())
+ {
+ if (p.qname () != xml::qname (xmlns, "column"))
+ break; // Not our elements.
+
+ p.next ();
+ p.content (content::empty);
+
+ uname n (p.attribute<uname> ("name"));
+ column* c (s.lookup<column, drop_column> (n));
+ if (c == 0)
+ throw parsing (p, "invalid column name in the 'name' attribute");
+
+ string o (p.attribute ("options", string ()));
+ g.new_edge<contains> (*this, *c, o);
+
+ p.next_expect (parser::end_element);
+ }
+ }
+
+ void key::
+ serialize_content (xml::serializer& s) const
+ {
+ for (contains_iterator i (contains_begin ()); i != contains_end (); ++i)
+ {
+ s.start_element (xmlns, "column");
+ s.attribute ("name", i->column ().name ());
+ if (!i->options ().empty ())
+ s.attribute ("options", i->options ());
+ s.end_element ();
+ }
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // contains
+ //
+ {
+ type_info ti (typeid (contains));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // key
+ //
+ {
+ type_info ti (typeid (key));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/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/odb/semantics/relational/model.cxx b/odb/odb/semantics/relational/model.cxx
new file mode 100644
index 0000000..8763045
--- /dev/null
+++ b/odb/odb/semantics/relational/model.cxx
@@ -0,0 +1,54 @@
+// file : odb/semantics/relational/model.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/model.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ model::
+ model (model const& m, graph& g)
+ : qscope (m, 0, g),
+ version_ (m.version_)
+ {
+ }
+
+ model::
+ model (xml::parser& p, graph& g)
+ : qscope (p, 0, g),
+ version_ (p.attribute<version_type> ("version"))
+ {
+ }
+
+ void model::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "model");
+ s.attribute ("version", version_);
+ qscope::serialize_content (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ {
+ type_info ti (typeid (model));
+ ti.add_base (typeid (qscope));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/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/odb/semantics/relational/primary-key.cxx b/odb/odb/semantics/relational/primary-key.cxx
new file mode 100644
index 0000000..235340f
--- /dev/null
+++ b/odb/odb/semantics/relational/primary-key.cxx
@@ -0,0 +1,80 @@
+// file : odb/semantics/relational/primary-key.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/primary-key.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ primary_key::
+ primary_key (primary_key const& k, uscope& s, graph& g)
+ : key (k, s, g), auto__ (k.auto__), extra_map_ (k.extra_map_)
+ {
+ }
+
+ primary_key::
+ primary_key (xml::parser& p, uscope& s, graph& g)
+ : key (p, s, g),
+ auto__ (p.attribute ("auto", false))
+ {
+ // All unhandled attributes go into the extra map.
+ //
+ typedef xml::parser::attribute_map_type attr_map;
+ attr_map const& am (p.attribute_map ());
+
+ for (attr_map::const_iterator i (am.begin ()); i != am.end (); ++i)
+ {
+ if (!i->second.handled)
+ extra_map_[i->first.name ()] = i->second.value;
+ }
+ }
+
+ primary_key& primary_key::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<primary_key> (*this, s, g);
+ }
+
+ void primary_key::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "primary-key");
+ key::serialize_attributes (s);
+
+ if (auto_ ())
+ s.attribute ("auto", true);
+
+ for (extra_map::const_iterator i (extra_map_.begin ());
+ i != extra_map_.end (); ++i)
+ s.attribute (i->first, i->second);
+
+ key::serialize_content (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ unameable::parser_map_["primary-key"] =
+ &unameable::parser_impl<primary_key>;
+
+ using compiler::type_info;
+
+ {
+ type_info ti (typeid (primary_key));
+ ti.add_base (typeid (key));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/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/odb/semantics/relational/table.cxx b/odb/odb/semantics/relational/table.cxx
new file mode 100644
index 0000000..3bf763d
--- /dev/null
+++ b/odb/odb/semantics/relational/table.cxx
@@ -0,0 +1,183 @@
+// file : odb/semantics/relational/table.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/table.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ // table
+ //
+ table::
+ table (table const& t, qscope& s, graph& g, bool b)
+ : qnameable (t, g),
+ uscope (t, (b ? s.lookup<table, drop_table> (t.name ()) : 0), g),
+ options_ (t.options_),
+ extra_map_ (t.extra_map_)
+ {
+ }
+
+ table::
+ table (xml::parser& p, qscope& s, graph& g, bool b)
+ : qnameable (p, g),
+ uscope (
+ p,
+ (b ? s.lookup<table, drop_table> (
+ p.attribute<qnameable::name_type> ("name")) : 0),
+ g),
+ options_ (p.attribute ("options", string ()))
+ {
+ // All unhandled attributes go into the extra map.
+ //
+ typedef xml::parser::attribute_map_type attr_map;
+ attr_map const& am (p.attribute_map ());
+
+ for (attr_map::const_iterator i (am.begin ()); i != am.end (); ++i)
+ {
+ if (!i->second.handled)
+ extra_map_[i->first.name ()] = i->second.value;
+ }
+ }
+
+ table& table::
+ clone (qscope& s, graph& g) const
+ {
+ return g.new_node<table> (*this, s, g);
+ }
+
+ void table::
+ serialize_attributes (xml::serializer& s) const
+ {
+ qnameable::serialize_attributes (s);
+
+ if (!options_.empty ())
+ s.attribute ("options", options_);
+
+ for (extra_map::const_iterator i (extra_map_.begin ());
+ i != extra_map_.end (); ++i)
+ s.attribute (i->first, i->second);
+ }
+
+ void table::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "table");
+ serialize_attributes (s);
+ uscope::serialize_content (s);
+ s.end_element ();
+ }
+
+ // add_table
+ //
+ add_table& add_table::
+ clone (qscope& s, graph& g) const
+ {
+ return g.new_node<add_table> (*this, s, g);
+ }
+
+ void add_table::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "add-table");
+ table::serialize_attributes (s);
+ table::serialize_content (s);
+ s.end_element ();
+ }
+
+ // drop_table
+ //
+ drop_table::
+ drop_table (xml::parser& p, qscope&, graph& g)
+ : qnameable (p, g)
+ {
+ p.content (xml::content::empty);
+ }
+
+ drop_table& drop_table::
+ clone (qscope& s, graph& g) const
+ {
+ return g.new_node<drop_table> (*this, s, g);
+ }
+
+ void drop_table::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "drop-table");
+ qnameable::serialize_attributes (s);
+ s.end_element ();
+ }
+
+ // alter_table
+ //
+ alter_table& alter_table::
+ clone (qscope& s, graph& g) const
+ {
+ return g.new_node<alter_table> (*this, s, g);
+ }
+
+ void alter_table::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "alter-table");
+ table::serialize_attributes (s);
+ table::serialize_content (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ qnameable::parser_map& m (qnameable::parser_map_);
+
+ m["table"] = &qnameable::parser_impl<table>;
+ m["add-table"] = &qnameable::parser_impl<add_table>;
+ m["drop-table"] = &qnameable::parser_impl<drop_table>;
+ m["alter-table"] = &qnameable::parser_impl<alter_table>;
+
+ using compiler::type_info;
+
+ // table
+ //
+ {
+ type_info ti (typeid (table));
+ ti.add_base (typeid (qnameable));
+ ti.add_base (typeid (uscope));
+ insert (ti);
+ }
+
+ // add_table
+ //
+ {
+ type_info ti (typeid (add_table));
+ ti.add_base (typeid (table));
+ insert (ti);
+ }
+
+ // drop_table
+ //
+ {
+ type_info ti (typeid (drop_table));
+ ti.add_base (typeid (qnameable));
+ insert (ti);
+ }
+
+ // alter_table
+ //
+ {
+ type_info ti (typeid (alter_table));
+ ti.add_base (typeid (table));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/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/odb/semantics/template.cxx b/odb/odb/semantics/template.cxx
new file mode 100644
index 0000000..f492be0
--- /dev/null
+++ b/odb/odb/semantics/template.cxx
@@ -0,0 +1,88 @@
+// file : odb/semantics/template.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/template.hxx>
+
+namespace semantics
+{
+ template_::
+ template_ ()
+ {
+ }
+
+ instantiates::
+ instantiates ()
+ {
+ }
+
+ instantiation::
+ instantiation ()
+ {
+ }
+
+ type_template::
+ type_template ()
+ {
+ }
+
+ type_instantiation::
+ type_instantiation ()
+ {
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // template_
+ //
+ {
+ type_info ti (typeid (template_));
+ ti.add_base (typeid (nameable));
+ insert (ti);
+ }
+
+ // instantiates
+ //
+ {
+ type_info ti (typeid (instantiates));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // instantiation
+ //
+ {
+ type_info ti (typeid (instantiation));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ // type_template
+ //
+ {
+ type_info ti (typeid (type_template));
+ ti.add_base (typeid (template_));
+ insert (ti);
+ }
+
+ // type_instantiation
+ //
+ {
+ type_info ti (typeid (type_instantiation));
+ ti.add_base (typeid (type));
+ ti.add_base (typeid (instantiation));
+ insert (ti);
+ }
+
+ }
+ } init_;
+ }
+}
diff --git a/odb/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/odb/semantics/union-template.cxx b/odb/odb/semantics/union-template.cxx
new file mode 100644
index 0000000..21fc9c0
--- /dev/null
+++ b/odb/odb/semantics/union-template.cxx
@@ -0,0 +1,54 @@
+// file : odb/semantics/union-template.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/union-template.hxx>
+
+namespace semantics
+{
+ union_template::
+ union_template (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ union_instantiation::
+ union_instantiation (path const& file,
+ size_t line,
+ size_t column,
+ tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // union_template
+ //
+ {
+ type_info ti (typeid (union_template));
+ ti.add_base (typeid (type_template));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+
+ // union_instantiation
+ //
+ {
+ type_info ti (typeid (union_instantiation));
+ ti.add_base (typeid (union_));
+ ti.add_base (typeid (type_instantiation));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/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/odb/semantics/union.cxx b/odb/odb/semantics/union.cxx
new file mode 100644
index 0000000..007ef57
--- /dev/null
+++ b/odb/odb/semantics/union.cxx
@@ -0,0 +1,36 @@
+// file : odb/semantics/union.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/union.hxx>
+
+namespace semantics
+{
+ union_::
+ union_ (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // union_
+ //
+ {
+ type_info ti (typeid (union_));
+ ti.add_base (typeid (type));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/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/odb/semantics/unit.cxx b/odb/odb/semantics/unit.cxx
new file mode 100644
index 0000000..4f92aed
--- /dev/null
+++ b/odb/odb/semantics/unit.cxx
@@ -0,0 +1,42 @@
+// file : odb/semantics/unit.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/unit.hxx>
+
+namespace semantics
+{
+ unit::
+ unit (path const& file)
+ : node (file, 1, 1, global_namespace), graph_ (*this)
+ {
+ // Use a special edge to get this->name() return the global
+ // namespace name ("").
+ //
+ new_edge<global_names> (*this, *this);
+ node::unit (*this);
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // unit
+ //
+ {
+ type_info ti (typeid (unit));
+ ti.add_base (typeid (namespace_));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/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/odb/traversal/elements.hxx b/odb/odb/traversal/elements.hxx
new file mode 100644
index 0000000..d67a6d8
--- /dev/null
+++ b/odb/odb/traversal/elements.hxx
@@ -0,0 +1,238 @@
+// file : odb/traversal/elements.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_ELEMENTS_HXX
+#define ODB_TRAVERSAL_ELEMENTS_HXX
+
+#include <libcutl/compiler/traversal.hxx>
+#include <odb/semantics/elements.hxx>
+
+namespace traversal
+{
+ using namespace cutl;
+
+ //
+ //
+ typedef compiler::dispatcher<semantics::node> node_dispatcher;
+ typedef compiler::dispatcher<semantics::edge> edge_dispatcher;
+
+ //
+ //
+ struct node_base: node_dispatcher, edge_dispatcher
+ {
+ void
+ edge_traverser (edge_dispatcher& d)
+ {
+ edge_dispatcher::traverser (d);
+ }
+
+ edge_dispatcher&
+ edge_traverser ()
+ {
+ return *this;
+ }
+
+ using node_dispatcher::dispatch;
+ using edge_dispatcher::dispatch;
+
+ using edge_dispatcher::iterate_and_dispatch;
+ };
+
+ struct edge_base: edge_dispatcher, node_dispatcher
+ {
+ void
+ node_traverser (node_dispatcher& d)
+ {
+ node_dispatcher::traverser (d);
+ }
+
+ node_dispatcher&
+ node_traverser ()
+ {
+ return *this;
+ }
+
+ using edge_dispatcher::dispatch;
+ using node_dispatcher::dispatch;
+
+ using node_dispatcher::iterate_and_dispatch;
+ };
+
+ inline edge_base&
+ operator>> (node_base& n, edge_base& e)
+ {
+ n.edge_traverser (e);
+ return e;
+ }
+
+ inline node_base&
+ operator>> (edge_base& e, node_base& n)
+ {
+ e.node_traverser (n);
+ return n;
+ }
+
+ //
+ //
+ template <typename X>
+ struct node: compiler::traverser_impl<X, semantics::node>,
+ virtual node_base
+ {
+ };
+
+ template <typename X>
+ struct edge: compiler::traverser_impl<X, semantics::edge>,
+ virtual edge_base
+ {
+ };
+
+ //
+ // Edges
+ //
+
+ struct names: edge<semantics::names>
+ {
+ names ()
+ {
+ }
+
+ names (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct declares: edge<semantics::declares>
+ {
+ declares ()
+ {
+ }
+
+ declares (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct defines: edge<semantics::defines>
+ {
+ defines ()
+ {
+ }
+
+ defines (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct typedefs: edge<semantics::typedefs>
+ {
+ typedefs ()
+ {
+ }
+
+ typedefs (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct belongs: edge<semantics::belongs>
+ {
+ belongs ()
+ {
+ }
+
+ belongs (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ //
+ // Nodes
+ //
+
+ struct nameable: node<semantics::nameable> {};
+
+ //
+ //
+ template <typename T>
+ struct scope_template: node<T>
+ {
+ public:
+ virtual void
+ traverse (T& s)
+ {
+ names (s);
+ }
+
+ virtual void
+ names (T& s)
+ {
+ names (s, *this);
+ }
+
+ virtual void
+ names (T& s, edge_dispatcher& d)
+ {
+ this->iterate_and_dispatch (s.names_begin (), s.names_end (), d);
+ }
+ };
+
+ struct scope: scope_template<semantics::scope> {};
+
+ //
+ //
+ struct type: node<semantics::type> {};
+
+ //
+ //
+ struct instance: node<semantics::instance>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ belongs (type&);
+
+ virtual void
+ belongs (type&, edge_dispatcher&);
+ };
+
+ //
+ //
+ struct data_member: node<semantics::data_member>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ belongs (type&);
+
+ virtual void
+ belongs (type&, edge_dispatcher&);
+ };
+
+ //
+ //
+ struct unsupported_type: node<semantics::unsupported_type> {};
+}
+
+#endif // ODB_TRAVERSAL_ELEMENTS_HXX
diff --git a/odb/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/odb/traversal/relational/elements.hxx b/odb/odb/traversal/relational/elements.hxx
new file mode 100644
index 0000000..2b43ab0
--- /dev/null
+++ b/odb/odb/traversal/relational/elements.hxx
@@ -0,0 +1,162 @@
+// file : odb/traversal/relational/elements.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_ELEMENTS_HXX
+#define ODB_TRAVERSAL_RELATIONAL_ELEMENTS_HXX
+
+#include <libcutl/compiler/traversal.hxx>
+#include <odb/semantics/relational/elements.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ using namespace cutl;
+
+ //
+ //
+ typedef compiler::dispatcher<semantics::relational::node> node_dispatcher;
+ typedef compiler::dispatcher<semantics::relational::edge> edge_dispatcher;
+
+ //
+ //
+ struct node_base: node_dispatcher, edge_dispatcher
+ {
+ void
+ edge_traverser (edge_dispatcher& d)
+ {
+ edge_dispatcher::traverser (d);
+ }
+
+ edge_dispatcher&
+ edge_traverser ()
+ {
+ return *this;
+ }
+
+ using node_dispatcher::dispatch;
+ using edge_dispatcher::dispatch;
+
+ using edge_dispatcher::iterate_and_dispatch;
+ };
+
+ struct edge_base: edge_dispatcher, node_dispatcher
+ {
+ void
+ node_traverser (node_dispatcher& d)
+ {
+ node_dispatcher::traverser (d);
+ }
+
+ node_dispatcher&
+ node_traverser ()
+ {
+ return *this;
+ }
+
+ using edge_dispatcher::dispatch;
+ using node_dispatcher::dispatch;
+
+ using node_dispatcher::iterate_and_dispatch;
+ };
+
+ inline edge_base&
+ operator>> (node_base& n, edge_base& e)
+ {
+ n.edge_traverser (e);
+ return e;
+ }
+
+ inline node_base&
+ operator>> (edge_base& e, node_base& n)
+ {
+ e.node_traverser (n);
+ return n;
+ }
+
+ //
+ //
+ template <typename X>
+ struct node: compiler::traverser_impl<X, semantics::relational::node>,
+ virtual node_base
+ {
+ virtual void
+ traverse (X&) {}
+ };
+
+ template <typename X>
+ struct edge: compiler::traverser_impl<X, semantics::relational::edge>,
+ virtual edge_base
+ {
+ };
+
+ //
+ // Edges
+ //
+
+ template <typename N>
+ struct names: edge<semantics::relational::names<N> >
+ {
+ names ()
+ {
+ }
+
+ names (node_dispatcher& n)
+ {
+ this->node_traverser (n);
+ }
+
+ virtual void
+ traverse (semantics::relational::names<N>& e)
+ {
+ this->dispatch (e.nameable ());
+ }
+ };
+
+ typedef names<semantics::relational::uname> unames;
+ typedef names<semantics::relational::qname> qnames;
+
+ //
+ // Nodes
+ //
+
+ template <typename N>
+ struct nameable: node<semantics::relational::nameable<N> > {};
+
+ typedef nameable<semantics::relational::uname> unameable;
+ typedef nameable<semantics::relational::qname> qnameable;
+
+ //
+ //
+ template <typename T>
+ struct scope_template: node<T>
+ {
+ public:
+ virtual void
+ traverse (T& s)
+ {
+ names (s);
+ }
+
+ virtual void
+ names (T& s)
+ {
+ names (s, *this);
+ }
+
+ virtual void
+ names (T& s, edge_dispatcher& d)
+ {
+ this->iterate_and_dispatch (s.names_begin (), s.names_end (), d);
+ }
+ };
+
+ template <typename N>
+ struct scope: scope_template<semantics::relational::scope<N> > {};
+
+ typedef scope<semantics::relational::uname> uscope;
+ typedef scope<semantics::relational::qname> qscope;
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_ELEMENTS_HXX
diff --git a/odb/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/odb/validator.cxx b/odb/odb/validator.cxx
new file mode 100644
index 0000000..aa45294
--- /dev/null
+++ b/odb/odb/validator.cxx
@@ -0,0 +1,1917 @@
+// file : odb/validator.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <set>
+#include <iostream>
+
+#include <odb/traversal.hxx>
+#include <odb/common.hxx>
+#include <odb/context.hxx>
+#include <odb/diagnostics.hxx>
+#include <odb/validator.hxx>
+#include <odb/cxx-lexer.hxx>
+
+#include <odb/relational/validator.hxx>
+
+using namespace std;
+
+namespace
+{
+ // Resolve null overrides.
+ //
+ static void
+ override_null (semantics::node& n, string const& prefix = "")
+ {
+ string p (prefix.empty () ? prefix : prefix + '-');
+
+ if (n.count (p + "null") && n.count (p + "not-null"))
+ {
+ if (n.get<location_t> (p + "null-location") <
+ n.get<location_t> (p + "not-null-location"))
+ {
+ n.remove (p + "null");
+ n.remove (p + "null-location");
+ }
+ else
+ {
+ n.remove (p + "not-null");
+ n.remove (p + "not-null-location");
+ }
+ }
+ }
+
+ //
+ // Pass 1.
+ //
+
+ struct data_member1: traversal::data_member, context
+ {
+ data_member1 (bool& valid)
+ : valid_ (valid)
+ {
+ }
+
+ virtual void
+ traverse (type& m)
+ {
+ semantics::class_& c (dynamic_cast<semantics::class_&> (m.scope ()));
+ bool obj (object (c));
+
+ // If the class is marked transient, then mark each non-virtual
+ // data member as transient.
+ //
+ {
+ bool t (transient (m));
+
+ if (!t && c.count ("transient") && !m.count ("virtual"))
+ {
+ m.set ("transient", true);
+ t = true;
+ }
+
+ if (t)
+ return;
+ }
+
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ if (t.fq_anonymous (hint))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: unnamed type in data member declaration" << endl;
+
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " info: use 'typedef' to name this type" << endl;
+
+ valid_ = false;
+ }
+
+ // Make sure id or inverse member is not marked readonly since we
+ // depend on these three sets not having overlaps.
+ //
+ if (m.count ("readonly")) // context::readonly() also checks the class.
+ {
+ if (id (m))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: object id should not be declared readonly" << endl;
+
+ valid_ = false;
+ }
+
+ if (inverse (m))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: inverse object pointer should not be declared "
+ << "readonly" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ // Make sure a member of a section is an immediate member of an object.
+ // The same for the section member itself.
+ //
+ bool section (t.fq_name () == "::odb::section");
+
+ if (!obj)
+ {
+ if (m.count ("section-member"))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": " <<
+ "error: data member belonging to a section can only be a " <<
+ "direct member of a persistent class" << endl;
+ valid_ = false;
+ }
+
+ if (section)
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": " <<
+ "error: section data member can only be a direct member of a " <<
+ "persistent class" << endl;
+ valid_ = false;
+ }
+ }
+
+ // Make sure the load and update pragmas are only specified on
+ // section members.
+ //
+ if (!section)
+ {
+ if (m.count ("section-load"))
+ {
+ location_t loc (m.get<location_t> ("section-load-location"));
+ error (loc) << "'#pragma db load' can only be specified for "
+ "a section data member" << endl;
+ valid_ = false;
+ }
+
+ if (m.count ("section-update"))
+ {
+ location_t loc (m.get<location_t> ("section-update-location"));
+ error (loc) << "'#pragma db update' can only be specified for "
+ "a section data member" << endl;
+ valid_ = false;
+ }
+ }
+
+ // Check that the addition version makes sense.
+ //
+ unsigned long long av (m.get<unsigned long long> ("added", 0));
+ if (av != 0)
+ {
+ location_t l (m.get<location_t> ("added-location"));
+
+ if (id (m))
+ {
+ error (l) << "object id cannod be soft-added" << endl;
+ valid_ = false;
+ }
+
+ if (version (m))
+ {
+ error (l) << "optimistic concurrency version cannod be "
+ "soft-added" << endl;
+ valid_ = false;
+ }
+
+ if (!versioned ())
+ {
+ error (l) << "added data member in a non-versioned object " <<
+ "model" << endl;
+ valid_ = false;
+ }
+ else
+ {
+ model_version const& mv (version ());
+
+ if (av > mv.current)
+ {
+ error (l) << "addition version is greater than the current " <<
+ "model version" << endl;
+ valid_ = false;
+ }
+ else if (av <= mv.base)
+ {
+ error (l) << "addition version is less than or equal to the " <<
+ "base model version" << endl;
+ info (l) << "delete this pragma since migration to version " <<
+ av << " is no longer possible" << endl;
+ valid_ = false;
+ }
+ }
+ }
+
+ // Check that the deletion version makes sense.
+ //
+ unsigned long long dv (m.get<unsigned long long> ("deleted", 0));
+ if (dv != 0)
+ {
+ location_t l (m.get<location_t> ("deleted-location"));
+
+ if (id (m))
+ {
+ error (l) << "object id cannod be soft-deleted" << endl;
+ valid_ = false;
+ }
+
+ if (version (m))
+ {
+ error (l) << "optimistic concurrency version cannod be "
+ "soft-deleted" << endl;
+ valid_ = false;
+ }
+
+ if (!versioned ())
+ {
+ error (l) << "deleted data member in a non-versioned object " <<
+ "model" << endl;
+ valid_ = false;
+ }
+ else
+ {
+ model_version const& mv (version ());
+
+ if (dv > mv.current)
+ {
+ error (l) << "deletion version is greater than the current " <<
+ "model version" << endl;
+ valid_ = false;
+ }
+ else if (dv <= mv.base)
+ {
+ error (l) << "deletion version is less than or equal to the " <<
+ "base model version" << endl;
+ info (c.location ()) << "delete this data member since " <<
+ "migration to version " << dv << " is no longer possible" <<
+ endl;
+ valid_ = false;
+ }
+ }
+ }
+
+ // Make sure that addition and deletion versions are properly ordered.
+ // We can have both the [av, dv] (added then deleted) and [dv, av]
+ // (deleted then re-added) intervals.
+ //
+ if (av != 0 && dv != 0 && av == dv)
+ {
+ location_t al (m.get<location_t> ("added-location"));
+ location_t dl (m.get<location_t> ("deleted-location"));
+
+ error (al) << "addition and deletion versions are the same" << endl;
+ info (dl) << "deletion version is specified here" << endl;
+ valid_ = false;
+ }
+
+ if (section)
+ return; // Section data member is transient.
+
+ // Resolve null overrides.
+ //
+ override_null (m);
+ override_null (m, "value");
+ }
+
+ bool& valid_;
+ };
+
+ // Find special members (id, version).
+ //
+ struct special_members: traversal::class_, context
+ {
+ special_members (class_kind_type kind,
+ bool& valid,
+ semantics::data_member*& id,
+ semantics::data_member*& optimistic)
+ : kind_ (kind), member_ (valid, id, optimistic)
+ {
+ if (kind != class_view)
+ *this >> inherits_ >> *this;
+
+ *this >> names_ >> member_;
+ }
+
+ virtual void
+ traverse (semantics::class_& c)
+ {
+ // Skip transient bases.
+ //
+ switch (kind_)
+ {
+ case class_object:
+ {
+ if (!object (c))
+ return;
+ break;
+ }
+ case class_view:
+ {
+ break;
+ }
+ case class_composite:
+ {
+ if (!composite (c))
+ return;
+ break;
+ }
+ case class_other:
+ {
+ assert (false);
+ break;
+ }
+ }
+
+ // Views don't have bases.
+ //
+ if (kind_ != class_view)
+ inherits (c);
+
+ names (c);
+ }
+
+ private:
+ struct member: traversal::data_member, context
+ {
+ member (bool& valid,
+ semantics::data_member*& id,
+ semantics::data_member*& optimistic)
+ : valid_ (valid), id_ (id), optimistic_ (optimistic)
+ {
+ }
+
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ if (m.count ("id"))
+ {
+ if (id_ == 0)
+ id_ = &m;
+ else
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: multiple object id members" << endl;
+
+ os << id_->file () << ":" << id_->line () << ":" << id_->column ()
+ << ": info: previous id member is declared here" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ if (version (m))
+ {
+ if (optimistic_ == 0)
+ optimistic_ = &m;
+ else
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: multiple version members" << endl;
+
+ semantics::data_member& o (*optimistic_);
+
+ os << o.file () << ":" << o.line () << ":" << o.column ()
+ << ": info: previous version member is declared here" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+
+ bool& valid_;
+ semantics::data_member*& id_;
+ semantics::data_member*& optimistic_;
+ };
+
+ class_kind_type kind_;
+ member member_;
+ traversal::names names_;
+ traversal::inherits inherits_;
+ };
+
+ //
+ //
+ struct value_type: traversal::type, context
+ {
+ virtual void
+ traverse (semantics::type& t)
+ {
+ // Resolve null overrides.
+ //
+ override_null (t);
+ override_null (t, "value");
+ }
+ };
+
+ struct typedefs1: typedefs
+ {
+ typedefs1 (traversal::declares& d)
+ : typedefs (true), declares_ (d)
+ {
+ }
+
+ void
+ traverse (semantics::typedefs& t)
+ {
+ if (check (t))
+ traversal::typedefs::traverse (t);
+ else
+ declares_.traverse (t);
+ }
+
+ private:
+ traversal::declares& declares_;
+ };
+
+ //
+ //
+ struct class1: traversal::class_, context
+ {
+ class1 (bool& valid)
+ : valid_ (valid), typedefs_ (declares_), member_ (valid)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ *this >> declares_ >> vt_;
+
+ names_member_ >> member_;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck != class_other)
+ {
+ for (type::inherits_iterator i (c.inherits_begin ());
+ i != c.inherits_end (); ++i)
+ {
+ type& b (i->base ());
+
+ if (class_kind (b) == class_other)
+ continue;
+
+ location_t cl;
+ location_t bl;
+
+ // If we are in the same file, then use real locations (as
+ // opposed to definition locations) since that's the order
+ // in which we will be generating the code.
+ //
+ if (class_file (c) == class_file (b))
+ {
+ cl = class_real_location (c);
+ bl = class_real_location (b);
+ }
+ else
+ {
+ cl = class_location (c);
+ bl = class_location (b);
+ }
+
+ if (cl < bl)
+ {
+ // We cannot use class_name() for b since it might not have
+ // yet been processed (tree-hint).
+ //
+ error (cl) << "base class " << b.name () << " must "
+ << "be defined before derived class " << class_name (c)
+ << endl;
+
+ info (bl) << "class " << b.name () << " is define here"
+ << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+
+ switch (ck)
+ {
+ case class_object:
+ names (c);
+ traverse_object (c);
+ break;
+ case class_view:
+ names (c);
+ traverse_view (c);
+ break;
+ case class_composite:
+ names (c);
+ traverse_composite (c);
+ // Fall through.
+ case class_other:
+ vt_.dispatch (c);
+ break;
+ }
+ }
+
+ virtual void
+ traverse_object (type& c)
+ {
+ // Check that the deletion version makes sense.
+ //
+ if (unsigned long long v = c.get<unsigned long long> ("deleted", 0))
+ {
+ location_t l (c.get<location_t> ("deleted-location"));
+
+ if (!versioned ())
+ {
+ error (l) << "deleted class in a non-versioned object model" << endl;
+ valid_ = false;
+ }
+ else
+ {
+ model_version const& mv (version ());
+
+ if (v > mv.current)
+ {
+ error (l) << "deletion version is greater than the current " <<
+ "model version" << endl;
+ valid_ = false;
+ }
+ else if (v <= mv.base)
+ {
+ error (l) << "deletion version is less than or equal to the " <<
+ "base model version" << endl;
+ info (c.location ()) << "delete this class since migration to " <<
+ "version " << v << " is no longer possible" << endl;
+ valid_ = false;
+ }
+ }
+ }
+
+ // Check that the callback function exist.
+ //
+ if (c.count ("callback"))
+ {
+ string name (c.get<string> ("callback"));
+ tree decl (
+ lookup_qualified_name (
+ c.tree_node (), get_identifier (name.c_str ()), false, false));
+
+ if (decl != error_mark_node && TREE_CODE (decl) == BASELINK)
+ {
+ // Figure out if we have a const version of the callback. OVL_*
+ // macros work for both FUNCTION_DECL and OVERLOAD.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ for (ovl_iterator i (BASELINK_FUNCTIONS (decl)); i; ++i)
+#else
+ for (tree o (BASELINK_FUNCTIONS (decl)); o != 0; o = OVL_NEXT (o))
+#endif
+ {
+#if BUILDING_GCC_MAJOR >= 8
+ tree f (*i);
+#else
+ tree f (OVL_CURRENT (o));
+#endif
+ if (DECL_CONST_MEMFUNC_P (f))
+ {
+ c.set ("callback-const", true);
+ break;
+ }
+ }
+
+ //@@ Would be nice to check the signature of the function(s)
+ // instead of postponing it until the C++ compilation. Though
+ // we may still get C++ compilation errors because of const
+ // mismatch.
+ //
+ }
+ else
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ": "
+ << "error: unable to resolve member function '" << name << "' "
+ << "specified with '#pragma db callback' for class '"
+ << class_name (c) << "'" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ // Check bases.
+ //
+ type* poly_root (0);
+
+ for (type::inherits_iterator i (c.inherits_begin ());
+ i != c.inherits_end ();
+ ++i)
+ {
+ type& b (i->base ());
+
+ if (object (b))
+ {
+ if (type* r = polymorphic (b))
+ {
+ if (poly_root == 0)
+ {
+ poly_root = r;
+ c.set ("polymorphic-base", &static_cast<semantics::class_&> (b));
+ }
+ // If poly_root and r are the same, then we have virtual
+ // inheritance. Though we don't support it at the moment.
+ //
+ else //if (poly_root != r)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: persistent class '" << class_name (c) << "' "
+ << "derives from multiple polymorphic bases" << endl;
+
+ type& a (*poly_root);
+ os << a.file () << ":" << a.line () << ":" << a.column () << ":"
+ << " info: first polymorphic base defined here" << endl;
+
+ type& b (*r);
+ os << b.file () << ":" << b.line () << ":" << b.column () << ":"
+ << " info: second polymorphic base defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+ else if (view (b) || composite (b))
+ {
+ // @@ Should we use hint here?
+ //
+ string name (class_fq_name (b));
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: base class '" << name << "' is a view or value type"
+ << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: object types cannot derive from view or value "
+ << "types"
+ << endl;
+
+ os << b.file () << ":" << b.line () << ":" << b.column () << ":"
+ << " info: class '" << name << "' is defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ // Check members.
+ //
+ names (c, names_member_);
+
+ // Check special members.
+ //
+ using semantics::data_member;
+
+ data_member* id (0);
+ data_member* optimistic (0);
+ {
+ special_members t (class_object, valid_, id, optimistic);
+ t.traverse (c);
+ }
+
+ if (id == 0)
+ {
+ // An object without an id should either be reuse-abstract
+ // or explicitly marked as such. We check that it is not
+ // polymorphic below.
+ //
+ if (!(c.count ("id") || abstract (c)))
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: no data member designated as an object id" << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: use '#pragma db id' to specify an object id member"
+ << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: or explicitly declare that this persistent class "
+ << "has no object id with '#pragma db object no_id'" << endl;
+
+ valid_ = false;
+ }
+ }
+ else
+ {
+ // Convert id to a member path. This has to happen early since
+ // a lot of code that runs next (e.g., processor, pass 1) depends
+ // on this information being available.
+ //
+ data_member_path& idp (c.set ("id-member", data_member_path ()));
+ idp.push_back (id);
+
+ // See if we have a member path that we need to resolve.
+ //
+ const string& name (id->get<string> ("id"));
+ location_t l (id->get<location_t> ("id-location"));
+
+ if (!name.empty ())
+ {
+ if (id->count ("auto"))
+ {
+ error (l) << "nested id cannot be automatically assigned" << endl;
+ valid_ = false;
+ }
+
+ if (semantics::class_* comp = utype (*id).is_a<semantics::class_> ())
+ {
+ try
+ {
+ resolve_data_members (idp, *comp, name, l, lex_);
+ }
+ catch (const operation_failed&) {valid_ = false;}
+ }
+ else
+ {
+ error (l) << "nested id requires composite member" << endl;
+ valid_ = false;
+ }
+
+ // Mark the whole member as readonly.
+ //
+ id->set ("readonly", true);
+ }
+
+ data_member* idf (idp.front ());
+ data_member* idb (idp.back ());
+
+ // Complain if an id member has a default value (default value
+ // for the id's type is ok -- we will ignore it).
+ //
+ if (idb->count ("default"))
+ {
+ error (l) << "object id member cannot have default value" << endl;
+ valid_ = false;
+ }
+
+ // Complain if an id member is in a section.
+ //
+ if (idf->count ("section-member"))
+ {
+ error (l) << "object id member cannot be in a section" << endl;
+ valid_ = false;
+ }
+
+ // Automatically mark the id member as not null. If we already have
+ // an explicit null pragma for this member, issue an error.
+ //
+ if (idb->count ("null"))
+ {
+ error (l) << "object id member cannot be null" << endl;
+ valid_ = false;
+ }
+ else
+ idf->set ("not-null", true);
+ }
+
+ if (optimistic != 0)
+ {
+ semantics::data_member& m (*optimistic);
+
+ // Make sure we have the class declared optimistic.
+ //
+ if (&m.scope () == &c && !c.count ("optimistic"))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: version data member in a class not declared "
+ << "optimistic" << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: use '#pragma db optimistic' to declare this "
+ << "class optimistic" << endl;
+
+ valid_ = false;
+ }
+
+ // Make sure we have object id.
+ //
+ if (id == 0)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: optimistic class without an object id" << endl;
+
+ valid_ = false;
+ }
+
+ // Make sure id and version members are in the same class. The
+ // current architecture relies on that.
+ //
+ if (id != 0 && &id->scope () != &m.scope ())
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: object id and version members are in different "
+ << "classes" << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: object id and version members must be in the same "
+ << "class" << endl;
+
+ os << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": info: object id member is declared here" << endl;
+
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: version member is declared here" << endl;
+
+ valid_ = false;
+ }
+
+ // Make sure this class is not readonly.
+ //
+ if (readonly (c))
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: optimistic class cannot be readonly" << endl;
+
+ valid_ = false;
+ }
+
+ // Complain if the version member is in a section.
+ //
+ if (m.count ("section-member"))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: version member cannot be in a section" << endl;
+ valid_ = false;
+ }
+
+ // This takes care of also marking derived classes as optimistic.
+ //
+ c.set ("optimistic-member", optimistic);
+ }
+ else
+ {
+ // Make sure there is a version member if the class declared
+ // optimistic.
+ //
+ if (c.count ("optimistic"))
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: optimistic class without a version member" << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: use '#pragma db version' to declare on of the "
+ << "data members as a version" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ // Polymorphic inheritance.
+ //
+ if (c.count ("polymorphic") && poly_root == 0)
+ {
+ // Root of the hierarchy.
+ //
+
+ if (id == 0)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: polymorphic class without an object id" << endl;
+
+ valid_ = false;
+ }
+
+ if (!TYPE_POLYMORPHIC_P (c.tree_node ()))
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: non-polymorphic class (class without virtual "
+ << "functions) cannot be declared polymorphic" << endl;
+
+ valid_ = false;
+ }
+
+ poly_root = &c;
+ }
+
+ if (poly_root != 0)
+ c.set ("polymorphic-root", poly_root);
+
+ // Sectionable objects.
+ //
+ if (c.count ("sectionable"))
+ {
+ if (optimistic == 0)
+ {
+ location_t l (c.get<location_t> ("sectionable-location"));
+ error (l) << "only optimistic class can be sectionable" << endl;
+ valid_ = false;
+ }
+ else if (&optimistic->scope () != &c && poly_root != &c)
+ {
+ location l (c.get<location_t> ("sectionable-location"));
+ error (l) << "only optimistic class that declares the version " <<
+ "data member or that is a root of a polymorphic hierarchy can " <<
+ "be sectionable" << endl;
+ info (optimistic->location ()) << "version member is declared " <<
+ "here" << endl;
+ valid_ = false;
+ }
+ }
+
+ // Update features set based on this object.
+ //
+ if (options.at_once () || class_file (c) == unit.file ())
+ {
+ if (poly_root != 0)
+ features.polymorphic_object = true;
+ else if (id == 0 && !abstract (c))
+ features.no_id_object = true;
+ else
+ features.simple_object = true;
+ }
+ }
+
+ virtual void
+ traverse_view (type& c)
+ {
+ // Views require query support.
+ //
+ if (!options.generate_query ())
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: query support is required when using views"
+ << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: use the --generate-query option to enable query "
+ << "support"
+ << endl;
+
+ valid_ = false;
+ }
+
+ // Check that the callback function exist.
+ //
+ if (c.count ("callback"))
+ {
+ string name (c.get<string> ("callback"));
+ tree decl (
+ lookup_qualified_name (
+ c.tree_node (), get_identifier (name.c_str ()), false, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != BASELINK)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ": "
+ << "error: unable to resolve member function '" << name << "' "
+ << "specified with '#pragma db callback' for class '"
+ << class_name (c) << "'" << endl;
+
+ valid_ = false;
+ }
+
+ // No const version for views.
+
+ //@@ Would be nice to check the signature of the function(s)
+ // instead of postponing it until the C++ compilation. Though
+ // we may still get C++ compilation errors because of const
+ // mismatch.
+ //
+ }
+
+ // Check bases.
+ //
+ for (type::inherits_iterator i (c.inherits_begin ());
+ i != c.inherits_end ();
+ ++i)
+ {
+ type& b (i->base ());
+
+ if (object (b) || view (b) || composite (b))
+ {
+ // @@ Should we use hint here?
+ //
+ string name (class_fq_name (b));
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: base class '" << name << "' is an object, "
+ << "view, or value type"
+ << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: view types cannot derive from view, object or "
+ << "value types"
+ << endl;
+
+ os << b.file () << ":" << b.line () << ":" << b.column () << ":"
+ << " info: class '" << name << "' is defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ // Check members.
+ //
+ names (c, names_member_);
+
+ // Check id.
+ //
+ semantics::data_member* id (0);
+ semantics::data_member* optimistic (0);
+ {
+ special_members t (class_view, valid_, id, optimistic);
+ t.traverse (c);
+ }
+
+ if (id != 0)
+ {
+ os << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": error: view type data member cannot be designated as an "
+ << "object id" << endl;
+
+ valid_ = false;
+ }
+
+ if (optimistic != 0)
+ {
+ semantics::data_member& o (*optimistic);
+
+ os << o.file () << ":" << o.line () << ":" << o.column ()
+ << ": error: view type data member cannot be designated as a "
+ << "version" << endl;
+
+ valid_ = false;
+ }
+
+ // Update features set based on this view.
+ //
+ if (options.at_once () || class_file (c) == unit.file ())
+ {
+ features.view = true;
+ }
+ }
+
+ virtual void
+ traverse_composite (type& c)
+ {
+ for (type::inherits_iterator i (c.inherits_begin ());
+ i != c.inherits_end ();
+ ++i)
+ {
+ type& b (i->base ());
+
+ if (object (b) || view (b))
+ {
+ // @@ Should we use hint here?
+ //
+ string name (class_fq_name (b));
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: base class '" << name << "' is a view or object "
+ << "type"
+ << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: composite value types cannot derive from object "
+ << "or view types" << endl;
+
+ os << b.file () << ":" << b.line () << ":" << b.column () << ":"
+ << " info: class '" << name << "' is defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ // Check members.
+ //
+ names (c, names_member_);
+
+ // Check id.
+ //
+ semantics::data_member* id (0);
+ semantics::data_member* optimistic (0);
+ {
+ special_members t (class_composite, valid_, id, optimistic);
+ t.traverse (c);
+ }
+
+ if (id != 0)
+ {
+ os << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": error: value type data member cannot be designated as an "
+ << "object id" << endl;
+
+ valid_ = false;
+ }
+
+ if (optimistic != 0)
+ {
+ semantics::data_member& o (*optimistic);
+
+ os << o.file () << ":" << o.line () << ":" << o.column ()
+ << ": error: value type data member cannot be designated as a "
+ << "version" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ bool& valid_;
+
+ traversal::defines defines_;
+ traversal::declares declares_;
+ typedefs1 typedefs_;
+
+ value_type vt_;
+ data_member1 member_;
+ traversal::names names_member_;
+
+ cxx_string_lexer lex_;
+ };
+
+ //
+ // Pass 2.
+ //
+
+ struct data_member2: traversal::data_member, context
+ {
+ data_member2 (bool& valid): valid_ (valid) {}
+
+ // Verify that composite value 'c' that is used by data member
+ // 'm' is defined before (or inside) class 's'.
+ //
+ void
+ verify_composite_location (semantics::class_& c,
+ semantics::class_& s,
+ semantics::data_member& m)
+ {
+ if (c.in_scope (s))
+ return;
+
+ location_t cl;
+ location_t sl;
+
+ // If we are in the same file, then use real locations (as
+ // opposed to definition locations) since that's the order
+ // in which we will be generating the code.
+ //
+ if (class_file (c) == class_file (s))
+ {
+ cl = class_real_location (c);
+ sl = class_real_location (s);
+ }
+ else
+ {
+ cl = class_location (c);
+ sl = class_location (s);
+ }
+
+ if (sl < cl)
+ {
+ const string& cn (class_name (c));
+
+ error (sl)
+ << "composite value type " << class_fq_name (c) << " must "
+ << "be defined before being used in class " << class_fq_name (s)
+ << endl;
+
+ info (m.location ())
+ << "data member " << m.name () << " uses " << cn << endl;
+
+ info (cl) << "class " << cn << " is define here" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ virtual void
+ traverse (type& m)
+ {
+ using semantics::class_;
+
+ semantics::type& t (utype (m));
+ class_& s (dynamic_cast<class_&> (m.scope ()));
+
+ // Validate pointed-to objects.
+ //
+ class_* c;
+ if ((c = object_pointer (t)) || (c = points_to (m)))
+ {
+ bool poly (polymorphic (*c));
+ bool abst (abstract (*c));
+
+ // Make sure the pointed-to class is complete.
+ //
+ if (!c->complete ())
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "error: pointed-to class '" << class_fq_name (*c) << "' "
+ << "is incomplete" << endl;
+
+ os << c->file () << ":" << c->line () << ":" << c->column () << ": "
+ << "info: class '" << class_name (*c) << "' is declared here"
+ << endl;
+
+ os << c->file () << ":" << c->line () << ":" << c->column () << ": "
+ << "info: consider including its definition with the "
+ << "--odb-epilogue option" << endl;
+
+ valid_ = false;
+ return;
+ }
+
+ // Make sure the pointed-to class is not reuse-abstract.
+ //
+ if (abst && !poly)
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "error: pointed-to class '" << class_fq_name (*c) << "' "
+ << "is abstract" << endl;
+
+ os << c->file () << ":" << c->line () << ":" << c->column () << ": "
+ << "info: class '" << class_name (*c) << "' is defined here"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ // Make sure the pointed-to class has object id unless it is in a
+ // view where we can load no-id objects.
+ //
+ if (data_member_path* id = id_member (*c))
+ {
+ semantics::type& idt (utype (*id));
+
+ // Make sure composite id is defined before this pointer. Failed
+ // that we get non-obvious C++ compiler errors in generated code.
+ //
+ if (class_* comp = composite_wrapper (idt))
+ verify_composite_location (*comp, s, m);
+ }
+ else if (!view_member (m))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "error: pointed-to class '" << class_fq_name (*c) << "' "
+ << "has no object id" << endl;
+
+ os << c->file () << ":" << c->line () << ":" << c->column () << ": "
+ << "info: class '" << class_name (*c) << "' is defined here"
+ << endl;
+
+ valid_ = false;
+ return;
+ }
+
+ // Make sure the pointed-to class has a default ctor. Since we will
+ // use database::load() in the generated code, lack of a default ctor
+ // will lead to uncompilable generated code. Poly-abstract is Ok.
+ //
+ if (!c->default_ctor () && !(abst && poly))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "error: pointed-to class '" << class_fq_name (*c) << "' "
+ << "has no default constructor" << endl;
+
+ os << c->file () << ":" << c->line () << ":" << c->column () << ": "
+ << "info: class '" << class_name (*c) << "' is defined here"
+ << endl;
+
+ valid_ = false;
+ return;
+ }
+ }
+
+ // Make sure composite type is defined before (or inside)
+ // this class. Failed that we get non-obvious C++ compiler
+ // errors in generated code.
+ //
+ if (class_* comp = composite_wrapper (t))
+ verify_composite_location (*comp, s, m);
+
+ // Check that containers with composite value element types don't have
+ // any nested containers.
+ //
+ if (semantics::type* c = container (m))
+ {
+ class_* vt (0);
+ class_* kt (0);
+
+ switch (container_kind (*c))
+ {
+ case ck_map:
+ case ck_multimap:
+ {
+ kt = composite_wrapper (container_kt (m));
+ }
+ // Fall through.
+ default:
+ {
+ vt = composite_wrapper (container_vt (m));
+ }
+ }
+
+ if (vt != 0 && has_a (*vt, test_container))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "error: containers of containers not supported" << endl;
+
+ os << vt->file () << ":" << vt->line () << ":" << vt->column ()
+ << ": info: composite element value " << class_fq_name (*vt)
+ << " contains container(s)" << endl;
+
+ valid_ = false;
+ }
+
+ if (kt != 0 && has_a (*kt, test_container))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "error: containers of containers not supported" << endl;
+
+ os << kt->file () << ":" << kt->line () << ":" << kt->column ()
+ << ": info: composite element key " << class_fq_name (*kt)
+ << " contains container(s)" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+
+ bool& valid_;
+ };
+
+ // Make sure soft-delete versions make sense for dependent entities.
+ // We don't seem to need anything for soft-add since if an entity is
+ // not added (e.g., an object), then we cannot reference it in the
+ // C++ sense (e.g., a pointer).
+ //
+ struct version_dependencies: object_members_base
+ {
+ version_dependencies (bool& valid): valid_ (valid) {}
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ // For reuse inheritance we allow the base to be soft-deleted. In
+ // a sense, it becomes like an abstract class with the added
+ // functionality of being able to load old data.
+ //
+ // For polymorphism inheritance things are a lot more complicated
+ // and we don't allow a base to be soft-deleted since there is a
+ // link (foreign key) from the derived table.
+ //
+ semantics::class_* poly (polymorphic (c));
+ if (poly != 0 && poly != &c)
+ {
+ semantics::class_& base (polymorphic_base (c));
+ check_strict (
+ c, base, "polymorphic derived object", "polymorphic base");
+ }
+
+ // Don't go into bases.
+ //
+ names (c);
+ }
+
+ virtual void
+ traverse_simple (semantics::data_member& m)
+ {
+ semantics::class_& c (dynamic_cast<semantics::class_&> (m.scope ()));
+
+ switch (class_kind (c))
+ {
+ case class_object:
+ {
+ // Member shouldn't be deleted after the object that contains it.
+ //
+ check_lax (m, c, "data member", "object");
+ break;
+ }
+ // We leave it up to the user to make sure the view is consistent
+ // with the database schema it is being run on.
+ //
+ default:
+ break;
+ }
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ if (m != 0)
+ traverse_simple (*m); // Do simple value tests.
+ else
+ names (c); // Don't go into bases.
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ traverse_simple (m); // Do simple value tests.
+
+ // Pointer must be deleted before the pointed-to object.
+ //
+ check_strict (m, c, "object pointer", "pointed-to object");
+
+ // Inverse pointer must be deleted before the direct side.
+ //
+ if (data_member_path* imp = inverse (m))
+ check_strict (m, *imp, "inverse object pointer", "direct pointer");
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ traverse_simple (m); // Do simple value tests.
+
+ if (semantics::class_* c = object_pointer (container_vt (m)))
+ {
+ // Pointer must be deleted before the pointed-to object.
+ //
+ check_strict (m, *c, "object pointer", "pointed-to object");
+
+ // Inverse pointer must be deleted before the direct side.
+ //
+ if (data_member_path* imp = inverse (m, "value"))
+ check_strict (m, *imp, "inverse object pointer", "direct pointer");
+ }
+ }
+
+ private:
+ // Check for harmless things like a data member deleted after the
+ // object.
+ //
+ template <typename D, typename P>
+ void
+ check_lax (D& dep, P& pre, char const* dname, char const* pname)
+ {
+ unsigned long long dv (deleted (dep));
+ unsigned long long pv (deleted (pre));
+
+ if (pv == 0 || // Prerequisite never deleted.
+ dv == 0 || // Dependent never deleted.
+ dv <= pv) // Dependent deleted before prerequisite.
+ return;
+
+ location_t dl (dep.template get<location_t> ("deleted-location"));
+ location_t pl (pre.template get<location_t> ("deleted-location"));
+
+ error (dl) << dname << " is deleted after " << dname << endl;
+ info (pl) << pname << " deletion version is specified here" << endl;
+
+ valid_ = false;
+ }
+
+ // Check for harmful things that will result in an invalid database
+ // schema, like a pointer pointing to a deleted object.
+ //
+ template <typename D, typename P>
+ void
+ check_strict (D& dep, P& pre, char const* dname, char const* pname)
+ {
+ location_t dl (0), pl (0);
+ unsigned long long dv (deleted (dep, &dl));
+ unsigned long long pv (deleted (pre, &pl));
+
+ if (pv == 0) // Prerequisite never deleted.
+ return;
+
+ if (dv == 0) // Dependent never deleted.
+ {
+ location dl (dep.location ());
+
+ error (dl) << dname << " is not deleted" << endl;
+ info (pl) << pname << " is deleted here" << endl;
+
+ valid_ = false;
+ }
+ else if (pv > dv) // Prerequisite deleted before dependent.
+ {
+ error (dl) << dname << " is deleted after " << pname << endl;
+ info (pl) << pname << " deletion version is specified here" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ private:
+ bool& valid_;
+ };
+
+ struct class2: traversal::class_, context
+ {
+ class2 (bool& valid)
+ : valid_ (valid), has_lt_operator_ (0), typedefs_ (true), member_ (valid)
+ {
+ // Find the has_lt_operator function template.
+ //
+ tree odb (
+ lookup_qualified_name (
+ global_namespace, get_identifier ("odb"), false, false));
+
+ if (odb != error_mark_node)
+ {
+ tree compiler (
+ lookup_qualified_name (
+ odb, get_identifier ("compiler"), false, false));
+
+ if (compiler != error_mark_node)
+ {
+ has_lt_operator_ = lookup_qualified_name (
+ compiler, get_identifier ("has_lt_operator"), false, false);
+
+ if (has_lt_operator_ != error_mark_node)
+ {
+#if BUILDING_GCC_MAJOR >= 8
+ has_lt_operator_ = OVL_FIRST (has_lt_operator_);
+#else
+ has_lt_operator_ = OVL_CURRENT (has_lt_operator_);
+#endif
+ }
+ else
+ {
+ os << unit.file () << ": error: unable to resolve has_lt_operator "
+ << "function template inside odb::compiler" << endl;
+ has_lt_operator_ = 0;
+ }
+ }
+ else
+ os << unit.file () << ": error: unable to resolve compiler "
+ << "namespace inside odb" << endl;
+ }
+ else
+ os << unit.file () << ": error: unable to resolve odb namespace"
+ << endl;
+
+ if (has_lt_operator_ == 0)
+ valid_ = false;
+
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+
+ names_member_ >> member_;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other)
+ return;
+
+ names (c);
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ case class_composite: traverse_composite (c); break;
+ default: return;
+ }
+
+ // Check members.
+ //
+ names (c, names_member_);
+
+ // Check version dependencies.
+ //
+ {
+ version_dependencies vd (valid_);
+ vd.traverse (c);
+ }
+ }
+
+ virtual void
+ traverse_object (type& c)
+ {
+ bool abst (abstract (c));
+ bool poly (polymorphic (c));
+
+ // Make sure we have no empty or pointless sections unless we
+ // are reuse-abstract or polymorphic.
+ //
+ if (!poly && !abst)
+ {
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ user_section& s (*i);
+
+ // Skip the special version update section (we always treat it
+ // as abstract in reuse inheritance).
+ //
+ if (s.special == user_section::special_version)
+ continue;
+
+ semantics::data_member& m (*s.member);
+ location const& l (m.location ());
+
+ if (s.total == 0 && !s.containers)
+ {
+ error (l) << "empty section" << endl;
+
+ if (&m.scope () != &c)
+ info (c.location ()) << "as seen in this non-abstract " <<
+ "persistent class" << endl;
+
+ valid_ = false;
+ continue;
+ }
+
+ // Eager-loaded section with readonly members.
+ //
+ if (s.load == user_section::load_eager && s.update_empty ())
+ {
+ error (l) << "eager-loaded section with readonly members is " <<
+ "pointless" << endl;
+
+ if (&m.scope () != &c)
+ info (c.location ()) << "as seen in this non-abstract " <<
+ "persistent class" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+
+ if (data_member_path* id = id_member (c))
+ {
+ semantics::type& t (utype (*id));
+
+ // If this is a session object, make sure that the id type can
+ // be compared.
+ //
+ if (session (c) && has_lt_operator_ != 0)
+ {
+ tree args (make_tree_vec (1));
+ TREE_VEC_ELT (args, 0) = t.tree_node ();
+
+ tree inst (
+ instantiate_template (
+ has_lt_operator_, args, tf_none));
+
+ bool v (inst != error_mark_node);
+
+ // 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))
+ {
+ // Instantiate this function template to see if the value type
+ // provides operator<. Unfortunately, GCC instantiate_decl()
+ // does not provide any control over the diagnostics it issues
+ // in case of an error. To work around this, we are going to
+ // temporarily redirect diagnostics to /dev/null, which is
+ // where asm_out_file points to (see plugin.cxx).
+ //
+ // Needless to say, this is very hacky and we should quickly fail
+ // (as we do below) if there were errors.
+ //
+ int ec (errorcount);
+ FILE* s (global_dc->printer->buffer->stream);
+ global_dc->printer->buffer->stream = asm_out_file;
+
+ instantiate_decl (inst, false, false);
+
+ global_dc->printer->buffer->stream = s;
+ v = (ec == errorcount);
+ }
+#endif
+
+ if (!v)
+ {
+ semantics::data_member& idm (*id->front ());
+
+ os << t.file () << ":" << t.line () << ":" << t.column ()
+ << ": error: value type that is used as object id in "
+ << "persistent class with session support does not define "
+ << "the less than (<) comparison" << endl;
+
+ os << t.file () << ":" << t.line () << ":" << t.column ()
+ << ": info: provide operator< for this value type" << endl;
+
+ os << idm.file () << ":" << idm.line () << ":" << idm.column ()
+ << ": info: id member is defined here" << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column ()
+ << ": info: persistent class is defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+ else
+ {
+ // Make sure an object without id has no sections.
+ //
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ if (!uss.empty ())
+ {
+ semantics::data_member& m (*uss.front ().member);
+ os << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: object without id cannot have sections" << endl;
+ valid_ = false;
+ }
+ }
+
+ // Allow all the members to be deleted as long as there is no
+ // schema for this class.
+ //
+ column_count_type const& cc (column_count (c));
+ size_t cont (has_a (c, test_container));
+ size_t dcont (cont - has_a (c, test_container | exclude_deleted));
+
+ if ((cc.total == 0 && cont == 0) ||
+ (cc.total == cc.deleted && cont == dcont && !(abst || deleted (c))))
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: no persistent data members in the class" << endl;
+ valid_ = false;
+ }
+ }
+
+ virtual void
+ traverse_view (type& c)
+ {
+ // We don't check for the column count here since we may want to
+ // allow certain kinds of empty views. Instead, this is handled
+ // in relational::validation.
+
+ // See if any of the associated objects are polymorphic. If so,
+ // we need to include polymorphism-specific headers. Also, if the
+ // view is loading the object, then we will need the corresponding
+ // statements.
+ //
+ if (c.count ("objects"))
+ {
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ if (i->kind == view_object::object)
+ {
+ if (polymorphic (*i->obj))
+ features.polymorphic_object = true;
+ else if (i->ptr != 0)
+ {
+ (id_member (*i->obj) != 0
+ ? features.simple_object
+ : features.no_id_object) = true;
+ }
+ }
+ }
+ }
+ }
+
+ virtual void
+ traverse_composite (type& c)
+ {
+ // Allow all the members to be deleted.
+ //
+ column_count_type const& cc (column_count (c));
+ size_t cont (has_a (c, test_container));
+
+ if (cc.total == 0 && cont == 0)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: no persistent data members in the class" << endl;
+ valid_ = false;
+ }
+ }
+
+ bool& valid_;
+ tree has_lt_operator_;
+
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ data_member2 member_;
+ traversal::names names_member_;
+ };
+}
+
+void
+validate (options const& ops,
+ features& f,
+ semantics::unit& u,
+ semantics::path const& p,
+ unsigned short pass)
+{
+ bool valid (true);
+ database db (ops.database ()[0]);
+
+ // Validate options.
+ //
+ if (ops.generate_schema_only () &&
+ ops.schema_format ()[db].count (schema_format::embedded))
+ {
+ cerr << "error: --generate-schema-only is only valid when generating " <<
+ "schema as a standalone SQL or separate C++ file" << endl;
+ valid = false;
+ }
+
+ // Multi-database support options.
+ //
+ if (ops.multi_database () == multi_database::dynamic &&
+ ops.default_database_specified () &&
+ ops.default_database () != database::common)
+ {
+ cerr << "error: when dynamic multi-database support is used, the " <<
+ "default database can only be 'common'" << endl;
+ valid = false;
+ }
+
+ if (db == database::common &&
+ ops.multi_database () == multi_database::disabled)
+ {
+ cerr << "error: 'common' database is only valid with multi-database " <<
+ "support enabled" << endl;
+ valid = false;
+ }
+
+ // Changelog options.
+ //
+ if (ops.changelog_in ().count (db) != ops.changelog_out ().count (db))
+ {
+ cerr << "error: both --changelog-in and --changelog-out must be " <<
+ "specified" << endl;
+ valid = false;
+ }
+
+ if (!valid)
+ throw validator_failed ();
+
+ unique_ptr<context> ctx (create_context (cerr, u, ops, f, 0));
+
+ if (pass == 1)
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ traversal::declares unit_declares;
+ typedefs1 unit_typedefs (unit_declares);
+ traversal::namespace_ ns;
+ value_type vt;
+ class1 c (valid);
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_declares >> vt;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ traversal::declares ns_declares;
+ typedefs1 ns_typedefs (ns_declares);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_declares >> vt;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (u);
+ }
+ else
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (true);
+ traversal::namespace_ ns;
+ class2 c (valid);
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (true);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (u);
+ }
+
+ if (!valid)
+ throw validator_failed ();
+
+ switch (db)
+ {
+ case database::common:
+ {
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ try
+ {
+ relational::validate (ops, f, u, p, pass);
+ }
+ catch (operation_failed const&)
+ {
+ throw validator_failed ();
+ }
+
+ break;
+ }
+ }
+}
diff --git a/odb/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/option-functions.cxx b/odb/option-functions.cxx
deleted file mode 100644
index 00d36cd..0000000
--- a/odb/option-functions.cxx
+++ /dev/null
@@ -1,132 +0,0 @@
-// file : odb/option-functions.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <set>
-#include <utility> // std::make_pair()
-
-#include <odb/option-functions.hxx>
-
-using namespace std;
-
-void
-process_options (options& o)
-{
- database db (o.database ()[0]);
-
- // If --generate-schema-only was specified, then set --generate-schema
- // as well.
- //
- if (o.generate_schema_only ())
- o.generate_schema (true);
-
- // If --warn-hard was specified, then set both --warn-hard-{add,delete}.
- //
- if (o.warn_hard ())
- {
- o.warn_hard_add (true);
- o.warn_hard_delete (true);
- }
-
- // Set the default schema format depending on the database.
- //
- if (o.generate_schema () && o.schema_format ()[db].empty ())
- {
- set<schema_format>& f (o.schema_format ()[db]);
-
- switch (db)
- {
- case database::common:
- {
- break; // No schema for common.
- }
- case database::mssql:
- case database::mysql:
- case database::oracle:
- case database::pgsql:
- {
- f.insert (schema_format::sql);
- break;
- }
- case database::sqlite:
- {
- f.insert (schema_format::embedded);
- break;
- }
- }
- }
-
- // Set default --schema-version-table value.
- //
- if (o.schema_version_table ().count (db) == 0)
- o.schema_version_table ()[db] = "schema_version";
-
- // Set default --schema-name value.
- //
- if (o.schema_name ().count (db) == 0)
- o.schema_name ()[db] = "";
-
- // Set default --fkeys-deferrable-mode value.
- //
- if (o.fkeys_deferrable_mode ().count (db) == 0)
- o.fkeys_deferrable_mode ()[db] = deferrable::deferred;
-
- // Set default --{export,extern}-symbol values.
- //
- if (o.export_symbol ().count (db) == 0)
- o.export_symbol ()[db] = "";
-
- if (o.extern_symbol ().count (db) == 0)
- o.extern_symbol ()[db] = "";
-
- // Set default --*-file-suffix values.
- //
- {
- database cm (database::common);
-
- o.odb_file_suffix ().insert (make_pair (cm, string ("-odb")));
- o.sql_file_suffix ().insert (make_pair (cm, string ("")));
- o.schema_file_suffix ().insert (make_pair (cm, string ("-schema")));
- o.changelog_file_suffix ().insert (make_pair (cm, string ("")));
- }
-
- if (o.multi_database () == multi_database::disabled)
- {
- o.odb_file_suffix ().insert (make_pair (db, string ("-odb")));
- o.sql_file_suffix ().insert (make_pair (db, string ("")));
- o.schema_file_suffix ().insert (make_pair (db, string ("-schema")));
- o.changelog_file_suffix ().insert (make_pair (db, string ("")));
- }
- else
- {
- o.odb_file_suffix ().insert (make_pair (db, "-odb-" + db.string ()));
- o.sql_file_suffix ().insert (make_pair (db, "-" + db.string ()));
- o.schema_file_suffix ().insert (make_pair (db, "-schema-" + db.string ()));
- o.changelog_file_suffix ().insert (make_pair (db, "-" + db.string ()));
- }
-
- // Set default --default-database value.
- //
- if (!o.default_database_specified ())
- {
- switch (o.multi_database ())
- {
- case multi_database::disabled:
- {
- o.default_database (db);
- o.default_database_specified (true);
- break;
- }
- case multi_database::dynamic:
- {
- o.default_database (database::common);
- o.default_database_specified (true);
- break;
- }
- case multi_database::static_:
- {
- // No default database unless explicitly specified.
- break;
- }
- }
- }
-}
diff --git a/odb/option-types.cxx b/odb/option-types.cxx
deleted file mode 100644
index aac0288..0000000
--- a/odb/option-types.cxx
+++ /dev/null
@@ -1,349 +0,0 @@
-// file : odb/option-types.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <istream>
-#include <ostream>
-#include <algorithm> // std::lower_bound
-
-#include <odb/option-types.hxx>
-
-using namespace std;
-
-//
-// cxx_version
-//
-
-static const char* cxx_version_[] =
-{
- "c++98",
- "c++11",
- "c++14",
- "c++17"
-};
-
-string cxx_version::
-string () const
-{
- return cxx_version_[v_];
-}
-
-istream&
-operator>> (istream& is, cxx_version& v)
-{
- string s;
- is >> s;
-
- if (!is.fail ())
- {
- if (s == "c++98")
- v = cxx_version::cxx98;
- else if (s == "c++11")
- v = cxx_version::cxx11;
- else if (s == "c++14")
- v = cxx_version::cxx14;
- else if (s == "c++17")
- v = cxx_version::cxx17;
- else
- is.setstate (istream::failbit);
- }
-
- return is;
-}
-
-//
-// database
-//
-
-static const char* database_[] =
-{
- "common",
- "mssql",
- "mysql",
- "oracle",
- "pgsql",
- "sqlite"
-};
-
-static const char* database_name_[] =
-{
- "Common Interface",
- "SQL Server",
- "MySQL",
- "Oracle",
- "PostgreSQL",
- "SQLite"
-};
-
-string database::
-string () const
-{
- return database_[v_];
-}
-
-string database::
-name () const
-{
- return database_name_[v_];
-}
-
-istream&
-operator>> (istream& is, database& db)
-{
- string s;
- is >> s;
-
- if (!is.fail ())
- {
- const char** e (database_ + sizeof (database_) / sizeof (char*));
- const char** i (lower_bound (database_, e, s));
-
- if (i != e && *i == s)
- db = database::value (i - database_);
- else
- is.setstate (istream::failbit);
- }
-
- return is;
-}
-
-ostream&
-operator<< (ostream& os, database db)
-{
- return os << db.string ();
-}
-
-//
-// multi_database
-//
-
-static const char* multi_database_[] =
-{
- "dynamic",
- "static",
- "disabled"
-};
-
-string multi_database::
-string () const
-{
- return multi_database_[v_];
-}
-
-istream&
-operator>> (istream& is, multi_database& db)
-{
- string s;
- is >> s;
-
- if (!is.fail ())
- {
- const char** e (
- multi_database_ + sizeof (multi_database_) / sizeof (char*) - 1);
- const char** i (lower_bound (multi_database_, e, s));
-
- if (i != e && *i == s)
- db = multi_database::value (i - multi_database_);
- else
- is.setstate (istream::failbit);
- }
-
- return is;
-}
-
-ostream&
-operator<< (ostream& os, multi_database db)
-{
- return os << db.string ();
-}
-
-//
-// schema_format
-//
-
-static const char* schema_format_[] =
-{
- "embedded",
- "separate",
- "sql"
-};
-
-string schema_format::
-string () const
-{
- return schema_format_[v_];
-}
-
-istream&
-operator>> (istream& is, schema_format& sf)
-{
- string s;
- is >> s;
-
- if (!is.fail ())
- {
- const char** e (schema_format_ + sizeof (schema_format_) / sizeof (char*));
- const char** i (lower_bound (schema_format_, e, s));
-
- if (i != e && *i == s)
- sf = schema_format::value (i - schema_format_);
- else
- is.setstate (istream::failbit);
- }
-
- return is;
-}
-
-ostream&
-operator<< (ostream& os, schema_format sf)
-{
- return os << sf.string ();
-}
-
-//
-// name_case
-//
-
-istream&
-operator>> (istream& is, name_case& v)
-{
- string s;
- is >> s;
-
- if (!is.fail ())
- {
- if (s == "upper")
- v = name_case::upper;
- else if (s == "lower")
- v = name_case::lower;
- else
- is.setstate (istream::failbit);
- }
-
- return is;
-}
-
-//
-// pgsql_version
-//
-
-istream&
-operator>> (istream& is, pgsql_version& v)
-{
- unsigned short major, minor;
-
- // Extract the major version.
- //
- is >> major;
-
- if (!is.fail ())
- {
- // Extract the decimal point.
- //
- char p;
- is >> p;
-
- if (!is.fail () && p == '.')
- {
- // Extract the minor version.
- //
- is >> minor;
-
- if (!is.fail ())
- v = pgsql_version (major, minor);
- }
- else
- is.setstate (istream::failbit);
- }
-
- return is;
-}
-
-ostream&
-operator<< (ostream& os, pgsql_version v)
-{
- return os << v.ver_major () << '.' << v.ver_minor ();
-}
-
-//
-// oracle_version
-//
-
-istream&
-operator>> (istream& is, oracle_version& v)
-{
- unsigned short major, minor;
-
- // Extract the major version.
- //
- is >> major;
-
- if (!is.fail ())
- {
- // Extract the decimal point.
- //
- char p;
- is >> p;
-
- if (!is.fail () && p == '.')
- {
- // Extract the minor version.
- //
- is >> minor;
-
- if (!is.fail ())
- v = oracle_version (major, minor);
- }
- else
- is.setstate (istream::failbit);
- }
-
- return is;
-}
-
-ostream&
-operator<< (ostream& os, oracle_version v)
-{
- return os << v.ver_major () << '.' << v.ver_minor ();
-}
-
-//
-// mssql_version
-//
-
-istream&
-operator>> (istream& is, mssql_version& v)
-{
- unsigned short major, minor;
-
- // Extract the major version.
- //
- is >> major;
-
- if (!is.fail ())
- {
- // Extract the decimal point.
- //
- char p;
- is >> p;
-
- if (!is.fail () && p == '.')
- {
- // Extract the minor version.
- //
- is >> minor;
-
- if (!is.fail ())
- v = mssql_version (major, minor);
- }
- else
- is.setstate (istream::failbit);
- }
-
- return is;
-}
-
-ostream&
-operator<< (ostream& os, mssql_version v)
-{
- return os << v.ver_major () << '.' << v.ver_minor ();
-}
diff --git a/odb/option-types.hxx b/odb/option-types.hxx
deleted file mode 100644
index 4739892..0000000
--- a/odb/option-types.hxx
+++ /dev/null
@@ -1,390 +0,0 @@
-// file : odb/option-types.hxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#ifndef ODB_OPTION_TYPES_HXX
-#define ODB_OPTION_TYPES_HXX
-
-#include <map>
-#include <iosfwd>
-#include <string>
-#include <cassert>
-
-#include <odb/semantics/relational/name.hxx>
-#include <odb/semantics/relational/deferrable.hxx>
-
-using semantics::relational::qname;
-using semantics::relational::deferrable;
-
-struct cxx_version
-{
- enum value
- {
- cxx98,
- cxx11,
- cxx14,
- cxx17
- };
-
- cxx_version (value v = value (0)) : v_ (v) {}
- operator value () const {return v_;}
-
- std::string
- string () const;
-
-private:
- value v_;
-};
-
-std::istream&
-operator>> (std::istream&, cxx_version&);
-
-//
-//
-struct database
-{
- enum value
- {
- // Keep in alphabetic order.
- //
- common,
- mssql,
- mysql,
- oracle,
- pgsql,
- sqlite
- };
-
- database (value v = value (0)) : v_ (v) {}
- operator value () const {return v_;}
-
- std::string
- string () const;
-
- // Full name (e.g., PostgreSQL).
- //
- std::string
- name () const;
-
-private:
- value v_;
-};
-
-std::istream&
-operator>> (std::istream&, database&);
-
-std::ostream&
-operator<< (std::ostream&, database);
-
-//
-//
-template <typename V>
-struct database_map: std::map<database, V>
-{
- typedef std::map<database, V> base_type;
-
- using base_type::operator[];
-
- V const&
- operator[] (database const& k) const
- {
- typename base_type::const_iterator i (this->find (k));
- assert (i != this->end ());
- return i->second;
- }
-};
-
-//
-//
-struct multi_database
-{
- enum value
- {
- // Keep in alphabetic order.
- //
- dynamic,
- static_,
- disabled // Special value.
- };
-
- multi_database (value v = disabled) : v_ (v) {}
- operator value () const {return v_;}
-
- std::string
- string () const;
-
-private:
- value v_;
-};
-
-std::istream&
-operator>> (std::istream&, multi_database&);
-
-std::ostream&
-operator<< (std::ostream&, multi_database);
-
-//
-//
-struct schema_format
-{
- enum value
- {
- // Keep in alphabetic order.
- //
- embedded,
- separate,
- sql
- };
-
- schema_format (value v = value (0)) : v_ (v) {}
- operator value () const {return v_;}
-
- std::string
- string () const;
-
-private:
- value v_;
-};
-
-std::istream&
-operator>> (std::istream&, schema_format&);
-
-std::ostream&
-operator<< (std::ostream&, schema_format);
-
-//
-//
-struct name_case
-{
- enum value
- {
- upper,
- lower
- };
-
- name_case (value v = value (0)) : v_ (v) {}
- operator value () const {return v_;}
-
-private:
- value v_;
-};
-
-std::istream&
-operator>> (std::istream&, name_case&);
-
-//
-//
-struct pgsql_version
-{
- pgsql_version (unsigned short major, unsigned short minor)
- : major_ (major), minor_ (minor)
- {
- }
-
- unsigned short
- ver_major () const
- {
- return major_;
- }
-
- unsigned short
- ver_minor () const
- {
- return minor_;
- }
-
-private:
- unsigned short major_;
- unsigned short minor_;
-};
-
-inline bool
-operator== (const pgsql_version& x, const pgsql_version& y)
-{
- return x.ver_major () == y.ver_major ();
-}
-
-inline bool
-operator!= (const pgsql_version& x, const pgsql_version& y)
-{
- return !(x == y);
-}
-
-inline bool
-operator< (const pgsql_version& x, const pgsql_version& y)
-{
- return x.ver_major () < y.ver_major () ||
- (x.ver_major () == y.ver_major () &&
- x.ver_minor () < y.ver_minor ());
-}
-
-inline bool
-operator> (const pgsql_version& x, const pgsql_version& y)
-{
- return x.ver_major () > y.ver_major () ||
- (x.ver_major () == y.ver_major () &&
- x.ver_minor () > y.ver_minor ());
-}
-
-inline bool
-operator<= (const pgsql_version& x, const pgsql_version& y)
-{
- return !(x > y);
-}
-
-inline bool
-operator>= (const pgsql_version& x, const pgsql_version& y)
-{
- return !(x < y);
-}
-
-std::istream&
-operator>> (std::istream&, pgsql_version&);
-
-std::ostream&
-operator<< (std::ostream&, pgsql_version);
-
-//
-//
-struct oracle_version
-{
- oracle_version (unsigned short major, unsigned short minor)
- : major_ (major), minor_ (minor)
- {
- }
-
- unsigned short
- ver_major () const
- {
- return major_;
- }
-
- unsigned short
- ver_minor () const
- {
- return minor_;
- }
-
-private:
- unsigned short major_;
- unsigned short minor_;
-};
-
-inline bool
-operator== (const oracle_version& x, const oracle_version& y)
-{
- return x.ver_major () == y.ver_major ();
-}
-
-inline bool
-operator!= (const oracle_version& x, const oracle_version& y)
-{
- return !(x == y);
-}
-
-inline bool
-operator< (const oracle_version& x, const oracle_version& y)
-{
- return x.ver_major () < y.ver_major () ||
- (x.ver_major () == y.ver_major () &&
- x.ver_minor () < y.ver_minor ());
-}
-
-inline bool
-operator> (const oracle_version& x, const oracle_version& y)
-{
- return x.ver_major () > y.ver_major () ||
- (x.ver_major () == y.ver_major () &&
- x.ver_minor () > y.ver_minor ());
-}
-
-inline bool
-operator<= (const oracle_version& x, const oracle_version& y)
-{
- return !(x > y);
-}
-
-inline bool
-operator>= (const oracle_version& x, const oracle_version& y)
-{
- return !(x < y);
-}
-
-std::istream&
-operator>> (std::istream&, oracle_version&);
-
-std::ostream&
-operator<< (std::ostream&, oracle_version);
-
-//
-//
-struct mssql_version
-{
- mssql_version (unsigned short major, unsigned short minor)
- : major_ (major), minor_ (minor)
- {
- }
-
- unsigned short
- ver_major () const
- {
- return major_;
- }
-
- unsigned short
- ver_minor () const
- {
- return minor_;
- }
-
-private:
- unsigned short major_;
- unsigned short minor_;
-};
-
-inline bool
-operator== (const mssql_version& x, const mssql_version& y)
-{
- return x.ver_major () == y.ver_major ();
-}
-
-inline bool
-operator!= (const mssql_version& x, const mssql_version& y)
-{
- return !(x == y);
-}
-
-inline bool
-operator< (const mssql_version& x, const mssql_version& y)
-{
- return x.ver_major () < y.ver_major () ||
- (x.ver_major () == y.ver_major () &&
- x.ver_minor () < y.ver_minor ());
-}
-
-inline bool
-operator> (const mssql_version& x, const mssql_version& y)
-{
- return x.ver_major () > y.ver_major () ||
- (x.ver_major () == y.ver_major () &&
- x.ver_minor () > y.ver_minor ());
-}
-
-inline bool
-operator<= (const mssql_version& x, const mssql_version& y)
-{
- return !(x > y);
-}
-
-inline bool
-operator>= (const mssql_version& x, const mssql_version& y)
-{
- return !(x < y);
-}
-
-std::istream&
-operator>> (std::istream&, mssql_version&);
-
-std::ostream&
-operator<< (std::ostream&, mssql_version);
-
-#endif // ODB_OPTION_TYPES_HXX
diff --git a/odb/options.cli b/odb/options.cli
deleted file mode 100644
index bb8797b..0000000
--- a/odb/options.cli
+++ /dev/null
@@ -1,1082 +0,0 @@
-// file : odb/options.cli
-// license : GNU GPL v3; see accompanying LICENSE file
-
-include <set>;
-include <vector>;
-include <string>;
-include <cstddef>;
-include <cstdint>;
-
-include <odb/option-types.hxx>;
-
-class options
-{
- //
- // Wrapper options. These are not passed to the plugin.
- //
-
- std::uint64_t --build2-metadata; // Leave undocumented/hidden.
-
- bool --help {"Print usage information and exit."};
- bool --version {"Print version and exit."};
-
- //
- // C++ preprocessor options. Also not passed to the plugin.
- //
- std::vector<std::string> -I
- {
- "<dir>",
- "Add <dir> to the beginning of the list of directories to be searched
- for included header files."
- };
-
- std::vector<std::string> -D
- {
- "<name>[=<def>]",
- "Define macro <name> with definition <def>. If definition is omitted,
- define <name> to be 1."
- };
-
- std::vector<std::string> -U
- {
- "<name>",
- "Cancel any previous definitions of macro <name>, either built-in or
- provided with the \cb{-D} option."
- };
-
- //
- // Plugin options.
- //
- std::vector< ::database> --database | -d
- {
- "<db>",
- "Generate code for the <db> database. Valid values are \cb{mssql},
- \cb{mysql}, \cb{oracle}, \cb{pgsql}, \cb{sqlite}, and \cb{common}
- (multi-database mode only)."
- };
-
- ::multi_database --multi-database | -m = ::multi_database::disabled
- {
- "<type>",
- "Enable multi-database support and specify its type. Valid values
- for this option are \cb{static} and \cb{dynamic}.
-
- In the multi-database mode, options that determine the kind (for
- example, \cb{--schema-format}), names (for example,
- \cb{--odb-file-suffix}), or content (for example, prologue and
- epilogue options) of the output files can be prefixed with the
- database name followed by a colon, for example, \cb{mysql:value}.
- This restricts the value of such an option to only apply to
- generated files corresponding to this database."
- };
-
- ::database --default-database
- {
- "<db>",
- "When static multi-database support is used, specify the database that
- should be made the default. When dynamic multi-database support is used,
- \cb{common} is always made the default database."
- };
-
- bool --generate-query | -q
- {
- "Generate query support code. Without this support you cannot use views
- and can only load objects via their ids."
- };
-
- bool --generate-prepared
- {
- "Generate prepared query execution support code."
- };
-
- bool --omit-unprepared
- {
- "Omit un-prepared (once-off) query execution support code."
- };
-
- bool --generate-session | -e
- {
- "Generate session support code. With this option session support will
- be enabled by default for all the persistent classes except those for
- which it was explicitly disabled using the \cb{db session} pragma."
- };
-
- bool --generate-schema | -s
- {
- "Generate the database schema. The database schema contains SQL
- statements that create database tables necessary to store persistent
- classes defined in the file being compiled. Note that by applying
- this schema, all the existing information stored in such tables will
- be lost.
-
- Depending on the database being used (\cb{--database} option), the
- schema is generated either as a standalone SQL file or embedded into
- the generated C++ code. By default the SQL file is generated for
- the MySQL, PostgreSQL, Oracle, and Microsoft SQL Server databases
- and the schema is embedded into the C++ code for the SQLite database.
- Use the \cb{--schema-format} option to alter the default schema format.
-
- If database schema evolution support is enabled (that is, the object
- model version is specified), then this option also triggers the
- generation of database schema migration statements, again either as
- standalong SQL files or embedded into the generated C++ code. You can
- suppress the generation of schema migration statements by specifying
- the \cb{--suppress-migration} option."
- };
-
- bool --generate-schema-only
- {
- "Generate only the database schema. Note that this option is only valid
- when generating schema as a standalone SQL file (see \cb{--schema-format}
- for details)."
- };
-
- bool --suppress-migration
- {
- "Suppress the generation of database schema migration statements."
- };
-
- bool --suppress-schema-version
- {
- "Suppress the generation of schema version table. If you specify this
- option then you are also expected to manually specify the database
- schema version and migration state at runtime using the
- \cb{odb::database::schema_version()} function."
- };
-
- database_map<qname> --schema-version-table
- {
- "<name>",
- "Specify the alternative schema version table name instead of the default
- \cb{schema_version}. If you specify this option then you are also
- expected to manually specify the schema version table name at runtime
- using the \cb{odb::database::schema_version_table()} function. The table
- name can be qualified."
- };
-
- database_map<std::set< ::schema_format> > --schema-format
- {
- "<format>",
- "Generate the database schema in the specified format. Pass \cb{sql} as
- <format> to generate the database schema as a standalone SQL file or
- pass \cb{embedded} to embed the schema into the generated C++ code.
- The \cb{separate} value is similar to \cb{embedded} except the schema
- creation code is generated into a separate C++ file (\cb{name-schema.cxx}
- by default). This value is primarily useful if you want to place the
- schema creation functionality into a separate program or library.
- Repeat this option to generate the same database schema in multiple
- formats."
- };
-
- bool --omit-drop
- {
- "Omit \cb{DROP} statements from the generated database schema."
- };
-
- bool --omit-create
- {
- "Omit \cb{CREATE} statements from the generated database schema."
- };
-
- database_map<std::string> --schema-name
- {
- "<name>",
- "Use <name> as the database schema name. Schema names are primarily
- used to distinguish between multiple embedded schemas in the schema
- catalog. They are not to be confused with database schemas (database
- namespaces) which are specified with the \cb{--schema} option. If
- this option is not specified, the empty name, which is the default
- schema name, is used."
- };
-
- database_map<deferrable> --fkeys-deferrable-mode
- {
- "<m>",
- "Use constraint checking mode <m> in foreign keys generated for object
- relationships. Valid values for this option are \cb{not_deferrable},
- \cb{immediate}, and \cb{deferred} (default). MySQL and SQL Server do
- not support deferrable foreign keys and for these databases such keys
- are generated commented out. Other foreign keys generated by the ODB
- compiler (such as the ones used to support containers and polymorphic
- hierarchies) are always generated as not deferrable.
-
- Note also that if you use either \cb{not_deferrable} or \cb{immediate}
- mode, then the order in which you persist, update, and erase objects
- within a transaction becomes important."
- };
-
- std::string --default-pointer = "*"
- {
- "<ptr>",
- "Use <ptr> as the default pointer for persistent objects and views.
- Objects and views that do not have a pointer assigned with the
- \cb{db pointer} pragma will use this pointer by default. The value
- of this option can be '\cb{*}' which denotes the raw pointer and is
- the default, or qualified name of a smart pointer class template,
- for example, \cb{std::shared_ptr}. In the latter case, the ODB compiler
- constructs the object or view pointer by adding a single template
- argument of the object or view type to the qualified name, for example
- \cb{std::shared_ptr<object>}. The ODB runtime uses object and view
- pointers to return, and, in case of objects, pass and cache
- dynamically allocated instances of object and view types.
-
- Except for the raw pointer and the standard smart pointers defined
- in the \cb{<memory>} header file, you are expected to include the
- definition of the default pointer at the beginning of the generated
- header file. There are two common ways to achieve this: you can either
- include the necessary header in the file being compiled or you can use
- the \cb{--hxx-prologue} option to add the necessary \cb{#include}
- directive to the generated code."
- };
-
- std::string --session-type = "odb::session"
- {
- "<type>",
- "Use <type> as the alternative session type instead of the default
- \cb{odb::session}. This option can be used to specify a custom
- session implementation to be use by the persistent classes. Note
- that you will also need to include the definition of the custom
- session type into the generated header file. This is normally
- achieved with the \cb{--hxx-prologue*} options."
- };
-
- // The following option is "fake" in that it is actually handled by
- // argv_file_scanner. We have it here to get the documentation.
- //
- std::string --profile | -p
- {
- "<name>",
- "Specify a profile that should be used during compilation. A
- profile is an options file. The ODB compiler first looks for
- a database-specific version with the name constructed by appending
- the \cb{-}\ci{database}\cb{.options} suffix to <name>, where
- \ci{database} is the database name as specified with the
- \cb{--database} option. If this file is not found, then the
- ODB compiler looks for a database-independant version with the
- name constructed by appending just the \cb{.options} suffix.
-
- The profile options files are searched for in the same set of
- directories as C++ headers included with the \cb{#include <...>}
- directive (built-in paths plus those specified with the \cb{-I}
- options). The options file is first searched for in the directory
- itself and then in its \cb{odb/} subdirectory.
-
- For the format of the options file refer to the \cb{--options-file}
- option below. You can repeat this option to specify more than one
- profile."
- };
-
- bool --at-once
- {
- "Generate code for all the input files as well as for all the files that
- they include at once. The result is a single set of source/schema files
- that contain all the generated code. If more than one input file is
- specified together with this option, then the \cb{--input-name} option
- must also be specified in order to provide the base name for the output
- files. In this case, the directory part of such a base name is used as
- the location of the combined file. This can be important for the
- \cb{#include} directive resolution."
- };
-
- database_map<qname> --schema
- {
- "<schema>",
- "Specify a database schema (database namespace) that should be
- assigned to the persistent classes in the file being compiled.
- Database schemas are not to be confused with database schema
- names (schema catalog names) which are specified with the
- \cb{--schema-name} option."
- };
-
- // Export control.
- //
- database_map<std::string> --export-symbol
- {
- "<symbol>",
- "Insert <symbol> in places where DLL export/import control statements
- (\cb{__declspec(dllexport/dllimport)}) are necessary. See also the
- \cb{--extern-symbol} option below."
- };
-
- database_map<std::string> --extern-symbol
- {
- "<symbol>",
- "If <symbol> is defined, insert it in places where a template
- instantiation must be declared \cb{extern}. This option is normally
- used together with \cb{--export-symbol} when both multi-database
- support and queries are enabled."
- };
-
- // Language.
- //
- 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}."
- };
-
- // Diagnostics.
- //
- bool --warn-hard-add
- {
- "Warn about hard-added data members."
- };
-
- bool --warn-hard-delete
- {
- "Warn about hard-deleted data members and persistent classes."
- };
-
- bool --warn-hard
- {
- "Warn about both hard-added and hard-deleted data members and
- persistent classes."
- };
-
- // Output.
- //
- std::string --output-dir | -o
- {
- "<dir>",
- "Write the generated files to <dir> instead of the current directory."
- };
-
- std::string --input-name
- {
- "<name>",
- "Use <name> instead of the input file to derive the names of the
- generated files. If the \cb{--at-once} option is specified, then
- the directory part of <name> is used as the location of the
- combined file. Refer to the \cb{--at-once} option for details."
- };
-
- database_map<std::string> --changelog
- {
- "<file>",
- "Read/write changelog from/to <file> instead of the default changelog
- file. The default changelog file name is derived from the input file
- name and it is placed into the same directory as the input file. Note
- that the \cb{--output-dir} option does not affect the changelog file
- location. In other words, by default, the changelog file is treated
- as another input rather than output even though the ODB compiler may
- modify it. Use the \cb{--changelog-in} and \cb{--changelog-out}
- options to specify different input and output chaneglog files."
- };
-
- database_map<std::string> --changelog-in
- {
- "<file>",
- "Read changelog from <file> instead of the default changelog file. If
- this option is specified, then you must also specify the output
- chanegelog file with \cb{--changelog-out}."
- };
-
- database_map<std::string> --changelog-out
- {
- "<file>",
- "Write changelog to <file> instead of the default changelog file. If
- this option is specified, then you must also specify the input
- chanegelog file with \cb{--changelog-in}."
- };
-
- database_map<std::string> --changelog-dir
- {
- "<dir>",
- "Use <dir> instead of the input file directory as the changelog file
- directory. This directory is also added to changelog files specified
- with the \cb{--changelog}, \cb{--changelog-in}, and \cb{--changelog-in}
- options unless they are absolute paths."
- };
-
- bool --init-changelog
- {
- "Force re-initialization of the changelog even if one exists (all the
- existing change history will be lost). This option is primarily useful
- for automated testing."
- };
-
- database_map<std::string> --odb-file-suffix
- {
- "<suffix>",
- "Use <suffix> to construct the names of the generated C++ files. In
- the single-database mode the default value for this option is \cb{-odb}.
- In the multi-database mode it is \cb{-odb} for the files corresponding
- to the \cb{common} database and \c{\b{-odb-}\i{db}} (where \ci{db} is
- the database name) for other databases."
- };
-
- database_map<std::string> --sql-file-suffix
- {
- "<suffix>",
- "Use <suffix> to construct the name of the generated schema SQL file.
- In the single-database mode by default no suffix is used. In the
- multi-database mode the default value for this option is
- \c{\b{-}\i{db}} (where \ci{db} is the database name)."
- };
-
- database_map<std::string> --schema-file-suffix
- {
- "<suffix>",
- "Use <suffix> to construct the name of the generated schema C++ source
- file. In the single-database mode the default value for this option is
- \cb{-schema}. In the multi-database mode it is \c{\b{-schema-}\i{db}}
- (where \ci{db} is the database name). See the \cb{--schema-format}
- option for details."
- };
-
- database_map<std::string> --changelog-file-suffix
- {
- "<sfx>",
- "Use <sfx> to construct the name of the changelog file. In the
- single-database mode by default no suffix is used. In the
- multi-database mode the default value for this option is
- \c{\b{-}\i{db}} (where \ci{db} is the database name)."
- };
-
- std::string --hxx-suffix = ".hxx"
- {
- "<suffix>",
- "Use <suffix> instead of the default \cb{.hxx} to construct the name of
- the generated C++ header file."
- };
-
- std::string --ixx-suffix = ".ixx"
- {
- "<suffix>",
- "Use <suffix> instead of the default \cb{.ixx} to construct the name of
- the generated C++ inline file."
- };
-
- std::string --cxx-suffix = ".cxx"
- {
- "<suffix>",
- "Use <suffix> instead of the default \cb{.cxx} to construct the name of
- the generated C++ source file."
- };
-
- std::string --sql-suffix = ".sql"
- {
- "<suffix>",
- "Use <suffix> instead of the default \cb{.sql} to construct the name of
- the generated database schema file."
- };
-
- std::string --changelog-suffix = ".xml"
- {
- "<suffix>",
- "Use <suffix> instead of the default \cb{.xml} to construct the name of
- the changelog file."
- };
-
- // Prologues.
- //
- database_map<std::vector<std::string> > --hxx-prologue
- {
- "<text>",
- "Insert <text> at the beginning of the generated C++ header file."
- };
-
- database_map<std::vector<std::string> > --ixx-prologue
- {
- "<text>",
- "Insert <text> at the beginning of the generated C++ inline file."
- };
-
- database_map<std::vector<std::string> > --cxx-prologue
- {
- "<text>",
- "Insert <text> at the beginning of the generated C++ source file."
- };
-
- database_map<std::vector<std::string> > --schema-prologue
- {
- "<text>",
- "Insert <text> at the beginning of the generated schema C++ source file."
- };
-
- database_map<std::vector<std::string> > --sql-prologue
- {
- "<text>",
- "Insert <text> at the beginning of the generated database schema file."
- };
-
- database_map<std::vector<std::string> > --migration-prologue
- {
- "<text>",
- "Insert <text> at the beginning of the generated database migration file."
- };
-
- // Interludes.
- //
- database_map<std::vector<std::string> > --sql-interlude
- {
- "<text>",
- "Insert <text> after all the \cb{DROP} and before any \cb{CREATE}
- statements in the generated database schema file."
- };
-
- // Epilogues.
- //
- database_map<std::vector<std::string> > --hxx-epilogue
- {
- "<text>",
- "Insert <text> at the end of the generated C++ header file."
- };
-
- database_map<std::vector<std::string> > --ixx-epilogue
- {
- "<text>",
- "Insert <text> at the end of the generated C++ inline file."
- };
-
- database_map<std::vector<std::string> > --cxx-epilogue
- {
- "<text>",
- "Insert <text> at the end of the generated C++ source file."
- };
-
- database_map<std::vector<std::string> > --schema-epilogue
- {
- "<text>",
- "Insert <text> at the end of the generated schema C++ source file."
- };
-
- database_map<std::vector<std::string> > --sql-epilogue
- {
- "<text>",
- "Insert <text> at the end of the generated database schema file."
- };
-
- database_map<std::vector<std::string> > --migration-epilogue
- {
- "<text>",
- "Insert <text> at the end of the generated database migration file."
- };
-
- // Prologue files.
- //
- database_map<std::vector<std::string> > --hxx-prologue-file
- {
- "<file>",
- "Insert the content of <file> at the beginning of the generated C++
- header file."
- };
-
- database_map<std::vector<std::string> > --ixx-prologue-file
- {
- "<file>",
- "Insert the content of <file> at the beginning of the generated C++
- inline file."
- };
-
- database_map<std::vector<std::string> > --cxx-prologue-file
- {
- "<file>",
- "Insert the content of <file> at the beginning of the generated C++
- source file."
- };
-
- database_map<std::vector<std::string> > --schema-prologue-file
- {
- "<file>",
- "Insert the content of <file> at the beginning of the generated schema
- C++ source file."
- };
-
- database_map<std::vector<std::string> > --sql-prologue-file
- {
- "<file>",
- "Insert the content of <file> at the beginning of the generated
- database schema file."
- };
-
- database_map<std::vector<std::string> > --migration-prologue-file
- {
- "<f>",
- "Insert the content of file <f> at the beginning of the generated database
- migration file."
- };
-
- // Interlude files.
- //
- database_map<std::vector<std::string> > --sql-interlude-file
- {
- "<file>",
- "Insert the content of <file> after all the \cb{DROP} and before any
- \cb{CREATE} statements in the generated database schema file."
- };
-
- // Epilogue files.
- //
- database_map<std::vector<std::string> > --hxx-epilogue-file
- {
- "<file>",
- "Insert the content of <file> at the end of the generated C++ header
- file."
- };
-
- database_map<std::vector<std::string> > --ixx-epilogue-file
- {
- "<file>",
- "Insert the content of <file> at the end of the generated C++ inline
- file."
- };
-
- database_map<std::vector<std::string> > --cxx-epilogue-file
- {
- "<file>",
- "Insert the content of <file> at the end of the generated C++ source
- file."
- };
-
- database_map<std::vector<std::string> > --schema-epilogue-file
- {
- "<file>",
- "Insert the content of <file> at the end of the generated schema C++
- source file."
- };
-
- database_map<std::vector<std::string> > --sql-epilogue-file
- {
- "<file>",
- "Insert the content of <file> at the end of the generated database
- schema file."
- };
-
- database_map<std::vector<std::string> > --migration-epilogue-file
- {
- "<f>",
- "Insert the content of file <f> at the end of the generated database
- migration file."
- };
-
- // ODB compilation prologue/epilogue.
- //
- database_map<std::vector<std::string> > --odb-prologue
- {
- "<text>",
- "Compile <text> before the input header file. This option allows you
- to add additional declarations, such as custom traits specializations,
- to the ODB compilation process."
- };
-
- database_map<std::vector<std::string> > --odb-prologue-file
- {
- "<file>",
- "Compile <file> contents before the input header file. Prologue files
- are compiled after all the prologue text fragments (\cb{--odb-prologue}
- option)."
- };
-
- database_map<std::vector<std::string> > --odb-epilogue
- {
- "<text>",
- "Compile <text> after the input header file. This option allows you
- to add additional declarations, such as custom traits specializations,
- to the ODB compilation process."
- };
-
- database_map<std::vector<std::string> > --odb-epilogue-file
- {
- "<file>",
- "Compile <file> contents after the input header file. Epilogue files
- are compiled after all the epilogue text fragments (\cb{--odb-epilogue}
- option)."
- };
-
- // SQL names.
- //
- database_map<std::string> --table-prefix
- {
- "<prefix>",
- "Add <prefix> to table names and, for databases that have global index
- and/or foreign key names, to those names as well. The prefix is added to
- both names that were specified with the \cb{db table} and \cb{db index}
- pragmas and those that were automatically derived from class and data
- member names. If you require a separator, such as an underscore,
- between the prefix and the name, then you should include it into the
- prefix value."
- };
-
- database_map<std::string> --index-suffix
- {
- "<suffix>",
- "Use <suffix> instead of the default \cb{_i} to construct index names.
- The suffix is only added to names that were automatically derived from
- data member names. If you require a separator, such as an underscore,
- between the name and the suffix, then you should include it into the
- suffix value."
- };
-
- database_map<std::string> --fkey-suffix
- {
- "<suffix>",
- "Use <suffix> instead of the default \cb{_fk} to construct foreign key
- names. If you require a separator, such as an underscore, between the
- name and the suffix, then you should include it into the suffix value."
- };
-
- database_map<std::string> --sequence-suffix
- {
- "<suffix>",
- "Use <suffix> instead of the default \cb{_seq} to construct sequence
- names. If you require a separator, such as an underscore, between the
- name and the suffix, then you should include it into the suffix value."
- };
-
- database_map<name_case> --sql-name-case
- {
- "<case>",
- "Convert all automatically-derived SQL names to upper or lower case.
- Valid values for this option are \cb{upper} and \cb{lower}."
- };
-
- database_map<std::vector<std::string> > --table-regex
- {
- "<regex>",
- "Add <regex> to the list of regular expressions that is used to
- transform automatically-derived table names. See the SQL NAME
- TRANSFORMATIONS section below for details."
- };
-
- database_map<std::vector<std::string> > --column-regex
- {
- "<regex>",
- "Add <regex> to the list of regular expressions that is used to
- transform automatically-derived column names. See the SQL NAME
- TRANSFORMATIONS section below for details."
- };
-
- database_map<std::vector<std::string> > --index-regex
- {
- "<regex>",
- "Add <regex> to the list of regular expressions that is used to
- transform automatically-derived index names. See the SQL NAME
- TRANSFORMATIONS section below for details."
- };
-
- database_map<std::vector<std::string> > --fkey-regex
- {
- "<regex>",
- "Add <regex> to the list of regular expressions that is used to
- transform automatically-derived foreign key names. See the SQL NAME
- TRANSFORMATIONS section below for details."
- };
-
- database_map<std::vector<std::string> > --sequence-regex
- {
- "<regex>",
- "Add <regex> to the list of regular expressions that is used to
- transform automatically-derived sequence names. See the SQL NAME
- TRANSFORMATIONS section below for details."
- };
-
- database_map<std::vector<std::string> > --statement-regex
- {
- "<regex>",
- "Add <regex> to the list of regular expressions that is used to
- transform automatically-derived prepared statement names. See
- the SQL NAME TRANSFORMATIONS section below for details."
- };
-
- database_map<std::vector<std::string> > --sql-name-regex
- {
- "<regex>",
- "Add <regex> to the list of regular expressions that is used to
- transform all automatically-derived SQL names. See the SQL NAME
- TRANSFORMATIONS section below for details."
- };
-
- bool --sql-name-regex-trace
- {
- "Trace the process of applying regular expressions specified with the
- SQL name \cb{--*-regex} options. Use this option to find out why your
- regular expressions don't do what you expected them to do."
- };
-
- // Accessor/modifier options.
- //
- std::vector<std::string> --accessor-regex
- {
- "<regex>",
- "Add <regex> to the list of regular expressions used to transform
- data member names to function names when searching for a suitable
- accessor function. The argument to this option is a Perl-like regular
- expression in the form \c{\b{/}\i{pattern}\b{/}\i{replacement}\b{/}}.
- Any character can be used as a delimiter instead of '\cb{/}' and the
- delimiter can be escaped inside \ci{pattern} and \ci{replacement}
- with a backslash (\cb{\\}). You can specify multiple regular
- expressions by repeating this option.
-
- All the regular expressions are tried in the order specified and
- the first expression that produces a suitable accessor function is
- used. Each expression is tried twice: first with the actual member
- name and then with the member's \i{public name} which is obtained by
- removing the common member name decorations, such as leading and
- trailing underscores, the \cb{m_} prefix, etc. The ODB compiler also
- includes a number of built-in expressions for commonly used accessor
- names, such as \cb{get_foo}, \cb{getFoo}, \cb{getfoo}, and just
- \cb{foo}. The built-in expressions are tried last.
-
- As an example, the following expression transforms data members with
- public names in the form \cb{foo} to accessor names in the form
- \cb{GetFoo}:
-
- \cb{/(.+)/Get\\u$1/}
-
- See also the REGEX AND SHELL QUOTING section below."
- };
-
- bool --accessor-regex-trace
- {
- "Trace the process of applying regular expressions specified with the
- \cb{--accessor-regex} option. Use this option to find out why your
- regular expressions don't do what you expected them to do."
- };
-
- std::vector<std::string> --modifier-regex
- {
- "<regex>",
- "Add <regex> to the list of regular expressions used to transform
- data member names to function names when searching for a suitable
- modifier function. The argument to this option is a Perl-like regular
- expression in the form \c{\b{/}\i{pattern}\b{/}\i{replacement}\b{/}}.
- Any character can be used as a delimiter instead of '\cb{/}' and the
- delimiter can be escaped inside \ci{pattern} and \ci{replacement}
- with a backslash (\cb{\\}). You can specify multiple regular
- expressions by repeating this option.
-
- All the regular expressions are tried in the order specified and
- the first expression that produces a suitable modifier function is
- used. Each expression is tried twice: first with the actual member
- name and then with the member's \i{public name} which is obtained by
- removing the common member name decorations, such as leading and
- trailing underscores, the \cb{m_} prefix, etc. The ODB compiler also
- includes a number of built-in expressions for commonly used modifier
- names, such as \cb{set_foo}, \cb{setFoo}, \cb{setfoo}, and just
- \cb{foo}. The built-in expressions are tried last.
-
- As an example, the following expression transforms data members with
- public names in the form \cb{foo} to modifier names in the form
- \cb{SetFoo}:
-
- \cb{/(.+)/Set\\u$1/}
-
- See also the REGEX AND SHELL QUOTING section below."
- };
-
- bool --modifier-regex-trace
- {
- "Trace the process of applying regular expressions specified with the
- \cb{--modifier-regex} option. Use this option to find out why your
- regular expressions don't do what you expected them to do."
- };
-
- // Include options.
- //
- bool --include-with-brackets
- {
- "Use angle brackets (<>) instead of quotes (\"\") in the generated
- \cb{#include} directives."
- };
-
- std::string --include-prefix
- {
- "<prefix>",
- "Add <prefix> to the generated \cb{#include} directive paths."
- };
-
- std::vector<std::string> --include-regex
- {
- "<regex>",
- "Add <regex> to the list of regular expressions used to transform
- generated \cb{#include} directive paths. The argument to this option
- is a Perl-like regular expression in the form
- \c{\b{/}\i{pattern}\b{/}\i{replacement}\b{/}}. Any character can be
- used as a delimiter instead of '\cb{/}' and the delimiter can be escaped
- inside \ci{pattern} and \ci{replacement} with a backslash (\cb{\\}).
- You can specify multiple regular expressions by repeating this option.
- All the regular expressions are tried in the order specified and the
- first expression that matches is used.
-
- As an example, the following expression transforms include paths in
- the form \cb{foo/bar-odb.h} to paths in the form
- \cb{foo/generated/bar-odb.h}:
-
- \cb{%foo/(.+)-odb.h%foo/generated/$1-odb.h%}
-
- See also the REGEX AND SHELL QUOTING section below."
- };
-
- bool --include-regex-trace
- {
- "Trace the process of applying regular expressions specified with the
- \cb{--include-regex} option. Use this option to find out why your
- regular expressions don't do what you expected them to do."
- };
-
- std::string --guard-prefix
- {
- "<prefix>",
- "Add <prefix> to the generated header inclusion guards. The prefix is
- transformed to upper case and characters that are illegal in a
- preprocessor macro name are replaced with underscores."
- };
-
- bool --show-sloc
- {
- "Print the number of generated physical source lines of code (SLOC)."
- };
-
- std::size_t --sloc-limit
- {
- "<num>",
- "Check that the number of generated physical source lines of code (SLOC)
- does not exceed <num>."
- };
-
- // The following option is "fake" in that it is actually handled by
- // argv_file_scanner. We have it here to get the documentation.
- //
- std::string --options-file
- {
- "<file>",
- "Read additional options from <file>. Each option should appear on a
- separate line optionally followed by space or equal sign (\cb{=}) and an
- option value. Empty lines and lines starting with \cb{#} are ignored.
- Option values can be enclosed in double (\cb{\"}) or single (\cb{'})
- quotes to preserve leading and trailing whitespaces as well as to specify
- empty values. If the value itself contains trailing or leading quotes,
- enclose it with an extra pair of quotes, for example \cb{'\"x\"'}.
- Non-leading and non-trailing quotes are interpreted as being part of the
- option value.
-
- The semantics of providing options in a file is equivalent to providing
- the same set of options in the same order on the command line at the
- point where the \cb{--options-file} option is specified except that
- the shell escaping and quoting is not required. Repeat this option
- to specify more than one options file."
- };
-
- std::vector<std::string> -x
- {
- "<option>",
- "Pass <option> to the underlying C++ compiler (\cb{g++}). The <option>
- value that doesn't start with '\cb{-}' is considered the \cb{g++}
- executable name."
- };
-
- bool -v {"Print the commands executed to run the stages of compilation."};
-
- bool --trace {"Trace the compilation process."};
-
- //
- // MySQL-specific options.
- //
-
- std::string --mysql-engine = "InnoDB"
- {
- "<engine>",
- "Use <engine> instead of the default \cb{InnoDB} in the generated
- database schema file. For more information on the storage engine
- options see the MySQL documentation. If you would like to use the
- database-default engine, pass \cb{default} as the value for this
- option."
- };
-
- //
- // SQLite-specific options.
- //
-
- bool --sqlite-override-null
- {
- "Make all columns in the generated database schema allow \cb{NULL}
- values. This is primarily useful in schema migration since SQLite
- does not support dropping of columns. By making all columns \cb{NULL}
- we can later \"delete\" them by setting their values to \cb{NULL}.
- Note that this option overrides even the \cb{not_null} pragma."
- };
-
- bool --sqlite-lax-auto-id
- {
- "Do not force monotonically increasing automatically-assigned
- object ids. In this mode the generated database schema omits the
- \cb{AUTOINCREMENT} keyword which results in faster object persistence
- but may lead to automatically-assigned ids not being in a strictly
- ascending order. Refer to the SQLite documentation for details."
- };
-
- //
- // PostgreSQL-specific options.
- //
-
- ::pgsql_version --pgsql-server-version (7, 4)
- {
- "<ver>",
- "Specify the minimum PostgreSQL server version with which the generated
- C++ code and schema will be used. This information is used to enable
- version-specific optimizations and workarounds in the generated C++
- code and schema. The version must be in the \c{\i{major}\b{.}\i{minor}}
- form, for example, \cb{9.1}. If this option is not specified, then
- \cb{7.4} or later is assumed."
- };
-
- //
- // Oracle-specific options.
- //
-
- ::oracle_version --oracle-client-version (10, 1)
- {
- "<ver>",
- "Specify the minimum Oracle client library (OCI) version with which the
- generated C++ code will be linked. This information is used to enable
- version-specific optimizations and workarounds in the generated C++
- code. The version must be in the \c{\i{major}\b{.}\i{minor}} form,
- for example, \cb{11.2}. If this option is not specified, then
- \cb{10.1} or later is assumed."
- };
-
- bool --oracle-warn-truncation
- {
- "Warn about SQL names that are longer than 30 characters and are
- therefore truncated. Note that during database schema generation
- (\cb{--generate-schema}) ODB detects when such truncations lead
- to name conflicts and issues diagnostics even without this option
- specified."
- };
-
- //
- // SQL Server-specific options.
- //
-
- ::mssql_version --mssql-server-version (10, 0)
- {
- "<ver>",
- "Specify the minimum SQL Server server version with which the generated
- C++ code and schema will be used. This information is used to enable
- version-specific optimizations and workarounds in the generated C++
- code and schema. The version must be in the \c{\i{major}\b{.}\i{minor}}
- form, for example, \cb{9.0} (SQL Server 2005), \cb{10.5} (2008R2), or
- \cb{11.0} (2012). If this option is not specified, then \cb{10.0} (SQL
- Server 2008) or later is assumed."
- };
-
- unsigned int --mssql-short-limit = 1024
- {
- "<size>",
- "Specify the short data size limit. If a character, national character, or
- binary data type has a maximum length (in bytes) less than or equal to
- this limit, then it is treated as \i{short data}, otherwise it is \i{long
- data}. For short data ODB pre-allocates an intermediate buffer of
- the maximum size and binds it directly to a parameter or result
- column. This way the underlying API (ODBC) can read/write directly
- from/to this buffer. In the case of long data, the data is read/written
- in chunks using the \cb{SQLGetData()}/\cb{SQLPutData()} ODBC functions.
- While the long data approach reduces the amount of memory used by the
- application, it may require greater CPU resources. The default short
- data limit is 1024 bytes. When setting a custom short data limit, make
- sure that it is sufficiently large so that no object id in the
- application is treated as long data."
- };
-};
diff --git a/odb/options.cxx b/odb/options.cxx
deleted file mode 100644
index ec6cd7f..0000000
--- a/odb/options.cxx
+++ /dev/null
@@ -1,3907 +0,0 @@
-// -*- C++ -*-
-//
-// This file was generated by CLI, a command line interface
-// compiler for C++.
-//
-
-// Begin prologue.
-//
-#include <odb/option-parsers.hxx>
-//
-// End prologue.
-
-#include <odb/options.hxx>
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-#include <ostream>
-#include <sstream>
-#include <cstring>
-#include <fstream>
-
-namespace cli
-{
- // unknown_option
- //
- unknown_option::
- ~unknown_option () throw ()
- {
- }
-
- void unknown_option::
- print (::std::ostream& os) const
- {
- os << "unknown option '" << option ().c_str () << "'";
- }
-
- const char* unknown_option::
- what () const throw ()
- {
- return "unknown option";
- }
-
- // unknown_argument
- //
- unknown_argument::
- ~unknown_argument () throw ()
- {
- }
-
- void unknown_argument::
- print (::std::ostream& os) const
- {
- os << "unknown argument '" << argument ().c_str () << "'";
- }
-
- const char* unknown_argument::
- what () const throw ()
- {
- return "unknown argument";
- }
-
- // missing_value
- //
- missing_value::
- ~missing_value () throw ()
- {
- }
-
- void missing_value::
- print (::std::ostream& os) const
- {
- os << "missing value for option '" << option ().c_str () << "'";
- }
-
- const char* missing_value::
- what () const throw ()
- {
- return "missing option value";
- }
-
- // invalid_value
- //
- invalid_value::
- ~invalid_value () throw ()
- {
- }
-
- void invalid_value::
- print (::std::ostream& os) const
- {
- os << "invalid value '" << value ().c_str () << "' for option '"
- << option ().c_str () << "'";
-
- if (!message ().empty ())
- os << ": " << message ().c_str ();
- }
-
- const char* invalid_value::
- what () const throw ()
- {
- return "invalid option value";
- }
-
- // eos_reached
- //
- void eos_reached::
- print (::std::ostream& os) const
- {
- os << what ();
- }
-
- const char* eos_reached::
- what () const throw ()
- {
- return "end of argument stream reached";
- }
-
- // file_io_failure
- //
- file_io_failure::
- ~file_io_failure () throw ()
- {
- }
-
- void file_io_failure::
- print (::std::ostream& os) const
- {
- os << "unable to open file '" << file ().c_str () << "' or read failure";
- }
-
- const char* file_io_failure::
- what () const throw ()
- {
- return "unable to open file or read failure";
- }
-
- // unmatched_quote
- //
- unmatched_quote::
- ~unmatched_quote () throw ()
- {
- }
-
- void unmatched_quote::
- print (::std::ostream& os) const
- {
- os << "unmatched quote in argument '" << argument ().c_str () << "'";
- }
-
- const char* unmatched_quote::
- what () const throw ()
- {
- return "unmatched quote";
- }
-
- // scanner
- //
- scanner::
- ~scanner ()
- {
- }
-
- // argv_scanner
- //
- bool argv_scanner::
- more ()
- {
- return i_ < argc_;
- }
-
- const char* argv_scanner::
- peek ()
- {
- if (i_ < argc_)
- return argv_[i_];
- else
- throw eos_reached ();
- }
-
- const char* argv_scanner::
- next ()
- {
- if (i_ < argc_)
- {
- const char* r (argv_[i_]);
-
- if (erase_)
- {
- for (int i (i_ + 1); i < argc_; ++i)
- argv_[i - 1] = argv_[i];
-
- --argc_;
- argv_[argc_] = 0;
- }
- else
- ++i_;
-
- return r;
- }
- else
- throw eos_reached ();
- }
-
- void argv_scanner::
- skip ()
- {
- if (i_ < argc_)
- ++i_;
- else
- throw eos_reached ();
- }
-
- // argv_file_scanner
- //
- int argv_file_scanner::zero_argc_ = 0;
- std::string argv_file_scanner::empty_string_;
-
- bool argv_file_scanner::
- more ()
- {
- if (!args_.empty ())
- return true;
-
- while (base::more ())
- {
- // See if the next argument is the file option.
- //
- const char* a (base::peek ());
- const option_info* oi = 0;
- const char* ov = 0;
-
- if (!skip_)
- {
- if ((oi = find (a)) != 0)
- {
- base::next ();
-
- if (!base::more ())
- throw missing_value (a);
-
- ov = base::next ();
- }
- else if (std::strncmp (a, "-", 1) == 0)
- {
- if ((ov = std::strchr (a, '=')) != 0)
- {
- std::string o (a, 0, ov - a);
- if ((oi = find (o.c_str ())) != 0)
- {
- base::next ();
- ++ov;
- }
- }
- }
- }
-
- if (oi != 0)
- {
- if (oi->search_func != 0)
- {
- std::string f (oi->search_func (ov, oi->arg));
-
- if (!f.empty ())
- load (f);
- }
- else
- load (ov);
-
- if (!args_.empty ())
- return true;
- }
- else
- {
- if (!skip_)
- skip_ = (std::strcmp (a, "--") == 0);
-
- return true;
- }
- }
-
- return false;
- }
-
- const char* argv_file_scanner::
- peek ()
- {
- if (!more ())
- throw eos_reached ();
-
- return args_.empty () ? base::peek () : args_.front ().value.c_str ();
- }
-
- const std::string& argv_file_scanner::
- peek_file ()
- {
- if (!more ())
- throw eos_reached ();
-
- return args_.empty () ? empty_string_ : *args_.front ().file;
- }
-
- std::size_t argv_file_scanner::
- peek_line ()
- {
- if (!more ())
- throw eos_reached ();
-
- return args_.empty () ? 0 : args_.front ().line;
- }
-
- const char* argv_file_scanner::
- next ()
- {
- if (!more ())
- throw eos_reached ();
-
- if (args_.empty ())
- return base::next ();
- else
- {
- hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value);
- args_.pop_front ();
- return hold_[i_].c_str ();
- }
- }
-
- void argv_file_scanner::
- skip ()
- {
- if (!more ())
- throw eos_reached ();
-
- if (args_.empty ())
- return base::skip ();
- else
- args_.pop_front ();
- }
-
- const argv_file_scanner::option_info* argv_file_scanner::
- find (const char* a) const
- {
- for (std::size_t i (0); i < options_count_; ++i)
- if (std::strcmp (a, options_[i].option) == 0)
- return &options_[i];
-
- return 0;
- }
-
- void argv_file_scanner::
- load (const std::string& file)
- {
- using namespace std;
-
- ifstream is (file.c_str ());
-
- if (!is.is_open ())
- throw file_io_failure (file);
-
- files_.push_back (file);
-
- arg a;
- a.file = &*files_.rbegin ();
-
- for (a.line = 1; !is.eof (); ++a.line)
- {
- string line;
- getline (is, line);
-
- if (is.fail () && !is.eof ())
- throw file_io_failure (file);
-
- string::size_type n (line.size ());
-
- // Trim the line from leading and trailing whitespaces.
- //
- if (n != 0)
- {
- const char* f (line.c_str ());
- const char* l (f + n);
-
- const char* of (f);
- while (f < l && (*f == ' ' || *f == '\t' || *f == '\r'))
- ++f;
-
- --l;
-
- const char* ol (l);
- while (l > f && (*l == ' ' || *l == '\t' || *l == '\r'))
- --l;
-
- if (f != of || l != ol)
- line = f <= l ? string (f, l - f + 1) : string ();
- }
-
- // Ignore empty lines, those that start with #.
- //
- if (line.empty () || line[0] == '#')
- continue;
-
- string::size_type p (string::npos);
- if (line.compare (0, 1, "-") == 0)
- {
- p = line.find (' ');
-
- string::size_type q (line.find ('='));
- if (q != string::npos && q < p)
- p = q;
- }
-
- string s1;
- if (p != string::npos)
- {
- s1.assign (line, 0, p);
-
- // Skip leading whitespaces in the argument.
- //
- if (line[p] == '=')
- ++p;
- else
- {
- n = line.size ();
- for (++p; p < n; ++p)
- {
- char c (line[p]);
- if (c != ' ' && c != '\t' && c != '\r')
- break;
- }
- }
- }
- else if (!skip_)
- skip_ = (line == "--");
-
- string s2 (line, p != string::npos ? p : 0);
-
- // If the string (which is an option value or argument) is
- // wrapped in quotes, remove them.
- //
- n = s2.size ();
- char cf (s2[0]), cl (s2[n - 1]);
-
- if (cf == '"' || cf == '\'' || cl == '"' || cl == '\'')
- {
- if (n == 1 || cf != cl)
- throw unmatched_quote (s2);
-
- s2 = string (s2, 1, n - 2);
- }
-
- if (!s1.empty ())
- {
- // See if this is another file option.
- //
- const option_info* oi;
- if (!skip_ && (oi = find (s1.c_str ())))
- {
- if (s2.empty ())
- throw missing_value (oi->option);
-
- if (oi->search_func != 0)
- {
- std::string f (oi->search_func (s2.c_str (), oi->arg));
- if (!f.empty ())
- load (f);
- }
- else
- load (s2);
-
- continue;
- }
-
- a.value = s1;
- args_.push_back (a);
- }
-
- a.value = s2;
- args_.push_back (a);
- }
- }
-
- void options::
- push_back (const option& o)
- {
- container_type::size_type n (size ());
- container_type::push_back (o);
- map_[o.name ()] = n;
-
- for (option_names::const_iterator i (o.aliases ().begin ());
- i != o.aliases ().end (); ++i)
- map_[*i] = n;
- }
-
- template <typename X>
- struct parser
- {
- static void
- parse (X& x, bool& xs, scanner& s)
- {
- using namespace std;
-
- const char* o (s.next ());
- if (s.more ())
- {
- string v (s.next ());
- istringstream is (v);
- if (!(is >> x && is.peek () == istringstream::traits_type::eof ()))
- throw invalid_value (o, v);
- }
- else
- throw missing_value (o);
-
- xs = true;
- }
- };
-
- template <>
- struct parser<bool>
- {
- static void
- parse (bool& x, scanner& s)
- {
- s.next ();
- x = true;
- }
- };
-
- template <>
- struct parser<std::string>
- {
- static void
- parse (std::string& x, bool& xs, scanner& s)
- {
- const char* o (s.next ());
-
- if (s.more ())
- x = s.next ();
- else
- throw missing_value (o);
-
- xs = true;
- }
- };
-
- template <typename X>
- struct parser<std::vector<X> >
- {
- static void
- parse (std::vector<X>& c, bool& xs, scanner& s)
- {
- X x;
- bool dummy;
- parser<X>::parse (x, dummy, s);
- c.push_back (x);
- xs = true;
- }
- };
-
- template <typename X>
- struct parser<std::set<X> >
- {
- static void
- parse (std::set<X>& c, bool& xs, scanner& s)
- {
- X x;
- bool dummy;
- parser<X>::parse (x, dummy, s);
- c.insert (x);
- xs = true;
- }
- };
-
- template <typename K, typename V>
- struct parser<std::map<K, V> >
- {
- static void
- parse (std::map<K, V>& m, bool& xs, scanner& s)
- {
- const char* o (s.next ());
-
- if (s.more ())
- {
- 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);
- parser<K>::parse (k, dummy, s);
- }
-
- if (!vstr.empty ())
- {
- av[1] = const_cast<char*> (vstr.c_str ());
- argv_scanner s (0, ac, av);
- parser<V>::parse (v, dummy, s);
- }
-
- m[k] = v;
- }
- else
- throw missing_value (o);
-
- xs = true;
- }
- };
-
- template <typename X, typename T, T X::*M>
- void
- thunk (X& x, scanner& s)
- {
- parser<T>::parse (x.*M, s);
- }
-
- template <typename X, typename T, T X::*M, bool X::*S>
- void
- thunk (X& x, scanner& s)
- {
- parser<T>::parse (x.*M, x.*S, s);
- }
-}
-
-#include <map>
-#include <cstring>
-
-// options
-//
-
-options::
-options ()
-: build2_metadata_ (),
- build2_metadata_specified_ (false),
- help_ (),
- version_ (),
- I_ (),
- I_specified_ (false),
- D_ (),
- D_specified_ (false),
- U_ (),
- U_specified_ (false),
- database_ (),
- database_specified_ (false),
- multi_database_ (::multi_database::disabled),
- multi_database_specified_ (false),
- default_database_ (),
- default_database_specified_ (false),
- generate_query_ (),
- generate_prepared_ (),
- omit_unprepared_ (),
- generate_session_ (),
- generate_schema_ (),
- generate_schema_only_ (),
- suppress_migration_ (),
- suppress_schema_version_ (),
- schema_version_table_ (),
- schema_version_table_specified_ (false),
- schema_format_ (),
- schema_format_specified_ (false),
- omit_drop_ (),
- omit_create_ (),
- schema_name_ (),
- schema_name_specified_ (false),
- fkeys_deferrable_mode_ (),
- fkeys_deferrable_mode_specified_ (false),
- default_pointer_ ("*"),
- default_pointer_specified_ (false),
- session_type_ ("odb::session"),
- session_type_specified_ (false),
- profile_ (),
- profile_specified_ (false),
- at_once_ (),
- schema_ (),
- schema_specified_ (false),
- export_symbol_ (),
- export_symbol_specified_ (false),
- extern_symbol_ (),
- extern_symbol_specified_ (false),
- std_ (cxx_version::cxx98),
- std_specified_ (false),
- warn_hard_add_ (),
- warn_hard_delete_ (),
- warn_hard_ (),
- output_dir_ (),
- output_dir_specified_ (false),
- input_name_ (),
- input_name_specified_ (false),
- changelog_ (),
- changelog_specified_ (false),
- changelog_in_ (),
- changelog_in_specified_ (false),
- changelog_out_ (),
- changelog_out_specified_ (false),
- changelog_dir_ (),
- changelog_dir_specified_ (false),
- init_changelog_ (),
- odb_file_suffix_ (),
- odb_file_suffix_specified_ (false),
- sql_file_suffix_ (),
- sql_file_suffix_specified_ (false),
- schema_file_suffix_ (),
- schema_file_suffix_specified_ (false),
- changelog_file_suffix_ (),
- changelog_file_suffix_specified_ (false),
- hxx_suffix_ (".hxx"),
- hxx_suffix_specified_ (false),
- ixx_suffix_ (".ixx"),
- ixx_suffix_specified_ (false),
- cxx_suffix_ (".cxx"),
- cxx_suffix_specified_ (false),
- sql_suffix_ (".sql"),
- sql_suffix_specified_ (false),
- changelog_suffix_ (".xml"),
- changelog_suffix_specified_ (false),
- hxx_prologue_ (),
- hxx_prologue_specified_ (false),
- ixx_prologue_ (),
- ixx_prologue_specified_ (false),
- cxx_prologue_ (),
- cxx_prologue_specified_ (false),
- schema_prologue_ (),
- schema_prologue_specified_ (false),
- sql_prologue_ (),
- sql_prologue_specified_ (false),
- migration_prologue_ (),
- migration_prologue_specified_ (false),
- sql_interlude_ (),
- sql_interlude_specified_ (false),
- hxx_epilogue_ (),
- hxx_epilogue_specified_ (false),
- ixx_epilogue_ (),
- ixx_epilogue_specified_ (false),
- cxx_epilogue_ (),
- cxx_epilogue_specified_ (false),
- schema_epilogue_ (),
- schema_epilogue_specified_ (false),
- sql_epilogue_ (),
- sql_epilogue_specified_ (false),
- migration_epilogue_ (),
- migration_epilogue_specified_ (false),
- hxx_prologue_file_ (),
- hxx_prologue_file_specified_ (false),
- ixx_prologue_file_ (),
- ixx_prologue_file_specified_ (false),
- cxx_prologue_file_ (),
- cxx_prologue_file_specified_ (false),
- schema_prologue_file_ (),
- schema_prologue_file_specified_ (false),
- sql_prologue_file_ (),
- sql_prologue_file_specified_ (false),
- migration_prologue_file_ (),
- migration_prologue_file_specified_ (false),
- sql_interlude_file_ (),
- sql_interlude_file_specified_ (false),
- hxx_epilogue_file_ (),
- hxx_epilogue_file_specified_ (false),
- ixx_epilogue_file_ (),
- ixx_epilogue_file_specified_ (false),
- cxx_epilogue_file_ (),
- cxx_epilogue_file_specified_ (false),
- schema_epilogue_file_ (),
- schema_epilogue_file_specified_ (false),
- sql_epilogue_file_ (),
- sql_epilogue_file_specified_ (false),
- migration_epilogue_file_ (),
- migration_epilogue_file_specified_ (false),
- odb_prologue_ (),
- odb_prologue_specified_ (false),
- odb_prologue_file_ (),
- odb_prologue_file_specified_ (false),
- odb_epilogue_ (),
- odb_epilogue_specified_ (false),
- odb_epilogue_file_ (),
- odb_epilogue_file_specified_ (false),
- table_prefix_ (),
- table_prefix_specified_ (false),
- index_suffix_ (),
- index_suffix_specified_ (false),
- fkey_suffix_ (),
- fkey_suffix_specified_ (false),
- sequence_suffix_ (),
- sequence_suffix_specified_ (false),
- sql_name_case_ (),
- sql_name_case_specified_ (false),
- table_regex_ (),
- table_regex_specified_ (false),
- column_regex_ (),
- column_regex_specified_ (false),
- index_regex_ (),
- index_regex_specified_ (false),
- fkey_regex_ (),
- fkey_regex_specified_ (false),
- sequence_regex_ (),
- sequence_regex_specified_ (false),
- statement_regex_ (),
- statement_regex_specified_ (false),
- sql_name_regex_ (),
- sql_name_regex_specified_ (false),
- sql_name_regex_trace_ (),
- accessor_regex_ (),
- accessor_regex_specified_ (false),
- accessor_regex_trace_ (),
- modifier_regex_ (),
- modifier_regex_specified_ (false),
- modifier_regex_trace_ (),
- include_with_brackets_ (),
- include_prefix_ (),
- include_prefix_specified_ (false),
- include_regex_ (),
- include_regex_specified_ (false),
- include_regex_trace_ (),
- guard_prefix_ (),
- guard_prefix_specified_ (false),
- show_sloc_ (),
- sloc_limit_ (),
- sloc_limit_specified_ (false),
- options_file_ (),
- options_file_specified_ (false),
- x_ (),
- x_specified_ (false),
- v_ (),
- trace_ (),
- mysql_engine_ ("InnoDB"),
- mysql_engine_specified_ (false),
- sqlite_override_null_ (),
- sqlite_lax_auto_id_ (),
- pgsql_server_version_ (7, 4),
- pgsql_server_version_specified_ (false),
- oracle_client_version_ (10, 1),
- oracle_client_version_specified_ (false),
- oracle_warn_truncation_ (),
- mssql_server_version_ (10, 0),
- mssql_server_version_specified_ (false),
- mssql_short_limit_ (1024),
- mssql_short_limit_specified_ (false)
-{
-}
-
-options::
-options (int& argc,
- char** argv,
- bool erase,
- ::cli::unknown_mode opt,
- ::cli::unknown_mode arg)
-: build2_metadata_ (),
- build2_metadata_specified_ (false),
- help_ (),
- version_ (),
- I_ (),
- I_specified_ (false),
- D_ (),
- D_specified_ (false),
- U_ (),
- U_specified_ (false),
- database_ (),
- database_specified_ (false),
- multi_database_ (::multi_database::disabled),
- multi_database_specified_ (false),
- default_database_ (),
- default_database_specified_ (false),
- generate_query_ (),
- generate_prepared_ (),
- omit_unprepared_ (),
- generate_session_ (),
- generate_schema_ (),
- generate_schema_only_ (),
- suppress_migration_ (),
- suppress_schema_version_ (),
- schema_version_table_ (),
- schema_version_table_specified_ (false),
- schema_format_ (),
- schema_format_specified_ (false),
- omit_drop_ (),
- omit_create_ (),
- schema_name_ (),
- schema_name_specified_ (false),
- fkeys_deferrable_mode_ (),
- fkeys_deferrable_mode_specified_ (false),
- default_pointer_ ("*"),
- default_pointer_specified_ (false),
- session_type_ ("odb::session"),
- session_type_specified_ (false),
- profile_ (),
- profile_specified_ (false),
- at_once_ (),
- schema_ (),
- schema_specified_ (false),
- export_symbol_ (),
- export_symbol_specified_ (false),
- extern_symbol_ (),
- extern_symbol_specified_ (false),
- std_ (cxx_version::cxx98),
- std_specified_ (false),
- warn_hard_add_ (),
- warn_hard_delete_ (),
- warn_hard_ (),
- output_dir_ (),
- output_dir_specified_ (false),
- input_name_ (),
- input_name_specified_ (false),
- changelog_ (),
- changelog_specified_ (false),
- changelog_in_ (),
- changelog_in_specified_ (false),
- changelog_out_ (),
- changelog_out_specified_ (false),
- changelog_dir_ (),
- changelog_dir_specified_ (false),
- init_changelog_ (),
- odb_file_suffix_ (),
- odb_file_suffix_specified_ (false),
- sql_file_suffix_ (),
- sql_file_suffix_specified_ (false),
- schema_file_suffix_ (),
- schema_file_suffix_specified_ (false),
- changelog_file_suffix_ (),
- changelog_file_suffix_specified_ (false),
- hxx_suffix_ (".hxx"),
- hxx_suffix_specified_ (false),
- ixx_suffix_ (".ixx"),
- ixx_suffix_specified_ (false),
- cxx_suffix_ (".cxx"),
- cxx_suffix_specified_ (false),
- sql_suffix_ (".sql"),
- sql_suffix_specified_ (false),
- changelog_suffix_ (".xml"),
- changelog_suffix_specified_ (false),
- hxx_prologue_ (),
- hxx_prologue_specified_ (false),
- ixx_prologue_ (),
- ixx_prologue_specified_ (false),
- cxx_prologue_ (),
- cxx_prologue_specified_ (false),
- schema_prologue_ (),
- schema_prologue_specified_ (false),
- sql_prologue_ (),
- sql_prologue_specified_ (false),
- migration_prologue_ (),
- migration_prologue_specified_ (false),
- sql_interlude_ (),
- sql_interlude_specified_ (false),
- hxx_epilogue_ (),
- hxx_epilogue_specified_ (false),
- ixx_epilogue_ (),
- ixx_epilogue_specified_ (false),
- cxx_epilogue_ (),
- cxx_epilogue_specified_ (false),
- schema_epilogue_ (),
- schema_epilogue_specified_ (false),
- sql_epilogue_ (),
- sql_epilogue_specified_ (false),
- migration_epilogue_ (),
- migration_epilogue_specified_ (false),
- hxx_prologue_file_ (),
- hxx_prologue_file_specified_ (false),
- ixx_prologue_file_ (),
- ixx_prologue_file_specified_ (false),
- cxx_prologue_file_ (),
- cxx_prologue_file_specified_ (false),
- schema_prologue_file_ (),
- schema_prologue_file_specified_ (false),
- sql_prologue_file_ (),
- sql_prologue_file_specified_ (false),
- migration_prologue_file_ (),
- migration_prologue_file_specified_ (false),
- sql_interlude_file_ (),
- sql_interlude_file_specified_ (false),
- hxx_epilogue_file_ (),
- hxx_epilogue_file_specified_ (false),
- ixx_epilogue_file_ (),
- ixx_epilogue_file_specified_ (false),
- cxx_epilogue_file_ (),
- cxx_epilogue_file_specified_ (false),
- schema_epilogue_file_ (),
- schema_epilogue_file_specified_ (false),
- sql_epilogue_file_ (),
- sql_epilogue_file_specified_ (false),
- migration_epilogue_file_ (),
- migration_epilogue_file_specified_ (false),
- odb_prologue_ (),
- odb_prologue_specified_ (false),
- odb_prologue_file_ (),
- odb_prologue_file_specified_ (false),
- odb_epilogue_ (),
- odb_epilogue_specified_ (false),
- odb_epilogue_file_ (),
- odb_epilogue_file_specified_ (false),
- table_prefix_ (),
- table_prefix_specified_ (false),
- index_suffix_ (),
- index_suffix_specified_ (false),
- fkey_suffix_ (),
- fkey_suffix_specified_ (false),
- sequence_suffix_ (),
- sequence_suffix_specified_ (false),
- sql_name_case_ (),
- sql_name_case_specified_ (false),
- table_regex_ (),
- table_regex_specified_ (false),
- column_regex_ (),
- column_regex_specified_ (false),
- index_regex_ (),
- index_regex_specified_ (false),
- fkey_regex_ (),
- fkey_regex_specified_ (false),
- sequence_regex_ (),
- sequence_regex_specified_ (false),
- statement_regex_ (),
- statement_regex_specified_ (false),
- sql_name_regex_ (),
- sql_name_regex_specified_ (false),
- sql_name_regex_trace_ (),
- accessor_regex_ (),
- accessor_regex_specified_ (false),
- accessor_regex_trace_ (),
- modifier_regex_ (),
- modifier_regex_specified_ (false),
- modifier_regex_trace_ (),
- include_with_brackets_ (),
- include_prefix_ (),
- include_prefix_specified_ (false),
- include_regex_ (),
- include_regex_specified_ (false),
- include_regex_trace_ (),
- guard_prefix_ (),
- guard_prefix_specified_ (false),
- show_sloc_ (),
- sloc_limit_ (),
- sloc_limit_specified_ (false),
- options_file_ (),
- options_file_specified_ (false),
- x_ (),
- x_specified_ (false),
- v_ (),
- trace_ (),
- mysql_engine_ ("InnoDB"),
- mysql_engine_specified_ (false),
- sqlite_override_null_ (),
- sqlite_lax_auto_id_ (),
- pgsql_server_version_ (7, 4),
- pgsql_server_version_specified_ (false),
- oracle_client_version_ (10, 1),
- oracle_client_version_specified_ (false),
- oracle_warn_truncation_ (),
- mssql_server_version_ (10, 0),
- mssql_server_version_specified_ (false),
- mssql_short_limit_ (1024),
- mssql_short_limit_specified_ (false)
-{
- ::cli::argv_scanner s (argc, argv, erase);
- _parse (s, opt, arg);
-}
-
-options::
-options (int start,
- int& argc,
- char** argv,
- bool erase,
- ::cli::unknown_mode opt,
- ::cli::unknown_mode arg)
-: build2_metadata_ (),
- build2_metadata_specified_ (false),
- help_ (),
- version_ (),
- I_ (),
- I_specified_ (false),
- D_ (),
- D_specified_ (false),
- U_ (),
- U_specified_ (false),
- database_ (),
- database_specified_ (false),
- multi_database_ (::multi_database::disabled),
- multi_database_specified_ (false),
- default_database_ (),
- default_database_specified_ (false),
- generate_query_ (),
- generate_prepared_ (),
- omit_unprepared_ (),
- generate_session_ (),
- generate_schema_ (),
- generate_schema_only_ (),
- suppress_migration_ (),
- suppress_schema_version_ (),
- schema_version_table_ (),
- schema_version_table_specified_ (false),
- schema_format_ (),
- schema_format_specified_ (false),
- omit_drop_ (),
- omit_create_ (),
- schema_name_ (),
- schema_name_specified_ (false),
- fkeys_deferrable_mode_ (),
- fkeys_deferrable_mode_specified_ (false),
- default_pointer_ ("*"),
- default_pointer_specified_ (false),
- session_type_ ("odb::session"),
- session_type_specified_ (false),
- profile_ (),
- profile_specified_ (false),
- at_once_ (),
- schema_ (),
- schema_specified_ (false),
- export_symbol_ (),
- export_symbol_specified_ (false),
- extern_symbol_ (),
- extern_symbol_specified_ (false),
- std_ (cxx_version::cxx98),
- std_specified_ (false),
- warn_hard_add_ (),
- warn_hard_delete_ (),
- warn_hard_ (),
- output_dir_ (),
- output_dir_specified_ (false),
- input_name_ (),
- input_name_specified_ (false),
- changelog_ (),
- changelog_specified_ (false),
- changelog_in_ (),
- changelog_in_specified_ (false),
- changelog_out_ (),
- changelog_out_specified_ (false),
- changelog_dir_ (),
- changelog_dir_specified_ (false),
- init_changelog_ (),
- odb_file_suffix_ (),
- odb_file_suffix_specified_ (false),
- sql_file_suffix_ (),
- sql_file_suffix_specified_ (false),
- schema_file_suffix_ (),
- schema_file_suffix_specified_ (false),
- changelog_file_suffix_ (),
- changelog_file_suffix_specified_ (false),
- hxx_suffix_ (".hxx"),
- hxx_suffix_specified_ (false),
- ixx_suffix_ (".ixx"),
- ixx_suffix_specified_ (false),
- cxx_suffix_ (".cxx"),
- cxx_suffix_specified_ (false),
- sql_suffix_ (".sql"),
- sql_suffix_specified_ (false),
- changelog_suffix_ (".xml"),
- changelog_suffix_specified_ (false),
- hxx_prologue_ (),
- hxx_prologue_specified_ (false),
- ixx_prologue_ (),
- ixx_prologue_specified_ (false),
- cxx_prologue_ (),
- cxx_prologue_specified_ (false),
- schema_prologue_ (),
- schema_prologue_specified_ (false),
- sql_prologue_ (),
- sql_prologue_specified_ (false),
- migration_prologue_ (),
- migration_prologue_specified_ (false),
- sql_interlude_ (),
- sql_interlude_specified_ (false),
- hxx_epilogue_ (),
- hxx_epilogue_specified_ (false),
- ixx_epilogue_ (),
- ixx_epilogue_specified_ (false),
- cxx_epilogue_ (),
- cxx_epilogue_specified_ (false),
- schema_epilogue_ (),
- schema_epilogue_specified_ (false),
- sql_epilogue_ (),
- sql_epilogue_specified_ (false),
- migration_epilogue_ (),
- migration_epilogue_specified_ (false),
- hxx_prologue_file_ (),
- hxx_prologue_file_specified_ (false),
- ixx_prologue_file_ (),
- ixx_prologue_file_specified_ (false),
- cxx_prologue_file_ (),
- cxx_prologue_file_specified_ (false),
- schema_prologue_file_ (),
- schema_prologue_file_specified_ (false),
- sql_prologue_file_ (),
- sql_prologue_file_specified_ (false),
- migration_prologue_file_ (),
- migration_prologue_file_specified_ (false),
- sql_interlude_file_ (),
- sql_interlude_file_specified_ (false),
- hxx_epilogue_file_ (),
- hxx_epilogue_file_specified_ (false),
- ixx_epilogue_file_ (),
- ixx_epilogue_file_specified_ (false),
- cxx_epilogue_file_ (),
- cxx_epilogue_file_specified_ (false),
- schema_epilogue_file_ (),
- schema_epilogue_file_specified_ (false),
- sql_epilogue_file_ (),
- sql_epilogue_file_specified_ (false),
- migration_epilogue_file_ (),
- migration_epilogue_file_specified_ (false),
- odb_prologue_ (),
- odb_prologue_specified_ (false),
- odb_prologue_file_ (),
- odb_prologue_file_specified_ (false),
- odb_epilogue_ (),
- odb_epilogue_specified_ (false),
- odb_epilogue_file_ (),
- odb_epilogue_file_specified_ (false),
- table_prefix_ (),
- table_prefix_specified_ (false),
- index_suffix_ (),
- index_suffix_specified_ (false),
- fkey_suffix_ (),
- fkey_suffix_specified_ (false),
- sequence_suffix_ (),
- sequence_suffix_specified_ (false),
- sql_name_case_ (),
- sql_name_case_specified_ (false),
- table_regex_ (),
- table_regex_specified_ (false),
- column_regex_ (),
- column_regex_specified_ (false),
- index_regex_ (),
- index_regex_specified_ (false),
- fkey_regex_ (),
- fkey_regex_specified_ (false),
- sequence_regex_ (),
- sequence_regex_specified_ (false),
- statement_regex_ (),
- statement_regex_specified_ (false),
- sql_name_regex_ (),
- sql_name_regex_specified_ (false),
- sql_name_regex_trace_ (),
- accessor_regex_ (),
- accessor_regex_specified_ (false),
- accessor_regex_trace_ (),
- modifier_regex_ (),
- modifier_regex_specified_ (false),
- modifier_regex_trace_ (),
- include_with_brackets_ (),
- include_prefix_ (),
- include_prefix_specified_ (false),
- include_regex_ (),
- include_regex_specified_ (false),
- include_regex_trace_ (),
- guard_prefix_ (),
- guard_prefix_specified_ (false),
- show_sloc_ (),
- sloc_limit_ (),
- sloc_limit_specified_ (false),
- options_file_ (),
- options_file_specified_ (false),
- x_ (),
- x_specified_ (false),
- v_ (),
- trace_ (),
- mysql_engine_ ("InnoDB"),
- mysql_engine_specified_ (false),
- sqlite_override_null_ (),
- sqlite_lax_auto_id_ (),
- pgsql_server_version_ (7, 4),
- pgsql_server_version_specified_ (false),
- oracle_client_version_ (10, 1),
- oracle_client_version_specified_ (false),
- oracle_warn_truncation_ (),
- mssql_server_version_ (10, 0),
- mssql_server_version_specified_ (false),
- mssql_short_limit_ (1024),
- mssql_short_limit_specified_ (false)
-{
- ::cli::argv_scanner s (start, argc, argv, erase);
- _parse (s, opt, arg);
-}
-
-options::
-options (int& argc,
- char** argv,
- int& end,
- bool erase,
- ::cli::unknown_mode opt,
- ::cli::unknown_mode arg)
-: build2_metadata_ (),
- build2_metadata_specified_ (false),
- help_ (),
- version_ (),
- I_ (),
- I_specified_ (false),
- D_ (),
- D_specified_ (false),
- U_ (),
- U_specified_ (false),
- database_ (),
- database_specified_ (false),
- multi_database_ (::multi_database::disabled),
- multi_database_specified_ (false),
- default_database_ (),
- default_database_specified_ (false),
- generate_query_ (),
- generate_prepared_ (),
- omit_unprepared_ (),
- generate_session_ (),
- generate_schema_ (),
- generate_schema_only_ (),
- suppress_migration_ (),
- suppress_schema_version_ (),
- schema_version_table_ (),
- schema_version_table_specified_ (false),
- schema_format_ (),
- schema_format_specified_ (false),
- omit_drop_ (),
- omit_create_ (),
- schema_name_ (),
- schema_name_specified_ (false),
- fkeys_deferrable_mode_ (),
- fkeys_deferrable_mode_specified_ (false),
- default_pointer_ ("*"),
- default_pointer_specified_ (false),
- session_type_ ("odb::session"),
- session_type_specified_ (false),
- profile_ (),
- profile_specified_ (false),
- at_once_ (),
- schema_ (),
- schema_specified_ (false),
- export_symbol_ (),
- export_symbol_specified_ (false),
- extern_symbol_ (),
- extern_symbol_specified_ (false),
- std_ (cxx_version::cxx98),
- std_specified_ (false),
- warn_hard_add_ (),
- warn_hard_delete_ (),
- warn_hard_ (),
- output_dir_ (),
- output_dir_specified_ (false),
- input_name_ (),
- input_name_specified_ (false),
- changelog_ (),
- changelog_specified_ (false),
- changelog_in_ (),
- changelog_in_specified_ (false),
- changelog_out_ (),
- changelog_out_specified_ (false),
- changelog_dir_ (),
- changelog_dir_specified_ (false),
- init_changelog_ (),
- odb_file_suffix_ (),
- odb_file_suffix_specified_ (false),
- sql_file_suffix_ (),
- sql_file_suffix_specified_ (false),
- schema_file_suffix_ (),
- schema_file_suffix_specified_ (false),
- changelog_file_suffix_ (),
- changelog_file_suffix_specified_ (false),
- hxx_suffix_ (".hxx"),
- hxx_suffix_specified_ (false),
- ixx_suffix_ (".ixx"),
- ixx_suffix_specified_ (false),
- cxx_suffix_ (".cxx"),
- cxx_suffix_specified_ (false),
- sql_suffix_ (".sql"),
- sql_suffix_specified_ (false),
- changelog_suffix_ (".xml"),
- changelog_suffix_specified_ (false),
- hxx_prologue_ (),
- hxx_prologue_specified_ (false),
- ixx_prologue_ (),
- ixx_prologue_specified_ (false),
- cxx_prologue_ (),
- cxx_prologue_specified_ (false),
- schema_prologue_ (),
- schema_prologue_specified_ (false),
- sql_prologue_ (),
- sql_prologue_specified_ (false),
- migration_prologue_ (),
- migration_prologue_specified_ (false),
- sql_interlude_ (),
- sql_interlude_specified_ (false),
- hxx_epilogue_ (),
- hxx_epilogue_specified_ (false),
- ixx_epilogue_ (),
- ixx_epilogue_specified_ (false),
- cxx_epilogue_ (),
- cxx_epilogue_specified_ (false),
- schema_epilogue_ (),
- schema_epilogue_specified_ (false),
- sql_epilogue_ (),
- sql_epilogue_specified_ (false),
- migration_epilogue_ (),
- migration_epilogue_specified_ (false),
- hxx_prologue_file_ (),
- hxx_prologue_file_specified_ (false),
- ixx_prologue_file_ (),
- ixx_prologue_file_specified_ (false),
- cxx_prologue_file_ (),
- cxx_prologue_file_specified_ (false),
- schema_prologue_file_ (),
- schema_prologue_file_specified_ (false),
- sql_prologue_file_ (),
- sql_prologue_file_specified_ (false),
- migration_prologue_file_ (),
- migration_prologue_file_specified_ (false),
- sql_interlude_file_ (),
- sql_interlude_file_specified_ (false),
- hxx_epilogue_file_ (),
- hxx_epilogue_file_specified_ (false),
- ixx_epilogue_file_ (),
- ixx_epilogue_file_specified_ (false),
- cxx_epilogue_file_ (),
- cxx_epilogue_file_specified_ (false),
- schema_epilogue_file_ (),
- schema_epilogue_file_specified_ (false),
- sql_epilogue_file_ (),
- sql_epilogue_file_specified_ (false),
- migration_epilogue_file_ (),
- migration_epilogue_file_specified_ (false),
- odb_prologue_ (),
- odb_prologue_specified_ (false),
- odb_prologue_file_ (),
- odb_prologue_file_specified_ (false),
- odb_epilogue_ (),
- odb_epilogue_specified_ (false),
- odb_epilogue_file_ (),
- odb_epilogue_file_specified_ (false),
- table_prefix_ (),
- table_prefix_specified_ (false),
- index_suffix_ (),
- index_suffix_specified_ (false),
- fkey_suffix_ (),
- fkey_suffix_specified_ (false),
- sequence_suffix_ (),
- sequence_suffix_specified_ (false),
- sql_name_case_ (),
- sql_name_case_specified_ (false),
- table_regex_ (),
- table_regex_specified_ (false),
- column_regex_ (),
- column_regex_specified_ (false),
- index_regex_ (),
- index_regex_specified_ (false),
- fkey_regex_ (),
- fkey_regex_specified_ (false),
- sequence_regex_ (),
- sequence_regex_specified_ (false),
- statement_regex_ (),
- statement_regex_specified_ (false),
- sql_name_regex_ (),
- sql_name_regex_specified_ (false),
- sql_name_regex_trace_ (),
- accessor_regex_ (),
- accessor_regex_specified_ (false),
- accessor_regex_trace_ (),
- modifier_regex_ (),
- modifier_regex_specified_ (false),
- modifier_regex_trace_ (),
- include_with_brackets_ (),
- include_prefix_ (),
- include_prefix_specified_ (false),
- include_regex_ (),
- include_regex_specified_ (false),
- include_regex_trace_ (),
- guard_prefix_ (),
- guard_prefix_specified_ (false),
- show_sloc_ (),
- sloc_limit_ (),
- sloc_limit_specified_ (false),
- options_file_ (),
- options_file_specified_ (false),
- x_ (),
- x_specified_ (false),
- v_ (),
- trace_ (),
- mysql_engine_ ("InnoDB"),
- mysql_engine_specified_ (false),
- sqlite_override_null_ (),
- sqlite_lax_auto_id_ (),
- pgsql_server_version_ (7, 4),
- pgsql_server_version_specified_ (false),
- oracle_client_version_ (10, 1),
- oracle_client_version_specified_ (false),
- oracle_warn_truncation_ (),
- mssql_server_version_ (10, 0),
- mssql_server_version_specified_ (false),
- mssql_short_limit_ (1024),
- mssql_short_limit_specified_ (false)
-{
- ::cli::argv_scanner s (argc, argv, erase);
- _parse (s, opt, arg);
- end = s.end ();
-}
-
-options::
-options (int start,
- int& argc,
- char** argv,
- int& end,
- bool erase,
- ::cli::unknown_mode opt,
- ::cli::unknown_mode arg)
-: build2_metadata_ (),
- build2_metadata_specified_ (false),
- help_ (),
- version_ (),
- I_ (),
- I_specified_ (false),
- D_ (),
- D_specified_ (false),
- U_ (),
- U_specified_ (false),
- database_ (),
- database_specified_ (false),
- multi_database_ (::multi_database::disabled),
- multi_database_specified_ (false),
- default_database_ (),
- default_database_specified_ (false),
- generate_query_ (),
- generate_prepared_ (),
- omit_unprepared_ (),
- generate_session_ (),
- generate_schema_ (),
- generate_schema_only_ (),
- suppress_migration_ (),
- suppress_schema_version_ (),
- schema_version_table_ (),
- schema_version_table_specified_ (false),
- schema_format_ (),
- schema_format_specified_ (false),
- omit_drop_ (),
- omit_create_ (),
- schema_name_ (),
- schema_name_specified_ (false),
- fkeys_deferrable_mode_ (),
- fkeys_deferrable_mode_specified_ (false),
- default_pointer_ ("*"),
- default_pointer_specified_ (false),
- session_type_ ("odb::session"),
- session_type_specified_ (false),
- profile_ (),
- profile_specified_ (false),
- at_once_ (),
- schema_ (),
- schema_specified_ (false),
- export_symbol_ (),
- export_symbol_specified_ (false),
- extern_symbol_ (),
- extern_symbol_specified_ (false),
- std_ (cxx_version::cxx98),
- std_specified_ (false),
- warn_hard_add_ (),
- warn_hard_delete_ (),
- warn_hard_ (),
- output_dir_ (),
- output_dir_specified_ (false),
- input_name_ (),
- input_name_specified_ (false),
- changelog_ (),
- changelog_specified_ (false),
- changelog_in_ (),
- changelog_in_specified_ (false),
- changelog_out_ (),
- changelog_out_specified_ (false),
- changelog_dir_ (),
- changelog_dir_specified_ (false),
- init_changelog_ (),
- odb_file_suffix_ (),
- odb_file_suffix_specified_ (false),
- sql_file_suffix_ (),
- sql_file_suffix_specified_ (false),
- schema_file_suffix_ (),
- schema_file_suffix_specified_ (false),
- changelog_file_suffix_ (),
- changelog_file_suffix_specified_ (false),
- hxx_suffix_ (".hxx"),
- hxx_suffix_specified_ (false),
- ixx_suffix_ (".ixx"),
- ixx_suffix_specified_ (false),
- cxx_suffix_ (".cxx"),
- cxx_suffix_specified_ (false),
- sql_suffix_ (".sql"),
- sql_suffix_specified_ (false),
- changelog_suffix_ (".xml"),
- changelog_suffix_specified_ (false),
- hxx_prologue_ (),
- hxx_prologue_specified_ (false),
- ixx_prologue_ (),
- ixx_prologue_specified_ (false),
- cxx_prologue_ (),
- cxx_prologue_specified_ (false),
- schema_prologue_ (),
- schema_prologue_specified_ (false),
- sql_prologue_ (),
- sql_prologue_specified_ (false),
- migration_prologue_ (),
- migration_prologue_specified_ (false),
- sql_interlude_ (),
- sql_interlude_specified_ (false),
- hxx_epilogue_ (),
- hxx_epilogue_specified_ (false),
- ixx_epilogue_ (),
- ixx_epilogue_specified_ (false),
- cxx_epilogue_ (),
- cxx_epilogue_specified_ (false),
- schema_epilogue_ (),
- schema_epilogue_specified_ (false),
- sql_epilogue_ (),
- sql_epilogue_specified_ (false),
- migration_epilogue_ (),
- migration_epilogue_specified_ (false),
- hxx_prologue_file_ (),
- hxx_prologue_file_specified_ (false),
- ixx_prologue_file_ (),
- ixx_prologue_file_specified_ (false),
- cxx_prologue_file_ (),
- cxx_prologue_file_specified_ (false),
- schema_prologue_file_ (),
- schema_prologue_file_specified_ (false),
- sql_prologue_file_ (),
- sql_prologue_file_specified_ (false),
- migration_prologue_file_ (),
- migration_prologue_file_specified_ (false),
- sql_interlude_file_ (),
- sql_interlude_file_specified_ (false),
- hxx_epilogue_file_ (),
- hxx_epilogue_file_specified_ (false),
- ixx_epilogue_file_ (),
- ixx_epilogue_file_specified_ (false),
- cxx_epilogue_file_ (),
- cxx_epilogue_file_specified_ (false),
- schema_epilogue_file_ (),
- schema_epilogue_file_specified_ (false),
- sql_epilogue_file_ (),
- sql_epilogue_file_specified_ (false),
- migration_epilogue_file_ (),
- migration_epilogue_file_specified_ (false),
- odb_prologue_ (),
- odb_prologue_specified_ (false),
- odb_prologue_file_ (),
- odb_prologue_file_specified_ (false),
- odb_epilogue_ (),
- odb_epilogue_specified_ (false),
- odb_epilogue_file_ (),
- odb_epilogue_file_specified_ (false),
- table_prefix_ (),
- table_prefix_specified_ (false),
- index_suffix_ (),
- index_suffix_specified_ (false),
- fkey_suffix_ (),
- fkey_suffix_specified_ (false),
- sequence_suffix_ (),
- sequence_suffix_specified_ (false),
- sql_name_case_ (),
- sql_name_case_specified_ (false),
- table_regex_ (),
- table_regex_specified_ (false),
- column_regex_ (),
- column_regex_specified_ (false),
- index_regex_ (),
- index_regex_specified_ (false),
- fkey_regex_ (),
- fkey_regex_specified_ (false),
- sequence_regex_ (),
- sequence_regex_specified_ (false),
- statement_regex_ (),
- statement_regex_specified_ (false),
- sql_name_regex_ (),
- sql_name_regex_specified_ (false),
- sql_name_regex_trace_ (),
- accessor_regex_ (),
- accessor_regex_specified_ (false),
- accessor_regex_trace_ (),
- modifier_regex_ (),
- modifier_regex_specified_ (false),
- modifier_regex_trace_ (),
- include_with_brackets_ (),
- include_prefix_ (),
- include_prefix_specified_ (false),
- include_regex_ (),
- include_regex_specified_ (false),
- include_regex_trace_ (),
- guard_prefix_ (),
- guard_prefix_specified_ (false),
- show_sloc_ (),
- sloc_limit_ (),
- sloc_limit_specified_ (false),
- options_file_ (),
- options_file_specified_ (false),
- x_ (),
- x_specified_ (false),
- v_ (),
- trace_ (),
- mysql_engine_ ("InnoDB"),
- mysql_engine_specified_ (false),
- sqlite_override_null_ (),
- sqlite_lax_auto_id_ (),
- pgsql_server_version_ (7, 4),
- pgsql_server_version_specified_ (false),
- oracle_client_version_ (10, 1),
- oracle_client_version_specified_ (false),
- oracle_warn_truncation_ (),
- mssql_server_version_ (10, 0),
- mssql_server_version_specified_ (false),
- mssql_short_limit_ (1024),
- mssql_short_limit_specified_ (false)
-{
- ::cli::argv_scanner s (start, argc, argv, erase);
- _parse (s, opt, arg);
- end = s.end ();
-}
-
-options::
-options (::cli::scanner& s,
- ::cli::unknown_mode opt,
- ::cli::unknown_mode arg)
-: build2_metadata_ (),
- build2_metadata_specified_ (false),
- help_ (),
- version_ (),
- I_ (),
- I_specified_ (false),
- D_ (),
- D_specified_ (false),
- U_ (),
- U_specified_ (false),
- database_ (),
- database_specified_ (false),
- multi_database_ (::multi_database::disabled),
- multi_database_specified_ (false),
- default_database_ (),
- default_database_specified_ (false),
- generate_query_ (),
- generate_prepared_ (),
- omit_unprepared_ (),
- generate_session_ (),
- generate_schema_ (),
- generate_schema_only_ (),
- suppress_migration_ (),
- suppress_schema_version_ (),
- schema_version_table_ (),
- schema_version_table_specified_ (false),
- schema_format_ (),
- schema_format_specified_ (false),
- omit_drop_ (),
- omit_create_ (),
- schema_name_ (),
- schema_name_specified_ (false),
- fkeys_deferrable_mode_ (),
- fkeys_deferrable_mode_specified_ (false),
- default_pointer_ ("*"),
- default_pointer_specified_ (false),
- session_type_ ("odb::session"),
- session_type_specified_ (false),
- profile_ (),
- profile_specified_ (false),
- at_once_ (),
- schema_ (),
- schema_specified_ (false),
- export_symbol_ (),
- export_symbol_specified_ (false),
- extern_symbol_ (),
- extern_symbol_specified_ (false),
- std_ (cxx_version::cxx98),
- std_specified_ (false),
- warn_hard_add_ (),
- warn_hard_delete_ (),
- warn_hard_ (),
- output_dir_ (),
- output_dir_specified_ (false),
- input_name_ (),
- input_name_specified_ (false),
- changelog_ (),
- changelog_specified_ (false),
- changelog_in_ (),
- changelog_in_specified_ (false),
- changelog_out_ (),
- changelog_out_specified_ (false),
- changelog_dir_ (),
- changelog_dir_specified_ (false),
- init_changelog_ (),
- odb_file_suffix_ (),
- odb_file_suffix_specified_ (false),
- sql_file_suffix_ (),
- sql_file_suffix_specified_ (false),
- schema_file_suffix_ (),
- schema_file_suffix_specified_ (false),
- changelog_file_suffix_ (),
- changelog_file_suffix_specified_ (false),
- hxx_suffix_ (".hxx"),
- hxx_suffix_specified_ (false),
- ixx_suffix_ (".ixx"),
- ixx_suffix_specified_ (false),
- cxx_suffix_ (".cxx"),
- cxx_suffix_specified_ (false),
- sql_suffix_ (".sql"),
- sql_suffix_specified_ (false),
- changelog_suffix_ (".xml"),
- changelog_suffix_specified_ (false),
- hxx_prologue_ (),
- hxx_prologue_specified_ (false),
- ixx_prologue_ (),
- ixx_prologue_specified_ (false),
- cxx_prologue_ (),
- cxx_prologue_specified_ (false),
- schema_prologue_ (),
- schema_prologue_specified_ (false),
- sql_prologue_ (),
- sql_prologue_specified_ (false),
- migration_prologue_ (),
- migration_prologue_specified_ (false),
- sql_interlude_ (),
- sql_interlude_specified_ (false),
- hxx_epilogue_ (),
- hxx_epilogue_specified_ (false),
- ixx_epilogue_ (),
- ixx_epilogue_specified_ (false),
- cxx_epilogue_ (),
- cxx_epilogue_specified_ (false),
- schema_epilogue_ (),
- schema_epilogue_specified_ (false),
- sql_epilogue_ (),
- sql_epilogue_specified_ (false),
- migration_epilogue_ (),
- migration_epilogue_specified_ (false),
- hxx_prologue_file_ (),
- hxx_prologue_file_specified_ (false),
- ixx_prologue_file_ (),
- ixx_prologue_file_specified_ (false),
- cxx_prologue_file_ (),
- cxx_prologue_file_specified_ (false),
- schema_prologue_file_ (),
- schema_prologue_file_specified_ (false),
- sql_prologue_file_ (),
- sql_prologue_file_specified_ (false),
- migration_prologue_file_ (),
- migration_prologue_file_specified_ (false),
- sql_interlude_file_ (),
- sql_interlude_file_specified_ (false),
- hxx_epilogue_file_ (),
- hxx_epilogue_file_specified_ (false),
- ixx_epilogue_file_ (),
- ixx_epilogue_file_specified_ (false),
- cxx_epilogue_file_ (),
- cxx_epilogue_file_specified_ (false),
- schema_epilogue_file_ (),
- schema_epilogue_file_specified_ (false),
- sql_epilogue_file_ (),
- sql_epilogue_file_specified_ (false),
- migration_epilogue_file_ (),
- migration_epilogue_file_specified_ (false),
- odb_prologue_ (),
- odb_prologue_specified_ (false),
- odb_prologue_file_ (),
- odb_prologue_file_specified_ (false),
- odb_epilogue_ (),
- odb_epilogue_specified_ (false),
- odb_epilogue_file_ (),
- odb_epilogue_file_specified_ (false),
- table_prefix_ (),
- table_prefix_specified_ (false),
- index_suffix_ (),
- index_suffix_specified_ (false),
- fkey_suffix_ (),
- fkey_suffix_specified_ (false),
- sequence_suffix_ (),
- sequence_suffix_specified_ (false),
- sql_name_case_ (),
- sql_name_case_specified_ (false),
- table_regex_ (),
- table_regex_specified_ (false),
- column_regex_ (),
- column_regex_specified_ (false),
- index_regex_ (),
- index_regex_specified_ (false),
- fkey_regex_ (),
- fkey_regex_specified_ (false),
- sequence_regex_ (),
- sequence_regex_specified_ (false),
- statement_regex_ (),
- statement_regex_specified_ (false),
- sql_name_regex_ (),
- sql_name_regex_specified_ (false),
- sql_name_regex_trace_ (),
- accessor_regex_ (),
- accessor_regex_specified_ (false),
- accessor_regex_trace_ (),
- modifier_regex_ (),
- modifier_regex_specified_ (false),
- modifier_regex_trace_ (),
- include_with_brackets_ (),
- include_prefix_ (),
- include_prefix_specified_ (false),
- include_regex_ (),
- include_regex_specified_ (false),
- include_regex_trace_ (),
- guard_prefix_ (),
- guard_prefix_specified_ (false),
- show_sloc_ (),
- sloc_limit_ (),
- sloc_limit_specified_ (false),
- options_file_ (),
- options_file_specified_ (false),
- x_ (),
- x_specified_ (false),
- v_ (),
- trace_ (),
- mysql_engine_ ("InnoDB"),
- mysql_engine_specified_ (false),
- sqlite_override_null_ (),
- sqlite_lax_auto_id_ (),
- pgsql_server_version_ (7, 4),
- pgsql_server_version_specified_ (false),
- oracle_client_version_ (10, 1),
- oracle_client_version_specified_ (false),
- oracle_warn_truncation_ (),
- mssql_server_version_ (10, 0),
- mssql_server_version_specified_ (false),
- mssql_short_limit_ (1024),
- mssql_short_limit_specified_ (false)
-{
- _parse (s, opt, arg);
-}
-
-::cli::usage_para options::
-print_usage (::std::ostream& os, ::cli::usage_para p)
-{
- CLI_POTENTIALLY_UNUSED (os);
-
- if (p == ::cli::usage_para::text)
- os << ::std::endl;
-
- os << "--help Print usage information and exit." << ::std::endl;
-
- os << "--version Print version and exit." << ::std::endl;
-
- os << "-I <dir> Add <dir> to the beginning of the list of" << ::std::endl
- << " directories to be searched for included header" << ::std::endl
- << " files." << ::std::endl;
-
- os << "-D <name>[=<def>] Define macro <name> with definition <def>." << ::std::endl;
-
- os << "-U <name> Cancel any previous definitions of macro <name>," << ::std::endl
- << " either built-in or provided with the -D option." << ::std::endl;
-
- os << "--database|-d <db> Generate code for the <db> database." << ::std::endl;
-
- os << "--multi-database|-m <type> Enable multi-database support and specify its" << ::std::endl
- << " type." << ::std::endl;
-
- os << "--default-database <db> When static multi-database support is used," << ::std::endl
- << " specify the database that should be made the" << ::std::endl
- << " default." << ::std::endl;
-
- os << "--generate-query|-q Generate query support code." << ::std::endl;
-
- os << "--generate-prepared Generate prepared query execution support code." << ::std::endl;
-
- os << "--omit-unprepared Omit un-prepared (once-off) query execution" << ::std::endl
- << " support code." << ::std::endl;
-
- os << "--generate-session|-e Generate session support code." << ::std::endl;
-
- os << "--generate-schema|-s Generate the database schema." << ::std::endl;
-
- os << "--generate-schema-only Generate only the database schema." << ::std::endl;
-
- os << "--suppress-migration Suppress the generation of database schema" << ::std::endl
- << " migration statements." << ::std::endl;
-
- os << "--suppress-schema-version Suppress the generation of schema version table." << ::std::endl;
-
- os << "--schema-version-table <name> Specify the alternative schema version table name" << ::std::endl
- << " instead of the default schema_version." << ::std::endl;
-
- os << "--schema-format <format> Generate the database schema in the specified" << ::std::endl
- << " format." << ::std::endl;
-
- os << "--omit-drop Omit DROP statements from the generated database" << ::std::endl
- << " schema." << ::std::endl;
-
- os << "--omit-create Omit CREATE statements from the generated" << ::std::endl
- << " database schema." << ::std::endl;
-
- os << "--schema-name <name> Use <name> as the database schema name." << ::std::endl;
-
- os << "--fkeys-deferrable-mode <m> Use constraint checking mode <m> in foreign keys" << ::std::endl
- << " generated for object relationships." << ::std::endl;
-
- os << "--default-pointer <ptr> Use <ptr> as the default pointer for persistent" << ::std::endl
- << " objects and views." << ::std::endl;
-
- os << "--session-type <type> Use <type> as the alternative session type" << ::std::endl
- << " instead of the default odb::session." << ::std::endl;
-
- os << "--profile|-p <name> Specify a profile that should be used during" << ::std::endl
- << " compilation." << ::std::endl;
-
- os << "--at-once Generate code for all the input files as well as" << ::std::endl
- << " for all the files that they include at once." << ::std::endl;
-
- os << "--schema <schema> Specify a database schema (database namespace)" << ::std::endl
- << " that should be assigned to the persistent classes" << ::std::endl
- << " in the file being compiled." << ::std::endl;
-
- os << "--export-symbol <symbol> Insert <symbol> in places where DLL export/import" << ::std::endl
- << " control statements" << ::std::endl
- << " (__declspec(dllexport/dllimport)) are necessary." << ::std::endl;
-
- os << "--extern-symbol <symbol> If <symbol> is defined, insert it in places where" << ::std::endl
- << " a template instantiation must be declared extern." << ::std::endl;
-
- os << "--std <version> Specify the C++ standard that should be used" << ::std::endl
- << " during compilation." << ::std::endl;
-
- os << "--warn-hard-add Warn about hard-added data members." << ::std::endl;
-
- os << "--warn-hard-delete Warn about hard-deleted data members and" << ::std::endl
- << " persistent classes." << ::std::endl;
-
- os << "--warn-hard Warn about both hard-added and hard-deleted data" << ::std::endl
- << " members and persistent classes." << ::std::endl;
-
- os << "--output-dir|-o <dir> Write the generated files to <dir> instead of the" << ::std::endl
- << " current directory." << ::std::endl;
-
- os << "--input-name <name> Use <name> instead of the input file to derive" << ::std::endl
- << " the names of the generated files." << ::std::endl;
-
- os << "--changelog <file> Read/write changelog from/to <file> instead of" << ::std::endl
- << " the default changelog file." << ::std::endl;
-
- os << "--changelog-in <file> Read changelog from <file> instead of the default" << ::std::endl
- << " changelog file." << ::std::endl;
-
- os << "--changelog-out <file> Write changelog to <file> instead of the default" << ::std::endl
- << " changelog file." << ::std::endl;
-
- os << "--changelog-dir <dir> Use <dir> instead of the input file directory as" << ::std::endl
- << " the changelog file directory." << ::std::endl;
-
- os << "--init-changelog Force re-initialization of the changelog even if" << ::std::endl
- << " one exists (all the existing change history will" << ::std::endl
- << " be lost)." << ::std::endl;
-
- os << "--odb-file-suffix <suffix> Use <suffix> to construct the names of the" << ::std::endl
- << " generated C++ files." << ::std::endl;
-
- os << "--sql-file-suffix <suffix> Use <suffix> to construct the name of the" << ::std::endl
- << " generated schema SQL file." << ::std::endl;
-
- os << "--schema-file-suffix <suffix> Use <suffix> to construct the name of the" << ::std::endl
- << " generated schema C++ source file." << ::std::endl;
-
- os << "--changelog-file-suffix <sfx> Use <sfx> to construct the name of the changelog" << ::std::endl
- << " file." << ::std::endl;
-
- os << "--hxx-suffix <suffix> Use <suffix> instead of the default .hxx to" << ::std::endl
- << " construct the name of the generated C++ header" << ::std::endl
- << " file." << ::std::endl;
-
- os << "--ixx-suffix <suffix> Use <suffix> instead of the default .ixx to" << ::std::endl
- << " construct the name of the generated C++ inline" << ::std::endl
- << " file." << ::std::endl;
-
- os << "--cxx-suffix <suffix> Use <suffix> instead of the default .cxx to" << ::std::endl
- << " construct the name of the generated C++ source" << ::std::endl
- << " file." << ::std::endl;
-
- os << "--sql-suffix <suffix> Use <suffix> instead of the default .sql to" << ::std::endl
- << " construct the name of the generated database" << ::std::endl
- << " schema file." << ::std::endl;
-
- os << "--changelog-suffix <suffix> Use <suffix> instead of the default .xml to" << ::std::endl
- << " construct the name of the changelog file." << ::std::endl;
-
- os << "--hxx-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
- << " C++ header file." << ::std::endl;
-
- os << "--ixx-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
- << " C++ inline file." << ::std::endl;
-
- os << "--cxx-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
- << " C++ source file." << ::std::endl;
-
- os << "--schema-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
- << " schema C++ source file." << ::std::endl;
-
- os << "--sql-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
- << " database schema file." << ::std::endl;
-
- os << "--migration-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
- << " database migration file." << ::std::endl;
-
- os << "--sql-interlude <text> Insert <text> after all the DROP and before any" << ::std::endl
- << " CREATE statements in the generated database" << ::std::endl
- << " schema file." << ::std::endl;
-
- os << "--hxx-epilogue <text> Insert <text> at the end of the generated C++" << ::std::endl
- << " header file." << ::std::endl;
-
- os << "--ixx-epilogue <text> Insert <text> at the end of the generated C++" << ::std::endl
- << " inline file." << ::std::endl;
-
- os << "--cxx-epilogue <text> Insert <text> at the end of the generated C++" << ::std::endl
- << " source file." << ::std::endl;
-
- os << "--schema-epilogue <text> Insert <text> at the end of the generated schema" << ::std::endl
- << " C++ source file." << ::std::endl;
-
- os << "--sql-epilogue <text> Insert <text> at the end of the generated" << ::std::endl
- << " database schema file." << ::std::endl;
-
- os << "--migration-epilogue <text> Insert <text> at the end of the generated" << ::std::endl
- << " database migration file." << ::std::endl;
-
- os << "--hxx-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
- << " the generated C++ header file." << ::std::endl;
-
- os << "--ixx-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
- << " the generated C++ inline file." << ::std::endl;
-
- os << "--cxx-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
- << " the generated C++ source file." << ::std::endl;
-
- os << "--schema-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
- << " the generated schema C++ source file." << ::std::endl;
-
- os << "--sql-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
- << " the generated database schema file." << ::std::endl;
-
- os << "--migration-prologue-file <f> Insert the content of file <f> at the beginning" << ::std::endl
- << " of the generated database migration file." << ::std::endl;
-
- os << "--sql-interlude-file <file> Insert the content of <file> after all the DROP" << ::std::endl
- << " and before any CREATE statements in the generated" << ::std::endl
- << " database schema file." << ::std::endl;
-
- os << "--hxx-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
- << " generated C++ header file." << ::std::endl;
-
- os << "--ixx-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
- << " generated C++ inline file." << ::std::endl;
-
- os << "--cxx-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
- << " generated C++ source file." << ::std::endl;
-
- os << "--schema-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
- << " generated schema C++ source file." << ::std::endl;
-
- os << "--sql-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
- << " generated database schema file." << ::std::endl;
-
- os << "--migration-epilogue-file <f> Insert the content of file <f> at the end of the" << ::std::endl
- << " generated database migration file." << ::std::endl;
-
- os << "--odb-prologue <text> Compile <text> before the input header file." << ::std::endl;
-
- os << "--odb-prologue-file <file> Compile <file> contents before the input header" << ::std::endl
- << " file." << ::std::endl;
-
- os << "--odb-epilogue <text> Compile <text> after the input header file." << ::std::endl;
-
- os << "--odb-epilogue-file <file> Compile <file> contents after the input header" << ::std::endl
- << " file." << ::std::endl;
-
- os << "--table-prefix <prefix> Add <prefix> to table names and, for databases" << ::std::endl
- << " that have global index and/or foreign key names," << ::std::endl
- << " to those names as well." << ::std::endl;
-
- os << "--index-suffix <suffix> Use <suffix> instead of the default _i to" << ::std::endl
- << " construct index names." << ::std::endl;
-
- os << "--fkey-suffix <suffix> Use <suffix> instead of the default _fk to" << ::std::endl
- << " construct foreign key names." << ::std::endl;
-
- os << "--sequence-suffix <suffix> Use <suffix> instead of the default _seq to" << ::std::endl
- << " construct sequence names." << ::std::endl;
-
- os << "--sql-name-case <case> Convert all automatically-derived SQL names to" << ::std::endl
- << " upper or lower case." << ::std::endl;
-
- os << "--table-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
- << " that is used to transform automatically-derived" << ::std::endl
- << " table names." << ::std::endl;
-
- os << "--column-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
- << " that is used to transform automatically-derived" << ::std::endl
- << " column names." << ::std::endl;
-
- os << "--index-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
- << " that is used to transform automatically-derived" << ::std::endl
- << " index names." << ::std::endl;
-
- os << "--fkey-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
- << " that is used to transform automatically-derived" << ::std::endl
- << " foreign key names." << ::std::endl;
-
- os << "--sequence-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
- << " that is used to transform automatically-derived" << ::std::endl
- << " sequence names." << ::std::endl;
-
- os << "--statement-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
- << " that is used to transform automatically-derived" << ::std::endl
- << " prepared statement names." << ::std::endl;
-
- os << "--sql-name-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
- << " that is used to transform all" << ::std::endl
- << " automatically-derived SQL names." << ::std::endl;
-
- os << "--sql-name-regex-trace Trace the process of applying regular expressions" << ::std::endl
- << " specified with the SQL name --*-regex options." << ::std::endl;
-
- os << "--accessor-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
- << " used to transform data member names to function" << ::std::endl
- << " names when searching for a suitable accessor" << ::std::endl
- << " function." << ::std::endl;
-
- os << "--accessor-regex-trace Trace the process of applying regular expressions" << ::std::endl
- << " specified with the --accessor-regex option." << ::std::endl;
-
- os << "--modifier-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
- << " used to transform data member names to function" << ::std::endl
- << " names when searching for a suitable modifier" << ::std::endl
- << " function." << ::std::endl;
-
- os << "--modifier-regex-trace Trace the process of applying regular expressions" << ::std::endl
- << " specified with the --modifier-regex option." << ::std::endl;
-
- os << "--include-with-brackets Use angle brackets (<>) instead of quotes (\"\") in" << ::std::endl
- << " the generated #include directives." << ::std::endl;
-
- os << "--include-prefix <prefix> Add <prefix> to the generated #include directive" << ::std::endl
- << " paths." << ::std::endl;
-
- os << "--include-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
- << " used to transform generated #include directive" << ::std::endl
- << " paths." << ::std::endl;
-
- os << "--include-regex-trace Trace the process of applying regular expressions" << ::std::endl
- << " specified with the --include-regex option." << ::std::endl;
-
- os << "--guard-prefix <prefix> Add <prefix> to the generated header inclusion" << ::std::endl
- << " guards." << ::std::endl;
-
- os << "--show-sloc Print the number of generated physical source" << ::std::endl
- << " lines of code (SLOC)." << ::std::endl;
-
- os << "--sloc-limit <num> Check that the number of generated physical" << ::std::endl
- << " source lines of code (SLOC) does not exceed" << ::std::endl
- << " <num>." << ::std::endl;
-
- os << "--options-file <file> Read additional options from <file>." << ::std::endl;
-
- os << "-x <option> Pass <option> to the underlying C++ compiler" << ::std::endl
- << " (g++)." << ::std::endl;
-
- os << "-v Print the commands executed to run the stages of" << ::std::endl
- << " compilation." << ::std::endl;
-
- os << "--trace Trace the compilation process." << ::std::endl;
-
- os << "--mysql-engine <engine> Use <engine> instead of the default InnoDB in the" << ::std::endl
- << " generated database schema file." << ::std::endl;
-
- os << "--sqlite-override-null Make all columns in the generated database schema" << ::std::endl
- << " allow NULL values." << ::std::endl;
-
- os << "--sqlite-lax-auto-id Do not force monotonically increasing" << ::std::endl
- << " automatically-assigned object ids." << ::std::endl;
-
- os << "--pgsql-server-version <ver> Specify the minimum PostgreSQL server version" << ::std::endl
- << " with which the generated C++ code and schema will" << ::std::endl
- << " be used." << ::std::endl;
-
- os << "--oracle-client-version <ver> Specify the minimum Oracle client library (OCI)" << ::std::endl
- << " version with which the generated C++ code will be" << ::std::endl
- << " linked." << ::std::endl;
-
- os << "--oracle-warn-truncation Warn about SQL names that are longer than 30" << ::std::endl
- << " characters and are therefore truncated." << ::std::endl;
-
- os << "--mssql-server-version <ver> Specify the minimum SQL Server server version" << ::std::endl
- << " with which the generated C++ code and schema will" << ::std::endl
- << " be used." << ::std::endl;
-
- os << "--mssql-short-limit <size> Specify the short data size limit." << ::std::endl;
-
- p = ::cli::usage_para::option;
-
- return p;
-}
-
-struct _cli_options_desc_type: ::cli::options
-{
- _cli_options_desc_type ()
- {
- ::options::fill (*this);
- }
-};
-
-static _cli_options_desc_type _cli_options_desc_;
-
-void options::
-fill (::cli::options& os)
-{
- // --build2-metadata
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--build2-metadata", a, false, dv);
- os.push_back (o);
- }
-
- // --help
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--help", a, true, dv);
- os.push_back (o);
- }
-
- // --version
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--version", a, true, dv);
- os.push_back (o);
- }
-
- // -I
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("-I", a, false, dv);
- os.push_back (o);
- }
-
- // -D
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("-D", a, false, dv);
- os.push_back (o);
- }
-
- // -U
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("-U", a, false, dv);
- os.push_back (o);
- }
-
- // --database
- //
- {
- ::cli::option_names a;
- a.push_back ("-d");
- std::string dv;
- ::cli::option o ("--database", a, false, dv);
- os.push_back (o);
- }
-
- // --multi-database
- //
- {
- ::cli::option_names a;
- a.push_back ("-m");
- std::string dv;
- ::cli::option o ("--multi-database", a, false, dv);
- os.push_back (o);
- }
-
- // --default-database
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--default-database", a, false, dv);
- os.push_back (o);
- }
-
- // --generate-query
- //
- {
- ::cli::option_names a;
- a.push_back ("-q");
- std::string dv;
- ::cli::option o ("--generate-query", a, true, dv);
- os.push_back (o);
- }
-
- // --generate-prepared
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--generate-prepared", a, true, dv);
- os.push_back (o);
- }
-
- // --omit-unprepared
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--omit-unprepared", a, true, dv);
- os.push_back (o);
- }
-
- // --generate-session
- //
- {
- ::cli::option_names a;
- a.push_back ("-e");
- std::string dv;
- ::cli::option o ("--generate-session", a, true, dv);
- os.push_back (o);
- }
-
- // --generate-schema
- //
- {
- ::cli::option_names a;
- a.push_back ("-s");
- std::string dv;
- ::cli::option o ("--generate-schema", a, true, dv);
- os.push_back (o);
- }
-
- // --generate-schema-only
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--generate-schema-only", a, true, dv);
- os.push_back (o);
- }
-
- // --suppress-migration
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--suppress-migration", a, true, dv);
- os.push_back (o);
- }
-
- // --suppress-schema-version
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--suppress-schema-version", a, true, dv);
- os.push_back (o);
- }
-
- // --schema-version-table
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--schema-version-table", a, false, dv);
- os.push_back (o);
- }
-
- // --schema-format
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--schema-format", a, false, dv);
- os.push_back (o);
- }
-
- // --omit-drop
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--omit-drop", a, true, dv);
- os.push_back (o);
- }
-
- // --omit-create
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--omit-create", a, true, dv);
- os.push_back (o);
- }
-
- // --schema-name
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--schema-name", a, false, dv);
- os.push_back (o);
- }
-
- // --fkeys-deferrable-mode
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--fkeys-deferrable-mode", a, false, dv);
- os.push_back (o);
- }
-
- // --default-pointer
- //
- {
- ::cli::option_names a;
- std::string dv ("*");
- ::cli::option o ("--default-pointer", a, false, dv);
- os.push_back (o);
- }
-
- // --session-type
- //
- {
- ::cli::option_names a;
- std::string dv ("odb::session");
- ::cli::option o ("--session-type", a, false, dv);
- os.push_back (o);
- }
-
- // --profile
- //
- {
- ::cli::option_names a;
- a.push_back ("-p");
- std::string dv;
- ::cli::option o ("--profile", a, false, dv);
- os.push_back (o);
- }
-
- // --at-once
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--at-once", a, true, dv);
- os.push_back (o);
- }
-
- // --schema
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--schema", a, false, dv);
- os.push_back (o);
- }
-
- // --export-symbol
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--export-symbol", a, false, dv);
- os.push_back (o);
- }
-
- // --extern-symbol
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--extern-symbol", a, false, dv);
- os.push_back (o);
- }
-
- // --std
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--std", a, false, dv);
- os.push_back (o);
- }
-
- // --warn-hard-add
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--warn-hard-add", a, true, dv);
- os.push_back (o);
- }
-
- // --warn-hard-delete
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--warn-hard-delete", a, true, dv);
- os.push_back (o);
- }
-
- // --warn-hard
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--warn-hard", a, true, dv);
- os.push_back (o);
- }
-
- // --output-dir
- //
- {
- ::cli::option_names a;
- a.push_back ("-o");
- std::string dv;
- ::cli::option o ("--output-dir", a, false, dv);
- os.push_back (o);
- }
-
- // --input-name
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--input-name", a, false, dv);
- os.push_back (o);
- }
-
- // --changelog
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--changelog", a, false, dv);
- os.push_back (o);
- }
-
- // --changelog-in
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--changelog-in", a, false, dv);
- os.push_back (o);
- }
-
- // --changelog-out
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--changelog-out", a, false, dv);
- os.push_back (o);
- }
-
- // --changelog-dir
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--changelog-dir", a, false, dv);
- os.push_back (o);
- }
-
- // --init-changelog
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--init-changelog", a, true, dv);
- os.push_back (o);
- }
-
- // --odb-file-suffix
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--odb-file-suffix", a, false, dv);
- os.push_back (o);
- }
-
- // --sql-file-suffix
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--sql-file-suffix", a, false, dv);
- os.push_back (o);
- }
-
- // --schema-file-suffix
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--schema-file-suffix", a, false, dv);
- os.push_back (o);
- }
-
- // --changelog-file-suffix
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--changelog-file-suffix", a, false, dv);
- os.push_back (o);
- }
-
- // --hxx-suffix
- //
- {
- ::cli::option_names a;
- std::string dv (".hxx");
- ::cli::option o ("--hxx-suffix", a, false, dv);
- os.push_back (o);
- }
-
- // --ixx-suffix
- //
- {
- ::cli::option_names a;
- std::string dv (".ixx");
- ::cli::option o ("--ixx-suffix", a, false, dv);
- os.push_back (o);
- }
-
- // --cxx-suffix
- //
- {
- ::cli::option_names a;
- std::string dv (".cxx");
- ::cli::option o ("--cxx-suffix", a, false, dv);
- os.push_back (o);
- }
-
- // --sql-suffix
- //
- {
- ::cli::option_names a;
- std::string dv (".sql");
- ::cli::option o ("--sql-suffix", a, false, dv);
- os.push_back (o);
- }
-
- // --changelog-suffix
- //
- {
- ::cli::option_names a;
- std::string dv (".xml");
- ::cli::option o ("--changelog-suffix", a, false, dv);
- os.push_back (o);
- }
-
- // --hxx-prologue
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--hxx-prologue", a, false, dv);
- os.push_back (o);
- }
-
- // --ixx-prologue
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--ixx-prologue", a, false, dv);
- os.push_back (o);
- }
-
- // --cxx-prologue
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--cxx-prologue", a, false, dv);
- os.push_back (o);
- }
-
- // --schema-prologue
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--schema-prologue", a, false, dv);
- os.push_back (o);
- }
-
- // --sql-prologue
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--sql-prologue", a, false, dv);
- os.push_back (o);
- }
-
- // --migration-prologue
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--migration-prologue", a, false, dv);
- os.push_back (o);
- }
-
- // --sql-interlude
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--sql-interlude", a, false, dv);
- os.push_back (o);
- }
-
- // --hxx-epilogue
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--hxx-epilogue", a, false, dv);
- os.push_back (o);
- }
-
- // --ixx-epilogue
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--ixx-epilogue", a, false, dv);
- os.push_back (o);
- }
-
- // --cxx-epilogue
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--cxx-epilogue", a, false, dv);
- os.push_back (o);
- }
-
- // --schema-epilogue
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--schema-epilogue", a, false, dv);
- os.push_back (o);
- }
-
- // --sql-epilogue
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--sql-epilogue", a, false, dv);
- os.push_back (o);
- }
-
- // --migration-epilogue
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--migration-epilogue", a, false, dv);
- os.push_back (o);
- }
-
- // --hxx-prologue-file
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--hxx-prologue-file", a, false, dv);
- os.push_back (o);
- }
-
- // --ixx-prologue-file
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--ixx-prologue-file", a, false, dv);
- os.push_back (o);
- }
-
- // --cxx-prologue-file
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--cxx-prologue-file", a, false, dv);
- os.push_back (o);
- }
-
- // --schema-prologue-file
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--schema-prologue-file", a, false, dv);
- os.push_back (o);
- }
-
- // --sql-prologue-file
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--sql-prologue-file", a, false, dv);
- os.push_back (o);
- }
-
- // --migration-prologue-file
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--migration-prologue-file", a, false, dv);
- os.push_back (o);
- }
-
- // --sql-interlude-file
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--sql-interlude-file", a, false, dv);
- os.push_back (o);
- }
-
- // --hxx-epilogue-file
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--hxx-epilogue-file", a, false, dv);
- os.push_back (o);
- }
-
- // --ixx-epilogue-file
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--ixx-epilogue-file", a, false, dv);
- os.push_back (o);
- }
-
- // --cxx-epilogue-file
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--cxx-epilogue-file", a, false, dv);
- os.push_back (o);
- }
-
- // --schema-epilogue-file
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--schema-epilogue-file", a, false, dv);
- os.push_back (o);
- }
-
- // --sql-epilogue-file
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--sql-epilogue-file", a, false, dv);
- os.push_back (o);
- }
-
- // --migration-epilogue-file
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--migration-epilogue-file", a, false, dv);
- os.push_back (o);
- }
-
- // --odb-prologue
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--odb-prologue", a, false, dv);
- os.push_back (o);
- }
-
- // --odb-prologue-file
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--odb-prologue-file", a, false, dv);
- os.push_back (o);
- }
-
- // --odb-epilogue
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--odb-epilogue", a, false, dv);
- os.push_back (o);
- }
-
- // --odb-epilogue-file
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--odb-epilogue-file", a, false, dv);
- os.push_back (o);
- }
-
- // --table-prefix
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--table-prefix", a, false, dv);
- os.push_back (o);
- }
-
- // --index-suffix
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--index-suffix", a, false, dv);
- os.push_back (o);
- }
-
- // --fkey-suffix
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--fkey-suffix", a, false, dv);
- os.push_back (o);
- }
-
- // --sequence-suffix
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--sequence-suffix", a, false, dv);
- os.push_back (o);
- }
-
- // --sql-name-case
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--sql-name-case", a, false, dv);
- os.push_back (o);
- }
-
- // --table-regex
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--table-regex", a, false, dv);
- os.push_back (o);
- }
-
- // --column-regex
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--column-regex", a, false, dv);
- os.push_back (o);
- }
-
- // --index-regex
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--index-regex", a, false, dv);
- os.push_back (o);
- }
-
- // --fkey-regex
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--fkey-regex", a, false, dv);
- os.push_back (o);
- }
-
- // --sequence-regex
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--sequence-regex", a, false, dv);
- os.push_back (o);
- }
-
- // --statement-regex
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--statement-regex", a, false, dv);
- os.push_back (o);
- }
-
- // --sql-name-regex
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--sql-name-regex", a, false, dv);
- os.push_back (o);
- }
-
- // --sql-name-regex-trace
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--sql-name-regex-trace", a, true, dv);
- os.push_back (o);
- }
-
- // --accessor-regex
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--accessor-regex", a, false, dv);
- os.push_back (o);
- }
-
- // --accessor-regex-trace
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--accessor-regex-trace", a, true, dv);
- os.push_back (o);
- }
-
- // --modifier-regex
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--modifier-regex", a, false, dv);
- os.push_back (o);
- }
-
- // --modifier-regex-trace
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--modifier-regex-trace", a, true, dv);
- os.push_back (o);
- }
-
- // --include-with-brackets
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--include-with-brackets", a, true, dv);
- os.push_back (o);
- }
-
- // --include-prefix
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--include-prefix", a, false, dv);
- os.push_back (o);
- }
-
- // --include-regex
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--include-regex", a, false, dv);
- os.push_back (o);
- }
-
- // --include-regex-trace
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--include-regex-trace", a, true, dv);
- os.push_back (o);
- }
-
- // --guard-prefix
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--guard-prefix", a, false, dv);
- os.push_back (o);
- }
-
- // --show-sloc
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--show-sloc", a, true, dv);
- os.push_back (o);
- }
-
- // --sloc-limit
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--sloc-limit", a, false, dv);
- os.push_back (o);
- }
-
- // --options-file
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--options-file", a, false, dv);
- os.push_back (o);
- }
-
- // -x
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("-x", a, false, dv);
- os.push_back (o);
- }
-
- // -v
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("-v", a, true, dv);
- os.push_back (o);
- }
-
- // --trace
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--trace", a, true, dv);
- os.push_back (o);
- }
-
- // --mysql-engine
- //
- {
- ::cli::option_names a;
- std::string dv ("InnoDB");
- ::cli::option o ("--mysql-engine", a, false, dv);
- os.push_back (o);
- }
-
- // --sqlite-override-null
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--sqlite-override-null", a, true, dv);
- os.push_back (o);
- }
-
- // --sqlite-lax-auto-id
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--sqlite-lax-auto-id", a, true, dv);
- os.push_back (o);
- }
-
- // --pgsql-server-version
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--pgsql-server-version", a, false, dv);
- os.push_back (o);
- }
-
- // --oracle-client-version
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--oracle-client-version", a, false, dv);
- os.push_back (o);
- }
-
- // --oracle-warn-truncation
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--oracle-warn-truncation", a, true, dv);
- os.push_back (o);
- }
-
- // --mssql-server-version
- //
- {
- ::cli::option_names a;
- std::string dv;
- ::cli::option o ("--mssql-server-version", a, false, dv);
- os.push_back (o);
- }
-
- // --mssql-short-limit
- //
- {
- ::cli::option_names a;
- std::string dv ("1024");
- ::cli::option o ("--mssql-short-limit", a, false, dv);
- os.push_back (o);
- }
-}
-
-const ::cli::options& options::
-description ()
-{
- return _cli_options_desc_;
-}
-
-typedef
-std::map<std::string, void (*) (options&, ::cli::scanner&)>
-_cli_options_map;
-
-static _cli_options_map _cli_options_map_;
-
-struct _cli_options_map_init
-{
- _cli_options_map_init ()
- {
- _cli_options_map_["--build2-metadata"] =
- &::cli::thunk< options, std::uint64_t, &options::build2_metadata_,
- &options::build2_metadata_specified_ >;
- _cli_options_map_["--help"] =
- &::cli::thunk< options, bool, &options::help_ >;
- _cli_options_map_["--version"] =
- &::cli::thunk< options, bool, &options::version_ >;
- _cli_options_map_["-I"] =
- &::cli::thunk< options, std::vector<std::string>, &options::I_,
- &options::I_specified_ >;
- _cli_options_map_["-D"] =
- &::cli::thunk< options, std::vector<std::string>, &options::D_,
- &options::D_specified_ >;
- _cli_options_map_["-U"] =
- &::cli::thunk< options, std::vector<std::string>, &options::U_,
- &options::U_specified_ >;
- _cli_options_map_["--database"] =
- &::cli::thunk< options, std::vector< ::database>, &options::database_,
- &options::database_specified_ >;
- _cli_options_map_["-d"] =
- &::cli::thunk< options, std::vector< ::database>, &options::database_,
- &options::database_specified_ >;
- _cli_options_map_["--multi-database"] =
- &::cli::thunk< options, ::multi_database, &options::multi_database_,
- &options::multi_database_specified_ >;
- _cli_options_map_["-m"] =
- &::cli::thunk< options, ::multi_database, &options::multi_database_,
- &options::multi_database_specified_ >;
- _cli_options_map_["--default-database"] =
- &::cli::thunk< options, ::database, &options::default_database_,
- &options::default_database_specified_ >;
- _cli_options_map_["--generate-query"] =
- &::cli::thunk< options, bool, &options::generate_query_ >;
- _cli_options_map_["-q"] =
- &::cli::thunk< options, bool, &options::generate_query_ >;
- _cli_options_map_["--generate-prepared"] =
- &::cli::thunk< options, bool, &options::generate_prepared_ >;
- _cli_options_map_["--omit-unprepared"] =
- &::cli::thunk< options, bool, &options::omit_unprepared_ >;
- _cli_options_map_["--generate-session"] =
- &::cli::thunk< options, bool, &options::generate_session_ >;
- _cli_options_map_["-e"] =
- &::cli::thunk< options, bool, &options::generate_session_ >;
- _cli_options_map_["--generate-schema"] =
- &::cli::thunk< options, bool, &options::generate_schema_ >;
- _cli_options_map_["-s"] =
- &::cli::thunk< options, bool, &options::generate_schema_ >;
- _cli_options_map_["--generate-schema-only"] =
- &::cli::thunk< options, bool, &options::generate_schema_only_ >;
- _cli_options_map_["--suppress-migration"] =
- &::cli::thunk< options, bool, &options::suppress_migration_ >;
- _cli_options_map_["--suppress-schema-version"] =
- &::cli::thunk< options, bool, &options::suppress_schema_version_ >;
- _cli_options_map_["--schema-version-table"] =
- &::cli::thunk< options, database_map<qname>, &options::schema_version_table_,
- &options::schema_version_table_specified_ >;
- _cli_options_map_["--schema-format"] =
- &::cli::thunk< options, database_map<std::set< ::schema_format> >, &options::schema_format_,
- &options::schema_format_specified_ >;
- _cli_options_map_["--omit-drop"] =
- &::cli::thunk< options, bool, &options::omit_drop_ >;
- _cli_options_map_["--omit-create"] =
- &::cli::thunk< options, bool, &options::omit_create_ >;
- _cli_options_map_["--schema-name"] =
- &::cli::thunk< options, database_map<std::string>, &options::schema_name_,
- &options::schema_name_specified_ >;
- _cli_options_map_["--fkeys-deferrable-mode"] =
- &::cli::thunk< options, database_map<deferrable>, &options::fkeys_deferrable_mode_,
- &options::fkeys_deferrable_mode_specified_ >;
- _cli_options_map_["--default-pointer"] =
- &::cli::thunk< options, std::string, &options::default_pointer_,
- &options::default_pointer_specified_ >;
- _cli_options_map_["--session-type"] =
- &::cli::thunk< options, std::string, &options::session_type_,
- &options::session_type_specified_ >;
- _cli_options_map_["--profile"] =
- &::cli::thunk< options, std::string, &options::profile_,
- &options::profile_specified_ >;
- _cli_options_map_["-p"] =
- &::cli::thunk< options, std::string, &options::profile_,
- &options::profile_specified_ >;
- _cli_options_map_["--at-once"] =
- &::cli::thunk< options, bool, &options::at_once_ >;
- _cli_options_map_["--schema"] =
- &::cli::thunk< options, database_map<qname>, &options::schema_,
- &options::schema_specified_ >;
- _cli_options_map_["--export-symbol"] =
- &::cli::thunk< options, database_map<std::string>, &options::export_symbol_,
- &options::export_symbol_specified_ >;
- _cli_options_map_["--extern-symbol"] =
- &::cli::thunk< options, database_map<std::string>, &options::extern_symbol_,
- &options::extern_symbol_specified_ >;
- _cli_options_map_["--std"] =
- &::cli::thunk< options, cxx_version, &options::std_,
- &options::std_specified_ >;
- _cli_options_map_["--warn-hard-add"] =
- &::cli::thunk< options, bool, &options::warn_hard_add_ >;
- _cli_options_map_["--warn-hard-delete"] =
- &::cli::thunk< options, bool, &options::warn_hard_delete_ >;
- _cli_options_map_["--warn-hard"] =
- &::cli::thunk< options, bool, &options::warn_hard_ >;
- _cli_options_map_["--output-dir"] =
- &::cli::thunk< options, std::string, &options::output_dir_,
- &options::output_dir_specified_ >;
- _cli_options_map_["-o"] =
- &::cli::thunk< options, std::string, &options::output_dir_,
- &options::output_dir_specified_ >;
- _cli_options_map_["--input-name"] =
- &::cli::thunk< options, std::string, &options::input_name_,
- &options::input_name_specified_ >;
- _cli_options_map_["--changelog"] =
- &::cli::thunk< options, database_map<std::string>, &options::changelog_,
- &options::changelog_specified_ >;
- _cli_options_map_["--changelog-in"] =
- &::cli::thunk< options, database_map<std::string>, &options::changelog_in_,
- &options::changelog_in_specified_ >;
- _cli_options_map_["--changelog-out"] =
- &::cli::thunk< options, database_map<std::string>, &options::changelog_out_,
- &options::changelog_out_specified_ >;
- _cli_options_map_["--changelog-dir"] =
- &::cli::thunk< options, database_map<std::string>, &options::changelog_dir_,
- &options::changelog_dir_specified_ >;
- _cli_options_map_["--init-changelog"] =
- &::cli::thunk< options, bool, &options::init_changelog_ >;
- _cli_options_map_["--odb-file-suffix"] =
- &::cli::thunk< options, database_map<std::string>, &options::odb_file_suffix_,
- &options::odb_file_suffix_specified_ >;
- _cli_options_map_["--sql-file-suffix"] =
- &::cli::thunk< options, database_map<std::string>, &options::sql_file_suffix_,
- &options::sql_file_suffix_specified_ >;
- _cli_options_map_["--schema-file-suffix"] =
- &::cli::thunk< options, database_map<std::string>, &options::schema_file_suffix_,
- &options::schema_file_suffix_specified_ >;
- _cli_options_map_["--changelog-file-suffix"] =
- &::cli::thunk< options, database_map<std::string>, &options::changelog_file_suffix_,
- &options::changelog_file_suffix_specified_ >;
- _cli_options_map_["--hxx-suffix"] =
- &::cli::thunk< options, std::string, &options::hxx_suffix_,
- &options::hxx_suffix_specified_ >;
- _cli_options_map_["--ixx-suffix"] =
- &::cli::thunk< options, std::string, &options::ixx_suffix_,
- &options::ixx_suffix_specified_ >;
- _cli_options_map_["--cxx-suffix"] =
- &::cli::thunk< options, std::string, &options::cxx_suffix_,
- &options::cxx_suffix_specified_ >;
- _cli_options_map_["--sql-suffix"] =
- &::cli::thunk< options, std::string, &options::sql_suffix_,
- &options::sql_suffix_specified_ >;
- _cli_options_map_["--changelog-suffix"] =
- &::cli::thunk< options, std::string, &options::changelog_suffix_,
- &options::changelog_suffix_specified_ >;
- _cli_options_map_["--hxx-prologue"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::hxx_prologue_,
- &options::hxx_prologue_specified_ >;
- _cli_options_map_["--ixx-prologue"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::ixx_prologue_,
- &options::ixx_prologue_specified_ >;
- _cli_options_map_["--cxx-prologue"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::cxx_prologue_,
- &options::cxx_prologue_specified_ >;
- _cli_options_map_["--schema-prologue"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::schema_prologue_,
- &options::schema_prologue_specified_ >;
- _cli_options_map_["--sql-prologue"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_prologue_,
- &options::sql_prologue_specified_ >;
- _cli_options_map_["--migration-prologue"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::migration_prologue_,
- &options::migration_prologue_specified_ >;
- _cli_options_map_["--sql-interlude"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_interlude_,
- &options::sql_interlude_specified_ >;
- _cli_options_map_["--hxx-epilogue"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::hxx_epilogue_,
- &options::hxx_epilogue_specified_ >;
- _cli_options_map_["--ixx-epilogue"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::ixx_epilogue_,
- &options::ixx_epilogue_specified_ >;
- _cli_options_map_["--cxx-epilogue"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::cxx_epilogue_,
- &options::cxx_epilogue_specified_ >;
- _cli_options_map_["--schema-epilogue"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::schema_epilogue_,
- &options::schema_epilogue_specified_ >;
- _cli_options_map_["--sql-epilogue"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_epilogue_,
- &options::sql_epilogue_specified_ >;
- _cli_options_map_["--migration-epilogue"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::migration_epilogue_,
- &options::migration_epilogue_specified_ >;
- _cli_options_map_["--hxx-prologue-file"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::hxx_prologue_file_,
- &options::hxx_prologue_file_specified_ >;
- _cli_options_map_["--ixx-prologue-file"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::ixx_prologue_file_,
- &options::ixx_prologue_file_specified_ >;
- _cli_options_map_["--cxx-prologue-file"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::cxx_prologue_file_,
- &options::cxx_prologue_file_specified_ >;
- _cli_options_map_["--schema-prologue-file"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::schema_prologue_file_,
- &options::schema_prologue_file_specified_ >;
- _cli_options_map_["--sql-prologue-file"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_prologue_file_,
- &options::sql_prologue_file_specified_ >;
- _cli_options_map_["--migration-prologue-file"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::migration_prologue_file_,
- &options::migration_prologue_file_specified_ >;
- _cli_options_map_["--sql-interlude-file"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_interlude_file_,
- &options::sql_interlude_file_specified_ >;
- _cli_options_map_["--hxx-epilogue-file"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::hxx_epilogue_file_,
- &options::hxx_epilogue_file_specified_ >;
- _cli_options_map_["--ixx-epilogue-file"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::ixx_epilogue_file_,
- &options::ixx_epilogue_file_specified_ >;
- _cli_options_map_["--cxx-epilogue-file"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::cxx_epilogue_file_,
- &options::cxx_epilogue_file_specified_ >;
- _cli_options_map_["--schema-epilogue-file"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::schema_epilogue_file_,
- &options::schema_epilogue_file_specified_ >;
- _cli_options_map_["--sql-epilogue-file"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_epilogue_file_,
- &options::sql_epilogue_file_specified_ >;
- _cli_options_map_["--migration-epilogue-file"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::migration_epilogue_file_,
- &options::migration_epilogue_file_specified_ >;
- _cli_options_map_["--odb-prologue"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::odb_prologue_,
- &options::odb_prologue_specified_ >;
- _cli_options_map_["--odb-prologue-file"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::odb_prologue_file_,
- &options::odb_prologue_file_specified_ >;
- _cli_options_map_["--odb-epilogue"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::odb_epilogue_,
- &options::odb_epilogue_specified_ >;
- _cli_options_map_["--odb-epilogue-file"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::odb_epilogue_file_,
- &options::odb_epilogue_file_specified_ >;
- _cli_options_map_["--table-prefix"] =
- &::cli::thunk< options, database_map<std::string>, &options::table_prefix_,
- &options::table_prefix_specified_ >;
- _cli_options_map_["--index-suffix"] =
- &::cli::thunk< options, database_map<std::string>, &options::index_suffix_,
- &options::index_suffix_specified_ >;
- _cli_options_map_["--fkey-suffix"] =
- &::cli::thunk< options, database_map<std::string>, &options::fkey_suffix_,
- &options::fkey_suffix_specified_ >;
- _cli_options_map_["--sequence-suffix"] =
- &::cli::thunk< options, database_map<std::string>, &options::sequence_suffix_,
- &options::sequence_suffix_specified_ >;
- _cli_options_map_["--sql-name-case"] =
- &::cli::thunk< options, database_map<name_case>, &options::sql_name_case_,
- &options::sql_name_case_specified_ >;
- _cli_options_map_["--table-regex"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::table_regex_,
- &options::table_regex_specified_ >;
- _cli_options_map_["--column-regex"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::column_regex_,
- &options::column_regex_specified_ >;
- _cli_options_map_["--index-regex"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::index_regex_,
- &options::index_regex_specified_ >;
- _cli_options_map_["--fkey-regex"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::fkey_regex_,
- &options::fkey_regex_specified_ >;
- _cli_options_map_["--sequence-regex"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sequence_regex_,
- &options::sequence_regex_specified_ >;
- _cli_options_map_["--statement-regex"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::statement_regex_,
- &options::statement_regex_specified_ >;
- _cli_options_map_["--sql-name-regex"] =
- &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_name_regex_,
- &options::sql_name_regex_specified_ >;
- _cli_options_map_["--sql-name-regex-trace"] =
- &::cli::thunk< options, bool, &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_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_options_map_["--include-with-brackets"] =
- &::cli::thunk< options, bool, &options::include_with_brackets_ >;
- _cli_options_map_["--include-prefix"] =
- &::cli::thunk< options, std::string, &options::include_prefix_,
- &options::include_prefix_specified_ >;
- _cli_options_map_["--include-regex"] =
- &::cli::thunk< options, std::vector<std::string>, &options::include_regex_,
- &options::include_regex_specified_ >;
- _cli_options_map_["--include-regex-trace"] =
- &::cli::thunk< options, bool, &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_options_map_["--sloc-limit"] =
- &::cli::thunk< options, std::size_t, &options::sloc_limit_,
- &options::sloc_limit_specified_ >;
- _cli_options_map_["--options-file"] =
- &::cli::thunk< options, std::string, &options::options_file_,
- &options::options_file_specified_ >;
- _cli_options_map_["-x"] =
- &::cli::thunk< options, std::vector<std::string>, &options::x_,
- &options::x_specified_ >;
- _cli_options_map_["-v"] =
- &::cli::thunk< options, bool, &options::v_ >;
- _cli_options_map_["--trace"] =
- &::cli::thunk< options, bool, &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_options_map_["--sqlite-lax-auto-id"] =
- &::cli::thunk< options, bool, &options::sqlite_lax_auto_id_ >;
- _cli_options_map_["--pgsql-server-version"] =
- &::cli::thunk< options, ::pgsql_version, &options::pgsql_server_version_,
- &options::pgsql_server_version_specified_ >;
- _cli_options_map_["--oracle-client-version"] =
- &::cli::thunk< options, ::oracle_version, &options::oracle_client_version_,
- &options::oracle_client_version_specified_ >;
- _cli_options_map_["--oracle-warn-truncation"] =
- &::cli::thunk< options, bool, &options::oracle_warn_truncation_ >;
- _cli_options_map_["--mssql-server-version"] =
- &::cli::thunk< options, ::mssql_version, &options::mssql_server_version_,
- &options::mssql_server_version_specified_ >;
- _cli_options_map_["--mssql-short-limit"] =
- &::cli::thunk< options, unsigned int, &options::mssql_short_limit_,
- &options::mssql_short_limit_specified_ >;
- }
-};
-
-static _cli_options_map_init _cli_options_map_init_;
-
-bool options::
-_parse (const char* o, ::cli::scanner& s)
-{
- _cli_options_map::const_iterator i (_cli_options_map_.find (o));
-
- if (i != _cli_options_map_.end ())
- {
- (*(i->second)) (*this, s);
- return true;
- }
-
- return false;
-}
-
-bool options::
-_parse (::cli::scanner& s,
- ::cli::unknown_mode opt_mode,
- ::cli::unknown_mode arg_mode)
-{
- // Can't skip combined flags (--no-combined-flags).
- //
- assert (opt_mode != ::cli::unknown_mode::skip);
-
- bool r = false;
- bool opt = true;
-
- while (s.more ())
- {
- const char* o = s.peek ();
-
- if (std::strcmp (o, "--") == 0)
- {
- opt = false;
- s.skip ();
- r = true;
- continue;
- }
-
- if (opt)
- {
- if (_parse (o, s))
- {
- r = true;
- continue;
- }
-
- if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0')
- {
- // Handle combined option values.
- //
- std::string co;
- if (const char* v = std::strchr (o, '='))
- {
- co.assign (o, 0, v - o);
- ++v;
-
- int ac (2);
- char* av[] =
- {
- const_cast<char*> (co.c_str ()),
- const_cast<char*> (v)
- };
-
- ::cli::argv_scanner ns (0, ac, av);
-
- if (_parse (co.c_str (), ns))
- {
- // Parsed the option but not its value?
- //
- if (ns.end () != 2)
- throw ::cli::invalid_value (co, v);
-
- s.next ();
- r = true;
- continue;
- }
- else
- {
- // Set the unknown option and fall through.
- //
- o = co.c_str ();
- }
- }
-
- // Handle combined flags.
- //
- char cf[3];
- {
- const char* p = o + 1;
- for (; *p != '\0'; ++p)
- {
- if (!((*p >= 'a' && *p <= 'z') ||
- (*p >= 'A' && *p <= 'Z') ||
- (*p >= '0' && *p <= '9')))
- break;
- }
-
- if (*p == '\0')
- {
- for (p = o + 1; *p != '\0'; ++p)
- {
- std::strcpy (cf, "-");
- cf[1] = *p;
- cf[2] = '\0';
-
- int ac (1);
- char* av[] =
- {
- cf
- };
-
- ::cli::argv_scanner ns (0, ac, av);
-
- if (!_parse (cf, ns))
- break;
- }
-
- if (*p == '\0')
- {
- // All handled.
- //
- s.next ();
- r = true;
- continue;
- }
- else
- {
- // Set the unknown option and fall through.
- //
- o = cf;
- }
- }
- }
-
- switch (opt_mode)
- {
- case ::cli::unknown_mode::skip:
- {
- s.skip ();
- r = true;
- continue;
- }
- case ::cli::unknown_mode::stop:
- {
- break;
- }
- case ::cli::unknown_mode::fail:
- {
- throw ::cli::unknown_option (o);
- }
- }
-
- break;
- }
- }
-
- switch (arg_mode)
- {
- case ::cli::unknown_mode::skip:
- {
- s.skip ();
- r = true;
- continue;
- }
- case ::cli::unknown_mode::stop:
- {
- break;
- }
- case ::cli::unknown_mode::fail:
- {
- throw ::cli::unknown_argument (o);
- }
- }
-
- break;
- }
-
- return r;
-}
-
-// Begin epilogue.
-//
-//
-// End epilogue.
-
diff --git a/odb/options.hxx b/odb/options.hxx
deleted file mode 100644
index dce0fe0..0000000
--- a/odb/options.hxx
+++ /dev/null
@@ -1,2308 +0,0 @@
-// -*- C++ -*-
-//
-// This file was generated by CLI, a command line interface
-// compiler for C++.
-//
-
-#ifndef ODB_OPTIONS_HXX
-#define ODB_OPTIONS_HXX
-
-// Begin prologue.
-//
-//
-// End prologue.
-
-#include <list>
-#include <deque>
-#include <map>
-#include <vector>
-#include <iosfwd>
-#include <string>
-#include <cstddef>
-#include <exception>
-
-#ifndef CLI_POTENTIALLY_UNUSED
-# if defined(_MSC_VER) || defined(__xlC__)
-# define CLI_POTENTIALLY_UNUSED(x) (void*)&x
-# else
-# define CLI_POTENTIALLY_UNUSED(x) (void)x
-# endif
-#endif
-
-namespace cli
-{
- class usage_para
- {
- public:
- enum value
- {
- none,
- text,
- option
- };
-
- usage_para (value);
-
- operator value () const
- {
- return v_;
- }
-
- private:
- value v_;
- };
-
- class unknown_mode
- {
- public:
- enum value
- {
- skip,
- stop,
- fail
- };
-
- unknown_mode (value);
-
- operator value () const
- {
- return v_;
- }
-
- private:
- value v_;
- };
-
- // Exceptions.
- //
-
- class exception: public std::exception
- {
- public:
- virtual void
- print (::std::ostream&) const = 0;
- };
-
- ::std::ostream&
- operator<< (::std::ostream&, const exception&);
-
- class unknown_option: public exception
- {
- public:
- virtual
- ~unknown_option () throw ();
-
- unknown_option (const std::string& option);
-
- const std::string&
- option () const;
-
- virtual void
- print (::std::ostream&) const;
-
- virtual const char*
- what () const throw ();
-
- private:
- std::string option_;
- };
-
- class unknown_argument: public exception
- {
- public:
- virtual
- ~unknown_argument () throw ();
-
- unknown_argument (const std::string& argument);
-
- const std::string&
- argument () const;
-
- virtual void
- print (::std::ostream&) const;
-
- virtual const char*
- what () const throw ();
-
- private:
- std::string argument_;
- };
-
- class missing_value: public exception
- {
- public:
- virtual
- ~missing_value () throw ();
-
- missing_value (const std::string& option);
-
- const std::string&
- option () const;
-
- virtual void
- print (::std::ostream&) const;
-
- virtual const char*
- what () const throw ();
-
- private:
- std::string option_;
- };
-
- class invalid_value: public exception
- {
- public:
- virtual
- ~invalid_value () throw ();
-
- invalid_value (const std::string& option,
- const std::string& value,
- const std::string& message = std::string ());
-
- const std::string&
- option () const;
-
- const std::string&
- value () const;
-
- const std::string&
- message () const;
-
- virtual void
- print (::std::ostream&) const;
-
- virtual const char*
- what () const throw ();
-
- private:
- std::string option_;
- std::string value_;
- std::string message_;
- };
-
- class eos_reached: public exception
- {
- public:
- virtual void
- print (::std::ostream&) const;
-
- virtual const char*
- what () const throw ();
- };
-
- class file_io_failure: public exception
- {
- public:
- virtual
- ~file_io_failure () throw ();
-
- file_io_failure (const std::string& file);
-
- const std::string&
- file () const;
-
- virtual void
- print (::std::ostream&) const;
-
- virtual const char*
- what () const throw ();
-
- private:
- std::string file_;
- };
-
- class unmatched_quote: public exception
- {
- public:
- virtual
- ~unmatched_quote () throw ();
-
- unmatched_quote (const std::string& argument);
-
- const std::string&
- argument () const;
-
- virtual void
- print (::std::ostream&) const;
-
- virtual const char*
- what () const throw ();
-
- private:
- std::string argument_;
- };
-
- // Command line argument scanner interface.
- //
- // The values returned by next() are guaranteed to be valid
- // for the two previous arguments up until a call to a third
- // peek() or next().
- //
- class scanner
- {
- public:
- virtual
- ~scanner ();
-
- virtual bool
- more () = 0;
-
- virtual const char*
- peek () = 0;
-
- virtual const char*
- next () = 0;
-
- virtual void
- skip () = 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);
-
- int
- end () const;
-
- virtual bool
- more ();
-
- virtual const char*
- peek ();
-
- virtual const char*
- next ();
-
- virtual void
- skip ();
-
- private:
- int i_;
- int& argc_;
- char** argv_;
- bool erase_;
- };
-
- class argv_file_scanner: public argv_scanner
- {
- public:
- argv_file_scanner (int& argc,
- char** argv,
- const std::string& option,
- bool erase = false);
-
- argv_file_scanner (int start,
- int& argc,
- char** argv,
- const std::string& option,
- bool erase = false);
-
- argv_file_scanner (const std::string& file,
- const std::string& option);
-
- struct option_info
- {
- // If search_func is not NULL, it is called, with the arg
- // value as the second argument, to locate the options file.
- // If it returns an empty string, then the file is ignored.
- //
- const char* option;
- std::string (*search_func) (const char*, void* arg);
- void* arg;
- };
-
- argv_file_scanner (int& argc,
- char** argv,
- const option_info* options,
- std::size_t options_count,
- bool erase = false);
-
- argv_file_scanner (int start,
- int& argc,
- char** argv,
- const option_info* options,
- std::size_t options_count,
- bool erase = false);
-
- argv_file_scanner (const std::string& file,
- const option_info* options = 0,
- std::size_t options_count = 0);
-
- virtual bool
- more ();
-
- virtual const char*
- peek ();
-
- virtual const char*
- next ();
-
- virtual void
- skip ();
-
- // Return the file path if the peeked at argument came from a file and
- // the empty string otherwise. The reference is guaranteed to be valid
- // till the end of the scanner lifetime.
- //
- const std::string&
- peek_file ();
-
- // Return the 1-based line number if the peeked at argument came from
- // a file and zero otherwise.
- //
- std::size_t
- peek_line ();
-
- private:
- const option_info*
- find (const char*) const;
-
- void
- load (const std::string& file);
-
- typedef argv_scanner base;
-
- const std::string option_;
- option_info option_info_;
- const option_info* options_;
- std::size_t options_count_;
-
- struct arg
- {
- std::string value;
- const std::string* file;
- std::size_t line;
- };
-
- std::deque<arg> args_;
- std::list<std::string> files_;
-
- // Circular buffer of two arguments.
- //
- std::string hold_[2];
- std::size_t i_;
-
- bool skip_;
-
- static int zero_argc_;
- static std::string empty_string_;
- };
-
- typedef std::vector<std::string> option_names;
-
- class option
- {
- public:
-
- const std::string&
- name () const;
-
- const option_names&
- aliases () const;
-
- bool
- flag () const;
-
- const std::string&
- default_value () const;
-
- public:option ();
- option (const std::string& name,
- const option_names& aliases,
- bool flag,
- const std::string& default_value);
-
- private:
- std::string name_;
- option_names aliases_;
- bool flag_;
- std::string default_value_;
- };
-
- class options: public std::vector<option>
- {
- public:
- typedef std::vector<option> container_type;
-
- container_type::const_iterator
- find (const std::string& name) const;
-
- void
- push_back (const option&);
- private:
- typedef std::map<std::string, container_type::size_type> map_type;
- map_type map_;
- };
-
- template <typename X>
- struct parser;
-}
-
-#include <set>
-
-#include <vector>
-
-#include <string>
-
-#include <cstddef>
-
-#include <cstdint>
-
-#include <odb/option-types.hxx>
-
-class options
-{
- public:
- options ();
-
- options (int& argc,
- char** argv,
- bool erase = false,
- ::cli::unknown_mode option = ::cli::unknown_mode::fail,
- ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
-
- options (int start,
- int& argc,
- char** argv,
- bool erase = false,
- ::cli::unknown_mode option = ::cli::unknown_mode::fail,
- ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
-
- options (int& argc,
- char** argv,
- int& end,
- bool erase = false,
- ::cli::unknown_mode option = ::cli::unknown_mode::fail,
- ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
-
- options (int start,
- int& argc,
- char** argv,
- int& end,
- bool erase = false,
- ::cli::unknown_mode option = ::cli::unknown_mode::fail,
- ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
-
- options (::cli::scanner&,
- ::cli::unknown_mode option = ::cli::unknown_mode::fail,
- ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
-
- // Option accessors and modifiers.
- //
- const std::uint64_t&
- build2_metadata () const;
-
- std::uint64_t&
- build2_metadata ();
-
- void
- build2_metadata (const std::uint64_t&);
-
- bool
- build2_metadata_specified () const;
-
- void
- build2_metadata_specified (bool);
-
- const bool&
- help () const;
-
- bool&
- help ();
-
- void
- help (const bool&);
-
- const bool&
- version () const;
-
- bool&
- version ();
-
- void
- version (const bool&);
-
- const std::vector<std::string>&
- I () const;
-
- std::vector<std::string>&
- I ();
-
- void
- I (const std::vector<std::string>&);
-
- bool
- I_specified () const;
-
- void
- I_specified (bool);
-
- const std::vector<std::string>&
- D () const;
-
- std::vector<std::string>&
- D ();
-
- void
- D (const std::vector<std::string>&);
-
- bool
- D_specified () const;
-
- void
- D_specified (bool);
-
- const std::vector<std::string>&
- U () const;
-
- std::vector<std::string>&
- U ();
-
- void
- U (const std::vector<std::string>&);
-
- bool
- U_specified () const;
-
- void
- U_specified (bool);
-
- const std::vector< ::database>&
- database () const;
-
- std::vector< ::database>&
- database ();
-
- void
- database (const std::vector< ::database>&);
-
- bool
- database_specified () const;
-
- void
- database_specified (bool);
-
- const ::multi_database&
- multi_database () const;
-
- ::multi_database&
- multi_database ();
-
- void
- multi_database (const ::multi_database&);
-
- bool
- multi_database_specified () const;
-
- void
- multi_database_specified (bool);
-
- const ::database&
- default_database () const;
-
- ::database&
- default_database ();
-
- void
- default_database (const ::database&);
-
- bool
- default_database_specified () const;
-
- void
- default_database_specified (bool);
-
- const bool&
- generate_query () const;
-
- bool&
- generate_query ();
-
- void
- generate_query (const bool&);
-
- const bool&
- generate_prepared () const;
-
- bool&
- generate_prepared ();
-
- void
- generate_prepared (const bool&);
-
- const bool&
- omit_unprepared () const;
-
- bool&
- omit_unprepared ();
-
- void
- omit_unprepared (const bool&);
-
- const bool&
- generate_session () const;
-
- bool&
- generate_session ();
-
- void
- generate_session (const bool&);
-
- const bool&
- generate_schema () const;
-
- bool&
- generate_schema ();
-
- void
- generate_schema (const bool&);
-
- const bool&
- generate_schema_only () const;
-
- bool&
- generate_schema_only ();
-
- void
- generate_schema_only (const bool&);
-
- const bool&
- suppress_migration () const;
-
- bool&
- suppress_migration ();
-
- void
- suppress_migration (const bool&);
-
- const bool&
- suppress_schema_version () const;
-
- bool&
- suppress_schema_version ();
-
- void
- suppress_schema_version (const bool&);
-
- const database_map<qname>&
- schema_version_table () const;
-
- database_map<qname>&
- schema_version_table ();
-
- void
- schema_version_table (const database_map<qname>&);
-
- bool
- schema_version_table_specified () const;
-
- void
- schema_version_table_specified (bool);
-
- const database_map<std::set< ::schema_format> >&
- schema_format () const;
-
- database_map<std::set< ::schema_format> >&
- schema_format ();
-
- void
- schema_format (const database_map<std::set< ::schema_format> >&);
-
- bool
- schema_format_specified () const;
-
- void
- schema_format_specified (bool);
-
- const bool&
- omit_drop () const;
-
- bool&
- omit_drop ();
-
- void
- omit_drop (const bool&);
-
- const bool&
- omit_create () const;
-
- bool&
- omit_create ();
-
- void
- omit_create (const bool&);
-
- const database_map<std::string>&
- schema_name () const;
-
- database_map<std::string>&
- schema_name ();
-
- void
- schema_name (const database_map<std::string>&);
-
- bool
- schema_name_specified () const;
-
- void
- schema_name_specified (bool);
-
- const database_map<deferrable>&
- fkeys_deferrable_mode () const;
-
- database_map<deferrable>&
- fkeys_deferrable_mode ();
-
- void
- fkeys_deferrable_mode (const database_map<deferrable>&);
-
- bool
- fkeys_deferrable_mode_specified () const;
-
- void
- fkeys_deferrable_mode_specified (bool);
-
- const std::string&
- default_pointer () const;
-
- std::string&
- default_pointer ();
-
- void
- default_pointer (const std::string&);
-
- bool
- default_pointer_specified () const;
-
- void
- default_pointer_specified (bool);
-
- const std::string&
- session_type () const;
-
- std::string&
- session_type ();
-
- void
- session_type (const std::string&);
-
- bool
- session_type_specified () const;
-
- void
- session_type_specified (bool);
-
- const std::string&
- profile () const;
-
- std::string&
- profile ();
-
- void
- profile (const std::string&);
-
- bool
- profile_specified () const;
-
- void
- profile_specified (bool);
-
- const bool&
- at_once () const;
-
- bool&
- at_once ();
-
- void
- at_once (const bool&);
-
- const database_map<qname>&
- schema () const;
-
- database_map<qname>&
- schema ();
-
- void
- schema (const database_map<qname>&);
-
- bool
- schema_specified () const;
-
- void
- schema_specified (bool);
-
- const database_map<std::string>&
- export_symbol () const;
-
- database_map<std::string>&
- export_symbol ();
-
- void
- export_symbol (const database_map<std::string>&);
-
- bool
- export_symbol_specified () const;
-
- void
- export_symbol_specified (bool);
-
- const database_map<std::string>&
- extern_symbol () const;
-
- database_map<std::string>&
- extern_symbol ();
-
- void
- extern_symbol (const database_map<std::string>&);
-
- bool
- extern_symbol_specified () const;
-
- void
- extern_symbol_specified (bool);
-
- const cxx_version&
- std () const;
-
- cxx_version&
- std ();
-
- void
- std (const cxx_version&);
-
- bool
- std_specified () const;
-
- void
- std_specified (bool);
-
- const bool&
- warn_hard_add () const;
-
- bool&
- warn_hard_add ();
-
- void
- warn_hard_add (const bool&);
-
- const bool&
- warn_hard_delete () const;
-
- bool&
- warn_hard_delete ();
-
- void
- warn_hard_delete (const bool&);
-
- const bool&
- warn_hard () const;
-
- bool&
- warn_hard ();
-
- void
- warn_hard (const bool&);
-
- const std::string&
- output_dir () const;
-
- std::string&
- output_dir ();
-
- void
- output_dir (const std::string&);
-
- bool
- output_dir_specified () const;
-
- void
- output_dir_specified (bool);
-
- const std::string&
- input_name () const;
-
- std::string&
- input_name ();
-
- void
- input_name (const std::string&);
-
- bool
- input_name_specified () const;
-
- void
- input_name_specified (bool);
-
- const database_map<std::string>&
- changelog () const;
-
- database_map<std::string>&
- changelog ();
-
- void
- changelog (const database_map<std::string>&);
-
- bool
- changelog_specified () const;
-
- void
- changelog_specified (bool);
-
- const database_map<std::string>&
- changelog_in () const;
-
- database_map<std::string>&
- changelog_in ();
-
- void
- changelog_in (const database_map<std::string>&);
-
- bool
- changelog_in_specified () const;
-
- void
- changelog_in_specified (bool);
-
- const database_map<std::string>&
- changelog_out () const;
-
- database_map<std::string>&
- changelog_out ();
-
- void
- changelog_out (const database_map<std::string>&);
-
- bool
- changelog_out_specified () const;
-
- void
- changelog_out_specified (bool);
-
- const database_map<std::string>&
- changelog_dir () const;
-
- database_map<std::string>&
- changelog_dir ();
-
- void
- changelog_dir (const database_map<std::string>&);
-
- bool
- changelog_dir_specified () const;
-
- void
- changelog_dir_specified (bool);
-
- const bool&
- init_changelog () const;
-
- bool&
- init_changelog ();
-
- void
- init_changelog (const bool&);
-
- const database_map<std::string>&
- odb_file_suffix () const;
-
- database_map<std::string>&
- odb_file_suffix ();
-
- void
- odb_file_suffix (const database_map<std::string>&);
-
- bool
- odb_file_suffix_specified () const;
-
- void
- odb_file_suffix_specified (bool);
-
- const database_map<std::string>&
- sql_file_suffix () const;
-
- database_map<std::string>&
- sql_file_suffix ();
-
- void
- sql_file_suffix (const database_map<std::string>&);
-
- bool
- sql_file_suffix_specified () const;
-
- void
- sql_file_suffix_specified (bool);
-
- const database_map<std::string>&
- schema_file_suffix () const;
-
- database_map<std::string>&
- schema_file_suffix ();
-
- void
- schema_file_suffix (const database_map<std::string>&);
-
- bool
- schema_file_suffix_specified () const;
-
- void
- schema_file_suffix_specified (bool);
-
- const database_map<std::string>&
- changelog_file_suffix () const;
-
- database_map<std::string>&
- changelog_file_suffix ();
-
- void
- changelog_file_suffix (const database_map<std::string>&);
-
- bool
- changelog_file_suffix_specified () const;
-
- void
- changelog_file_suffix_specified (bool);
-
- const std::string&
- hxx_suffix () const;
-
- std::string&
- hxx_suffix ();
-
- void
- hxx_suffix (const std::string&);
-
- bool
- hxx_suffix_specified () const;
-
- void
- hxx_suffix_specified (bool);
-
- const std::string&
- ixx_suffix () const;
-
- std::string&
- ixx_suffix ();
-
- void
- ixx_suffix (const std::string&);
-
- bool
- ixx_suffix_specified () const;
-
- void
- ixx_suffix_specified (bool);
-
- const std::string&
- cxx_suffix () const;
-
- std::string&
- cxx_suffix ();
-
- void
- cxx_suffix (const std::string&);
-
- bool
- cxx_suffix_specified () const;
-
- void
- cxx_suffix_specified (bool);
-
- const std::string&
- sql_suffix () const;
-
- std::string&
- sql_suffix ();
-
- void
- sql_suffix (const std::string&);
-
- bool
- sql_suffix_specified () const;
-
- void
- sql_suffix_specified (bool);
-
- const std::string&
- changelog_suffix () const;
-
- std::string&
- changelog_suffix ();
-
- void
- changelog_suffix (const std::string&);
-
- bool
- changelog_suffix_specified () const;
-
- void
- changelog_suffix_specified (bool);
-
- const database_map<std::vector<std::string> >&
- hxx_prologue () const;
-
- database_map<std::vector<std::string> >&
- hxx_prologue ();
-
- void
- hxx_prologue (const database_map<std::vector<std::string> >&);
-
- bool
- hxx_prologue_specified () const;
-
- void
- hxx_prologue_specified (bool);
-
- const database_map<std::vector<std::string> >&
- ixx_prologue () const;
-
- database_map<std::vector<std::string> >&
- ixx_prologue ();
-
- void
- ixx_prologue (const database_map<std::vector<std::string> >&);
-
- bool
- ixx_prologue_specified () const;
-
- void
- ixx_prologue_specified (bool);
-
- const database_map<std::vector<std::string> >&
- cxx_prologue () const;
-
- database_map<std::vector<std::string> >&
- cxx_prologue ();
-
- void
- cxx_prologue (const database_map<std::vector<std::string> >&);
-
- bool
- cxx_prologue_specified () const;
-
- void
- cxx_prologue_specified (bool);
-
- const database_map<std::vector<std::string> >&
- schema_prologue () const;
-
- database_map<std::vector<std::string> >&
- schema_prologue ();
-
- void
- schema_prologue (const database_map<std::vector<std::string> >&);
-
- bool
- schema_prologue_specified () const;
-
- void
- schema_prologue_specified (bool);
-
- const database_map<std::vector<std::string> >&
- sql_prologue () const;
-
- database_map<std::vector<std::string> >&
- sql_prologue ();
-
- void
- sql_prologue (const database_map<std::vector<std::string> >&);
-
- bool
- sql_prologue_specified () const;
-
- void
- sql_prologue_specified (bool);
-
- const database_map<std::vector<std::string> >&
- migration_prologue () const;
-
- database_map<std::vector<std::string> >&
- migration_prologue ();
-
- void
- migration_prologue (const database_map<std::vector<std::string> >&);
-
- bool
- migration_prologue_specified () const;
-
- void
- migration_prologue_specified (bool);
-
- const database_map<std::vector<std::string> >&
- sql_interlude () const;
-
- database_map<std::vector<std::string> >&
- sql_interlude ();
-
- void
- sql_interlude (const database_map<std::vector<std::string> >&);
-
- bool
- sql_interlude_specified () const;
-
- void
- sql_interlude_specified (bool);
-
- const database_map<std::vector<std::string> >&
- hxx_epilogue () const;
-
- database_map<std::vector<std::string> >&
- hxx_epilogue ();
-
- void
- hxx_epilogue (const database_map<std::vector<std::string> >&);
-
- bool
- hxx_epilogue_specified () const;
-
- void
- hxx_epilogue_specified (bool);
-
- const database_map<std::vector<std::string> >&
- ixx_epilogue () const;
-
- database_map<std::vector<std::string> >&
- ixx_epilogue ();
-
- void
- ixx_epilogue (const database_map<std::vector<std::string> >&);
-
- bool
- ixx_epilogue_specified () const;
-
- void
- ixx_epilogue_specified (bool);
-
- const database_map<std::vector<std::string> >&
- cxx_epilogue () const;
-
- database_map<std::vector<std::string> >&
- cxx_epilogue ();
-
- void
- cxx_epilogue (const database_map<std::vector<std::string> >&);
-
- bool
- cxx_epilogue_specified () const;
-
- void
- cxx_epilogue_specified (bool);
-
- const database_map<std::vector<std::string> >&
- schema_epilogue () const;
-
- database_map<std::vector<std::string> >&
- schema_epilogue ();
-
- void
- schema_epilogue (const database_map<std::vector<std::string> >&);
-
- bool
- schema_epilogue_specified () const;
-
- void
- schema_epilogue_specified (bool);
-
- const database_map<std::vector<std::string> >&
- sql_epilogue () const;
-
- database_map<std::vector<std::string> >&
- sql_epilogue ();
-
- void
- sql_epilogue (const database_map<std::vector<std::string> >&);
-
- bool
- sql_epilogue_specified () const;
-
- void
- sql_epilogue_specified (bool);
-
- const database_map<std::vector<std::string> >&
- migration_epilogue () const;
-
- database_map<std::vector<std::string> >&
- migration_epilogue ();
-
- void
- migration_epilogue (const database_map<std::vector<std::string> >&);
-
- bool
- migration_epilogue_specified () const;
-
- void
- migration_epilogue_specified (bool);
-
- const database_map<std::vector<std::string> >&
- hxx_prologue_file () const;
-
- database_map<std::vector<std::string> >&
- hxx_prologue_file ();
-
- void
- hxx_prologue_file (const database_map<std::vector<std::string> >&);
-
- bool
- hxx_prologue_file_specified () const;
-
- void
- hxx_prologue_file_specified (bool);
-
- const database_map<std::vector<std::string> >&
- ixx_prologue_file () const;
-
- database_map<std::vector<std::string> >&
- ixx_prologue_file ();
-
- void
- ixx_prologue_file (const database_map<std::vector<std::string> >&);
-
- bool
- ixx_prologue_file_specified () const;
-
- void
- ixx_prologue_file_specified (bool);
-
- const database_map<std::vector<std::string> >&
- cxx_prologue_file () const;
-
- database_map<std::vector<std::string> >&
- cxx_prologue_file ();
-
- void
- cxx_prologue_file (const database_map<std::vector<std::string> >&);
-
- bool
- cxx_prologue_file_specified () const;
-
- void
- cxx_prologue_file_specified (bool);
-
- const database_map<std::vector<std::string> >&
- schema_prologue_file () const;
-
- database_map<std::vector<std::string> >&
- schema_prologue_file ();
-
- void
- schema_prologue_file (const database_map<std::vector<std::string> >&);
-
- bool
- schema_prologue_file_specified () const;
-
- void
- schema_prologue_file_specified (bool);
-
- const database_map<std::vector<std::string> >&
- sql_prologue_file () const;
-
- database_map<std::vector<std::string> >&
- sql_prologue_file ();
-
- void
- sql_prologue_file (const database_map<std::vector<std::string> >&);
-
- bool
- sql_prologue_file_specified () const;
-
- void
- sql_prologue_file_specified (bool);
-
- const database_map<std::vector<std::string> >&
- migration_prologue_file () const;
-
- database_map<std::vector<std::string> >&
- migration_prologue_file ();
-
- void
- migration_prologue_file (const database_map<std::vector<std::string> >&);
-
- bool
- migration_prologue_file_specified () const;
-
- void
- migration_prologue_file_specified (bool);
-
- const database_map<std::vector<std::string> >&
- sql_interlude_file () const;
-
- database_map<std::vector<std::string> >&
- sql_interlude_file ();
-
- void
- sql_interlude_file (const database_map<std::vector<std::string> >&);
-
- bool
- sql_interlude_file_specified () const;
-
- void
- sql_interlude_file_specified (bool);
-
- const database_map<std::vector<std::string> >&
- hxx_epilogue_file () const;
-
- database_map<std::vector<std::string> >&
- hxx_epilogue_file ();
-
- void
- hxx_epilogue_file (const database_map<std::vector<std::string> >&);
-
- bool
- hxx_epilogue_file_specified () const;
-
- void
- hxx_epilogue_file_specified (bool);
-
- const database_map<std::vector<std::string> >&
- ixx_epilogue_file () const;
-
- database_map<std::vector<std::string> >&
- ixx_epilogue_file ();
-
- void
- ixx_epilogue_file (const database_map<std::vector<std::string> >&);
-
- bool
- ixx_epilogue_file_specified () const;
-
- void
- ixx_epilogue_file_specified (bool);
-
- const database_map<std::vector<std::string> >&
- cxx_epilogue_file () const;
-
- database_map<std::vector<std::string> >&
- cxx_epilogue_file ();
-
- void
- cxx_epilogue_file (const database_map<std::vector<std::string> >&);
-
- bool
- cxx_epilogue_file_specified () const;
-
- void
- cxx_epilogue_file_specified (bool);
-
- const database_map<std::vector<std::string> >&
- schema_epilogue_file () const;
-
- database_map<std::vector<std::string> >&
- schema_epilogue_file ();
-
- void
- schema_epilogue_file (const database_map<std::vector<std::string> >&);
-
- bool
- schema_epilogue_file_specified () const;
-
- void
- schema_epilogue_file_specified (bool);
-
- const database_map<std::vector<std::string> >&
- sql_epilogue_file () const;
-
- database_map<std::vector<std::string> >&
- sql_epilogue_file ();
-
- void
- sql_epilogue_file (const database_map<std::vector<std::string> >&);
-
- bool
- sql_epilogue_file_specified () const;
-
- void
- sql_epilogue_file_specified (bool);
-
- const database_map<std::vector<std::string> >&
- migration_epilogue_file () const;
-
- database_map<std::vector<std::string> >&
- migration_epilogue_file ();
-
- void
- migration_epilogue_file (const database_map<std::vector<std::string> >&);
-
- bool
- migration_epilogue_file_specified () const;
-
- void
- migration_epilogue_file_specified (bool);
-
- const database_map<std::vector<std::string> >&
- odb_prologue () const;
-
- database_map<std::vector<std::string> >&
- odb_prologue ();
-
- void
- odb_prologue (const database_map<std::vector<std::string> >&);
-
- bool
- odb_prologue_specified () const;
-
- void
- odb_prologue_specified (bool);
-
- const database_map<std::vector<std::string> >&
- odb_prologue_file () const;
-
- database_map<std::vector<std::string> >&
- odb_prologue_file ();
-
- void
- odb_prologue_file (const database_map<std::vector<std::string> >&);
-
- bool
- odb_prologue_file_specified () const;
-
- void
- odb_prologue_file_specified (bool);
-
- const database_map<std::vector<std::string> >&
- odb_epilogue () const;
-
- database_map<std::vector<std::string> >&
- odb_epilogue ();
-
- void
- odb_epilogue (const database_map<std::vector<std::string> >&);
-
- bool
- odb_epilogue_specified () const;
-
- void
- odb_epilogue_specified (bool);
-
- const database_map<std::vector<std::string> >&
- odb_epilogue_file () const;
-
- database_map<std::vector<std::string> >&
- odb_epilogue_file ();
-
- void
- odb_epilogue_file (const database_map<std::vector<std::string> >&);
-
- bool
- odb_epilogue_file_specified () const;
-
- void
- odb_epilogue_file_specified (bool);
-
- const database_map<std::string>&
- table_prefix () const;
-
- database_map<std::string>&
- table_prefix ();
-
- void
- table_prefix (const database_map<std::string>&);
-
- bool
- table_prefix_specified () const;
-
- void
- table_prefix_specified (bool);
-
- const database_map<std::string>&
- index_suffix () const;
-
- database_map<std::string>&
- index_suffix ();
-
- void
- index_suffix (const database_map<std::string>&);
-
- bool
- index_suffix_specified () const;
-
- void
- index_suffix_specified (bool);
-
- const database_map<std::string>&
- fkey_suffix () const;
-
- database_map<std::string>&
- fkey_suffix ();
-
- void
- fkey_suffix (const database_map<std::string>&);
-
- bool
- fkey_suffix_specified () const;
-
- void
- fkey_suffix_specified (bool);
-
- const database_map<std::string>&
- sequence_suffix () const;
-
- database_map<std::string>&
- sequence_suffix ();
-
- void
- sequence_suffix (const database_map<std::string>&);
-
- bool
- sequence_suffix_specified () const;
-
- void
- sequence_suffix_specified (bool);
-
- const database_map<name_case>&
- sql_name_case () const;
-
- database_map<name_case>&
- sql_name_case ();
-
- void
- sql_name_case (const database_map<name_case>&);
-
- bool
- sql_name_case_specified () const;
-
- void
- sql_name_case_specified (bool);
-
- const database_map<std::vector<std::string> >&
- table_regex () const;
-
- database_map<std::vector<std::string> >&
- table_regex ();
-
- void
- table_regex (const database_map<std::vector<std::string> >&);
-
- bool
- table_regex_specified () const;
-
- void
- table_regex_specified (bool);
-
- const database_map<std::vector<std::string> >&
- column_regex () const;
-
- database_map<std::vector<std::string> >&
- column_regex ();
-
- void
- column_regex (const database_map<std::vector<std::string> >&);
-
- bool
- column_regex_specified () const;
-
- void
- column_regex_specified (bool);
-
- const database_map<std::vector<std::string> >&
- index_regex () const;
-
- database_map<std::vector<std::string> >&
- index_regex ();
-
- void
- index_regex (const database_map<std::vector<std::string> >&);
-
- bool
- index_regex_specified () const;
-
- void
- index_regex_specified (bool);
-
- const database_map<std::vector<std::string> >&
- fkey_regex () const;
-
- database_map<std::vector<std::string> >&
- fkey_regex ();
-
- void
- fkey_regex (const database_map<std::vector<std::string> >&);
-
- bool
- fkey_regex_specified () const;
-
- void
- fkey_regex_specified (bool);
-
- const database_map<std::vector<std::string> >&
- sequence_regex () const;
-
- database_map<std::vector<std::string> >&
- sequence_regex ();
-
- void
- sequence_regex (const database_map<std::vector<std::string> >&);
-
- bool
- sequence_regex_specified () const;
-
- void
- sequence_regex_specified (bool);
-
- const database_map<std::vector<std::string> >&
- statement_regex () const;
-
- database_map<std::vector<std::string> >&
- statement_regex ();
-
- void
- statement_regex (const database_map<std::vector<std::string> >&);
-
- bool
- statement_regex_specified () const;
-
- void
- statement_regex_specified (bool);
-
- const database_map<std::vector<std::string> >&
- sql_name_regex () const;
-
- database_map<std::vector<std::string> >&
- sql_name_regex ();
-
- void
- sql_name_regex (const database_map<std::vector<std::string> >&);
-
- bool
- sql_name_regex_specified () const;
-
- void
- sql_name_regex_specified (bool);
-
- const bool&
- sql_name_regex_trace () const;
-
- bool&
- sql_name_regex_trace ();
-
- void
- sql_name_regex_trace (const bool&);
-
- const std::vector<std::string>&
- accessor_regex () const;
-
- std::vector<std::string>&
- accessor_regex ();
-
- void
- accessor_regex (const std::vector<std::string>&);
-
- bool
- accessor_regex_specified () const;
-
- void
- accessor_regex_specified (bool);
-
- const bool&
- accessor_regex_trace () const;
-
- bool&
- accessor_regex_trace ();
-
- void
- accessor_regex_trace (const bool&);
-
- const std::vector<std::string>&
- modifier_regex () const;
-
- std::vector<std::string>&
- modifier_regex ();
-
- void
- modifier_regex (const std::vector<std::string>&);
-
- bool
- modifier_regex_specified () const;
-
- void
- modifier_regex_specified (bool);
-
- const bool&
- modifier_regex_trace () const;
-
- bool&
- modifier_regex_trace ();
-
- void
- modifier_regex_trace (const bool&);
-
- const bool&
- include_with_brackets () const;
-
- bool&
- include_with_brackets ();
-
- void
- include_with_brackets (const bool&);
-
- const std::string&
- include_prefix () const;
-
- std::string&
- include_prefix ();
-
- void
- include_prefix (const std::string&);
-
- bool
- include_prefix_specified () const;
-
- void
- include_prefix_specified (bool);
-
- const std::vector<std::string>&
- include_regex () const;
-
- std::vector<std::string>&
- include_regex ();
-
- void
- include_regex (const std::vector<std::string>&);
-
- bool
- include_regex_specified () const;
-
- void
- include_regex_specified (bool);
-
- const bool&
- include_regex_trace () const;
-
- bool&
- include_regex_trace ();
-
- void
- include_regex_trace (const bool&);
-
- const std::string&
- guard_prefix () const;
-
- std::string&
- guard_prefix ();
-
- void
- guard_prefix (const std::string&);
-
- bool
- guard_prefix_specified () const;
-
- void
- guard_prefix_specified (bool);
-
- const bool&
- show_sloc () const;
-
- bool&
- show_sloc ();
-
- void
- show_sloc (const bool&);
-
- const std::size_t&
- sloc_limit () const;
-
- std::size_t&
- sloc_limit ();
-
- void
- sloc_limit (const std::size_t&);
-
- bool
- sloc_limit_specified () const;
-
- void
- sloc_limit_specified (bool);
-
- const std::string&
- options_file () const;
-
- std::string&
- options_file ();
-
- void
- options_file (const std::string&);
-
- bool
- options_file_specified () const;
-
- void
- options_file_specified (bool);
-
- const std::vector<std::string>&
- x () const;
-
- std::vector<std::string>&
- x ();
-
- void
- x (const std::vector<std::string>&);
-
- bool
- x_specified () const;
-
- void
- x_specified (bool);
-
- const bool&
- v () const;
-
- bool&
- v ();
-
- void
- v (const bool&);
-
- const bool&
- trace () const;
-
- bool&
- trace ();
-
- void
- trace (const bool&);
-
- const std::string&
- mysql_engine () const;
-
- std::string&
- mysql_engine ();
-
- void
- mysql_engine (const std::string&);
-
- bool
- mysql_engine_specified () const;
-
- void
- mysql_engine_specified (bool);
-
- const bool&
- sqlite_override_null () const;
-
- bool&
- sqlite_override_null ();
-
- void
- sqlite_override_null (const bool&);
-
- const bool&
- sqlite_lax_auto_id () const;
-
- bool&
- sqlite_lax_auto_id ();
-
- void
- sqlite_lax_auto_id (const bool&);
-
- const ::pgsql_version&
- pgsql_server_version () const;
-
- ::pgsql_version&
- pgsql_server_version ();
-
- void
- pgsql_server_version (const ::pgsql_version&);
-
- bool
- pgsql_server_version_specified () const;
-
- void
- pgsql_server_version_specified (bool);
-
- const ::oracle_version&
- oracle_client_version () const;
-
- ::oracle_version&
- oracle_client_version ();
-
- void
- oracle_client_version (const ::oracle_version&);
-
- bool
- oracle_client_version_specified () const;
-
- void
- oracle_client_version_specified (bool);
-
- const bool&
- oracle_warn_truncation () const;
-
- bool&
- oracle_warn_truncation ();
-
- void
- oracle_warn_truncation (const bool&);
-
- const ::mssql_version&
- mssql_server_version () const;
-
- ::mssql_version&
- mssql_server_version ();
-
- void
- mssql_server_version (const ::mssql_version&);
-
- bool
- mssql_server_version_specified () const;
-
- void
- mssql_server_version_specified (bool);
-
- const unsigned int&
- mssql_short_limit () const;
-
- unsigned int&
- mssql_short_limit ();
-
- void
- mssql_short_limit (const unsigned int&);
-
- bool
- mssql_short_limit_specified () const;
-
- void
- mssql_short_limit_specified (bool);
-
- // Print usage information.
- //
- static ::cli::usage_para
- print_usage (::std::ostream&,
- ::cli::usage_para = ::cli::usage_para::none);
-
- // Option description.
- //
- static const ::cli::options&
- description ();
-
- // Implementation details.
- //
- protected:
- friend struct _cli_options_desc_type;
-
- static void
- fill (::cli::options&);
-
- bool
- _parse (const char*, ::cli::scanner&);
-
- private:
- bool
- _parse (::cli::scanner&,
- ::cli::unknown_mode option,
- ::cli::unknown_mode argument);
-
- public:
- std::uint64_t build2_metadata_;
- bool build2_metadata_specified_;
- bool help_;
- bool version_;
- std::vector<std::string> I_;
- bool I_specified_;
- std::vector<std::string> D_;
- bool D_specified_;
- std::vector<std::string> U_;
- bool U_specified_;
- std::vector< ::database> database_;
- bool database_specified_;
- ::multi_database multi_database_;
- bool multi_database_specified_;
- ::database default_database_;
- bool default_database_specified_;
- bool generate_query_;
- bool generate_prepared_;
- bool omit_unprepared_;
- bool generate_session_;
- bool generate_schema_;
- bool generate_schema_only_;
- bool suppress_migration_;
- bool suppress_schema_version_;
- database_map<qname> schema_version_table_;
- bool schema_version_table_specified_;
- database_map<std::set< ::schema_format> > schema_format_;
- bool schema_format_specified_;
- bool omit_drop_;
- bool omit_create_;
- database_map<std::string> schema_name_;
- bool schema_name_specified_;
- database_map<deferrable> fkeys_deferrable_mode_;
- bool fkeys_deferrable_mode_specified_;
- std::string default_pointer_;
- bool default_pointer_specified_;
- std::string session_type_;
- bool session_type_specified_;
- std::string profile_;
- bool profile_specified_;
- bool at_once_;
- database_map<qname> schema_;
- bool schema_specified_;
- database_map<std::string> export_symbol_;
- bool export_symbol_specified_;
- database_map<std::string> extern_symbol_;
- bool extern_symbol_specified_;
- cxx_version std_;
- bool std_specified_;
- bool warn_hard_add_;
- bool warn_hard_delete_;
- bool warn_hard_;
- std::string output_dir_;
- bool output_dir_specified_;
- std::string input_name_;
- bool input_name_specified_;
- database_map<std::string> changelog_;
- bool changelog_specified_;
- database_map<std::string> changelog_in_;
- bool changelog_in_specified_;
- database_map<std::string> changelog_out_;
- bool changelog_out_specified_;
- database_map<std::string> changelog_dir_;
- bool changelog_dir_specified_;
- bool init_changelog_;
- database_map<std::string> odb_file_suffix_;
- bool odb_file_suffix_specified_;
- database_map<std::string> sql_file_suffix_;
- bool sql_file_suffix_specified_;
- database_map<std::string> schema_file_suffix_;
- bool schema_file_suffix_specified_;
- database_map<std::string> changelog_file_suffix_;
- bool changelog_file_suffix_specified_;
- std::string hxx_suffix_;
- bool hxx_suffix_specified_;
- std::string ixx_suffix_;
- bool ixx_suffix_specified_;
- std::string cxx_suffix_;
- bool cxx_suffix_specified_;
- std::string sql_suffix_;
- bool sql_suffix_specified_;
- std::string changelog_suffix_;
- bool changelog_suffix_specified_;
- database_map<std::vector<std::string> > hxx_prologue_;
- bool hxx_prologue_specified_;
- database_map<std::vector<std::string> > ixx_prologue_;
- bool ixx_prologue_specified_;
- database_map<std::vector<std::string> > cxx_prologue_;
- bool cxx_prologue_specified_;
- database_map<std::vector<std::string> > schema_prologue_;
- bool schema_prologue_specified_;
- database_map<std::vector<std::string> > sql_prologue_;
- bool sql_prologue_specified_;
- database_map<std::vector<std::string> > migration_prologue_;
- bool migration_prologue_specified_;
- database_map<std::vector<std::string> > sql_interlude_;
- bool sql_interlude_specified_;
- database_map<std::vector<std::string> > hxx_epilogue_;
- bool hxx_epilogue_specified_;
- database_map<std::vector<std::string> > ixx_epilogue_;
- bool ixx_epilogue_specified_;
- database_map<std::vector<std::string> > cxx_epilogue_;
- bool cxx_epilogue_specified_;
- database_map<std::vector<std::string> > schema_epilogue_;
- bool schema_epilogue_specified_;
- database_map<std::vector<std::string> > sql_epilogue_;
- bool sql_epilogue_specified_;
- database_map<std::vector<std::string> > migration_epilogue_;
- bool migration_epilogue_specified_;
- database_map<std::vector<std::string> > hxx_prologue_file_;
- bool hxx_prologue_file_specified_;
- database_map<std::vector<std::string> > ixx_prologue_file_;
- bool ixx_prologue_file_specified_;
- database_map<std::vector<std::string> > cxx_prologue_file_;
- bool cxx_prologue_file_specified_;
- database_map<std::vector<std::string> > schema_prologue_file_;
- bool schema_prologue_file_specified_;
- database_map<std::vector<std::string> > sql_prologue_file_;
- bool sql_prologue_file_specified_;
- database_map<std::vector<std::string> > migration_prologue_file_;
- bool migration_prologue_file_specified_;
- database_map<std::vector<std::string> > sql_interlude_file_;
- bool sql_interlude_file_specified_;
- database_map<std::vector<std::string> > hxx_epilogue_file_;
- bool hxx_epilogue_file_specified_;
- database_map<std::vector<std::string> > ixx_epilogue_file_;
- bool ixx_epilogue_file_specified_;
- database_map<std::vector<std::string> > cxx_epilogue_file_;
- bool cxx_epilogue_file_specified_;
- database_map<std::vector<std::string> > schema_epilogue_file_;
- bool schema_epilogue_file_specified_;
- database_map<std::vector<std::string> > sql_epilogue_file_;
- bool sql_epilogue_file_specified_;
- database_map<std::vector<std::string> > migration_epilogue_file_;
- bool migration_epilogue_file_specified_;
- database_map<std::vector<std::string> > odb_prologue_;
- bool odb_prologue_specified_;
- database_map<std::vector<std::string> > odb_prologue_file_;
- bool odb_prologue_file_specified_;
- database_map<std::vector<std::string> > odb_epilogue_;
- bool odb_epilogue_specified_;
- database_map<std::vector<std::string> > odb_epilogue_file_;
- bool odb_epilogue_file_specified_;
- database_map<std::string> table_prefix_;
- bool table_prefix_specified_;
- database_map<std::string> index_suffix_;
- bool index_suffix_specified_;
- database_map<std::string> fkey_suffix_;
- bool fkey_suffix_specified_;
- database_map<std::string> sequence_suffix_;
- bool sequence_suffix_specified_;
- database_map<name_case> sql_name_case_;
- bool sql_name_case_specified_;
- database_map<std::vector<std::string> > table_regex_;
- bool table_regex_specified_;
- database_map<std::vector<std::string> > column_regex_;
- bool column_regex_specified_;
- database_map<std::vector<std::string> > index_regex_;
- bool index_regex_specified_;
- database_map<std::vector<std::string> > fkey_regex_;
- bool fkey_regex_specified_;
- database_map<std::vector<std::string> > sequence_regex_;
- bool sequence_regex_specified_;
- database_map<std::vector<std::string> > statement_regex_;
- bool statement_regex_specified_;
- database_map<std::vector<std::string> > sql_name_regex_;
- bool sql_name_regex_specified_;
- bool sql_name_regex_trace_;
- std::vector<std::string> accessor_regex_;
- bool accessor_regex_specified_;
- bool accessor_regex_trace_;
- std::vector<std::string> modifier_regex_;
- bool modifier_regex_specified_;
- bool modifier_regex_trace_;
- bool include_with_brackets_;
- std::string include_prefix_;
- bool include_prefix_specified_;
- std::vector<std::string> include_regex_;
- bool include_regex_specified_;
- bool include_regex_trace_;
- std::string guard_prefix_;
- bool guard_prefix_specified_;
- bool show_sloc_;
- std::size_t sloc_limit_;
- bool sloc_limit_specified_;
- std::string options_file_;
- bool options_file_specified_;
- std::vector<std::string> x_;
- bool x_specified_;
- bool v_;
- bool trace_;
- std::string mysql_engine_;
- bool mysql_engine_specified_;
- bool sqlite_override_null_;
- bool sqlite_lax_auto_id_;
- ::pgsql_version pgsql_server_version_;
- bool pgsql_server_version_specified_;
- ::oracle_version oracle_client_version_;
- bool oracle_client_version_specified_;
- bool oracle_warn_truncation_;
- ::mssql_version mssql_server_version_;
- bool mssql_server_version_specified_;
- unsigned int mssql_short_limit_;
- bool mssql_short_limit_specified_;
-};
-
-#include <odb/options.ixx>
-
-// Begin epilogue.
-//
-//
-// End epilogue.
-
-#endif // ODB_OPTIONS_HXX
diff --git a/odb/options.ixx b/odb/options.ixx
deleted file mode 100644
index 87e81bc..0000000
--- a/odb/options.ixx
+++ /dev/null
@@ -1,3450 +0,0 @@
-// -*- C++ -*-
-//
-// This file was generated by CLI, a command line interface
-// compiler for C++.
-//
-
-// Begin prologue.
-//
-//
-// End prologue.
-
-#include <cassert>
-
-namespace cli
-{
- // usage_para
- //
- inline usage_para::
- usage_para (value v)
- : v_ (v)
- {
- }
-
- // unknown_mode
- //
- inline unknown_mode::
- unknown_mode (value v)
- : v_ (v)
- {
- }
-
- // exception
- //
- inline ::std::ostream&
- operator<< (::std::ostream& os, const exception& e)
- {
- e.print (os);
- return os;
- }
-
- // unknown_option
- //
- inline unknown_option::
- unknown_option (const std::string& option)
- : option_ (option)
- {
- }
-
- inline const std::string& unknown_option::
- option () const
- {
- return option_;
- }
-
- // unknown_argument
- //
- inline unknown_argument::
- unknown_argument (const std::string& argument)
- : argument_ (argument)
- {
- }
-
- inline const std::string& unknown_argument::
- argument () const
- {
- return argument_;
- }
-
- // missing_value
- //
- inline missing_value::
- missing_value (const std::string& option)
- : option_ (option)
- {
- }
-
- inline const std::string& missing_value::
- option () const
- {
- return option_;
- }
-
- // invalid_value
- //
- inline invalid_value::
- invalid_value (const std::string& option,
- const std::string& value,
- const std::string& message)
- : option_ (option),
- value_ (value),
- message_ (message)
- {
- }
-
- inline const std::string& invalid_value::
- option () const
- {
- return option_;
- }
-
- inline const std::string& invalid_value::
- value () const
- {
- return value_;
- }
-
- inline const std::string& invalid_value::
- message () const
- {
- return message_;
- }
-
- // file_io_failure
- //
- inline file_io_failure::
- file_io_failure (const std::string& file)
- : file_ (file)
- {
- }
-
- inline const std::string& file_io_failure::
- file () const
- {
- return file_;
- }
-
- // unmatched_quote
- //
- inline unmatched_quote::
- unmatched_quote (const std::string& argument)
- : argument_ (argument)
- {
- }
-
- inline const std::string& unmatched_quote::
- argument () const
- {
- return argument_;
- }
-
- // argv_scanner
- //
- inline argv_scanner::
- argv_scanner (int& argc, char** argv, bool erase)
- : 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)
- {
- }
-
- inline int argv_scanner::
- end () const
- {
- return i_;
- }
-
- // argv_file_scanner
- //
- inline argv_file_scanner::
- argv_file_scanner (int& argc,
- char** argv,
- const std::string& option,
- bool erase)
- : argv_scanner (argc, argv, erase),
- option_ (option),
- options_ (&option_info_),
- options_count_ (1),
- i_ (1),
- skip_ (false)
- {
- option_info_.option = option_.c_str ();
- option_info_.search_func = 0;
- }
-
- inline argv_file_scanner::
- argv_file_scanner (int start,
- int& argc,
- char** argv,
- const std::string& option,
- bool erase)
- : argv_scanner (start, argc, argv, erase),
- option_ (option),
- options_ (&option_info_),
- options_count_ (1),
- i_ (1),
- skip_ (false)
- {
- option_info_.option = option_.c_str ();
- option_info_.search_func = 0;
- }
-
- inline argv_file_scanner::
- argv_file_scanner (const std::string& file,
- const std::string& option)
- : argv_scanner (0, zero_argc_, 0),
- option_ (option),
- options_ (&option_info_),
- options_count_ (1),
- i_ (1),
- skip_ (false)
- {
- option_info_.option = option_.c_str ();
- option_info_.search_func = 0;
-
- load (file);
- }
-
- inline argv_file_scanner::
- argv_file_scanner (int& argc,
- char** argv,
- const option_info* options,
- std::size_t options_count,
- bool erase)
- : argv_scanner (argc, argv, erase),
- options_ (options),
- options_count_ (options_count),
- i_ (1),
- skip_ (false)
- {
- }
-
- inline argv_file_scanner::
- argv_file_scanner (int start,
- int& argc,
- char** argv,
- const option_info* options,
- std::size_t options_count,
- bool erase)
- : argv_scanner (start, argc, argv, erase),
- options_ (options),
- options_count_ (options_count),
- i_ (1),
- skip_ (false)
- {
- }
-
- inline argv_file_scanner::
- argv_file_scanner (const std::string& file,
- const option_info* options,
- std::size_t options_count)
- : argv_scanner (0, zero_argc_, 0),
- options_ (options),
- options_count_ (options_count),
- i_ (1),
- skip_ (false)
- {
- load (file);
- }
-
- inline const std::string& option::
- name () const
- {
- return name_;
- }
-
- inline const option_names& option::
- aliases () const
- {
- return aliases_;
- }
-
- inline bool option::
- flag () const
- {
- return flag_;
- }
-
- inline const std::string& option::
- default_value () const
- {
- return default_value_;
- }
-
- inline option::
- option ()
- {
- }
-
- inline option::
- option (const std::string& n,
- const option_names& a,
- bool f,
- const std::string& dv)
- : name_ (n), aliases_ (a), flag_ (f), default_value_ (dv)
- {
- }
-
- inline options::container_type::const_iterator options::
- find (const std::string& name) const
- {
- map_type::const_iterator i (map_.find (name));
- return i != map_.end () ? begin () + i->second : end ();
- }
-}
-
-// options
-//
-
-inline const std::uint64_t& options::
-build2_metadata () const
-{
- return this->build2_metadata_;
-}
-
-inline std::uint64_t& options::
-build2_metadata ()
-{
- return this->build2_metadata_;
-}
-
-inline void options::
-build2_metadata(const std::uint64_t& x)
-{
- this->build2_metadata_ = x;
-}
-
-inline bool options::
-build2_metadata_specified () const
-{
- return this->build2_metadata_specified_;
-}
-
-inline void options::
-build2_metadata_specified(bool x)
-{
- this->build2_metadata_specified_ = x;
-}
-
-inline const bool& options::
-help () const
-{
- return this->help_;
-}
-
-inline bool& options::
-help ()
-{
- return this->help_;
-}
-
-inline void options::
-help(const bool& x)
-{
- this->help_ = x;
-}
-
-inline const bool& options::
-version () const
-{
- return this->version_;
-}
-
-inline bool& options::
-version ()
-{
- return this->version_;
-}
-
-inline void options::
-version(const bool& x)
-{
- this->version_ = x;
-}
-
-inline const std::vector<std::string>& options::
-I () const
-{
- return this->I_;
-}
-
-inline std::vector<std::string>& options::
-I ()
-{
- return this->I_;
-}
-
-inline void options::
-I(const std::vector<std::string>& x)
-{
- this->I_ = x;
-}
-
-inline bool options::
-I_specified () const
-{
- return this->I_specified_;
-}
-
-inline void options::
-I_specified(bool x)
-{
- this->I_specified_ = x;
-}
-
-inline const std::vector<std::string>& options::
-D () const
-{
- return this->D_;
-}
-
-inline std::vector<std::string>& options::
-D ()
-{
- return this->D_;
-}
-
-inline void options::
-D(const std::vector<std::string>& x)
-{
- this->D_ = x;
-}
-
-inline bool options::
-D_specified () const
-{
- return this->D_specified_;
-}
-
-inline void options::
-D_specified(bool x)
-{
- this->D_specified_ = x;
-}
-
-inline const std::vector<std::string>& options::
-U () const
-{
- return this->U_;
-}
-
-inline std::vector<std::string>& options::
-U ()
-{
- return this->U_;
-}
-
-inline void options::
-U(const std::vector<std::string>& x)
-{
- this->U_ = x;
-}
-
-inline bool options::
-U_specified () const
-{
- return this->U_specified_;
-}
-
-inline void options::
-U_specified(bool x)
-{
- this->U_specified_ = x;
-}
-
-inline const std::vector< ::database>& options::
-database () const
-{
- return this->database_;
-}
-
-inline std::vector< ::database>& options::
-database ()
-{
- return this->database_;
-}
-
-inline void options::
-database(const std::vector< ::database>& x)
-{
- this->database_ = x;
-}
-
-inline bool options::
-database_specified () const
-{
- return this->database_specified_;
-}
-
-inline void options::
-database_specified(bool x)
-{
- this->database_specified_ = x;
-}
-
-inline const ::multi_database& options::
-multi_database () const
-{
- return this->multi_database_;
-}
-
-inline ::multi_database& options::
-multi_database ()
-{
- return this->multi_database_;
-}
-
-inline void options::
-multi_database(const ::multi_database& x)
-{
- this->multi_database_ = x;
-}
-
-inline bool options::
-multi_database_specified () const
-{
- return this->multi_database_specified_;
-}
-
-inline void options::
-multi_database_specified(bool x)
-{
- this->multi_database_specified_ = x;
-}
-
-inline const ::database& options::
-default_database () const
-{
- return this->default_database_;
-}
-
-inline ::database& options::
-default_database ()
-{
- return this->default_database_;
-}
-
-inline void options::
-default_database(const ::database& x)
-{
- this->default_database_ = x;
-}
-
-inline bool options::
-default_database_specified () const
-{
- return this->default_database_specified_;
-}
-
-inline void options::
-default_database_specified(bool x)
-{
- this->default_database_specified_ = x;
-}
-
-inline const bool& options::
-generate_query () const
-{
- return this->generate_query_;
-}
-
-inline bool& options::
-generate_query ()
-{
- return this->generate_query_;
-}
-
-inline void options::
-generate_query(const bool& x)
-{
- this->generate_query_ = x;
-}
-
-inline const bool& options::
-generate_prepared () const
-{
- return this->generate_prepared_;
-}
-
-inline bool& options::
-generate_prepared ()
-{
- return this->generate_prepared_;
-}
-
-inline void options::
-generate_prepared(const bool& x)
-{
- this->generate_prepared_ = x;
-}
-
-inline const bool& options::
-omit_unprepared () const
-{
- return this->omit_unprepared_;
-}
-
-inline bool& options::
-omit_unprepared ()
-{
- return this->omit_unprepared_;
-}
-
-inline void options::
-omit_unprepared(const bool& x)
-{
- this->omit_unprepared_ = x;
-}
-
-inline const bool& options::
-generate_session () const
-{
- return this->generate_session_;
-}
-
-inline bool& options::
-generate_session ()
-{
- return this->generate_session_;
-}
-
-inline void options::
-generate_session(const bool& x)
-{
- this->generate_session_ = x;
-}
-
-inline const bool& options::
-generate_schema () const
-{
- return this->generate_schema_;
-}
-
-inline bool& options::
-generate_schema ()
-{
- return this->generate_schema_;
-}
-
-inline void options::
-generate_schema(const bool& x)
-{
- this->generate_schema_ = x;
-}
-
-inline const bool& options::
-generate_schema_only () const
-{
- return this->generate_schema_only_;
-}
-
-inline bool& options::
-generate_schema_only ()
-{
- return this->generate_schema_only_;
-}
-
-inline void options::
-generate_schema_only(const bool& x)
-{
- this->generate_schema_only_ = x;
-}
-
-inline const bool& options::
-suppress_migration () const
-{
- return this->suppress_migration_;
-}
-
-inline bool& options::
-suppress_migration ()
-{
- return this->suppress_migration_;
-}
-
-inline void options::
-suppress_migration(const bool& x)
-{
- this->suppress_migration_ = x;
-}
-
-inline const bool& options::
-suppress_schema_version () const
-{
- return this->suppress_schema_version_;
-}
-
-inline bool& options::
-suppress_schema_version ()
-{
- return this->suppress_schema_version_;
-}
-
-inline void options::
-suppress_schema_version(const bool& x)
-{
- this->suppress_schema_version_ = x;
-}
-
-inline const database_map<qname>& options::
-schema_version_table () const
-{
- return this->schema_version_table_;
-}
-
-inline database_map<qname>& options::
-schema_version_table ()
-{
- return this->schema_version_table_;
-}
-
-inline void options::
-schema_version_table(const database_map<qname>& x)
-{
- this->schema_version_table_ = x;
-}
-
-inline bool options::
-schema_version_table_specified () const
-{
- return this->schema_version_table_specified_;
-}
-
-inline void options::
-schema_version_table_specified(bool x)
-{
- this->schema_version_table_specified_ = x;
-}
-
-inline const database_map<std::set< ::schema_format> >& options::
-schema_format () const
-{
- return this->schema_format_;
-}
-
-inline database_map<std::set< ::schema_format> >& options::
-schema_format ()
-{
- return this->schema_format_;
-}
-
-inline void options::
-schema_format(const database_map<std::set< ::schema_format> >& x)
-{
- this->schema_format_ = x;
-}
-
-inline bool options::
-schema_format_specified () const
-{
- return this->schema_format_specified_;
-}
-
-inline void options::
-schema_format_specified(bool x)
-{
- this->schema_format_specified_ = x;
-}
-
-inline const bool& options::
-omit_drop () const
-{
- return this->omit_drop_;
-}
-
-inline bool& options::
-omit_drop ()
-{
- return this->omit_drop_;
-}
-
-inline void options::
-omit_drop(const bool& x)
-{
- this->omit_drop_ = x;
-}
-
-inline const bool& options::
-omit_create () const
-{
- return this->omit_create_;
-}
-
-inline bool& options::
-omit_create ()
-{
- return this->omit_create_;
-}
-
-inline void options::
-omit_create(const bool& x)
-{
- this->omit_create_ = x;
-}
-
-inline const database_map<std::string>& options::
-schema_name () const
-{
- return this->schema_name_;
-}
-
-inline database_map<std::string>& options::
-schema_name ()
-{
- return this->schema_name_;
-}
-
-inline void options::
-schema_name(const database_map<std::string>& x)
-{
- this->schema_name_ = x;
-}
-
-inline bool options::
-schema_name_specified () const
-{
- return this->schema_name_specified_;
-}
-
-inline void options::
-schema_name_specified(bool x)
-{
- this->schema_name_specified_ = x;
-}
-
-inline const database_map<deferrable>& options::
-fkeys_deferrable_mode () const
-{
- return this->fkeys_deferrable_mode_;
-}
-
-inline database_map<deferrable>& options::
-fkeys_deferrable_mode ()
-{
- return this->fkeys_deferrable_mode_;
-}
-
-inline void options::
-fkeys_deferrable_mode(const database_map<deferrable>& x)
-{
- this->fkeys_deferrable_mode_ = x;
-}
-
-inline bool options::
-fkeys_deferrable_mode_specified () const
-{
- return this->fkeys_deferrable_mode_specified_;
-}
-
-inline void options::
-fkeys_deferrable_mode_specified(bool x)
-{
- this->fkeys_deferrable_mode_specified_ = x;
-}
-
-inline const std::string& options::
-default_pointer () const
-{
- return this->default_pointer_;
-}
-
-inline std::string& options::
-default_pointer ()
-{
- return this->default_pointer_;
-}
-
-inline void options::
-default_pointer(const std::string& x)
-{
- this->default_pointer_ = x;
-}
-
-inline bool options::
-default_pointer_specified () const
-{
- return this->default_pointer_specified_;
-}
-
-inline void options::
-default_pointer_specified(bool x)
-{
- this->default_pointer_specified_ = x;
-}
-
-inline const std::string& options::
-session_type () const
-{
- return this->session_type_;
-}
-
-inline std::string& options::
-session_type ()
-{
- return this->session_type_;
-}
-
-inline void options::
-session_type(const std::string& x)
-{
- this->session_type_ = x;
-}
-
-inline bool options::
-session_type_specified () const
-{
- return this->session_type_specified_;
-}
-
-inline void options::
-session_type_specified(bool x)
-{
- this->session_type_specified_ = x;
-}
-
-inline const std::string& options::
-profile () const
-{
- return this->profile_;
-}
-
-inline std::string& options::
-profile ()
-{
- return this->profile_;
-}
-
-inline void options::
-profile(const std::string& x)
-{
- this->profile_ = x;
-}
-
-inline bool options::
-profile_specified () const
-{
- return this->profile_specified_;
-}
-
-inline void options::
-profile_specified(bool x)
-{
- this->profile_specified_ = x;
-}
-
-inline const bool& options::
-at_once () const
-{
- return this->at_once_;
-}
-
-inline bool& options::
-at_once ()
-{
- return this->at_once_;
-}
-
-inline void options::
-at_once(const bool& x)
-{
- this->at_once_ = x;
-}
-
-inline const database_map<qname>& options::
-schema () const
-{
- return this->schema_;
-}
-
-inline database_map<qname>& options::
-schema ()
-{
- return this->schema_;
-}
-
-inline void options::
-schema(const database_map<qname>& x)
-{
- this->schema_ = x;
-}
-
-inline bool options::
-schema_specified () const
-{
- return this->schema_specified_;
-}
-
-inline void options::
-schema_specified(bool x)
-{
- this->schema_specified_ = x;
-}
-
-inline const database_map<std::string>& options::
-export_symbol () const
-{
- return this->export_symbol_;
-}
-
-inline database_map<std::string>& options::
-export_symbol ()
-{
- return this->export_symbol_;
-}
-
-inline void options::
-export_symbol(const database_map<std::string>& x)
-{
- this->export_symbol_ = x;
-}
-
-inline bool options::
-export_symbol_specified () const
-{
- return this->export_symbol_specified_;
-}
-
-inline void options::
-export_symbol_specified(bool x)
-{
- this->export_symbol_specified_ = x;
-}
-
-inline const database_map<std::string>& options::
-extern_symbol () const
-{
- return this->extern_symbol_;
-}
-
-inline database_map<std::string>& options::
-extern_symbol ()
-{
- return this->extern_symbol_;
-}
-
-inline void options::
-extern_symbol(const database_map<std::string>& x)
-{
- this->extern_symbol_ = x;
-}
-
-inline bool options::
-extern_symbol_specified () const
-{
- return this->extern_symbol_specified_;
-}
-
-inline void options::
-extern_symbol_specified(bool x)
-{
- this->extern_symbol_specified_ = x;
-}
-
-inline const cxx_version& options::
-std () const
-{
- return this->std_;
-}
-
-inline cxx_version& options::
-std ()
-{
- return this->std_;
-}
-
-inline void options::
-std(const cxx_version& x)
-{
- this->std_ = x;
-}
-
-inline bool options::
-std_specified () const
-{
- return this->std_specified_;
-}
-
-inline void options::
-std_specified(bool x)
-{
- this->std_specified_ = x;
-}
-
-inline const bool& options::
-warn_hard_add () const
-{
- return this->warn_hard_add_;
-}
-
-inline bool& options::
-warn_hard_add ()
-{
- return this->warn_hard_add_;
-}
-
-inline void options::
-warn_hard_add(const bool& x)
-{
- this->warn_hard_add_ = x;
-}
-
-inline const bool& options::
-warn_hard_delete () const
-{
- return this->warn_hard_delete_;
-}
-
-inline bool& options::
-warn_hard_delete ()
-{
- return this->warn_hard_delete_;
-}
-
-inline void options::
-warn_hard_delete(const bool& x)
-{
- this->warn_hard_delete_ = x;
-}
-
-inline const bool& options::
-warn_hard () const
-{
- return this->warn_hard_;
-}
-
-inline bool& options::
-warn_hard ()
-{
- return this->warn_hard_;
-}
-
-inline void options::
-warn_hard(const bool& x)
-{
- this->warn_hard_ = x;
-}
-
-inline const std::string& options::
-output_dir () const
-{
- return this->output_dir_;
-}
-
-inline std::string& options::
-output_dir ()
-{
- return this->output_dir_;
-}
-
-inline void options::
-output_dir(const std::string& x)
-{
- this->output_dir_ = x;
-}
-
-inline bool options::
-output_dir_specified () const
-{
- return this->output_dir_specified_;
-}
-
-inline void options::
-output_dir_specified(bool x)
-{
- this->output_dir_specified_ = x;
-}
-
-inline const std::string& options::
-input_name () const
-{
- return this->input_name_;
-}
-
-inline std::string& options::
-input_name ()
-{
- return this->input_name_;
-}
-
-inline void options::
-input_name(const std::string& x)
-{
- this->input_name_ = x;
-}
-
-inline bool options::
-input_name_specified () const
-{
- return this->input_name_specified_;
-}
-
-inline void options::
-input_name_specified(bool x)
-{
- this->input_name_specified_ = x;
-}
-
-inline const database_map<std::string>& options::
-changelog () const
-{
- return this->changelog_;
-}
-
-inline database_map<std::string>& options::
-changelog ()
-{
- return this->changelog_;
-}
-
-inline void options::
-changelog(const database_map<std::string>& x)
-{
- this->changelog_ = x;
-}
-
-inline bool options::
-changelog_specified () const
-{
- return this->changelog_specified_;
-}
-
-inline void options::
-changelog_specified(bool x)
-{
- this->changelog_specified_ = x;
-}
-
-inline const database_map<std::string>& options::
-changelog_in () const
-{
- return this->changelog_in_;
-}
-
-inline database_map<std::string>& options::
-changelog_in ()
-{
- return this->changelog_in_;
-}
-
-inline void options::
-changelog_in(const database_map<std::string>& x)
-{
- this->changelog_in_ = x;
-}
-
-inline bool options::
-changelog_in_specified () const
-{
- return this->changelog_in_specified_;
-}
-
-inline void options::
-changelog_in_specified(bool x)
-{
- this->changelog_in_specified_ = x;
-}
-
-inline const database_map<std::string>& options::
-changelog_out () const
-{
- return this->changelog_out_;
-}
-
-inline database_map<std::string>& options::
-changelog_out ()
-{
- return this->changelog_out_;
-}
-
-inline void options::
-changelog_out(const database_map<std::string>& x)
-{
- this->changelog_out_ = x;
-}
-
-inline bool options::
-changelog_out_specified () const
-{
- return this->changelog_out_specified_;
-}
-
-inline void options::
-changelog_out_specified(bool x)
-{
- this->changelog_out_specified_ = x;
-}
-
-inline const database_map<std::string>& options::
-changelog_dir () const
-{
- return this->changelog_dir_;
-}
-
-inline database_map<std::string>& options::
-changelog_dir ()
-{
- return this->changelog_dir_;
-}
-
-inline void options::
-changelog_dir(const database_map<std::string>& x)
-{
- this->changelog_dir_ = x;
-}
-
-inline bool options::
-changelog_dir_specified () const
-{
- return this->changelog_dir_specified_;
-}
-
-inline void options::
-changelog_dir_specified(bool x)
-{
- this->changelog_dir_specified_ = x;
-}
-
-inline const bool& options::
-init_changelog () const
-{
- return this->init_changelog_;
-}
-
-inline bool& options::
-init_changelog ()
-{
- return this->init_changelog_;
-}
-
-inline void options::
-init_changelog(const bool& x)
-{
- this->init_changelog_ = x;
-}
-
-inline const database_map<std::string>& options::
-odb_file_suffix () const
-{
- return this->odb_file_suffix_;
-}
-
-inline database_map<std::string>& options::
-odb_file_suffix ()
-{
- return this->odb_file_suffix_;
-}
-
-inline void options::
-odb_file_suffix(const database_map<std::string>& x)
-{
- this->odb_file_suffix_ = x;
-}
-
-inline bool options::
-odb_file_suffix_specified () const
-{
- return this->odb_file_suffix_specified_;
-}
-
-inline void options::
-odb_file_suffix_specified(bool x)
-{
- this->odb_file_suffix_specified_ = x;
-}
-
-inline const database_map<std::string>& options::
-sql_file_suffix () const
-{
- return this->sql_file_suffix_;
-}
-
-inline database_map<std::string>& options::
-sql_file_suffix ()
-{
- return this->sql_file_suffix_;
-}
-
-inline void options::
-sql_file_suffix(const database_map<std::string>& x)
-{
- this->sql_file_suffix_ = x;
-}
-
-inline bool options::
-sql_file_suffix_specified () const
-{
- return this->sql_file_suffix_specified_;
-}
-
-inline void options::
-sql_file_suffix_specified(bool x)
-{
- this->sql_file_suffix_specified_ = x;
-}
-
-inline const database_map<std::string>& options::
-schema_file_suffix () const
-{
- return this->schema_file_suffix_;
-}
-
-inline database_map<std::string>& options::
-schema_file_suffix ()
-{
- return this->schema_file_suffix_;
-}
-
-inline void options::
-schema_file_suffix(const database_map<std::string>& x)
-{
- this->schema_file_suffix_ = x;
-}
-
-inline bool options::
-schema_file_suffix_specified () const
-{
- return this->schema_file_suffix_specified_;
-}
-
-inline void options::
-schema_file_suffix_specified(bool x)
-{
- this->schema_file_suffix_specified_ = x;
-}
-
-inline const database_map<std::string>& options::
-changelog_file_suffix () const
-{
- return this->changelog_file_suffix_;
-}
-
-inline database_map<std::string>& options::
-changelog_file_suffix ()
-{
- return this->changelog_file_suffix_;
-}
-
-inline void options::
-changelog_file_suffix(const database_map<std::string>& x)
-{
- this->changelog_file_suffix_ = x;
-}
-
-inline bool options::
-changelog_file_suffix_specified () const
-{
- return this->changelog_file_suffix_specified_;
-}
-
-inline void options::
-changelog_file_suffix_specified(bool x)
-{
- this->changelog_file_suffix_specified_ = x;
-}
-
-inline const std::string& options::
-hxx_suffix () const
-{
- return this->hxx_suffix_;
-}
-
-inline std::string& options::
-hxx_suffix ()
-{
- return this->hxx_suffix_;
-}
-
-inline void options::
-hxx_suffix(const std::string& x)
-{
- this->hxx_suffix_ = x;
-}
-
-inline bool options::
-hxx_suffix_specified () const
-{
- return this->hxx_suffix_specified_;
-}
-
-inline void options::
-hxx_suffix_specified(bool x)
-{
- this->hxx_suffix_specified_ = x;
-}
-
-inline const std::string& options::
-ixx_suffix () const
-{
- return this->ixx_suffix_;
-}
-
-inline std::string& options::
-ixx_suffix ()
-{
- return this->ixx_suffix_;
-}
-
-inline void options::
-ixx_suffix(const std::string& x)
-{
- this->ixx_suffix_ = x;
-}
-
-inline bool options::
-ixx_suffix_specified () const
-{
- return this->ixx_suffix_specified_;
-}
-
-inline void options::
-ixx_suffix_specified(bool x)
-{
- this->ixx_suffix_specified_ = x;
-}
-
-inline const std::string& options::
-cxx_suffix () const
-{
- return this->cxx_suffix_;
-}
-
-inline std::string& options::
-cxx_suffix ()
-{
- return this->cxx_suffix_;
-}
-
-inline void options::
-cxx_suffix(const std::string& x)
-{
- this->cxx_suffix_ = x;
-}
-
-inline bool options::
-cxx_suffix_specified () const
-{
- return this->cxx_suffix_specified_;
-}
-
-inline void options::
-cxx_suffix_specified(bool x)
-{
- this->cxx_suffix_specified_ = x;
-}
-
-inline const std::string& options::
-sql_suffix () const
-{
- return this->sql_suffix_;
-}
-
-inline std::string& options::
-sql_suffix ()
-{
- return this->sql_suffix_;
-}
-
-inline void options::
-sql_suffix(const std::string& x)
-{
- this->sql_suffix_ = x;
-}
-
-inline bool options::
-sql_suffix_specified () const
-{
- return this->sql_suffix_specified_;
-}
-
-inline void options::
-sql_suffix_specified(bool x)
-{
- this->sql_suffix_specified_ = x;
-}
-
-inline const std::string& options::
-changelog_suffix () const
-{
- return this->changelog_suffix_;
-}
-
-inline std::string& options::
-changelog_suffix ()
-{
- return this->changelog_suffix_;
-}
-
-inline void options::
-changelog_suffix(const std::string& x)
-{
- this->changelog_suffix_ = x;
-}
-
-inline bool options::
-changelog_suffix_specified () const
-{
- return this->changelog_suffix_specified_;
-}
-
-inline void options::
-changelog_suffix_specified(bool x)
-{
- this->changelog_suffix_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-hxx_prologue () const
-{
- return this->hxx_prologue_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-hxx_prologue ()
-{
- return this->hxx_prologue_;
-}
-
-inline void options::
-hxx_prologue(const database_map<std::vector<std::string> >& x)
-{
- this->hxx_prologue_ = x;
-}
-
-inline bool options::
-hxx_prologue_specified () const
-{
- return this->hxx_prologue_specified_;
-}
-
-inline void options::
-hxx_prologue_specified(bool x)
-{
- this->hxx_prologue_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-ixx_prologue () const
-{
- return this->ixx_prologue_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-ixx_prologue ()
-{
- return this->ixx_prologue_;
-}
-
-inline void options::
-ixx_prologue(const database_map<std::vector<std::string> >& x)
-{
- this->ixx_prologue_ = x;
-}
-
-inline bool options::
-ixx_prologue_specified () const
-{
- return this->ixx_prologue_specified_;
-}
-
-inline void options::
-ixx_prologue_specified(bool x)
-{
- this->ixx_prologue_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-cxx_prologue () const
-{
- return this->cxx_prologue_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-cxx_prologue ()
-{
- return this->cxx_prologue_;
-}
-
-inline void options::
-cxx_prologue(const database_map<std::vector<std::string> >& x)
-{
- this->cxx_prologue_ = x;
-}
-
-inline bool options::
-cxx_prologue_specified () const
-{
- return this->cxx_prologue_specified_;
-}
-
-inline void options::
-cxx_prologue_specified(bool x)
-{
- this->cxx_prologue_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-schema_prologue () const
-{
- return this->schema_prologue_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-schema_prologue ()
-{
- return this->schema_prologue_;
-}
-
-inline void options::
-schema_prologue(const database_map<std::vector<std::string> >& x)
-{
- this->schema_prologue_ = x;
-}
-
-inline bool options::
-schema_prologue_specified () const
-{
- return this->schema_prologue_specified_;
-}
-
-inline void options::
-schema_prologue_specified(bool x)
-{
- this->schema_prologue_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-sql_prologue () const
-{
- return this->sql_prologue_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-sql_prologue ()
-{
- return this->sql_prologue_;
-}
-
-inline void options::
-sql_prologue(const database_map<std::vector<std::string> >& x)
-{
- this->sql_prologue_ = x;
-}
-
-inline bool options::
-sql_prologue_specified () const
-{
- return this->sql_prologue_specified_;
-}
-
-inline void options::
-sql_prologue_specified(bool x)
-{
- this->sql_prologue_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-migration_prologue () const
-{
- return this->migration_prologue_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-migration_prologue ()
-{
- return this->migration_prologue_;
-}
-
-inline void options::
-migration_prologue(const database_map<std::vector<std::string> >& x)
-{
- this->migration_prologue_ = x;
-}
-
-inline bool options::
-migration_prologue_specified () const
-{
- return this->migration_prologue_specified_;
-}
-
-inline void options::
-migration_prologue_specified(bool x)
-{
- this->migration_prologue_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-sql_interlude () const
-{
- return this->sql_interlude_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-sql_interlude ()
-{
- return this->sql_interlude_;
-}
-
-inline void options::
-sql_interlude(const database_map<std::vector<std::string> >& x)
-{
- this->sql_interlude_ = x;
-}
-
-inline bool options::
-sql_interlude_specified () const
-{
- return this->sql_interlude_specified_;
-}
-
-inline void options::
-sql_interlude_specified(bool x)
-{
- this->sql_interlude_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-hxx_epilogue () const
-{
- return this->hxx_epilogue_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-hxx_epilogue ()
-{
- return this->hxx_epilogue_;
-}
-
-inline void options::
-hxx_epilogue(const database_map<std::vector<std::string> >& x)
-{
- this->hxx_epilogue_ = x;
-}
-
-inline bool options::
-hxx_epilogue_specified () const
-{
- return this->hxx_epilogue_specified_;
-}
-
-inline void options::
-hxx_epilogue_specified(bool x)
-{
- this->hxx_epilogue_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-ixx_epilogue () const
-{
- return this->ixx_epilogue_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-ixx_epilogue ()
-{
- return this->ixx_epilogue_;
-}
-
-inline void options::
-ixx_epilogue(const database_map<std::vector<std::string> >& x)
-{
- this->ixx_epilogue_ = x;
-}
-
-inline bool options::
-ixx_epilogue_specified () const
-{
- return this->ixx_epilogue_specified_;
-}
-
-inline void options::
-ixx_epilogue_specified(bool x)
-{
- this->ixx_epilogue_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-cxx_epilogue () const
-{
- return this->cxx_epilogue_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-cxx_epilogue ()
-{
- return this->cxx_epilogue_;
-}
-
-inline void options::
-cxx_epilogue(const database_map<std::vector<std::string> >& x)
-{
- this->cxx_epilogue_ = x;
-}
-
-inline bool options::
-cxx_epilogue_specified () const
-{
- return this->cxx_epilogue_specified_;
-}
-
-inline void options::
-cxx_epilogue_specified(bool x)
-{
- this->cxx_epilogue_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-schema_epilogue () const
-{
- return this->schema_epilogue_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-schema_epilogue ()
-{
- return this->schema_epilogue_;
-}
-
-inline void options::
-schema_epilogue(const database_map<std::vector<std::string> >& x)
-{
- this->schema_epilogue_ = x;
-}
-
-inline bool options::
-schema_epilogue_specified () const
-{
- return this->schema_epilogue_specified_;
-}
-
-inline void options::
-schema_epilogue_specified(bool x)
-{
- this->schema_epilogue_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-sql_epilogue () const
-{
- return this->sql_epilogue_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-sql_epilogue ()
-{
- return this->sql_epilogue_;
-}
-
-inline void options::
-sql_epilogue(const database_map<std::vector<std::string> >& x)
-{
- this->sql_epilogue_ = x;
-}
-
-inline bool options::
-sql_epilogue_specified () const
-{
- return this->sql_epilogue_specified_;
-}
-
-inline void options::
-sql_epilogue_specified(bool x)
-{
- this->sql_epilogue_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-migration_epilogue () const
-{
- return this->migration_epilogue_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-migration_epilogue ()
-{
- return this->migration_epilogue_;
-}
-
-inline void options::
-migration_epilogue(const database_map<std::vector<std::string> >& x)
-{
- this->migration_epilogue_ = x;
-}
-
-inline bool options::
-migration_epilogue_specified () const
-{
- return this->migration_epilogue_specified_;
-}
-
-inline void options::
-migration_epilogue_specified(bool x)
-{
- this->migration_epilogue_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-hxx_prologue_file () const
-{
- return this->hxx_prologue_file_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-hxx_prologue_file ()
-{
- return this->hxx_prologue_file_;
-}
-
-inline void options::
-hxx_prologue_file(const database_map<std::vector<std::string> >& x)
-{
- this->hxx_prologue_file_ = x;
-}
-
-inline bool options::
-hxx_prologue_file_specified () const
-{
- return this->hxx_prologue_file_specified_;
-}
-
-inline void options::
-hxx_prologue_file_specified(bool x)
-{
- this->hxx_prologue_file_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-ixx_prologue_file () const
-{
- return this->ixx_prologue_file_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-ixx_prologue_file ()
-{
- return this->ixx_prologue_file_;
-}
-
-inline void options::
-ixx_prologue_file(const database_map<std::vector<std::string> >& x)
-{
- this->ixx_prologue_file_ = x;
-}
-
-inline bool options::
-ixx_prologue_file_specified () const
-{
- return this->ixx_prologue_file_specified_;
-}
-
-inline void options::
-ixx_prologue_file_specified(bool x)
-{
- this->ixx_prologue_file_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-cxx_prologue_file () const
-{
- return this->cxx_prologue_file_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-cxx_prologue_file ()
-{
- return this->cxx_prologue_file_;
-}
-
-inline void options::
-cxx_prologue_file(const database_map<std::vector<std::string> >& x)
-{
- this->cxx_prologue_file_ = x;
-}
-
-inline bool options::
-cxx_prologue_file_specified () const
-{
- return this->cxx_prologue_file_specified_;
-}
-
-inline void options::
-cxx_prologue_file_specified(bool x)
-{
- this->cxx_prologue_file_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-schema_prologue_file () const
-{
- return this->schema_prologue_file_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-schema_prologue_file ()
-{
- return this->schema_prologue_file_;
-}
-
-inline void options::
-schema_prologue_file(const database_map<std::vector<std::string> >& x)
-{
- this->schema_prologue_file_ = x;
-}
-
-inline bool options::
-schema_prologue_file_specified () const
-{
- return this->schema_prologue_file_specified_;
-}
-
-inline void options::
-schema_prologue_file_specified(bool x)
-{
- this->schema_prologue_file_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-sql_prologue_file () const
-{
- return this->sql_prologue_file_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-sql_prologue_file ()
-{
- return this->sql_prologue_file_;
-}
-
-inline void options::
-sql_prologue_file(const database_map<std::vector<std::string> >& x)
-{
- this->sql_prologue_file_ = x;
-}
-
-inline bool options::
-sql_prologue_file_specified () const
-{
- return this->sql_prologue_file_specified_;
-}
-
-inline void options::
-sql_prologue_file_specified(bool x)
-{
- this->sql_prologue_file_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-migration_prologue_file () const
-{
- return this->migration_prologue_file_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-migration_prologue_file ()
-{
- return this->migration_prologue_file_;
-}
-
-inline void options::
-migration_prologue_file(const database_map<std::vector<std::string> >& x)
-{
- this->migration_prologue_file_ = x;
-}
-
-inline bool options::
-migration_prologue_file_specified () const
-{
- return this->migration_prologue_file_specified_;
-}
-
-inline void options::
-migration_prologue_file_specified(bool x)
-{
- this->migration_prologue_file_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-sql_interlude_file () const
-{
- return this->sql_interlude_file_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-sql_interlude_file ()
-{
- return this->sql_interlude_file_;
-}
-
-inline void options::
-sql_interlude_file(const database_map<std::vector<std::string> >& x)
-{
- this->sql_interlude_file_ = x;
-}
-
-inline bool options::
-sql_interlude_file_specified () const
-{
- return this->sql_interlude_file_specified_;
-}
-
-inline void options::
-sql_interlude_file_specified(bool x)
-{
- this->sql_interlude_file_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-hxx_epilogue_file () const
-{
- return this->hxx_epilogue_file_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-hxx_epilogue_file ()
-{
- return this->hxx_epilogue_file_;
-}
-
-inline void options::
-hxx_epilogue_file(const database_map<std::vector<std::string> >& x)
-{
- this->hxx_epilogue_file_ = x;
-}
-
-inline bool options::
-hxx_epilogue_file_specified () const
-{
- return this->hxx_epilogue_file_specified_;
-}
-
-inline void options::
-hxx_epilogue_file_specified(bool x)
-{
- this->hxx_epilogue_file_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-ixx_epilogue_file () const
-{
- return this->ixx_epilogue_file_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-ixx_epilogue_file ()
-{
- return this->ixx_epilogue_file_;
-}
-
-inline void options::
-ixx_epilogue_file(const database_map<std::vector<std::string> >& x)
-{
- this->ixx_epilogue_file_ = x;
-}
-
-inline bool options::
-ixx_epilogue_file_specified () const
-{
- return this->ixx_epilogue_file_specified_;
-}
-
-inline void options::
-ixx_epilogue_file_specified(bool x)
-{
- this->ixx_epilogue_file_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-cxx_epilogue_file () const
-{
- return this->cxx_epilogue_file_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-cxx_epilogue_file ()
-{
- return this->cxx_epilogue_file_;
-}
-
-inline void options::
-cxx_epilogue_file(const database_map<std::vector<std::string> >& x)
-{
- this->cxx_epilogue_file_ = x;
-}
-
-inline bool options::
-cxx_epilogue_file_specified () const
-{
- return this->cxx_epilogue_file_specified_;
-}
-
-inline void options::
-cxx_epilogue_file_specified(bool x)
-{
- this->cxx_epilogue_file_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-schema_epilogue_file () const
-{
- return this->schema_epilogue_file_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-schema_epilogue_file ()
-{
- return this->schema_epilogue_file_;
-}
-
-inline void options::
-schema_epilogue_file(const database_map<std::vector<std::string> >& x)
-{
- this->schema_epilogue_file_ = x;
-}
-
-inline bool options::
-schema_epilogue_file_specified () const
-{
- return this->schema_epilogue_file_specified_;
-}
-
-inline void options::
-schema_epilogue_file_specified(bool x)
-{
- this->schema_epilogue_file_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-sql_epilogue_file () const
-{
- return this->sql_epilogue_file_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-sql_epilogue_file ()
-{
- return this->sql_epilogue_file_;
-}
-
-inline void options::
-sql_epilogue_file(const database_map<std::vector<std::string> >& x)
-{
- this->sql_epilogue_file_ = x;
-}
-
-inline bool options::
-sql_epilogue_file_specified () const
-{
- return this->sql_epilogue_file_specified_;
-}
-
-inline void options::
-sql_epilogue_file_specified(bool x)
-{
- this->sql_epilogue_file_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-migration_epilogue_file () const
-{
- return this->migration_epilogue_file_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-migration_epilogue_file ()
-{
- return this->migration_epilogue_file_;
-}
-
-inline void options::
-migration_epilogue_file(const database_map<std::vector<std::string> >& x)
-{
- this->migration_epilogue_file_ = x;
-}
-
-inline bool options::
-migration_epilogue_file_specified () const
-{
- return this->migration_epilogue_file_specified_;
-}
-
-inline void options::
-migration_epilogue_file_specified(bool x)
-{
- this->migration_epilogue_file_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-odb_prologue () const
-{
- return this->odb_prologue_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-odb_prologue ()
-{
- return this->odb_prologue_;
-}
-
-inline void options::
-odb_prologue(const database_map<std::vector<std::string> >& x)
-{
- this->odb_prologue_ = x;
-}
-
-inline bool options::
-odb_prologue_specified () const
-{
- return this->odb_prologue_specified_;
-}
-
-inline void options::
-odb_prologue_specified(bool x)
-{
- this->odb_prologue_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-odb_prologue_file () const
-{
- return this->odb_prologue_file_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-odb_prologue_file ()
-{
- return this->odb_prologue_file_;
-}
-
-inline void options::
-odb_prologue_file(const database_map<std::vector<std::string> >& x)
-{
- this->odb_prologue_file_ = x;
-}
-
-inline bool options::
-odb_prologue_file_specified () const
-{
- return this->odb_prologue_file_specified_;
-}
-
-inline void options::
-odb_prologue_file_specified(bool x)
-{
- this->odb_prologue_file_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-odb_epilogue () const
-{
- return this->odb_epilogue_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-odb_epilogue ()
-{
- return this->odb_epilogue_;
-}
-
-inline void options::
-odb_epilogue(const database_map<std::vector<std::string> >& x)
-{
- this->odb_epilogue_ = x;
-}
-
-inline bool options::
-odb_epilogue_specified () const
-{
- return this->odb_epilogue_specified_;
-}
-
-inline void options::
-odb_epilogue_specified(bool x)
-{
- this->odb_epilogue_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-odb_epilogue_file () const
-{
- return this->odb_epilogue_file_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-odb_epilogue_file ()
-{
- return this->odb_epilogue_file_;
-}
-
-inline void options::
-odb_epilogue_file(const database_map<std::vector<std::string> >& x)
-{
- this->odb_epilogue_file_ = x;
-}
-
-inline bool options::
-odb_epilogue_file_specified () const
-{
- return this->odb_epilogue_file_specified_;
-}
-
-inline void options::
-odb_epilogue_file_specified(bool x)
-{
- this->odb_epilogue_file_specified_ = x;
-}
-
-inline const database_map<std::string>& options::
-table_prefix () const
-{
- return this->table_prefix_;
-}
-
-inline database_map<std::string>& options::
-table_prefix ()
-{
- return this->table_prefix_;
-}
-
-inline void options::
-table_prefix(const database_map<std::string>& x)
-{
- this->table_prefix_ = x;
-}
-
-inline bool options::
-table_prefix_specified () const
-{
- return this->table_prefix_specified_;
-}
-
-inline void options::
-table_prefix_specified(bool x)
-{
- this->table_prefix_specified_ = x;
-}
-
-inline const database_map<std::string>& options::
-index_suffix () const
-{
- return this->index_suffix_;
-}
-
-inline database_map<std::string>& options::
-index_suffix ()
-{
- return this->index_suffix_;
-}
-
-inline void options::
-index_suffix(const database_map<std::string>& x)
-{
- this->index_suffix_ = x;
-}
-
-inline bool options::
-index_suffix_specified () const
-{
- return this->index_suffix_specified_;
-}
-
-inline void options::
-index_suffix_specified(bool x)
-{
- this->index_suffix_specified_ = x;
-}
-
-inline const database_map<std::string>& options::
-fkey_suffix () const
-{
- return this->fkey_suffix_;
-}
-
-inline database_map<std::string>& options::
-fkey_suffix ()
-{
- return this->fkey_suffix_;
-}
-
-inline void options::
-fkey_suffix(const database_map<std::string>& x)
-{
- this->fkey_suffix_ = x;
-}
-
-inline bool options::
-fkey_suffix_specified () const
-{
- return this->fkey_suffix_specified_;
-}
-
-inline void options::
-fkey_suffix_specified(bool x)
-{
- this->fkey_suffix_specified_ = x;
-}
-
-inline const database_map<std::string>& options::
-sequence_suffix () const
-{
- return this->sequence_suffix_;
-}
-
-inline database_map<std::string>& options::
-sequence_suffix ()
-{
- return this->sequence_suffix_;
-}
-
-inline void options::
-sequence_suffix(const database_map<std::string>& x)
-{
- this->sequence_suffix_ = x;
-}
-
-inline bool options::
-sequence_suffix_specified () const
-{
- return this->sequence_suffix_specified_;
-}
-
-inline void options::
-sequence_suffix_specified(bool x)
-{
- this->sequence_suffix_specified_ = x;
-}
-
-inline const database_map<name_case>& options::
-sql_name_case () const
-{
- return this->sql_name_case_;
-}
-
-inline database_map<name_case>& options::
-sql_name_case ()
-{
- return this->sql_name_case_;
-}
-
-inline void options::
-sql_name_case(const database_map<name_case>& x)
-{
- this->sql_name_case_ = x;
-}
-
-inline bool options::
-sql_name_case_specified () const
-{
- return this->sql_name_case_specified_;
-}
-
-inline void options::
-sql_name_case_specified(bool x)
-{
- this->sql_name_case_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-table_regex () const
-{
- return this->table_regex_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-table_regex ()
-{
- return this->table_regex_;
-}
-
-inline void options::
-table_regex(const database_map<std::vector<std::string> >& x)
-{
- this->table_regex_ = x;
-}
-
-inline bool options::
-table_regex_specified () const
-{
- return this->table_regex_specified_;
-}
-
-inline void options::
-table_regex_specified(bool x)
-{
- this->table_regex_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-column_regex () const
-{
- return this->column_regex_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-column_regex ()
-{
- return this->column_regex_;
-}
-
-inline void options::
-column_regex(const database_map<std::vector<std::string> >& x)
-{
- this->column_regex_ = x;
-}
-
-inline bool options::
-column_regex_specified () const
-{
- return this->column_regex_specified_;
-}
-
-inline void options::
-column_regex_specified(bool x)
-{
- this->column_regex_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-index_regex () const
-{
- return this->index_regex_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-index_regex ()
-{
- return this->index_regex_;
-}
-
-inline void options::
-index_regex(const database_map<std::vector<std::string> >& x)
-{
- this->index_regex_ = x;
-}
-
-inline bool options::
-index_regex_specified () const
-{
- return this->index_regex_specified_;
-}
-
-inline void options::
-index_regex_specified(bool x)
-{
- this->index_regex_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-fkey_regex () const
-{
- return this->fkey_regex_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-fkey_regex ()
-{
- return this->fkey_regex_;
-}
-
-inline void options::
-fkey_regex(const database_map<std::vector<std::string> >& x)
-{
- this->fkey_regex_ = x;
-}
-
-inline bool options::
-fkey_regex_specified () const
-{
- return this->fkey_regex_specified_;
-}
-
-inline void options::
-fkey_regex_specified(bool x)
-{
- this->fkey_regex_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-sequence_regex () const
-{
- return this->sequence_regex_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-sequence_regex ()
-{
- return this->sequence_regex_;
-}
-
-inline void options::
-sequence_regex(const database_map<std::vector<std::string> >& x)
-{
- this->sequence_regex_ = x;
-}
-
-inline bool options::
-sequence_regex_specified () const
-{
- return this->sequence_regex_specified_;
-}
-
-inline void options::
-sequence_regex_specified(bool x)
-{
- this->sequence_regex_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-statement_regex () const
-{
- return this->statement_regex_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-statement_regex ()
-{
- return this->statement_regex_;
-}
-
-inline void options::
-statement_regex(const database_map<std::vector<std::string> >& x)
-{
- this->statement_regex_ = x;
-}
-
-inline bool options::
-statement_regex_specified () const
-{
- return this->statement_regex_specified_;
-}
-
-inline void options::
-statement_regex_specified(bool x)
-{
- this->statement_regex_specified_ = x;
-}
-
-inline const database_map<std::vector<std::string> >& options::
-sql_name_regex () const
-{
- return this->sql_name_regex_;
-}
-
-inline database_map<std::vector<std::string> >& options::
-sql_name_regex ()
-{
- return this->sql_name_regex_;
-}
-
-inline void options::
-sql_name_regex(const database_map<std::vector<std::string> >& x)
-{
- this->sql_name_regex_ = x;
-}
-
-inline bool options::
-sql_name_regex_specified () const
-{
- return this->sql_name_regex_specified_;
-}
-
-inline void options::
-sql_name_regex_specified(bool x)
-{
- this->sql_name_regex_specified_ = x;
-}
-
-inline const bool& options::
-sql_name_regex_trace () const
-{
- return this->sql_name_regex_trace_;
-}
-
-inline bool& options::
-sql_name_regex_trace ()
-{
- return this->sql_name_regex_trace_;
-}
-
-inline void options::
-sql_name_regex_trace(const bool& x)
-{
- this->sql_name_regex_trace_ = x;
-}
-
-inline const std::vector<std::string>& options::
-accessor_regex () const
-{
- return this->accessor_regex_;
-}
-
-inline std::vector<std::string>& options::
-accessor_regex ()
-{
- return this->accessor_regex_;
-}
-
-inline void options::
-accessor_regex(const std::vector<std::string>& x)
-{
- this->accessor_regex_ = x;
-}
-
-inline bool options::
-accessor_regex_specified () const
-{
- return this->accessor_regex_specified_;
-}
-
-inline void options::
-accessor_regex_specified(bool x)
-{
- this->accessor_regex_specified_ = x;
-}
-
-inline const bool& options::
-accessor_regex_trace () const
-{
- return this->accessor_regex_trace_;
-}
-
-inline bool& options::
-accessor_regex_trace ()
-{
- return this->accessor_regex_trace_;
-}
-
-inline void options::
-accessor_regex_trace(const bool& x)
-{
- this->accessor_regex_trace_ = x;
-}
-
-inline const std::vector<std::string>& options::
-modifier_regex () const
-{
- return this->modifier_regex_;
-}
-
-inline std::vector<std::string>& options::
-modifier_regex ()
-{
- return this->modifier_regex_;
-}
-
-inline void options::
-modifier_regex(const std::vector<std::string>& x)
-{
- this->modifier_regex_ = x;
-}
-
-inline bool options::
-modifier_regex_specified () const
-{
- return this->modifier_regex_specified_;
-}
-
-inline void options::
-modifier_regex_specified(bool x)
-{
- this->modifier_regex_specified_ = x;
-}
-
-inline const bool& options::
-modifier_regex_trace () const
-{
- return this->modifier_regex_trace_;
-}
-
-inline bool& options::
-modifier_regex_trace ()
-{
- return this->modifier_regex_trace_;
-}
-
-inline void options::
-modifier_regex_trace(const bool& x)
-{
- this->modifier_regex_trace_ = x;
-}
-
-inline const bool& options::
-include_with_brackets () const
-{
- return this->include_with_brackets_;
-}
-
-inline bool& options::
-include_with_brackets ()
-{
- return this->include_with_brackets_;
-}
-
-inline void options::
-include_with_brackets(const bool& x)
-{
- this->include_with_brackets_ = x;
-}
-
-inline const std::string& options::
-include_prefix () const
-{
- return this->include_prefix_;
-}
-
-inline std::string& options::
-include_prefix ()
-{
- return this->include_prefix_;
-}
-
-inline void options::
-include_prefix(const std::string& x)
-{
- this->include_prefix_ = x;
-}
-
-inline bool options::
-include_prefix_specified () const
-{
- return this->include_prefix_specified_;
-}
-
-inline void options::
-include_prefix_specified(bool x)
-{
- this->include_prefix_specified_ = x;
-}
-
-inline const std::vector<std::string>& options::
-include_regex () const
-{
- return this->include_regex_;
-}
-
-inline std::vector<std::string>& options::
-include_regex ()
-{
- return this->include_regex_;
-}
-
-inline void options::
-include_regex(const std::vector<std::string>& x)
-{
- this->include_regex_ = x;
-}
-
-inline bool options::
-include_regex_specified () const
-{
- return this->include_regex_specified_;
-}
-
-inline void options::
-include_regex_specified(bool x)
-{
- this->include_regex_specified_ = x;
-}
-
-inline const bool& options::
-include_regex_trace () const
-{
- return this->include_regex_trace_;
-}
-
-inline bool& options::
-include_regex_trace ()
-{
- return this->include_regex_trace_;
-}
-
-inline void options::
-include_regex_trace(const bool& x)
-{
- this->include_regex_trace_ = x;
-}
-
-inline const std::string& options::
-guard_prefix () const
-{
- return this->guard_prefix_;
-}
-
-inline std::string& options::
-guard_prefix ()
-{
- return this->guard_prefix_;
-}
-
-inline void options::
-guard_prefix(const std::string& x)
-{
- this->guard_prefix_ = x;
-}
-
-inline bool options::
-guard_prefix_specified () const
-{
- return this->guard_prefix_specified_;
-}
-
-inline void options::
-guard_prefix_specified(bool x)
-{
- this->guard_prefix_specified_ = x;
-}
-
-inline const bool& options::
-show_sloc () const
-{
- return this->show_sloc_;
-}
-
-inline bool& options::
-show_sloc ()
-{
- return this->show_sloc_;
-}
-
-inline void options::
-show_sloc(const bool& x)
-{
- this->show_sloc_ = x;
-}
-
-inline const std::size_t& options::
-sloc_limit () const
-{
- return this->sloc_limit_;
-}
-
-inline std::size_t& options::
-sloc_limit ()
-{
- return this->sloc_limit_;
-}
-
-inline void options::
-sloc_limit(const std::size_t& x)
-{
- this->sloc_limit_ = x;
-}
-
-inline bool options::
-sloc_limit_specified () const
-{
- return this->sloc_limit_specified_;
-}
-
-inline void options::
-sloc_limit_specified(bool x)
-{
- this->sloc_limit_specified_ = x;
-}
-
-inline const std::string& options::
-options_file () const
-{
- return this->options_file_;
-}
-
-inline std::string& options::
-options_file ()
-{
- return this->options_file_;
-}
-
-inline void options::
-options_file(const std::string& x)
-{
- this->options_file_ = x;
-}
-
-inline bool options::
-options_file_specified () const
-{
- return this->options_file_specified_;
-}
-
-inline void options::
-options_file_specified(bool x)
-{
- this->options_file_specified_ = x;
-}
-
-inline const std::vector<std::string>& options::
-x () const
-{
- return this->x_;
-}
-
-inline std::vector<std::string>& options::
-x ()
-{
- return this->x_;
-}
-
-inline void options::
-x(const std::vector<std::string>& x)
-{
- this->x_ = x;
-}
-
-inline bool options::
-x_specified () const
-{
- return this->x_specified_;
-}
-
-inline void options::
-x_specified(bool x)
-{
- this->x_specified_ = x;
-}
-
-inline const bool& options::
-v () const
-{
- return this->v_;
-}
-
-inline bool& options::
-v ()
-{
- return this->v_;
-}
-
-inline void options::
-v(const bool& x)
-{
- this->v_ = x;
-}
-
-inline const bool& options::
-trace () const
-{
- return this->trace_;
-}
-
-inline bool& options::
-trace ()
-{
- return this->trace_;
-}
-
-inline void options::
-trace(const bool& x)
-{
- this->trace_ = x;
-}
-
-inline const std::string& options::
-mysql_engine () const
-{
- return this->mysql_engine_;
-}
-
-inline std::string& options::
-mysql_engine ()
-{
- return this->mysql_engine_;
-}
-
-inline void options::
-mysql_engine(const std::string& x)
-{
- this->mysql_engine_ = x;
-}
-
-inline bool options::
-mysql_engine_specified () const
-{
- return this->mysql_engine_specified_;
-}
-
-inline void options::
-mysql_engine_specified(bool x)
-{
- this->mysql_engine_specified_ = x;
-}
-
-inline const bool& options::
-sqlite_override_null () const
-{
- return this->sqlite_override_null_;
-}
-
-inline bool& options::
-sqlite_override_null ()
-{
- return this->sqlite_override_null_;
-}
-
-inline void options::
-sqlite_override_null(const bool& x)
-{
- this->sqlite_override_null_ = x;
-}
-
-inline const bool& options::
-sqlite_lax_auto_id () const
-{
- return this->sqlite_lax_auto_id_;
-}
-
-inline bool& options::
-sqlite_lax_auto_id ()
-{
- return this->sqlite_lax_auto_id_;
-}
-
-inline void options::
-sqlite_lax_auto_id(const bool& x)
-{
- this->sqlite_lax_auto_id_ = x;
-}
-
-inline const ::pgsql_version& options::
-pgsql_server_version () const
-{
- return this->pgsql_server_version_;
-}
-
-inline ::pgsql_version& options::
-pgsql_server_version ()
-{
- return this->pgsql_server_version_;
-}
-
-inline void options::
-pgsql_server_version(const ::pgsql_version& x)
-{
- this->pgsql_server_version_ = x;
-}
-
-inline bool options::
-pgsql_server_version_specified () const
-{
- return this->pgsql_server_version_specified_;
-}
-
-inline void options::
-pgsql_server_version_specified(bool x)
-{
- this->pgsql_server_version_specified_ = x;
-}
-
-inline const ::oracle_version& options::
-oracle_client_version () const
-{
- return this->oracle_client_version_;
-}
-
-inline ::oracle_version& options::
-oracle_client_version ()
-{
- return this->oracle_client_version_;
-}
-
-inline void options::
-oracle_client_version(const ::oracle_version& x)
-{
- this->oracle_client_version_ = x;
-}
-
-inline bool options::
-oracle_client_version_specified () const
-{
- return this->oracle_client_version_specified_;
-}
-
-inline void options::
-oracle_client_version_specified(bool x)
-{
- this->oracle_client_version_specified_ = x;
-}
-
-inline const bool& options::
-oracle_warn_truncation () const
-{
- return this->oracle_warn_truncation_;
-}
-
-inline bool& options::
-oracle_warn_truncation ()
-{
- return this->oracle_warn_truncation_;
-}
-
-inline void options::
-oracle_warn_truncation(const bool& x)
-{
- this->oracle_warn_truncation_ = x;
-}
-
-inline const ::mssql_version& options::
-mssql_server_version () const
-{
- return this->mssql_server_version_;
-}
-
-inline ::mssql_version& options::
-mssql_server_version ()
-{
- return this->mssql_server_version_;
-}
-
-inline void options::
-mssql_server_version(const ::mssql_version& x)
-{
- this->mssql_server_version_ = x;
-}
-
-inline bool options::
-mssql_server_version_specified () const
-{
- return this->mssql_server_version_specified_;
-}
-
-inline void options::
-mssql_server_version_specified(bool x)
-{
- this->mssql_server_version_specified_ = x;
-}
-
-inline const unsigned int& options::
-mssql_short_limit () const
-{
- return this->mssql_short_limit_;
-}
-
-inline unsigned int& options::
-mssql_short_limit ()
-{
- return this->mssql_short_limit_;
-}
-
-inline void options::
-mssql_short_limit(const unsigned int& x)
-{
- this->mssql_short_limit_ = x;
-}
-
-inline bool options::
-mssql_short_limit_specified () const
-{
- return this->mssql_short_limit_specified_;
-}
-
-inline void options::
-mssql_short_limit_specified(bool x)
-{
- this->mssql_short_limit_specified_ = x;
-}
-
-// Begin epilogue.
-//
-//
-// End epilogue.
diff --git a/odb/parser.cxx b/odb/parser.cxx
deleted file mode 100644
index 58388c9..0000000
--- a/odb/parser.cxx
+++ /dev/null
@@ -1,2323 +0,0 @@
-// file : odb/parser.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <odb/gcc.hxx> // Keep it first.
-
-#include <set>
-#include <map>
-#include <vector>
-#include <string>
-#include <cassert>
-#include <sstream>
-#include <iostream>
-
-#include <odb/diagnostics.hxx>
-#include <odb/parser.hxx>
-#include <odb/semantics.hxx>
-
-using namespace std;
-using namespace semantics;
-
-class parser::impl
-{
-public:
- typedef parser::failed failed;
-
- impl (options const&, loc_pragmas&, ns_loc_pragmas&, decl_pragmas&);
-
- unique_ptr<unit>
- parse (tree global_scope, path const& main_file);
-
-private:
- typedef semantics::access access;
-
- // Extended GCC tree declaration that is either a GCC tree
- // declaration, a virtual declaration, or a pragma. If it is
- // a pragma, then the assoc flag indicated whether this pragma
- // has been associated with a declaration. Otherwise, the assoc
- // flag indicates whether pragmas have been associated with this
- // declaration (we use this to ignore certain declarations for
- // pragma association purposes, e.g., the anonymous type in
- // struct {...} m_).
- //
- struct tree_decl
- {
- tree decl;
- virt_declaration const* vdecl;
- pragma const* prag;
- mutable bool assoc; // Allow modification via std::set iterator.
-
- tree_decl (tree d): decl (d), vdecl (0), prag (0), assoc (false) {}
- tree_decl (virt_declaration const& d)
- : decl (0), vdecl (&d), prag (0), assoc (false) {}
- tree_decl (pragma const& p)
- : decl (0), vdecl (0), prag (&p), assoc (false) {}
-
- bool
- operator< (tree_decl const& y) const;
- };
-
- typedef multiset<tree_decl> decl_set;
-
-private:
- void
- collect (tree ns);
-
- void
- emit ();
-
- // Emit a type declaration. This is either a named class-type definition/
- // declaration or a typedef. In the former case the function returns the
- // newly created type node. In the latter case it returns 0.
- //
- type*
- emit_type_decl (tree);
-
- // Emit a template declaration.
- //
- void
- emit_template_decl (tree);
-
- class_template&
- emit_class_template (tree, bool stub = false);
-
- union_template&
- emit_union_template (tree, bool stub = false);
-
- template <typename T>
- T&
- emit_class (tree, path const& f, size_t l, size_t c, bool stub = false);
-
- template <typename T>
- T&
- emit_union (tree, path const& f, size_t l, size_t c, bool stub = false);
-
- // Access is not used when creating a stub.
- //
- enum_&
- emit_enum (tree,
- access,
- path const& f,
- size_t l,
- size_t c,
- bool stub = false);
-
- // Create new or find existing semantic graph type.
- //
- type&
- emit_type (tree, access, path const& f, size_t l, size_t c);
-
- type&
- create_type (tree, access, path const& f, size_t l, size_t c);
-
- string
- emit_type_name (tree, bool direct = true);
-
-
- //
- // Pragma handling.
- //
- void
- add_pragma (node&, pragma const&);
-
- // Process positioned and named pragmas.
- //
- void
- process_pragmas (declaration const&,
- node&,
- string const& name,
- decl_set::const_iterator begin,
- decl_set::const_iterator cur,
- decl_set::const_iterator end);
-
- // Process named pragmas only.
- //
- void
- process_named_pragmas (declaration const&, node&);
-
- void
- diagnose_unassoc_pragmas (decl_set const&);
-
- // Return declaration's fully-qualified scope name (e.g., ::foo::bar).
- //
- string
- fq_scope (tree);
-
- // Return declaration's access.
- //
- access
- decl_access (tree decl)
- {
- // Note that TREE_PUBLIC() returns something other than what we need.
- //
- if (TREE_PRIVATE (decl))
- return access::private_;
-
- if (TREE_PROTECTED (decl))
- return access::protected_;
-
- return access::public_;
- }
-
- //
- //
- template <typename T>
- void
- define_fund (tree);
-
-private:
- options const& ops_;
- loc_pragmas& loc_pragmas_;
- ns_loc_pragmas& ns_loc_pragmas_;
- decl_pragmas& decl_pragmas_;
-
- bool trace;
- ostream& ts;
-
- unit* unit_;
- scope* scope_;
- size_t error_;
-
- decl_set decls_;
-
- typedef map<location_t, tree> decl_map;
- decl_map all_decls_;
-};
-
-bool parser::impl::tree_decl::
-operator< (tree_decl const& y) const
-{
- location_t xl, yl;
- int xb (0), yb (0);
-
- if (decl != 0)
- xl = real_source_location (decl);
- else if (vdecl != 0)
- {
- xl = vdecl->ord;
- xb = vdecl->ord_bias;
- }
- else
- xl = prag->loc;
-
- if (y.decl != 0)
- yl = real_source_location (y.decl);
- else if (y.vdecl != 0)
- {
- yl = y.vdecl->ord;
- yb = y.vdecl->ord_bias;
- }
- else
- yl = y.prag->loc;
-
- // If both are virtual and at the same location, use the definition
- // location to order them.
- //
- if (vdecl != 0 && y.vdecl != 0 && xl == yl && xb == yb)
- {
- xl = vdecl->loc;
- yl = y.vdecl->loc;
- }
-
- return xl < yl || (xl == yl && xb < yb);
-}
-
-//
-// Function templates.
-//
-
-template <typename T>
-void parser::impl::
-define_fund (tree t)
-{
- t = TYPE_MAIN_VARIANT (t);
- char const* name (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (t))));
-
- T& node (unit_->new_fund_node<T> (t));
- unit_->new_edge<defines> (*scope_, node, name);
- unit_->insert (t, node);
-
- process_named_pragmas (t, node);
-}
-
-template <typename T>
-T& parser::impl::
-emit_class (tree c, path const& file, size_t line, size_t clmn, bool stub)
-{
- c = TYPE_MAIN_VARIANT (c);
-
- // See if there is a stub already for this type.
- //
- T* c_node (0);
-
- if (node* n = unit_->find (c))
- {
- c_node = &dynamic_cast<T&> (*n);
- }
- else
- {
- c_node = &unit_->new_node<T> (file, line, clmn, c);
- unit_->insert (c, *c_node);
- }
-
- if (stub || !COMPLETE_TYPE_P (c))
- return *c_node;
-
- // Traverse base information.
- //
- tree bis (TYPE_BINFO (c));
- size_t n (bis ? BINFO_N_BASE_BINFOS (bis) : 0);
-
- for (size_t i (0); i < n; i++)
- {
- tree bi (BINFO_BASE_BINFO (bis, i));
- access a (access::public_);
-
- if (BINFO_BASE_ACCESSES (bis))
- {
- tree ac (BINFO_BASE_ACCESS (bis, i));
-
- if (ac == NULL_TREE || ac == access_public_node)
- {
- a = access::public_;
- }
- else if (ac == access_protected_node)
- {
- a = access::protected_;
- }
- else
- {
- assert (ac == access_private_node);
- a = access::private_;
- }
- }
-
- bool virt (BINFO_VIRTUAL_P (bi));
- tree base (TYPE_MAIN_VARIANT (BINFO_TYPE (bi)));
-
- // Find the corresponding graph node. If we cannot find one then
- // the base is a template instantiation since an ordinary class
- // has to be defined (complete) in order to be a base.
- //
- class_* b_node (0);
- string name;
-
- if (node* n = unit_->find (base))
- {
- b_node = &dynamic_cast<class_&> (*n);
-
- if (trace)
- name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (base)));
- }
- else
- {
- // Use public access for a template instantiation in the inheritance
- // declaration.
- //
- b_node = &dynamic_cast<class_&> (
- emit_type (base, access::public_, file, line, clmn));
-
- if (trace)
- name = emit_type_name (base);
- }
-
- unit_->new_edge<inherits> (*c_node, *b_node, a, virt);
-
- if (trace)
- ts << "\t" << a.string () << (virt ? " virtual" : "") << " base "
- << name << " (" << static_cast<type*> (b_node) << ")" << endl;
- }
-
- // Collect member declarations so that we can traverse them in
- // the source code order.
- //
- decl_set decls;
-
- for (tree d (TYPE_FIELDS (c)); d != NULL_TREE; d = TREE_CHAIN (d))
- {
- switch (TREE_CODE (d))
- {
- case TYPE_DECL:
- {
- if (!DECL_SELF_REFERENCE_P (d))
- decls.insert (d);
- break;
- }
- case TEMPLATE_DECL:
- {
- if (DECL_CLASS_TEMPLATE_P (d))
- decls.insert (d);
- break;
- }
- case FIELD_DECL:
- {
- if (!DECL_ARTIFICIAL (d))
- decls.insert (d);
- break;
- }
- default:
- {
- break;
- }
- }
- }
-
- // Add virtual declarations if any.
- //
- {
- virt_declarations::const_iterator i (virt_declarations_.find (c));
-
- if (i != virt_declarations_.end ())
- decls.insert (i->second.begin (), i->second.end ());
- }
-
- // Add location pragmas if any.
- //
- {
- loc_pragmas::const_iterator i (loc_pragmas_.find (c));
-
- if (i != loc_pragmas_.end ())
- decls.insert (i->second.begin (), i->second.end ());
- }
-
- scope* prev_scope (scope_);
- scope_ = c_node;
-
- for (decl_set::const_iterator b (decls.begin ()), i (b), e (decls.end ());
- i != e; ++i)
- {
- // Skip pragmas.
- //
- if (i->prag != 0)
- continue;
-
- // Handle virtual declarations.
- //
- if (i->vdecl != 0)
- {
- virt_declaration const& vd (*i->vdecl);
-
- switch (vd.tree_code)
- {
- case FIELD_DECL:
- {
- // First check that it doesn't conflict with any of the real
- // data members defined in this class.
- //
- tree d (
- lookup_qualified_name (
- c, get_identifier (vd.name.c_str ()), false, false));
-
- if (d != error_mark_node && TREE_CODE (d) == FIELD_DECL)
- {
- error (vd.loc) << "virtual data member declaration '" << vd.name
- << "' conflicts with a previous declaration"
- << endl;
-
- location_t l (real_source_location (d));
- info (l) << "'" << vd.name << "' was previously declared here"
- << endl;
-
- throw failed ();
- }
-
- path file (LOCATION_FILE (vd.loc));
- size_t line (LOCATION_LINE (vd.loc));
- size_t clmn (LOCATION_COLUMN (vd.loc));
-
- access a (access::public_);
-
- type& type_node (emit_type (vd.type, a, file, line, clmn));
- data_member& member_node (
- unit_->new_node<data_member> (file, line, clmn, tree (0)));
-
- unit_->new_edge<names> (*c_node, member_node, vd.name, a);
- belongs& edge (unit_->new_edge<belongs> (member_node, type_node));
-
- // See if there is a name hint for this type.
- //
- if (names* hint = unit_->find_hint (vd.type))
- edge.hint (*hint);
-
- // Process pragmas that may be associated with this field.
- //
- process_pragmas (vd, member_node, vd.name, b, i, e);
- break;
- }
- default:
- {
- assert (false);
- break;
- }
- }
- continue;
- }
-
- tree d (i->decl);
-
- switch (TREE_CODE (d))
- {
- case TYPE_DECL:
- {
- type* n (emit_type_decl (d));
-
- // If this is a named class-type definition, then handle
- // the pragmas.
- //
- if (n != 0)
- process_pragmas (n->tree_node (), *n, n->name (), b, i, e);
-
- break;
- }
- case TEMPLATE_DECL:
- {
- emit_template_decl (d);
- break;
- }
- case FIELD_DECL:
- {
- // If this is a bit-field then TREE_TYPE may be a modified type
- // with lesser precision. In this case, DECL_BIT_FIELD_TYPE
- // will be the type that was original specified. Use that type
- // for now. Furthermore, bitfields can be anonymous, which we
- // ignore.
- //
- //
- bool bf (DECL_C_BIT_FIELD (d));
-
- if (bf && DECL_NAME (d) == 0)
- break;
-
- // Another case where we can have NULL name is anonymous struct
- // or union extension, for example:
- //
- // struct s
- // {
- // union
- // {
- // int a;
- // int b;
- // };
- // };
- //
- // GCC appears to create a fake member for such a struct/union
- // without any name. Ignore such members for now.
- //
- if (DECL_NAME (d) == 0)
- break;
-
- tree t (bf ? DECL_BIT_FIELD_TYPE (d) : TREE_TYPE (d));
-
- char const* name (IDENTIFIER_POINTER (DECL_NAME (d)));
-
- path file (DECL_SOURCE_FILE (d));
- size_t line (DECL_SOURCE_LINE (d));
- size_t clmn (DECL_SOURCE_COLUMN (d));
-
- access a (decl_access (d));
-
- type& type_node (emit_type (t, a, file, line, clmn));
- data_member& member_node (
- unit_->new_node<data_member> (file, line, clmn, d));
- unit_->insert (d, member_node);
-
- unit_->new_edge<names> (*c_node, member_node, name, a);
- belongs& edge (unit_->new_edge<belongs> (member_node, type_node));
-
- // See if there is a name hint for this type.
- //
- if (names* hint = unit_->find_hint (t))
- edge.hint (*hint);
-
- if (trace)
- {
- string type_name (emit_type_name (t));
-
- ts << "\t" << a.string () << " data member " << type_name
- << " (" << &type_node << ") " << name << " at "
- << file << ":" << line << endl;
- }
-
- // Process pragmas that may be associated with this field.
- //
- process_pragmas (d, member_node, name, b, i, e);
-
- break;
- }
- default:
- {
- assert (false);
- break;
- }
- }
- }
-
- // Diagnose any position pragmas that haven't been associated.
- //
- diagnose_unassoc_pragmas (decls);
-
- scope_ = prev_scope;
- return *c_node;
-}
-
-template <typename T>
-T& parser::impl::
-emit_union (tree u, path const& file, size_t line, size_t clmn, bool stub)
-{
- u = TYPE_MAIN_VARIANT (u);
-
- // See if there is a stub already for this type.
- //
- T* u_node (0);
-
- if (node* n = unit_->find (u))
- {
- u_node = &dynamic_cast<T&> (*n);
- }
- else
- {
- u_node = &unit_->new_node<T> (file, line, clmn, u);
- unit_->insert (u, *u_node);
- }
-
- if (stub || !COMPLETE_TYPE_P (u))
- return *u_node;
-
- // Collect member declarations so that we can traverse them in
- // the source code order.
- //
- decl_set decls;
-
- for (tree d (TYPE_FIELDS (u)); d != NULL_TREE ; d = TREE_CHAIN (d))
- {
- switch (TREE_CODE (d))
- {
- case TYPE_DECL:
- {
- if (!DECL_SELF_REFERENCE_P (d))
- decls.insert (d);
- break;
- }
- case TEMPLATE_DECL:
- {
- if (DECL_CLASS_TEMPLATE_P (d))
- decls.insert (d);
- break;
- }
- case FIELD_DECL:
- {
- if (!DECL_ARTIFICIAL (d))
- decls.insert (d);
- break;
- }
- default:
- {
- break;
- }
- }
- }
-
- // Add location pragmas if any.
- //
- {
- loc_pragmas::const_iterator i (loc_pragmas_.find (u));
-
- if (i != loc_pragmas_.end ())
- decls.insert (i->second.begin (), i->second.end ());
- }
-
- scope* prev_scope (scope_);
- scope_ = u_node;
-
- for (decl_set::const_iterator b (decls.begin ()), i (b), e (decls.end ());
- i != e; ++i)
- {
- // Skip pragmas.
- //
- if (i->prag)
- continue;
-
- tree d (i->decl);
-
- switch (TREE_CODE (d))
- {
- case TYPE_DECL:
- {
- type* n (emit_type_decl (d));
-
- // If this is a named class-type definition, then handle
- // the pragmas.
- //
- if (n != 0)
- process_pragmas (n->tree_node (), *n, n->name (), b, i, e);
-
- break;
- }
- case TEMPLATE_DECL:
- {
- emit_template_decl (d);
- break;
- }
- case FIELD_DECL:
- {
- // We can have NULL name in case of anonymous struct or union
- // extension, for example:
- //
- // union s
- // {
- // struct
- // {
- // int a;
- // int b;
- // };
- // int c;
- // };
- //
- // GCC appears to create a fake member for such a struct/union
- // without any name. Ignore such members for now.
- //
- if (DECL_NAME (d) == 0)
- break;
-
- tree t (TREE_TYPE (d));
- char const* name (IDENTIFIER_POINTER (DECL_NAME (d)));
-
- path file (DECL_SOURCE_FILE (d));
- size_t line (DECL_SOURCE_LINE (d));
- size_t clmn (DECL_SOURCE_COLUMN (d));
-
- access a (decl_access (d));
-
- type& type_node (emit_type (t, a, file, line, clmn));
- data_member& member_node (
- unit_->new_node<data_member> (file, line, clmn, d));
- unit_->insert (d, member_node);
-
- unit_->new_edge<names> (*u_node, member_node, name, a);
- belongs& edge (unit_->new_edge<belongs> (member_node, type_node));
-
- // See if there is a name hint for this type.
- //
- if (names* hint = unit_->find_hint (t))
- edge.hint (*hint);
-
- if (trace)
- {
- string type_name (emit_type_name (t));
-
- ts << "\t" << a.string () << " union member " << type_name
- << " (" << &type_node << ") " << name << " at "
- << file << ":" << line << endl;
- }
-
- // Process pragmas that may be associated with this field.
- //
- process_pragmas (d, member_node, name, b, i, e);
-
- break;
- }
- default:
- {
- break;
- }
- }
- }
-
- // Diagnose any position pragmas that haven't been associated.
- //
- diagnose_unassoc_pragmas (decls);
-
- scope_ = prev_scope;
- return *u_node;
-}
-
-//
-// Functions.
-//
-
-parser::impl::
-impl (options const& ops,
- loc_pragmas & lp,
- ns_loc_pragmas& nslp,
- decl_pragmas& dp)
- : ops_ (ops),
- loc_pragmas_ (lp),
- ns_loc_pragmas_ (nslp),
- decl_pragmas_ (dp),
- trace (ops.trace ()),
- ts (cerr)
-{
-}
-
-unique_ptr<unit> parser::impl::
-parse (tree global_scope, path const& main_file)
-{
- unique_ptr<unit> u (new unit (main_file));
- u->insert (global_namespace, *u);
- process_named_pragmas (global_namespace, *u);
-
- unit_ = u.get ();
- scope_ = unit_;
- error_ = 0;
-
- // Define fundamental types.
- //
- define_fund<fund_void> (void_type_node);
- define_fund<fund_bool> (boolean_type_node);
- define_fund<fund_char> (char_type_node);
- define_fund<fund_wchar> (wchar_type_node);
-
- if (ops_.std () >= cxx_version::cxx11)
- {
- define_fund<fund_char16> (char16_type_node);
- define_fund<fund_char32> (char32_type_node);
- }
-
- define_fund<fund_signed_char> (signed_char_type_node);
- define_fund<fund_unsigned_char> (unsigned_char_type_node);
- define_fund<fund_short> (short_integer_type_node);
- define_fund<fund_unsigned_short> (short_unsigned_type_node);
- define_fund<fund_int> (integer_type_node);
- define_fund<fund_unsigned_int> (unsigned_type_node);
- define_fund<fund_long> (long_integer_type_node);
- define_fund<fund_unsigned_long> (long_unsigned_type_node);
- define_fund<fund_long_long> (long_long_integer_type_node);
- define_fund<fund_unsigned_long_long> (long_long_unsigned_type_node);
- define_fund<fund_float> (float_type_node);
- define_fund<fund_double> (double_type_node);
- define_fund<fund_long_double> (long_double_type_node);
-
- // First collect all the namespace-level declarations we are
- // interested in in the line-decl map so that they appear in
- // the source code order.
- //
- collect (global_scope);
-
- // Add namespace-level position pragmas if any.
- //
- {
- loc_pragmas::const_iterator i (loc_pragmas_.find (global_namespace));
-
- if (i != loc_pragmas_.end ())
- decls_.insert (i->second.begin (), i->second.end ());
- }
-
- // Convert position namespace pragmas to name pragmas.
- //
- for (ns_loc_pragmas::const_iterator i (ns_loc_pragmas_.begin ());
- i != ns_loc_pragmas_.end (); ++i)
- {
- pragma const& p (i->pragma);
-
- decl_map::const_iterator j (all_decls_.lower_bound (p.loc));
-
- if (j == all_decls_.end ())
- {
- error (p.loc)
- << "db pragma '" << p.pragma_name << "' is not associated with a "
- << "namespace declaration" << endl;
- error_++;
- continue;
- }
-
- // Find the "namespace difference" between this declaration and
- // the pragma's namespace. The outermost namespace in the result
- // is what we are looking for.
- //
- tree ns (0);
-
- for (tree prev (j->second), scope (CP_DECL_CONTEXT (prev));;
- scope = CP_DECL_CONTEXT (scope))
- {
- if (scope == i->ns)
- {
- ns = prev;
- break;
- }
-
- if (scope == global_namespace)
- break;
-
- prev = scope;
- }
-
- if (ns == 0 || TREE_CODE (ns) != NAMESPACE_DECL)
- {
- error (p.loc)
- << "db pragma '" << p.pragma_name << "' is not associated with a "
- << "namespace declaration" << endl;
- error_++;
- continue;
- }
-
- pragma_set& s (decl_pragmas_[ns]);
- pragma_set::iterator it (s.find (p.context_name));
-
- // Make sure we override only if this pragma came after the one
- // already in the set.
- //
- if (it == s.end () || it->second.loc <= p.loc)
- s.insert (p);
- }
-
- // Construct the semantic graph.
- //
- if (error_ == 0)
- emit ();
-
- if (error_ > 0)
- throw failed ();
-
- return u;
-}
-
-void parser::impl::
-collect (tree ns)
-{
- cp_binding_level* level = NAMESPACE_LEVEL (ns);
- tree decl = level->names;
-
- // Collect declarations.
- //
- for (; decl != NULL_TREE; decl = TREE_CHAIN (decl))
- {
- all_decls_[real_source_location (decl)] = decl;
-
- if (DECL_IS_BUILTIN (decl))
- continue;
-
- switch (TREE_CODE (decl))
- {
- case TYPE_DECL:
- {
- // Skip special type declarations.
- //
- if (DECL_NAME (decl) == NULL_TREE)
- continue;
-
- tree type (TREE_TYPE (decl));
- if (LAMBDA_TYPE_P (type))
- continue;
-
- decls_.insert (decl);
- break;
- }
- case TEMPLATE_DECL:
- {
- if (DECL_CLASS_TEMPLATE_P (decl))
- decls_.insert (decl);
-
- break;
- }
- default:
- {
- break;
- }
- }
- }
-
- // Traverse namespaces.
- //
- for (
-#if BUILDING_GCC_MAJOR >= 8
- decl = level->names;
-#else
- decl = level->namespaces;
-#endif
- decl != NULL_TREE;
- decl = TREE_CHAIN (decl))
- {
-#if BUILDING_GCC_MAJOR >= 8
- // Now namespaces are interleaved with other declarations. In fact, we
- // could probably collect everything in a single pass.
- //
- if (TREE_CODE (decl) != NAMESPACE_DECL)
- continue;
-#endif
-
- // Ignore namespace aliases.
- //
- if (DECL_NAMESPACE_ALIAS (decl))
- continue;
-
- if (!DECL_IS_BUILTIN (decl) || DECL_NAMESPACE_STD_P (decl))
- {
- if (trace)
- {
- tree dn (DECL_NAME (decl));
- char const* name (dn ? IDENTIFIER_POINTER (dn) : "<anonymous>");
-
- ts << "namespace " << name << " at "
- << DECL_SOURCE_FILE (decl) << ":"
- << DECL_SOURCE_LINE (decl) << endl;
- }
-
- collect (decl);
- }
- }
-}
-
-void parser::impl::
-emit ()
-{
- for (decl_set::const_iterator b (decls_.begin ()), i (b),
- e (decls_.end ()); i != e; ++i)
- {
- // Skip pragmas.
- //
- if (i->prag)
- continue;
-
- tree decl (i->decl);
-
- // Get this declaration's namespace and unwind our scope until
- // we find a common prefix of namespaces.
- //
- string pfx;
- string ns (fq_scope (decl));
-
- for (pfx = scope_->fq_name (); !pfx.empty (); pfx = scope_->fq_name ())
- {
- size_t n (pfx.size ());
-
- // Make sure we handle cases like ns="::foobar", pfx="::foo".
- //
- if (ns.compare (0, n, pfx) == 0 && (ns.size () == n || ns[n - 1] == ':'))
- break;
-
- if (trace)
- ts << "closing namespace " << scope_->name () << endl;
-
- scope_ = &scope_->scope_ ();
- }
-
- // Build the rest of the namespace hierarchy for this declaration.
- //
- if (ns != pfx)
- {
- path f (DECL_SOURCE_FILE (decl));
- size_t l (DECL_SOURCE_LINE (decl));
- size_t c (DECL_SOURCE_COLUMN (decl));
-
- for (size_t b (pfx.size () + 2), e (ns.find ("::", b));
- b != string::npos;)
- {
- string n (ns, b, e == string::npos ? e : e - b);
-
- if (trace)
- ts << "opening namespace " << n << " for "
- << DECL_SOURCE_FILE (decl) << ":"
- << DECL_SOURCE_LINE (decl) << endl;
-
- // Use the declarations's file, line, and column as an
- // approximation for this namespace origin. Also resolve
- // the tree node for this namespace.
- //
-#if BUILDING_GCC_MAJOR >= 8
- tree tree_node (
- get_namespace_binding (
- scope_->tree_node (), get_identifier (n.c_str ())));
-#else
- tree tree_node (
- namespace_binding (
- get_identifier (n.c_str ()), scope_->tree_node ()));
-#endif
-
- namespace_& node (unit_->new_node<namespace_> (f, l, c, tree_node));
- unit_->new_edge<defines> (*scope_, node, n);
-
- if (namespace_* orig =
- dynamic_cast<namespace_*> (unit_->find (tree_node)))
- {
- // This is an extension.
- //
- node.original (*orig);
- }
- else
- {
- // This is the original. Add it to the map and process any
- // pragmas it might have (at this stage namespaces can only
- // have name pragmas).
- //
- unit_->insert (tree_node, node);
- process_named_pragmas (tree_node, node);
- }
-
- scope_ = &node;
-
- if (e == string::npos)
- b = e;
- else
- {
- b = e + 2;
- e = ns.find ("::", b);
- }
- }
- }
-
- switch (TREE_CODE (decl))
- {
- case TYPE_DECL:
- {
- type* n (emit_type_decl (decl));
-
- // If this is a named class-type definition, then handle
- // the pragmas.
- //
- if (n != 0)
- process_pragmas (n->tree_node (), *n, n->name (), b, i, e);
-
- break;
- }
- case TEMPLATE_DECL:
- {
- emit_template_decl (decl);
- break;
- }
- default:
- {
- break;
- }
- }
- }
-
- // Diagnose any position pragmas that haven't been associated.
- //
- diagnose_unassoc_pragmas (decls_);
-}
-
-type* parser::impl::
-emit_type_decl (tree decl)
-{
- tree t (TREE_TYPE (decl));
- gcc_tree_code_type tc (TREE_CODE (t));
-
- tree decl_name (DECL_NAME (decl));
- char const* name (IDENTIFIER_POINTER (decl_name));
-
- if (DECL_ARTIFICIAL (decl) &&
- (tc == RECORD_TYPE || tc == UNION_TYPE || tc == ENUMERAL_TYPE))
- {
- // If we have an anonymous class typedef, use the user-
- // supplied name instead of the synthesized one. ARM
- // says that in typedef struct {} S; S becomes struct's
- // name.
- //
- if (IDENTIFIER_ANON_P (decl_name))
- {
- tree d (TYPE_NAME (t));
-
- if (d != NULL_TREE &&
- !DECL_ARTIFICIAL (d) &&
- DECL_NAME (d) != NULL_TREE &&
- !IDENTIFIER_ANON_P (DECL_NAME (d)))
- {
- decl = d;
- decl_name = DECL_NAME (decl);
- name = IDENTIFIER_POINTER (decl_name);
- }
- else
- {
- // This type has only the synthesized name which means that
- // it is either typedef'ed as a derived type or it is used
- // to declare a varibale or similar. The first case will be
- // covered by the typedef handling code below. The second
- // case will be covere by emit_type().
- //
- return 0;
- }
- }
-
- path file (DECL_SOURCE_FILE (decl));
- size_t line (DECL_SOURCE_LINE (decl));
- size_t clmn (DECL_SOURCE_COLUMN (decl));
-
- type* node (0);
-
- // Pointers to member functions are represented as record
- // types. Detect and handle this case.
- //
- if (TYPE_PTRMEMFUNC_P (t))
- {
- t = TYPE_MAIN_VARIANT (t);
- node = &unit_->new_node<unsupported_type> (
- file, line, clmn, t, "pointer_to_member_function_type");
- unit_->insert (t, *node);
- }
- else
- {
-
- if (trace)
- ts << "start " << gcc_tree_code_name(tc) << " " << name
- << " at " << file << ":" << line << endl;
-
- switch (tc)
- {
- case RECORD_TYPE:
- {
- node = &emit_class<class_> (t, file, line, clmn);
- break;
- }
- case UNION_TYPE:
- {
- node = &emit_union<union_> (t, file, line, clmn);
- break;
- }
- case ENUMERAL_TYPE:
- {
- node = &emit_enum (t, decl_access (decl), file, line, clmn);
- break;
- }
- default:
- break;
- }
-
- if (trace)
- ts << "end " << gcc_tree_code_name(tc) << " " << name
- << " (" << node << ") at "
- << DECL_SOURCE_FILE (decl) << ":"
- << DECL_SOURCE_LINE (decl) << endl;
- }
-
- if (COMPLETE_TYPE_P (t))
- unit_->new_edge<defines> (*scope_, *node, name);
- else
- unit_->new_edge<declares> (*scope_, *node, name);
-
- return node;
- }
- else
- {
- // Normal typedef. We need to detect and ignore the anonymous
- // class typedef case described above since we already used
- // this name to define the class.
- //
- if ((tc == RECORD_TYPE || tc == UNION_TYPE || tc == ENUMERAL_TYPE) &&
- TYPE_NAME (TYPE_MAIN_VARIANT (t)) == decl)
- return 0;
-
- path f (DECL_SOURCE_FILE (decl));
- size_t l (DECL_SOURCE_LINE (decl));
- size_t c (DECL_SOURCE_COLUMN (decl));
-
- type& node (emit_type (t, decl_access (decl), f, l, c));
- typedefs& edge (unit_->new_edge<typedefs> (*scope_, node, name));
-
- // Find our hint.
- //
- if (tree ot = DECL_ORIGINAL_TYPE (decl))
- {
- if (names* hint = unit_->find_hint (ot))
- edge.hint (*hint);
- }
-
- // Add this edge to the hint map. It may already be there if we
- // are handling something like this:
- //
- // typedef foo bar;
- // typedef bar foo;
- //
- // GCC also appears to re-purpose a node for another name (not
- // sure if its a bug or a feature), so use the latest seen name.
- //
- unit_->insert_hint (t, edge);
-
- if (trace)
- {
- string s (emit_type_name (t, false));
-
- ts << "typedef " << s << " (" << &node << ") -> " << name
- << " at " << f << ":" << l << endl;
- }
-
- return 0;
- }
-}
-
-void parser::impl::
-emit_template_decl (tree decl)
-{
- // Currently we only handle class/union templates.
- //
- tree t (TREE_TYPE (DECL_TEMPLATE_RESULT (decl)));
- gcc_tree_code_type tc (TREE_CODE (t));
-
- if (trace)
- {
- ts << gcc_tree_code_name(tc) << " template (" << decl << ") "
- << IDENTIFIER_POINTER (DECL_NAME (decl)) << " (" << t << ") at "
- << DECL_SOURCE_FILE (decl) << ":"
- << DECL_SOURCE_LINE (decl) << endl;
-
- ts << "specializations:" << endl;
-
- for (tree s (DECL_TEMPLATE_SPECIALIZATIONS (decl));
- s != NULL_TREE; s = TREE_CHAIN (s))
- {
- tree t (TREE_TYPE (s));
- tree d (TYPE_NAME (t));
-
- ts << "\tspecialization " << t << " at "
- << DECL_SOURCE_FILE (d) << ":"
- << DECL_SOURCE_LINE (d) << endl;
- }
-
- ts << "instantiations:" << endl;
-
- for (tree i (DECL_TEMPLATE_INSTANTIATIONS (decl));
- i != NULL_TREE; i = TREE_CHAIN (i))
- {
- tree t (TREE_VALUE (i));
- tree d (TYPE_NAME (t));
-
- ts << "\tinstantiation " << t << " at "
- << DECL_SOURCE_FILE (d) << ":"
- << DECL_SOURCE_LINE (d) << endl;
- }
- }
-
- char const* name (IDENTIFIER_POINTER (DECL_NAME (decl)));
-
- if (trace)
- ts << "start " << gcc_tree_code_name(tc) << " template " << name << " at "
- << DECL_SOURCE_FILE (decl) << ":"
- << DECL_SOURCE_LINE (decl) << endl;
-
- type_template* t_node (0);
-
- if (tc == RECORD_TYPE)
- t_node = &emit_class_template (decl);
- else
- t_node = &emit_union_template (decl);
-
- if (COMPLETE_TYPE_P (t))
- unit_->new_edge<defines> (*scope_, *t_node, name);
- else
- unit_->new_edge<declares> (*scope_, *t_node, name);
-
- if (trace)
- ts << "end " << gcc_tree_code_name(tc) << " template " << name
- << " (" << t_node << ") at "
- << DECL_SOURCE_FILE (decl) << ":"
- << DECL_SOURCE_LINE (decl) << endl;
-}
-
-class_template& parser::impl::
-emit_class_template (tree t, bool stub)
-{
- // See if there is a stub already for this template.
- //
- class_template* ct_node (0);
- tree c (TREE_TYPE (DECL_TEMPLATE_RESULT (t)));
-
- if (node* n = unit_->find (t))
- {
- ct_node = &dynamic_cast<class_template&> (*n);
- }
- else
- {
- path f (DECL_SOURCE_FILE (t));
- size_t ln (DECL_SOURCE_LINE (t));
- size_t cl (DECL_SOURCE_COLUMN (t));
-
- ct_node = &unit_->new_node<class_template> (f, ln, cl, c);
- unit_->insert (t, *ct_node);
- }
-
- if (stub || !COMPLETE_TYPE_P (c))
- return *ct_node;
-
- // Collect member declarations so that we can traverse them in
- // the source code order. For now we are only interested in
- // nested class template declarations.
- //
- decl_set decls;
-
- for (tree d (TYPE_FIELDS (c)); d != NULL_TREE ; d = TREE_CHAIN (d))
- {
- switch (TREE_CODE (d))
- {
- case TEMPLATE_DECL:
- {
- if (DECL_CLASS_TEMPLATE_P (d))
- decls.insert (d);
- break;
- }
- default:
- {
- break;
- }
- }
- }
-
- scope* prev_scope (scope_);
- scope_ = ct_node;
-
- for (decl_set::const_iterator i (decls.begin ()), e (decls.end ());
- i != e; ++i)
- {
- // Skip pragmas.
- //
- if (i->prag)
- continue;
-
- tree d (i->decl);
-
- switch (TREE_CODE (d))
- {
- case TEMPLATE_DECL:
- {
- emit_template_decl (d);
- break;
- }
- default:
- {
- break;
- }
- }
- }
-
- // Diagnose any position pragmas that haven't been associated.
- //
- diagnose_unassoc_pragmas (decls);
-
- scope_ = prev_scope;
- return *ct_node;
-}
-
-union_template& parser::impl::
-emit_union_template (tree t, bool stub)
-{
- // See if there is a stub already for this template.
- //
- union_template* ut_node (0);
- tree u (TREE_TYPE (DECL_TEMPLATE_RESULT (t)));
-
- if (node* n = unit_->find (t))
- {
- ut_node = &dynamic_cast<union_template&> (*n);
- }
- else
- {
- path f (DECL_SOURCE_FILE (t));
- size_t l (DECL_SOURCE_LINE (t));
- size_t c (DECL_SOURCE_COLUMN (t));
-
- ut_node = &unit_->new_node<union_template> (f, l, c, u);
- unit_->insert (t, *ut_node);
- }
-
- if (stub || !COMPLETE_TYPE_P (u))
- return *ut_node;
-
- // Collect member declarations so that we can traverse them in
- // the source code order. For now we are only interested in
- // nested class template declarations.
- //
- decl_set decls;
-
- for (tree d (TYPE_FIELDS (u)); d != NULL_TREE ; d = TREE_CHAIN (d))
- {
- switch (TREE_CODE (d))
- {
- case TEMPLATE_DECL:
- {
- if (DECL_CLASS_TEMPLATE_P (d))
- decls.insert (d);
- break;
- }
- default:
- {
- break;
- }
- }
- }
-
- scope* prev_scope (scope_);
- scope_ = ut_node;
-
- for (decl_set::const_iterator i (decls.begin ()), e (decls.end ());
- i != e; ++i)
- {
- // Skip pragmas.
- //
- if (i->prag)
- continue;
-
- tree d (i->decl);
-
- switch (TREE_CODE (d))
- {
- case TEMPLATE_DECL:
- {
- emit_template_decl (d);
- break;
- }
- default:
- {
- break;
- }
- }
- }
-
- // Diagnose any position pragmas that haven't been associated.
- //
- diagnose_unassoc_pragmas (decls);
-
- scope_ = prev_scope;
- return *ut_node;
-}
-
-enum_& parser::impl::
-emit_enum (tree e,
- access access,
- path const& file,
- size_t line,
- size_t clmn,
- bool stub)
-{
- e = TYPE_MAIN_VARIANT (e);
-
- // See if there is a stub already for this type.
- //
- enum_* e_node (0);
-
- if (node* n = unit_->find (e))
- e_node = &dynamic_cast<enum_&> (*n);
- else
- {
- e_node = &unit_->new_node<enum_> (file, line, clmn, e);
-
- // Set the underlying type even for incomplete (forward-declared) enums.
- //
- tree ut (ENUM_UNDERLYING_TYPE (e));
- names* hint (unit_->find_hint (ut));
- integral_type* un = dynamic_cast<integral_type*> (
- unit_->find (TYPE_MAIN_VARIANT (ut)));
-
- // For "old" enums GCC creates a distinct type node and the only way to
- // get to one of the known integrals is via its name.
- //
- if (un == 0)
- {
- ut = TREE_TYPE (TYPE_NAME (ut));
- un = dynamic_cast<integral_type*> (unit_->find (TYPE_MAIN_VARIANT (ut)));
- }
-
- underlies& edge (unit_->new_edge<underlies> (*un, *e_node));
-
- if (hint != 0)
- edge.hint (*hint);
-
- unit_->insert (e, *e_node);
- }
-
- if (stub || !COMPLETE_TYPE_P (e))
- return *e_node;
-
- // Traverse enumerators.
- //
- for (tree er (TYPE_VALUES (e)); er != NULL_TREE ; er = TREE_CHAIN (er))
- {
- char const* name (IDENTIFIER_POINTER (TREE_PURPOSE (er)));
- tree decl (TREE_VALUE (er));
- tree tval (DECL_INITIAL (decl));
-
- unsigned long long val (integer_value (tval));
-
- // There doesn't seem to be a way to get the proper position for
- // each enumerator.
- //
- enumerator& er_node = unit_->new_node<enumerator> (
- file, line, clmn, er, val);
- unit_->new_edge<enumerates> (*e_node, er_node);
- unit_->insert (decl, er_node);
-
- // In C++11 the enumerators are always available in the enum's
- // scope, even for old enums.
- //
- if (ops_.std () >= cxx_version::cxx11)
- unit_->new_edge<names> (*e_node, er_node, name, access::public_);
-
- // Inject enumerators into the outer scope unless this is an
- // enum class.
- //
- if (UNSCOPED_ENUM_P (e))
- unit_->new_edge<names> (*scope_, er_node, name, access);
-
- if (trace)
- ts << "\tenumerator " << name << " at " << file << ":" << line << endl;
- }
-
- return *e_node;
-}
-
-type& parser::impl::
-emit_type (tree t,
- access access,
- path const& file,
- size_t line,
- size_t clmn)
-{
- tree mv (TYPE_MAIN_VARIANT (t));
-
- if (trace)
- {
- ts << gcc_tree_code_name(TREE_CODE (t)) << " " << t
- << " main " << mv << endl;
-
- for (tree v (TYPE_MAIN_VARIANT (t)); v != 0; v = TYPE_NEXT_VARIANT (v))
- ts << "\tvariant " << v << " " << CP_TYPE_CONST_P (v) << endl;
- }
-
- node* n (unit_->find (mv));
-
- type& r (n != 0
- ? dynamic_cast<type&> (*n)
- : create_type (t, access, file, line, clmn));
-
- if (trace && n != 0)
- ts << "found node " << &r << " for type " << mv << endl;
-
- if (cp_type_quals (t) == TYPE_UNQUALIFIED)
- {
- unit_->insert (t, r); // Add this variant to the map.
- return r;
- }
-
- // See if this type already has this variant.
- //
- bool qc (CP_TYPE_CONST_P (t));
- bool qv (CP_TYPE_VOLATILE_P (t));
- bool qr (CP_TYPE_RESTRICT_P (t));
-
- for (type::qualified_iterator i (r.qualified_begin ());
- i != r.qualified_end (); ++i)
- {
- qualifier& q (i->qualifier ());
-
- if (q.const_ () == qc && q.volatile_ () == qv && q.restrict_ () == qr)
- {
- if (trace)
- ts << "found qualifier variant " << &q << endl;
-
- unit_->insert (t, q); // Add this variant to the map.
- return q;
- }
- }
-
- // No such variant yet. Create a new one. Qualified types are not
- // unique in the tree so don't add this node to the map.
- //
- qualifier& q (unit_->new_node<qualifier> (file, line, clmn, t, qc, qv, qr));
- qualifies& e (unit_->new_edge<qualifies> (q, r));
- unit_->insert (t, q);
-
- // See if there is a name hint for this type.
- //
- // If TREE_TYPE (TYPE_NAME (t)) != t then we have an inline qualifier,
- // as in:
- //
- // const foo x;
- //
- // If they are equal, then there are two possible cases. The first is
- // when we have a qualifier typedef, as in:
- //
- // typedef const foo cfoo;
- // cfoo x;
- //
- // The second is when we have a qualifier typedef but what is actually
- // used in the declaration is an inline qualifier, as in:
- //
- // typedef const foo cfoo;
- // const foo x;
- //
- // Unfortunately, in GCC, these two cases are indistinguishable. In
- // certain cases this can lead to a wrong hint being used for the base
- // type, for example:
- //
- // typedef foo my_foo;
- // typedef foo foo_t;
- // typedef const foo_t cfoo;
- //
- // const my_foo x;
- //
- // Above, the hint will be foo_t while it should be my_foo.
- //
- tree bt (0);
-
- if (tree decl = TYPE_NAME (t))
- {
- bt = TREE_TYPE (decl);
-
- if (t == bt)
- {
- // A const type can be named only with a typedef. Get the
- // original type.
- //
- tree ot (DECL_ORIGINAL_TYPE (decl));
-
- // And chase it one more time to get rid of the qualification.
- //
- decl = TYPE_NAME (ot);
- bt = decl != 0 ? TREE_TYPE (decl) : 0;
- }
- }
-
- if (bt != 0)
- {
- if (names* hint = unit_->find_hint (bt))
- e.hint (*hint);
- }
-
- process_named_pragmas (t, q);
-
- return q;
-}
-
-type& parser::impl::
-create_type (tree t,
- access access,
- path const& file,
- size_t line,
- size_t clmn)
-{
- type* r (0);
- gcc_tree_code_type tc (TREE_CODE (t));
-
- switch (tc)
- {
- //
- // User-defined types.
- //
- case RECORD_TYPE:
- case UNION_TYPE:
- {
- t = TYPE_MAIN_VARIANT (t);
- tree ti (TYPE_TEMPLATE_INFO (t));
-
- if (ti == NULL_TREE)
- {
- // Ordinary class. There are two situations which can lead
- // here. First is when we have an anonymous class that is
- // part of the declaration, for example:
- //
- // typedef const struct {...} s;
- //
- // The second situation is a named class definition which
- // we haven't parsed yet. In this case we are going to
- // create a "stub" class node which will be processed and
- // filled in later.
- //
-
- // Pointers to member functions are represented as record
- // types. Detect and handle this case.
- //
- if (TYPE_PTRMEMFUNC_P (t))
- {
- r = &unit_->new_node<unsupported_type> (
- file, line, clmn, t, "pointer_to_member_function_type");
- unit_->insert (t, *r);
- }
- else
- {
- tree d (TYPE_NAME (t));
-
- if (trace)
- ts << "start anon/stub " << gcc_tree_code_name(tc) << " at "
- << file << ":" << line << endl;
-
- if (d == NULL_TREE || IDENTIFIER_ANON_P (DECL_NAME (d)))
- {
- if (tc == RECORD_TYPE)
- r = &emit_class<class_> (t, file, line, clmn);
- else
- r = &emit_union<union_> (t, file, line, clmn);
- }
- else
- {
- // Use the "defining" declaration's file, line, and column
- // information to create the stub.
- //
- path f (DECL_SOURCE_FILE (d));
- size_t l (DECL_SOURCE_LINE (d));
- size_t c (DECL_SOURCE_COLUMN (d));
-
- if (tc == RECORD_TYPE)
- r = &emit_class<class_> (t, f, l, c, true);
- else
- r = &emit_union<union_> (t, f, l, c, true);
- }
-
- if (trace)
- ts << "end anon/stub " << gcc_tree_code_name(tc) << " (" << r << ")"
- << " at " << file << ":" << line << endl;
- }
- }
- else
- {
- // Template instantiation.
- //
- tree decl (TI_TEMPLATE (ti)); // DECL_TEMPLATE
-
- // Get to the most general template declaration.
- //
- while (DECL_TEMPLATE_INFO (decl))
- decl = DECL_TI_TEMPLATE (decl);
-
- type_template* t_node (0);
-
- // Find the template node or create a stub if none exist.
- //
- if (node* n = unit_->find (decl))
- t_node = &dynamic_cast<type_template&> (*n);
- else
- {
- if (trace)
- ts << "start stub " << gcc_tree_code_name(tc) << " template for ("
- << decl << ") at " << file << ":" << line << endl;
-
- if (tc == RECORD_TYPE)
- t_node = &emit_class_template (decl, true);
- else
- t_node = &emit_union_template (decl, true);
-
- if (trace)
- ts << "end stub " << gcc_tree_code_name(tc) << " template ("
- << t_node << ") at " << file << ":" << line << endl;
- }
-
- if (trace)
- ts << "start " << gcc_tree_code_name(tc) << " instantiation ("
- << t << ") for template (" << t_node << ")"
- << " at " << file << ":" << line << endl;
-
- type_instantiation* i_node (0);
-
- if (tc == RECORD_TYPE)
- i_node = &emit_class<class_instantiation> (t, file, line, clmn);
- else
- i_node = &emit_union<union_instantiation> (t, file, line, clmn);
-
- if (trace)
- ts << "end " << gcc_tree_code_name(tc) << " instantiation ("
- << static_cast<type*> (i_node) << ")"
- << " at " << file << ":" << line << endl;
-
- unit_->new_edge<instantiates> (*i_node, *t_node);
- process_named_pragmas (t, *i_node);
- r = i_node;
- }
-
- break;
- }
- case ENUMERAL_TYPE:
- {
- // The same logic as in the "ordinary class" case above
- // applies here.
- //
-
- t = TYPE_MAIN_VARIANT (t);
- tree d (TYPE_NAME (t));
-
- if (trace)
- ts << "start anon/stub " << gcc_tree_code_name(tc) << " at "
- << file << ":" << line << endl;
-
- if (d == NULL_TREE || IDENTIFIER_ANON_P (DECL_NAME (d)))
- {
- r = &emit_enum (t, access, file, line, clmn);
- }
- else
- {
- // Use the "defining" declaration's file, line, and column
- // information to create the stub.
- //
- path f (DECL_SOURCE_FILE (d));
- size_t l (DECL_SOURCE_LINE (d));
- size_t c (DECL_SOURCE_COLUMN (d));
-
- r = &emit_enum (t, access, f, l, c, true);
- }
-
- if (trace)
- ts << "end anon/stub " << gcc_tree_code_name(tc) << " (" << r << ")"
- << " at " << file << ":" << line << endl;
-
- break;
- }
-
- //
- // Derived types.
- //
-
- case ARRAY_TYPE:
- {
- unsigned long long size (0);
-
- if (tree index = TYPE_DOMAIN (t))
- {
- tree max (TYPE_MAX_VALUE (index));
-
- if (TREE_CODE (max) == INTEGER_CST)
- {
- size = integer_value (max);
-
- // The docs say that TYPE_DOMAIN will be NULL if the
- // array doesn't specify bounds. In reality, it is
- // set to ~0.
- //
- if (size == ~(unsigned long long) (0))
- size = 0;
-
- size++; // Convert max index to size.
- }
- else
- {
- error (file, line, clmn)
- << "non-integer array index " <<
- gcc_tree_code_name(TREE_CODE (max)) << endl;
-
- throw failed ();
- }
- }
-
- // In GCC tree a const array has both the array type itself and the
- // element type marked as const. This doesn't bode well with our
- // semantic graph model where we have a separate type node for
- // qualifiers. To fix this, we are going to strip the const
- // qualification from the element type and only preserve it in
- // the array type. In other words, we view it as "constant array"
- // rather than "array of constant elements".
- //
- using semantics::array; // vs std::array.
-
- tree bt (TREE_TYPE (t));
- tree bt_mv (TYPE_MAIN_VARIANT (bt));
- type& bt_node (emit_type (bt_mv, access::public_, file, line, clmn));
- t = TYPE_MAIN_VARIANT (t);
- array& a (unit_->new_node<array> (file, line, clmn, t, size));
- unit_->insert (t, a);
- contains& edge (unit_->new_edge<contains> (a, bt_node));
-
- // See if there is a name hint for the base type.
- //
- if (names* hint = unit_->find_hint (
- cp_type_quals (bt) == TYPE_UNQUALIFIED ? bt : bt_mv))
- edge.hint (*hint);
-
- process_named_pragmas (t, a);
- r = &a;
- break;
- }
- case REFERENCE_TYPE:
- {
- tree bt (TREE_TYPE (t));
- type& bt_node (emit_type (bt, access::public_, file, line, clmn));
- t = TYPE_MAIN_VARIANT (t);
- reference& ref (unit_->new_node<reference> (file, line, clmn, t));
- unit_->insert (t, ref);
- references& edge (unit_->new_edge<references> (ref, bt_node));
-
- // See if there is a name hint for the base type.
- //
- if (names* hint = unit_->find_hint (bt))
- edge.hint (*hint);
-
- process_named_pragmas (t, ref);
- r = &ref;
- break;
- }
- case POINTER_TYPE:
- {
- if (!TYPE_PTRMEM_P (t))
- {
- tree bt (TREE_TYPE (t));
- type& bt_node (emit_type (bt, access::public_, file, line, clmn));
- t = TYPE_MAIN_VARIANT (t);
- pointer& p (unit_->new_node<pointer> (file, line, clmn, t));
- unit_->insert (t, p);
- points& edge (unit_->new_edge<points> (p, bt_node));
-
- // See if there is a name hint for the base type.
- //
- if (names* hint = unit_->find_hint (bt))
- edge.hint (*hint);
-
- process_named_pragmas (t, p);
- r = &p;
- }
- else
- {
- t = TYPE_MAIN_VARIANT (t);
- r = &unit_->new_node<unsupported_type> (
- file, line, clmn, t, "pointer_to_data_member_type");
- unit_->insert (t, *r);
-
- if (trace)
- ts << "unsupported pointer_to_data_member_type (" << r << ")"
- << " at " << file << ":" << line << endl;
- }
- break;
- }
- default:
- {
- t = TYPE_MAIN_VARIANT (t);
- r = &unit_->new_node<unsupported_type> (
- file, line, clmn, t, gcc_tree_code_name(tc));
- unit_->insert (t, *r);
-
- if (trace)
- ts << "unsupported " << gcc_tree_code_name(tc) << " (" << r << ")"
- << " at " << file << ":" << line << endl;
-
- break;
- }
- }
-
- return *r;
-}
-
-string parser::impl::
-emit_type_name (tree type, bool direct)
-{
- // First see if there is a "direct" name for this type.
- //
- if (direct)
- {
- if (tree decl = TYPE_NAME (type))
- {
- tree t (TREE_TYPE (decl));
-
- if (t != 0 && same_type_p (type, t))
- return IDENTIFIER_POINTER (DECL_NAME (decl));
- }
- }
-
- string r;
-
- if (CP_TYPE_CONST_P (type))
- r += " const";
-
- if (CP_TYPE_VOLATILE_P (type))
- r += " volatile";
-
- if (CP_TYPE_RESTRICT_P (type))
- r += " __restrict";
-
- gcc_tree_code_type tc (TREE_CODE (type));
-
- switch (tc)
- {
- //
- // User-defined types.
- //
-
- case RECORD_TYPE:
- case UNION_TYPE:
- {
- tree ti (TYPE_TEMPLATE_INFO (type));
-
- if (ti == NULL_TREE)
- {
- // Ordinary class.
- //
- type = TYPE_MAIN_VARIANT (type);
-
- // Pointers to member functions are represented as record
- // types and don't have names, not even the synthesized ones.
- //
- if (TYPE_PTRMEMFUNC_P (type))
- r = "<pointer-to-member-function>" + r;
- else
- {
- tree name (TYPE_NAME (type));
- r = IDENTIFIER_POINTER (DECL_NAME (name)) + r;
- }
- }
- else
- {
- // Template instantiation.
- //
- tree decl (TI_TEMPLATE (ti)); // DECL_TEMPLATE
- string id (IDENTIFIER_POINTER (DECL_NAME (decl)));
-
- id += '<';
-
- tree args (INNERMOST_TEMPLATE_ARGS (TI_ARGS (ti)));
-
- for (size_t i (0), n (TREE_VEC_LENGTH (args)); i < n ; ++i)
- {
- tree a (TREE_VEC_ELT (args, i));
-
- if (i != 0)
- id += ", ";
-
- // Assume type-only arguments.
- //
- id += emit_type_name (a);
- }
-
- id += '>';
-
- r = id + r;
- }
-
- break;
- }
-
- case ENUMERAL_TYPE:
- {
- type = TYPE_MAIN_VARIANT (type);
- tree decl (TYPE_NAME (type));
- r = IDENTIFIER_POINTER (DECL_NAME (decl)) + r;
- break;
- }
-
- //
- // Derived types.
- //
-
- case ARRAY_TYPE:
- {
- unsigned long long size (0);
-
- if (tree index = TYPE_DOMAIN (type))
- {
- tree max (TYPE_MAX_VALUE (index));
-
- if (TREE_CODE (max) == INTEGER_CST)
- {
- size = integer_value (max);
-
- // Same as above.
- //
- if (size == ~(unsigned long long) (0))
- size = 0;
-
- size++;
- }
- else
- {
- // Non-integer array index which we do not support. The
- // error has been/will be issued in emit_type.
- //
- }
- }
-
- tree t (TREE_TYPE (type));
-
- if (size != 0)
- {
- ostringstream ostr;
- ostr << size;
- r = emit_type_name (t) + "[" + ostr.str () + "]" + r;
- }
- else
- r = emit_type_name (t) + "[]" + r;
-
- break;
- }
- case REFERENCE_TYPE:
- {
- tree t (TREE_TYPE (type));
- r = emit_type_name (t) + "&" + r;
- break;
- }
- case POINTER_TYPE:
- {
- if (!TYPE_PTRMEM_P (type))
- {
- tree t (TREE_TYPE (type));
- r = emit_type_name (t) + "*" + r;
- }
- else
- r = "<pointer_to_member_type>";
-
- break;
- }
-
- //
- // Fundamental types.
- //
-
- case VOID_TYPE:
- case REAL_TYPE:
- case BOOLEAN_TYPE:
- case INTEGER_TYPE:
- {
- type = TYPE_MAIN_VARIANT (type);
- tree decl (TYPE_NAME (type));
- r = IDENTIFIER_POINTER (DECL_NAME (decl)) + r;
- break;
- }
- default:
- {
- r = "<" + string (gcc_tree_code_name(tc)) + ">";
- break;
- }
- }
-
- return r;
-}
-
-void parser::impl::
-process_pragmas (declaration const& decl,
- node& node,
- string const& name,
- decl_set::const_iterator begin,
- decl_set::const_iterator cur,
- decl_set::const_iterator /*end*/)
-{
- // First process the position pragmas by iterating backwards until
- // we get to the preceding non-pragma declaration that has been
- // associated.
- //
- pragma_set prags;
-
- if (cur != begin)
- {
- decl_set::const_iterator i (cur);
- for (--i; i != begin && (i->prag != 0 || !i->assoc); --i) ;
-
- for (; i != cur; ++i)
- {
- if (i->prag == 0) // Skip declarations.
- continue;
-
- assert (!i->assoc);
-
- if (i->prag->check (decl, name, i->prag->pragma_name, i->prag->loc))
- prags.insert (*i->prag);
- else
- error_++; // Diagnostic has already been issued.
-
- i->assoc = true; // Mark this pragma as associated.
- }
-
- cur->assoc = true; // Mark the declaration as associated.
- }
-
- // Now see if there are any named pragmas for this declaration. By
- // doing this after handling the position pragmas we ensure correct
- // overriding.
- //
- {
- decl_pragmas::const_iterator i (decl_pragmas_.find (decl));
-
- if (i != decl_pragmas_.end ())
- prags.insert (i->second.begin (), i->second.end ());
- }
-
- // Finally, copy the resulting pragma set to context.
- //
- for (pragma_set::iterator i (prags.begin ()); i != prags.end (); ++i)
- add_pragma (node, i->second);
-}
-
-void parser::impl::
-process_named_pragmas (declaration const& decl, node& node)
-{
- pragma_set prags;
-
- decl_pragmas::const_iterator i (decl_pragmas_.find (decl));
-
- if (i != decl_pragmas_.end ())
- prags.insert (i->second.begin (), i->second.end ());
-
- // Copy the resulting pragma set to context.
- //
- for (pragma_set::iterator i (prags.begin ()); i != prags.end (); ++i)
- add_pragma (node, i->second);
-}
-
-void parser::impl::
-add_pragma (node& n, pragma const& p)
-{
- if (trace)
- ts << "\t\t pragma " << p.pragma_name << endl;
-
- string const& k (p.context_name);
-
- if (p.add == 0)
- {
- n.set (k, p.value);
- n.set (k + "-location", p.loc);
- }
- else
- p.add (n, k, p.value, p.loc);
-}
-
-void parser::impl::
-diagnose_unassoc_pragmas (decl_set const& decls)
-{
- for (decl_set::const_iterator i (decls.begin ()), e (decls.end ());
- i != e; ++i)
- {
- if (i->prag && !i->assoc)
- {
- pragma const& p (*i->prag);
- error (p.loc)
- << "db pragma '" << p.pragma_name << "' is not associated with a "
- << "declaration" << endl;
- error_++;
- }
- }
-}
-
-string parser::impl::
-fq_scope (tree decl)
-{
- string s, tmp;
-
- for (tree scope (CP_DECL_CONTEXT (decl));
- scope != global_namespace;)
- {
- tree prev (CP_DECL_CONTEXT (scope));
-
- // If this is an inline namespace, pretend it doesn't exist.
- //
-#if BUILDING_GCC_MAJOR >= 8
- if (!is_nested_namespace (prev, scope, true))
-#else
- if (!is_associated_namespace (prev, scope))
-#endif
- {
- tree n = DECL_NAME (scope);
-
- tmp = "::";
- tmp += (n != NULL_TREE ? IDENTIFIER_POINTER (n) : "");
- tmp += s;
- s.swap (tmp);
- }
-
- scope = prev;
- }
-
- return s;
-}
-
-//
-// parser
-//
-
-parser::
-~parser ()
-{
- // Needs parser::impl definition.
-}
-
-parser::
-parser (options const& ops,
- loc_pragmas& lp,
- ns_loc_pragmas& nslp,
- decl_pragmas& dp)
- : impl_ (new impl (ops, lp, nslp, dp))
-{
-}
-
-unique_ptr<unit> parser::
-parse (tree global_scope, path const& main_file)
-{
- return impl_->parse (global_scope, main_file);
-}
diff --git a/odb/plugin.cxx b/odb/plugin.cxx
deleted file mode 100644
index aff90ee..0000000
--- a/odb/plugin.cxx
+++ /dev/null
@@ -1,458 +0,0 @@
-// file : odb/plugin.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <odb/gcc.hxx> // Keep it first.
-
-#include <unistd.h> // stat()
-#include <sys/types.h> // stat
-#include <sys/stat.h> // stat
-
-#include <memory> // std::unique_ptr
-#include <string>
-#include <vector>
-#include <cstring> // std::strcpy, std::strstr
-#include <cassert>
-#include <iostream>
-
-#include <cutl/re.hxx>
-#include <cutl/fs/path.hxx>
-
-#include <odb/pragma.hxx>
-#include <odb/parser.hxx>
-#include <odb/options.hxx>
-#include <odb/option-functions.hxx>
-#include <odb/features.hxx>
-#include <odb/profile.hxx>
-#include <odb/version.hxx>
-#include <odb/validator.hxx>
-#include <odb/processor.hxx>
-#include <odb/generator.hxx>
-#include <odb/semantics/unit.hxx>
-
-using namespace std;
-using namespace semantics;
-
-using cutl::fs::path;
-using cutl::fs::invalid_path;
-
-typedef vector<path> paths;
-
-#if defined(_WIN32) && !defined(ODB_STATIC_PLUGIN)
-__declspec(dllexport)
-#endif
-int plugin_is_GPL_compatible;
-
-unique_ptr<options const> options_;
-paths profile_paths_;
-path file_; // File being compiled.
-paths inputs_; // List of input files in at-once mode or just file_.
-
-bool (*cpp_diagnostic_prev) (
- cpp_reader*,
-#if BUILDING_GCC_MAJOR >= 9
- cpp_diagnostic_level,
- cpp_warning_reason,
-#else
- int,
- int,
-#endif
-#if BUILDING_GCC_MAJOR >= 6
- rich_location*,
-#else
- location_t,
- unsigned int,
-#endif
- const char*,
- va_list*);
-
-static bool
-cpp_diagnostic_filter (cpp_reader* r,
-#if BUILDING_GCC_MAJOR >= 9
- cpp_diagnostic_level level,
- cpp_warning_reason reason,
-#else
- int level,
- int reason,
-#endif
-#if BUILDING_GCC_MAJOR >= 6
- rich_location* l,
-#else
- location_t l,
- unsigned int column_override,
-#endif
- const char* msg,
- va_list* ap)
-{
- // #pragma once in the main file. Note that the message that we get is
- // potentially translated so we search for the substring (there is
- // currently only one warning in libcpp that mentions #pragma once).
- // Unfortunately, some translations translate the 'once' word (e.g,
- // #pragma uno; I wonder if one can actually specify it like that in
- // the source code). Oh, well, there is only so much we can do.
- //
- if (strstr (msg, "#pragma once") != 0)
- return true;
-
- return cpp_diagnostic_prev (
- r,
- level,
- reason,
-#if BUILDING_GCC_MAJOR >= 6
- l,
-#else
- l,
- column_override,
-#endif
- msg,
- ap);
-}
-
-// A prefix of the _cpp_file struct. This struct is not part of the
-// public interface so we have to resort to this technique (based on
-// libcpp/files.c).
-//
-struct cpp_file_prefix
-{
- char const* name;
- char const* path;
- char const* pchname;
- char const* dir_name;
- _cpp_file* next_file;
- const uchar* buffer;
- const uchar* buffer_start;
- const cpp_hashnode *cmacro;
- cpp_dir *dir;
- struct stat st;
-};
-
-extern "C" void
-start_unit_callback (void*, void*)
-{
- // Set the preprocessor error callback to filter out useless diagnostics.
- //
- cpp_callbacks* cb (cpp_get_callbacks (parse_in));
-
-#if BUILDING_GCC_MAJOR >= 9
- cpp_diagnostic_prev = cb->diagnostic;
- cb->diagnostic = &cpp_diagnostic_filter;
-#else
- cpp_diagnostic_prev = cb->error;
- cb->error = &cpp_diagnostic_filter;
-#endif
-
- if (cpp_diagnostic_prev == 0)
- {
- cerr << "ice: expected cpp diagnostic callback to be set" << endl;
- exit (1);
- }
-
- // Set the directory of the main file (stdin) to that of the orginal
- // file so that relative inclusion works. Also adjust the path and
- // re-stat the file so that #pragma once works.
- //
- cpp_buffer* b (cpp_get_buffer (parse_in));
- _cpp_file* f (cpp_get_file (b));
- cpp_dir* d (cpp_get_dir (f));
- char const* p (cpp_get_path (f));
-
- cpp_file_prefix* fp (reinterpret_cast<cpp_file_prefix*> (f));
-
- // Perform some sanity checks.
- //
- if (p != 0 && *p == '\0' // The path should be empty (stdin).
- && cpp_get_prev (b) == 0 // This is the only buffer (main file).
- && fp->path == p // Our prefix corresponds to the actual type.
- && fp->dir == d // Our prefix corresponds to the actual type.
- && fp->dir_name == 0) // The directory part hasn't been initialized.
- {
- // The dir_name is initialized by libcpp lazily so we can preemptively
- // set it to what we need.
- //
- path d (file_.directory ());
- char* s;
-
- if (d.empty ())
- {
- s = XNEWVEC (char, 1);
- *s = '\0';
- }
- else
- {
- size_t n (d.string ().size ());
- s = XNEWVEC (char, n + 2);
- strcpy (s, d.string ().c_str ());
- s[n] = path::traits::directory_separator;
- s[n + 1] = '\0';
- }
-
- fp->dir_name = s;
-
- // Unless we are in the at-once mode (where input files are actually
- // #include'ed into the synthesized stdin), pretend that we are the
- // actual input file. This is necessary for the #pragma once to work.
- //
- // All this relies on the way things are implemented in libcpp. In
- // particular, the #pragma once code first checks if the mtime and
- // size of files match (that's why we need stat() below). If they
- // do, then it goes ahead and compares their contents. To re-load
- // the contents of the file libcpp uses the path (that's why we
- // need to adjust that as well).
- //
- if (inputs_.size () == 1)
- {
- string const& f (file_.string ());
-
- XDELETEVEC (fp->path);
- size_t n (f.size ());
- char* p (XNEWVEC (char, n + 1));
- strcpy (p, f.c_str ());
- p[n] = '\0';
- fp->path = p;
-
- // This call shouldn't fail since we've just opened it in the driver.
- stat (fp->path, &fp->st);
- }
- }
- else
- {
- cerr << "ice: unable to initialize main file directory" << endl;
- exit (1);
- }
-}
-
-extern "C" void
-gate_callback (void*, void*)
-{
- // If there were errors during compilation, let GCC handle the
- // exit.
- //
- if (errorcount || sorrycount)
- return;
-
- int r (0);
-
- try
- {
- // Post process pragmas.
- //
- post_process_pragmas ();
-
- // Parse the GCC tree to semantic graph.
- //
- parser p (*options_, loc_pragmas_, ns_loc_pragmas_, decl_pragmas_);
- unique_ptr<unit> u (p.parse (global_namespace, file_));
-
- features f;
-
- // Process, pass 1.
- //
- process (*options_, f, *u, file_, 1);
-
- // Validate, pass 1.
- //
- validate (*options_, f, *u, file_, 1);
-
- // Process, pass 2.
- //
- process (*options_, f, *u, file_, 2);
-
- // Validate, pass 2.
- //
- validate (*options_, f, *u, file_, 2);
-
- // Generate.
- //
- generate (*options_, f, *u, file_, inputs_);
- }
- catch (cutl::re::format const& e)
- {
- cerr << "error: invalid regex: '" << e.regex () << "': " <<
- e.description () << endl;
- r = 1;
- }
- catch (pragmas_failed const&)
- {
- // Diagnostics has aready been issued.
- //
- r = 1;
- }
- catch (parser::failed const&)
- {
- // Diagnostics has aready been issued.
- //
- r = 1;
- }
- catch (validator_failed const&)
- {
- // Diagnostics has aready been issued.
- //
- r = 1;
- }
- catch (processor_failed const&)
- {
- // Diagnostics has aready been issued.
- //
- r = 1;
- }
- catch (generator_failed const&)
- {
- // Diagnostics has aready been issued.
- //
- r = 1;
- }
-
- exit (r);
-}
-
-static char const* const odb_version = ODB_COMPILER_VERSION_STR;
-
-typedef vector<string> strings;
-
-extern "C"
-#if defined(_WIN32) && !defined(ODB_STATIC_PLUGIN)
-__declspec(dllexport)
-#endif
-int
-plugin_init (plugin_name_args* plugin_info, plugin_gcc_version*)
-{
- int r (0);
- plugin_info->version = odb_version;
-
- try
- {
- // Parse options.
- //
- {
- strings argv_str;
- argv_str.push_back (plugin_info->base_name);
-
- for (int i (0); i < plugin_info->argc; ++i)
- {
- plugin_argument& a (plugin_info->argv[i]);
-
- // A value cannot contain '=' so it is passed as the backspace
- // character.
- //
- string v (a.value != 0 ? a.value : "");
- for (size_t i (0); i < v.size (); ++i)
- if (v[i] == '\b')
- v[i] = '=';
-
- // Handle service options.
- //
- if (strcmp (a.key, "svc-path") == 0)
- {
- profile_paths_.push_back (path (v));
- continue;
- }
-
- if (strcmp (a.key, "svc-file") == 0)
- {
- // First is the main file. Subsequent are inputs in the at-once
- // mode.
- //
- if (file_.empty ())
- file_ = path (v);
- else
- inputs_.push_back (path (v));
-
- continue;
- }
-
- string opt (strlen (a.key) > 1 ? "--" : "-");
- opt += a.key;
-
- argv_str.push_back (opt);
-
- if (a.value != 0)
- argv_str.push_back (v);
- }
-
- vector<char*> argv;
- for (strings::iterator i (argv_str.begin ()); i != argv_str.end (); ++i)
- argv.push_back (const_cast<char*> (i->c_str ()));
-
- int argc (static_cast<int> (argv.size ()));
-
- if (inputs_.empty ())
- inputs_.push_back (file_);
-
- // Two-phase options parsing, similar to the driver.
- //
- cli::argv_file_scanner::option_info oi[3];
- oi[0].option = "--options-file";
- oi[0].search_func = 0;
- oi[1].option = "-p";
- oi[2].option = "--profile";
-
- database db;
- {
- oi[1].search_func = &profile_search_ignore;
- oi[2].search_func = &profile_search_ignore;
-
- cli::argv_file_scanner scan (argc, &argv[0], oi, 3);
- options ops (scan);
- assert (ops.database_specified ());
- db = ops.database ()[0];
- }
-
- profile_data pd (profile_paths_, db, "odb plugin");
- oi[1].search_func = &profile_search;
- oi[2].search_func = &profile_search;
- oi[1].arg = &pd;
- oi[2].arg = &pd;
-
- cli::argv_file_scanner scan (argc, &argv[0], oi, 3);
- unique_ptr<options> ops (
- new options (scan, cli::unknown_mode::fail, cli::unknown_mode::fail));
-
- // Process options.
- //
- process_options (*ops);
-
- options_ = move (ops);
- pragma_db_ = db;
- pragma_multi_ = options_->multi_database ();
- }
-
- if (options_->trace ())
- cerr << "starting plugin " << plugin_info->base_name << endl;
-
- // Disable assembly output. GCC doesn't define HOST_BIT_BUCKET
- // correctly for MinGW (it still used /dev/null which fails to
- // open).
- //
-#ifdef _WIN32
- asm_file_name = "nul";
-#else
- asm_file_name = HOST_BIT_BUCKET;
-#endif
-
- // Register callbacks.
- //
- register_callback (plugin_info->base_name,
- PLUGIN_PRAGMAS,
- register_odb_pragmas,
- 0);
-
- register_callback (plugin_info->base_name,
- PLUGIN_START_UNIT,
- start_unit_callback,
- 0);
-
- register_callback (plugin_info->base_name,
- PLUGIN_OVERRIDE_GATE,
- &gate_callback,
- 0);
- }
- catch (cli::exception const& ex)
- {
- cerr << ex << endl;
- r = 1;
- }
-
- if (r != 0)
- exit (r);
-
- return r;
-}
diff --git a/odb/pragma.cxx b/odb/pragma.cxx
deleted file mode 100644
index bd1d848..0000000
--- a/odb/pragma.cxx
+++ /dev/null
@@ -1,4444 +0,0 @@
-// file : odb/pragma.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <odb/gcc.hxx>
-
-#include <cctype> // std::isalnum
-#include <limits>
-#include <vector>
-#include <sstream>
-
-#include <odb/diagnostics.hxx>
-#include <odb/lookup.hxx>
-#include <odb/pragma.hxx>
-#include <odb/cxx-token.hxx>
-#include <odb/cxx-lexer.hxx>
-
-#include <odb/context.hxx>
-#include <odb/relational/context.hxx>
-
-using namespace std;
-using namespace cutl;
-
-using container::any;
-
-virt_declarations virt_declarations_;
-loc_pragmas loc_pragmas_;
-decl_pragmas decl_pragmas_;
-ns_loc_pragmas ns_loc_pragmas_;
-
-database pragma_db_;
-multi_database pragma_multi_;
-
-template <typename X>
-void
-accumulate (compiler::context& ctx, string const& k, any const& v, location_t)
-{
- // Empty values are used to indicate that this pragma must be ignored.
- //
- if (v.empty ())
- return;
-
- typedef vector<X> container;
-
- container& c (ctx.count (k)
- ? ctx.get<container> (k)
- : ctx.set (k, container ()));
-
- c.push_back (v.value<X> ());
-}
-
-// Parse a qualified string. It can be in one of the following formats:
-//
-// "foo.bar.baz" (can have empty leading component, e.g., ".bar.baz")
-// "foo"."bar"."baz" (can have empty leading component, e.g., ""."bar"."baz")
-// ."foo.bar" (used to specify unqualifed names with periods)
-//
-// Empty leading components means fully qualified (similar to ::foo in C++).
-//
-static bool
-parse_qname (cxx_lexer& l,
- cpp_ttype& tt,
- string& tl,
- tree& tn,
- string const& p, // Pragma name for diagnostic.
- qname& name,
- bool* expr = 0, // If specified, detect an expression
- string* expr_str = 0) // and store it here.
-{
- assert (tt == CPP_STRING || tt == CPP_DOT);
-
- // Handle the special case of unqualified name which can contain periods.
- //
- if (tt == CPP_DOT)
- {
- tt = l.next (tl, &tn);
-
- if (tt != CPP_STRING)
- {
- error (l) << "name expected after '.' in db pragma " << p << endl;
- return false;
- }
-
- name = tl;
- tt = l.next (tl, &tn);
- return true;
- }
-
- name.clear ();
- string str (tl);
-
- // See what comes after the string.
- //
- tt = l.next (tl, &tn);
-
- if (tt == CPP_DOT)
- {
- name.append (str);
-
- for (; tt == CPP_DOT; tt = l.next (tl, &tn))
- {
- tt = l.next (tl, &tn);
-
- if (tt != CPP_STRING)
- {
- error (l) << "name expected after '.' in db pragma " << p << endl;
- return false;
- }
-
- name.append (tl);
- }
-
- return true;
- }
-
- if (expr != 0 && tt == CPP_PLUS)
- {
- *expr = true;
- *expr_str = str;
- return true;
- }
-
- // Scan the string looking for '.' as well as for non-identifier
- // characters if we need to detect expressions.
- //
- string::size_type prev (string::npos);
-
- for (size_t i (0); i < str.size (); ++i)
- {
- char c (str[i]);
-
- if (c == '.')
- {
- if (prev == string::npos)
- name.append (string (str, 0, i));
- else
- name.append (string (str, prev + 1, i - prev - 1));
-
- prev = i;
- }
- else if (expr != 0 && !(isalnum (c) || c == '_'))
- {
- *expr = true;
- *expr_str = str;
- return true;
- }
- }
-
- if (prev == string::npos)
- name.append (str);
- else
- name.append (string (str, prev + 1, string::npos));
-
- return true;
-}
-
-static bool
-parse_expression (cxx_lexer& l,
- cpp_ttype& tt,
- string& tl,
- tree& tn,
- cxx_tokens& ts,
- string const& prag)
-{
- // Keep reading tokens until we see a mis-matching ')' or ',' while
- // keeping track of the '()' and '{}' balance.
- //
- size_t p_balance (0), b_balance (0);
-
- for (; tt != CPP_EOF; tt = l.next (tl, &tn))
- {
- bool done (false);
- cxx_token ct (l.location (), tt);
-
- switch (tt)
- {
- case CPP_OPEN_BRACE:
- {
- b_balance++;
- break;
- }
- case CPP_CLOSE_BRACE:
- {
- b_balance--;
- break;
- }
- case CPP_OPEN_PAREN:
- {
- p_balance++;
- break;
- }
- case CPP_CLOSE_PAREN:
- {
- if (p_balance == 0 && b_balance == 0)
- done = true;
- else
- p_balance--;
- break;
- }
- case CPP_COMMA:
- {
- if (p_balance == 0 && b_balance == 0)
- done = true;
- else
- break;
- }
- case CPP_STRING:
- {
- ct.literal = tl;
- break;
- }
- case CPP_NAME:
- //case CPP_KEYWORD: see default:
- {
- ct.literal = tl;
- break;
- }
- case CPP_NUMBER:
- {
- switch (TREE_CODE (tn))
- {
- case INTEGER_CST:
- {
- tree type (TREE_TYPE (tn));
- unsigned long long v (integer_value (tn));
-
- ostringstream os;
- os << v;
-
- if (type == long_long_integer_type_node)
- os << "LL";
- else if (type == long_long_unsigned_type_node)
- os << "ULL";
- else if (type == long_integer_type_node)
- os << "L";
- else if (type == long_unsigned_type_node)
- os << "UL";
- else if (
- TYPE_UNSIGNED (type) &&
- TYPE_PRECISION (type) >= TYPE_PRECISION (integer_type_node))
- os << "U";
-
- ct.literal = os.str ();
- break;
- }
- case REAL_CST:
- {
- tree type (TREE_TYPE (tn));
- REAL_VALUE_TYPE val (TREE_REAL_CST (tn));
-
- // This is the best we can do. val cannot be INF or NaN.
- //
- char tmp[256];
- real_to_decimal (tmp, &val, sizeof (tmp), 0, true);
- istringstream is (tmp);
- ostringstream os;
-
- if (type == float_type_node)
- {
- float f;
- is >> f;
- os << f << 'F';
- }
- else
- {
- double d;
- is >> d;
- os << d;
- }
-
- ct.literal = os.str ();
- break;
- }
- default:
- {
- error (l) << "unexpected numeric constant in db pragma "
- << prag << endl;
- return false;
- }
- }
-
- break;
- }
- default:
- {
- // CPP_KEYWORD is not in the cpp_ttype enumeration.
- //
- if (tt == CPP_KEYWORD)
- ct.literal = tl;
-
- break;
- }
- }
-
- if (done)
- break;
-
- // We don't store the tree node in ct since we converted numbers to
- // string literals.
- //
- ts.push_back (ct);
- }
-
- return true;
-}
-
-static string
-parse_scoped_name (cxx_lexer& l,
- cpp_ttype& tt,
- string& tl,
- tree& tn,
- string const& prag)
-{
- try
- {
- return lookup::parse_scoped_name (l, tt, tl, tn);
- }
- catch (lookup::invalid_name const&)
- {
- error (l) << "invalid name in db pragma " << prag << endl;
- return "";
- }
-}
-
-static tree
-resolve_scoped_name (cxx_lexer& l,
- cpp_ttype& tt,
- string& tl,
- tree& tn,
- tree start_scope,
- string& name,
- bool is_type,
- string const& prag,
- bool trailing_scope = false,
- cpp_ttype* prev_tt = 0)
-{
- try
- {
- cpp_ttype ptt; // Not used.
- tree r (
- lookup::resolve_scoped_name (
- l, tt, tl, tn, ptt, start_scope, name, is_type, trailing_scope));
-
- if (prev_tt != 0)
- *prev_tt = ptt;
-
- return r;
- }
- catch (lookup::invalid_name const&)
- {
- error (l) << "invalid name in db pragma " << prag << endl;
- return 0;
- }
- catch (lookup::unable_to_resolve const& e)
- {
- if (e.last ())
- error (l) << "unable to resolve " << (is_type ? "type " : "") << "name "
- << "'" << e.name () << "' in db pragma " << prag << endl;
- else
- error (l) << "unable to resolve name '" << e.name () << "' in db pragma "
- << prag << endl;
-
- return 0;
- }
-}
-
-// Resolve a data member in the specified scope taking into account virtual
-// member declarations.
-//
-declaration
-resolve_data_member (tree scope,
- const cxx_tokens& name,
- string& decl_name, // Note: appended to.
- string const& prag)
-{
- declaration decl;
-
- if (name.size () == 1 && name.back ().type == CPP_NAME)
- {
- virt_declarations::iterator i (virt_declarations_.find (scope));
-
- if (i != virt_declarations_.end ())
- {
- string const& n (name.back ().literal);
-
- virt_declaration_set::const_iterator j (i->second.find (n, FIELD_DECL));
-
- if (j != i->second.end ())
- {
- decl = declaration (*j);
- decl_name += n;
- }
- }
- }
-
- if (!decl)
- {
- cxx_tokens_lexer l;
- l.start (name);
-
- tree tn;
- string tl;
- cpp_ttype tt (l.next (tl));
-
- tree d (resolve_scoped_name (
- l, tt, tl, tn, scope, decl_name, false, prag));
-
- if (d == 0)
- return decl; // Diagnostics has already been issued.
-
- decl = declaration (d);
- }
-
- return decl;
-}
-
-static bool
-check_spec_decl_type (declaration const& d,
- string const& name,
- string const& p,
- location_t l)
-{
- gcc_tree_code_type tc (d.tree_code ());
- bool type (TREE_CODE_CLASS (tc) == tcc_type);
-
- if (p == "no_id")
- {
- // No_id can be used on objects only.
- //
- if (tc != RECORD_TYPE)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a class" << endl;
- return false;
- }
- }
- else if (p == "id" ||
- p == "auto" ||
- p == "column" ||
- p == "inverse" ||
- p == "on_delete" ||
- p == "points_to" ||
- p == "section" ||
- p == "load" ||
- p == "update" ||
- p == "version" ||
- p == "index" ||
- p == "unique" ||
- p == "get" ||
- p == "set" ||
- p == "access")
- {
- if (tc != FIELD_DECL)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a data member" << endl;
- return false;
- }
- }
- else if (p == "transient")
- {
- // Transient can be used for both data members and classes (object,
- // view, or composite value).
- //
- if (tc != FIELD_DECL && tc != RECORD_TYPE)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a data member or class" << endl;
- return false;
- }
- }
- else if (p == "added")
- {
- // Added can be used for data members only.
- //
- if (tc != FIELD_DECL)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a data member" << endl;
- return false;
- }
- }
- else if (p == "deleted")
- {
- // Deleted can be used for both data members and classes (object,
- // view of composite value).
- //
- if (tc != FIELD_DECL && tc != RECORD_TYPE)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a data member or class" << endl;
- return false;
- }
- }
- else if (p == "readonly")
- {
- // Readonly can be used for both data members and classes (object or
- // composite value).
- //
- if (tc != FIELD_DECL && tc != RECORD_TYPE)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a data member or class" << endl;
- return false;
- }
- }
- else if (p == "abstract" ||
- p == "callback" ||
- p == "query" ||
- p == "object" ||
- p == "optimistic" ||
- p == "polymorphic" ||
- p == "definition" ||
- p == "sectionable" ||
- p == "bulk")
- {
- if (tc != RECORD_TYPE)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a class" << endl;
- return false;
- }
- }
- else if (p == "pointer")
- {
- // Table can be used for namespaces and classes (object or view).
- //
- if (tc != NAMESPACE_DECL && tc != RECORD_TYPE)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a class" << endl;
- return false;
- }
- }
- else if (p == "table")
- {
- // Table can be used for namespaces, members (container), and types
- // (container, object, or view).
- //
- if (tc != NAMESPACE_DECL && tc != FIELD_DECL && !type)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a namespace, type, or data member" << endl;
- return false;
- }
- }
- else if (p == "session")
- {
- // Session can be used only for namespaces and objects.
- //
- if (tc != NAMESPACE_DECL && tc != RECORD_TYPE)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a namespace or class" << endl;
- return false;
- }
- }
- else if (p == "schema")
- {
- // For now schema can be used only for namespaces and objects.
- //
- if (tc != NAMESPACE_DECL && tc != RECORD_TYPE)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a namespace or class" << endl;
- return false;
- }
- }
- else if (p == "type" ||
- p == "id_type" ||
- p == "value_type" ||
- p == "index_type" ||
- p == "key_type")
- {
- // Type can be used for both members and types.
- //
- if (tc != FIELD_DECL && !type)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a type or data member" << endl;
- return false;
- }
- }
- else if (p == "default")
- {
- // Default can be used for both members and types.
- //
- if (tc != FIELD_DECL && !type)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a type or data member" << endl;
- return false;
- }
- }
- else if (p == "value_column" ||
- p == "index_column" ||
- p == "key_column" ||
- p == "id_column")
- {
- // Container columns can be used for both members (container) and
- // types (container).
- //
- if (tc != FIELD_DECL && !type)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a type or data member" << endl;
- return false;
- }
- }
- else if (p == "options" ||
- p == "value_options" ||
- p == "index_options" ||
- p == "key_options" ||
- p == "id_options")
- {
- // Options can be used for both members and types.
- //
- if (tc != FIELD_DECL && !type)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a type or data member" << endl;
- return false;
- }
- }
- else if (p == "null" ||
- p == "not_null" ||
- p == "key_null" ||
- p == "key_not_null" ||
- p == "value_null" ||
- p == "value_not_null")
- {
- // Null pragmas can be used for both members and types (values,
- // containers, and pointers).
- //
- if (tc != FIELD_DECL && !type)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a type or data member" << endl;
- return false;
- }
- }
- else if (p == "unordered")
- {
- // Unordered can be used for both members (container) and
- // types (container).
- //
- if (tc != FIELD_DECL && !type)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a type or data member" << endl;
- return false;
- }
- }
- else if (p == "virtual")
- {
- // Virtual is specified for a member.
- //
- if (tc != FIELD_DECL)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a data member" << endl;
- return false;
- }
- }
- else if (p == "simple" ||
- p == "container")
- {
- // Apply to both members and types.
- //
- if (tc != FIELD_DECL && !type)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a type or data member" << endl;
- return false;
- }
- }
- else
- {
- error (l) << "unknown db pragma " << p << endl;
- return false;
- }
-
- return true;
-}
-
-static void
-add_pragma (pragma const& prag, declaration const& decl, bool ns)
-{
- if (decl)
- decl_pragmas_[decl].insert (prag);
- else
- {
- tree scope (current_scope ());
-
- if (!ns)
- {
- if (!CLASS_TYPE_P (scope))
- scope = global_namespace;
-
- loc_pragmas_[scope].push_back (prag);
- }
- else
- ns_loc_pragmas_.push_back (ns_loc_pragma (scope, prag));
- }
-}
-
-static void
-handle_pragma (cxx_lexer& l,
- string db,
- string p,
- string const& qualifier,
- any& qualifier_value,
- declaration const& decl,
- string const& decl_name,
- bool ns) // True if this is a position namespace pragma.
-{
- cpp_ttype tt;
- string tl;
- tree tn;
-
- // See if there is a database prefix. The db argument may already
- // contain it.
- //
- if (db.empty () &&
- (p == "mysql" ||
- p == "sqlite" ||
- p == "pgsql" ||
- p == "oracle" ||
- p == "mssql"))
- {
- tt = l.next (tl);
-
- if (tt != CPP_COLON)
- {
- error (l) << "':' expected after database prefix " << p << endl;
- return;
- }
-
- // Specifier prefix.
- //
- db = p;
- tt = l.next (p);
-
- if (tt != CPP_NAME && tt != CPP_KEYWORD)
- {
- error (l) << "expected specifier after databas prefix " << db << endl;
- return;
- }
- }
-
- string name (p); // Pragma name.
- any val; // Pragma value.
- pragma::add_func adder (0); // Custom context adder.
- location_t loc (l.location ()); // Pragma location.
-
- if (qualifier == "model")
- {
- // version(unsigned long long base,
- // unsigned long long current,
- // open|closed)
- //
-
- // Make sure we've got the correct declaration type.
- //
- assert (decl == global_namespace);
-
- if (p == "version")
- {
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- model_version v;
-
- // base
- //
- if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
- {
- error (l) << "unsigned integer expected as base version" << endl;
- return;
- }
-
- v.base = integer_value (tn);
-
- if (v.base == 0)
- {
- error (l) << "base version cannot be zero" << endl;
- return;
- }
-
- // current
- //
- if (l.next (tl, &tn) != CPP_COMMA)
- {
- error (l) << "current version expected after base version" << endl;
- return;
- }
-
- if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
- {
- error (l) << "unsigned integer expected as current version" << endl;
- return;
- }
-
- v.current = integer_value (tn);
-
- if (v.current == 0)
- {
- error (l) << "current version cannot be zero" << endl;
- return;
- }
-
- if (v.base > v.current)
- {
- error (l) << "current version should be greater than or equal to " <<
- "base version" << endl;
- return;
- }
-
- // open|closed
- //
- tt = l.next (tl, &tn);
- if (tt == CPP_COMMA)
- {
- if (l.next (tl, &tn) != CPP_NAME || (tl != "open" && tl != "closed"))
- {
- error (l) << "open or closed expected after current version" << endl;
- return;
- }
-
- v.open = (tl == "open");
- tt = l.next (tl, &tn);
- }
- else
- v.open = true;
-
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- name = "model-version";
- val = v;
- tt = l.next (tl, &tn);
- }
- else
- {
- error (l) << "unknown db pragma " << p << endl;
- return;
- }
- }
- else if (qualifier == "map")
- {
- // type("<regex>") | type(<name>)
- // as("<subst>") | as(<name>)
- // to("<subst>") | to(<expr>)
- // from("<subst>") | from(<expr>)
- //
-
- if (p != "type" &&
- p != "as" &&
- p != "to" &&
- p != "from")
- {
- error (l) << "unknown db pragma " << p << endl;
- return;
- }
-
- // Make sure we've got the correct declaration type.
- //
- assert (decl == global_namespace);
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- if (qualifier_value.type_info () == typeid (custom_cxx_type))
- {
- // C++ type mapping.
- //
- custom_cxx_type& ct (qualifier_value.value<custom_cxx_type> ());
-
- if (p == "type" || p == "as")
- {
- // Can be built-in type (e.g., bool).
- //
- if (tt == CPP_NAME || tt == CPP_KEYWORD || tt == CPP_SCOPE)
- {
- string name;
- tree decl (
- resolve_scoped_name (
- l, tt, tl, tn, current_scope (), name, true, p));
-
- if (decl == 0)
- return; // Diagnostics has already been issued.
-
- if (TREE_CODE (decl) != TYPE_DECL)
- {
- error (loc) << "name '" << name << "' in db pragma "
- << p << " does not refer to a type" << endl;
- return;
- }
-
- (p == "type" ? ct.type_node : ct.as_node) = TREE_TYPE (decl);
- (p == "type" ? ct.type_name : ct.as_name) = name;
- }
- else
- {
- error (l) << "type name expected in db pragma " << p << endl;
- return;
- }
- }
- else if (p == "to" || p == "from")
- {
- if (tt != CPP_CLOSE_PAREN) // Empty expression is ok.
- {
- if (!parse_expression (
- l, tt, tl, tn, (p == "to" ? ct.to : ct.from), p))
- return; // Diagnostics has already been issued.
- }
- }
- }
- else
- {
- using relational::custom_db_type;
-
- // Database type mapping.
- //
- custom_db_type& ct (qualifier_value.value<custom_db_type> ());
-
- if (p == "type")
- {
- if (tt != CPP_STRING)
- {
- error (l) << "type name regex expected in db pragma " << p << endl;
- return;
- }
-
- try
- {
- // Make it case-insensitive.
- //
- ct.type.assign (tl, true);
- }
- catch (regex_format const& e)
- {
- error (l) << "invalid regex: '" << e.regex () << "' in db pragma "
- << p << ": " << e.description () << endl;
- return;
- }
- }
- else if (p == "as")
- {
- if (tt != CPP_STRING)
- {
- error (l) << "type name expected in db pragma " << p << endl;
- return;
- }
-
- ct.as = tl;
- }
- else if (p == "to")
- {
- if (tt != CPP_STRING)
- {
- error (l) << "expression expected in db pragma " << p << endl;
- return;
- }
-
- ct.to = tl;
- }
- else if (p == "from")
- {
- if (tt != CPP_STRING)
- {
- error (l) << "expression expected in db pragma " << p << endl;
- return;
- }
-
- ct.from = tl;
- }
-
- tt = l.next (tl, &tn);
- }
-
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- name.clear (); // We don't need to add anything for this pragma.
- tt = l.next (tl, &tn);
- }
- else if (qualifier == "index")
- {
- // unique
- // type("<type>")
- // method("<method>")
- // options("<options>")
- // member(<name>[, "<options>"])
- // members(<name>[, <name>...])
- //
-
- if (p != "unique" &&
- p != "type" &&
- p != "method" &&
- p != "options" &&
- p != "member" &&
- p != "members")
- {
- error (l) << "unknown db pragma " << p << endl;
- return;
- }
-
- using relational::index;
- index& in (qualifier_value.value<index> ());
-
- if (p == "unique")
- in.type = "UNIQUE";
- else
- {
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- if (p == "type")
- {
- if (tt != CPP_STRING)
- {
- error (l) << "index type expected in db pragma " << p << endl;
- return;
- }
-
- in.type = tl;
- tt = l.next (tl, &tn);
- }
- else if (p == "method")
- {
- if (tt != CPP_STRING)
- {
- error (l) << "index method expected in db pragma " << p << endl;
- return;
- }
-
- in.method = tl;
- tt = l.next (tl, &tn);
- }
- else if (p == "options")
- {
- if (tt != CPP_STRING)
- {
- error (l) << "index options expected in db pragma " << p << endl;
- return;
- }
-
- in.options = tl;
- tt = l.next (tl, &tn);
- }
- else if (p == "member")
- {
- if (tt != CPP_NAME)
- {
- error (l) << "data member name expected in db pragma " << p << endl;
- return;
- }
-
- index::member m;
- m.loc = loc;
- m.name = tl;
-
- tt = l.next (tl, &tn);
-
- // Parse nested members if any.
- //
- for (; tt == CPP_DOT; tt = l.next (tl, &tn))
- {
- if (l.next (tl, &tn) != CPP_NAME)
- {
- error (l) << "name expected after '.' in db pragma " << p << endl;
- return;
- }
-
- m.name += '.';
- m.name += tl;
- }
-
- // Parse member options, if any.
- //
- if (tt == CPP_COMMA)
- {
- if (l.next (tl, &tn) != CPP_STRING)
- {
- error (l) << "index member options expected in db pragma " << p
- << endl;
- return;
- }
-
- m.options = tl;
- tt = l.next (tl, &tn);
- }
-
- in.members.push_back (m);
- }
- else if (p == "members")
- {
- for (;;)
- {
- if (tt != CPP_NAME)
- {
- error (l) << "data member name expected in db pragma " << p
- << endl;
- return;
- }
-
- index::member m;
- m.loc = l.location ();
- m.name = tl;
-
- tt = l.next (tl, &tn);
-
- // Parse nested members if any.
- //
- for (; tt == CPP_DOT; tt = l.next (tl, &tn))
- {
- if (l.next (tl, &tn) != CPP_NAME)
- {
- error (l) << "name expected after '.' in db pragma " << p
- << endl;
- return;
- }
-
- m.name += '.';
- m.name += tl;
- }
-
- in.members.push_back (m);
-
- if (tt == CPP_COMMA)
- tt = l.next (tl, &tn);
- else
- break;
- }
- }
-
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
- }
-
- name.clear (); // We don't need to add anything for this pragma.
- tt = l.next (tl, &tn);
- }
- else if (p == "index" ||
- p == "unique")
- {
- // index
- // unique
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- tt = l.next (tl, &tn);
- }
- else if (p == "get" ||
- p == "set" ||
- p == "access")
- {
- // get(name|expr)
- // set(name|expr)
- // access(name|expr)
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- member_access ma (loc, p == "set" ? "modifier" : "accessor", false);
-
- tt = l.next (tl, &tn);
- if (tt != CPP_CLOSE_PAREN) // Empty expression are ok.
- {
- if (!parse_expression (l, tt, tl, tn, ma.expr, p))
- return; // Diagnostics has already been issued.
-
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
- }
-
- val = ma;
-
- // Convert access to the get/set pair.
- //
- if (p == "access")
- {
- if (db.empty () || db == pragma_db_.string ())
- add_pragma (
- pragma (p, "get", val, loc, &check_spec_decl_type, 0), decl, ns);
-
- ma.kind = "modifier";
- val = ma;
- name = "set";
- }
-
- tt = l.next (tl, &tn);
- }
- else if (p == "table")
- {
- // table (<name>)
- // table (<name> [= "<alias>"] [type] [: "<cond>"] (view only)
- //
- // <name> := "name" | "name.name" | "name"."name"
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- if (tt != CPP_STRING && tt != CPP_DOT)
- {
- error (l) << "table name expected in db pragma " << p << endl;
- return;
- }
-
- // The table specifier is used for both objects and views. In case
- // of an object, the context values is just a string. In case of a
- // view, the context value is a view_object entry. The problem is
- // that at this stage we don't know whether we are dealing with an
- // object or a view. To resolve this in a somewhat hackish way, we
- // are going to create both a string and a view_object entry.
- //
- view_object vo;
- vo.kind = view_object::table;
-
- if (!parse_qname (l, tt, tl, tn, p, vo.tbl_name))
- return; // Diagnostics has already been issued.
-
- if (tt == CPP_EQ)
- {
- // We have an alias.
- //
- if (l.next (tl, &tn) != CPP_STRING)
- {
- error (l) << "table alias expected after '=' in db pragma " << p
- << endl;
- return;
- }
-
- vo.alias = tl;
- tt = l.next (tl, &tn);
- }
-
- if (tt == CPP_NAME)
- {
- // We have a JOIN type.
- //
- if (tl == "left")
- vo.join = view_object::left;
- else if (tl == "right")
- vo.join = view_object::right;
- else if (tl == "full")
- vo.join = view_object::full;
- else if (tl == "inner")
- vo.join = view_object::inner;
- else if (tl == "cross")
- vo.join = view_object::cross;
- else
- {
- error (l) << "unknown join type '" << tl << "'" << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
- }
- else
- vo.join = view_object::left;
-
- if (tt == CPP_COLON)
- {
- // We have a condition.
- //
- if (vo.join == view_object::cross)
- {
- error (l)
- << "no join condition can be specified for a cross join" << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- if (!parse_expression (l, tt, tl, tn, vo.cond, p))
- return; // Diagnostics has already been issued.
-
- if (vo.cond.empty ())
- {
- error (l) << "join condition expected after ':' in db pragma " << p
- << endl;
- return;
- }
- }
-
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- // Add the "table" pragma.
- //
- if (vo.alias.empty () && vo.cond.empty ())
- {
- if (db.empty () || db == pragma_db_.string ())
- {
- add_pragma (
- pragma (p, name, vo.tbl_name, loc, &check_spec_decl_type, 0),
- decl,
- ns);
- }
- }
-
- vo.scope = current_scope ();
- vo.loc = loc;
- val = vo;
- name = "objects";
- adder = &accumulate<view_object>;
-
- tt = l.next (tl, &tn);
- }
- else if (p == "session")
- {
- // session
- // session (true|false)
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- tt = l.next (tl, &tn);
-
- if (tt == CPP_OPEN_PAREN)
- {
- tt = l.next (tl, &tn);
-
- if (tt != CPP_KEYWORD || (tl != "true" && tl != "false"))
- {
- error (l) << "true or false expected in db pragma " << p << endl;
- return;
- }
-
- val = (tl == "true");
-
- tt = l.next (tl, &tn);
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
- }
- }
- else if (p == "schema")
- {
- // schema (<name>)
- //
- // <name> := "name" | "name.name" | "name"."name"
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- if (tt != CPP_STRING && tt != CPP_DOT)
- {
- error (l) << "table name expected in db pragma " << p << endl;
- return;
- }
-
- qname s;
- if (!parse_qname (l, tt, tl, tn, p, s))
- return; // Diagnostics has already been issued.
-
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- val = s;
- tt = l.next (tl, &tn);
- }
- else if (p == "pointer")
- {
- // pointer (qname)
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- class_pointer cp;
- size_t pb (0);
- bool punc (false);
-
- for (tt = l.next (tl, &tn);
- tt != CPP_EOF && (tt != CPP_CLOSE_PAREN || pb != 0);
- tt = l.next (tl, &tn))
- {
- if (punc && tt > CPP_LAST_PUNCTUATOR)
- cp.name += ' ';
-
- punc = false;
-
- if (tt == CPP_OPEN_PAREN)
- pb++;
- else if (tt == CPP_CLOSE_PAREN)
- pb--;
-
- // @@ Need to handle literals, at least integer.
- //
- switch (tt)
- {
- case CPP_LESS:
- {
- cp.name += "< ";
- break;
- }
- case CPP_GREATER:
- {
- cp.name += " >";
- break;
- }
- case CPP_COMMA:
- {
- cp.name += ", ";
- break;
- }
- case CPP_NAME:
- // case CPP_KEYWORD: // see default:
- {
- cp.name += tl;
- punc = true;
- break;
- }
- default:
- {
- if (tt == CPP_KEYWORD)
- {
- cp.name += tl;
- punc = true;
- }
- else if (tt <= CPP_LAST_PUNCTUATOR)
- cp.name += cxx_lexer::token_spelling[tt];
- else
- {
- error (l) << "unexpected token '" << cxx_lexer::token_spelling[tt]
- << "' in db pragma " << p << endl;
- return;
- }
- break;
- }
- }
- }
-
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- if (cp.name.empty ())
- {
- error (l) << "expected pointer name in db pragma " << p << endl;
- return;
- }
-
- cp.scope = current_scope ();
- cp.loc = loc;
- val = cp;
-
- tt = l.next (tl, &tn);
- }
- else if (p == "abstract")
- {
- // abstract
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- tt = l.next (tl, &tn);
- }
- else if (p == "optimistic")
- {
- // optimistic
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- tt = l.next (tl, &tn);
- }
- else if (p == "polymorphic")
- {
- // polymorphic
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- tt = l.next (tl, &tn);
- }
- else if (p == "definition")
- {
- // definition
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- val = l.location ();
- tt = l.next (tl, &tn);
- }
- else if (p == "sectionable")
- {
- // sectionable
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- tt = l.next (tl, &tn);
- }
- else if (p == "callback")
- {
- // callback (name)
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- if (tt != CPP_NAME)
- {
- error (l) << "member function name expected in db pragma " << p << endl;
- return;
- }
-
- val = tl;
-
- if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
- }
- else if (p == "bulk")
- {
- // bulk (batch-size)
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- // base
- //
- if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
- {
- error (l) << "unsigned integer expected as batch size" << endl;
- return;
- }
-
- unsigned long long b (integer_value (tn));
-
- if (b == 0 || b == 1)
- {
- error (l) << "batch size has to be greater than 1" << endl;
- return;
- }
-
- val = b;
-
- if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
- }
- else if (p == "query")
- {
- // query ()
- // query ("statement")
- // query (expr)
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- view_query vq;
-
- bool s (false);
- string str;
-
- if (tt == CPP_STRING)
- {
- s = true;
- str = tl;
- tt = l.next (tl, &tn);
- }
-
- if (tt == CPP_CLOSE_PAREN)
- {
- if (s)
- vq.literal = str;
- else
- {
- // Empty query() pragma indicates that the statement will be
- // provided at runtime. Encode this case as empty literal
- // and expression.
- //
- }
- }
- else
- {
- // Expression.
- //
- if (s)
- vq.expr.push_back (cxx_token (0, CPP_STRING, str));
-
- if (!parse_expression (l, tt, tl, tn, vq.expr, p))
- return; // Diagnostics has already been issued.
- }
-
- // Disallow query(, distinct).
- //
- if (tt == CPP_COMMA && vq.expr.empty ())
- {
- error (l) << "query expression expected in db pragma " << p << endl;
- return;
- }
-
- // The query expression can be omitted with the modifier in its
- // place. Handle this case.
- //
- if (vq.expr.size () == 1 && vq.expr.front ().type == CPP_NAME)
- {
- string const& n (vq.expr.front ().literal);
-
- if (n == "distinct")
- vq.distinct = true;
- else if (n == "for_update")
- vq.for_update = true;
-
- if (vq.distinct || vq.for_update)
- vq.expr.clear ();
- }
-
- if (tt == CPP_COMMA)
- {
- do
- {
- if (l.next (tl, &tn) != CPP_NAME)
- {
- error (l) << "result modifier expected in db pragma " << p << endl;
- return;
- }
-
- if (tl == "distinct")
- vq.distinct = true;
- else if (tl == "for_update")
- vq.for_update = true;
- else
- {
- error (l) << "unknown result modifier '" << tl << "'" << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- } while (tt == CPP_COMMA);
- }
-
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- vq.scope = current_scope ();
- vq.loc = loc;
- val = vq;
- tt = l.next (tl, &tn);
- }
- else if (p == "object")
- {
- // object (fq-name [ = name] [type] [: expr])
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- if (tt != CPP_NAME && tt != CPP_SCOPE)
- {
- error (l) << "type name expected in db pragma " << p << endl;
- return;
- }
-
- view_object vo;
- vo.kind = view_object::object;
- vo.obj_node = resolve_scoped_name (
- l, tt, tl, tn, current_scope (), vo.obj_name, true, p);
-
- if (vo.obj_node == 0)
- return; // Diagnostics has already been issued.
-
- // Get the actual type if this is a TYPE_DECL. Also get the main variant.
- //
- if (TREE_CODE (vo.obj_node) == TYPE_DECL)
- vo.obj_node = TREE_TYPE (vo.obj_node);
-
- if (TYPE_P (vo.obj_node)) // Can be a template.
- vo.obj_node = TYPE_MAIN_VARIANT (vo.obj_node);
-
- if (tt == CPP_EQ)
- {
- // We have an alias.
- //
- if (l.next (tl, &tn) != CPP_NAME)
- {
- error (l) << "alias name expected after '=' in db pragma " << p
- << endl;
- return;
- }
-
- vo.alias = tl;
- tt = l.next (tl, &tn);
- }
-
- if (tt == CPP_NAME)
- {
- // We have a JOIN type.
- //
- if (tl == "left")
- vo.join = view_object::left;
- else if (tl == "right")
- vo.join = view_object::right;
- else if (tl == "full")
- vo.join = view_object::full;
- else if (tl == "inner")
- vo.join = view_object::inner;
- else if (tl == "cross")
- vo.join = view_object::cross;
- else
- {
- error (l) << "unknown join type '" << tl << "'" << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
- }
- else
- vo.join = view_object::left;
-
- if (tt == CPP_COLON)
- {
- // We have a condition.
- //
- if (vo.join == view_object::cross)
- {
- error (l)
- << "no join condition can be specified for a cross join" << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- if (!parse_expression (l, tt, tl, tn, vo.cond, p))
- return; // Diagnostics has already been issued.
-
- if (vo.cond.empty ())
- {
- error (l) << "join condition expected after ':' in db pragma " << p
- << endl;
- return;
- }
- }
-
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- vo.scope = current_scope ();
- vo.loc = loc;
- val = vo;
- name = "objects"; // Change the context entry name.
- adder = &accumulate<view_object>;
-
- tt = l.next (tl, &tn);
- }
- else if (p == "id")
- {
- // id[(member-path)]
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- string name;
-
- tt = l.next (tl, &tn);
- if (tt == CPP_OPEN_PAREN)
- {
- if (l.next (tl, &tn) != CPP_NAME)
- {
- error (l) << "data member name expected in db pragma " << p
- << endl;
- return;
- }
-
- name = tl;
-
- for (tt = l.next (tl, &tn); tt == CPP_DOT; tt = l.next (tl, &tn))
- {
- if (l.next (tl, &tn) != CPP_NAME)
- {
- error (l) << "name expected after '.' in db pragma " << p << endl;
- return;
- }
-
- name += '.';
- name += tl;
- }
-
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
- }
-
- val = name;
- }
- else if (p == "no_id")
- {
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- name = "id";
- val = false;
-
- tt = l.next (tl, &tn);
- }
- else if (p == "auto")
- {
- // auto
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- tt = l.next (tl, &tn);
- }
- else if (p == "column")
- {
- // column ("<name>")
- // column ("<name>.<name>") (view only)
- // column ("<name>"."<name>") (view only)
- // column (fq-name) (view only)
- // column (expr) (view only)
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- bool expr (false);
- string expr_str;
- if (tt == CPP_STRING || tt == CPP_DOT)
- {
- qname qn;
-
- if (!parse_qname (l, tt, tl, tn, p, qn, &expr, &expr_str))
- return; // Diagnostics has already been issued.
-
- if (tt == CPP_CLOSE_PAREN)
- {
- table_column tc;
- tc.expr = expr;
-
- if (expr)
- tc.column = expr_str;
- else
- {
- tc.table = qn.qualifier ();
- tc.column = qn.uname ();
- }
-
- val = tc;
- }
- else if (!expr)
- {
- error (l) << "column name, expression, or data member name expected "
- << "in db pragma " << p << endl;
- return;
- }
- }
-
- if (val.empty ())
- {
- // We have an expression.
- //
- column_expr e;
-
- if (expr)
- {
- e.push_back (column_expr_part ());
- e.back ().kind = column_expr_part::literal;
- e.back ().value = expr_str;
-
- if (tt != CPP_PLUS)
- {
- error (l) << "'+' or ')' expected in db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
- }
-
- for (;;)
- {
- if (tt == CPP_STRING)
- {
- e.push_back (column_expr_part ());
- e.back ().kind = column_expr_part::literal;
- e.back ().value = tl;
-
- tt = l.next (tl, &tn);
- }
- else if (tt == CPP_NAME || tt == CPP_SCOPE)
- {
- string name (parse_scoped_name (l, tt, tl, tn, p));
-
- if (name.empty ())
- return; // Diagnostics has already been issued.
-
- // Resolve nested members if any.
- //
- for (; tt == CPP_DOT; tt = l.next (tl, &tn))
- {
- if (l.next (tl, &tn) != CPP_NAME)
- {
- error (l) << "name expected after '.' in db pragma " << p
- << endl;
- return;
- }
-
- name += '.';
- name += tl;
- }
-
- e.push_back (column_expr_part ());
- e.back ().kind = column_expr_part::reference;
- e.back ().value = name;
- e.back ().scope = current_scope ();
- e.back ().loc = loc;
- }
- else
- {
- error (l) << "column name, expression, or data member name expected "
- << "in db pragma " << p << endl;
- return;
- }
-
- if (tt == CPP_PLUS)
- tt = l.next (tl, &tn);
- else if (tt == CPP_CLOSE_PAREN)
- break;
- else
- {
- error (l) << "'+' or ')' expected in db pragma " << p << endl;
- return;
- }
- }
-
- e.loc = loc;
- val = e;
- name = "column-expr";
- }
-
- tt = l.next (tl, &tn);
- }
- else if (p == "value_column" ||
- p == "index_column" ||
- p == "key_column" ||
- p == "id_column")
- {
- // value_column ("<name>")
- // index_column ("<name>")
- // key_column ("<name>")
- // id_column ("<name>")
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- if (tt != CPP_STRING)
- {
- error (l) << "column name expected in db pragma " << p << endl;
- return;
- }
-
- val = tl;
-
- if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
- }
- else if (p == "options" ||
- p == "value_options" ||
- p == "index_options" ||
- p == "key_options" ||
- p == "id_options")
- {
- // options (["<name>"])
- // value_options (["<name>"])
- // index_options (["<name>"])
- // key_options (["<name>"])
- // id_options (["<name>"])
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- if (tt == CPP_STRING)
- {
- // Ignore empty options strings. Internally, we use them to
- // indicate options reset (see below).
- //
- if (!tl.empty ())
- val = tl;
-
- tt = l.next (tl, &tn);
- }
- else if (tt == CPP_CLOSE_PAREN)
- {
- // Empty options specifier signals options reset. Encode it as an
- // empty string.
- //
- val = string ();
- }
- else
- {
- error (l) << "options string expected in db pragma " << p << endl;
- return;
- }
-
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- adder = &accumulate<string>;
- tt = l.next (tl, &tn);
- }
- else if (p == "type" ||
- p == "id_type" ||
- p == "value_type" ||
- p == "index_type" ||
- p == "key_type")
- {
- // type ("<type>")
- // id_type ("<type>")
- // value_type ("<type>")
- // index_type ("<type>")
- // key_type ("<type>")
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- if (tt != CPP_STRING)
- {
- error (l) << "type name expected in db pragma " << p << endl;
- return;
- }
-
- val = tl;
-
- if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
- }
- else if (p == "null" ||
- p == "not_null" ||
- p == "key_null" ||
- p == "key_not_null" ||
- p == "value_null" ||
- p == "value_not_null")
- {
- // null
- // not_null
- // key_null
- // key_not_null
- // value_null
- // value_not_null
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- tt = l.next (tl, &tn);
- }
- else if (p == "default")
- {
- // default () (<empty>)
- // default (null) (n)
- // default (true|false) (t|f)
- // default ([+|-]<number>) (-|+)
- // default ("string") (s)
- // default (<enumerator>) (e)
- //
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- default_value dv;
-
- switch (tt)
- {
- case CPP_CLOSE_PAREN:
- {
- // Default value reset.
- //
- dv.kind = default_value::reset;
- break;
- }
- case CPP_STRING:
- {
- dv.kind = default_value::string;
- dv.literal = tl;
- tt = l.next (tl, &tn);
- break;
- }
- case CPP_NAME:
- {
- // This can be null or an enumerator name.
- //
- if (tl == "null")
- {
- dv.kind = default_value::null;
- tt = l.next (tl, &tn);
- break;
- }
- }
- // Fall through.
- case CPP_SCOPE:
- {
- // We have a potentially scopped enumerator name.
- //
- dv.enum_value = resolve_scoped_name (
- l, tt, tl, tn, current_scope (), dv.literal, false, p);
-
- if (dv.enum_value == 0)
- return; // Diagnostics has already been issued.
-
- dv.kind = default_value::enumerator;
- break;
- }
- case CPP_MINUS:
- case CPP_PLUS:
- {
- if (tt == CPP_MINUS)
- dv.literal = "-";
-
- tt = l.next (tl, &tn);
-
- if (tt != CPP_NUMBER)
- {
- error (l) << "expected numeric constant after '"
- << (tt == CPP_MINUS ? "-" : "+") << "' in db pragma "
- << p << endl;
- return;
- }
- }
- // Fall through.
- case CPP_NUMBER:
- {
- switch (TREE_CODE (tn))
- {
- case INTEGER_CST:
- {
- dv.int_value = integer_value (tn);
- dv.kind = default_value::integer;
- break;
- }
- case REAL_CST:
- {
- REAL_VALUE_TYPE d (TREE_REAL_CST (tn));
-
- if (REAL_VALUE_ISINF (d))
- dv.float_value = numeric_limits<double>::infinity ();
- else if (REAL_VALUE_ISNAN (d))
- dv.float_value = numeric_limits<double>::quiet_NaN ();
- else
- {
- char tmp[256];
- real_to_decimal (tmp, &d, sizeof (tmp), 0, true);
- istringstream is (tmp);
- is >> dv.float_value;
- }
-
- if (dv.literal == "-")
- dv.float_value = -dv.float_value;
-
- dv.kind = default_value::floating;
- break;
- }
- default:
- {
- error (l) << "unexpected numeric constant in db pragma " << p
- << endl;
- return;
- }
- }
-
- tt = l.next (tl, &tn);
- break;
- }
- default:
- {
- // This can be the true or false keyword.
- //
- if (tt == CPP_KEYWORD && (tl == "true" || tl == "false"))
- {
- dv.kind = default_value::boolean;
- dv.literal = tl;
- tt = l.next (tl, &tn);
- }
- else
- {
- error (l) << "unexpected expression in db pragma " << p << endl;
- return;
- }
-
- break;
- }
- }
-
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- val = dv;
- tt = l.next (tl, &tn);
- }
- else if (p == "inverse")
- {
- // inverse (name)
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- if (tt != CPP_NAME)
- {
- error (l) << "member name expected in db pragma " << p << endl;
- return;
- }
-
- string name (tl);
-
- tt = l.next (tl, &tn);
-
- // Parse nested members if any.
- //
- for (; tt == CPP_DOT; tt = l.next (tl, &tn))
- {
- if (l.next (tl, &tn) != CPP_NAME)
- {
- error (l) << "name expected after '.' in db pragma " << p << endl;
- return;
- }
-
- name += '.';
- name += tl;
- }
-
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- val = name;
- tt = l.next (tl, &tn);
- }
- else if (p == "on_delete")
- {
- // on_delete (cascade|set_null)
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- if (l.next (tl, &tn) != CPP_NAME || (tl != "cascade" && tl != "set_null"))
- {
- error (l) << "cascade or set_null expected after '('" << endl;
- return;
- }
-
- using semantics::relational::foreign_key;
- val = (tl == "cascade" ? foreign_key::cascade : foreign_key::set_null);
-
- if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
- }
- else if (p == "points_to")
- {
- // points_to(<fq-name>)
- //
-
- tree type;
- string type_name;
-
- string p (tl);
- location_t loc (l.location ());
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- if (tt == CPP_NAME || tt == CPP_SCOPE)
- {
- type = resolve_scoped_name (
- l, tt, tl, tn, current_scope (), type_name, true, p);
-
- if (type == 0)
- return; // Diagnostics has already been issued.
-
- // Get the actual type if this is a TYPE_DECL. Also get the main
- // variant.
- //
- if (TREE_CODE (type) == TYPE_DECL)
- type = TREE_TYPE (type);
-
- if (TYPE_P (type)) // Can be a template.
- type = TYPE_MAIN_VARIANT (type);
-
- if (TREE_CODE (type) != RECORD_TYPE)
- {
- error (loc) << "name '" << type_name << "' in db pragma " << p
- << " does not refer to a class" << endl;
- return;
- }
-
- val = type;
- }
- else
- {
- error (l) << "class name expected in db pragma " << p << endl;
- return;
- }
-
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
- }
- else if (p == "section")
- {
- // section (name)
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- if (tt != CPP_NAME)
- {
- error (l) << "member name expected in db pragma " << p << endl;
- return;
- }
-
- name = "section-member";
- val = tl;
-
- if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
- }
- else if (p == "load")
- {
- // load (eager|lazy)
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- if (tt != CPP_NAME || (tl != "eager" && tl != "lazy"))
- {
- error (l) << "eager or lazy expected in db pragma " << p << endl;
- return;
- }
-
- name = "section-load";
- val = (tl == "eager"
- ? user_section::load_eager
- : user_section::load_lazy);
-
- if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
- }
- else if (p == "update")
- {
- // update (always|change|manual)
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- if (tt != CPP_NAME ||
- (tl != "always" && tl != "change" && tl != "manual"))
- {
- error (l) << "always, change, or manual expected in db pragma " <<
- p << endl;
- return;
- }
-
- name = "section-update";
-
- if (tl == "always")
- val = user_section::update_always;
- else if (tl == "change")
- val = user_section::update_change;
- else
- val = user_section::update_manual;
-
- if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
- }
- else if (p == "unordered")
- {
- // unordered
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- tt = l.next (tl, &tn);
- }
- else if (p == "readonly")
- {
- // readonly
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- tt = l.next (tl, &tn);
- }
- else if (p == "transient")
- {
- // transient
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- tt = l.next (tl, &tn);
- }
- else if (p == "added" || p == "deleted")
- {
- // added (unsigned long long version)
- // deleted (unsigned long long version)
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- char const* n (p == "added" ? "addition" : "deletion");
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
- {
- error (l) << "unsigned integer expected as " << n << " version" << endl;
- return;
- }
-
- unsigned long long v (integer_value (tn));
-
- if (v == 0)
- {
- error (l) << n << " version cannot be zero" << endl;
- return;
- }
-
- if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- val = v;
- tt = l.next (tl, &tn);
- }
- else if (p == "version")
- {
- // version
- //
-
- // Make sure we've got the correct declaration type.
- //
- if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
- return;
-
- tt = l.next (tl, &tn);
- }
- else if (p == "virtual")
- {
- // Stray virtual specifier (i.e., without explicit member name).
- //
- error (l) << "virtual member declaration requires name" << endl;
- return;
- }
- else if (p == "before" || p == "after")
- {
- // Stray before/after specifier (i.e., without preceding virtual).
- //
- error (l) << p << " specifier must follow virtual" << endl;
- return;
- }
- else
- {
- error (l) << "unknown db pragma " << p << endl;
- return;
- }
-
- // Add the pragma unless was indicated otherwise.
- //
- if (!name.empty () && (db.empty () || db == pragma_db_.string ()))
- {
- // If the value is not specified and we don't use a custom adder,
- // then make it bool (flag).
- //
- if (adder == 0 && val.empty ())
- val = true;
-
- // Convert '_' to '-' in the context name so we get foo-bar instead
- // of foo_bar (that's the convention used).
- //
- for (size_t i (0); i < name.size (); ++i)
- if (name[i] == '_')
- name[i] = '-';
-
- // Record this pragma.
- //
- add_pragma (
- pragma (p, name, val, loc, &check_spec_decl_type, adder), decl, ns);
- }
-
- // Mark the type or member as simple value or container, depending
- // on the pragma. For static multi-database support we only do it
- // if the pragma applies to this database since in this case we can
- // have different mappings for different databases (e.g., composite
- // in one and simple in another). For dynamic multi-database support
- // we do this regardless of the database since here the mapping
- // should the consistent.
- //
- // @@ Did we add new simple value or container pragmas and forgot to
- // account for them here?
- //
- if ((qualifier == "value" || qualifier == "member") &&
- (pragma_multi_ == multi_database::dynamic || db.empty () ||
- db == pragma_db_.string ()))
- {
- // We assume a data member is simple only if the database type was
- // specified explicitly.
- //
- if (name == "type" ||
- name == "id-type" ||
- (qualifier == "value" &&
- (name == "null" ||
- name == "not-null" ||
- name == "default" ||
- name == "options")))
- {
- add_pragma (pragma (p, "simple", true, loc, &check_spec_decl_type, 0),
- decl,
- false);
- }
- else if (name == "table" ||
- name == "value-type" ||
- name == "index-type" ||
- name == "key-type" ||
-
- name == "key-null" ||
- name == "key-not-null" ||
- name == "value-null" ||
- name == "value-not-null" ||
-
- name == "value-column" ||
- name == "index-column" ||
- name == "key-column" ||
- name == "index-column" ||
-
- name == "value-options" ||
- name == "index-options" ||
- name == "key-options" ||
- name == "index-options" ||
-
- name == "unordered")
- {
- add_pragma (pragma (p, "container", true, loc, &check_spec_decl_type, 0),
- decl,
- false);
- }
- }
-
- // See if there are any more pragmas.
- //
- if (tt == CPP_NAME || tt == CPP_KEYWORD)
- {
- handle_pragma (l,
- "",
- tl,
- qualifier,
- qualifier_value,
- decl,
- decl_name,
- ns);
- }
- else if (tt != CPP_EOF)
- error (l) << "unexpected text after " << p << " in db pragma" << endl;
-}
-
-//
-// Qualifiers.
-//
-
-static bool
-check_qual_decl_type (declaration const& d,
- string const& name,
- string const& p,
- location_t l)
-{
- gcc_tree_code_type tc (d.tree_code ());
- bool type (TREE_CODE_CLASS (tc) == tcc_type);
-
- if (p == "model" ||
- p == "map")
- {
- assert (d == global_namespace);
- }
- else if (p == "index")
- {
- if (tc != RECORD_TYPE)
- {
- // For an index, name is not empty only if the class name was
- // specified explicitly. Otherwise, the index definition scope
- // is assumed.
- //
- if (name.empty ())
- {
- error (l) << "db pragma " << p << " outside of a class scope" << endl;
- info (l) << "use the db pragma " << p << "(<class-name>) syntax "
- << " instead" << endl;
- }
- else
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a class" << endl;
- return false;
- }
- }
- else if (p == "namespace")
- {
- if (tc != NAMESPACE_DECL)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a namespace" << endl;
- return false;
- }
- }
- else if (p == "object" ||
- p == "view")
- {
- if (tc != RECORD_TYPE)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a class" << endl;
- return false;
- }
- }
- else if (p == "value")
- {
- if (!type)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a type" << endl;
- return false;
- }
- }
- else if (p == "member")
- {
- if (tc != FIELD_DECL)
- {
- error (l) << "name '" << name << "' in db pragma " << p << " does "
- << "not refer to a data member" << endl;
- return false;
- }
- }
- else
- {
- error (l) << "unknown db pragma " << p << endl;
- return false;
- }
-
- return true;
-}
-
-static void
-add_qual_entry (compiler::context& ctx,
- string const& k,
- any const& v,
- location_t l)
-{
- // Store the TYPE_DECL node that was referred to in the pragma. This
- // can be used later as a name hint in case the type is a template
- // instantiation. Also store the pragma location which is used as
- // the "definition point" for this instantiation.
- //
- ctx.set ("tree-node", v);
- ctx.set ("location", l);
-
- ctx.set (k, true);
-}
-
-static void
-handle_pragma_qualifier (cxx_lexer& l, string p)
-{
- cpp_ttype tt;
- string tl;
- tree tn;
-
- declaration decl;
- tree orig_decl (0); // Original declarations as used in the pragma.
- string decl_name;
-
- // Check for a database prefix.
- //
- string db;
-
- if (p == "mysql" ||
- p == "sqlite" ||
- p == "pgsql" ||
- p == "oracle" ||
- p == "mssql")
- {
- tt = l.next (tl);
-
- if (tt == CPP_COLON)
- {
- // Specifier prefix.
- //
- db = p;
- tt = l.next (p);
- }
- else
- {
- // Qualifier prefix. Ignore the rest if the databases don't match.
- //
- if (p != pragma_db_.string ())
- return;
-
- p = tl;
- }
-
- if (tt != CPP_NAME && tt != CPP_KEYWORD)
- {
- error (l) << "expected specifier after db " << db << " pragma" << endl;
- return;
- }
-
- // Make sure a qualifier prefix is not used before a specifier.
- //
- if (!db.empty () &&
- (p == "model" ||
- p == "map" ||
- p == "namespace" ||
- p == "object" ||
- p == "view" ||
- p == "value" ||
- p == "member"))
- {
- error (l) << "specifier prefix '" << db << ":' used before qualifier " <<
- p << endl;
- return;
- }
- }
-
- //
- //
- string name (p); // Pragma name.
- any val; // Pragma value.
- location_t loc (l.location ()); // Pragma location.
- pragma::add_func adder (0); // Custom context adder.
- bool ns (false); // Namespace location pragma.
-
- cxx_tokens saved_tokens; // Saved token sequence to be replayed.
-
- // Pragma qualifiers.
- //
- if (p == "model")
- {
- orig_decl = global_namespace;
- decl = declaration (orig_decl);
- tt = l.next (tl, &tn);
- }
- else if (p == "map")
- {
- // map type("<regex>") as("<subst>") [to("<subst>")] [from("<subst>")]
- // map type(<c++-type>) as(<c++-type>) [to(<expr>)] [from(<expr>)]
- //
-
- // The thing that determines whether this is a database or C++ type
- // mapping is what we have inside 'type'. So to handle this we are
- // going to pre-scan the pragma looking for 'type' and saving the
- // tokens. Once we determine what this is, we replay the saved
- // tokens to actually parse them.
- //
-
- // Determine what this is by scanning the pragma until we see
- // the 'type' qualifier or EOF.
- //
- bool db (true);
-
- bool done (false);
- size_t balance (0);
- for (tt = l.next (tl, &tn);
- !(done && balance == 0) && tt != CPP_EOF;
- tt = l.next (tl, &tn))
- {
- saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
-
- switch (tt)
- {
- case CPP_OPEN_PAREN:
- {
- balance++;
- continue;
- }
- case CPP_CLOSE_PAREN:
- {
- if (balance > 0)
- balance--;
- else
- {
- error (l) << "unbalanced parenthesis in db pragma " << p << endl;
- return;
- }
- continue;
- }
- case CPP_NAME:
- {
- if (balance == 0 && tl == "type")
- break;
-
- continue;
- }
- default:
- continue;
- }
-
- tt = l.next (tl, &tn);
- saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
-
- if (tt == CPP_OPEN_PAREN)
- {
- balance++;
-
- tt = l.next (tl, &tn);
- saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
-
- db = (tt == CPP_STRING);
- }
-
- done = true; // Scan until the closing ')'.
- }
-
- if (balance != 0)
- {
- error (l) << "unbalanced parenthesis in db pragma " << p << endl;
- return;
- }
-
- orig_decl = global_namespace;
- decl = declaration (orig_decl);
-
- if (db)
- {
- using relational::custom_db_type;
-
- custom_db_type ct;
- ct.loc = loc;
- val = ct;
- name = "custom-db-types";
- adder = &accumulate<custom_db_type>;
- }
- else
- {
- custom_cxx_type ct;
- ct.loc = loc;
- ct.scope = current_scope ();
- val = ct;
- name = "custom-cxx-types";
- adder = &accumulate<custom_cxx_type>;
- }
- }
- else if (p == "index")
- {
- // Index can be both a qualifier and a specifier. Things are complicated
- // by the fact that when it is a specifier, it belongs to a member which
- // means that the actual qualifier ('member') can be omitted. So we need
- // to distinguish between cases like these:
- //
- // #pragma db index type("INTEGER") // specifier
- // #pragma db index type("UNIQUE") member(foo_) // qualifier
- //
- // The thing that determines whether this is a qualifier or a specifier
- // is the presence of the 'member' or 'members' specifier. So to handle
- // this we are going to pre-scan the pragma looking for 'member' or
- // 'members' and saving the tokens. Once we determine what this is,
- // we replay the saved tokens to actually parse them.
- //
- tt = l.next (tl, &tn);
-
- if (tt != CPP_OPEN_PAREN)
- {
- // Determine what this is by scanning the pragma until we see
- // the 'member' qualifier or EOF.
- //
- bool qual (false);
- size_t balance (0);
-
- for (; tt != CPP_EOF; tt = l.next (tl, &tn))
- {
- switch (tt)
- {
- case CPP_OPEN_PAREN:
- {
- balance++;
- break;
- }
- case CPP_CLOSE_PAREN:
- {
- if (balance > 0)
- balance--;
- else
- {
- error (l) << "unbalanced parenthesis in db pragma " << p << endl;
- return;
- }
- break;
- }
- case CPP_NAME:
- {
- if (balance == 0 && (tl == "member" || tl == "members"))
- qual = true;
- break;
- }
- default:
- break;
- }
-
- if (qual)
- break;
-
- saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
- }
-
- if (balance != 0)
- {
- error (l) << "unbalanced parenthesis in db pragma " << p << endl;
- return;
- }
-
- if (qual)
- {
- // This is a qualifer. The saved tokens sequence contains tokens
- // until the first 'member' or 'members' specifier. So we will
- // first need to re-play these tokens and then continue parsing
- // as if we just saw the 'member' or 'members' specifier. The
- // token type (tt) and token literal (tl) variables should contain
- // the correct values.
- //
-
- // Also check that no specifier prefix was used for this qualifer.
- //
- if (!db.empty ())
- {
- error (loc) << "specifier prefix '" << db << ":' used before " <<
- "qualifier index" << endl;
- return;
- }
-
- orig_decl = current_scope ();
- decl = declaration (orig_decl);
- }
- else
- {
- // This is a specifier. The saved tokens sequence contains all the
- // tokens in this pragma until EOF.
- //
- cxx_tokens_lexer l;
- l.start (saved_tokens, loc);
- handle_pragma (
- l, db, "index", "member", val, declaration (), "", false);
- return;
- }
- }
-
- relational::index in;
- in.loc = loc;
-
- if (tt == CPP_OPEN_PAREN)
- {
- // Specifier with the class fq-name, index name, or both.
- //
- // index(<fq-name>)
- // index("<name>")
- // index(<fq-name>::"<name>")
- //
- tt = l.next (tl, &tn);
-
- // Resolve class name, if any.
- //
- if (tt == CPP_NAME || tt == CPP_SCOPE)
- {
- cpp_ttype ptt;
- orig_decl = resolve_scoped_name (
- l, tt, tl, tn, current_scope (), decl_name, true, p, true, &ptt);
-
- if (orig_decl == 0)
- return; // Diagnostics has already been issued.
-
- // Get the actual type if this is a TYPE_DECL. Also get the main
- // variant.
- //
- if (TREE_CODE (orig_decl) == TYPE_DECL)
- orig_decl = TREE_TYPE (orig_decl);
-
- if (TYPE_P (orig_decl)) // Can be a template.
- decl = declaration (TYPE_MAIN_VARIANT (orig_decl));
- else
- decl = declaration (orig_decl);
-
- if (tt == CPP_STRING && ptt != CPP_SCOPE)
- {
- error (l) << "'::' expected before index name in db pragma " << p
- << endl;
- return;
- }
-
- if (tt != CPP_STRING && ptt == CPP_SCOPE)
- {
- error (l) << "index name expected after '::' in db pragma " << p
- << endl;
- return;
- }
- }
- else
- {
- orig_decl = current_scope ();
- decl = declaration (orig_decl);
- }
-
- // Make sure we've got the correct declaration type.
- //
- if (!check_qual_decl_type (decl, decl_name, p, loc))
- return;
-
- if (tt == CPP_STRING)
- {
- in.name = tl;
- tt = l.next (tl, &tn);
- }
-
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
- }
-
- val = in;
- adder = &accumulate<relational::index>;
- }
- else if (p == "namespace")
- {
- // namespace [(<identifier>)]
- // namespace () (refers to global namespace)
- //
-
- tt = l.next (tl, &tn);
-
- if (tt == CPP_OPEN_PAREN)
- {
- tt = l.next (tl, &tn);
-
- if (tt == CPP_NAME || tt == CPP_SCOPE)
- {
- orig_decl = resolve_scoped_name (
- l, tt, tl, tn, current_scope (), decl_name, false, p);
-
- if (orig_decl == 0)
- return; // Diagnostics has already been issued.
-
- // Make sure we've got the correct declaration type.
- //
- if (!check_qual_decl_type (orig_decl, decl_name, p, loc))
- return;
-
- // Resolve namespace aliases if any.
- //
- orig_decl = ORIGINAL_NAMESPACE (orig_decl);
- decl = declaration (orig_decl);
-
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
- }
- else if (tt == CPP_CLOSE_PAREN)
- {
- orig_decl = global_namespace;
- decl = declaration (orig_decl);
- tt = l.next (tl, &tn);
- }
- else
- {
- error (l) << "data member name expected in db pragma " << p << endl;
- return;
- }
- }
- else
- {
- // Make sure we are in a namespace scope.
- //
- if (TREE_CODE (current_scope ()) != NAMESPACE_DECL)
- {
- error (l) << "db pragma " << p << " is not in a namespace scope"
- << endl;
- return;
- }
-
- ns = true;
- }
- }
- else if (p == "object" ||
- p == "view" ||
- p == "value")
- {
- // object [(<identifier>)]
- // view [(<identifier>)]
- // value [(<identifier>)]
- //
-
- tt = l.next (tl, &tn);
-
- if (tt == CPP_OPEN_PAREN)
- {
- tt = l.next (tl, &tn);
-
- // Can be built-in type (e.g., bool).
- //
- if (tt == CPP_NAME || tt == CPP_KEYWORD || tt == CPP_SCOPE)
- {
- orig_decl = resolve_scoped_name (
- l, tt, tl, tn, current_scope (), decl_name, true, p);
-
- if (orig_decl == 0)
- return; // Diagnostics has already been issued.
-
- // Get the actual type if this is a TYPE_DECL. Also get the main
- // variant.
- //
- if (TREE_CODE (orig_decl) == TYPE_DECL)
- orig_decl = TREE_TYPE (orig_decl);
-
- if (TYPE_P (orig_decl)) // Can be a template.
- decl = declaration (TYPE_MAIN_VARIANT (orig_decl));
- else
- decl = declaration (orig_decl);
-
- // Make sure we've got the correct declaration type.
- //
- if (!check_qual_decl_type (decl, decl_name, p, loc))
- return;
-
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
- }
- else
- {
- error (l) << "type name expected in db pragma " << p << endl;
- return;
- }
- }
- }
- else if (p == "member")
- {
- // member [(<identifier>)]
- //
-
- tt = l.next (tl, &tn);
-
- if (tt == CPP_OPEN_PAREN)
- {
- tt = l.next (tl, &tn);
-
- if (tt != CPP_NAME && tt != CPP_SCOPE)
- {
- error (l) << "data member name expected in db pragma " << p << endl;
- return;
- }
-
- // We need to see if this is a virtual data member declaration. Also,
- // if it is not, then the name can still refer to one so we need to
- // take extra steps to handle that. But first, we save the name and
- // look for the 'virtual' specifier.
- //
- cxx_tokens name_tokens;
- for (; tt != CPP_CLOSE_PAREN && tt != CPP_EOF; tt = l.next (tl, &tn))
- name_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
-
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- // Now scan the remainder of the pragma looking for the 'virtual'
- // keyword and saving the tokens in between for later.
- //
- bool virt (false);
- size_t balance (0);
- for (tt = l.next (tl, &tn); tt != CPP_EOF; tt = l.next (tl, &tn))
- {
- switch (tt)
- {
- case CPP_OPEN_PAREN:
- {
- balance++;
- break;
- }
- case CPP_CLOSE_PAREN:
- {
- if (balance > 0)
- balance--;
- else
- {
- error (l) << "unbalanced parenthesis in db pragma " << p << endl;
- return;
- }
- break;
- }
- default:
- {
- if (balance == 0 && tt == CPP_KEYWORD && tl == "virtual")
- virt = true;
- break;
- }
- }
-
- if (virt)
- break;
-
- saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
- }
-
- if (balance != 0)
- {
- error (l) << "unbalanced parenthesis in db pragma " << p << endl;
- return;
- }
-
- // Regardless of whether this is a virtual member declaration or a
- // reference, resolve its scope name (if one is specified), which
- // should be a class. We will need it in both cases.
- //
- tree scope;
- if (name_tokens.size () > 2) // scope::name
- {
- size_t n (name_tokens.size ());
-
- if (name_tokens[n - 2].type != CPP_SCOPE ||
- name_tokens[n - 1].type != CPP_NAME)
- {
- error (l) << "invalid name in db pragma " << p << endl;
- return;
- }
-
- cxx_tokens scope_tokens (1, name_tokens.back ());
- name_tokens.pop_back (); // ::
- name_tokens.pop_back (); // name
- name_tokens.swap (scope_tokens);
-
- cxx_tokens_lexer l;
- l.start (scope_tokens);
-
- tree tn;
- string tl;
- cpp_ttype tt (l.next (tl));
-
- scope = resolve_scoped_name (
- l, tt, tl, tn, current_scope (), decl_name, true, p);
-
- if (scope == 0)
- return; // Diagnostics has already been issued.
-
- scope = TYPE_MAIN_VARIANT (TREE_TYPE (scope));
-
- if (tt != CPP_EOF)
- {
- error (l) << "invalid name in db pragma " << p << endl;
- return;
- }
-
- decl_name += "::";
- }
- else
- scope = current_scope ();
-
- if (virt)
- {
- // Should be a single name.
- //
- if (name_tokens.size () > 1 || name_tokens.back ().type != CPP_NAME)
- {
- location_t l (name_tokens.back ().loc);
- error (l) << "invalid name in db pragma " << p << endl;
- return;
- }
- string const& name (name_tokens.back ().literal);
-
- // Parse the remainder of the virtual specifier.
- //
- // virtual(<fq-name>)
- //
- tree type;
- string type_name;
- location_t ord (loc);
- int ord_bias (0);
- {
- string p (tl);
- location_t loc (l.location ());
-
- if (l.next (tl, &tn) != CPP_OPEN_PAREN)
- {
- error (l) << "'(' expected after db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- // Can be built-in type (e.g., bool).
- //
- if (tt == CPP_NAME || tt == CPP_KEYWORD || tt == CPP_SCOPE)
- {
- type = resolve_scoped_name (
- l, tt, tl, tn, current_scope (), type_name, true, p);
-
- if (type == 0)
- return; // Diagnostics has already been issued.
-
- if (TREE_CODE (type) != TYPE_DECL)
- {
- error (loc) << "name '" << type_name << "' in db pragma "
- << p << " does not refer to a type" << endl;
- return;
- }
-
- type = TREE_TYPE (type);
- }
- else
- {
- error (l) << "type name expected in db pragma " << p << endl;
- return;
- }
-
- if (tt != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma " << p << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
-
- // See if we have before/after specifiers.
- //
- if (tt == CPP_NAME && tl == "before")
- {
- // before[(<member>)]
- //
- // Before without the member name means first.
- //
- tt = l.next (tl, &tn);
-
- if (tt == CPP_OPEN_PAREN)
- {
- if (l.next (tl, &tn) != CPP_NAME)
- {
- error (l) << "')' member name expected in db pragma before"
- << endl;
- }
-
- string dn;
- cxx_tokens ts (1, cxx_token (l.location (), CPP_NAME, tl));
- declaration d (resolve_data_member (scope, ts, dn, "before"));
-
- if (!d)
- return; // Diagnostics has already been issued.
-
- if (d.virt)
- {
- ord = d.decl.virt->ord;
- ord_bias = d.decl.virt->ord_bias - 1;
- }
- else
- {
- ord = real_source_location (d.decl.real);
- ord_bias = -1;
- }
-
- if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma before"
- << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
- }
- else
- ord = 0;
- }
-
- if (tt == CPP_NAME && tl == "after")
- {
- // after[(<member>)]
- //
- // Before without the member name means last.
- //
- tt = l.next (tl, &tn);
-
- if (tt == CPP_OPEN_PAREN)
- {
- if (l.next (tl, &tn) != CPP_NAME)
- {
- error (l) << "')' member name expected in db pragma after"
- << endl;
- }
-
- string dn;
- cxx_tokens ts (1, cxx_token (l.location (), CPP_NAME, tl));
- declaration d (resolve_data_member (scope, ts, dn, "after"));
-
- if (!d)
- return; // Diagnostics has already been issued.
-
- if (d.virt)
- {
- ord = d.decl.virt->ord;
- ord_bias = d.decl.virt->ord_bias + 1;
- }
- else
- {
- ord = real_source_location (d.decl.real);
- ord_bias = 1;
- }
-
- if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
- {
- error (l) << "')' expected at the end of db pragma after"
- << endl;
- return;
- }
-
- tt = l.next (tl, &tn);
- }
- else
- ord = ~location_t (0);
- }
- }
-
- pair<virt_declaration_set::const_iterator, bool> r (
- virt_declarations_[scope].insert (
- virt_declaration (loc, ord, ord_bias, name, FIELD_DECL, type)));
-
- if (!r.second)
- {
- error (loc) << "virtual data member declaration '" << name
- << "' conflicts with a previous declaration" << endl;
-
- info (r.first->loc) << "'" << name << "' was previously "
- << "declared here" << endl;
- return;
- }
-
- decl_name += name;
- decl = declaration (*r.first);
-
- // Mark it as virtual using the standard pragma machinery.
- //
- add_pragma (
- pragma ("virtual", "virtual", true, loc, &check_spec_decl_type, 0),
- decl,
- false);
- }
- else
- {
- // Not a virtual member declaration.
- //
- decl = resolve_data_member (scope, name_tokens, decl_name, p);
-
- if (!decl)
- return; // Diagnostics has already been issued.
-
- // Make sure we've got the correct declaration type.
- //
- if (!check_qual_decl_type (decl, decl_name, p, loc))
- return;
- }
- }
- }
- //
- // The member qualifier can be omitted so we need to also handle all
- // the member pragmas here.
- //
- else if (p == "id" ||
- p == "auto" ||
- p == "unique" ||
- p == "get" ||
- p == "set" ||
- p == "access" ||
- p == "column" ||
- p == "value_column" ||
- p == "index_column" ||
- p == "key_column" ||
- p == "id_column" ||
- p == "options" ||
- p == "value_options" ||
- p == "index_options" ||
- p == "key_options" ||
- p == "id_options" ||
- p == "type" ||
- p == "id_type" ||
- p == "value_type" ||
- p == "index_type" ||
- p == "key_type" ||
- p == "table" ||
- p == "null" ||
- p == "not_null" ||
- p == "key_null" ||
- p == "key_not_null" ||
- p == "value_null" ||
- p == "value_not_null" ||
- p == "default" ||
- p == "section" ||
- p == "load" ||
- p == "update" ||
- p == "inverse" ||
- p == "on_delete" ||
- p == "points_to" ||
- p == "unordered" ||
- p == "readonly" ||
- p == "transient" ||
- p == "added" ||
- p == "deleted" ||
- p == "version" ||
- p == "virtual")
- {
- handle_pragma (l, db, p, "member", val, declaration (), "", false);
- return;
- }
- else
- {
- error (l) << "unknown db pragma " << p << endl;
- return;
- }
-
- // Record this pragma. Delay this until after we process the
- // specifiers for value (see comment below for the reason).
- //
- if (adder == 0)
- val = orig_decl;
-
- pragma prag (p,
- name, // For now no need to translate '_' to '-'.
- val,
- loc,
- &check_qual_decl_type,
- adder != 0 ? adder : &add_qual_entry);
-
- tree scope;
- if (!decl)
- {
- scope = current_scope ();
-
- if (!ns && !CLASS_TYPE_P (scope))
- scope = global_namespace;
- }
-
- any* pval;
- if (p != "value")
- {
- if (decl)
- pval = &decl_pragmas_[decl].insert (prag).value;
- else
- {
- if (!ns)
- {
- pragma_list& pl (loc_pragmas_[scope]);
- pl.push_back (prag);
- pval = &pl.back ().value;
- }
- else
- {
- ns_loc_pragmas_.push_back (ns_loc_pragma (scope, prag));
- pval = &ns_loc_pragmas_.back ().pragma.value;
- }
- }
- }
- else
- pval = &val;
-
- // See if there are any saved tokens to replay.
- //
- if (!saved_tokens.empty ())
- {
- cxx_tokens_lexer l;
- l.start (saved_tokens);
-
- string tl;
- cpp_ttype tt (l.next (tl));
-
- if (tt == CPP_NAME || tt == CPP_KEYWORD)
- {
- handle_pragma (l, "", tl, p, *pval, decl, decl_name, ns);
-
- if (errorcount != 0) // Avoid parsing the rest if there was an error.
- return;
- }
- else if (tt != CPP_EOF)
- {
- error (l) << "unexpected text after " << p << " in db pragma" << endl;
- return;
- }
- }
-
- size_t count (0);
- if (tt == CPP_NAME || tt == CPP_KEYWORD)
- {
- if (decl)
- count = decl_pragmas_[decl].size ();
- else
- count = loc_pragmas_[scope].size ();
-
- handle_pragma (l, "", tl, p, *pval, decl, decl_name, ns);
- }
- else if (tt != CPP_EOF)
- {
- error (l) << "unexpected text after " << p << " in db pragma" << endl;
- return;
- }
-
- // Record the value pragma. Here things are complicated by the fact
- // that we use the value pragma by itself to signify a composite value
- // type declaration. Consider this example:
- //
- // #pragma db value pgsql:type("POINT")
- // class point {...};
- //
- // Should this class be considered composite value type in other
- // databases (because that's what would happen by default)? Probably
- // not. So to overcome this we are going to detect and ignore cases
- // where (a) some specifiers followed the value qualifier but (b)
- // none of them are for the database that we are compiling for.
- //
- if (p == "value")
- {
- if (decl)
- {
- pragma_set& ps (decl_pragmas_[decl]);
-
- if (tt == CPP_EOF || ps.size () > count)
- ps.insert (prag);
- }
- else
- {
- pragma_list& pl (loc_pragmas_[scope]);
-
- if (tt == CPP_EOF || pl.size () > count)
- pl.push_back (prag);
- }
- }
-}
-
-/*
-extern "C" void
-handle_pragma_db_mysql (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "mysql");
-}
-
-extern "C" void
-handle_pragma_db_sqlite (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "sqlite");
-}
-
-extern "C" void
-handle_pragma_db_pgsql (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "pgsql");
-}
-
-extern "C" void
-handle_pragma_db_oracle (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "oracle");
-}
-
-extern "C" void
-handle_pragma_db_mssql (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "mssql");
-}
-
-extern "C" void
-handle_pragma_db_model (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "model");
-}
-
-extern "C" void
-handle_pragma_db_map (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "map");
-}
-
-extern "C" void
-handle_pragma_db_namespace (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "namespace");
-}
-
-extern "C" void
-handle_pragma_db_object (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "object");
-}
-
-extern "C" void
-handle_pragma_db_view (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "view");
-}
-
-extern "C" void
-handle_pragma_db_value (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "value");
-}
-
-extern "C" void
-handle_pragma_db_member (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "member");
-}
-
-extern "C" void
-handle_pragma_db_id (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "id");
-}
-
-extern "C" void
-handle_pragma_db_auto (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "auto");
-}
-
-extern "C" void
-handle_pragma_db_column (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "column");
-}
-
-extern "C" void
-handle_pragma_db_vcolumn (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "value_column");
-}
-
-extern "C" void
-handle_pragma_db_icolumn (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "index_column");
-}
-
-extern "C" void
-handle_pragma_db_kcolumn (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "key_column");
-}
-
-extern "C" void
-handle_pragma_db_idcolumn (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "id_column");
-}
-
-extern "C" void
-handle_pragma_db_options (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "options");
-}
-
-extern "C" void
-handle_pragma_db_voptions (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "value_options");
-}
-
-extern "C" void
-handle_pragma_db_ioptions (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "index_options");
-}
-
-extern "C" void
-handle_pragma_db_koptions (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "key_options");
-}
-
-extern "C" void
-handle_pragma_db_idoptions (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "id_options");
-}
-
-extern "C" void
-handle_pragma_db_type (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "type");
-}
-
-extern "C" void
-handle_pragma_db_id_type (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "id_type");
-}
-
-extern "C" void
-handle_pragma_db_vtype (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "value_type");
-}
-
-extern "C" void
-handle_pragma_db_itype (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "index_type");
-}
-
-extern "C" void
-handle_pragma_db_ktype (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "key_type");
-}
-
-extern "C" void
-handle_pragma_db_table (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "table");
-}
-
-extern "C" void
-handle_pragma_db_null (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "null");
-}
-
-extern "C" void
-handle_pragma_db_not_null (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "not_null");
-}
-
-extern "C" void
-handle_pragma_db_value_null (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "value_null");
-}
-
-extern "C" void
-handle_pragma_db_value_not_null (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "value_not_null");
-}
-
-extern "C" void
-handle_pragma_db_key_null (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "key_null");
-}
-
-extern "C" void
-handle_pragma_db_key_not_null (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "key_not_null");
-}
-
-extern "C" void
-handle_pragma_db_default (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "default");
-}
-
-extern "C" void
-handle_pragma_db_section (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "section");
-}
-
-extern "C" void
-handle_pragma_db_load (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "load");
-}
-
-extern "C" void
-handle_pragma_db_update (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "update");
-}
-
-extern "C" void
-handle_pragma_db_inverse (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "inverse");
-}
-
-extern "C" void
-handle_pragma_db_on_delete (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "on_delete");
-}
-
-extern "C" void
-handle_pragma_db_points_to (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "points_to");
-}
-
-extern "C" void
-handle_pragma_db_unordered (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "unordered");
-}
-
-extern "C" void
-handle_pragma_db_readonly (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "readonly");
-}
-
-extern "C" void
-handle_pragma_db_transient (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "transient");
-}
-
-extern "C" void
-handle_pragma_db_added (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "added");
-}
-
-extern "C" void
-handle_pragma_db_deleted (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "deleted");
-}
-
-extern "C" void
-handle_pragma_db_version (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "version");
-}
-
-extern "C" void
-handle_pragma_db_virtual (cpp_reader* r)
-{
- handle_pragma_qualifier (r, "virtual");
-}
-*/
-
-extern "C" void
-handle_pragma_db (cpp_reader*)
-{
- cxx_pragma_lexer l;
- l.start ();
-
- string tl;
- cpp_ttype tt (l.next (tl));
-
- if (tt != CPP_NAME && tt != CPP_KEYWORD)
- {
- error (l) << "expected specifier after db pragma" << endl;
- return;
- }
-
- handle_pragma_qualifier (l, tl);
-}
-
-extern "C" void
-register_odb_pragmas (void*, void*)
-{
- // GCC has a limited number of pragma slots and we have exhausted them.
- // A workaround is to make 'db' a pragma rather than a namespace. This
- // way we only have one pragma but the drawback of this approach is the
- // fact that the specifier or qualifier name will now be macro-expanded
- // (though this happens anyway if we have multiple specifiers in a single
- // pragma). Once the GCC folks fix this, we can go back to the namespace
- // approach.
- //
- c_register_pragma_with_expansion (0, "db", handle_pragma_db);
-
- /*
- c_register_pragma_with_expansion ("db", "mysql", handle_pragma_db_mysql);
- c_register_pragma_with_expansion ("db", "sqlite", handle_pragma_db_sqlite);
- c_register_pragma_with_expansion ("db", "pgsql", handle_pragma_db_pgsql);
- c_register_pragma_with_expansion ("db", "oracle", handle_pragma_db_oracle);
- c_register_pragma_with_expansion ("db", "mssql", handle_pragma_db_mssql);
- c_register_pragma_with_expansion ("db", "model", handle_pragma_db_model);
- c_register_pragma_with_expansion ("db", "map", handle_pragma_db_map);
- c_register_pragma_with_expansion ("db", "namespace", handle_pragma_db_namespace);
- c_register_pragma_with_expansion ("db", "object", handle_pragma_db_object);
- c_register_pragma_with_expansion ("db", "view", handle_pragma_db_view);
- c_register_pragma_with_expansion ("db", "value", handle_pragma_db_value);
- c_register_pragma_with_expansion ("db", "member", handle_pragma_db_member);
- c_register_pragma_with_expansion ("db", "id", handle_pragma_db_id);
- c_register_pragma_with_expansion ("db", "auto", handle_pragma_db_auto);
- c_register_pragma_with_expansion ("db", "column", handle_pragma_db_column);
- c_register_pragma_with_expansion ("db", "value_column", handle_pragma_db_vcolumn);
- c_register_pragma_with_expansion ("db", "index_column", handle_pragma_db_icolumn);
- c_register_pragma_with_expansion ("db", "key_column", handle_pragma_db_kcolumn);
- c_register_pragma_with_expansion ("db", "id_column", handle_pragma_db_idcolumn);
- c_register_pragma_with_expansion ("db", "options", handle_pragma_db_options);
- c_register_pragma_with_expansion ("db", "value_options", handle_pragma_db_voptions);
- c_register_pragma_with_expansion ("db", "index_options", handle_pragma_db_ioptions);
- c_register_pragma_with_expansion ("db", "key_options", handle_pragma_db_koptions);
- c_register_pragma_with_expansion ("db", "id_options", handle_pragma_db_idoptions);
- c_register_pragma_with_expansion ("db", "type", handle_pragma_db_type);
- c_register_pragma_with_expansion ("db", "id_type", handle_pragma_db_id_type);
- c_register_pragma_with_expansion ("db", "value_type", handle_pragma_db_vtype);
- c_register_pragma_with_expansion ("db", "index_type", handle_pragma_db_itype);
- c_register_pragma_with_expansion ("db", "key_type", handle_pragma_db_ktype);
- c_register_pragma_with_expansion ("db", "table", handle_pragma_db_table);
- c_register_pragma_with_expansion ("db", "null", handle_pragma_db_null);
- c_register_pragma_with_expansion ("db", "not_null", handle_pragma_db_not_null);
- c_register_pragma_with_expansion ("db", "key_null", handle_pragma_db_key_null);
- c_register_pragma_with_expansion ("db", "key_not_null", handle_pragma_db_key_not_null);
- c_register_pragma_with_expansion ("db", "value_null", handle_pragma_db_value_null);
- c_register_pragma_with_expansion ("db", "value_not_null", handle_pragma_db_value_not_null);
- c_register_pragma_with_expansion ("db", "default", handle_pragma_db_default);
- c_register_pragma_with_expansion ("db", "section", handle_pragma_db_section);
- c_register_pragma_with_expansion ("db", "load", handle_pragma_db_load);
- c_register_pragma_with_expansion ("db", "update", handle_pragma_db_update);
- c_register_pragma_with_expansion ("db", "inverse", handle_pragma_db_inverse);
- c_register_pragma_with_expansion ("db", "on_delete", handle_pragma_db_on_delete);
- c_register_pragma_with_expansion ("db", "points_to", handle_pragma_db_points_to);
- c_register_pragma_with_expansion ("db", "unordered", handle_pragma_db_unordered);
- c_register_pragma_with_expansion ("db", "readonly", handle_pragma_db_readonly);
- c_register_pragma_with_expansion ("db", "transient", handle_pragma_db_transient);
- c_register_pragma_with_expansion ("db", "added", handle_pragma_db_added);
- c_register_pragma_with_expansion ("db", "deleted", handle_pragma_db_deleted);
- c_register_pragma_with_expansion ("db", "version", handle_pragma_db_version);
- c_register_pragma_with_expansion ("db", "virtual", handle_pragma_db_virtual);
- */
-}
-
-void
-post_process_pragmas ()
-{
- // Make sure object, view, and composite class template instantiations
- // are fully instantiated.
- //
- for (decl_pragmas::iterator i (decl_pragmas_.begin ()),
- e (decl_pragmas_.end ()); i != e; ++i)
- {
- if (i->first.virt)
- continue;
-
- tree type (i->first.decl.real);
-
- if (!(CLASS_TYPE_P (type) && CLASSTYPE_TEMPLATE_INSTANTIATION (type)))
- continue;
-
- // Check whether this is an object, view, or composite value type.
- //
- pragma const* p (0);
- string diag_name;
-
- for (pragma_set::iterator j (i->second.begin ()), e (i->second.end ());
- j != e; ++j)
- {
- string const& name (j->second.context_name);
-
- if (name == "object")
- {
- p = &j->second;
- diag_name = "persistent object";
- break;
- }
- else if (name == "view")
- {
- p = &j->second;
- diag_name = "view";
- break;
- }
- else if (name == "value")
- {
- p = &j->second;
- diag_name = "composite value";
- break;
- }
- // We don't want to instantiate simple values since they may be
- // incomplete.
- //
- else if (name == "simple" || name == "container")
- {
- p = 0;
- break;
- }
- }
-
- if (p == 0)
- continue;
-
- // Make sure it is instantiated.
- //
- tree decl (TYPE_NAME (p->value.value<tree> ()));
- location_t loc (real_source_location (decl));
-
- // Reset input location so that we get nice diagnostics in case
- // of an error.
- //
- input_location = loc;
-
- if (instantiate_class_template (type) == error_mark_node ||
- errorcount != 0 ||
- !COMPLETE_TYPE_P (type))
- {
- error (loc) << "unable to instantiate " << diag_name << " class template"
- << endl;
- throw pragmas_failed ();
- }
- }
-}
diff --git a/odb/pragma.hxx b/odb/pragma.hxx
deleted file mode 100644
index 45b3528..0000000
--- a/odb/pragma.hxx
+++ /dev/null
@@ -1,287 +0,0 @@
-// file : odb/pragma.hxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#ifndef ODB_PRAGMA_HXX
-#define ODB_PRAGMA_HXX
-
-#include <odb/gcc.hxx>
-
-#include <map>
-#include <set>
-#include <vector>
-#include <string>
-
-#include <odb/option-types.hxx> // database
-
-#include <cutl/container/any.hxx>
-#include <cutl/container/multi-index.hxx>
-#include <cutl/compiler/context.hxx>
-
-struct virt_declaration
-{
- virt_declaration (location_t l,
- location_t o,
- int ob,
- std::string const& n,
- gcc_tree_code_type tc,
- tree t)
- : loc (l), ord (o), ord_bias (ob), name (n), tree_code (tc), type (t) {}
-
- location_t loc;
- location_t ord; // Ordering location for before/after support.
- int ord_bias; // Ordering bias for the same locations.
- std::string name;
- gcc_tree_code_type tree_code;
- tree type; // Declaration's type.
-};
-
-// Note that we consider virtual declarations with the same name but
-// different tree codes unequal. If that is too loose, then the
-// inserting code must do additional checks.
-//
-struct virt_declaration_set
-{
- typedef cutl::container::key<std::string, gcc_tree_code_type> key;
- typedef std::map<key, virt_declaration> map;
- typedef cutl::container::map_const_iterator<map> const_iterator;
-
- std::pair<const_iterator, bool>
- insert (const virt_declaration& v)
- {
- std::pair<map::iterator, bool> r (
- map_.insert (map::value_type (key (v.name, v.tree_code), v)));
-
- const_iterator i (r.first);
-
- if (r.second)
- r.first->first.assign (i->name, i->tree_code);
-
- return std::make_pair (i, r.second);
- }
-
- const_iterator
- find (std::string const& name, gcc_tree_code_type tree_code) const
- {
- return map_.find (key (name, tree_code));
- }
-
- const_iterator begin () const {return map_.begin ();}
- const_iterator end () const {return map_.end ();}
-
-private:
- map map_;
-};
-
-// Map of scopes (e.g., class, namespace) to sets of virtual declarations.
-//
-typedef std::map<tree, virt_declaration_set> virt_declarations;
-extern virt_declarations virt_declarations_;
-
-// Real or virtual declaration. If it is real, then it is a pointer to
-// the GCC tree node. Otherwise, it is a pointer to virt_declaration
-// from virt_declarations_ above.
-//
-struct declaration
-{
- declaration (): virt (false) {decl.real = 0;}
- declaration (tree d): virt (false) {decl.real = d;}
- declaration (virt_declaration const& d): virt (true) {decl.virt = &d;}
-
- bool virt;
-
- union
- {
- tree real;
- virt_declaration const* virt;
- } decl;
-
- gcc_tree_code_type
- tree_code () const
- {
- return (virt ? decl.virt->tree_code : TREE_CODE (decl.real));
- }
-
- typedef bool declaration::*bool_convertible;
- operator bool_convertible () const
- {
- return ptr () == 0 ? 0 : &declaration::virt;
- }
-
-public:
- bool
- operator== (declaration const& x) const
- {
- return virt == x.virt && ptr () == x.ptr ();
- }
-
- bool
- operator!= (declaration const& x) const
- {
- return !(*this == x);
- }
-
- bool
- operator< (declaration const& x) const
- {
- return virt < x.virt || (virt == x.virt && ptr () < x.ptr ());
- }
-
-public:
- void const*
- ptr () const
- {
- return virt
- ? static_cast<void const*> (decl.virt)
- : static_cast<void const*> (decl.real);
- }
-};
-
-inline bool
-operator== (declaration const& x, tree y)
-{
- return !x.virt && x.decl.real == y;
-}
-
-inline bool
-operator== (tree x, declaration const& y)
-{
- return !y.virt && y.decl.real == x;
-}
-
-struct pragma
-{
- // Check that the pragma is applicable to the declaration. Return true
- // on success, complain and return false otherwise.
- //
- typedef bool (*check_func) (declaration const& decl,
- std::string const& decl_name,
- std::string const& prag_name,
- location_t);
-
- // Add the pragma value to the context.
- //
- typedef void (*add_func) (cutl::compiler::context&,
- std::string const& key,
- cutl::container::any const& value,
- location_t);
-
- pragma (std::string const& pn,
- std::string const& cn,
- cutl::container::any const& v,
- location_t l,
- check_func c,
- add_func a)
- : pragma_name (pn),
- context_name (cn),
- value (v),
- loc (l),
- check (c),
- add (a)
- {
- }
-
- std::string pragma_name; // Actual pragma name for diagnostics.
- std::string context_name; // Context entry name.
- cutl::container::any value;
- location_t loc;
- check_func check;
- add_func add;
-};
-
-typedef std::vector<pragma> pragma_list;
-
-// A set of pragmas. Insertion of a pragma with the same name and no
-// custom add function overrides the old value.
-//
-struct pragma_set: std::multimap<std::string, pragma>
-{
- typedef std::multimap<std::string, pragma> base;
-
- pragma&
- insert (pragma const& p)
- {
- std::string const& n (p.context_name);
- std::pair<iterator, iterator> r (equal_range (n));
-
- iterator i (end ());
-
- if (p.add == 0)
- {
- if (r.first != r.second)
- {
- i = r.first;
- assert (++r.first == r.second);
-
- i->second = p;
- }
- }
- else if (r.first != r.second)
- assert ((--r.second)->second.loc <= p.loc);
-
- if (i == end ())
- i = base::insert (base::value_type (n, p));
-
- return i->second;
- }
-
- void
- insert (const_iterator begin, const_iterator end)
- {
- for (; begin != end; ++begin)
- insert (begin->second);
- }
-
- // Return the last pragma in the equal range which (by construction) has the
- // location greater or equal to all the other pragmas in this range.
- //
- iterator
- find (std::string const& n)
- {
- return equal_range (n).second;
- }
-};
-
-
-// Position pragmas inside a class or namespace. The key for the
-// namespace case is the global_namespace node.
-//
-typedef std::map<tree, pragma_list> loc_pragmas;
-extern loc_pragmas loc_pragmas_;
-
-// Position pragmas for namespaces. Because re-opened namespaces do
-// not have any representation in the GCC tree, these are handled in
-// a special way. They are stored as a list of pragmas and their outer
-// namespaces.
-//
-struct ns_loc_pragma
-{
- typedef ::pragma pragma_type;
- ns_loc_pragma (tree n, pragma_type const& p): ns (n), pragma (p) {}
-
- tree ns;
- pragma_type pragma;
-};
-
-typedef std::vector<ns_loc_pragma> ns_loc_pragmas;
-extern ns_loc_pragmas ns_loc_pragmas_;
-
-// Pragmas associated with specific declarations (real or virtual).
-//
-typedef std::map<declaration, pragma_set> decl_pragmas;
-extern decl_pragmas decl_pragmas_;
-
-// Database we are generating code for as well as multi-database support.
-// Used to ignore database-specific pragmas.
-//
-extern database pragma_db_;
-extern multi_database pragma_multi_;
-
-extern "C" void
-register_odb_pragmas (void*, void*);
-
-struct pragmas_failed {};
-
-void
-post_process_pragmas ();
-
-#endif // ODB_PRAGMA_HXX
diff --git a/odb/processor.cxx b/odb/processor.cxx
deleted file mode 100644
index dd0a706..0000000
--- a/odb/processor.cxx
+++ /dev/null
@@ -1,3237 +0,0 @@
-// file : odb/processor.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <odb/gcc.hxx>
-
-#include <iostream>
-#include <algorithm> // std::find
-
-#include <odb/common.hxx>
-#include <odb/lookup.hxx>
-#include <odb/context.hxx>
-#include <odb/cxx-lexer.hxx>
-#include <odb/processor.hxx>
-#include <odb/diagnostics.hxx>
-
-#include <odb/relational/processor.hxx>
-
-using namespace std;
-
-namespace
-{
- // Find name hint for this type decl.
- //
- static semantics::names*
- find_hint (semantics::unit& u, tree decl)
- {
- semantics::names* r (0);
-
- for (tree ot (DECL_ORIGINAL_TYPE (decl));
- ot != 0;
- ot = decl ? DECL_ORIGINAL_TYPE (decl) : 0)
- {
- if ((r = u.find_hint (ot)))
- break;
-
- decl = TYPE_NAME (ot);
- }
-
- return r;
- }
-
- // Indirect (dynamic) context values.
- //
- static semantics::type*
- id_tree_type (semantics::names*& hint)
- {
- context& c (context::current ());
- data_member_path& id (*context::id_member (*c.top_object));
- return &c.utype (id, hint);
- }
-
- struct data_member: traversal::data_member, context
- {
- virtual void
- traverse (semantics::data_member& m)
- {
- if (transient (m))
- return;
-
- semantics::names* hint;
- semantics::type& t (utype (m, hint));
-
- // See if this member is a section.
- //
- if (t.fq_name () == "::odb::section")
- {
- using semantics::class_;
-
- class_& c (dynamic_cast<class_&> (m.scope ()));
- class_* poly_root (polymorphic (c));
- semantics::data_member* opt (optimistic (c));
-
- // If we have sections in a polymorphic optimistic hierarchy,
- // then the version member should be in the root.
- //
- if (poly_root == &c && opt != 0 && &opt->scope () != &c)
- {
- error (m.location ()) << "version must be a direct data member " <<
- "of a class that contains sections" << endl;
- info (opt->location ()) << "version member is declared here" << endl;
- throw operation_failed ();
- }
-
- process_user_section (m, c);
-
- // We don't need a modifier but the accessor should be by-reference.
- //
- process_access (m, "get");
-
- member_access& ma (m.get<member_access> ("get"));
- if (ma.by_value)
- {
- error (ma.loc) << "accessor returning a value cannot be used "
- << "for a section" << endl;
- info (ma.loc) << "accessor returning a const reference is required"
- << endl;
- info (m.location ()) << "data member is defined here" << endl;
- throw operation_failed ();
- }
-
- // Mark this member as transient since we don't store it in the
- // database.
- //
- m.set ("transient", true);
-
- features.section = true;
- return;
- }
- else
- {
- process_access (m, "get");
- process_access (m, "set");
- }
-
- // See if this member belongs to a section.
- //
- if (m.count ("section-member") != 0)
- process_section_member (m);
-
- // We don't need to do any further processing for common if we
- // are generating static multi-database code.
- //
- if (multi_static && options.database ()[0] == database::common)
- return;
-
- // Handle wrappers.
- //
- semantics::type* wt (0), *qwt (0);
- semantics::names* whint (0);
- if (process_wrapper (t))
- {
- qwt = t.get<semantics::type*> ("wrapper-type");
- whint = t.get<semantics::names*> ("wrapper-hint");
- wt = &utype (*qwt, whint);
- }
-
- // If the type is const and the member is not id, version, or
- // inverse, then mark it as readonly. In case of a wrapper,
- // both the wrapper type and the wrapped type must be const.
- // To see why, consider these possibilities:
- //
- // unique_ptr<const T> - can modify by setting a new pointer
- // const unique_ptr<T> - can modify by changing the pointed-to value
- //
- if (const_member (m) && !(id (m) || version (m) || m.count ("inverse")))
- {
- if (qwt == 0 || const_type (*qwt))
- m.set ("readonly", true);
- }
-
- process_points_to (m);
-
- if (composite_wrapper (t))
- return;
-
- // Process object pointer. The resulting column will be a simple
- // or composite value.
- //
- if (process_object_pointer (m, t))
- return;
-
- // Before checking if this is a container, check if this member
- // or its type were deduced to be a simple value based on the
- // pragmas. This is necessary because a container member (e.g.,
- // vector<char>) can be "overridden" into a simple value (e.g.,
- // BLOB) with a pragma.
- //
- if (m.count ("simple") ||
- t.count ("simple") ||
- (wt != 0 && wt->count ("simple")))
- return;
-
- process_container (m, (wt != 0 ? *wt : t));
- }
-
- //
- // Process member access expressions.
- //
-
- enum found_type
- {
- found_none,
- found_some, // Found something but keep looking for a better one.
- found_best
- };
-
- // Check if a function is a suitable accessor for this member.
- //
- found_type
- check_accessor (semantics::data_member& m,
- tree f,
- string const& n,
- member_access& ma,
- bool strict)
- {
- // Must be const.
- //
- if (!DECL_CONST_MEMFUNC_P (f))
- return found_none;
-
- // Accessor is a function with no arguments (other than 'this').
- //
- if (FUNCTION_FIRST_USER_PARMTYPE (f) != void_list_node)
- return found_none;
-
- // Note that to get the return type we have to use
- // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as
- // suggested in the documentation.
- //
- tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f))));
- gcc_tree_code_type tc (TREE_CODE (r));
-
- // In the strict mode make sure the function returns for non-array
- // types a value or a (const) reference to the member type and for
- // array types a (const) pointer to element type. In the lax mode
- // we just check that the return value is not void.
- //
- if (strict)
- {
- semantics::type& t (utype (m));
- semantics::array* ar (dynamic_cast<semantics::array*> (&t));
-
- if (ar != 0 && tc != POINTER_TYPE)
- return found_none;
-
- tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (r) : r);
- tree bt_mv (TYPE_MAIN_VARIANT (bt));
-
- if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv)
- return found_none;
- }
- else if (r == void_type_node)
- return found_none;
-
- cxx_tokens& e (ma.expr);
- e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
- e.push_back (cxx_token (0, CPP_DOT));
- e.push_back (cxx_token (0, CPP_NAME, n));
- e.push_back (cxx_token (0, CPP_OPEN_PAREN));
- e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
-
- // See if it returns by value.
- //
- ma.by_value = (tc != REFERENCE_TYPE && tc != POINTER_TYPE);
-
- return found_best;
- }
-
- // Check if a function is a suitable modifier for this member.
- //
- found_type
- check_modifier (semantics::data_member& m,
- tree f,
- string const& n,
- member_access& ma,
- bool strict)
- {
- tree a (FUNCTION_FIRST_USER_PARMTYPE (f));
-
- // For a modifier, it can either be a function that returns a non-
- // const reference (or non-const pointer, in case the member is an
- // array) or a by-value modifier that sets a new value. If both are
- // available, we prefer the former for efficiency.
- //
- cxx_tokens& e (ma.expr);
- semantics::type& t (utype (m));
- semantics::array* ar (dynamic_cast<semantics::array*> (&t));
-
- if (a == void_list_node)
- {
- // Note that to get the return type we have to use
- // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as
- // suggested in the documentation.
- //
- tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f))));
- gcc_tree_code_type tc (TREE_CODE (r));
-
- // By-reference modifier. Should return a reference or a pointer.
- //
- if (tc != (ar != 0 ? POINTER_TYPE : REFERENCE_TYPE))
- return found_none;
-
- // The base type should not be const and, in strict mode, should
- // match the member type.
- //
- tree bt (TREE_TYPE (r));
-
- if (CP_TYPE_CONST_P (bt))
- return found_none;
-
- tree bt_mv (TYPE_MAIN_VARIANT (bt));
-
- if (strict && (ar != 0 ? ar->base_type () : t).tree_node () != bt_mv)
- return found_none;
-
- e.clear (); // Could contain by value modifier.
- e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
- e.push_back (cxx_token (0, CPP_DOT));
- e.push_back (cxx_token (0, CPP_NAME, n));
- e.push_back (cxx_token (0, CPP_OPEN_PAREN));
- e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
-
- return found_best;
- }
- // Otherwise look for a by value modifier, which is a function
- // with a single argument.
- //
- else if (TREE_CHAIN (a) == void_list_node)
- {
- // In the lax mode any function with a single argument works
- // for us. And we don't care what it returns.
- //
- if (strict)
- {
- // In the strict mode make sure the argument matches the
- // member. This is exactly the same logic as in accessor
- // with regards to arrays, references, etc.
- //
- tree at (TREE_VALUE (a));
- gcc_tree_code_type tc (TREE_CODE (at));
-
- if (ar != 0 && tc != POINTER_TYPE)
- return found_none;
-
- tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (at) : at);
- tree bt_mv (TYPE_MAIN_VARIANT (bt));
-
- if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv)
- return found_none;
- }
-
- if (e.empty ())
- {
- e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
- e.push_back (cxx_token (0, CPP_DOT));
- e.push_back (cxx_token (0, CPP_NAME, n));
- e.push_back (cxx_token (0, CPP_OPEN_PAREN));
- e.push_back (cxx_token (0, CPP_QUERY));
- e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
-
- // Continue searching in case there is version that returns a
- // non-const reference which we prefer for efficiency.
- //
- return found_some;
- }
- else
- return found_none; // We didn't find anything better.
- }
-
- return found_none;
- }
-
- void
- process_access (semantics::data_member& m, std::string const& k)
- {
- bool virt (m.count ("virtual"));
-
- // Ignore certain special virtual members.
- //
- if (virt && (m.count ("polymorphic-ref") || m.count ("discriminator")))
- return;
-
- char const* kind (k == "get" ? "accessor" : "modifier");
- semantics::class_& c (dynamic_cast<semantics::class_&> (m.scope ()));
-
- // If we don't have an access expression, try to come up with
- // one.
- //
- if (!m.count (k))
- {
- found_type found (found_none);
- semantics::access const& a (m.named ().access ());
- member_access& ma (
- m.set (
- k, member_access (m.location (), kind, true)));
-
- // If this member is not virtual and is either public or if we
- // are a friend of this class, then go for the member directly.
- //
- if (!virt && (a == semantics::access::public_ ||
- c.get<bool> ("friend")))
- {
- ma.expr.push_back (cxx_token (0, CPP_KEYWORD, "this"));
- ma.expr.push_back (cxx_token (0, CPP_DOT));
- ma.expr.push_back (cxx_token (0, CPP_NAME, m.name ()));
- found = found_best;
- }
-
- // Otherwise, try to find a suitable accessor/modifier.
- //
-
- // First try the original name. If that doesn't produce anything,
- // then try the public name.
- //
- bool t (k == "get"
- ? options.accessor_regex_trace ()
- : options.modifier_regex_trace ());
- regex_mapping const& re (
- k == "get" ? accessor_regex : modifier_regex);
-
- for (unsigned short j (0); found != found_best && j != 2; ++j)
- {
- string b (j == 0 ? m.name () : public_name (m, false));
-
- // Skip the second pass if original and public names are the same.
- //
- if (j == 1 && b == m.name ())
- continue;
-
- if (t)
- cerr << kind << (j == 0 ? " original" : " public")
- << " name '" << b << "'" << endl;
-
- for (regex_mapping::const_iterator i (re.begin ());
- found != found_best && i != re.end ();
- ++i)
- {
- if (t)
- cerr << "try: '" << i->regex () << "' : ";
-
- if (!i->match (b))
- {
- if (t)
- cerr << '-' << endl;
- continue;
- }
-
- string n (i->replace (b));
-
- if (t)
- cerr << "'" << n << "' : ";
-
- tree decl (
- lookup_qualified_name (
- c.tree_node (), get_identifier (n.c_str ()), false, false));
-
- if (decl == error_mark_node || TREE_CODE (decl) != BASELINK)
- {
- if (t)
- cerr << '-' << endl;
- continue;
- }
-
- // OVL_* macros work for both FUNCTION_DECL and OVERLOAD.
- //
-#if BUILDING_GCC_MAJOR >= 8
- for (ovl_iterator i (BASELINK_FUNCTIONS (decl)); i; ++i)
-#else
- for (tree o (BASELINK_FUNCTIONS (decl)); o != 0; o = OVL_NEXT (o))
-#endif
- {
-#if BUILDING_GCC_MAJOR >= 8
- tree f (*i);
-#else
- tree f (OVL_CURRENT (o));
-#endif
- // We are only interested in public non-static member
- // functions. Note that TREE_PUBLIC() returns something
- // other than what we need.
- //
- if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (f) ||
- TREE_PRIVATE (f) || TREE_PROTECTED (f))
- continue;
-
- found_type r (k == "get"
- ? check_accessor (m, f, n, ma, true)
- : check_modifier (m, f, n, ma, true));
-
- if (r != found_none)
- {
- // Update the location of the access expression to point
- // to this function.
- //
- ma.loc = location (real_source_location (f));
- found = r;
- }
- }
-
- if (t)
- cerr << (found != found_none ? '+' : '-') << endl;
- }
- }
-
- // If that didn't work then the generated code won't be able
- // to access this member.
- //
- if (found == found_none)
- {
- location const& l (m.location ());
-
- if (virt)
- {
- error (l) << "no suitable " << kind << " function could be "
- << "automatically found for virtual data member '"
- << m.name () << "'" << endl;
-
- info (l) << "use '#pragma db " << k << "' to explicitly "
- << "specify the " << kind << " function or "
- << "expression" << endl;
- }
- else
- {
- error (l) << "data member '" << m.name () << "' is "
- << a.string () << " and no suitable " << kind
- << " function could be automatically found" << endl;
-
- info (l) << "consider making class 'odb::access' a friend of "
- << "class '" << class_name (c) << "'" << endl;
-
- info (l) << "or use '#pragma db " << k << "' to explicitly "
- << "specify the " << kind << " function or "
- << "expression" << endl;
- }
-
- throw operation_failed ();
- }
- }
-
- member_access& ma (m.get<member_access> (k));
-
- if (ma.empty ())
- return;
-
- cxx_tokens& e (ma.expr);
-
- // If it is just a name, resolve it and convert to an appropriate
- // expression.
- //
- if (e.size () == 1 && e.back ().type == CPP_NAME)
- {
- string n (e.back ().literal);
- e.clear ();
-
- tree decl (
- lookup_qualified_name (
- c.tree_node (), get_identifier (n.c_str ()), false, false));
-
- if (decl == error_mark_node)
- {
- error (ma.loc) << "unable to resolve data member or function "
- << "name '" << n << "'" << endl;
- throw operation_failed ();
- }
-
- switch (TREE_CODE (decl))
- {
- case FIELD_DECL:
- {
- e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
- e.push_back (cxx_token (0, CPP_DOT));
- e.push_back (cxx_token (0, CPP_NAME, n));
- break;
- }
- case BASELINK:
- {
- // OVL_* macros work for both FUNCTION_DECL and OVERLOAD.
- //
-#if BUILDING_GCC_MAJOR >= 8
- for (ovl_iterator i (BASELINK_FUNCTIONS (decl)); i; ++i)
-#else
- for (tree o (BASELINK_FUNCTIONS (decl)); o != 0; o = OVL_NEXT (o))
-#endif
- {
-#if BUILDING_GCC_MAJOR >= 8
- tree f (*i);
-#else
- tree f (OVL_CURRENT (o));
-#endif
- // We are only interested in non-static member functions.
- //
- if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (f))
- continue;
-
- if ((k == "get"
- ? check_accessor (m, f, n, ma, false)
- : check_modifier (m, f, n, ma, false)) == found_best)
- break;
- }
-
- if (e.empty ())
- {
- error (ma.loc) << "unable to find suitable " << kind
- << " function '" << n << "'" << endl;
- throw operation_failed ();
- }
- break;
- }
- default:
- {
- error (ma.loc) << "name '" << n << "' does not refer to a data "
- << "member or function" << endl;
- throw operation_failed ();
- }
- }
- }
-
- // If there is no 'this' keyword, then add it as a prefix.
- //
- {
- bool t (false);
- for (cxx_tokens::iterator i (e.begin ()); i != e.end (); ++i)
- {
- if (i->type == CPP_KEYWORD && i->literal == "this")
- {
- t = true;
- break;
- }
- }
-
- if (!t)
- {
- e.insert (e.begin (), cxx_token (0, CPP_DOT));
- e.insert (e.begin (), cxx_token (0, CPP_KEYWORD, "this"));
- }
- }
-
- // Check that there is no placeholder in the accessor expression.
- //
- if (k == "get" && ma.placeholder ())
- {
- error (ma.loc) << "(?) placeholder in the accessor expression"
- << endl;
- throw operation_failed ();
- }
-
- // Check that the member type is default-constructible if we
- // have a by value modifier.
- //
- if (k == "set" && ma.placeholder ())
- {
- semantics::class_* c (dynamic_cast<semantics::class_*> (&utype (m)));
-
- // Assume all other types are default-constructible.
- //
- if (c != 0)
- {
- // If this type is a class template instantiation, then make
- // sure it is instantiated. While types used in real members
- // will be instantiated, this is not necessarily the case for
- // virtual members. Without the instantiation we won't be able
- // to detect whether the type has the default ctor.
- //
- // It would have been cleaner to do it in post_process_pragmas()
- // but there we don't yet know whether we need the default ctor.
- // And it is a good idea not to require instantiability unless
- // we really need it.
- //
- tree type (c->tree_node ());
-
- if (!COMPLETE_TYPE_P (type) &&
- CLASSTYPE_TEMPLATE_INSTANTIATION (type))
- {
- // Reset input location so that we get nice diagnostics in
- // case of an error. Use the location of the virtual pragma.
- //
- location_t loc (m.get<location_t> ("virtual-location"));
- input_location = loc;
-
- if (instantiate_class_template (type) == error_mark_node ||
- errorcount != 0 ||
- !COMPLETE_TYPE_P (type))
- {
- error (loc) << "unable to instantiate virtual data member " <<
- "type" << endl;
- throw operation_failed ();
- }
- }
-
- if (!c->default_ctor ())
- {
- error (ma.loc) << "modifier expression requires member type " <<
- "to be default-constructible" << endl;
- throw operation_failed ();
- }
- }
- }
- }
-
- //
- // Process section.
- //
-
- user_section&
- process_user_section (semantics::data_member& m, semantics::class_& c)
- {
- user_sections& uss (c.get<user_sections> ("user-sections"));
-
- user_section::load_type l (
- m.get ("section-load", user_section::load_eager));
-
- user_section::update_type u (
- m.get ("section-update", user_section::update_always));
-
- if (l == user_section::load_eager && u == user_section::update_always)
- {
- location const& l (m.location ());
-
- error (l) << "eager-loaded, always-updated section is pointless"
- << endl;
-
- info (l) << "use '#pragma db load' and/or '#pragma db update' to "
- "specify an alternative loading and/or updating strategy" << endl;
-
- info (l) << "or remove the section altogether" << endl;
-
- throw operation_failed ();
- }
-
- size_t n (uss.count (user_sections::count_total |
- user_sections::count_all |
- user_sections::count_special_version));
- user_section us (m, c, n, l, u);
-
- // We may already have seen this section (e.g., forward reference
- // from a member of this section).
- //
- user_sections::iterator i (find (uss.begin (), uss.end (), us));
-
- if (i != uss.end ())
- return *i;
-
- // If we are adding a new section to an optimistic class with
- // version in a base, make sure the base is sectionable.
- //
- semantics::data_member* opt (optimistic (c));
- if (opt != 0 && &opt->scope () != &c)
- {
- semantics::class_* poly_root (polymorphic (c));
- semantics::node* base (poly_root ? poly_root : &opt->scope ());
-
- if (!base->count ("sectionable"))
- {
- error (m.location ()) << "adding new section to a derived class " <<
- "in an optimistic hierarchy requires sectionable base class" <<
- endl;
-
- info (base->location ()) << "use '#pragma db object sectionable' " <<
- "to make the base class of this hierarchy sectionable" << endl;
-
- throw operation_failed ();
- }
- }
-
- uss.push_back (us);
- return uss.back ();
- }
-
- void
- process_section_member (semantics::data_member& m)
- {
- using semantics::class_;
- using semantics::data_member;
-
- string name (m.get<string> ("section-member"));
- location_t loc (m.get<location_t> ("section-member-location"));
- class_& c (dynamic_cast<class_&> (m.scope ()));
-
- class_* poly_root (polymorphic (c));
- bool poly_derived (poly_root != 0 && poly_root != &c);
-
- try
- {
- data_member& us (c.lookup<data_member> (name, class_::include_hidden));
-
- // Make sure we are referencing a section.
- //
- if (utype (us).fq_name () != "::odb::section")
- {
- error (loc) << "data member '" << name << "' in '#pragma db " <<
- "section' is not of the odb::section type" << endl;
- throw operation_failed ();
- }
-
- // If the section is in the base, handle polymorphic inheritance.
- //
- class_& b (dynamic_cast<class_&> (us.scope ()));
- user_section* s (0);
-
- if (&c != &b && poly_derived)
- {
- user_sections& uss (c.get<user_sections> ("user-sections"));
-
- // This is a section override. See if we have already handled
- // this section.
- //
- for (user_sections::iterator i (uss.begin ());
- s == 0 && i != uss.end ();
- ++i)
- {
- if (i->member == &us)
- s = &*i;
- }
-
- // Otherwise, find and copy the nearest override in the base.
- // The result should be a chain of overrides leading all the
- // way to the original section.
- //
- if (s == 0)
- {
- for (class_* b (&polymorphic_base (c));;
- b = &polymorphic_base (*b))
- {
- user_sections& buss (b->get<user_sections> ("user-sections"));
-
- for (user_sections::iterator i (buss.begin ());
- s == 0 && i != buss.end ();
- ++i)
- {
- if (i->member == &us)
- {
- uss.push_back (*i);
- uss.back ().object = &c;
- uss.back ().base = &*i;
- s = &uss.back ();
- }
- }
-
- if (s != 0)
- break;
-
- assert (b != poly_root); // We should have found it by now.
- }
- }
- }
- else
- s = &process_user_section (us, c);
-
- // Mark the member as added/deleted if the section is added/deleted.
- // Also check that the version ordering is correct.
- //
- if (unsigned long long sav = added (*s->member))
- {
- location_t sl (s->member->get<location_t> ("added-location"));
-
- if (unsigned long long mav = added (m))
- {
- location_t ml (m.get<location_t> ("added-location"));
-
- if (mav < sav)
- {
- error (ml) << "member addition version is less than the " <<
- "section addition version" << endl;
- info (sl) << "section addition version is specified here" <<
- endl;
- throw operation_failed ();
- }
-
- if (mav == sav)
- {
- error (ml) << "member addition version is the same as " <<
- "section addition version" << endl;
- info (sl) << "section addition version is specified here" <<
- endl;
- info (ml) << "delete this pragma" << endl;
- throw operation_failed ();
- }
- }
- else
- {
- m.set ("added", sav);
- m.set ("added-location", sl);
- }
- }
-
- if (unsigned long long sdv = deleted (*s->member))
- {
- location_t sl (s->member->get<location_t> ("deleted-location"));
-
- if (unsigned long long mdv = deleted (m))
- {
- location_t ml (m.get<location_t> ("deleted-location"));
-
- if (mdv > sdv)
- {
- error (ml) << "member deletion version is greater than the " <<
- "section deletion version" << endl;
- info (sl) << "section deletion version is specified here" <<
- endl;
- throw operation_failed ();
- }
-
- if (mdv == sdv)
- {
- error (ml) << "member deletion version is the same as " <<
- "section deletion version" << endl;
- info (sl) << "section deletion version is specified here" <<
- endl;
- info (ml) << "delete this pragma" << endl;
- throw operation_failed ();
- }
- }
- else
- {
- m.set ("deleted", sdv);
- m.set ("deleted-location", sl);
- }
- }
-
- // Insert as object_section.
- //
- m.set ("section", static_cast<object_section*> (s));
- }
- catch (semantics::unresolved const& e)
- {
- if (e.type_mismatch)
- error (loc) << "name '" << name << "' in '#pragma db section' " <<
- "does not refer to a data member" << endl;
- else
- error (loc) << "unable to resolve data member '" << name << "' " <<
- "specified with '#pragma db section'" << endl;
-
- throw operation_failed ();
- }
- catch (semantics::ambiguous const& e)
- {
- error (loc) << "data member name '" << name << "' specified " <<
- "with '#pragma db section' is ambiguous" << endl;
-
- info (e.first.named ().location ()) << "could resolve to this " <<
- "data member" << endl;
-
- info (e.second.named ().location ()) << "or could resolve to " <<
- "this data member" << endl;
-
- throw operation_failed ();
- }
- }
-
- //
- // Process wrapper.
- //
-
- bool
- process_wrapper (semantics::type& t)
- {
- if (t.count ("wrapper"))
- return t.get<bool> ("wrapper");
-
- // Check this type with wrapper_traits.
- //
- tree inst (instantiate_template (wrapper_traits_, t.tree_node ()));
-
- if (inst == 0)
- {
- t.set ("wrapper", false);
- return false;
- }
-
- // @@ This points to the primary template, not the specialization.
- //
- tree decl (TYPE_NAME (inst));
-
- string f (DECL_SOURCE_FILE (decl));
- size_t l (DECL_SOURCE_LINE (decl));
- size_t c (DECL_SOURCE_COLUMN (decl));
-
- // Get the wrapped type.
- //
- try
- {
- tree decl (
- lookup_qualified_name (
- inst, get_identifier ("wrapped_type"), true, false));
-
- if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
- throw operation_failed ();
-
- // The wrapped_type alias is a typedef in an instantiation
- // that we just instantiated dynamically. As a result there
- // is no semantic graph edges corresponding to this typedef
- // since we haven't parsed it yet (unless it was instantiated
- // explicitly by the user; see below). So to get the tree node
- // that can actually be resolved to the graph node, we use
- // the source type of this typedef.
- //
- tree type (DECL_ORIGINAL_TYPE (decl));
-
- bool qc (CP_TYPE_CONST_P (type));
- bool qv (CP_TYPE_VOLATILE_P (type));
- bool qr (CP_TYPE_RESTRICT_P (type));
-
- type = TYPE_MAIN_VARIANT (type);
- semantics::type* wt (
- dynamic_cast<semantics::type*> (unit.find (type)));
-
- // Object pointers and wrappers often use the same smart
- // pointers so check if the wrapped type is an object.
- //
- if (object (*wt))
- {
- t.set ("wrapper", false);
- return false;
- }
-
- if (qc || qv || qr)
- {
- for (semantics::type::qualified_iterator i (wt->qualified_begin ());
- i != wt->qualified_end (); ++i)
- {
- semantics::qualifier& q (i->qualifier ());
-
- if (q.const_ () == qc &&
- q.volatile_ () == qv &&
- q.restrict_ () == qr)
- {
- wt = &q;
- break;
- }
- }
- }
-
- // Find the hint.
- //
- // If we can't find any, then try to fallback to the wrapped_type
- // alias inside wrapper_traits. This requires an explicit
- // wrapper_traits instantiation (see above).
- //
- semantics::names* wh (find_hint (unit, decl));
-
- if (wh == nullptr)
- wh = unit.find_hint (TREE_TYPE (decl));
-
- t.set ("wrapper-type", wt);
- t.set ("wrapper-hint", wh);
- }
- catch (operation_failed const&)
- {
- os << f << ":" << l << ":" << c << ": error: "
- << "wrapper_traits specialization does not define the "
- << "wrapped_type type" << endl;
- throw;
- }
-
- // Get the null_handler flag.
- //
- bool null_handler (false);
-
- try
- {
- tree nh (
- lookup_qualified_name (
- inst, get_identifier ("null_handler"), false, false));
-
- if (nh == error_mark_node || TREE_CODE (nh) != VAR_DECL)
- throw operation_failed ();
-
- // Instantiate this decalaration so that we can get its value.
- //
- if (DECL_TEMPLATE_INSTANTIATION (nh) &&
- !DECL_TEMPLATE_INSTANTIATED (nh) &&
- !DECL_EXPLICIT_INSTANTIATION (nh))
- instantiate_decl (nh, false, false);
-
- tree init (DECL_INITIAL (nh));
-
- if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
- throw operation_failed ();
-
- null_handler = static_cast<bool> (integer_value (init));
- t.set ("wrapper-null-handler", null_handler);
- }
- catch (operation_failed const&)
- {
- os << f << ":" << l << ":" << c << ": error: "
- << "wrapper_traits specialization does not define the "
- << "null_handler constant" << endl;
- throw;
- }
-
- // Get the null_default flag.
- //
- if (null_handler)
- {
- try
- {
- tree nh (
- lookup_qualified_name (
- inst, get_identifier ("null_default"), false, false));
-
- if (nh == error_mark_node || TREE_CODE (nh) != VAR_DECL)
- throw operation_failed ();
-
- // Instantiate this decalaration so that we can get its value.
- //
- if (DECL_TEMPLATE_INSTANTIATION (nh) &&
- !DECL_TEMPLATE_INSTANTIATED (nh) &&
- !DECL_EXPLICIT_INSTANTIATION (nh))
- instantiate_decl (nh, false, false);
-
- tree init (DECL_INITIAL (nh));
-
- if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
- throw operation_failed ();
-
- t.set ("wrapper-null-default",
- static_cast<bool> (integer_value (init)));
- }
- catch (operation_failed const&)
- {
- os << f << ":" << l << ":" << c << ": error: "
- << "wrapper_traits specialization does not define the "
- << "null_default constant" << endl;
- throw;
- }
- }
-
- // Check if the wrapper is a TR1 template instantiation.
- //
- if (tree ti = TYPE_TEMPLATE_INFO (t.tree_node ()))
- {
- tree decl (TI_TEMPLATE (ti)); // DECL_TEMPLATE
-
- // Get to the most general template declaration.
- //
- while (DECL_TEMPLATE_INFO (decl))
- decl = DECL_TI_TEMPLATE (decl);
-
- bool& tr1 (features.tr1_pointer);
- bool& boost (features.boost_pointer);
-
- string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER));
-
- // In case of a boost TR1 implementation, we cannot distinguish
- // between the boost:: and std::tr1:: usage since the latter is
- // just a using-declaration for the former.
- //
- tr1 = tr1
- || n.compare (0, 8, "std::tr1") == 0
- || n.compare (0, 10, "::std::tr1") == 0;
-
- boost = boost
- || n.compare (0, 17, "boost::shared_ptr") == 0
- || n.compare (0, 19, "::boost::shared_ptr") == 0;
- }
-
- t.set ("wrapper", true);
- return true;
- }
-
- //
- // Process object pointer.
- //
-
- semantics::class_*
- process_object_pointer (semantics::data_member& m,
- semantics::type& t,
- string const& kp = string ())
- {
- using semantics::class_;
- using semantics::data_member;
-
- class_* c (0);
-
- // The overall idea is as follows: try to instantiate the pointer
- // traits class template. If we are successeful, then get the
- // element type and see if it is an object.
- //
- if (t.count ("element-type"))
- c = t.get<class_*> ("element-type");
- else
- {
- tree inst (instantiate_template (pointer_traits_, t.tree_node ()));
-
- if (inst == 0)
- return 0;
-
- // @@ This points to the primary template, not the specialization.
- //
- tree decl (TYPE_NAME (inst));
-
- string fl (DECL_SOURCE_FILE (decl));
- size_t ln (DECL_SOURCE_LINE (decl));
- size_t cl (DECL_SOURCE_COLUMN (decl));
-
- // Get the element type.
- //
- tree tn (0);
- try
- {
- tree decl (
- lookup_qualified_name (
- inst, get_identifier ("element_type"), true, false));
-
- if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
- throw operation_failed ();
-
- tn = TYPE_MAIN_VARIANT (TREE_TYPE (decl));
-
- // Check if the pointer is a TR1 template instantiation.
- //
- if (tree ti = TYPE_TEMPLATE_INFO (t.tree_node ()))
- {
- decl = TI_TEMPLATE (ti); // DECL_TEMPLATE
-
- // Get to the most general template declaration.
- //
- while (DECL_TEMPLATE_INFO (decl))
- decl = DECL_TI_TEMPLATE (decl);
-
- bool& tr1 (features.tr1_pointer);
- bool& boost (features.boost_pointer);
-
- string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER));
-
- // In case of a boost TR1 implementation, we cannot distinguish
- // between the boost:: and std::tr1:: usage since the latter is
- // just a using-declaration for the former.
- //
- tr1 = tr1
- || n.compare (0, 8, "std::tr1") == 0
- || n.compare (0, 10, "::std::tr1") == 0;
-
- boost = boost
- || n.compare (0, 17, "boost::shared_ptr") == 0
- || n.compare (0, 19, "::boost::shared_ptr") == 0;
- }
- }
- catch (operation_failed const&)
- {
- os << fl << ":" << ln << ":" << cl << ": error: pointer_traits "
- << "specialization does not define the 'element_type' type"
- << endl;
- throw;
- }
-
- c = dynamic_cast<class_*> (unit.find (tn));
-
- if (c == 0 || !object (*c))
- return 0;
-
- t.set ("element-type", c);
-
- // Determine the pointer kind.
- //
- try
- {
- tree kind (
- lookup_qualified_name (
- inst, get_identifier ("kind"), false, false));
-
- if (kind == error_mark_node || TREE_CODE (kind) != VAR_DECL)
- throw operation_failed ();
-
- // Instantiate this decalaration so that we can get its value.
- //
- if (DECL_TEMPLATE_INSTANTIATION (kind) &&
- !DECL_TEMPLATE_INSTANTIATED (kind) &&
- !DECL_EXPLICIT_INSTANTIATION (kind))
- instantiate_decl (kind, false, false);
-
- tree init (DECL_INITIAL (kind));
-
- if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
- throw operation_failed ();
-
- pointer_kind_type pk = static_cast<pointer_kind_type> (
- integer_value (init));
- t.set ("pointer-kind", pk);
- }
- catch (operation_failed const&)
- {
- os << fl << ":" << ln << ":" << cl << ": error: pointer_traits "
- << "specialization does not define the 'kind' constant" << endl;
- throw;
- }
-
- // Get the lazy flag.
- //
- try
- {
- tree lazy (
- lookup_qualified_name (
- inst, get_identifier ("lazy"), false, false));
-
- if (lazy == error_mark_node || TREE_CODE (lazy) != VAR_DECL)
- throw operation_failed ();
-
- // Instantiate this decalaration so that we can get its value.
- //
- if (DECL_TEMPLATE_INSTANTIATION (lazy) &&
- !DECL_TEMPLATE_INSTANTIATED (lazy) &&
- !DECL_EXPLICIT_INSTANTIATION (lazy))
- instantiate_decl (lazy, false, false);
-
- tree init (DECL_INITIAL (lazy));
-
- if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
- throw operation_failed ();
-
- t.set ("pointer-lazy", static_cast<bool> (integer_value (init)));
- }
- catch (operation_failed const&)
- {
- os << fl << ":" << ln << ":" << cl << ": error: pointer_traits "
- << "specialization does not define the 'kind' constant" << endl;
- throw;
- }
- }
-
- // See if this is the inverse side of a bidirectional relationship.
- // If so, then resolve the member path and cache it in the context.
- //
- if (m.count ("inverse"))
- {
- string name (m.get<string> ("inverse"));
- location_t l (m.get<location_t> ("inverse-location"));
- data_member_path mp (resolve_data_members (*c, name, l, lex_));
-
- {
- string tl;
- data_member& m (*mp.back ());
-
- if (container (m) && lex_.next (tl) != CPP_EOF)
- {
- error (l) << "unexpect name after container member " <<
- m.name () << " in '#pragma db inverse'" << endl;
- throw operation_failed ();
- }
- }
-
- // Validate each member.
- //
- for (data_member_path::iterator i (mp.begin ()); i != mp.end (); ++i)
- {
- data_member& im (**i);
- const string& n (im.name ());
-
- if (im.count ("transient"))
- {
- error (l) << "data member '" << n << "' specified with " <<
- "'#pragma db inverse' is transient" << endl;
- info (im.location ()) << "data member '" << n << "' is " <<
- "defined here" << endl;
- throw operation_failed ();
- }
-
- if (im.count ("inverse") || im.count ("value-inverse"))
- {
- error (l) << "data member '" << n << "' specified with " <<
- "'#pragma db inverse' is itself inverse" << endl;
- info (im.location ()) << "data member '" << n << "' is " <<
- "defined here" << endl;
- throw operation_failed ();
- }
- }
-
- // @@ Would be good to check that the other end is actually
- // an object pointer/points_to and points to the correct
- // object. But the other class may not have been processed
- // yet. Need to do in validator, pass 2.
- //
- m.remove ("inverse");
- m.set (kp + (kp.empty () ? "": "-") + "inverse", mp);
- }
-
- return c;
- }
-
- //
- // Process points-to pragma.
- //
-
- void
- process_points_to (semantics::data_member& m,
- string const& /*kp*/ = string ())
- {
- if (!m.count ("points-to"))
- return;
-
- using semantics::class_;
-
- tree t (m.get<tree> ("points-to"));
- location_t l (m.get<location_t> ("points-to-location"));
-
- class_* c (dynamic_cast<class_*> (unit.find (t)));
-
- if (c == 0 || !object (*c))
- {
- error (l) << "name specified with '#pragma db points_to' does "
- << "not refer to an object" << endl;
- throw operation_failed ();
- }
-
- m.remove ("points-to");
- m.set (/*kp + (kp.empty () ? "": "-") + */"points-to", c);
- }
-
- //
- // Process container.
- //
-
- void
- process_container_value (semantics::type& t,
- semantics::data_member& m,
- string const& prefix,
- bool obj_ptr)
- {
- process_wrapper (t);
-
- if (composite_wrapper (t))
- return;
-
- if (obj_ptr)
- process_object_pointer (m, t, prefix);
- }
-
- bool
- process_container (semantics::data_member& m, semantics::type& t)
- {
- // The overall idea is as follows: try to instantiate the container
- // traits class template. If we are successeful, then this is a
- // container type and we can extract the various information from
- // the instantiation. Otherwise, this is not a container.
- //
- location ml (m.location ());
-
- container_kind_type ck;
- bool smart;
- semantics::type* vt (0);
- semantics::type* it (0);
- semantics::type* kt (0);
-
- semantics::names* vh (0);
- semantics::names* ih (0);
- semantics::names* kh (0);
-
- if (t.count ("container-kind"))
- {
- ck = t.get<container_kind_type> ("container-kind");
- smart = t.get<bool> ("container-smart");
-
- vt = &utype (m, vh, "value");
-
- if (ck == ck_ordered)
- it = &utype (m, ih, "index");
-
- if (ck == ck_map || ck == ck_multimap)
- kt = &utype (m, kh, "key");
- }
- else
- {
- tree inst (instantiate_template (container_traits_, t.tree_node ()));
-
- if (inst == 0)
- return false;
-
- // @@ This points to the primary template, not the specialization.
- //
- tree decl (TYPE_NAME (inst));
-
- string f (DECL_SOURCE_FILE (decl));
- size_t l (DECL_SOURCE_LINE (decl));
- size_t c (DECL_SOURCE_COLUMN (decl));
-
- // Determine the container kind.
- //
- try
- {
- tree decl (
- lookup_qualified_name (
- inst, get_identifier ("kind"), false, false));
-
- if (decl == error_mark_node || TREE_CODE (decl) != VAR_DECL)
- throw operation_failed ();
-
- // Instantiate this decalaration so that we can get its value.
- //
- if (DECL_TEMPLATE_INSTANTIATION (decl) &&
- !DECL_TEMPLATE_INSTANTIATED (decl) &&
- !DECL_EXPLICIT_INSTANTIATION (decl))
- instantiate_decl (decl, false, false);
-
- tree init (DECL_INITIAL (decl));
-
- if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
- throw operation_failed ();
-
- ck = static_cast<container_kind_type> (integer_value (init));
- }
- catch (operation_failed const&)
- {
- os << f << ":" << l << ":" << c << ": error: "
- << "container_traits specialization does not define the "
- << "container kind constant" << endl;
-
- throw;
- }
-
- t.set ("container-kind", ck);
-
- // See if it is a smart container.
- //
- try
- {
- tree decl (
- lookup_qualified_name (
- inst, get_identifier ("smart"), false, false));
-
- if (decl == error_mark_node || TREE_CODE (decl) != VAR_DECL)
- throw operation_failed ();
-
- // Instantiate this decalaration so that we can get its value.
- //
- if (DECL_TEMPLATE_INSTANTIATION (decl) &&
- !DECL_TEMPLATE_INSTANTIATED (decl) &&
- !DECL_EXPLICIT_INSTANTIATION (decl))
- instantiate_decl (decl, false, false);
-
- tree init (DECL_INITIAL (decl));
-
- if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
- throw operation_failed ();
-
- smart = static_cast<bool> (integer_value (init));
- }
- catch (operation_failed const&)
- {
- os << f << ":" << l << ":" << c << ": error: "
- << "container_traits specialization does not define the "
- << "'smart' constant" << endl;
- throw;
- }
-
- // For now we only support ordered smart containers.
- //
- if (smart && ck != ck_ordered)
- {
- os << f << ":" << l << ":" << c << ": error: only ordered smart " <<
- "containers are currently supported" << endl;
- throw operation_failed ();
- }
-
- t.set ("container-smart", smart);
-
- // Mark id column as not null.
- //
- t.set ("id-not-null", true);
-
- // Get the value type.
- //
- {
- tree decl (lookup_qualified_name (
- inst, get_identifier ("value_type"), true, false));
-
- if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
- {
- os << f << ":" << l << ":" << c << ": error: "
- << "container_traits specialization does not define the "
- << "value_type type" << endl;
-
- throw operation_failed ();
- }
-
- tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
-
- if (semantics::node* n = unit.find (type))
- vt = &dynamic_cast<semantics::type&> (*n);
- else
- {
- error (ml) << "container value type is not instantiated" << endl;
- info (ml) << "use typedef/using to instantiate" << endl;
- throw operation_failed ();
- }
-
- // Find the hint.
- //
- vh = find_hint (unit, decl);
- }
-
-
- t.set ("value-tree-type", vt);
- t.set ("value-tree-hint", vh);
- vt = &utype (m, vh, "value"); // Map.
-
- // Issue a warning if we are relaxing null-ness in the container
- // type.
- //
- if (t.count ("value-null") && vt->count ("not-null"))
- {
- os << t.file () << ":" << t.line () << ":" << t.column () << ":"
- << " warning: container value declared null while its type "
- << "is declared not null" << endl;
- }
-
- // Get the index type for ordered containers.
- //
- if (ck == ck_ordered)
- {
- tree decl (
- lookup_qualified_name (
- inst, get_identifier ("index_type"), true, false));
-
- if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
- {
- os << f << ":" << l << ":" << c << ": error: "
- << "container_traits specialization does not define the "
- << "index_type type" << endl;
- throw operation_failed ();
- }
-
- tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
-
- if (semantics::node* n = unit.find (type))
- it = &dynamic_cast<semantics::type&> (*n);
- else
- {
- error (ml) << "container index type is not instantiated" << endl;
- info (ml) << "use typedef/using to instantiate" << endl;
- throw operation_failed ();
- }
-
- // Find the hint.
- //
- ih = find_hint (unit, decl);
-
- t.set ("index-not-null", true);
- t.set ("index-tree-type", it);
- t.set ("index-tree-hint", ih);
- it = &utype (m, ih, "index"); // Map.
- }
-
- // Get the key type for maps.
- //
- if (ck == ck_map || ck == ck_multimap)
- {
- tree decl (
- lookup_qualified_name (
- inst, get_identifier ("key_type"), true, false));
-
- if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
- {
- os << f << ":" << l << ":" << c << ": error: "
- << "container_traits specialization does not define the "
- << "key_type type" << endl;
- throw operation_failed ();
- }
-
- tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
-
- if (semantics::node* n = unit.find (type))
- kt = &dynamic_cast<semantics::type&> (*n);
- else
- {
- error (ml) << "container key type is not instantiated" << endl;
- info (ml) << "use typedef/using to instantiate" << endl;
- throw operation_failed ();
- }
-
- // Find the hint.
- //
- kh = find_hint (unit, decl);
-
- t.set ("key-tree-type", kt);
- t.set ("key-tree-hint", kh);
- kt = &utype (m, kh, "key"); // Map.
-
- // Issue a warning if we are relaxing null-ness in the container
- // type.
- //
- if (t.count ("key-null") && kt->count ("not-null"))
- {
- os << t.file () << ":" << t.line () << ":" << t.column () << ":"
- << " warning: container key declared null while its type "
- << "is declared not null" << endl;
- }
- }
-
- // Check if we are versioned. For now we are not allowing for
- // soft-add/delete in container keys (might be used in WHERE,
- // primary key).
- //
- {
- semantics::class_* comp (0);
- switch (ck)
- {
- case ck_ordered:
- {
- comp = composite (*vt);
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- comp = composite (*kt);
- if (comp == 0 || column_count (*comp).soft == 0)
- {
- comp = composite (*vt);
- break;
- }
-
- error (ml) << "map key type cannot have soft-added/deleted " <<
- "data members" << endl;
- info (kt->location ()) << "key type is defined here" << endl;
- throw operation_failed ();
- }
- case ck_set:
- case ck_multiset:
- {
- comp = composite (*vt);
- if (comp == 0 || column_count (*comp).soft == 0)
- {
- comp = 0;
- break;
- }
-
- error (ml) << "set value type cannot have soft-added/deleted " <<
- "data members" << endl;
- info (vt->location ()) << "value type is defined here" << endl;
- throw operation_failed ();
- }
- }
-
- if (force_versioned || (comp != 0 && column_count (*comp).soft != 0))
- t.set ("versioned", true);
- }
- }
-
- // Process member data.
- //
- m.set ("id-tree-type", &id_tree_type);
-
- // Has to be first to handle inverse.
- //
- process_container_value (*vt, m, "value", true);
-
- if (it != 0)
- process_container_value (*it, m, "index", false);
-
- if (kt != 0)
- process_container_value (*kt, m, "key", true);
-
- // A map cannot be an inverse container.
- //
- if (m.count ("value-inverse") && (ck == ck_map || ck == ck_multimap))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: inverse container cannot be a map" << endl;
- throw operation_failed ();
- }
-
- // If this is an inverse side of a bidirectional object relationship
- // and it is an ordered container, mark it as unordred since there is
- // no concept of order in this construct.
- //
- if (ck == ck_ordered && m.count ("value-inverse"))
- m.set ("unordered", true);
-
- // Issue an error if we have a non-inverse smart unordered container.
- //
- if (smart && ck == ck_ordered && unordered (m) &&
- !m.count ("value-inverse"))
- {
- error (ml) << "smart ordered container cannot be unordered" << endl;
- throw operation_failed ();
- }
-
- // Issue a warning if we are relaxing null-ness in the member.
- //
- if (m.count ("value-null") &&
- (t.count ("value-not-null") || vt->count ("not-null")))
- {
- warn (ml) << "container value declared null while the container "
- << "type or value type declares it as not null" << endl;
- }
-
- if (ck == ck_map || ck == ck_multimap)
- {
- if (m.count ("key-null") &&
- (t.count ("key-not-null") || kt->count ("not-null")))
- {
- warn (ml) << "container key declared null while the container "
- << "type or key type declares it as not null" << endl;
- }
- }
-
- return true;
- }
-
- //
- // Implementation details (c-tor, helpers).
- //
-
- data_member ()
- {
- // Find the odb namespace.
- //
- tree odb = lookup_qualified_name (
- global_namespace, get_identifier ("odb"), false, false);
-
- if (odb == error_mark_node)
- {
- os << unit.file () << ": error: unable to resolve odb namespace"
- << endl;
-
- throw operation_failed ();
- }
-
- // Find wrapper traits.
- //
- wrapper_traits_ = lookup_qualified_name (
- odb, get_identifier ("wrapper_traits"), true, false);
-
- if (wrapper_traits_ == error_mark_node ||
- !DECL_CLASS_TEMPLATE_P (wrapper_traits_))
- {
- os << unit.file () << ": error: unable to resolve wrapper_traits "
- << "in the odb namespace" << endl;
-
- throw operation_failed ();
- }
-
- // Find pointer traits.
- //
- pointer_traits_ = lookup_qualified_name (
- odb, get_identifier ("pointer_traits"), true, false);
-
- if (pointer_traits_ == error_mark_node ||
- !DECL_CLASS_TEMPLATE_P (pointer_traits_))
- {
- os << unit.file () << ": error: unable to resolve pointer_traits "
- << "in the odb namespace" << endl;
-
- throw operation_failed ();
- }
-
- // Find the access class.
- //
- tree access = lookup_qualified_name (
- odb, get_identifier ("access"), true, false);
-
- if (access == error_mark_node)
- {
- os << unit.file () << ": error: unable to resolve access class"
- << "in the odb namespace" << endl;
-
- throw operation_failed ();
- }
-
- access = TREE_TYPE (access);
-
- // Find container_traits.
- //
- container_traits_ = lookup_qualified_name (
- access, get_identifier ("container_traits"), true, false);
-
- if (container_traits_ == error_mark_node ||
- !DECL_CLASS_TEMPLATE_P (container_traits_))
- {
- os << unit.file () << ": error: unable to resolve container_traits "
- << "in the odb namespace" << endl;
-
- throw operation_failed ();
- }
- }
-
- static tree
- instantiate_template (tree t, tree arg)
- {
- tree args (make_tree_vec (1));
- TREE_VEC_ELT (args, 0) = arg;
-
- // This step should succeed regardles of whether there is a
- // specialization for this type.
- //
- tree inst (
- lookup_template_class (t, args, 0, 0, 0, tf_warning_or_error));
-
- if (inst == error_mark_node)
- {
- // Diagnostics has already been issued by lookup_template_class.
- //
- throw operation_failed ();
- }
-
- inst = TYPE_MAIN_VARIANT (inst);
-
- // The instantiation may already be complete if it matches a
- // (complete) specialization or was used before.
- //
- if (!COMPLETE_TYPE_P (inst))
- inst = instantiate_class_template (inst);
-
- // If we cannot instantiate this type, assume there is no suitable
- // specialization for it.
- //
- if (inst == error_mark_node || !COMPLETE_TYPE_P (inst))
- return 0;
-
- return inst;
- }
-
- private:
- tree wrapper_traits_;
- tree pointer_traits_;
- tree container_traits_;
-
- cxx_string_lexer lex_;
- };
-
- struct view_data_member: traversal::data_member, context
- {
- view_data_member (semantics::class_& c)
- : view_ (c),
- amap_ (c.get<view_alias_map> ("alias-map")),
- omap_ (c.get<view_object_map> ("object-map")) {}
-
- virtual void
- traverse (semantics::data_member& m)
- {
- using semantics::data_member;
-
- if (transient (m))
- return;
-
- semantics::type& t (utype (m));
-
- if (semantics::class_* c = object_pointer (t))
- {
- location const& l (m.location ());
-
- if (lazy_pointer (t))
- {
- error (l) << "lazy object pointer in view" << endl;
- throw operation_failed ();
- }
-
- // Find the corresponding associated object. First see if this
- // data member name matches any aliases.
- //
- view_alias_map::iterator i (amap_.find (m.name ()));
-
- if (i == amap_.end ())
- i = amap_.find (public_name (m, false));
-
- view_object* vo (0);
-
- if (i != amap_.end ())
- {
- vo = i->second;
-
- if (vo->obj != c) // @@ Poly base/derived difference?
- {
- error (l) << "different pointed-to and associated objects" << endl;
- info (vo->loc) << "associated object is defined here" << endl;
- throw operation_failed ();
- }
- }
- else
- {
- // If there is no alias match, try the object type.
- //
- view_object_map::iterator i (omap_.find (c));
-
- if (i == omap_.end ())
- {
- error (l) << "unable to find associated object for object "
- << "pointer" << endl;
- info (l) << "use associated object alias as this data member "
- << "name" << endl;
- throw operation_failed ();
- }
-
- vo = i->second;
- }
-
- if (vo->ptr != 0)
- {
- location const& l2 (vo->ptr->location ());
-
- error (l) << "associated object is already loaded via another "
- << "object pointer" << endl;
- info (l2) << "the other data member is defined here" << endl;
- info (l2) << "use associated object alias as this data member "
- << "name to load a different object" << endl;
-
- throw operation_failed ();
- }
-
- vo->ptr = &m;
- m.set ("view-object", vo);
- }
- }
-
- private:
- semantics::class_& view_;
- view_alias_map& amap_;
- view_object_map& omap_;
- };
-
- // Figure out the "summary" added/deleted version for a composite
- // value type.
- //
- struct summary_version: object_members_base
- {
- summary_version (): av (0), dv (0), a_ (true), d_ (true) {}
-
- virtual void
- traverse_simple (semantics::data_member&)
- {
- if (a_)
- {
- if (unsigned long long v = added (member_path_))
- {
- if (av == 0 || av < v)
- av = v;
- }
- else
- {
- av = 0;
- a_ = false;
- }
- }
-
- if (d_)
- {
- if (unsigned long long v = deleted (member_path_))
- {
- if (dv == 0 || dv > v)
- dv = v;
- }
- else
- {
- dv = 0;
- d_ = false;
- }
- }
- }
-
- public:
- unsigned long long av;
- unsigned long long dv;
-
- bool a_;
- bool d_;
- };
-
- struct class_: traversal::class_, context
- {
- class_ ()
- : typedefs_ (true),
- std_string_ (0),
- std_string_hint_ (0),
- access_ (0)
- {
- *this >> defines_ >> *this;
- *this >> typedefs_ >> *this;
-
- member_names_ >> member_;
-
- // Resolve the std::string type node.
- //
- using semantics::scope;
-
- for (scope::names_iterator_pair ip (unit.find ("std"));
- ip.first != ip.second; ++ip.first)
- {
- if (scope* ns = dynamic_cast<scope*> (&ip.first->named ()))
- {
- scope::names_iterator_pair jp (ns->find ("string"));
-
- if (jp.first != jp.second)
- {
- std_string_ = dynamic_cast<semantics::type*> (
- &jp.first->named ());
- std_string_hint_ = &*jp.first;
- break;
- }
- }
- }
-
- assert (std_string_ != 0); // No std::string?
-
- // Resolve odb::access, if any.
- //
- tree odb = lookup_qualified_name (
- global_namespace, get_identifier ("odb"), false, false);
-
- if (odb != error_mark_node)
- {
- access_ = lookup_qualified_name (
- odb, get_identifier ("access"), true, false);
-
- access_ = (access_ != error_mark_node ? TREE_TYPE (access_) : 0);
- }
- }
-
- virtual void
- traverse (type& c)
- {
- class_kind_type k (class_kind (c));
-
- if (k == class_other)
- return;
-
- names (c); // Process nested classes.
-
- // Check if odb::access is a friend of this class.
- //
- c.set ("friend", access_ != 0 && is_friend (c.tree_node (), access_));
-
- // Assign pointer.
- //
- if (k == class_object || k == class_view)
- assign_pointer (c);
-
- if (k == class_object)
- traverse_object_pre (c);
- else if (k == class_view)
- traverse_view_pre (c);
-
- names (c, member_names_);
-
- if (k == class_object)
- traverse_object_post (c);
- else if (k == class_view)
- traverse_view_post (c);
- else if (k == class_composite)
- traverse_composite_post (c);
- }
-
- //
- // Object.
- //
-
- virtual void
- traverse_object_pre (type& c)
- {
- using semantics::class_;
- using semantics::data_member;
-
- class_* poly_root (polymorphic (c));
-
- // Sections.
- //
- user_sections& uss (c.set ("user-sections", user_sections (c)));
-
- // Copy sections from reuse bases. For polymorphic classes, sections
- // are overridden.
- //
- if (poly_root == 0 || poly_root == &c)
- {
- for (type::inherits_iterator i (c.inherits_begin ());
- i != c.inherits_end (); ++i)
- {
- type& b (i->base ());
-
- if (object (b))
- {
- user_sections& buss (b.get<user_sections> ("user-sections"));
-
- for (user_sections::iterator j (buss.begin ());
- j != buss.end ();
- ++j)
- {
- // Don't copy the special version update section.
- //
- if (j->special == user_section::special_version)
- continue;
-
- uss.push_back (*j);
- uss.back ().object = &c;
- uss.back ().base = &*j;
- }
- }
- }
- }
-
- // Determine whether it is a session object.
- //
- if (!c.count ("session"))
- {
- // If this is a derived class in a polymorphic hierarchy,
- // then it should have the same session value as the root.
- //
- if (poly_root != 0 && poly_root != &c)
- c.set ("session", session (*poly_root));
- else
- {
- // See if any of the namespaces containing this class specify
- // the session value.
- //
- bool found (false);
- for (semantics::scope* s (&class_scope (c));; s = &s->scope_ ())
- {
- using semantics::namespace_;
-
- namespace_* ns (dynamic_cast<namespace_*> (s));
-
- if (ns == 0)
- continue; // Some other scope.
-
- if (ns->extension ())
- ns = &ns->original ();
-
- if (ns->count ("session"))
- {
- c.set ("session", ns->get<bool> ("session"));
- found = true;
- break;
- }
-
- if (ns->global_scope ())
- break;
- }
-
- // If still not found, then use the default value.
- //
- if (!found)
- c.set ("session", options.generate_session ());
- }
- }
-
- if (session (c))
- features.session_object = true;
-
- if (poly_root != 0)
- {
- using namespace semantics;
- using semantics::data_member;
-
- data_member_path& id (*id_member (*poly_root));
- data_member* idm (id.front ());
-
- if (poly_root != &c)
- {
- // If we are a derived class in the polymorphic persistent
- // class hierarchy, then add a synthesized virtual pointer
- // member that points back to the root.
- //
- semantics::class_& base (polymorphic_base (c));
-
- if (&base != poly_root)
- idm = &dynamic_cast<data_member&> (base.names_begin ()->named ());
-
- path const& f (idm->file ());
- size_t l (idm->line ()), col (idm->column ());
-
- semantics::data_member& m (
- unit.new_node<semantics::data_member> (f, l, col, tree (0)));
- m.set ("virtual", true);
-
- // Make it the first member in the class. This is important:
- // we rely on the corrensponding foreign key to be first.
- //
- node_position<type, scope::names_iterator> np (c, c.names_end ());
- unit.new_edge<semantics::names> (
- np, m, idm->name (), access::public_);
-
- // Use the raw pointer as this member's type.
- //
- if (!base.pointed_p ())
- {
- // Create the pointer type in the graph. The pointer node
- // in GCC seems to always be present, even if not explicitly
- // used in the translation unit.
- //
- tree t (base.tree_node ());
- tree ptr (TYPE_POINTER_TO (t));
- assert (ptr != 0);
- ptr = TYPE_MAIN_VARIANT (ptr);
- pointer& p (unit.new_node<pointer> (f, l, col, ptr));
- unit.insert (ptr, p);
- unit.new_edge<points> (p, base);
- assert (base.pointed_p ());
- }
-
- unit.new_edge<belongs> (m, base.pointed ().pointer ());
-
- // Mark it as a special kind of id.
- //
- m.set ("id", string ());
- m.set ("polymorphic-ref", true);
-
- // Make sure we also use the same column name as the root.
- //
- if (composite_wrapper (utype (id)))
- m.set ("column", table_column (column_prefix (id, true).prefix));
- else
- m.set ("column", table_column (column_name (id)));
- }
- else
- {
- // If we are a root of the polymorphic persistent class hierarchy,
- // then add a synthesized virtual member for the discriminator.
- // Use the location of the polymorphic pragma as the location of
- // this member.
- //
- location_t loc (c.get<location_t> ("polymorphic-location"));
- semantics::data_member& m (
- unit.new_node<semantics::data_member> (
- path (LOCATION_FILE (loc)),
- LOCATION_LINE (loc),
- LOCATION_COLUMN (loc),
- tree (0)));
- m.set ("virtual", true);
-
- // Insert it after the id member (or first if this id comes
- // from reuse-base).
- //
- node_position<type, scope::names_iterator> np (
- c, c.find (idm->named ()));
- unit.new_edge<semantics::names> (
- np, m, "typeid_", access::public_);
-
- belongs& edge (unit.new_edge<belongs> (m, *std_string_));
- edge.hint (*std_string_hint_);
-
- m.set ("readonly", true);
- m.set ("discriminator", true);
-
- c.set ("discriminator", &m);
- }
- }
- }
-
- virtual void
- traverse_object_post (type& c)
- {
- semantics::class_* poly_root (polymorphic (c));
- bool poly_derived (poly_root != 0 && poly_root != &c);
-
- semantics::data_member* opt (optimistic (c));
-
- // Figure out if we are versioned. We are versioned if we have
- // soft-added/deleted columns ourselves or our poly-base is
- // versioned.
- //
- if (force_versioned ||
- column_count (c).soft != 0 ||
- (poly_derived && polymorphic_base (c).count ("versioned")))
- c.set ("versioned", true);
-
- // Sections.
- //
- user_sections& uss (c.get<user_sections> ("user-sections"));
-
- // See if we need to add a special fake section for version update.
- //
- if (c.count ("sectionable"))
- {
- uss.push_back (
- user_section (*opt,
- c,
- uss.count (user_sections::count_total |
- user_sections::count_all |
- user_sections::count_special_version),
- user_section::load_lazy,
- user_section::update_manual,
- user_section::special_version));
-
- // If we are a root of a polymorphic hierarchy and the version is in
- // a reuse-base, then we need to make sure that base is sectionable
- // and derive from its special version update section.
- //
- semantics::node& opt_base (opt->scope ());
- if (poly_root == &c && &opt_base != &c)
- {
- if (!opt_base.count ("sectionable"))
- {
- location_t l (c.get<location_t> ("sectionable-location"));
-
- error (l) << "reuse base class of a sectionable polymorphic " <<
- "root class must be sectionable" << endl;
-
- info (opt_base.location ()) << "use '#pragma db object " <<
- "sectionable' to make the base class of this hierarchy " <<
- "sectionable" << endl;
-
- throw operation_failed ();
- }
-
- uss.back ().base =
- &opt_base.get<user_sections> ("user-sections").back ();
- }
- }
-
- // Calculate column counts for sections.
- //
- for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
- {
- column_count_type cc (column_count (c, &*i));
- i->total = cc.total;
- i->inverse = cc.inverse;
- i->readonly = cc.readonly;
-
- // Figure out if we are versioned. We are versioned if we have
- // soft-added/deleted columns ourselves or our poly-base is
- // versioned.
- //
- if (force_versioned || cc.soft != 0 ||
- (poly_derived && i->base != 0 && i->base->versioned))
- i->versioned = true;
-
- if (size_t n = has_a (c, test_container, &*i))
- {
- i->containers = true;
- i->versioned_containers =
- n != has_a (c,
- test_container |
- exclude_deleted | exclude_added | exclude_versioned,
- &*i);
-
- if ((n = has_a (c, test_readwrite_container, &*i)))
- {
- i->readwrite_containers = true;
- i->readwrite_versioned_containers =
- n != has_a (c,
- test_readwrite_container |
- exclude_deleted | exclude_added | exclude_versioned,
- &*i);
- }
- }
- }
- }
-
- //
- // View.
- //
-
- virtual void
- traverse_view_pre (type& c)
- {
- // Resolve referenced objects from tree nodes to semantic graph
- // nodes. Also populate maps and compute counts.
- //
- view_alias_map& amap (c.set ("alias-map", view_alias_map ()));
- view_object_map& omap (c.set ("object-map", view_object_map ()));
-
- size_t& obj_count (c.set ("object-count", size_t (0)));
- size_t& tbl_count (c.set ("table-count", size_t (0)));
-
- if (c.count ("objects"))
- {
- using semantics::class_;
-
- view_objects& objs (c.get<view_objects> ("objects"));
-
- for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
- {
- if (i->kind != view_object::object)
- {
- tbl_count++;
- continue;
- }
- else
- obj_count++;
-
- tree n (TYPE_MAIN_VARIANT (i->obj_node));
-
- if (TREE_CODE (n) != RECORD_TYPE)
- {
- error (i->loc) << "name '" << i->obj_name << "' in db pragma " <<
- "object does not name a class" << endl;
-
- throw operation_failed ();
- }
-
- class_& o (dynamic_cast<class_&> (*unit.find (n)));
-
- if (!object (o))
- {
- error (i->loc) << "name '" << i->obj_name << "' in db pragma " <<
- "object does not name a persistent class" << endl;
-
- info (o.location ()) << "class '" << i->obj_name << "' is " <<
- "defined here" << endl;
-
- throw operation_failed ();
- }
-
- i->obj = &o;
- i->ptr = 0; // Nothing yet.
-
- if (i->alias.empty ())
- {
- if (!omap.insert (view_object_map::value_type (&o, &*i)).second)
- {
- error (i->loc) << "persistent class '" << i->obj_name <<
- "' is used in the view more than once" << endl;
-
- error (omap[&o]->loc) << "previously used here" << endl;
-
- info (i->loc) << "use the alias clause to assign it a " <<
- "different name" << endl;
-
- throw operation_failed ();
- }
-
- // Also add the bases of a polymorphic object.
- //
- class_* poly_root (polymorphic (o));
-
- if (poly_root != 0 && poly_root != &o)
- {
- for (class_* b (&polymorphic_base (o));;
- b = &polymorphic_base (*b))
- {
- if (!omap.insert (view_object_map::value_type (b, &*i)).second)
- {
- error (i->loc) << "base class '" << class_name (*b) <<
- "' is used in the view more than once" << endl;
-
- error (omap[b]->loc) << "previously used here" << endl;
-
- info (i->loc) << "use the alias clause to assign it a " <<
- "different name" << endl;
-
- throw operation_failed ();
- }
-
- if (b == poly_root)
- break;
- }
- }
- }
- else
- {
- if (!amap.insert (
- view_alias_map::value_type (i->alias, &*i)).second)
- {
- error (i->loc) << "alias '" << i->alias << "' is used in " <<
- "the view more than once" << endl;
-
- throw operation_failed ();
- }
- }
- }
- }
- }
-
- virtual void
- traverse_view_post (type& c)
- {
- // Handle data members.
- //
- {
- view_data_member t (c);
- traversal::names n (t);
- names (c, n);
- }
-
- // Figure out if we are versioned. Forced versioning is handled
- // in relational/processing.
- //
- if (column_count (c).soft != 0)
- c.set ("versioned", true);
- }
-
- //
- // Composite.
- //
-
- virtual void
- traverse_composite_post (type& c)
- {
- // Figure out if we are versioned.
- //
- if (force_versioned || column_count (c).soft != 0)
- {
- c.set ("versioned", true);
-
- // See if we are "summarily" added/deleted, that is, all the
- // columns are added/deleted. Note: this does not include
- // containers.
- //
- summary_version sv;
- sv.traverse (c);
-
- // Note: there are no locations.
- //
- if (sv.av != 0)
- c.set ("added", sv.av);
-
- if (sv.dv != 0)
- c.set ("deleted", sv.dv);
- }
- }
-
- //
- // Assign object/view pointer.
- //
-
- void
- assign_pointer (type& c)
- {
- location_t loc (0); // Pragma location, or 0 if not used.
-
- try
- {
- bool raw;
- string ptr;
- string const& type (class_fq_name (c));
-
- tree decl (0); // Resolved template node.
- string decl_name; // User-provided template name.
- tree resolve_scope (0); // Scope in which we resolve names.
-
- class_pointer const* cp (0);
- bool cp_template (false);
-
- if (c.count ("pointer"))
- {
- cp = &c.get<class_pointer> ("pointer");
- }
- // If we are a derived type in polymorphic hierarchy, then use
- // our root's pointer type by default.
- //
- else if (semantics::class_* r = polymorphic (c))
- {
- if (&c != r && r->count ("pointer-template"))
- cp = r->get<class_pointer const*> ("pointer-template");
- }
-
- if (cp != 0)
- {
- string const& p (cp->name);
-
- if (p == "*")
- {
- raw = true;
- ptr = type + "*";
- cp_template = true;
- }
- else if (p[p.size () - 1] == '*')
- {
- raw = true;
- ptr = p;
- }
- else if (p.find ('<') != string::npos)
- {
- // Template-id.
- //
- raw = false; // Fair to assume not raw, though technically can be.
- ptr = p;
- decl_name.assign (p, 0, p.find ('<'));
- }
- else
- {
- // This is not a template-id. Resolve it and see if it is a
- // template or a type.
- //
- decl = resolve_name (p, cp->scope, true);
- gcc_tree_code_type tc (TREE_CODE (decl));
-
- if (tc == TYPE_DECL)
- {
- raw = (TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE);
- ptr = p;
-
- // This can be a typedef'ed alias for a TR1 template-id.
- //
- if (tree ti = TYPE_TEMPLATE_INFO (TREE_TYPE (decl)))
- {
- decl = TI_TEMPLATE (ti); // DECL_TEMPLATE
-
- // Get to the most general template declaration.
- //
- while (DECL_TEMPLATE_INFO (decl))
- decl = DECL_TI_TEMPLATE (decl);
- }
- else
- decl = 0; // Not a template.
- }
- else if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl))
- {
- raw = false;
- ptr = p + "< " + type + " >";
- decl_name = p;
- cp_template = true;
- }
- else
- {
- error (cp->loc)
- << "name '" << p << "' specified with db pragma pointer "
- << "does not name a type or a template" << endl;
-
- throw operation_failed ();
- }
- }
-
- // Resolve scope is the scope of the pragma.
- //
- resolve_scope = cp->scope;
- loc = cp->loc;
- }
- else
- {
- // See if any of the namespaces containing this class specify
- // a pointer.
- //
- for (semantics::scope* s (&class_scope (c));; s = &s->scope_ ())
- {
- using semantics::namespace_;
-
- namespace_* ns (dynamic_cast<namespace_*> (s));
-
- if (ns == 0)
- continue; // Some other scope.
-
- if (ns->extension ())
- ns = &ns->original ();
-
- if (!ns->count ("pointer"))
- {
- if (ns->global_scope ())
- break;
- else
- continue;
- }
-
- cp = &ns->get<class_pointer> ("pointer");
- string const& p (cp->name);
-
- // Namespace-specified pointer can only be '*' or are template.
- //
- if (p == "*")
- {
- raw = true;
- ptr = type + "*";
- }
- else if (p[p.size () - 1] == '*')
- {
- error (cp->loc)
- << "name '" << p << "' specified with db pragma pointer "
- << "at namespace level cannot be a raw pointer" << endl;
- }
- else if (p.find ('<') != string::npos)
- {
- error (cp->loc)
- << "name '" << p << "' specified with db pragma pointer "
- << "at namespace level cannot be a template-id" << endl;
- }
- else
- {
- // Resolve this name and make sure it is a template.
- //
- decl = resolve_name (p, cp->scope, true);
- gcc_tree_code_type tc (TREE_CODE (decl));
-
- if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl))
- {
- raw = false;
- ptr = p + "< " + type + " >";
- decl_name = p;
- }
- else
- {
- error (cp->loc)
- << "name '" << p << "' specified with db pragma pointer "
- << "does not name a template" << endl;
- }
- }
-
- if (ptr.empty ())
- throw operation_failed ();
-
- cp_template = true;
-
- // Resolve scope is the scope of the pragma.
- //
- resolve_scope = cp->scope;
- loc = cp->loc;
- break;
- }
-
- // Use the default pointer.
- //
- if (ptr.empty ())
- {
- string const& p (options.default_pointer ());
-
- if (p == "*")
- {
- raw = true;
- ptr = type + "*";
- }
- else
- {
- raw = false;
- ptr = p + "< " + type + " >";
- decl_name = p;
- }
-
- // Resolve scope is the scope of the class.
- //
- resolve_scope = class_scope (c).tree_node ();
- }
- }
-
- // If this class is a root of a polymorphic hierarchy, then cache
- // the pointer template so that we can use it for derived classes.
- //
- if (cp != 0 && cp_template && polymorphic (c) == &c)
- c.set ("pointer-template", cp);
-
- // Check if we are using TR1.
- //
- if (decl != 0 || !decl_name.empty ())
- {
- bool& tr1 (features.tr1_pointer);
- bool& boost (features.boost_pointer);
-
- // First check the user-supplied name.
- //
- tr1 = tr1
- || decl_name.compare (0, 8, "std::tr1") == 0
- || decl_name.compare (0, 10, "::std::tr1") == 0;
-
- // If there was no match, also resolve the name since it can be
- // a using-declaration for a TR1 template.
- //
- if (!tr1)
- {
- if (decl == 0)
- decl = resolve_name (decl_name, resolve_scope, false);
-
- if (TREE_CODE (decl) != TEMPLATE_DECL || !
- DECL_CLASS_TEMPLATE_P (decl))
- {
- // This is only checked for the --default-pointer option.
- //
- error (c.file (), c.line (), c.column ())
- << "name '" << decl_name << "' specified with the "
- << "--default-pointer option does not name a class "
- << "template" << endl;
-
- throw operation_failed ();
- }
-
- string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER));
-
- // In case of a boost TR1 implementation, we cannot distinguish
- // between the boost:: and std::tr1:: usage since the latter is
- // just a using-declaration for the former.
- //
- tr1 = tr1
- || n.compare (0, 8, "std::tr1") == 0
- || n.compare (0, 10, "::std::tr1") == 0;
-
- boost = boost
- || n.compare (0, 17, "boost::shared_ptr") == 0
- || n.compare (0, 19, "::boost::shared_ptr") == 0;
- }
- }
-
- // Fully-qualify all the unqualified components of the name.
- //
- try
- {
- lex_.start (ptr);
- ptr.clear ();
-
- string t;
- bool punc (false);
- bool scoped (false);
-
- for (cpp_ttype tt (lex_.next (t));
- tt != CPP_EOF;
- tt = lex_.next (t))
- {
- if (punc && tt > CPP_LAST_PUNCTUATOR)
- ptr += ' ';
-
- punc = false;
-
- switch (static_cast<unsigned> (tt))
- {
- case CPP_LESS:
- {
- ptr += "< ";
- break;
- }
- case CPP_GREATER:
- {
- ptr += " >";
- break;
- }
- case CPP_COMMA:
- {
- ptr += ", ";
- break;
- }
- case CPP_NAME:
- {
- // If the name was not preceeded with '::', look it
- // up in the pragmas's scope and add the qualifer.
- //
- if (!scoped)
- {
- tree decl (resolve_name (t, resolve_scope, false));
- tree scope (CP_DECL_CONTEXT (decl));
-
- // If this is an inline namespace, skip it until we get
- // to the non-inline one.
- //
- while (scope != global_namespace)
- {
- tree prev (CP_DECL_CONTEXT (scope));
-
-#if BUILDING_GCC_MAJOR >= 8
- if (!is_nested_namespace (prev, scope, true))
-#else
- if (!is_associated_namespace (prev, scope))
-#endif
- break;
-
- scope = prev;
- }
-
- if (scope != global_namespace)
- {
- ptr += "::";
- ptr += decl_as_string (scope, TFF_PLAIN_IDENTIFIER);
- }
-
- ptr += "::";
- }
-
- ptr += t;
- punc = true;
- break;
- }
- case CPP_KEYWORD:
- case CPP_NUMBER:
- {
- ptr += t;
- punc = true;
- break;
- }
- default:
- {
- ptr += t;
- break;
- }
- }
-
- scoped = (tt == CPP_SCOPE);
- }
- }
- catch (cxx_lexer::invalid_input const&)
- {
- throw operation_failed ();
- }
-
- c.set ("object-pointer", ptr);
- c.set ("object-pointer-raw", raw);
- }
- catch (invalid_name const& ex)
- {
- if (loc != 0)
- error (loc)
- << "name '" << ex.name () << "' specified with db pragma "
- << "pointer is invalid" << endl;
- else
- error (c.file (), c.line (), c.column ())
- << "name '" << ex.name () << "' specified with the "
- << "--default-pointer option is invalid" << endl;
-
-
- throw operation_failed ();
- }
- catch (unable_to_resolve const& ex)
- {
- if (loc != 0)
- error (loc)
- << "unable to resolve name '" << ex.name () << "' specified "
- << "with db pragma pointer" << endl;
- else
- error (c.file (), c.line (), c.column ())
- << "unable to resolve name '" << ex.name () << "' specified "
- << "with the --default-pointer option" << endl;
-
- throw operation_failed ();
- }
- }
-
- private:
- struct invalid_name
- {
- invalid_name (string const& n): name_ (n) {}
-
- string const&
- name () const {return name_;}
-
- private:
- string name_;
- };
-
- typedef lookup::unable_to_resolve unable_to_resolve;
-
- tree
- resolve_name (string const& qn, tree scope, bool is_type)
- {
- try
- {
- string tl;
- tree tn;
- cpp_ttype tt, ptt;
-
- nlex_.start (qn);
- tt = nlex_.next (tl, &tn);
-
- string name;
- return lookup::resolve_scoped_name (
- nlex_, tt, tl, tn, ptt, scope, name, is_type);
- }
- catch (cxx_lexer::invalid_input const&)
- {
- throw invalid_name (qn);
- }
- catch (lookup::invalid_name const&)
- {
- throw invalid_name (qn);
- }
- }
-
- private:
- traversal::defines defines_;
- typedefs typedefs_;
-
- data_member member_;
- traversal::names member_names_;
-
- cxx_string_lexer lex_;
- cxx_string_lexer nlex_; // Nested lexer.
-
- semantics::type* std_string_;
- semantics::names* std_string_hint_;
-
- tree access_; // odb::access node.
- };
-
- static bool
- check_to_from (const cxx_tokens& ex, const char* c, location_t l)
- {
- // Make sure we have one and only one placeholder (?).
- //
- bool r (false), m (true);
-
- for (cxx_tokens::const_iterator i (ex.begin ()), e (ex.end ()); i != e;)
- {
- if (i->type == CPP_OPEN_PAREN)
- {
- if (++i != e && i->type == CPP_QUERY)
- {
- if (++i != e && i->type == CPP_CLOSE_PAREN)
- {
- if (r)
- m = false; // Multiple (?), can't move.
- else
- r = true;
- }
- }
- }
- else
- ++i;
- }
-
- if (!r)
- {
- error (l) << "no '(?)' expression in the '" << c << "' clause "
- << "of db pragma map" << endl;
-
- throw operation_failed ();
- }
-
- return m;
- }
-}
-
-static void
-process1 (semantics::unit& u)
-{
- // Process custom C++ type mapping.
- //
-
- // Create an empty list if we don't have one. This makes the
- // rest of the code simpler.
- //
- if (!u.count ("custom-cxx-types"))
- u.set ("custom-cxx-types", custom_cxx_types ());
-
- custom_cxx_types & cts (u.get<custom_cxx_types> ("custom-cxx-types"));
-
- for (custom_cxx_types::iterator i (cts.begin ()); i != cts.end (); ++i)
- {
- custom_cxx_type& ct (*i);
-
- // type
- //
- if (ct.type_node == 0)
- {
- error (ct.loc) << "'type' clause expected in db pragma map" << endl;
- throw operation_failed ();
- }
-
- ct.type = dynamic_cast<semantics::type*> (
- u.find (TYPE_MAIN_VARIANT (ct.type_node)));
- ct.type_hint = u.find_hint (ct.type_node);
-
- // as
- //
- if (ct.as_node == 0)
- {
- error (ct.loc) << "'as' clause expected in db pragma map" << endl;
- throw operation_failed ();
- }
-
- ct.as = dynamic_cast<semantics::type*> (
- u.find (TYPE_MAIN_VARIANT (ct.as_node)));
- ct.as_hint = u.find_hint (ct.as_node);
-
- // to
- //
- {
- cxx_tokens& e (ct.to);
-
- if (e.empty ())
- {
- e.push_back (cxx_token (0, CPP_OPEN_PAREN));
- e.push_back (cxx_token (0, CPP_QUERY));
- e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
- ct.to_move = true;
- }
- else
- ct.to_move = check_to_from (e, "to", ct.loc);
- }
-
- // from
- //
- {
- cxx_tokens& e (ct.from);
-
- if (e.empty ())
- {
- e.push_back (cxx_token (0, CPP_OPEN_PAREN));
- e.push_back (cxx_token (0, CPP_QUERY));
- e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
- ct.from_move = true;
- }
- else
- ct.from_move = check_to_from (e, "from", ct.loc);
- }
-
- // Resolve mapping scope.
- //
- semantics::scope* s (dynamic_cast<semantics::scope*> (u.find (ct.scope)));
- if (s == 0)
- {
- error (ct.loc) << "unable to resolve db pragma map scope" << endl;
- throw operation_failed ();
- }
-
- if (semantics::namespace_* ns = dynamic_cast<semantics::namespace_*> (s))
- {
- if (ns->extension ())
- s = &ns->original ();
- }
-
- // Enter into the map.
- //
- if (!s->count ("custom-cxx-type-map"))
- s->set ("custom-cxx-type-map", custom_cxx_type_map ());
-
- s->get<custom_cxx_type_map> ("custom-cxx-type-map")[ct.type] = &ct;
- }
-}
-
-static void
-process2 (options const& ops, features& f, semantics::unit& u)
-{
- unique_ptr<context> ctx (create_context (cerr, u, ops, f, 0));
-
- // Common processing.
- //
- {
- traversal::unit unit;
- traversal::defines unit_defines;
- typedefs unit_typedefs (true);
- traversal::namespace_ ns;
- class_ c;
-
- unit >> unit_defines >> ns;
- unit_defines >> c;
- unit >> unit_typedefs >> c;
-
- traversal::defines ns_defines;
- typedefs ns_typedefs (true);
-
- ns >> ns_defines >> ns;
- ns_defines >> c;
- ns >> ns_typedefs >> c;
-
- unit.dispatch (ctx->unit);
- }
-
- // Database-specific processing.
- //
- switch (ops.database ()[0])
- {
- case database::common:
- {
- break;
- }
- case database::mssql:
- case database::mysql:
- case database::oracle:
- case database::pgsql:
- case database::sqlite:
- {
- relational::process ();
- break;
- }
- }
-}
-
-void
-process (options const& ops,
- features& f,
- semantics::unit& u,
- semantics::path const&,
- unsigned short pass)
-{
- try
- {
- if (pass == 1)
- process1 (u);
- else if (pass == 2)
- process2 (ops, f, u);
- }
- catch (operation_failed const&)
- {
- // Processing failed. Diagnostics has already been issued.
- //
- throw processor_failed ();
- }
-}
diff --git a/odb/profile.hxx b/odb/profile.hxx
deleted file mode 100644
index 8b892dc..0000000
--- a/odb/profile.hxx
+++ /dev/null
@@ -1,39 +0,0 @@
-// file : odb/profile.hxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#ifndef ODB_PROFILE_HXX
-#define ODB_PROFILE_HXX
-
-#include <set>
-#include <vector>
-#include <string>
-
-#include <cutl/fs/path.hxx>
-
-#include <odb/option-types.hxx>
-
-struct profile_data
-{
- typedef cutl::fs::path path;
- typedef std::vector<path> paths;
-
- profile_data (paths const& p, database d, char const* n)
- : search_paths (p), db (d), name (n)
- {
- }
-
- paths const& search_paths;
- database db;
- char const* name;
- std::set<path> loaded;
-};
-
-struct profile_failure {};
-
-std::string
-profile_search (char const* profile, void* arg);
-
-std::string
-profile_search_ignore (char const* profile, void* arg);
-
-#endif // ODB_PROFILE_HXX
diff --git a/odb/relational/generate.hxx b/odb/relational/generate.hxx
deleted file mode 100644
index c3d19c7..0000000
--- a/odb/relational/generate.hxx
+++ /dev/null
@@ -1,81 +0,0 @@
-// file : odb/relational/generate.hxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#ifndef ODB_RELATIONAL_GENERATE_HXX
-#define ODB_RELATIONAL_GENERATE_HXX
-
-#include <string>
-#include <cutl/shared-ptr.hxx>
-
-#include <odb/context.hxx>
-#include <odb/semantics/relational/model.hxx>
-#include <odb/semantics/relational/changeset.hxx>
-#include <odb/semantics/relational/changelog.hxx>
-
-namespace relational
-{
- namespace header
- {
- void
- generate ();
- }
-
- namespace inline_
- {
- void
- generate ();
- }
-
- namespace source
- {
- void
- generate ();
- }
-
- namespace model
- {
- cutl::shared_ptr<semantics::relational::model>
- generate ();
- }
-
- namespace changelog
- {
- // Returns NULL if the changelog is unchanged.
- //
- cutl::shared_ptr<semantics::relational::changelog>
- generate (semantics::relational::model&,
- model_version const&,
- semantics::relational::changelog* old, // Can be NULL.
- std::string const& in_name,
- std::string const& out_name,
- options const&);
- }
-
- namespace schema
- {
- void
- generate_prologue ();
-
- void
- generate_epilogue ();
-
- void
- generate_drop ();
-
- void
- generate_create ();
-
- void
- generate_migrate_pre (semantics::relational::changeset&);
-
- void
- generate_migrate_post (semantics::relational::changeset&);
-
- // Generate embedded schema.
- //
- void
- generate_source (semantics::relational::changelog*);
- }
-}
-
-#endif // ODB_RELATIONAL_GENERATE_HXX
diff --git a/odb/relational/header.hxx b/odb/relational/header.hxx
deleted file mode 100644
index 30a61ea..0000000
--- a/odb/relational/header.hxx
+++ /dev/null
@@ -1,1464 +0,0 @@
-// file : odb/relational/header.hxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#ifndef ODB_RELATIONAL_HEADER_HXX
-#define ODB_RELATIONAL_HEADER_HXX
-
-#include <odb/relational/context.hxx>
-#include <odb/relational/common.hxx>
-
-namespace relational
-{
- namespace header
- {
- //
- // image_type
- //
-
- struct image_member: virtual member_base
- {
- typedef image_member base;
-
- image_member (string const& var = string ())
- : member_base (var, 0, 0, string (), string ()) {}
-
- image_member (string const& var,
- semantics::type& t,
- const custom_cxx_type* ct,
- string const& fq_type,
- string const& key_prefix)
- : member_base (var, &t, ct, fq_type, key_prefix) {}
- };
-
- template <typename T>
- struct image_member_impl: image_member, virtual member_base_impl<T>
- {
- typedef image_member_impl base_impl;
-
- image_member_impl (base const& x)
- : member_base::base (x), // virtual base
- base (x),
- member_image_type_ (base::type_override_,
- base::custom_override_,
- base::fq_type_override_,
- base::key_prefix_)
- {
- }
-
- typedef typename member_base_impl<T>::member_info member_info;
-
- virtual bool
- pre (member_info& mi)
- {
- if (container (mi))
- return false;
-
- image_type = member_image_type_->image_type (mi.m);
-
- if (var_override_.empty ())
- os << "// " << mi.m.name () << endl
- << "//" << endl;
-
- return true;
- }
-
- virtual void
- traverse_pointer (member_info& mi)
- {
- // Object pointers in views require special treatment.
- //
- if (view_member (mi.m))
- {
- using semantics::class_;
-
- class_& c (*mi.ptr);
- class_* poly_root (polymorphic (c));
- bool poly_derived (poly_root != 0 && poly_root != &c);
-
- if (poly_derived)
- // Use a helper to create a complete chain of images all
- // the way to the root (see libodb/odb/view-image.hxx).
- //
- os << "view_object_image<" << endl
- << " " << class_fq_name (c) << "," << endl
- << " " << class_fq_name (*poly_root) << "," << endl
- << " id_" << db << " >";
- else
- os << "object_traits_impl< " << class_fq_name (c) << ", " <<
- "id_" << db << " >::image_type";
-
- os << " " << mi.var << "value;"
- << endl;
- }
- else
- member_base_impl<T>::traverse_pointer (mi);
- }
-
- virtual void
- traverse_composite (member_info& mi)
- {
- os << image_type << " " << mi.var << "value;"
- << endl;
- }
-
- protected:
- string image_type;
- instance<member_image_type> member_image_type_;
- };
-
- struct image_base: traversal::class_, virtual context
- {
- typedef image_base base;
-
- image_base (): first_ (true) {}
-
- virtual void
- traverse (type& c)
- {
- bool obj (object (c));
-
- // Ignore transient bases. Not used for views.
- //
- if (!(obj || composite (c)))
- return;
-
- if (first_)
- {
- os << ": ";
- first_ = false;
- }
- else
- {
- os << "," << endl
- << " ";
- }
-
- string const& type (class_fq_name (c));
-
- if (obj)
- os << "object_traits_impl< " << type << ", id_" << db <<
- " >::image_type";
- else
- os << "composite_value_traits< " << type << ", id_" << db <<
- " >::image_type";
- }
-
- private:
- bool first_;
- };
-
- struct image_type: traversal::class_, virtual context
- {
- typedef image_type base;
-
- image_type ()
- {
- *this >> names_member_ >> member_;
- }
-
- image_type (image_type const&)
- : root_context (), context () //@@ -Wextra
- {
- *this >> names_member_ >> member_;
- }
-
- virtual void
- image_extra (type&)
- {
- }
-
- virtual void
- traverse (type& c)
- {
- type* poly_root (polymorphic (c));
- bool poly_derived (poly_root != 0 && poly_root != &c);
-
- os << "struct image_type";
-
- if (!view (c))
- {
- // Don't go into the base if we are a derived type in a
- // polymorphic hierarchy.
- //
- if (!poly_derived)
- {
- instance<image_base> b;
- traversal::inherits i (*b);
- inherits (c, i);
- }
- }
-
- os << "{";
-
- if (poly_derived)
- os << "base_traits::image_type* base;"
- << endl;
-
- names (c);
-
- // We don't need a version if this is a composite value type
- // or reuse-abstract object.
- //
- if (!(composite (c) || (abstract (c) && !polymorphic (c))))
- os << "std::size_t version;"
- << endl;
-
- image_extra (c);
-
- os << "};";
- }
-
- private:
- instance<image_member> member_;
- traversal::names names_member_;
- };
-
- // Member-specific traits types for container members.
- //
- struct container_traits: object_members_base, virtual context
- {
- typedef container_traits base;
-
- container_traits (semantics::class_& c)
- : object_members_base (true, false, false), c_ (c)
- {
- }
-
- virtual void
- traverse_pointer (semantics::data_member&, semantics::class_&)
- {
- // We don't want to traverse composite id.
- }
-
- virtual void
- traverse_composite (semantics::data_member* m, semantics::class_& c)
- {
- if (object (c_))
- object_members_base::traverse_composite (m, c);
- else
- {
- // If we are generating traits for a composite value type, then
- // we don't want to go into its bases or it composite members.
- //
- if (m == 0 && &c == &c_)
- names (c);
- }
- }
-
- virtual void
- container_public_extra_pre (semantics::data_member&, semantics::type&)
- {
- }
-
- virtual void
- container_public_extra_post (semantics::data_member&, semantics::type&)
- {
- }
-
- virtual void
- traverse_container (semantics::data_member& m, semantics::type& c)
- {
- using semantics::type;
- using semantics::class_;
-
- // Figure out if this member is from a base object or composite
- // value and if it's from an object, whether it is reuse-abstract.
- //
- bool base, reuse_abst;
-
- if (object (c_))
- {
- base = cur_object != &c_ ||
- !object (dynamic_cast<type&> (m.scope ()));
- reuse_abst = abstract (c_) && !polymorphic (c_);
- }
- else
- {
- base = false; // We don't go into bases.
- reuse_abst = true; // Always abstract.
- }
-
- container_kind_type ck (container_kind (c));
-
- const custom_cxx_type* vct (0);
- const custom_cxx_type* ict (0);
- const custom_cxx_type* kct (0);
-
- type& vt (container_vt (m, &vct));
- type* it (0);
- type* kt (0);
-
- bool ordered (false);
- bool inverse (context::inverse (m, "value"));
-
- switch (ck)
- {
- case ck_ordered:
- {
- if (!unordered (m))
- {
- it = &container_it (m, &ict);
- ordered = true;
- }
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- kt = &container_kt (m, &kct);
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- break;
- }
- }
-
- bool smart (!inverse &&
- (ck != ck_ordered || ordered) &&
- container_smart (c));
-
- string name (flat_prefix_ + public_name (m) + "_traits");
-
- // Figure out column counts.
- //
- size_t id_columns, value_columns, data_columns, cond_columns;
- bool versioned (context::versioned (m));
-
- if (!reuse_abst)
- {
- type& idt (container_idt (m));
-
- if (class_* idc = composite_wrapper (idt))
- id_columns = column_count (*idc).total;
- else
- id_columns = 1;
-
- data_columns = cond_columns = id_columns;
-
- switch (ck)
- {
- case ck_ordered:
- {
- // Add one for the index.
- //
- if (ordered)
- {
- data_columns++;
-
- if (smart)
- cond_columns++;
- }
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- // Add some for the key.
- //
- size_t n;
-
- class_* ptr (object_pointer (*kt));
- semantics::type& t (ptr == 0 ? *kt : utype (*id_member (*ptr)));
-
- if (class_* comp = composite_wrapper (t))
- n = column_count (*comp).total;
- else
- n = 1;
-
- data_columns += n;
-
- // Key is not currently used (see also bind()).
- //
- // cond_columns += n;
-
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- // Not currently used (see also bind())
- //
- // Value is also a key.
- //
- // class_* ptr (object_pointer (vt));
- // semantics::type& t (ptr == 0 ? vt : utype (*id_member (*ptr)));
- //
- // if (class_* comp = composite_wrapper (t))
- // cond_columns += column_count (*comp).total;
- // else
- // cond_columns++;
- //
- break;
- }
- }
-
- {
- class_* ptr (object_pointer (vt));
- semantics::type& t (ptr == 0 ? vt : utype (*id_member (*ptr)));
-
- if (class_* comp = composite_wrapper (t))
- value_columns = column_count (*comp).total;
- else
- value_columns = 1;
-
- data_columns += value_columns;
- }
-
- // Store column counts for the source generator.
- //
- m.set ("id-column-count", id_columns);
- m.set ("value-column-count", value_columns);
- m.set ("cond-column-count", cond_columns);
- m.set ("data-column-count", data_columns);
- }
-
- os << "// " << m.name () << endl
- << "//" << endl
- << "struct " << exp << name;
-
- if (base)
- {
- semantics::class_& b (dynamic_cast<semantics::class_&> (m.scope ()));
- string const& type (class_fq_name (b));
-
- if (object (b))
- os << ": access::object_traits_impl< " << type << ", id_" <<
- db << " >::" << name;
- else
- os << ": access::composite_value_traits< " << type << ", id_" <<
- db << " >::" << public_name (m) << "_traits"; // No prefix_.
- }
-
- os << "{";
-
- container_public_extra_pre (m, c);
-
- if (!reuse_abst)
- {
- // column_count
- //
- os << "static const std::size_t id_column_count = " <<
- id_columns << "UL;";
-
- if (smart)
- os << "static const std::size_t value_column_count = " <<
- value_columns << "UL;"
- << "static const std::size_t cond_column_count = " <<
- cond_columns << "UL;";
-
- os << "static const std::size_t data_column_count = " <<
- data_columns << "UL;"
- << endl;
-
- os << "static const bool versioned = " << versioned << ";"
- << endl;
-
- // Statements.
- //
- os << "static const char insert_statement[];"
- << "static const char select_statement[];";
-
- if (smart)
- os << "static const char update_statement[];";
-
- os << "static const char delete_statement[];"
- << endl;
- }
-
- if (base)
- {
- container_public_extra_post (m, c);
- os << "};";
-
- return;
- }
-
- // container_type
- // container_traits
- // index_type
- // key_type
- // value_type
- //
- os << "typedef ";
-
- {
- semantics::names* hint;
- semantics::type& t (utype (m, hint));
-
- if (semantics::type* wt = wrapper (t))
- {
- // Use the hint from the wrapper unless the wrapped type is
- // qualified. In this case use the hint for the unqualified
- // type.
- //
- hint = t.get<semantics::names*> ("wrapper-hint");
- utype (*wt, hint);
-
- os << c.fq_name (hint);
- }
- else
- // t and c are the same.
- //
- os << t.fq_name (hint);
- }
-
- os << " container_type;";
-
- os << "typedef" << endl
- << "odb::access::container_traits<container_type>" << endl
- << "container_traits_type;";
-
- switch (ck)
- {
- case ck_ordered:
- {
- os << "typedef container_traits_type::index_type index_type;";
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "typedef container_traits_type::key_type key_type;";
- }
- case ck_set:
- case ck_multiset:
- {
- break;
- }
- }
-
- os << "typedef container_traits_type::value_type value_type;"
- << endl;
-
- // functions_type
- //
- switch (ck)
- {
- case ck_ordered:
- {
- os << "typedef " << (smart ? "smart_" : "") <<
- "ordered_functions<index_type, value_type> functions_type;";
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "typedef map_functions<key_type, value_type> " <<
- "functions_type;";
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- os << "typedef set_functions<value_type> functions_type;";
- break;
- }
- }
-
- os << "typedef " << db << "::" << (smart ? "smart_" : "")
- << "container_statements< " << name << " > statements_type;"
- << endl;
-
- // cond_image_type (object id is taken from the object image).
- //
- // For dumb containers we use the id binding directly.
- //
- if (smart)
- {
- os << "struct cond_image_type"
- << "{";
-
- switch (ck)
- {
- case ck_ordered:
- {
- if (ordered)
- {
- os << "// index" << endl
- << "//" << endl;
- instance<image_member> im (
- "index_", *it, ict, "index_type", "index");
- im->traverse (m);
- }
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "// key" << endl
- << "//" << endl;
- instance<image_member> im ("key_", *kt, kct, "key_type", "key");
- im->traverse (m);
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- os << "// value" << endl
- << "//" << endl;
- instance<image_member> im (
- "value_", vt, vct, "value_type", "value");
- im->traverse (m);
- break;
- }
- }
-
- os << "std::size_t version;"
- << "};";
- }
-
- // data_image_type (object id is taken from the object image)
- //
- os << "struct data_image_type"
- << "{";
-
- switch (ck)
- {
- case ck_ordered:
- {
- if (ordered)
- {
- os << "// index" << endl
- << "//" << endl;
- instance<image_member> im (
- "index_", *it, ict, "index_type", "index");
- im->traverse (m);
- }
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "// key" << endl
- << "//" << endl;
- instance<image_member> im ("key_", *kt, kct, "key_type", "key");
- im->traverse (m);
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- break;
- }
- }
-
- os << "// value" << endl
- << "//" << endl;
- instance<image_member> im ("value_", vt, vct, "value_type", "value");
- im->traverse (m);
-
- os << "std::size_t version;"
- << "};";
-
- // bind (cond_image)
- //
- if (smart)
- os << "static void" << endl
- << "bind (" << bind_vector << "," << endl
- << "const " << bind_vector << " id," << endl
- << "std::size_t id_size," << endl
- << "cond_image_type&);"
- << endl;
-
- // bind (data_image)
- //
- os << "static void" << endl
- << "bind (" << bind_vector << "," << endl
- << "const " << bind_vector << " id," << endl
- << "std::size_t id_size," << endl
- << "data_image_type&";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration&";
-
- os << ");"
- << endl;
-
- // bind (cond_image, data_image) (update)
- //
- if (smart)
- {
- os << "static void" << endl
- << "bind (" << bind_vector << "," << endl
- << "const " << bind_vector << " id," << endl
- << "std::size_t id_size," << endl
- << "cond_image_type&," << endl
- << "data_image_type&";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration&";
-
- os << ");"
- << endl;
- }
-
- // grow ()
- //
- if (generate_grow)
- {
- os << "static void" << endl
- << "grow (data_image_type&," << endl
- << truncated_vector;
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration&";
-
- os << ");"
- << endl;
- }
-
- // init (data_image)
- //
- if (!inverse)
- {
- os << "static void" << endl
- << "init (data_image_type&," << endl;
-
- switch (ck)
- {
- case ck_ordered:
- {
- if (ordered)
- os << "index_type*," << endl;
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "const key_type*," << endl;
- break;
- }
- case ck_set:
- case ck_multiset:
- break;
- }
-
- os << "const value_type&";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration&";
-
- os << ");"
- << endl;
- }
-
- // init (cond_image)
- //
- if (smart)
- {
- os << "static void" << endl;
-
- switch (ck)
- {
- case ck_ordered:
- {
- os << "init (cond_image_type&, index_type);";
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- // os << "init (data_image_type&, const key_type&);";
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- // os << "init (data_image_type&, const value_type&);";
- break;
- }
- }
-
- os << endl;
- }
-
- // init (data)
- //
- os << "static void" << endl
- << "init (";
-
- switch (ck)
- {
- case ck_ordered:
- {
- if (ordered)
- os << "index_type&," << endl;
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "key_type&," << endl;
- break;
- }
- case ck_set:
- case ck_multiset:
- break;
- }
-
- os << "value_type&," << endl;
- os << "const data_image_type&," << endl
- << "database*";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration&";
-
- os << ");"
- << endl;
-
- // insert
- //
- os << "static void" << endl;
-
- switch (ck)
- {
- case ck_ordered:
- {
- os << "insert (index_type, const value_type&, void*);";
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "insert (const key_type&, const value_type&, void*);";
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- os << "insert (const value_type&, void*);";
- break;
- }
- }
-
- os << endl;
-
- // select
- //
- os << "static bool" << endl;
-
- switch (ck)
- {
- case ck_ordered:
- {
- os << "select (index_type&, value_type&, void*);";
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "select (key_type&, value_type&, void*);";
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- os << "select (value_type&, void*);";
- break;
- }
- }
-
- os << endl;
-
- // update
- //
- if (smart)
- {
- os << "static void" << endl;
-
- switch (ck)
- {
- case ck_ordered:
- {
- os << "update (index_type, const value_type&, void*);";
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- //os << "update (const key_type&, const value_type&, void*);";
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- //os << "update (const value_type&, const value_type&, void*);";
- break;
- }
- }
-
- os << endl;
- }
-
- // delete_
- //
- os << "static void" << endl
- << "delete_ (";
-
- if (smart)
- {
- switch (ck)
- {
- case ck_ordered:
- {
- os << "index_type, ";
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- break;
- }
- }
- }
-
- os << "void*);"
- << endl;
-
- // persist
- //
- if (!inverse)
- {
- os << "static void" << endl
- << "persist (const container_type&," << endl
- << "statements_type&";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration&";
-
- os << ");"
- << endl;
- }
-
- // load
- //
- os << "static void" << endl
- << "load (container_type&," << endl
- << "statements_type&";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration&";
-
- os << ");"
- << endl;
-
- // update
- //
- if (!(inverse || readonly (member_path_, member_scope_)))
- {
- os << "static void" << endl
- << "update (const container_type&," << endl
- << "statements_type&";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration&";
-
- os << ");"
- << endl;
- }
-
- // erase
- //
- if (!inverse)
- {
- os << "static void" << endl
- << "erase (";
-
- if (smart)
- os << "const container_type*, ";
-
- os << "statements_type&);"
- << endl;
- }
-
- container_public_extra_post (m, c);
-
- os << "};";
- }
-
- protected:
- semantics::class_& c_;
- };
-
- //
- //
- struct section_traits: virtual context
- {
- typedef section_traits base;
-
- section_traits (semantics::class_& c): c_ (c) {}
-
- virtual void
- section_public_extra_pre (user_section&)
- {
- }
-
- virtual void
- section_public_extra_post (user_section&)
- {
- }
-
- virtual void
- traverse (user_section& s)
- {
- semantics::class_* poly_root (polymorphic (c_));
- bool poly (poly_root != 0);
- bool poly_derived (poly && poly_root != &c_);
-
- semantics::data_member* opt (optimistic (c_));
-
- // Treat the special version update sections as abstract in reuse
- // inheritance.
- //
- bool reuse_abst (!poly &&
- (abstract (c_) ||
- s.special == user_section::special_version));
-
- bool load (s.total != 0 && s.separate_load ());
- bool load_con (s.containers && s.separate_load ());
- bool load_opt (s.optimistic () && s.separate_load ());
-
- bool update (s.total != s.inverse + s.readonly); // Always separate.
- bool update_con (s.readwrite_containers);
- bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
-
- // Don't generate anything for empty sections.
- //
- if (!(load || load_con || load_opt ||
- update || update_con || update_opt))
- return;
-
- // If we are adding a new section to a derived class in an optimistic
- // hierarchy, then pretend it inherits from the special version update
- // section.
- //
- user_section* rs (0);
- if (opt != 0)
- {
- // Skip overrides and get to the new section if polymorphic.
- //
- for (rs = &s; poly && rs->base != 0; rs = rs->base) ;
-
- if (rs != 0)
- {
- if (rs->object != &opt->scope ())
- rs->base = &(poly ? poly_root : &opt->scope ())->
- get<user_sections> ("user-sections").back ();
- else
- rs = 0;
- }
- }
-
- string name (public_name (*s.member) + "_traits");
-
- os << "// " << s.member->name () << endl
- << "//" << endl
- << "struct " << exp << name
- << "{";
-
- os << "typedef object_traits_impl<object_type, id_" << db <<
- ">::image_type image_type;"
- << "typedef object_traits_impl<object_type, id_" << db <<
- ">::id_image_type id_image_type;"
- << endl;
-
- section_public_extra_pre (s);
-
- // bind (id, image_type)
- //
- // If id is NULL, then id is ignored (select). Otherwise, it is
- // copied at the end (update).
- //
- if (load || load_opt || update || update_opt)
- {
- os << "static std::size_t" << endl
- << "bind (" << bind_vector << "," << endl
- << "const " << bind_vector << " id," << endl
- << "std::size_t id_size," << endl
- << "image_type&," << endl
- << db << "::statement_kind";
-
- if (s.versioned)
- os << "," << endl
- << "const schema_version_migration&";
-
- os << ");"
- << endl;
- }
-
- // grow ()
- //
- // We have to have out own version because the truncated vector
- // will have different number of elements.
- //
- if (generate_grow && (load || load_opt))
- {
- os << "static bool" << endl
- << "grow (image_type&," << endl
- << truncated_vector;
-
- if (s.versioned)
- os << "," << endl
- << "const schema_version_migration&";
-
- os << ");"
- << endl;
- }
-
- // init (object, image)
- //
- if (load)
- {
- os << "static void" << endl
- << "init (object_type&," << endl
- << "const image_type&," << endl
- << "database*";
-
- if (s.versioned)
- os << "," << endl
- << "const schema_version_migration&";
-
- os << ");"
- << endl;
- }
-
- // init (image, object)
- //
- if (update)
- {
- os << "static " << (generate_grow ? "bool" : "void") << endl
- << "init (image_type&," << endl
- << "const object_type&";
-
- if (s.versioned)
- os << "," << endl
- << "const schema_version_migration&";
-
- os << ");"
- << endl;
- }
-
- // The rest does not apply to reuse-abstract sections.
- //
- if (reuse_abst)
- {
- section_public_extra_post (s);
- os << "};";
- return;
- }
-
- // column_count
- //
- column_count_type const& cc (column_count (poly ? *poly_root : c_));
-
- // Generate load and update column counts even when they are zero so
- // that we can instantiate section_statements.
- //
- os << "static const std::size_t id_column_count = " << cc.id << "UL;";
-
- os << "static const std::size_t managed_optimistic_load_column_count" <<
- " = " << cc.optimistic_managed << "UL;"
- << "static const std::size_t load_column_count = " <<
- (load ? s.total_total () : 0) << "UL;";
-
- os << "static const std::size_t managed_optimistic_update_column_count" <<
- " = " << (poly_derived ? 0 : cc.optimistic_managed) << "UL;"
- << "static const std::size_t update_column_count = " <<
- (update ? s.total - s.inverse - s.readonly : 0) << "UL;"
- << endl;
-
- os << "static const bool versioned = " << s.versioned << ";"
- << endl;
-
- // Statements.
- //
- if (load || load_opt)
- os << "static const char select_statement[];"
- << endl;
-
- if (update || update_opt)
- os << "static const char update_statement[];"
- << endl;
-
- // Section statements.
- //
- if (load || load_opt || update || update_opt)
- os << "typedef " << db << "::section_statements< object_type, " <<
- name << " > statements_type;"
- << endl;
-
- // We pass statement cache instead of just statements because
- // we may also need statements for containers.
- //
-
- // load ()
- //
- if (load || load_opt || load_con)
- os << "static void" << endl
- << "load (extra_statement_cache_type&, object_type&" <<
- (poly ? ", bool top = true" : "") << ");"
- << endl;
-
- // update ()
- //
- if (update || update_opt || update_con)
- os << "static void" << endl
- << "update (extra_statement_cache_type&, const object_type&" <<
- (poly_derived && s.base != 0 ? ", bool base = true" : "") << ");"
- << endl;
-
- section_public_extra_post (s);
-
- os << "};";
-
- if (rs != 0)
- rs->base = 0;
- }
-
- protected:
- semantics::class_& c_;
- };
-
- // First pass over objects, views, and composites. Some code must be
- // split into two parts to deal with yet undefined types.
- //
- struct class1: traversal::class_, virtual context
- {
- typedef class1 base;
-
- class1 ()
- : typedefs_ (false),
- id_image_member_ ("id_"),
- version_image_member_ ("version_"),
- discriminator_image_member_ ("discriminator_"),
- query_columns_type_ (false, true, false),
- pointer_query_columns_type_ (true, true, false)
- {
- *this >> defines_ >> *this;
- *this >> typedefs_ >> *this;
- }
-
- class1 (class1 const&)
- : root_context (), //@@ -Wextra
- context (),
- typedefs_ (false),
- id_image_member_ ("id_"),
- version_image_member_ ("version_"),
- discriminator_image_member_ ("discriminator_"),
- query_columns_type_ (false, true, false),
- pointer_query_columns_type_ (true, true, false)
- {
- *this >> defines_ >> *this;
- *this >> typedefs_ >> *this;
- }
-
- virtual void
- traverse (type& c)
- {
- class_kind_type ck (class_kind (c));
-
- if (ck == class_other ||
- (!options.at_once () && class_file (c) != unit.file ()))
- return;
-
- names (c);
-
- switch (ck)
- {
- case class_object: traverse_object (c); break;
- case class_view: traverse_view (c); break;
- case class_composite: traverse_composite (c); break;
- default: break;
- }
- }
-
- virtual void
- object_public_extra_pre (type&)
- {
- }
-
- virtual void
- object_public_extra_post (type&)
- {
- }
-
- virtual void
- traverse_object (type&);
-
- virtual void
- view_public_extra_pre (type&)
- {
- }
-
- virtual void
- view_public_extra_post (type&)
- {
- }
-
- virtual void
- traverse_view (type&);
-
- virtual void
- traverse_composite (type&);
-
- private:
- traversal::defines defines_;
- typedefs typedefs_;
-
- instance<image_type> image_type_;
- instance<image_member> id_image_member_;
- instance<image_member> version_image_member_;
- instance<image_member> discriminator_image_member_;
-
- instance<query_columns_type> query_columns_type_;
- instance<query_columns_type> pointer_query_columns_type_;
- };
-
- // Second pass over objects, views, and composites.
- //
- struct class2: traversal::class_, virtual context
- {
- typedef class2 base;
-
- class2 ()
- : typedefs_ (false),
- query_columns_type_ (false, true, false),
- query_columns_type_inst_ (false, false, true),
- view_query_columns_type_ (true)
- {
- *this >> defines_ >> *this;
- *this >> typedefs_ >> *this;
- }
-
- class2 (class2 const&)
- : root_context (), //@@ -Wextra
- context (),
- typedefs_ (false),
- query_columns_type_ (false, true, false),
- query_columns_type_inst_ (false, false, true),
- view_query_columns_type_ (true)
- {
- *this >> defines_ >> *this;
- *this >> typedefs_ >> *this;
- }
-
- virtual void
- traverse (type& c)
- {
- class_kind_type ck (class_kind (c));
-
- if (ck == class_other ||
- (!options.at_once () && class_file (c) != unit.file ()))
- return;
-
- names (c);
-
- switch (ck)
- {
- case class_object: traverse_object (c); break;
- case class_view: traverse_view (c); break;
- case class_composite: traverse_composite (c); break;
- default: break;
- }
- }
-
- virtual void
- traverse_object (type& c)
- {
- if (options.generate_query ())
- {
- os << "// " << class_name (c) << endl
- << "//" << endl;
-
- // query_columns
- //
- // If we don't have any pointers, then query_columns is generated
- // in pass 1 (see the comment in class1 for details).
- //
- if (has_a (c, test_pointer | include_base))
- query_columns_type_->traverse (c);
-
- // Generate extern template declarations.
- //
- if (multi_dynamic)
- query_columns_type_inst_->traverse (c);
- }
-
- // Move header comment out of if-block if adding any code here.
- }
-
- virtual void
- traverse_view (type& c)
- {
- // query_columns
- //
- if (c.get<size_t> ("object-count") != 0)
- {
- os << "// " << class_name (c) << endl
- << "//" << endl;
-
- view_query_columns_type_->traverse (c);
- }
-
- // Move header comment out of if-block if adding any code here.
- }
-
- virtual void
- traverse_composite (type&)
- {
- }
-
- private:
- traversal::defines defines_;
- typedefs typedefs_;
-
- instance<query_columns_type> query_columns_type_;
- instance<query_columns_type> query_columns_type_inst_;
- instance<view_query_columns_type> view_query_columns_type_;
- };
-
- struct include: virtual context
- {
- typedef include base;
-
- virtual void
- generate ()
- {
- os << "#include <odb/details/buffer.hxx>" << endl
- << endl;
-
- os << "#include <odb/" << db << "/version.hxx>" << endl
- << "#include <odb/" << db << "/forward.hxx>" << endl
- << "#include <odb/" << db << "/binding.hxx>" << endl
- << "#include <odb/" << db << "/" << db << "-types.hxx>" << endl;
-
- if (options.generate_query ())
- {
- os << "#include <odb/" << db << "/query.hxx>" << endl;
-
- if (multi_dynamic)
- os << "#include <odb/" << db << "/query-dynamic.hxx>" << endl;
- }
-
- os << endl;
- }
- };
- }
-}
-
-#endif // ODB_RELATIONAL_HEADER_HXX
diff --git a/odb/relational/inline.hxx b/odb/relational/inline.hxx
deleted file mode 100644
index 48ababf..0000000
--- a/odb/relational/inline.hxx
+++ /dev/null
@@ -1,689 +0,0 @@
-// file : odb/relational/inline.hxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#ifndef ODB_RELATIONAL_INLINE_HXX
-#define ODB_RELATIONAL_INLINE_HXX
-
-#include <odb/diagnostics.hxx>
-#include <odb/relational/context.hxx>
-#include <odb/relational/common.hxx>
-
-namespace relational
-{
- namespace inline_
- {
- //
- // get/set null (composite value only)
- //
-
- struct null_member: virtual member_base
- {
- typedef null_member base;
-
- null_member (bool get)
- : member_base (0, 0, string (), string ()), get_ (get) {}
-
- protected:
- bool get_;
- };
-
- template <typename T>
- struct null_member_impl: null_member, virtual member_base_impl<T>
- {
- typedef null_member_impl base_impl;
-
- null_member_impl (base const& x): base (x) {}
-
- typedef typename member_base_impl<T>::member_info member_info;
-
- virtual bool
- pre (member_info& mi)
- {
- // If the member is soft- added or deleted, check the version.
- //
- unsigned long long av (added (mi.m));
- unsigned long long dv (deleted (mi.m));
- if (av != 0 || dv != 0)
- {
- os << "if (";
-
- if (av != 0)
- os << "svm >= schema_version_migration (" << av << "ULL, true)";
-
- if (av != 0 && dv != 0)
- os << " &&" << endl;
-
- if (dv != 0)
- os << "svm <= schema_version_migration (" << dv << "ULL, true)";
-
- os << ")"
- << "{";
- }
-
- // If the whole value type is readonly, then set will never be
- // called with sk == statement_update.
- //
- if (!get_ && !readonly (*context::top_object))
- {
- semantics::class_* c;
-
- if (readonly (mi.m) || ((c = composite (mi.t)) && readonly (*c)))
- os << "if (sk == statement_insert)" << endl;
- }
-
- return true;
- }
-
- virtual void
- post (member_info& mi)
- {
- if (added (mi.m) || deleted (mi.m))
- os << "}";
- }
-
- virtual void
- traverse_composite (member_info& mi)
- {
- string traits ("composite_value_traits< " + mi.fq_type () + ", id_" +
- db.string () + " >");
-
- if (get_)
- os << "r = r && " << traits << "::get_null (" <<
- "i." << mi.var << "value";
- else
- os << traits << "::set_null (i." << mi.var << "value, sk";
-
- if (versioned (*composite (mi.t)))
- os << ", svm";
-
- os << ");";
- }
- };
-
- struct null_base: traversal::class_, virtual context
- {
- typedef null_base base;
-
- null_base (bool get): get_ (get) {}
-
- virtual void
- traverse (type& c)
- {
- // Ignore transient bases.
- //
- if (!composite (c))
- return;
-
- string traits ("composite_value_traits< " + class_fq_name (c) +
- ", id_" + db.string () + " >");
-
- // If the derived value type is readonly, then set will never be
- // called with sk == statement_update.
- //
- if (!get_ && readonly (c) && !readonly (*context::top_object))
- os << "if (sk == statement_insert)" << endl;
-
- if (get_)
- os << "r = r && " << traits << "::get_null (i";
- else
- os << traits << "::set_null (i, sk";
-
- if (versioned (c))
- os << ", svm";
-
- os << ");";
- }
-
- protected:
- bool get_;
- };
-
- //
- //
- struct class_: traversal::class_, virtual context
- {
- typedef class_ base;
-
- class_ ()
- : typedefs_ (false),
- get_null_base_ (true),
- get_null_member_ (true),
- set_null_base_ (false),
- set_null_member_ (false)
- {
- init ();
- }
-
- class_ (class_ const&)
- : root_context (), //@@ -Wextra
- context (),
- typedefs_ (false),
- get_null_base_ (true),
- get_null_member_ (true),
- set_null_base_ (false),
- set_null_member_ (false)
- {
- init ();
- }
-
- void
- init ()
- {
- *this >> defines_ >> *this;
- *this >> typedefs_ >> *this;
-
- get_null_base_inherits_ >> get_null_base_;
- get_null_member_names_ >> get_null_member_;
-
- set_null_base_inherits_ >> set_null_base_;
- set_null_member_names_ >> set_null_member_;
- }
-
- virtual void
- traverse (type& c)
- {
- class_kind_type ck (class_kind (c));
-
- if (ck == class_other ||
- (!options.at_once () && class_file (c) != unit.file ()))
- return;
-
- names (c);
-
- context::top_object = context::cur_object = &c;
-
- switch (ck)
- {
- case class_object: traverse_object (c); break;
- case class_view: traverse_view (c); break;
- case class_composite: traverse_composite (c); break;
- default: break;
- }
-
- context::top_object = context::cur_object = 0;
- }
-
- virtual void
- object_extra (type&)
- {
- }
-
- virtual void
- traverse_object (type& c)
- {
- using semantics::data_member;
-
- data_member_path* id (id_member (c));
- data_member* idf (id ? id->front () : 0);
- bool auto_id (id && auto_ (*id));
- bool base_id (id && &idf->scope () != &c); // Comes from base.
- data_member* optimistic (context::optimistic (c));
-
- // Base class that contains the object id and version for optimistic
- // concurrency.
- //
- type* base (base_id ? dynamic_cast<type*> (&idf->scope ()) : 0);
-
- type* poly_root (context::polymorphic (c));
- bool poly (poly_root != 0);
- bool poly_derived (poly && poly_root != &c);
-
- bool abst (abstract (c));
- bool reuse_abst (abst && !poly);
-
- bool versioned (context::versioned (c));
-
- string const& type (class_fq_name (c));
- string traits ("access::object_traits_impl< " + type + ", id_" +
- db.string () + " >");
-
- user_sections& uss (c.get<user_sections> ("user-sections"));
-
- os << "// " << class_name (c) << endl
- << "//" << endl
- << endl;
-
- object_extra (c);
-
- if (id != 0 && base_id)
- {
- if (!poly_derived)
- {
- // id (id_image_type)
- //
- if (auto_id)
- {
- os << "inline" << endl
- << traits << "::id_type" << endl
- << traits << "::" << endl
- << "id (const id_image_type& i)"
- << "{"
- << "return object_traits_impl< " << class_fq_name (*base) <<
- ", id_" << db << " >::id (i);"
- << "}";
- }
-
- // id (image_type)
- //
- if (options.generate_query ())
- {
- os << "inline" << endl
- << traits << "::id_type" << endl
- << traits << "::" << endl
- << "id (const image_type& i)"
- << "{"
- << "return object_traits_impl< " << class_fq_name (*base) <<
- ", id_" << db << " >::id (i);"
- << "}";
- }
-
- // version (image_type)
- //
- if (optimistic != 0)
- {
- os << "inline" << endl
- << traits << "::version_type" << endl
- << traits << "::" << endl
- << "version (const image_type& i)"
- << "{"
- << "return object_traits_impl< " << class_fq_name (*base) <<
- ", id_" << db << " >::version (i);"
- << "}";
- }
- }
-
- // bind (id_image_type)
- //
- os << "inline" << endl
- << "void " << traits << "::" << endl
- << "bind (" << bind_vector << " b, id_image_type& i" <<
- (optimistic != 0 ? ", bool bv" : "") << ")"
- << "{"
- << "object_traits_impl< " << class_fq_name (*base) << ", id_" <<
- db << " >::bind (b, i" << (optimistic != 0 ? ", bv" : "") << ");"
- << "}";
-
- os << "inline" << endl
- << "void " << traits << "::" << endl
- << "init (id_image_type& i, const id_type& id" <<
- (optimistic != 0 ? ", const version_type* v" : "") << ")"
- << "{"
- << "object_traits_impl< " << class_fq_name (*base) << ", id_" <<
- db << " >::init (i, id" << (optimistic != 0 ? ", v" : "") << ");"
- << "}";
- }
-
- if (poly_derived)
- {
- size_t depth (polymorphic_depth (c));
-
- // check_version
- //
- os << "inline" << endl
- << "bool " << traits << "::" << endl
- << "check_version (const std::size_t* v, const image_type& i)"
- << "{"
- << "return ";
-
- string image ("i.");
- for (size_t i (0); i < depth; ++i)
- {
- os << (i == 0 ? "" : " ||") << endl
- << " v[" << i << "UL] != " << image << "version";
-
- image += "base->";
- }
-
- os << ";"
- << "}";
-
- // update_version
- //
- os << "inline" << endl
- << "void " << traits << "::" << endl
- << "update_version (std::size_t* v, const image_type& i, " <<
- db << "::binding* b)"
- << "{";
-
- image = "i.";
- for (size_t i (0); i < depth; ++i)
- {
- os << "v[" << i << "UL] = " << image << "version;";
- image += "base->";
- }
-
- // A poly-abstract class always has only one entry in the
- // bindings array.
- //
- if (abst)
- os << "b[0].version++;";
- else
- for (size_t i (0); i < depth; ++i)
- os << "b[" << i << "UL].version++;";
-
- os << "}";
- }
-
- // The rest does not apply to reuse-abstract objects.
- //
- if (reuse_abst)
- return;
-
- // erase (object_type)
- //
- if (id != 0 && !poly && optimistic == 0 &&
- !has_a (c, test_smart_container))
- {
- os << "inline" << endl
- << "void " << traits << "::" << endl
- << "erase (database& db, const object_type& obj)"
- << "{"
- << "callback (db, obj, callback_event::pre_erase);"
- << "erase (db, id (obj));";
-
- // Note that we don't reset sections since the object is now
- // transient and the state of a section in a transient object
- // is undefined.
-
- os << "callback (db, obj, callback_event::post_erase);"
- << "}";
- }
-
- // load (section) [thunk version; poly_derived is true]
- //
- if (uss.count (user_sections::count_total |
- user_sections::count_load |
- (poly ? user_sections::count_load_empty : 0)) != 0 &&
- uss.count (user_sections::count_new |
- user_sections::count_load |
- (poly ? user_sections::count_load_empty : 0)) == 0)
- {
- os << "inline" << endl
- << "bool " << traits << "::" << endl
- << "load (connection& conn, object_type& obj, section& s, " <<
- "const info_type* pi)"
- << "{"
- << "return base_traits::load (conn, obj, s, pi);"
- << "}";
- }
-
- // update (section) [thunk version; poly_derived is true]
- //
- if (uss.count (user_sections::count_total |
- user_sections::count_update |
- (poly ? user_sections::count_update_empty : 0)) != 0 &&
- uss.count (user_sections::count_new |
- user_sections::count_update |
- (poly ? user_sections::count_update_empty : 0)) == 0)
- {
- os << "inline" << endl
- << "bool " << traits << "::" << endl
- << "update (connection& conn, const object_type& obj, " <<
- "const section& s, const info_type* pi)"
- << "{"
- << "return base_traits::update (conn, obj, s, pi);"
- << "}";
- }
-
- // load_()
- //
- if (id != 0 &&
- !(poly_derived ||
- has_a (c, test_container | include_eager_load, &main_section) ||
- uss.count (user_sections::count_new |
- user_sections::count_load |
- (poly ? user_sections::count_load_empty : 0)) != 0))
- {
- os << "inline" << endl
- << "void " << traits << "::" << endl
- << "load_ (statements_type& sts," << endl
- << "object_type& obj," << endl
- << "bool";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (sts);"
- << "ODB_POTENTIALLY_UNUSED (obj);";
-
- if (versioned)
- os << "ODB_POTENTIALLY_UNUSED (svm);";
-
- os << endl;
-
- // Mark eager sections as loaded.
- //
- for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
- {
- // Skip special sections.
- //
- if (i->special == user_section::special_version)
- continue;
-
- data_member& m (*i->member);
-
- // If the section is soft- added or deleted, check the version.
- // We can only end up here if the object itself is versioned
- // (simple value section).
- //
- unsigned long long av (added (m));
- unsigned long long dv (deleted (m));
- if (av != 0 || dv != 0)
- {
- os << "if (";
-
- if (av != 0)
- os << "svm >= schema_version_migration (" << av << "ULL, true)";
-
- if (av != 0 && dv != 0)
- os << " &&" << endl;
-
- if (dv != 0)
- os << "svm <= schema_version_migration (" << dv << "ULL, true)";
-
- os << ")" << endl;
- }
-
- // Section access is always by reference.
- //
- member_access& ma (m.get<member_access> ("get"));
- if (!ma.synthesized)
- os << "// From " << location_string (ma.loc, true) << endl;
-
- os << ma.translate ("obj") << ".reset (true, false);"
- << endl;
- }
-
- os << "}";
- }
-
- if (poly && need_image_clone && options.generate_query ())
- {
- // root_image ()
- //
- os << "inline" << endl
- << traits << "::root_traits::image_type&" << endl
- << traits << "::" << endl
- << "root_image (image_type& i)"
- << "{";
-
- if (poly_derived)
- os << "return base_traits::root_image (*i.base);";
- else
- os << "return i;";
-
- os << "}";
-
- // clone_image ()
- //
- os << "inline" << endl
- << traits << "::image_type*" << endl
- << traits << "::" << endl
- << "clone_image (image_type& i)"
- << "{";
-
- if (poly_derived)
- os << "details::unique_ptr<base_traits::image_type> p (" << endl
- << "base_traits::clone_image (*i.base));"
- << "image_type* c (new image_type (i));"
- << "c->base = p.release ();"
- << "return c;";
- else
- os << "return new image_type (i);";
-
- os << "}";
-
- // copy_image ()
- //
- os << "inline" << endl
- << "void " << traits << "::" << endl
- << "copy_image (image_type& d, image_type& s)"
- << "{";
-
- if (poly_derived)
- os << "base_traits::image_type* b (d.base);"
- << "base_traits::copy_image (*b, *s.base);"
- << "d = s;" // Overwrites the base pointer.
- << "d.base = b;";
- else
- os << "d = s;";
-
- os << "}";
-
- // free_image ()
- //
- os << "inline" << endl
- << "void " << traits << "::" << endl
- << "free_image (image_type* i)"
- << "{";
-
- if (poly_derived)
- os << "base_traits::free_image (i->base);";
-
- os << "delete i;"
- << "}";
- }
- }
-
- virtual void
- view_extra (type&)
- {
- }
-
- virtual void
- traverse_view (type& c)
- {
- string const& type (class_fq_name (c));
- string traits ("access::view_traits_impl< " + type + ", id_" +
- db.string () + " >");
-
- os << "// " << class_name (c) << endl
- << "//" << endl
- << endl;
-
- view_extra (c);
- }
-
- virtual void
- traverse_composite (type& c)
- {
- bool versioned (context::versioned (c));
-
- string const& type (class_fq_name (c));
- string traits ("access::composite_value_traits< " + type + ", id_" +
- db.string () + " >");
-
- os << "// " << class_name (c) << endl
- << "//" << endl
- << endl;
-
- if (!has_a (c, test_container))
- {
- // get_null (image)
- //
- os << "inline" << endl
- << "bool " << traits << "::" << endl
- << "get_null (const image_type& i";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{";
-
- if (versioned)
- os << "ODB_POTENTIALLY_UNUSED (svm);"
- << endl;
-
- os << "bool r (true);";
-
- inherits (c, get_null_base_inherits_);
- names (c, get_null_member_names_);
-
- os << "return r;"
- << "}";
-
- // set_null (image)
- //
- os << "inline" << endl
- << "void " << traits << "::" << endl
- << "set_null (image_type& i," << endl
- << db << "::statement_kind sk";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (sk);";
-
- if (versioned)
- os << "ODB_POTENTIALLY_UNUSED (svm);";
-
- os << endl
- << "using namespace " << db << ";"
- << endl;
-
- inherits (c, set_null_base_inherits_);
- names (c, set_null_member_names_);
-
- os << "}";
- }
- }
-
- private:
- traversal::defines defines_;
- typedefs typedefs_;
-
- instance<null_base> get_null_base_;
- traversal::inherits get_null_base_inherits_;
- instance<null_member> get_null_member_;
- traversal::names get_null_member_names_;
-
- instance<null_base> set_null_base_;
- traversal::inherits set_null_base_inherits_;
- instance<null_member> set_null_member_;
- traversal::names set_null_member_names_;
- };
-
- struct include: virtual context
- {
- typedef include base;
-
- virtual void
- generate ()
- {
- if (features.polymorphic_object && options.generate_query ())
- os << "#include <odb/details/unique-ptr.hxx>" << endl
- << endl;
- }
- };
- }
-}
-
-#endif // ODB_RELATIONAL_INLINE_HXX
diff --git a/odb/relational/model.hxx b/odb/relational/model.hxx
deleted file mode 100644
index b7a07ea..0000000
--- a/odb/relational/model.hxx
+++ /dev/null
@@ -1,868 +0,0 @@
-// file : odb/relational/model.hxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#ifndef ODB_RELATIONAL_MODEL_HXX
-#define ODB_RELATIONAL_MODEL_HXX
-
-#include <map>
-#include <set>
-#include <cassert>
-#include <sstream>
-
-#include <odb/semantics/relational.hxx>
-
-#include <odb/relational/common.hxx>
-#include <odb/relational/context.hxx>
-
-namespace relational
-{
- namespace model
- {
- typedef std::set<qname> tables;
- typedef std::map<qname, semantics::node*> deleted_table_map;
- typedef std::map<uname, semantics::data_member*> deleted_column_map;
-
- struct object_columns: object_columns_base, virtual context
- {
- typedef object_columns base;
-
- object_columns (sema_rel::model& model,
- sema_rel::table& table,
- bool object)
- : model_ (model),
- table_ (table),
- object_ (object),
- pkey_ (0),
- id_override_ (false)
- {
- }
-
- virtual void
- traverse_object (semantics::class_& c)
- {
- if (context::top_object != &c)
- {
- // We are in one of the bases. Set the id_prefix to its
- // (unqualified) name.
- //
- string t (id_prefix_);
- id_prefix_ = class_name (c) + "::";
- object_columns_base::traverse_object (c);
- id_prefix_ = t;
- }
- else
- object_columns_base::traverse_object (c);
- }
-
- virtual void
- traverse_composite (semantics::data_member* m, semantics::class_& c)
- {
- string t (id_prefix_);
-
- if (m != 0)
- // Member of a composite type. Add the data member to id_prefix.
- //
- if (!id_override_)
- id_prefix_ += m->name () + ".";
- else
- id_override_ = false;
- else
- // Composite base. Add its unqualified name to id_prefix.
- //
- id_prefix_ += class_name (c) + "::";
-
- object_columns_base::traverse_composite (m, c);
-
- id_prefix_ = t;
- }
-
- virtual void
- traverse (semantics::data_member& m,
- semantics::type& t,
- string const& kp,
- string const& dn,
- semantics::class_* to = 0)
- {
- // This overrides the member name for a composite container value
- // or key.
- //
- if (!kp.empty ())
- {
- semantics::class_* c (object_pointer (t));
- if (composite_wrapper (c == 0 ? t : utype (*id_member (*c))))
- {
- id_prefix_ = kp + ".";
- id_override_ = true;
- }
- }
-
- object_columns_base::traverse (m, t, kp, dn, to);
- }
-
- using object_columns_base::traverse;
-
- virtual bool
- traverse_column (semantics::data_member& m, string const& name, bool)
- {
- if (semantics::data_member* m = deleted_member (member_path_))
- {
- table_.get<deleted_column_map> ("deleted-map")[name] = m;
- return false;
- }
-
- string col_id (id_prefix_ +
- (key_prefix_.empty () ? m.name () : key_prefix_));
-
- sema_rel::column& c (
- model_.new_node<sema_rel::column> (col_id, type (m), null (m)));
- c.set ("cxx-location", m.location ());
- c.set ("member-path", member_path_);
- model_.new_edge<sema_rel::unames> (table_, c, name);
-
- // An id member cannot have a default value.
- //
- if (!object_columns_base::id ())
- {
- string const& d (default_ (m));
-
- if (!d.empty ())
- c.default_ (d);
- }
-
- // If we have options, add them.
- //
- string const& o (column_options (m, key_prefix_));
-
- if (!o.empty ())
- c.options (o);
-
- constraints (m, name, col_id, c);
- return true;
- }
-
- virtual string
- type (semantics::data_member&)
- {
- return object_columns_base::column_type ();
- }
-
- virtual bool
- null (semantics::data_member&)
- {
- return !object_columns_base::id () && object_columns_base::null ();
- }
-
- virtual string
- default_null (semantics::data_member&)
- {
- return "NULL";
- }
-
- virtual string
- default_bool (semantics::data_member&, bool v)
- {
- // Most databases do not support boolean literals. Those that
- // do should override this.
- //
- return (v ? "1" : "0");
- }
-
- virtual string
- default_integer (semantics::data_member&, unsigned long long v, bool neg)
- {
- std::ostringstream ostr;
- ostr << (neg ? "-" : "") << v;
- return ostr.str ();
- }
-
- virtual string
- default_float (semantics::data_member&, double v)
- {
- std::ostringstream ostr;
- ostr << v;
- return ostr.str ();
- }
-
- virtual string
- default_string (semantics::data_member&, string const& v)
- {
- return quote_string (v);
- }
-
- virtual string
- default_enum (semantics::data_member&,
- tree /*enumerator*/,
- string const& /*name*/)
- {
- // Has to be implemented by the database-specific override.
- //
- assert (false);
- return string ();
- }
-
- virtual void
- primary_key (sema_rel::primary_key&)
- {
- }
-
- virtual void
- constraints (semantics::data_member& m,
- string const& /* name */,
- string const& /* id */,
- sema_rel::column& c)
- {
- if (object_)
- {
- if (semantics::data_member* idm = id ())
- {
- if (pkey_ == 0)
- {
- pkey_ = &model_.new_node<sema_rel::primary_key> (
- m.count ("auto"));
- pkey_->set ("cxx-location", idm->location ());
-
- // In most databases the primary key constraint can be
- // manipulated without an explicit name. So we use the special
- // empty name for primary keys in order not to clash with
- // columns and other constraints. If the target database does
- // not support unnamed primary key manipulation, then the
- // database-specific code will have to come up with a suitable
- // name.
- //
- model_.new_edge<sema_rel::unames> (table_, *pkey_, "");
- primary_key (*pkey_);
- }
-
- model_.new_edge<sema_rel::contains> (*pkey_, c);
- }
- }
- }
-
- virtual void
- traverse_pointer (semantics::data_member& m, semantics::class_& c)
- {
- // Ignore inverse object pointers.
- //
- if (inverse (m, key_prefix_))
- return;
-
- if (deleted (member_path_))
- {
- // Still traverse it as columns so that we can populate the
- // deleted map.
- //
- object_columns_base::traverse_pointer (m, c);
- return;
- }
-
- // Get the position of the last column.
- //
- sema_rel::table::names_iterator i (table_.names_end ());
-
- while (i != table_.names_begin ())
- {
- --i;
- if (i->nameable ().is_a<sema_rel::column> ())
- break;
- }
-
- // Traverse the object pointer as columns.
- //
- object_columns_base::traverse_pointer (m, c);
-
- // Get to the first column that we have added.
- //
- if (i != table_.names_end ())
- ++i; // Next column.
- else
- i = table_.names_begin ();
-
- foreign_key (m, c, i);
- }
-
- virtual void
- traverse_points_to (semantics::data_member& m, semantics::class_& c)
- {
- if (deleted (member_path_))
- {
- // Still traverse it as columns so that we can populate the
- // deleted map.
- //
- object_columns_base::traverse_points_to (m, c);
- return;
- }
-
- // Get the position of the last column.
- //
- sema_rel::table::names_iterator i (table_.names_end ());
-
- while (i != table_.names_begin ())
- {
- --i;
- if (i->nameable ().is_a<sema_rel::column> ())
- break;
- }
-
- // Traverse the data member as columns.
- //
- object_columns_base::traverse_points_to (m, c);
-
- // Get to the first column that we have added.
- //
- if (i != table_.names_end ())
- ++i; // Next column.
- else
- i = table_.names_begin ();
-
- foreign_key (m, c, i);
- }
-
- virtual void
- foreign_key (semantics::data_member& m,
- semantics::class_& c,
- sema_rel::table::names_iterator i)
- {
- using sema_rel::column;
- using sema_rel::foreign_key;
-
- string id (id_prefix_ +
- (key_prefix_.empty () ? m.name () : key_prefix_));
-
- deferrable def (
- m.get<deferrable> ("deferrable",
- options.fkeys_deferrable_mode ()[db]));
-
- foreign_key::action_type on_delete (
- m.get<foreign_key::action_type> (
- "on-delete", foreign_key::no_action));
-
- foreign_key& fk (
- model_.new_node<foreign_key> (id, table_name (c), def, on_delete));
-
- fk.set ("cxx-location", m.location ());
-
- bool simple;
-
- // Get referenced columns.
- //
- {
- data_member_path& id (*id_member (c));
-
- instance<object_columns_list> ocl;
- ocl->traverse (id);
-
- for (object_columns_list::iterator i (ocl->begin ());
- i != ocl->end (); ++i)
- fk.referenced_columns ().push_back (i->name);
-
- simple = (fk.referenced_columns ().size () == 1);
- }
-
- // Get referencing columns.
- //
- for (; i != table_.names_end (); ++i)
- {
- if (column* c = dynamic_cast<column*> (&i->nameable ()))
- model_.new_edge<sema_rel::contains> (fk, *c);
- else
- break;
- }
-
- // Derive the constraint name. Generally, we want it to be based
- // on the column name. This is straightforward for single-column
- // references. In case of a composite id, we will need to use the
- // column prefix which is based on the data member name, unless
- // overridden by the user. In the latter case the prefix can be
- // empty, in which case we will just fall back on the member's
- // public name.
- //
- string name;
-
- if (simple)
- name = fk.contains_begin ()->column ().name ();
- else
- {
- string p (column_prefix (m, key_prefix_, default_name_).prefix);
-
- if (p.empty ())
- p = public_name_db (m);
- else if (p[p.size () - 1] == '_')
- p.resize (p.size () - 1); // Remove trailing underscore.
-
- name = column_prefix_.prefix + p;
- }
-
- model_.new_edge<sema_rel::unames> (
- table_, fk, fkey_name (table_.name (), name));
- }
-
- protected:
- string
- default_ (semantics::data_member&);
-
- protected:
- sema_rel::model& model_;
- sema_rel::table& table_;
- bool object_;
- sema_rel::primary_key* pkey_;
- string id_prefix_;
- bool id_override_;
- };
-
- struct object_indexes: traversal::class_, virtual context
- {
- typedef object_indexes base;
-
- object_indexes (sema_rel::model& model, sema_rel::table& table)
- : model_ (model), table_ (table)
- {
- *this >> inherits_ >> *this;
- }
-
- object_indexes (object_indexes const& x)
- : root_context (), context (), //@@ -Wextra
- model_ (x.model_), table_ (x.table_)
- {
- *this >> inherits_ >> *this;
- }
-
- virtual void
- traverse (type& c)
- {
- if (!object (c)) // Ignore transient bases.
- return;
-
- // Polymorphic bases get their own tables.
- //
- if (!polymorphic (c))
- inherits (c);
-
- indexes& ins (c.get<indexes> ("index"));
-
- for (indexes::iterator i (ins.begin ()); i != ins.end (); ++i)
- {
- // Using index name as its id.
- //
- sema_rel::index& in (
- model_.new_node<sema_rel::index> (
- i->name, i->type, i->method, i->options));
- in.set ("cxx-location", location (i->loc));
- model_.new_edge<sema_rel::unames> (table_, in, i->name);
-
- for (index::members_type::iterator j (i->members.begin ());
- j != i->members.end (); ++j)
- {
- using sema_rel::column;
-
- index::member& im (*j);
-
- semantics::type* t (&utype (*im.path.back ()));
-
- if (semantics::class_* ptr = object_pointer (*t))
- t = &utype (*id_member (*ptr));
-
- if (type* comp = composite_wrapper (*t))
- {
- // Composite value. Get the list of the columns. Note that
- // the column prefix needs to contain all the components.
- //
- instance<object_columns_list> ocl (
- column_prefix (im.path, true));
- ocl->traverse (*comp);
-
- for (object_columns_list::iterator i (ocl->begin ());
- i != ocl->end (); ++i)
- {
- column* c (table_.find<column> (i->name));
- assert (c != 0);
- model_.new_edge<sema_rel::contains> (in, *c, im.options);
- }
- }
- else
- {
- // Simple value. Get the column name and look it up in the
- // table.
- //
- column* c (table_.find<column> (column_name (im.path)));
- assert (c != 0);
- model_.new_edge<sema_rel::contains> (in, *c, im.options);
- }
- }
- }
- }
-
- private:
- sema_rel::model& model_;
- sema_rel::table& table_;
-
- traversal::inherits inherits_;
- };
-
- struct member_create: object_members_base, virtual context
- {
- typedef member_create base;
-
- member_create (sema_rel::model& model)
- : object_members_base (false, true, false), model_ (model)
- {
- }
-
- virtual void
- traverse_pointer (semantics::data_member&, semantics::class_&)
- {
- // We don't want to traverse composite id.
- }
-
- virtual void
- traverse_object (semantics::class_& c)
- {
- if (context::top_object != &c)
- {
- // We are in one of the bases. Set the id_prefix to its
- // (unqualified) name.
- //
- string t (id_prefix_);
- id_prefix_ = class_name (c) + "::";
- object_members_base::traverse_object (c);
- id_prefix_ = t;
- }
- else
- {
- // Top-level object. Set its id as a prefix.
- //
- id_prefix_ = string (class_fq_name (c), 2) + "::";
- object_members_base::traverse_object (c);
- }
- }
-
- virtual void
- traverse_composite (semantics::data_member* m, semantics::class_& c)
- {
- string t (id_prefix_);
-
- if (m != 0)
- // Member of a composite type. Add the data member to id_prefix.
- //
- id_prefix_ += m->name () + ".";
- else
- // Composite base. Add its unqualified name to id_prefix.
- //
- id_prefix_ += class_name (c) + "::";
-
- object_members_base::traverse_composite (m, c);
-
- id_prefix_ = t;
- }
-
- virtual string
- table_options (semantics::data_member&, semantics::type&)
- {
- return "";
- }
-
- virtual void
- traverse_container (semantics::data_member& m, semantics::type& ct)
- {
- using semantics::type;
- using semantics::data_member;
-
- using sema_rel::column;
-
- // Ignore inverse containers of object pointers.
- //
- if (inverse (m, "value"))
- return;
-
- container_kind_type ck (container_kind (ct));
- qname const& name (table_name (m, table_prefix_));
-
- // Ignore deleted container members.
- //
- if (semantics::data_member* m = deleted_member (member_path_))
- {
- model_.get<deleted_table_map> ("deleted-map")[name] = m;
- return;
- }
-
- // Add the [] decorator to distinguish this id from non-container
- // ids (we don't want to ever end up comparing, for example, an
- // object table to a container table).
- //
- string id (id_prefix_ + m.name () + "[]");
-
- sema_rel::table& t (model_.new_node<sema_rel::table> (id));
- t.set ("cxx-location", m.location ());
- t.set ("member-path", member_path_);
- t.set ("deleted-map", deleted_column_map ());
- model_.new_edge<sema_rel::qnames> (model_, t, name);
-
- t.options (table_options (m, ct));
- t.extra ()["kind"] = "container";
-
- // object_id
- //
- {
- bool f (false); //@@ (im)persfect forwarding.
- instance<object_columns> oc (model_, t, f);
- oc->traverse (m, container_idt (m), "id", "object_id");
- }
-
- // Foreign key and index for the object id. Keep this foreign
- // key first since we reply on this information to lookup the
- // corresponding object table.
- //
- {
- // Derive the name prefix. See the comment for the other foreign
- // key code above.
- //
- // Note also that id_name can be a column prefix (if id is
- // composite), in which case it can be empty. In this case
- // we just fallback on the default name.
- //
- // Finally, this is a top-level column, so there is no column
- // prefix.
- //
- string id_name (
- column_name (m, "id", "object_id", column_prefix ()));
-
- if (id_name.empty ())
- id_name = "object_id";
-
- // Foreign key.
- //
- sema_rel::foreign_key& fk (
- model_.new_node<sema_rel::foreign_key> (
- id + ".id",
- table_name (*context::top_object),
- sema_rel::deferrable::not_deferrable,
- sema_rel::foreign_key::cascade));
- fk.set ("cxx-location", m.location ());
- model_.new_edge<sema_rel::unames> (
- t, fk, fkey_name (t.name (), id_name));
-
- // Get referenced columns.
- //
- {
- data_member_path& id (*id_member (*context::top_object));
-
- instance<object_columns_list> ocl;
- ocl->traverse (id);
-
- for (object_columns_list::iterator i (ocl->begin ());
- i != ocl->end (); ++i)
- fk.referenced_columns ().push_back (i->name);
- }
-
- // All the columns we have in this table so far are for the
- // object id. Add them to the foreign key.
- //
- for (sema_rel::table::names_iterator i (t.names_begin ());
- i != t.names_end ();
- ++i)
- {
- if (column* c = dynamic_cast<column*> (&i->nameable ()))
- model_.new_edge<sema_rel::contains> (fk, *c);
- }
-
- // Index. See if we have a custom index.
- //
- index* sin (m.count ("id-index") ? &m.get<index> ("id-index") : 0);
- sema_rel::index* in (0);
-
- if (sin != 0)
- {
- in = &model_.new_node<sema_rel::index> (
- id + ".id", sin->type, sin->method, sin->options);
- in->set ("cxx-location", location (sin->loc));
- }
- else
- {
- in = &model_.new_node<sema_rel::index> (id + ".id");
- in->set ("cxx-location", m.location ());
- }
-
- model_.new_edge<sema_rel::unames> (
- t,
- *in,
- sin != 0 && !sin->name.empty ()
- ? sin->name
- : index_name (name, id_name));
-
- // All the columns we have in this table so far are for the
- // object id. Add them to the index.
- //
- for (sema_rel::table::names_iterator i (t.names_begin ());
- i != t.names_end ();
- ++i)
- {
- if (column* c = dynamic_cast<column*> (&i->nameable ()))
- model_.new_edge<sema_rel::contains> (
- *in, *c, (sin != 0 ? sin->members.back ().options : ""));
- }
- }
-
- // index (simple value)
- //
- bool ordered (ck == ck_ordered && !unordered (m));
- if (ordered)
- {
- // Column.
- //
- {
- bool f (false); //@@ (im)persfect forwarding.
- instance<object_columns> oc (model_, t, f);
- oc->traverse (m, container_it (m), "index", "index");
- }
-
- // This is a simple value so the name cannot be empty. It is
- // also a top-level column, so there is no column prefix.
- //
- string col (column_name (m, "index", "index", column_prefix ()));
-
- // Index. See if we have a custom index.
- //
- index* sin (m.count ("index-index")
- ? &m.get<index> ("index-index")
- : 0);
- sema_rel::index* in (0);
-
- if (sin != 0)
- {
- in = &model_.new_node<sema_rel::index> (
- id + ".index", sin->type, sin->method, sin->options);
- in->set ("cxx-location", location (sin->loc));
- }
- else
- {
- in = &model_.new_node<sema_rel::index> (id + ".index");
- in->set ("cxx-location", m.location ());
- }
-
- model_.new_edge<sema_rel::unames> (
- t,
- *in,
- sin != 0 && !sin->name.empty ()
- ? sin->name
- : index_name (name, col));
-
- column* c (t.find<column> (col));
- assert (c != 0);
-
- model_.new_edge<sema_rel::contains> (
- *in,
- *c,
- (sin != 0 ? sin->members.back ().options : ""));
- }
-
- // key
- //
- if (ck == ck_map || ck == ck_multimap)
- {
- bool f (false); //@@ (im)persfect forwarding.
- instance<object_columns> oc (model_, t, f);
- oc->traverse (m, container_kt (m), "key", "key");
- }
-
- // value
- //
- {
- bool f (false); //@@ (im)persfect forwarding.
- instance<object_columns> oc (model_, t, f);
- oc->traverse (m, container_vt (m), "value", "value");
- }
- }
-
- protected:
- sema_rel::model& model_;
- string id_prefix_;
- };
-
- struct class_: traversal::class_, virtual context
- {
- typedef class_ base;
-
- class_ (sema_rel::model& model): model_ (model) {}
-
- virtual string
- table_options (type&)
- {
- return "";
- }
-
- virtual void
- traverse (type& c)
- {
- if (!options.at_once () && class_file (c) != unit.file ())
- return;
-
- if (!object (c))
- return;
-
- semantics::class_* poly (polymorphic (c));
-
- if (abstract (c) && poly == 0)
- return;
-
- qname const& name (table_name (c));
-
- // If the table with this name was already seen, assume the
- // user knows what they are doing and skip it.
- //
- if (tables_.count (name))
- return;
-
- if (deleted (c))
- {
- model_.get<deleted_table_map> ("deleted-map")[name] = &c;
- return;
- }
-
- string id (class_fq_name (c), 2); // Remove leading '::'.
-
- sema_rel::table& t (model_.new_node<sema_rel::table> (id));
- t.set ("cxx-location", c.location ());
- t.set ("class", &c);
- t.set ("deleted-map", deleted_column_map ());
- model_.new_edge<sema_rel::qnames> (model_, t, name);
-
- t.options (table_options (c));
-
- t.extra ()["kind"] =(poly == 0
- ? "object"
- : (poly == &c
- ? "polymorphic root object"
- : "polymorphic derived object"));
-
- // Add columns.
- //
- {
- bool tr (true); //@@ (im)persfect forwarding.
- instance<object_columns> oc (model_, t, tr);
- oc->traverse (c);
- }
-
- // Add indexes.
- //
- {
- instance<object_indexes> oi (model_, t);
- oi->traverse (c);
- }
-
- tables_.insert (name);
-
- // Create tables for members.
- //
- {
- instance<member_create> mc (model_);
- mc->traverse (c);
- }
- }
-
- protected:
- sema_rel::model& model_;
- tables tables_;
- };
- }
-}
-
-#endif // ODB_RELATIONAL_MODEL_HXX
diff --git a/odb/relational/mysql/model.cxx b/odb/relational/mysql/model.cxx
deleted file mode 100644
index 2ec9d8b..0000000
--- a/odb/relational/mysql/model.cxx
+++ /dev/null
@@ -1,135 +0,0 @@
-// file : odb/relational/mysql/model.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <sstream>
-
-#include <odb/relational/model.hxx>
-#include <odb/relational/mysql/context.hxx>
-
-using namespace std;
-
-namespace relational
-{
- namespace mysql
- {
- namespace model
- {
- namespace relational = relational::model;
-
- struct object_columns: relational::object_columns, context
- {
- object_columns (base const& x): base (x) {}
-
- virtual string
- default_bool (semantics::data_member&, bool v)
- {
- // MySQL has TRUE and FALSE as just aliases for 1 and 0. Still
- // use them for self-documentation.
- //
- return v ? "TRUE" : "FALSE";
- }
-
- virtual string
- default_enum (semantics::data_member& m, tree en, string const& name)
- {
- // Make sure the column is mapped to an ENUM or integer type.
- //
- sql_type const& t (parse_sql_type (column_type (), m, false));
-
- switch (t.type)
- {
- case sql_type::ENUM:
- case sql_type::TINYINT:
- case sql_type::SMALLINT:
- case sql_type::MEDIUMINT:
- case sql_type::INT:
- case sql_type::BIGINT:
- break;
- default:
- {
- cerr << m.file () << ":" << m.line () << ":" << m.column ()
- << ": error: column with default value specified as C++ "
- << "enumerator must map to MySQL ENUM or integer type"
- << endl;
-
- throw operation_failed ();
- }
- }
-
- using semantics::enum_;
- using semantics::enumerator;
-
- enumerator& er (dynamic_cast<enumerator&> (*unit.find (en)));
- enum_& e (er.enum_ ());
-
- if (t.type == sql_type::ENUM)
- {
- // Assuming the enumerators in the C++ enum and MySQL ENUM are
- // in the same order, calculate the poistion of the C++
- // enumerator and use that as an index in the MySQL ENUM.
- //
- size_t pos (0);
-
- for (enum_::enumerates_iterator i (e.enumerates_begin ()),
- end (e.enumerates_end ()); i != end; ++i)
- {
- if (&i->enumerator () == &er)
- break;
-
- pos++;
- }
-
- if (pos < t.enumerators.size ())
- return t.enumerators[pos];
- else
- {
- cerr << m.file () << ":" << m.line () << ":" << m.column ()
- << ": error: unable to map C++ enumerator '" << name
- << "' to MySQL ENUM value" << endl;
-
- throw operation_failed ();
- }
- }
- else
- {
- ostringstream ostr;
-
- if (e.unsigned_ ())
- ostr << er.value ();
- else
- ostr << static_cast<long long> (er.value ());
-
- return ostr.str ();
- }
- }
- };
- entry<object_columns> object_columns_;
-
- struct member_create: relational::member_create, context
- {
- member_create (base const& x): base (x) {}
-
- virtual string
- table_options (semantics::data_member&, semantics::type&)
- {
- string const& engine (options.mysql_engine ());
- return engine != "default" ? "ENGINE=" + engine : "";
- }
- };
- entry<member_create> member_create_;
-
- struct class_: relational::class_, context
- {
- class_ (base const& x): base (x) {}
-
- virtual string
- table_options (type&)
- {
- string const& engine (options.mysql_engine ());
- return engine != "default" ? "ENGINE=" + engine : "";
- }
- };
- entry<class_> class__;
- }
- }
-}
diff --git a/odb/relational/pgsql/context.cxx b/odb/relational/pgsql/context.cxx
deleted file mode 100644
index a9f34dd..0000000
--- a/odb/relational/pgsql/context.cxx
+++ /dev/null
@@ -1,786 +0,0 @@
-// file : odb/relational/pgsql/context.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <cassert>
-#include <sstream>
-
-#include <odb/diagnostics.hxx>
-
-#include <odb/sql-token.hxx>
-#include <odb/sql-lexer.hxx>
-
-#include <odb/relational/pgsql/context.hxx>
-#include <odb/relational/pgsql/common.hxx>
-
-using namespace std;
-
-namespace relational
-{
- namespace pgsql
- {
- namespace
- {
- struct type_map_entry
- {
- char const* const cxx_type;
- char const* const db_type;
- char const* const db_id_type;
- bool const null;
- };
-
- type_map_entry type_map[] =
- {
- {"bool", "BOOLEAN", 0, false},
-
- {"char", "CHAR(1)", 0, false},
- {"signed char", "SMALLINT", 0, false},
- {"unsigned char", "SMALLINT", 0, false},
-
- {"short int", "SMALLINT", 0, false},
- {"short unsigned int", "SMALLINT", 0, false},
-
- {"int", "INTEGER", 0, false},
- {"unsigned int", "INTEGER", 0, false},
-
- {"long int", "BIGINT", 0, false},
- {"long unsigned int", "BIGINT", 0, false},
-
- {"long long int", "BIGINT", 0, false},
- {"long long unsigned int", "BIGINT", 0, false},
-
- {"float", "REAL", 0, false},
- {"double", "DOUBLE PRECISION", 0, false},
-
- {"::std::string", "TEXT", 0, false},
-
- {"::size_t", "BIGINT", 0, false},
- {"::std::size_t", "BIGINT", 0, false}
- };
- }
-
- context* context::current_;
-
- context::
- ~context ()
- {
- if (current_ == this)
- current_ = 0;
- }
-
- context::
- context (ostream& os,
- semantics::unit& u,
- options_type const& ops,
- features_type& f,
- sema_rel::model* m)
- : root_context (os, u, ops, f, data_ptr (new (shared) data (os))),
- base_context (static_cast<data*> (root_context::data_.get ()), m),
- data_ (static_cast<data*> (base_context::data_))
- {
- assert (current_ == 0);
- current_ = this;
-
- generate_grow = true;
- need_alias_as = true;
- insert_send_auto_id = false;
- delay_freeing_statement_result = false;
- need_image_clone = false;
- generate_bulk = false;
- global_index = true;
- global_fkey = false;
- data_->bind_vector_ = "pgsql::bind*";
- data_->truncated_vector_ = "bool*";
-
- // Populate the C++ type to DB type map.
- //
- for (size_t i (0); i < sizeof (type_map) / sizeof (type_map_entry); ++i)
- {
- type_map_entry const& e (type_map[i]);
-
- type_map_type::value_type v (
- e.cxx_type,
- db_type_type (
- e.db_type, e.db_id_type ? e.db_id_type : e.db_type, e.null));
-
- data_->type_map_.insert (v);
- }
- }
-
- context::
- context ()
- : data_ (current ().data_)
- {
- }
-
- namespace
- {
- struct has_grow: traversal::class_
- {
- has_grow (bool& r, user_section* s)
- : r_ (r), section_ (s)
- {
- *this >> inherits_ >> *this;
- }
-
- virtual void
- traverse (type& c)
- {
- // Ignore transient bases.
- //
- if (!(context::object (c) || context::composite (c)))
- return;
-
- if (section_ == 0 && c.count ("pgsql-grow"))
- r_ = c.get<bool> ("pgsql-grow");
- else
- {
- // r_ should be false.
- //
- inherits (c);
-
- if (!r_)
- names (c);
-
- if (section_ == 0)
- c.set ("pgsql-grow", r_);
- }
- }
-
- private:
- bool& r_;
- user_section* section_;
- traversal::inherits inherits_;
- };
-
- struct has_grow_member: member_base
- {
- has_grow_member (bool& r, user_section* section = 0)
- : relational::member_base (0, 0, string (), string (), section),
- r_ (r) {}
-
- has_grow_member (bool& r,
- user_section* section,
- semantics::type* t,
- const custom_cxx_type* ct,
- string const& key_prefix = string ())
- : relational::member_base (t, ct, string (), key_prefix, section),
- r_ (r) {}
-
- virtual bool
- pre (member_info& mi)
- {
- // If we have a key prefix (container), then it can't be in a
- // section (while mi.m can). The same for top-level -- if we got
- // called, then we shouldn't ignore it.
- //
- return !key_prefix_.empty () || top_level_ ||
- (section_ == 0 && !separate_load (mi.m)) ||
- (section_ != 0 && *section_ == section (mi.m));
- }
-
- virtual void
- traverse_composite (member_info& mi)
- {
- // By calling grow() instead of recursing, we reset any overrides.
- // We also don't pass section since they don't apply inside
- // composites.
- //
- r_ = r_ || context::grow (dynamic_cast<semantics::class_&> (mi.t));
- }
-
- virtual void
- traverse_numeric (member_info&)
- {
- r_ = true;
- }
-
- virtual void
- traverse_string (member_info&)
- {
- r_ = true;
- }
-
- virtual void
- traverse_varbit (member_info&)
- {
- r_ = true;
- }
-
- private:
- bool& r_;
- };
- }
-
- bool context::
- grow_impl (semantics::class_& c, user_section* section)
- {
- if (section == 0 && c.count ("pgsql-grow"))
- return c.get<bool> ("pgsql-grow");
-
- bool r (false);
- has_grow ct (r, section);
- has_grow_member mt (r, section);
- traversal::names names;
- ct >> names >> mt;
- ct.traverse (c);
- return r;
- }
-
- bool context::
- grow_impl (semantics::data_member& m)
- {
- bool r (false);
- has_grow_member mt (r);
- mt.traverse (m, true);
- return r;
- }
-
- bool context::
- grow_impl (semantics::data_member& m,
- semantics::type& t,
- const custom_cxx_type* ct,
- string const& kp)
- {
- bool r (false);
- has_grow_member mt (r, 0, &t, ct, kp);
- mt.traverse (m, true);
- return r;
- }
-
- string const& context::
- convert_expr (string const& sqlt, semantics::data_member& m, bool to)
- {
- sql_type const& t (parse_sql_type (sqlt, m));
- return to ? t.to : t.from;
- }
-
- string context::
- quote_id_impl (qname const& id) const
- {
- string r;
-
- bool f (true);
- for (qname::iterator i (id.begin ()); i < id.end (); ++i)
- {
- if (i->empty ())
- continue;
-
- // Warn if the name is greater than the (NAMEDATALEN - 1) limit,
- // which is 63 in the default PG build.
- //
- if (i->size () > 63)
- {
- cerr << "warning: SQL name '" << *i << "' is longer than "
- << "the default PostgreSQL name limit of 63 characters "
- << "and may be truncated" << endl;
-
- cerr << "info: consider shortening it using #pragma db "
- << "table/column/index or --*-regex options" << endl;
- }
-
- if (f)
- f = false;
- else
- r += '.';
-
- r += '"';
- r += *i;
- r += '"';
- }
-
- return r;
- }
-
- string context::
- statement_name (string const& type, string const& name, semantics::node& n)
- {
- // Put the type first so that in the case of truncation it
- // remains thus lowering the chance of a clash.
- //
- string r (type);
- r += '_';
- r += name;
-
- r = transform_name (r, sql_name_statement);
-
- // Warn if the name is greater than the (NAMEDATALEN - 1) limit,
- // which is 63 in the default PG build.
- //
- // Note that we have to do it in addition to the above since this
- // name doesn't go through quote_id().
- //
- if (r.size () > 63)
- {
- location const& l (n.location ());
-
- warn (l) << "prepared statement name '" << r << "' is longer than "
- << "the default PostgreSQL name limit of 63 characters "
- << "and may be truncated" << endl;
-
- info (l) << "consider shortening the corresponding namespace "
- << "name, class name, or data member name" << endl;
-
- info (l) << "or shortening the statement name itself using the "
- << "--statement-regex option" << endl;
- }
-
- return r;
- }
-
- string context::
- database_type_impl (semantics::type& t,
- semantics::names* hint,
- bool id,
- bool* null)
- {
- string r (base_context::database_type_impl (t, hint, id, null));
-
- if (!r.empty ())
- return r;
-
- using semantics::array;
-
- // char[N] mapping.
- //
- if (array* a = dynamic_cast<array*> (&t))
- {
- semantics::type& bt (a->base_type ());
- if (bt.is_a<semantics::fund_char> ())
- {
- unsigned long long n (a->size ());
-
- if (n == 0)
- return r;
- else if (n == 1)
- r = "CHAR(";
- else
- {
- r = "VARCHAR(";
- n--;
- }
-
- ostringstream ostr;
- ostr << n;
- r += ostr.str ();
- r += ')';
- }
- }
-
- return r;
- }
-
- //
- // SQL type parsing.
- //
-
- sql_type const& context::
- parse_sql_type (string const& t, semantics::data_member& m, bool custom)
- {
- // If this proves to be too expensive, we can maintain a cache of
- // parsed types across contexts.
- //
- data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t));
-
- if (i != data_->sql_type_cache_.end ()
- && (custom ? i->second.custom_cached : i->second.straight_cached))
- {
- return (custom ? i->second.custom : i->second.straight);
- }
- else
- {
- try
- {
- sql_type st (
- parse_sql_type (
- t,
- custom ? &unit.get<custom_db_types> ("custom-db-types") : 0));
-
- if (custom)
- return data_->sql_type_cache_[t].cache_custom (st);
- else
- return data_->sql_type_cache_[t].cache_straight (st);
- }
- catch (invalid_sql_type const& e)
- {
- cerr << m.file () << ":" << m.line () << ":" << m.column ()
- << ": error: " << e.message () << endl;
-
- throw operation_failed ();
- }
- }
- }
-
- inline sql_type
- error (bool fail, string const& m)
- {
- if (!fail)
- return sql_type ();
- else
- throw context::invalid_sql_type (m);
- }
-
- sql_type context::
- parse_sql_type (string sqlt, custom_db_types const* ct)
- {
- try
- {
- sql_type r;
-
- // First run the type through the custom mapping, if requested.
- //
- if (ct != 0)
- {
- for (custom_db_types::const_iterator i (ct->begin ());
- i != ct->end (); ++i)
- {
- custom_db_type const& t (*i);
-
- if (t.type.match (sqlt))
- {
- r.to = t.type.replace (sqlt, t.to);
- r.from = t.type.replace (sqlt, t.from);
- sqlt = t.type.replace (sqlt, t.as);
- break;
- }
- }
- }
-
- sql_lexer l (sqlt);
-
- // While most type names use single identifier, there are
- // a couple of exceptions to this rule:
- //
- // BIT VARYING (VARBIT)
- // CHARACTER VARYING (VARRCHAR)
- // DOUBLE PRECISION (DOUBLE)
- // TIME WITH TIME ZONE (not currently supported)
- // TIMESTAMP WITH TIME ZONE (not currently supported)
- //
-
- enum state
- {
- parse_prefix,
- parse_name,
- parse_range,
- parse_suffix,
- parse_done
- };
-
- state s (parse_prefix);
- string prefix;
- bool flt (false);
-
- for (sql_token t (l.next ());
- s != parse_done && t.type () != sql_token::t_eos;
- t = l.next ())
- {
- sql_token::token_type tt (t.type ());
-
- switch (s)
- {
- case parse_prefix:
- {
- if (tt == sql_token::t_identifier)
- {
- string const& id (context::upcase (t.identifier ()));
-
- if (id == "BIT" ||
- id == "CHARACTER" ||
- id == "DOUBLE")
- {
- prefix = id;
- s = parse_name;
- continue;
- }
- }
-
- s = parse_name;
- }
- // Fall through.
- case parse_name:
- {
- if (tt == sql_token::t_identifier)
- {
- bool match (true);
- string const& id (context::upcase (t.identifier ()));
-
- //
- // Numeric types.
- //
- if (id == "BOOL" || id == "BOOLEAN")
- {
- r.type = sql_type::BOOLEAN;
- }
- else if (id == "SMALLINT" || id == "INT2")
- {
- r.type = sql_type::SMALLINT;
- }
- else if (id == "INT" ||
- id == "INTEGER" ||
- id == "INT4")
- {
- r.type = sql_type::INTEGER;
- }
- else if (id == "BIGINT")
- {
- r.type = sql_type::BIGINT;
- }
- else if (id == "REAL" || id == "FLOAT4")
- {
- r.type = sql_type::REAL;
- }
- else if ((id == "PRECISION" && prefix == "DOUBLE") ||
- id == "FLOAT8")
- {
- r.type = sql_type::DOUBLE;
- }
- else if (id == "FLOAT")
- {
- // Assign a type only once we know the precision of the
- // float.
- //
- flt = true;
- }
- else if (id == "NUMERIC" || id == "DECIMAL")
- {
- r.type = sql_type::NUMERIC;
- }
- //
- // Date-time types.
- //
- else if (id == "DATE")
- {
- r.type = sql_type::DATE;
- }
- else if (id == "TIME")
- {
- r.type = sql_type::TIME;
- }
- else if (id == "TIMETZ")
- {
- return error (ct, "PostgreSQL time zones are not currently "
- "supported");
- }
- else if (id == "TIMESTAMP")
- {
- r.type = sql_type::TIMESTAMP;
- }
- else if (id == "TIMESTAMPTZ")
- {
- return error (ct, "PostgreSQL time zones are not currently "
- "supported");
- }
- //
- // String and binary types.
- //
- else if (id == "CHAR")
- {
- r.type = sql_type::CHAR;
- }
- else if (id == "VARCHAR")
- {
- r.type = sql_type::VARCHAR;
- }
- else if (id == "TEXT")
- {
- r.type = sql_type::TEXT;
- }
- else if (id == "VARYING")
- {
- if (prefix == "BIT")
- r.type = sql_type::VARBIT;
- else if (prefix == "CHARACTER")
- r.type = sql_type::VARCHAR;
- }
- else if (id == "BYTEA")
- {
- r.type = sql_type::BYTEA;
- }
- else if (id == "VARBIT")
- {
- r.type = sql_type::VARBIT;
- }
- //
- // Other types.
- //
- else if (id == "UUID")
- {
- r.type = sql_type::UUID;
- }
- else
- match = false;
-
- if (match)
- {
- s = parse_range;
- continue;
- }
- }
-
- // Some prefixes can also be type names if not followed
- // by the actual type name.
- //
- if (!prefix.empty ())
- {
- if (prefix == "BIT")
- {
- r.type = sql_type::BIT;
- }
- else if (prefix == "CHARACTER")
- {
- r.type = sql_type::CHAR;
- }
- }
-
- if (r.type == sql_type::invalid)
- {
- return error (
- ct,
- tt == sql_token::t_identifier
- ? "unknown PostgreSQL type '" + t.identifier () + "'"
- : "expected PostgreSQL type name");
- }
-
- s = parse_range;
- }
- // Fall through.
- case parse_range:
- {
- if (t.punctuation () == sql_token::p_lparen)
- {
- t = l.next ();
-
- if (t.type () != sql_token::t_int_lit)
- {
- return error (ct, "integer range expected in PostgreSQL "
- "type declaration");
- }
-
- unsigned int v;
- istringstream is (t.literal ());
-
- if (!(is >> v && is.eof ()))
- {
- return error (ct, "invalid range value '" + t.literal () +
- "' in PostgreSQL type declaration");
- }
-
- r.range = true;
- r.range_value = v;
-
- t = l.next ();
-
- if (t.punctuation () == sql_token::p_comma)
- {
- // We have the second range value. Skip it.
- //
- l.next ();
- t = l.next ();
- }
-
- if (t.punctuation () != sql_token::p_rparen)
- {
- return error (ct, "expected ')' in PostgreSQL type "
- "declaration");
- }
-
- s = parse_suffix;
- continue;
- }
-
- s = parse_suffix;
- }
- // Fall through.
- case parse_suffix:
- {
- if (r.type == sql_type::TIME || r.type == sql_type::TIMESTAMP)
- {
- string const& id1 (context::upcase (t.identifier ()));
-
- if (id1 == "WITH")
- {
- t = l.next ();
- tt = t.type ();
-
- if (tt == sql_token::t_identifier)
- {
- string const& id2 (context::upcase (t.identifier ()));
-
- if (id2 == "TIME")
- {
- t = l.next ();
- tt = t.type ();
-
- if (tt == sql_token::t_identifier)
- {
- string const& id3 (context::upcase (t.identifier ()));
-
- if (id3 == "ZONE")
- {
- // This code shall not fall through.
- //
- return error (ct, "PostgreSQL time zones are not "
- "currently supported");
- }
- }
- }
- }
- }
- }
-
- return error (
- ct,
- tt == sql_token::t_identifier
- ? "unknown PostgreSQL type '" + t.identifier () + "'"
- : "unknown PostgreSQL type");
- }
- case parse_done:
- {
- assert (false);
- break;
- }
- }
- }
-
- if (s == parse_name && !prefix.empty ())
- {
- // Some prefixes can also be type names if not followed
- // by the actual type name.
- //
- if (prefix == "BIT")
- {
- r.type = sql_type::BIT;
- }
- else if (prefix == "CHARACTER")
- {
- r.type = sql_type::CHAR;
- }
- }
-
- if (flt)
- {
- r.type = r.range && r.range_value < 25 ?
- sql_type::REAL :
- sql_type::DOUBLE;
- }
-
- if (r.type == sql_type::invalid)
- return error (ct, "incomplete PostgreSQL type declaration");
-
- // If range is omitted for CHAR or BIT types, it defaults to 1.
- //
- if ((r.type == sql_type::CHAR || r.type == sql_type::BIT) && !r.range)
- {
- r.range = true;
- r.range_value = 1;
- }
-
- return r;
- }
- catch (sql_lexer::invalid_input const& e)
- {
- return error (ct, "invalid PostgreSQL type declaration: " + e.message);
- }
- }
- }
-}
diff --git a/odb/relational/pgsql/header.cxx b/odb/relational/pgsql/header.cxx
deleted file mode 100644
index ff00eaa..0000000
--- a/odb/relational/pgsql/header.cxx
+++ /dev/null
@@ -1,271 +0,0 @@
-// file : odb/relational/pgsql/header.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <odb/relational/header.hxx>
-
-#include <odb/relational/pgsql/common.hxx>
-#include <odb/relational/pgsql/context.hxx>
-
-namespace relational
-{
- namespace pgsql
- {
- namespace header
- {
- namespace relational = relational::header;
-
- struct class1: relational::class1
- {
- class1 (base const& x): base (x) {}
-
- virtual void
- object_public_extra_post (type& c)
- {
- bool abst (abstract (c));
-
- type* poly_root (polymorphic (c));
- bool poly (poly_root != 0);
- bool poly_derived (poly && poly_root != &c);
-
- if (abst && !poly)
- return;
-
- data_member_path* id (id_member (c));
- semantics::data_member* optimistic (context::optimistic (c));
-
- column_count_type const& cc (column_count (c));
-
- size_t update_columns (
- cc.total - cc.id - cc.inverse - cc.readonly - cc.separate_update);
-
- // Statement names.
- //
- os << "static const char persist_statement_name[];";
-
- if (id != 0)
- {
- if (poly_derived)
- os << "static const char* const find_statement_names[" <<
- (abst ? "1" : "depth") << "];";
- else
- os << "static const char find_statement_name[];";
-
- if (poly && !poly_derived)
- os << "static const char find_discriminator_statement_name[];";
-
- if (update_columns != 0)
- os << "static const char update_statement_name[];";
-
- os << "static const char erase_statement_name[];";
-
- if (optimistic != 0)
- os << "static const char optimistic_erase_statement_name[];";
- }
-
- // Query statement name.
- //
- if (options.generate_query ())
- os << "static const char query_statement_name[];"
- << "static const char erase_query_statement_name[];";
-
- os << endl;
-
- // Statement types.
- //
- os << "static const unsigned int persist_statement_types[];";
-
- if (id != 0)
- {
- os << "static const unsigned int find_statement_types[];";
-
- if (update_columns != 0)
- os << "static const unsigned int update_statement_types[];";
-
- if (optimistic != 0)
- os << "static const unsigned int " <<
- "optimistic_erase_statement_types[];";
- }
-
- os << endl;
- }
-
- virtual void
- view_public_extra_post (type&)
- {
- // Statement names.
- //
- os << "static const char query_statement_name[];"
- << endl;
- }
- };
- entry<class1> class1_entry_;
-
- struct container_traits: relational::container_traits, context
- {
- container_traits (base const& x): base (x) {}
-
- virtual void
- container_public_extra_pre (semantics::data_member& m,
- semantics::type& t)
- {
- if (!object (c_) || (abstract (c_) && !polymorphic (c_)))
- return;
-
- bool smart (!inverse (m, "value") && !unordered (m) &&
- container_smart (t));
-
- // Container statement names.
- //
- os << "static const char select_name[];"
- << "static const char insert_name[];";
-
- if (smart)
- os << "static const char update_name[];";
-
- os << "static const char delete_name[];"
- << endl;
-
- // Container statement types.
- //
- os << "static const unsigned int insert_types[];";
-
- if (smart)
- os << "static const unsigned int update_types[];"
- << "static const unsigned int delete_types[];";
-
- os << endl;
- }
- };
- entry<container_traits> container_traits_;
-
- struct section_traits: relational::section_traits, context
- {
- section_traits (base const& x): base (x) {}
-
- virtual void
- section_public_extra_post (user_section& s)
- {
- semantics::class_* poly_root (polymorphic (c_));
- bool poly (poly_root != 0);
-
- if (!poly && (abstract (c_) ||
- s.special == user_section::special_version))
- return;
-
- bool load (s.total != 0 && s.separate_load ());
- bool load_opt (s.optimistic () && s.separate_load ());
-
- bool update (s.total != s.inverse + s.readonly); // Always separate.
- bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
-
- // Statement names.
- //
- if (load || load_opt)
- os << "static const char select_name[];"
- << endl;
-
- if (update || update_opt)
- os << "static const char update_name[];"
- << endl;
-
- // Statement types.
- //
- if (update || update_opt)
- os << "static const unsigned int update_types[];";
- }
- };
- entry<section_traits> section_traits_;
-
- struct image_member: relational::image_member_impl<sql_type>,
- member_base
- {
- image_member (base const& x)
- : member_base::base (x), // virtual base
- member_base::base_impl (x), // virtual base
- base_impl (x),
- member_base (x) {}
-
- virtual void
- traverse_integer (member_info& mi)
- {
- os << image_type << " " << mi.var << "value;"
- << "bool " << mi.var << "null;"
- << endl;
- }
-
- virtual void
- traverse_float (member_info& mi)
- {
- os << image_type << " " << mi.var << "value;"
- << "bool " << mi.var << "null;"
- << endl;
- }
-
- virtual void
- traverse_numeric (member_info& mi)
- {
- // Exchanged as strings. Can have up to 1000 digits not counting
- // '-' and '.'.
- //
-
- os << image_type << " " << mi.var << "value;"
- << "std::size_t " << mi.var << "size;"
- << "bool " << mi.var << "null;"
- << endl;
- }
-
- virtual void
- traverse_date_time (member_info& mi)
- {
- os << image_type << " " << mi.var << "value;"
- << "bool " << mi.var << "null;"
- << endl;
- }
-
- virtual void
- traverse_string (member_info& mi)
- {
- os << image_type << " " << mi.var << "value;"
- << "std::size_t " << mi.var << "size;"
- << "bool " << mi.var << "null;"
- << endl;
- }
-
- virtual void
- traverse_bit (member_info& mi)
- {
- // Additional 4 bytes at the beginning of the array specify
- // the number of significant bits in the image. This number
- // is stored in network byte order.
- //
- unsigned int n (4 + mi.st->range / 8 + (mi.st->range % 8 ? 1 : 0));
-
- os << "unsigned char " << mi.var << "value[" << n << "];"
- << "std::size_t " << mi.var << "size;"
- << "bool " << mi.var << "null;"
- << endl;
- }
-
- virtual void
- traverse_varbit (member_info& mi)
- {
- os << image_type << " " << mi.var << "value;"
- << "std::size_t " << mi.var << "size;"
- << "bool " << mi.var << "null;"
- << endl;
- }
-
- virtual void
- traverse_uuid (member_info& mi)
- {
- // UUID is a 16-byte sequence.
- //
- os << "unsigned char " << mi.var << "value[16];"
- << "bool " << mi.var << "null;"
- << endl;
- }
- };
- entry<image_member> image_member_;
- }
- }
-}
diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx
deleted file mode 100644
index 580103d..0000000
--- a/odb/relational/pgsql/source.cxx
+++ /dev/null
@@ -1,1140 +0,0 @@
-// file : odb/relational/pgsql/source.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <sstream>
-
-#include <odb/relational/source.hxx>
-
-#include <odb/relational/pgsql/common.hxx>
-#include <odb/relational/pgsql/context.hxx>
-
-using namespace std;
-
-namespace relational
-{
- namespace pgsql
- {
- namespace source
- {
- namespace relational = relational::source;
-
- struct query_parameters: relational::query_parameters
- {
- query_parameters (base const& x): base (x), i_ (0) {}
-
- virtual string
- next (semantics::data_member&, const string&, const string&)
- {
- ostringstream ss;
- ss << "$" << ++i_;
-
- return ss.str ();
- }
-
- virtual string
- auto_id (semantics::data_member&, const string&, const string&)
- {
- return "DEFAULT";
- }
-
- private:
- size_t i_;
- };
- entry<query_parameters> query_parameters_;
-
- namespace
- {
- const char* integer_buffer_types[] =
- {
- "pgsql::bind::boolean_",
- "pgsql::bind::smallint",
- "pgsql::bind::integer",
- "pgsql::bind::bigint"
- };
-
- const char* float_buffer_types[] =
- {
- "pgsql::bind::real",
- "pgsql::bind::double_"
- };
-
- const char* char_bin_buffer_types[] =
- {
- "pgsql::bind::text", // CHAR
- "pgsql::bind::text", // VARCHAR
- "pgsql::bind::text", // TEXT
- "pgsql::bind::bytea" // BYTEA
- };
-
- const char* date_time_buffer_types[] =
- {
- "pgsql::bind::date",
- "pgsql::bind::time",
- "pgsql::bind::timestamp"
- };
-
- const char* oids[] =
- {
- "pgsql::bool_oid", // BOOLEAN
- "pgsql::int2_oid", // SMALLINT
- "pgsql::int4_oid", // INTEGER
- "pgsql::int8_oid", // BIGINT
- "pgsql::float4_oid", // REAL
- "pgsql::float8_oid", // DOUBLE
- "pgsql::numeric_oid", // NUMERIC
- "pgsql::date_oid", // DATE
- "pgsql::time_oid", // TIME
- "pgsql::timestamp_oid", // TIMESTAMP
- "pgsql::text_oid", // CHAR
- "pgsql::text_oid", // VARCHAR
- "pgsql::text_oid", // TEXT
- "pgsql::bytea_oid", // BYTEA
- "pgsql::bit_oid", // BIT
- "pgsql::varbit_oid", // VARBIT
- "pgsql::uuid_oid" // UUID
- };
- }
-
- struct statement_oids: object_columns_base, context
- {
- statement_oids (statement_kind sk,
- bool first = true,
- object_section* section = 0)
- : object_columns_base (first, column_prefix (), section), sk_ (sk)
- {
- }
-
- virtual bool
- section_test (data_member_path const& mp)
- {
- object_section& s (section (mp));
-
- // Include eager loaded members into the main section for
- // SELECT statements.
- //
- return section_ == 0 ||
- *section_ == s ||
- (sk_ == statement_select &&
- *section_ == main_section &&
- !s.separate_load ());
- }
-
- virtual void
- traverse_pointer (semantics::data_member& m, semantics::class_& c)
- {
- // Ignore certain columns depending on what kind statement we are
- // generating. See object_columns in common source generator for
- // details.
- //
- if (!(inverse (m, key_prefix_) && sk_ != statement_select))
- object_columns_base::traverse_pointer (m, c);
- }
-
- virtual bool
- traverse_column (semantics::data_member& m,
- string const&,
- bool first)
- {
- // Ignore certain columns depending on what kind statement we are
- // generating. See object_columns in common source generator for
- // details.
- //
- if (id ())
- {
- if (sk_ == statement_update ||
- (sk_ == statement_insert && auto_ (m)))
- return false;
- }
-
- if (sk_ == statement_update &&
- readonly (member_path_, member_scope_))
- return false;
-
- if ((sk_ == statement_insert || sk_ == statement_update) &&
- version (m))
- return false;
-
- if (!first)
- os << ',' << endl;
-
- os << oids[parse_sql_type (column_type (), m).type];
-
- return true;
- }
-
- private:
- statement_kind sk_;
- };
-
- //
- // bind
- //
-
- struct bind_member: relational::bind_member_impl<sql_type>,
- member_base
- {
- bind_member (base const& x)
- : member_base::base (x), // virtual base
- member_base::base_impl (x), // virtual base
- base_impl (x),
- member_base (x)
- {
- }
-
- virtual void
- traverse_integer (member_info& mi)
- {
- os << b << ".type = " <<
- integer_buffer_types[mi.st->type - sql_type::BOOLEAN] << ";"
- << b << ".buffer = &" << arg << "." << mi.var << "value;"
- << b << ".is_null = &" << arg << "." << mi.var << "null;";
- }
-
- virtual void
- traverse_float (member_info& mi)
- {
- os << b << ".type = " <<
- float_buffer_types[mi.st->type - sql_type::REAL] << ";"
- << b << ".buffer = &" << arg << "." << mi.var << "value;"
- << b << ".is_null = &" << arg << "." << mi.var << "null;";
- }
-
- virtual void
- traverse_numeric (member_info& mi)
- {
- os << b << ".type = pgsql::bind::numeric;"
- << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
- << b << ".capacity = " << arg << "." << mi.var <<
- "value.capacity ();"
- << b << ".size = &" << arg << "." << mi.var << "size;"
- << b << ".is_null = &" << arg << "." << mi.var << "null;";
- }
-
- virtual void
- traverse_date_time (member_info& mi)
- {
- os << b << ".type = " <<
- date_time_buffer_types[mi.st->type - sql_type::DATE] << ";"
- << b << ".buffer = &" << arg << "." << mi.var << "value;"
- << b << ".is_null = &" << arg << "." << mi.var << "null;";
- }
-
- virtual void
- traverse_string (member_info& mi)
- {
- os << b << ".type = " <<
- char_bin_buffer_types[mi.st->type - sql_type::CHAR] << ";"
- << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
- << b << ".capacity = " << arg << "." << mi.var <<
- "value.capacity ();"
- << b << ".size = &" << arg << "." << mi.var << "size;"
- << b << ".is_null = &" << arg << "." << mi.var << "null;";
- }
-
- virtual void
- traverse_bit (member_info& mi)
- {
- os << b << ".type = pgsql::bind::bit;"
- << b << ".buffer = " << arg << "." << mi.var << "value;"
- << b << ".capacity = sizeof (" << arg << "." << mi.var << "value);"
- << b << ".size = &" << arg << "." << mi.var << "size;"
- << b << ".is_null = &" << arg << "." << mi.var << "null;";
- }
-
- virtual void
- traverse_varbit (member_info& mi)
- {
- os << b << ".type = pgsql::bind::varbit;"
- << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
- << b << ".capacity = " << arg << "." << mi.var <<
- "value.capacity ();"
- << b << ".size = &" << arg << "." << mi.var << "size;"
- << b << ".is_null = &" << arg << "." << mi.var << "null;";
- }
-
- virtual void
- traverse_uuid (member_info& mi)
- {
- os << b << ".type = pgsql::bind::uuid;"
- << b << ".buffer = " << arg << "." << mi.var << "value;"
- << b << ".is_null = &" << arg << "." << mi.var << "null;";
- }
- };
- entry<bind_member> bind_member_;
-
- //
- // grow
- //
-
- struct grow_member: relational::grow_member_impl<sql_type>,
- member_base
- {
- grow_member (base const& x)
- : member_base::base (x), // virtual base
- member_base::base_impl (x), // virtual base
- base_impl (x),
- member_base (x) {}
-
- virtual void
- traverse_integer (member_info&)
- {
- os << e << " = 0;"
- << endl;
- }
-
- virtual void
- traverse_float (member_info&)
- {
- os << e << " = 0;"
- << endl;
- }
-
- virtual void
- traverse_numeric (member_info& mi)
- {
- os << "if (" << e << ")" << endl
- << "{"
- << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
- << "grew = true;"
- << "}";
- }
-
- virtual void
- traverse_date_time (member_info&)
- {
- os << e << " = 0;"
- << endl;
- }
-
- virtual void
- traverse_string (member_info& mi)
- {
- os << "if (" << e << ")" << endl
- << "{"
- << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
- << "grew = true;"
- << "}";
- }
-
- virtual void
- traverse_bit (member_info&)
- {
- os << e << " = 0;"
- << endl;
- }
-
- virtual void
- traverse_varbit (member_info& mi)
- {
- os << "if (" << e << ")" << endl
- << "{"
- << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
- << "grew = true;"
- << "}";
- }
-
- virtual void
- traverse_uuid (member_info&)
- {
- os << e << " = 0;"
- << endl;
- }
- };
- entry<grow_member> grow_member_;
-
- //
- // init image
- //
-
- struct init_image_member: relational::init_image_member_impl<sql_type>,
- member_base
- {
- init_image_member (base const& x)
- : member_base::base (x), // virtual base
- member_base::base_impl (x), // virtual base
- base_impl (x),
- member_base (x)
- {
- }
-
- virtual void
- set_null (member_info& mi)
- {
- os << "i." << mi.var << "null = true;";
- }
-
- virtual void
- traverse_integer (member_info& mi)
- {
- os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");"
- << "i." << mi.var << "null = is_null;";
- }
-
- virtual void
- traverse_float (member_info& mi)
- {
- os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");"
- << "i." << mi.var << "null = is_null;";
- }
-
- virtual void
- traverse_numeric (member_info& mi)
- {
- // @@ Optimization: can remove growth check if buffer is fixed.
- //
- os << "std::size_t size (0);"
- << "std::size_t cap (i." << mi.var << "value.capacity ());"
- << traits << "::set_image (" << endl
- << "i." << mi.var << "value," << endl
- << "size," << endl
- << "is_null," << endl
- << member << ");"
- << "i." << mi.var << "null = is_null;"
- << "i." << mi.var << "size = size;"
- << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
- }
-
- virtual void
- traverse_date_time (member_info& mi)
- {
- os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");"
- << "i." << mi.var << "null = is_null;";
- }
-
- virtual void
- traverse_string (member_info& mi)
- {
- os << "std::size_t size (0);"
- << "std::size_t cap (i." << mi.var << "value.capacity ());"
- << traits << "::set_image (" << endl
- << "i." << mi.var << "value," << endl
- << "size," << endl
- << "is_null," << endl
- << member << ");"
- << "i." << mi.var << "null = is_null;"
- << "i." << mi.var << "size = size;"
- << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
- }
-
- virtual void
- traverse_bit (member_info& mi)
- {
- os << "std::size_t size (0);"
- << traits << "::set_image (" << endl
- << "i." << mi.var << "value," << endl
- << "sizeof (i." << mi.var << "value)," << endl
- << "size," << endl
- << "is_null," << endl
- << member << ");"
- << "i." << mi.var << "null = is_null;"
- << "i." << mi.var << "size = size;";
- }
-
- virtual void
- traverse_varbit (member_info& mi)
- {
- os << "std::size_t size (0);"
- << "std::size_t cap (i." << mi.var << "value.capacity ());"
- << traits << "::set_image (" << endl
- << "i." << mi.var << "value," << endl
- << "size," << endl
- << "is_null," << endl
- << member << ");"
- << "i." << mi.var << "null = is_null;"
- << "i." << mi.var << "size = size;"
- << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
- }
-
- virtual void
- traverse_uuid (member_info& mi)
- {
- os << traits << "::set_image (" << endl
- << "i." << mi.var << "value, is_null, " << member << ");"
- << "i." << mi.var << "null = is_null;";
- }
- };
- entry<init_image_member> init_image_member_;
-
- //
- // init value
- //
-
- struct init_value_member: relational::init_value_member_impl<sql_type>,
- member_base
- {
- init_value_member (base const& x)
- : member_base::base (x), // virtual base
- member_base::base_impl (x), // virtual base
- base_impl (x),
- member_base (x)
- {
- }
-
- virtual void
- get_null (string const& var) const
- {
- os << "i." << var << "null";
- }
-
- virtual void
- traverse_integer (member_info& mi)
- {
- os << traits << "::set_value (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "i." << mi.var << "null);"
- << endl;
- }
-
- virtual void
- traverse_float (member_info& mi)
- {
- os << traits << "::set_value (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "i." << mi.var << "null);"
- << endl;
- }
-
- virtual void
- traverse_numeric (member_info& mi)
- {
- os << traits << "::set_value (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "i." << mi.var << "size," << endl
- << "i." << mi.var << "null);"
- << endl;
- }
-
- virtual void
- traverse_date_time (member_info& mi)
- {
- os << traits << "::set_value (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "i." << mi.var << "null);"
- << endl;
- }
-
- virtual void
- traverse_string (member_info& mi)
- {
- os << traits << "::set_value (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "i." << mi.var << "size," << endl
- << "i." << mi.var << "null);"
- << endl;
- }
-
- virtual void
- traverse_bit (member_info& mi)
- {
- // Presented as byte.
- //
- os << traits << "::set_value (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "i." << mi.var << "size," << endl
- << "i." << mi.var << "null);"
- << endl;
- }
-
- virtual void
- traverse_varbit (member_info& mi)
- {
- // Presented as bytea.
- //
- os << traits << "::set_value (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "i." << mi.var << "size," << endl
- << "i." << mi.var << "null);"
- << endl;
- }
-
- virtual void
- traverse_uuid (member_info& mi)
- {
- os << traits << "::set_value (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "i." << mi.var << "null);"
- << endl;
- }
- };
- entry<init_value_member> init_value_member_;
-
- struct class_: relational::class_, context
- {
- class_ (base const& x): base (x) {}
-
- virtual string
- persist_statement_extra (type& c,
- relational::query_parameters&,
- persist_position p)
- {
- string r;
-
- if (p == persist_after_values)
- {
- data_member_path* id (id_member (c));
-
- type* poly_root (polymorphic (c));
- bool poly_derived (poly_root != 0 && poly_root != &c);
-
- // Top-level auto id.
- //
- if (id != 0 && !poly_derived && auto_ (*id))
- r = "RETURNING " +
- convert_from (column_qname (*id), *id->back ());
- }
-
- return r;
- }
-
- virtual void
- object_extra (type& c)
- {
- bool abst (abstract (c));
-
- type* poly_root (polymorphic (c));
- bool poly (poly_root != 0);
- bool poly_derived (poly && poly_root != &c);
-
- if (abst && !poly)
- return;
-
- data_member_path* id (id_member (c));
- semantics::data_member* optimistic (context::optimistic (c));
-
- column_count_type const& cc (column_count (c));
-
- size_t update_columns (
- cc.total - cc.id - cc.inverse - cc.readonly - cc.separate_update);
-
- string const& n (class_fq_name (c));
- string const& fn (flat_name (n));
- string traits ("access::object_traits_impl< " + n + ", id_pgsql >");
-
- os << "const char " << traits << "::" << endl
- << "persist_statement_name[] = " <<
- strlit (statement_name ("persist", fn, c)) << ";"
- << endl;
-
- if (id != 0)
- {
- if (poly_derived)
- {
- os << "const char* const " << traits << "::" << endl
- << "find_statement_names[] ="
- << "{";
-
- for (size_t i (0), n (abst ? 1 : polymorphic_depth (c));
- i < n;
- ++i)
- {
- if (i != 0)
- os << "," << endl;
-
- ostringstream ostr;
- ostr << "find_" << i;
- os << strlit (statement_name (ostr.str (), fn, c));
- }
-
- os << "};";
- }
- else
- os << "const char " << traits << "::" << endl
- << "find_statement_name[] = " <<
- strlit (statement_name ("find", fn, c)) << ";"
- << endl;
-
- if (poly && !poly_derived)
- os << "const char " << traits << "::" << endl
- << "find_discriminator_statement_name[] = " <<
- strlit (statement_name ("find_discriminator", fn, c)) << ";"
- << endl;
-
- if (update_columns != 0)
- os << "const char " << traits << "::" << endl
- << "update_statement_name[] = " <<
- strlit (statement_name ("update", fn, c)) << ";"
- << endl;
-
- os << "const char " << traits << "::" << endl
- << "erase_statement_name[] = " <<
- strlit (statement_name ("erase", fn, c)) << ";"
- << endl;
-
- if (optimistic != 0)
- os << "const char " << traits << "::" << endl
- << "optimistic_erase_statement_name[] = " <<
- strlit (statement_name ("erase_optimistic", fn, c)) << ";"
- << endl;
- }
-
- // Query statement name.
- //
- if (options.generate_query ())
- {
- os << "const char " << traits << "::" << endl
- << "query_statement_name[] = " <<
- strlit (statement_name ("query", fn, c)) << ";"
- << endl
- << "const char " << traits << "::" << endl
- << "erase_query_statement_name[] = " <<
- strlit (statement_name ("erase_query", fn, c)) << ";"
- << endl;
- }
-
- // Statement types.
- //
-
- // persist_statement_types.
- //
- {
- os << "const unsigned int " << traits << "::" << endl
- << "persist_statement_types[] ="
- << "{";
-
- statement_oids st (statement_insert);
- st.traverse (c);
-
- // Empty array is not portable. So add a dummy member if we
- // are not sending anything with the insert statement.
- //
- if (cc.total == cc.inverse + cc.optimistic_managed +
- (id != 0 && !poly_derived && auto_ (*id) ? cc.id : 0))
- os << "0";
-
- os << "};";
- }
-
- // find_statement_types.
- //
- if (id != 0)
- {
- os << "const unsigned int " << traits << "::" << endl
- << "find_statement_types[] ="
- << "{";
-
- statement_oids st (statement_select, true);
- st.traverse (*id);
-
- os << "};";
- }
-
- // update_statement_types.
- //
- if (id != 0 && update_columns != 0)
- {
- os << "const unsigned int " << traits << "::" << endl
- << "update_statement_types[] ="
- << "{";
-
- {
- statement_oids st (statement_update, true, &main_section);
- st.traverse (c);
- }
-
- // Not the same as update_columns.
- //
- bool first (cc.total == cc.id + cc.inverse + cc.readonly +
- cc.separate_update + cc.optimistic_managed);
-
- statement_oids st (statement_where, first);
- st.traverse (*id);
-
- if (optimistic != 0)
- st.traverse (*optimistic);
-
- os << "};";
- }
-
- if (id != 0 && optimistic != 0)
- {
- os << "const unsigned int " << traits << "::" << endl
- << "optimistic_erase_statement_types[] ="
- << "{";
-
- statement_oids st (statement_where);
- st.traverse (*id);
- st.traverse (*optimistic);
-
- os << "};";
- }
- }
-
- virtual void
- extra_statement_cache_extra_args (bool c, bool s)
- {
- bool u (c || s);
-
- os << "," << endl
- << db << "::native_binding&" << (u ? " idn" : "") << "," << endl
- << "const unsigned int*" << (u ? " idt" : "");
- }
-
- virtual void
- view_extra (type& c)
- {
- string const& n (class_fq_name (c));
- string const& fn (flat_name (n));
- string traits ("access::view_traits_impl< " + n + ", id_pgsql >");
-
- os << "const char " << traits << "::" << endl
- << "query_statement_name[] = " <<
- strlit (statement_name ("query", fn, c)) << ";"
- << endl;
- }
-
- virtual void
- object_query_statement_ctor_args (type&,
- string const& q,
- bool process,
- bool prep)
- {
- os << "sts.connection ()," << endl;
-
- if (prep)
- os << "n," << endl;
- else
- os << "query_statement_name," << endl;
-
- os << "text," << endl
- << process << "," << endl // Process.
- << "true," << endl // Optimize.
- << q << ".parameter_types ()," << endl
- << q << ".parameter_count ()," << endl
- << q << ".parameters_binding ()," << endl
- << "imb";
- }
-
- virtual void
- object_erase_query_statement_ctor_args (type&)
- {
- os << "conn," << endl
- << "erase_query_statement_name," << endl
- << "text," << endl
- << "q.parameter_types ()," << endl
- << "q.parameter_count ()," << endl
- << "q.parameters_binding ()";
- }
-
- virtual void
- view_query_statement_ctor_args (type&,
- string const& q,
- bool process,
- bool prep)
- {
- os << "sts.connection ()," << endl;
-
- if (prep)
- os << "n," << endl;
- else
- os << "query_statement_name," << endl;
-
- os << q << ".clause ()," << endl
- << process << "," << endl // Process.
- << "true," << endl // Optimize.
- << q << ".parameter_types ()," << endl
- << q << ".parameter_count ()," << endl
- << q << ".parameters_binding ()," << endl
- << "imb";
- }
-
- virtual void
- post_query_ (type&, bool once_off)
- {
- if (once_off)
- os << "st->deallocate ();";
- }
- };
- entry<class_> class_entry_;
-
- struct container_traits : relational::container_traits, context
- {
- container_traits (base const& x): base (x) {}
-
- virtual void
- container_extra (semantics::data_member& m, semantics::type& t)
- {
- if (!object (c_) || (abstract (c_) && !polymorphic (c_)))
- return;
-
- container_kind_type ck (container_kind (t));
-
- string const& pn (public_name (m));
- string scope (scope_ + "::" + flat_prefix_ + pn + "_traits");
-
- data_member_path* imp (inverse (m, "value"));
- bool inv (imp != 0);
-
- bool smart (!inv && !unordered (m) && container_smart (t));
-
- // Statment names.
- //
-
- // Prefix top-object name to avoid conflicts with inherited
- // member statement names.
- //
- string fn (
- flat_name (
- class_fq_name (*top_object) + "_" + flat_prefix_ + pn));
-
- os << "const char " << scope << "::" << endl
- << "select_name[] = " <<
- strlit (statement_name ("select", fn, m)) << ";"
- << endl
- << "const char " << scope << "::" << endl
- << "insert_name[] = " <<
- strlit (statement_name ("insert", fn, m)) << ";"
- << endl;
-
- if (smart)
- os << "const char " << scope << "::" << endl
- << "update_name[] = " <<
- strlit (statement_name ("update", fn, m)) << ";"
- << endl;
-
- os << "const char " << scope << "::" << endl
- << "delete_name[] = " <<
- strlit (statement_name ("delete", fn, m)) << ";"
- << endl;
-
- // Statement types.
- //
-
- semantics::type& vt (container_vt (m));
- semantics::type& idt (container_idt (m));
-
- // insert statement types.
- //
- {
- os << "const unsigned int " << scope << "::" << endl
- << "insert_types[] ="
- << "{";
-
- if (!inv)
- {
- statement_oids so (statement_insert);
-
- so.traverse (m, idt, "id", "object_id");
-
- switch (ck)
- {
- case ck_ordered:
- {
- if (!unordered (m))
- so.traverse (m, container_it (m), "index", "index");
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- so.traverse (m, container_kt (m), "key", "key");
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- break;
- }
- }
-
- so.traverse (m, vt, "value", "value");
- }
- else
- // MSVC does not allow zero length arrays or uninitialized
- // non-extern const values.
- //
- os << "0";
-
- os << "};";
- }
-
- // update statement types.
- //
- if (smart)
- {
- os << "const unsigned int " << scope << "::" << endl
- << "update_types[] ="
- << "{";
-
- {
- // Use insert instead of update to include read-only members.
- //
- statement_oids so (statement_insert);
- so.traverse (m, vt, "value", "value");
- }
-
- statement_oids so (statement_where, false);
- so.traverse (m, idt, "id", "object_id");
-
- switch (ck)
- {
- case ck_ordered:
- {
- if (!unordered (m))
- so.traverse (m, container_it (m), "index", "index");
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- //so.traverse (m, container_kt (t), "key", "key");
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- //so.traverse (m, vt, "value", "value");
- break;
- }
- }
-
- os << "};";
- }
-
- // delete statement types.
- //
- if (smart)
- {
- os << "const unsigned int " << scope << "::" << endl
- << "delete_types[] ="
- << "{";
-
- statement_oids so (statement_where);
- so.traverse (m, idt, "id", "object_id");
-
- switch (ck)
- {
- case ck_ordered:
- {
- if (!unordered (m))
- so.traverse (m, container_it (m), "index", "index");
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- //so.traverse (m, container_kt (t), "key", "key");
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- //so.traverse (m, vt, "value", "value");
- break;
- }
- }
-
- os << "};";
- }
- }
- };
- entry<container_traits> container_traits_;
-
- struct section_traits : relational::section_traits, context
- {
- section_traits (base const& x): base (x) {}
-
- virtual void
- section_extra (user_section& s)
- {
- semantics::class_* poly_root (polymorphic (c_));
- bool poly (poly_root != 0);
-
- if (!poly && (abstract (c_) ||
- s.special == user_section::special_version))
- return;
-
- semantics::data_member* opt (optimistic (c_));
-
- bool load (s.total != 0 && s.separate_load ());
- bool load_opt (s.optimistic () && s.separate_load ());
-
- bool update (s.total != s.inverse + s.readonly); // Always separate.
- bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
-
- string name (public_name (*s.member));
- string scope (scope_ + "::" + name + "_traits");
-
- // Statment names.
- //
-
- // Prefix object name to avoid conflicts with inherited member
- // statement names.
- //
- string fn (flat_name (class_fq_name (c_) + "_" + name));
-
- if (load || load_opt)
- os << "const char " << scope << "::" << endl
- << "select_name[] = " <<
- strlit (statement_name ("select", fn, *s.member)) << ";"
- << endl;
-
- if (update || update_opt)
- os << "const char " << scope << "::" << endl
- << "update_name[] = " <<
- strlit (statement_name ("update", fn, *s.member)) << ";"
- << endl;
-
- // Statement types.
- //
- if (update || update_opt)
- {
- os << "const unsigned int " << scope << "::" << endl
- << "update_types[] ="
- << "{";
-
- {
- statement_oids st (statement_update, true, &s);
- st.traverse (c_);
- }
-
- statement_oids st (statement_where, !update);
- st.traverse (*id_member (c_));
-
- if (s.optimistic ()) // Note: not update_opt.
- st.traverse (*opt);
-
- os << "};";
- }
- }
- };
- entry<section_traits> section_traits_;
-
- struct container_cache_init_members:
- relational::container_cache_init_members
- {
- container_cache_init_members (base const& x): base (x) {}
-
- virtual void
- extra_members ()
- {
- os << ", idn, idt";
- }
- };
- entry<container_cache_init_members> container_cache_init_members_;
-
- struct section_cache_init_members:
- relational::section_cache_init_members
- {
- section_cache_init_members (base const& x): base (x) {}
-
- virtual void
- extra_members ()
- {
- os << ", idn, idt";
- }
- };
- entry<section_cache_init_members> section_cache_init_members_;
- }
- }
-}
diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx
deleted file mode 100644
index aac8d79..0000000
--- a/odb/relational/processor.cxx
+++ /dev/null
@@ -1,1562 +0,0 @@
-// file : odb/relational/processor.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <odb/gcc.hxx>
-
-#include <vector>
-#include <algorithm>
-
-#include <odb/diagnostics.hxx>
-#include <odb/lookup.hxx>
-#include <odb/cxx-lexer.hxx>
-#include <odb/common.hxx>
-
-#include <odb/relational/context.hxx>
-#include <odb/relational/processor.hxx>
-
-using namespace std;
-
-namespace relational
-{
- namespace
- {
- // Indirect (dynamic) context values.
- //
- static string
- id_column_type ()
- {
- context& c (context::current ());
- data_member_path& id (*context::id_member (*c.top_object));
- return id.back ()->get<string> ("column-id-type");
- }
-
- struct data_member: traversal::data_member, context
- {
- virtual void
- traverse (semantics::data_member& m)
- {
- if (transient (m))
- return;
-
- semantics::names* hint;
- semantics::type& t (utype (m, hint));
-
- semantics::type* wt;
- semantics::names* whint (0);
- if ((wt = wrapper (t, whint)))
- wt = &utype (*wt, whint);
-
- // Determine the member kind.
- //
- enum {simple, composite, container, unknown} kind (unknown);
-
- // See if this is a composite value type.
- //
- if (composite_wrapper (t))
- kind = composite;
-
- // If not, see if it is a simple value.
- //
- if (kind == unknown)
- {
- string type, id_type;
-
- if (m.count ("id-type"))
- id_type = m.get<string> ("id-type");
-
- if (m.count ("type"))
- {
- type = m.get<string> ("type");
-
- if (id_type.empty ())
- id_type = type;
- }
-
- if (semantics::class_* c = object_pointer (t))
- {
- // An object pointer in view doesn't really have a "column"
- // so pretend that it has already been handled.
- //
- if (view_member (m))
- kind = simple;
- else
- {
- // This is an object pointer. The column type is the pointed-to
- // object id type.
- //
- semantics::data_member& id (*id_member (*c)->back ());
-
- semantics::names* idhint;
- semantics::type& idt (utype (id, idhint));
-
- // The id type can be a composite value type.
- //
- if (composite_wrapper (idt))
- kind = composite;
- else
- {
- semantics::type* wt;
- semantics::names* whint (0);
- if ((wt = wrapper (idt, whint)))
- wt = &utype (*wt, whint);
-
- if (type.empty () && id.count ("id-type"))
- type = id.get<string> ("id-type");
-
- if (type.empty () && id.count ("type"))
- type = id.get<string> ("type");
-
- // The rest should be identical to the code for the id_type in
- // the else block.
- //
- if (type.empty () && idt.count ("id-type"))
- type = idt.get<string> ("id-type");
-
- if (type.empty () && wt != 0 && wt->count ("id-type"))
- type = wt->get<string> ("id-type");
-
- if (type.empty () && idt.count ("type"))
- type = idt.get<string> ("type");
-
- if (type.empty () && wt != 0 && wt->count ("type"))
- type = wt->get<string> ("type");
-
- if (type.empty ())
- type = database_type (idt, idhint, true);
-
- if (type.empty () && wt != 0)
- type = database_type (*wt, whint, true);
-
- id_type = type;
- }
- }
- }
- else
- {
- if (id_type.empty () && t.count ("id-type"))
- id_type = t.get<string> ("id-type");
-
- if (id_type.empty () && wt != 0 && wt->count ("id-type"))
- id_type = wt->get<string> ("id-type");
-
- if (type.empty () && t.count ("type"))
- type = t.get<string> ("type");
-
- if (type.empty () && wt != 0 && wt->count ("type"))
- type = wt->get<string> ("type");
-
- if (id_type.empty ())
- id_type = type;
-
- if (id_type.empty ())
- id_type = database_type (t, hint, true);
-
- if (id_type.empty () && wt != 0)
- id_type = database_type (*wt, whint, true);
-
- bool null (false);
- if (type.empty ())
- type = database_type (t, hint, false, &null);
-
- if (type.empty () && wt != 0)
- type = database_type (*wt, whint, false, &null);
-
- // Use id mapping for discriminators.
- //
- if (id (m) || discriminator (m))
- type = id_type;
- // Allow NULL if requested by the default mapping.
- //
- else if (null && !m.count ("not-null"))
- m.set ("null", true);
- }
-
- if (kind == unknown && !type.empty ())
- {
- m.set ("column-type", type);
- m.set ("column-id-type", id_type);
-
- // Issue a warning if we are relaxing null-ness.
- //
- if (m.count ("null") && t.count ("not-null"))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " warning: data member declared null while its type is "
- << "declared not null" << endl;
- }
-
- kind = simple;
- }
- }
-
- // If not a simple value, see if this is a container.
- //
- if (kind == unknown && context::container (m))
- {
- process_container (m, (wt != 0 ? *wt : t));
- kind = container;
- }
-
- // If it is none of the above then we have an error.
- //
- if (kind == unknown)
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: unable to map C++ type '" << t.fq_name (hint)
- << "' used in data member '" << m.name () << "' to a "
- << db.name () << " database type" << endl;
-
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " info: use '#pragma db type' to specify the database type"
- << endl;
-
- throw operation_failed ();
- }
-
- if (m.count ("polymorphic-ref"))
- {
- // Copy the column name from the root's id member, if specified.
- //
- {
- semantics::class_& r (*object_pointer (t));
- semantics::data_member& id (*id_member (r)->front ());
-
- if (id.count ("column"))
- m.set ("column", id.get<table_column> ("column"));
- }
-
- m.set ("not-null", true);
- m.set ("deferrable",
- sema_rel::deferrable (sema_rel::deferrable::not_deferrable));
- m.set ("on-delete", sema_rel::foreign_key::cascade);
- }
-
- process_index (m);
- }
-
- // Convert index/unique specifiers to the index entry in the object.
- //
- void
- process_index (semantics::data_member& m)
- {
- bool ip (m.count ("index"));
- bool up (m.count ("unique"));
-
- if (ip || up)
- {
- using semantics::class_;
- class_& c (dynamic_cast<class_&> (m.scope ()));
-
- indexes& ins (c.count ("index")
- ? c.get<indexes> ("index")
- : c.set ("index", indexes ()));
-
- index in;
- in.loc = m.get<location_t> (
- ip ? "index-location" : "unique-location");
-
- if (up)
- in.type = "UNIQUE";
-
- index::member im;
- im.loc = in.loc;
- im.name = m.name ();
- im.path.push_back (&m);
- in.members.push_back (im);
-
- // Insert it in the location order.
- //
- ins.insert (
- lower_bound (ins.begin (), ins.end (), in, index_comparator ()),
- in);
- }
- }
-
- void
- process_container_value (semantics::type& t,
- semantics::names* hint,
- semantics::data_member& m,
- string const& prefix,
- bool obj_ptr)
- {
- if (composite_wrapper (t))
- return;
-
- semantics::names* wh (0);
- semantics::type* wt (wrapper (t, wh));
-
- string type;
- semantics::type& ct (utype (m));
-
- // Custom mapping can come from these places (listed in the order
- // of priority): member, container type, value type. To complicate
- // things a bit, for object references, it can also come from the
- // member and value type of the id member.
- //
- if (m.count (prefix + "-type"))
- type = m.get<string> (prefix + "-type");
-
- if (type.empty () && ct.count (prefix + "-type"))
- type = ct.get<string> (prefix + "-type");
-
- semantics::class_* c;
- if (obj_ptr && (c = object_pointer (t)))
- {
- // This is an object pointer. The column type is the pointed-to
- // object id type.
- //
- semantics::data_member& id (*id_member (*c)->back ());
-
- semantics::names* idhint;
- semantics::type& idt (utype (id, idhint));
-
- // Nothing to do if this is a composite value type.
- //
- if (composite_wrapper (idt))
- return;
-
- semantics::type* wt (0);
- semantics::names* whint (0);
- if ((wt = wrapper (idt, whint)))
- wt = &utype (*wt, whint);
-
- if (type.empty () && id.count ("id-type"))
- type = id.get<string> ("id-type");
-
- if (type.empty () && id.count ("type"))
- type = id.get<string> ("type");
-
- // The rest of the code is identical to the else block except here
- // we have to check for "id-type" before checking for "type".
- //
-
- if (type.empty () && idt.count ("id-type"))
- type = idt.get<string> ("id-type");
-
- if (type.empty () && wt != 0 && wt->count ("id-type"))
- type = wt->get<string> ("id-type");
-
- if (type.empty () && idt.count ("type"))
- type = idt.get<string> ("type");
-
- if (type.empty () && wt != 0 && wt->count ("type"))
- type = wt->get<string> ("type");
-
- if (type.empty ())
- type = database_type (idt, idhint, true);
-
- if (type.empty () && wt != 0)
- type = database_type (*wt, whint, true);
- }
- else
- {
- if (type.empty () && t.count ("type"))
- type = t.get<string> ("type");
-
- if (type.empty () && wt != 0 && wt->count ("type"))
- type = wt->get<string> ("type");
-
- bool null (false);
- if (type.empty ())
- type = database_type (t, hint, false, &null);
-
- if (type.empty () && wt != 0)
- type = database_type (*wt, wh, false, &null);
-
- // Allow NULL if requested by the default mapping.
- //
- if (null && !m.count (prefix + "-not-null"))
- m.set (prefix + "-null", true);
- }
-
- if (!type.empty ())
- {
- m.set (prefix + "-column-type", type);
- m.set (prefix + "-column-id-type", type);
- return;
- }
-
- // We do not support nested containers so skip that test.
- //
-
- // If it is none of the above then we have an error.
- //
- string fq_type (t.fq_anonymous () ? "<anonymous>" : t.fq_name ());
-
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: unable to map C++ type '" << fq_type << "' used in "
- << "data member '" << m.name () << "' to a " << db.name ()
- << " database type" << endl;
-
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " info: use '#pragma db " << prefix << "_type' to specify the "
- << "database type" << endl;
-
- throw operation_failed ();
- }
-
- void
- process_container (semantics::data_member& m, semantics::type& t)
- {
- container_kind_type ck (t.get<container_kind_type> ("container-kind"));
-
- semantics::names* vh (0);
- semantics::names* ih (0);
- semantics::names* kh (0);
-
- semantics::type* vt (&utype (m, vh, "value"));
- semantics::type* it (ck == ck_ordered ? &utype (m, ih, "index") : 0);
- semantics::type* kt (ck == ck_map || ck == ck_multimap
- ? &utype (m, kh, "key")
- : 0);
-
- // Process member data.
- //
- m.set ("id-column-type", &id_column_type);
-
- process_container_value (*vt, vh, m, "value", true);
-
- if (it != 0)
- process_container_value (*it, ih, m, "index", false);
-
- if (kt != 0)
- process_container_value (*kt, kh, m, "key", true);
- }
- };
-
- //
- //
- struct view_data_member: traversal::data_member, context
- {
- view_data_member (semantics::class_& c)
- : view_ (c),
- query_ (c.get<view_query> ("query")),
- amap_ (c.get<view_alias_map> ("alias-map")),
- omap_ (c.get<view_object_map> ("object-map"))
- {
- }
-
- struct assoc_member
- {
- semantics::data_member* m;
- view_object* vo;
- };
-
- typedef vector<assoc_member> assoc_members;
-
- virtual void
- traverse (semantics::data_member& m)
- {
- using semantics::data_member;
-
- if (transient (m))
- return;
-
- semantics::type& t (utype (m));
-
- // Object pointers are associated with objects.
- //
- if (object_pointer (t))
- return;
-
- data_member* src_m (0); // Source member.
-
- // Resolve member references in column expressions.
- //
- if (m.count ("column"))
- {
- // Column literal.
- //
- if (query_.kind != view_query::condition)
- {
- warn (m.get<location_t> ("column-location"))
- << "db pragma column ignored in a view with "
- << (query_.kind == view_query::runtime ? "runtime" : "complete")
- << " query" << endl;
- }
-
- return;
- }
- else if (m.count ("column-expr"))
- {
- column_expr& e (m.get<column_expr> ("column-expr"));
-
- if (query_.kind != view_query::condition)
- {
- warn (e.loc)
- << "db pragma column ignored in a view with "
- << (query_.kind == view_query::runtime ? "runtime" : "complete")
- << " query" << endl;
- return;
- }
-
- for (column_expr::iterator i (e.begin ()); i != e.end (); ++i)
- {
- // This code is quite similar to translate_expression in the
- // source generator.
- //
- try
- {
- using semantics::scope;
- using semantics::class_;
-
- if (i->kind != column_expr_part::reference)
- continue;
-
- lex_.start (i->value);
-
- string tl;
- tree tn;
- cpp_ttype tt (lex_.next (tl, &tn));
-
- data_member* m (0);
- view_object* vo (0);
-
- // Check if this is an alias.
- //
- if (tt == CPP_NAME)
- {
- view_alias_map::iterator j (amap_.find (tl));
-
- if (j != amap_.end ())
- {
- vo = j->second;
-
- // Skip '::'.
- //
- if (lex_.next (tl, &tn) != CPP_SCOPE)
- {
- error (i->loc) << "member name expected after an alias " <<
- "in db pragma column" << endl;
- throw operation_failed ();
- }
-
- if (lex_.next (tl, &tn) != CPP_NAME)
- throw lookup::invalid_name ();
-
- m = &vo->obj->lookup<data_member> (
- tl, scope::include_hidden);
-
- tt = lex_.next (tl, &tn);
- }
- }
-
- // If it is not an alias, do the normal lookup.
- //
- if (vo == 0)
- {
- // Also get the object type. We need to do it so that
- // we can get the correct (derived) table name (the
- // member itself can come from a base class).
- //
- scope* s;
- string name;
- cpp_ttype ptt; // Not used.
- m = &lookup::resolve_scoped_name<data_member> (
- lex_, tt, tl, tn, ptt,
- dynamic_cast<scope&> (*unit.find (i->scope)),
- name,
- false,
- &s);
-
- view_object_map::iterator j (
- omap_.find (dynamic_cast<class_*> (s)));
-
- if (j == omap_.end ())
- {
- error (i->loc) << "name '" << name << "' in db pragma " <<
- "column does not refer to a data member of a " <<
- "persistent class that is used in this view" << endl;
- throw operation_failed ();
- }
-
- vo = j->second;
- }
-
- i->member_path.push_back (m);
-
- // Figure out the table name/alias for this member.
- //
- if (class_* root = polymorphic (*vo->obj))
- {
- // If the object is polymorphic, then figure out which of the
- // bases this member comes from and use the corresponding
- // table.
- //
- class_* c (&static_cast<class_&> (m->scope ()));
-
- // If this member's class is not polymorphic (root uses reuse
- // inheritance), then use the root table.
- //
- if (!polymorphic (*c))
- c = root;
-
- // In a polymorphic hierarchy we have several tables and the
- // provided alias is used as a prefix together with the table
- // name to form the actual alias.
- //
- qname const& t (table_name (*c));
-
- if (vo->alias.empty ())
- i->table = t;
- else
- i->table = qname (vo->alias + "_" + t.uname ());
- }
- else
- i->table = vo->alias.empty ()
- ? table_name (*vo->obj)
- : qname (vo->alias);
-
- // Finally, resolve nested members if any.
- //
- for (; tt == CPP_DOT; tt = lex_.next (tl, &tn))
- {
- lex_.next (tl, &tn); // Get CPP_NAME.
-
- // Check that the outer member is composite and also
- // unwrap it while at it.
- //
- class_* comp (composite_wrapper (utype (*m)));
- if (comp == 0)
- {
- error (i->loc) << "data member '" << m->name () << "' " <<
- "specified in db pragma column is not composite" << endl;
- throw operation_failed ();
- }
-
- m = &comp->lookup<data_member> (tl, class_::include_hidden);
- i->member_path.push_back (m);
- }
-
- // If the expression is just this reference, then we have
- // a source member.
- //
- if (e.size () == 1)
- src_m = m;
- }
- catch (lookup::invalid_name const&)
- {
- error (i->loc) << "invalid name in db pragma column" << endl;
- throw operation_failed ();
- }
- catch (semantics::unresolved const& e)
- {
- if (e.type_mismatch)
- error (i->loc) << "name '" << e.name << "' in db pragma " <<
- "column does not refer to a data member" << endl;
- else
- error (i->loc) << "unable to resolve data member '" <<
- e.name << "' specified with db pragma column" << endl;
-
- throw operation_failed ();
- }
- catch (semantics::ambiguous const& e)
- {
- error (i->loc) << "data member name '" << e.first.name () <<
- "' specified with db pragma column is ambiguous" << endl;
-
- info (e.first.named ().location ()) << "could resolve to " <<
- "this data member" << endl;
-
- info (e.second.named ().location ()) << "or could resolve " <<
- "to this data member" << endl;
-
- throw operation_failed ();
- }
- }
-
- // Check that the source member is not transient or inverse. Also
- // check that the C++ types are the same (sans cvr-qualification
- // and wrapping) and issue a warning if they differ. In rare cases
- // where this is not a mistake, the user can use a phony expression
- // (e.g., "" + person:name) to disable the warning. Note that in
- // this case there will be no type pragma copying, which is probably
- // ok seeing that the C++ types are different.
- //
- //
- if (src_m != 0)
- {
- string reason;
-
- if (transient (*src_m))
- reason = "transient";
- else if (inverse (*src_m))
- reason = "inverse";
-
- if (!reason.empty ())
- {
- error (e.loc)
- << "object data member '" << src_m->name () << "' specified "
- << "in db pragma column is " << reason << endl;
- throw operation_failed ();
- }
-
- if (!member_resolver::check_types (utype (*src_m), utype (m)))
- {
- warn (e.loc)
- << "object data member '" << src_m->name () << "' specified "
- << "in db pragma column has a different type compared to the "
- << "view data member" << endl;
-
- info (src_m->file (), src_m->line (), src_m->column ())
- << "object data member is defined here" << endl;
-
- info (m.file (), m.line (), m.column ())
- << "view data member is defined here" << endl;
- }
- }
- }
- // This member has no column information. If we are generating our
- // own query, try to find a member with the same (or similar) name
- // in one of the associated objects.
- //
- else if (query_.kind == view_query::condition)
- {
- view_objects& objs (view_.get<view_objects> ("objects"));
-
- assoc_members exact_members, pub_members;
- member_resolver resolver (exact_members, pub_members, m);
-
- for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
- {
- if (i->kind == view_object::object)
- resolver.traverse (*i);
- }
-
- assoc_members& members (
- !exact_members.empty () ? exact_members : pub_members);
-
- // Issue diagnostics if we didn't find any or found more
- // than one.
- //
- if (members.empty ())
- {
- error (m.file (), m.line (), m.column ())
- << "unable to find a corresponding data member for '"
- << m.name () << "' in any of the associated objects" << endl;
-
- info (m.file (), m.line (), m.column ())
- << "use db pragma column to specify the corresponding data "
- << "member or column name" << endl;
-
- throw operation_failed ();
- }
- else if (members.size () > 1)
- {
- error (m.file (), m.line (), m.column ())
- << "corresponding data member for '" << m.name () << "' is "
- << "ambiguous" << endl;
-
- info (m.file (), m.line (), m.column ())
- << "candidates are:" << endl;
-
- for (assoc_members::const_iterator i (members.begin ());
- i != members.end ();
- ++i)
- {
- info (i->m->file (), i->m->line (), i->m->column ())
- << " '" << i->m->name () << "' in object '"
- << i->vo->name () << "'" << endl;
- }
-
- info (m.file (), m.line (), m.column ())
- << "use db pragma column to resolve this ambiguity" << endl;
-
- throw operation_failed ();
- }
-
- // Synthesize the column expression for this member.
- //
- assoc_member const& am (members.back ());
-
- column_expr& e (m.set ("column-expr", column_expr ()));
- e.push_back (column_expr_part ());
- column_expr_part& ep (e.back ());
-
- ep.kind = column_expr_part::reference;
-
-
- // If this object is polymorphic, then figure out which of the
- // bases this member comes from and use the corresponding table.
- //
- using semantics::class_;
-
- if (class_* root = polymorphic (*am.vo->obj))
- {
- class_* c (&static_cast<class_&> (am.m->scope ()));
-
- // If this member's class is not polymorphic (root uses reuse
- // inheritance), then use the root table.
- //
- if (!polymorphic (*c))
- c = root;
-
- // In a polymorphic hierarchy we have several tables and the
- // provided alias is used as a prefix together with the table
- // name to form the actual alias.
- //
- qname const& t (table_name (*c));
-
- if (am.vo->alias.empty ())
- ep.table = t;
- else
- ep.table = qname (am.vo->alias + "_" + t.uname ());
- }
- else
- ep.table = am.vo->alias.empty ()
- ? table_name (*am.vo->obj)
- : qname (am.vo->alias);
-
- ep.member_path.push_back (am.m);
-
- src_m = am.m;
- }
-
- // If we have the source member and don't have the type pragma of
- // our own, but the source member does, then copy the columnt type
- // over. In case the source member is a pointer, also check the id
- // member.
- //
- if (src_m != 0 && !m.count ("type"))
- {
- if (src_m->count ("type"))
- m.set ("column-type", src_m->get<string> ("column-type"));
- else if (semantics::class_* c = object_pointer (utype (*src_m)))
- {
- semantics::data_member& id (*id_member (*c)->back ());
-
- if (id.count ("type"))
- m.set ("column-type", id.get<string> ("column-type"));
- }
- }
-
- // Check the return statements above if you add any extra logic
- // here.
- }
-
- struct member_resolver: traversal::class_
- {
- member_resolver (assoc_members& members,
- assoc_members& pub_members,
- semantics::data_member& m)
- : member_ (members, pub_members, m)
- {
- *this >> names_ >> member_;
- *this >> inherits_ >> *this;
- }
-
- void
- traverse (view_object& vo)
- {
- member_.vo_ = &vo;
-
- // First look for an exact match.
- //
- {
- member_.exact_ = true;
- member_.found_ = false;
- traverse (*vo.obj);
- }
-
- // If we didn't find an exact match, then look for a public
- // name match.
- //
- if (!member_.found_)
- {
- member_.exact_ = false;
- traverse (*vo.obj);
- }
- }
-
- virtual void
- traverse (type& c)
- {
- if (!object (c))
- return; // Ignore transient bases.
-
- names (c);
-
- // If we already found a match in one of the derived classes,
- // don't go into bases to get the standard "hiding" behavior.
- //
- if (!member_.found_)
- inherits (c);
- }
-
- public:
- static bool
- check_types (semantics::type& ot, semantics::type& vt)
- {
- using semantics::type;
-
- // Require that the types be the same sans the wrapping and
- // cvr-qualification. If the object member type is a pointer,
- // use the id type of the pointed-to object.
- //
- type* t1;
-
- if (semantics::class_* c = object_pointer (ot))
- t1 = &utype (*id_member (*c));
- else
- t1 = &ot;
-
- type* t2 (&vt);
-
- if (type* wt1 = context::wrapper (*t1))
- t1 = &utype (*wt1);
-
- if (type* wt2 = context::wrapper (*t2))
- t2 = &utype (*wt2);
-
- if (t1 != t2)
- return false;
-
- return true;
- }
-
- private:
- struct data_member: traversal::data_member
- {
- data_member (assoc_members& members,
- assoc_members& pub_members,
- semantics::data_member& m)
- : members_ (members),
- pub_members_ (pub_members),
- name_ (m.name ()),
- pub_name_ (context::current ().public_name (m)),
- type_ (utype (m))
- {
- }
-
- virtual void
- traverse (type& m)
- {
- if (exact_)
- {
- if (name_ == m.name () && check (m))
- {
- assoc_member am;
- am.m = &m;
- am.vo = vo_;
- members_.push_back (am);
- found_ = true;
- }
- }
- else
- {
- if (pub_name_ == context::current ().public_name (m) &&
- check (m))
- {
- assoc_member am;
- am.m = &m;
- am.vo = vo_;
- pub_members_.push_back (am);
- found_ = true;
- }
- }
- }
-
- bool
- check (semantics::data_member& m)
- {
- // Make sure that the found node can possibly match.
- //
- if (context::transient (m) ||
- context::inverse (m) ||
- m.count ("polymorphic-ref"))
- return false;
-
- return check_types (utype (m), type_);
- }
-
- assoc_members& members_;
- assoc_members& pub_members_;
-
- string name_;
- string pub_name_;
- semantics::type& type_;
-
- view_object* vo_;
- bool exact_;
- bool found_;
- };
-
- traversal::names names_;
- data_member member_;
- traversal::inherits inherits_;
- };
-
- private:
- semantics::class_& view_;
- view_query& query_;
- view_alias_map& amap_;
- view_object_map& omap_;
- cxx_string_lexer lex_;
- };
-
- struct class_: traversal::class_, context
- {
- class_ ()
- : typedefs_ (true)
- {
- *this >> defines_ >> *this;
- *this >> typedefs_ >> *this;
-
- member_names_ >> member_;
- }
-
- virtual void
- traverse (type& c)
- {
- class_kind_type k (class_kind (c));
-
- if (k == class_other)
- return;
-
- names (c); // Process nested classes.
- names (c, member_names_);
-
- if (k == class_object)
- traverse_object (c);
- else if (k == class_view)
- traverse_view (c);
- }
-
- //
- // Object.
- //
-
- virtual void
- traverse_object (type& c)
- {
- // Remove the bulk pragma if this database doesn't support bulk
- // operations.
- //
- if (c.count ("bulk") && !generate_bulk)
- c.remove ("bulk");
-
- // Process indexes. Here we need to do two things: resolve member
- // names to member paths and assign names to unnamed indexes. We
- // are also going to handle the special container indexes.
- //
- indexes& ins (c.count ("index")
- ? c.get<indexes> ("index")
- : c.set ("index", indexes ()));
-
- for (indexes::iterator i (ins.begin ()); i != ins.end ();)
- {
- index& in (*i);
-
- // This should never happen since a db index pragma without
- // the member specifier will be treated as a member pragma.
- //
- assert (!in.members.empty ());
-
- // First resolve member names.
- //
- index::members_type::iterator j (in.members.begin ());
- for (; j != in.members.end (); ++j)
- {
- index::member& im (*j);
-
- if (!im.path.empty ())
- continue; // Already resolved.
-
- im.path = resolve_data_members (c, im.name, im.loc, lex_);
-
- if (container (*im.path.back ()))
- break;
- }
-
- // Add the table prefix if this database has global index names.
- //
- if (!in.name.empty () && global_index)
- in.name = table_name_prefix (class_scope (c)) + in.name;
-
- // Handle container indexes.
- //
- if (j != in.members.end ())
- {
- // Do some sanity checks.
- //
- if (in.members.size () != 1)
- {
- error (in.loc) << "multiple data members specified for a "
- << "container index" << endl;
- throw operation_failed ();
- }
-
- string tl;
- if (lex_.next (tl) != CPP_DOT || lex_.next (tl) != CPP_NAME ||
- (tl != "id" && tl != "index"))
- {
- error (j->loc) << ".id or .index special member expected in a "
- << "container index" << endl;
- throw operation_failed ();
- }
-
- string n (tl);
-
- if (lex_.next (tl) != CPP_EOF)
- {
- error (j->loc) << "unexpected text after ." << n << " in "
- << "db pragma member" << endl;
- throw operation_failed ();
- }
-
- // Move this index to the container member.
- //
- j->path.back ()->set (n + "-index", *i);
- i = ins.erase (i);
- continue;
- }
-
- // Now assign the name if the index is unnamed. We have to
- // add table name as a prefix here since there is not way
- // to distinguish between user-assigned and auto-derived
- // names in the model.
- //
- if (in.name.empty ())
- {
- // Make sure there is only one member.
- //
- if (in.members.size () > 1)
- {
- error (in.loc) << "unnamed index with more than one data "
- << "member" << endl;
- throw operation_failed ();
- }
-
- // Generally, we want the index name to be based on the column
- // name. This is straightforward for single-column members. In
- // case of a composite member, we will need to use the column
- // prefix which is based on the data member name, unless
- // overridden by the user. In the latter case the prefix can
- // be empty, in which case we will just fall back on the
- // member's public name.
- //
- string n (column_prefix (in.members.front ().path, true).prefix);
-
- if (n.empty ())
- n = public_name_db (*in.members.front ().path.back ());
- else if (n[n.size () - 1] == '_')
- n.resize (n.size () - 1); // Remove trailing underscore.
-
- in.name = index_name (table_name (c), n);
- }
-
- ++i;
- }
- }
-
- //
- // View.
- //
-
- struct relationship
- {
- semantics::data_member* member;
- string name;
- view_object* pointer;
- view_object* pointee;
- };
-
- typedef vector<relationship> relationships;
-
- virtual void
- traverse_view (type& c)
- {
- bool has_q (c.count ("query"));
- bool has_o (c.count ("objects"));
-
- // Determine the kind of query template we've got.
- //
- view_query& vq (has_q
- ? c.get<view_query> ("query")
- : c.set ("query", view_query ()));
- if (has_q)
- {
- if (!vq.literal.empty ())
- {
- string q (upcase (vq.literal));
-
- //@@ We need to recognize database-specific list of prefixes. For
- // example, PG has WITH. Alternatively (or in addition) we could
- // do the same comment trick (e.g., /*SELECT*/ to treat it as a
- // SELECT-like queiry).
- //
- if (q.compare (0, 7, "SELECT ") == 0)
- vq.kind = view_query::complete_select;
- else if (q.compare (0, 5, "EXEC ") == 0 ||
- q.compare (0, 5, "CALL ") == 0 ||
- q.compare (0, 8, "EXECUTE ") == 0)
- vq.kind = view_query::complete_execute;
- //
- // Hint for databases that use SELECT for stored procedure
- // calls (e.g., PostgreSQL).
- //
- else if (q.compare (0, 8, "/*CALL*/") == 0)
- {
- vq.literal = string (vq.literal, q[8] == ' ' ? 9 : 8);
- vq.kind = view_query::complete_execute;
- }
- else
- vq.kind = view_query::condition;
- }
- else if (!vq.expr.empty ())
- {
- // If the first token in the expression is a string and
- // it starts with "SELECT " or is equal to "SELECT" or
- // one of the stored procedure call keywords, then we
- // have a complete query.
- //
- if (vq.expr.front ().type == CPP_STRING)
- {
- string q (upcase (vq.expr.front ().literal));
-
- if (q.compare (0, 7, "SELECT ") == 0 || q == "SELECT")
- vq.kind = view_query::complete_select;
- else if (q.compare (0, 5, "EXEC ") == 0 || q == "EXEC" ||
- q.compare (0, 5, "CALL ") == 0 || q == "CALL" ||
- q.compare (0, 8, "EXECUTE ") == 0 || q == "EXECUTE")
- vq.kind = view_query::complete_execute;
- else if (q.compare (0, 8, "/*CALL*/") == 0)
- {
- vq.expr.front ().literal =
- string (vq.expr.front ().literal, q[8] == ' ' ? 9 : 8);
- vq.kind = view_query::complete_execute;
- }
- else
- vq.kind = view_query::condition;
- }
- else
- vq.kind = view_query::condition;
- }
- else
- vq.kind = (vq.distinct || vq.for_update)
- ? view_query::condition // The query(distinct) case.
- : view_query::runtime;
- }
- else
- vq.kind = has_o ? view_query::condition : view_query::runtime;
-
- if ((vq.distinct || vq.for_update) && vq.kind != view_query::condition)
- {
- error (vq.loc)
- << "result modifier specified for "
- << (vq.kind == view_query::runtime ? "runtime" : "native")
- << " query" << endl;
-
- throw operation_failed ();
- }
-
- // We cannot have an incomplete query if there are not objects
- // to derive the rest from.
- //
- if (vq.kind == view_query::condition && !has_o)
- {
- error (c.file (), c.line (), c.column ())
- << "view '" << class_fq_name (c) << "' has an incomplete query "
- << "template and no associated objects" << endl;
-
- info (c.file (), c.line (), c.column ())
- << "use db pragma query to provide a complete query template"
- << endl;
-
- info (c.file (), c.line (), c.column ())
- << "or use db pragma object to associate one or more objects "
- << "with the view"
- << endl;
-
- throw operation_failed ();
- }
-
- // Process join conditions.
- //
- if (has_o)
- {
- view_objects& objs (c.get<view_objects> ("objects"));
-
- for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
- {
- if (i == objs.begin () && i->join != view_object::left)
- {
- error (i->loc)
- << "no join type can be specified for the first associated "
- << (i->kind == view_object::object ? "object" : "table")
- << endl;
- throw operation_failed ();
- }
-
- if (i->kind != view_object::object)
- {
- // Make sure we have join conditions for tables unless it
- // is the first entry.
- //
- if (i != objs.begin () && i->cond.empty ())
- {
- error (i->loc)
- << "missing join condition in db pragma table" << endl;
-
- throw operation_failed ();
- }
-
- continue;
- }
-
- // If we have to generate the query and there was no JOIN
- // condition specified by the user, try to come up with one
- // automatically based on object relationships. CROSS JOIN
- // has no condition.
- //
- if (vq.kind == view_query::condition &&
- i->cond.empty () &&
- i != objs.begin () &&
- i->join != view_object::cross)
- {
- relationships rs;
-
- // Check objects specified prior to this one for any
- // relationships. We don't examine objects that were
- // specified after this one because that would require
- // rearranging the JOIN order.
- //
- for (view_objects::iterator j (objs.begin ()); j != i; ++j)
- {
- if (j->kind != view_object::object)
- continue; // Skip tables.
-
- // First see if any of the objects that were specified
- // prior to this object point to it.
- //
- {
- relationship_resolver r (rs, *i, true);
- r.traverse (*j);
- }
-
- // Now see if this object points to any of the objects
- // specified prior to it.
- //
- {
- relationship_resolver r (rs, *j, false);
- r.traverse (*i);
- }
- }
-
- // Issue diagnostics if we didn't find any or found more
- // than one.
- //
- if (rs.empty ())
- {
- error (i->loc)
- << "unable to find an object relationship involving "
- << "object '" << i->name () << "' and any of the previously "
- << "associated objects" << endl;
-
- info (i->loc)
- << "use the join condition clause in db pragma object "
- << "to specify a custom join condition" << endl;
-
- throw operation_failed ();
- }
- else if (rs.size () > 1)
- {
- error (i->loc)
- << "object relationship for object '" << i->name () << "' "
- << "is ambiguous" << endl;
-
- info (i->loc)
- << "candidates are:" << endl;
-
- for (relationships::const_iterator j (rs.begin ());
- j != rs.end ();
- ++j)
- {
- semantics::data_member& m (*j->member);
-
- info (m.file (), m.line (), m.column ())
- << " '" << j->name << "' "
- << "in object '" << j->pointer->name () << "' "
- << "pointing to '" << j->pointee->name () << "'"
- << endl;
- }
-
- info (i->loc)
- << "use the join condition clause in db pragma object "
- << "to resolve this ambiguity" << endl;
-
- throw operation_failed ();
- }
-
- // Synthesize the condition.
- //
- relationship const& r (rs.back ());
-
- string name (r.pointer->alias.empty ()
- ? class_fq_name (*r.pointer->obj)
- : r.pointer->alias);
- name += "::";
- name += r.name;
-
- lex_.start (name);
-
- string t;
- for (cpp_ttype tt (lex_.next (t));
- tt != CPP_EOF;
- tt = lex_.next (t))
- {
- i->cond.push_back (cxx_token (lex_.location (), tt, t));
- }
- }
- }
- }
-
- // Handle forced versioning. When versioning is forced, ignore
- // it for native views.
- //
- if (force_versioned && vq.kind == view_query::condition)
- c.set ("versioned", true);
-
- // Handle data members.
- //
- {
- view_data_member t (c);
- traversal::names n (t);
- names (c, n);
- }
- }
-
- struct relationship_resolver: object_members_base
- {
- relationship_resolver (relationships& rs,
- view_object& pointee,
- bool forward)
- // Look in polymorphic bases only for previously-associated
- // objects since backward pointers from bases will result in
- // the pathological case (we will have to join the base table
- // first, which means we will get both bases and derived objects
- // instead of just derived).
- //
- : object_members_base (false, false, true, forward),
- relationships_ (rs),
- // Ignore self-references if we are looking for backward
- // pointers since they were already added to the list in
- // the previous pass.
- //
- self_pointer_ (forward),
- pointer_ (0),
- pointee_ (pointee)
- {
- }
-
- void
- traverse (view_object& pointer)
- {
- pointer_ = &pointer;
- object_members_base::traverse (*pointer.obj);
- }
-
- virtual void
- traverse_pointer (semantics::data_member& m, semantics::class_& c)
- {
- // Ignore polymorphic id references.
- //
- if (m.count ("polymorphic-ref"))
- return;
-
- // Ignore inverse sides of the same relationship to avoid
- // phony conflicts caused by the direct side that will end
- // up in the relationship list as well. Unless the inverse
- // member is in the polymorphic base in which case we will
- // miss it since we don't examine inside poly bases on the
- // backwards scan (see above).
- //
- if (data_member_path* imp = inverse (m))
- {
- if (&imp->front ()->scope () == &c) // Direct member.
- return;
- }
-
- // Ignore self-pointers if requested.
- //
- if (!self_pointer_ && pointer_->obj == &c)
- return;
-
- if (pointee_.obj == &c)
- {
- relationships_.push_back (relationship ());
- relationships_.back ().member = &m;
- relationships_.back ().name = member_prefix_ + m.name ();
- relationships_.back ().pointer = pointer_;
- relationships_.back ().pointee = &pointee_;
- }
- }
-
- virtual void
- traverse_container (semantics::data_member& m, semantics::type&)
- {
- if (semantics::class_* c =
- object_pointer (context::container_vt (m)))
- {
- if (inverse (m, "value"))
- return;
-
- // Ignore self-pointers if requested.
- //
- if (!self_pointer_ && pointer_->obj == c)
- return;
-
- if (pointee_.obj == c)
- {
- relationships_.push_back (relationship ());
- relationships_.back ().member = &m;
- relationships_.back ().name = member_prefix_ + m.name ();
- relationships_.back ().pointer = pointer_;
- relationships_.back ().pointee = &pointee_;
- }
- }
- }
-
- private:
- relationships& relationships_;
- bool self_pointer_;
- view_object* pointer_;
- view_object& pointee_;
- };
-
- private:
- cxx_string_lexer lex_;
-
- traversal::defines defines_;
- typedefs typedefs_;
-
- data_member member_;
- traversal::names member_names_;
- };
- }
-
- void
- process ()
- {
- context ctx;
-
- traversal::unit unit;
- traversal::defines unit_defines;
- typedefs unit_typedefs (true);
- traversal::namespace_ ns;
- class_ c;
-
- unit >> unit_defines >> ns;
- unit_defines >> c;
- unit >> unit_typedefs >> c;
-
- traversal::defines ns_defines;
- typedefs ns_typedefs (true);
-
- ns >> ns_defines >> ns;
- ns_defines >> c;
- ns >> ns_typedefs >> c;
-
- unit.dispatch (ctx.unit);
- }
-}
diff --git a/odb/relational/schema.hxx b/odb/relational/schema.hxx
deleted file mode 100644
index c5e16c6..0000000
--- a/odb/relational/schema.hxx
+++ /dev/null
@@ -1,1603 +0,0 @@
-// file : odb/relational/schema.hxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#ifndef ODB_RELATIONAL_SCHEMA_HXX
-#define ODB_RELATIONAL_SCHEMA_HXX
-
-#include <set>
-#include <vector>
-#include <cassert>
-
-#include <odb/emitter.hxx>
-#include <odb/relational/common.hxx>
-#include <odb/relational/context.hxx>
-
-namespace relational
-{
- namespace schema
- {
- typedef std::set<qname> table_set;
-
- struct common: virtual context
- {
- typedef ::emitter emitter_type;
-
- common (emitter_type& e, ostream& os, schema_format f)
- : e_ (e), os_ (os), format_ (f) {}
-
- void
- pre_statement ()
- {
- e_.pre ();
- diverge (os_);
- }
-
- void
- post_statement ()
- {
- restore ();
- e_.post ();
- }
-
- emitter_type&
- emitter () const
- {
- return e_;
- }
-
- ostream&
- stream () const
- {
- return os_;
- }
-
- public:
- // Find an entity corresponding to the drop node in alter_table.
- //
- template <typename T, typename D>
- T&
- find (D& d)
- {
- using sema_rel::model;
- using sema_rel::changeset;
- using sema_rel::table;
- using sema_rel::alter_table;
-
- alter_table& at (dynamic_cast<alter_table&> (d.scope ()));
- changeset& cs (dynamic_cast<changeset&> (at.scope ()));
- model& bm (cs.base_model ());
- table* bt (bm.find<table> (at.name ()));
- assert (bt != 0);
- T* b (bt->find<T> (d.name ()));
- assert (b != 0);
- return *b;
- }
-
- protected:
- emitter_type& e_;
- ostream& os_;
- schema_format format_;
- };
-
- //
- // Drop.
- //
-
- // Only used in migration.
- //
- struct drop_column: trav_rel::drop_column, common
- {
- typedef drop_column base;
-
- drop_column (common const& c, bool* first = 0)
- : common (c),
- first_ (first != 0 ? *first : first_data_),
- first_data_ (true)
- {
- }
-
- drop_column (drop_column const& c)
- : root_context (), // @@ -Wextra
- context (),
- common (c),
- first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
- first_data_ (c.first_data_)
- {
- }
-
- virtual void
- drop_header ()
- {
- // By default ADD COLUMN though some databases use just ADD.
- //
- os << "DROP COLUMN ";
- }
-
- virtual void
- traverse (sema_rel::drop_column& dc)
- {
- if (first_)
- first_ = false;
- else
- os << ",";
-
- os << endl
- << " ";
- drop_header ();
- os << quote_id (dc.name ());
- }
-
- protected:
- bool& first_;
- bool first_data_;
- };
-
- // Normally only used in migration but some databases use it as a
- // base to also drop foreign keys in schema.
- //
- struct drop_foreign_key: trav_rel::foreign_key,
- trav_rel::drop_foreign_key,
- trav_rel::add_foreign_key, // Override.
- common
- {
- typedef drop_foreign_key base;
-
- // Schema constructor.
- //
- drop_foreign_key (common const& c, table_set& dropped, bool* first = 0)
- : common (c),
- dropped_ (&dropped),
- first_ (first != 0 ? *first : first_data_),
- first_data_ (true)
- {
- }
-
- // Migration constructor.
- //
- drop_foreign_key (common const& c, bool* first = 0)
- : common (c),
- dropped_ (0),
- first_ (first != 0 ? *first : first_data_),
- first_data_ (true)
- {
- }
-
- drop_foreign_key (drop_foreign_key const& c)
- : root_context (), // @@ -Wextra
- context (),
- common (c),
- dropped_ (c.dropped_),
- first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
- first_data_ (c.first_data_)
- {
- }
-
- virtual void
- drop_header ()
- {
- os << "DROP CONSTRAINT ";
- }
-
- virtual void
- traverse (sema_rel::foreign_key& fk)
- {
- // If the table which we reference is droped before us, then
- // we need to drop the constraint first. Similarly, if the
- // referenced table is not part if this model, then assume
- // it is dropped before us. In migration we always do this
- // first.
- //
- sema_rel::table& t (dynamic_cast<sema_rel::table&> (fk.scope ()));
-
- if (dropped_ != 0)
- {
- sema_rel::qname const& rt (fk.referenced_table ());
- sema_rel::model& m (dynamic_cast<sema_rel::model&> (t.scope ()));
-
- if (dropped_->find (rt) == dropped_->end () &&
- m.find (rt) != m.names_end ())
- return;
- }
-
- drop (t, fk);
- }
-
- virtual void
- drop (sema_rel::table& t, sema_rel::foreign_key& fk)
- {
- // When generating schema we would need to check if the key exists.
- // So this implementation will need to be customize on the per-
- // database level.
- //
- pre_statement ();
-
- os << "ALTER TABLE " << quote_id (t.name ()) << endl
- << " ";
- drop_header ();
- os << quote_id (fk.name ()) << endl;
-
- post_statement ();
- }
-
- virtual void
- traverse (sema_rel::drop_foreign_key& dfk)
- {
- if (first_)
- first_ = false;
- else
- os << ",";
-
- os << endl;
- drop (dfk);
- }
-
- virtual void
- drop (sema_rel::drop_foreign_key& dfk)
- {
- os << " ";
- drop_header ();
- os << quote_id (dfk.name ());
- }
-
- protected:
- table_set* dropped_;
- bool& first_;
- bool first_data_;
- };
-
- // Currently only used in migration.
- //
- struct drop_index: trav_rel::drop_index, common
- {
- typedef drop_index base;
-
- enum index_type {unique, non_unique, all};
-
- drop_index (common const& c, index_type t = all)
- : common (c), type_ (t) {}
-
- virtual void
- traverse (sema_rel::drop_index& di)
- {
- // Find the index we are dropping in the base model.
- //
- traverse (find<sema_rel::index> (di));
- }
-
- virtual void
- traverse (sema_rel::index& in)
- {
- if (type_ == unique &&
- in.type ().find ("UNIQUE") == string::npos &&
- in.type ().find ("unique") == string::npos)
- return;
-
- if (type_ == non_unique && (
- in.type ().find ("UNIQUE") != string::npos ||
- in.type ().find ("unique") != string::npos))
- return;
-
- pre_statement ();
- drop (in);
- post_statement ();
- }
-
- virtual string
- name (sema_rel::index& in)
- {
- return quote_id (in.name ());
- }
-
- virtual void
- drop (sema_rel::index& in)
- {
- os << "DROP INDEX " << name (in) << endl;
- }
-
- protected:
- index_type type_;
- };
-
- struct drop_table: trav_rel::table,
- trav_rel::drop_table,
- trav_rel::add_table, // Override.
- trav_rel::alter_table, // Override.
- common
- {
- typedef drop_table base;
-
- drop_table (emitter_type& e, ostream& os, schema_format f)
- : common (e, os, f) {}
-
- virtual void
- drop (sema_rel::table& t, bool migration)
- {
- pre_statement ();
- os << "DROP TABLE " << (migration ? "" : "IF EXISTS ") <<
- quote_id (t.name ()) << endl;
- post_statement ();
- }
-
- virtual void
- delete_ (sema_rel::qname const& rtable,
- sema_rel::qname const& dtable,
- sema_rel::primary_key& rkey,
- sema_rel::primary_key& dkey)
- {
- pre_statement ();
-
- // This might not be the most efficient way for every database.
- //
- os << "DELETE FROM " << quote_id (rtable) << endl
- << " WHERE EXISTS (SELECT 1 FROM " << quote_id (dtable) << endl
- << " WHERE ";
-
- for (size_t i (0); i != rkey.contains_size (); ++i)
- {
- if (i != 0)
- os << endl
- << " AND ";
-
- os << quote_id (rtable) << "." <<
- quote_id (rkey.contains_at (i).column ().name ()) << " = " <<
- quote_id (dtable) << "." <<
- quote_id (dkey.contains_at (i).column ().name ());
- }
-
- os << ")" << endl;
-
- post_statement ();
- }
-
- virtual void
- traverse (sema_rel::table& t, bool migration)
- {
- // By default drop foreign keys referencing tables that would
- // have already been dropped on the first pass.
- //
- if (pass_ == 1)
- {
- // Drop constraints. In migration this is always done on pass 1.
- //
- if (migration)
- {
- instance<drop_foreign_key> dfk (*this);
- trav_rel::unames n (*dfk);
- names (t, n);
- }
- else
- {
- dropped_.insert (t.name ()); // Add it before to cover self-refs.
-
- instance<drop_foreign_key> dfk (*this, dropped_);
- trav_rel::unames n (*dfk);
- names (t, n);
- }
- }
- else
- {
- if (migration && t.extra ()["kind"] == "polymorphic derived object")
- {
- // If we are dropping a polymorphic derived object, then we
- // also have to clean the base tables. Note that this won't
- // trigger cascade deletion since we have dropped all the
- // keys on pass 1. But we still need to do this in the base
- // to root order in order not to trigger other cascades.
- //
- using sema_rel::model;
- using sema_rel::table;
- using sema_rel::primary_key;
- using sema_rel::foreign_key;
-
- model& m (dynamic_cast<model&> (t.scope ()));
-
- table* p (&t);
- do
- {
- // The polymorphic link is the first primary key.
- //
- for (table::names_iterator i (p->names_begin ());
- i != p->names_end (); ++i)
- {
- if (foreign_key* fk = dynamic_cast<foreign_key*> (
- &i->nameable ()))
- {
- p = m.find<table> (fk->referenced_table ());
- assert (p != 0); // Base table should be there.
- break;
- }
- }
-
- primary_key& rkey (*p->find<primary_key> (""));
- primary_key& dkey (*t.find<primary_key> (""));
- assert (rkey.contains_size () == dkey.contains_size ());
- delete_ (p->name (), t.name (), rkey, dkey);
- }
- while (p->extra ()["kind"] != "polymorphic root object");
- }
-
- drop (t, migration);
- }
- }
-
- virtual void
- traverse (sema_rel::table& t)
- {
- traverse (t, false);
- }
-
- virtual void
- traverse (sema_rel::drop_table& dt)
- {
- using sema_rel::model;
- using sema_rel::changeset;
- using sema_rel::table;
-
- // Find the table we are dropping in the base model.
- //
- changeset& cs (dynamic_cast<changeset&> (dt.scope ()));
- model& bm (cs.base_model ());
- table* t (bm.find<table> (dt.name ()));
- assert (t != 0);
- traverse (*t, true);
- }
-
- using table::names;
-
- void
- pass (unsigned short p)
- {
- pass_ = p;
- }
-
- protected:
- unsigned short pass_;
- table_set dropped_;
- };
-
- struct drop_model: trav_rel::model, common
- {
- typedef drop_model base;
-
- drop_model (emitter_type& e, ostream& os, schema_format f)
- : common (e, os, f)
- {
- }
-
- virtual void
- traverse (sema_rel::model& m)
- {
- // Traverse named entities in the reverse order. This way we
- // drop them in the order opposite to creating.
- //
- for (sema_rel::model::names_iterator begin (m.names_begin ()),
- end (m.names_end ()); begin != end;)
- dispatch (*--end);
- }
-
- void
- pass (unsigned short p)
- {
- pass_ = p;
- }
-
- protected:
- unsigned short pass_;
- };
-
- //
- // Create.
- //
-
- struct create_column: trav_rel::column,
- trav_rel::add_column,
- trav_rel::alter_column, // Override.
- common
- {
- typedef create_column base;
-
- create_column (common const& c,
- bool override_null = true,
- bool* first = 0)
- : common (c),
- override_null_ (override_null),
- first_ (first != 0 ? *first : first_data_),
- first_data_ (true)
- {
- }
-
- create_column (create_column const& c)
- : root_context (), // @@ -Wextra
- context (),
- common (c),
- override_null_ (c.override_null_),
- first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
- first_data_ (c.first_data_)
- {
- }
-
- virtual void
- traverse (sema_rel::column& c)
- {
- if (first_)
- first_ = false;
- else
- os << ",";
-
- os << endl
- << " ";
- create (c);
- }
-
- virtual void
- add_header ()
- {
- // By default ADD COLUMN though some databases use just ADD.
- //
- os << "ADD COLUMN ";
- }
-
- virtual void
- traverse (sema_rel::add_column& ac)
- {
- if (first_)
- first_ = false;
- else
- os << ",";
-
- os << endl
- << " ";
- add_header ();
- create (ac);
- }
-
- virtual void
- create (sema_rel::column& c)
- {
- using sema_rel::column;
-
- // See if this column is (part of) a primary key.
- //
- sema_rel::primary_key* pk (0);
-
- for (column::contained_iterator i (c.contained_begin ());
- i != c.contained_end ();
- ++i)
- {
- if ((pk = dynamic_cast<sema_rel::primary_key*> (&i->key ())))
- break;
- }
-
- os << quote_id (c.name ()) << " ";
-
- type (c, pk != 0 && pk->auto_ ());
- constraints (c, pk);
-
- if (!c.options ().empty ())
- os << " " << c.options ();
- }
-
- virtual void
- constraints (sema_rel::column& c, sema_rel::primary_key* pk)
- {
- null (c);
-
- if (!c.default_ ().empty ())
- os << " DEFAULT " << c.default_ ();
-
- // If this is a single-column primary key, generate it inline.
- //
- if (pk != 0 && pk->contains_size () == 1)
- primary_key ();
-
- if (pk != 0 && pk->auto_ ())
- auto_ (*pk);
- }
-
- virtual void
- type (sema_rel::column& c, bool /*auto*/)
- {
- os << c.type ();
- }
-
- virtual void
- null (sema_rel::column& c)
- {
- bool n (c.null ());
-
- // If we are adding a new column that doesn't allow NULL nor has
- // a default value, add it as NULL. Later, after migration, we
- // will convert it to NOT NULL.
- //
- if (override_null_ && c.is_a<sema_rel::add_column> () &&
- !n && c.default_ ().empty ())
- n = true;
-
- // Specify both cases explicitly for better readability,
- // especially in ALTER COLUMN clauses.
- //
- os << (n ? " NULL" : " NOT NULL");
- }
-
- virtual void
- primary_key ()
- {
- os << " PRIMARY KEY";
- }
-
- virtual void
- auto_ (sema_rel::primary_key&)
- {
- }
-
- protected:
- bool override_null_; // Override NOT NULL in add_column.
- bool& first_;
- bool first_data_;
- bool add_;
- };
-
- struct create_primary_key: trav_rel::primary_key, common
- {
- typedef create_primary_key base;
-
- create_primary_key (common const& c): common (c) {}
-
- virtual void
- traverse (sema_rel::primary_key& pk)
- {
- // Single-column primary keys are generated inline in the
- // column declaration.
- //
- if (pk.contains_size () == 1)
- return;
-
- // We will always follow a column.
- //
- os << "," << endl;
-
- create (pk);
- }
-
- virtual void
- create (sema_rel::primary_key& pk)
- {
- using sema_rel::primary_key;
-
- // By default we create unnamed primary key constraint.
- //
-
- os << " PRIMARY KEY (";
-
- for (primary_key::contains_iterator i (pk.contains_begin ());
- i != pk.contains_end ();
- ++i)
- {
- if (i != pk.contains_begin ())
- os << "," << endl
- << " ";
-
- os << quote_id (i->column ().name ());
- }
-
- os << ")";
- }
- };
-
- struct create_foreign_key: trav_rel::foreign_key,
- trav_rel::add_foreign_key,
- common
- {
- typedef create_foreign_key base;
-
- // Schema constructor, pass 1.
- //
- create_foreign_key (common const& c, table_set& created, bool* first = 0)
- : common (c),
- created_ (&created),
- first_ (first != 0 ? *first : first_data_),
- first_data_ (true)
- {
- }
-
- // Schema constructor, pass 2 and migration constructor.
- //
- create_foreign_key (common const& c, bool* first = 0)
- : common (c),
- created_ (0),
- first_ (first != 0 ? *first : first_data_),
- first_data_ (true)
- {
- }
-
- create_foreign_key (create_foreign_key const& c)
- : root_context (), // @@ -Wextra
- context (),
- common (c),
- created_ (c.created_),
- first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
- first_data_ (c.first_data_)
- {
- }
-
- virtual void
- traverse (sema_rel::foreign_key& fk)
- {
- if (created_ != 0)
- {
- // Pass 1.
- //
- // If the referenced table has already been defined, do the
- // foreign key definition in the table definition. Otherwise
- // postpone it until pass 2 where we do it via ALTER TABLE.
- //
- if (created_->find (fk.referenced_table ()) != created_->end ())
- {
- traverse_create (fk);
- fk.set (db.string () + "-fk-defined", true); // Mark it as defined.
- }
- }
- else
- {
- // Pass 2.
- //
- if (!fk.count (db.string () + "-fk-defined"))
- traverse_add (fk);
- }
- }
-
- virtual void
- traverse_create (sema_rel::foreign_key& fk)
- {
- if (first_)
- first_ = false;
- else
- os << ",";
-
- os << endl
- << " CONSTRAINT ";
- create (fk);
- }
-
- virtual void
- traverse_add (sema_rel::foreign_key& fk)
- {
- if (first_)
- first_ = false;
- else
- os << ",";
-
- os << endl;
- add (fk);
- }
-
- virtual void
- traverse (sema_rel::add_foreign_key& afk)
- {
- traverse_add (afk);
- }
-
- virtual void
- add_header ()
- {
- os << "ADD CONSTRAINT ";
- }
-
- virtual void
- add (sema_rel::foreign_key& fk)
- {
- os << " ";
- add_header ();
- create (fk);
- }
-
- virtual void
- create (sema_rel::foreign_key& fk)
- {
- using sema_rel::foreign_key;
-
- os << name (fk) << endl
- << " FOREIGN KEY (";
-
- for (foreign_key::contains_iterator i (fk.contains_begin ());
- i != fk.contains_end ();
- ++i)
- {
- if (i != fk.contains_begin ())
- os << "," << endl
- << " ";
-
- os << quote_id (i->column ().name ());
- }
-
- string tn (table_name (fk));
- string tn_pad (tn.size (), ' ');
-
- os << ")" << endl
- << " REFERENCES " << tn << " (";
-
- foreign_key::columns const& refs (fk.referenced_columns ());
- for (foreign_key::columns::const_iterator i (refs.begin ());
- i != refs.end ();
- ++i)
- {
- if (i != refs.begin ())
- os << "," << endl
- << " " << tn_pad;
-
- os << quote_id (*i);
- }
-
- os << ")";
-
- if (fk.on_delete () != foreign_key::no_action)
- on_delete (fk.on_delete ());
-
- if (!fk.not_deferrable ())
- deferrable (fk.deferrable ());
- }
-
- virtual string
- name (sema_rel::foreign_key& fk)
- {
- return quote_id (fk.name ());
- }
-
- virtual string
- table_name (sema_rel::foreign_key& fk)
- {
- return quote_id (fk.referenced_table ());
- }
-
- virtual void
- on_delete (sema_rel::foreign_key::action_type a)
- {
- using sema_rel::foreign_key;
-
- switch (a)
- {
- case foreign_key::no_action:
- break;
- case foreign_key::cascade:
- {
- os << endl
- << " ON DELETE CASCADE";
- break;
- }
- case foreign_key::set_null:
- {
- os << endl
- << " ON DELETE SET NULL";
- break;
- }
- }
- }
-
- virtual void
- deferrable (sema_rel::deferrable d)
- {
- os << endl
- << " DEFERRABLE INITIALLY " << d;
- }
-
- protected:
- table_set* created_;
- bool& first_;
- bool first_data_;
- };
-
- struct create_index: trav_rel::index, common
- {
- typedef create_index base;
-
- enum index_type {unique, non_unique, all};
-
- create_index (common const& c, index_type t = all)
- : common (c), type_ (t) {}
-
- virtual void
- traverse (sema_rel::index& in)
- {
- if (type_ == unique &&
- in.type ().find ("UNIQUE") == string::npos &&
- in.type ().find ("unique") == string::npos)
- return;
-
- if (type_ == non_unique && (
- in.type ().find ("UNIQUE") != string::npos ||
- in.type ().find ("unique") != string::npos))
- return;
-
- pre_statement ();
- create (in);
- post_statement ();
- }
-
- virtual string
- name (sema_rel::index& in)
- {
- return quote_id (in.name ());
- }
-
- virtual string
- table_name (sema_rel::index& in)
- {
- return quote_id (static_cast<sema_rel::table&> (in.scope ()).name ());
- }
-
- virtual void
- columns (sema_rel::index& in)
- {
- using sema_rel::index;
-
- for (index::contains_iterator i (in.contains_begin ());
- i != in.contains_end ();
- ++i)
- {
- if (in.contains_size () > 1)
- {
- if (i != in.contains_begin ())
- os << ",";
-
- os << endl
- << " ";
- }
-
- os << quote_id (i->column ().name ());
-
- if (!i->options ().empty ())
- os << ' ' << i->options ();
- }
- }
-
- virtual void
- create (sema_rel::index& in)
- {
- // Default implementation that ignores the method.
- //
- os << "CREATE ";
-
- if (!in.type ().empty ())
- os << in.type () << ' ';
-
- os << "INDEX " << name (in) << endl
- << " ON " << table_name (in) << " (";
-
- columns (in);
-
- os << ")" << endl;
-
- if (!in.options ().empty ())
- os << ' ' << in.options () << endl;
- }
-
- protected:
- index_type type_;
- };
-
- struct create_table: trav_rel::table,
- trav_rel::alter_table, // Override.
- common
- {
- typedef create_table base;
-
- using trav_rel::table::names;
-
- create_table (emitter_type& e, ostream& os, schema_format f)
- : common (e, os, f) {}
-
- virtual void
- create_pre (sema_rel::qname const& table)
- {
- os << "CREATE TABLE " << quote_id (table) << " (";
- }
-
- virtual void
- create_post (sema_rel::table& t)
- {
- os << ")" << endl;
-
- if (!t.options ().empty ())
- os << " " << t.options () << endl;
- }
-
- virtual void
- create (sema_rel::table& t)
- {
- pre_statement ();
- create_pre (t.name ());
-
- instance<create_column> c (*this);
- instance<create_primary_key> pk (*this);
-
- // We will always follow a column, so set first to false.
- //
- bool f (false); // (Im)perfect forwarding.
- bool* pf (&f); // (Im)perfect forwarding.
- instance<create_foreign_key> fk (*this, created_, pf);
-
- trav_rel::unames n;
- n >> c;
- n >> pk;
- n >> fk;
-
- names (t, n);
-
- create_post (t);
- post_statement ();
-
- // Create indexes.
- //
- {
- instance<create_index> in (*this);
- trav_rel::unames n (*in);
- names (t, n);
- }
- }
-
- // See if there are any undefined foreign keys that we need to
- // add with ALTER TABLE.
- //
- bool
- check_undefined_fk (sema_rel::table& t)
- {
- for (sema_rel::table::names_iterator i (t.names_begin ());
- i != t.names_end (); ++i)
- {
- if (i->nameable ().is_a<sema_rel::foreign_key> () &&
- !i->nameable ().count (db.string () + "-fk-defined"))
- return true;
- }
- return false;
- }
-
- virtual void
- traverse (sema_rel::table& t)
- {
- // By default add foreign keys referencing tables that haven't
- // yet been defined on the second pass.
- //
- if (pass_ == 1)
- {
- // In migration we always add foreign keys on pass 2.
- //
- if (!t.is_a<sema_rel::add_table> ())
- created_.insert (t.name ()); // Add it before to cover self-refs.
-
- create (t);
- }
- else
- {
- // Add undefined foreign keys.
- //
- if (check_undefined_fk (t))
- {
- pre_statement ();
- os << "ALTER TABLE " << quote_id (t.name ());
-
- instance<create_foreign_key> cfk (*this);
- trav_rel::unames n (*cfk);
- names (t, n);
- os << endl;
-
- post_statement ();
- }
- }
- }
-
- void
- pass (unsigned short p)
- {
- pass_ = p;
- }
-
- protected:
- unsigned short pass_;
- table_set created_;
- };
-
- struct create_model: trav_rel::model, common
- {
- typedef create_model base;
-
- create_model (emitter_type& e, ostream& os, schema_format f)
- : common (e, os, f) {}
-
- void
- pass (unsigned short p)
- {
- pass_ = p;
- }
-
- protected:
- unsigned short pass_;
- };
-
- //
- // Alter.
- //
-
- struct alter_column: trav_rel::alter_column,
- trav_rel::add_column,
- common
- {
- typedef alter_column base;
-
- alter_column (common const& c, bool pre, bool* first = 0)
- : common (c),
- pre_ (pre),
- first_ (first != 0 ? *first : first_data_),
- first_data_ (true),
- fl_ (false),
- def_ (c, fl_)
- {
- }
-
- alter_column (alter_column const& c)
- : root_context (), // @@ -Wextra
- context (),
- common (c),
- pre_ (c.pre_),
- first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
- first_data_ (c.first_data_),
- fl_ (false),
- def_ (c, fl_)
- {
- }
-
- virtual void
- alter_header ()
- {
- os << "ALTER COLUMN ";
- }
-
- virtual void
- alter (sema_rel::column& c)
- {
- // By default use the whole definition.
- //
- def_->create (c);
- }
-
- virtual void
- traverse (sema_rel::column& c)
- {
- // Relax (NULL) in pre and tighten (NOT NULL) in post.
- //
- if (pre_ != c.null ())
- return;
-
- if (first_)
- first_ = false;
- else
- os << ",";
-
- os << endl
- << " ";
- alter_header ();
- alter (c);
- }
-
- virtual void
- traverse (sema_rel::alter_column& ac)
- {
- assert (ac.null_altered ());
- traverse (static_cast<sema_rel::column&> (ac));
- }
-
- virtual void
- traverse (sema_rel::add_column& ac)
- {
- // We initially add NOT NULL columns without default values as
- // NULL. Now, after the migration, we convert them to NOT NULL.
- //
- if (!ac.null () && ac.default_ ().empty ())
- traverse (static_cast<sema_rel::column&> (ac));
- }
-
- protected:
- bool pre_;
- bool& first_;
- bool first_data_;
- bool fl_; // (Im)perfect forwarding.
- instance<create_column> def_;
- };
-
- struct alter_table_common: trav_rel::alter_table, common
- {
- alter_table_common (emitter_type& e, ostream& os, schema_format f)
- : common (e, os, f) {}
-
- template <typename T>
- T*
- check (sema_rel::alter_table& at)
- {
- for (sema_rel::alter_table::names_iterator i (at.names_begin ());
- i != at.names_end (); ++i)
- {
- if (T* x = dynamic_cast<T*> (&i->nameable ()))
- return x;
- }
- return 0;
- }
-
- sema_rel::column*
- check_alter_column_null (sema_rel::alter_table& at, bool v)
- {
- for (sema_rel::alter_table::names_iterator i (at.names_begin ());
- i != at.names_end (); ++i)
- {
- using sema_rel::add_column;
- using sema_rel::alter_column;
-
- if (alter_column* ac = dynamic_cast<alter_column*> (&i->nameable ()))
- {
- if (ac->null_altered () && ac->null () == v)
- return ac;
- }
-
- // If we are testing for NOT NULL, also look for new columns that
- // we initially add as NULL and later convert to NOT NULL.
- //
- if (!v)
- {
- if (add_column* ac = dynamic_cast<add_column*> (&i->nameable ()))
- {
- if (!ac->null () && ac->default_ ().empty ())
- return ac;
- }
- }
- }
- return 0;
- }
-
- void
- pass (unsigned short p)
- {
- pass_ = p;
- }
-
- protected:
- unsigned short pass_;
- };
-
- struct alter_table_pre: alter_table_common
- {
- typedef alter_table_pre base;
-
- alter_table_pre (emitter_type& e, ostream& os, schema_format f)
- : alter_table_common (e, os, f) {}
-
- // Check if there will be any clauses in ALTER TABLE.
- //
- using alter_table_common::check;
-
- virtual bool
- check (sema_rel::alter_table& at)
- {
- // If changing the below test, make sure to also update tests
- // in database-specific code.
- //
- return
- check<sema_rel::drop_foreign_key> (at) ||
- check<sema_rel::add_column> (at) ||
- check_alter_column_null (at, true);
- }
-
- virtual void
- alter (sema_rel::alter_table& at)
- {
- // By default we generate all the alterations in a single ALTER TABLE
- // statement. Quite a few databases don't support this.
- //
- pre_statement ();
- os << "ALTER TABLE " << quote_id (at.name ());
-
- bool f (true); // Shared first flag.
- bool* pf (&f); // (Im)perfect forwarding.
- bool tl (true); // (Im)perfect forwarding.
- instance<create_column> cc (*this, tl, pf);
- instance<alter_column> ac (*this, tl, pf);
- instance<drop_foreign_key> dfk (*this, pf);
- trav_rel::unames n;
- n >> cc;
- n >> ac;
- n >> dfk;
- names (at, n);
- os << endl;
-
- post_statement ();
- }
-
- virtual void
- traverse (sema_rel::alter_table& at)
- {
- if (pass_ == 1)
- {
- // Drop unique indexes.
- //
- {
- drop_index::index_type it (drop_index::unique);
- instance<drop_index> in (*this, it);
- trav_rel::unames n (*in);
- names (at, n);
- }
-
- if (check (at))
- alter (at);
- }
- else
- {
- // Add non-unique indexes.
- //
- {
- create_index::index_type it (create_index::non_unique);
- instance<create_index> in (*this, it);
- trav_rel::unames n (*in);
- names (at, n);
- }
- }
- }
- };
-
- struct changeset_pre: trav_rel::changeset, common
- {
- typedef changeset_pre base;
-
- changeset_pre (emitter_type& e, ostream& os, schema_format f)
- : common (e, os, f) {}
-
- void
- pass (unsigned short p)
- {
- pass_ = p;
- }
-
- protected:
- unsigned short pass_;
- };
-
- struct alter_table_post: alter_table_common
- {
- typedef alter_table_post base;
-
- alter_table_post (emitter_type& e, ostream& os, schema_format f)
- : alter_table_common (e, os, f) {}
-
- // Check if there will be any clauses in ALTER TABLE.
- //
- using alter_table_common::check;
-
- virtual bool
- check (sema_rel::alter_table& at)
- {
- // If changing the below test, make sure to also update tests
- // in database-specific code.
- //
- return
- check<sema_rel::add_foreign_key> (at) ||
- check<sema_rel::drop_column> (at) ||
- check_alter_column_null (at, false);
- }
-
- virtual void
- alter (sema_rel::alter_table& at)
- {
- // By default we generate all the alterations in a single ALTER TABLE
- // statement. Quite a few databases don't support this.
- //
- pre_statement ();
- os << "ALTER TABLE " << quote_id (at.name ());
-
- bool f (true); // Shared first flag.
- bool* pf (&f); // (Im)perfect forwarding.
- bool fl (false); // (Im)perfect forwarding.
- instance<drop_column> dc (*this, pf);
- instance<alter_column> ac (*this, fl, pf);
- instance<create_foreign_key> fk (*this, pf);
-
- trav_rel::unames n;
- n >> dc;
- n >> ac;
- n >> fk;
- names (at, n);
- os << endl;
-
- post_statement ();
- }
-
- virtual void
- traverse (sema_rel::alter_table& at)
- {
- if (pass_ == 1)
- {
- // Drop non-unique indexes.
- //
- {
- drop_index::index_type it (drop_index::non_unique);
- instance<drop_index> in (*this, it);
- trav_rel::unames n (*in);
- names (at, n);
- }
- }
- else
- {
- if (check (at))
- alter (at);
-
- // Add unique indexes.
- //
- {
- create_index::index_type it (create_index::unique);
- instance<create_index> in (*this, it);
- trav_rel::unames n (*in);
- names (at, n);
- }
- }
- }
- };
-
- struct changeset_post: trav_rel::changeset, common
- {
- typedef changeset_post base;
-
- changeset_post (emitter_type& e, ostream& os, schema_format f)
- : common (e, os, f) {}
-
- virtual void
- traverse (sema_rel::changeset& m)
- {
- // Traverse named entities in the reverse order. This way we
- // drop them in the order opposite to creating.
- //
- for (sema_rel::changeset::names_iterator begin (m.names_begin ()),
- end (m.names_end ()); begin != end;)
- dispatch (*--end);
- }
-
- void
- pass (unsigned short p)
- {
- pass_ = p;
- }
-
- protected:
- unsigned short pass_;
- };
-
- //
- // Schema version table.
- //
-
- struct version_table: common
- {
- typedef version_table base;
-
- version_table (emitter_type& e, ostream& os, schema_format f)
- : common (e, os, f),
- table_ (options.schema_version_table ()[db]),
- qt_ (quote_id (table_)),
- qs_ (quote_string (options.schema_name ()[db])),
- qn_ (quote_id ("name")),
- qv_ (quote_id ("version")),
- qm_ (quote_id ("migration"))
- {
- }
-
- // Create the version table if it doesn't exist.
- //
- virtual void
- create_table () {}
-
- // Remove the version entry. Called after the DROP statements.
- //
- virtual void
- drop ()
- {
- pre_statement ();
-
- os << "DELETE FROM " << qt_ << endl
- << " WHERE " << qn_ << " = " << qs_ << endl;
-
- post_statement ();
- }
-
- // Set the version. Called after the CREATE statements.
- //
- virtual void
- create (sema_rel::version) {}
-
- // Set the version and migration state to true. Called after
- // the pre migration statements.
- //
- virtual void
- migrate_pre (sema_rel::version v)
- {
- pre_statement ();
-
- os << "UPDATE " << qt_ << endl
- << " SET " << qv_ << " = " << v << ", " << qm_ << " = 1" << endl
- << " WHERE " << qn_ << " = " << qs_ << endl;
-
- post_statement ();
- }
-
- // Set migration state to false. Called after the post migration
- // statements.
- //
- virtual void
- migrate_post ()
- {
- pre_statement ();
-
- os << "UPDATE " << qt_ << endl
- << " SET " << qm_ << " = 0" << endl
- << " WHERE " << qn_ << " = " << qs_ << endl;
-
- post_statement ();
- }
-
- protected:
- sema_rel::qname table_;
- string qt_; // Quoted table.
- string qs_; // Quoted schema name string.
- string qn_; // Quoted name column.
- string qv_; // Quoted version column.
- string qm_; // Quoted migration column.
- };
-
- //
- // SQL output.
- //
-
- struct sql_emitter: emitter, virtual context
- {
- typedef sql_emitter base;
-
- virtual void
- pre ()
- {
- first_ = true;
- }
-
- virtual void
- line (const std::string& l)
- {
- if (first_ && !l.empty ())
- first_ = false;
- else
- os << endl;
-
- os << l;
- }
-
- virtual void
- post ()
- {
- if (!first_) // Ignore empty statements.
- os << ';' << endl
- << endl;
- }
-
- protected:
- bool first_;
- };
-
- struct sql_file: virtual context
- {
- typedef sql_file base;
-
- virtual void
- prologue ()
- {
- }
-
- virtual void
- epilogue ()
- {
- }
- };
- }
-}
-
-#endif // ODB_RELATIONAL_SCHEMA_HXX
diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx
deleted file mode 100644
index 20c431a..0000000
--- a/odb/relational/source.cxx
+++ /dev/null
@@ -1,6324 +0,0 @@
-// file : odb/relational/source.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <map>
-
-#include <odb/gcc.hxx>
-
-#include <odb/lookup.hxx>
-#include <odb/cxx-lexer.hxx>
-
-#include <odb/relational/source.hxx>
-#include <odb/relational/generate.hxx>
-
-using namespace std;
-
-void relational::source::class_::
-traverse_object (type& c)
-{
- using semantics::data_member;
-
- data_member_path* id (id_member (c));
- data_member* idf (id ? id->front () : 0);
- data_member* idb (id ? id->back () : 0);
- bool auto_id (id && auto_ (*id));
- bool base_id (id && &idf->scope () != &c); // Comes from base.
-
- data_member* opt (optimistic (c));
-
- type* poly_root (polymorphic (c));
- bool poly (poly_root != 0);
- bool poly_derived (poly && poly_root != &c);
- type* poly_base (poly_derived ? &polymorphic_base (c) : 0);
- size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1);
- data_member* discriminator (poly ? context::discriminator (*poly_root) : 0);
-
- bool abst (abstract (c));
- bool reuse_abst (abst && !poly);
- bool readonly (context::readonly (c));
-
- bool grow (false);
- bool grow_id (false);
-
- if (generate_grow)
- {
- grow = context::grow (c);
- grow_id = (id ? context::grow (*idb) : false) ||
- (opt ? context::grow (*opt) : false);
- }
-
- column_count_type const& cc (column_count (c));
- bool versioned (context::versioned (c));
-
- // Schema name as a string literal or empty.
- //
- string schema_name (options.schema_name ()[db]);
- if (!schema_name.empty ())
- schema_name = strlit (schema_name);
-
- string const& type (class_fq_name (c));
- string traits ("access::object_traits_impl< " + type + ", id_" +
- db.string () + " >");
-
- user_sections& uss (c.get<user_sections> ("user-sections"));
- user_sections* buss (poly_base != 0
- ? &poly_base->get<user_sections> ("user-sections")
- : 0);
-
- os << "// " << class_name (c) << endl
- << "//" << endl
- << endl;
-
- object_extra (c);
-
- //
- // Query (abstract and concrete).
- //
-
- // query_columns
- //
- if (options.generate_query ())
- query_columns_type_->traverse (c);
-
- // Statement cache (definition).
- //
- if (!reuse_abst && id != 0)
- {
- bool sections (false);
- bool containers (has_a (c, test_container));
-
- os << "struct " << traits << "::extra_statement_cache_type"
- << "{";
-
- instance<container_cache_members> cm;
- cm->traverse (c);
-
- if (containers)
- os << endl;
-
- instance<section_cache_members> sm;
- for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
- {
- // Skip the special version update section in reuse inheritance (we
- // always treat it as abstract).
- //
- if (i->special == user_section::special_version && !poly)
- continue;
-
- // Generate an entry for a readonly section in optimistic
- // polymorphic root since it can be overridden and we may
- // need to update the version.
- //
- if (!i->empty () || (poly && i->optimistic ()))
- {
- sm->traverse (*i);
- sections = true;
- }
- }
-
- if (sections)
- os << endl;
-
- os << "extra_statement_cache_type (" << endl
- << db << "::connection&" << (containers || sections ? " c" : "") <<
- "," << endl
- << "image_type&" << (sections ? " im" : "") << "," << endl
- << "id_image_type&" << (sections ? " idim" : "") << "," << endl
- << db << "::binding&" << (containers || sections ? " id" : "") <<
- "," << endl
- << db << "::binding&" << (sections ? " idv" : "");
-
- extra_statement_cache_extra_args (containers, sections);
-
- os << ")";
-
- instance<container_cache_init_members> cim;
- cim->traverse (c);
-
- instance<section_cache_init_members> sim (!containers);
- for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
- {
- if (i->special == user_section::special_version && !poly)
- continue;
-
- if (!i->empty () || (poly && i->optimistic ()))
- sim->traverse (*i);
- }
-
- os << "{"
- << "}"
- << "};";
- }
-
- //
- // Containers (abstract and concrete).
- //
-
- {
- instance<container_traits> t (c);
- t->traverse (c);
- }
-
- //
- // Sections (abstract and concrete).
- //
-
- for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
- {
- instance<section_traits> t (c);
- t->traverse (*i);
- }
-
- //
- // Functions (abstract and concrete).
- //
-
- // id(), version()
- //
- if (!poly_derived && id != 0 && !base_id)
- {
- // id (id_image_type)
- //
- if (auto_id)
- {
- os << traits << "::id_type" << endl
- << traits << "::" << endl
- << "id (const id_image_type& i)"
- << "{"
- << db << "::database* db (0);"
- << "ODB_POTENTIALLY_UNUSED (db);"
- << endl
- << "id_type id;";
- init_id_value_member_id_image_->traverse (*idb);
- os << "return id;"
- << "}";
- }
-
- // id (image)
- //
- if (options.generate_query ())
- {
- os << traits << "::id_type" << endl
- << traits << "::" << endl
- << "id (const image_type& i)"
- << "{"
- << db << "::database* db (0);"
- << "ODB_POTENTIALLY_UNUSED (db);"
- << endl
- << "id_type id;";
-
- // Handle nested id.
- //
- if (id->size () > 1)
- {
- string var;
-
- for (data_member_path::const_iterator i (id->begin ());
- i != id->end ();
- ++i)
- {
- // The same logic as in member_base.
- //
- if (!var.empty ())
- var += "value."; // Composite.
-
- string const& name ((*i)->name ());
- var += name;
-
- if (name[name.size () - 1] != '_')
- var += '_';
- }
-
- instance<init_value_member> t ("id", var);
- t->traverse (*idb);
- }
- else
- init_id_value_member_->traverse (*idb);
-
- os << "return id;"
- << "}";
- }
-
- // version (image)
- //
- if (opt != 0)
- {
- os << traits << "::version_type" << endl
- << traits << "::" << endl
- << "version (const image_type& i)"
- << "{"
- << "version_type v;";
- init_version_value_member_->traverse (*opt);
- os << "return v;"
- << "}";
- }
- }
-
- // discriminator(image)
- //
- if (poly && !poly_derived)
- {
- os << traits << "::discriminator_type" << endl
- << traits << "::" << endl
- << "discriminator (const image_type& i)"
- << "{"
- << db << "::database* db (0);"
- << "ODB_POTENTIALLY_UNUSED (db);"
- << endl
- << "discriminator_type d;";
- init_discriminator_value_member_->traverse (*discriminator);
- os << "return d;"
- << "}";
- }
-
- // grow ()
- //
- if (generate_grow)
- {
- os << "bool " << traits << "::" << endl
- << "grow (image_type& i," << endl
- << truncated_vector << " t";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- if (poly_derived)
- os << "," << endl
- << "std::size_t d";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (i);"
- << "ODB_POTENTIALLY_UNUSED (t);";
-
- if (versioned)
- os << "ODB_POTENTIALLY_UNUSED (svm);";
-
- os << endl
- << "bool grew (false);"
- << endl;
-
- index_ = 0;
-
- if (poly_derived)
- {
- // Select column count for this class.
- //
- size_t cols (cc.total - cc.id);
-
- os << "// " << class_name (*poly_base) << " base" << endl
- << "//" << endl
- << "if (--d != 0)"
- << "{"
- << "if (base_traits::grow (*i.base, " <<
- "t + " << cols << "UL" <<
- (context::versioned (*poly_base) ? ", svm" : "") <<
- (poly_base != poly_root ? ", d" : "") << "))" << endl
- << "i.base->version++;"
- << "}";
- }
- else
- inherits (c, grow_base_inherits_);
-
- names (c, grow_member_names_);
-
- os << "return grew;"
- << "}";
- }
-
- // bind (image_type)
- //
- os << "void " << traits << "::" << endl
- << "bind (" << bind_vector << " b," << endl;
-
- // If we are a derived type in a polymorphic hierarchy, then
- // we get the external id binding.
- //
- if (poly_derived)
- os << "const " << bind_vector << " id," << endl
- << "std::size_t id_size," << endl;
-
- os << "image_type& i," << endl
- << db << "::statement_kind sk";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (sk);";
-
- if (versioned)
- os << "ODB_POTENTIALLY_UNUSED (svm);";
-
- os << endl
- << "using namespace " << db << ";"
- << endl;
-
- if (readonly)
- os << "assert (sk != statement_update);"
- << endl;
-
- os << "std::size_t n (0);"
- << endl;
-
- if (poly_derived)
- {
- // The id reference comes first in the insert statement.
- //
- os << "// " << idf->name () << endl
- << "//" << endl
- << "if (sk == statement_insert)"
- << "{"
- << "if (id != 0)" << endl
- << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
- << "n += id_size;" // Not in if for "id unchanged" optimization.
- << "}";
- }
- else
- inherits (c, bind_base_inherits_);
-
- names (c, bind_member_names_);
-
- if (poly_derived)
- {
- // The id reference comes last in the update statement.
- //
- if (!readonly)
- os << "// " << idf->name () << endl
- << "//" << endl
- << "if (sk == statement_update)"
- << "{"
- << "if (id != 0)" << endl
- << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
- << "n += id_size;" // Not in if for "id unchanged" optimization.
- << "}";
-
- // Bind the image chain for the select statement. Seeing that
- // this is the last statement in the function, we don't care
- // about updating n.
- //
- os << "// " << class_name (*poly_base) << " base" << endl
- << "//" << endl
- << "if (sk == statement_select)" << endl
- << "base_traits::bind (b + n, ";
-
- if (poly_base != poly_root)
- os << "id, id_size, ";
-
- os << "*i.base, sk" <<
- (context::versioned (*poly_base) ? ", svm" : "") << ");";
- }
-
- os << "}";
-
- // bind (id_image_type)
- //
- if (!poly_derived && id != 0 && !base_id)
- {
- os << "void " << traits << "::" << endl
- << "bind (" << bind_vector << " b, id_image_type& i" <<
- (opt != 0 ? ", bool bv" : "") << ")"
- << "{"
- << "std::size_t n (0);";
-
- if (composite_wrapper (utype (*id)))
- os << db << "::statement_kind sk (" << db << "::statement_select);";
-
- bind_id_member_->traverse (*idb);
-
- if (opt != 0)
- {
- os << "if (bv)"
- << "{"
- << "n += " << column_count (c).id << ";"
- << endl;
-
- bind_version_member_->traverse (*opt);
- os << "}";
- }
-
- os << "}";
- }
-
- // init (image, object)
- //
- os << (generate_grow ? "bool " : "void ") << traits << "::" << endl
- << "init (image_type& i," << endl
- << "const object_type& o," << endl
- << db << "::statement_kind sk";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (i);"
- << "ODB_POTENTIALLY_UNUSED (o);"
- << "ODB_POTENTIALLY_UNUSED (sk);";
-
- if (versioned)
- os << "ODB_POTENTIALLY_UNUSED (svm);";
-
- os << endl
- << "using namespace " << db << ";"
- << endl;
-
- if (readonly)
- os << "assert (sk != statement_update);"
- << endl;
-
- init_image_pre (c);
-
- if (generate_grow)
- os << "bool grew (false);"
- << endl;
-
- if (!poly_derived)
- inherits (c, init_image_base_inherits_);
-
- names (c, init_image_member_names_);
-
- if (generate_grow)
- os << "return grew;";
-
- os << "}";
-
- // init (object, image)
- //
- os << "void " << traits << "::" << endl
- << "init (object_type& o," << endl
- << "const image_type& i," << endl
- << "database* db";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- if (poly_derived)
- os << "," << endl
- << "std::size_t d";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (o);"
- << "ODB_POTENTIALLY_UNUSED (i);"
- << "ODB_POTENTIALLY_UNUSED (db);";
-
- if (versioned)
- os << "ODB_POTENTIALLY_UNUSED (svm);";
-
- os << endl;
-
- if (poly_derived)
- {
- os << "// " << class_name (*poly_base) << " base" << endl
- << "//" << endl
- << "if (--d != 0)" << endl
- << "base_traits::init (o, *i.base, db" <<
- (context::versioned (*poly_base) ? ", svm" : "") <<
- (poly_base != poly_root ? ", d" : "") << ");"
- << endl;
- }
- else
- inherits (c, init_value_base_inherits_);
-
- names (c, init_value_member_names_);
-
- os << "}";
-
- // init (id_image, id)
- //
- if (id != 0 && !base_id)
- {
- os << "void " << traits << "::" << endl
- << "init (id_image_type& i, const id_type& id" <<
- (opt != 0 ? ", const version_type* v" : "") << ")"
- << "{";
-
- if (grow_id)
- os << "bool grew (false);";
-
- if (composite_wrapper (utype (*id)))
- os << db << "::statement_kind sk (" << db << "::statement_select);";
-
- init_id_image_member_->traverse (*idb);
-
- if (opt != 0)
- {
- // Here we rely on the fact that init_image_member
- // always wraps the statements in a block.
- //
- os << "if (v != 0)";
- init_version_image_member_->traverse (*opt);
- }
-
- if (grow_id)
- os << "if (grew)" << endl
- << "i.version++;";
-
- os << "}";
- }
-
- // The rest does not apply to reuse-abstract objects.
- //
- if (reuse_abst)
- return;
-
- //
- // Containers (concrete).
- //
-
- //
- // Sections (concrete).
- //
-
- // Polymorphic map.
- //
- if (poly)
- {
- if (!poly_derived)
- os << traits << "::map_type*" << endl
- << traits << "::map;"
- << endl;
-
- // Sections. Do we have any new sections or overrides?
- //
- bool sections (
- uss.count (user_sections::count_new |
- user_sections::count_override |
- user_sections::count_load |
- user_sections::count_update |
- user_sections::count_special_version |
- (poly ? user_sections::count_optimistic : 0)) != 0);
- if (sections)
- {
- string const& fn (flat_name (type));
-
- size_t n (uss.count (user_sections::count_total |
- user_sections::count_all |
- user_sections::count_special_version));
-
- map<size_t, user_section*> m;
- for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
- m[i->index] = &*i;
-
- os << "static const "<< traits << "::" << (abst ? "abstract_" : "") <<
- "info_type::section_functions" << endl
- << "section_functions_for_" << fn << "[] ="
- << "{";
-
- for (size_t i (0); i != n; ++i)
- {
- os << (i != 0 ? "," : "") << "{";
-
- map<size_t, user_section*>::iterator j (m.find (i));
-
- if (j != m.end ())
- {
- user_section& s (*j->second);
- string n (public_name (*s.member));
-
- // load
- //
- if (s.load_empty ())
- os << "0";
- else
- os << "&odb::section_load_impl< " << type << ", id_" << db <<
- ", " << traits << "::" << n << "_traits >";
-
- os << "," << endl;
-
- // update
- //
- // Generate an entry for a readonly section in optimistic
- // polymorphic root since it can be overridden and we may
- // need to update the version.
- //
- if (s.update_empty () && !(poly && s.optimistic ()))
- os << "0";
- else
- os << "&odb::section_update_impl< " << type << ", id_" << db <<
- ", " << traits << "::" << n << "_traits >";
- }
- else
- os << "0," << endl
- << "0";
-
- os << "}";
- }
-
- os << "};";
-
- os << "static const "<< traits << "::" << (abst ? "abstract_" : "") <<
- "info_type::section_list" << endl
- << "section_list_for_" << fn << " ="
- << "{"
- << n << "UL," << endl
- << "section_functions_for_" << fn
- << "};";
- }
-
- //
- //
- os << "const " << traits << "::" << (abst ? "abstract_" : "") <<
- "info_type" << endl
- << traits << "::info (" << endl
- << "typeid (" << type << ")," << endl;
-
- if (poly_derived)
- os << "&object_traits_impl< " << class_fq_name (*poly_base) <<
- ", id_" << db << " >::info," << endl;
- else
- os << "0," << endl;
-
- // Sections.
- //
- if (sections)
- os << "&section_list_for_" << flat_name (type);
- else
- os << "0";
-
- if (!abst)
- {
- os << "," << endl
- << strlit (string (type, 2, string::npos)) << "," << endl
- << "&odb::create_impl< " << type << " >," << endl
- << "&odb::dispatch_impl< " << type << ", id_" << db << " >," << endl;
-
- if (poly_derived)
- os << "&statements_type::delayed_loader";
- else
- os << "0";
- }
-
- os << ");"
- << endl;
-
- if (!abst)
- os << "static const " << traits << "::entry_type" << endl
- << "polymorphic_entry_for_" << flat_name (type) << ";"
- << endl;
- }
-
- //
- // Statements.
- //
- bool update_columns (
- cc.total != cc.id + cc.inverse + cc.readonly + cc.separate_update);
-
- qname table (table_name (c));
- string qtable (quote_id (table));
-
- // persist_statement
- //
- {
- string sep (versioned ? "\n" : " ");
-
- statement_columns sc;
- {
- statement_kind sk (statement_insert); // Imperfect forwarding.
- instance<object_columns> ct (sk, sc);
- ct->traverse (c);
- process_statement_columns (sc, statement_insert, versioned);
- }
-
- bool dv (sc.empty ()); // The DEFAULT VALUES syntax.
-
- os << "const char " << traits << "::persist_statement[] =" << endl
- << strlit ("INSERT INTO " + qtable + sep) << endl;
-
- for (statement_columns::const_iterator b (sc.begin ()), i (b),
- e (sc.end ()); i != e;)
- {
- string s;
-
- if (i == b)
- s += '(';
- s += i->column;
- s += (++i != e ? ',' : ')');
- s += sep;
-
- os << strlit (s) << endl;
- }
-
- instance<query_parameters> qp (statement_insert, table);
-
- string extra (persist_statement_extra (c, *qp, persist_after_columns));
-
- if (!extra.empty ())
- os << strlit (extra + sep) << endl;
-
- string values;
- if (!dv)
- {
- os << strlit ("VALUES" + sep) << endl;
-
- values += '(';
- instance<persist_statement_params> pt (values, *qp, sep);
- pt->traverse (c);
- values += ')';
- }
- else
- values = "DEFAULT VALUES";
-
- extra = persist_statement_extra (c, *qp, persist_after_values);
-
- if (!extra.empty ())
- values += sep;
-
- os << strlit (values);
-
- if (!extra.empty ())
- os << endl
- << strlit (extra);
-
- os << ";"
- << endl;
- }
-
- // Index of the first empty SELECT statement in poly-derived
- // statement list. All subsequent statements are also empty.
- // The first statement can never be empty (contains id and
- // type_id).
- //
- size_t empty_depth (0);
-
- if (id != 0)
- {
- string sep (versioned ? "\n" : " ");
-
- instance<object_columns_list> id_cols;
- id_cols->traverse (*id);
-
- std::vector<size_t> find_column_counts (abst ? 1 : poly_depth);
-
- // find_statement
- //
- if (poly_derived)
- os << "const char* const " << traits << "::find_statements[] ="
- << "{";
- else
- os << "const char " << traits << "::find_statement[] =" << endl;
-
- for (size_t d (poly_depth); d != 0;)
- {
- statement_columns sc;
- {
- statement_kind sk (statement_select); // Imperfect forwarding.
- object_section* s (&main_section); // Imperfect forwarding.
- instance<object_columns> t (qtable, sk, sc, d, s);
- t->traverse (c);
- process_statement_columns (sc, statement_select, versioned);
- find_column_counts[poly_depth - d] = sc.size ();
- }
-
- if (sc.size () != 0)
- {
- os << strlit ("SELECT" + sep) << endl;
-
- for (statement_columns::const_iterator i (sc.begin ()),
- e (sc.end ()); i != e;)
- {
- string const& c (i->column);
- os << strlit (c + (++i != e ? "," : "") + sep) << endl;
- }
-
- os << strlit ("FROM " + qtable + sep) << endl;
-
- if (d != 1)
- {
- bool f (false); // @@ (im)perfect forwarding
- size_t d1 (d - 1); //@@ (im)perfect forward.
- instance<polymorphic_object_joins> j (c, f, d1);
- j->traverse (polymorphic_base (c));
-
- for (strings::const_iterator i (j->begin ()); i != j->end (); ++i)
- os << strlit (*i + sep) << endl;
- }
-
- {
- // For the find statement, don't join section members.
- //
- bool f (false); // @@ (im)perfect forwarding
- object_section* s (&main_section); // @@ (im)perfect forwarding
- instance<object_joins> j (c, f, d, s);
- j->traverse (c);
-
- for (strings::const_iterator i (j->begin ()); i != j->end (); ++i)
- os << strlit (*i + sep) << endl;
- }
-
- string where ("WHERE ");
- instance<query_parameters> qp (statement_select, table);
- for (object_columns_list::iterator b (id_cols->begin ()), i (b);
- i != id_cols->end (); ++i)
- {
- if (i != b)
- where += " AND ";
-
- where += qtable + "." + quote_id (i->name) + "=" +
- convert_to (qp->next (*i), i->type, *i->member);
- }
-
- os << strlit (where);
- }
- else
- os << strlit (""); // Empty SELECT statement.
-
- if (abst)
- break;
-
- if (--d != 0)
- os << "," << endl
- << endl;
- }
-
- if (poly_derived)
- os << "};";
- else
- os << ";"
- << endl;
-
- // find_column_counts
- //
- if (poly_derived)
- {
- os << "const std::size_t " << traits << "::find_column_counts[] ="
- << "{";
-
- for (std::vector<size_t>::iterator b (find_column_counts.begin ()),
- i (b), e (find_column_counts.end ()); i != e;)
- {
- os << *i << "UL";
-
- if (*i == 0 && empty_depth == 0)
- empty_depth = i - b;
-
- if (++i != e)
- os << ',' << endl;
- }
-
- os << "};";
- }
-
- // find_discriminator_statement
- //
- if (poly && !poly_derived)
- {
- statement_columns sc;
- {
- statement_kind sk (statement_select); // Imperfect forwarding.
- instance<object_columns> t (qtable, sk, sc);
- t->traverse (*discriminator);
-
- if (opt != 0)
- t->traverse (*opt);
-
- process_statement_columns (sc, statement_select, false);
- }
-
- os << "const char " << traits << "::" << endl
- << "find_discriminator_statement[] =" << endl
- << strlit ("SELECT ") << endl;
-
- for (statement_columns::const_iterator i (sc.begin ()),
- e (sc.end ()); i != e;)
- {
- string const& c (i->column);
- os << strlit (c + (++i != e ? ", " : " ")) << endl;
- }
-
- os << strlit ("FROM " + qtable + " ") << endl;
-
- string where ("WHERE ");
- instance<query_parameters> qp (statement_select, table);
- for (object_columns_list::iterator b (id_cols->begin ()), i (b);
- i != id_cols->end (); ++i)
- {
- if (i != b)
- where += " AND ";
-
- where += qtable + "." + quote_id (i->name) + "=" +
- convert_to (qp->next (*i), i->type, *i->member);
- }
-
- os << strlit (where) << ";"
- << endl;
- }
-
- // update_statement
- //
- if (update_columns)
- {
- string sep (versioned ? "\n" : " ");
-
- instance<query_parameters> qp (statement_update, table);
-
- statement_columns sc;
- {
- query_parameters* p (qp.get ()); // Imperfect forwarding.
- statement_kind sk (statement_update); // Imperfect forwarding.
- object_section* s (&main_section); // Imperfect forwarding.
- instance<object_columns> t (sk, sc, p, s);
- t->traverse (c);
- process_statement_columns (sc, statement_update, versioned);
- }
-
- os << "const char " << traits << "::update_statement[] =" << endl
- << strlit ("UPDATE " + qtable + sep) << endl
- << strlit ("SET" + sep) << endl;
-
- for (statement_columns::const_iterator i (sc.begin ()),
- e (sc.end ()); i != e;)
- {
- string const& c (i->column);
- os << strlit (c + (++i != e ? "," : "") + sep) << endl;
- }
-
- // This didn't work out: cannot change the identity column.
- //
- //if (sc.empty ())
- //{
- // // We can end up with nothing to set if we need to "touch" a row
- // // in order to increment its optimistic concurrency version. In
- // // this case just do a dummy assignment based on the id column.
- // //
- // string const& c (quote_id (id_cols->begin ()->name));
- // os << strlit (c + "=" + c) << endl;
- //}
-
- string extra (update_statement_extra (c));
-
- if (!extra.empty ())
- os << strlit (extra + sep) << endl;
-
- string where ("WHERE ");
- for (object_columns_list::iterator b (id_cols->begin ()), i (b);
- i != id_cols->end (); ++i)
- {
- if (i != b)
- where += " AND ";
-
- where += quote_id (i->name) + "=" +
- convert_to (qp->next (*i), i->type, *i->member);
- }
-
- // Top-level version column.
- //
- if (opt != 0 && !poly_derived)
- {
- string name (column_qname (*opt, column_prefix ()));
- string type (column_type (*opt));
-
- where += " AND " + name + "=" +
- convert_to (qp->next (*opt, name, type), type, *opt);
- }
-
- os << strlit (where) << ";"
- << endl;
- }
-
- // erase_statement
- //
- {
- instance<query_parameters> qp (statement_delete, table);
- os << "const char " << traits << "::erase_statement[] =" << endl
- << strlit ("DELETE FROM " + qtable + " ") << endl;
-
- string where ("WHERE ");
- for (object_columns_list::iterator b (id_cols->begin ()), i (b);
- i != id_cols->end (); ++i)
- {
- if (i != b)
- where += " AND ";
-
- where += quote_id (i->name) + "=" +
- convert_to (qp->next (*i), i->type, *i->member);
- }
-
- os << strlit (where) << ";"
- << endl;
- }
-
- if (opt != 0 && !poly_derived)
- {
- instance<query_parameters> qp (statement_delete, table);
-
- os << "const char " << traits << "::optimistic_erase_statement[] " <<
- "=" << endl
- << strlit ("DELETE FROM " + qtable + " ") << endl;
-
- string where ("WHERE ");
- for (object_columns_list::iterator b (id_cols->begin ()), i (b);
- i != id_cols->end (); ++i)
- {
- if (i != b)
- where += " AND ";
-
- where += quote_id (i->name) + "=" +
- convert_to (qp->next (*i), i->type, *i->member);
- }
-
- // Top-level version column.
- //
- string name (column_qname (*opt, column_prefix ()));
- string type (column_type (*opt));
-
- where += " AND " + name + "=" +
- convert_to (qp->next (*opt, name, type), type, *opt);
-
- os << strlit (where) << ";"
- << endl;
- }
- }
-
- // query_statement
- //
- bool query_optimize (false);
- if (options.generate_query ())
- {
- // query_statement
- //
- strings joins;
- if (poly_depth != 1)
- {
- bool t (true); //@@ (im)perfect forwarding
- size_t d (poly_depth - 1); //@@ (im)perfect forward.
- instance<polymorphic_object_joins> j (c, t, d);
- j->traverse (polymorphic_base (c));
- joins = j->joins;
- }
-
- if (id != 0)
- {
- // For the query statement we also join section members so that they
- // can be used in the WHERE clause.
- //
- bool t (true); //@@ (im)perfect forwarding
- instance<object_joins> j (c, t, poly_depth);
- j->traverse (c);
- joins.insert (joins.end (), j->begin (), j->end ());
- }
-
- query_optimize = !joins.empty ();
-
- statement_columns sc;
- {
- statement_kind sk (statement_select); //@@ Imperfect forwarding.
- object_section* s (&main_section); //@@ Imperfect forwarding.
- instance<object_columns> oc (qtable, sk, sc, poly_depth, s);
- oc->traverse (c);
- process_statement_columns (
- sc, statement_select, versioned || query_optimize);
- }
-
- string sep (versioned || query_optimize ? "\n" : " ");
-
- os << "const char " << traits << "::query_statement[] =" << endl
- << strlit ("SELECT" + sep) << endl;
-
- for (statement_columns::const_iterator i (sc.begin ()),
- e (sc.end ()); i != e;)
- {
- string const& c (i->column);
- os << strlit (c + (++i != e ? "," : "") + sep) << endl;
- }
-
- string prev ("FROM " + qtable);
-
- for (strings::const_iterator i (joins.begin ()); i != joins.end (); ++i)
- {
- os << strlit (prev + sep) << endl;
- prev = *i;
- }
-
- os << strlit (prev) << ";"
- << endl;
-
- // erase_query_statement
- //
- os << "const char " << traits << "::erase_query_statement[] =" << endl
- << strlit ("DELETE FROM " + qtable) << ";"
- << endl;
-
- // table_name
- //
- os << "const char " << traits << "::table_name[] =" << endl
- << strlit (qtable) << ";" // Use quoted name.
- << endl;
- }
-
- // persist ()
- //
- size_t persist_containers (has_a (c, test_straight_container));
- bool persist_versioned_containers (
- persist_containers >
- has_a (c,
- test_straight_container |
- exclude_deleted | exclude_added | exclude_versioned));
-
- os << "void " << traits << "::" << endl
- << "persist (database& db, " << (auto_id ? "" : "const ") <<
- "object_type& obj";
-
- if (poly)
- os << ", bool top, bool dyn";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (db);";
-
- if (poly)
- os << "ODB_POTENTIALLY_UNUSED (top);";
-
- os << endl
- << "using namespace " << db << ";"
- << endl;
-
- if (poly)
- os << "if (dyn)" << endl
- << "{"
- << "const std::type_info& t (typeid (obj));"
- << endl
- << "if (t != info.type)"
- << "{"
- << "const info_type& pi (root_traits::map->find (t));"
- << "pi.dispatch (info_type::call_persist, db, &obj, 0);"
- << "return;"
- << "}"
- << "}";
-
- // If we are database-poly-abstract but not C++-abstract, then make
- // sure we are not trying to persist an instance of an abstract class.
- //
- if (abst && !c.abstract ())
- os << "if (top)" << endl
- << "throw abstract_class ();"
- << endl;
-
- os << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());";
-
- if (versioned ||
- persist_versioned_containers ||
- uss.count (user_sections::count_new |
- user_sections::count_all |
- user_sections::count_versioned_only) != 0)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));";
-
- os << endl;
-
- // Call callback (pre_persist).
- //
- if (!abst) // If we are poly-abstract, then top will always be false.
- {
- if (poly)
- os << "if (top)" << endl;
-
- os << "callback (db," << endl
- << (auto_id ? "static_cast<const object_type&> (obj)," : "obj,") << endl
- << "callback_event::pre_persist);"
- << endl;
- }
-
- // Call our base if we are a derived type in a polymorphic
- // hierarchy.
- //
- if (poly_derived)
- os << "base_traits::persist (db, obj, false, false);"
- << endl;
-
- os << "image_type& im (sts.image ());"
- << "binding& imb (sts.insert_image_binding ());";
-
- if (poly_derived)
- os << "const binding& idb (sts.id_image_binding ());";
-
- os << endl;
-
- if (generate_grow)
- os << "if (";
-
- os << "init (im, obj, statement_insert" << (versioned ? ", svm" : "") << ")";
-
- if (generate_grow)
- os << ")" << endl
- << "im.version++";
-
- os << ";"
- << endl;
-
- if (!poly_derived && auto_id && insert_send_auto_id)
- {
- string const& n (idf->name ());
- string var ("im." + n + (n[n.size () - 1] == '_' ? "" : "_"));
- init_auto_id (*idf, var); // idf == idb, since auto
- }
-
- os << "if (";
-
- if (poly_derived)
- os << "idb.version != sts.insert_id_binding_version () ||" << endl;
-
- os << "im.version != sts.insert_image_version () ||" << endl
- << "imb.version == 0)"
- << "{"
- << "bind (imb.bind, ";
-
- if (poly_derived)
- os << "idb.bind, idb.count, ";
-
- os << "im, statement_insert" << (versioned ? ", svm" : "") << ");";
-
- if (poly_derived)
- os << "sts.insert_id_binding_version (idb.version);";
-
- os << "sts.insert_image_version (im.version);"
- << "imb.version++;"
- << "}";
-
- // Bind id image since that's where the returned id and/or version will
- // be extracted.
- //
- if (!poly_derived)
- {
- bool bv (opt != 0 && optimistic_insert_bind_version (*opt));
- if (bv || auto_id)
- {
- os << "{"
- << "id_image_type& i (sts.id_image ());"
- << "binding& b (sts.id_image_binding ());"
- << "if (i.version != sts.id_image_version () || b.version == 0)"
- << "{"
- << "bind (b.bind, i);"
- << "sts.id_image_version (i.version);"
- << "b.version++;";
- if (opt != 0)
- os << "sts.optimistic_id_image_binding ().version++;";
- os << "}"
- << "}";
- }
- }
-
- os << "insert_statement& st (sts.persist_statement ());"
- << "if (!st.execute ())" << endl
- << "throw object_already_persistent ();"
- << endl;
-
- if (!poly_derived && auto_id) // idf == idb since auto
- {
- set_member (*idf, "obj", "id (sts.id_image ())", "db", "id_type");
- os << endl;
- }
-
- // Set the optimistic concurrency version in the object member.
- //
- if (opt != 0 && !poly_derived)
- {
- // If we don't have auto id, then obj is a const reference.
- //
- set_member (*opt,
- (auto_id ? "obj" : "const_cast<object_type&> (obj)"),
- optimistic_version_init (*opt),
- "", // No database.
- "version_type");
- os << endl;
- }
-
- // Initialize id_image and binding if we are a root of a polymorphic
- // hierarchy or if we have non-inverse containers.
- //
- if (!poly_derived && (poly || persist_containers))
- {
- // If this is a polymorphic root without containers, then we only
- // need to do this if we are not a top-level call. If we are poly-
- // abstract, then top will always be false.
- //
- if (poly && !persist_containers && !abst)
- os << "if (!top)"
- << "{";
-
- os << "id_image_type& i (sts.id_image ());"
- << "init (i, id (obj));"
- << endl
- << "binding& idb (sts.id_image_binding ());"
- << "if (i.version != sts.id_image_version () || idb.version == 0)"
- << "{"
- << "bind (idb.bind, i);"
- << "sts.id_image_version (i.version);"
- << "idb.version++;";
- if (opt != 0)
- os << "sts.optimistic_id_image_binding ().version++;";
- os << "}";
-
- if (poly && !persist_containers && !abst)
- os << "}";
- }
-
- if (persist_containers)
- {
- os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
- << endl;
-
- instance<container_calls> t (container_calls::persist_call);
- t->traverse (c);
- }
-
- // Reset sections: loaded, unchanged.
- //
- for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
- {
- // Skip special sections.
- //
- if (i->special == user_section::special_version)
- continue;
-
- // Skip overridden sections; they have been reset by the base.
- //
- if (i->base != 0 && poly_derived)
- continue;
-
- data_member& m (*i->member);
-
- // If the section is soft- added or deleted, check the version.
- //
- unsigned long long av (added (m));
- unsigned long long dv (deleted (m));
- if (av != 0 || dv != 0)
- {
- os << "if (";
-
- if (av != 0)
- os << "svm >= schema_version_migration (" << av << "ULL, true)";
-
- if (av != 0 && dv != 0)
- os << " &&" << endl;
-
- if (dv != 0)
- os << "svm <= schema_version_migration (" << dv << "ULL, true)";
-
- os << ")" << endl;
- }
-
- // Section access is always by reference.
- //
- member_access& ma (m.get<member_access> ("get"));
- if (!ma.synthesized)
- os << "// From " << location_string (ma.loc, true) << endl;
-
- os << ma.translate ("obj") << ".reset (true, false);"
- << endl;
- }
-
- // Call callback (post_persist).
- //
- if (!abst) // If we are poly-abstract, then top will always be false.
- {
- if (poly)
- os << "if (top)" << endl;
-
- os << "callback (db," << endl
- << (auto_id ? "static_cast<const object_type&> (obj)," : "obj,") << endl
- << "callback_event::post_persist);";
- }
-
- os << "}";
-
- // persist() bulk
- //
- if (c.count ("bulk-persist"))
- {
- os << "void " << traits << "::" << endl
- << "persist (database& db," << endl
- << (auto_id ? "" : "const ") << "object_type** objs," << endl
- << "std::size_t n," << endl
- << "multiple_exceptions& mex)"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (db);"
- << endl
- << "using namespace " << db << ";"
- << endl;
-
- os << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());";
-
- if (versioned ||
- uss.count (user_sections::count_new |
- user_sections::count_all |
- user_sections::count_versioned_only) != 0)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));";
-
- os << endl;
-
- os << "for (std::size_t i (0); i != n; ++i)"
- << "{"
- << "const object_type& obj (*objs[i]);"
- << "callback (db, obj, callback_event::pre_persist);"
- //@@ assumption: generate_grow is false
- //@@ assumption: insert_send_auto_id is false
- << "init (sts.image (i), obj, statement_insert" <<
- (versioned ? ", svm" : "") << ");"
- << "}";
-
- //@@ assumption: generate_grow is false
- os << "binding& imb (sts.insert_image_binding ());"
- << "if (imb.version == 0)"
- << "{"
- << "bind (imb.bind, sts.image (), statement_insert" <<
- (versioned ? ", svm" : "") << ");"
- << "imb.version++;"
- << "}";
-
- // Bind id image since that's where the returned id and/or version will
- // be extracted.
- //
- bool bv (opt != 0 && optimistic_insert_bind_version (*opt));
- if (bv || auto_id)
- {
- os << "binding& idb (sts.id_image_binding ());"
- //@@ assumption: generate_grow is false
- << "if (idb.version == 0)"
- << "{"
- << "bind (idb.bind, sts.id_image ());"
- << "idb.version++;";
- if (opt != 0)
- os << "sts.optimistic_id_image_binding ().version++;";
- os << "}";
- }
-
- os << "insert_statement& st (sts.persist_statement ());"
- << "n = st.execute (n, mex);" // Actual number of rows attempted.
- << endl;
-
- os << "for (std::size_t i (0); i != n; ++i)"
- << "{"
- << "bool r (st.result (i));" // Sets current in mex.
- << endl
- << "if (mex[i] != 0)" << endl // Pending exception for this position.
- << "continue;"
- << endl
- << "if (!r)"
- << "{"
- << "mex.insert (i, object_already_persistent ());"
- << "continue;"
- << "}"
- << "if (mex.fatal ())" << endl // Don't do any extra work.
- << "continue;"
- << endl
- << (auto_id ? "" : "const ") << "object_type& obj (*objs[i]);"
- << endl;
-
- // Extract auto id.
- //
- if (auto_id) // idb == idf, since auto
- {
- set_member (*idf, "obj", "id (sts.id_image (i))", "db", "id_type");
- os << endl;
- }
-
- // Set the optimistic concurrency version.
- //
- if (opt != 0)
- {
- // If we don't have auto id, then obj is a const reference.
- //
- set_member (*opt,
- (auto_id ? "obj" : "const_cast<object_type&> (obj)"),
- optimistic_version_init (*opt, true),
- "", // No database.
- "version_type");
- os << endl;
- }
-
- // Reset sections: loaded, unchanged.
- //
- for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
- {
- // Skip special sections.
- //
- if (i->special == user_section::special_version)
- continue;
-
- data_member& m (*i->member);
-
- // If the section is soft- added or deleted, check the version.
- //
- unsigned long long av (added (m));
- unsigned long long dv (deleted (m));
- if (av != 0 || dv != 0)
- {
- os << "if (";
-
- if (av != 0)
- os << "svm >= schema_version_migration (" << av << "ULL, true)";
-
- if (av != 0 && dv != 0)
- os << " &&" << endl;
-
- if (dv != 0)
- os << "svm <= schema_version_migration (" << dv << "ULL, true)";
-
- os << ")" << endl;
- }
-
- // Section access is always by reference.
- //
- member_access& ma (m.get<member_access> ("get"));
- if (!ma.synthesized)
- os << "// From " << location_string (ma.loc, true) << endl;
-
- os << ma.translate ("obj") << ".reset (true, false);"
- << endl;
- }
-
- os << "callback (db," << endl
- << (auto_id ? "static_cast<const object_type&> (obj)," : "obj,") << endl
- << "callback_event::post_persist);"
- << "}" // for
- << "}"; // persist ()
- }
-
- // update ()
- //
- if (id != 0 && (!readonly || poly))
- {
- size_t update_containers (
- has_a (c, test_readwrite_container, &main_section));
-
- bool update_versioned_containers (
- update_containers >
- has_a (c,
- test_readwrite_container |
- exclude_deleted | exclude_added | exclude_versioned,
- &main_section));
-
- // See if we have any sections that we might have to update.
- //
- bool sections (false);
- bool versioned_sections (false);
- for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
- {
- // This test will automatically skip the special version update section.
- //
- if (i->update_empty () || i->update == user_section::update_manual)
- continue;
-
- sections = true;
-
- if (added (*i->member) || deleted (*i->member))
- versioned_sections = true;
-
- // For change-updated sections, also check if we have any
- // versioned smart containers.
- //
- if (i->update != user_section::update_change)
- continue;
-
- if (has_a (c, test_smart_container, &*i) !=
- has_a (c, test_smart_container | exclude_deleted | exclude_added, &*i))
- versioned_sections = true;
- }
-
- os << "void " << traits << "::" << endl
- << "update (database& db, const object_type& obj";
-
- if (poly)
- os << ", bool top, bool dyn";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (db);";
-
- if (poly)
- os << "ODB_POTENTIALLY_UNUSED (top);";
-
- os << endl
- << "using namespace " << db << ";"
- << "using " << db << "::update_statement;"
- << endl;
-
- if (poly)
- os << "if (dyn)" << endl
- << "{"
- << "const std::type_info& t (typeid (obj));"
- << endl
- << "if (t != info.type)"
- << "{"
- << "const info_type& pi (root_traits::map->find (t));"
- << "pi.dispatch (info_type::call_update, db, &obj, 0);"
- << "return;"
- << "}"
- << "}";
-
- // If we are database-poly-abstract but not C++-abstract, then make
- // sure we are not trying to update an instance of an abstract class.
- //
- if (abst && !c.abstract ())
- os << "if (top)" << endl
- << "throw abstract_class ();"
- << endl;
-
- // If we are readonly, then there is nothing else to do.
- //
- if (!readonly)
- {
- // Call callback (pre_update).
- //
- if (!abst) // If we are poly-abstract then top will always be false.
- {
- if (poly)
- os << "if (top)" << endl;
-
- os << "callback (db, obj, callback_event::pre_update);"
- << endl;
- }
-
- bool sts (false);
-
- if ((versioned && update_columns) ||
- update_versioned_containers ||
- versioned_sections)
- {
- sts = true;
- os << db << "::transaction& tr (" << db <<
- "::transaction::current ());"
- << db << "::connection& conn (tr.connection ());"
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());"
- << endl;
-
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));"
- << endl;
- }
-
- // If we have change-updated sections that contain change-tracking
- // containers, then mark such sections as changed if any of the
- // containers were changed. Do this before calling the base so that
- // we account for the whole hierarchy before we actually start
- // updating any sections (important for optimistic concurrency
- // version).
- //
- for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
- {
- user_section& s (*i);
-
- if (s.update != user_section::update_change)
- continue;
-
- if (!has_a (c, test_smart_container, &s))
- continue;
-
- data_member& m (*s.member);
-
- os << "// " << m.name () << endl
- << "//" << endl;
-
- // If the section is soft- added or deleted, check the version.
- //
- unsigned long long av (added (m));
- unsigned long long dv (deleted (m));
- if (av != 0 || dv != 0)
- {
- os << "if (";
-
- if (av != 0)
- os << "svm >= schema_version_migration (" << av << "ULL, true)";
-
- if (av != 0 && dv != 0)
- os << " &&" << endl;
-
- if (dv != 0)
- os << "svm <= schema_version_migration (" << dv << "ULL, true)";
-
- os << ")";
- }
-
- os << "{";
-
- // Section access is always by reference.
- //
- member_access& ma (m.get<member_access> ("get"));
- if (!ma.synthesized)
- os << "// From " << location_string (ma.loc, true) << endl;
-
- // VC++ cannot grok the constructor syntax.
- //
- os << "const odb::section& s = " << ma.translate ("obj") << ";"
- << endl;
-
- os << "if (";
-
- // Unless we are always loaded, test for being loaded.
- //
- if (s.load != user_section::load_eager)
- os << "s.loaded () && ";
-
- // And for not already being changed.
- //
- os << "!s.changed ())"
- << "{";
-
- instance<container_calls> t (container_calls::section_call, &s);
- t->traverse (c);
-
- os << "}" // if
- << "}";
- }
-
- if (poly_derived)
- {
- bool readonly_base (context::readonly (*poly_base));
-
- if (!sts && (readonly_base ||
- update_columns ||
- update_containers ||
- sections))
- {
- os << db << "::transaction& tr (" << db <<
- "::transaction::current ());"
- << db << "::connection& conn (tr.connection ());"
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());"
- << endl;
- }
-
- // Unless our base is readonly, call it first.
- //
- if (!readonly_base)
- {
- os << "base_traits::update (db, obj, false, false);"
- << endl;
- }
- else
- {
- // Otherwise, we have to initialize the id image ourselves. If
- // we don't have any columns, containers or sections to update,
- // then we only have to do it if this is not a top-level call.
- // If we are abstract, then top is always false.
- //
- if (!update_columns && !update_containers && !sections && !abst)
- os << "if (!top)";
-
- os << "{"
- << "id_image_type& i (sts.id_image ());"
- << "init (i, id (obj));"
- << endl;
-
- os << "binding& idb (sts.id_image_binding ());"
- << "if (i.version != sts.id_image_version () || idb.version == 0)"
- << "{"
- << "bind (idb.bind, i);"
- << "sts.id_image_version (i.version);"
- << "idb.version++;"
- // Optimistic poly base cannot be readonly.
- << "}"
- << "}";
- }
-
- if (update_columns)
- {
- // Initialize the object image.
- //
- os << "image_type& im (sts.image ());";
-
- if (generate_grow)
- os << "if (";
-
- os << "init (im, obj, statement_update" <<
- (versioned ? ", svm" : "") << ")";
-
- if (generate_grow)
- os << ")" << endl
- << "im.version++";
-
- os << ";"
- << endl;
-
- os << "const binding& idb (sts.id_image_binding ());"
- << "binding& imb (sts.update_image_binding ());"
- << "if (idb.version != sts.update_id_binding_version () ||" << endl
- << "im.version != sts.update_image_version () ||" << endl
- << "imb.version == 0)"
- << "{"
- << "bind (imb.bind, idb.bind, idb.count, im, statement_update" <<
- (versioned ? ", svm" : "") << ");"
- << "sts.update_id_binding_version (idb.version);"
- << "sts.update_image_version (im.version);"
- << "imb.version++;"
- << "}";
-
- os << "update_statement& st (sts.update_statement ());"
- << "if (";
-
- if (versioned)
- os << "!st.empty () && ";
-
- os << "st.execute () == 0)" << endl
- << "throw object_not_persistent ();"
- << endl;
- }
-
- // Otherwise, nothing else to do here if we don't have any columns
- // to update.
- }
- else if (update_columns)
- {
- if (!sts)
- os << db << "::transaction& tr (" << db <<
- "::transaction::current ());"
- << db << "::connection& conn (tr.connection ());"
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());"
- << endl;
-
- // Initialize id image.
- //
- if (opt != 0)
- os << "const version_type& v (version (obj));";
-
- os << "id_image_type& idi (sts.id_image ());"
- << "init (idi, id (obj)" << (opt != 0 ? ", &v" : "") << ");"
- << endl;
-
- // Initialize object image.
- //
- os << "image_type& im (sts.image ());";
-
- if (generate_grow)
- os << "if (";
-
- os << "init (im, obj, statement_update" <<
- (versioned ? ", svm" : "") << ")";
-
- if (generate_grow)
- os << ")" << endl
- << "im.version++";
-
- os << ";"
- << endl;
-
- // The update binding is bound to two images (object and id)
- // so we have to track both versions.
- //
- os << "bool u (false);" // Avoid incrementing version twice.
- << "binding& imb (sts.update_image_binding ());"
- << "if (im.version != sts.update_image_version () ||" << endl
- << "imb.version == 0)"
- << "{"
- << "bind (imb.bind, im, statement_update" <<
- (versioned ? ", svm" : "") << ");"
- << "sts.update_image_version (im.version);"
- << "imb.version++;"
- << "u = true;"
- << "}";
-
- // To update the id part of the update binding we have to do
- // it indirectly via the id binding, which just points to the
- // suffix of the update bind array (see object_statements).
- //
- os << "binding& idb (sts.id_image_binding ());"
- << "if (idi.version != sts.update_id_image_version () ||" << endl
- << "idb.version == 0)"
- << "{"
- // If the id binding is up-to-date, then that means update
- // binding is too and we just need to update the versions.
- //
- << "if (idi.version != sts.id_image_version () ||" << endl
- << "idb.version == 0)"
- << "{"
- << "bind (idb.bind, idi);"
- // Update the id binding versions since we may use them later
- // to update containers.
- //
- << "sts.id_image_version (idi.version);"
- << "idb.version++;";
- if (opt != 0)
- os << "sts.optimistic_id_image_binding ().version++;";
- os << "}"
- << "sts.update_id_image_version (idi.version);"
- << endl
- << "if (!u)" << endl
- << "imb.version++;"
- << "}";
-
- os << "update_statement& st (sts.update_statement ());"
- << "if (";
-
- if (versioned)
- os << "!st.empty () && ";
-
- os << "st.execute () == 0)" << endl;
-
- if (opt == 0)
- os << "throw object_not_persistent ();";
- else
- os << "throw object_changed ();";
-
- os << endl;
- }
- else if (poly || update_containers || sections)
- {
- // We don't have any columns to update but we may still need
- // to initialize the id image if we are polymorphic or have
- // any containers or sections.
- //
- if (!sts)
- os << db << "::transaction& tr (" << db <<
- "::transaction::current ());"
- << db << "::connection& conn (tr.connection ());"
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());"
- << endl;
-
- // The same rationale as a couple of screens up.
- //
- if (poly && !update_containers && !sections && !abst)
- os << "if (!top)";
-
- os << "{"
- << "id_image_type& i (sts.id_image ());"
- << "init (i, id (obj));"
- << endl;
-
- os << "binding& idb (sts.id_image_binding ());"
- << "if (i.version != sts.id_image_version () || idb.version == 0)"
- << "{"
- << "bind (idb.bind, i);"
- << "sts.id_image_version (i.version);"
- << "idb.version++;"
- // Cannot be optimistic.
- << "}"
- << "}";
- }
-
- if (update_containers || sections)
- os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
- << endl;
-
- if (update_containers)
- {
- instance<container_calls> t (container_calls::update_call,
- &main_section);
- t->traverse (c);
- }
-
- // Update the optimistic concurrency version in the object member.
- // Do it before updating sections since they will update it as well.
- // The same code as in section_traits.
- //
- if (opt != 0 && !poly_derived)
- {
- // Object is passed as const reference so we need to cast away
- // constness.
- //
- const char* obj ("const_cast<object_type&> (obj)");
- string inc (optimistic_version_increment (*opt));
-
- if (inc == "1")
- inc_member (*opt, obj, "obj", "version_type");
- else
- set_member (*opt, obj, inc, "", "version_type");
-
- os << endl;
- }
-
- // Update sections that are loaded and need updating.
- //
- for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
- {
- if (i->update_empty () || i->update == user_section::update_manual)
- continue;
-
- // Here is the deal: for polymorphic section overrides, we could
- // have used the same approach as in load_() where the class that
- // defines the section data member initiates the section update.
- // However, if we used this approach, then we would get what can
- // be called "interleaved" update statements. That is, a section
- // update would update the section data in "derived" tables before
- // updating the main section in these derived tables. While this
- // does not feel right, it can also cause more real problems if,
- // for example, there are constraints or some such. Generally,
- // we always want to update the main section data first for each
- // table in the hierarchy.
- //
- // So what we are going to do instead is call section traits
- // update at each override point (and indicate to it not to
- // call base). One tricky aspect is who is going to reset the
- // changed state. We have to do it at the top since otherwise
- // overrides won't know the section has changed. Finding out
- // whether we are the final override (in section terms, not
- // object terms) is tricky.
- //
- data_member& m (*i->member);
-
- // If the section is soft- added or deleted, check the version.
- //
- unsigned long long av (added (m));
- unsigned long long dv (deleted (m));
- if (av != 0 || dv != 0)
- {
- os << "if (";
-
- if (av != 0)
- os << "svm >= schema_version_migration (" << av << "ULL, true)";
-
- if (av != 0 && dv != 0)
- os << " &&" << endl;
-
- if (dv != 0)
- os << "svm <= schema_version_migration (" << dv << "ULL, true)";
-
- os << ")"
- << "{";
- }
-
- // Section access is always by reference.
- //
- member_access& ma (m.get<member_access> ("get"));
- if (!ma.synthesized)
- os << "// From " << location_string (ma.loc, true) << endl;
-
- os << "if (";
-
- // Unless we are always loaded, test for being loaded.
- //
- string sep;
- if (i->load != user_section::load_eager)
- {
- os << ma.translate ("obj") << ".loaded ()";
- sep = " && ";
- }
-
- // If change-updated section, check that it has changed.
- //
- if (i->update == user_section::update_change)
- os << sep << ma.translate ("obj") << ".changed ()";
-
- os << ")"
- << "{";
-
- // Re-initialize the id+ver image with new version which may
- // have changed. Here we take a bit of a shortcut and not
- // re-bind the image since we know only version may have
- // changed and that is always an integer (cannot grow).
- //
- if (opt != 0)
- {
- os << "const version_type& v (version (obj));"
- << "init (sts.id_image (), id (obj), &v);"
- << endl;
- }
-
- os << public_name (m) << "_traits::update (esc, obj";
-
- if (poly_derived && i->base != 0)
- {
- // Normally we don't want to call base (base object's update()
- // would have called it; see comment above). There is one
- // exception, however, and that is a readonly base section
- // in optimistic class. In this case, base object's update()
- // won't call it (it is readonly) but it needs to be called
- // in order to increment the version.
- //
- bool base (false);
- for (user_section* b (i->base); !base && b != 0; b = b->base)
- {
- if (b->total != b->inverse + b->readonly ||
- b->readwrite_containers)
- break; // We have a readwrite base.
- else if (b->optimistic ())
- base = true; // We have a readonly optimistic root.
- }
-
- os << ", " << base;
- }
-
- os << ");";
-
- // Clear the change flag and arm the transaction rollback callback.
- //
- if (i->update == user_section::update_change)
- {
- // If polymorphic, do it only if we are the final override.
- //
- if (poly)
- os << "if (root_traits::map->find (typeid (obj))." <<
- "final_section_update (info, " << i->index << "UL))" << endl;
-
- os << ma.translate ("obj") << ".reset (true, false, &tr);";
- }
-
- os << "}";
-
- if (av != 0 || dv != 0)
- os << "}";
- }
-
- // Call callback (post_update).
- //
- if (!abst) // If we are poly-abstract, then top will always be false.
- {
- if (poly)
- os << "if (top)"
- << "{";
-
- os << "callback (db, obj, callback_event::post_update);"
- << "pointer_cache_traits::update (db, obj);";
-
- if (poly)
- os << "}";
- }
- } // readonly
-
- os << "}";
- }
-
- // update () bulk
- //
- if (id != 0 && !readonly && c.count ("bulk-update"))
- {
- os << "void " << traits << "::" << endl
- << "update (database& db," << endl
- << "const object_type** objs," << endl
- << "std::size_t n," << endl
- << "multiple_exceptions& mex)"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (db);"
- << endl
- << "using namespace " << db << ";"
- << "using " << db << "::update_statement;"
- << endl
- << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());";
-
- if (versioned)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));";
-
- os << endl
- << "for (std::size_t i (0); i != n; ++i)"
- << "{"
- << "const object_type& obj (*objs[i]);"
- << "callback (db, obj, callback_event::pre_update);";
-
- if (opt != 0)
- os << "const version_type& v (version (obj));";
-
- os << "init (sts.id_image (i), id (obj)" << (opt != 0 ? ", &v" : "") << ");"
- //@@ assumption: generate_grow false
- << "init (sts.image (i), obj, statement_update);"
- << "}";
-
- // Update bindings.
- //
- os << "binding& idb (sts.id_image_binding ());"
- << "binding& imb (sts.update_image_binding ());"
- << endl;
-
- //@@ assumption: generate_grow false
- //
- os << "bool u (false);" // Avoid incrementing version twice.
- << "if (imb.version == 0)"
- << "{"
- << "bind (imb.bind, sts.image (), statement_update" <<
- (versioned ? ", svm" : "") << ");"
- << "imb.version++;"
- << "u = true;"
- << "}";
-
- //@@ assumption: generate_grow false
- //
- os << "if (idb.version == 0)"
- << "{"
- << "bind (idb.bind, sts.id_image ());"
- << "idb.version++;";
- if (opt != 0)
- os << "sts.optimistic_id_image_binding ().version++;";
- os << endl
- << "if (!u)" << endl
- << "imb.version++;"
- << "}";
-
- // We don't need the versioned check here since the statement cannot
- // be empty; otherwise it would have been an empty object which is
- // illegal.
- //
- os << "update_statement& st (sts.update_statement ());"
- << "n = st.execute (n, mex);"; // Actual number of rows attempted.
-
- os << endl
- << "for (std::size_t i (0); i != n; ++i)"
- << "{"
- << "unsigned long long r (st.result (i));" // Also sets current in mex.
- << endl
- << "if (mex[i] != 0)" << endl // Pending exception from result().
- << "continue;"
- << endl
- << "if (r != 1)"
- << "{"
- << "mex.insert (i," << endl
- //@@ assumption: result_unknown
- << "(r == update_statement::result_unknown)," << endl
- << (opt == 0 ? "object_not_persistent" : "object_changed") << " ());"
- << "continue;"
- << "}"
- << "if (mex.fatal ())" << endl // Don't do any extra work.
- << "continue;"
- << endl
- << "const object_type& obj (*objs[i]);";
-
- // Update the optimistic concurrency version in the object member.
- //
- if (opt != 0)
- {
- // Object is passed as const reference so we need to cast away
- // constness.
- //
- const char* obj ("const_cast<object_type&> (obj)");
- string inc (optimistic_version_increment (*opt));
-
- if (inc == "1")
- inc_member (*opt, obj, "obj", "version_type");
- else
- set_member (*opt, obj, inc, "", "version_type");
-
- os << endl;
- }
-
- os << "callback (db, obj, callback_event::post_update);"
- << "pointer_cache_traits::update (db, obj);"
- << "}" // for
- << "}"; // update()
- }
-
- // erase (id)
- //
- size_t erase_containers (has_a (c, test_straight_container));
- bool erase_versioned_containers (
- erase_containers >
- has_a (c, test_straight_container | exclude_deleted | exclude_added));
-
- if (id != 0)
- {
- os << "void " << traits << "::" << endl
- << "erase (database& db, const id_type& id";
-
- if (poly)
- os << ", bool top, bool dyn";
-
- os << ")"
- << "{"
- << "using namespace " << db << ";"
- << endl
- << "ODB_POTENTIALLY_UNUSED (db);";
-
- if (poly)
- os << "ODB_POTENTIALLY_UNUSED (top);";
-
- os << endl
- << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());"
- << endl;
-
- // Get the discriminator and determine the dynamic type of
- // this object.
- //
- if (poly)
- {
- os << "if (dyn)" << endl
- << "{"
- << "discriminator_type d;"
- << "root_traits::discriminator_ (sts.root_statements (), id, &d);";
-
- if (!abst)
- os << endl
- << "if (d != info.discriminator)"
- << "{";
-
- os << "const info_type& pi (root_traits::map->find (d));"
- << endl
- // Check that the dynamic type is derived from the static type.
- //
- << "if (!pi.derived (info))" << endl
- << "throw object_not_persistent ();"
- << endl
- << "pi.dispatch (info_type::call_erase, db, 0, &id);"
- << "return;";
-
- if (!abst)
- os << "}";
-
- os << "}";
- }
-
- // Initialize id image.
- //
- if (!abst) // If we are poly-abstract, then top will always be false.
- {
- if (poly)
- os << "if (top)"
- << "{";
-
- os << "id_image_type& i (sts.id_image ());"
- << "init (i, id);"
- << endl;
-
- os << "binding& idb (sts.id_image_binding ());"
- << "if (i.version != sts.id_image_version () || idb.version == 0)"
- << "{"
- << "bind (idb.bind, i);"
- << "sts.id_image_version (i.version);"
- << "idb.version++;";
- if (opt != 0)
- os << "sts.optimistic_id_image_binding ().version++;";
- os << "}";
-
- if (poly)
- os << "}";
- }
-
- // Erase containers first so that there are no reference
- // violations (we don't want to rely on ON DELETE CASCADE
- // here since in case of a custom schema, it might not be
- // there).
- //
- if (erase_containers)
- {
- os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());";
-
- if (erase_versioned_containers)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));";
-
- os << endl;
-
- instance<container_calls> t (container_calls::erase_id_call);
- t->traverse (c);
- }
-
- os << "if (sts.erase_statement ().execute () != 1)" << endl
- << "throw object_not_persistent ();"
- << endl;
-
- if (poly_derived)
- {
- // Call our base last (we erase polymorphic objects from base
- // to derived in order not to trigger cascading deletes).
- //
- os << "base_traits::erase (db, id, false, false);"
- << endl;
- }
-
- // Remove from the object cache.
- //
- if (!abst) // If we are poly-abstract, then top will always be false.
- {
- if (poly)
- os << "if (top)" << endl;
-
- os << "pointer_cache_traits::erase (db, id);";
- }
-
- os << "}";
- }
-
- // erase (id) bulk
- //
- if (id != 0 && c.count ("bulk-erase"))
- {
- os << "std::size_t " << traits << "::" << endl
- << "erase (database& db," << endl
- << "const id_type** ids," << endl
- << "std::size_t n," << endl
- << "multiple_exceptions& mex)"
- << "{"
- << "using namespace " << db << ";"
- << endl
- << "ODB_POTENTIALLY_UNUSED (db);"
- << endl
- << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());"
- << endl
- << "for (std::size_t i (0); i != n; ++i)" << endl
- << "init (sts.id_image (i), *ids[i]);"
- << endl
- << "binding& idb (sts.id_image_binding ());"
- //@@ assumption: generate_grow false
- << "if (idb.version == 0)"
- << "{"
- << "bind (idb.bind, sts.id_image ());"
- << "idb.version++;"
- << (opt != 0 ? "sts.optimistic_id_image_binding ().version++;" : "")
- << "}"
- << "delete_statement& st (sts.erase_statement ());"
- << "n = st.execute (n, mex);" // Set to actual number of rows attempted.
- << endl
- << "for (std::size_t i (0); i != n; ++i)"
- << "{"
- << "unsigned long long r (st.result (i));" // Sets current in mex.
- << endl
- << "if (mex[i] != 0)" << endl // Pending exception from result().
- << "continue;"
- << endl
- << "if (r != 1)"
- << "{"
- << "mex.insert (i," << endl
- //@@ assumption: result_unknown
- << "(r == delete_statement::result_unknown)," << endl
- << "object_not_persistent ());"
- << "continue;"
- << "}"
- << "if (mex.fatal ())" << endl // Don't do any extra work.
- << "continue;"
- << "pointer_cache_traits::erase (db, *ids[i]);"
- << "}" // for
- << "return n;"
- << "}"; // erase()
- }
-
- // erase (object)
- //
- bool erase_smart_containers (has_a (c, test_smart_container));
-
- if (id != 0 && (poly || opt != 0 || erase_smart_containers))
- {
- os << "void " << traits << "::" << endl
- << "erase (database& db, const object_type& obj";
-
- if (poly)
- os << ", bool top, bool dyn";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (db);";
-
- if (poly)
- os << "ODB_POTENTIALLY_UNUSED (top);";
-
- os << endl;
-
- if (poly)
- os << "if (dyn)" << endl
- << "{"
- << "const std::type_info& t (typeid (obj));"
- << endl
- << "if (t != info.type)"
- << "{"
- << "const info_type& pi (root_traits::map->find (t));"
- << "pi.dispatch (info_type::call_erase, db, &obj, 0);"
- << "return;"
- << "}"
- << "}";
-
- // If we are database-poly-abstract but not C++-abstract, then make
- // sure we are not trying to erase an instance of an abstract class.
- //
- if (abst && !c.abstract ())
- os << "if (top)" << endl
- << "throw abstract_class ();"
- << endl;
-
- if (opt != 0 || erase_smart_containers)
- {
- string rsts (poly_derived ? "rsts" : "sts");
-
- os << "using namespace " << db << ";"
- << endl
- << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());";
-
- if (poly_derived)
- os << endl
- << "root_statements_type& rsts (sts.root_statements ());"
- << "ODB_POTENTIALLY_UNUSED (rsts);";
-
- os << endl;
-
- // Call callback (pre_erase).
- //
- if (!abst) // If we are poly-abstract, then top will always be false.
- {
- if (poly)
- os << "if (top)" << endl;
-
- os << "callback (db, obj, callback_event::pre_erase);"
- << endl;
- }
-
- if (!abst || erase_containers)
- {
- os << "const id_type& id (object_traits_impl::id (obj));"
- << endl;
- }
-
- // Smart containers case.
- //
- if (opt == 0)
- {
- // Initialize id image.
- //
- if (!abst) // If we are poly-abstract, then top will always be false.
- {
- if (poly)
- os << "if (top)"
- << "{";
-
- os << "id_image_type& i (" << rsts << ".id_image ());"
- << "init (i, id);"
- << endl;
-
- os << "binding& idb (" << rsts << ".id_image_binding ());"
- << "if (i.version != " << rsts << ".id_image_version () || " <<
- "idb.version == 0)"
- << "{"
- << "bind (idb.bind, i);"
- << rsts << ".id_image_version (i.version);"
- << "idb.version++;"
- // Cannot be optimistic.
- << "}";
-
- if (poly)
- os << "}";
- }
-
- // Erase containers first so that there are no reference
- // violations (we don't want to rely on ON DELETE CASCADE
- // here since in case of a custom schema, it might not be
- // there).
- //
-
- if (erase_containers)
- {
- os << "extra_statement_cache_type& esc (" <<
- "sts.extra_statement_cache ());";
-
- if (erase_versioned_containers)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));";
-
- os << endl;
-
- instance<container_calls> t (container_calls::erase_obj_call);
- t->traverse (c);
- }
-
- os << "if (sts.erase_statement ().execute () != 1)" << endl
- << "throw object_not_persistent ();"
- << endl;
- }
- // Optimistic case.
- //
- else
- {
- // Initialize id + managed column image.
- //
- if (!abst) // If we are poly-abstract, then top will always be false.
- {
- if (poly)
- os << "if (top)"
- << "{";
-
- os << "const version_type& v (version (obj));"
- << "id_image_type& i (" << rsts << ".id_image ());"
- << "init (i, id, &v);"
- << endl;
-
- os << "binding& idb (" << rsts << ".id_image_binding ());"
- << "if (i.version != " << rsts << ".id_image_version () ||" << endl
- << "idb.version == 0)"
- << "{"
- << "bind (idb.bind, i);"
- << rsts << ".id_image_version (i.version);"
- << "idb.version++;"
- << rsts << ".optimistic_id_image_binding ().version++;"
- << "}";
-
- if (poly)
- os << "}"; // if (top)
- }
-
- // If this is a derived type in a polymorphic hierarchy, then we
- // need to check the version (stored in root) before we go ahead
- // and start deleting things. Also use the same code for root with
- // containers since it is more efficient than the find_() method
- // below.
- //
- bool svm (false);
- if (poly_derived || (poly && erase_containers))
- {
- // Only do the check in the top-level call.
- //
- if (!abst) // If we are poly-abstract, then top will always be false.
- {
- os << "if (top)"
- << "{"
- << "version_type v;"
- << "root_traits::discriminator_ (" << rsts << ", id, 0, &v);"
- << endl;
-
- os << "if (v != version (obj))" << endl
- << "throw object_changed ();"
- << "}";
- }
- }
- else if (erase_containers)
- {
- // Things get complicated here: we don't want to trash the
- // containers and then find out that the versions don't match
- // and we therefore cannot delete the object. After all, there
- // is no guarantee that the user will abort the transaction.
- // In fact, a perfectly reasonable scenario is to reload the
- // object, re-check the condition, decide not to delete the
- // object, and then commit the transaction.
- //
- // There doesn't seem to be anything better than first making
- // sure we can delete the object, then deleting the container
- // data, and then deleting the object. To check that we can
- // delete the object we are going to use find_() and then
- // compare the versions. A special-purpose SELECT query would
- // have been more efficient but it would complicated and bloat
- // things significantly.
- //
-
- if (versioned)
- {
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));"
- << endl;
- svm = true;
- }
-
- os << "if (!find_ (sts, &id" <<
- (versioned ? ", svm" : "") << "))" << endl
- << "throw object_changed ();"
- << endl;
-
- if (delay_freeing_statement_result)
- os << "sts.find_statement ().free_result ();"
- << endl;
-
- os << "if (version (sts.image ()) != version (obj))" << endl
- << "throw object_changed ();"
- << endl;
- }
-
- // Erase containers first so that there are no reference
- // violations (we don't want to rely on ON DELETE CASCADE
- // here since in case of a custom schema, it might not be
- // there).
- //
- if (erase_containers)
- {
- os << "extra_statement_cache_type& esc (" <<
- "sts.extra_statement_cache ());";
-
- if (erase_versioned_containers && !svm)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));";
-
- os << endl;
-
- instance<container_calls> t (container_calls::erase_obj_call);
- t->traverse (c);
- }
-
- const char* st (
- poly_derived ? "erase_statement" : "optimistic_erase_statement");
-
- os << "if (sts." << st << " ().execute () != 1)" << endl
- << "throw object_changed ();"
- << endl;
- }
-
- if (poly_derived)
- {
- // Call our base last (we erase polymorphic objects from base
- // to derived in order not to trigger cascading deletes).
- //
- os << "base_traits::erase (db, obj, false, false);"
- << endl;
- }
-
- if (!abst) // If we are poly-abstract, then top will always be false.
- {
- if (poly)
- os << "if (top)"
- << "{";
-
- // Note that we don't reset sections since the object is now
- // transient and the state of a section in a transient object
- // is undefined.
-
- // Remove from the object cache.
- //
- os << "pointer_cache_traits::erase (db, id);";
-
- // Call callback (post_erase).
- //
- os << "callback (db, obj, callback_event::post_erase);";
-
- if (poly)
- os << "}";
- }
- }
- else
- {
- os << "callback (db, obj, callback_event::pre_erase);"
- << "erase (db, id (obj), true, false);"
- << "callback (db, obj, callback_event::post_erase);";
- }
-
- os << "}";
- }
-
- // erase (object) bulk
- //
- if (id != 0 && c.count ("bulk-erase"))
- {
- os << "void " << traits << "::" << endl
- << "erase (database& db," << endl
- << "const object_type** objs," << endl
- << "std::size_t n," << endl
- << "multiple_exceptions& mex)"
- << "{";
-
- // In non-optimistic case delegate to erase(id).
- //
- if (opt == 0)
- {
- os << "id_type a[batch];"
- << "const id_type* p[batch];"
- << endl
- << "for (std::size_t i (0); i != n; ++i)"
- << "{"
- << "const object_type& obj (*objs[i]);"
- << "callback (db, obj, callback_event::pre_erase);"
- << "a[i] = id (obj);"
- << "p[i] = a + i;"
- << "}"
- << "n = erase (db, p, n, mex);"
- << endl
- << "if (mex.fatal ())" << endl // Don't do any extra work.
- << "return;"
- << endl
- << "for (std::size_t i (0); i != n; ++i)"
- << "{"
- << "if (mex[i] == 0)" << endl // No pending exception.
- << "callback (db, *objs[i], callback_event::post_erase);"
- << "}"; // for
- }
- else
- {
- os << "using namespace " << db << ";"
- << endl
- << "ODB_POTENTIALLY_UNUSED (db);"
- << endl
- << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());"
- << endl
- << "for (std::size_t i (0); i != n; ++i)"
- << "{"
- << "const object_type& obj (*objs[i]);"
- << "callback (db, obj, callback_event::pre_erase);"
- << "const version_type& v (version (obj));"
- << "init (sts.id_image (i), id (obj), &v);"
- << "}";
-
- os << "binding& idb (sts.id_image_binding ());"
- //@@ assumption: generate_grow false
- << "if (idb.version == 0)"
- << "{"
- << "bind (idb.bind, sts.id_image ());"
- << "idb.version++;"
- << "sts.optimistic_id_image_binding ().version++;"
- << "}"
- << "delete_statement& st (sts.optimistic_erase_statement ());"
- << "n = st.execute (n, mex);" // Set to actual number of attempted.
- << endl
- << "for (std::size_t i (0); i != n; ++i)"
- << "{"
- << "unsigned long long r (st.result (i));" // Sets current in mex.
- << endl
- << "if (mex[i] != 0)" << endl // Pending exception from result().
- << "continue;"
- << endl
- << "if (r != 1)"
- << "{"
- << "mex.insert (i," << endl
- //@@ assumption: result_unknown
- << "(r == delete_statement::result_unknown)," << endl
- << "object_changed ());"
- << "continue;"
- << "}"
- << "if (mex.fatal ())" << endl // Don't do any extra work.
- << "continue;"
- << endl
- << "const object_type& obj (*objs[i]);"
- << "pointer_cache_traits::erase (db, id (obj));"
- << "callback (db, obj, callback_event::post_erase);"
- << "}"; // for
- }
-
- os << "}"; // erase()
- }
-
- // find (id)
- //
- if (id != 0 && c.default_ctor ())
- {
- string rsts (poly_derived ? "rsts" : "sts");
-
- os << traits << "::pointer_type" << endl
- << traits << "::" << endl
- << "find (database& db, const id_type& id)"
- << "{"
- << "using namespace " << db << ";"
- << endl;
-
- // First check the session.
- //
- os << "{";
-
- if (poly_derived)
- os << "root_traits::pointer_type rp (pointer_cache_traits::find (" <<
- "db, id));"
- << endl
- << "if (!root_traits::pointer_traits::null_ptr (rp))" << endl
- << "return" << endl
- << " root_traits::pointer_traits::dynamic_pointer_cast<" <<
- "object_type> (rp);";
- else
- os << "pointer_type p (pointer_cache_traits::find (db, id));"
- << endl
- << "if (!pointer_traits::null_ptr (p))" << endl
- << "return p;";
-
- os << "}";
-
- os << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());";
-
- if (versioned)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));";
-
- if (poly_derived)
- os << "root_statements_type& rsts (sts.root_statements ());";
-
- os << endl
- << "statements_type::auto_lock l (" << rsts << ");";
-
- if (delay_freeing_statement_result)
- os << "auto_result ar;";
-
- if (poly)
- os << "root_traits::discriminator_type d;";
-
- os << endl
- << "if (l.locked ())"
- << "{"
- << "if (!find_ (sts, &id" << (versioned ? ", svm" : "") << "))" << endl
- << "return pointer_type ();";
-
- if (delay_freeing_statement_result)
- os << endl
- << "ar.set (sts.find_statement (" << (poly_derived ? "depth" : "") <<
- "));";
-
- if (poly)
- os << "d = root_traits::discriminator (" << rsts << ".image ());";
-
- os << "}";
-
- if (poly)
- {
- // If statements are locked, then get the discriminator by
- // executing a special SELECT statement. We need it to be
- // able to create an object of the correct dynamic type
- // which will be loaded later.
- //
- os << "else" << endl
- << "root_traits::discriminator_ (" << rsts << ", id, &d);"
- << endl;
-
- if (abst)
- os << "const info_type& pi (root_traits::map->find (d));"
- << endl;
- else
- os << "const info_type& pi (" << endl
- << "d == info.discriminator ? info : root_traits::map->find (d));"
- << endl;
- }
-
- // Create the object.
- //
- if (poly_derived)
- {
- os << "root_traits::pointer_type rp (pi.create ());"
- << "pointer_type p (" << endl
- << "root_traits::pointer_traits::static_pointer_cast<object_type> " <<
- "(rp));"
- << "pointer_traits::guard pg (p);"
- << endl;
-
- // Insert it as a root pointer (for non-unique pointers, rp should
- // still be valid and for unique pointers this is a no-op).
- //
- os << "pointer_cache_traits::insert_guard ig (" << endl
- << "pointer_cache_traits::insert (db, id, rp));"
- << endl;
- }
- else
- {
- if (poly)
- os << "pointer_type p (pi.create ());";
- else
- os << "pointer_type p (" << endl
- << "access::object_factory<object_type, pointer_type>::create ());";
-
- os << "pointer_traits::guard pg (p);"
- << endl;
-
- os << "pointer_cache_traits::insert_guard ig (" << endl
- << "pointer_cache_traits::insert (db, id, p));"
- << endl;
- }
-
- os << "object_type& obj (pointer_traits::get_ref (p));"
- << endl
- << "if (l.locked ())"
- << "{"
- << "select_statement& st (sts.find_statement (" <<
- (poly_derived ? "depth" : "") << "));"
- << "ODB_POTENTIALLY_UNUSED (st);"
- << endl;
-
- if (poly)
- os << "callback_event ce (callback_event::pre_load);"
- << "pi.dispatch (info_type::call_callback, db, &obj, &ce);";
- else
- os << "callback (db, obj, callback_event::pre_load);";
-
- os << "init (obj, sts.image (), &db" << (versioned ? ", svm" : "") << ");";
-
- init_value_extra ();
-
- if (delay_freeing_statement_result)
- os << "ar.free ();";
-
- os << "load_ (sts, obj, false" << (versioned ? ", svm" : "") << ");";
-
- if (poly)
- // Load the dynamic part of the object unless static and dynamic
- // types are the same.
- //
- os << endl
- << "if (&pi != &info)"
- << "{"
- << "std::size_t d (depth);"
- << "pi.dispatch (info_type::call_load, db, &obj, &d);"
- << "}";
-
- os << rsts << ".load_delayed (" << (versioned ? "&svm" : "0") << ");"
- << "l.unlock ();";
-
- if (poly)
- os << "ce = callback_event::post_load;"
- << "pi.dispatch (info_type::call_callback, db, &obj, &ce);";
- else
- os << "callback (db, obj, callback_event::post_load);";
-
- os << "pointer_cache_traits::load (ig.position ());"
- << "}"
- << "else" << endl
- << rsts << ".delay_load (id, obj, ig.position ()" <<
- (poly ? ", pi.delayed_loader" : "") << ");"
- << endl;
-
- os << "ig.release ();"
- << "pg.release ();"
- << "return p;"
- << "}";
- }
-
- // find (id, obj)
- //
- if (id != 0)
- {
- string rsts (poly_derived ? "rsts" : "sts");
-
- os << "bool " << traits << "::" << endl
- << "find (database& db, const id_type& id, object_type& obj";
-
- if (poly)
- os << ", bool dyn";
-
- os << ")"
- << "{";
-
- if (poly)
- os << "ODB_POTENTIALLY_UNUSED (dyn);"
- << endl;
-
- os << "using namespace " << db << ";"
- << endl;
-
- if (poly)
- {
- if (!abst)
- os << "if (dyn)" << endl
- << "{";
-
- os << "const std::type_info& t (typeid (obj));";
-
- if (!abst)
- os << endl
- << "if (t != info.type)"
- << "{";
-
- os << "const info_type& pi (root_traits::map->find (t));"
- << "return pi.dispatch (info_type::call_find, db, &obj, &id);";
-
- if (!abst)
- os << "}"
- << "}";
- }
-
- if (!abst)
- {
- os << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());";
-
- if (versioned)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));";
-
- if (poly_derived)
- os << "root_statements_type& rsts (sts.root_statements ());";
-
- // This can only be top-level call so auto_lock must succeed.
- //
- os << endl
- << "statements_type::auto_lock l (" << rsts << ");"
- << "assert (l.locked ()) /* Must be a top-level call. */;"
- << endl;
-
- os << "if (!find_ (sts, &id" <<
- (versioned ? ", svm" : "") << "))" << endl
- << "return false;"
- << endl;
-
- os << "select_statement& st (sts.find_statement (" <<
- (poly_derived ? "depth" : "") << "));"
- << "ODB_POTENTIALLY_UNUSED (st);"
- << endl;
-
- if (delay_freeing_statement_result)
- os << "auto_result ar (st);";
-
- os << "reference_cache_traits::position_type pos (" << endl
- << "reference_cache_traits::insert (db, id, obj));"
- << "reference_cache_traits::insert_guard ig (pos);"
- << endl
- << "callback (db, obj, callback_event::pre_load);"
- << "init (obj, sts.image (), &db" <<
- (versioned ? ", svm" : "") << ");";
-
- init_value_extra ();
-
- if (delay_freeing_statement_result)
- os << "ar.free ();";
-
- os << "load_ (sts, obj, false" << (versioned ? ", svm" : "") << ");"
- << rsts << ".load_delayed (" << (versioned ? "&svm" : "0") << ");"
- << "l.unlock ();"
- << "callback (db, obj, callback_event::post_load);"
- << "reference_cache_traits::load (pos);"
- << "ig.release ();"
- << "return true;";
- }
-
- os << "}";
- }
-
- // reload ()
- //
- if (id != 0)
- {
- string rsts (poly_derived ? "rsts" : "sts");
-
- // This implementation is almost exactly the same as find(id, obj)
- // above except that it doesn't interract with the object cache and,
- // in case of optimistic concurrency, checks if the object actually
- // needs to be reloaded.
- //
- os << "bool " << traits << "::" << endl
- << "reload (database& db, object_type& obj";
-
- if (poly)
- os << ", bool dyn";
-
- os << ")"
- << "{";
-
- if (poly)
- os << "ODB_POTENTIALLY_UNUSED (dyn);"
- << endl;
-
- os << "using namespace " << db << ";"
- << endl;
-
- if (poly)
- {
- if (!abst)
- os << "if (dyn)" << endl
- << "{";
-
- os << "const std::type_info& t (typeid (obj));";
-
- if (!abst)
- os << endl
- << "if (t != info.type)"
- << "{";
-
- os << "const info_type& pi (root_traits::map->find (t));"
- << "return pi.dispatch (info_type::call_reload, db, &obj, 0);";
-
- if (!abst)
- os << "}"
- << "}";
- }
-
- if (!abst)
- {
- os << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());";
-
- if (versioned)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));";
-
- if (poly_derived)
- os << "root_statements_type& rsts (sts.root_statements ());";
-
- // This can only be top-level call so auto_lock must succeed.
- //
- os << endl
- << "statements_type::auto_lock l (" << rsts << ");"
- << "assert (l.locked ()) /* Must be a top-level call. */;"
- << endl;
-
- os << "const id_type& id (object_traits_impl::id (obj));"
- << "if (!find_ (sts, &id" <<
- (versioned ? ", svm" : "") << "))" << endl
- << "return false;"
- << endl;
-
- os << "select_statement& st (sts.find_statement (" <<
- (poly_derived ? "depth" : "") << "));"
- << "ODB_POTENTIALLY_UNUSED (st);"
- << endl;
-
- if (delay_freeing_statement_result)
- os << "auto_result ar (st);"
- << endl;
-
- if (opt != 0)
- {
- const char* tr (poly_derived ? "root_traits::" : "");
-
- os << "if (" << tr << "version (" << rsts << ".image ()) == " <<
- tr << "version (obj))" << endl
- << "return true;"
- << endl;
- }
-
- os << "callback (db, obj, callback_event::pre_load);"
- << "init (obj, sts.image (), &db" <<
- (versioned ? ", svm" : "") << ");";
-
- init_value_extra ();
-
- if (delay_freeing_statement_result)
- os << "ar.free ();";
-
- os << "load_ (sts, obj, true" << (versioned ? ", svm" : "") << ");"
- << rsts << ".load_delayed (" << (versioned ? "&svm" : "0") << ");"
- << "l.unlock ();"
- << "callback (db, obj, callback_event::post_load);"
- << "return true;";
- }
-
- os << "}";
- }
-
- // load (section) [non-thunk version]
- //
- if (uss.count (user_sections::count_new |
- user_sections::count_load |
- (poly ? user_sections::count_load_empty : 0)) != 0)
- {
- string rsts (poly_derived ? "rsts" : "sts");
-
- os << "bool " << traits << "::" << endl
- << "load (connection& conn, object_type& obj, section& s" <<
- (poly ? ", const info_type* pi" : "") << ")"
- << "{"
- << "using namespace " << db << ";"
- << endl;
-
- if (poly)
- {
- // Resolve type information if we are doing indirect calls.
- //
- os << "bool top (pi == 0);" // Top-level call.
- << "if (top)"
- << "{"
- << "const std::type_info& t (typeid (obj));";
-
- if (!abst)
- os << endl
- << "if (t == info.type)" << endl
- << "pi = &info;"
- << "else" << endl;
-
- os << "pi = &root_traits::map->find (t);"
- << "}";
- }
-
- // Lock the statements for the object itself. We need to do this since we
- // are using the id image and loading of object pointers can overwrite it.
- //
- os << db << "::connection& c (static_cast<" << db <<
- "::connection&> (conn));"
- << "statements_type& sts (c.statement_cache ()." <<
- "find_object<object_type> ());";
-
- if (versioned)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));";
-
- if (poly_derived)
- os << "root_statements_type& rsts (sts.root_statements ());";
-
- os << endl
- << "statements_type::auto_lock l (" << rsts << ");";
-
- // It this is a top-level call, then auto_lock must succeed.
- //
- if (poly)
- os << "if (top)" << endl;
-
- os << "assert (l.locked ()) /* Must be a top-level call. */;"
- << endl;
-
- os << "bool r (false);"
- << endl;
-
- // If our poly-base has load sections, then call the base version
- // first. Besides checking for sections, it will also initialize
- // the id image.
- //
- if (poly_derived &&
- buss->count (user_sections::count_total |
- user_sections::count_load |
- user_sections::count_load_empty) != 0)
- {
- os << "if (base_traits::load (conn, obj, s, pi))" << endl
- << "r = true;"
- << endl;
- }
- else
- {
- // Initialize id image (all this is equivalent to using rsts).
- //
- os << "id_image_type& i (sts.id_image ());"
- << "init (i, id (obj));"
- << endl;
-
- os << "binding& idb (sts.id_image_binding ());"
- << "if (i.version != sts.id_image_version () || idb.version == 0)"
- << "{"
- << "bind (idb.bind, i);"
- << "sts.id_image_version (i.version);"
- << "idb.version++;";
- if (opt != 0)
- os << "sts.optimistic_id_image_binding ().version++;";
- os << "}";
- }
-
- // Resolve extra statements if we are doing direct calls.
- //
- if (!poly)
- os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
- << endl;
-
- // Dispatch.
- //
- for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
- {
- // Skip special sections.
- //
- if (i->special == user_section::special_version)
- continue;
-
- if (i->load == user_section::load_eager)
- continue;
-
- // Overridden sections are handled by the base.
- //
- if (poly_derived && i->base != 0)
- continue;
-
- data_member& m (*i->member);
-
- // Section access is always by reference.
- //
- member_access& ma (m.get<member_access> ("get"));
- if (!ma.synthesized)
- os << "// From " << location_string (ma.loc, true) << endl;
-
- os << "if (!r && &s == &" << ma.translate ("obj") << ")"
- << "{";
-
- if (!poly)
- os << public_name (m) << "_traits::load (esc, obj);";
- else
- {
- // If this is an empty section, then there may not be any
- // overrides.
- //
- os << "info_type::section_load sl (" <<
- "pi->find_section_load (" << i->index << "UL));";
-
- if (i->load_empty ())
- os << "if (sl != 0)" << endl;
-
- os << "sl (conn, obj, true);";
- }
-
- os << "r = true;"
- << "}";
- }
-
- if (poly)
- os << "if (top)"
- << "{";
-
- os << rsts << ".load_delayed (" << (versioned ? "&svm" : "0") << ");"
- << "l.unlock ();";
-
- if (poly)
- os << "}";
-
- os << "return r;"
- << "}";
- }
-
- // update (section) [non-thunk version]
- //
- if (uss.count (user_sections::count_new |
- user_sections::count_update |
- (poly ? user_sections::count_update_empty : 0)) != 0)
- {
- os << "bool " << traits << "::" << endl
- << "update (connection& conn, const object_type& obj, " <<
- "const section& s" << (poly ? ", const info_type* pi" : "") << ")"
- << "{"
- << "using namespace " << db << ";"
- << endl;
-
- if (poly)
- {
- // Resolve type information if we are doing indirect calls.
- //
- os << "if (pi == 0)" // Top-level call.
- << "{"
- << "const std::type_info& t (typeid (obj));";
-
- if (!abst)
- os << endl
- << "if (t == info.type)" << endl
- << "pi = &info;"
- << "else" << endl;
-
- os << "pi = &root_traits::map->find (t);"
- << "}";
- }
-
- // If our poly-base has update sections, then call the base version
- // first. Besides checking for sections, it will also initialize the
- // id image.
- //
- if (poly_derived &&
- buss->count (user_sections::count_total |
- user_sections::count_update |
- user_sections::count_update_empty) != 0)
- {
- os << "if (base_traits::update (conn, obj, s, pi))" << endl
- << "return true;"
- << endl;
- }
- else
- {
- // Resolve extra statements if we are doing direct calls.
- //
- os << db << "::connection& c (static_cast<" << db <<
- "::connection&> (conn));"
- << "statements_type& sts (c.statement_cache ()." <<
- "find_object<object_type> ());";
-
- if (!poly)
- os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());";
-
- os << endl;
-
- // Initialize id image. This is not necessarily the root of the
- // polymorphic hierarchy.
- //
- if (opt != 0)
- os << "const version_type& v (version (obj));";
-
- os << "id_image_type& i (sts.id_image ());";
-
- os << "init (i, id (obj)" << (opt != 0 ? ", &v" : "") << ");"
- << endl;
-
- os << "binding& idb (sts.id_image_binding ());"
- << "if (i.version != sts.id_image_version () || idb.version == 0)"
- << "{"
- << "bind (idb.bind, i);"
- << "sts.id_image_version (i.version);"
- << "idb.version++;";
- if (opt != 0)
- os << "sts.optimistic_id_image_binding ().version++;";
- os << "}";
- }
-
- // Dispatch.
- //
- bool e (false);
- for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
- {
- // Skip special sections.
- //
- if (i->special == user_section::special_version)
- continue;
-
- // Overridden sections are handled by the base.
- //
- if (poly_derived && i->base != 0)
- continue;
-
- // Skip read-only sections. Polymorphic sections are always
- // (potentially) read-write.
- //
- if (!poly && i->update_empty ())
- continue;
-
- data_member& m (*i->member);
-
- // Section access is always by reference.
- //
- member_access& ma (m.get<member_access> ("get"));
- if (!ma.synthesized)
- os << "// From " << location_string (ma.loc, true) << endl;
-
- if (e)
- os << "else ";
- else
- e = true;
-
- os << "if (&s == &" << ma.translate ("obj") << ")";
-
- if (!poly)
- os << public_name (m) << "_traits::update (esc, obj);";
- else
- {
- // If this is a readonly section, then there may not be any
- // overrides.
- //
- os << "{"
- << "info_type::section_update su (" <<
- "pi->find_section_update (" << i->index << "UL));";
-
- if (i->update_empty ())
- {
- // For optimistic section, also check that we are not the
- // final override, since otherwise we will increment the
- // version without updating anything.
- //
- if (i->optimistic ())
- os << "if (su != 0 && su != info.sections->functions[" <<
- i->index << "UL].update)" << endl;
- else
- os << "if (su != 0)" << endl;
- }
-
- os << "su (conn, obj);"
- << "}";
- }
- }
-
- os << "else" << endl
- << "return false;"
- << endl
- << "return true;"
- << "}";
- }
-
- // find_ ()
- //
- if (id != 0)
- {
- os << "bool " << traits << "::" << endl
- << "find_ (statements_type& sts," << endl
- << "const id_type* id";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- if (poly_derived && !abst)
- os << "," << endl
- << "std::size_t d";
-
- os << ")"
- << "{"
- << "using namespace " << db << ";"
- << endl;
-
- // Initialize id image.
- //
- if (poly_derived && !abst)
- os << "if (d == depth)"
- << "{";
-
- os << "id_image_type& i (sts.id_image ());"
- << "init (i, *id);"
- << endl;
-
- os << "binding& idb (sts.id_image_binding ());"
- << "if (i.version != sts.id_image_version () || idb.version == 0)"
- << "{"
- << "bind (idb.bind, i);"
- << "sts.id_image_version (i.version);"
- << "idb.version++;";
- if (opt != 0)
- os << "sts.optimistic_id_image_binding ().version++;";
- os << "}";
-
- if (poly_derived && !abst)
- os << "}";
-
- // Rebind data image.
- //
- os << "image_type& im (sts.image ());"
- << "binding& imb (sts.select_image_binding (" <<
- (poly_derived ? (abst ? "depth" : "d") : "") << "));"
- << endl;
-
- if (poly_derived)
- {
- os << "if (imb.version == 0 ||" << endl
- << "check_version (sts.select_image_versions (), im))"
- << "{"
- << "bind (imb.bind, 0, 0, im, statement_select" <<
- (versioned ? ", svm" : "") << ");"
- << "update_version (sts.select_image_versions ()," << endl
- << "im," << endl
- << "sts.select_image_bindings ());"
- << "}";
- }
- else
- {
- os << "if (im.version != sts.select_image_version () ||" << endl
- << "imb.version == 0)"
- << "{"
- << "bind (imb.bind, im, statement_select" <<
- (versioned ? ", svm" : "") << ");"
- << "sts.select_image_version (im.version);"
- << "imb.version++;"
- << "}";
- }
-
- os << "select_statement& st (sts.find_statement (" <<
- (poly_derived ? (abst ? "depth" : "d") : "") << "));"
- << endl;
-
- // The dynamic loader SELECT statement can be dynamically empty.
- //
- if (versioned && poly_derived && !abst)
- os << "if (st.empty ())" << endl
- << "return true;"
- << endl;
-
- os << "st.execute ();"
- << "auto_result ar (st);"
- << "select_statement::result r (st.fetch ());"
- << endl;
-
- if (grow)
- {
- os << "if (r == select_statement::truncated)"
- << "{"
- << "if (grow (im, sts.select_image_truncated ()" <<
- (versioned ? ", svm" : "") <<
- (poly_derived ? (abst ? ", depth" : ", d") : "") << "))" << endl
- << "im.version++;"
- << endl;
-
- if (poly_derived)
- {
- os << "if (check_version (sts.select_image_versions (), im))"
- << "{"
- << "bind (imb.bind, 0, 0, im, statement_select" <<
- (versioned ? ", svm" : "") << ");"
- << "update_version (sts.select_image_versions ()," << endl
- << "im," << endl
- << "sts.select_image_bindings ());"
- << "st.refetch ();"
- << "}";
- }
- else
- {
- os << "if (im.version != sts.select_image_version ())"
- << "{"
- << "bind (imb.bind, im, statement_select" <<
- (versioned ? ", svm" : "") << ");"
- << "sts.select_image_version (im.version);"
- << "imb.version++;"
- << "st.refetch ();"
- << "}";
- }
-
- os << "}";
- }
-
- // If we are delaying, only free the result if it is empty.
- //
- if (delay_freeing_statement_result)
- os << "if (r != select_statement::no_data)"
- << "{"
- << "ar.release ();"
- << "return true;"
- << "}"
- << "else" << endl
- << "return false;";
- else
- os << "return r != select_statement::no_data;";
-
- os << "}";
- }
-
- // load_()
- //
- // Load containers, reset/reload sections.
- //
- size_t load_containers (
- has_a (c, test_container | include_eager_load, &main_section));
-
- if (poly_derived ||
- load_containers ||
- uss.count (user_sections::count_new |
- user_sections::count_load |
- (poly ? user_sections::count_load_empty : 0)) != 0)
- {
- bool load_versioned_containers (
- load_containers >
- has_a (c,
- test_container | include_eager_load |
- exclude_deleted | exclude_added | exclude_versioned,
- &main_section));
-
- os << "void " << traits << "::" << endl
- << "load_ (statements_type& sts," << endl
- << "object_type& obj," << endl
- << "bool reload";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- if (poly_derived)
- os << "," << endl
- << "std::size_t d";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (reload);";
- if (versioned)
- os << "ODB_POTENTIALLY_UNUSED (svm);";
- os << endl;
-
- if (poly_derived)
- os << "if (--d != 0)" << endl
- << "base_traits::load_ (sts.base_statements (), obj, reload" <<
- (context::versioned (*poly_base) ? ", svm" : "") <<
- (poly_base != poly_root ? ", d" : "") << ");"
- << endl;
-
- if (load_containers ||
- (!poly && uss.count (user_sections::count_new |
- user_sections::count_load) != 0))
- os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
- << endl;
-
- if (!versioned && (
- load_versioned_containers ||
- uss.count (user_sections::count_new |
- user_sections::count_all |
- user_sections::count_versioned_only) != 0))
- {
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));"
- << endl;
- }
-
- if (load_containers)
- {
- instance<container_calls> t (container_calls::load_call, &main_section);
- t->traverse (c);
- }
-
- for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
- {
- // Skip special sections.
- //
- if (i->special == user_section::special_version)
- continue;
-
- // Skip overridden sections; they are handled by the base.
- //
- if (i->base != 0 && poly_derived)
- continue;
-
- data_member& m (*i->member);
-
- // If the section is soft- added or deleted, check the version.
- //
- unsigned long long av (added (m));
- unsigned long long dv (deleted (m));
- if (av != 0 || dv != 0)
- {
- os << "if (";
-
- if (av != 0)
- os << "svm >= schema_version_migration (" << av << "ULL, true)";
-
- if (av != 0 && dv != 0)
- os << " &&" << endl;
-
- if (dv != 0)
- os << "svm <= schema_version_migration (" << dv << "ULL, true)";
-
- os << ")"
- << "{";
- }
-
- // Section access is always by reference.
- //
- member_access& ma (m.get<member_access> ("get"));
- if (!ma.synthesized)
- os << "// From " << location_string (ma.loc, true) << endl;
-
- if (i->load == user_section::load_eager)
- {
- // Mark an eager section as loaded.
- //
- os << ma.translate ("obj") << ".reset (true, false);"
- << endl;
- continue;
- }
-
- os << "if (reload)"
- << "{"
- // Reload sections that have been loaded, clear change state.
- //
- << "if (" << ma.translate ("obj") << ".loaded ())"
- << "{";
-
- if (!poly)
- os << public_name (m) << "_traits::load (esc, obj);";
- else
- {
- os << "info_type::section_load sl (" << endl
- << "root_traits::map->find (typeid (obj)).find_section_load (" <<
- i->index << "UL));";
-
- if (i->load_empty ())
- os << "if (sl != 0)" << endl;
-
- os << "sl (sts.connection (), obj, true);";
- }
-
- os << ma.translate ("obj") << ".reset (true, false);"
- << "}"
- << "}"
- << "else" << endl
- // Reset to unloaded, unchanged state.
- << ma.translate ("obj") << ".reset ();";
-
- if (av != 0 || dv != 0)
- os << "}";
- else
- os << endl;
- }
-
- os << "}";
- }
-
- // load_()
- //
- // Load the dynamic part of the object. We don't need it if we are
- // poly-abstract.
- //
- if (poly_derived && !abst)
- {
- os << "void " << traits << "::" << endl
- << "load_ (database& db, root_type& r, std::size_t d)"
- << "{"
- << "using namespace " << db << ";"
- << endl
- << "object_type& obj (static_cast<object_type&> (r));"
- << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());"
- << endl
- << "d = depth - d;" // Convert to distance from derived.
- << endl;
-
- if (versioned)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));"
- << endl;
-
- // Avoid trying to execute an empty SELECT statement.
- //
- if (empty_depth != 0)
- os << "if (d > " << (poly_depth - empty_depth) << "UL)"
- << "{";
-
- os << "if (!find_ (sts, 0" << (versioned ? ", svm" : "") << ", d))" << endl
- << "throw object_not_persistent ();" // Database inconsistency.
- << endl;
-
- os << "select_statement& st (sts.find_statement (d));"
- << "ODB_POTENTIALLY_UNUSED (st);"
- << endl;
-
- // The resulting SELECT statement may end up being dynamically empty.
- //
- if (versioned)
- os << "if (!st.empty ())"
- << "{";
-
- if (delay_freeing_statement_result)
- os << "auto_result ar (st);";
-
- os << "init (obj, sts.image (), &db" << (versioned ? ", svm" : "") <<
- ", d);";
-
- init_value_extra ();
-
- if (delay_freeing_statement_result)
- os << "ar.free ();";
-
- if (versioned)
- os << "}";
-
- if (empty_depth != 0)
- os << "}";
-
- os << "load_ (sts, obj, false, " << (versioned ? "svm, " : "") << "d);"
- << "}";
- }
-
- // discriminator_ ()
- //
- if (poly && !poly_derived)
- {
- os << "void " << traits << "::" << endl
- << "discriminator_ (statements_type& sts," << endl
- << "const id_type& id," << endl
- << "discriminator_type* pd";
-
- if (opt != 0)
- os << "," << endl
- << "version_type* pv";
-
- os << ")"
- << "{"
- << "using namespace " << db << ";"
- << endl;
-
- // Initialize id image.
- //
- os << "id_image_type& idi (sts.discriminator_id_image ());"
- << "init (idi, id);"
- << endl;
-
- os << "binding& idb (sts.discriminator_id_image_binding ());"
- << "if (idi.version != sts.discriminator_id_image_version () ||" << endl
- << "idb.version == 0)"
- << "{"
- << "bind (idb.bind, idi" << (opt != 0 ? ", false" : "") << ");"
- << "sts.discriminator_id_image_version (idi.version);"
- << "idb.version++;"
- << "}";
-
- // Rebind data image.
- //
- os << "discriminator_image_type& i (sts.discriminator_image ());"
- << "binding& imb (sts.discriminator_image_binding ());"
- << endl
- << "if (i.version != sts.discriminator_image_version () ||" << endl
- << "imb.version == 0)"
- << "{"
- // Generate bind code inline. For now discriminator is simple
- // value so we don't need statement kind (sk).
- //
- << bind_vector << " b (imb.bind);"
- << "std::size_t n (0);"
- << "{";
- bind_discriminator_member_->traverse (*discriminator);
- os << "}";
-
- if (opt != 0)
- {
- os << "n++;" // For now discriminator is a simple value.
- << "{";
- bind_version_member_->traverse (*opt);
- os << "}";
- }
-
- os << "sts.discriminator_image_version (i.version);"
- << "imb.version++;"
- << "}";
-
- os << "{"
- << "select_statement& st (sts.find_discriminator_statement ());"
- << "st.execute ();"
- << "auto_result ar (st);"
- << "select_statement::result r (st.fetch ());"
- << endl
- << "if (r == select_statement::no_data)"
- << "{";
-
- if (opt != 0)
- os << "if (pv != 0)" << endl
- << "throw object_changed ();"
- << "else" << endl;
-
- os << "throw object_not_persistent ();"
- << "}";
-
- if (generate_grow &&
- (context::grow (*discriminator) ||
- (opt != 0 && context::grow (*opt))))
- {
- os << "else if (r == select_statement::truncated)"
- << "{";
-
- // Generate grow code inline.
- //
- os << "bool grew (false);"
- << truncated_vector << " t (sts.discriminator_image_truncated ());"
- << endl;
-
- index_ = 0;
- grow_discriminator_member_->traverse (*discriminator);
-
- if (opt != 0)
- grow_version_member_->traverse (*opt);
-
- os << "if (grew)" << endl
- << "i.version++;"
- << endl;
-
- os << "if (i.version != sts.discriminator_image_version ())"
- << "{"
- // Generate bind code inline. The same code as above.
- //
- << bind_vector << " b (imb.bind);"
- << "std::size_t n (0);"
- << "{";
- bind_discriminator_member_->traverse (*discriminator);
- os << "}";
-
- if (opt != 0)
- {
- os << "n++;" // For now discriminator is a simple value.
- << "{";
- bind_version_member_->traverse (*opt);
- os << "}";
- }
-
- os << "sts.discriminator_image_version (i.version);"
- << "imb.version++;"
- << "st.refetch ();"
- << "}"
- << "}";
- }
-
- // Discriminator cannot be long data (no streaming).
- //
- os << "}";
-
- // Initialize value inline instead of generating a separate
- // init() function. For now discriminator is simple value so
- // we don't need the database (db).
- //
- os << "if (pd != 0)"
- << "{"
- << "discriminator_type& d (*pd);";
- init_named_discriminator_value_member_->traverse (*discriminator);
- os << "}";
-
- if (opt != 0)
- {
- os << "if (pv != 0)"
- << "{"
- << "version_type& v (*pv);";
- init_named_version_value_member_->traverse (*opt);
- os << "}";
- }
-
- os << "}";
- }
-
- if (options.generate_query ())
- {
- char const* result_type;
- if (poly)
- result_type = "polymorphic_object_result_impl<object_type>";
- else if (id != 0)
- result_type = "object_result_impl<object_type>";
- else
- result_type = "no_id_object_result_impl<object_type>";
-
- string sep (versioned || query_optimize ? "\n" : " ");
-
- // Unprepared.
- //
- if (!options.omit_unprepared ())
- {
- // query ()
- //
- os << "result< " << traits << "::object_type >" << endl
- << traits << "::" << endl
- << "query (database&, const query_base_type& q)"
- << "{"
- << "using namespace " << db << ";"
- << "using odb::details::shared;"
- << "using odb::details::shared_ptr;"
- << endl;
-
- os << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << endl
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());";
-
- if (versioned)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));";
-
- os << endl;
-
- // Rebind the image if necessary.
- //
- os << "image_type& im (sts.image ());"
- << "binding& imb (sts.select_image_binding (" <<
- (poly_derived ? "depth" : "") << "));"
- << endl;
-
- if (poly_derived)
- {
- os << "if (imb.version == 0 ||" << endl
- << "check_version (sts.select_image_versions (), im))"
- << "{"
- << "bind (imb.bind, 0, 0, im, statement_select" <<
- (versioned ? ", svm" : "") << ");"
- << "update_version (sts.select_image_versions ()," << endl
- << "im," << endl
- << "sts.select_image_bindings ());"
- << "}";
- }
- else
- {
- os << "if (im.version != sts.select_image_version () ||" << endl
- << "imb.version == 0)"
- << "{"
- << "bind (imb.bind, im, statement_select" <<
- (versioned ? ", svm" : "") << ");"
- << "sts.select_image_version (im.version);"
- << "imb.version++;"
- << "}";
- }
-
- os << "std::string text (query_statement);"
- << "if (!q.empty ())"
- << "{"
- << "text += " << strlit (sep) << ";"
- << "text += q.clause ();"
- << "}";
-
- os << "q.init_parameters ();"
- << "shared_ptr<select_statement> st (" << endl
- << "new (shared) select_statement (" << endl;
- object_query_statement_ctor_args (
- c, "q", versioned || query_optimize, false);
- os << "));" << endl
- << "st->execute ();";
-
- post_query_ (c, true);
-
- os << endl
- << "shared_ptr< odb::" << result_type << " > r (" << endl
- << "new (shared) " << db << "::" << result_type << " (" << endl
- << "q, st, sts, " << (versioned ? "&svm" : "0") << "));"
- << endl
- << "return result<object_type> (r);"
- << "}";
-
- // query(odb::query_base)
- //
- if (multi_dynamic)
- os << "result< " << traits << "::object_type >" << endl
- << traits << "::" << endl
- << "query (database& db, const odb::query_base& q)"
- << "{"
- << "return query (db, query_base_type (q));"
- << "}";
- }
-
- // erase_query
- //
- os << "unsigned long long " << traits << "::" << endl
- << "erase_query (database&, const query_base_type& q)"
- << "{"
- << "using namespace " << db << ";"
- << endl
- << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << endl
- << "std::string text (erase_query_statement);"
- << "if (!q.empty ())"
- << "{"
- << "text += ' ';"
- << "text += q.clause ();"
- << "}"
- << "q.init_parameters ();"
- << "delete_statement st (" << endl;
- object_erase_query_statement_ctor_args (c);
- os << ");"
- << endl
- << "return st.execute ();"
- << "}";
-
- // erase_query(odb::query_base)
- //
- if (multi_dynamic)
- os << "unsigned long long " << traits << "::" << endl
- << "erase_query (database& db, const odb::query_base& q)"
- << "{"
- << "return erase_query (db, query_base_type (q));"
- << "}";
-
- // Prepared. Very similar to unprepared but has some annoying variations
- // that make it difficult to factor out something common.
- //
- if (options.generate_prepared ())
- {
- // prepare_query
- //
- os << "odb::details::shared_ptr<prepared_query_impl>" << endl
- << traits << "::" << endl
- << "prepare_query (connection& c, const char* n, " <<
- "const query_base_type& q)"
- << "{"
- << "using namespace " << db << ";"
- << "using odb::details::shared;"
- << "using odb::details::shared_ptr;"
- << endl;
-
- os << db << "::connection& conn (" << endl
- << "static_cast<" << db << "::connection&> (c));"
- << endl
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());";
-
- if (versioned)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));";
-
- os << endl;
-
- // Rebind the image if necessary.
- //
- os << "image_type& im (sts.image ());"
- << "binding& imb (sts.select_image_binding (" <<
- (poly_derived ? "depth" : "") << "));"
- << endl;
-
- if (poly_derived)
- {
- os << "if (imb.version == 0 ||" << endl
- << "check_version (sts.select_image_versions (), im))"
- << "{"
- << "bind (imb.bind, 0, 0, im, statement_select" <<
- (versioned ? ", svm" : "") << ");"
- << "update_version (sts.select_image_versions ()," << endl
- << "im," << endl
- << "sts.select_image_bindings ());"
- << "}";
- }
- else
- {
- os << "if (im.version != sts.select_image_version () ||" << endl
- << "imb.version == 0)"
- << "{"
- << "bind (imb.bind, im, statement_select" <<
- (versioned ? ", svm" : "") << ");"
- << "sts.select_image_version (im.version);"
- << "imb.version++;"
- << "}";
- }
-
- os << "std::string text (query_statement);"
- << "if (!q.empty ())"
- << "{"
- << "text += " << strlit (sep) << ";"
- << "text += q.clause ();"
- << "}";
-
- os << "shared_ptr<" << db << "::prepared_query_impl> r (" << endl
- << "new (shared) " << db << "::prepared_query_impl (conn));"
- << "r->name = n;"
- << "r->execute = &execute_query;"
- << "r->query = q;"
- << "r->stmt.reset (" << endl
- << "new (shared) select_statement (" << endl;
- object_query_statement_ctor_args (
- c, "r->query", versioned || query_optimize, true);
- os << "));"
- << endl
- << "return r;"
- << "}";
-
- // prepare_query(odb::query_base)
- //
- if (multi_dynamic)
- os << "odb::details::shared_ptr<prepared_query_impl>" << endl
- << traits << "::" << endl
- << "prepare_query (connection& c, const char* n, " <<
- "const odb::query_base& q)"
- << "{"
- << "return prepare_query (c, n, query_base_type (q));"
- << "}";
-
- // execute_query
- //
- os << "odb::details::shared_ptr<result_impl>" << endl
- << traits << "::" << endl
- << "execute_query (prepared_query_impl& q)"
- << "{"
- << "using namespace " << db << ";"
- << "using odb::details::shared;"
- << "using odb::details::shared_ptr;"
- << endl
- << db << "::prepared_query_impl& pq (" << endl
- << "static_cast<" << db << "::prepared_query_impl&> (q));"
- << "shared_ptr<select_statement> st (" << endl
- << "odb::details::inc_ref (" << endl
- << "static_cast<select_statement*> (pq.stmt.get ())));"
- << endl;
-
- os << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << 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 ());"
- << endl
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_object<object_type> ());";
-
- if (versioned)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));";
-
- os << endl;
-
- // Rebind the image if necessary.
- //
- os << "image_type& im (sts.image ());"
- << "binding& imb (sts.select_image_binding (" <<
- (poly_derived ? "depth" : "") << "));"
- << endl;
-
- if (poly_derived)
- {
- os << "if (imb.version == 0 ||" << endl
- << "check_version (sts.select_image_versions (), im))"
- << "{"
- << "bind (imb.bind, 0, 0, im, statement_select" <<
- (versioned ? ", svm" : "") << ");"
- << "update_version (sts.select_image_versions ()," << endl
- << "im," << endl
- << "sts.select_image_bindings ());"
- << "}";
- }
- else
- {
- os << "if (im.version != sts.select_image_version () ||" << endl
- << "imb.version == 0)"
- << "{"
- << "bind (imb.bind, im, statement_select" <<
- (versioned ? ", svm" : "") << ");"
- << "sts.select_image_version (im.version);"
- << "imb.version++;"
- << "}";
- }
-
- os << "pq.query.init_parameters ();"
- << "st->execute ();";
- post_query_ (c, false);
-
- os << endl
- << "return shared_ptr<result_impl> (" << endl
- << "new (shared) " << db << "::" << result_type << " (" << endl
- << "pq.query, st, sts, " << (versioned ? "&svm" : "0") << "));"
- << "}";
- }
- }
-
- // Generate function table registration for dynamic multi-database
- // support.
- //
- if (multi_dynamic)
- {
- string fn (flat_name (type));
- string dt ("access::object_traits_impl< " + type + ", id_common >");
-
- os << "static const" << endl
- << dt << "::" << endl
- << "function_table_type function_table_" << fn << "_ ="
- << "{";
-
- // persist ()
- //
- os << "&" << traits << "::persist";
-
- if (id != 0)
- {
- // find (id)
- //
- if (c.default_ctor ())
- os << "," << endl
- << "&" << traits << "::find";
-
- // find (id, obj)
- //
- os << "," << endl
- << "&" << traits << "::find";
-
- // reload ()
- //
- os << "," << endl
- << "&" << traits << "::reload";
-
- // update ()
- //
- if (!readonly || poly)
- os << "," << endl
- << "&" << traits << "::update";
-
- // erase ()
- //
- os << "," << endl
- << "&" << traits << "::erase";
-
- os << "," << endl
- << "&" << traits << "::erase";
-
- // Sections.
- //
- if (uss.count (user_sections::count_total |
- user_sections::count_load |
- (poly ? user_sections::count_load_empty : 0)) != 0)
- os << "," << endl
- << "&" << traits << "::load";
-
- if (uss.count (user_sections::count_total |
- user_sections::count_update |
- (poly ? user_sections::count_update_empty : 0)) != 0)
- os << "," << endl
- << "&" << traits << "::update";
- }
-
- if (options.generate_query ())
- {
- if (!options.omit_unprepared ())
- os << "," << endl
- << "&" << traits << "::query";
-
- os << "," << endl
- << "&" << traits << "::erase_query";
-
- if (options.generate_prepared ())
- {
- os << "," << endl
- << "&" << traits << "::prepare_query";
-
- os << "," << endl
- << "&" << traits << "::execute_query";
- }
- }
-
- os << "};";
-
- os << "static const object_function_table_entry< " << type << ", " <<
- "id_" << db << " >" << endl
- << "function_table_entry_" << fn << "_ (" << endl
- << "&function_table_" << fn << "_);"
- << endl;
- }
-}
-
-void relational::source::class_::
-traverse_view (type& c)
-{
- view_query& vq (c.get<view_query> ("query"));
-
- // Only process the view query if it is versioned since the query text
- // (e.g., in the native view) must be structured. We also shouldn't try
- // to optimize JOINs (since the objects are JOINed explicitly by the
- // user), unless we are adding poly-base/derived JOINs.
- //
- bool versioned (context::versioned (c));
- bool query_optimize (false);
-
- string const& type (class_fq_name (c));
- string traits ("access::view_traits_impl< " + type + ", id_" +
- db.string () + " >");
-
- size_t columns (column_count (c).total);
-
- // Schema name as a string literal or empty.
- //
- string schema_name (options.schema_name ()[db]);
- if (!schema_name.empty ())
- schema_name = strlit (schema_name);
-
- // Generate the from-list. Also collect relationships via which
- // the objects are joined.
- //
- strings from;
- view_relationship_map rel_map;
-
- if (vq.kind == view_query::condition)
- {
- view_objects& objs (c.get<view_objects> ("objects"));
-
- for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
- {
- bool first (i == objs.begin ());
- string l;
-
- //
- // Tables.
- //
-
- if (i->kind == view_object::table)
- {
- if (first)
- {
- l = "FROM ";
- l += quote_id (i->tbl_name);
-
- if (!i->alias.empty ())
- l += (need_alias_as ? " AS " : " ") + quote_id (i->alias);
-
- l += from_trailer (c);
-
- from.push_back (l);
- continue;
- }
-
- l = join_syntax (*i);
- l += ' ';
- l += quote_id (i->tbl_name);
-
- if (!i->alias.empty ())
- l += (need_alias_as ? " AS " : " ") + quote_id (i->alias);
-
- if (i->join == view_object::cross) // No ON condition for CROSS JOIN.
- {
- from.push_back (l);
- continue;
- }
-
- semantics::scope& scope (
- dynamic_cast<semantics::scope&> (*unit.find (i->scope)));
-
- expression e (
- translate_expression (
- c, i->cond, scope, i->loc, "table"));
-
- if (e.kind != expression::literal)
- {
- error (i->loc) << "invalid join condition in db pragma " <<
- "table" << endl;
- throw operation_failed ();
- }
-
- l += " ON";
-
- // Add the pragma location for easier error tracking.
- //
- from.push_back (l);
- from.push_back ("// From " + location_string (i->loc, true));
- from.push_back (e.value);
- continue;
- }
-
- //
- // Objects.
- //
- semantics::class_& o (*i->obj);
-
- bool poly (polymorphic (o));
- size_t poly_depth (poly ? polymorphic_depth (o) : 1);
-
- string alias (i->alias);
-
- // For polymorphic objects, alias is just a prefix.
- //
- if (poly && !alias.empty ())
- alias += "_" + table_name (o).uname ();
-
- // First object.
- //
- if (first)
- {
- l = "FROM ";
- l += table_qname (o);
-
- if (!alias.empty ())
- l += (need_alias_as ? " AS " : " ") + quote_id (alias);
-
- l += from_trailer (c);
-
- from.push_back (l);
-
- if (poly_depth != 1)
- {
- bool t (true); //@@ (im)perfect forwarding
- size_t d (poly_depth - 1); //@@ (im)perfect forward.
- instance<polymorphic_object_joins> j (o, t, d, i->alias);
- j->traverse (polymorphic_base (o));
-
- from.insert (from.end (), j->begin (), j->end ());
- query_optimize = query_optimize || !j->joins.empty ();
- }
- continue;
- }
-
- semantics::scope& scope (
- dynamic_cast<semantics::scope&> (*unit.find (i->scope)));
-
- expression e (i->join == view_object::cross
- ? expression ("") // Dummy literal expression.
- : translate_expression (
- c, i->cond, scope, i->loc, "object"));
-
- // Literal expression.
- //
- if (e.kind == expression::literal)
- {
- l = join_syntax (*i);
- l += ' ';
- l += table_qname (o);
-
- if (!alias.empty ())
- l += (need_alias_as ? " AS " : " ") + quote_id (alias);
-
- if (i->join == view_object::cross) // No ON condition for CROSS JOIN.
- {
- from.push_back (l);
- continue;
- }
-
- l += " ON";
-
- // Add the pragma location for easier error tracking.
- //
- from.push_back (l);
- from.push_back ("// From " + location_string (i->loc, true));
- from.push_back (e.value);
-
- if (poly_depth != 1)
- {
- bool t (true); //@@ (im)perfect forwarding
- size_t d (poly_depth - 1); //@@ (im)perfect forward.
- instance<polymorphic_object_joins> j (o, t, d, i->alias);
- j->traverse (polymorphic_base (o));
-
- from.insert (from.end (), j->begin (), j->end ());
- query_optimize = query_optimize || !j->joins.empty ();
- }
- continue;
- }
-
- // We have an object relationship (pointer) for which we need
- // to come up with the corresponding JOIN condition. If this
- // is a to-many relationship, then we first need to JOIN the
- // container table. This code is similar to object_joins.
- //
- // Note that this cannot be CROSS JOIN; we've handled that case
- // above.
- //
- using semantics::data_member;
-
- data_member& m (*e.member_path.back ());
- data_member_path* imp (inverse (m));
-
- // Resolve the pointed-to object to view_object and do
- // some sanity checks while at it.
- //
- semantics::class_* c (0);
-
- if (container (m))
- c = object_pointer (container_vt (m));
- else
- c = object_pointer (utype (m));
-
- view_object* vo (0);
-
- // Check if the pointed-to object has been previously associated
- // with this view and is unambiguous. A pointer to ourselves is
- // always assumed to point to this association.
- //
- if (&o == c)
- vo = &*i;
- else
- {
- bool ambig (false);
-
- for (view_objects::iterator j (objs.begin ()); j != i; ++j)
- {
- if (j->obj != c)
- continue;
-
- if (vo == 0)
- {
- vo = &*j;
- continue;
- }
-
- // If it is the first ambiguous object, issue the
- // error.
- //
- if (!ambig)
- {
- error (i->loc) << "pointed-to object '" << class_name (*c) <<
- "' is ambiguous" << endl;
- info (i->loc) << "candidates are:" << endl;
- info (vo->loc) << " '" << vo->name () << "'" << endl;
- ambig = true;
- }
-
- info (j->loc) << " '" << j->name () << "'" << endl;
- }
-
- if (ambig)
- {
- info (i->loc) << "use the other side of the relationship " <<
- "or full join condition clause in db pragma object to " <<
- "resolve this ambiguity" << endl;
- throw operation_failed ();
- }
-
- if (vo == 0)
- {
- error (i->loc) << "pointed-to object '" << class_name (*c) <<
- "' specified in the join condition has not been " <<
- "previously associated with this view" << endl;
- throw operation_failed ();
- }
- }
-
- // JOIN relationship points to us:
- // vo - us
- // e.vo - other side
- // e.member_path - in other side
- //
- // JOIN relationship points to other side:
- // vo - other side
- // e.vo - us
- // e.member_path - in us
- //
- if (imp == 0)
- rel_map.insert (make_pair (e.member_path, make_pair (e.vo, vo)));
- else
- rel_map.insert (make_pair (*imp, make_pair (vo, e.vo)));
-
- // Left and right-hand side table names.
- //
- qname lt;
- {
- using semantics::class_;
-
- class_& o (*e.vo->obj);
- string const& a (e.vo->alias);
-
- if (class_* root = polymorphic (o))
- {
- // If the object is polymorphic, then figure out which of the
- // bases this member comes from and use the corresponding
- // table.
- //
- class_* c (
- &static_cast<class_&> (
- e.member_path.front ()->scope ()));
-
- // If this member's class is not polymorphic (root uses reuse
- // inheritance), then use the root table.
- //
- if (!polymorphic (*c))
- c = root;
-
- qname const& t (table_name (*c));
-
- if (a.empty ())
- lt = t;
- else
- lt = qname (a + "_" + t.uname ());
- }
- else
- lt = a.empty () ? table_name (o) : qname (a);
- }
-
- qname rt;
- {
- qname t (table_name (*vo->obj));
- string const& a (vo->alias);
- rt = a.empty ()
- ? t
- : qname (polymorphic (*vo->obj) ? a + "_" + t.uname () : a);
- }
-
- // First join the container table if necessary.
- //
- semantics::type* cont (container (imp != 0 ? *imp->back () : m));
-
- string ct; // Container table.
- if (cont != 0)
- {
- l = join_syntax (*i);
- l += ' ';
-
- // The same relationship can be used by multiple associated
- // objects. So if the object that contains this container has
- // an alias, then also construct one for the table that we
- // are joining.
- //
- {
- using semantics::class_;
-
- qname t;
-
- // In a polymorphic hierarchy the member can be in a base (see
- // above).
- //
- if (class_* root = polymorphic (imp != 0 ? *vo->obj : *e.vo->obj))
- {
- class_* c;
- if (imp == 0)
- {
- c = &static_cast<class_&> (e.member_path.front ()->scope ());
-
- if (!polymorphic (*c))
- c = root;
-
- t = table_name (*c, e.member_path);
- }
- else
- {
- c = &static_cast<class_&> (imp->front ()->scope ());
-
- if (!polymorphic (*c))
- c = root;
-
- t = table_name (*c, *imp);
- }
- }
- else
- t = imp != 0
- ? table_name (*vo->obj, *imp)
- : table_name (*e.vo->obj, e.member_path);
-
- // The tricky part is to figure out which view_object, vo
- // or e.vo we should use. We want to use the alias from the
- // object that actually contains this container. The following
- // might not make intuitive sense, but it has been verified
- // with the truth table.
- //
- string const& a (imp != 0 ? vo->alias : e.vo->alias);
-
- if (a.empty ())
- {
- ct = quote_id (t);
- l += ct;
- }
- else
- {
- ct = quote_id (a + '_' + t.uname ());
- l += quote_id (t);
- l += (need_alias_as ? " AS " : " ") + ct;
- }
- }
-
- l += " ON";
- from.push_back (l);
-
- // If we are the pointed-to object, then we have to turn
- // things around. This is necessary to have the proper
- // JOIN order. There seems to be a pattern there but it
- // is not yet intuitively clear what it means.
- //
- instance<object_columns_list> c_cols; // Container columns.
- instance<object_columns_list> o_cols; // Object columns.
-
- qname* ot; // Object table (either lt or rt).
-
- if (imp != 0)
- {
- semantics::data_member& imb (*imp->back ());
-
- if (&o == c)
- {
- // container.value = pointer.id
- //
- data_member_path& id (*id_member (*e.vo->obj));
-
- c_cols->traverse (imb, utype (id), "value", "value");
- o_cols->traverse (id);
- ot = &lt;
- }
- else
- {
- // container.id = pointed-to.id
- //
- data_member_path& id (*id_member (*vo->obj));
-
- c_cols->traverse (imb, utype (id), "id", "object_id", vo->obj);
- o_cols->traverse (id);
- ot = &rt;
- }
- }
- else
- {
- if (&o == c)
- {
- // container.id = pointer.id
- //
- data_member_path& id (*id_member (*e.vo->obj));
-
- c_cols->traverse (
- m, utype (id), "id", "object_id", e.vo->obj);
- o_cols->traverse (id);
- ot = &lt;
- }
- else
- {
- // container.value = pointed-to.id
- //
- data_member_path& id (*id_member (*vo->obj));
-
- c_cols->traverse (m, utype (id), "value", "value");
- o_cols->traverse (id);
- ot = &rt;
- }
- }
-
- for (object_columns_list::iterator b (c_cols->begin ()), i (b),
- j (o_cols->begin ()); i != c_cols->end (); ++i, ++j)
- {
- l.clear ();
-
- if (i != b)
- l += "AND ";
-
- l += ct;
- l += '.';
- l += quote_id (i->name);
- l += '=';
- l += quote_id (*ot);
- l += '.';
- l += quote_id (j->name);
-
- from.push_back (strlit (l));
- }
- }
-
- // If we have already joined the container with the desired
- // join type, then use LEFT JOIN to join the object to the
- // container. This is the right thing to do since an entry
- // in the container can only point (either via id or value)
- // to a single object.
- //
- l = (cont == 0 ? join_syntax (*i) : "LEFT JOIN");
- l += ' ';
-
- l += table_qname (o);
-
- if (!alias.empty ())
- l += (need_alias_as ? " AS " : " ") + quote_id (alias);
-
- l += " ON";
- from.push_back (l);
-
- if (cont != 0)
- {
- instance<object_columns_list> c_cols; // Container columns.
- instance<object_columns_list> o_cols; // Object columns.
-
- qname* ot; // Object table (either lt or rt).
-
- if (imp != 0)
- {
- semantics::data_member& imb (*imp->back ());
-
- if (&o == c)
- {
- // container.id = pointed-to.id
- //
- data_member_path& id (*id_member (*vo->obj));
-
- c_cols->traverse (imb, utype (id), "id", "object_id", vo->obj);
- o_cols->traverse (id);
- ot = &rt;
- }
- else
- {
- // container.value = pointer.id
- //
- data_member_path& id (*id_member (*e.vo->obj));
-
- c_cols->traverse (imb, utype (id), "value", "value");
- o_cols->traverse (id);
- ot = &lt;
- }
- }
- else
- {
- if (&o == c)
- {
- // container.value = pointed-to.id
- //
- data_member_path& id (*id_member (*vo->obj));
-
- c_cols->traverse (m, utype (id), "value", "value");
- o_cols->traverse (id);
- ot = &rt;
- }
- else
- {
- // container.id = pointer.id
- //
- data_member_path& id (*id_member (*e.vo->obj));
-
- c_cols->traverse (m, utype (id), "id", "object_id", e.vo->obj);
- o_cols->traverse (id);
- ot = &lt;
- }
- }
-
- for (object_columns_list::iterator b (c_cols->begin ()), i (b),
- j (o_cols->begin ()); i != c_cols->end (); ++i, ++j)
- {
- l.clear ();
-
- if (i != b)
- l += "AND ";
-
- l += ct;
- l += '.';
- l += quote_id (i->name);
- l += '=';
- l += quote_id (*ot);
- l += '.';
- l += quote_id (j->name);
-
- from.push_back (strlit (l));
- }
- }
- else
- {
- instance<object_columns_list> l_cols;
- instance<object_columns_list> r_cols;
-
- if (imp != 0)
- {
- // our.id = pointed-to.pointer
- //
- l_cols->traverse (*id_member (*e.vo->obj));
- r_cols->traverse (*imp->back (), column_prefix (*imp));
- }
- else
- {
- // our.pointer = pointed-to.id
- //
- l_cols->traverse (*e.member_path.back (),
- column_prefix (e.member_path));
- r_cols->traverse (*id_member (*vo->obj));
- }
-
- for (object_columns_list::iterator b (l_cols->begin ()), i (b),
- j (r_cols->begin ()); i != l_cols->end (); ++i, ++j)
- {
- l.clear ();
-
- if (i != b)
- l += "AND ";
-
- l += quote_id (lt);
- l += '.';
- l += quote_id (i->name);
- l += '=';
- l += quote_id (rt);
- l += '.';
- l += quote_id (j->name);
-
- from.push_back (strlit (l));
- }
- }
-
- if (poly_depth != 1)
- {
- bool t (true); //@@ (im)perfect forwarding
- size_t d (poly_depth - 1); //@@ (im)perfect forward.
- instance<polymorphic_object_joins> j (o, t, d, i->alias);
- j->traverse (polymorphic_base (o));
-
- from.insert (from.end (), j->begin (), j->end ());
- query_optimize = query_optimize || !j->joins.empty ();
- }
- } // End JOIN-generating for-loop.
- }
-
- // Check that pointed-to objects inside objects that we are loading
- // have session support enabled (required to have a shared copy).
- // Also see if we need to throw if there is no session.
- //
- bool need_session (false);
- if (vq.kind == view_query::condition)
- {
- view_objects& objs (c.get<view_objects> ("objects"));
- for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
- {
- if (i->kind != view_object::object || i->ptr == 0)
- continue; // Not an object or not loaded.
-
- instance<view_object_check> t (*i, rel_map);
- t->traverse (*i->obj);
- need_session = need_session || t->session_;
- }
- }
-
- os << "// " << class_name (c) << endl
- << "//" << endl
- << endl;
-
- view_extra (c);
-
- // query_columns
- //
- if (c.get<size_t> ("object-count") != 0)
- view_query_columns_type_->traverse (c);
-
- //
- // Functions.
- //
-
- // grow ()
- //
- if (generate_grow && columns != 0)
- {
- os << "bool " << traits << "::" << endl
- << "grow (image_type& i," << endl
- << truncated_vector << " t";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (i);"
- << "ODB_POTENTIALLY_UNUSED (t);";
-
- if (versioned)
- os << "ODB_POTENTIALLY_UNUSED (svm);";
-
- os << endl
- << "bool grew (false);"
- << endl;
-
- index_ = 0;
- names (c, grow_member_names_);
-
- os << "return grew;"
- << "}";
- }
-
- // bind (image_type)
- //
- if (columns != 0)
- {
- os << "void " << traits << "::" << endl
- << "bind (" << bind_vector << " b," << endl
- << "image_type& i";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{";
-
- if (versioned)
- os << "ODB_POTENTIALLY_UNUSED (svm);"
- << endl;
-
- os << "using namespace " << db << ";"
- << endl
- << db << "::statement_kind sk (statement_select);"
- << "ODB_POTENTIALLY_UNUSED (sk);"
- << endl
- << "std::size_t n (0);"
- << endl;
-
- names (c, bind_member_names_);
-
- os << "}";
- }
-
- // init (view, image)
- //
- if (columns != 0)
- {
- os << "void " << traits << "::" << endl
- << "init (view_type& o," << endl
- << "const image_type& i," << endl
- << "database* db";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (o);"
- << "ODB_POTENTIALLY_UNUSED (i);"
- << "ODB_POTENTIALLY_UNUSED (db);";
-
- if (versioned)
- os << "ODB_POTENTIALLY_UNUSED (svm);";
-
- os << endl;
-
- if (need_session)
- os << "if (!" << options.session_type () << "::_has_cache ())" << endl
- << "throw session_required ();"
- << endl;
-
- if (has_a (c, test_pointer))
- os << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << endl;
-
- names (c, init_view_pointer_member_pre_names_);
- names (c, init_value_member_names_);
- names (c, init_view_pointer_member_post_names_);
-
- os << "}";
- }
-
- // query_statement()
- //
- if (vq.kind != view_query::runtime)
- {
- os << traits << "::query_base_type" << endl
- << traits << "::" << endl
- << "query_statement (const query_base_type& q)"
- << "{";
-
- if (vq.kind == view_query::complete_select ||
- vq.kind == view_query::complete_execute)
- {
- os << "query_base_type r (" << endl;
-
- bool ph (false);
- bool pred (vq.kind == view_query::complete_select);
-
- if (!vq.literal.empty ())
- {
- // See if we have the '(?)' placeholder.
- //
- // @@ Ideally we would need to make sure we don't match
- // this inside strings and quoted identifier. So the
- // proper way to handle this would be to tokenize the
- // statement using sql_lexer, once it is complete enough.
- //
- string::size_type p (vq.literal.find ("(?)"));
-
- if (p != string::npos)
- {
- ph = true;
- // For the SELECT query we keep the parenthesis in (?) and
- // also handle the case where the query expression is empty.
- //
- if (pred)
- os << strlit (string (vq.literal, 0, p + 1)) << " +" << endl
- << "(q.empty () ? query_base_type::true_expr : q) +" << endl
- << strlit (string (vq.literal, p + 2));
- else
- {
- os << strlit (string (vq.literal, 0, p)) << " + q";
-
- p += 3;
- if (p != vq.literal.size ())
- os << " + " << strlit (string (vq.literal, p));
- }
- }
- else
- os << strlit (vq.literal);
- }
- else
- {
- semantics::scope& scope (
- dynamic_cast<semantics::scope&> (*unit.find (vq.scope)));
-
- // Output the pragma location for easier error tracking.
- //
- os << "// From " << location_string (vq.loc, true) << endl
- << translate_expression (
- c, vq.expr, scope, vq.loc, "query", &ph, pred).value;
- }
-
- os << ");";
-
- // If there was no placeholder, add the query condition
- // at the end.
- //
- if (!ph)
- os << endl
- << "if (!q.empty ())"
- << "{"
- << "r += " << strlit (versioned ? "\n" : " ") << ";"
- << "r += q.clause_prefix ();"
- << "r += q;"
- << "}";
- }
- else // vq.kind == view_query::condition
- {
- // Use the from-list generated above.
- //
- statement_columns sc;
- {
- instance<view_columns> t (sc, from, rel_map);
- t->traverse (c);
- process_statement_columns (
- sc, statement_select, versioned || query_optimize);
- }
-
- string sep (versioned || query_optimize ? "\n" : " ");
-
- os << "query_base_type r (" << endl
- << strlit ((vq.distinct ? "SELECT DISTINCT" : "SELECT") + sep);
-
- for (statement_columns::const_iterator i (sc.begin ()),
- e (sc.end ()); i != e;)
- {
- string const& c (i->column);
- os << endl
- << strlit (c + (++i != e ? "," : "") + sep);
- }
-
- os << ");"
- << endl;
-
- // It is much easier to add the separator at the beginning of the
- // next line since the JOIN condition may not be a string literal.
- //
- for (strings::const_iterator i (from.begin ()); i != from.end (); ++i)
- {
- if (i->compare (0, 5, "FROM ") == 0)
- os << "r += " << strlit (*i) << ";";
- else if (i->compare (0, 3, "// ") == 0)
- os << *i << endl;
- else
- {
- // See if this is a JOIN. The exact spelling is database-dependent,
- // but we know there is the JOIN word in there somewhere and before
- // it we should only have keywords and spaces.
- //
- size_t p (i->find ("JOIN "));
- if (p != string::npos)
- {
- // Make sure before it we only have A-Z and spaces.
- //
- for (char c; p != 0; --p)
- {
- c = (*i)[p - 1];
- if ((c < 'A' || c > 'Z') && c != ' ')
- break;
- }
-
- if (p == 0)
- os << endl
- << "r += " << strlit (sep + *i) << ";";
- }
-
- // Something else, assume already a string literal.
- //
- if (p != 0)
- os << "r += " << *i << ";";
- }
- }
-
- // Generate the query condition.
- //
- if (!vq.literal.empty () || !vq.expr.empty ())
- {
- os << endl
- << "query_base_type c (" << endl;
-
- bool ph (false);
-
- if (!vq.literal.empty ())
- {
- // See if we have the '(?)' placeholder.
- //
- // @@ Ideally we would need to make sure we don't match
- // this inside strings and quoted identifier. So the
- // proper way to handle this would be to tokenize the
- // statement using sql_lexer, once it is complete enough.
- //
- string::size_type p (vq.literal.find ("(?)"));
-
- if (p != string::npos)
- {
- ph = true;
- os << strlit (string (vq.literal, 0, p + 1))<< " +" << endl
- << "(q.empty () ? query_base_type::true_expr : q) +" << endl
- << strlit (string (vq.literal, p + 2));
- }
- else
- os << strlit (vq.literal);
-
- os << ");";
- }
- else
- {
- semantics::scope& scope (
- dynamic_cast<semantics::scope&> (*unit.find (vq.scope)));
-
- // Output the pragma location for easier error tracking.
- //
- os << "// From " << location_string (vq.loc, true) << endl
- << translate_expression (
- c, vq.expr, scope, vq.loc, "query", &ph).value;
-
- os << ");";
-
- // Optimize the query if it had a placeholder. This gets
- // rid of useless clauses like WHERE TRUE.
- //
- if (ph)
- os << endl
- << "c.optimize ();";
- }
-
- if (!ph)
- os << endl
- << "c += q;";
-
- os << endl
- << "if (!c.empty ())"
- << "{"
- << "r += " << strlit (sep) << ";"
- << "r += c.clause_prefix ();"
- << "r += c;"
- << "}";
-
- string st (select_trailer (c));
- if (!st.empty ())
- {
- os << "r += " << strlit (sep) << ";"
- << "r += " << strlit (st) << ";";
- }
- }
- else
- {
- os << endl
- << "if (!q.empty ())"
- << "{"
- << "r += " << strlit (sep) << ";"
- << "r += q.clause_prefix ();"
- << "r += q;"
- << "}";
- }
- }
-
- os << "return r;"
- << "}";
- }
-
- // Unprepared.
- //
- if (!options.omit_unprepared ())
- {
- os << "result< " << traits << "::view_type >" << endl
- << traits << "::" << endl
- << "query (database&, const query_base_type& q)"
- << "{"
- << "using namespace " << db << ";"
- << "using odb::details::shared;"
- << "using odb::details::shared_ptr;"
- << endl;
-
- os << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_view<view_type> ());";
-
- if (versioned)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));";
-
- os << endl
- << "image_type& im (sts.image ());"
- << "binding& imb (sts.image_binding ());"
- << endl
- << "if (im.version != sts.image_version () || imb.version == 0)"
- << "{"
- << "bind (imb.bind, im" << (versioned ? ", svm" : "") << ");"
- << "sts.image_version (im.version);"
- << "imb.version++;"
- << "}";
-
- if (vq.kind == view_query::runtime)
- os << "const query_base_type& qs (q);";
- else
- os << "const query_base_type& qs (query_statement (q));";
-
- os << "qs.init_parameters ();"
- << "shared_ptr<select_statement> st (" << endl
- << "new (shared) select_statement (" << endl;
- view_query_statement_ctor_args (
- c, "qs", versioned || query_optimize, false);
- os << "));" << endl
- << "st->execute ();";
-
- post_query_ (c, true);
-
- os << endl
- << "shared_ptr< odb::view_result_impl<view_type> > r (" << endl
- << "new (shared) " << db << "::view_result_impl<view_type> (" << endl
- << "qs, st, sts, " << (versioned ? "&svm" : "0") << "));"
- << endl
- << "return result<view_type> (r);"
- << "}";
-
- // query(odb::query_base)
- //
- if (multi_dynamic)
- os << "result< " << traits << "::view_type >" << endl
- << traits << "::" << endl
- << "query (database& db, const odb::query_base& q)"
- << "{"
- << "return query (db, query_base_type (q));"
- << "}";
- }
-
- // Prepared. Very similar to unprepared but has some annoying variations
- // that make it difficult to factor out something common.
- //
- if (options.generate_prepared ())
- {
- // prepare_query
- //
- os << "odb::details::shared_ptr<prepared_query_impl>" << endl
- << traits << "::" << endl
- << "prepare_query (connection& c, const char* n, " <<
- "const query_base_type& q)"
- << "{"
- << "using namespace " << db << ";"
- << "using odb::details::shared;"
- << "using odb::details::shared_ptr;"
- << endl;
-
- os << db << "::connection& conn (" << endl
- << "static_cast<" << db << "::connection&> (c));"
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_view<view_type> ());";
-
- if (versioned)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));";
-
- os << endl;
-
- // Rebind the image if necessary.
- //
- os << "image_type& im (sts.image ());"
- << "binding& imb (sts.image_binding ());"
- << endl
- << "if (im.version != sts.image_version () || imb.version == 0)"
- << "{"
- << "bind (imb.bind, im" << (versioned ? ", svm" : "") << ");"
- << "sts.image_version (im.version);"
- << "imb.version++;"
- << "}";
-
- os << "shared_ptr<" << db << "::prepared_query_impl> r (" << endl
- << "new (shared) " << db << "::prepared_query_impl (conn));"
- << "r->name = n;"
- << "r->execute = &execute_query;";
-
- if (vq.kind == view_query::runtime)
- os << "r->query = q;";
- else
- os << "r->query = query_statement (q);";
-
- os << "r->stmt.reset (" << endl
- << "new (shared) select_statement (" << endl;
- view_query_statement_ctor_args (
- c, "r->query", versioned || query_optimize, true);
- os << "));"
- << endl
- << "return r;"
- << "}";
-
- // prepare_query(odb::query_base)
- //
- if (multi_dynamic)
- os << "odb::details::shared_ptr<prepared_query_impl>" << endl
- << traits << "::" << endl
- << "prepare_query (connection& c, const char* n, " <<
- "const odb::query_base& q)"
- << "{"
- << "return prepare_query (c, n, query_base_type (q));"
- << "}";
-
- // execute_query
- //
- os << "odb::details::shared_ptr<result_impl>" << endl
- << traits << "::" << endl
- << "execute_query (prepared_query_impl& q)"
- << "{"
- << "using namespace " << db << ";"
- << "using odb::details::shared;"
- << "using odb::details::shared_ptr;"
- << endl
- << db << "::prepared_query_impl& pq (" << endl
- << "static_cast<" << db << "::prepared_query_impl&> (q));"
- << "shared_ptr<select_statement> st (" << endl
- << "odb::details::inc_ref (" << endl
- << "static_cast<select_statement*> (pq.stmt.get ())));"
- << endl;
-
- os << db << "::connection& conn (" << endl
- << db << "::transaction::current ().connection ());"
- << 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 ());"
- << endl
- << "statements_type& sts (" << endl
- << "conn.statement_cache ().find_view<view_type> ());";
-
- if (versioned)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration (" << schema_name << "));";
-
- os << endl;
-
- // Rebind the image if necessary.
- //
- os << "image_type& im (sts.image ());"
- << "binding& imb (sts.image_binding ());"
- << endl
- << "if (im.version != sts.image_version () || imb.version == 0)"
- << "{"
- << "bind (imb.bind, im" << (versioned ? ", svm" : "") << ");"
- << "sts.image_version (im.version);"
- << "imb.version++;"
- << "}";
-
- os << "pq.query.init_parameters ();"
- << "st->execute ();";
-
- post_query_ (c, false);
-
- os << endl
- << "return shared_ptr<result_impl> (" << endl
- << "new (shared) " << db << "::view_result_impl<view_type> (" << endl
- << "pq.query, st, sts, " << (versioned ? "&svm" : "0") << "));"
- << "}";
- }
-
- // Generate function table registration for dynamic multi-database
- // support.
- //
- if (multi_dynamic)
- {
- string fn (flat_name (type));
- string dt ("access::view_traits_impl< " + type + ", id_common >");
-
- os << "static const" << endl
- << dt << "::" << endl
- << "function_table_type function_table_" << fn << "_ ="
- << "{";
-
- if (!options.omit_unprepared ())
- os << "&" << traits << "::query";
-
- if (options.generate_prepared ())
- {
- if (!options.omit_unprepared ())
- os << "," << endl;
-
- os << "&" << traits << "::prepare_query" << "," << endl
- << "&" << traits << "::execute_query";
- }
-
- os << "};";
-
- os << "static const view_function_table_entry< " << type << ", " <<
- "id_" << db << " >" << endl
- << "function_table_entry_" << fn << "_ (" << endl
- << "&function_table_" << fn << "_);"
- << endl;
- }
-}
-
-namespace relational
-{
- namespace source
- {
- static inline void
- add_space (string& s)
- {
- string::size_type n (s.size ());
- if (n != 0 && s[n - 1] != ' ')
- s += ' ';
- }
-
- static string
- translate_name_trailer (cxx_lexer& l,
- cpp_ttype& tt,
- string& tl,
- tree& tn,
- cpp_ttype& ptt)
- {
- string r;
-
- for (; tt != CPP_EOF; ptt = tt, tt = l.next (tl, &tn))
- {
- bool done (false);
-
- switch (tt)
- {
- case CPP_SCOPE:
- case CPP_DOT:
- {
- r += cxx_lexer::token_spelling[tt];
- break;
- }
- default:
- {
- // Handle CPP_KEYWORD here to avoid a warning (it is not
- // part of the cpp_ttype enumeration).
- //
- if (tt == CPP_NAME || tt == CPP_KEYWORD)
- {
- // For names like 'foo::template bar'.
- //
- if (ptt == CPP_NAME || ptt == CPP_KEYWORD)
- r += ' ';
-
- r += tl;
- }
- else
- done = true;
-
- break;
- }
- }
-
- if (done)
- break;
- }
-
- return r;
- }
-
- static class_::expression
- translate_name (cxx_lexer& l,
- cpp_ttype& tt,
- string& tl,
- tree& tn,
- cpp_ttype& ptt,
- semantics::scope& start_scope,
- location_t loc,
- string const& prag,
- bool check_ptr,
- view_alias_map const& amap,
- view_object_map const& omap)
- {
- using semantics::scope;
- using semantics::data_member;
- typedef class_::expression expression;
-
- bool multi_obj ((amap.size () + omap.size ()) > 1);
-
- bool fail (false);
- string name;
- string r ("query_columns");
- context& ctx (context::current ());
-
- // This code is quite similar to view_data_members in the type
- // processor.
- //
- try
- {
- data_member* m (0);
- view_object* vo (0);
-
- // Check if this is an alias.
- //
- if (tt == CPP_NAME)
- {
- view_alias_map::const_iterator i (amap.find (tl));
-
- if (i != amap.end ())
- {
- if (multi_obj)
- {
- r += "::";
- r += i->first;
- }
-
- vo = i->second;
- fail = true; // This must be a data member.
-
- // Skip '::'.
- //
- ptt = tt;
- tt = l.next (tl, &tn);
-
- if (tt != CPP_SCOPE)
- {
- error (loc) << "member name expected after an alias in db " <<
- "pragma " << prag << endl;
- throw operation_failed ();
- }
-
- ptt = tt;
- if (l.next (tl, &tn) != CPP_NAME)
- throw lookup::invalid_name ();
-
- m = &vo->obj->lookup<data_member> (tl, scope::include_hidden);
-
- tt = l.next (tl, &tn);
- }
- }
-
- // If it is not an alias, do the normal lookup.
- //
- if (vo == 0)
- {
- // Also get the object type. We need to do it so that
- // we can get the correct (derived) object name (the
- // member itself can come from a base class).
- //
- scope* s;
- cpp_ttype ptt; // Not used.
- m = &lookup::resolve_scoped_name<data_member> (
- l, tt, tl, tn, ptt,
- start_scope,
- name,
- false,
- &s);
-
- view_object_map::const_iterator i (
- omap.find (dynamic_cast<semantics::class_*> (s)));
-
- if (i == omap.end ())
- {
- // Not an object associated with this view. Assume it
- // is some other valid name.
- //
- return expression (
- name + translate_name_trailer (l, tt, tl, tn, ptt));
- }
-
- vo = i->second;
-
- if (multi_obj)
- {
- r += "::";
- r += context::class_name (*vo->obj);
- }
- }
-
- expression e (vo);
- r += "::";
- r += ctx.public_name (*m);
-
- // Assemble the member path if we may need to return a pointer
- // expression.
- //
- if (check_ptr)
- e.member_path.push_back (m);
-
- fail = true; // Now we definitely fail if anything goes wrong.
-
- // Finally, resolve nested members if any.
- //
- for (; tt == CPP_DOT; ptt = tt, tt = l.next (tl, &tn))
- {
- // Check if this member is actually of a composite value type.
- // This is to handle expressions like "object::member.is_null ()"
- // correctly. The remaining issue here is that in the future
- // is_null()/is_not_null() will be valid for composite values
- // as well.
- //
- semantics::class_* comp (
- context::composite_wrapper (context::utype (*m)));
- if (comp == 0)
- break;
-
- ptt = tt;
- tt = l.next (tl, &tn);
-
- if (tt != CPP_NAME)
- {
- error (loc) << "name expected after '.' in db pragma " <<
- prag << endl;
- throw operation_failed ();
- }
-
- m = &comp->lookup<data_member> (tl, scope::include_hidden);
-
- r += '.';
- r += ctx.public_name (*m);
-
- if (check_ptr)
- e.member_path.push_back (m);
- }
-
- // If requested, check if this member is a pointer. We only do this
- // if there is nothing after this name.
- //
- if (check_ptr && tt == CPP_EOF)
- {
- using semantics::type;
-
- type* t;
-
- if (context::container (*m))
- t = &context::container_vt (*m);
- else
- t = &context::utype (*m);
-
- if (context::object_pointer (*t))
- return e;
- }
-
- // Read the remainder of the expression (e.g., '.is_null ()') if
- // the member is not composite and we bailed out from the above
- // loop.
- //
- if (tt == CPP_DOT)
- r += translate_name_trailer (l, tt, tl, tn, ptt);
-
- return expression (r);
- }
- catch (lookup::invalid_name const&)
- {
- if (!fail)
- return expression (
- name + translate_name_trailer (l, tt, tl, tn, ptt));
-
- error (loc) << "invalid name in db pragma " << prag << endl;
- throw operation_failed ();
- }
- catch (semantics::unresolved const& e)
- {
- if (!fail)
- return expression (
- name + translate_name_trailer (l, tt, tl, tn, ptt));
-
- if (e.type_mismatch)
- error (loc) << "name '" << e.name << "' in db pragma " << prag <<
- " does not refer to a data member" << endl;
- else
- error (loc) << "unable to resolve data member '" << e.name <<
- "' specified with db pragma " << prag << endl;
-
- throw operation_failed ();
- }
- catch (semantics::ambiguous const& e)
- {
- error (loc) << "data member name '" << e.first.name () << "' " <<
- "specified with db pragma " << prag << " is ambiguous" << endl;
-
- info (e.first.named ().location ()) << "could resolve to this " <<
- "data member" << endl;
-
- info (e.second.named ().location ()) << "or could resolve to this " <<
- "data member" << endl;
-
- throw operation_failed ();
- }
- }
-
- class_::expression class_::
- translate_expression (type& c,
- cxx_tokens const& ts,
- semantics::scope& scope,
- location_t loc,
- string const& prag,
- bool* placeholder,
- bool predicate)
- {
- // This code is similar to translate() from context.cxx.
- //
-
- // The overall idea is as folows: read in tokens and add them
- // to the string. If a token starts a name, try to resolve it
- // to an object member (taking into account aliases). If this
- // was successful, translate it to the query column reference.
- // Otherwise, output it as is.
- //
- // If the placeholder argument is not NULL, then we need to
- // detect the special '(?)' token sequence and replace it
- // with the query variable ('q').
- //
- expression e ("");
- string& r (e.value);
-
- view_alias_map const& amap (c.get<view_alias_map> ("alias-map"));
- view_object_map const& omap (c.get<view_object_map> ("object-map"));
-
- cxx_tokens_lexer l;
- l.start (ts);
-
- tree tn;
- string tl;
- for (cpp_ttype tt (l.next (tl, &tn)), ptt (CPP_EOF); tt != CPP_EOF;)
- {
- // Try to format the expression to resemble the style of the
- // generated code.
- //
- switch (tt)
- {
- case CPP_NOT:
- {
- add_space (r);
- r += '!';
- break;
- }
- case CPP_COMMA:
- {
- r += ", ";
- break;
- }
- case CPP_OPEN_PAREN:
- {
- if (ptt == CPP_NAME ||
- ptt == CPP_KEYWORD)
- add_space (r);
-
- r += '(';
- break;
- }
- case CPP_CLOSE_PAREN:
- {
- r += ')';
- break;
- }
- case CPP_OPEN_SQUARE:
- {
- r += '[';
- break;
- }
- case CPP_CLOSE_SQUARE:
- {
- r += ']';
- break;
- }
- case CPP_OPEN_BRACE:
- {
- add_space (r);
- r += "{ ";
- break;
- }
- case CPP_CLOSE_BRACE:
- {
- add_space (r);
- r += '}';
- break;
- }
- case CPP_SEMICOLON:
- {
- r += ';';
- break;
- }
- case CPP_ELLIPSIS:
- {
- add_space (r);
- r += "...";
- break;
- }
- case CPP_PLUS:
- case CPP_MINUS:
- {
- bool unary (ptt != CPP_NAME &&
- ptt != CPP_SCOPE &&
- ptt != CPP_NUMBER &&
- ptt != CPP_STRING &&
- ptt != CPP_CLOSE_PAREN &&
- ptt != CPP_PLUS_PLUS &&
- ptt != CPP_MINUS_MINUS);
-
- if (!unary)
- add_space (r);
-
- r += cxx_lexer::token_spelling[tt];
-
- if (!unary)
- r += ' ';
- break;
- }
- case CPP_PLUS_PLUS:
- case CPP_MINUS_MINUS:
- {
- if (ptt != CPP_NAME &&
- ptt != CPP_CLOSE_PAREN &&
- ptt != CPP_CLOSE_SQUARE)
- add_space (r);
-
- r += cxx_lexer::token_spelling[tt];
- break;
- }
- case CPP_DEREF:
- case CPP_DEREF_STAR:
- case CPP_DOT:
- case CPP_DOT_STAR:
- {
- r += cxx_lexer::token_spelling[tt];
- break;
- }
- case CPP_STRING:
- {
- if (ptt == CPP_NAME ||
- ptt == CPP_KEYWORD ||
- ptt == CPP_STRING ||
- ptt == CPP_NUMBER)
- add_space (r);
-
- r += strlit (tl);
- break;
- }
- case CPP_NUMBER:
- {
- if (ptt == CPP_NAME ||
- ptt == CPP_KEYWORD ||
- ptt == CPP_STRING ||
- ptt == CPP_NUMBER)
- add_space (r);
-
- r += tl;
- break;
- }
- case CPP_SCOPE:
- case CPP_NAME:
- {
- // Start of a name.
- //
- if (ptt == CPP_NAME ||
- ptt == CPP_KEYWORD ||
- ptt == CPP_STRING ||
- ptt == CPP_NUMBER)
- add_space (r);
-
- // Check if this is a pointer expression.
- //
- // If r is not empty, then it means this is not just the
- // name. If placeholder is not 0, then we are translating
- // a query expression, not a join condition.
- //
- expression e (
- translate_name (
- l, tt, tl, tn, ptt,
- scope, loc, prag,
- r.empty () && placeholder == 0, amap, omap));
-
- if (e.kind == expression::literal)
- r += e.value;
- else
- return e;
-
- continue; // We have already extracted the next token.
- }
- case CPP_QUERY:
- {
- if (placeholder != 0 && !*placeholder)
- {
- if (ptt == CPP_OPEN_PAREN)
- {
- // Get the next token and see if it is ')'.
- //
- ptt = tt;
- tt = l.next (tl, &tn);
-
- if (tt == CPP_CLOSE_PAREN)
- {
- *placeholder = true;
-
- // Predicate is true if this is a SELECT statement clause.
- // Otherwise it is a stored procedure parameters.
- //
- if (predicate)
- r += "q.empty () ? query_base_type::true_expr : q";
- else
- {
- r.resize (r.size () - 1); // Remove opening paren.
- r += "q";
- break; // Skip the closing paren as well.
- }
- }
- else
- {
- // The same as in the default case.
- //
- add_space (r);
- r += "? ";
- }
- continue; // We have already gotten the next token.
- }
- }
- }
- // Fall through.
- default:
- {
- // Handle CPP_KEYWORD here to avoid a warning (it is not
- // part of the cpp_ttype enumeration).
- //
- if (tt == CPP_KEYWORD)
- {
- if (ptt == CPP_NAME ||
- ptt == CPP_KEYWORD ||
- ptt == CPP_STRING ||
- ptt == CPP_NUMBER)
- add_space (r);
-
- r += tl;
- }
- else
- {
- // All the other operators.
- //
- add_space (r);
- r += cxx_lexer::token_spelling[tt];
- r += ' ';
- }
- break;
- }
- }
-
- //
- // Watch out for the continue statements above if you add any
- // logic here.
- //
-
- ptt = tt;
- tt = l.next (tl, &tn);
- }
-
- return e;
- }
-
- void
- generate ()
- {
- context ctx;
- ostream& os (ctx.os);
-
- traversal::unit unit;
- traversal::defines unit_defines;
- typedefs unit_typedefs (false);
- traversal::namespace_ ns;
- instance<class_> c;
-
- unit >> unit_defines >> ns;
- unit_defines >> c;
- unit >> unit_typedefs >> c;
-
- traversal::defines ns_defines;
- typedefs ns_typedefs (false);
-
- ns >> ns_defines >> ns;
- ns_defines >> c;
- ns >> ns_typedefs >> c;
-
- instance<include> i;
- i->generate ();
-
- os << "namespace odb"
- << "{";
-
- unit.dispatch (ctx.unit);
-
- os << "}";
- }
- }
-}
diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx
deleted file mode 100644
index f82b5ad..0000000
--- a/odb/relational/source.hxx
+++ /dev/null
@@ -1,7142 +0,0 @@
-// file : odb/relational/source.hxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#ifndef ODB_RELATIONAL_SOURCE_HXX
-#define ODB_RELATIONAL_SOURCE_HXX
-
-#include <map>
-#include <set>
-#include <list>
-#include <vector>
-#include <sstream>
-
-#include <odb/diagnostics.hxx>
-
-#include <odb/relational/context.hxx>
-#include <odb/relational/common.hxx>
-#include <odb/relational/schema.hxx>
-
-namespace relational
-{
- namespace source
- {
- // Column literal in a statement (e.g., in select-list, etc).
- //
- struct statement_column
- {
- statement_column (): member (0) {}
- statement_column (std::string const& tbl,
- std::string const& col,
- std::string const& t,
- semantics::data_member& m,
- std::string const& kp = "")
- : table (tbl), column (col), type (t), member (&m), key_prefix (kp)
- {
- }
-
- std::string table; // Schema-qualifed and quoted table name.
- std::string column; // Table-qualifed and quoted column expr.
- std::string type; // Column SQL type.
- semantics::data_member* member;
- std::string key_prefix;
- };
-
- typedef std::list<statement_column> statement_columns;
-
- // Query parameter generator. A new instance is created for each
- // query, so the customized version can have a counter to implement,
- // for example, numbered parameters (e.g., $1, $2, etc). The auto_id()
- // function is called instead of next() for the automatically-assigned
- // object id member when generating the persist statement. If empty
- // string is returned, then parameter is ignored.
- //
- struct query_parameters: virtual context
- {
- typedef query_parameters base;
-
- query_parameters (statement_kind sk, qname const& table)
- : sk_ (sk), table_ (table) {}
-
- virtual string
- next (semantics::data_member&,
- const std::string& /*column*/, // Table qualified and quoted.
- const std::string& /*sqlt*/)
- {
- return "?";
- }
-
- virtual string
- auto_id (semantics::data_member& m,
- const std::string& column,
- const std::string& sqlt)
- {
- return next (m, column, sqlt);
- }
-
- string
- next (const object_columns_list::column& c)
- {
- return next (*c.member, quote_id (c.name), c.type);
- }
-
- string
- next (const statement_column& c)
- {
- return next (*c.member, c.column, c.type);
- }
-
- protected:
- statement_kind sk_;
- qname table_;
- };
-
- struct object_columns: object_columns_base, virtual context
- {
- typedef object_columns base;
-
- // If provided, used to resolve table/alias names for inverse
- // pointed-to and base objects. Returns qualified name.
- //
- struct table_name_resolver
- {
- virtual string
- resolve_pointer (semantics::data_member&) const = 0;
-
- virtual string
- resolve_base (semantics::class_&) const = 0;
- };
-
- object_columns (statement_kind sk,
- statement_columns& sc,
- query_parameters* param = 0,
- object_section* section = 0)
- : object_columns_base (true, true, section),
- sk_ (sk),
- ro_ (true),
- sc_ (sc),
- param_ (param),
- table_name_resolver_ (0),
- depth_ (1)
- {
- }
-
- object_columns (statement_kind sk,
- bool ignore_ro,
- statement_columns& sc,
- query_parameters* param)
- : object_columns_base (true, true, 0),
- sk_ (sk),
- ro_ (ignore_ro),
- sc_ (sc),
- param_ (param),
- table_name_resolver_ (0),
- depth_ (1)
- {
- }
-
- object_columns (std::string const& table_qname,
- statement_kind sk,
- statement_columns& sc,
- size_t depth = 1,
- object_section* section = 0,
- table_name_resolver* tnr = 0)
- : object_columns_base (true, true, section),
- sk_ (sk),
- ro_ (true),
- sc_ (sc),
- param_ (0),
- table_name_ (table_qname),
- table_name_resolver_ (tnr),
- depth_ (depth)
- {
- }
-
- virtual bool
- section_test (data_member_path const& mp)
- {
- object_section& s (section (mp));
-
- // Include eager loaded members into the main section for
- // SELECT statements. Also include optimistic version into
- // section's SELECT and UPDATE statements.
- //
- return section_ == 0 ||
- *section_ == s ||
- (sk_ == statement_select &&
- *section_ == main_section &&
- !s.separate_load ()) ||
- (version (mp) &&
- (sk_ == statement_update || sk_ == statement_select));
- }
-
- virtual void
- traverse_object (semantics::class_& c)
- {
- // If we are generating a select statement and this is a derived
- // type in a polymorphic hierarchy, then we need to include base
- // columns, but do it in reverse order as well as switch the table
- // name (base columns come from different tables).
- //
- semantics::class_* poly_root (polymorphic (c));
- if (poly_root != 0 && poly_root != &c)
- {
- names (c);
-
- if (sk_ == statement_select && --depth_ != 0)
- {
- semantics::class_& b (polymorphic_base (c));
-
- table_name_ = table_name_resolver_ != 0
- ? table_name_resolver_->resolve_base (b)
- : table_qname (b);
-
- inherits (c);
- }
- }
- else
- object_columns_base::traverse_object (c);
- }
-
- virtual void
- traverse_pointer (semantics::data_member& m, semantics::class_& c)
- {
- // Ignore polymorphic id references for select statements.
- //
- if (sk_ == statement_select && m.count ("polymorphic-ref"))
- return;
-
- data_member_path* imp (inverse (m, key_prefix_));
-
- // Ignore certain columns depending on what kind of statement we are
- // generating. Columns corresponding to the inverse members are
- // only present in the select statements.
- //
- if (imp != 0 && sk_ != statement_select)
- return;
-
- // Inverse object pointers come from a joined table.
- //
- if (imp != 0)
- {
- bool poly (polymorphic (c) != 0);
- semantics::data_member& imf (*imp->front ());
- semantics::data_member& imb (*imp->back ());
-
- // In a polymorphic hierarchy the inverse member can be in
- // the base class, in which case we should use that table.
- //
- semantics::class_& imc (
- poly ? dynamic_cast<semantics::class_&> (imf.scope ()) : c);
-
- data_member_path& id (*id_member (imc));
- semantics::type& idt (utype (id));
-
- if (container (imb))
- {
- // This container is a direct member of the class so the table
- // prefix is just the class table name. We don't assign join
- // aliases for container tables so use the actual table name.
- // Note that the if(!table_name_.empty ()) test may look wrong
- // at first but it is not; if table_name_ is empty then we are
- // generating a container table where we don't qualify columns
- // with tables.
- //
- string table;
-
- if (!table_name_.empty ())
- {
- if (table_name_resolver_ != 0)
- table = table_name_resolver_->resolve_pointer (m);
- else
- table = table_qname (imc, *imp);
- }
-
- instance<object_columns> oc (table, sk_, sc_);
- oc->traverse (imb, idt, "id", "object_id", &imc);
- }
- else
- {
- // Use the join alias instead of the actual table name unless we
- // are handling a container. Generally, we want the join alias
- // to be based on the column name. This is straightforward for
- // single-column references. In case of a composite id, we will
- // need to use the column prefix which is based on the data
- // member name, unless overridden by the user. In the latter
- // case the prefix can be empty, in which case we will just
- // fall back on the member's public name. Note that the
- // if(!table_name_.empty ()) test may look wrong at first but
- // it is not; if table_name_ is empty then we are generating a
- // container table where we don't qualify columns with tables.
- //
- string alias;
-
- if (!table_name_.empty ())
- {
- if (table_name_resolver_ != 0)
- alias = table_name_resolver_->resolve_pointer (m);
- else
- {
- string n;
-
- if (composite_wrapper (idt))
- {
- n = column_prefix (m, key_prefix_, default_name_).prefix;
-
- if (n.empty ())
- n = public_name_db (m);
- else if (n[n.size () - 1] == '_')
- n.resize (n.size () - 1); // Remove trailing underscore.
- }
- else
- {
- bool dummy;
- n = column_name (m, key_prefix_, default_name_, dummy);
- }
-
- alias = column_prefix_.prefix + n;
-
- if (poly)
- {
- qname const& table (table_name (imc));
- alias = quote_id (alias + "_" + table.uname ());
- }
- else
- alias = quote_id (alias);
- }
- }
-
- instance<object_columns> oc (alias, sk_, sc_);
- oc->traverse (id);
- }
- }
- else
- object_columns_base::traverse_pointer (m, c);
- }
-
- virtual bool
- traverse_column (semantics::data_member& m, string const& name, bool)
- {
- // Ignore certain columns depending on what kind statement we are
- // generating. Id and readonly columns are not present in the update
- // statements.
- //
- if ((id () || readonly (member_path_, member_scope_)) &&
- sk_ == statement_update && ro_)
- return false;
-
- return column (m, table_name_, quote_id (name));
- }
-
- virtual bool
- column (semantics::data_member& m,
- string const& table,
- string const& column)
- {
- string r;
-
- if (!table.empty ())
- {
- r += table; // Already quoted.
- r += '.';
- }
-
- r += column; // Already quoted.
-
- string const& sqlt (column_type ());
-
- // Version column (optimistic concurrency) requires special
- // handling in the UPDATE statement.
- //
- //
- if (sk_ == statement_update && version (m))
- {
- r += "=" + r + "+1";
- }
- else if (param_ != 0)
- {
- r += '=';
- r += convert_to (param_->next (m, column, sqlt), sqlt, m);
- }
- else if (sk_ == statement_select)
- r = convert_from (r, sqlt, m);
-
- sc_.push_back (statement_column (table, r, sqlt, m, key_prefix_));
- return true;
- }
-
- protected:
- statement_kind sk_;
- bool ro_;
- statement_columns& sc_;
- query_parameters* param_;
- string table_name_;
- table_name_resolver* table_name_resolver_;
- size_t depth_;
- };
-
- struct view_columns: object_columns_base,
- object_columns::table_name_resolver,
- virtual context
- {
- typedef view_columns base;
-
- view_columns (statement_columns& sc,
- strings& from,
- const view_relationship_map& rm)
- : sc_ (sc), from_ (from), rel_map_ (rm), in_composite_ (false) {}
-
- // Implementation of table_name_resolver for object_columns.
- //
- virtual string
- resolve_pointer (semantics::data_member& m) const
- {
- view_object& us (*ptr_->get<view_object*> ("view-object"));
-
- data_member_path& imp (*inverse (m));
- semantics::data_member& imf (*imp.front ());
- semantics::data_member& imb (*imp.back ());
-
- using semantics::class_;
- typedef view_relationship_map::const_iterator iterator;
-
- std::pair<iterator, iterator> r (rel_map_.equal_range (imp));
-
- for (; r.first != r.second; ++r.first)
- {
- if (r.first->second.second != &us) // Not our associated.
- continue;
-
- view_object& vo (*r.first->second.first); // First because inverse.
-
- // Derive the table name the same way as the JOIN code.
- //
- class_* c (vo.obj);
- if (class_* root = polymorphic (*c))
- {
- // Can be in base.
- //
- c = &static_cast<class_&> (imf.scope ());
-
- if (!polymorphic (*c))
- c = root;
- }
-
- string const& a (vo.alias);
-
- if (container (imb))
- {
- // If this is a container, then object_columns will use the
- // column from the container table, not from the object table
- // (which, strictly speaking, might not have been JOIN'ed).
- //
- qname t (table_name (*c, imp));
- return a.empty ()
- ? quote_id (t)
- : quote_id (a + '_' + t.uname ());
- }
- else
- {
- qname t;
- if (a.empty ())
- t = table_name (*c);
- else
- {
- if (polymorphic (*c))
- t = qname (a + "_" + table_name (*c).uname ());
- else
- t = qname (a);
- }
- return quote_id (t);
- }
- }
-
- // So there is no associated object for this column. The initial
- // plan was to complain and ask the user to explicitly associate
- // the object. This is not a bad plan except for one thing: if
- // the direct side of the relationship is a container, then
- // associating that object explicitly will result in both the
- // container table and the object table being JOIN'ed. But we
- // only need the container table (for the object id) So we will
- // be joining a table for nothing, which is not very clean. So
- // the alternative, and more difficult, plan is to go ahead and
- // add the necessary JOIN's automatically.
- //
- // This code follows the normal JOIN generation code.
- //
- class_* o (object_pointer (utype (m)));
- if (class_* root = polymorphic (*o))
- {
- o = &static_cast<class_&> (imf.scope ());
-
- if (!polymorphic (*o))
- o = root;
- }
-
- string const& a (us.alias);
- string lt (a.empty () ? table_qname (*us.obj) : quote_id (a));
- string rt;
- qname ct (container (imb) ? table_name (*o, imp) : table_name (*o));
-
- string l ("LEFT JOIN ");
-
- if (a.empty ())
- {
- rt = quote_id (ct);
- l += rt;
- }
- else
- {
- // The same relationship can be used by multiple associated
- // objects. So if we have an alias, then also construct one
- // for the table that we are joining.
- //
- rt = quote_id (a + '_' + ct.uname ());
- l += quote_id (ct);
- l += (need_alias_as ? " AS " : " ") + rt;
- }
-
- l += " ON";
- from_.push_back (l);
-
- instance<object_columns_list> l_cols; // Our id columns.
- instance<object_columns_list> r_cols; // Other side id columns.
-
- data_member_path& id (*id_member (*us.obj));
-
- l_cols->traverse (id);
-
- if (container (imb))
- r_cols->traverse (imb, utype (id), "value", "value");
- else
- r_cols->traverse (imb, column_prefix (imp));
-
- for (object_columns_list::iterator b (l_cols->begin ()), i (b),
- j (r_cols->begin ()); i != l_cols->end (); ++i, ++j)
- {
- l.clear ();
-
- if (i != b)
- l += "AND ";
-
- l += lt;
- l += '.';
- l += quote_id (i->name);
- l += '=';
- l += rt;
- l += '.';
- l += quote_id (j->name);
-
- from_.push_back (strlit (l));
- }
-
- return rt;
-
- /*
- // The alternative implementation:
- //
- location const& l1 (m.location ());
- location const& l2 (ptr_->location ());
-
- string n1 (class_name (*object_pointer (utype (m))));
- string n2 (class_name (*object_pointer (utype (*ptr_))));
-
- error (l1) << "object '" << n1 << "' pointed-to by the inverse "
- << "data member in object '" << n2 << "' must be "
- << "explicitly associated with the view" << endl;
-
- info (l2) << "view data member that loads '" << n2 << "' is "
- << "defined here" << endl;
-
- throw operation_failed ();
- */
- }
-
- virtual string
- resolve_base (semantics::class_& b) const
- {
- view_object& vo (*ptr_->get<view_object*> ("view-object"));
-
- qname t (vo.alias.empty ()
- ? table_name (b)
- : qname (vo.alias + "_" + table_name (b).uname ()));
-
- return quote_id (t);
- }
-
- virtual void
- traverse_pointer (semantics::data_member& m, semantics::class_& c)
- {
- type* poly_root (polymorphic (c));
- bool poly (poly_root != 0);
- bool poly_derived (poly && poly_root != &c);
- size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1);
-
- view_object& vo (*m.get<view_object*> ("view-object"));
- string const& a (vo.alias);
-
- qname t;
- if (a.empty ())
- t = table_name (c);
- else
- {
- if (poly)
- t = qname (a + "_" + table_name (c).uname ());
- else
- t = qname (a);
- }
- string qt (quote_id (t));
-
- ptr_ = &m;
-
- statement_kind sk (statement_select); // Imperfect forwarding.
- object_section* s (&main_section); // Imperfect forwarding.
- instance<object_columns> oc (qt, sk, sc_, poly_depth, s, this);
- oc->traverse (c);
- }
-
- virtual void
- traverse_composite (semantics::data_member* pm, semantics::class_& c)
- {
- if (in_composite_)
- {
- object_columns_base::traverse_composite (pm, c);
- return;
- }
-
- // Override the column prerix.
- //
- semantics::data_member& m (*pm);
-
- // If we have literal column specified, use that.
- //
- if (m.count ("column"))
- {
- table_column const& tc (m.get<table_column> ("column"));
-
- if (!tc.table.empty ())
- table_prefix_ = tc.table;
-
- column_prefix_ = object_columns_base::column_prefix (m);
- }
- // Otherwise, see if there is a column expression. For composite
- // members in a view, this should be a single reference.
- //
- else if (m.count ("column-expr"))
- {
- column_expr const& e (m.get<column_expr> ("column-expr"));
-
- if (e.size () > 1)
- {
- cerr << m.file () << ":" << m.line () << ":" << m.column ()
- << ": error: column expression specified for a data member "
- << "of a composite value type" << endl;
-
- throw operation_failed ();
- }
-
- data_member_path const& mp (e.back ().member_path);
-
- if (mp.size () > 1)
- {
- cerr << m.file () << ":" << m.line () << ":" << m.column ()
- << ": error: invalid data member in db pragma column"
- << endl;
-
- throw operation_failed ();
- }
-
- table_prefix_ = e.back ().table;
- column_prefix_ = object_columns_base::column_prefix (*mp.back ());
- }
- else
- {
- cerr << m.file () << ":" << m.line () << ":" << m.column ()
- << ": error: no column prefix provided for a view data member"
- << endl;
-
- cerr << m.file () << ":" << m.line () << ":" << m.column ()
- << ": info: use db pragma column to specify the column prefix"
- << endl;
-
- throw operation_failed ();
- }
-
- in_composite_ = true;
- object_columns_base::traverse_composite (pm, c);
- in_composite_ = false;
- }
-
- virtual bool
- traverse_column (semantics::data_member& m, string const& name, bool)
- {
- string tbl;
- string col;
-
- // If we are inside a composite value, use the standard
- // column name machinery.
- //
- if (in_composite_)
- {
- if (!table_prefix_.empty ())
- {
- tbl = quote_id (table_prefix_);
- col += tbl;
- col += '.';
- }
-
- col += quote_id (name);
- }
- // If we have literal column specified, use that.
- //
- else if (m.count ("column"))
- {
- table_column const& tc (m.get<table_column> ("column"));
-
- if (!tc.expr)
- {
- if (!tc.table.empty ())
- {
- tbl = quote_id (tc.table);
- col += tbl;
- col += '.';
- }
-
- col += quote_id (tc.column);
- }
- else
- col += tc.column;
- }
- // Otherwise, see if there is a column expression.
- //
- else if (m.count ("column-expr"))
- {
- column_expr const& e (m.get<column_expr> ("column-expr"));
-
- for (column_expr::const_iterator i (e.begin ()); i != e.end (); ++i)
- {
- switch (i->kind)
- {
- case column_expr_part::literal:
- {
- col += i->value;
- break;
- }
- case column_expr_part::reference:
- {
- tbl = quote_id (i->table);
- col += tbl;
- col += '.';
- col += column_qname (i->member_path);
- break;
- }
- }
- }
- }
- else
- {
- cerr << m.file () << ":" << m.line () << ":" << m.column ()
- << ": error: no column name provided for a view data member"
- << endl;
-
- cerr << m.file () << ":" << m.line () << ":" << m.column ()
- << ": info: use db pragma column to specify the column name"
- << endl;
-
- throw operation_failed ();
- }
-
- return column (m, tbl, col);
- }
-
- // The column argument is a qualified and quoted column or
- // expression.
- //
- virtual bool
- column (semantics::data_member& m,
- string const& table,
- string const& column)
- {
- string const& sqlt (column_type ());
- sc_.push_back (
- statement_column (
- table, convert_from (column, sqlt, m), sqlt, m));
- return true;
- }
-
- protected:
- statement_columns& sc_;
- strings& from_;
- const view_relationship_map& rel_map_;
-
- bool in_composite_;
- qname table_prefix_; // Table corresponding to column_prefix_;
-
- // Set to the current pointer data member that we are traversing.
- //
- semantics::data_member* ptr_;
- };
-
- struct polymorphic_object_joins: object_columns_base, virtual context
- {
- typedef polymorphic_object_joins base;
-
- polymorphic_object_joins (semantics::class_& obj,
- bool query,
- size_t depth,
- string const& alias = "",
- user_section* section = 0)
- : object_columns_base (true, true),
- obj_ (obj),
- query_ (query),
- depth_ (depth),
- section_ (section),
- alias_ (alias)
- {
- // Get the table and id columns.
- //
- table_ = alias_.empty ()
- ? table_qname (obj_)
- : quote_id (alias_ + "_" + table_name (obj_).uname ());
-
- cols_->traverse (*id_member (obj_));
- }
-
- virtual void
- traverse_object (semantics::class_& c)
- {
- // If section is specified, skip bases that don't add anything
- // to load.
- //
- bool skip (false), stop (false);
- if (section_ != 0)
- {
- skip = true;
-
- if (section_->object == &c)
- {
- user_section& s (*section_);
-
- if (s.total != 0 || s.optimistic ())
- skip = false;
-
- section_ = s.base; // Move to the next base.
-
- if (section_ == 0)
- stop = true; // Stop at this base if there are no more overrides.
- }
- }
- // Skip intermediates that don't add any data members.
- //
- else if (!query_)
- {
- column_count_type const& cc (column_count (c));
- if (cc.total == cc.id + cc.separate_load)
- skip = true;
- }
-
- if (!skip)
- {
- std::ostringstream cond;
-
- qname table (table_name (c));
- string alias (alias_.empty ()
- ? quote_id (table)
- : quote_id (alias_ + "_" + table.uname ()));
-
- for (object_columns_list::iterator b (cols_->begin ()), i (b);
- i != cols_->end ();
- ++i)
- {
- if (i != b)
- cond << " AND ";
-
- string qn (quote_id (i->name));
- cond << alias << '.' << qn << '=' << table_ << '.' << qn;
- }
-
- string line ("LEFT JOIN " + quote_id (table));
-
- if (!alias_.empty ())
- line += (need_alias_as ? " AS " : " ") + alias;
-
- line += " ON " + cond.str ();
-
- joins.push_back (line);
- }
-
- if (!stop && --depth_ != 0)
- inherits (c);
- }
-
- public:
- strings joins;
-
- strings::const_iterator
- begin () const {return joins.begin ();}
-
- strings::const_iterator
- end () const {return joins.end ();}
-
- private:
- semantics::class_& obj_;
- bool query_;
- size_t depth_;
- user_section* section_;
- string alias_;
- string table_;
- instance<object_columns_list> cols_;
- };
-
- struct object_joins: object_columns_base, virtual context
- {
- typedef object_joins base;
-
- //@@ context::{cur,top}_object; might have to be created every time.
- //
- object_joins (semantics::class_& scope,
- bool query,
- size_t depth,
- object_section* section = 0)
- : object_columns_base (true, true, section),
- query_ (query),
- depth_ (depth),
- table_ (table_qname (scope)),
- id_ (*id_member (scope))
- {
- id_cols_->traverse (id_);
- }
-
- virtual bool
- section_test (data_member_path const& mp)
- {
- object_section& s (section (mp));
-
- // Include eager loaded members into the main section.
- //
- return section_ == 0 ||
- *section_ == s ||
- (*section_ == main_section && !s.separate_load ());
- }
-
- virtual void
- traverse_object (semantics::class_& c)
- {
- // If this is a derived type in a polymorphic hierarchy, then we
- // need to include base joins, but do it in reverse order as well
- // as switch the table name (base columns come from different
- // tables).
- //
- semantics::class_* poly_root (polymorphic (c));
- if (poly_root != 0 && poly_root != &c)
- {
- names (c);
-
- if (query_ || --depth_ != 0)
- {
- table_ = table_qname (polymorphic_base (c));
- inherits (c);
- }
- }
- else
- object_columns_base::traverse_object (c);
- }
-
- virtual void
- traverse_pointer (semantics::data_member& m, semantics::class_& c)
- {
- // Ignore polymorphic id references; they are joined by
- // polymorphic_object_joins in a special way.
- //
- if (m.count ("polymorphic-ref"))
- return;
-
- string t, a, dt, da;
- std::ostringstream cond, dcond; // @@ diversion?
-
- // Derive table alias for this member. Generally, we want the
- // alias to be based on the column name. This is straightforward
- // for single-column references. In case of a composite id, we
- // will need to use the column prefix which is based on the data
- // member name, unless overridden by the user. In the latter
- // case the prefix can be empty, in which case we will just
- // fall back on the member's public name.
- //
- string alias;
- {
- string n;
-
- if (composite_wrapper (utype (*id_member (c))))
- {
- n = column_prefix (m, key_prefix_, default_name_).prefix;
-
- if (n.empty ())
- n = public_name_db (m);
- else if (n[n.size () - 1] == '_')
- n.resize (n.size () - 1); // Remove trailing underscore.
- }
- else
- {
- bool dummy;
- n = column_name (m, key_prefix_, default_name_, dummy);
- }
-
- alias = column_prefix_.prefix + n;
- }
-
- semantics::class_* poly_root (polymorphic (c));
- bool poly (poly_root != 0);
-
- semantics::class_* joined_obj (0);
-
- if (data_member_path* imp = inverse (m, key_prefix_))
- {
- semantics::data_member& imf (*imp->front ());
- semantics::data_member& imb (*imp->back ());
-
- // In a polymorphic hierarchy the inverse member can be in
- // the base class, in which case we should use that table.
- //
- semantics::class_& imc (
- poly ? dynamic_cast<semantics::class_&> (imf.scope ()) : c);
-
- if (container (imb))
- {
- // This container is a direct member of the class so the table
- // prefix is just the class table name.
- //
- t = table_qname (imc, *imp);
-
- // Container's value is our id.
- //
- instance<object_columns_list> id_cols;
- id_cols->traverse (imb, utype (id_), "value", "value");
-
- for (object_columns_list::iterator b (id_cols->begin ()), i (b),
- j (id_cols_->begin ()); i != id_cols->end (); ++i, ++j)
- {
-
- if (i != b)
- cond << " AND ";
-
- cond << t << '.' << quote_id (i->name) << '=' <<
- table_ << '.' << quote_id (j->name);
- }
-
- // Add the join for the object itself so that we are able to
- // use it in the WHERE clause.
- //
- if (query_)
- {
- // Here we can use the most derived class instead of the
- // one containing the inverse member.
- //
- qname const& table (table_name (c));
-
- dt = quote_id (table);
- da = quote_id (poly ? alias + "_" + table.uname () : alias);
-
- data_member_path& id (*id_member (c));
-
- instance<object_columns_list> oid_cols, cid_cols;
- oid_cols->traverse (id);
- cid_cols->traverse (imb, utype (id), "id", "object_id", &c);
-
- for (object_columns_list::iterator b (cid_cols->begin ()), i (b),
- j (oid_cols->begin ()); i != cid_cols->end (); ++i, ++j)
- {
-
- if (i != b)
- dcond << " AND ";
-
- dcond << da << '.' << quote_id (j->name) << '=' <<
- t << '.' << quote_id (i->name);
- }
-
- joined_obj = &c;
- }
- }
- else
- {
- qname const& table (table_name (imc));
-
- t = quote_id (table);
- a = quote_id (poly ? alias + "_" + table.uname () : alias);
-
- instance<object_columns_list> id_cols;
- id_cols->traverse (imb, column_prefix (*imp));
-
- for (object_columns_list::iterator b (id_cols->begin ()), i (b),
- j (id_cols_->begin ()); i != id_cols->end (); ++i, ++j)
- {
- if (i != b)
- cond << " AND ";
-
- cond << a << '.' << quote_id (i->name) << '=' <<
- table_ << '.' << quote_id (j->name);
- }
-
- // If we are generating query, JOIN base/derived classes so
- // that we can use their data in the WHERE clause.
- //
- if (query_)
- joined_obj = &imc;
- }
- }
- else if (query_)
- {
- // We need the join to be able to use the referenced object
- // in the WHERE clause.
- //
- qname const& table (table_name (c));
-
- t = quote_id (table);
- a = quote_id (poly ? alias + "_" + table.uname () : alias);
-
- instance<object_columns_list> oid_cols (column_prefix_);
- oid_cols->traverse (m);
-
- instance<object_columns_list> pid_cols;
- pid_cols->traverse (*id_member (c));
-
- for (object_columns_list::iterator b (pid_cols->begin ()), i (b),
- j (oid_cols->begin ()); i != pid_cols->end (); ++i, ++j)
- {
-
- if (i != b)
- cond << " AND ";
-
- cond << a << '.' << quote_id (i->name) << '=' <<
- table_ << '.' << quote_id (j->name);
- }
-
- joined_obj = &c;
- }
-
- if (!t.empty ())
- {
- string line ("LEFT JOIN ");
- line += t;
-
- if (!a.empty ())
- line += (need_alias_as ? " AS " : " ") + a;
-
- line += " ON ";
- line += cond.str ();
-
- joins.push_back (line);
- }
-
- // Add dependent join (i.e., an object table join via the
- // container table).
- //
- if (!dt.empty ())
- {
- string line ("LEFT JOIN ");
- line += dt;
-
- if (!da.empty ())
- line += (need_alias_as ? " AS " : " ") + da;
-
- line += " ON ";
- line += dcond.str ();
-
- joins.push_back (line);
- }
-
- // If we joined the object that is part of a polymorphic type
- // hierarchy, then we may need join its bases as well as its
- // derived types. This is only done for queries.
- //
- if (joined_obj != 0 && poly)
- {
- size_t depth (polymorphic_depth (*joined_obj));
-
- // Join "up" (derived).
- //
- if (joined_obj != &c)
- {
- bool t (true); //@@ (im)perfect forward.
- size_t d (polymorphic_depth (c) - depth); //@@ (im)perfect forward.
- instance<polymorphic_object_joins> j (*joined_obj, t, d, alias);
- j->traverse (c);
- joins.insert (joins.end (), j->joins.begin (), j->joins.end ());
- }
-
- // Join "down" (base).
- //
- if (joined_obj != poly_root)
- {
- bool t (true); //@@ (im)perfect forward.
- size_t d (depth - 1); //@@ (im)perfect forward.
- instance<polymorphic_object_joins> j (*joined_obj, t, d, alias);
- j->traverse (polymorphic_base (*joined_obj));
- joins.insert (joins.end (), j->joins.begin (), j->joins.end ());
- }
- }
- }
-
- public:
- strings joins;
-
- strings::const_iterator
- begin () const {return joins.begin ();}
-
- strings::const_iterator
- end () const {return joins.end ();}
-
- private:
- bool query_;
- size_t depth_;
- string table_;
- data_member_path& id_;
- instance<object_columns_list> id_cols_;
- };
-
- // Check that eager object pointers in the objects that a view loads
- // can be loaded from the cache (i.e., they have session support
- // enabled).
- //
- struct view_object_check: object_members_base
- {
- typedef view_object_check base;
-
- view_object_check (view_object& vo, view_relationship_map& rm)
- : object_members_base (false, &main_section),
- session_ (false), vo_ (vo), rel_map_ (rm) {}
-
- virtual bool
- section_test (data_member_path const& mp)
- {
- // Include eager loaded members into the main section.
- //
- object_section& s (section (mp));
- return *section_ == s || !s.separate_load ();
- }
-
- virtual void
- traverse_pointer (semantics::data_member& m, semantics::class_& c)
- {
- // Ignore polymorphic id references.
- //
- if (m.count ("polymorphic-ref"))
- return;
-
- check (m, inverse (m), utype (m), c);
- }
-
- virtual void
- traverse_container (semantics::data_member& m, semantics::type&)
- {
- semantics::type& vt (container_vt (m));
-
- if (semantics::class_* cvt = composite_wrapper (vt))
- {
- // Check this composite value for any pointers.
- //
- instance<view_object_check> t (vo_, rel_map_);
- t->traverse (*cvt);
-
- session_ = session_ || t->session_;
- }
- else if (semantics::class_* c = object_pointer (vt))
- check (m, inverse (m, "value"), vt, *c);
- }
-
- void
- check (semantics::data_member& m,
- data_member_path* imp,
- semantics::type& pt, // Pointer type.
- semantics::class_& c)
- {
- // We don't care about lazy pointers.
- //
- if (lazy_pointer (pt))
- return;
-
- // First check the pointed-to object recursively.
- //
- if (!c.count ("view-object-check-seen"))
- {
- c.set ("view-object-check-seen", true);
- instance<view_object_check> t (vo_, rel_map_);
- t->traverse (c);
-
- // We may come again for another view.
- //
- c.remove ("view-object-check-seen");
-
- session_ = session_ || t->session_;
- }
-
- // See if the pointed-to object in this relationship is loaded
- // by this view.
- //
- typedef view_relationship_map::const_iterator iterator;
-
- std::pair<iterator, iterator> r (
- rel_map_.equal_range (imp != 0 ? *imp : member_path_));
-
- if (r.first == r.second)
- return; // This relationship does not figure in the view.
-
- view_object& vo (*(imp != 0
- ? r.first->second.first
- : r.first->second.second));
-
- assert (vo.obj == &c); // Got the above right?
-
- if (vo.ptr == 0)
- return; // This object is not loaded by the view.
-
- // The pointed-to object in this relationship is loaded
- // by the view. The hard question, of course, is whether
- // it has anything to do with us. We assume it does.
- //
- if (!session (c))
- {
- // Always direct data member.
- //
- semantics::class_& v (
- dynamic_cast<semantics::class_&> (vo.ptr->scope ()));
-
- location const& l1 (c.location ());
- location const& l2 (m.location ());
- location const& l3 (vo_.ptr->location ());
- location const& l4 (vo.ptr->location ());
-
- string on (class_name (c));
- string vn (class_name (v));
-
- error (l1) << "object '" << on << "' has session support disabled "
- << "but may be loaded by view '" << vn << "' via "
- << "several data members" << endl;
-
- info (l2) << "indirectly via this data member..." << endl;
- info (l3) << "...as a result of this object load" << endl;
- info (l4) << "and directly as a result of this load" << endl;
- info (l1) << "session support is required to only load one copy "
- << "of the object" << endl;
- info (l1) << "and don't forget to create a session instance when "
- << "using this view" << endl;
-
- throw operation_failed ();
- }
-
- session_ = true;
- }
-
- bool session_;
-
- private:
- view_object& vo_;
- view_relationship_map& rel_map_;
- };
-
- //
- // bind
- //
-
- struct bind_member: virtual member_base
- {
- typedef bind_member base;
-
- // NULL section means we are generating object bind().
- //
- bind_member (string const& var = string (),
- string const& arg = string (),
- object_section* section = 0)
- : member_base (var, 0, 0, string (), string (), section),
- arg_override_ (arg) {}
-
- bind_member (string const& var,
- string const& arg,
- semantics::type& t,
- const custom_cxx_type* ct,
- string const& fq_type,
- string const& key_prefix)
- : member_base (var, &t, ct, fq_type, key_prefix),
- arg_override_ (arg) {}
-
- protected:
- string arg_override_;
- };
-
- template <typename T>
- struct bind_member_impl: bind_member, virtual member_base_impl<T>
- {
- typedef bind_member_impl base_impl;
-
- bind_member_impl (base const& x): base (x) {}
-
- typedef typename member_base_impl<T>::member_info member_info;
-
- virtual bool
- pre (member_info& mi)
- {
- if (container (mi))
- return false;
-
- // Treat version as present in every section.
- //
- if (section_ != 0 && !version (mi.m) && *section_ != section (mi.m))
- return false;
-
- // Ignore polymorphic id references; they are bound in a special
- // way.
- //
- if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
- return false;
-
- std::ostringstream ostr;
- ostr << "b[n]";
- b = ostr.str ();
-
- arg = arg_override_.empty () ? string ("i") : arg_override_;
-
- if (var_override_.empty ())
- {
- // Ignore inverse, separately-loaded members in the main
- // section (nothing to persist).
- //
- if (section_ == 0 && separate_load (mi.m) && inverse (mi.m))
- return false;
-
- semantics::class_* comp (composite (mi.t));
-
- os << "// " << mi.m.name () << endl
- << "//" << endl;
-
- // Order of these tests is important.
- //
- if (!insert_send_auto_id && auto_ (mi.m))
- os << "if (sk != statement_insert && sk != statement_update)"
- << "{";
- else if (section_ == 0 && separate_load (mi.m))
- os << "if (sk == statement_insert)"
- << "{";
- else if (inverse (mi.m, key_prefix_) || version (mi.m))
- os << "if (sk == statement_select)"
- << "{";
- // If the whole class is readonly, then we will never be
- // called with sk == statement_update.
- //
- else if (!readonly (*context::top_object))
- {
- if (id (mi.m) ||
- readonly (mi.m) ||
- (comp != 0 && readonly (*comp)) ||
- (section_ == 0 && separate_update (mi.m)))
- os << "if (sk != statement_update)"
- << "{";
- }
-
- // If the member is soft- added or deleted, check the version.
- //
- unsigned long long av (added (mi.m));
- unsigned long long dv (deleted (mi.m));
-
- // If this is a composite member, see if it is summarily
- // added/deleted.
- //
- if (comp != 0)
- {
- unsigned long long cav (added (*comp));
- unsigned long long cdv (deleted (*comp));
-
- if (cav != 0 && (av == 0 || av < cav))
- av = cav;
-
- if (cdv != 0 && (dv == 0 || dv > cdv))
- dv = cdv;
- }
-
- // If the addition/deletion version is the same as the section's,
- // then we don't need the test.
- //
- if (user_section* s = dynamic_cast<user_section*> (section_))
- {
- if (av == added (*s->member))
- av = 0;
-
- if (dv == deleted (*s->member))
- dv = 0;
- }
-
- if (av != 0 || dv != 0)
- {
- os << "if (";
-
- if (av != 0)
- os << "svm >= schema_version_migration (" << av << "ULL, true)";
-
- if (av != 0 && dv != 0)
- os << " &&" << endl;
-
- if (dv != 0)
- os << "svm <= schema_version_migration (" << dv << "ULL, true)";
-
- os << ")"
- << "{";
- }
- }
-
- return true;
- }
-
- virtual void
- post (member_info& mi)
- {
- if (var_override_.empty ())
- {
- semantics::class_* comp (composite (mi.t));
-
- // We need to increment the index even if we skipped this
- // member due to the schema version.
- //
- unsigned long long av (added (mi.m));
- unsigned long long dv (deleted (mi.m));
-
- if (comp != 0)
- {
- unsigned long long cav (added (*comp));
- unsigned long long cdv (deleted (*comp));
-
- if (cav != 0 && (av == 0 || av < cav))
- av = cav;
-
- if (cdv != 0 && (dv == 0 || dv > cdv))
- dv = cdv;
- }
-
- if (user_section* s = dynamic_cast<user_section*> (section_))
- {
- if (av == added (*s->member))
- av = 0;
-
- if (dv == deleted (*s->member))
- dv = 0;
- }
-
- if (av != 0 || dv != 0)
- os << "}";
-
- if (mi.ptr != 0 && view_member (mi.m))
- {
- // See column_count_impl for details on what's going on here.
- //
- column_count_type cc;
- if (semantics::class_* root = polymorphic (*mi.ptr))
- {
- for (semantics::class_* b (mi.ptr);; b = &polymorphic_base (*b))
- {
- column_count_type const& ccb (column_count (*b));
-
- cc.total += ccb.total - (b != root ? ccb.id : 0);
- cc.separate_load += ccb.separate_load;
-
- if (b == root)
- break;
- }
- }
- else
- cc = column_count (*mi.ptr);
-
- os << "n += " << cc.total - cc.separate_load << "UL;";
- }
- else if (comp != 0)
- {
- bool ro (readonly (*comp));
- column_count_type const& cc (column_count (*comp));
-
- os << "n += " << cc.total << "UL";
-
- // select = total
- // insert = total - inverse
- // update = total - inverse - readonly
- //
- if (cc.inverse != 0 || (!ro && cc.readonly != 0))
- {
- os << " - (" << endl
- << "sk == statement_select ? 0 : ";
-
- if (cc.inverse != 0)
- os << cc.inverse << "UL";
-
- if (!ro && cc.readonly != 0)
- {
- if (cc.inverse != 0)
- os << " + ";
-
- os << "(" << endl
- << "sk == statement_insert ? 0 : " <<
- cc.readonly << "UL)";
- }
-
- os << ")";
- }
-
- os << ";";
- }
- else
- os << "n++;";
-
- bool block (false);
-
- // The same logic as in pre().
- //
- if (!insert_send_auto_id && auto_ (mi.m))
- block = true;
- else if (section_ == 0 && separate_load (mi.m))
- block = true;
- else if (inverse (mi.m, key_prefix_) || version (mi.m))
- block = true;
- else if (!readonly (*context::top_object))
- {
- semantics::class_* c;
-
- if (id (mi.m) ||
- readonly (mi.m) ||
- ((c = composite (mi.t)) && readonly (*c)) ||
- (section_ == 0 && separate_update (mi.m)))
- block = true;
- }
-
- if (block)
- os << "}";
- else
- os << endl;
- }
- }
-
- virtual void
- traverse_pointer (member_info& mi)
- {
- // Object pointers in views require special treatment.
- //
- if (view_member (mi.m))
- {
- semantics::class_& c (*mi.ptr);
- semantics::class_* poly_root (polymorphic (c));
- bool poly_derived (poly_root != 0 && poly_root != &c);
-
- os << "object_traits_impl< " << class_fq_name (c) << ", id_" <<
- db << " >::bind (" << endl
- << "b + n, " << (poly_derived ? "0, 0, " : "") << arg << "." <<
- mi.var << "value, sk" << (versioned (c) ? ", svm" : "") << ");";
- }
- else
- member_base_impl<T>::traverse_pointer (mi);
- }
-
- virtual void
- traverse_composite (member_info& mi)
- {
- os << "composite_value_traits< " << mi.fq_type () << ", id_" <<
- db << " >::bind (" << endl
- << "b + n, " << arg << "." << mi.var << "value, sk" <<
- (versioned (*composite (mi.t)) ? ", svm" : "") << ");";
- }
-
- protected:
- string b;
- string arg;
- };
-
- struct bind_base: traversal::class_, virtual context
- {
- typedef bind_base base;
-
- virtual void
- traverse (type& c)
- {
- bool obj (object (c));
-
- // Ignore transient bases. Not used for views.
- //
- if (!(obj || composite (c)))
- return;
-
- os << "// " << class_name (c) << " base" << endl
- << "//" << endl;
-
- // If the derived class is readonly, then we will never be
- // called with sk == statement_update.
- //
- bool ro (readonly (c));
- bool check (ro && !readonly (*context::top_object));
-
- if (check)
- os << "if (sk != statement_update)"
- << "{";
-
- if (obj)
- os << "object_traits_impl< ";
- else
- os << "composite_value_traits< ";
-
- os << class_fq_name (c) << ", id_" << db << " >::bind (b + n, i, sk" <<
- (versioned (c) ? ", svm" : "") << ");";
-
- column_count_type const& cc (column_count (c));
-
- os << "n += ";
-
- // select = total - separate_load
- // insert = total - inverse - optimistic_managed - id(auto & !sending)
- // update = total - inverse - optimistic_managed - id - readonly -
- // separate_update
- //
- size_t select (cc.total - cc.separate_load);
- size_t insert (cc.total - cc.inverse - cc.optimistic_managed);
- size_t update (insert - cc.id - cc.readonly - cc.separate_update);
-
- data_member_path* id;
- if (!insert_send_auto_id && (id = id_member (c)) != 0 && auto_ (*id))
- insert -= cc.id;
-
- if (select == insert && insert == update)
- os << select << "UL;";
- else if (select != insert && insert == update)
- os << "sk == statement_select ? " << select << "UL : " <<
- insert << "UL;";
- else if (select == insert && insert != update)
- os << "sk == statement_update ? " << update << "UL : " <<
- select << "UL;";
- else
- os << "sk == statement_select ? " << select << "UL : " <<
- "sk == statement_insert ? " << insert << "UL : " <<
- update << "UL;";
-
- if (check)
- os << "}";
- else
- os << endl;
- }
- };
-
- //
- // grow
- //
-
- struct grow_member: virtual member_base
- {
- typedef grow_member base;
-
- grow_member (size_t& index,
- string const& var = string (),
- user_section* section = 0)
- : member_base (var, 0, 0, string (), string (), section),
- index_ (index) {}
-
- grow_member (size_t& index,
- string const& var,
- semantics::type& t,
- const custom_cxx_type* ct,
- string const& fq_type,
- string const& key_prefix)
- : member_base (var, &t, ct, fq_type, key_prefix), index_ (index) {}
-
- protected:
- size_t& index_;
- };
-
- template <typename T>
- struct grow_member_impl: grow_member, virtual member_base_impl<T>
- {
- typedef grow_member_impl base_impl;
-
- grow_member_impl (base const& x)
- : member_base::base (x), // virtual base
- base (x) {}
-
- typedef typename member_base_impl<T>::member_info member_info;
-
- virtual bool
- pre (member_info& mi)
- {
- if (container (mi))
- return false;
-
- // If we have a key prefix (container), then it can't be in a section
- // (while mi.m can). The same for top-level -- if we got called, then
- // we shouldn't ignore it. (Same logic as in has_grow_member).
- //
- if (!(!key_prefix_.empty () || top_level_ ||
- (section_ == 0 && !separate_load (mi.m)) ||
- (section_ != 0 && *section_ == section (mi.m))))
- return false;
-
- // Ignore polymorphic id references; they are not returned by
- // the select statement.
- //
- if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
- return false;
-
- std::ostringstream ostr;
- ostr << "t[" << index_ << "UL]";
- e = ostr.str ();
-
- if (var_override_.empty ())
- {
- os << "// " << mi.m.name () << endl
- << "//" << endl;
-
- semantics::class_* comp (composite (mi.t));
-
- // If the member is soft- added or deleted, check the version.
- //
- unsigned long long av (added (mi.m));
- unsigned long long dv (deleted (mi.m));
-
- // If this is a composite member, see if it is summarily
- // added/deleted.
- //
- if (comp != 0)
- {
- unsigned long long cav (added (*comp));
- unsigned long long cdv (deleted (*comp));
-
- if (cav != 0 && (av == 0 || av < cav))
- av = cav;
-
- if (cdv != 0 && (dv == 0 || dv > cdv))
- dv = cdv;
- }
-
- // If the addition/deletion version is the same as the section's,
- // then we don't need the test.
- //
- if (user_section* s = dynamic_cast<user_section*> (section_))
- {
- if (av == added (*s->member))
- av = 0;
-
- if (dv == deleted (*s->member))
- dv = 0;
- }
-
- if (av != 0 || dv != 0)
- {
- os << "if (";
-
- if (av != 0)
- os << "svm >= schema_version_migration (" << av << "ULL, true)";
-
- if (av != 0 && dv != 0)
- os << " &&" << endl;
-
- if (dv != 0)
- os << "svm <= schema_version_migration (" << dv << "ULL, true)";
-
- os << ")"
- << "{";
- }
- }
-
- return true;
- }
-
- virtual void
- post (member_info& mi)
- {
- semantics::class_* comp (composite (mi.t));
-
- if (var_override_.empty ())
- {
- unsigned long long av (added (mi.m));
- unsigned long long dv (deleted (mi.m));
-
- if (comp != 0)
- {
- unsigned long long cav (added (*comp));
- unsigned long long cdv (deleted (*comp));
-
- if (cav != 0 && (av == 0 || av < cav))
- av = cav;
-
- if (cdv != 0 && (dv == 0 || dv > cdv))
- dv = cdv;
- }
-
- if (user_section* s = dynamic_cast<user_section*> (section_))
- {
- if (av == added (*s->member))
- av = 0;
-
- if (dv == deleted (*s->member))
- dv = 0;
- }
-
- if (av != 0 || dv != 0)
- os << "}";
- }
-
- if (mi.ptr != 0 && view_member (mi.m))
- {
- // See column_count_impl for details on what's going on here.
- //
- column_count_type cc;
- if (semantics::class_* root = polymorphic (*mi.ptr))
- {
- for (semantics::class_* b (mi.ptr);; b = &polymorphic_base (*b))
- {
- column_count_type const& ccb (column_count (*b));
-
- cc.total += ccb.total - (b != root ? ccb.id : 0);
- cc.separate_load += ccb.separate_load;
-
- if (b == root)
- break;
- }
- }
- else
- cc = column_count (*mi.ptr);
-
- index_ += cc.total - cc.separate_load;
- }
- else if (comp != 0)
- index_ += column_count (*comp).total;
- else
- index_++;
- }
-
- virtual void
- traverse_pointer (member_info& mi)
- {
- // Object pointers in views require special treatment. They
- // can only be immediate members of the view class.
- //
- if (view_member (mi.m))
- {
- semantics::class_& c (*mi.ptr);
-
- os << "if (object_traits_impl< " << class_fq_name (c) <<
- ", id_" << db << " >::grow (" << endl
- << "i." << mi.var << "value, t + " << index_ << "UL" <<
- (versioned (c) ? ", svm" : "") << "))" << endl
- << "grew = true;"
- << endl;
- }
- else
- member_base_impl<T>::traverse_pointer (mi);
- }
-
- virtual void
- traverse_composite (member_info& mi)
- {
- semantics::class_& c (*composite (mi.t));
-
- os << "if (composite_value_traits< " << mi.fq_type () <<
- ", id_" << db << " >::grow (" << endl
- << "i." << mi.var << "value, t + " << index_ << "UL" <<
- (versioned (c) ? ", svm" : "") << "))" << endl
- << "grew = true;"
- << endl;
- }
-
- protected:
- string e;
- };
-
- struct grow_base: traversal::class_, virtual context
- {
- typedef grow_base base;
-
- grow_base (size_t& index): index_ (index) {}
-
- virtual void
- traverse (type& c)
- {
- bool obj (object (c));
-
- // Ignore transient bases. Not used for views.
- //
- if (!(obj || composite (c)))
- return;
-
- os << "// " << class_name (c) << " base" << endl
- << "//" << endl;
-
- os << "if (";
-
- if (obj)
- os << "object_traits_impl< ";
- else
- os << "composite_value_traits< ";
-
- os << class_fq_name (c) << ", id_" << db << " >::grow (" << endl
- << "i, t + " << index_ << "UL" <<
- (versioned (c) ? ", svm" : "") << "))" << endl
- << "grew = true;"
- << endl;
-
- index_ += column_count (c).total;
- }
-
- protected:
- size_t& index_;
- };
-
- //
- // init image
- //
-
- struct init_image_member: virtual member_base
- {
- typedef init_image_member base;
-
- init_image_member (string const& var = string (),
- string const& member = string (),
- user_section* section = 0)
- : member_base (var, 0, 0, string (), string (), section),
- member_override_ (member)
- {
- }
-
- init_image_member (string const& var,
- string const& member,
- semantics::type& t,
- const custom_cxx_type* ct,
- string const& fq_type,
- string const& key_prefix)
- : member_base (var, &t, ct, fq_type, key_prefix),
- member_override_ (member)
- {
- }
-
- protected:
- string member_override_;
- };
-
- template <typename T>
- struct init_image_member_impl: init_image_member,
- virtual member_base_impl<T>
- {
- typedef init_image_member_impl base_impl;
-
- init_image_member_impl (base const& x)
- : base (x),
- member_database_type_id_ (base::type_override_,
- base::custom_override_,
- base::fq_type_override_,
- base::key_prefix_)
- {
- }
-
- typedef typename member_base_impl<T>::member_info member_info;
-
- virtual void
- set_null (member_info&) = 0;
-
- virtual void
- check_accessor (member_info&, member_access&) {}
-
- virtual bool
- pre (member_info& mi)
- {
- // Ignore containers (they get their own table) and inverse
- // object pointers (they are not present in this binding).
- //
- if (container (mi) || inverse (mi.m, key_prefix_))
- return false;
-
- if (section_ != 0 && *section_ != section (mi.m))
- return false;
-
- // Ignore polymorphic id references; they are initialized in a
- // special way.
- //
- if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
- return false;
-
- semantics::class_* comp (composite (mi.t));
-
- if (!member_override_.empty ())
- {
- member = member_override_;
- os << "{";
- }
- else
- {
- // If we are generating standard init() and this member
- // contains version, ignore it.
- //
- if (version (mi.m))
- return false;
-
- // If we don't send auto id in INSERT statement, ignore this
- // member altogether (we never send auto id in UPDATE).
- //
- if (!insert_send_auto_id && auto_ (mi.m))
- return false;
-
- os << "// " << mi.m.name () << endl
- << "//" << endl;
-
- // If the member is soft- added or deleted, check the version.
- //
- unsigned long long av (added (mi.m));
- unsigned long long dv (deleted (mi.m));
-
- // If this is a composite member, see if it is summarily
- // added/deleted.
- //
- if (comp != 0)
- {
- unsigned long long cav (added (*comp));
- unsigned long long cdv (deleted (*comp));
-
- if (cav != 0 && (av == 0 || av < cav))
- av = cav;
-
- if (cdv != 0 && (dv == 0 || dv > cdv))
- dv = cdv;
- }
-
- // If the addition/deletion version is the same as the section's,
- // then we don't need the test.
- //
- if (user_section* s = dynamic_cast<user_section*> (section_))
- {
- if (av == added (*s->member))
- av = 0;
-
- if (dv == deleted (*s->member))
- dv = 0;
- }
-
- if (av != 0 || dv != 0)
- {
- os << "if (";
-
- if (av != 0)
- os << "svm >= schema_version_migration (" << av << "ULL, true)";
-
- if (av != 0 && dv != 0)
- os << " &&" << endl;
-
- if (dv != 0)
- os << "svm <= schema_version_migration (" << dv << "ULL, true)";
-
- os << ")"
- << "{";
- }
-
- // If the whole class is readonly, then we will never be
- // called with sk == statement_update.
- //
- if (!readonly (*context::top_object))
- {
- if (id (mi.m) ||
- readonly (mi.m) ||
- (section_ == 0 && separate_update (mi.m)) ||
- (comp != 0 && readonly (*comp))) // Can't be id.
- {
- // If we are generating section init(), then sk can only be
- // statement_update.
- //
- if (section_ == 0)
- os << "if (sk == statement_insert)";
- }
- }
-
- os << "{";
-
- if (discriminator (mi.m))
- member = "di.discriminator";
- else
- {
- // Get the member using the accessor expression.
- //
- member_access& ma (mi.m.template get<member_access> ("get"));
-
- // Make sure this kind of member can be accessed with this
- // kind of accessor (database-specific, e.g., streaming).
- //
- if (comp == 0)
- check_accessor (mi, ma);
-
- // If this is not a synthesized expression, then output
- // its location for easier error tracking.
- //
- if (!ma.synthesized)
- os << "// From " << location_string (ma.loc, true) << endl;
-
- // Use the original type to form the const reference. VC++
- // cannot grok the constructor syntax.
- //
- os << member_ref_type (mi.m, true, "v") << " =" << endl
- << " " << ma.translate ("o") << ";"
- << endl;
-
- member = "v";
- }
- }
-
- // Translate.
- //
- if (mi.ct != 0)
- {
- os << "// From " << location_string (mi.ct->loc, true) << endl
- << type_ref_type (*mi.ct->as, mi.ct->as_hint, true, "vt") <<
- " =" << endl
- << " " << mi.ct->translate_to (member) << ";"
- << endl;
-
- member = "vt";
- }
-
- // If this is a wrapped composite value, then we need to "unwrap"
- // it. If this is a NULL wrapper, then we also need to handle that.
- // For simple values this is taken care of by the value_traits
- // specializations.
- //
- if (mi.wrapper != 0 && comp != 0)
- {
- // The wrapper type, not the wrapped type.
- //
- string const& wt (mi.fq_type (false));
-
- // If this is a NULL wrapper and the member can be NULL, then
- // we need to handle the NULL value.
- //
- if (null (mi.m, key_prefix_) &&
- mi.wrapper->template get<bool> ("wrapper-null-handler"))
- {
- os << "if (wrapper_traits< " << wt << " >::get_null (" <<
- member << "))" << endl
- << "composite_value_traits< " << mi.fq_type () << ", id_" <<
- db << " >::set_null (" << endl
- << "i." << mi.var << "value, sk" <<
- (versioned (*comp) ? ", svm" : "") << ");"
- << "else"
- << "{";
- }
-
- os << "const" << mi.fq_type () << "& vw = " << endl
- << " wrapper_traits< " + wt + " >::get_ref (" + member + ");"
- << endl;
-
- member = "vw";
- }
-
- if (discriminator (mi.m))
- os << "const info_type& di (map->find (typeid (o)));"
- << endl;
-
- if (mi.ptr != 0)
- {
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- semantics::type& pt (utype (mi.m, key_prefix_));
-
- type = "obj_traits::id_type";
-
- // Handle NULL pointers and extract the id.
- //
- os << "typedef object_traits< " << class_fq_name (*mi.ptr) <<
- " > obj_traits;";
-
- if (weak_pointer (pt))
- {
- os << "typedef odb::pointer_traits< " << mi.ptr_fq_type () <<
- " > wptr_traits;"
- << "typedef odb::pointer_traits< wptr_traits::" <<
- "strong_pointer_type > ptr_traits;"
- << endl
- << "wptr_traits::strong_pointer_type sp (" <<
- "wptr_traits::lock (" << member << "));";
-
- member = "sp";
- }
- else
- os << "typedef odb::pointer_traits< " << mi.ptr_fq_type () <<
- " > ptr_traits;"
- << endl;
-
- os << "bool is_null (ptr_traits::null_ptr (" << member << "));"
- << "if (!is_null)"
- << "{"
- << "const " << type << "& ptr_id (" << endl;
-
- if (lazy_pointer (pt))
- os << "ptr_traits::object_id< ptr_traits::element_type > (" <<
- member << ")";
- else
- os << "obj_traits::id (ptr_traits::get_ref (" << member << "))";
-
- os << ");"
- << endl;
-
- member = "ptr_id";
- }
- else if (comp != 0)
- type = mi.fq_type ();
- else
- {
- type = mi.fq_type ();
-
- // Indicate to the value_traits whether this column can be NULL.
- //
- os << "bool is_null (" << null (mi.m, key_prefix_) << ");";
- }
-
- if (comp != 0)
- traits = "composite_value_traits< " + type + ", id_" +
- db.string () + " >";
- else
- {
- db_type_id = member_database_type_id_->database_type_id (mi.m);
- traits = db.string () + "::value_traits<\n "
- + type + ",\n "
- + db_type_id + " >";
- }
-
- return true;
- }
-
- virtual void
- post (member_info& mi)
- {
- semantics::class_* comp (composite (mi.t));
-
- if (mi.ptr != 0)
- {
- os << "}"
- << "else" << endl;
-
- if (!null (mi.m, key_prefix_))
- os << "throw null_pointer ();";
- else if (comp != 0)
- os << traits << "::set_null (i." << mi.var << "value, sk" <<
- (versioned (*comp) ? ", svm" : "") << ");";
- else
- set_null (mi);
- }
-
- if (mi.wrapper != 0 && comp != 0)
- {
- if (null (mi.m, key_prefix_) &&
- mi.wrapper->template get<bool> ("wrapper-null-handler"))
- os << "}";
- }
-
- os << "}";
-
- if (member_override_.empty ())
- {
- unsigned long long av (added (mi.m));
- unsigned long long dv (deleted (mi.m));
-
- if (comp != 0)
- {
- unsigned long long cav (added (*comp));
- unsigned long long cdv (deleted (*comp));
-
- if (cav != 0 && (av == 0 || av < cav))
- av = cav;
-
- if (cdv != 0 && (dv == 0 || dv > cdv))
- dv = cdv;
- }
-
- if (user_section* s = dynamic_cast<user_section*> (section_))
- {
- if (av == added (*s->member))
- av = 0;
-
- if (dv == deleted (*s->member))
- dv = 0;
- }
-
- if (av != 0 || dv != 0)
- os << "}";
- }
- }
-
- virtual void
- traverse_composite (member_info& mi)
- {
- bool grow (generate_grow &&
- context::grow (mi.m, mi.t, mi.ct, key_prefix_));
-
- if (grow)
- os << "if (";
-
- os << traits << "::init (" << endl
- << "i." << mi.var << "value," << endl
- << member << "," << endl
- << "sk";
-
- if (versioned (*composite (mi.t)))
- os << "," << endl
- << "svm";
-
- os << ")";
-
- if (grow)
- os << ")" << endl
- << "grew = true";
-
- os << ";";
- }
-
- protected:
- string type;
- string db_type_id;
- string member;
- string traits;
-
- instance<member_database_type_id> member_database_type_id_;
- };
-
- struct init_image_base: traversal::class_, virtual context
- {
- typedef init_image_base base;
-
- virtual void
- traverse (type& c)
- {
- bool obj (object (c));
-
- // Ignore transient bases. Not used for views.
- //
- if (!(obj || composite (c)))
- return;
-
- os << "// " << class_name (c) << " base" << endl
- << "//" << endl;
-
- // If the derived class is readonly, then we will never be
- // called with sk == statement_update.
- //
- bool check (readonly (c) && !readonly (*context::top_object));
-
- if (check)
- os << "if (sk != statement_update)"
- << "{";
-
- if (generate_grow)
- os << "if (";
-
- if (obj)
- os << "object_traits_impl< ";
- else
- os << "composite_value_traits< ";
-
- os << class_fq_name (c) << ", id_" << db << " >::init (i, o, sk" <<
- (versioned (c) ? ", svm" : "") << ")";
-
- if (generate_grow)
- os << ")" << endl
- << "grew = true";
-
- os << ";";
-
- if (check)
- os << "}";
- else
- os << endl;
- }
- };
-
- //
- // init value
- //
-
- struct init_value_member: virtual member_base
- {
- typedef init_value_member base;
-
- init_value_member (string const& member = string (),
- string const& var = string (),
- bool ignore_implicit_discriminator = true,
- user_section* section = 0)
- : member_base (var, 0, 0, string (), string (), section),
- member_override_ (member),
- ignore_implicit_discriminator_ (ignore_implicit_discriminator)
- {
- }
-
- init_value_member (string const& var,
- string const& member,
- semantics::type& t,
- const custom_cxx_type* ct,
- string const& fq_type,
- string const& key_prefix)
- : member_base (var, &t, ct, fq_type, key_prefix),
- member_override_ (member),
- ignore_implicit_discriminator_ (true)
- {
- }
-
- virtual void
- get_null (string const& /*var*/) const {};
-
- protected:
- string member_override_;
- bool ignore_implicit_discriminator_;
- };
-
- template <typename T>
- struct init_value_member_impl: init_value_member,
- virtual member_base_impl<T>
- {
- typedef init_value_member_impl base_impl;
-
- init_value_member_impl (base const& x)
- : base (x),
- member_database_type_id_ (base::type_override_,
- base::custom_override_,
- base::fq_type_override_,
- base::key_prefix_)
- {
- }
-
- typedef typename member_base_impl<T>::member_info member_info;
-
- virtual void
- get_null (string const& var) const = 0;
-
- virtual void
- check_modifier (member_info&, member_access&) {}
-
- virtual bool
- pre (member_info& mi)
- {
- if (container (mi))
- return false;
-
- if (section_ != 0 && *section_ != section (mi.m))
- return false;
-
- // Ignore polymorphic id references; they are initialized in a
- // special way.
- //
- if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
- return false;
-
- // Ignore implicit discriminators.
- //
- if (ignore_implicit_discriminator_ && discriminator (mi.m))
- return false;
-
- semantics::class_* comp (composite (mi.t));
-
- if (!member_override_.empty ())
- {
- os << "{";
- member = member_override_;
- }
- else
- {
- // Ignore separately loaded members.
- //
- if (section_ == 0 && separate_load (mi.m))
- return false;
-
- os << "// " << mi.m.name () << endl
- << "//" << endl;
-
- // If the member is soft- added or deleted, check the version.
- //
- unsigned long long av (added (mi.m));
- unsigned long long dv (deleted (mi.m));
-
- // If this is a composite member, see if it is summarily
- // added/deleted.
- //
- if (comp != 0)
- {
- unsigned long long cav (added (*comp));
- unsigned long long cdv (deleted (*comp));
-
- if (cav != 0 && (av == 0 || av < cav))
- av = cav;
-
- if (cdv != 0 && (dv == 0 || dv > cdv))
- dv = cdv;
- }
-
- // If the addition/deletion version is the same as the section's,
- // then we don't need the test.
- //
- if (user_section* s = dynamic_cast<user_section*> (section_))
- {
- if (av == added (*s->member))
- av = 0;
-
- if (dv == deleted (*s->member))
- dv = 0;
- }
-
- if (av != 0 || dv != 0)
- {
- os << "if (";
-
- if (av != 0)
- os << "svm >= schema_version_migration (" << av << "ULL, true)";
-
- if (av != 0 && dv != 0)
- os << " &&" << endl;
-
- if (dv != 0)
- os << "svm <= schema_version_migration (" << dv << "ULL, true)";
-
- os << ")";
- }
-
- os << "{";
-
- if (mi.ptr != 0 && view_member (mi.m))
- return true; // That's enough for the object pointer in view.
-
- // Set the member using the modifier expression.
- //
- member_access& ma (mi.m.template get<member_access> ("set"));
-
- // Make sure this kind of member can be modified with this
- // kind of accessor (database-specific, e.g., streaming).
- //
- if (comp == 0)
- check_modifier (mi, ma);
-
- // If this is not a synthesized expression, then output
- // its location for easier error tracking.
- //
- if (!ma.synthesized)
- os << "// From " << location_string (ma.loc, true) << endl;
-
- // See if we are modifying via a reference or proper modifier.
- //
- if (ma.placeholder ())
- os << member_val_type (mi.m, false, "v") << ";"
- << endl;
- else
- {
- // Use the original type to form the reference. VC++ cannot
- // grok the constructor syntax.
- //
- os << member_ref_type (mi.m, false, "v") << " =" << endl
- << " ";
-
- // If this member is const and we have a synthesized direct
- // access, then cast away constness. Otherwise, we assume
- // that the user-provided expression handles this.
- //
- bool cast (mi.cq && ma.direct ());
- if (cast)
- os << "const_cast< " << member_ref_type (mi.m, false) <<
- " > (" << endl;
-
- os << ma.translate ("o");
-
- if (cast)
- os << ")";
-
- os << ";"
- << endl;
- }
-
- member = "v";
- }
-
- // Translate.
- //
- if (mi.ct != 0)
- {
- os << type_val_type (*mi.ct->as, mi.ct->as_hint, false, "vt") << ";"
- << endl;
-
- translate_member = member;
- member = "vt";
- }
-
- // If this is a wrapped composite value, then we need to "unwrap" it.
- // If this is a NULL wrapper, then we also need to handle that. For
- // simple values this is taken care of by the value_traits
- // specializations.
- //
- if (mi.wrapper != 0 && comp != 0)
- {
- // The wrapper type, not the wrapped type.
- //
- string const& wt (mi.fq_type (false));
-
- // If this is a NULL wrapper and the member can be NULL, then
- // we need to handle the NULL value.
- //
- if (null (mi.m, key_prefix_) &&
- mi.wrapper->template get<bool> ("wrapper-null-handler"))
- {
- os << "if (composite_value_traits< " << mi.fq_type () <<
- ", id_" << db << " >::get_null (" << endl
- << "i." << mi.var << "value" <<
- (versioned (*comp) ? ", svm" : "") << "))" << endl
- << "wrapper_traits< " << wt << " >::set_null (" << member + ");"
- << "else"
- << "{";
- }
-
- os << mi.fq_type () << "& vw =" << endl
- << " wrapper_traits< " + wt + " >::set_ref (" + member + ");"
- << endl;
-
- wrap_member = member;
- member = "vw";
- }
-
- if (mi.ptr != 0)
- {
- type = "obj_traits::id_type";
-
- // Handle NULL pointers and extract the id.
- //
- os << "typedef object_traits< " << class_fq_name (*mi.ptr) <<
- " > obj_traits;"
- << "typedef odb::pointer_traits< " << mi.ptr_fq_type () <<
- " > ptr_traits;"
- << endl;
-
- os << "if (";
-
- if (comp != 0)
- os << "composite_value_traits< " << type << ", id_" << db <<
- " >::get_null (" << endl
- << "i." << mi.var << "value" <<
- (versioned (*comp) ? ", svm" : "") << ")";
- else
- get_null (mi.var);
-
- os << ")" << endl;
-
- // Don't throw null_pointer if we can't have NULLs and the pointer
- // is NULL since this can be useful during migration. Instead, we
- // rely on the database enforcing this.
- //
- os << member << " = ptr_traits::pointer_type ();";
-
- os << "else"
- << "{";
-
- os << type << " ptr_id;";
-
- member = "ptr_id";
- }
- else
- type = mi.fq_type ();
-
- if (comp != 0)
- traits = "composite_value_traits< " + type + ", id_" +
- db.string () + " >";
- else
- {
- db_type_id = member_database_type_id_->database_type_id (mi.m);
- traits = db.string () + "::value_traits<\n "
- + type + ",\n "
- + db_type_id + " >";
- }
-
- return true;
- }
-
- virtual void
- post (member_info& mi)
- {
- if (mi.ptr != 0)
- {
- if (view_member (mi.m))
- {
- // The object pointer in view doesn't need any of this.
- os << "}";
- return;
- }
-
- // Restore the member variable name.
- //
- member = member_override_.empty () ? "v" : member_override_;
-
- // When handling a pointer, mi.t is the id type of the referenced
- // object.
- //
- semantics::type& pt (utype (mi.m, key_prefix_));
-
- if (lazy_pointer (pt))
- os << member << " = ptr_traits::pointer_type (" << endl
- << "*static_cast<" << db << "::database*> (db), ptr_id);";
- else
- {
- os << "// If a compiler error points to the line below, then" << endl
- << "// it most likely means that a pointer used in a member" << endl
- << "// cannot be initialized from an object pointer." << endl
- << "//" << endl
- << member << " = ptr_traits::pointer_type (" << endl
- << "static_cast<" << db << "::database*> (db)->load<" << endl
- << " obj_traits::object_type > (ptr_id));";
-
- // If we are loading into an eager weak pointer, make sure there
- // is someone else holding a strong pointer to it (normally a
- // session). Otherwise, the object will be loaded and immediately
- // deleted. Besides not making much sense, this also breaks the
- // delayed loading machinery which expects the object to be around
- // at least until the top-level load() returns.
- //
- if (weak_pointer (pt))
- {
- os << endl
- << "if (odb::pointer_traits<" <<
- "ptr_traits::strong_pointer_type>::null_ptr (" << endl
- << "ptr_traits::lock (" << member << ")))" << endl
- << "throw session_required ();";
- }
- }
-
- os << "}";
- }
-
- // Wrap back (so to speak).
- //
- if (mi.wrapper != 0 && composite (mi.t) != 0)
- {
- if (null (mi.m, key_prefix_) &&
- mi.wrapper->template get<bool> ("wrapper-null-handler"))
- os << "}";
-
- member = wrap_member;
- }
-
- // Untranslate.
- //
- if (mi.ct != 0)
- {
- //@@ Use move() in C++11? Or not.
- //
- os << "// From " << location_string (mi.ct->loc, true) << endl
- << translate_member << " = " <<
- mi.ct->translate_from (member) << ";";
-
- member = translate_member;
- }
-
- // Call the modifier if we are using a proper one.
- //
- if (member_override_.empty ())
- {
- member_access& ma (mi.m.template get<member_access> ("set"));
-
- if (ma.placeholder ())
- {
- // If this is not a synthesized expression, then output its
- // location for easier error tracking.
- //
- if (!ma.synthesized)
- os << "// From " << location_string (ma.loc, true) << endl;
-
- os << ma.translate (
- "o", "v", "*static_cast<" + db.string () + "::database*> (db)")
- << ";";
- }
- }
-
- os << "}";
- }
-
- virtual void
- traverse_pointer (member_info& mi)
- {
- // Object pointers in views require special treatment.
- //
- if (view_member (mi.m))
- {
- // This is the middle part. The pre and post parts are generated
- // by init_view_pointer_member below.
- //
- using semantics::class_;
-
- class_& c (*mi.ptr);
- class_* poly_root (polymorphic (c));
- bool poly (poly_root != 0);
- bool poly_derived (poly && poly_root != &c);
-
- string o_tp (mi.var + "object_type");
- string o_tr (mi.var + "object_traits");
- string r_tr (poly_derived ? mi.var + "root_traits" : o_tr);
- string i_tp (mi.var + "info_type");
-
- string id (mi.var + "id");
- string o (mi.var + "o");
- string pi (mi.var + "pi"); // Polymorphic type info.
-
- // If load_() will be loading containers or the rest of the
- // polymorphic object, then we need to perform several extra
- // things. We need to initialize the id image in the object
- // statements. We also have to lock the statements so that
- // nobody messes up this id image.
- //
- bool init_id (
- poly ||
- has_a (c, test_container | include_eager_load, &main_section));
-
- bool versioned (context::versioned (c));
-
- os << "if (" << o << " != 0)"
- << "{";
-
- if (poly)
- os << "callback_event ce (callback_event::pre_load);"
- << pi << "->dispatch (" << i_tp << "::call_callback, " <<
- "*db, " << o << ", &ce);";
- else
- os << o_tr << "::callback (*db, *" << o <<
- ", callback_event::pre_load);";
-
- os << o_tr << "::init (*" << o << ", i." << mi.var << "value, db" <<
- (versioned ? ", svm" : "") << ");";
-
- // Call load_() to load the rest of the object (containers, etc).
- //
- if (id_member (poly ? *poly_root : c) != 0)
- {
- const char* s (poly_derived ? "osts" : "sts");
-
- os << o_tr << "::statements_type& " << s << " (" << endl
- << "conn.statement_cache ().find_object<" << o_tp << "> ());";
-
- if (poly_derived)
- os << r_tr << "::statements_type& sts (osts.root_statements ());";
-
- if (init_id)
- {
- // This can only be top-level call so lock must succeed.
- //
- os << r_tr << "::statements_type::auto_lock l (sts);"
- << "assert (l.locked ()) /* Must be a top-level call. */;"
- << endl
- << r_tr << "::id_image_type& i (sts.id_image ());"
- << r_tr << "::init (i, " << id << ");"
- << db << "::binding& idb (sts.id_image_binding ());"
- << "if (i.version != sts.id_image_version () || " <<
- "idb.version == 0)"
- << "{"
- << r_tr << "::bind (idb.bind, i);"
- << "sts.id_image_version (i.version);"
- << "idb.version++;";
- if (optimistic (poly ? *poly_root : c) != 0)
- os << "sts.optimistic_id_image_binding ().version++;";
- os << "}";
- }
-
- os << o_tr << "::load_ (" << s << ", *" << o << ", false" <<
- (versioned ? ", svm" : "") << ");";
-
- // Load the dynamic part of the object unless static and dynamic
- // types are the same.
- //
- if (poly)
- os << endl
- << "if (" << pi << " != &" << o_tr << "::info)"
- << "{"
- << "std::size_t d (" << o_tr << "::depth);"
- << pi << "->dispatch (" << i_tp << "::call_load, *db, " <<
- o << ", &d);"
- << "}";
-
- if (init_id)
- os << "sts.load_delayed (" << (versioned ? "&svm" : "0") << ");"
- << "l.unlock ();";
- }
-
- os << "}";
- }
- else
- member_base_impl<T>::traverse_pointer (mi);
- }
-
- virtual void
- traverse_composite (member_info& mi)
- {
- os << traits << "::init (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "db";
-
- if (versioned (*composite (mi.t)))
- os << "," << endl
- << "svm";
-
- os << ");"
- << endl;
- }
-
- protected:
- string type;
- string db_type_id;
- string traits;
- string member;
- string translate_member; // Untranslated member.
- string wrap_member; // Wrapped member.
-
- instance<member_database_type_id> member_database_type_id_;
- };
-
- // This class generates the pre and post parts. The middle part is
- // generated by init_value_member above.
- //
- struct init_view_pointer_member: virtual member_base,
- member_base_impl<bool> // Dummy SQL type.
- {
- typedef init_view_pointer_member base;
-
- init_view_pointer_member (bool pre, init_value_member const& ivm)
- : member_base (0, 0, string (), string (), 0),
- pre_ (pre), init_value_member_ (ivm) {}
-
- virtual bool
- pre (member_info& mi)
- {
- // Only interested in object pointers inside views.
- //
- return mi.ptr != 0 && view_member (mi.m);
- }
-
- virtual void
- traverse_pointer (member_info& mi)
- {
- using semantics::class_;
-
- class_& c (*mi.ptr);
- bool abst (abstract (c));
- class_* poly_root (polymorphic (c));
- bool poly (poly_root != 0);
- bool poly_derived (poly && poly_root != &c);
- size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1);
-
- data_member_path* idm (id_member (poly ? *poly_root : c));
-
- os << "// " << mi.m.name () << (pre_ ? " pre" : " post") << endl
- << "//" << endl;
-
- string o_tp (mi.var + "object_type");
- string o_tr (mi.var + "object_traits");
- string r_tr (poly_derived ? mi.var + "root_traits" : o_tr);
- string i_tp (mi.var + "info_type");
- string p_tp (mi.var + "pointer_type");
- string p_tr (mi.var + "pointer_traits");
- string c_tr (mi.var + "cache_traits");
-
- string id (mi.var + "id"); // Object id.
- string p (mi.var + "p"); // Object pointer.
- string pg (mi.var + "pg"); // Pointer guard.
- string ig (mi.var + "ig"); // Cache insert guard.
- string o (mi.var + "o"); // Object.
- string pi (mi.var + "pi"); // Polymorphic type info.
-
- bool op_raw (c.get<bool> ("object-pointer-raw"));
- bool mp_raw (utype (mi.m).is_a<semantics::pointer> ());
-
- // Output aliases and variables before any schema version if-
- // blocks since we need to be able to access them across all
- // three parts.
- //
- if (pre_)
- {
- os << "typedef " << class_fq_name (c) << " " << o_tp << ";"
- << "typedef object_traits_impl<" << o_tp << ", id_" << db <<
- "> " << o_tr << ";";
-
- if (poly_derived)
- os << "typedef " << o_tr << "::root_traits " << r_tr << ";";
-
- if (poly)
- os << "typedef " << r_tr << "::info_type " << i_tp << ";";
-
- os << "typedef " << r_tr << "::pointer_type " << p_tp << ";"
- << "typedef " << r_tr << "::pointer_traits " << p_tr << ";";
- if (idm != 0)
- os << "typedef " << r_tr << "::pointer_cache_traits " <<
- c_tr << ";";
- os << endl;
-
- if (idm != 0)
- os << r_tr << "::id_type " << id << ";";
- os << p_tp << " " << p << (op_raw ? " = 0" : "") << ";" // VC++
- << p_tr << "::guard " << pg << ";";
- if (idm != 0)
- os << c_tr << "::insert_guard " << ig << ";";
- os << o_tp << "* " << o << " (0);";
-
- if (poly)
- os << "const " << i_tp << "* " << pi << " = 0;"; // VC++
-
- os << endl;
- }
-
- // If the member is soft- added or deleted, check the version.
- //
- unsigned long long av (added (mi.m));
- unsigned long long dv (deleted (mi.m));
-
- if (av != 0 || dv != 0)
- {
- os << "if (";
-
- if (av != 0)
- os << "svm >= schema_version_migration (" << av << "ULL, true)";
-
- if (av != 0 && dv != 0)
- os << " &&" << endl;
-
- if (dv != 0)
- os << "svm <= schema_version_migration (" << dv << "ULL, true)";
-
- os << ")";
- }
-
- os << "{";
-
- if (pre_)
- {
- string id_im;
- if (idm != 0)
- {
- // Check for NULL.
- //
- string id_var;
- {
- id_im = mi.var + "value";
-
- // In a polymorphic class, the id is in the root image.
- //
- for (size_t i (0); i < poly_depth - 1; ++i)
- id_im += (i == 0 ? ".base" : "->base");
-
- string n;
- for (data_member_path::const_iterator i (idm->begin ());
- i != idm->end ();
- ++i)
- {
- // The same logic as in member_base.
- //
- if (!n.empty ())
- n += "value."; // Composite.
-
- string const& name ((*i)->name ());
- n += name;
-
- if (n[n.size () - 1] != '_')
- n += '_';
- }
-
- id_var = id_im + (poly_derived ? "->" : ".") + n;
- id_im = (poly_derived ? "*i." : "i.") + id_im;
- }
-
- os << "if (";
-
- if (semantics::class_* comp = composite (mi.t))
- os << "!composite_value_traits< " << o_tr << "::id_type, id_" <<
- db << " >::get_null (" << endl
- << "i." << id_var << "value" <<
- (versioned (*comp) ? ", svm" : "") << ")";
- else
- {
- os << "!(";
- init_value_member_.get_null (id_var);
- os << ")";
- }
-
- os << ")"
- << "{";
-
- // Check cache.
- //
- os << id << " = " << r_tr << "::id (" << id_im << ");"
- << p << " = " << c_tr << "::find (*db, " << id << ");"
- << endl;
-
- os << "if (" << p_tr << "::null_ptr (" << p << "))"
- << "{";
- }
-
- // To support by-value object loading, we are going to load
- // into an existing instance if the pointer is already not
- // NULL. To limit the potential misuse (especially when it
- // comes to sessions), we are going to limit this support
- // only to raw pointers. Furthermore, we will only insert
- // such an object into the cache if its object pointer is
- // also raw.
- //
- if (mp_raw && !poly)
- {
- // Get the member using the accessor expression.
- //
- member_access& ma (mi.m.get<member_access> ("get"));
-
- // If this is not a synthesized expression, then output
- // its location for easier error tracking.
- //
- if (!ma.synthesized)
- os << "// From " << location_string (ma.loc, true) << endl;
-
- // Use the original type to form the const reference. VC++
- // cannot grok the constructor syntax.
- //
- os << member_ref_type (mi.m, true, "m") << " =" << endl
- << " " << ma.translate ("o") << ";"
- << endl;
-
- os << "if (m != 0)"
- << "{";
-
- if (op_raw)
- os << ig << ".reset (" << c_tr << "::insert (*db, " << id <<
- ", m));";
-
- os << o << " = m;"
- << "}"
- << "else"
- << "{";
- }
-
- if (poly)
- {
- os << r_tr << "::discriminator_type d (" << endl
- << r_tr << "::discriminator (" << id_im << "));";
-
- if (abst)
- os << pi << " = &" << r_tr << "::map->find (d);";
- else
- os << pi << " = (d == " << o_tr << "::info.discriminator" << endl
- << "? &" << o_tr << "::info" << endl
- << ": &" << r_tr << "::map->find (d));";
-
- os << p << " = " << pi << "->create ();";
- }
- else
- os << p << " = object_factory<" << o_tp << ", " << p_tp <<
- ">::create ();";
-
- os << pg << ".reset (" << p << ");";
- if (idm != 0)
- os << ig << ".reset (" << c_tr << "::insert (*db, " << id <<
- ", " << p << "));";
-
- if (poly_derived)
- os << o << " = static_cast<" << o_tp << "*> (" << p_tr <<
- "::get_ptr (" << p << "));";
- else
- os << o << " = " << p_tr << "::get_ptr (" << p << ");";
-
- if (mp_raw && !poly)
- os << "}";
-
- if (idm != 0)
- os << "}" // Cache.
- << "}"; // NULL.
- }
- else
- {
- os << "if (" << o << " != 0)"
- << "{";
-
- if (poly)
- os << "callback_event ce (callback_event::post_load);"
- << pi << "->dispatch (" << i_tp << "::call_callback, " <<
- "*db, " << o << ", &ce);";
- else
- os << o_tr << "::callback (*db, *" << o <<
- ", callback_event::post_load);";
-
- if (idm != 0)
- {
- if (mp_raw && !op_raw && !poly)
- os << "if (!" << p_tr << "::null_ptr (" << p << "))"
- << "{";
-
- os << c_tr << "::load (" << ig << ".position ());"
- << ig << ".release ();";
-
- if (mp_raw && !op_raw && !poly)
- os << "}";
- }
-
- os << pg << ".release ();";
-
- os << "}";
-
- // If member pointer is not raw, then result is in p.
- // If both member and object are raw, then result is in o.
- // If member is raw but object is not, then result is in
- // p if it is not NULL, and in o (either NULL or the same
- // as the member value) otherwise.
- //
- member_access& ma (mi.m.get<member_access> ("set"));
-
- if (ma.empty () && !poly)
- {
- // It is ok to have empty modifier expression as long as
- // the member pointer is raw. This is the by-value load
- // and the user is not interested in learning whether the
- // object is NULL.
- //
- if (!mp_raw)
- {
- error (ma.loc) << "non-empty modifier expression required " <<
- "for loading an object via a smart pointer" << endl;
- throw operation_failed ();
- }
-
- os << "// Empty modifier expression was specified for this\n"
- << "// object so make sure we have actually loaded the\n"
- << "// data into the existing instance rather than, say,\n"
- << "// finding the object in the cache or creating a new one.\n"
- << "//\n"
- << "assert (" << p_tr << "::null_ptr (" << p << "));";
- }
- else
- {
- if (!(mp_raw && op_raw) || poly)
- {
- string r (options.std () >= cxx_version::cxx11
- ? "std::move (" + p + ")"
- : p);
-
- if (poly_derived)
- // This pointer could have come from cache, so use dynamic
- // cast.
- //
- r = p_tr + "::dynamic_pointer_cast<" + o_tp + "> (\n" +
- r + ")";
-
- // Unless the pointer is raw, explicitly construct the
- // smart pointer from the object pointer so that we get
- // the behavior similar to calling database::load() (in
- // both cases we are the "ownership end-points"; unless
- // the object was already in the session before loading
- // this view (in which case using raw pointers as object
- // pointers is a really stupid idea), this logic will do
- // the right thing and what the user most likely expects.
- //
- if (!mp_raw)
- r = member_val_type (mi.m, false) + " (\n" + r + ")";
-
- if (mp_raw && !op_raw)
- os << "if (!" << p_tr << "::null_ptr (" << p << "))" << endl;
-
- os << "// If a compiler error points to the line below, then\n"
- << "// it most likely means that a pointer used in view\n"
- << "// member cannot be initialized from an object pointer.\n"
- << "//" << endl;
-
- set_member (mi.m, "o", r, "db");
- }
-
- if (mp_raw && !poly)
- {
- if (!op_raw)
- os << "else" << endl; // NULL p
-
- set_member (mi.m, "o", o, "db");
- }
- }
- }
-
- os << "}";
- }
-
- virtual bool const&
- member_sql_type (semantics::data_member&) {return pre_;};
-
- protected:
- bool pre_;
- init_value_member const& init_value_member_;
- };
-
- struct init_value_base: traversal::class_, virtual context
- {
- typedef init_value_base base;
-
- virtual void
- traverse (type& c)
- {
- bool obj (object (c));
-
- // Ignore transient bases. Not used for views.
- //
- if (!(obj || composite (c)))
- return;
-
- os << "// " << class_name (c) << " base" << endl
- << "//" << endl;
-
- if (obj)
- os << "object_traits_impl< ";
- else
- os << "composite_value_traits< ";
-
- os << class_fq_name (c) << ", id_" << db << " >::init (o, i, db" <<
- (versioned (c) ? ", svm" : "") << ");"
- << endl;
- }
- };
-
- // Member-specific traits types for container members.
- //
- struct container_traits: object_members_base, virtual context
- {
- typedef container_traits base;
-
- container_traits (semantics::class_& c)
- : object_members_base (
- true,
- object (c), // Only build table prefix for objects.
- false),
- c_ (c)
- {
- scope_ = object (c)
- ? "access::object_traits_impl< "
- : "access::composite_value_traits< ";
-
- scope_ += class_fq_name (c) + ", id_" + db.string () + " >";
- }
-
- // Unless the database system can execute several interleaving
- // statements, cache the result set.
- //
- virtual void
- cache_result (string const& statement)
- {
- os << statement << ".cache ();";
- }
-
- // Additional code that need to be executed following the call to
- // init_value.
- //
- virtual void
- init_value_extra ()
- {
- }
-
- virtual void
- process_statement_columns (statement_columns&,
- statement_kind,
- bool /*dynamic*/)
- {
- }
-
- virtual void
- traverse_pointer (semantics::data_member&, semantics::class_&)
- {
- // We don't want to traverse composite id.
- }
-
- virtual void
- traverse_composite (semantics::data_member* m, semantics::class_& c)
- {
- if (object (c_))
- object_members_base::traverse_composite (m, c);
- else
- {
- // If we are generating traits for a composite value type, then
- // we don't want to go into its bases or it composite members.
- //
- if (m == 0 && &c == &c_)
- names (c);
- }
- }
-
- virtual void
- container_extra (semantics::data_member&, semantics::type&)
- {
- }
-
- virtual void
- traverse_container (semantics::data_member& m, semantics::type& t)
- {
- using semantics::type;
-
- // Figure out if this member is from a base object or composite
- // value and if it's from an object, whether it is reuse-abstract.
- //
- bool base, reuse_abst;
-
- if (object (c_))
- {
- base = cur_object != &c_ ||
- !object (dynamic_cast<type&> (m.scope ()));
- reuse_abst = abstract (c_) && !polymorphic (c_);
- }
- else
- {
- base = false; // We don't go into bases.
- reuse_abst = true; // Always abstract.
- }
-
- container_kind_type ck (container_kind (t));
-
- const custom_cxx_type* vct (0);
- const custom_cxx_type* ict (0);
- const custom_cxx_type* kct (0);
-
- type& vt (container_vt (m, &vct));
- type* it (0);
- type* kt (0);
-
- data_member_path* imp (context::inverse (m, "value"));
-
- bool ordered (false);
- bool inverse (imp != 0);
- bool grow (false);
-
- switch (ck)
- {
- case ck_ordered:
- {
- if (!unordered (m))
- {
- it = &container_it (m, &ict);
- ordered = true;
-
- if (generate_grow)
- grow = grow || context::grow (m, *it, ict, "index");
- }
-
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- kt = &container_kt (m, &kct);
-
- if (generate_grow)
- grow = grow || context::grow (m, *kt, kct, "key");
-
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- break;
- }
- }
-
- bool smart (!inverse &&
- (ck != ck_ordered || ordered) &&
- container_smart (t));
-
- if (generate_grow)
- grow = grow || context::grow (m, vt, vct, "value");
-
- bool eager_ptr (is_a (member_path_,
- member_scope_,
- test_eager_pointer,
- vt,
- "value"));
- if (!eager_ptr)
- {
- if (semantics::class_* cvt = composite_wrapper (vt))
- eager_ptr = has_a (*cvt, test_eager_pointer);
- }
-
- bool versioned (context::versioned (m));
-
- string name (flat_prefix_ + public_name (m) + "_traits");
- string scope (scope_ + "::" + name);
-
- os << "// " << m.name () << endl
- << "//" << endl
- << endl;
-
- container_extra (m, t);
-
- //
- // Statements.
- //
- if (!reuse_abst)
- {
- string sep (versioned ? "\n" : " ");
-
- semantics::type& idt (container_idt (m));
-
- qname table (table_name (m, table_prefix_));
- string qtable (quote_id (table));
- instance<object_columns_list> id_cols;
- instance<object_columns_list> ik_cols; // index/key columns
-
- if (smart)
- {
- switch (ck)
- {
- case ck_ordered:
- {
- ik_cols->traverse (m, *it, "index", "index");
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- break;
- }
- }
- }
-
- // select_statement
- //
- os << "const char " << scope << "::" << endl
- << "select_statement[] =" << endl;
-
- if (inverse)
- {
- semantics::class_* c (object_pointer (vt));
- semantics::data_member& imf (*imp->front ());
- semantics::data_member& imb (*imp->back ());
-
- // In a polymorphic hierarchy the inverse member can be in
- // the base class, in which case we should use that class
- // for the table name, etc.
- //
- if (polymorphic (*c))
- c = &dynamic_cast<semantics::class_&> (imf.scope ());
-
- data_member_path& inv_id (*id_member (*c));
-
- qname inv_table; // Other table name.
- string inv_qtable;
- instance<object_columns_list> inv_id_cols; // Other id column.
- instance<object_columns_list> inv_fid_cols; // Other foreign id
- // column (ref to us).
- statement_columns sc;
-
- if (container (imb))
- {
- // many(i)-to-many
- //
-
- // This other container is a direct member of the class so the
- // table prefix is just the class table name.
- //
- inv_table = table_name (*c, *imp);
- inv_qtable = quote_id (inv_table);
-
- inv_id_cols->traverse (imb, utype (inv_id), "id", "object_id", c);
- inv_fid_cols->traverse (imb, idt, "value", "value");
-
- for (object_columns_list::iterator i (inv_id_cols->begin ());
- i != inv_id_cols->end (); ++i)
- {
- // If this is a simple id, then pass the "id" key prefix. If
- // it is a composite id, then the members have no prefix.
- //
- sc.push_back (
- statement_column (
- inv_qtable,
- inv_qtable + "." + quote_id (i->name),
- i->type,
- *i->member,
- inv_id_cols->size () == 1 ? "id" : ""));
- }
- }
- else
- {
- // many(i)-to-one
- //
- inv_table = table_name (*c);
- inv_qtable = quote_id (inv_table);
-
- inv_id_cols->traverse (inv_id);
- inv_fid_cols->traverse (imb, column_prefix (*imp));
-
- for (object_columns_list::iterator i (inv_id_cols->begin ());
- i != inv_id_cols->end (); ++i)
- {
- sc.push_back (
- statement_column (
- inv_qtable,
- inv_qtable + "." + quote_id (i->name),
- i->type,
- *i->member));
- }
- }
-
- process_statement_columns (sc, statement_select, versioned);
-
- os << strlit ("SELECT" + sep) << endl;
-
- for (statement_columns::const_iterator i (sc.begin ()),
- e (sc.end ()); i != e;)
- {
- string const& c (i->column);
- os << strlit (c + (++i != e ? "," : "") + sep) << endl;
- }
-
- instance<query_parameters> qp (statement_select, inv_table);
- os << strlit ("FROM " + inv_qtable + sep) << endl;
-
- string where ("WHERE ");
- for (object_columns_list::iterator b (inv_fid_cols->begin ()),
- i (b); i != inv_fid_cols->end (); ++i)
- {
- if (i != b)
- where += " AND ";
-
- where += inv_qtable + "." + quote_id (i->name) + "=" +
- convert_to (qp->next (*i), i->type, *i->member);
- }
- os << strlit (where);
- }
- else
- {
- id_cols->traverse (m, idt, "id", "object_id");
-
- statement_columns sc;
- statement_kind sk (statement_select); // Imperfect forwarding.
- instance<object_columns> t (qtable, sk, sc);
-
- switch (ck)
- {
- case ck_ordered:
- {
- if (ordered)
- t->traverse (m, *it, "index", "index");
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- t->traverse (m, *kt, "key", "key");
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- break;
- }
- }
-
- t->traverse (m, vt, "value", "value");
-
- process_statement_columns (sc, statement_select, versioned);
-
- os << strlit ("SELECT" + sep) << endl;
-
- for (statement_columns::const_iterator i (sc.begin ()),
- e (sc.end ()); i != e;)
- {
- string const& c (i->column);
- os << strlit (c + (++i != e ? "," : "") + sep) << endl;
- }
-
- instance<query_parameters> qp (statement_select, table);
- os << strlit ("FROM " + qtable + sep) << endl;
-
- string where ("WHERE ");
- for (object_columns_list::iterator b (id_cols->begin ()), i (b);
- i != id_cols->end (); ++i)
- {
- if (i != b)
- where += " AND ";
-
- where += qtable + "." + quote_id (i->name) + "=" +
- convert_to (qp->next (*i), i->type, *i->member);
- }
-
- if (ordered)
- {
- // Top-level column.
- //
- string const& col (
- column_qname (m, "index", "index", column_prefix ()));
-
- where += " ORDER BY " + qtable + "." + col;
- }
-
- os << strlit (where);
- }
-
- os << ";"
- << endl;
-
- // insert_statement
- //
- os << "const char " << scope << "::" << endl
- << "insert_statement[] =" << endl;
-
- if (inverse)
- os << strlit ("") << ";"
- << endl;
- else
- {
- statement_columns sc;
- statement_kind sk (statement_insert); // Imperfect forwarding.
- instance<object_columns> t (sk, sc);
-
- t->traverse (m, idt, "id", "object_id");
-
- switch (ck)
- {
- case ck_ordered:
- {
- if (ordered)
- t->traverse (m, *it, "index", "index");
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- t->traverse (m, *kt, "key", "key");
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- break;
- }
- }
-
- t->traverse (m, vt, "value", "value");
-
- process_statement_columns (sc, statement_insert, versioned);
-
- os << strlit ("INSERT INTO " + qtable + sep) << endl;
-
- for (statement_columns::const_iterator b (sc.begin ()), i (b),
- e (sc.end ()); i != e;)
- {
- string s;
-
- if (i == b)
- s += '(';
- s += i->column;
- s += (++i != e ? ',' : ')');
- s += sep;
-
- os << strlit (s) << endl;
- }
-
- os << strlit ("VALUES" + sep) << endl;
-
- string values ("(");
- instance<query_parameters> qp (statement_insert, table);
- for (statement_columns::const_iterator b (sc.begin ()), i (b),
- e (sc.end ()); i != e; ++i)
- {
- if (i != b)
- {
- values += ',';
- values += sep;
- }
-
- values += convert_to (qp->next (*i), i->type, *i->member);
- }
- values += ')';
-
- os << strlit (values) << ";"
- << endl;
- }
-
- // update_statement
- //
- if (smart)
- {
- os << "const char " << scope << "::" << endl
- << "update_statement[] =" << endl
- << strlit ("UPDATE " + qtable + sep) << endl
- << strlit ("SET" + sep) << endl;
-
- instance<query_parameters> qp (statement_update, table);
- statement_columns sc;
- {
- bool f (false); // Imperfect forwarding.
- query_parameters* p (qp.get ()); // Imperfect forwarding.
- statement_kind sk (statement_update); // Imperfect forwarding.
- instance<object_columns> t (sk, f, sc, p);
- t->traverse (m, vt, "value", "value");
- process_statement_columns (sc, statement_update, versioned);
- }
-
- for (statement_columns::const_iterator i (sc.begin ()),
- e (sc.end ()); i != e;)
- {
- string const& c (i->column);
- os << strlit (c + (++i != e ? "," : "") + sep) << endl;
- }
-
- string where ("WHERE ");
- for (object_columns_list::iterator b (id_cols->begin ()), i (b);
- i != id_cols->end (); ++i)
- {
- if (i != b)
- where += " AND ";
-
- where += quote_id (i->name) + "=" +
- convert_to (qp->next (*i), i->type, *i->member);
- }
-
- for (object_columns_list::iterator b (ik_cols->begin ()), i (b);
- i != ik_cols->end (); ++i)
- {
- where += " AND " + quote_id (i->name) + "=" +
- convert_to (qp->next (*i), i->type, *i->member);
- }
-
- os << strlit (where) << ";"
- << endl;
- }
-
- // delete_statement
- //
- os << "const char " << scope << "::" << endl
- << "delete_statement[] =" << endl;
-
- if (inverse)
- os << strlit ("") << ";"
- << endl;
- else
- {
- instance<query_parameters> qp (statement_delete, table);
-
- os << strlit ("DELETE FROM " + qtable + " ") << endl;
-
- string where ("WHERE ");
- for (object_columns_list::iterator b (id_cols->begin ()), i (b);
- i != id_cols->end (); ++i)
- {
- if (i != b)
- where += " AND ";
-
- where += quote_id (i->name) + "=" +
- convert_to (qp->next (*i), i->type, *i->member);
- }
-
- if (smart)
- {
- for (object_columns_list::iterator b (ik_cols->begin ()), i (b);
- i != ik_cols->end (); ++i)
- {
- where += " AND " + quote_id (i->name) +
- (ck == ck_ordered ? ">=" : "=") +
- convert_to (qp->next (*i), i->type, *i->member);
- }
- }
-
- os << strlit (where) << ";"
- << endl;
- }
- }
-
- if (base)
- return;
-
- //
- // Functions.
- //
-
- // bind (cond_image_type)
- //
- if (smart)
- {
- os << "void " << scope << "::" << endl
- << "bind (" << bind_vector << " b," << endl
- << "const " << bind_vector << " id," << endl
- << "std::size_t id_size," << endl
- << "cond_image_type& c)"
- << "{"
- << "using namespace " << db << ";"
- << endl
- << "statement_kind sk (statement_select);"
- << "ODB_POTENTIALLY_UNUSED (sk);"
- << endl
- << "std::size_t n (0);"
- << endl;
-
- os << "// object_id" << endl
- << "//" << endl
- << "if (id != 0)" << endl
- << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
- << "n += id_size;" // Not in if for "id unchanged" optimization.
- << endl;
-
- // We don't need to update the bind index since this is the
- // last element.
- //
- switch (ck)
- {
- case ck_ordered:
- {
- if (ordered)
- {
- os << "// index" << endl
- << "//" << endl;
- instance<bind_member> bm (
- "index_", "c", *it, ict, "index_type", "index");
- bm->traverse (m);
- }
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "// key" << endl
- << "//" << endl;
- instance<bind_member> bm (
- "key_", "c", *kt, kct, "key_type", "key");
- bm->traverse (m);
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- os << "// value" << endl
- << "//" << endl;
- instance<bind_member> bm (
- "value_", "c", vt, vct, "value_type", "value");
- bm->traverse (m);
- break;
- }
- }
- os << "}";
- }
-
- // bind (data_image_type)
- //
- {
- os << "void " << scope << "::" << endl
- << "bind (" << bind_vector << " b," << endl
- << "const " << bind_vector << " id," << endl
- << "std::size_t id_size," << endl
- << "data_image_type& d";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "using namespace " << db << ";"
- << endl
- // In the case of containers, insert and select column sets are
- // the same since we can't have inverse members as container
- // elements.
- //
- << "statement_kind sk (statement_select);"
- << "ODB_POTENTIALLY_UNUSED (sk);"
- << endl
- << "size_t n (0);"
- << endl;
-
- os << "// object_id" << endl
- << "//" << endl
- << "if (id != 0)" << endl
- << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
- << "n += id_size;" // Not in if for "id unchanged" optimization.
- << endl;
-
- switch (ck)
- {
- case ck_ordered:
- {
- if (ordered)
- {
- os << "// index" << endl
- << "//" << endl;
- instance<bind_member> bm (
- "index_", "d", *it, ict, "index_type", "index");
- bm->traverse (m);
- os << "n++;" // Simple value.
- << endl;
- }
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "// key" << endl
- << "//" << endl;
- instance<bind_member> bm (
- "key_", "d", *kt, kct, "key_type", "key");
- bm->traverse (m);
-
- if (semantics::class_* c = composite_wrapper (*kt))
- os << "n += " << column_count (*c).total << "UL;"
- << endl;
- else
- os << "n++;"
- << endl;
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- break;
- }
- }
-
- // We don't need to update the bind index since this is the
- // last element.
- //
- os << "// value" << endl
- << "//" << endl;
- instance<bind_member> bm (
- "value_", "d", vt, vct, "value_type", "value");
- bm->traverse (m);
-
- os << "}";
- }
-
- // bind (cond_image, data_image) (update)
- //
- if (smart)
- {
- os << "void " << scope << "::" << endl
- << "bind (" << bind_vector << " b," << endl
- << "const " << bind_vector << " id," << endl
- << "std::size_t id_size," << endl
- << "cond_image_type& c," << endl
- << "data_image_type& d";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "using namespace " << db << ";"
- << endl
- // Use insert instead of update to include read-only members.
- //
- << "statement_kind sk (statement_insert);"
- << "ODB_POTENTIALLY_UNUSED (sk);"
- << endl
- << "std::size_t n (0);"
- << endl;
-
- os << "// value" << endl
- << "//" << endl;
- instance<bind_member> bm (
- "value_", "d", vt, vct, "value_type", "value");
- bm->traverse (m);
-
- if (semantics::class_* c = composite_wrapper (vt))
- os << "n += " << column_count (*c).total << "UL;"
- << endl;
- else
- os << "n++;"
- << endl;
-
- os << "// object_id" << endl
- << "//" << endl
- << "if (id != 0)" << endl
- << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
- << "n += id_size;" // Not in if for "id unchanged" optimization.
- << endl;
-
- // We don't need to update the bind index since this is the
- // last element.
- //
- switch (ck)
- {
- case ck_ordered:
- {
- if (ordered)
- {
- os << "// index" << endl
- << "//" << endl;
- instance<bind_member> bm (
- "index_", "c", *it, ict, "index_type", "index");
- bm->traverse (m);
- }
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "// key" << endl
- << "//" << endl;
- instance<bind_member> bm (
- "key_", "c", *kt, kct, "key_type", "key");
- bm->traverse (m);
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- os << "// value" << endl
- << "//" << endl;
- instance<bind_member> bm (
- "value_", "c", vt, vct, "value_type", "value");
- bm->traverse (m);
- break;
- }
- }
- os << "}";
- }
-
- // grow ()
- //
- if (generate_grow)
- {
- size_t index (0);
-
- os << "void " << scope << "::" << endl
- << "grow (data_image_type& i," << endl
- << truncated_vector << " t";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "bool grew (false);"
- << endl;
-
- switch (ck)
- {
- case ck_ordered:
- {
- if (ordered)
- {
- os << "// index" << endl
- << "//" << endl;
- instance<grow_member> gm (
- index, "index_", *it, ict, "index_type", "index");
- gm->traverse (m);
- }
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "// key" << endl
- << "//" << endl;
- instance<grow_member> gm (
- index, "key_", *kt, kct, "key_type", "key");
- gm->traverse (m);
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- break;
- }
- }
-
- os << "// value" << endl
- << "//" << endl;
- instance<grow_member> gm (
- index, "value_", vt, vct, "value_type", "value");
- gm->traverse (m);
-
- os << "if (grew)" << endl
- << "i.version++;"
- << "}";
- }
-
- // init (data_image)
- //
- if (!inverse)
- {
- os << "void " << scope << "::" << endl
- << "init (data_image_type& i," << endl;
-
- switch (ck)
- {
- case ck_ordered:
- {
- if (ordered)
- os << "index_type* j," << endl;
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "const key_type* k," << endl;
- break;
- }
- case ck_set:
- case ck_multiset:
- break;
- }
-
- os << "const value_type& v";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "using namespace " << db << ";"
- << endl
- << "statement_kind sk (statement_insert);"
- << "ODB_POTENTIALLY_UNUSED (sk);"
- << endl;
-
- if (generate_grow)
- os << "bool grew (false);"
- << endl;
-
- switch (ck)
- {
- case ck_ordered:
- {
- if (ordered)
- {
- os << "// index" << endl
- << "//" << endl
- << "if (j != 0)";
-
- instance<init_image_member> im (
- "index_", "*j", *it, ict, "index_type", "index");
- im->traverse (m);
- }
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "// key" << endl
- << "//" << endl
- << "if (k != 0)";
-
- instance<init_image_member> im (
- "key_", "*k", *kt, kct, "key_type", "key");
- im->traverse (m);
-
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- break;
- }
- }
-
- os << "// value" << endl
- << "//" << endl;
- {
- instance<init_image_member> im (
- "value_", "v", vt, vct, "value_type", "value");
- im->traverse (m);
- }
-
- if (generate_grow)
- os << "if (grew)" << endl
- << "i.version++;";
-
- os << "}";
- }
-
- // init (cond_image)
- //
- if (smart)
- {
- os << "void " << scope << "::" << endl;
-
- switch (ck)
- {
- case ck_ordered:
- {
- os << "init (cond_image_type& i, index_type j)"
- << "{"
- << "using namespace " << db << ";"
- << endl
- << "statement_kind sk (statement_select);"
- << "ODB_POTENTIALLY_UNUSED (sk);"
- << endl;
-
- instance<init_image_member> im (
- "index_", "j", *it, ict, "index_type", "index");
- im->traverse (m);
-
- os << "}";
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- // Need to handle growth.
- //
- // os << "init (data_image_type&, const key_type&);";
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- // Need to handle growth.
- //
- // os << "init (data_image_type&, const value_type&);";
- break;
- }
- }
-
- os << endl;
- }
-
- // init (data)
- //
- os << "void " << scope << "::" << endl
- << "init (";
-
- switch (ck)
- {
- case ck_ordered:
- {
- if (ordered)
- os << "index_type& j," << endl;
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "key_type& k," << endl;
- break;
- }
- case ck_set:
- case ck_multiset:
- break;
- }
-
- os << "value_type& v," << endl;
- os << "const data_image_type& i," << endl
- << "database* db";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (db);"
- << endl;
-
- switch (ck)
- {
- case ck_ordered:
- {
- if (ordered)
- {
- os << "// index" << endl
- << "//" << endl;
-
- instance<init_value_member> im (
- "index_", "j", *it, ict, "index_type", "index");
- im->traverse (m);
- }
-
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "// key" << endl
- << "//" << endl;
-
- instance<init_value_member> im (
- "key_", "k", *kt, kct, "key_type", "key");
- im->traverse (m);
-
- break;
- }
- case ck_set:
- case ck_multiset:
- break;
- }
-
- os << "// value" << endl
- << "//" << endl;
- {
- // If the value is an object pointer, pass the id type as a
- // type override.
- //
- instance<init_value_member> im (
- "value_", "v", vt, vct, "value_type", "value");
- im->traverse (m);
- }
- os << "}";
-
- // insert
- //
- {
- string ia, ka, va, da;
-
- if (!inverse)
- {
- ia = ordered ? " i" : "";
- ka = " k";
- va = " v";
- da = " d";
- }
-
- os << "void " << scope << "::" << endl;
-
- switch (ck)
- {
- case ck_ordered:
- {
- os << "insert (index_type" << ia << ", " <<
- "const value_type&" << va << ", " <<
- "void*" << da << ")";
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "insert (const key_type&" << ka << ", " <<
- "const value_type&" << va << ", " <<
- "void*" << da << ")";
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- os << "insert (const value_type&" << va << ", " <<
- "void*" << da << ")";
- break;
- }
- }
-
- os << "{";
-
- if (!inverse)
- {
- os << "using namespace " << db << ";"
- << endl
- << "statements_type& sts (*static_cast< statements_type* > (d));"
- << "data_image_type& di (sts.data_image ());";
-
- if (versioned)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration ());";
-
- os << endl
- << "init (di, ";
-
- switch (ck)
- {
- case ck_ordered:
- {
- if (ordered)
- os << "&i, ";
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "&k, ";
- break;
- }
- case ck_set:
- case ck_multiset:
- break;
- }
-
- os << "v" << (versioned ? ", svm" : "") << ");";
-
- os << endl
- << "if (sts.data_binding_test_version ())"
- << "{"
- << "const binding& id (sts.id_binding ());"
- << "bind (sts.data_bind (), id.bind, id.count, di" <<
- (versioned ? ", svm" : "") << ");"
- << "sts.data_binding_update_version ();"
- << "}"
- << "if (!sts.insert_statement ().execute ())" << endl
- << "throw object_already_persistent ();";
- }
-
- os << "}";
- }
-
- // update
- //
- if (smart)
- {
- os << "void " << scope << "::" << endl;
-
- switch (ck)
- {
- case ck_ordered:
- {
- os << "update (index_type i, const value_type& v, void* d)";
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- break;
- }
- }
-
- os << "{";
-
- os << "using namespace " << db << ";"
- << endl
- << "statements_type& sts (*static_cast< statements_type* > (d));"
- << "cond_image_type& ci (sts.cond_image ());"
- << "data_image_type& di (sts.data_image ());";
-
- if (versioned)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration ());";
-
- os << endl;
-
- switch (ck)
- {
- case ck_ordered:
- {
- os << "init (ci, i);";
- os << "init (di, 0, v" << (versioned ? ", svm" : "") << ");";
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- //os << "init (di, 0, v);";
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- //os << "init (di, v);";
- break;
- }
- }
-
- os << endl
- << "if (sts.update_binding_test_version ())"
- << "{"
- << "const binding& id (sts.id_binding ());"
- << "bind (sts.update_bind (), id.bind, id.count, ci, di" <<
- (versioned ? ", svm" : "") << ");"
- << "sts.update_binding_update_version ();"
- << "}";
-
- os << "if (sts.update_statement ().execute () == 0)" << endl
- << "throw object_not_persistent ();"
- << "}";
- }
-
- // select
- //
- os << "bool " << scope << "::" << endl;
-
- switch (ck)
- {
- case ck_ordered:
- {
- os << "select (index_type&" << (ordered ? " i" : "") <<
- ", value_type& v, void* d)";
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "select (key_type& k, value_type& v, void* d)";
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- os << "select (value_type& v, void* d)";
- break;
- }
- }
-
- os << "{"
- << "using namespace " << db << ";"
- << "using " << db << "::select_statement;" // Conflicts.
- << endl
- << "statements_type& sts (*static_cast< statements_type* > (d));"
- << "data_image_type& di (sts.data_image ());";
-
- if (versioned)
- os << "const schema_version_migration& svm (" <<
- "sts.version_migration ());";
-
- os << endl
- << "init (";
-
- // Extract current element.
- //
- switch (ck)
- {
- case ck_ordered:
- {
- if (ordered)
- os << "i, ";
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- os << "k, ";
- break;
- }
- case ck_set:
- case ck_multiset:
- break;
- }
-
- os << "v, di, &sts.connection ().database ()" <<
- (versioned ? ", svm" : "") << ");"
- << endl;
-
- init_value_extra ();
-
- // If we are loading an eager pointer, then the call to init
- // above executes other statements which potentially could
- // change the image, including the id.
- //
- if (eager_ptr)
- {
- os << "if (sts.data_binding_test_version ())"
- << "{"
- << "const binding& id (sts.id_binding ());"
- << "bind (sts.data_bind (), id.bind, id.count, di" <<
- (versioned ? ", svm" : "") << ");"
- << "sts.data_binding_update_version ();"
- << "}";
- }
-
- // Fetch next.
- //
- os << "select_statement& st (sts.select_statement ());"
- << "select_statement::result r (st.fetch ());";
-
- if (grow)
- os << endl
- << "if (r == select_statement::truncated)"
- << "{"
- << "grow (di, sts.select_image_truncated ()" <<
- (versioned ? ", svm" : "") << ");"
- << endl
- << "if (sts.data_binding_test_version ())"
- << "{"
- // Id cannot change.
- //
- << "bind (sts.data_bind (), 0, sts.id_binding ().count, di" <<
- (versioned ? ", svm" : "") << ");"
- << "sts.data_binding_update_version ();"
- << "st.refetch ();"
- << "}"
- << "}";
-
- os << "return r != select_statement::no_data;"
- << "}";
-
- // delete_
- //
- os << "void " << scope << "::" << endl
- << "delete_ (";
-
- if (smart)
- {
- switch (ck)
- {
- case ck_ordered:
- {
- os << "index_type i, ";
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- break;
- }
- }
- }
-
- os << "void*" << (inverse ? "" : " d") << ")"
- << "{";
-
- if (!inverse)
- {
- os << "using namespace " << db << ";"
- << endl
- << "statements_type& sts (*static_cast< statements_type* > (d));";
-
- if (smart)
- {
- os << "cond_image_type& ci (sts.cond_image ());"
- << endl;
-
- switch (ck)
- {
- case ck_ordered:
- {
- os << "init (ci, i);";
- break;
- }
- case ck_map:
- case ck_multimap:
- {
- break;
- }
- case ck_set:
- case ck_multiset:
- {
- break;
- }
- }
-
- os << endl
- << "if (sts.cond_binding_test_version ())"
- << "{"
- << "const binding& id (sts.id_binding ());"
- << "bind (sts.cond_bind (), id.bind, id.count, ci);"
- << "sts.cond_binding_update_version ();"
- << "}";
- }
-
- os << "sts.delete_statement ().execute ();";
- }
-
- os << "}";
-
- // persist
- //
- if (!inverse)
- {
- os << "void " << scope << "::" << endl
- << "persist (const container_type& c," << endl
- << "statements_type& sts";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "using namespace " << db << ";"
- << endl
- << "functions_type& fs (sts.functions ());";
-
- if (versioned)
- os << "sts.version_migration (svm);";
-
- if (!smart && ck == ck_ordered)
- os << "fs.ordered_ = " << ordered << ";";
-
- os << "container_traits_type::persist (c, fs);"
- << "}";
- }
-
- // load
- //
- os << "void " << scope << "::" << endl
- << "load (container_type& c," << endl
- << "statements_type& sts";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "using namespace " << db << ";"
- << "using " << db << "::select_statement;" // Conflicts.
- << endl
- << "const binding& id (sts.id_binding ());"
- << endl
- << "if (sts.data_binding_test_version ())"
- << "{"
- << "bind (sts.data_bind (), id.bind, id.count, sts.data_image ()" <<
- (versioned ? ", svm" : "") << ");"
- << "sts.data_binding_update_version ();"
- << "}"
- // We use the id binding directly so no need to check cond binding.
- //
- << "select_statement& st (sts.select_statement ());"
- << "st.execute ();"
- << "auto_result ar (st);";
-
- // If we are loading eager object pointers, we may need to cache
- // the result since we will be loading other objects.
- //
- if (eager_ptr)
- cache_result ("st");
-
- os << "select_statement::result r (st.fetch ());";
-
- if (grow)
- os << endl
- << "if (r == select_statement::truncated)"
- << "{"
- << "data_image_type& di (sts.data_image ());"
- << "grow (di, sts.select_image_truncated ()" <<
- (versioned ? ", svm" : "") << ");"
- << endl
- << "if (sts.data_binding_test_version ())"
- << "{"
- // Id cannot change.
- //
- << "bind (sts.data_bind (), 0, id.count, di" <<
- (versioned ? ", svm" : "") << ");"
- << "sts.data_binding_update_version ();"
- << "st.refetch ();"
- << "}"
- << "}";
-
- os << "bool more (r != select_statement::no_data);"
- << endl
- << "functions_type& fs (sts.functions ());";
-
- if (versioned)
- os << "sts.version_migration (svm);";
-
- if (!smart && ck == ck_ordered)
- os << "fs.ordered_ = " << ordered << ";";
-
- os << "container_traits_type::load (c, more, fs);"
- << "}";
-
- // update
- //
- if (!(inverse || readonly (member_path_, member_scope_)))
- {
- os << "void " << scope << "::" << endl
- << "update (const container_type& c," << endl
- << "statements_type& sts";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "using namespace " << db << ";"
- << endl
- << "functions_type& fs (sts.functions ());";
-
- if (versioned)
- os << "sts.version_migration (svm);";
-
- if (!smart && ck == ck_ordered)
- os << "fs.ordered_ = " << ordered << ";";
-
- os << "container_traits_type::update (c, fs);"
- << "}";
- }
-
- // erase
- //
- if (!inverse)
- {
- os << "void " << scope << "::" << endl
- << "erase (";
-
- if (smart)
- os << "const container_type* c, ";
-
- os << "statements_type& sts)"
- << "{"
- << "using namespace " << db << ";"
- << endl
- << "functions_type& fs (sts.functions ());";
-
- if (!smart && ck == ck_ordered)
- os << "fs.ordered_ = " << ordered << ";";
-
- os << "container_traits_type::erase (" << (smart ? "c, " : "") << "fs);"
- << "}";
- }
- }
-
- protected:
- string scope_;
- semantics::class_& c_;
- };
-
- // Extra statement cache members for containers.
- //
- struct container_cache_members: object_members_base, virtual context
- {
- typedef container_cache_members base;
-
- container_cache_members ()
- : object_members_base (true, false, false)
- {
- }
-
- virtual void
- traverse_container (semantics::data_member& m, semantics::type& c)
- {
- bool smart (!context::inverse (m, "value") &&
- !unordered (m) &&
- container_smart (c));
-
- string traits (flat_prefix_ + public_name (m) + "_traits");
-
- os << db << "::" << (smart ? "smart_" : "") <<
- "container_statements_impl< " << traits << " > " <<
- flat_prefix_ << m.name () << ";";
- }
- };
-
- struct container_cache_init_members: object_members_base, virtual context
- {
- typedef container_cache_init_members base;
-
- container_cache_init_members ()
- : object_members_base (true, false, false), first_ (true)
- {
- }
-
- virtual void
- traverse_container (semantics::data_member& m, semantics::type&)
- {
- if (first_)
- {
- os << endl
- << ": ";
- first_ = false;
- }
- else
- os << "," << endl
- << " ";
-
- os << flat_prefix_ << m.name () << " (c, id";
- extra_members ();
- os << ")";
- }
-
- virtual void
- extra_members () {}
-
- protected:
- bool first_;
- };
-
- // Extra statement cache members for sections.
- //
- struct section_cache_members: virtual context
- {
- typedef section_cache_members base;
-
- virtual void
- traverse (user_section& s)
- {
- string traits (public_name (*s.member) + "_traits");
-
- os << db << "::" << "section_statements< " <<
- class_fq_name (*s.object) << ", " << traits << " > " <<
- s.member->name () << ";";
- }
- };
-
- struct section_cache_init_members: virtual context
- {
- typedef section_cache_init_members base;
-
- section_cache_init_members (bool first): first_ (first) {}
-
- virtual void
- traverse (user_section& s)
- {
- if (first_)
- {
- os << endl
- << ": ";
- first_ = false;
- }
- else
- os << "," << endl
- << " ";
-
- os << s.member->name () << " (c, im, idim, id, idv";
- extra_members ();
- os << ")";
- }
-
- virtual void
- extra_members () {}
-
- protected:
- bool first_;
- };
-
- // Calls for container members.
- //
- struct container_calls: object_members_base, virtual context
- {
- typedef container_calls base;
-
- enum call_type
- {
- persist_call,
- load_call,
- update_call,
- erase_obj_call,
- erase_id_call,
- section_call
- };
-
- container_calls (call_type call, object_section* section = 0)
- : object_members_base (true, false, true, false, section),
- call_ (call),
- obj_prefix_ ("obj"),
- by_value_ (0)
- {
- }
-
- virtual bool
- section_test (data_member_path const& mp)
- {
- object_section& s (section (mp));
-
- // Include eager loaded members into the main section for
- // load calls.
- //
- return section_ == 0 ||
- *section_ == s ||
- (call_ == load_call &&
- *section_ == main_section &&
- !s.separate_load ());
- }
-
- virtual void
- traverse_pointer (semantics::data_member&, semantics::class_&)
- {
- // We don't want to traverse composite id.
- }
-
- virtual void
- traverse_composite_wrapper (semantics::data_member* m,
- semantics::class_& c,
- semantics::type* w)
- {
- if (m == 0 ||
- call_ == erase_id_call ||
- (call_ == load_call && by_value_ != 0))
- {
- object_members_base::traverse_composite (m, c);
- return;
- }
-
- // Get this member using the accessor expression.
- //
- member_access& ma (
- m->get<member_access> (call_ == load_call ? "set" : "get"));
-
- // We don't support by-value modifiers for composite values
- // with containers. However, at this point we don't know
- // whether this composite value has any containers. So we
- // are just going to set a flag that can be checked in
- // traverse_container() below.
- //
- if (call_ == load_call && ma.placeholder ())
- {
- by_value_ = &ma;
- object_members_base::traverse_composite (m, c);
- by_value_ = 0;
- return;
- }
-
- // We also don't support by-value accessors is there is a
- // smart container inside (which, again, we don't know at
- // this point). So keep track of such first instance.
- //
- member_access* old_by_value (by_value_);
- if (call_ != load_call && ma.by_value && by_value_ == 0)
- by_value_ = &ma;
-
- string old_op (obj_prefix_);
- string old_f (from_);
- obj_prefix_.clear ();
-
- // If this member is const and we have a synthesized direct
- // access, then cast away constness. Otherwise, we assume
- // that the user-provided expression handles this.
- //
- bool cast (call_ == load_call && ma.direct () && const_member (*m));
- if (cast)
- obj_prefix_ = "const_cast< " + member_ref_type (*m, false) +
- " > (\n";
-
- obj_prefix_ += ma.translate (old_op);
-
- if (cast)
- obj_prefix_ += ")";
-
- // If this is not a synthesized expression, then store its
- // location which we will output later for easier error
- // tracking.
- //
- if (!ma.synthesized)
- from_ += "// From " + location_string (ma.loc, true) + "\n";
-
- // If this is a wrapped composite value, then we need to "unwrap" it.
- //
- if (w != 0)
- {
- semantics::names* hint;
- semantics::type& t (utype (*m, hint));
-
- // Because we cannot have nested containers, member type should
- // be the same as w.
- //
- assert (&t == w);
-
- obj_prefix_ = "wrapper_traits< " + t.fq_name (hint) + " >::" +
- (call_ == load_call ? "set_ref" : "get_ref") +
- " (\n" + obj_prefix_ + ")";
- }
-
- object_members_base::traverse_composite (m, c);
- from_ = old_f;
- obj_prefix_ = old_op;
- by_value_ = old_by_value;
- }
-
- virtual void
- traverse_container (semantics::data_member& m, semantics::type& c)
- {
- using semantics::type;
-
- bool inverse (context::inverse (m, "value"));
- bool smart (!inverse && !unordered (m) && container_smart (c));
- bool versioned (context::versioned (m));
-
- // In certain cases we don't need to do anything.
- //
- if ((call_ != load_call && inverse) ||
- (call_ == section_call && !smart) ||
- (call_ == update_call && readonly (member_path_, member_scope_)))
- return;
-
- string const& name (m.name ());
- string sts_name (flat_prefix_ + name);
- string traits (flat_prefix_ + public_name (m) + "_traits");
-
- os << "// " << member_prefix_ << m.name () << endl
- << "//" << endl;
-
- // Get this member using the accessor expression.
- //
- string var;
- member_access& ma (
- m.get<member_access> (call_ == load_call ? "set" : "get"));
-
- // We don't support by-value modifiers for composite values
- // with containers.
- //
- if (call_ == load_call && by_value_ != 0)
- {
- error (by_value_->loc) << "by-value modification of a composite "
- << "value with container is not supported"
- << endl;
- info (m.location ()) << "container member is defined here" << endl;
- throw operation_failed ();
- }
-
- // We don't support by-value accessors for smart containers.
- //
- if (call_ != load_call && smart)
- {
- if (by_value_ != 0)
- {
- error (by_value_->loc) << "by-value access to a composite value "
- << "with smart container is not supported"
- << endl;
- info (m.location ()) << "container member is defined here" << endl;
- throw operation_failed ();
- }
-
- if (ma.by_value)
- {
- error (ma.loc) << "by-value access to a smart container is not "
- << "supported" << endl;
- info (m.location ()) << "container member is defined here" << endl;
- throw operation_failed ();
- }
- }
-
- // If the member is soft- added or deleted, check the version.
- //
- unsigned long long av (added (member_path_));
- unsigned long long dv (deleted (member_path_));
-
- // If the addition/deletion version is the same as the section's,
- // then we don't need the test.
- //
- if (user_section* s = dynamic_cast<user_section*> (section_))
- {
- if (av == added (*s->member))
- av = 0;
-
- if (dv == deleted (*s->member))
- dv = 0;
- }
-
- if (av != 0 || dv != 0)
- {
- os << "if (";
-
- if (av != 0)
- os << "svm >= schema_version_migration (" << av << "ULL, true)";
-
- if (av != 0 && dv != 0)
- os << " &&" << endl;
-
- if (dv != 0)
- os << "svm <= schema_version_migration (" << dv << "ULL, true)";
-
- os << ")" << endl;
- }
-
- os << "{";
-
- if (call_ != erase_id_call && (call_ != erase_obj_call || smart))
- {
- // See if we are modifying via a reference or proper modifier.
- //
- if (call_ == load_call && ma.placeholder ())
- os << member_val_type (m, false, "v") << ";"
- << endl;
- else
- {
- // Note: this case is for both access and modification.
- //
-
- // Output stored locations, if any.
- //
- os << from_;
-
- // If this is not a synthesized expression, then output its
- // location for easier error tracking.
- //
- if (!ma.synthesized)
- os << "// From " << location_string (ma.loc, true) << endl;
-
- // Note that here we don't decay arrays.
- //
- const string& ref_type (
- member_ref_type (m, call_ != load_call, "v", false /* decay */));
-
- // VC++ cannot grok the constructor syntax.
- //
- os << ref_type << " =" << endl
- << " ";
-
- // If this member is const and we have a synthesized direct
- // access, then cast away constness. Otherwise, we assume
- // that the user-provided expression handles this.
- //
- bool cast (call_ == load_call && ma.direct () && const_member (m));
- if (cast)
- os << "const_cast< " << member_ref_type (m, false, "", false) <<
- " > (" << endl;
-
- os << ma.translate (obj_prefix_);
-
- if (cast)
- os << ")";
-
- os << ";"
- << endl;
- }
-
- var = "v";
-
- semantics::names* hint;
- semantics::type& t (utype (m, hint));
-
- // If this is a wrapped container, then we need to "unwrap" it.
- //
- if (wrapper (t))
- {
- var = "wrapper_traits< " + t.fq_name (hint) + " >::" +
- (call_ == load_call ? "set_ref" : "get_ref") + " (" + var + ")";
- }
- }
-
- switch (call_)
- {
- case persist_call:
- {
- os << traits << "::persist (" << endl
- << var << "," << endl
- << "esc." << sts_name;
-
- if (versioned)
- os << "," << endl
- << "svm";
-
- os << ");";
- break;
- }
- case load_call:
- {
- os << traits << "::load (" << endl
- << var << "," << endl
- << "esc." << sts_name;
-
- if (versioned)
- os << "," << endl
- << "svm";
-
- os << ");";
- break;
- }
- case update_call:
- {
- os << traits << "::update (" << endl
- << var << "," << endl
- << "esc." << sts_name;
-
- if (versioned)
- os << "," << endl
- << "svm";
-
- os << ");";
- break;
- }
- case erase_obj_call:
- {
- os << traits << "::erase (" << endl;
-
- if (smart)
- os << "&" << var << "," << endl;
-
- os << "esc." << sts_name << ");"
- << endl;
- break;
- }
- case erase_id_call:
- {
- os << traits << "::erase (" << endl;
-
- if (smart)
- os << "0," << endl;
-
- os << "esc." << sts_name << ");"
- << endl;
- break;
- }
- case section_call:
- {
- os << "if (" << traits << "::container_traits_type::changed (" <<
- var << "))" << endl
- << "s.reset (true, true);"; // loaded, changed
- break;
- }
- }
-
- if (call_ == load_call)
- {
- // Call the modifier if we are using a proper one.
- //
- if (ma.placeholder ())
- {
- os << endl
- << from_;
-
- // If this is not a synthesized expression, then output its
- // location for easier error tracking.
- //
- if (!ma.synthesized)
- os << "// From " << location_string (ma.loc, true) << endl;
-
- os << ma.translate (
- obj_prefix_, "v", "static_cast<" + db.string () +
- "::database&> (db)") << ";";
- }
- }
-
- os << "}";
- }
-
- protected:
- call_type call_;
- string obj_prefix_;
- string from_;
- member_access* by_value_;
- };
-
- //
- //
- struct section_traits: traversal::class_, virtual context
- {
- typedef section_traits base;
-
- section_traits (semantics::class_& c)
- : c_ (c),
- scope_ ("access::object_traits_impl< " + class_fq_name (c) +
- ", id_" + db.string () + " >")
- {
- }
-
- // Additional code that need to be executed following the call to
- // init_value().
- //
- virtual void
- init_value_extra ()
- {
- }
-
- virtual void
- process_statement_columns (statement_columns&,
- statement_kind,
- bool /*dynamic*/)
- {
- }
-
- virtual void
- section_extra (user_section&)
- {
- }
-
- // Returning "1" means increment by one.
- //
- virtual string
- optimistic_version_increment (semantics::data_member&)
- {
- return "1";
- }
-
- virtual string
- update_statement_extra (user_section&)
- {
- return "";
- }
-
- virtual void
- traverse (user_section& s)
- {
- using semantics::class_;
- using semantics::data_member;
-
- data_member& m (*s.member);
-
- class_* poly_root (polymorphic (c_));
- bool poly (poly_root != 0);
- bool poly_derived (poly && poly_root != &c_);
- class_* poly_base (poly_derived ? &polymorphic_base (c_) : 0);
-
- data_member* opt (optimistic (c_));
-
- // Treat the special version update sections as abstract in reuse
- // inheritance.
- //
- bool reuse_abst (!poly &&
- (abstract (c_) ||
- s.special == user_section::special_version));
-
- bool load (s.total != 0 && s.separate_load ());
- bool load_con (s.containers && s.separate_load ());
- bool load_opt (s.optimistic () && s.separate_load ());
-
- bool update (s.total != s.inverse + s.readonly); // Always separate.
- bool update_con (s.readwrite_containers);
- bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
-
- // Don't generate anything for empty sections.
- //
- if (!(load || load_con || load_opt ||
- update || update_con || update_opt))
- return;
-
- // If we are adding a new section to a derived class in an optimistic
- // polymorphic hierarchy, then pretend it inherits from the special
- // version update section.
- //
- user_section* rs (0);
- if (opt != 0)
- {
- // Skip overrides and get to the new section if polymorphic.
- //
- for (rs = &s; poly && rs->base != 0; rs = rs->base) ;
-
- if (rs != 0)
- {
- if (rs->object != &opt->scope ())
- rs->base = &(poly ? poly_root : &opt->scope ())->
- get<user_sections> ("user-sections").back ();
- else
- rs = 0;
- }
- }
-
- string name (public_name (m) + "_traits");
- string scope (scope_ + "::" + name);
-
- os << "// " << m.name () << endl
- << "//" << endl
- << endl;
-
- // bind (id, image_type)
- //
- if (load || load_opt || update || update_opt)
- {
- os << "std::size_t " << scope << "::" << endl
- << "bind (" << bind_vector << " b," << endl
- << "const " << bind_vector << (reuse_abst ? "," : " id,") << endl
- << "std::size_t" << (reuse_abst ? "," : " id_size,") << endl
- << "image_type& i," << endl
- << db << "::statement_kind sk";
-
- if (s.versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (sk);";
-
- if (s.versioned)
- os << "ODB_POTENTIALLY_UNUSED (svm);";
-
- os << endl
- << "using namespace " << db << ";"
- << endl
- << "std::size_t n (0);"
- << endl;
-
- // Bind reuse base. It is always first and we never ask it
- // to bind id(+ver).
- //
- if (s.base != 0 && !poly_derived)
- {
- user_section& b (*s.base);
-
- bool load (b.total != 0 && b.separate_load ());
- bool load_opt (b.optimistic () && b.separate_load ());
-
- bool update (b.total != b.inverse + b.readonly);
-
- if (load || load_opt || update)
- os << "// " << class_name (*b.object) << endl
- << "//" << endl
- << "n += object_traits_impl< " << class_fq_name (*b.object) <<
- ", id_" << db << " >::" << public_name (*b.member) <<
- "_traits::bind (" << endl
- << "b, 0, 0, i, sk" << (b.versioned ? ", svm" : "") << ");"
- << endl;
- }
-
- // Bind members.
- //
- {
- instance<bind_member> bm ("", "", &s);
- traversal::names n (*bm);
- names (c_, n);
- }
-
- // Bind polymorphic image chain for the select statement.
- //
- if (s.base != 0 && poly_derived && s.separate_load ())
- {
- // Find the next base that has something to load, if any.
- //
- user_section* b (s.base);
- string acc (".base");
- for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
- {
- if (b->object == bo)
- {
- if (b->total != 0 || b->optimistic ())
- break;
-
- b = b->base;
- if (b == 0 || !polymorphic (*b->object))
- {
- b = 0;
- break;
- }
- }
- acc += "->base";
- }
-
- if (b != 0)
- os << "// " << class_name (*b->object) << endl
- << "//" << endl
- << "if (sk == statement_select)" << endl
- << "n += object_traits_impl< " << class_fq_name (*b->object) <<
- ", id_" << db << " >::" << public_name (*b->member) <<
- "_traits::bind (" << endl
- << "b + n, 0, 0, *i" << acc << ", sk" <<
- (b->versioned ? ", svm" : "") << ");"
- << endl;
- }
-
- if (!reuse_abst)
- os << "// object_id" << endl
- << "//" << endl
- << "if (id != 0)" << endl
- << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
- << "n += id_size;" // Not in if for "id unchanged" optimization.
- << endl;
-
- os << "return n;"
- << "}";
- }
-
- // grow ()
- //
- if (generate_grow && (load || load_opt))
- {
- os << "bool " << scope << "::" << endl
- << "grow (image_type& i," << endl
- << truncated_vector << " t";
-
- if (s.versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (i);"
- << "ODB_POTENTIALLY_UNUSED (t);";
-
- if (s.versioned)
- os << "ODB_POTENTIALLY_UNUSED (svm);";
-
- os << endl
- << "bool grew (false);"
- << endl;
-
- size_t index (0);
-
- if (s.base != 0 && !poly_derived)
- {
- user_section& b (*s.base);
-
- bool load (b.total != 0);
- bool load_opt (b.optimistic ());
-
- if (load || load_opt)
- {
- os << "// " << class_name (*b.object) << endl
- << "//" << endl
- << "grew = object_traits_impl< " << class_fq_name (*b.object) <<
- ", id_" << db << " >::" << public_name (*b.member) <<
- "_traits::grow (i, t" << (b.versioned ? ", svm" : "") << ");"
- << endl;
-
- index += b.total + (load_opt ? 1 : 0);
- }
- }
-
- {
- user_section* ps (&s);
- instance<grow_member> gm (index, "", ps);
- traversal::names n (*gm);
- names (c_, n);
- }
-
- // Grow polymorphic image chain.
- //
- if (s.base != 0 && poly_derived)
- {
- // Find the next base that has something to load, if any.
- //
- user_section* b (s.base);
- string acc (".base");
- size_t cols;
- for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
- {
- if (b->object == bo)
- {
- cols = b->total + (b->optimistic () ? 1 : 0);
- if (cols != 0)
- break;
-
- b = b->base;
- if (b == 0 || !polymorphic (*b->object))
- {
- b = 0;
- break;
- }
- }
- acc += "->base";
- }
-
- if (b != 0)
- os << "// " << class_name (*b->object) << endl
- << "//" << endl
- << "if (object_traits_impl< " << class_fq_name (*b->object) <<
- ", id_" << db << " >::" << public_name (*b->member) <<
- "_traits::grow (" << endl
- << "*i" << acc << ", t + " << cols << "UL" <<
- (b->versioned ? ", svm" : "") << "))" << endl
- << "i" << acc << "->version++;"
- << endl;
- }
-
- os << "return grew;" << endl
- << "}";
- }
-
- // init (object, image)
- //
- if (load)
- {
- os << "void " << scope << "::" << endl
- << "init (object_type& o," << endl
- << "const image_type& i," << endl
- << "database* db";
-
- if (s.versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (db);";
-
- if (s.versioned)
- os << "ODB_POTENTIALLY_UNUSED (svm);";
-
- os << endl;
-
- if (s.base != 0)
- {
- if (!poly_derived)
- {
- user_section& b (*s.base);
-
- bool load (b.total != 0);
-
- if (load)
- os << "// " << class_name (*b.object) << endl
- << "//" << endl
- << "object_traits_impl< " << class_fq_name (*b.object) <<
- ", id_" << db << " >::" << public_name (*b.member) <<
- "_traits::init (o, i, db" <<
- (b.versioned ? ", svm" : "") << ");"
- << endl;
- }
- else
- {
- // Find the next base that has something to load, if any.
- //
- user_section* b (s.base);
- string acc (".base");
- for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
- {
- if (b->object == bo)
- {
- if (b->total != 0)
- break;
-
- b = b->base;
- if (b == 0 || !polymorphic (*b->object))
- {
- b = 0;
- break;
- }
- }
- acc += "->base";
- }
-
- if (b != 0)
- os << "// " << class_name (*b->object) << endl
- << "//" << endl
- << "object_traits_impl< " << class_fq_name (*b->object) <<
- ", id_" << db << " >::" << public_name (*b->member) <<
- "_traits::init (" << endl
- << "o, *i" << acc << ", db" <<
- (b->versioned ? ", svm" : "") << ");"
- << endl;
- }
- }
-
- {
- instance<init_value_member> iv ("", "", true, &s);
- traversal::names n (*iv);
- names (c_, n);
- }
-
- os << "}";
- }
-
- // init (image, object)
- //
- if (update)
- {
- os << (generate_grow ? "bool " : "void ") << scope << "::" << endl
- << "init (image_type& i," << endl
- << "const object_type& o";
-
- if (s.versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{";
-
- if (s.versioned)
- os << "ODB_POTENTIALLY_UNUSED (svm);"
- << endl;
-
- os << "using namespace " << db << ";"
- << endl
- << "statement_kind sk (statement_insert);"
- << "ODB_POTENTIALLY_UNUSED (sk);"
- << endl;
-
- // There is no call to init_image_pre() here (which calls the
- // copy callback for some databases) since we are not going to
- // touch any of the members that were loaded by query.
-
- if (generate_grow)
- os << "bool grew (false);"
- << endl;
-
- if (s.base != 0 && !poly_derived)
- {
- user_section& b (*s.base);
-
- bool update (b.total != b.inverse + b.readonly);
-
- if (update)
- os << "// " << class_name (*b.object) << endl
- << "//" << endl
- << (generate_grow ? "grew = " : "") <<
- "object_traits_impl< " << class_fq_name (*b.object) <<
- ", id_" << db << " >::" << public_name (*b.member) <<
- "_traits::init (i, o" << (b.versioned ? ", svm" : "") << ");"
- << endl;
- }
-
- {
- instance<init_image_member> ii ("", "", &s);
- traversal::names n (*ii);
- names (c_, n);
- }
-
- if (generate_grow)
- os << "return grew;";
-
- os << "}";
- }
-
- // The rest does not apply to reuse-abstract sections.
- //
- if (reuse_abst)
- {
- section_extra (s);
- return;
- }
-
- string sep (s.versioned ? "\n" : " ");
-
- // Schema name as a string literal or empty.
- //
- string schema_name (options.schema_name ()[db]);
- if (!schema_name.empty ())
- schema_name = strlit (schema_name);
-
- // Statements.
- //
- qname table (table_name (c_));
- string qtable (quote_id (table));
-
- instance<object_columns_list> id_cols;
- id_cols->traverse (*id_member (c_));
-
- // select_statement
- //
- if (load || load_opt)
- {
- size_t depth (poly_derived ? polymorphic_depth (c_) : 1);
-
- statement_columns sc;
- {
- statement_kind sk (statement_select); // Imperfect forwarding.
- object_section* ps (&s); // Imperfect forwarding.
- instance<object_columns> t (qtable, sk, sc, depth, ps);
- t->traverse (c_);
- process_statement_columns (sc, statement_select, s.versioned);
- }
-
- os << "const char " << scope << "::" << endl
- << "select_statement[] =" << endl
- << strlit ("SELECT" + sep) << endl;
-
- for (statement_columns::const_iterator i (sc.begin ()),
- e (sc.end ()); i != e;)
- {
- string const& c (i->column);
- os << strlit (c + (++i != e ? "," : "") + sep) << endl;
- }
-
- os << strlit ("FROM " + qtable + sep) << endl;
-
- // Join polymorphic bases.
- //
- if (depth != 1 && s.base != 0)
- {
- bool f (false); //@@ (im)perfect forwarding
- size_t d (depth - 1); //@@ (im)perfect forward.
- instance<polymorphic_object_joins> j (c_, f, d, "", s.base);
- j->traverse (*poly_base);
-
- for (strings::const_iterator i (j->begin ()); i != j->end (); ++i)
- os << strlit (*i + sep) << endl;
- }
-
- // Join tables of inverse members belonging to this section.
- //
- {
- bool f (false); // @@ (im)perfect forwarding
- object_section* ps (&s); // @@ (im)perfect forwarding
- instance<object_joins> j (c_, f, depth, ps);
- j->traverse (c_);
-
- for (strings::const_iterator i (j->begin ()); i != j->end (); ++i)
- os << strlit (*i + sep) << endl;
- }
-
- string where ("WHERE ");
- instance<query_parameters> qp (statement_select, table);
- for (object_columns_list::iterator b (id_cols->begin ()), i (b);
- i != id_cols->end (); ++i)
- {
- if (i != b)
- where += " AND ";
-
- where += qtable + "." + quote_id (i->name) + "=" +
- convert_to (qp->next (*i), i->type, *i->member);
- }
-
- os << strlit (where) << ";"
- << endl;
- }
-
- // update_statement
- //
- if (update || update_opt)
- {
- instance<query_parameters> qp (statement_update, table);
-
- statement_columns sc;
- {
- query_parameters* p (qp.get ()); // Imperfect forwarding.
- statement_kind sk (statement_update); // Imperfect forwarding.
- object_section* ps (&s); // Imperfect forwarding.
- instance<object_columns> t (sk, sc, p, ps);
- t->traverse (c_);
- process_statement_columns (sc, statement_update, s.versioned);
- }
-
- os << "const char " << scope << "::" << endl
- << "update_statement[] =" << endl
- << strlit ("UPDATE " + qtable + sep) << endl
- << strlit ("SET" + sep) << endl;
-
- for (statement_columns::const_iterator i (sc.begin ()),
- e (sc.end ()); i != e;)
- {
- string const& c (i->column);
- os << strlit (c + (++i != e ? "," : "") + sep) << endl;
- }
-
- // This didn't work out: cannot change the identity column.
- //
- //if (sc.empty ())
- //{
- // // We can end up with nothing to set if we need to "touch" a row
- // // in order to increment its optimistic concurrency version. In
- // // this case just do a dummy assignment based on the id column.
- // //
- // string const& c (quote_id (id_cols->begin ()->name));
- // os << strlit (c + "=" + c) << endl;
- //}
-
- string extra (update_statement_extra (s));
-
- if (!extra.empty ())
- os << strlit (extra + sep) << endl;
-
- string where ("WHERE ");
- for (object_columns_list::iterator b (id_cols->begin ()), i (b);
- i != id_cols->end (); ++i)
- {
- if (i != b)
- where += " AND ";
-
- where += quote_id (i->name) + "=" +
- convert_to (qp->next (*i), i->type, *i->member);
- }
-
- if (s.optimistic ()) // Note: not update_opt.
- {
- string name (column_qname (*opt, column_prefix ()));
- string type (column_type (*opt));
-
- where += " AND " + name + "=" +
- convert_to (qp->next (*opt, name, type), type, *opt);
- }
-
- os << strlit (where) << ";"
- << endl;
- }
-
- // load ()
- //
- if (load || load_opt || load_con)
- {
- os << "void " << scope << "::" << endl
- << "load (extra_statement_cache_type& esc, object_type& obj" <<
- (poly ? ", bool top" : "") << ")"
- << "{";
-
- if (poly)
- os << "ODB_POTENTIALLY_UNUSED (top);"
- << endl;
-
- if (s.versioned || s.versioned_containers)
- os << "const schema_version_migration& svm (" << endl
- << "esc." << m.name () << ".version_migration (" <<
- schema_name << "));"
- << endl;
-
- // Load values, if any.
- //
- if (load || load_opt)
- {
- // The SELECT statement for the top override loads all the
- // values.
- //
- if (poly)
- os << "if (top)"
- << "{";
-
- // Note that we don't use delayed load machinery here. While
- // a section can definitely contain self-referencing pointers,
- // loading such a pointer won't mess up the data members in the
- // image that we care about. It also holds true for streaming
- // result, since the bindings are different.
-
- os << "using namespace " << db << ";"
- << "using " << db << "::select_statement;" // Conflicts.
- << endl
- << "statements_type& sts (esc." << m.name () << ");"
- << endl
- << "image_type& im (sts.image ());"
- << "binding& imb (sts.select_image_binding ());"
- << endl;
-
- // For the polymorphic case, instead of storing an array of
- // versions as we do for objects, we will add all the versions
- // up and use that as a cumulative image chain version. If you
- // meditate a bit on that, you will realize that it will work
- // (hint: versions can only increase).
- //
- string ver;
- string ver_decl;
-
- if (s.base != 0 && poly_derived)
- {
- ver = "imv";
- ver_decl = "std::size_t imv (im.version";
-
- user_section* b (s.base);
- string acc ("im.base");
- for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
- {
- if (b->object == bo)
- {
- if (b->total != 0 || b->optimistic ())
- ver_decl += " +\n" + acc + "->version";
-
- b = b->base;
- if (b == 0 || !polymorphic (*b->object))
- {
- b = 0;
- break;
- }
- }
- acc += "->base";
- }
-
- ver_decl += ")";
-
- os << ver_decl << ";"
- << endl;
- }
- else
- ver = "im.version";
-
- os << "if (" << ver << " != sts.select_image_version () ||" << endl
- << "imb.version == 0)"
- << "{"
- << "bind (imb.bind, 0, 0, im, statement_select" <<
- (s.versioned ? ", svm" : "") << ");"
- << "sts.select_image_version (" << ver << ");"
- << "imb.version++;"
- << "}";
-
- // Id binding is assumed initialized and bound.
- //
- os << "select_statement& st (sts.select_statement ());";
-
- // The statement can be dynamically empty.
- //
- if (s.versioned)
- os << "if (!st.empty ())"
- << "{";
-
- os << "st.execute ();"
- << "auto_result ar (st);"
- << "select_statement::result r (st.fetch ());"
- << endl;
-
- os << "if (r == select_statement::no_data)" << endl
- << "throw object_not_persistent ();"
- << endl;
-
- if (grow (c_, &s))
- {
- os << "if (r == select_statement::truncated)"
- << "{"
- << "if (grow (im, sts.select_image_truncated ()" <<
- (s.versioned ? ", svm" : "") << "))" << endl
- << "im.version++;"
- << endl;
-
- // The same logic as above.
- //
- if (s.base != 0 && poly_derived)
- os << ver_decl << ";"
- << endl;
-
- os << "if (" << ver << " != sts.select_image_version ())"
- << "{"
- << "bind (imb.bind, 0, 0, im, statement_select" <<
- (s.versioned ? ", svm" : "") << ");"
- << "sts.select_image_version (" << ver << ");"
- << "imb.version++;"
- << "st.refetch ();"
- << "}"
- << "}";
- }
-
- if (opt != 0) // Not load_opt, we do it in poly-derived as well.
- {
- os << "if (";
-
- if (poly_derived)
- {
- os << "root_traits::version (*im.base";
- for (class_* b (poly_base);
- b != poly_root;
- b = &polymorphic_base (*b))
- os << "->base";
- os << ")";
- }
- else
- os << "version (im)";
-
- os << " != " << (poly_derived ? "root_traits::" : "") <<
- "version (obj))" << endl
- << "throw object_changed ();"
- << endl;
- }
-
- if (load)
- {
- os << "init (obj, im, &sts.connection ().database ()" <<
- (s.versioned ? ", svm" : "") << ");";
- init_value_extra (); // Stream results, etc.
- os << endl;
- }
-
- if (s.versioned)
- os << "}"; // if (!st.empty ())
-
- if (poly)
- os << "}"; // if (top)
- }
-
- // Call base to load its containers, if this is an override.
- //
- if (poly_derived && s.base != 0)
- {
- user_section* b (s.base);
- for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
- {
- if (b->object == bo)
- {
- // If we don't have any values of our own but out base
- // does, then allow it to load them.
- //
- if (b->containers ||
- (!load && (b->total != 0 || b->optimistic ())))
- break;
-
- b = b->base;
- if (b == 0 || !polymorphic (*b->object))
- {
- b = 0;
- break;
- }
- }
- }
-
- // This one is tricky: ideally we would do a direct call to
- // the base's load() (which may not be our immediate base,
- // BTW) but there is no easy way to resolve base's extra
- // statements from ours. So, instead, we are going to go
- // via the dispatch machinery which requires a connection
- // rather than statements. Not the most efficient way but
- // simple.
-
- // Find the "previous" override by starting the search from
- // our base.
- //
- if (b != 0)
- {
- // Note that here we are using the base section index to
- // handle the special version update base.
- //
- os << "info.base->find_section_load (" << b->index << "UL) (" <<
- "esc." << m.name () << ".connection (), obj, " <<
- // If we don't have any values of our own, then allow the
- // base load its.
- //
- (load ? "false" : "top") << ");"
- << endl;
- }
- }
-
- // Load our containers, if any.
- //
- if (s.containers)
- {
- instance<container_calls> t (container_calls::load_call, &s);
- t->traverse (c_);
- }
-
- os << "}";
- }
-
- // update ()
- //
- if (update || update_opt || update_con)
- {
- os << "void " << scope << "::" << endl
- << "update (extra_statement_cache_type& esc, " <<
- "const object_type& obj" <<
- (poly_derived && s.base != 0 ? ", bool base" : "") << ")"
- << "{";
-
- // Call base if this is an override.
- //
- if (poly_derived && s.base != 0)
- {
- user_section* b (s.base);
- for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
- {
- if (b->object == bo)
- {
- if (b->total != b->inverse + b->readonly ||
- b->readwrite_containers ||
- (poly && b->optimistic ()))
- break;
-
- b = b->base;
- if (b == 0 || !polymorphic (*b->object))
- {
- b = 0;
- break;
- }
- }
- }
-
- // The same (tricky) logic as in load(). Note that here we are
- // using the base section index to handle the special version
- // update base.
- //
- if (b != 0)
- os << "if (base)" << endl
- << "info.base->find_section_update (" << b->index <<
- "UL) (esc." << m.name () << ".connection (), obj);"
- << endl;
- else
- os << "ODB_POTENTIALLY_UNUSED (base);"
- << endl;
- }
-
- if (s.versioned || s.readwrite_versioned_containers)
- os << "const schema_version_migration& svm (" << endl
- << "esc." << m.name () << ".version_migration (" <<
- schema_name << "));"
- << endl;
-
- // Update values, if any.
- //
- if (update || update_opt)
- {
- os << "using namespace " << db << ";"
- << "using " << db << "::update_statement;" // Conflicts.
- << endl
- << "statements_type& sts (esc." << m.name () << ");"
- << endl
- << "image_type& im (sts.image ());"
- << "const binding& id (sts.idv_binding ());" // id+version
- << "binding& imb (sts.update_image_binding ());"
- << endl;
-
- if (update)
- {
- if (generate_grow)
- os << "if (";
-
- os << "init (im, obj" << (s.versioned ? ", svm" : "") << ")";
-
- if (generate_grow)
- os << ")" << endl
- << "im.version++";
-
- os << ";"
- << endl;
- }
-
- os << "if (im.version != sts.update_image_version () ||" << endl
- << "id.version != sts.update_id_binding_version () ||" << endl
- << "imb.version == 0)"
- << "{"
- << "bind (imb.bind, id.bind, id.count, im, statement_update" <<
- (s.versioned ? ", svm" : "") << ");"
- << "sts.update_image_version (im.version);"
- << "sts.update_id_binding_version (id.version);"
- << "imb.version++;"
- << "}";
-
- os << "update_statement& st (sts.update_statement ());"
- << "if (";
-
- if (s.versioned)
- os << "!st.empty () && ";
-
- os << "st.execute () == 0)" << endl;
-
- if (opt == 0)
- os << "throw object_not_persistent ();";
- else
- os << "throw object_changed ();";
-
- os << endl;
- }
-
- // Update readwrite containers if any.
- //
- if (s.readwrite_containers)
- {
- instance<container_calls> t (container_calls::update_call, &s);
- t->traverse (c_);
- }
-
- // Update the optimistic concurrency version in the object member.
- // Very similar code to object.
- //
- if (s.optimistic ()) // Note: not update_opt.
- {
- // Object is passed as const reference so we need to cast away
- // constness.
- //
- const char* obj ("const_cast<object_type&> (obj)");
- string inc (optimistic_version_increment (*opt));
-
- if (inc == "1")
- inc_member (*opt, obj, "obj", "version_type");
- else
- set_member (*opt, obj, inc, "", "version_type");
- }
-
- os << "}";
- }
-
- section_extra (s);
-
- if (rs != 0)
- rs->base = 0;
- }
-
- protected:
- semantics::class_& c_;
- string scope_;
- };
-
- // Output a list of parameters for the persist statement.
- //
- struct persist_statement_params: object_columns_base, virtual context
- {
- typedef persist_statement_params base;
-
- persist_statement_params (string& params,
- query_parameters& qp,
- const string& sep)
- : params_ (params), qp_ (qp), sep_ (sep)
- {
- }
-
- virtual void
- traverse_pointer (semantics::data_member& m, semantics::class_& c)
- {
- if (!inverse (m, key_prefix_))
- object_columns_base::traverse_pointer (m, c);
- }
-
- virtual bool
- traverse_column (semantics::data_member& m,
- string const& name,
- bool first)
- {
- string p;
-
- if (version (m))
- p = version_value (m);
- else
- {
- const string& qname (quote_id (name));
- const string& type (column_type ());
-
- p = auto_ (m) // Only simple, direct id can be auto.
- ? qp_.auto_id (m, qname, type)
- : qp_.next (m, qname, type);
- }
-
- if (!p.empty ())
- {
- if (!first)
- {
- params_ += ',';
- params_ += sep_;
- }
-
- params_ += (p != "DEFAULT" ? convert_to (p, column_type (), m) : p);
- }
-
- return !p.empty ();
- }
-
- virtual string
- version_value (semantics::data_member&)
- {
- return "1";
- }
-
- private:
- string& params_;
- query_parameters& qp_;
- const string& sep_;
- };
-
- //
- //
- struct class_: traversal::class_, virtual context
- {
- typedef class_ base;
-
- class_ ()
- : typedefs_ (false),
- query_columns_type_ (false, false, false),
- view_query_columns_type_ (false),
- grow_base_ (index_),
- grow_member_ (index_),
- grow_version_member_ (index_, "version_"),
- grow_discriminator_member_ (index_, "discriminator_"),
- bind_id_member_ ("id_"),
- bind_version_member_ ("version_"),
- bind_discriminator_member_ ("discriminator_"),
- init_id_image_member_ ("id_", "id"),
- init_version_image_member_ ("version_", "(*v)"),
- init_view_pointer_member_pre_ (true, *init_value_member_),
- init_view_pointer_member_post_ (false, *init_value_member_),
- init_id_value_member_ ("id"),
- init_id_value_member_id_image_ ("id", "id_"),
- init_version_value_member_ ("v"),
- init_named_version_value_member_ ("v", "version_"),
- init_discriminator_value_member_ ("d", "", false),
- init_named_discriminator_value_member_ (
- "d", "discriminator_", false)
- {
- init ();
- }
-
- class_ (class_ const&)
- : root_context (), //@@ -Wextra
- context (),
- typedefs_ (false),
- query_columns_type_ (false, false, false),
- view_query_columns_type_ (false),
- grow_base_ (index_),
- grow_member_ (index_),
- grow_version_member_ (index_, "version_"),
- grow_discriminator_member_ (index_, "discriminator_"),
- bind_id_member_ ("id_"),
- bind_version_member_ ("version_"),
- bind_discriminator_member_ ("discriminator_"),
- init_id_image_member_ ("id_", "id"),
- init_version_image_member_ ("version_", "(*v)"),
- init_view_pointer_member_pre_ (true, *init_value_member_),
- init_view_pointer_member_post_ (false, *init_value_member_),
- init_id_value_member_ ("id"),
- init_id_value_member_id_image_ ("id", "id_"),
- init_version_value_member_ ("v"),
- init_named_version_value_member_ ("v", "version_"),
- init_discriminator_value_member_ ("d", "", false),
- init_named_discriminator_value_member_ (
- "d", "discriminator_", false)
- {
- init ();
- }
-
- void
- init ()
- {
- *this >> defines_ >> *this;
- *this >> typedefs_ >> *this;
-
- if (generate_grow)
- {
- grow_base_inherits_ >> grow_base_;
- grow_member_names_ >> grow_member_;
- }
-
- bind_base_inherits_ >> bind_base_;
- bind_member_names_ >> bind_member_;
-
- init_image_base_inherits_ >> init_image_base_;
- init_image_member_names_ >> init_image_member_;
-
- init_value_base_inherits_ >> init_value_base_;
- init_value_member_names_ >> init_value_member_;
-
- init_view_pointer_member_pre_names_ >> init_view_pointer_member_pre_;
- init_view_pointer_member_post_names_ >> init_view_pointer_member_post_;
- }
-
- virtual void
- init_auto_id (semantics::data_member&, // id member
- string const&) // image variable prefix
- {
- if (insert_send_auto_id)
- assert (false);
- }
-
- virtual void
- init_image_pre (type&)
- {
- }
-
- virtual void
- init_value_extra ()
- {
- }
-
- virtual void
- traverse (type& c)
- {
- class_kind_type ck (class_kind (c));
-
- if (ck == class_other ||
- (!options.at_once () && class_file (c) != unit.file ()))
- return;
-
- names (c);
-
- context::top_object = context::cur_object = &c;
-
- switch (ck)
- {
- case class_object: traverse_object (c); break;
- case class_view: traverse_view (c); break;
- case class_composite: traverse_composite (c); break;
- default: break;
- }
-
- context::top_object = context::cur_object = 0;
- }
-
- //
- // statements
- //
-
- enum persist_position
- {
- persist_after_columns,
- persist_after_values
- };
-
- virtual string
- persist_statement_extra (type&, query_parameters&, persist_position)
- {
- return "";
- }
-
- virtual string
- update_statement_extra (type&)
- {
- return "";
- }
-
- //
- // common
- //
-
- virtual void
- post_query_ (type&, bool /*once_off*/)
- {
- }
-
- virtual void
- process_statement_columns (statement_columns&,
- statement_kind,
- bool /*dynamic*/)
- {
- }
-
- //
- // object
- //
-
- virtual void
- object_extra (type&) {}
-
- virtual void
- extra_statement_cache_extra_args (bool /*containers*/,
- bool /*sections*/) {}
-
- virtual void
- object_query_statement_ctor_args (type&,
- std::string const& q,
- bool process,
- bool /*prepared*/)
- {
- os << "conn," << endl
- << "text," << endl
- << process << "," << endl // Process.
- << "true," << endl // Optimize.
- << q << ".parameters_binding ()," << endl
- << "imb";
- }
-
- virtual void
- object_erase_query_statement_ctor_args (type&)
- {
- os << "conn," << endl
- << "text," << endl
- << "q.parameters_binding ()";
- }
-
- virtual string
- optimistic_version_init (semantics::data_member&, bool /*index*/ = false)
- {
- return "1";
- }
-
- // Returning "1" means increment by one.
- //
- virtual string
- optimistic_version_increment (semantics::data_member&,
- bool /*index*/ = false)
- {
- return "1";
- }
-
- virtual bool
- optimistic_insert_bind_version (semantics::data_member&)
- {
- return false;
- }
-
- virtual void
- traverse_object (type& c);
-
- //
- // view
- //
-
- virtual void
- view_extra (type&)
- {
- }
-
- virtual void
- view_query_statement_ctor_args (type&,
- string const& q,
- bool process,
- bool /*prepared*/)
- {
- os << "conn," << endl
- << q << ".clause ()," << endl
- << process << "," << endl // Process.
- << "true," << endl // Optimize.
- << q << ".parameters_binding ()," << endl
- << "imb";
- }
-
- virtual string
- from_trailer (type&) { return "";}
-
- virtual string
- select_trailer (type& c)
- {
- return c.get<view_query> ("query").for_update ? "FOR UPDATE" : "";
- }
-
- virtual string
- join_syntax (view_object const& vo)
- {
- const char* r (0);
-
- switch (vo.join)
- {
- case view_object::left: r = "LEFT JOIN"; break;
- case view_object::right: r = "RIGHT JOIN"; break;
- case view_object::full: r = "FULL JOIN"; break;
- case view_object::inner: r = "INNER JOIN"; break;
- case view_object::cross: r = "CROSS JOIN"; break;
- }
-
- return r;
- }
-
- virtual void
- traverse_view (type& c);
-
- struct expression
- {
- explicit
- expression (std::string const& v): kind (literal), value (v) {}
- expression (view_object* vo): kind (pointer), vo (vo) {}
-
- enum kind_type {literal, pointer};
-
- kind_type kind;
- std::string value;
- data_member_path member_path;
- view_object* vo;
- };
-
- expression
- translate_expression (type& c,
- cxx_tokens const&,
- semantics::scope& start_scope,
- location_t loc,
- string const& prag,
- bool* placeholder = 0,
- bool predicate = true);
- //
- // composite
- //
-
- virtual void
- traverse_composite (type& c)
- {
- bool versioned (context::versioned (c));
-
- string const& type (class_fq_name (c));
- string traits ("access::composite_value_traits< " + type + ", id_" +
- db.string () + " >");
-
- os << "// " << class_name (c) << endl
- << "//" << endl
- << endl;
-
- // Containers.
- //
- {
- instance<container_traits> t (c);
- t->traverse (c);
- }
-
- // grow ()
- //
- if (generate_grow)
- {
- os << "bool " << traits << "::" << endl
- << "grow (image_type& i," << endl
- << truncated_vector << " t";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (i);"
- << "ODB_POTENTIALLY_UNUSED (t);";
-
- if (versioned)
- os << "ODB_POTENTIALLY_UNUSED (svm);";
-
- os << endl
- << "bool grew (false);"
- << endl;
-
- index_ = 0;
- inherits (c, grow_base_inherits_);
- names (c, grow_member_names_);
-
- os << "return grew;"
- << "}";
- }
-
- // bind (image_type)
- //
- os << "void " << traits << "::" << endl
- << "bind (" << bind_vector << " b," << endl
- << "image_type& i," << endl
- << db << "::statement_kind sk";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (b);"
- << "ODB_POTENTIALLY_UNUSED (i);"
- << "ODB_POTENTIALLY_UNUSED (sk);";
-
- if (versioned)
- os << "ODB_POTENTIALLY_UNUSED (svm);";
-
- os << endl
- << "using namespace " << db << ";"
- << endl;
-
- if (readonly (c))
- os << "assert (sk != statement_update);"
- << endl;
-
- os << "std::size_t n (0);"
- << "ODB_POTENTIALLY_UNUSED (n);"
- << endl;
-
- inherits (c, bind_base_inherits_);
- names (c, bind_member_names_);
-
- os << "}";
-
- // init (image, value)
- //
- os << (generate_grow ? "bool " : "void ") << traits << "::" << endl
- << "init (image_type& i," << endl
- << "const value_type& o," << endl
- << db << "::statement_kind sk";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (i);"
- << "ODB_POTENTIALLY_UNUSED (o);"
- << "ODB_POTENTIALLY_UNUSED (sk);";
-
- if (versioned)
- os << "ODB_POTENTIALLY_UNUSED (svm);";
-
- os << endl
- << "using namespace " << db << ";"
- << endl;
-
- if (readonly (c))
- os << "assert (sk != statement_update);"
- << endl;
-
- if (generate_grow)
- os << "bool grew (false);"
- << endl;
-
- inherits (c, init_image_base_inherits_);
- names (c, init_image_member_names_);
-
- if (generate_grow)
- os << "return grew;";
-
- os << "}";
-
- // init (value, image)
- //
- os << "void " << traits << "::" << endl
- << "init (value_type& o," << endl
- << "const image_type& i," << endl
- << "database* db";
-
- if (versioned)
- os << "," << endl
- << "const schema_version_migration& svm";
-
- os << ")"
- << "{"
- << "ODB_POTENTIALLY_UNUSED (o);"
- << "ODB_POTENTIALLY_UNUSED (i);"
- << "ODB_POTENTIALLY_UNUSED (db);";
-
- if (versioned)
- os << "ODB_POTENTIALLY_UNUSED (svm);";
-
- os << endl;
-
- inherits (c, init_value_base_inherits_);
- names (c, init_value_member_names_);
-
- os << "}";
- }
-
- private:
- traversal::defines defines_;
- typedefs typedefs_;
-
- instance<query_columns_type> query_columns_type_;
- instance<view_query_columns_type> view_query_columns_type_;
-
- size_t index_;
- instance<grow_base> grow_base_;
- traversal::inherits grow_base_inherits_;
- instance<grow_member> grow_member_;
- traversal::names grow_member_names_;
- instance<grow_member> grow_version_member_;
- instance<grow_member> grow_discriminator_member_;
-
-
- instance<bind_base> bind_base_;
- traversal::inherits bind_base_inherits_;
- instance<bind_member> bind_member_;
- traversal::names bind_member_names_;
- instance<bind_member> bind_id_member_;
- instance<bind_member> bind_version_member_;
- instance<bind_member> bind_discriminator_member_;
-
- instance<init_image_base> init_image_base_;
- traversal::inherits init_image_base_inherits_;
- instance<init_image_member> init_image_member_;
- traversal::names init_image_member_names_;
-
- instance<init_image_member> init_id_image_member_;
- instance<init_image_member> init_version_image_member_;
-
- instance<init_value_base> init_value_base_;
- traversal::inherits init_value_base_inherits_;
- instance<init_value_member> init_value_member_;
- traversal::names init_value_member_names_;
-
- instance<init_view_pointer_member> init_view_pointer_member_pre_;
- instance<init_view_pointer_member> init_view_pointer_member_post_;
- traversal::names init_view_pointer_member_pre_names_;
- traversal::names init_view_pointer_member_post_names_;
-
- instance<init_value_member> init_id_value_member_;
- instance<init_value_member> init_id_value_member_id_image_;
- instance<init_value_member> init_version_value_member_;
- instance<init_value_member> init_named_version_value_member_;
- instance<init_value_member> init_discriminator_value_member_;
- instance<init_value_member> init_named_discriminator_value_member_;
- };
-
- struct include: virtual context
- {
- typedef include base;
-
- virtual void
- generate ()
- {
- extra_pre ();
-
- os << "#include <cassert>" << endl
- << "#include <cstring> // std::memcpy" << endl;
-
- if (features.polymorphic_object)
- os << "#include <typeinfo>" << endl;
-
- os << endl;
-
- if (features.polymorphic_object)
- os << "#include <odb/polymorphic-map.hxx>" << endl;
-
- if (embedded_schema)
- os << "#include <odb/schema-catalog-impl.hxx>" << endl;
-
- if (multi_dynamic)
- os << "#include <odb/function-table.hxx>" << endl;
-
- os << endl;
-
- os << "#include <odb/" << db << "/traits.hxx>" << endl
- << "#include <odb/" << db << "/database.hxx>" << endl
- << "#include <odb/" << db << "/transaction.hxx>" << endl
- << "#include <odb/" << db << "/connection.hxx>" << endl
- << "#include <odb/" << db << "/statement.hxx>" << endl
- << "#include <odb/" << db << "/statement-cache.hxx>" << endl;
-
- if (features.simple_object)
- os << "#include <odb/" << db << "/simple-object-statements.hxx>" << endl;
-
- if (features.polymorphic_object)
- os << "#include <odb/" << db << "/polymorphic-object-statements.hxx>" << endl;
-
- if (features.no_id_object)
- os << "#include <odb/" << db << "/no-id-object-statements.hxx>" << endl;
-
- if (features.view)
- os << "#include <odb/" << db << "/view-statements.hxx>" << endl;
-
- if (features.section)
- os << "#include <odb/" << db << "/section-statements.hxx>" << endl;
-
- os << "#include <odb/" << db << "/container-statements.hxx>" << endl
- << "#include <odb/" << db << "/exceptions.hxx>" << endl;
-
- if (options.generate_query ())
- {
- if (options.generate_prepared ())
- os << "#include <odb/" << db << "/prepared-query.hxx>" << endl;
-
- if (features.simple_object)
- os << "#include <odb/" << db << "/simple-object-result.hxx>" << endl;
-
- if (features.polymorphic_object)
- os << "#include <odb/" << db << "/polymorphic-object-result.hxx>" << endl;
-
- if (features.no_id_object)
- os << "#include <odb/" << db << "/no-id-object-result.hxx>" << endl;
-
- if (features.view)
- os << "#include <odb/" << db << "/view-result.hxx>" << endl;
- }
-
- extra_post ();
-
- os << endl;
- }
-
- virtual void
- extra_pre ()
- {
- }
-
- virtual void
- extra_post ()
- {
- }
- };
- }
-}
-
-#endif // ODB_RELATIONAL_SOURCE_HXX
diff --git a/odb/relational/sqlite/source.cxx b/odb/relational/sqlite/source.cxx
deleted file mode 100644
index 2624af2..0000000
--- a/odb/relational/sqlite/source.cxx
+++ /dev/null
@@ -1,470 +0,0 @@
-// file : odb/relational/sqlite/source.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <odb/relational/source.hxx>
-
-#include <odb/relational/sqlite/common.hxx>
-#include <odb/relational/sqlite/context.hxx>
-
-using namespace std;
-
-namespace relational
-{
- namespace sqlite
- {
- namespace source
- {
- namespace relational = relational::source;
-
- struct query_parameters: relational::query_parameters, context
- {
- query_parameters (base const& x): base (x) {}
-
- virtual string
- next (semantics::data_member& m,
- const string& column,
- const string& sqlt)
- {
- // Handle stream columns. Specifically, we somehow need to
- // pass the column name to the code that runs in the
- // statement. So what we are going to do is encode it
- // in the parameter name.
- //
- if (sk_ == statement_insert || sk_ == statement_update)
- {
- const sql_type& t (parse_sql_type (sqlt, m, false));
- if (t.stream)
- {
- // The column name is quoted.
- //
- string r (column);
- r[0] = '$'; // Replace leading '"'.
- r.resize (r.size () - 1); // Remove trailing '"'.
-
- // Verify it only contains allowed characters.
- //
- for (size_t i (1); i != r.size (); ++i)
- {
- char c (r[i]);
- if (c != '_' &&
- (c < '0' || c > '9') &&
- (c < 'a' || c > 'z') &&
- (c < 'A' || c > 'Z'))
- {
- cerr << m.file () << ":" << m.line () << ":" << m.column ()
- << ": error: unsupported character '" << c << "' in "
- << sqlt << " column name " << column << endl;
-
- cerr << m.file () << ":" << m.line () << ":" << m.column ()
- << ": info: STREAM column can contain alpha-numeric "
- << "characters plus '_'" << endl;
-
- throw operation_failed ();
- }
- }
-
- // For TEXT columns, since we use the *_bind_zeroblob()
- // function (there is no *_bind_zerotext()), the value
- // that will be stored is BLOB, not TEXT, unless we
- // explicitly CAST it. The user better make sure the
- // encoding of raw TEXT data they are going to write
- // matches the database encoding.
- //
- if (t.type == sql_type::TEXT)
- r = "CAST(" + r + " AS TEXT)";
-
- return r;
- }
- }
-
- return "?";
- }
- };
- entry<query_parameters> query_parameters_;
-
- //
- // bind
- //
-
- struct bind_member: relational::bind_member_impl<sql_type>,
- member_base
- {
- bind_member (base const& x)
- : member_base::base (x), // virtual base
- member_base::base_impl (x), // virtual base
- base_impl (x),
- member_base (x)
- {
- }
-
- virtual void
- traverse_integer (member_info& mi)
- {
- os << b << ".type = sqlite::bind::integer;"
- << b << ".buffer = &" << arg << "." << mi.var << "value;"
- << b << ".is_null = &" << arg << "." << mi.var << "null;";
- }
-
- virtual void
- traverse_real (member_info& mi)
- {
- os << b << ".type = sqlite::bind::real;"
- << b << ".buffer = &" << arg << "." << mi.var << "value;"
- << b << ".is_null = &" << arg << "." << mi.var << "null;";
- }
-
- virtual void
- traverse_text (member_info& mi)
- {
- os << b << ".type = sqlite::image_traits<" << endl
- << " " << mi.fq_type () << "," << endl
- << " sqlite::id_text>::bind_value;"
- << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
- << b << ".size = &" << arg << "." << mi.var << "size;"
- << b << ".capacity = " << arg << "." << mi.var <<
- "value.capacity ();"
- << b << ".is_null = &" << arg << "." << mi.var << "null;";
- }
-
- virtual void
- traverse_blob (member_info& mi)
- {
- os << b << ".type = sqlite::bind::blob;"
- << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
- << b << ".size = &" << arg << "." << mi.var << "size;"
- << b << ".capacity = " << arg << "." << mi.var <<
- "value.capacity ();"
- << b << ".is_null = &" << arg << "." << mi.var << "null;";
- }
-
- virtual void
- traverse_stream (member_info& mi)
- {
- os << b << ".type = sqlite::bind::stream;"
- << b << ".buffer = &" << arg << "." << mi.var << "value;"
- << b << ".size = &" << arg << "." << mi.var << "size;"
- << b << ".is_null = &" << arg << "." << mi.var << "null;";
- }
- };
- entry<bind_member> bind_member_;
-
- //
- // grow
- //
-
- struct grow_member: relational::grow_member_impl<sql_type>,
- member_base
- {
- grow_member (base const& x)
- : member_base::base (x), // virtual base
- member_base::base_impl (x), // virtual base
- base_impl (x),
- member_base (x) {}
-
- virtual void
- traverse_integer (member_info&)
- {
- os << e << " = false;"
- << endl;
- }
-
- virtual void
- traverse_real (member_info&)
- {
- os << e << " = false;"
- << endl;
- }
-
- virtual void
- traverse_string (member_info& mi)
- {
- os << "if (" << e << ")" << endl
- << "{"
- << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
- << "grew = true;"
- << "}";
- }
-
- virtual void
- traverse_stream (member_info&)
- {
- os << e << " = false;"
- << endl;
- }
- };
- entry<grow_member> grow_member_;
-
- //
- // init image
- //
-
- struct init_image_member: relational::init_image_member_impl<sql_type>,
- member_base
- {
- init_image_member (base const& x)
- : member_base::base (x), // virtual base
- member_base::base_impl (x), // virtual base
- base_impl (x),
- member_base (x)
- {
- }
-
- virtual void
- set_null (member_info& mi)
- {
- os << "i." << mi.var << "null = true;";
- }
-
- virtual void
- traverse_integer (member_info& mi)
- {
- os << traits << "::set_image (" << endl
- << "i." << mi.var << "value," << endl
- << "is_null," << endl
- << member << ");"
- << "i." << mi.var << "null = is_null;";
- }
-
- virtual void
- traverse_real (member_info& mi)
- {
- os << traits << "::set_image (" << endl
- << "i." << mi.var << "value," << endl
- << "is_null," << endl
- << member << ");"
- << "i." << mi.var << "null = is_null;";
- }
-
- virtual void
- traverse_string (member_info& mi)
- {
- os << "std::size_t cap (i." << mi.var << "value.capacity ());"
- << traits << "::set_image (" << endl
- << "i." << mi.var << "value," << endl
- << "i." << mi.var << "size," << endl
- << "is_null," << endl
- << member << ");"
- << "i." << mi.var << "null = is_null;"
- << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
- }
-
- virtual void
- traverse_stream (member_info& mi)
- {
- os << traits << "::set_image (" << endl
- << "i." << mi.var << "value," << endl
- << "i." << mi.var << "size," << endl
- << "is_null," << endl
- << member << ");"
- << "i." << mi.var << "null = is_null;";
- }
- };
- entry<init_image_member> init_image_member_;
-
- //
- // init value
- //
-
- struct init_value_member: relational::init_value_member_impl<sql_type>,
- member_base
- {
- init_value_member (base const& x)
- : member_base::base (x), // virtual base
- member_base::base_impl (x), // virtual base
- base_impl (x),
- member_base (x)
- {
- }
-
- virtual void
- get_null (string const& var) const
- {
- os << "i." << var << "null";
- }
-
- virtual void
- traverse_integer (member_info& mi)
- {
- os << traits << "::set_value (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "i." << mi.var << "null);"
- << endl;
- }
-
- virtual void
- traverse_real (member_info& mi)
- {
- os << traits << "::set_value (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "i." << mi.var << "null);"
- << endl;
- }
-
- virtual void
- traverse_string (member_info& mi)
- {
- os << traits << "::set_value (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "i." << mi.var << "size," << endl
- << "i." << mi.var << "null);"
- << endl;
- }
-
- virtual void
- traverse_stream (member_info& mi)
- {
- os << traits << "::set_value (" << endl
- << member << "," << endl
- << "i." << mi.var << "value," << endl
- << "i." << mi.var << "size," << endl
- << "i." << mi.var << "null);"
- << endl;
- }
- };
- entry<init_value_member> init_value_member_;
-
- struct statement_columns_common: context
- {
- void
- process (relational::statement_columns& cs, statement_kind sk)
- {
- using relational::statement_columns;
-
- // For SELECT statements, add _ROWID_ "follow-up" column to
- // each stream column. The reason we need both, and not just
- // ROWID is the NULL value. Let's hope that SELECT'ing a BLOB
- // but not actually reading it with sqlite3_result_blob() is
- // as fast as not SELECT'ing it.
- //
- if (sk != statement_select)
- return;
-
- for (statement_columns::iterator i (cs.begin ());
- i != cs.end (); ++i)
- {
- if (parse_sql_type (i->type, *i->member).stream)
- {
- // Column is already table-qualified and quoted. Do some
- // surgery to replace it with _ROWID_. That is, we want to
- // transform "table"."column" to "table"."_ROWID_".
- //
- string c (i->column);
- string::size_type n (c.size ()), p (c.rfind ('"', n - 2));
- assert (p != string::npos);
- string as (c, p + 1, n - p - 2);
- c.resize (p);
- c += "\"_ROWID_\"";
-
- // We are going to pack this "tightly", without any newlines,
- // so that the statement processing code treats them as a
- // single column.
- //
- i->column += ',';
- i->column += c;
- }
- }
- }
- };
-
- struct container_traits: relational::container_traits,
- statement_columns_common
- {
- container_traits (base const& x): base (x) {}
-
- virtual void
- cache_result (string const&)
- {
- // Caching is not necessary since SQLite can execute several
- // interleaving statements.
- //
- }
-
- virtual void
- process_statement_columns (relational::statement_columns& cols,
- statement_kind sk,
- bool)
- {
- statement_columns_common::process (cols, sk);
- }
- };
- entry<container_traits> container_traits_;
-
- struct section_traits: relational::section_traits,
- statement_columns_common
- {
- section_traits (base const& x): base (x) {}
-
- virtual void
- process_statement_columns (relational::statement_columns& cols,
- statement_kind sk)
- {
- statement_columns_common::process (cols, sk);
- }
- };
- entry<section_traits> section_traits_;
-
- struct class_: relational::class_, statement_columns_common
- {
- class_ (base const& x): base (x) {}
-
- virtual void
- init_auto_id (semantics::data_member& m, string const& im)
- {
- // Don't set the id value to NULL if this is a nullable wrapper.
- // This will allow the user to control whether the value is auto or
- // manually assigned by using something like this:
- //
- // #pragma db auto
- // odb::nullable<int64_t> id;
- //
- semantics::type& t (utype (m));
- if (wrapper (t) && t.template get<bool> ("wrapper-null-handler"))
- return;
-
- os << im << "null = true;"
- << endl;
- }
-
- virtual string
- select_trailer (type&)
- {
- // SQLite has not support for FOR UPDATE and since this is an
- // optimization, we simply ignore it.
- //
- return "";
- }
-
- virtual string
- join_syntax (view_object const& vo)
- {
- const char* n (0);
-
- if (vo.join == view_object::full)
- n = "FULL OUTER JOIN";
- else if (vo.join == view_object::right)
- n = "RIGHT OUTER JOIN";
-
- if (n != 0)
- {
- error (vo.loc) << n << " is not supported by SQLite" << endl;
- throw operation_failed ();
- }
-
- return base::join_syntax (vo);
- }
-
- virtual void
- process_statement_columns (relational::statement_columns& cols,
- statement_kind sk,
- bool)
- {
- statement_columns_common::process (cols, sk);
- }
- };
- entry<class_> class_entry_;
- }
- }
-}
diff --git a/odb/semantics/class-template.cxx b/odb/semantics/class-template.cxx
deleted file mode 100644
index d764b79..0000000
--- a/odb/semantics/class-template.cxx
+++ /dev/null
@@ -1,54 +0,0 @@
-// file : odb/semantics/class-template.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <cutl/compiler/type-info.hxx>
-#include <odb/semantics/class-template.hxx>
-
-namespace semantics
-{
- class_template::
- class_template (path const& file, size_t line, size_t column, tree tn)
- : node (file, line, column, tn)
- {
- }
-
- class_instantiation::
- class_instantiation (path const& file,
- size_t line,
- size_t column,
- tree tn)
- : node (file, line, column, tn)
- {
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- using compiler::type_info;
-
- // class_template
- //
- {
- type_info ti (typeid (class_template));
- ti.add_base (typeid (type_template));
- ti.add_base (typeid (scope));
- insert (ti);
- }
-
- // class_instantiation
- //
- {
- type_info ti (typeid (class_instantiation));
- ti.add_base (typeid (class_));
- ti.add_base (typeid (type_instantiation));
- insert (ti);
- }
- }
- } init_;
- }
-}
diff --git a/odb/semantics/class.cxx b/odb/semantics/class.cxx
deleted file mode 100644
index acecb35..0000000
--- a/odb/semantics/class.cxx
+++ /dev/null
@@ -1,175 +0,0 @@
-// file : odb/semantics/class.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <odb/gcc.hxx> // TYPE_HAS_DEFAULT_CONSTRUCTOR
-
-#include <cutl/compiler/type-info.hxx>
-#include <odb/semantics/class.hxx>
-
-namespace semantics
-{
- inherits::
- inherits (access_type access, bool virt)
- : virt_ (virt), access_ (access)
- {
- }
-
- class_::
- class_ (path const& file, size_t line, size_t column, tree tn)
- : node (file, line, column, tn)
- {
- }
-
- bool class_::
- default_ctor () const
- {
- tree t (tree_node ());
-
- // TYPE_HAS_DEFAULT_CONSTRUCTOR() returns true if we have a deleted
- // default ctor. locate_ctor(), on the other hand, returns NULL_TREE in
- // this case.
- //
- if (TYPE_HAS_DEFAULT_CONSTRUCTOR (t))
- {
-#if BUILDING_GCC_MAJOR >= 8
-
- // Work around GCC bug 86441. Essentially, we should not trigger an
- // instantiation or completion of the default ctor. As a result, we will
- // assume that if we have a lazy default ctor, it is not implicitly
- // deleted.
- //
- if (CLASSTYPE_LAZY_DEFAULT_CTOR (t))
- return true;
-
- for (ovl_iterator i (CLASSTYPE_CONSTRUCTORS (t)); i; ++i)
- {
- tree f (*i);
-
- if (TREE_CODE (f) == FUNCTION_DECL && DECL_DELETED_FN (f))
- continue;
-
- if (default_ctor_p (f))
- return true;
- }
-#else
- return locate_ctor (t) != NULL_TREE;
-#endif
-
- }
-
- return false;
- }
-
- bool class_::
- complete () const
- {
- return COMPLETE_TYPE_P (tree_node ());
- }
-
- bool class_::
- abstract () const
- {
- return CLASSTYPE_PURE_VIRTUALS (tree_node ());
- }
-
- names* class_::
- lookup (string const& name,
- type_id const& ti,
- unsigned int flags,
- bool* ph) const
- {
- bool h (false);
- bool& rh (ph != 0 ? *ph : h);
-
- names* r (scope::lookup (name, ti, flags | exclude_outer, &rh));
-
- if (r != 0)
- return r;
-
- // If we found a name but the types didn't match, then bail out
- // unless we want hidden names.
- //
- if (rh && (flags & include_hidden) == 0)
- return 0;
-
- // Look in the base classes unless requested not to. For the name
- // lookup purposes, bases can be viewed as a parallel set of outer
- // scopes that are searched after the class scope and before any
- // real outer scope. Interestingly, outer scopes of bases are not
- // considered during this lookup, only their bases.
- //
- if ((flags & exclude_base) == 0)
- {
- // Being hidden in one base doesn't mean it is also hidden in the
- // other. Normally that would be an ambiguous lookup, but we use
- // relaxed rules.
- //
- bool any_h (false); // Indicates whether any base hides the name.
-
- for (inherits_iterator i (inherits_begin ()); i != inherits_end (); ++i)
- {
- bool h (false); // Indicates whether this base hides the name.
- names* br (i->base ().lookup (name, ti, flags | exclude_outer, &h));
- any_h = any_h || h;
-
- if (br != 0)
- {
- if (r != 0)
- throw ambiguous (*r, *br);
-
- r = br;
-
- if (h)
- rh = true;
- }
- }
-
- if (r != 0)
- return r;
-
- if (any_h)
- {
- rh = true;
- if ((flags & include_hidden) == 0)
- return 0;
- }
- }
-
- // Look in the outer scope unless requested not to.
- //
- if ((flags & exclude_outer) == 0)
- return scope ().lookup (name, ti, flags, &rh);
-
- return 0;
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- using compiler::type_info;
-
- // inherits
- //
- {
- type_info ti (typeid (inherits));
- ti.add_base (typeid (edge));
- insert (ti);
- }
-
- // class_
- //
- {
- type_info ti (typeid (class_));
- ti.add_base (typeid (type));
- ti.add_base (typeid (scope));
- insert (ti);
- }
- }
- } init_;
- }
-}
diff --git a/odb/semantics/derived.cxx b/odb/semantics/derived.cxx
deleted file mode 100644
index 9cf1504..0000000
--- a/odb/semantics/derived.cxx
+++ /dev/null
@@ -1,254 +0,0 @@
-// file : odb/semantics/derived.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <sstream>
-
-#include <cutl/compiler/type-info.hxx>
-#include <odb/semantics/derived.hxx>
-
-using namespace std;
-
-namespace semantics
-{
- qualifies::
- qualifies ()
- : hint_ (0)
- {
- }
-
- qualifier::
- qualifier (path const& file,
- size_t line,
- size_t column,
- tree tn,
- bool c,
- bool v,
- bool r)
- : node (file, line, column, tn), c_ (c), v_ (v), r_ (r)
- {
- }
-
- string qualifier::
- fq_name (names* hint) const
- {
- if (hint != 0 || defined_ != 0)
- return nameable::fq_name (hint);
-
- // GCC type_as_string() for some reason cannot correctly print names
- // like 'const std::string'. Instead it prints 'const string'.
- //
- type& bt (base_type ());
-
- // Use the trailing qualifier syntax so that we don't get bogged down
- // in stuff like 'const const foo*'. We also have to handle arrays in
- // a special way since char[16] const is not a legal syntax.
- //
- string q;
- if (c_)
- q += " const";
-
- if (v_)
- q += " volatile";
-
- if (r_)
- q += " __restrict";
-
- hint = qualifies ().hint ();
-
- if (array* a = dynamic_cast<array*> (&bt))
- return a->fq_name (hint, q);
- else
- return bt.fq_name (hint) + q;
- }
-
- points::
- points ()
- : hint_ (0)
- {
- }
-
- pointer::
- pointer (path const& file, size_t line, size_t column, tree tn)
- : node (file, line, column, tn)
- {
- }
-
- string pointer::
- fq_name (names* hint) const
- {
- if (hint != 0 || defined_ != 0)
- return nameable::fq_name (hint);
-
- // GCC type_as_string() for some reason cannot correctly print names
- // like 'const std::string*'. Instead it prints 'const string*'.
- //
- string r (base_type ().fq_name (points ().hint ()));
- r += '*';
- return r;
- }
-
- references::
- references ()
- : hint_ (0)
- {
- }
-
- reference::
- reference (path const& file, size_t line, size_t column, tree tn)
- : node (file, line, column, tn)
- {
- }
-
- string reference::
- fq_name (names* hint) const
- {
- if (hint != 0 || defined_ != 0)
- return nameable::fq_name (hint);
-
- // GCC type_as_string() for some reason cannot correctly print names
- // like 'const std::string&'. Instead it prints 'const string&'.
- //
- string r (base_type ().fq_name (points ().hint ()));
- r += '&';
- return r;
- }
-
- contains::
- contains ()
- : hint_ (0)
- {
- }
-
- array::
- array (path const& file,
- size_t line,
- size_t column,
- tree tn,
- unsigned long long size)
- : node (file, line, column, tn), size_ (size)
- {
- }
-
- string array::
- fq_name (names* hint) const
- {
- // GCC type_as_string() for some reason cannot correctly print names
- // like 'const std::string[123]'. Instead it prints 'const string[123]'.
- //
- string t;
- return fq_name (hint, t);
- }
-
- string array::
- fq_name (names* hint, string& t) const
- {
- if (hint != 0 || defined_ != 0)
- return nameable::fq_name (hint) + t;
-
- t += '[';
- ostringstream ostr;
- ostr << size ();
- t += ostr.str ();
-
- if (size () > 0xFFFFFFFF)
- t += "ULL";
- else if (size () > 2147483647)
- t += "U";
-
- t += ']';
-
- type& bt (base_type ());
- hint = contains ().hint ();
- array* a;
-
- if (hint != 0 || (a = dynamic_cast<array*> (&bt)) == 0)
- return bt.fq_name (hint) + t;
- else
- return a->fq_name (0, t);
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- using compiler::type_info;
-
- // derived_type
- //
- {
- type_info ti (typeid (derived_type));
- ti.add_base (typeid (type));
- insert (ti);
- }
-
- // qualifies
- //
- {
- type_info ti (typeid (qualifies));
- ti.add_base (typeid (edge));
- insert (ti);
- }
-
- // qualifier
- //
- {
- type_info ti (typeid (qualifier));
- ti.add_base (typeid (derived_type));
- insert (ti);
- }
-
- // points
- //
- {
- type_info ti (typeid (points));
- ti.add_base (typeid (edge));
- insert (ti);
- }
-
- // pointer
- //
- {
- type_info ti (typeid (pointer));
- ti.add_base (typeid (derived_type));
- insert (ti);
- }
-
- // references
- //
- {
- type_info ti (typeid (references));
- ti.add_base (typeid (edge));
- insert (ti);
- }
-
- // reference
- //
- {
- type_info ti (typeid (reference));
- ti.add_base (typeid (derived_type));
- insert (ti);
- }
-
- // contains
- //
- {
- type_info ti (typeid (contains));
- ti.add_base (typeid (edge));
- insert (ti);
- }
-
- // array
- //
- {
- type_info ti (typeid (array));
- ti.add_base (typeid (derived_type));
- insert (ti);
- }
- }
- } init_;
- }
-}
diff --git a/odb/semantics/derived.hxx b/odb/semantics/derived.hxx
deleted file mode 100644
index 60c4896..0000000
--- a/odb/semantics/derived.hxx
+++ /dev/null
@@ -1,438 +0,0 @@
-// file : odb/semantics/derived.hxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#ifndef ODB_SEMANTICS_DERIVED_HXX
-#define ODB_SEMANTICS_DERIVED_HXX
-
-#include <odb/semantics/elements.hxx>
-
-namespace semantics
-{
- //
- // Derived types (cvr-qualifiers, pointer, reference, and array).
- //
-
- class derived_type: public type
- {
- public:
- virtual type&
- base_type () const = 0;
- };
-
- //
- //
- class qualifier;
-
- class qualifies: public edge
- {
- public:
- typedef semantics::type type_type;
- typedef semantics::qualifier qualifier_type;
-
- type_type&
- type () const
- {
- return *type_;
- }
-
- qualifier_type&
- qualifier () const
- {
- return *qualifier_;
- }
-
- // Name hint of the base type.
- //
- public:
- void
- hint (names& hint)
- {
- hint_ = &hint;
- }
-
- names*
- hint () const
- {
- return hint_;
- }
-
- public:
- qualifies ();
-
- void
- set_left_node (qualifier_type& n)
- {
- qualifier_ = &n;
- }
-
- void
- set_right_node (type_type& n)
- {
- type_ = &n;
- }
-
- protected:
- type_type* type_;
- qualifier_type* qualifier_;
- names* hint_;
- };
-
- class qualifier: public derived_type
- {
- public:
- typedef semantics::qualifies qualifies_type;
-
- bool
- const_ () const
- {
- return c_;
- }
-
- bool
- volatile_ () const
- {
- return v_;
- }
-
- bool
- restrict_ () const
- {
- return r_;
- }
-
- virtual type&
- base_type () const
- {
- return qualifies_->type ();
- }
-
- qualifies_type&
- qualifies () const
- {
- return *qualifies_;
- }
-
- public:
- virtual string
- fq_name (names*) const;
-
- public:
- qualifier (path const&,
- size_t line,
- size_t column,
- tree,
- bool c,
- bool v,
- bool r);
-
- void
- add_edge_left (qualifies_type& e)
- {
- qualifies_ = &e;
- }
-
- private:
- bool c_, v_, r_;
- qualifies_type* qualifies_;
- };
-
- //
- //
- class pointer;
-
- class points: public edge
- {
- public:
- typedef semantics::type type_type;
- typedef semantics::pointer pointer_type;
-
- type_type&
- type () const
- {
- return *type_;
- }
-
- pointer_type&
- pointer () const
- {
- return *pointer_;
- }
-
- // Name hint of the base type.
- //
- public:
- void
- hint (names& hint)
- {
- hint_ = &hint;
- }
-
- names*
- hint () const
- {
- return hint_;
- }
-
- public:
- points ();
-
- void
- set_left_node (pointer_type& n)
- {
- pointer_ = &n;
- }
-
- void
- set_right_node (type_type& n)
- {
- type_ = &n;
- }
-
- protected:
- type_type* type_;
- pointer_type* pointer_;
- names* hint_;
- };
-
- class pointer: public derived_type
- {
- public:
- typedef semantics::points points_type;
-
- virtual type&
- base_type () const
- {
- return points_->type ();
- }
-
- points_type&
- points () const
- {
- return *points_;
- }
-
- public:
- virtual string
- fq_name (names*) const;
-
- public:
- pointer (path const&, size_t line, size_t column, tree);
-
- void
- add_edge_left (points_type& e)
- {
- points_ = &e;
- }
-
- private:
- points_type* points_;
- };
-
-
- //
- //
- class reference;
-
- class references: public edge
- {
- public:
- typedef semantics::type type_type;
- typedef semantics::reference reference_type;
-
- type_type&
- type () const
- {
- return *type_;
- }
-
- reference_type&
- reference () const
- {
- return *reference_;
- }
-
- // Name hint of the base type.
- //
- public:
- void
- hint (names& hint)
- {
- hint_ = &hint;
- }
-
- names*
- hint () const
- {
- return hint_;
- }
-
- public:
- references ();
-
- void
- set_left_node (reference_type& n)
- {
- reference_ = &n;
- }
-
- void
- set_right_node (type_type& n)
- {
- type_ = &n;
- }
-
- protected:
- type_type* type_;
- reference_type* reference_;
- names* hint_;
- };
-
- class reference: public derived_type
- {
- public:
- typedef semantics::references references_type;
-
- virtual type&
- base_type () const
- {
- return references_->type ();
- }
-
- references_type&
- references () const
- {
- return *references_;
- }
-
- public:
- virtual string
- fq_name (names*) const;
-
- public:
- reference (path const&, size_t line, size_t column, tree);
-
- void
- add_edge_left (references_type& e)
- {
- references_ = &e;
- }
-
- private:
- references_type* references_;
- };
-
-
- //
- //
- class array;
-
- class contains: public edge
- {
- public:
- typedef semantics::type type_type;
- typedef semantics::array array_type;
-
- type_type&
- type () const
- {
- return *type_;
- }
-
- array_type&
- array () const
- {
- return *array_;
- }
-
- // Name hint of the base type.
- //
- public:
- void
- hint (names& hint)
- {
- hint_ = &hint;
- }
-
- names*
- hint () const
- {
- return hint_;
- }
-
- public:
- contains ();
-
- void
- set_left_node (array_type& n)
- {
- array_ = &n;
- }
-
- void
- set_right_node (type_type& n)
- {
- type_ = &n;
- }
-
- protected:
- type_type* type_;
- array_type* array_;
- names* hint_;
- };
-
- class array: public derived_type
- {
- public:
- typedef semantics::contains contains_type;
-
- // Return the number of elements in the array or 0 if it is not
- // specified (e.g., int[]).
- //
- unsigned long long
- size () const
- {
- return size_;
- }
-
- virtual type&
- base_type () const
- {
- return contains_->type ();
- }
-
- contains_type&
- contains () const
- {
- return *contains_;
- }
-
- public:
- virtual string
- fq_name (names*) const;
-
- private:
- friend class qualifier;
-
- string
- fq_name (names*, string& trailer) const;
-
- public:
- array (path const&,
- size_t line,
- size_t column,
- tree,
- unsigned long long size);
-
- void
- add_edge_left (contains_type& e)
- {
- contains_ = &e;
- }
-
- private:
- contains_type* contains_;
- unsigned long long size_;
- };
-}
-
-#endif // ODB_SEMANTICS_DERIVED_HXX
diff --git a/odb/semantics/elements.cxx b/odb/semantics/elements.cxx
deleted file mode 100644
index 2d266cf..0000000
--- a/odb/semantics/elements.cxx
+++ /dev/null
@@ -1,619 +0,0 @@
-// file : odb/semantics/elements.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <odb/gcc.hxx>
-
-#include <cutl/compiler/type-info.hxx>
-
-#include <odb/cxx-lexer.hxx>
-
-#include <odb/semantics/elements.hxx>
-#include <odb/semantics/namespace.hxx>
-#include <odb/semantics/unit.hxx>
-
-using namespace std;
-
-namespace semantics
-{
- // access
- //
- static char const* access_str[] = {"public", "protected", "private"};
-
- char const* access::
- string () const
- {
- return access_str[value_];
- }
-
- //
- //
- node::
- node (path const& file, size_t line, size_t column, tree tn)
- : tree_node_ (tn), loc_ (file, line, column)
- {
- }
-
- node::
- node ()
- : loc_ (0)
- {
- // GCC plugin machinery #define's abort as a macro.
- //
- // std::abort ();
- abort ();
- }
-
- // nameable
- //
-
- bool nameable::
- in_scope (scope_type& s)
- {
- for (scope_type* p (&scope ());; p = &p->scope_ ())
- {
- //@@ Need to handle namespace extensions.
- //
- if (p == &s)
- return true;
-
- if (p->global_scope ())
- break;
- }
-
- return false;
- }
-
- bool nameable::
- anonymous_ () const
- {
- tree n (tree_node ());
-
- if (TYPE_P (n))
- {
- tree name (0);
-
- if (tree decl = TYPE_NAME (n))
- name = DECL_NAME (decl);
-
- return name != 0 && IDENTIFIER_ANON_P (name);
- }
-
- return true;
- }
-
- bool nameable::
- fq_anonymous_ (scope_entry const* prev) const
- {
- scope_entry scope (this, prev);
-
- // Nameable is fq-anonymous if all the paths to the global scope
- // have at least one anonymous link.
- //
- if (defined_ != 0 || !named_.empty ())
- {
- if (named ().global_scope ())
- return false;
-
- if (defined_ != 0)
- {
- nameable const& s (defined_->scope ());
-
- if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
- return false;
- }
-
- for (names_list::const_iterator i (named_.begin ()), e (named_.end ());
- i != e; ++i)
- {
- nameable const& s ((*i)->scope ());
-
- if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
- return false;
- }
- }
-
- // If we can get a literal name for this type node, then it is not
- // anonymous as long as its scope is not anonymous.
- //
- tree type (tree_node ());
-
- if (TYPE_P (type))
- {
- tree name (0);
-
- if (tree decl = TYPE_NAME (type))
- {
- name = DECL_NAME (decl);
- if (name != 0 && IDENTIFIER_ANON_P (name))
- return true;
-
- tree s (CP_DECL_CONTEXT (decl));
-
- gcc_tree_code_type tc (TREE_CODE (s));
-
- if (tc == TYPE_DECL)
- s = TREE_TYPE (s);
- else if (tc == NAMESPACE_DECL)
- {
- // "Unwind" any inline namespaces since they are not in
- // semantic grapth.
- //
- while (s != global_namespace)
- {
- tree prev (CP_DECL_CONTEXT (s));
-
-#if BUILDING_GCC_MAJOR >= 8
- if (!is_nested_namespace (prev, s, true))
-#else
- if (!is_associated_namespace (prev, s))
-#endif
- break;
-
- s = prev;
- }
- }
-
- if (nameable* n = dynamic_cast<nameable*> (unit ().find (s)))
- return scope.find (n) || n->fq_anonymous_ (&scope);
- }
- else
- return false; // Assume this is a derived type (e.g., pointer).
- }
-
- return true;
- }
-
- bool nameable::
- fq_anonymous (names* hint) const
- {
- if (hint != 0 || defined_ != 0)
- {
- names& n (hint ? *hint : *defined_);
-
- if (n.global_scope ())
- return false;
-
- return n.scope ().fq_anonymous ();
- }
- else
- return fq_anonymous ();
- }
-
- static string
- qualify_names (string const& n, bool qualify_first)
- {
- // @@ Creating a lexer for each call is a bad idea. Need
- // to cache it somewhere.
- //
- cxx_string_lexer l;
- l.start (n);
-
- string r, t;
- bool punc (false);
- bool scoped (false);
-
- // Names returned by GCC's type_as_string() (on which this function
- // is called) include inline namespaces (e.g., std::__cxx11::string).
- // So, besides fully-qualifying names, this function also needs to get
- // rid of those. The idea is to resolve names as we lex them, skipping
- // inline namespaces and stopping once we reach something other than a
- // namespace.
- //
- tree ns (global_namespace);
- tree id;
-
- for (cpp_ttype tt = l.next (t, &id); tt != CPP_EOF; tt = l.next (t, &id))
- {
- if (punc && tt > CPP_LAST_PUNCTUATOR)
- r += ' ';
-
- punc = false;
- tree new_ns (global_namespace); // By default, revert to global.
-
- switch (static_cast<unsigned> (tt))
- {
- case CPP_LESS:
- {
- r += "< ";
- break;
- }
- case CPP_GREATER:
- {
- r += " >";
- break;
- }
- case CPP_COMMA:
- {
- r += ", ";
- break;
- }
- case CPP_NAME:
- {
- // Check if this is a namespace and, if so, whether it is
- // inline.
- //
- if (ns != 0)
- {
- new_ns = lookup_qualified_name (ns, id, false, false);
-
- if (new_ns == error_mark_node ||
- TREE_CODE (new_ns) != NAMESPACE_DECL)
- new_ns = 0; // Not a namespace, stop resolving.
- else
- {
- // Check if this is an inline namespace and skip it if so.
- //
-#if BUILDING_GCC_MAJOR >= 8
- if (is_nested_namespace (ns, new_ns, true))
-#else
- if (is_associated_namespace (ns, new_ns))
-#endif
- {
- // Skip also the following scope operator. Strictly speaking
- // there could be none (i.e., this is a name of an inline
- // namespace) but we only use this function to print names
- // of anonymous types.
- //
- assert (l.next (t) == CPP_SCOPE);
- continue;
- }
- }
- }
- else
- new_ns = 0; // Keep it disabled until we hit a new name.
-
- // If the name was not preceeded with '::', qualify it.
- //
- if (!scoped)
- {
- if (!qualify_first)
- qualify_first = true;
- else
- r += "::";
- }
-
- r += t;
- punc = true;
- break;
- }
- case CPP_KEYWORD:
- case CPP_NUMBER:
- {
- r += t;
- punc = true;
- break;
- }
- case CPP_SCOPE:
- {
- new_ns = ns; // Don't change the namespace.
- }
- // Fall through.
- default:
- {
- r += t;
- break;
- }
- }
-
- scoped = (tt == CPP_SCOPE);
- ns = new_ns;
- }
-
- return r;
- }
-
- string nameable::
- name_ () const
- {
- tree n (tree_node ());
-
- if (!TYPE_P (n))
- return "<anonymous>";
-
- // @@ Doing this once and caching the result is probably a
- // good idea.
- //
- return qualify_names (
- type_as_string (n, TFF_PLAIN_IDENTIFIER | TFF_UNQUALIFIED_NAME), false);
- }
-
- string nameable::
- fq_name () const
- {
- return fq_name_ (0);
- }
-
- string nameable::
- fq_name_ (scope_entry const* prev) const
- {
- // @@ Doing this once and caching the result is probably a
- // good idea.
- //
- scope_entry scope (this, prev);
-
- if (named_p () && named ().global_scope ())
- return "";
-
- if (defined_ != 0)
- {
- nameable const& s (defined_->scope ());
-
- if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
- return s.fq_name_ (&scope) + "::" + name ();
- }
-
- for (names_list::const_iterator i (named_.begin ()), e (named_.end ());
- i != e; ++i)
- {
- nameable const& s ((*i)->scope ());
-
- if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
- return s.fq_name_ (&scope) + "::" + name ();
- }
-
- tree n (tree_node ());
-
- if (!TYPE_P (n))
- return "<anonymous>";
-
- return qualify_names (type_as_string (n, TFF_PLAIN_IDENTIFIER), true);
- }
-
- string nameable::
- fq_name (names* hint) const
- {
- if (hint != 0 || defined_ != 0)
- {
- names& n (hint ? *hint : *defined_);
-
- if (n.global_scope ())
- return "";
-
- return n.scope ().fq_name () + "::" + n.name ();
- }
- else
- {
- // Since there was no hint, prefer the literal name over the names
- // edges.
- //
- tree n (tree_node ());
-
- if (TYPE_P (n))
- return qualify_names (type_as_string (n, TFF_PLAIN_IDENTIFIER), true);
-
- // Last resort is to call the other version of fq_name which will
- // check the names edges.
- //
- return fq_name ();
- }
- }
-
- // scope
- //
-
- scope::names_iterator_pair scope::
- find (string const& name) const
- {
- names_map::const_iterator i (names_map_.find (name));
-
- if (i == names_map_.end ())
- return names_iterator_pair (names_.end (), names_.end ());
- else
- return names_iterator_pair (i->second.begin (), i->second.end ());
- }
-
- scope::names_iterator scope::
- find (names& e)
- {
- list_iterator_map::iterator i (iterator_map_.find (&e));
- return i != iterator_map_.end () ? i->second : names_.end ();
- }
-
- static bool
- is_base (type_id const& b, compiler::type_info const& d)
- {
- using compiler::type_info;
-
- for (type_info::base_iterator i (d.begin_base ());
- i != d.end_base (); ++i)
- {
- type_info const& ti (i->type_info ());
-
- if (b == ti.type_id () || is_base (b, ti))
- return true;
- }
-
- return false;
- }
-
- names* scope::
- lookup (string const& name,
- type_id const& ti,
- unsigned int flags,
- bool* hidden) const
- {
- names_iterator_pair p (find (name));
- names* r (0);
-
- for (names_const_iterator i (p.first); i != p.second; ++i)
- {
- type_id const& xti (typeid (i->named ()));
-
- // If types are equal, then we found a match. Also check if ti is
- // a base type of xti.
- //
- if (xti == ti || is_base (ti, compiler::lookup (xti)))
- {
- if (r != 0)
- {
- // If both are namespaces, then the one is just an extension
- // of the other.
- //
- if (!(r->named ().is_a<namespace_> () &&
- i->named ().is_a<namespace_> ()))
- throw ambiguous (*r, *i);
- }
- else
- r = &*i;
- }
- }
-
- if (r != 0)
- return r;
-
- // If we found a name but the types didn't match, then bail out
- // unless we want hidden names.
- //
- if (p.first != p.second)
- {
- if (hidden != 0)
- *hidden = true;
-
- if ((flags & include_hidden) == 0)
- return 0;
- }
-
- // Look in the outer scope unless requested not to or if this is
- // the global scope.
- //
- if ((flags & exclude_outer) == 0 && !global_scope ())
- return scope ().lookup (name, ti, flags, hidden);
-
- return 0;
- }
-
- void scope::
- add_edge_left (names& e)
- {
- names_list::iterator i (names_.insert (names_.end (), &e));
- iterator_map_[&e] = i;
- names_map_[e.name ()].push_back (&e);
- }
-
- void scope::
- add_edge_left (names& e, names_iterator after)
- {
- names_list::iterator i;
-
- if (after.base () == names_.end ())
- i = names_.insert (names_.begin (), &e);
- else
- {
- names_list::iterator j (after.base ());
- i = names_.insert (++j, &e);
- }
-
- iterator_map_[&e] = i;
- names_map_[e.name ()].push_back (&e);
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- using compiler::type_info;
-
- // node
- //
- insert (type_info (typeid (node)));
-
- // edge
- //
- insert (type_info (typeid (edge)));
-
- // names
- //
- {
- type_info ti (typeid (names));
- ti.add_base (typeid (edge));
- insert (ti);
- }
-
- // declares
- //
- {
- type_info ti (typeid (declares));
- ti.add_base (typeid (names));
- insert (ti);
- }
-
- // defines
- //
- {
- type_info ti (typeid (defines));
- ti.add_base (typeid (declares));
- insert (ti);
- }
-
- // typedefs
- //
- {
- type_info ti (typeid (typedefs));
- ti.add_base (typeid (declares));
- insert (ti);
- }
-
- // nameable
- //
- {
- type_info ti (typeid (nameable));
- ti.add_base (typeid (node));
- insert (ti);
- }
-
- // scope
- //
- {
- type_info ti (typeid (scope));
- ti.add_base (typeid (nameable));
- insert (ti);
- }
-
- // type
- //
- {
- type_info ti (typeid (type));
- ti.add_base (typeid (nameable));
- insert (ti);
- }
-
- // belongs
- //
- {
- type_info ti (typeid (belongs));
- ti.add_base (typeid (edge));
- insert (ti);
- }
-
- // instance
- //
- {
- type_info ti (typeid (instance));
- ti.add_base (typeid (node));
- insert (ti);
- }
-
- // data_member
- //
- {
- type_info ti (typeid (data_member));
- ti.add_base (typeid (nameable));
- ti.add_base (typeid (instance));
- insert (ti);
- }
-
- // unsupported_type
- //
- {
- type_info ti (typeid (unsupported_type));
- ti.add_base (typeid (type));
- insert (ti);
- }
- }
- } init_;
- }
-}
diff --git a/odb/semantics/elements.hxx b/odb/semantics/elements.hxx
deleted file mode 100644
index 12164ba..0000000
--- a/odb/semantics/elements.hxx
+++ /dev/null
@@ -1,841 +0,0 @@
-// file : odb/semantics/elements.hxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#ifndef ODB_SEMANTICS_ELEMENTS_HXX
-#define ODB_SEMANTICS_ELEMENTS_HXX
-
-#include <map>
-#include <list>
-#include <vector>
-#include <string>
-#include <cstdlib> // std::abort
-#include <cstddef> // std::size_t
-#include <utility> // std::pair
-#include <cassert>
-
-#include <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 <odb/gcc-fwd.hxx>
-#include <odb/location.hxx>
-
-namespace semantics
-{
- using namespace cutl;
-
- using std::size_t;
- using std::string;
-
- using container::graph;
- using container::pointer_iterator;
-
- using compiler::type_id;
- using compiler::context;
-
- //
- //
- using fs::path;
- using fs::invalid_path;
-
- //
- //
- class access
- {
- public:
- enum value { public_, protected_, private_ };
-
- access (value v)
- : value_ (v)
- {
- }
-
- operator value () const
- {
- return value_;
- }
-
- char const* string () const;
-
- private:
- value value_;
- };
-
- //
- //
- class node;
- class edge;
- class unit;
-
- // Support for inserting edges at specified positions.
- //
- template <typename N, typename I>
- struct node_position
- {
- node_position (N& node, I pos)
- : node_ (node), pos_ (pos)
- {
- }
-
- operator N& () const
- {
- return node_;
- }
-
- template <typename E>
- void
- add_edge_left (E& e)
- {
- node_.add_edge_left (e, pos_);
- }
-
- template <typename E>
- void
- add_edge_right (E& e)
- {
- node_.add_edge_right (e, pos_);
- }
-
- private:
- N& node_;
- I pos_;
- };
-
- //
- //
- class edge: public context
- {
- public:
- virtual
- ~edge () {}
-
- public:
- template <typename X>
- X*
- is_a () {return dynamic_cast<X*> (this);}
-
- template <typename X>
- const X*
- is_a () const {return dynamic_cast<const X*> (this);}
- };
-
- //
- //
- class node: public context
- {
- public:
- virtual
- ~node () {}
-
- public:
- tree
- tree_node () const
- {
- return tree_node_;
- }
-
- public:
- typedef ::location location_type;
-
- path const&
- file () const
- {
- return loc_.file;
- }
-
- size_t
- line () const
- {
- return loc_.line;
- }
-
- size_t
- column () const
- {
- return loc_.column;
- }
-
- location_type const&
- location () const
- {
- return loc_;
- }
-
- public:
- template <typename X>
- X*
- is_a () {return dynamic_cast<X*> (this);}
-
- template <typename X>
- const X*
- is_a () const {return dynamic_cast<const X*> (this);}
-
- public:
- node (path const& file, size_t line, size_t column, tree);
-
- // Sink functions that allow extensions in the form of one-way
- // edges.
- //
- void
- add_edge_left (edge&) {}
-
- void
- add_edge_right (edge&) {}
-
- protected:
- // For virtual inheritance. Should never be actually called.
- //
- node ();
-
- protected:
- typedef semantics::unit unit_type;
-
- unit_type const&
- unit () const
- {
- return *unit_;
- }
-
- unit_type&
- unit ()
- {
- return *unit_;
- }
-
- private:
- friend class semantics::unit;
-
- void
- unit (unit_type& u)
- {
- unit_ = &u;
- }
-
- private:
- tree tree_node_;
- unit_type* unit_;
-
- location_type loc_;
- };
-
- //
- //
- class scope;
- class nameable;
-
-
- //
- //
- class names: public edge
- {
- public:
- typedef semantics::scope scope_type;
- typedef semantics::access access_type;
-
- string const&
- name () const
- {
- return name_;
- }
-
- scope_type&
- scope () const
- {
- return *scope_;
- }
-
- // Return true if the entity that this edge names is a global scope.
- // In this case calling scope() is undefined behavior.
- //
- bool
- global_scope () const
- {
- return scope_ == 0;
- }
-
- nameable&
- named () const
- {
- return *named_;
- }
-
- access_type
- access () const
- {
- return access_;
- }
-
- // Names edge in terms of which this edge was defined. Can be NULL.
- //
- public:
- void
- hint (names& hint)
- {
- hint_ = &hint;
- }
-
- names*
- hint () const
- {
- return hint_;
- }
-
- public:
- names (string const& name, access_type access = access_type::public_)
- : name_ (name), access_ (access), hint_ (0)
- {
- }
-
- void
- set_left_node (scope_type& n)
- {
- scope_ = &n;
- }
-
- void
- set_right_node (nameable& n)
- {
- named_ = &n;
- }
-
- protected:
- scope_type* scope_;
- nameable* named_;
- string name_;
- access_type access_;
- names* hint_;
- };
-
- //
- // Declarations and definitions.
- //
-
- class declares: public names
- {
- public:
- declares (string const& name, access_type access = access_type::public_)
- : names (name, access)
- {
- }
- };
-
- class defines: public declares
- {
- public:
- defines (string const& name, access_type access = access_type::public_)
- : declares (name, access)
- {
- }
- };
-
- class type;
- class typedefs: public declares
- {
- public:
- typedef semantics::type type_type;
-
- type_type&
- type () const;
-
- public:
- typedefs (string const& name, access_type access = access_type::public_)
- : declares (name, access)
- {
- }
- };
-
- //
- //
- class nameable: public virtual node
- {
- typedef std::vector<names*> names_list;
-
- public:
- typedef semantics::scope scope_type;
-
- // Return true if this type is unnamed and no literal name, such as
- // template-id or derived type declarator, can be used instead.
- //
- bool
- anonymous () const
- {
- if (defined_ != 0 || !named_.empty ())
- return false;
-
- return anonymous_ ();
- }
-
- // Return true if the node itself or any of the scopes up to the
- // global scope is anonymous. For a named class nested in an unnamed
- // class, anonymous() will return false and fq_anonymous() will
- // return true.
- //
- bool
- fq_anonymous () const
- {
- return fq_anonymous_ (0);
- }
-
- // As above but use the hint to select the first outer scope. If
- // hint is 0, use the defines edge.
- //
- bool
- fq_anonymous (names* hint) const;
-
- // Return the node's unqualifed name. If the node has a name, then
- // return it, preferring the defines edge. Otherwise, return a
- // literal name, e.g., template-id or a derived type declarator.
- // Finally, if the type is anonymous, return <anonymous> string.
- //
- string
- name () const
- {
- if (defined_ != 0)
- return defined_->name ();
-
- if (!named_.empty ())
- return named_[0]->name ();
-
- return name_ ();
- }
-
- // Return the node's fully-qualifed name.
- //
- virtual string
- fq_name () const;
-
- // As above but use the hint to select the first outer scope. If hint
- // is 0, use the defines edge.
- //
- virtual string
- fq_name (names* hint) const;
-
- // Return true if the type is named.
- //
- bool
- named_p () const
- {
- return defined_ != 0 || !named_.empty ();
- }
-
- scope_type&
- scope () const
- {
- return named ().scope ();
- }
-
- names&
- named () const
- {
- return defined_ != 0 ? *defined_ : *named_[0];
- }
-
- bool
- in_scope (scope_type&);
-
- public:
- nameable ()
- : defined_ (0)
- {
- }
-
- void
- add_edge_right (defines& e)
- {
- assert (defined_ == 0);
- defined_ = &e;
- }
-
- void
- add_edge_right (names& e)
- {
- named_.push_back (&e);
- }
-
- using node::add_edge_right;
-
- protected:
- // We need to keep the scope we have seen in the fq_* function
- // family in order to detect names that are inside the node
- // and which would otherwise lead to infinite recursion. Here
- // is the canonical example:
- //
- // template <typename X>
- // class c
- // {
- // typedef c this_type;
- // };
- //
- struct scope_entry
- {
- scope_entry (nameable const* e, scope_entry const* p)
- : entry_ (e), prev_ (p)
- {
- }
-
- bool
- find (nameable const* n) const
- {
- for (scope_entry const* i (this); i != 0; i = i->prev_)
- if (i->entry_ == n)
- return true;
-
- return false;
- }
-
- private:
- nameable const* entry_;
- scope_entry const* prev_;
- };
-
- bool
- anonymous_ () const;
-
- bool
- fq_anonymous_ (scope_entry const*) const;
-
- string
- name_ () const;
-
- string
- fq_name_ (scope_entry const*) const;
-
- protected:
- defines* defined_;
- names_list named_;
- };
-
-
- // Ambiguous name lookup exception.
- //
- struct ambiguous
- {
- ambiguous (names& f, names& s): first (f), second (s) {}
- names& first;
- names& second;
- };
-
- // Unresolved name lookup exception.
- //
- struct unresolved
- {
- unresolved (string const& n, bool tm): name (n), type_mismatch (tm) {}
- string name;
- bool type_mismatch; // True if the name resolved but types didn't match.
- };
-
- //
- //
- class scope: public virtual nameable
- {
- protected:
- typedef std::list<names*> names_list;
- typedef std::map<names*, names_list::iterator> list_iterator_map;
- typedef std::map<string, names_list> names_map;
-
- public:
- typedef pointer_iterator<names_list::iterator> names_iterator;
- typedef pointer_iterator<names_list::const_iterator> names_const_iterator;
-
- typedef
- std::pair<names_const_iterator, names_const_iterator>
- names_iterator_pair;
-
- public:
- bool
- global_scope () const
- {
- return named ().global_scope ();
- }
-
- scope&
- scope_ () const
- {
- return nameable::scope ();
- }
-
- names_iterator
- names_begin ()
- {
- return names_.begin ();
- }
-
- names_iterator
- names_end ()
- {
- return names_.end ();
- }
-
- names_const_iterator
- names_begin () const
- {
- return names_.begin ();
- }
-
- names_const_iterator
- names_end () const
- {
- return names_.end ();
- }
-
- // Find a name in this scope.
- //
- public:
- virtual names_iterator_pair
- find (string const& name) const;
-
- names_iterator
- find (names&);
-
- // Lookup a name of the specified type in this scope and, if not
- // found, in outer scopes.
- //
- public:
- static unsigned int const exclude_outer = 0x01; // Exclude outer scopes.
- static unsigned int const include_hidden = 0x02; // Include hidden names.
-
- virtual names*
- lookup (string const& name,
- type_id const&,
- unsigned int flags = 0,
- bool* hidden = 0) const;
-
- template <typename T>
- T&
- lookup (string const& name, unsigned int flags = 0) const
- {
- bool hidden (false);
-
- if (names* n = lookup (name, typeid (T), flags, &hidden))
- return dynamic_cast<T&> (n->named ());
-
- throw unresolved (name, hidden);
- }
-
- public:
- scope (path const& file, size_t line, size_t column, tree tn)
- : node (file, line, column, tn)
- {
- }
-
- void
- add_edge_left (names&);
-
- void
- add_edge_left (names&, names_iterator after);
-
- using nameable::add_edge_right;
-
- protected:
- scope ()
- {
- }
-
- private:
- names_list names_;
- list_iterator_map iterator_map_;
- names_map names_map_;
- };
-
- //
- //
- class points;
-
- class belongs;
- class qualifies;
-
- class type: public virtual nameable
- {
- typedef std::vector<qualifies*> qualified;
-
- public:
- typedef pointer_iterator<qualified::const_iterator> qualified_iterator;
-
- qualified_iterator
- qualified_begin () const
- {
- return qualified_.begin ();
- }
-
- qualified_iterator
- qualified_end () const
- {
- return qualified_.end ();
- }
-
- public:
- bool
- pointed_p () const {return pointed_ != 0;}
-
- points&
- pointed () const {return *pointed_;}
-
- public:
- type (): pointed_ (0) {}
-
- void
- add_edge_right (belongs&)
- {
- }
-
- void
- add_edge_right (qualifies& e)
- {
- qualified_.push_back (&e);
- }
-
- void
- add_edge_right (points& e)
- {
- pointed_ = &e;
- }
-
- using nameable::add_edge_right;
-
- private:
- qualified qualified_;
- points* pointed_;
- };
-
- //
- //
- class instance;
-
- class belongs: public edge
- {
- public:
- typedef semantics::type type_type;
- typedef semantics::instance instance_type;
-
- type_type&
- type () const
- {
- return *type_;
- }
-
- instance_type&
- instance () const
- {
- return *instance_;
- }
-
- public:
- void
- hint (names& hint)
- {
- hint_ = &hint;
- }
-
- names*
- hint () const
- {
- return hint_;
- }
-
- public:
- belongs ()
- : hint_ (0)
- {
- }
-
- void
- set_left_node (instance_type& n)
- {
- instance_ = &n;
- }
-
- void
- set_right_node (type_type& n)
- {
- type_ = &n;
- }
-
- private:
- type_type* type_;
- instance_type* instance_;
- names* hint_;
- };
-
- //
- //
- class instance: public virtual node
- {
- public:
- typedef semantics::type type_type;
- typedef semantics::belongs belongs_type;
-
- type_type&
- type () const
- {
- return belongs_->type ();
- }
-
- belongs_type&
- belongs () const
- {
- return *belongs_;
- }
-
- public:
- void
- add_edge_left (belongs_type& e)
- {
- belongs_ = &e;
- }
-
- protected:
- instance ()
- {
- }
-
- private:
- belongs_type* belongs_;
- };
-
- // Data member for class and union types.
- //
- class data_member: public nameable, public instance
- {
- public:
- data_member (path const& file, size_t line, size_t column, tree tn)
- : node (file, line, column, tn)
- {
- }
-
- protected:
- data_member ()
- {
- }
- };
-
- // Unsupported type.
- //
- class unsupported_type: public type
- {
- public:
- string const&
- type_name () const
- {
- return type_name_;
- }
-
- public:
- unsupported_type (path const& file,
- size_t line,
- size_t column,
- tree tn,
- string const& type_name)
- : node (file, line, column, tn), type_name_ (type_name)
- {
- }
-
- private:
- string const type_name_;
- };
-}
-
-#include <odb/semantics/elements.ixx>
-
-#endif // ODB_SEMANTICS_ELEMENTS_HXX
diff --git a/odb/semantics/enum.cxx b/odb/semantics/enum.cxx
deleted file mode 100644
index 7fd8204..0000000
--- a/odb/semantics/enum.cxx
+++ /dev/null
@@ -1,85 +0,0 @@
-// file : odb/semantics/enum.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <cutl/compiler/type-info.hxx>
-#include <odb/semantics/enum.hxx>
-
-namespace semantics
-{
- enumerates::
- enumerates ()
- {
- }
-
- enumerator::
- enumerator (path const& file,
- size_t line,
- size_t column,
- tree tn,
- unsigned long long value)
- : node (file, line, column, tn), value_ (value)
- {
- }
-
- underlies::
- underlies ()
- : type_ (0), enum__ (0), hint_ (0)
- {
- }
-
- enum_::
- enum_ (path const& file,
- size_t line,
- size_t column,
- tree tn)
- : node (file, line, column, tn)
- {
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- using compiler::type_info;
-
- // enumerates
- //
- {
- type_info ti (typeid (enumerates));
- ti.add_base (typeid (edge));
- insert (ti);
- }
-
- // enumerator
- //
- {
- type_info ti (typeid (enumerator));
- ti.add_base (typeid (nameable));
- ti.add_base (typeid (instance));
- insert (ti);
- }
-
- // underlies
- //
- {
- type_info ti (typeid (underlies));
- ti.add_base (typeid (edge));
- insert (ti);
- }
-
- // enum_
- //
- {
- type_info ti (typeid (enum_));
- ti.add_base (typeid (type));
- ti.add_base (typeid (scope));
- insert (ti);
- }
- }
- } init_;
- }
-}
diff --git a/odb/semantics/fundamental.cxx b/odb/semantics/fundamental.cxx
deleted file mode 100644
index 82446a1..0000000
--- a/odb/semantics/fundamental.cxx
+++ /dev/null
@@ -1,222 +0,0 @@
-// file : odb/semantics/fundamental.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <odb/gcc.hxx>
-
-#include <cutl/compiler/type-info.hxx>
-#include <odb/semantics/fundamental.hxx>
-
-namespace semantics
-{
- string fund_type::
- fq_name () const
- {
- return name ();
- }
-
- string fund_type::
- fq_name (names* hint) const
- {
- if (hint == 0)
- return name ();
-
- return type::fq_name (hint);
- }
-
- // char
- //
- bool fund_char::
- unsigned_ () const
- {
- return TYPE_UNSIGNED (tree_node ()) != 0;
- }
-
- // wchar_t
- //
- bool fund_wchar::
- unsigned_ () const
- {
- return TYPE_UNSIGNED (tree_node ()) != 0;
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- using compiler::type_info;
-
- // fund_type
- //
- {
- type_info ti (typeid (fund_type));
- ti.add_base (typeid (type));
- insert (ti);
- }
-
- // fund_void
- //
- {
- type_info ti (typeid (fund_void));
- ti.add_base (typeid (fund_type));
- insert (ti);
- }
-
- // integral_type
- //
- {
- type_info ti (typeid (integral_type));
- ti.add_base (typeid (fund_type));
- insert (ti);
- }
-
- // fund_bool
- //
- {
- type_info ti (typeid (fund_bool));
- ti.add_base (typeid (integral_type));
- insert (ti);
- }
-
- // fund_char
- //
- {
- type_info ti (typeid (fund_char));
- ti.add_base (typeid (integral_type));
- insert (ti);
- }
-
- // fund_wchar
- //
- {
- type_info ti (typeid (fund_wchar));
- ti.add_base (typeid (integral_type));
- insert (ti);
- }
-
- // fund_char16
- //
- {
- type_info ti (typeid (fund_char16));
- ti.add_base (typeid (integral_type));
- insert (ti);
- }
-
- // fund_char32
- //
- {
- type_info ti (typeid (fund_char32));
- ti.add_base (typeid (integral_type));
- insert (ti);
- }
-
- // fund_signed_char
- //
- {
- type_info ti (typeid (fund_signed_char));
- ti.add_base (typeid (integral_type));
- insert (ti);
- }
-
- // fund_unsigned_char
- //
- {
- type_info ti (typeid (fund_unsigned_char));
- ti.add_base (typeid (integral_type));
- insert (ti);
- }
-
- // fund_short
- //
- {
- type_info ti (typeid (fund_short));
- ti.add_base (typeid (integral_type));
- insert (ti);
- }
-
- // fund_unsigned_short
- //
- {
- type_info ti (typeid (fund_unsigned_short));
- ti.add_base (typeid (integral_type));
- insert (ti);
- }
-
- // fund_int
- //
- {
- type_info ti (typeid (fund_int));
- ti.add_base (typeid (integral_type));
- insert (ti);
- }
-
- // fund_unsigned_int
- //
- {
- type_info ti (typeid (fund_unsigned_int));
- ti.add_base (typeid (integral_type));
- insert (ti);
- }
-
- // fund_long
- //
- {
- type_info ti (typeid (fund_long));
- ti.add_base (typeid (integral_type));
- insert (ti);
- }
-
- // fund_unsigned_long
- //
- {
- type_info ti (typeid (fund_unsigned_long));
- ti.add_base (typeid (integral_type));
- insert (ti);
- }
-
- // fund_long_long
- //
- {
- type_info ti (typeid (fund_long_long));
- ti.add_base (typeid (integral_type));
- insert (ti);
- }
-
- // fund_unsigned_long_long
- //
- {
- type_info ti (typeid (fund_unsigned_long_long));
- ti.add_base (typeid (integral_type));
- insert (ti);
- }
-
- // fund_float
- //
- {
- type_info ti (typeid (fund_float));
- ti.add_base (typeid (fund_type));
- insert (ti);
- }
-
- // fund_double
- //
- {
- type_info ti (typeid (fund_double));
- ti.add_base (typeid (fund_type));
- insert (ti);
- }
-
- // fund_long_double
- //
- {
- type_info ti (typeid (fund_long_double));
- ti.add_base (typeid (fund_type));
- insert (ti);
- }
- }
- } init_;
- }
-}
diff --git a/odb/semantics/namespace.cxx b/odb/semantics/namespace.cxx
deleted file mode 100644
index 0e1442c..0000000
--- a/odb/semantics/namespace.cxx
+++ /dev/null
@@ -1,107 +0,0 @@
-// file : odb/semantics/namespace.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <cutl/compiler/type-info.hxx>
-#include <odb/semantics/namespace.hxx>
-
-namespace semantics
-{
- namespace_::
- namespace_ (path const& file, size_t line, size_t column, tree tn)
- : node (file, line, column, tn), original_ (0)
- {
- }
-
- namespace_::
- namespace_ ()
- : original_ (0)
- {
- }
-
- names* namespace_::
- lookup (string const& name,
- type_id const& ti,
- unsigned int flags,
- bool* hidden) const
- {
- if (original_ != 0)
- return original_->lookup (name, ti, flags, hidden);
-
- // Being hidden in one namespace doesn't mean it is also hidden in
- // the other. Normally that would be an ambiguous lookup, but we use
- // relaxed rules.
- //
- bool h (false); // Indicates whether this namespace hides the name.
- bool any_h (false); // Indicates whether any namespace hides the name.
-
- names* r (scope::lookup (name, ti, flags | exclude_outer, &h));
- any_h = any_h || h;
-
- if (r != 0 && h && hidden != 0)
- *hidden = true;
-
- for (extensions_iterator i (extensions_begin ());
- i != extensions_end ();
- ++i)
- {
- h = false;
- names* er ((*i)->scope::lookup (name, ti, flags | exclude_outer, &h));
- any_h = any_h || h;
-
- if (er != 0)
- {
- if (r != 0)
- {
- // If both are namespaces, then the one is just an extension
- // of the other.
- //
- if (!(r->named ().is_a<namespace_> () &&
- er->named ().is_a<namespace_> ()))
- throw ambiguous (*r, *er);
- }
- else
- r = er;
-
- if (h && hidden != 0)
- *hidden = true;
- }
- }
-
- if (r != 0)
- return r;
-
- if (any_h)
- {
- if (hidden != 0)
- *hidden = true;
-
- if ((flags & include_hidden) == 0)
- return 0;
- }
-
- // Look in the outer scope unless requested not to or if this is
- // the global scope.
- //
- if ((flags & exclude_outer) == 0 && !global_scope ())
- return scope ().lookup (name, ti, flags, hidden);
-
- return 0;
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- using compiler::type_info;
-
- type_info ti (typeid (namespace_));
- ti.add_base (typeid (scope));
- insert (ti);
- }
- } init_;
- }
-}
diff --git a/odb/semantics/relational/changelog.cxx b/odb/semantics/relational/changelog.cxx
deleted file mode 100644
index 8cee9dd..0000000
--- a/odb/semantics/relational/changelog.cxx
+++ /dev/null
@@ -1,187 +0,0 @@
-// file : odb/semantics/relational/changelog.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <vector>
-#include <sstream>
-
-#include <cutl/compiler/type-info.hxx>
-
-#include <odb/semantics/relational/changelog.hxx>
-#include <odb/semantics/relational/model.hxx>
-#include <odb/semantics/relational/changeset.hxx>
-
-using namespace std;
-
-namespace semantics
-{
- namespace relational
- {
- changelog::
- changelog (xml::parser& p)
- : contains_model_ (0)
- {
- using namespace xml;
-
- p.next_expect (parser::start_element, xmlns, "changelog");
- p.content (content::complex);
-
- if (p.attribute<unsigned int> ("version") != 1)
- throw parsing (p, "unsupported changelog format version");
-
- database_ = p.attribute ("database");
- schema_name_ = p.attribute ("schema-name", "");
-
- // Because things are stored in the reverse order, first save the
- // changesets as XML chunks and then re-parse them in the reverse
- // order. We have to do it this way so that we can do lookups along
- // the alters edges.
- //
- typedef vector<string> changesets;
- changesets cs;
-
- for (parser::event_type e (p.peek ());
- e == parser::start_element;
- e = p.peek ())
- {
- if (p.qname () != xml::qname (xmlns, "changeset"))
- break; // Not our elements.
-
- ostringstream os;
- os.exceptions (ios_base::badbit | ios_base::failbit);
- serializer s (os, "changeset", 0); // No pretty-printing.
- size_t depth (0);
-
- do
- {
- switch (p.next ())
- {
- case parser::start_element:
- {
- s.start_element (p.qname ());
-
- if (depth == 0)
- s.namespace_decl (xmlns, "");
-
- typedef parser::attribute_map_type attr_map;
- attr_map const& am (p.attribute_map ());
-
- for (attr_map::const_iterator i (am.begin ());
- i != am.end (); ++i)
- s.attribute (i->first, i->second.value);
-
- depth++;
- break;
- }
- case parser::end_element:
- {
- depth--;
- s.end_element ();
- break;
- }
- case parser::characters:
- {
- s.characters (p.value ());
- break;
- }
- default:
- {
- depth = 0;
- break;
- }
- }
- } while (depth != 0);
-
- cs.push_back (os.str ());
- }
-
- // Get the model.
- //
- p.next_expect (parser::start_element, xmlns, "model");
- model_type& m (new_node<model_type> (p, *this));
- new_edge<contains_model_type> (*this, m);
- p.next_expect (parser::end_element);
-
- // Re-parse the changesets in reverse order.
- //
- qscope* base (&m);
- for (changesets::reverse_iterator i (cs.rbegin ()); i != cs.rend (); ++i)
- {
- istringstream is (*i);
- is.exceptions (ios_base::badbit | ios_base::failbit);
- parser ip (is, p.input_name ());
-
- ip.next_expect (parser::start_element, xmlns, "changeset");
-
- changeset& c (new_node<changeset> (ip, *base, *this));
- new_edge<contains_changeset> (*this, c);
- base = &c;
-
- ip.next_expect (parser::end_element);
- }
-
- p.next_expect (parser::end_element);
- }
-
- void changelog::
- serialize (xml::serializer& s) const
- {
- s.start_element (xmlns, "changelog");
- s.namespace_decl (xmlns, "");
- s.attribute ("database", database_);
- if (!schema_name_.empty ())
- s.attribute ("schema-name", schema_name_);
- s.attribute ("version", 1); // Format version.
-
- // For better readability serialize things in reverse order so that
- // the most recent changeset appears first.
- //
- for (contains_changeset_list::const_reverse_iterator i (
- contains_changeset_.rbegin ());
- i != contains_changeset_.rend (); ++i)
- {
- (*i)->changeset ().serialize (s);
- s.characters ("\n");
- }
-
- model ().serialize (s);
- s.end_element ();
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- using compiler::type_info;
-
- // contains_model
- //
- {
- type_info ti (typeid (contains_model));
- ti.add_base (typeid (edge));
- insert (ti);
- }
-
- // contains_changeset
- //
- {
- type_info ti (typeid (contains_changeset));
- ti.add_base (typeid (edge));
- insert (ti);
- }
-
- // changelog
- //
- {
- type_info ti (typeid (changelog));
- ti.add_base (typeid (node));
- insert (ti);
- }
- }
- } init_;
- }
- }
-}
diff --git a/odb/semantics/relational/changeset.cxx b/odb/semantics/relational/changeset.cxx
deleted file mode 100644
index e643285..0000000
--- a/odb/semantics/relational/changeset.cxx
+++ /dev/null
@@ -1,56 +0,0 @@
-// file : odb/semantics/relational/changeset.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <cutl/compiler/type-info.hxx>
-
-#include <odb/semantics/relational/changeset.hxx>
-
-namespace semantics
-{
- namespace relational
- {
- changeset::
- changeset (changeset const& c, qscope& b, graph& g)
- : qscope (c, &b, g),
- version_ (c.version_),
- alters_model_ (0)
- {
- }
-
- changeset::
- changeset (xml::parser& p, qscope& b, graph& g)
- : qscope (p, &b, g),
- version_ (p.attribute<version_type> ("version")),
- alters_model_ (0)
- {
- }
-
- void changeset::
- serialize (xml::serializer& s) const
- {
- s.start_element (xmlns, "changeset");
- s.attribute ("version", version_);
- qscope::serialize_content (s);
- s.end_element ();
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- using compiler::type_info;
-
- {
- type_info ti (typeid (changeset));
- ti.add_base (typeid (qscope));
- insert (ti);
- }
- }
- } init_;
- }
- }
-}
diff --git a/odb/semantics/relational/column.cxx b/odb/semantics/relational/column.cxx
deleted file mode 100644
index e62a460..0000000
--- a/odb/semantics/relational/column.cxx
+++ /dev/null
@@ -1,201 +0,0 @@
-// file : odb/semantics/relational/column.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <cutl/compiler/type-info.hxx>
-
-#include <odb/semantics/relational/column.hxx>
-
-namespace semantics
-{
- namespace relational
- {
- // column
- //
- column::
- column (column const& c, uscope&, graph& g)
- : unameable (c, g),
- type_ (c.type_),
- null_ (c.null_),
- default__ (c.default__),
- options_ (c.options_)
- {
- }
-
- column::
- column (xml::parser& p, uscope&, graph& g)
- : unameable (p, g),
- type_ (p.attribute ("type", string ())),
- null_ (p.attribute<bool> ("null")),
- default__ (p.attribute ("default", string ())),
- options_ (p.attribute ("options", string ()))
- {
- p.content (xml::content::empty);
- }
-
- column& column::
- clone (uscope& s, graph& g) const
- {
- return g.new_node<column> (*this, s, g);
- }
-
- void column::
- serialize (xml::serializer& s) const
- {
- s.start_element (xmlns, "column");
- serialize_attributes (s);
- s.end_element ();
- }
-
- void column::
- serialize_attributes (xml::serializer& s) const
- {
- unameable::serialize_attributes (s);
-
- s.attribute ("type", type ());
- s.attribute ("null", null ()); // Output even if false.
-
- if (!default_ ().empty ())
- s.attribute ("default", default_ ());
-
- if (!options ().empty ())
- s.attribute ("options", options ());
- }
-
- // add_column
- //
- add_column& add_column::
- clone (uscope& s, graph& g) const
- {
- return g.new_node<add_column> (*this, s, g);
- }
-
- void add_column::
- serialize (xml::serializer& s) const
- {
- s.start_element (xmlns, "add-column");
- column::serialize_attributes (s);
- s.end_element ();
- }
-
- // drop_column
- //
- drop_column::
- drop_column (xml::parser& p, uscope&, graph& g)
- : unameable (p, g)
- {
- p.content (xml::content::empty);
- }
-
- drop_column& drop_column::
- clone (uscope& s, graph& g) const
- {
- return g.new_node<drop_column> (*this, s, g);
- }
-
- void drop_column::
- serialize (xml::serializer& s) const
- {
- s.start_element (xmlns, "drop-column");
- unameable::serialize_attributes (s);
- s.end_element ();
- }
-
- // alter_column
- //
- alter_column::
- alter_column (alter_column const& ac, uscope& s, graph& g)
- : column (ac, s, g),
- alters_ (0),
- null_altered_ (ac.null_altered_)
- {
- column* b (s.lookup<column, drop_column> (ac.name ()));
- assert (b != 0);
- g.new_edge<alters> (*this, *b);
- }
-
- alter_column::
- alter_column (xml::parser& p, uscope& s, graph& g)
- : column (p, s, g),
- alters_ (0),
- null_altered_ (p.attribute_present ("null"))
- {
- name_type n (p.attribute<name_type> ("name"));
- column* b (s.lookup<column, drop_column> (n));
- assert (b != 0);
- g.new_edge<alters> (*this, *b);
- }
-
- alter_column& alter_column::
- clone (uscope& s, graph& g) const
- {
- return g.new_node<alter_column> (*this, s, g);
- }
-
- void alter_column::
- serialize (xml::serializer& s) const
- {
- s.start_element (xmlns, "alter-column");
-
- // Here we override the standard column logic.
- //
- unameable::serialize_attributes (s);
-
- if (null_altered_)
- s.attribute ("null", null_);
-
- s.end_element ();
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- unameable::parser_map& m (unameable::parser_map_);
-
- m["column"] = &unameable::parser_impl<column>;
- m["add-column"] = &unameable::parser_impl<add_column>;
- m["drop-column"] = &unameable::parser_impl<drop_column>;
- m["alter-column"] = &unameable::parser_impl<alter_column>;
-
- using compiler::type_info;
-
- // column
- //
- {
- type_info ti (typeid (column));
- ti.add_base (typeid (unameable));
- insert (ti);
- }
-
- // add_column
- //
- {
- type_info ti (typeid (add_column));
- ti.add_base (typeid (column));
- insert (ti);
- }
-
- // drop_column
- //
- {
- type_info ti (typeid (drop_column));
- ti.add_base (typeid (unameable));
- insert (ti);
- }
-
- // alter_column
- //
- {
- type_info ti (typeid (alter_column));
- ti.add_base (typeid (column));
- insert (ti);
- }
- }
- } init_;
- }
- }
-}
diff --git a/odb/semantics/relational/elements.cxx b/odb/semantics/relational/elements.cxx
deleted file mode 100644
index 192c882..0000000
--- a/odb/semantics/relational/elements.cxx
+++ /dev/null
@@ -1,179 +0,0 @@
-// file : odb/semantics/relational/elements.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <cutl/compiler/type-info.hxx>
-
-#include <odb/semantics/relational/elements.hxx>
-#include <odb/semantics/relational/column.hxx>
-#include <odb/semantics/relational/primary-key.hxx>
-
-namespace semantics
-{
- namespace relational
- {
- string const xmlns = "http://www.codesynthesis.com/xmlns/odb/changelog";
-
- // duplicate_name
- //
- template <>
- duplicate_name::
- duplicate_name (uscope& s, unameable& o, unameable& d)
- : scope (s), orig (o), dup (d), name (o.name ())
- {
- }
-
- template <>
- duplicate_name::
- duplicate_name (qscope& s, qnameable& o, qnameable& d)
- : scope (s), orig (o), dup (d), name (o.name ().string ())
- {
- }
-
- // scope<uname>
- //
- template <>
- void scope<uname>::
- add_edge_left (names_type& e)
- {
- nameable_type& n (e.nameable ());
- name_type const& name (e.name ());
-
- typename names_map::iterator i (names_map_.find (name));
-
- if (i == names_map_.end ())
- {
- typename names_list::iterator i;
-
- // We want the order to be add/alter columns first, then the
- // primary key, then other keys, and finnally drop columns.
- //
- if (n.is_a<column> () ||
- n.is_a<add_column> () ||
- n.is_a<alter_column> ())
- {
- i = names_.insert (first_key_, &e);
- }
- else if (!n.is_a<drop_column> ())
- {
- if (n.is_a<primary_key> ())
- first_key_ = i = names_.insert (
- first_key_ != names_.end () ? first_key_ : first_drop_column_,
- &e);
- else
- {
- i = names_.insert (first_drop_column_, &e);
-
- if (first_key_ == names_.end ())
- first_key_ = i;
- }
- }
- else
- {
- i = names_.insert (names_.end (), &e);
-
- if (first_drop_column_ == names_.end ())
- first_drop_column_ = i;
- }
-
- names_map_[name] = i;
- iterator_map_[&e] = i;
- }
- else
- throw duplicate_name (*this, (*i->second)->nameable (), n);
- }
-
- template <>
- void scope<uname>::
- remove_edge_left (names_type& e)
- {
- typename names_iterator_map::iterator i (iterator_map_.find (&e));
- assert (i != iterator_map_.end ());
-
- // If we are removing the first key, then move to the next key (or
- // the end which means there are no keys).
- //
- if (first_key_ == i->second)
- first_key_++;
-
- // The same for the first drop column.
- //
- if (first_drop_column_ == i->second)
- first_drop_column_++;
-
- names_.erase (i->second);
- names_map_.erase (e.name ());
- iterator_map_.erase (i);
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- using compiler::type_info;
-
- // node
- //
- insert (type_info (typeid (node)));
-
- // edge
- //
- insert (type_info (typeid (edge)));
-
- // alters
- //
- {
- type_info ti (typeid (alters));
- ti.add_base (typeid (edge));
- insert (ti);
- }
-
- // names
- //
- {
- type_info ti (typeid (unames));
- ti.add_base (typeid (edge));
- insert (ti);
- }
-
- {
- type_info ti (typeid (qnames));
- ti.add_base (typeid (edge));
- insert (ti);
- }
-
- // nameable
- //
- {
- type_info ti (typeid (unameable));
- ti.add_base (typeid (node));
- insert (ti);
- }
-
- {
- type_info ti (typeid (qnameable));
- ti.add_base (typeid (node));
- insert (ti);
- }
-
- // scope
- //
- {
- type_info ti (typeid (uscope));
- ti.add_base (typeid (node));
- insert (ti);
- }
-
- {
- type_info ti (typeid (qscope));
- ti.add_base (typeid (node));
- insert (ti);
- }
- }
- } init_;
- }
- }
-}
diff --git a/odb/semantics/relational/elements.hxx b/odb/semantics/relational/elements.hxx
deleted file mode 100644
index 61cd4f3..0000000
--- a/odb/semantics/relational/elements.hxx
+++ /dev/null
@@ -1,468 +0,0 @@
-// file : odb/semantics/relational/elements.hxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#ifndef ODB_SEMANTICS_RELATIONAL_ELEMENTS_HXX
-#define ODB_SEMANTICS_RELATIONAL_ELEMENTS_HXX
-
-#include <map>
-#include <list>
-#include <vector>
-#include <string>
-#include <cassert>
-
-#include <cutl/container/graph.hxx>
-#include <cutl/container/pointer-iterator.hxx>
-#include <cutl/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>
-
-namespace semantics
-{
- namespace relational
- {
- using namespace cutl;
-
- using std::string;
-
- using container::pointer_iterator;
- using compiler::context;
-
- typedef unsigned long long version;
-
- //
- //
- extern string const xmlns;
-
- //
- //
- class node;
- class edge;
-
- typedef container::graph<node, edge> graph;
-
- //
- //
- class edge: public context
- {
- public:
- template <typename X>
- bool
- is_a () const
- {
- return dynamic_cast<X const*> (this) != 0;
- }
-
- public:
- virtual
- ~edge () {}
- };
-
- //
- //
- class node: public context
- {
- // Return name of the node.
- //
- public:
- virtual string
- kind () const = 0;
-
- public:
- template <typename X>
- bool
- is_a () const
- {
- return dynamic_cast<X const*> (this) != 0;
- }
-
- public:
- virtual
- ~node () {}
-
- // XML serialization.
- //
- virtual void
- serialize (xml::serializer&) const = 0;
-
- // Sink functions that allow extensions in the form of one-way
- // edges.
- //
- void
- add_edge_right (edge&) {}
-
- void
- remove_edge_right (edge&) {}
- };
-
- //
- //
- class alters: public edge
- {
- public:
- node&
- base () const {return *base_;}
-
- node&
- modifier () const {return *modifier_;}
-
- public:
- alters () : base_ (0), modifier_ (0) {}
-
- void
- set_left_node (node& m)
- {
- assert (modifier_ == 0);
- modifier_ = &m;
- }
-
- void
- set_right_node (node& b)
- {
- assert (base_ == 0);
- base_ = &b;
- }
-
- void
- clear_left_node (node& m)
- {
- assert (modifier_ == &m);
- modifier_ = 0;
- }
-
- void
- clear_right_node (node& b)
- {
- assert (base_ == &b);
- base_ = 0;
- }
-
- protected:
- node* base_;
- node* modifier_;
- };
-
- //
- //
- template <typename N>
- class scope;
-
- template <typename N>
- class nameable;
-
- //
- //
- template <typename N>
- class names: public edge
- {
- public:
- typedef N name_type;
- typedef relational::scope<N> scope_type;
- typedef relational::nameable<N> nameable_type;
-
- name_type const&
- name () const
- {
- return name_;
- }
-
- scope_type&
- scope () const
- {
- return *scope_;
- }
-
- nameable_type&
- nameable () const
- {
- return *nameable_;
- }
-
- public:
- names (name_type const& name): name_ (name) {}
-
- void
- set_left_node (scope_type& n)
- {
- scope_ = &n;
- }
-
- void
- set_right_node (nameable_type& n)
- {
- nameable_ = &n;
- }
-
- void
- clear_left_node (scope_type& n)
- {
- assert (scope_ == &n);
- scope_ = 0;
- }
-
- void
- clear_right_node (nameable_type& n)
- {
- assert (nameable_ == &n);
- nameable_ = 0;
- }
-
- protected:
- name_type name_;
- scope_type* scope_;
- nameable_type* nameable_;
- };
-
- typedef names<uname> unames;
- typedef names<qname> qnames;
-
- //
- //
- template <typename N>
- class nameable: public virtual node
- {
- public:
- typedef N name_type;
- typedef relational::names<N> names_type;
- typedef relational::scope<N> scope_type;
-
- name_type const&
- name () const {return named_->name ();}
-
- scope_type&
- scope () const {return named ().scope ();}
-
- names_type&
- named () const {return *named_;}
-
- string const&
- id () const {return id_;}
-
- public:
- // Id identifies the C++ node (e.g., a class or a data member) that
- // this model node corresponds to. The ids are not necessarily unique
- // (e.g., there can be a foreign key and an index with the same id that
- // correspond to a container member). However, in any given scope, the
- // {id,typeid} must be unique. This becomes important when we try to
- // find correspondance between nodes during model diff'ing.
- //
- nameable (string const& id): id_ (id), named_ (0) {}
-
- virtual nameable&
- clone (scope_type&, graph&) const = 0;
-
- // Virtual because we call it via nameable interface (e.g., in copy).
- //
- virtual void
- add_edge_right (names_type& e)
- {
- assert (named_ == 0);
- named_ = &e;
- }
-
- virtual void
- remove_edge_right (names_type& e)
- {
- assert (named_ == &e);
- named_ = 0;
- }
-
- using node::add_edge_right;
- using node::remove_edge_right;
-
- protected:
- nameable (nameable const&, graph& g);
- nameable (xml::parser&, graph& g);
-
- void
- serialize_attributes (xml::serializer&) const;
-
- public:
- typedef void (*parser_func) (xml::parser&, scope_type&, graph&);
- typedef std::map<std::string, parser_func> parser_map;
- static parser_map parser_map_;
-
- template <typename T>
- static void
- parser_impl (xml::parser&, scope_type&, graph&);
-
- private:
- string id_;
- names_type* named_;
- };
-
- typedef nameable<uname> unameable;
- typedef nameable<qname> qnameable;
-
-
- //
- //
- struct duplicate_name
- {
- template <typename N>
- duplicate_name (relational::scope<N>&,
- relational::nameable<N>& orig,
- relational::nameable<N>& dup);
-
- node& scope;
- node& orig;
- node& dup;
-
- string name;
- };
-
- template <typename N>
- class scope: public virtual node
- {
- protected:
- typedef N name_type;
- typedef relational::names<N> names_type;
- typedef relational::nameable<N> nameable_type;
-
- typedef std::list<names_type*> names_list;
- typedef std::map<name_type, typename names_list::iterator> names_map;
- typedef
- std::map<names_type const*, typename names_list::iterator>
- names_iterator_map;
-
- public:
- typedef pointer_iterator<typename names_list::iterator> names_iterator;
- typedef
- pointer_iterator<typename names_list::const_iterator>
- names_const_iterator;
-
- public:
- // Iteration.
- //
- names_iterator
- names_begin ()
- {
- return names_.begin ();
- }
-
- names_iterator
- names_end ()
- {
- return names_.end ();
- }
-
- names_const_iterator
- names_begin () const
- {
- return names_.begin ();
- }
-
- names_const_iterator
- names_end () const
- {
- return names_.end ();
- }
-
- bool
- names_empty () const
- {
- return names_.empty ();
- }
-
- // Find (this scope only).
- //
- template <typename T>
- T*
- find (name_type const&);
-
- names_iterator
- find (name_type const&);
-
- names_const_iterator
- find (name_type const&) const;
-
- names_iterator
- find (names_type const&);
-
- names_const_iterator
- find (names_type const&) const;
-
- // Lookup in this and all altered scopes until we find what we are
- // looking for or hit a stop node of type S (e.g., drop_*).
- //
- template <typename T, typename S>
- T*
- lookup (name_type const&);
-
- public:
- scope*
- base () const
- {
- return alters_ != 0 ? &dynamic_cast<scope&> (alters_->base ()) : 0;
- }
-
- public:
- scope ()
- : first_key_ (names_.end ()),
- first_drop_column_ (names_.end ()),
- alters_ (0) {}
-
- // Virtual because we call it via scope interface (e.g., in copy).
- //
- virtual void
- add_edge_left (alters& a)
- {
- assert (alters_ == 0);
- alters_ = &a;
- }
-
- virtual void
- remove_edge_left (alters& a)
- {
- assert (alters_ == &a);
- alters_ = 0;
- }
-
- virtual void
- add_edge_left (names_type&);
-
- virtual void
- remove_edge_left (names_type&);
-
- protected:
- scope (scope const&, scope* base, graph&);
- scope (xml::parser&, scope* base, graph&);
-
- void
- serialize_content (xml::serializer&) const;
-
- protected:
- names_list names_;
- names_map names_map_;
- names_iterator_map iterator_map_;
-
- typename names_list::iterator first_key_;
- typename names_list::iterator first_drop_column_;
-
- alters* alters_;
- };
-
- template <>
- void scope<uname>::
- add_edge_left (names_type&);
-
- template <>
- void scope<uname>::
- remove_edge_left (names_type&);
-
- typedef scope<uname> uscope;
- typedef scope<qname> qscope;
- }
-}
-
-#include <odb/semantics/relational/elements.txx>
-
-#endif // ODB_SEMANTICS_RELATIONAL_ELEMENTS_HXX
diff --git a/odb/semantics/relational/foreign-key.cxx b/odb/semantics/relational/foreign-key.cxx
deleted file mode 100644
index b4c85f9..0000000
--- a/odb/semantics/relational/foreign-key.cxx
+++ /dev/null
@@ -1,218 +0,0 @@
-// file : odb/semantics/relational/foreign-key.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <ostream>
-#include <istream>
-
-#include <cutl/compiler/type-info.hxx>
-
-#include <odb/semantics/relational/foreign-key.hxx>
-
-using namespace std;
-
-namespace semantics
-{
- namespace relational
- {
- static const char* action_str[] = {"NO ACTION", "CASCADE", "SET NULL"};
-
- ostream&
- operator<< (ostream& os, foreign_key::action_type v)
- {
- return os << action_str[v];
- }
-
- istream&
- operator>> (istream& is, foreign_key::action_type& v)
- {
- string s;
- getline (is, s);
-
- if (!is.eof ())
- is.setstate (istream::failbit);
-
- if (!is.fail ())
- {
- if (s == "NO ACTION")
- v = foreign_key::no_action;
- else if (s == "CASCADE")
- v = foreign_key::cascade;
- else if (s == "SET NULL")
- v = foreign_key::set_null;
- else
- is.setstate (istream::failbit);
- }
-
- return is;
- }
-
- foreign_key::
- foreign_key (foreign_key const& k, uscope& s, graph& g)
- : key (k, s, g),
- referenced_table_ (k.referenced_table_),
- referenced_columns_ (k.referenced_columns_),
- deferrable_ (k.deferrable_),
- on_delete_ (k.on_delete_)
- {
- }
-
- foreign_key::
- foreign_key (xml::parser& p, uscope& s, graph& g)
- : key (p, s, g),
- deferrable_ (p.attribute ("deferrable", deferrable_type ())),
- on_delete_ (p.attribute ("on-delete", no_action))
- {
- using namespace xml;
-
- p.next_expect (parser::start_element, xmlns, "references");
- referenced_table_ = p.attribute<qname> ("table");
- p.content (content::complex);
-
- for (parser::event_type e (p.peek ());
- e == parser::start_element;
- e = p.peek ())
- {
- if (p.qname () != xml::qname (xmlns, "column"))
- break; // Not our elements.
-
- p.next ();
- referenced_columns_.push_back (p.attribute<uname> ("name"));
- p.content (content::empty);
- p.next_expect (parser::end_element);
- }
-
- p.next_expect (parser::end_element);
- }
-
- foreign_key& foreign_key::
- clone (uscope& s, graph& g) const
- {
- return g.new_node<foreign_key> (*this, s, g);
- }
-
- void foreign_key::
- serialize_attributes (xml::serializer& s) const
- {
- key::serialize_attributes (s);
-
- if (deferrable () != deferrable_type::not_deferrable)
- s.attribute ("deferrable", deferrable ());
-
- if (on_delete () != no_action)
- s.attribute ("on-delete", on_delete ());
- }
-
- void foreign_key::
- serialize_content (xml::serializer& s) const
- {
- key::serialize_content (s);
-
- // Referenced columns.
- //
- s.start_element (xmlns, "references");
- s.attribute ("table", referenced_table ());
-
- for (columns::const_iterator i (referenced_columns_.begin ());
- i != referenced_columns_.end (); ++i)
- {
- s.start_element (xmlns, "column");
- s.attribute ("name", *i);
- s.end_element ();
- }
-
- s.end_element (); // references
- }
-
- void foreign_key::
- serialize (xml::serializer& s) const
- {
- s.start_element (xmlns, "foreign-key");
- serialize_attributes (s);
- serialize_content (s);
- s.end_element (); // foreign-key
- }
-
- // add_foreign_key
- //
- add_foreign_key& add_foreign_key::
- clone (uscope& s, graph& g) const
- {
- return g.new_node<add_foreign_key> (*this, s, g);
- }
-
- void add_foreign_key::
- serialize (xml::serializer& s) const
- {
- s.start_element (xmlns, "add-foreign-key");
- foreign_key::serialize_attributes (s);
- foreign_key::serialize_content (s);
- s.end_element ();
- }
-
- // drop_foreign_key
- //
- drop_foreign_key::
- drop_foreign_key (xml::parser& p, uscope&, graph& g)
- : unameable (p, g)
- {
- p.content (xml::content::empty);
- }
-
- drop_foreign_key& drop_foreign_key::
- clone (uscope& s, graph& g) const
- {
- return g.new_node<drop_foreign_key> (*this, s, g);
- }
-
- void drop_foreign_key::
- serialize (xml::serializer& s) const
- {
- s.start_element (xmlns, "drop-foreign-key");
- unameable::serialize_attributes (s);
- s.end_element ();
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- unameable::parser_map& m (unameable::parser_map_);
-
- m["foreign-key"] = &unameable::parser_impl<foreign_key>;
- m["add-foreign-key"] = &unameable::parser_impl<add_foreign_key>;
- m["drop-foreign-key"] = &unameable::parser_impl<drop_foreign_key>;
-
- using compiler::type_info;
-
- // foreign_key
- //
- {
- type_info ti (typeid (foreign_key));
- ti.add_base (typeid (key));
- insert (ti);
- }
-
- // add_foreign_key
- //
- {
- type_info ti (typeid (add_foreign_key));
- ti.add_base (typeid (foreign_key));
- insert (ti);
- }
-
- // drop_foreign_key
- //
- {
- type_info ti (typeid (drop_foreign_key));
- ti.add_base (typeid (unameable));
- insert (ti);
- }
- }
- } init_;
- }
- }
-}
diff --git a/odb/semantics/relational/index.cxx b/odb/semantics/relational/index.cxx
deleted file mode 100644
index 7e6bb94..0000000
--- a/odb/semantics/relational/index.cxx
+++ /dev/null
@@ -1,145 +0,0 @@
-// file : odb/semantics/relational/index.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <cutl/compiler/type-info.hxx>
-
-#include <odb/semantics/relational/index.hxx>
-
-namespace semantics
-{
- namespace relational
- {
- // index
- //
- index::
- index (index const& i, uscope& s, graph& g)
- : key (i, s, g),
- type_ (i.type_),
- method_ (i.method_),
- options_ (i.options_)
- {
- }
-
- index::
- index (xml::parser& p, uscope& s, graph& g)
- : key (p, s, g),
- type_ (p.attribute ("type", string ())),
- method_ (p.attribute ("method", string ())),
- options_ (p.attribute ("options", string ()))
- {
- }
-
- index& index::
- clone (uscope& s, graph& g) const
- {
- return g.new_node<index> (*this, s, g);
- }
-
- void index::
- serialize_attributes (xml::serializer& s) const
- {
- key::serialize_attributes (s);
-
- if (!type ().empty ())
- s.attribute ("type", type ());
-
- if (!method ().empty ())
- s.attribute ("method", method ());
-
- if (!options ().empty ())
- s.attribute ("options", options ());
- }
-
- void index::
- serialize (xml::serializer& s) const
- {
- s.start_element (xmlns, "index");
- serialize_attributes (s);
- key::serialize_content (s);
- s.end_element ();
- }
-
- // add_index
- //
- add_index& add_index::
- clone (uscope& s, graph& g) const
- {
- return g.new_node<add_index> (*this, s, g);
- }
-
- void add_index::
- serialize (xml::serializer& s) const
- {
- s.start_element (xmlns, "add-index");
- index::serialize_attributes (s);
- index::serialize_content (s);
- s.end_element ();
- }
-
- // drop_index
- //
- drop_index::
- drop_index (xml::parser& p, uscope&, graph& g)
- : unameable (p, g)
- {
- p.content (xml::content::empty);
- }
-
- drop_index& drop_index::
- clone (uscope& s, graph& g) const
- {
- return g.new_node<drop_index> (*this, s, g);
- }
-
- void drop_index::
- serialize (xml::serializer& s) const
- {
- s.start_element (xmlns, "drop-index");
- unameable::serialize_attributes (s);
- s.end_element ();
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- unameable::parser_map& m (unameable::parser_map_);
-
- m["index"] = &unameable::parser_impl<index>;
- m["add-index"] = &unameable::parser_impl<add_index>;
- m["drop-index"] = &unameable::parser_impl<drop_index>;
-
- using compiler::type_info;
-
- // index
- //
- {
- type_info ti (typeid (index));
- ti.add_base (typeid (key));
- insert (ti);
- }
-
- // add_index
- //
- {
- type_info ti (typeid (add_index));
- ti.add_base (typeid (index));
- insert (ti);
- }
-
- // drop_index
- //
- {
- type_info ti (typeid (drop_index));
- ti.add_base (typeid (unameable));
- insert (ti);
- }
- }
- } init_;
- }
- }
-}
diff --git a/odb/semantics/relational/key.cxx b/odb/semantics/relational/key.cxx
deleted file mode 100644
index 318fe96..0000000
--- a/odb/semantics/relational/key.cxx
+++ /dev/null
@@ -1,97 +0,0 @@
-// file : odb/semantics/relational/key.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <cutl/compiler/type-info.hxx>
-
-#include <odb/semantics/relational/key.hxx>
-#include <odb/semantics/relational/column.hxx>
-
-namespace semantics
-{
- namespace relational
- {
- key::
- key (key const& k, uscope& s, graph& g)
- : unameable (k, g)
- {
- for (contains_iterator i (k.contains_begin ());
- i != k.contains_end (); ++i)
- {
- column* c (s.lookup<column, drop_column> (i->column ().name ()));
- assert (c != 0);
- g.new_edge<contains> (*this, *c, i->options ());
- }
- }
-
- key::
- key (xml::parser& p, uscope& s, graph& g)
- : unameable (p, g)
- {
- using namespace xml;
- p.content (content::complex);
-
- for (parser::event_type e (p.peek ());
- e == parser::start_element;
- e = p.peek ())
- {
- if (p.qname () != xml::qname (xmlns, "column"))
- break; // Not our elements.
-
- p.next ();
- p.content (content::empty);
-
- uname n (p.attribute<uname> ("name"));
- column* c (s.lookup<column, drop_column> (n));
- if (c == 0)
- throw parsing (p, "invalid column name in the 'name' attribute");
-
- string o (p.attribute ("options", string ()));
- g.new_edge<contains> (*this, *c, o);
-
- p.next_expect (parser::end_element);
- }
- }
-
- void key::
- serialize_content (xml::serializer& s) const
- {
- for (contains_iterator i (contains_begin ()); i != contains_end (); ++i)
- {
- s.start_element (xmlns, "column");
- s.attribute ("name", i->column ().name ());
- if (!i->options ().empty ())
- s.attribute ("options", i->options ());
- s.end_element ();
- }
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- using compiler::type_info;
-
- // contains
- //
- {
- type_info ti (typeid (contains));
- ti.add_base (typeid (edge));
- insert (ti);
- }
-
- // key
- //
- {
- type_info ti (typeid (key));
- ti.add_base (typeid (node));
- insert (ti);
- }
- }
- } init_;
- }
- }
-}
diff --git a/odb/semantics/relational/model.cxx b/odb/semantics/relational/model.cxx
deleted file mode 100644
index b300274..0000000
--- a/odb/semantics/relational/model.cxx
+++ /dev/null
@@ -1,54 +0,0 @@
-// file : odb/semantics/relational/model.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <cutl/compiler/type-info.hxx>
-
-#include <odb/semantics/relational/model.hxx>
-
-namespace semantics
-{
- namespace relational
- {
- model::
- model (model const& m, graph& g)
- : qscope (m, 0, g),
- version_ (m.version_)
- {
- }
-
- model::
- model (xml::parser& p, graph& g)
- : qscope (p, 0, g),
- version_ (p.attribute<version_type> ("version"))
- {
- }
-
- void model::
- serialize (xml::serializer& s) const
- {
- s.start_element (xmlns, "model");
- s.attribute ("version", version_);
- qscope::serialize_content (s);
- s.end_element ();
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- using compiler::type_info;
-
- {
- type_info ti (typeid (model));
- ti.add_base (typeid (qscope));
- insert (ti);
- }
- }
- } init_;
- }
- }
-}
diff --git a/odb/semantics/relational/primary-key.cxx b/odb/semantics/relational/primary-key.cxx
deleted file mode 100644
index 043374f..0000000
--- a/odb/semantics/relational/primary-key.cxx
+++ /dev/null
@@ -1,80 +0,0 @@
-// file : odb/semantics/relational/primary-key.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <cutl/compiler/type-info.hxx>
-
-#include <odb/semantics/relational/primary-key.hxx>
-
-namespace semantics
-{
- namespace relational
- {
- primary_key::
- primary_key (primary_key const& k, uscope& s, graph& g)
- : key (k, s, g), auto__ (k.auto__), extra_map_ (k.extra_map_)
- {
- }
-
- primary_key::
- primary_key (xml::parser& p, uscope& s, graph& g)
- : key (p, s, g),
- auto__ (p.attribute ("auto", false))
- {
- // All unhandled attributes go into the extra map.
- //
- typedef xml::parser::attribute_map_type attr_map;
- attr_map const& am (p.attribute_map ());
-
- for (attr_map::const_iterator i (am.begin ()); i != am.end (); ++i)
- {
- if (!i->second.handled)
- extra_map_[i->first.name ()] = i->second.value;
- }
- }
-
- primary_key& primary_key::
- clone (uscope& s, graph& g) const
- {
- return g.new_node<primary_key> (*this, s, g);
- }
-
- void primary_key::
- serialize (xml::serializer& s) const
- {
- s.start_element (xmlns, "primary-key");
- key::serialize_attributes (s);
-
- if (auto_ ())
- s.attribute ("auto", true);
-
- for (extra_map::const_iterator i (extra_map_.begin ());
- i != extra_map_.end (); ++i)
- s.attribute (i->first, i->second);
-
- key::serialize_content (s);
- s.end_element ();
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- unameable::parser_map_["primary-key"] =
- &unameable::parser_impl<primary_key>;
-
- using compiler::type_info;
-
- {
- type_info ti (typeid (primary_key));
- ti.add_base (typeid (key));
- insert (ti);
- }
- }
- } init_;
- }
- }
-}
diff --git a/odb/semantics/relational/table.cxx b/odb/semantics/relational/table.cxx
deleted file mode 100644
index b9700b1..0000000
--- a/odb/semantics/relational/table.cxx
+++ /dev/null
@@ -1,183 +0,0 @@
-// file : odb/semantics/relational/table.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <cutl/compiler/type-info.hxx>
-
-#include <odb/semantics/relational/table.hxx>
-
-namespace semantics
-{
- namespace relational
- {
- // table
- //
- table::
- table (table const& t, qscope& s, graph& g, bool b)
- : qnameable (t, g),
- uscope (t, (b ? s.lookup<table, drop_table> (t.name ()) : 0), g),
- options_ (t.options_),
- extra_map_ (t.extra_map_)
- {
- }
-
- table::
- table (xml::parser& p, qscope& s, graph& g, bool b)
- : qnameable (p, g),
- uscope (
- p,
- (b ? s.lookup<table, drop_table> (
- p.attribute<qnameable::name_type> ("name")) : 0),
- g),
- options_ (p.attribute ("options", string ()))
- {
- // All unhandled attributes go into the extra map.
- //
- typedef xml::parser::attribute_map_type attr_map;
- attr_map const& am (p.attribute_map ());
-
- for (attr_map::const_iterator i (am.begin ()); i != am.end (); ++i)
- {
- if (!i->second.handled)
- extra_map_[i->first.name ()] = i->second.value;
- }
- }
-
- table& table::
- clone (qscope& s, graph& g) const
- {
- return g.new_node<table> (*this, s, g);
- }
-
- void table::
- serialize_attributes (xml::serializer& s) const
- {
- qnameable::serialize_attributes (s);
-
- if (!options_.empty ())
- s.attribute ("options", options_);
-
- for (extra_map::const_iterator i (extra_map_.begin ());
- i != extra_map_.end (); ++i)
- s.attribute (i->first, i->second);
- }
-
- void table::
- serialize (xml::serializer& s) const
- {
- s.start_element (xmlns, "table");
- serialize_attributes (s);
- uscope::serialize_content (s);
- s.end_element ();
- }
-
- // add_table
- //
- add_table& add_table::
- clone (qscope& s, graph& g) const
- {
- return g.new_node<add_table> (*this, s, g);
- }
-
- void add_table::
- serialize (xml::serializer& s) const
- {
- s.start_element (xmlns, "add-table");
- table::serialize_attributes (s);
- table::serialize_content (s);
- s.end_element ();
- }
-
- // drop_table
- //
- drop_table::
- drop_table (xml::parser& p, qscope&, graph& g)
- : qnameable (p, g)
- {
- p.content (xml::content::empty);
- }
-
- drop_table& drop_table::
- clone (qscope& s, graph& g) const
- {
- return g.new_node<drop_table> (*this, s, g);
- }
-
- void drop_table::
- serialize (xml::serializer& s) const
- {
- s.start_element (xmlns, "drop-table");
- qnameable::serialize_attributes (s);
- s.end_element ();
- }
-
- // alter_table
- //
- alter_table& alter_table::
- clone (qscope& s, graph& g) const
- {
- return g.new_node<alter_table> (*this, s, g);
- }
-
- void alter_table::
- serialize (xml::serializer& s) const
- {
- s.start_element (xmlns, "alter-table");
- table::serialize_attributes (s);
- table::serialize_content (s);
- s.end_element ();
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- qnameable::parser_map& m (qnameable::parser_map_);
-
- m["table"] = &qnameable::parser_impl<table>;
- m["add-table"] = &qnameable::parser_impl<add_table>;
- m["drop-table"] = &qnameable::parser_impl<drop_table>;
- m["alter-table"] = &qnameable::parser_impl<alter_table>;
-
- using compiler::type_info;
-
- // table
- //
- {
- type_info ti (typeid (table));
- ti.add_base (typeid (qnameable));
- ti.add_base (typeid (uscope));
- insert (ti);
- }
-
- // add_table
- //
- {
- type_info ti (typeid (add_table));
- ti.add_base (typeid (table));
- insert (ti);
- }
-
- // drop_table
- //
- {
- type_info ti (typeid (drop_table));
- ti.add_base (typeid (qnameable));
- insert (ti);
- }
-
- // alter_table
- //
- {
- type_info ti (typeid (alter_table));
- ti.add_base (typeid (table));
- insert (ti);
- }
- }
- } init_;
- }
- }
-}
diff --git a/odb/semantics/template.cxx b/odb/semantics/template.cxx
deleted file mode 100644
index d49cf20..0000000
--- a/odb/semantics/template.cxx
+++ /dev/null
@@ -1,88 +0,0 @@
-// file : odb/semantics/template.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <cutl/compiler/type-info.hxx>
-#include <odb/semantics/template.hxx>
-
-namespace semantics
-{
- template_::
- template_ ()
- {
- }
-
- instantiates::
- instantiates ()
- {
- }
-
- instantiation::
- instantiation ()
- {
- }
-
- type_template::
- type_template ()
- {
- }
-
- type_instantiation::
- type_instantiation ()
- {
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- using compiler::type_info;
-
- // template_
- //
- {
- type_info ti (typeid (template_));
- ti.add_base (typeid (nameable));
- insert (ti);
- }
-
- // instantiates
- //
- {
- type_info ti (typeid (instantiates));
- ti.add_base (typeid (edge));
- insert (ti);
- }
-
- // instantiation
- //
- {
- type_info ti (typeid (instantiation));
- ti.add_base (typeid (node));
- insert (ti);
- }
-
- // type_template
- //
- {
- type_info ti (typeid (type_template));
- ti.add_base (typeid (template_));
- insert (ti);
- }
-
- // type_instantiation
- //
- {
- type_info ti (typeid (type_instantiation));
- ti.add_base (typeid (type));
- ti.add_base (typeid (instantiation));
- insert (ti);
- }
-
- }
- } init_;
- }
-}
diff --git a/odb/semantics/union-template.cxx b/odb/semantics/union-template.cxx
deleted file mode 100644
index f2c3f94..0000000
--- a/odb/semantics/union-template.cxx
+++ /dev/null
@@ -1,54 +0,0 @@
-// file : odb/semantics/union-template.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <cutl/compiler/type-info.hxx>
-#include <odb/semantics/union-template.hxx>
-
-namespace semantics
-{
- union_template::
- union_template (path const& file, size_t line, size_t column, tree tn)
- : node (file, line, column, tn)
- {
- }
-
- union_instantiation::
- union_instantiation (path const& file,
- size_t line,
- size_t column,
- tree tn)
- : node (file, line, column, tn)
- {
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- using compiler::type_info;
-
- // union_template
- //
- {
- type_info ti (typeid (union_template));
- ti.add_base (typeid (type_template));
- ti.add_base (typeid (scope));
- insert (ti);
- }
-
- // union_instantiation
- //
- {
- type_info ti (typeid (union_instantiation));
- ti.add_base (typeid (union_));
- ti.add_base (typeid (type_instantiation));
- insert (ti);
- }
- }
- } init_;
- }
-}
diff --git a/odb/semantics/union.cxx b/odb/semantics/union.cxx
deleted file mode 100644
index 980dfa4..0000000
--- a/odb/semantics/union.cxx
+++ /dev/null
@@ -1,36 +0,0 @@
-// file : odb/semantics/union.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <cutl/compiler/type-info.hxx>
-#include <odb/semantics/union.hxx>
-
-namespace semantics
-{
- union_::
- union_ (path const& file, size_t line, size_t column, tree tn)
- : node (file, line, column, tn)
- {
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- using compiler::type_info;
-
- // union_
- //
- {
- type_info ti (typeid (union_));
- ti.add_base (typeid (type));
- ti.add_base (typeid (scope));
- insert (ti);
- }
- }
- } init_;
- }
-}
diff --git a/odb/semantics/unit.cxx b/odb/semantics/unit.cxx
deleted file mode 100644
index fe191bc..0000000
--- a/odb/semantics/unit.cxx
+++ /dev/null
@@ -1,42 +0,0 @@
-// file : odb/semantics/unit.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <odb/gcc.hxx>
-
-#include <cutl/compiler/type-info.hxx>
-#include <odb/semantics/unit.hxx>
-
-namespace semantics
-{
- unit::
- unit (path const& file)
- : node (file, 1, 1, global_namespace), graph_ (*this)
- {
- // Use a special edge to get this->name() return the global
- // namespace name ("").
- //
- new_edge<global_names> (*this, *this);
- node::unit (*this);
- }
-
- // type info
- //
- namespace
- {
- struct init
- {
- init ()
- {
- using compiler::type_info;
-
- // unit
- //
- {
- type_info ti (typeid (unit));
- ti.add_base (typeid (namespace_));
- insert (ti);
- }
- }
- } init_;
- }
-}
diff --git a/tests/.gitignore b/odb/tests/.gitignore
index 35ec43f..35ec43f 100644
--- a/tests/.gitignore
+++ b/odb/tests/.gitignore
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/tests/build/bootstrap.build b/odb/tests/build/bootstrap.build
index 5e48571..5e48571 100644
--- a/tests/build/bootstrap.build
+++ b/odb/tests/build/bootstrap.build
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/tests/buildfile b/odb/tests/buildfile
index 80a4e67..80a4e67 100644
--- a/tests/buildfile
+++ b/odb/tests/buildfile
diff --git a/tests/testscript b/odb/tests/testscript
index 5448eca..5448eca 100644
--- a/tests/testscript
+++ b/odb/tests/testscript
diff --git a/odb/traversal/elements.hxx b/odb/traversal/elements.hxx
deleted file mode 100644
index c4236ce..0000000
--- a/odb/traversal/elements.hxx
+++ /dev/null
@@ -1,238 +0,0 @@
-// file : odb/traversal/elements.hxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#ifndef ODB_TRAVERSAL_ELEMENTS_HXX
-#define ODB_TRAVERSAL_ELEMENTS_HXX
-
-#include <cutl/compiler/traversal.hxx>
-#include <odb/semantics/elements.hxx>
-
-namespace traversal
-{
- using namespace cutl;
-
- //
- //
- typedef compiler::dispatcher<semantics::node> node_dispatcher;
- typedef compiler::dispatcher<semantics::edge> edge_dispatcher;
-
- //
- //
- struct node_base: node_dispatcher, edge_dispatcher
- {
- void
- edge_traverser (edge_dispatcher& d)
- {
- edge_dispatcher::traverser (d);
- }
-
- edge_dispatcher&
- edge_traverser ()
- {
- return *this;
- }
-
- using node_dispatcher::dispatch;
- using edge_dispatcher::dispatch;
-
- using edge_dispatcher::iterate_and_dispatch;
- };
-
- struct edge_base: edge_dispatcher, node_dispatcher
- {
- void
- node_traverser (node_dispatcher& d)
- {
- node_dispatcher::traverser (d);
- }
-
- node_dispatcher&
- node_traverser ()
- {
- return *this;
- }
-
- using edge_dispatcher::dispatch;
- using node_dispatcher::dispatch;
-
- using node_dispatcher::iterate_and_dispatch;
- };
-
- inline edge_base&
- operator>> (node_base& n, edge_base& e)
- {
- n.edge_traverser (e);
- return e;
- }
-
- inline node_base&
- operator>> (edge_base& e, node_base& n)
- {
- e.node_traverser (n);
- return n;
- }
-
- //
- //
- template <typename X>
- struct node: compiler::traverser_impl<X, semantics::node>,
- virtual node_base
- {
- };
-
- template <typename X>
- struct edge: compiler::traverser_impl<X, semantics::edge>,
- virtual edge_base
- {
- };
-
- //
- // Edges
- //
-
- struct names: edge<semantics::names>
- {
- names ()
- {
- }
-
- names (node_dispatcher& n)
- {
- node_traverser (n);
- }
-
- virtual void
- traverse (type&);
- };
-
- struct declares: edge<semantics::declares>
- {
- declares ()
- {
- }
-
- declares (node_dispatcher& n)
- {
- node_traverser (n);
- }
-
- virtual void
- traverse (type&);
- };
-
- struct defines: edge<semantics::defines>
- {
- defines ()
- {
- }
-
- defines (node_dispatcher& n)
- {
- node_traverser (n);
- }
-
- virtual void
- traverse (type&);
- };
-
- struct typedefs: edge<semantics::typedefs>
- {
- typedefs ()
- {
- }
-
- typedefs (node_dispatcher& n)
- {
- node_traverser (n);
- }
-
- virtual void
- traverse (type&);
- };
-
- struct belongs: edge<semantics::belongs>
- {
- belongs ()
- {
- }
-
- belongs (node_dispatcher& n)
- {
- node_traverser (n);
- }
-
- virtual void
- traverse (type&);
- };
-
- //
- // Nodes
- //
-
- struct nameable: node<semantics::nameable> {};
-
- //
- //
- template <typename T>
- struct scope_template: node<T>
- {
- public:
- virtual void
- traverse (T& s)
- {
- names (s);
- }
-
- virtual void
- names (T& s)
- {
- names (s, *this);
- }
-
- virtual void
- names (T& s, edge_dispatcher& d)
- {
- this->iterate_and_dispatch (s.names_begin (), s.names_end (), d);
- }
- };
-
- struct scope: scope_template<semantics::scope> {};
-
- //
- //
- struct type: node<semantics::type> {};
-
- //
- //
- struct instance: node<semantics::instance>
- {
- virtual void
- traverse (type&);
-
- virtual void
- belongs (type&);
-
- virtual void
- belongs (type&, edge_dispatcher&);
- };
-
- //
- //
- struct data_member: node<semantics::data_member>
- {
- virtual void
- traverse (type&);
-
- virtual void
- belongs (type&);
-
- virtual void
- belongs (type&, edge_dispatcher&);
- };
-
- //
- //
- struct unsupported_type: node<semantics::unsupported_type> {};
-}
-
-#endif // ODB_TRAVERSAL_ELEMENTS_HXX
diff --git a/odb/traversal/relational/elements.hxx b/odb/traversal/relational/elements.hxx
deleted file mode 100644
index 9ecdc6a..0000000
--- a/odb/traversal/relational/elements.hxx
+++ /dev/null
@@ -1,162 +0,0 @@
-// file : odb/traversal/relational/elements.hxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#ifndef ODB_TRAVERSAL_RELATIONAL_ELEMENTS_HXX
-#define ODB_TRAVERSAL_RELATIONAL_ELEMENTS_HXX
-
-#include <cutl/compiler/traversal.hxx>
-#include <odb/semantics/relational/elements.hxx>
-
-namespace traversal
-{
- namespace relational
- {
- using namespace cutl;
-
- //
- //
- typedef compiler::dispatcher<semantics::relational::node> node_dispatcher;
- typedef compiler::dispatcher<semantics::relational::edge> edge_dispatcher;
-
- //
- //
- struct node_base: node_dispatcher, edge_dispatcher
- {
- void
- edge_traverser (edge_dispatcher& d)
- {
- edge_dispatcher::traverser (d);
- }
-
- edge_dispatcher&
- edge_traverser ()
- {
- return *this;
- }
-
- using node_dispatcher::dispatch;
- using edge_dispatcher::dispatch;
-
- using edge_dispatcher::iterate_and_dispatch;
- };
-
- struct edge_base: edge_dispatcher, node_dispatcher
- {
- void
- node_traverser (node_dispatcher& d)
- {
- node_dispatcher::traverser (d);
- }
-
- node_dispatcher&
- node_traverser ()
- {
- return *this;
- }
-
- using edge_dispatcher::dispatch;
- using node_dispatcher::dispatch;
-
- using node_dispatcher::iterate_and_dispatch;
- };
-
- inline edge_base&
- operator>> (node_base& n, edge_base& e)
- {
- n.edge_traverser (e);
- return e;
- }
-
- inline node_base&
- operator>> (edge_base& e, node_base& n)
- {
- e.node_traverser (n);
- return n;
- }
-
- //
- //
- template <typename X>
- struct node: compiler::traverser_impl<X, semantics::relational::node>,
- virtual node_base
- {
- virtual void
- traverse (X&) {}
- };
-
- template <typename X>
- struct edge: compiler::traverser_impl<X, semantics::relational::edge>,
- virtual edge_base
- {
- };
-
- //
- // Edges
- //
-
- template <typename N>
- struct names: edge<semantics::relational::names<N> >
- {
- names ()
- {
- }
-
- names (node_dispatcher& n)
- {
- this->node_traverser (n);
- }
-
- virtual void
- traverse (semantics::relational::names<N>& e)
- {
- this->dispatch (e.nameable ());
- }
- };
-
- typedef names<semantics::relational::uname> unames;
- typedef names<semantics::relational::qname> qnames;
-
- //
- // Nodes
- //
-
- template <typename N>
- struct nameable: node<semantics::relational::nameable<N> > {};
-
- typedef nameable<semantics::relational::uname> unameable;
- typedef nameable<semantics::relational::qname> qnameable;
-
- //
- //
- template <typename T>
- struct scope_template: node<T>
- {
- public:
- virtual void
- traverse (T& s)
- {
- names (s);
- }
-
- virtual void
- names (T& s)
- {
- names (s, *this);
- }
-
- virtual void
- names (T& s, edge_dispatcher& d)
- {
- this->iterate_and_dispatch (s.names_begin (), s.names_end (), d);
- }
- };
-
- template <typename N>
- struct scope: scope_template<semantics::relational::scope<N> > {};
-
- typedef scope<semantics::relational::uname> uscope;
- typedef scope<semantics::relational::qname> qscope;
- }
-}
-
-#endif // ODB_TRAVERSAL_RELATIONAL_ELEMENTS_HXX
diff --git a/odb/validator.cxx b/odb/validator.cxx
deleted file mode 100644
index bf9aa6b..0000000
--- a/odb/validator.cxx
+++ /dev/null
@@ -1,1912 +0,0 @@
-// file : odb/validator.cxx
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <odb/gcc.hxx>
-
-#include <set>
-#include <iostream>
-
-#include <odb/traversal.hxx>
-#include <odb/common.hxx>
-#include <odb/context.hxx>
-#include <odb/diagnostics.hxx>
-#include <odb/validator.hxx>
-#include <odb/cxx-lexer.hxx>
-
-#include <odb/relational/validator.hxx>
-
-using namespace std;
-
-namespace
-{
- // Resolve null overrides.
- //
- static void
- override_null (semantics::node& n, string const& prefix = "")
- {
- string p (prefix.empty () ? prefix : prefix + '-');
-
- if (n.count (p + "null") && n.count (p + "not-null"))
- {
- if (n.get<location_t> (p + "null-location") <
- n.get<location_t> (p + "not-null-location"))
- {
- n.remove (p + "null");
- n.remove (p + "null-location");
- }
- else
- {
- n.remove (p + "not-null");
- n.remove (p + "not-null-location");
- }
- }
- }
-
- //
- // Pass 1.
- //
-
- struct data_member1: traversal::data_member, context
- {
- data_member1 (bool& valid)
- : valid_ (valid)
- {
- }
-
- virtual void
- traverse (type& m)
- {
- semantics::class_& c (dynamic_cast<semantics::class_&> (m.scope ()));
- bool obj (object (c));
-
- // If the class is marked transient, then mark each non-virtual
- // data member as transient.
- //
- {
- bool t (transient (m));
-
- if (!t && c.count ("transient") && !m.count ("virtual"))
- {
- m.set ("transient", true);
- t = true;
- }
-
- if (t)
- return;
- }
-
- semantics::names* hint;
- semantics::type& t (utype (m, hint));
-
- if (t.fq_anonymous (hint))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: unnamed type in data member declaration" << endl;
-
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " info: use 'typedef' to name this type" << endl;
-
- valid_ = false;
- }
-
- // Make sure id or inverse member is not marked readonly since we
- // depend on these three sets not having overlaps.
- //
- if (m.count ("readonly")) // context::readonly() also checks the class.
- {
- if (id (m))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: object id should not be declared readonly" << endl;
-
- valid_ = false;
- }
-
- if (inverse (m))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: inverse object pointer should not be declared "
- << "readonly" << endl;
-
- valid_ = false;
- }
- }
-
- // Make sure a member of a section is an immediate member of an object.
- // The same for the section member itself.
- //
- bool section (t.fq_name () == "::odb::section");
-
- if (!obj)
- {
- if (m.count ("section-member"))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ": " <<
- "error: data member belonging to a section can only be a " <<
- "direct member of a persistent class" << endl;
- valid_ = false;
- }
-
- if (section)
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ": " <<
- "error: section data member can only be a direct member of a " <<
- "persistent class" << endl;
- valid_ = false;
- }
- }
-
- // Make sure the load and update pragmas are only specified on
- // section members.
- //
- if (!section)
- {
- if (m.count ("section-load"))
- {
- location_t loc (m.get<location_t> ("section-load-location"));
- error (loc) << "'#pragma db load' can only be specified for "
- "a section data member" << endl;
- valid_ = false;
- }
-
- if (m.count ("section-update"))
- {
- location_t loc (m.get<location_t> ("section-update-location"));
- error (loc) << "'#pragma db update' can only be specified for "
- "a section data member" << endl;
- valid_ = false;
- }
- }
-
- // Check that the addition version makes sense.
- //
- unsigned long long av (m.get<unsigned long long> ("added", 0));
- if (av != 0)
- {
- location_t l (m.get<location_t> ("added-location"));
-
- if (id (m))
- {
- error (l) << "object id cannod be soft-added" << endl;
- valid_ = false;
- }
-
- if (version (m))
- {
- error (l) << "optimistic concurrency version cannod be "
- "soft-added" << endl;
- valid_ = false;
- }
-
- if (!versioned ())
- {
- error (l) << "added data member in a non-versioned object " <<
- "model" << endl;
- valid_ = false;
- }
- else
- {
- model_version const& mv (version ());
-
- if (av > mv.current)
- {
- error (l) << "addition version is greater than the current " <<
- "model version" << endl;
- valid_ = false;
- }
- else if (av <= mv.base)
- {
- error (l) << "addition version is less than or equal to the " <<
- "base model version" << endl;
- info (l) << "delete this pragma since migration to version " <<
- av << " is no longer possible" << endl;
- valid_ = false;
- }
- }
- }
-
- // Check that the deletion version makes sense.
- //
- unsigned long long dv (m.get<unsigned long long> ("deleted", 0));
- if (dv != 0)
- {
- location_t l (m.get<location_t> ("deleted-location"));
-
- if (id (m))
- {
- error (l) << "object id cannod be soft-deleted" << endl;
- valid_ = false;
- }
-
- if (version (m))
- {
- error (l) << "optimistic concurrency version cannod be "
- "soft-deleted" << endl;
- valid_ = false;
- }
-
- if (!versioned ())
- {
- error (l) << "deleted data member in a non-versioned object " <<
- "model" << endl;
- valid_ = false;
- }
- else
- {
- model_version const& mv (version ());
-
- if (dv > mv.current)
- {
- error (l) << "deletion version is greater than the current " <<
- "model version" << endl;
- valid_ = false;
- }
- else if (dv <= mv.base)
- {
- error (l) << "deletion version is less than or equal to the " <<
- "base model version" << endl;
- info (c.location ()) << "delete this data member since " <<
- "migration to version " << dv << " is no longer possible" <<
- endl;
- valid_ = false;
- }
- }
- }
-
- // Make sure that addition and deletion versions are properly ordered.
- // We can have both the [av, dv] (added then deleted) and [dv, av]
- // (deleted then re-added) intervals.
- //
- if (av != 0 && dv != 0 && av == dv)
- {
- location_t al (m.get<location_t> ("added-location"));
- location_t dl (m.get<location_t> ("deleted-location"));
-
- error (al) << "addition and deletion versions are the same" << endl;
- info (dl) << "deletion version is specified here" << endl;
- valid_ = false;
- }
-
- if (section)
- return; // Section data member is transient.
-
- // Resolve null overrides.
- //
- override_null (m);
- override_null (m, "value");
- }
-
- bool& valid_;
- };
-
- // Find special members (id, version).
- //
- struct special_members: traversal::class_, context
- {
- special_members (class_kind_type kind,
- bool& valid,
- semantics::data_member*& id,
- semantics::data_member*& optimistic)
- : kind_ (kind), member_ (valid, id, optimistic)
- {
- if (kind != class_view)
- *this >> inherits_ >> *this;
-
- *this >> names_ >> member_;
- }
-
- virtual void
- traverse (semantics::class_& c)
- {
- // Skip transient bases.
- //
- switch (kind_)
- {
- case class_object:
- {
- if (!object (c))
- return;
- break;
- }
- case class_view:
- {
- break;
- }
- case class_composite:
- {
- if (!composite (c))
- return;
- break;
- }
- case class_other:
- {
- assert (false);
- break;
- }
- }
-
- // Views don't have bases.
- //
- if (kind_ != class_view)
- inherits (c);
-
- names (c);
- }
-
- private:
- struct member: traversal::data_member, context
- {
- member (bool& valid,
- semantics::data_member*& id,
- semantics::data_member*& optimistic)
- : valid_ (valid), id_ (id), optimistic_ (optimistic)
- {
- }
-
- virtual void
- traverse (semantics::data_member& m)
- {
- if (m.count ("id"))
- {
- if (id_ == 0)
- id_ = &m;
- else
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: multiple object id members" << endl;
-
- os << id_->file () << ":" << id_->line () << ":" << id_->column ()
- << ": info: previous id member is declared here" << endl;
-
- valid_ = false;
- }
- }
-
- if (version (m))
- {
- if (optimistic_ == 0)
- optimistic_ = &m;
- else
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: multiple version members" << endl;
-
- semantics::data_member& o (*optimistic_);
-
- os << o.file () << ":" << o.line () << ":" << o.column ()
- << ": info: previous version member is declared here" << endl;
-
- valid_ = false;
- }
- }
- }
-
- bool& valid_;
- semantics::data_member*& id_;
- semantics::data_member*& optimistic_;
- };
-
- class_kind_type kind_;
- member member_;
- traversal::names names_;
- traversal::inherits inherits_;
- };
-
- //
- //
- struct value_type: traversal::type, context
- {
- virtual void
- traverse (semantics::type& t)
- {
- // Resolve null overrides.
- //
- override_null (t);
- override_null (t, "value");
- }
- };
-
- struct typedefs1: typedefs
- {
- typedefs1 (traversal::declares& d)
- : typedefs (true), declares_ (d)
- {
- }
-
- void
- traverse (semantics::typedefs& t)
- {
- if (check (t))
- traversal::typedefs::traverse (t);
- else
- declares_.traverse (t);
- }
-
- private:
- traversal::declares& declares_;
- };
-
- //
- //
- struct class1: traversal::class_, context
- {
- class1 (bool& valid)
- : valid_ (valid), typedefs_ (declares_), member_ (valid)
- {
- *this >> defines_ >> *this;
- *this >> typedefs_ >> *this;
- *this >> declares_ >> vt_;
-
- names_member_ >> member_;
- }
-
- virtual void
- traverse (type& c)
- {
- class_kind_type ck (class_kind (c));
-
- if (ck != class_other)
- {
- for (type::inherits_iterator i (c.inherits_begin ());
- i != c.inherits_end (); ++i)
- {
- type& b (i->base ());
-
- if (class_kind (b) == class_other)
- continue;
-
- location_t cl;
- location_t bl;
-
- // If we are in the same file, then use real locations (as
- // opposed to definition locations) since that's the order
- // in which we will be generating the code.
- //
- if (class_file (c) == class_file (b))
- {
- cl = class_real_location (c);
- bl = class_real_location (b);
- }
- else
- {
- cl = class_location (c);
- bl = class_location (b);
- }
-
- if (cl < bl)
- {
- // We cannot use class_name() for b since it might not have
- // yet been processed (tree-hint).
- //
- error (cl) << "base class " << b.name () << " must "
- << "be defined before derived class " << class_name (c)
- << endl;
-
- info (bl) << "class " << b.name () << " is define here"
- << endl;
-
- valid_ = false;
- }
- }
- }
-
- switch (ck)
- {
- case class_object:
- names (c);
- traverse_object (c);
- break;
- case class_view:
- names (c);
- traverse_view (c);
- break;
- case class_composite:
- names (c);
- traverse_composite (c);
- // Fall through.
- case class_other:
- vt_.dispatch (c);
- break;
- }
- }
-
- virtual void
- traverse_object (type& c)
- {
- // Check that the deletion version makes sense.
- //
- if (unsigned long long v = c.get<unsigned long long> ("deleted", 0))
- {
- location_t l (c.get<location_t> ("deleted-location"));
-
- if (!versioned ())
- {
- error (l) << "deleted class in a non-versioned object model" << endl;
- valid_ = false;
- }
- else
- {
- model_version const& mv (version ());
-
- if (v > mv.current)
- {
- error (l) << "deletion version is greater than the current " <<
- "model version" << endl;
- valid_ = false;
- }
- else if (v <= mv.base)
- {
- error (l) << "deletion version is less than or equal to the " <<
- "base model version" << endl;
- info (c.location ()) << "delete this class since migration to " <<
- "version " << v << " is no longer possible" << endl;
- valid_ = false;
- }
- }
- }
-
- // Check that the callback function exist.
- //
- if (c.count ("callback"))
- {
- string name (c.get<string> ("callback"));
- tree decl (
- lookup_qualified_name (
- c.tree_node (), get_identifier (name.c_str ()), false, false));
-
- if (decl != error_mark_node && TREE_CODE (decl) == BASELINK)
- {
- // Figure out if we have a const version of the callback. OVL_*
- // macros work for both FUNCTION_DECL and OVERLOAD.
- //
-#if BUILDING_GCC_MAJOR >= 8
- for (ovl_iterator i (BASELINK_FUNCTIONS (decl)); i; ++i)
-#else
- for (tree o (BASELINK_FUNCTIONS (decl)); o != 0; o = OVL_NEXT (o))
-#endif
- {
-#if BUILDING_GCC_MAJOR >= 8
- tree f (*i);
-#else
- tree f (OVL_CURRENT (o));
-#endif
- if (DECL_CONST_MEMFUNC_P (f))
- {
- c.set ("callback-const", true);
- break;
- }
- }
-
- //@@ Would be nice to check the signature of the function(s)
- // instead of postponing it until the C++ compilation. Though
- // we may still get C++ compilation errors because of const
- // mismatch.
- //
- }
- else
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ": "
- << "error: unable to resolve member function '" << name << "' "
- << "specified with '#pragma db callback' for class '"
- << class_name (c) << "'" << endl;
-
- valid_ = false;
- }
- }
-
- // Check bases.
- //
- type* poly_root (0);
-
- for (type::inherits_iterator i (c.inherits_begin ());
- i != c.inherits_end ();
- ++i)
- {
- type& b (i->base ());
-
- if (object (b))
- {
- if (type* r = polymorphic (b))
- {
- if (poly_root == 0)
- {
- poly_root = r;
- c.set ("polymorphic-base", &static_cast<semantics::class_&> (b));
- }
- // If poly_root and r are the same, then we have virtual
- // inheritance. Though we don't support it at the moment.
- //
- else //if (poly_root != r)
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: persistent class '" << class_name (c) << "' "
- << "derives from multiple polymorphic bases" << endl;
-
- type& a (*poly_root);
- os << a.file () << ":" << a.line () << ":" << a.column () << ":"
- << " info: first polymorphic base defined here" << endl;
-
- type& b (*r);
- os << b.file () << ":" << b.line () << ":" << b.column () << ":"
- << " info: second polymorphic base defined here" << endl;
-
- valid_ = false;
- }
- }
- }
- else if (view (b) || composite (b))
- {
- // @@ Should we use hint here?
- //
- string name (class_fq_name (b));
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: base class '" << name << "' is a view or value type"
- << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: object types cannot derive from view or value "
- << "types"
- << endl;
-
- os << b.file () << ":" << b.line () << ":" << b.column () << ":"
- << " info: class '" << name << "' is defined here" << endl;
-
- valid_ = false;
- }
- }
-
- // Check members.
- //
- names (c, names_member_);
-
- // Check special members.
- //
- using semantics::data_member;
-
- data_member* id (0);
- data_member* optimistic (0);
- {
- special_members t (class_object, valid_, id, optimistic);
- t.traverse (c);
- }
-
- if (id == 0)
- {
- // An object without an id should either be reuse-abstract
- // or explicitly marked as such. We check that it is not
- // polymorphic below.
- //
- if (!(c.count ("id") || abstract (c)))
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: no data member designated as an object id" << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: use '#pragma db id' to specify an object id member"
- << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: or explicitly declare that this persistent class "
- << "has no object id with '#pragma db object no_id'" << endl;
-
- valid_ = false;
- }
- }
- else
- {
- // Convert id to a member path. This has to happen early since
- // a lot of code that runs next (e.g., processor, pass 1) depends
- // on this information being available.
- //
- data_member_path& idp (c.set ("id-member", data_member_path ()));
- idp.push_back (id);
-
- // See if we have a member path that we need to resolve.
- //
- const string& name (id->get<string> ("id"));
- location_t l (id->get<location_t> ("id-location"));
-
- if (!name.empty ())
- {
- if (id->count ("auto"))
- {
- error (l) << "nested id cannot be automatically assigned" << endl;
- valid_ = false;
- }
-
- if (semantics::class_* comp = utype (*id).is_a<semantics::class_> ())
- {
- try
- {
- resolve_data_members (idp, *comp, name, l, lex_);
- }
- catch (const operation_failed&) {valid_ = false;}
- }
- else
- {
- error (l) << "nested id requires composite member" << endl;
- valid_ = false;
- }
-
- // Mark the whole member as readonly.
- //
- id->set ("readonly", true);
- }
-
- data_member* idf (idp.front ());
- data_member* idb (idp.back ());
-
- // Complain if an id member has a default value (default value
- // for the id's type is ok -- we will ignore it).
- //
- if (idb->count ("default"))
- {
- error (l) << "object id member cannot have default value" << endl;
- valid_ = false;
- }
-
- // Complain if an id member is in a section.
- //
- if (idf->count ("section-member"))
- {
- error (l) << "object id member cannot be in a section" << endl;
- valid_ = false;
- }
-
- // Automatically mark the id member as not null. If we already have
- // an explicit null pragma for this member, issue an error.
- //
- if (idb->count ("null"))
- {
- error (l) << "object id member cannot be null" << endl;
- valid_ = false;
- }
- else
- idf->set ("not-null", true);
- }
-
- if (optimistic != 0)
- {
- semantics::data_member& m (*optimistic);
-
- // Make sure we have the class declared optimistic.
- //
- if (&m.scope () == &c && !c.count ("optimistic"))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: version data member in a class not declared "
- << "optimistic" << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: use '#pragma db optimistic' to declare this "
- << "class optimistic" << endl;
-
- valid_ = false;
- }
-
- // Make sure we have object id.
- //
- if (id == 0)
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: optimistic class without an object id" << endl;
-
- valid_ = false;
- }
-
- // Make sure id and version members are in the same class. The
- // current architecture relies on that.
- //
- if (id != 0 && &id->scope () != &m.scope ())
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: object id and version members are in different "
- << "classes" << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: object id and version members must be in the same "
- << "class" << endl;
-
- os << id->file () << ":" << id->line () << ":" << id->column ()
- << ": info: object id member is declared here" << endl;
-
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: version member is declared here" << endl;
-
- valid_ = false;
- }
-
- // Make sure this class is not readonly.
- //
- if (readonly (c))
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: optimistic class cannot be readonly" << endl;
-
- valid_ = false;
- }
-
- // Complain if the version member is in a section.
- //
- if (m.count ("section-member"))
- {
- os << m.file () << ":" << m.line () << ":" << m.column ()
- << ": error: version member cannot be in a section" << endl;
- valid_ = false;
- }
-
- // This takes care of also marking derived classes as optimistic.
- //
- c.set ("optimistic-member", optimistic);
- }
- else
- {
- // Make sure there is a version member if the class declared
- // optimistic.
- //
- if (c.count ("optimistic"))
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: optimistic class without a version member" << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: use '#pragma db version' to declare on of the "
- << "data members as a version" << endl;
-
- valid_ = false;
- }
- }
-
- // Polymorphic inheritance.
- //
- if (c.count ("polymorphic") && poly_root == 0)
- {
- // Root of the hierarchy.
- //
-
- if (id == 0)
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: polymorphic class without an object id" << endl;
-
- valid_ = false;
- }
-
- if (!TYPE_POLYMORPHIC_P (c.tree_node ()))
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: non-polymorphic class (class without virtual "
- << "functions) cannot be declared polymorphic" << endl;
-
- valid_ = false;
- }
-
- poly_root = &c;
- }
-
- if (poly_root != 0)
- c.set ("polymorphic-root", poly_root);
-
- // Sectionable objects.
- //
- if (c.count ("sectionable"))
- {
- if (optimistic == 0)
- {
- location_t l (c.get<location_t> ("sectionable-location"));
- error (l) << "only optimistic class can be sectionable" << endl;
- valid_ = false;
- }
- else if (&optimistic->scope () != &c && poly_root != &c)
- {
- location l (c.get<location_t> ("sectionable-location"));
- error (l) << "only optimistic class that declares the version " <<
- "data member or that is a root of a polymorphic hierarchy can " <<
- "be sectionable" << endl;
- info (optimistic->location ()) << "version member is declared " <<
- "here" << endl;
- valid_ = false;
- }
- }
-
- // Update features set based on this object.
- //
- if (options.at_once () || class_file (c) == unit.file ())
- {
- if (poly_root != 0)
- features.polymorphic_object = true;
- else if (id == 0 && !abstract (c))
- features.no_id_object = true;
- else
- features.simple_object = true;
- }
- }
-
- virtual void
- traverse_view (type& c)
- {
- // Views require query support.
- //
- if (!options.generate_query ())
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: query support is required when using views"
- << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: use the --generate-query option to enable query "
- << "support"
- << endl;
-
- valid_ = false;
- }
-
- // Check that the callback function exist.
- //
- if (c.count ("callback"))
- {
- string name (c.get<string> ("callback"));
- tree decl (
- lookup_qualified_name (
- c.tree_node (), get_identifier (name.c_str ()), false, false));
-
- if (decl == error_mark_node || TREE_CODE (decl) != BASELINK)
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ": "
- << "error: unable to resolve member function '" << name << "' "
- << "specified with '#pragma db callback' for class '"
- << class_name (c) << "'" << endl;
-
- valid_ = false;
- }
-
- // No const version for views.
-
- //@@ Would be nice to check the signature of the function(s)
- // instead of postponing it until the C++ compilation. Though
- // we may still get C++ compilation errors because of const
- // mismatch.
- //
- }
-
- // Check bases.
- //
- for (type::inherits_iterator i (c.inherits_begin ());
- i != c.inherits_end ();
- ++i)
- {
- type& b (i->base ());
-
- if (object (b) || view (b) || composite (b))
- {
- // @@ Should we use hint here?
- //
- string name (class_fq_name (b));
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: base class '" << name << "' is an object, "
- << "view, or value type"
- << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: view types cannot derive from view, object or "
- << "value types"
- << endl;
-
- os << b.file () << ":" << b.line () << ":" << b.column () << ":"
- << " info: class '" << name << "' is defined here" << endl;
-
- valid_ = false;
- }
- }
-
- // Check members.
- //
- names (c, names_member_);
-
- // Check id.
- //
- semantics::data_member* id (0);
- semantics::data_member* optimistic (0);
- {
- special_members t (class_view, valid_, id, optimistic);
- t.traverse (c);
- }
-
- if (id != 0)
- {
- os << id->file () << ":" << id->line () << ":" << id->column ()
- << ": error: view type data member cannot be designated as an "
- << "object id" << endl;
-
- valid_ = false;
- }
-
- if (optimistic != 0)
- {
- semantics::data_member& o (*optimistic);
-
- os << o.file () << ":" << o.line () << ":" << o.column ()
- << ": error: view type data member cannot be designated as a "
- << "version" << endl;
-
- valid_ = false;
- }
-
- // Update features set based on this view.
- //
- if (options.at_once () || class_file (c) == unit.file ())
- {
- features.view = true;
- }
- }
-
- virtual void
- traverse_composite (type& c)
- {
- for (type::inherits_iterator i (c.inherits_begin ());
- i != c.inherits_end ();
- ++i)
- {
- type& b (i->base ());
-
- if (object (b) || view (b))
- {
- // @@ Should we use hint here?
- //
- string name (class_fq_name (b));
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: base class '" << name << "' is a view or object "
- << "type"
- << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: composite value types cannot derive from object "
- << "or view types" << endl;
-
- os << b.file () << ":" << b.line () << ":" << b.column () << ":"
- << " info: class '" << name << "' is defined here" << endl;
-
- valid_ = false;
- }
- }
-
- // Check members.
- //
- names (c, names_member_);
-
- // Check id.
- //
- semantics::data_member* id (0);
- semantics::data_member* optimistic (0);
- {
- special_members t (class_composite, valid_, id, optimistic);
- t.traverse (c);
- }
-
- if (id != 0)
- {
- os << id->file () << ":" << id->line () << ":" << id->column ()
- << ": error: value type data member cannot be designated as an "
- << "object id" << endl;
-
- valid_ = false;
- }
-
- if (optimistic != 0)
- {
- semantics::data_member& o (*optimistic);
-
- os << o.file () << ":" << o.line () << ":" << o.column ()
- << ": error: value type data member cannot be designated as a "
- << "version" << endl;
-
- valid_ = false;
- }
- }
-
- bool& valid_;
-
- traversal::defines defines_;
- traversal::declares declares_;
- typedefs1 typedefs_;
-
- value_type vt_;
- data_member1 member_;
- traversal::names names_member_;
-
- cxx_string_lexer lex_;
- };
-
- //
- // Pass 2.
- //
-
- struct data_member2: traversal::data_member, context
- {
- data_member2 (bool& valid): valid_ (valid) {}
-
- // Verify that composite value 'c' that is used by data member
- // 'm' is defined before (or inside) class 's'.
- //
- void
- verify_composite_location (semantics::class_& c,
- semantics::class_& s,
- semantics::data_member& m)
- {
- if (c.in_scope (s))
- return;
-
- location_t cl;
- location_t sl;
-
- // If we are in the same file, then use real locations (as
- // opposed to definition locations) since that's the order
- // in which we will be generating the code.
- //
- if (class_file (c) == class_file (s))
- {
- cl = class_real_location (c);
- sl = class_real_location (s);
- }
- else
- {
- cl = class_location (c);
- sl = class_location (s);
- }
-
- if (sl < cl)
- {
- const string& cn (class_name (c));
-
- error (sl)
- << "composite value type " << class_fq_name (c) << " must "
- << "be defined before being used in class " << class_fq_name (s)
- << endl;
-
- info (m.location ())
- << "data member " << m.name () << " uses " << cn << endl;
-
- info (cl) << "class " << cn << " is define here" << endl;
-
- valid_ = false;
- }
- }
-
- virtual void
- traverse (type& m)
- {
- using semantics::class_;
-
- semantics::type& t (utype (m));
- class_& s (dynamic_cast<class_&> (m.scope ()));
-
- // Validate pointed-to objects.
- //
- class_* c;
- if ((c = object_pointer (t)) || (c = points_to (m)))
- {
- bool poly (polymorphic (*c));
- bool abst (abstract (*c));
-
- // Make sure the pointed-to class is complete.
- //
- if (!c->complete ())
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ": "
- << "error: pointed-to class '" << class_fq_name (*c) << "' "
- << "is incomplete" << endl;
-
- os << c->file () << ":" << c->line () << ":" << c->column () << ": "
- << "info: class '" << class_name (*c) << "' is declared here"
- << endl;
-
- os << c->file () << ":" << c->line () << ":" << c->column () << ": "
- << "info: consider including its definition with the "
- << "--odb-epilogue option" << endl;
-
- valid_ = false;
- return;
- }
-
- // Make sure the pointed-to class is not reuse-abstract.
- //
- if (abst && !poly)
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ": "
- << "error: pointed-to class '" << class_fq_name (*c) << "' "
- << "is abstract" << endl;
-
- os << c->file () << ":" << c->line () << ":" << c->column () << ": "
- << "info: class '" << class_name (*c) << "' is defined here"
- << endl;
-
- throw operation_failed ();
- }
-
- // Make sure the pointed-to class has object id unless it is in a
- // view where we can load no-id objects.
- //
- if (data_member_path* id = id_member (*c))
- {
- semantics::type& idt (utype (*id));
-
- // Make sure composite id is defined before this pointer. Failed
- // that we get non-obvious C++ compiler errors in generated code.
- //
- if (class_* comp = composite_wrapper (idt))
- verify_composite_location (*comp, s, m);
- }
- else if (!view_member (m))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ": "
- << "error: pointed-to class '" << class_fq_name (*c) << "' "
- << "has no object id" << endl;
-
- os << c->file () << ":" << c->line () << ":" << c->column () << ": "
- << "info: class '" << class_name (*c) << "' is defined here"
- << endl;
-
- valid_ = false;
- return;
- }
-
- // Make sure the pointed-to class has a default ctor. Since we will
- // use database::load() in the generated code, lack of a default ctor
- // will lead to uncompilable generated code. Poly-abstract is Ok.
- //
- if (!c->default_ctor () && !(abst && poly))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ": "
- << "error: pointed-to class '" << class_fq_name (*c) << "' "
- << "has no default constructor" << endl;
-
- os << c->file () << ":" << c->line () << ":" << c->column () << ": "
- << "info: class '" << class_name (*c) << "' is defined here"
- << endl;
-
- valid_ = false;
- return;
- }
- }
-
- // Make sure composite type is defined before (or inside)
- // this class. Failed that we get non-obvious C++ compiler
- // errors in generated code.
- //
- if (class_* comp = composite_wrapper (t))
- verify_composite_location (*comp, s, m);
-
- // Check that containers with composite value element types don't have
- // any nested containers.
- //
- if (semantics::type* c = container (m))
- {
- class_* vt (0);
- class_* kt (0);
-
- switch (container_kind (*c))
- {
- case ck_map:
- case ck_multimap:
- {
- kt = composite_wrapper (container_kt (m));
- }
- // Fall through.
- default:
- {
- vt = composite_wrapper (container_vt (m));
- }
- }
-
- if (vt != 0 && has_a (*vt, test_container))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ": "
- << "error: containers of containers not supported" << endl;
-
- os << vt->file () << ":" << vt->line () << ":" << vt->column ()
- << ": info: composite element value " << class_fq_name (*vt)
- << " contains container(s)" << endl;
-
- valid_ = false;
- }
-
- if (kt != 0 && has_a (*kt, test_container))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ": "
- << "error: containers of containers not supported" << endl;
-
- os << kt->file () << ":" << kt->line () << ":" << kt->column ()
- << ": info: composite element key " << class_fq_name (*kt)
- << " contains container(s)" << endl;
-
- valid_ = false;
- }
- }
- }
-
- bool& valid_;
- };
-
- // Make sure soft-delete versions make sense for dependent entities.
- // We don't seem to need anything for soft-add since if an entity is
- // not added (e.g., an object), then we cannot reference it in the
- // C++ sense (e.g., a pointer).
- //
- struct version_dependencies: object_members_base
- {
- version_dependencies (bool& valid): valid_ (valid) {}
-
- virtual void
- traverse_object (semantics::class_& c)
- {
- // For reuse inheritance we allow the base to be soft-deleted. In
- // a sense, it becomes like an abstract class with the added
- // functionality of being able to load old data.
- //
- // For polymorphism inheritance things are a lot more complicated
- // and we don't allow a base to be soft-deleted since there is a
- // link (foreign key) from the derived table.
- //
- semantics::class_* poly (polymorphic (c));
- if (poly != 0 && poly != &c)
- {
- semantics::class_& base (polymorphic_base (c));
- check_strict (
- c, base, "polymorphic derived object", "polymorphic base");
- }
-
- // Don't go into bases.
- //
- names (c);
- }
-
- virtual void
- traverse_simple (semantics::data_member& m)
- {
- semantics::class_& c (dynamic_cast<semantics::class_&> (m.scope ()));
-
- switch (class_kind (c))
- {
- case class_object:
- {
- // Member shouldn't be deleted after the object that contains it.
- //
- check_lax (m, c, "data member", "object");
- break;
- }
- // We leave it up to the user to make sure the view is consistent
- // with the database schema it is being run on.
- //
- default:
- break;
- }
- }
-
- virtual void
- traverse_composite (semantics::data_member* m, semantics::class_& c)
- {
- if (m != 0)
- traverse_simple (*m); // Do simple value tests.
- else
- names (c); // Don't go into bases.
- }
-
- virtual void
- traverse_pointer (semantics::data_member& m, semantics::class_& c)
- {
- traverse_simple (m); // Do simple value tests.
-
- // Pointer must be deleted before the pointed-to object.
- //
- check_strict (m, c, "object pointer", "pointed-to object");
-
- // Inverse pointer must be deleted before the direct side.
- //
- if (data_member_path* imp = inverse (m))
- check_strict (m, *imp, "inverse object pointer", "direct pointer");
- }
-
- virtual void
- traverse_container (semantics::data_member& m, semantics::type&)
- {
- traverse_simple (m); // Do simple value tests.
-
- if (semantics::class_* c = object_pointer (container_vt (m)))
- {
- // Pointer must be deleted before the pointed-to object.
- //
- check_strict (m, *c, "object pointer", "pointed-to object");
-
- // Inverse pointer must be deleted before the direct side.
- //
- if (data_member_path* imp = inverse (m, "value"))
- check_strict (m, *imp, "inverse object pointer", "direct pointer");
- }
- }
-
- private:
- // Check for harmless things like a data member deleted after the
- // object.
- //
- template <typename D, typename P>
- void
- check_lax (D& dep, P& pre, char const* dname, char const* pname)
- {
- unsigned long long dv (deleted (dep));
- unsigned long long pv (deleted (pre));
-
- if (pv == 0 || // Prerequisite never deleted.
- dv == 0 || // Dependent never deleted.
- dv <= pv) // Dependent deleted before prerequisite.
- return;
-
- location_t dl (dep.template get<location_t> ("deleted-location"));
- location_t pl (pre.template get<location_t> ("deleted-location"));
-
- error (dl) << dname << " is deleted after " << dname << endl;
- info (pl) << pname << " deletion version is specified here" << endl;
-
- valid_ = false;
- }
-
- // Check for harmful things that will result in an invalid database
- // schema, like a pointer pointing to a deleted object.
- //
- template <typename D, typename P>
- void
- check_strict (D& dep, P& pre, char const* dname, char const* pname)
- {
- location_t dl (0), pl (0);
- unsigned long long dv (deleted (dep, &dl));
- unsigned long long pv (deleted (pre, &pl));
-
- if (pv == 0) // Prerequisite never deleted.
- return;
-
- if (dv == 0) // Dependent never deleted.
- {
- location dl (dep.location ());
-
- error (dl) << dname << " is not deleted" << endl;
- info (pl) << pname << " is deleted here" << endl;
-
- valid_ = false;
- }
- else if (pv > dv) // Prerequisite deleted before dependent.
- {
- error (dl) << dname << " is deleted after " << pname << endl;
- info (pl) << pname << " deletion version is specified here" << endl;
-
- valid_ = false;
- }
- }
-
- private:
- bool& valid_;
- };
-
- struct class2: traversal::class_, context
- {
- class2 (bool& valid)
- : valid_ (valid), has_lt_operator_ (0), typedefs_ (true), member_ (valid)
- {
- // Find the has_lt_operator function template.
- //
- tree odb (
- lookup_qualified_name (
- global_namespace, get_identifier ("odb"), false, false));
-
- if (odb != error_mark_node)
- {
- tree compiler (
- lookup_qualified_name (
- odb, get_identifier ("compiler"), false, false));
-
- if (compiler != error_mark_node)
- {
- has_lt_operator_ = lookup_qualified_name (
- compiler, get_identifier ("has_lt_operator"), false, false);
-
- if (has_lt_operator_ != error_mark_node)
- {
-#if BUILDING_GCC_MAJOR >= 8
- has_lt_operator_ = OVL_FIRST (has_lt_operator_);
-#else
- has_lt_operator_ = OVL_CURRENT (has_lt_operator_);
-#endif
- }
- else
- {
- os << unit.file () << ": error: unable to resolve has_lt_operator "
- << "function template inside odb::compiler" << endl;
- has_lt_operator_ = 0;
- }
- }
- else
- os << unit.file () << ": error: unable to resolve compiler "
- << "namespace inside odb" << endl;
- }
- else
- os << unit.file () << ": error: unable to resolve odb namespace"
- << endl;
-
- if (has_lt_operator_ == 0)
- valid_ = false;
-
- *this >> defines_ >> *this;
- *this >> typedefs_ >> *this;
-
- names_member_ >> member_;
- }
-
- virtual void
- traverse (type& c)
- {
- class_kind_type ck (class_kind (c));
-
- if (ck == class_other)
- return;
-
- names (c);
-
- switch (ck)
- {
- case class_object: traverse_object (c); break;
- case class_view: traverse_view (c); break;
- case class_composite: traverse_composite (c); break;
- default: return;
- }
-
- // Check members.
- //
- names (c, names_member_);
-
- // Check version dependencies.
- //
- {
- version_dependencies vd (valid_);
- vd.traverse (c);
- }
- }
-
- virtual void
- traverse_object (type& c)
- {
- bool abst (abstract (c));
- bool poly (polymorphic (c));
-
- // Make sure we have no empty or pointless sections unless we
- // are reuse-abstract or polymorphic.
- //
- if (!poly && !abst)
- {
- user_sections& uss (c.get<user_sections> ("user-sections"));
-
- for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
- {
- user_section& s (*i);
-
- // Skip the special version update section (we always treat it
- // as abstract in reuse inheritance).
- //
- if (s.special == user_section::special_version)
- continue;
-
- semantics::data_member& m (*s.member);
- location const& l (m.location ());
-
- if (s.total == 0 && !s.containers)
- {
- error (l) << "empty section" << endl;
-
- if (&m.scope () != &c)
- info (c.location ()) << "as seen in this non-abstract " <<
- "persistent class" << endl;
-
- valid_ = false;
- continue;
- }
-
- // Eager-loaded section with readonly members.
- //
- if (s.load == user_section::load_eager && s.update_empty ())
- {
- error (l) << "eager-loaded section with readonly members is " <<
- "pointless" << endl;
-
- if (&m.scope () != &c)
- info (c.location ()) << "as seen in this non-abstract " <<
- "persistent class" << endl;
-
- valid_ = false;
- }
- }
- }
-
- if (data_member_path* id = id_member (c))
- {
- semantics::type& t (utype (*id));
-
- // If this is a session object, make sure that the id type can
- // be compared.
- //
- if (session (c) && has_lt_operator_ != 0)
- {
- tree args (make_tree_vec (1));
- TREE_VEC_ELT (args, 0) = t.tree_node ();
-
- tree inst (
- instantiate_template (
- has_lt_operator_, args, tf_none));
-
- bool v (inst != error_mark_node);
-
- if (v &&
- DECL_TEMPLATE_INSTANTIATION (inst) &&
- !DECL_TEMPLATE_INSTANTIATED (inst))
- {
- // Instantiate this function template to see if the value type
- // provides operator<. Unfortunately, GCC instantiate_decl()
- // does not provide any control over the diagnostics it issues
- // in case of an error. To work around this, we are going to
- // temporarily redirect diagnostics to /dev/null, which is
- // where asm_out_file points to (see plugin.cxx).
- //
- // Needless to say, this is very hacky and we should quickly fail
- // (as we do below) if there were errors.
- //
- int ec (errorcount);
- FILE* s (global_dc->printer->buffer->stream);
- global_dc->printer->buffer->stream = asm_out_file;
-
- instantiate_decl (inst, false, false);
-
- global_dc->printer->buffer->stream = s;
- v = (ec == errorcount);
- }
-
- if (!v)
- {
- semantics::data_member& idm (*id->front ());
-
- os << t.file () << ":" << t.line () << ":" << t.column ()
- << ": error: value type that is used as object id in "
- << "persistent class with session support does not define "
- << "the less than (<) comparison" << endl;
-
- os << t.file () << ":" << t.line () << ":" << t.column ()
- << ": info: provide operator< for this value type" << endl;
-
- os << idm.file () << ":" << idm.line () << ":" << idm.column ()
- << ": info: id member is defined here" << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column ()
- << ": info: persistent class is defined here" << endl;
-
- valid_ = false;
- }
- }
- }
- else
- {
- // Make sure an object without id has no sections.
- //
- user_sections& uss (c.get<user_sections> ("user-sections"));
-
- if (!uss.empty ())
- {
- semantics::data_member& m (*uss.front ().member);
- os << m.file () << ":" << m.line () << ":" << m.column ()
- << ": error: object without id cannot have sections" << endl;
- valid_ = false;
- }
- }
-
- // Allow all the members to be deleted as long as there is no
- // schema for this class.
- //
- column_count_type const& cc (column_count (c));
- size_t cont (has_a (c, test_container));
- size_t dcont (cont - has_a (c, test_container | exclude_deleted));
-
- if ((cc.total == 0 && cont == 0) ||
- (cc.total == cc.deleted && cont == dcont && !(abst || deleted (c))))
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: no persistent data members in the class" << endl;
- valid_ = false;
- }
- }
-
- virtual void
- traverse_view (type& c)
- {
- // We don't check for the column count here since we may want to
- // allow certain kinds of empty views. Instead, this is handled
- // in relational::validation.
-
- // See if any of the associated objects are polymorphic. If so,
- // we need to include polymorphism-specific headers. Also, if the
- // view is loading the object, then we will need the corresponding
- // statements.
- //
- if (c.count ("objects"))
- {
- view_objects& objs (c.get<view_objects> ("objects"));
-
- for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
- {
- if (i->kind == view_object::object)
- {
- if (polymorphic (*i->obj))
- features.polymorphic_object = true;
- else if (i->ptr != 0)
- {
- (id_member (*i->obj) != 0
- ? features.simple_object
- : features.no_id_object) = true;
- }
- }
- }
- }
- }
-
- virtual void
- traverse_composite (type& c)
- {
- // Allow all the members to be deleted.
- //
- column_count_type const& cc (column_count (c));
- size_t cont (has_a (c, test_container));
-
- if (cc.total == 0 && cont == 0)
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: no persistent data members in the class" << endl;
- valid_ = false;
- }
- }
-
- bool& valid_;
- tree has_lt_operator_;
-
- traversal::defines defines_;
- typedefs typedefs_;
-
- data_member2 member_;
- traversal::names names_member_;
- };
-}
-
-void
-validate (options const& ops,
- features& f,
- semantics::unit& u,
- semantics::path const& p,
- unsigned short pass)
-{
- bool valid (true);
- database db (ops.database ()[0]);
-
- // Validate options.
- //
- if (ops.generate_schema_only () &&
- ops.schema_format ()[db].count (schema_format::embedded))
- {
- cerr << "error: --generate-schema-only is only valid when generating " <<
- "schema as a standalone SQL or separate C++ file" << endl;
- valid = false;
- }
-
- // Multi-database support options.
- //
- if (ops.multi_database () == multi_database::dynamic &&
- ops.default_database_specified () &&
- ops.default_database () != database::common)
- {
- cerr << "error: when dynamic multi-database support is used, the " <<
- "default database can only be 'common'" << endl;
- valid = false;
- }
-
- if (db == database::common &&
- ops.multi_database () == multi_database::disabled)
- {
- cerr << "error: 'common' database is only valid with multi-database " <<
- "support enabled" << endl;
- valid = false;
- }
-
- // Changelog options.
- //
- if (ops.changelog_in ().count (db) != ops.changelog_out ().count (db))
- {
- cerr << "error: both --changelog-in and --changelog-out must be " <<
- "specified" << endl;
- valid = false;
- }
-
- if (!valid)
- throw validator_failed ();
-
- unique_ptr<context> ctx (create_context (cerr, u, ops, f, 0));
-
- if (pass == 1)
- {
- traversal::unit unit;
- traversal::defines unit_defines;
- traversal::declares unit_declares;
- typedefs1 unit_typedefs (unit_declares);
- traversal::namespace_ ns;
- value_type vt;
- class1 c (valid);
-
- unit >> unit_defines >> ns;
- unit_defines >> c;
- unit >> unit_declares >> vt;
- unit >> unit_typedefs >> c;
-
- traversal::defines ns_defines;
- traversal::declares ns_declares;
- typedefs1 ns_typedefs (ns_declares);
-
- ns >> ns_defines >> ns;
- ns_defines >> c;
- ns >> ns_declares >> vt;
- ns >> ns_typedefs >> c;
-
- unit.dispatch (u);
- }
- else
- {
- traversal::unit unit;
- traversal::defines unit_defines;
- typedefs unit_typedefs (true);
- traversal::namespace_ ns;
- class2 c (valid);
-
- unit >> unit_defines >> ns;
- unit_defines >> c;
- unit >> unit_typedefs >> c;
-
- traversal::defines ns_defines;
- typedefs ns_typedefs (true);
-
- ns >> ns_defines >> ns;
- ns_defines >> c;
- ns >> ns_typedefs >> c;
-
- unit.dispatch (u);
- }
-
- if (!valid)
- throw validator_failed ();
-
- switch (db)
- {
- case database::common:
- {
- break;
- }
- case database::mssql:
- case database::mysql:
- case database::oracle:
- case database::pgsql:
- case database::sqlite:
- {
- try
- {
- relational::validate (ops, f, u, p, pass);
- }
- catch (operation_failed const&)
- {
- throw validator_failed ();
- }
-
- break;
- }
- }
-}
diff --git a/odb/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
diff --git a/packages.manifest b/packages.manifest
new file mode 100644
index 0000000..95df333
--- /dev/null
+++ b/packages.manifest
@@ -0,0 +1,20 @@
+: 1
+location: odb/
+:
+location: libodb/
+:
+location: libodb-mysql/
+:
+location: libodb-sqlite/
+:
+location: libodb-pgsql/
+:
+location: libodb-oracle/
+:
+location: libodb-mssql/
+:
+location: libodb-boost/
+:
+location: libodb-qt/
+:
+location: odb-tests/
diff --git a/repositories.manifest b/repositories.manifest
index b5047b5..b22a702 100644
--- a/repositories.manifest
+++ b/repositories.manifest
@@ -1,6 +1,18 @@
: 1
summary: ODB compiler repository
+#:
+#role: prerequisite
+#location: https://git.build2.org/packaging/mysql/mysql.git##HEAD
+
+#:
+#role: prerequisite
+#location: https://git.build2.org/packaging/sqlite/sqlite.git##HEAD
+
+#:
+#role: prerequisite
+#location: https://git.build2.org/packaging/postgresql/postgresql.git##HEAD
+
:
role: prerequisite
location: https://git.codesynthesis.com/libcutl/libcutl.git##HEAD
@@ -8,3 +20,12 @@ location: https://git.codesynthesis.com/libcutl/libcutl.git##HEAD
:
role: prerequisite
location: https://git.codesynthesis.com/libstudxml/libstudxml.git##HEAD
+
+:
+role: prerequisite
+location: https://git.codesynthesis.com/cli/cli.git##HEAD
+
+:
+role: prerequisite
+location: https://pkg.cppget.org/1/alpha
+trust: 70:64:FE:E4:E0:F3:60:F1:B4:51:E1:FA:12:5C:E0:B3:DB:DF:96:33:39:B9:2E:E5:C2:68:63:4C:A6:47:39:43
diff --git a/tests/build/root.build b/tests/build/root.build
deleted file mode 100644
index c797685..0000000
--- a/tests/build/root.build
+++ /dev/null
@@ -1,16 +0,0 @@
-# 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 odb = odb%exe{odb}
-testscript{*}: test = $odb
-
-# Specify the test target for cross-testing.
-#
-test.target = $cxx.target
diff --git a/version b/version
deleted file mode 100644
index a60117f..0000000
--- a/version
+++ /dev/null
@@ -1 +0,0 @@
-2.5.0-b.20